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 | |