xref: /DADK/dadk-config/src/manifest.rs (revision 1ad837a44976ed9fbcb8d70a0b0b47ea3286c5ed)
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 
74 /// Returns the default path for the rootfs configuration file.
75 fn default_rootfs_config_path() -> PathBuf {
76     set_used_default();
77     "config/rootfs.toml".into()
78 }
79 
80 /// Returns the default path for the hypervisor configuration file.
81 fn default_hypervisor_config_path() -> PathBuf {
82     set_used_default();
83     "config/hypervisor.toml".into()
84 }
85 
86 /// Returns the default path for the boot configuration file.
87 fn default_boot_config_path() -> PathBuf {
88     set_used_default();
89     "config/boot.toml".into()
90 }
91 
92 #[cfg(test)]
93 mod tests {
94     use super::*;
95     use std::io::Write;
96     use tempfile::NamedTempFile;
97 
98     /// Test loading a complete configuration file
99     #[test]
100     fn test_full_load_success() -> Result<()> {
101         let toml_content = r#"
102             [metadata]
103             arch = "x86_64"
104             rootfs_config = "config/rootfs-x86_64.toml"
105             hypervisor_config = "config/hypervisor-x86_64.toml"
106             boot_config = "config/boot-x86_64.toml"
107         "#;
108 
109         let mut temp_file = NamedTempFile::new()?;
110         temp_file.write_all(toml_content.as_bytes())?;
111 
112         let path = temp_file.path().to_path_buf();
113         let manifest = DadkManifestFile::load(&path)?;
114 
115         assert_eq!(manifest.metadata.arch, TargetArch::X86_64);
116         assert_eq!(
117             manifest.metadata.rootfs_config,
118             PathBuf::from("config/rootfs-x86_64.toml")
119         );
120         assert_eq!(
121             manifest.metadata.hypervisor_config,
122             PathBuf::from("config/hypervisor-x86_64.toml")
123         );
124         assert_eq!(
125             manifest.metadata.boot_config,
126             PathBuf::from("config/boot-x86_64.toml")
127         );
128         assert!(!manifest.used_default);
129 
130         Ok(())
131     }
132 
133     /// Test whether an error is reported when the file does not exist.
134     #[test]
135     fn test_load_file_not_found() {
136         let path = PathBuf::from("non_existent_file.toml");
137         let result = DadkManifestFile::load(&path);
138 
139         assert!(result.is_err());
140     }
141 
142     /// Test whether an error is reported when the TOML content is invalid
143     #[test]
144     fn test_load_invalid_toml() -> Result<()> {
145         let invalid_toml_content = r#"
146             [metadata
147             arch = "x86_64"
148         "#;
149 
150         let mut temp_file = NamedTempFile::new()?;
151         temp_file.write_all(invalid_toml_content.as_bytes())?;
152 
153         let path = temp_file.path().to_path_buf();
154         let result = DadkManifestFile::load(&path);
155 
156         assert!(result.is_err());
157 
158         Ok(())
159     }
160 
161     /// Test whether an error is reported when the arch field is invalid
162     #[test]
163     fn test_load_invalid_arch_toml() -> Result<()> {
164         // Invalid arch value
165         let invalid_toml_content = r#"
166             [metadata]
167             arch = "abcde"
168         "#;
169 
170         let mut temp_file = NamedTempFile::new()?;
171         temp_file.write_all(invalid_toml_content.as_bytes())?;
172 
173         let path = temp_file.path().to_path_buf();
174         let result = DadkManifestFile::load(&path);
175 
176         assert!(result.is_err());
177 
178         Ok(())
179     }
180 
181     /// Test whether an error is reported when a required field is missing
182     #[test]
183     fn test_load_missing_required_fields() -> Result<()> {
184         let toml_content = r#"
185             [metadata]
186             # arch field is missing
187         "#;
188 
189         let mut temp_file = NamedTempFile::new()?;
190         temp_file.write_all(toml_content.as_bytes())?;
191 
192         let path = temp_file.path().to_path_buf();
193         let result = DadkManifestFile::load(&path);
194 
195         assert!(result.is_err());
196 
197         Ok(())
198     }
199 
200     /// Test whether default values are used
201     /// when the rootfs_config and other configuration file path fields are not set
202     #[test]
203     fn test_load_default_config_path_value() -> Result<()> {
204         let toml_content = r#"
205             [metadata]
206             arch = "x86_64"
207         "#;
208         let mut temp_file = NamedTempFile::new()?;
209         temp_file.write_all(toml_content.as_bytes())?;
210         let path = temp_file.path().to_path_buf();
211         let manifest = DadkManifestFile::load(&path)?;
212         assert_eq!(manifest.used_default, true);
213         assert_eq!(
214             manifest.metadata.rootfs_config,
215             PathBuf::from("config/rootfs.toml")
216         );
217         assert_eq!(
218             manifest.metadata.hypervisor_config,
219             PathBuf::from("config/hypervisor.toml")
220         );
221         assert_eq!(
222             manifest.metadata.boot_config,
223             PathBuf::from("config/boot.toml")
224         );
225         Ok(())
226     }
227 }
228