cargo_zng/res/
about.rs

1use std::{
2    cmp::Ordering,
3    fmt::Write as _,
4    fs,
5    path::{Path, PathBuf},
6    process::Stdio,
7};
8
9use crate::util::workspace_dir;
10
11pub fn find_about(metadata: Option<&Path>, verbose: bool) -> zng_env::About {
12    if let Some(m) = metadata {
13        if verbose {
14            println!("parsing `{}`", m.display());
15        }
16
17        let cargo_toml = fs::read_to_string(m).unwrap_or_else(|e| fatal!("cannot read `{}`, {e}", m.display()));
18        return zng_env::About::parse_manifest(&cargo_toml).unwrap_or_else(|e| fatal!("cannot parse `{}`, {e}", m.display()));
19    }
20
21    let mut options = Vec::with_capacity(1);
22
23    let workspace_manifest =
24        workspace_dir().unwrap_or_else(|| fatal!("cannot locate workspace, use --metadata if source is not in a cargo project"));
25    if verbose {
26        println!("workspace `{}`", workspace_manifest.display())
27    }
28
29    for manifest in glob::glob(&format!(
30        "{}/**/Cargo.toml",
31        workspace_manifest.display().to_string().replace("\\", "/").trim_end_matches('/')
32    ))
33    .unwrap_or_else(|e| fatal!("cannot search metadata, {e}"))
34    {
35        let manifest = manifest.unwrap_or_else(|e| fatal!("error searching metadata, {e}"));
36        let _empty = PathBuf::new();
37        let manifest_dir = manifest.parent().unwrap_or(&_empty);
38        if manifest_dir.as_os_str().is_empty() {
39            continue;
40        }
41
42        let output = std::process::Command::new("cargo")
43            .arg("locate-project")
44            .arg("--workspace")
45            .arg("--message-format=plain")
46            .current_dir(manifest_dir)
47            .stderr(Stdio::inherit())
48            .output()
49            .unwrap_or_else(|e| fatal!("cannot locate workspace in `{}`, {e}", manifest_dir.display()));
50        if !output.status.success() {
51            continue;
52        }
53        let w2 = Path::new(std::str::from_utf8(&output.stdout).unwrap().trim()).parent().unwrap();
54        if w2 != workspace_manifest {
55            if verbose {
56                println!("skip `{}`, not a workspace member", manifest.display())
57            }
58            continue;
59        }
60
61        let cargo_toml = fs::read_to_string(&manifest).unwrap_or_else(|e| fatal!("cannot read `{}`, {e}", manifest.display()));
62        let about = match zng_env::About::parse_manifest(&cargo_toml) {
63            Ok(a) => a,
64            Err(e) => {
65                if e.message().contains("missing field `package`") {
66                    if verbose {
67                        println!("skip `{}`, no package metadata", manifest.display());
68                    }
69                } else {
70                    error!("cannot parse `{}`, {e}", manifest.display());
71                }
72                continue;
73            }
74        };
75
76        if about.has_about || manifest_dir.join("src/main.rs").exists() {
77            options.push(about);
78        } else if verbose {
79            println!(
80                "skip `{}` cause it has no zng metadata and/or it is not a bin crate",
81                manifest.display()
82            );
83        }
84    }
85
86    match options.len().cmp(&1) {
87        Ordering::Less => fatal!("cannot find main crate metadata, workspace has no bin crate, use --metadata to select a source"),
88        Ordering::Equal => options.remove(0),
89        Ordering::Greater => {
90            let mut main_options = Vec::with_capacity(1);
91            for (i, o) in options.iter().enumerate() {
92                if o.has_about {
93                    main_options.push(i);
94                }
95            }
96            match main_options.len().cmp(&1) {
97                Ordering::Equal => options.remove(main_options[0]),
98                Ordering::Less => {
99                    let mut msg = "cannot find main crate metadata, workspace has multiple bin crates\n".to_owned();
100                    for o in &options {
101                        writeln!(&mut msg, "   {}", o.pkg_name).unwrap();
102                    }
103                    writeln!(
104                        &mut msg,
105                        "set [package.metadata.zng.about]app=\"Display Name\" in one of the crates\nor use --metadata to select the source"
106                    )
107                    .unwrap();
108                    fatal!("{msg}");
109                }
110                Ordering::Greater => {
111                    let mut msg = "cannot find main crate metadata, workspace has multiple metadata sources\n".to_owned();
112                    for i in main_options {
113                        writeln!(&mut msg, "   {}", options[i].pkg_name).unwrap();
114                    }
115                    writeln!(
116                        &mut msg,
117                        "set [package.metadata.zng.about] in only one crate\nor use --metadata to select the source"
118                    )
119                    .unwrap();
120                    fatal!("{msg}");
121                }
122            }
123        }
124    }
125}