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-crate configuration. 16 17 use std::{fs::read_to_string, io, path::Path}; 18 19 use serde::Deserialize; 20 21 /// A parsed android_config.toml file 22 #[derive(Deserialize, Debug, Default, Clone)] 23 pub struct CrateConfig { 24 #[serde(default, skip_serializing_if = "Vec::is_empty")] 25 deletions: Vec<String>, 26 27 #[serde(default, skip_serializing_if = "Vec::is_empty")] 28 update_with: Vec<String>, 29 } 30 31 /// Error types for the 'crate_config' crate. 32 #[derive(thiserror::Error, Debug)] 33 pub enum Error { 34 /// Error parsing TOML. 35 #[error(transparent)] 36 TomlParseError(#[from] toml::de::Error), 37 /// Error reading TOML file. 38 #[error(transparent)] 39 IoError(#[from] io::Error), 40 } 41 42 /// The crate config file name. 43 pub static CONFIG_FILE_NAME: &str = "android_config.toml"; 44 45 impl CrateConfig { 46 /// Read the android_config.toml file in the specified directory. If not present, 47 /// a default version is returned. read(crate_dir: impl AsRef<Path>) -> Result<CrateConfig, Error>48 pub fn read(crate_dir: impl AsRef<Path>) -> Result<CrateConfig, Error> { 49 let config_file = crate_dir.as_ref().join(CONFIG_FILE_NAME); 50 if !config_file.exists() { 51 return Ok(CrateConfig::default()); 52 } 53 let toml: CrateConfig = toml::from_str(read_to_string(config_file)?.as_str())?; 54 Ok(toml) 55 } 56 /// Get an iterator over directories and files to delete. deletions(&self) -> impl Iterator<Item = &str>57 pub fn deletions(&self) -> impl Iterator<Item = &str> { 58 self.deletions.iter().map(|d| d.as_str()) 59 } 60 /// Get an iterator of crates that also need to be updated at the same time as this crate. update_with(&self) -> impl Iterator<Item = &str>61 pub fn update_with(&self) -> impl Iterator<Item = &str> { 62 self.update_with.iter().map(|d| d.as_str()) 63 } 64 } 65 66 #[cfg(test)] 67 mod tests { 68 use std::fs::write; 69 70 use super::*; 71 72 #[test] basic()73 fn basic() { 74 let dir = tempfile::tempdir().expect("Failed to create tempdir"); 75 write(dir.path().join(CONFIG_FILE_NAME), r#"deletions = ["foo"]"#) 76 .expect("Failed to write to tempdir"); 77 let config = CrateConfig::read(dir.path()).expect("Failed to read config file"); 78 assert_eq!(config.deletions, ["foo"]); 79 } 80 81 #[test] default()82 fn default() { 83 let dir = tempfile::tempdir().expect("Failed to create tempdir"); 84 let config = CrateConfig::read(dir.path()).expect("Failed to get default config"); 85 assert!(config.deletions.is_empty()); 86 } 87 88 #[test] parse_error()89 fn parse_error() { 90 let dir = tempfile::tempdir().expect("Failed to create tempdir"); 91 write(dir.path().join(CONFIG_FILE_NAME), r#"blah"#).expect("Failed to write to tempdir"); 92 assert!(matches!(CrateConfig::read(dir.path()), Err(Error::TomlParseError(_)))); 93 } 94 } 95