use std::path::PathBuf;
use anyhow::Result;
use serde::Deserialize;
use crate::common::target_arch::TargetArch;
use std::fs;
use toml;
#[derive(Debug, Clone, Deserialize)]
pub struct DadkManifestFile {
pub metadata: Metadata,
#[serde(skip)]
pub used_default: bool,
}
impl DadkManifestFile {
pub fn load(path: &PathBuf) -> Result<Self> {
let content = fs::read_to_string(path)?;
Self::load_from_str(&content)
}
pub fn load_from_str(content: &str) -> Result<Self> {
let mut manifest_toml: DadkManifestFile = toml::from_str(content)?;
manifest_toml.used_default = check_used_default();
Ok(manifest_toml)
}
}
thread_local! {
static USED_DEFAULT: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
}
fn set_used_default() {
USED_DEFAULT.with(|used_default| {
used_default.set(true);
});
}
fn check_used_default() -> bool {
USED_DEFAULT.with(|used_default| used_default.get())
}
#[derive(Debug, Clone, Deserialize)]
pub struct Metadata {
pub arch: TargetArch,
#[serde(default = "default_rootfs_config_path", rename = "rootfs-config")]
pub rootfs_config: PathBuf,
#[serde(
default = "default_hypervisor_config_path",
rename = "hypervisor-config"
)]
pub hypervisor_config: PathBuf,
#[serde(default = "default_boot_config_path", rename = "boot-config")]
pub boot_config: PathBuf,
#[serde(default = "default_sysroot_dir", rename = "sysroot-dir")]
pub sysroot_dir: PathBuf,
#[serde(default = "default_cache_root_dir", rename = "cache-root-dir")]
pub cache_root_dir: PathBuf,
#[deprecated(note = "This field is deprecated and will be removed in DADK 1.0")]
#[serde(default = "default_user_config_dir", rename = "user-config-dir")]
pub user_config_dir: PathBuf,
}
fn default_rootfs_config_path() -> PathBuf {
set_used_default();
"config/rootfs.toml".into()
}
fn default_hypervisor_config_path() -> PathBuf {
set_used_default();
"config/hypervisor.toml".into()
}
fn default_boot_config_path() -> PathBuf {
set_used_default();
"config/boot.toml".into()
}
fn default_sysroot_dir() -> PathBuf {
set_used_default();
"bin/sysroot".into()
}
fn default_cache_root_dir() -> PathBuf {
set_used_default();
"bin/dadk_cache".into()
}
fn default_user_config_dir() -> PathBuf {
set_used_default();
"user/dadk/config".into()
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
use tempfile::NamedTempFile;
#[test]
fn test_full_load_success() -> Result<()> {
let toml_content = r#"
[metadata]
arch = "x86_64"
rootfs-config = "config/rootfs-x86_64.toml"
hypervisor-config = "config/hypervisor-x86_64.toml"
boot-config = "config/boot-x86_64.toml"
sysroot-dir = "bin/sysroot"
cache-root-dir = "bin/dadk_cache"
user-config-dir = "user/dadk/config"
"#;
let mut temp_file = NamedTempFile::new()?;
temp_file.write_all(toml_content.as_bytes())?;
let path = temp_file.path().to_path_buf();
let manifest = DadkManifestFile::load(&path)?;
assert_eq!(manifest.metadata.arch, TargetArch::X86_64);
assert_eq!(
manifest.metadata.rootfs_config,
PathBuf::from("config/rootfs-x86_64.toml")
);
assert_eq!(
manifest.metadata.hypervisor_config,
PathBuf::from("config/hypervisor-x86_64.toml")
);
assert_eq!(
manifest.metadata.boot_config,
PathBuf::from("config/boot-x86_64.toml")
);
assert_eq!(manifest.metadata.sysroot_dir, PathBuf::from("bin/sysroot"));
assert!(!manifest.used_default);
Ok(())
}
#[test]
fn test_load_file_not_found() {
let path = PathBuf::from("non_existent_file.toml");
let result = DadkManifestFile::load(&path);
assert!(result.is_err());
}
#[test]
fn test_load_invalid_toml() -> Result<()> {
let invalid_toml_content = r#"
[metadata
arch = "x86_64"
"#;
let mut temp_file = NamedTempFile::new()?;
temp_file.write_all(invalid_toml_content.as_bytes())?;
let path = temp_file.path().to_path_buf();
let result = DadkManifestFile::load(&path);
assert!(result.is_err());
Ok(())
}
#[test]
fn test_load_invalid_arch_toml() -> Result<()> {
let invalid_toml_content = r#"
[metadata]
arch = "abcde"
"#;
let mut temp_file = NamedTempFile::new()?;
temp_file.write_all(invalid_toml_content.as_bytes())?;
let path = temp_file.path().to_path_buf();
let result = DadkManifestFile::load(&path);
assert!(result.is_err());
Ok(())
}
#[test]
fn test_load_missing_required_fields() -> Result<()> {
let toml_content = r#"
[metadata]
# arch field is missing
"#;
let mut temp_file = NamedTempFile::new()?;
temp_file.write_all(toml_content.as_bytes())?;
let path = temp_file.path().to_path_buf();
let result = DadkManifestFile::load(&path);
assert!(result.is_err());
Ok(())
}
#[test]
fn test_load_default_config_path_value() -> Result<()> {
let toml_content = r#"
[metadata]
arch = "x86_64"
"#;
let mut temp_file = NamedTempFile::new()?;
temp_file.write_all(toml_content.as_bytes())?;
let path = temp_file.path().to_path_buf();
let manifest = DadkManifestFile::load(&path)?;
assert_eq!(manifest.used_default, true);
assert_eq!(
manifest.metadata.rootfs_config,
PathBuf::from("config/rootfs.toml")
);
assert_eq!(
manifest.metadata.hypervisor_config,
PathBuf::from("config/hypervisor.toml")
);
assert_eq!(
manifest.metadata.boot_config,
PathBuf::from("config/boot.toml")
);
Ok(())
}
}