nydus_builder/
attributes.rs1use std::collections::HashMap;
6use std::path::{Path, PathBuf};
7use std::{fs, path};
8
9use anyhow::Result;
10use gix_attributes::parse;
11use gix_attributes::parse::Kind;
12
13const KEY_TYPE: &str = "type";
14const KEY_CRCS: &str = "crcs";
15const VAL_EXTERNAL: &str = "external";
16
17pub struct Parser {}
18
19#[derive(Clone, Debug, Eq, PartialEq, Default)]
20pub struct Item {
21 pub pattern: PathBuf,
22 pub attributes: HashMap<String, String>,
23}
24
25#[derive(Clone, Debug, Eq, PartialEq, Default)]
26pub struct Attributes {
27 pub items: HashMap<PathBuf, HashMap<String, String>>,
28 pub crcs: HashMap<PathBuf, Vec<u32>>,
29}
30
31impl Attributes {
32 pub fn from<P: AsRef<Path>>(path: P) -> Result<Attributes> {
34 let content = fs::read(path)?;
35 let _items = parse(&content);
36
37 let mut items = HashMap::new();
38 let mut crcs = HashMap::new();
39 for _item in _items {
40 let _item = _item?;
41 if let Kind::Pattern(pattern) = _item.0 {
42 let mut path = PathBuf::from(pattern.text.to_string());
43 if !path.is_absolute() {
44 path = path::Path::new("/").join(path);
45 }
46 let mut current_path = path.clone();
47 let mut attributes = HashMap::new();
48 let mut _type = String::new();
49 let mut _crcs = vec![];
50 for line in _item.1 {
51 let line = line?;
52 let name = line.name.as_str();
53 let state = line.state.as_bstr().unwrap_or_default();
54 if name == KEY_TYPE {
55 _type = state.to_string();
56 }
57 if name == KEY_CRCS {
58 _crcs = state
59 .to_string()
60 .split(',')
61 .map(|s| {
62 let trimmed = s.trim();
63 let hex_str = if let Some(stripped) = trimmed.strip_prefix("0x") {
64 stripped
65 } else {
66 trimmed
67 };
68 u32::from_str_radix(hex_str, 16).map_err(|e| anyhow::anyhow!(e))
69 })
70 .collect::<Result<Vec<u32>, _>>()?;
71 }
72 attributes.insert(name.to_string(), state.to_string());
73 }
74 crcs.insert(path.clone(), _crcs);
75 items.insert(path, attributes);
76
77 while let Some(parent) = current_path.parent() {
79 if parent == Path::new("/") {
80 break;
81 }
82 let mut attributes = HashMap::new();
83 if !items.contains_key(parent) {
84 attributes.insert(KEY_TYPE.to_string(), VAL_EXTERNAL.to_string());
85 items.insert(parent.to_path_buf(), attributes);
86 }
87 current_path = parent.to_path_buf();
88 }
89 }
90 }
91
92 Ok(Attributes { items, crcs })
93 }
94
95 fn check_external(&self, attributes: &HashMap<String, String>) -> bool {
96 attributes.get(KEY_TYPE) == Some(&VAL_EXTERNAL.to_string())
97 }
98
99 pub fn is_external<P: AsRef<Path>>(&self, path: P) -> bool {
100 if let Some(attributes) = self.items.get(path.as_ref()) {
101 return self.check_external(attributes);
102 }
103 false
104 }
105
106 pub fn is_prefix_external<P: AsRef<Path>>(&self, target: P) -> bool {
107 self.items
108 .iter()
109 .any(|item| item.0.starts_with(&target) && self.check_external(item.1))
110 }
111
112 pub fn get_value<P: AsRef<Path>, K: AsRef<str>>(&self, path: P, key: K) -> Option<String> {
113 if let Some(attributes) = self.items.get(path.as_ref()) {
114 return attributes.get(key.as_ref()).map(|s| s.to_string());
115 }
116 None
117 }
118
119 pub fn get_values<P: AsRef<Path>>(&self, path: P) -> Option<&HashMap<String, String>> {
120 self.items.get(path.as_ref())
121 }
122
123 pub fn get_crcs<P: AsRef<Path>>(&self, path: P) -> Option<&Vec<u32>> {
124 self.crcs.get(path.as_ref())
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use std::{collections::HashMap, fs, path::PathBuf};
131
132 use super::{Attributes, Item};
133 use vmm_sys_util::tempfile::TempFile;
134
135 #[test]
136 fn test_attribute_parse() {
137 let file = TempFile::new().unwrap();
138 fs::write(
139 file.as_path(),
140 "/foo type=external crcs=0x1234,0x5678
141 /bar type=external crcs=0x1234,0x5678
142 /models/foo/bar type=external",
143 )
144 .unwrap();
145
146 let attributes = Attributes::from(file.as_path()).unwrap();
147 let _attributes_base: HashMap<String, String> =
148 [("type".to_string(), "external".to_string())]
149 .iter()
150 .cloned()
151 .collect();
152 let _attributes: HashMap<String, String> = [
153 ("type".to_string(), "external".to_string()),
154 ("crcs".to_string(), "0x1234,0x5678".to_string()),
155 ]
156 .iter()
157 .cloned()
158 .collect();
159
160 let items_map: HashMap<PathBuf, HashMap<String, String>> = vec![
161 Item {
162 pattern: PathBuf::from("/foo"),
163 attributes: _attributes.clone(),
164 },
165 Item {
166 pattern: PathBuf::from("/bar"),
167 attributes: _attributes.clone(),
168 },
169 Item {
170 pattern: PathBuf::from("/models"),
171 attributes: _attributes_base.clone(),
172 },
173 Item {
174 pattern: PathBuf::from("/models/foo"),
175 attributes: _attributes_base.clone(),
176 },
177 Item {
178 pattern: PathBuf::from("/models/foo/bar"),
179 attributes: _attributes_base.clone(),
180 },
181 ]
182 .into_iter()
183 .map(|item| (item.pattern, item.attributes))
184 .collect();
185
186 assert_eq!(attributes.items, items_map);
187 assert_eq!(attributes.get_crcs("/foo"), Some(&vec![0x1234, 0x5678]))
188 }
189}