Move logic for detecting different kinds of externs to cargo_out.

This will work differently when not running cargo.

Bug: 293289578
Test: atest cargo_embargo.test
Change-Id: I6c7b1f6266d7d66bcbb68387d06f59306695008e
diff --git a/tools/cargo_embargo/src/cargo.rs b/tools/cargo_embargo/src/cargo.rs
index 23e6447..9a9949c 100644
--- a/tools/cargo_embargo/src/cargo.rs
+++ b/tools/cargo_embargo/src/cargo.rs
@@ -72,11 +72,11 @@
     pub package_name: String,
     pub version: Option<String>,
     pub types: Vec<CrateType>,
-    pub target: Option<String>,                 // --target
-    pub features: Vec<String>,                  // --cfg feature=
-    pub cfgs: Vec<String>,                      // non-feature --cfg
-    pub externs: Vec<(String, Option<String>)>, // name => rlib file
-    pub codegens: Vec<String>,                  // -C
+    pub target: Option<String>, // --target
+    pub features: Vec<String>,  // --cfg feature=
+    pub cfgs: Vec<String>,      // non-feature --cfg
+    pub externs: Vec<Extern>,
+    pub codegens: Vec<String>, // -C
     pub cap_lints: String,
     pub static_libs: Vec<String>,
     pub shared_libs: Vec<String>,
@@ -84,3 +84,17 @@
     pub package_dir: PathBuf, // canonicalized
     pub main_src: PathBuf,    // relative to package_dir
 }
+
+/// A dependency of a Rust crate.
+#[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
+pub struct Extern {
+    pub name: String,
+    pub lib_name: String,
+    pub extern_type: ExternType,
+}
+
+#[derive(Debug, Deserialize, Eq, PartialEq, Serialize)]
+pub enum ExternType {
+    Rust,
+    ProcMacro,
+}
diff --git a/tools/cargo_embargo/src/cargo/cargo_out.rs b/tools/cargo_embargo/src/cargo/cargo_out.rs
index 74acf5c..f04c013 100644
--- a/tools/cargo_embargo/src/cargo/cargo_out.rs
+++ b/tools/cargo_embargo/src/cargo/cargo_out.rs
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 use super::metadata::WorkspaceMetadata;
-use super::{Crate, CrateType};
+use super::{Crate, CrateType, Extern, ExternType};
 use anyhow::anyhow;
 use anyhow::bail;
 use anyhow::Context;
@@ -236,12 +236,32 @@
                     // example: memoffset=/some/path/libmemoffset-2cfda327d156e680.rmeta
                     let arg = arg_iter.next().unwrap();
                     if let Some((name, path)) = arg.split_once('=') {
-                        out.externs.push((
-                            name.to_string(),
-                            Some(path.split('/').last().unwrap().to_string()),
-                        ));
-                    } else {
-                        out.externs.push((arg.to_string(), None));
+                        let filename = path.split('/').last().unwrap();
+
+                        // Example filename: "libgetrandom-fd8800939535fc59.rmeta"
+                        static REGEX: Lazy<Regex> = Lazy::new(|| {
+                            Regex::new(r"^lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$").unwrap()
+                        });
+
+                        let Some(lib_name) = REGEX.captures(filename).and_then(|x| x.get(1)) else {
+                            bail!("bad filename for extern {}: {}", name, filename);
+                        };
+                        let extern_type =
+                            if filename.ends_with(".rlib") || filename.ends_with(".rmeta") {
+                                ExternType::Rust
+                            } else if filename.ends_with(".so") {
+                                // Assume .so files are always proc_macros. May not always be right.
+                                ExternType::ProcMacro
+                            } else {
+                                bail!("Unexpected extension for extern filename {}", filename);
+                            };
+                        out.externs.push(Extern {
+                            name: name.to_string(),
+                            lib_name: lib_name.as_str().to_string(),
+                            extern_type,
+                        });
+                    } else if arg != "proc_macro" {
+                        panic!("No filename for {}", arg);
                     }
                 }
                 _ if arg.starts_with("-C") => {
diff --git a/tools/cargo_embargo/src/main.rs b/tools/cargo_embargo/src/main.rs
index cbcac39..84638ca 100644
--- a/tools/cargo_embargo/src/main.rs
+++ b/tools/cargo_embargo/src/main.rs
@@ -36,10 +36,8 @@
 use anyhow::Context;
 use anyhow::Result;
 use bp::*;
-use cargo::{cargo_out::parse_cargo_out, Crate, CrateType};
+use cargo::{cargo_out::parse_cargo_out, Crate, CrateType, ExternType};
 use clap::Parser;
-use once_cell::sync::Lazy;
-use regex::Regex;
 use std::collections::BTreeMap;
 use std::collections::VecDeque;
 use std::fs::File;
@@ -505,27 +503,10 @@
 
         let mut rust_libs = Vec::new();
         let mut proc_macro_libs = Vec::new();
-        for (extern_name, filename) in &crate_.externs {
-            if extern_name == "proc_macro" {
-                continue;
-            }
-            let filename =
-                filename.as_ref().unwrap_or_else(|| panic!("no filename for {}", extern_name));
-            // Example filename: "libgetrandom-fd8800939535fc59.rmeta"
-            static REGEX: Lazy<Regex> =
-                Lazy::new(|| Regex::new(r"^lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$").unwrap());
-            let lib_name = if let Some(x) = REGEX.captures(filename).and_then(|x| x.get(1)) {
-                x
-            } else {
-                bail!("bad filename for extern {}: {}", extern_name, filename);
-            };
-            if filename.ends_with(".rlib") || filename.ends_with(".rmeta") {
-                rust_libs.push(lib_name.as_str().to_string());
-            } else if filename.ends_with(".so") {
-                // Assume .so files are always proc_macros. May not always be right.
-                proc_macro_libs.push(lib_name.as_str().to_string());
-            } else {
-                unreachable!();
+        for extern_dep in &crate_.externs {
+            match extern_dep.extern_type {
+                ExternType::Rust => rust_libs.push(extern_dep.lib_name.clone()),
+                ExternType::ProcMacro => proc_macro_libs.push(extern_dep.lib_name.clone()),
             }
         }
 
diff --git a/tools/cargo_embargo/testdata/either/crates.json b/tools/cargo_embargo/testdata/either/crates.json
index aa38632..27f5977 100644
--- a/tools/cargo_embargo/testdata/either/crates.json
+++ b/tools/cargo_embargo/testdata/either/crates.json
@@ -24,7 +24,9 @@
     "target": "x86_64-unknown-linux-gnu",
     "features": ["default", "use_std"],
     "cfgs": [],
-    "externs": [["serde_json", "libserde_json-a330ac0e36ca324c.rlib"]],
+    "externs": [
+      { "name": "serde_json", "lib_name": "serde_json", "extern_type": "Rust" }
+    ],
     "codegens": [],
     "cap_lints": "",
     "static_libs": [],
diff --git a/tools/cargo_embargo/testdata/plotters/crates.json b/tools/cargo_embargo/testdata/plotters/crates.json
index 04851c9..b65bbf9 100644
--- a/tools/cargo_embargo/testdata/plotters/crates.json
+++ b/tools/cargo_embargo/testdata/plotters/crates.json
@@ -8,9 +8,17 @@
     "features": ["area_series", "line_series", "plotters-svg", "svg_backend"],
     "cfgs": [],
     "externs": [
-      ["num_traits", "libnum_traits-93a69c224ed38166.rmeta"],
-      ["plotters_backend", "libplotters_backend-4110c43be89cf520.rmeta"],
-      ["plotters_svg", "libplotters_svg-e0f639cce7c4701c.rmeta"]
+      { "name": "num_traits", "lib_name": "num_traits", "extern_type": "Rust" },
+      {
+        "name": "plotters_backend",
+        "lib_name": "plotters_backend",
+        "extern_type": "Rust"
+      },
+      {
+        "name": "plotters_svg",
+        "lib_name": "plotters_svg",
+        "extern_type": "Rust"
+      }
     ],
     "codegens": [],
     "cap_lints": "",