| 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 { |
| 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 | |
| 15 | fn to_string(&self, deps: &BTreeSet<Box<str>>) -> String { |
| 16 | // Transforms a string by escaping spaces and backslashes. |
| 17 | let escape: impl Fn(&str) -> String = |s: &str| s.replace(' \\' , " \\\\" ).replace(from:' ' , to:" \\ " ); |
| 18 | |
| 19 | let mut buf: String = format!(" {}:" , escape(&self.output_module)); |
| 20 | for file: &Box 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 | |