Repository: zng-ui/zng (language: rust)

Issue:
cargo zng res cannot find metadata
<!--
Please, make sure:

- The issue happens in the latest crate release or newer (master branch).
- The issue happens after `cargo update`.
-->

#### I tried this code:

```rust
cargo do zng res
```

#### Following these steps:

1 - Run `cargo do pack common` on the template repo.

#### I saw this happen:

Error.

#### I expected this to happen:
Resourced packed.

#### Error log or stack trace:

<details>
  <summary>stderr</summary>

```
build resources
error: cannot find main crate metadata, workspace has no bin crate
use --metadata to select a source
error: resources build failed
       command exited with error code 102
```

</details>


Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cdbbb9b82..c303b1c44 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,7 @@
 # Unpublished
 
+* Allow missing authors field in `zng_env::About::parse_manifest`.
+* Fix `cargo zng res` not finding any metadata.
 
 # 0.8.0
 
diff --git a/crates/cargo-zng/README.md b/crates/cargo-zng/README.md
index 34efbbb4c..17ef8d421 100644
--- a/crates/cargo-zng/README.md
+++ b/crates/cargo-zng/README.md
@@ -199,22 +199,21 @@ Build resources
 
 Build resources
 
-Builds resources SOURCE to TARGET, delegates `.zr-{tool}` files to `cargo-zng-res-{tool}`
-executables and crates.
+Builds resources SOURCE to TARGET, delegates `.zr-{tool}` files to `cargo-zng-res-{tool}` executables and crates.
 
 Usage: cargo zng res [OPTIONS] [SOURCE] [TARGET]
 
 Arguments:
   [SOURCE]
           Resources source dir
-
+          
           [default: res]
 
   [TARGET]
           Resources target dir
-
+          
           This directory is wiped before each build.
-
+          
           [default: target/res]
 
 Options:
@@ -223,7 +222,7 @@ Options:
 
       --tool-dir <DIR>
           Search for `zng-res-{tool}` in this directory first
-
+          
           [default: tools]
 
       --tools
@@ -234,14 +233,24 @@ Options:
 
       --tool-cache <TOOL_CACHE>
           Tools cache dir
-
+          
           [default: target/res.cache]
 
       --recursion-limit <RECURSION_LIMIT>
           Number of build passes allowed before final
-
+          
           [default: 32]
 
+      --metadata <TOML_FILE>
+          TOML file that that defines metadata uses by tools (ZR_APP, ZR_ORG, ..)
+          
+          This is only needed if the workspace has multiple bin crates and none or many set '[package.metadata.zng.about]'.
+          
+          See `zng::env::About` for more details.
+
+      --metadata-dump
+          Writes the metadata extracted the workspace or --metadata
+
   -h, --help
           Print help (see a summary with '-h')
 
diff --git a/crates/cargo-zng/src/res.rs b/crates/cargo-zng/src/res.rs
index 9b41e3cac..408702da6 100644
--- a/crates/cargo-zng/src/res.rs
+++ b/crates/cargo-zng/src/res.rs
@@ -59,8 +59,12 @@ pub struct ResArgs {
     /// and none or many set '[package.metadata.zng.about]'.
     ///
     /// See `zng::env::About` for more details.
-    #[arg(long, value_name = "TOML")]
+    #[arg(long, value_name = "TOML_FILE")]
     metadata: Option<PathBuf>,
+
+    /// Writes the metadata extracted the workspace or --metadata
+    #[arg(long, action)]
+    metadata_dump: bool,
 }
 
 fn canonicalize(path: &Path) -> PathBuf {
@@ -78,6 +82,14 @@ pub(crate) fn run(mut args: ResArgs) {
         return tool_help(&args.tool_dir, &t);
     }
 
+    if args.metadata_dump {
+        let about = about::find_about(args.metadata.as_deref());
+        crate::res::tool::visit_about_vars(&about, |key, value| {
+            println!("{key}={value}");
+        });
+        return;
+    }
+
     if !args.source.exists() {
         fatal!("source dir does not exist");
     }
diff --git a/crates/cargo-zng/src/res/about.rs b/crates/cargo-zng/src/res/about.rs
index 9c5cf8250..5aea9a388 100644
--- a/crates/cargo-zng/src/res/about.rs
+++ b/crates/cargo-zng/src/res/about.rs
@@ -20,11 +20,12 @@ pub fn find_about(metadata: Option<&Path>) -> zng_env::About {
                 .arg("locate-project")
                 .arg("--workspace")
                 .arg("--message-format=plain")
+                .current_dir(manifest.parent().unwrap())
                 .output()
                 .unwrap_or_else(|e| fatal!("cannot locate workspace, {e}"));
 
             if output.status.success() {
-                let w2 = Path::new(std::str::from_utf8(&output.stdout).unwrap().trim());
+                let w2 = Path::new(std::str::from_utf8(&output.stdout).unwrap().trim()).parent().unwrap();
                 if workspace_manifest == w2 {
                     let cargo_toml = fs::read_to_string(&manifest).unwrap_or_else(|e| fatal!("cannot read `{}`, {e}", manifest.display()));
                     options.push(
@@ -37,7 +38,7 @@ pub fn find_about(metadata: Option<&Path>) -> zng_env::About {
     }
 
     match options.len().cmp(&1) {
-        Ordering::Less => fatal!("cannot find main crate metadata, workspace has no bin crate\nuse --metadata to select a source"),
+        Ordering::Less => fatal!("cannot find main crate metadata, workspace has no bin crate, use --metadata to select a source"),
         Ordering::Equal => options.remove(0),
         Ordering::Greater => {
             let mut main_options = Vec::with_capacity(1);
@@ -55,7 +56,7 @@ pub fn find_about(metadata: Option<&Path>) -> zng_env::About {
                     }
                     writeln!(
                         &mut msg,
-                        r#"set [package.metadata.zng.about]app="Display Name"\nin one of the crates"#
+                        "set [package.metadata.zng.about]app=\"Display Name\" in one of the crates\nor use --metadata to select the source"
                     )
                     .unwrap();
                     fatal!("{msg}");
@@ -67,7 +68,7 @@ pub fn find_about(metadata: Option<&Path>) -> zng_env::About {
                     }
                     writeln!(
                         &mut msg,
-                        r#"set [package.metadata.zng.about] in only one crate\nor use --metadata to select the source"#
+                        "set [package.metadata.zng.about] in only one crate\nor use --metadata to select the source"
                     )
                     .unwrap();
                     fatal!("{msg}");
diff --git a/crates/cargo-zng/src/res/tool.rs b/crates/cargo-zng/src/res/tool.rs
index fb9f53d3d..df55be882 100644
--- a/crates/cargo-zng/src/res/tool.rs
+++ b/crates/cargo-zng/src/res/tool.rs
@@ -97,6 +97,18 @@ pub fn visit_tools(local: &Path, mut tool: impl FnMut(Tool) -> anyhow::Result<Co
     Ok(())
 }
 
+pub fn visit_about_vars(about: &About, mut visit: impl FnMut(&str, &str)) {
+    visit(ZR_APP, &about.app);
+    visit(ZR_CRATE_NAME, &about.crate_name);
+    visit(ZR_HOMEPAGE, &about.homepage);
+    visit(ZR_ORG, &about.org);
+    visit(ZR_PKG_AUTHORS, &about.pkg_authors.clone().join(","));
+    visit(ZR_PKG_NAME, &about.pkg_name);
+    visit(ZR_QUALIFIER, &about.qualifier);
+    visit(ZR_VERSION, &about.version.to_string());
+    visit(ZR_DESCRIPTION, &about.description);
+}
+
 pub struct Tool {
     pub name: String,
     pub kind: ToolKind,
@@ -147,25 +159,18 @@ impl Tool {
             target = target_dir.join(p);
         }
 
-        self.run_cmd(
-            cmd.env(ZR_WORKSPACE_DIR, std::env::current_dir().unwrap())
-                .env(ZR_SOURCE_DIR, source_dir)
-                .env(ZR_TARGET_DIR, target_dir)
-                .env(ZR_REQUEST_DD, request.parent().unwrap())
-                .env(ZR_REQUEST, request)
-                .env(ZR_TARGET_DD, target.parent().unwrap())
-                .env(ZR_TARGET, target)
-                .env(ZR_CACHE_DIR, cache.join(cache_dir))
-                .env(ZR_APP, &about.app)
-                .env(ZR_CRATE_NAME, &about.crate_name)
-                .env(ZR_DESCRIPTION, &about.description)
-                .env(ZR_HOMEPAGE, &about.homepage)
-                .env(ZR_ORG, &about.org)
-                .env(ZR_PKG_AUTHORS, about.pkg_authors.clone().join(","))
-                .env(ZR_PKG_NAME, &about.pkg_name)
-                .env(ZR_QUALIFIER, &about.qualifier)
-                .env(ZR_VERSION, about.version.to_string()),
-        )
+        cmd.env(ZR_WORKSPACE_DIR, std::env::current_dir().unwrap())
+            .env(ZR_SOURCE_DIR, source_dir)
+            .env(ZR_TARGET_DIR, target_dir)
+            .env(ZR_REQUEST_DD, request.parent().unwrap())
+            .env(ZR_REQUEST, request)
+            .env(ZR_TARGET_DD, target.parent().unwrap())
+            .env(ZR_TARGET, target)
+            .env(ZR_CACHE_DIR, cache.join(cache_dir));
+        visit_about_vars(about, |key, value| {
+            cmd.env(key, value);
+        });
+        self.run_cmd(&mut cmd)
     }
 
     fn cmd(&self) -> std::process::Command {
diff --git a/crates/zng-env/src/lib.rs b/crates/zng-env/src/lib.rs
index 7b7dade27..7d0c04dca 100644
--- a/crates/zng-env/src/lib.rs
+++ b/crates/zng-env/src/lib.rs
@@ -124,7 +124,7 @@ impl About {
         let mut about = About {
             crate_name: m.package.name.replace('-', "_").into(),
             pkg_name: m.package.name,
-            pkg_authors: m.package.authors,
+            pkg_authors: m.package.authors.unwrap_or_default(),
             version: m.package.version,
             description: m.package.description.unwrap_or_default(),
             homepage: m.package.homepage.unwrap_or_default(),
@@ -191,7 +191,7 @@ struct Package {
     version: Version,
     description: Option<Txt>,
     homepage: Option<Txt>,
-    authors: Box<[Txt]>,
+    authors: Option<Box<[Txt]>>,
     metadata: Option<Metadata>,
 }
 #[derive(serde::Deserialize)]


Test patch: