Move GoogleMetadata to a separate crate.
Bug: 339424309
Test: treehugger
Change-Id: Iad394080afc7ad7885b2104acc26692f9cc0dbee
diff --git a/tools/external_crates/Cargo.toml b/tools/external_crates/Cargo.toml
index ab31806..cfcb6d7 100644
--- a/tools/external_crates/Cargo.toml
+++ b/tools/external_crates/Cargo.toml
@@ -1,6 +1,7 @@
[workspace]
members = [
"crate_health",
+ "google_metadata",
"license_checker",
"name_and_version",
"name_and_version_proc_macros",
diff --git a/tools/external_crates/crate_health/Cargo.toml b/tools/external_crates/crate_health/Cargo.toml
index 2894dc9..fb955e8 100644
--- a/tools/external_crates/crate_health/Cargo.toml
+++ b/tools/external_crates/crate_health/Cargo.toml
@@ -25,9 +25,10 @@
tinytemplate = "1.2"
walkdir = "2"
whoami = "1"
+google_metadata = { path = "../google_metadata"}
+license_checker = { path = "../license_checker" }
name_and_version = { path = "../name_and_version" }
name_and_version_proc_macros = { path = "../name_and_version_proc_macros" }
-license_checker = { path = "../license_checker" }
rooted_path = { path = "../rooted_path" }
[build-dependencies]
diff --git a/tools/external_crates/crate_health/src/google_metadata.rs b/tools/external_crates/crate_health/src/google_metadata.rs
deleted file mode 100644
index 17b1a7a..0000000
--- a/tools/external_crates/crate_health/src/google_metadata.rs
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright (C) 2024 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use std::{
- fs::{read_to_string, write},
- path::PathBuf,
-};
-
-use anyhow::{anyhow, Result};
-use chrono::{DateTime, Datelike, Local};
-use license_checker::LicenseState;
-use name_and_version::NamedAndVersioned;
-
-use crate::{
- license::most_restrictive_type,
- metadata::{self, Identifier, MetaData},
-};
-
-pub struct GoogleMetadata {
- path: PathBuf,
- metadata: MetaData,
-}
-
-impl GoogleMetadata {
- pub fn try_from<P: Into<PathBuf>>(path: P) -> Result<Self> {
- let path = path.into();
- let metadata = read_to_string(&path)?;
- let metadata: metadata::MetaData = protobuf::text_format::parse_from_str(&metadata)?;
- Ok(GoogleMetadata { path, metadata })
- }
- pub fn init<P: Into<PathBuf>>(
- path: P,
- nv: &dyn NamedAndVersioned,
- desc: &str,
- licenses: &LicenseState,
- ) -> Result<Self> {
- let path = path.into();
- if path.exists() {
- return Err(anyhow!("{} already exists", path.display()));
- }
- let mut metadata = GoogleMetadata { path, metadata: MetaData::new() };
- metadata.metadata.set_name(nv.name().to_string());
- metadata.metadata.set_description(desc.to_string());
- metadata.set_date_to_today()?;
- metadata.set_identifier(nv)?;
- let third_party = metadata.metadata.third_party.mut_or_insert_default();
- third_party.set_homepage(nv.crates_io_homepage());
- third_party.set_license_type(most_restrictive_type(licenses));
- Ok(metadata)
- }
- pub fn write(&self) -> Result<()> {
- Ok(write(&self.path, protobuf::text_format::print_to_string_pretty(&self.metadata))?)
- }
- pub fn set_date_to_today(&mut self) -> Result<()> {
- let now: DateTime<Local> = Local::now();
- let date = self
- .metadata
- .third_party
- .mut_or_insert_default()
- .last_upgrade_date
- .mut_or_insert_default();
- date.set_day(now.day().try_into()?);
- date.set_month(now.month().try_into()?);
- date.set_year(now.year());
- Ok(())
- }
- pub fn set_identifier(&mut self, nv: &dyn NamedAndVersioned) -> Result<()> {
- if self.metadata.name.as_ref().ok_or(anyhow!("No name"))? != nv.name() {
- return Err(anyhow!("Names don't match"));
- }
- let mut identifier = Identifier::new();
- identifier.set_type("Archive".to_string());
- identifier.set_value(nv.crate_archive_url());
- identifier.set_version(nv.version().to_string());
- self.metadata.third_party.mut_or_insert_default().identifier.clear();
- self.metadata.third_party.mut_or_insert_default().identifier.push(identifier);
- Ok(())
- }
- pub fn migrate_homepage(&mut self) -> bool {
- let mut homepage = None;
- for (idx, identifier) in self.metadata.third_party.identifier.iter().enumerate() {
- if identifier.type_.as_ref().unwrap_or(&String::new()).to_lowercase() == "homepage" {
- match homepage {
- Some(info) => panic!("Homepage set twice? {info:?} {identifier:?}"),
- None => homepage = Some((idx, identifier.clone())),
- }
- }
- }
- let Some(homepage) = homepage else { return false };
- self.metadata.third_party.mut_or_insert_default().identifier.remove(homepage.0);
- self.metadata.third_party.mut_or_insert_default().homepage = homepage.1.value;
- true
- }
- pub fn migrate_archive(&mut self) -> bool {
- let mut updated = false;
- for identifier in self.metadata.third_party.mut_or_insert_default().identifier.iter_mut() {
- if identifier.type_ == Some("ARCHIVE".to_string()) {
- identifier.type_ = Some("Archive".to_string());
- updated = true;
- }
- }
- updated
- }
-}
diff --git a/tools/external_crates/crate_health/src/lib.rs b/tools/external_crates/crate_health/src/lib.rs
index cae566a..e0bba78 100644
--- a/tools/external_crates/crate_health/src/lib.rs
+++ b/tools/external_crates/crate_health/src/lib.rs
@@ -41,9 +41,6 @@
};
mod android_bp;
-pub use self::google_metadata::GoogleMetadata;
-mod google_metadata;
-
pub use self::license::{most_restrictive_type, update_module_license_files};
mod license;
@@ -111,5 +108,3 @@
.run_quiet_and_expect_success()?;
Ok(())
}
-
-include!(concat!(env!("OUT_DIR"), "/protos/mod.rs"));
diff --git a/tools/external_crates/crate_health/src/license.rs b/tools/external_crates/crate_health/src/license.rs
index 6ec75d6..32a81b7 100644
--- a/tools/external_crates/crate_health/src/license.rs
+++ b/tools/external_crates/crate_health/src/license.rs
@@ -18,9 +18,9 @@
path::Path,
};
-use crate::metadata::LicenseType;
use anyhow::{anyhow, Result};
use glob::glob;
+use google_metadata::metadata::LicenseType;
use lazy_static::lazy_static;
use license_checker::LicenseState;
use spdx::{LicenseReq, Licensee};
diff --git a/tools/external_crates/crate_health/src/managed_repo.rs b/tools/external_crates/crate_health/src/managed_repo.rs
index c117429..f809a47 100644
--- a/tools/external_crates/crate_health/src/managed_repo.rs
+++ b/tools/external_crates/crate_health/src/managed_repo.rs
@@ -23,6 +23,7 @@
use anyhow::{anyhow, Result};
use glob::glob;
+use google_metadata::GoogleMetadata;
use itertools::Itertools;
use license_checker::find_licenses;
use name_and_version::{NameAndVersion, NameAndVersionMap, NameAndVersionRef, NamedAndVersioned};
@@ -31,8 +32,8 @@
use spdx::Licensee;
use crate::{
- cargo_embargo_autoconfig, copy_dir, update_module_license_files, Crate, CrateCollection,
- GoogleMetadata, Migratable, PseudoCrate, VersionMatch,
+ cargo_embargo_autoconfig, copy_dir, most_restrictive_type, update_module_license_files, Crate,
+ CrateCollection, Migratable, PseudoCrate, VersionMatch,
};
pub struct ManagedRepo {
@@ -421,10 +422,11 @@
update_module_license_files(&krate.path().abs(), &licenses)?;
let metadata = GoogleMetadata::init(
- krate.path().abs().join("METADATA"),
- &krate,
+ krate.path().join("METADATA")?,
+ krate.name(),
+ krate.version().to_string(),
krate.description(),
- &licenses,
+ most_restrictive_type(&licenses),
)?;
metadata.write()?;
diff --git a/tools/external_crates/crate_health/src/version_match.rs b/tools/external_crates/crate_health/src/version_match.rs
index a1985e6..37dc342 100644
--- a/tools/external_crates/crate_health/src/version_match.rs
+++ b/tools/external_crates/crate_health/src/version_match.rs
@@ -15,9 +15,10 @@
use std::{collections::BTreeMap, path::Path};
use anyhow::{anyhow, Result};
+use google_metadata::GoogleMetadata;
use name_and_version::{NameAndVersion, NameAndVersionMap, NamedAndVersioned};
-use crate::{generate_android_bps, CrateCollection, GoogleMetadata, Migratable};
+use crate::{generate_android_bps, CrateCollection, Migratable};
#[derive(Debug)]
pub struct VersionPair<'a, T> {
@@ -203,7 +204,7 @@
writeback |= metadata.migrate_archive();
if pair.source.version() != pair.dest.version() {
metadata.set_date_to_today()?;
- metadata.set_identifier(pair.dest)?;
+ metadata.set_identifier(pair.dest.name(), pair.dest.version().to_string())?;
writeback |= true;
}
if writeback {
diff --git a/tools/external_crates/google_metadata/.gitignore b/tools/external_crates/google_metadata/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/tools/external_crates/google_metadata/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/tools/external_crates/google_metadata/Cargo.toml b/tools/external_crates/google_metadata/Cargo.toml
new file mode 100644
index 0000000..5130c10
--- /dev/null
+++ b/tools/external_crates/google_metadata/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "google_metadata"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+chrono = "0.4"
+protobuf = "3"
+thiserror = "1.0"
+
+[build-dependencies]
+protobuf-codegen = "3"
+protobuf-parse = "3"
\ No newline at end of file
diff --git a/tools/external_crates/crate_health/build.rs b/tools/external_crates/google_metadata/build.rs
similarity index 100%
rename from tools/external_crates/crate_health/build.rs
rename to tools/external_crates/google_metadata/build.rs
diff --git a/tools/external_crates/google_metadata/src/lib.rs b/tools/external_crates/google_metadata/src/lib.rs
new file mode 100644
index 0000000..fba892b
--- /dev/null
+++ b/tools/external_crates/google_metadata/src/lib.rs
@@ -0,0 +1,159 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use std::{
+ fs::{read_to_string, write},
+ path::PathBuf,
+};
+
+use chrono::{DateTime, Datelike, Local};
+use protobuf::text_format::ParseError;
+
+include!(concat!(env!("OUT_DIR"), "/protos/mod.rs"));
+use crate::metadata::{Identifier, LicenseType, MetaData};
+
+#[derive(thiserror::Error, Debug)]
+pub enum Error {
+ #[error("File exists: {}", .0.display())]
+ FileExists(PathBuf),
+ #[error("Crate name not set")]
+ CrateNameMissing(),
+ #[error("Crate names don't match: {} in METADATA vs {}", .0, .1)]
+ CrateNameMismatch(String, String),
+ #[error("Glob pattern error")]
+ ParseError(#[from] ParseError),
+ #[error("Write error")]
+ WriteError(#[from] std::io::Error),
+}
+
+pub struct GoogleMetadata {
+ path: PathBuf,
+ metadata: MetaData,
+}
+
+impl GoogleMetadata {
+ /// Reads an existing METADATA file.
+ pub fn try_from<P: Into<PathBuf>>(path: P) -> Result<Self, Error> {
+ let path = path.into();
+ let metadata = read_to_string(&path)?;
+ let metadata: metadata::MetaData = protobuf::text_format::parse_from_str(&metadata)?;
+ Ok(GoogleMetadata { path, metadata })
+ }
+ /// Initializes a new METADATA file.
+ pub fn init<P: Into<PathBuf>, Q: Into<String>, R: Into<String>, S: Into<String>>(
+ path: P,
+ name: Q,
+ version: R,
+ desc: S,
+ license_type: LicenseType,
+ ) -> Result<Self, Error> {
+ let path = path.into();
+ if path.exists() {
+ return Err(Error::FileExists(path));
+ }
+ let mut metadata = GoogleMetadata { path, metadata: MetaData::new() };
+ let name = name.into();
+ metadata.set_date_to_today()?;
+ metadata.set_identifier(&name, version)?;
+ let third_party = metadata.metadata.third_party.mut_or_insert_default();
+ third_party.set_homepage(crates_io_homepage(&name));
+ third_party.set_license_type(license_type);
+ metadata.metadata.set_name(name);
+ metadata.metadata.set_description(desc.into());
+ Ok(metadata)
+ }
+ /// Writes to the METADATA file.
+ ///
+ /// The existing file is overwritten.
+ pub fn write(&self) -> Result<(), Error> {
+ Ok(write(&self.path, protobuf::text_format::print_to_string_pretty(&self.metadata))?)
+ }
+ /// Sets the date fields to today's date.
+ pub fn set_date_to_today(&mut self) -> Result<(), Error> {
+ let now: DateTime<Local> = Local::now();
+ let date = self
+ .metadata
+ .third_party
+ .mut_or_insert_default()
+ .last_upgrade_date
+ .mut_or_insert_default();
+ date.set_day(now.day().try_into().unwrap());
+ date.set_month(now.month().try_into().unwrap());
+ date.set_year(now.year());
+ Ok(())
+ }
+ /// Sets the identifier.
+ ///
+ /// The identifier contains the URL and version for the crate.
+ pub fn set_identifier<Q: Into<String>>(
+ &mut self,
+ name: impl AsRef<str>,
+ version: Q,
+ ) -> Result<(), Error> {
+ let name_in_metadata = self.metadata.name.as_ref().ok_or(Error::CrateNameMissing())?;
+ if name_in_metadata != name.as_ref() {
+ return Err(Error::CrateNameMismatch(
+ name_in_metadata.clone(),
+ name.as_ref().to_string(),
+ ));
+ }
+ let mut identifier = Identifier::new();
+ identifier.set_type("Archive".to_string());
+ let version = version.into();
+ identifier.set_value(crate_archive_url(name, &version));
+ identifier.set_version(version);
+ self.metadata.third_party.mut_or_insert_default().identifier.clear();
+ self.metadata.third_party.mut_or_insert_default().identifier.push(identifier);
+ Ok(())
+ }
+ /// Migrate homepage from an identifier to its own field.
+ pub fn migrate_homepage(&mut self) -> bool {
+ let mut homepage = None;
+ for (idx, identifier) in self.metadata.third_party.identifier.iter().enumerate() {
+ if identifier.type_.as_ref().unwrap_or(&String::new()).to_lowercase() == "homepage" {
+ match homepage {
+ Some(info) => panic!("Homepage set twice? {info:?} {identifier:?}"),
+ None => homepage = Some((idx, identifier.clone())),
+ }
+ }
+ }
+ let Some(homepage) = homepage else { return false };
+ self.metadata.third_party.mut_or_insert_default().identifier.remove(homepage.0);
+ self.metadata.third_party.mut_or_insert_default().homepage = homepage.1.value;
+ true
+ }
+ /// Normalize case of 'Archive' identifiers.
+ pub fn migrate_archive(&mut self) -> bool {
+ let mut updated = false;
+ for identifier in self.metadata.third_party.mut_or_insert_default().identifier.iter_mut() {
+ if identifier.type_ == Some("ARCHIVE".to_string()) {
+ identifier.type_ = Some("Archive".to_string());
+ updated = true;
+ }
+ }
+ updated
+ }
+}
+
+fn crate_archive_url(name: impl AsRef<str>, version: impl AsRef<str>) -> String {
+ format!(
+ "https://static.crates.io/crates/{}/{}-{}.crate",
+ name.as_ref(),
+ name.as_ref(),
+ version.as_ref()
+ )
+}
+fn crates_io_homepage(name: impl AsRef<str>) -> String {
+ format!("https://crates.io/crates/{}", name.as_ref())
+}
diff --git a/tools/external_crates/crate_health/src/protos/metadata.proto b/tools/external_crates/google_metadata/src/protos/metadata.proto
similarity index 100%
rename from tools/external_crates/crate_health/src/protos/metadata.proto
rename to tools/external_crates/google_metadata/src/protos/metadata.proto