1 // Copyright (C) 2023 The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 use anyhow::{anyhow, Result}; 16 use cargo::{ 17 core::{Manifest, SourceId}, 18 util::toml::read_manifest, 19 Config, 20 }; 21 use name_and_version::{NameAndVersionRef, NamedAndVersioned}; 22 use rooted_path::RootedPath; 23 use semver::Version; 24 25 use crate::CrateError; 26 27 #[derive(Debug, Clone)] 28 pub struct Crate { 29 manifest: Manifest, 30 path: RootedPath, 31 } 32 33 impl NamedAndVersioned for Crate { name(&self) -> &str34 fn name(&self) -> &str { 35 self.manifest.name().as_str() 36 } version(&self) -> &Version37 fn version(&self) -> &Version { 38 self.manifest.version() 39 } key(&self) -> NameAndVersionRef40 fn key(&self) -> NameAndVersionRef { 41 NameAndVersionRef::new(self.name(), self.version()) 42 } 43 } 44 45 impl Crate { new(manifest: Manifest, path: RootedPath) -> Crate46 pub fn new(manifest: Manifest, path: RootedPath) -> Crate { 47 Crate { manifest, path } 48 } from(manifest_dir: RootedPath) -> Result<Crate>49 pub fn from(manifest_dir: RootedPath) -> Result<Crate> { 50 let source_id = SourceId::for_path(manifest_dir.abs())?; 51 let (manifest, _nested) = read_manifest( 52 manifest_dir.join("Cargo.toml").unwrap().as_ref(), 53 source_id, 54 &Config::default()?, 55 )?; 56 match manifest { 57 cargo::core::EitherManifest::Real(r) => Ok(Crate::new(r, manifest_dir)), 58 cargo::core::EitherManifest::Virtual(_) => { 59 Err(anyhow!(CrateError::VirtualCrate(manifest_dir.as_ref().to_path_buf()))) 60 } 61 } 62 } 63 description(&self) -> &str64 pub fn description(&self) -> &str { 65 self.manifest.metadata().description.as_deref().unwrap_or("") 66 } license(&self) -> Option<&str>67 pub fn license(&self) -> Option<&str> { 68 self.manifest.metadata().license.as_deref() 69 } repository(&self) -> Option<&str>70 pub fn repository(&self) -> Option<&str> { 71 self.manifest.metadata().repository.as_deref() 72 } path(&self) -> &RootedPath73 pub fn path(&self) -> &RootedPath { 74 &self.path 75 } 76 is_migration_denied(&self) -> bool77 pub fn is_migration_denied(&self) -> bool { 78 const MIGRATION_DENYLIST: &[&str] = &[ 79 "external/rust/crates/openssl/", // It's complicated. 80 "external/rust/cxx/", // It's REALLY complicated. 81 ]; 82 MIGRATION_DENYLIST.iter().any(|prefix| self.path().rel().starts_with(prefix)) 83 } 84 } 85 86 #[cfg(test)] 87 mod tests { 88 use std::{ 89 fs::{create_dir, write}, 90 path::Path, 91 }; 92 93 use super::*; 94 use anyhow::anyhow; 95 use tempfile::tempdir; 96 write_test_manifest(temp_crate_dir: &Path, name: &str, version: &str) -> Result<RootedPath>97 fn write_test_manifest(temp_crate_dir: &Path, name: &str, version: &str) -> Result<RootedPath> { 98 let temp_crate_dir = RootedPath::new("/", temp_crate_dir.strip_prefix("/")?)?; 99 write( 100 temp_crate_dir.join("Cargo.toml")?, 101 format!("[package]\nname = \"{}\"\nversion = \"{}\"\n", name, version), 102 )?; 103 let lib_rs = temp_crate_dir.join("src/lib.rs")?; 104 create_dir(lib_rs.abs().parent().ok_or(anyhow!("Failed to get parent"))?)?; 105 write(lib_rs, "// foo")?; 106 Ok(temp_crate_dir) 107 } 108 109 #[test] test_from_and_properties() -> Result<()>110 fn test_from_and_properties() -> Result<()> { 111 let temp_crate_dir = tempdir()?; 112 let manifest_dir = write_test_manifest(temp_crate_dir.path(), "foo", "1.2.0")?; 113 let krate = Crate::from(manifest_dir)?; 114 assert_eq!(krate.name(), "foo"); 115 assert_eq!(krate.version().to_string(), "1.2.0"); 116 Ok(()) 117 } 118 119 #[test] test_from_error() -> Result<()>120 fn test_from_error() -> Result<()> { 121 let temp_crate_dir = tempdir()?; 122 let manifest_dir = write_test_manifest(temp_crate_dir.path(), "foo", "1.2.0")?; 123 assert!(Crate::from(manifest_dir.join("blah")?).is_err()); 124 Ok(()) 125 } 126 } 127