1 use std::path::PathBuf; 2 3 use anyhow::Result; 4 use serde::Deserialize; 5 6 use crate::common::target_arch::TargetArch; 7 8 use std::fs; 9 use toml; 10 11 /// The main configuration file for DADK 12 #[derive(Debug, Clone, Deserialize)] 13 pub struct DadkManifestFile { 14 pub metadata: Metadata, 15 16 /// A flag variable used to indicate whether 17 /// the default value function was called during deserialization. 18 #[serde(skip)] 19 pub used_default: bool, 20 } 21 22 impl DadkManifestFile { 23 pub fn load(path: &PathBuf) -> Result<Self> { 24 // 读取文件内容 25 let content = fs::read_to_string(path)?; 26 Self::load_from_str(&content) 27 } 28 29 pub fn load_from_str(content: &str) -> Result<Self> { 30 // Parse TOML content 31 let mut manifest_toml: DadkManifestFile = toml::from_str(content)?; 32 33 manifest_toml.used_default = check_used_default(); 34 35 Ok(manifest_toml) 36 } 37 } 38 39 thread_local! { 40 /// Global variable to track if default values were used during deserialization. 41 static USED_DEFAULT: std::cell::Cell<bool> = const { std::cell::Cell::new(false) }; 42 } 43 44 /// Call this function to set a flag when 45 /// default values are used during DADK manifest parsing 46 fn set_used_default() { 47 USED_DEFAULT.with(|used_default| { 48 used_default.set(true); 49 }); 50 } 51 52 /// Check if default values were used during deserialization. 53 fn check_used_default() -> bool { 54 USED_DEFAULT.with(|used_default| used_default.get()) 55 } 56 57 #[derive(Debug, Clone, Deserialize)] 58 pub struct Metadata { 59 /// Target processor architecture 60 pub arch: TargetArch, 61 /// Rootfs configuration file path 62 #[serde(default = "default_rootfs_config_path")] 63 pub rootfs_config: PathBuf, 64 65 /// Hypervisor configuration file path 66 #[serde(default = "default_hypervisor_config_path")] 67 pub hypervisor_config: PathBuf, 68 69 /// Boot configuration file path 70 #[serde(default = "default_boot_config_path")] 71 pub boot_config: PathBuf, 72 73 /// Sysroot directory path 74 #[serde(default = "default_sysroot_dir")] 75 pub sysroot_dir: PathBuf, 76 } 77 78 /// Returns the default path for the rootfs configuration file. 79 fn default_rootfs_config_path() -> PathBuf { 80 set_used_default(); 81 "config/rootfs.toml".into() 82 } 83 84 /// Returns the default path for the hypervisor configuration file. 85 fn default_hypervisor_config_path() -> PathBuf { 86 set_used_default(); 87 "config/hypervisor.toml".into() 88 } 89 90 /// Returns the default path for the boot configuration file. 91 fn default_boot_config_path() -> PathBuf { 92 set_used_default(); 93 "config/boot.toml".into() 94 } 95 96 /// Returns the default path for the sysroot directory. 97 fn default_sysroot_dir() -> PathBuf { 98 set_used_default(); 99 "bin/sysroot".into() 100 } 101 102 #[cfg(test)] 103 mod tests { 104 use super::*; 105 use std::io::Write; 106 use tempfile::NamedTempFile; 107 108 /// Test loading a complete configuration file 109 #[test] 110 fn test_full_load_success() -> Result<()> { 111 let toml_content = r#" 112 [metadata] 113 arch = "x86_64" 114 rootfs_config = "config/rootfs-x86_64.toml" 115 hypervisor_config = "config/hypervisor-x86_64.toml" 116 boot_config = "config/boot-x86_64.toml" 117 sysroot_dir = "bin/sysroot" 118 "#; 119 120 let mut temp_file = NamedTempFile::new()?; 121 temp_file.write_all(toml_content.as_bytes())?; 122 123 let path = temp_file.path().to_path_buf(); 124 let manifest = DadkManifestFile::load(&path)?; 125 126 assert_eq!(manifest.metadata.arch, TargetArch::X86_64); 127 assert_eq!( 128 manifest.metadata.rootfs_config, 129 PathBuf::from("config/rootfs-x86_64.toml") 130 ); 131 assert_eq!( 132 manifest.metadata.hypervisor_config, 133 PathBuf::from("config/hypervisor-x86_64.toml") 134 ); 135 assert_eq!( 136 manifest.metadata.boot_config, 137 PathBuf::from("config/boot-x86_64.toml") 138 ); 139 assert_eq!(manifest.metadata.sysroot_dir, PathBuf::from("bin/sysroot")); 140 assert!(!manifest.used_default); 141 142 Ok(()) 143 } 144 145 /// Test whether an error is reported when the file does not exist. 146 #[test] 147 fn test_load_file_not_found() { 148 let path = PathBuf::from("non_existent_file.toml"); 149 let result = DadkManifestFile::load(&path); 150 151 assert!(result.is_err()); 152 } 153 154 /// Test whether an error is reported when the TOML content is invalid 155 #[test] 156 fn test_load_invalid_toml() -> Result<()> { 157 let invalid_toml_content = r#" 158 [metadata 159 arch = "x86_64" 160 "#; 161 162 let mut temp_file = NamedTempFile::new()?; 163 temp_file.write_all(invalid_toml_content.as_bytes())?; 164 165 let path = temp_file.path().to_path_buf(); 166 let result = DadkManifestFile::load(&path); 167 168 assert!(result.is_err()); 169 170 Ok(()) 171 } 172 173 /// Test whether an error is reported when the arch field is invalid 174 #[test] 175 fn test_load_invalid_arch_toml() -> Result<()> { 176 // Invalid arch value 177 let invalid_toml_content = r#" 178 [metadata] 179 arch = "abcde" 180 "#; 181 182 let mut temp_file = NamedTempFile::new()?; 183 temp_file.write_all(invalid_toml_content.as_bytes())?; 184 185 let path = temp_file.path().to_path_buf(); 186 let result = DadkManifestFile::load(&path); 187 188 assert!(result.is_err()); 189 190 Ok(()) 191 } 192 193 /// Test whether an error is reported when a required field is missing 194 #[test] 195 fn test_load_missing_required_fields() -> Result<()> { 196 let toml_content = r#" 197 [metadata] 198 # arch field is missing 199 "#; 200 201 let mut temp_file = NamedTempFile::new()?; 202 temp_file.write_all(toml_content.as_bytes())?; 203 204 let path = temp_file.path().to_path_buf(); 205 let result = DadkManifestFile::load(&path); 206 207 assert!(result.is_err()); 208 209 Ok(()) 210 } 211 212 /// Test whether default values are used 213 /// when the rootfs_config and other configuration file path fields are not set 214 #[test] 215 fn test_load_default_config_path_value() -> Result<()> { 216 let toml_content = r#" 217 [metadata] 218 arch = "x86_64" 219 "#; 220 let mut temp_file = NamedTempFile::new()?; 221 temp_file.write_all(toml_content.as_bytes())?; 222 let path = temp_file.path().to_path_buf(); 223 let manifest = DadkManifestFile::load(&path)?; 224 assert_eq!(manifest.used_default, true); 225 assert_eq!( 226 manifest.metadata.rootfs_config, 227 PathBuf::from("config/rootfs.toml") 228 ); 229 assert_eq!( 230 manifest.metadata.hypervisor_config, 231 PathBuf::from("config/hypervisor.toml") 232 ); 233 assert_eq!( 234 manifest.metadata.boot_config, 235 PathBuf::from("config/boot.toml") 236 ); 237 Ok(()) 238 } 239 } 240