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}