1use std::path::PathBuf;
2use std::str::FromStr;
3
4use anyhow::{format_err, Error};
5use roxmltree::Node;
6use serde::Serialize;
7
8use crate::utils::prelude::*;
9
10#[derive(Debug, PartialEq, Eq, Clone, Serialize)]
11pub enum FileCategory {
12 Doc,
13 Header,
14 Include,
15 Library,
16 Object,
17 Source,
18 SourceC,
19 SourceCpp,
20 SourceAsm,
21 LinkerScript,
22 Utility,
23 Image,
24 PreIncludeGlobal,
25 PreIncludeLocal,
26 Other,
27}
28
29impl FromStr for FileCategory {
30 type Err = Error;
31 fn from_str(from: &str) -> Result<Self, Error> {
32 match from {
33 "doc" => Ok(FileCategory::Doc),
34 "header" => Ok(FileCategory::Header),
35 "include" => Ok(FileCategory::Include),
36 "library" => Ok(FileCategory::Library),
37 "object" => Ok(FileCategory::Object),
38 "source" => Ok(FileCategory::Source),
39 "sourceC" => Ok(FileCategory::SourceC),
40 "sourceCpp" => Ok(FileCategory::SourceCpp),
41 "sourceAsm" => Ok(FileCategory::SourceAsm),
42 "linkerScript" => Ok(FileCategory::LinkerScript),
43 "utility" => Ok(FileCategory::Utility),
44 "image" => Ok(FileCategory::Image),
45 "preIncludeGlobal" => Ok(FileCategory::PreIncludeGlobal),
46 "preIncludeLocal" => Ok(FileCategory::PreIncludeLocal),
47 "other" => Ok(FileCategory::Other),
48 unknown => Err(format_err!("Unknown file category {}", unknown)),
49 }
50 }
51}
52
53#[derive(Debug, PartialEq, Eq, Clone, Serialize)]
54pub enum FileAttribute {
55 Config,
56 Template,
57}
58
59impl FromStr for FileAttribute {
60 type Err = Error;
61 fn from_str(from: &str) -> Result<Self, Error> {
62 match from {
63 "config" => Ok(FileAttribute::Config),
64 "template" => Ok(FileAttribute::Template),
65 unknown => Err(format_err!("Unknown file attribute {}", unknown)),
66 }
67 }
68}
69
70#[derive(Debug, Clone, Serialize)]
71pub struct FileRef {
72 pub path: PathBuf,
73 category: FileCategory,
74 attr: Option<FileAttribute>,
75 pub condition: Option<String>,
76 select: Option<String>,
77 src: Option<String>,
78 version: Option<String>,
79}
80
81impl FromElem for FileRef {
82 fn from_elem(e: &Node) -> Result<Self, Error> {
83 assert_root_name(e, "file")?;
84 Ok(Self {
85 path: attr_map(e, "name")?,
86 category: attr_parse(e, "category")?,
87 attr: attr_parse(e, "attr").ok(),
88 condition: attr_map(e, "condition").ok(),
89 select: attr_map(e, "select").ok(),
90 src: attr_map(e, "src").ok(),
91 version: attr_map(e, "version").ok(),
92 })
93 }
94}
95
96#[derive(Debug, Clone, Serialize)]
97pub struct ComponentBuilder {
98 pub vendor: Option<String>,
99 pub class: Option<String>,
100 pub group: Option<String>,
101 pub sub_group: Option<String>,
102 pub variant: Option<String>,
103 pub version: Option<String>,
104 pub api_version: Option<String>,
105 pub condition: Option<String>,
106 pub max_instances: Option<u8>,
107 pub is_default: bool,
108 pub deprecated: bool,
109 pub description: String,
110 pub rte_addition: String,
111 pub files: Vec<FileRef>,
112}
113
114impl FromElem for ComponentBuilder {
115 fn from_elem(e: &Node) -> Result<Self, Error> {
116 assert_root_name(e, "component")?;
117 let vendor: Option<String> = attr_map(e, "Cvendor").ok();
118 let class: Option<String> = attr_map(e, "Cclass").ok();
119 let group: Option<String> = attr_map(e, "Cgroup").ok();
120 let sub_group: Option<String> = attr_map(e, "Csub").ok();
121 let vendor_string = vendor.clone().unwrap_or_else(|| "Vendor".into());
122 let class_string = class.clone().unwrap_or_else(|| "Class".into());
123 let group_string = group.clone().unwrap_or_else(|| "Group".into());
124 let sub_group_string = sub_group.clone().unwrap_or_else(|| "SubGroup".into());
125 let files = if let Some(node) = e.children().find(|c| c.tag_name().name() == "files") {
126 log::debug!(
127 "Working on {}::{}::{}::{}",
128 vendor_string,
129 class_string,
130 group_string,
131 sub_group_string,
132 );
133 FileRef::vec_from_children(node.children())
134 } else {
135 Vec::new()
136 };
137 Ok(Self {
138 vendor,
139 class,
140 group,
141 sub_group,
142 version: attr_map(e, "Cversion").ok(),
143 variant: attr_map(e, "Cvariant").ok(),
144 api_version: attr_map(e, "Capiversion").ok(),
145 condition: attr_map(e, "condition").ok(),
146 max_instances: attr_parse(e, "maxInstances").ok(),
147 is_default: attr_parse(e, "isDefaultVariant").unwrap_or(true),
148 description: child_text(e, "description")?,
149 deprecated: child_text(e, "deprecated")
150 .map(|s| s.parse().unwrap_or(false))
151 .unwrap_or(false),
152 rte_addition: child_text(e, "RTE_components_h").unwrap_or_default(),
153 files,
154 })
155 }
156}
157
158#[derive(Debug, Serialize)]
159pub struct Bundle {
160 name: String,
161 class: String,
162 version: String,
163 vendor: Option<String>,
164 description: String,
165 doc: String,
166 components: Vec<ComponentBuilder>,
167}
168
169impl Bundle {
170 pub fn into_components(self) -> Vec<ComponentBuilder> {
171 let class = self.class;
172 let version = self.version;
173 let vendor = self.vendor;
174 if self.components.is_empty() {
175 log::warn!("Bundle should not be empty")
176 }
177 self.components
178 .into_iter()
179 .map(|comp| ComponentBuilder {
180 class: comp.class.or_else(|| Some(class.clone())),
181 version: comp.version.or_else(|| Some(version.clone())),
182 vendor: comp.vendor.or_else(|| vendor.clone()),
183 ..comp
184 })
185 .collect()
186 }
187}
188
189impl FromElem for Bundle {
190 fn from_elem(e: &Node) -> Result<Self, Error> {
191 assert_root_name(e, "bundle")?;
192 let name: String = attr_map(e, "Cbundle")?;
193 let class: String = attr_map(e, "Cclass")?;
194 let version: String = attr_map(e, "Cversion")?;
195 let components = e
199 .children()
200 .filter_map(move |chld| {
201 if chld.tag_name().name() == "component" {
202 ComponentBuilder::from_elem(&chld).ok()
203 } else {
204 None
205 }
206 })
207 .collect();
208 Ok(Self {
209 name,
210 class,
211 version,
212 vendor: attr_map(e, "Cvendor").ok(),
213 description: child_text(e, "description")?,
214 doc: child_text(e, "doc")?,
215 components,
216 })
217 }
218}
219
220fn child_to_component_iter(e: &Node) -> Result<Box<dyn Iterator<Item = ComponentBuilder>>, Error> {
221 match e.tag_name().name() {
222 "bundle" => {
223 let bundle = Bundle::from_elem(e)?;
224 Ok(Box::new(bundle.into_components().into_iter()))
225 }
226 "component" => {
227 let component = ComponentBuilder::from_elem(e)?;
228 Ok(Box::new(Some(component).into_iter()))
229 }
230 _ => Err(format_err!(
231 "element of name {} is not allowed as a descendant of components ({:?})",
232 e.tag_name().name(),
233 e
234 )),
235 }
236}
237
238#[derive(Default)]
239pub struct ComponentBuilders(pub(crate) Vec<ComponentBuilder>);
240
241impl FromElem for ComponentBuilders {
242 fn from_elem(e: &Node) -> Result<Self, Error> {
243 assert_root_name(e, "components")?;
244 Ok(ComponentBuilders(
245 e.children()
246 .filter(|e| e.is_element())
247 .flat_map(move |c| match child_to_component_iter(&c) {
248 Ok(iter) => iter,
249 Err(e) => {
250 log::error!("when trying to parse component: {}", e);
251 Box::new(None.into_iter())
252 }
253 })
254 .collect(),
255 ))
256 }
257}