xref: /DADK/dadk-config/src/manifest.rs (revision c9f71754563759e653d59a110f15298891564be9)
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", 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.
92 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.
98 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.
104 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.
110 fn default_sysroot_dir() -> PathBuf {
111     set_used_default();
112     "bin/sysroot".into()
113 }
114 
115 /// Returns the default path for the cache directory.
116 fn default_cache_root_dir() -> PathBuf {
117     set_used_default();
118     "bin/dadk_cache".into()
119 }
120 
121 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]
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]
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]
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]
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]
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]
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