1 /// Generating build depfiles from parsed bindings. 2 use std::{collections::BTreeSet, path::PathBuf}; 3 4 #[derive(Clone, Debug)] 5 pub(crate) struct DepfileSpec { 6 pub output_module: String, 7 pub depfile_path: PathBuf, 8 } 9 10 impl DepfileSpec { write(&self, deps: &BTreeSet<Box<str>>) -> std::io::Result<()>11 pub fn write(&self, deps: &BTreeSet<Box<str>>) -> std::io::Result<()> { 12 std::fs::write(&self.depfile_path, self.to_string(deps)) 13 } 14 to_string(&self, deps: &BTreeSet<Box<str>>) -> String15 fn to_string(&self, deps: &BTreeSet<Box<str>>) -> String { 16 // Transforms a string by escaping spaces and backslashes. 17 let escape = |s: &str| s.replace('\\', "\\\\").replace(' ', "\\ "); 18 19 let mut buf = format!("{}:", escape(&self.output_module)); 20 for file in deps { 21 buf = format!("{} {}", buf, escape(file)); 22 } 23 buf 24 } 25 } 26 27 #[cfg(test)] 28 mod tests { 29 use super::*; 30 31 #[test] 32 fn escaping_depfile() { 33 let spec = DepfileSpec { 34 output_module: "Mod Name".to_owned(), 35 depfile_path: PathBuf::new(), 36 }; 37 38 let deps: BTreeSet<_> = vec![ 39 r"/absolute/path".into(), 40 r"C:\win\absolute\path".into(), 41 r"../relative/path".into(), 42 r"..\win\relative\path".into(), 43 r"../path/with spaces/in/it".into(), 44 r"..\win\path\with spaces\in\it".into(), 45 r"path\with/mixed\separators".into(), 46 ] 47 .into_iter() 48 .collect(); 49 assert_eq!( 50 spec.to_string(&deps), 51 "Mod\\ Name: \ 52 ../path/with\\ spaces/in/it \ 53 ../relative/path \ 54 ..\\\\win\\\\path\\\\with\\ spaces\\\\in\\\\it \ 55 ..\\\\win\\\\relative\\\\path \ 56 /absolute/path \ 57 C:\\\\win\\\\absolute\\\\path \ 58 path\\\\with/mixed\\\\separators" 59 ); 60 } 61 } 62