xref: /DADK/dadk-config/src/manifest.rs (revision 5129c63bfab60a54627def29dc1b53c722677e4e)
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