cmsis_pack/pdsc/
mod.rs

1use roxmltree::Node;
2use serde::{Deserialize, Serialize};
3use std::borrow::Cow;
4use std::collections::{BTreeMap, HashMap};
5use std::fs::OpenOptions;
6use std::io::Read;
7use std::path::Path;
8
9use crate::utils::prelude::*;
10use anyhow::{format_err, Error};
11
12mod component;
13mod condition;
14mod device;
15pub use component::{ComponentBuilders, FileRef};
16pub use condition::{Condition, Conditions};
17pub use device::{AccessPort, Algorithm, Core, Device, Devices, Memories, Processor};
18
19pub struct Release {
20    pub version: String,
21    pub text: String,
22}
23
24impl FromElem for Release {
25    fn from_elem(e: &Node) -> Result<Self, Error> {
26        assert_root_name(e, "release")?;
27        Ok(Self {
28            version: attr_map(e, "version")?,
29            text: e.text().unwrap().to_string(),
30        })
31    }
32}
33
34#[derive(Default)]
35pub struct Releases(Vec<Release>);
36
37impl Releases {
38    pub fn latest_release(&self) -> &Release {
39        &self.0[0]
40    }
41}
42
43impl FromElem for Releases {
44    fn from_elem(e: &Node) -> Result<Self, Error> {
45        assert_root_name(e, "releases")?;
46        let to_ret: Vec<_> = e
47            .children()
48            .filter(|e| e.is_element())
49            .flat_map(|c| Release::from_elem(&c).ok_warn())
50            .collect();
51        if to_ret.is_empty() {
52            Err(format_err!("There must be at least one release!"))
53        } else {
54            Ok(Releases(to_ret))
55        }
56    }
57}
58
59#[derive(Debug, Serialize, Deserialize)]
60pub struct DumpDevice<'a> {
61    name: &'a str,
62    memories: Cow<'a, Memories>,
63    algorithms: Cow<'a, Vec<Algorithm>>,
64    processors: Cow<'a, Vec<Processor>>,
65    from_pack: FromPack<'a>,
66    vendor: Option<&'a str>,
67    family: &'a str,
68    sub_family: Option<&'a str>,
69}
70
71#[derive(Clone, Debug, Serialize, Deserialize)]
72struct FromPack<'a> {
73    vendor: &'a str,
74    pack: &'a str,
75    version: &'a str,
76    url: &'a str,
77}
78
79impl<'a> FromPack<'a> {
80    fn new(vendor: &'a str, pack: &'a str, version: &'a str, url: &'a str) -> Self {
81        Self {
82            vendor,
83            pack,
84            version,
85            url,
86        }
87    }
88}
89
90impl<'a> DumpDevice<'a> {
91    fn from_device(dev: &'a Device, from_pack: FromPack<'a>) -> Self {
92        Self {
93            name: &dev.name,
94            memories: Cow::Borrowed(&dev.memories),
95            algorithms: Cow::Borrowed(&dev.algorithms),
96            processors: Cow::Borrowed(&dev.processors),
97            from_pack,
98            vendor: dev.vendor.as_deref(),
99            family: &dev.family,
100            sub_family: dev.sub_family.as_deref(),
101        }
102    }
103}
104
105pub struct Package {
106    pub name: String,
107    pub description: String,
108    pub vendor: String,
109    pub url: String,
110    pub license: Option<String>,
111    components: ComponentBuilders,
112    pub releases: Releases,
113    pub conditions: Conditions,
114    pub devices: Devices,
115    pub boards: Vec<Board>,
116}
117
118impl FromElem for Package {
119    fn from_elem(e: &Node) -> Result<Self, Error> {
120        assert_root_name(e, "package")?;
121        let name: String = child_text(e, "name")?;
122        let description: String = child_text(e, "description")?;
123        let vendor: String = child_text(e, "vendor")?;
124        let url: String = child_text(e, "url")?;
125        log::debug!("Working on {}::{}", vendor, name,);
126        let mut components = ComponentBuilders::default();
127        let mut releases = Releases::default();
128        let mut conditions = Conditions::default();
129        let mut devices = Devices::default();
130        let mut boards: Vec<Board> = Vec::new();
131        for child in e.children() {
132            match child.tag_name().name() {
133                "components" => {
134                    components = ComponentBuilders::from_elem(&child)
135                        .ok_warn()
136                        .unwrap_or_default();
137                }
138                "releases" => {
139                    releases = Releases::from_elem(&child).ok_warn().unwrap_or_default();
140                }
141                "conditions" => {
142                    conditions = Conditions::from_elem(&child).ok_warn().unwrap_or_default();
143                }
144                "devices" => {
145                    devices = Devices::from_elem(&child).ok_warn().unwrap_or_default();
146                }
147                "boards" => {
148                    boards = Board::vec_from_children(child.children());
149                }
150                _ => {}
151            }
152        }
153        Ok(Self {
154            name,
155            description,
156            vendor,
157            url,
158            components,
159            license: child_text(e, "license").ok(),
160            releases,
161            conditions,
162            devices,
163            boards,
164        })
165    }
166}
167
168#[derive(Debug, Deserialize, Serialize)]
169pub struct Board {
170    name: String,
171    mounted_devices: Vec<String>,
172}
173
174impl FromElem for Board {
175    fn from_elem(e: &Node) -> Result<Self, Error> {
176        Ok(Self {
177            name: attr_map(e, "name")?,
178            mounted_devices: e
179                .children()
180                .flat_map(|c| match c.tag_name().name() {
181                    "mountedDevice" => attr_map(&c, "Dname").ok(),
182                    _ => None,
183                })
184                .collect(),
185        })
186    }
187}
188
189#[derive(Debug, Serialize)]
190pub struct Component {
191    pub vendor: String,
192    pub class: String,
193    pub group: String,
194    pub sub_group: Option<String>,
195    pub variant: Option<String>,
196    pub version: String,
197    pub api_version: Option<String>,
198    pub condition: Option<String>,
199    pub max_instances: Option<u8>,
200    pub is_default: bool,
201    pub deprecated: bool,
202    pub description: String,
203    pub rte_addition: String,
204    pub files: Vec<FileRef>,
205}
206
207type Components = Vec<Component>;
208
209impl Package {
210    pub fn make_components(&self) -> Components {
211        self.components
212            .0
213            .clone()
214            .into_iter()
215            .map(|comp| Component {
216                vendor: comp.vendor.unwrap_or_else(|| self.vendor.clone()),
217                class: comp.class.unwrap(),
218                group: comp.group.unwrap(),
219                sub_group: comp.sub_group,
220                variant: comp.variant,
221                version: comp
222                    .version
223                    .unwrap_or_else(|| self.releases.latest_release().version.clone()),
224                api_version: comp.api_version,
225                condition: comp.condition,
226                max_instances: comp.max_instances,
227                is_default: comp.is_default,
228                deprecated: comp.deprecated,
229                description: comp.description,
230                rte_addition: comp.rte_addition,
231                files: comp.files,
232            })
233            .collect()
234    }
235
236    pub fn make_condition_lookup(&self) -> HashMap<&str, &Condition> {
237        let mut map = HashMap::with_capacity(self.conditions.0.len());
238        for cond in self.conditions.0.iter() {
239            if let Some(dup) = map.insert(cond.id.as_str(), cond) {
240                log::warn!("Duplicate Condition found {}", dup.id);
241            }
242        }
243        map
244    }
245
246    pub fn make_dump_devices(&self) -> Vec<(&str, DumpDevice<'_>)> {
247        let from_pack = FromPack::new(
248            &self.vendor,
249            &self.name,
250            &self.releases.latest_release().version,
251            &self.url,
252        );
253        self.devices
254            .0
255            .iter()
256            .map(|(name, d)| (name.as_str(), DumpDevice::from_device(d, from_pack.clone())))
257            .collect()
258    }
259}
260pub fn dump_devices<'a, P: AsRef<Path>, I: IntoIterator<Item = &'a Package>>(
261    pdscs: I,
262    device_dest: Option<P>,
263    board_dest: Option<P>,
264) -> Result<(), Error> {
265    let pdscs: Vec<&Package> = pdscs.into_iter().collect();
266    let devices = pdscs
267        .iter()
268        .flat_map(|pdsc| pdsc.make_dump_devices().into_iter())
269        .collect::<HashMap<_, _>>();
270    match device_dest {
271        Some(to_file) => {
272            if !devices.is_empty() {
273                let mut file_contents = Vec::new();
274                let mut old_devices: HashMap<&str, DumpDevice> = HashMap::new();
275                if let Ok(mut fd) = OpenOptions::new().read(true).open(to_file.as_ref()) {
276                    fd.read_to_end(&mut file_contents)?;
277                    old_devices = serde_json::from_slice(&file_contents).unwrap_or_default();
278                }
279                let mut all_devices = BTreeMap::new();
280                all_devices.extend(old_devices.iter());
281                all_devices.extend(devices.iter());
282                let mut options = OpenOptions::new();
283                options.write(true);
284                options.create(true);
285                options.truncate(true);
286                if let Ok(fd) = options.open(to_file.as_ref()) {
287                    serde_json::to_writer_pretty(fd, &all_devices).unwrap();
288                } else {
289                    println!("Could not open file {:?}", to_file.as_ref());
290                }
291            }
292        }
293        None => println!("{}", &serde_json::to_string_pretty(&devices).unwrap()),
294    }
295    let boards = pdscs
296        .iter()
297        .flat_map(|pdsc| pdsc.boards.iter())
298        .map(|b| (&b.name, b))
299        .collect::<HashMap<_, _>>();
300    match board_dest {
301        Some(to_file) => {
302            let mut file_contents = Vec::new();
303            let mut old_boards: HashMap<String, Board> = HashMap::new();
304            if let Ok(mut fd) = OpenOptions::new().read(true).open(to_file.as_ref()) {
305                fd.read_to_end(&mut file_contents)?;
306                old_boards = serde_json::from_slice(&file_contents).unwrap_or_default();
307            }
308            let mut all_boards = BTreeMap::new();
309            all_boards.extend(old_boards.iter());
310            all_boards.extend(boards.iter());
311            let mut options = OpenOptions::new();
312            options.write(true);
313            options.create(true);
314            options.truncate(true);
315            if let Ok(fd) = options.open(to_file.as_ref()) {
316                serde_json::to_writer_pretty(fd, &all_boards).unwrap();
317            } else {
318                println!("Could not open file {:?}", to_file.as_ref());
319            }
320        }
321        None => println!("{}", &serde_json::to_string_pretty(&devices).unwrap()),
322    }
323    Ok(())
324}
325
326pub fn dumps_components<'a, I>(pdscs: I) -> Result<String, Error>
327where
328    I: IntoIterator<Item = &'a Package>,
329{
330    let components = pdscs
331        .into_iter()
332        .flat_map(|pdsc| pdsc.make_components().into_iter())
333        .collect::<Vec<_>>();
334    Ok(serde_json::to_string_pretty(&components)?)
335}