Skip to main content

guicons_cli/
fetch.rs

1use guicons_core::IconEntrySource;
2use std::path::Path;
3
4#[derive(Debug)]
5pub struct FetchSummary {
6    pub fetched: Vec<String>,
7    pub skipped: Vec<String>,
8    pub failed: Vec<(String, String)>,
9}
10
11impl FetchSummary {
12    pub fn is_success(&self) -> bool {
13        self.failed.is_empty()
14    }
15}
16
17/// Populates `.cache/guicons/...` for every `iconify`/`url` entry in the
18/// manifest. `cache_search_start` should be the current directory in
19/// practice (see `main.rs`) - it's a parameter rather than read internally
20/// so tests can point it at a tempdir instead of the real process cwd. It
21/// must match whatever `guicons-build`'s codegen uses so a `fetch` here and
22/// a later `cargo build` agree on where the icon lives.
23pub fn fetch(manifest_path: &Path, cache_search_start: &Path, force: bool) -> Result<FetchSummary, Vec<String>> {
24    let (manifest, errors) = guicons_core::load_icon_manifest(manifest_path);
25    if !errors.is_empty() {
26        return Err(errors.iter().map(|e| e.to_string()).collect());
27    }
28
29    let mut summary = FetchSummary {
30        fetched: Vec::new(),
31        skipped: Vec::new(),
32        failed: Vec::new(),
33    };
34
35    for entry in manifest.entries() {
36        let (cache_path, url, label) = match entry.source() {
37            IconEntrySource::Iconify(id) => (
38                guicons_net::iconify_cache_path(cache_search_start, id),
39                guicons_net::iconify_url(id),
40                id.clone(),
41            ),
42            IconEntrySource::Url(url) => {
43                (guicons_net::url_cache_path(cache_search_start, url), url.clone(), url.clone())
44            }
45            IconEntrySource::File(_) | IconEntrySource::Glyph(_) => continue,
46        };
47
48        if cache_path.exists() && !force {
49            summary.skipped.push(label);
50            continue;
51        }
52
53        match guicons_net::download(&url, &cache_path) {
54            Ok(()) => summary.fetched.push(label),
55            Err(e) => summary.failed.push((label, e.to_string())),
56        }
57    }
58
59    Ok(summary)
60}