/// Generating build depfiles from parsed bindings. use std::{collections::BTreeSet, path::PathBuf}; #[derive(Clone, Debug)] pub(crate) struct DepfileSpec { pub output_module: String, pub depfile_path: PathBuf, } impl DepfileSpec { pub fn write(&self, deps: &BTreeSet>) -> std::io::Result<()> { std::fs::write(&self.depfile_path, self.to_string(deps)) } fn to_string(&self, deps: &BTreeSet>) -> String { // Transforms a string by escaping spaces and backslashes. let escape = |s: &str| s.replace('\\', "\\\\").replace(' ', "\\ "); let mut buf = format!("{}:", escape(&self.output_module)); for file in deps { buf = format!("{} {}", buf, escape(file)); } buf } } #[cfg(test)] mod tests { use super::*; #[test] fn escaping_depfile() { let spec = DepfileSpec { output_module: "Mod Name".to_owned(), depfile_path: PathBuf::new(), }; let deps: BTreeSet<_> = vec![ r"/absolute/path".into(), r"C:\win\absolute\path".into(), r"../relative/path".into(), r"..\win\relative\path".into(), r"../path/with spaces/in/it".into(), r"..\win\path\with spaces\in\it".into(), r"path\with/mixed\separators".into(), ] .into_iter() .collect(); assert_eq!( spec.to_string(&deps), "Mod\\ Name: \ ../path/with\\ spaces/in/it \ ../relative/path \ ..\\\\win\\\\path\\\\with\\ spaces\\\\in\\\\it \ ..\\\\win\\\\relative\\\\path \ /absolute/path \ C:\\\\win\\\\absolute\\\\path \ path\\\\with/mixed\\\\separators" ); } }