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 {
load(path: &PathBuf) -> Result<Self>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
load_from_str(content: &str) -> Result<Self>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
set_used_default()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.
check_used_default() -> bool53 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", rename = "rootfs-config")]
63 pub rootfs_config: PathBuf,
64
65 /// Hypervisor configuration file path
66 #[serde(
67 default = "default_hypervisor_config_path",
68 rename = "hypervisor-config"
69 )]
70 pub hypervisor_config: PathBuf,
71
72 /// Boot configuration file path
73 #[serde(default = "default_boot_config_path", rename = "boot-config")]
74 pub boot_config: PathBuf,
75
76 /// Sysroot directory path
77 #[serde(default = "default_sysroot_dir", rename = "sysroot-dir")]
78 pub sysroot_dir: PathBuf,
79
80 /// Root Cache directory path
81 #[serde(default = "default_cache_root_dir", rename = "cache-root-dir")]
82 pub cache_root_dir: PathBuf,
83
84 /// User configuration directory path
85 /// 这个字段只是临时用于兼容旧版本,v0.2版本重构完成后会删除
86 #[deprecated(note = "This field is deprecated and will be removed in DADK 0.2")]
87 #[serde(default = "default_user_config_dir", rename = "user-config-dir")]
88 pub user_config_dir: PathBuf,
89 }
90
91 /// Returns the default path for the rootfs configuration file.
default_rootfs_config_path() -> PathBuf92 fn default_rootfs_config_path() -> PathBuf {
93 set_used_default();
94 "config/rootfs.toml".into()
95 }
96
97 /// Returns the default path for the hypervisor configuration file.
default_hypervisor_config_path() -> PathBuf98 fn default_hypervisor_config_path() -> PathBuf {
99 set_used_default();
100 "config/hypervisor.toml".into()
101 }
102
103 /// Returns the default path for the boot configuration file.
default_boot_config_path() -> PathBuf104 fn default_boot_config_path() -> PathBuf {
105 set_used_default();
106 "config/boot.toml".into()
107 }
108
109 /// Returns the default path for the sysroot directory.
default_sysroot_dir() -> PathBuf110 fn default_sysroot_dir() -> PathBuf {
111 set_used_default();
112 "bin/sysroot".into()
113 }
114
115 /// Returns the default path for the cache directory.
default_cache_root_dir() -> PathBuf116 fn default_cache_root_dir() -> PathBuf {
117 set_used_default();
118 "bin/dadk_cache".into()
119 }
120
default_user_config_dir() -> PathBuf121 fn default_user_config_dir() -> PathBuf {
122 set_used_default();
123 "user/dadk/config".into()
124 }
125
126 #[cfg(test)]
127 mod tests {
128 use super::*;
129 use std::io::Write;
130 use tempfile::NamedTempFile;
131
132 /// Test loading a complete configuration file
133 #[test]
test_full_load_success() -> Result<()>134 fn test_full_load_success() -> Result<()> {
135 let toml_content = r#"
136 [metadata]
137 arch = "x86_64"
138 rootfs-config = "config/rootfs-x86_64.toml"
139 hypervisor-config = "config/hypervisor-x86_64.toml"
140 boot-config = "config/boot-x86_64.toml"
141 sysroot-dir = "bin/sysroot"
142 cache-root-dir = "bin/dadk_cache"
143 user-config-dir = "user/dadk/config"
144 "#;
145
146 let mut temp_file = NamedTempFile::new()?;
147 temp_file.write_all(toml_content.as_bytes())?;
148
149 let path = temp_file.path().to_path_buf();
150 let manifest = DadkManifestFile::load(&path)?;
151
152 assert_eq!(manifest.metadata.arch, TargetArch::X86_64);
153 assert_eq!(
154 manifest.metadata.rootfs_config,
155 PathBuf::from("config/rootfs-x86_64.toml")
156 );
157 assert_eq!(
158 manifest.metadata.hypervisor_config,
159 PathBuf::from("config/hypervisor-x86_64.toml")
160 );
161 assert_eq!(
162 manifest.metadata.boot_config,
163 PathBuf::from("config/boot-x86_64.toml")
164 );
165 assert_eq!(manifest.metadata.sysroot_dir, PathBuf::from("bin/sysroot"));
166 assert!(!manifest.used_default);
167
168 Ok(())
169 }
170
171 /// Test whether an error is reported when the file does not exist.
172 #[test]
test_load_file_not_found()173 fn test_load_file_not_found() {
174 let path = PathBuf::from("non_existent_file.toml");
175 let result = DadkManifestFile::load(&path);
176
177 assert!(result.is_err());
178 }
179
180 /// Test whether an error is reported when the TOML content is invalid
181 #[test]
test_load_invalid_toml() -> Result<()>182 fn test_load_invalid_toml() -> Result<()> {
183 let invalid_toml_content = r#"
184 [metadata
185 arch = "x86_64"
186 "#;
187
188 let mut temp_file = NamedTempFile::new()?;
189 temp_file.write_all(invalid_toml_content.as_bytes())?;
190
191 let path = temp_file.path().to_path_buf();
192 let result = DadkManifestFile::load(&path);
193
194 assert!(result.is_err());
195
196 Ok(())
197 }
198
199 /// Test whether an error is reported when the arch field is invalid
200 #[test]
test_load_invalid_arch_toml() -> Result<()>201 fn test_load_invalid_arch_toml() -> Result<()> {
202 // Invalid arch value
203 let invalid_toml_content = r#"
204 [metadata]
205 arch = "abcde"
206 "#;
207
208 let mut temp_file = NamedTempFile::new()?;
209 temp_file.write_all(invalid_toml_content.as_bytes())?;
210
211 let path = temp_file.path().to_path_buf();
212 let result = DadkManifestFile::load(&path);
213
214 assert!(result.is_err());
215
216 Ok(())
217 }
218
219 /// Test whether an error is reported when a required field is missing
220 #[test]
test_load_missing_required_fields() -> Result<()>221 fn test_load_missing_required_fields() -> Result<()> {
222 let toml_content = r#"
223 [metadata]
224 # arch field is missing
225 "#;
226
227 let mut temp_file = NamedTempFile::new()?;
228 temp_file.write_all(toml_content.as_bytes())?;
229
230 let path = temp_file.path().to_path_buf();
231 let result = DadkManifestFile::load(&path);
232
233 assert!(result.is_err());
234
235 Ok(())
236 }
237
238 /// Test whether default values are used
239 /// when the rootfs_config and other configuration file path fields are not set
240 #[test]
test_load_default_config_path_value() -> Result<()>241 fn test_load_default_config_path_value() -> Result<()> {
242 let toml_content = r#"
243 [metadata]
244 arch = "x86_64"
245 "#;
246 let mut temp_file = NamedTempFile::new()?;
247 temp_file.write_all(toml_content.as_bytes())?;
248 let path = temp_file.path().to_path_buf();
249 let manifest = DadkManifestFile::load(&path)?;
250 assert_eq!(manifest.used_default, true);
251 assert_eq!(
252 manifest.metadata.rootfs_config,
253 PathBuf::from("config/rootfs.toml")
254 );
255 assert_eq!(
256 manifest.metadata.hypervisor_config,
257 PathBuf::from("config/hypervisor.toml")
258 );
259 assert_eq!(
260 manifest.metadata.boot_config,
261 PathBuf::from("config/boot.toml")
262 );
263 Ok(())
264 }
265 }
266