1 // Copyright (C) 2025 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 //! TOML file to store per-repo configuration. 16 17 use std::{collections::BTreeSet, fs::read_to_string, io, path::Path}; 18 19 use serde::Deserialize; 20 21 /// A parsed android_repo_config.toml file 22 #[derive(Deserialize, Debug, Default, Clone)] 23 pub struct RepoConfig { 24 #[serde(default, skip_serializing_if = "Vec::is_empty")] 25 crate_denylist: BTreeSet<String>, 26 } 27 28 /// Error types for the 'repo_config' crate. 29 #[derive(thiserror::Error, Debug)] 30 pub enum Error { 31 /// Error parsing TOML. 32 #[error(transparent)] 33 TomlParseError(#[from] toml::de::Error), 34 /// Error reading TOML file. 35 #[error(transparent)] 36 IoError(#[from] io::Error), 37 } 38 39 /// The repo config file name. 40 pub static CONFIG_FILE_NAME: &str = "android_repo_config.toml"; 41 42 impl RepoConfig { 43 /// Read the android_repo_config.toml file in the specified directory. If not present, 44 /// a default version is returned. read(managed_repo_dir: impl AsRef<Path>) -> Result<RepoConfig, Error>45 pub fn read(managed_repo_dir: impl AsRef<Path>) -> Result<RepoConfig, Error> { 46 let config_file = managed_repo_dir.as_ref().join(CONFIG_FILE_NAME); 47 if !config_file.exists() { 48 return Ok(RepoConfig::default()); 49 } 50 let toml: RepoConfig = toml::from_str(read_to_string(config_file)?.as_str())?; 51 Ok(toml) 52 } 53 /// Returns true if the crate is on the crate denylist. is_allowed(&self, crate_name: impl AsRef<str>) -> bool54 pub fn is_allowed(&self, crate_name: impl AsRef<str>) -> bool { 55 !self.crate_denylist.contains(crate_name.as_ref()) 56 } 57 } 58 59 #[cfg(test)] 60 mod tests { 61 use std::fs::write; 62 63 use super::*; 64 65 #[test] basic()66 fn basic() { 67 let dir = tempfile::tempdir().expect("Failed to create tempdir"); 68 write(dir.path().join(CONFIG_FILE_NAME), r#"crate_denylist = ["foo"]"#) 69 .expect("Failed to write to tempdir"); 70 let config = RepoConfig::read(dir.path()).expect("Failed to read config file"); 71 assert!(!config.is_allowed("foo")); 72 assert!(config.is_allowed("bar")); 73 } 74 75 #[test] default()76 fn default() { 77 let dir = tempfile::tempdir().expect("Failed to create tempdir"); 78 let config = RepoConfig::read(dir.path()).expect("Failed to get default config"); 79 assert!(config.is_allowed("foo")); 80 } 81 82 #[test] parse_error()83 fn parse_error() { 84 let dir = tempfile::tempdir().expect("Failed to create tempdir"); 85 write(dir.path().join(CONFIG_FILE_NAME), r#"blah"#).expect("Failed to write to tempdir"); 86 assert!(matches!(RepoConfig::read(dir.path()), Err(Error::TomlParseError(_)))); 87 } 88 } 89