cmsis_cli/
lib.rs

1extern crate clap;
2extern crate pbr;
3
4use anyhow::Error;
5use clap::{App, Arg, ArgMatches, SubCommand};
6use pbr::ProgressBar;
7use std::io::Stdout;
8use std::path::Path;
9use std::sync::{Arc, Mutex};
10
11extern crate cmsis_pack;
12use cmsis_pack::pdsc::{dump_devices, Component, FileRef, Package};
13use cmsis_pack::update::{install, update, DownloadProgress};
14use cmsis_pack::utils::FromElem;
15
16mod config;
17
18pub use config::Config;
19
20struct CliProgress(Arc<Mutex<ProgressBar<Stdout>>>);
21
22impl DownloadProgress for CliProgress {
23    fn size(&self, files: usize) {
24        if let Ok(mut inner) = self.0.lock() {
25            inner.total = files as u64;
26            inner.show_speed = false;
27            inner.show_bar = true;
28        }
29    }
30    fn progress(&self, _: usize) {}
31    fn complete(&self) {
32        if let Ok(mut inner) = self.0.lock() {
33            inner.inc();
34        }
35    }
36    fn for_file(&self, _: &str) -> Self {
37        CliProgress(self.0.clone())
38    }
39}
40
41impl CliProgress {
42    fn new() -> Self {
43        let mut progress = ProgressBar::new(363);
44        progress.show_speed = false;
45        progress.show_time_left = false;
46        progress.format("[#> ]");
47        progress.message("Downloading Packs ");
48        CliProgress(Arc::new(Mutex::new(progress)))
49    }
50}
51
52pub fn install_args() -> App<'static, 'static> {
53    SubCommand::with_name("install")
54        .about("Install a CMSIS Pack file")
55        .version("0.1.0")
56        .arg(
57            Arg::with_name("PDSC")
58                .required(true)
59                .takes_value(true)
60                .index(1)
61                .multiple(true),
62        )
63}
64
65pub fn install_command(conf: &Config, args: &ArgMatches<'_>) -> Result<(), Error> {
66    let pdsc_list: Vec<_> = args
67        .values_of("PDSC")
68        .unwrap()
69        .filter_map(|input| Package::from_path(Path::new(input)).ok())
70        .collect();
71    let progress = CliProgress::new();
72    let updated = install(conf, pdsc_list.iter(), progress)?;
73    let num_updated = updated.iter().map(|_| 1).sum::<u32>();
74    match num_updated {
75        0 => {
76            log::info!("Already up to date");
77        }
78        1 => {
79            log::info!("Updated 1 package");
80        }
81        _ => {
82            log::info!("Updated {} package", num_updated);
83        }
84    }
85    Ok(())
86}
87
88pub fn update_args<'a, 'b>() -> App<'a, 'b> {
89    SubCommand::with_name("update")
90        .about("Update CMSIS PDSC files for indexing")
91        .version("0.1.0")
92}
93
94pub fn update_command(conf: &Config, _: &ArgMatches<'_>) -> Result<(), Error> {
95    let vidx_list = conf.read_vidx_list();
96    for url in vidx_list.iter() {
97        log::info!("Updating registry from `{}`", url);
98    }
99    let progress = CliProgress::new();
100    let updated = update(conf, vidx_list, progress)?;
101    let num_updated = updated.iter().map(|_| 1).sum::<u32>();
102    match num_updated {
103        0 => {
104            log::info!("Already up to date");
105        }
106        1 => {
107            log::info!("Updated 1 package");
108        }
109        _ => {
110            log::info!("Updated {} package", num_updated);
111        }
112    }
113    Ok(())
114}
115
116pub fn dump_devices_args<'a, 'b>() -> App<'a, 'b> {
117    SubCommand::with_name("dump-devices")
118        .about("Dump devices as json")
119        .version("0.1.0")
120        .arg(
121            Arg::with_name("devices")
122                .short("d")
123                .takes_value(true)
124                .help("Dump JSON in the specified file"),
125        )
126        .arg(
127            Arg::with_name("boards")
128                .short("b")
129                .takes_value(true)
130                .help("Dump JSON in the specified file"),
131        )
132        .arg(
133            Arg::with_name("INPUT")
134                .help("Input file to dump devices from")
135                .index(1),
136        )
137}
138
139pub fn dump_devices_command(c: &Config, args: &ArgMatches<'_>) -> Result<(), Error> {
140    let files = args
141        .value_of("INPUT")
142        .map(|input| vec![Path::new(input).to_path_buf()]);
143    let filenames = files
144        .or_else(|| {
145            c.pack_store.read_dir().ok().map(|rd| {
146                rd.flat_map(|dirent| dirent.into_iter().map(|p| p.path()))
147                    .collect()
148            })
149        })
150        .unwrap();
151    let pdscs = filenames
152        .into_iter()
153        .flat_map(|filename| match Package::from_path(&filename) {
154            Ok(c) => Some(c),
155            Err(e) => {
156                log::error!("parsing {:?}: {}", filename, e);
157                None
158            }
159        })
160        .collect::<Vec<Package>>();
161    let to_ret = dump_devices(&pdscs, args.value_of("devices"), args.value_of("boards"));
162    log::debug!("exiting");
163    to_ret
164}
165
166pub fn check_args<'a, 'b>() -> App<'a, 'b> {
167    SubCommand::with_name("check")
168        .about("Check a project or pack for correct usage of the CMSIS standard")
169        .version("0.1.0")
170        .arg(
171            Arg::with_name("INPUT")
172                .help("Input file to check")
173                .required(true)
174                .index(1),
175        )
176}
177
178pub fn check_command(_: &Config, args: &ArgMatches<'_>) -> Result<(), Error> {
179    let filename = args.value_of("INPUT").unwrap();
180    match Package::from_path(Path::new(filename)) {
181        Ok(c) => {
182            log::info!("Parsing succedded");
183            log::info!("{} Valid Conditions", c.conditions.0.len());
184            let cond_lookup = c.make_condition_lookup();
185            let mut num_components = 0;
186            let mut num_files = 0;
187            for Component {
188                class,
189                group,
190                condition,
191                files,
192                ..
193            } in c.make_components().iter()
194            {
195                num_components += 1;
196                num_files += files.len();
197                if let Some(ref cond_name) = condition {
198                    if !cond_lookup.contains_key(cond_name.as_str()) {
199                        log::warn!(
200                            "Component {}::{} references an unknown condition '{}'",
201                            class,
202                            group,
203                            cond_name
204                        );
205                    }
206                }
207                for FileRef {
208                    path, condition, ..
209                } in files.iter()
210                {
211                    if let Some(ref cond_name) = condition {
212                        if !cond_lookup.contains_key(cond_name.as_str()) {
213                            log::warn!(
214                                "File {:?} Component {}::{} references an unknown condition '{}'",
215                                path,
216                                class,
217                                group,
218                                cond_name
219                            );
220                        }
221                    }
222                }
223            }
224            log::info!("{} Valid Devices", c.devices.0.len());
225            log::info!("{} Valid Software Components", num_components);
226            log::info!("{} Valid Files References", num_files);
227        }
228        Err(e) => {
229            log::error!("parsing {}: {}", filename, e);
230        }
231    }
232    log::debug!("exiting");
233    Ok(())
234}