tree-type 0.4.5

Rust macros for creating type-safe filesystem tree structures
Documentation
#![allow(deprecated)]

use tempfile::TempDir;

use tree_type::tree_type;

#[allow(dead_code)]
fn default_config1(_: &ConfigFile1) -> Result<String, std::io::Error> {
    Ok("config1".to_string())
}

#[allow(dead_code)]
fn default_config3(_: &ConfigFile3) -> Result<String, std::io::Error> {
    Ok("config3".to_string())
}

#[allow(dead_code)]
fn default_config4(_: &ConfigFile4) -> Result<String, std::io::Error> {
    Ok("config4".to_string())
}

#[allow(dead_code)]
fn default_config5(_: &ConfigFile5) -> Result<String, std::io::Error> {
    Ok("config5".to_string())
}

#[allow(dead_code)]
fn default_config6(_: &ConfigFile6) -> Result<String, std::io::Error> {
    Ok("config6".to_string())
}

// Test 1: Optional with Default
tree_type! {
    TestRoot1 {
        #[optional]
        #[default(default_config1)]
        config("config.toml") as ConfigFile1
    }
}

#[test]
fn test_optional_with_default() {
    let temp_dir = TempDir::new().unwrap();
    let root = TestRoot1::new(temp_dir.path()).unwrap();

    root.sync().unwrap();
    assert!(root.config().exists());

    let temp_dir2 = TempDir::new().unwrap();
    let root2 = TestRoot1::new(temp_dir2.path()).unwrap();
    let result = root2.sync();
    assert!(result.is_ok());
    assert!(root2.config().exists());
}

// Test 2: Optional without validate
tree_type! {
    TestRoot2 {
        #[optional]
        config("config.toml")
    }
}

#[test]
fn test_optional_without_validate() {
    let temp_dir = TempDir::new().unwrap();
    let root = TestRoot2::new(temp_dir.path()).unwrap();

    let result = root.sync();
    assert!(result.is_ok());
}

// Test 3: Optional + Default + Validate
tree_type! {
    TestRoot3 {
        #[optional]
        #[default(default_config3)]
        config("config.toml") as ConfigFile3
    }
}

#[test]
fn test_optional_default_validate() {
    let temp_dir = TempDir::new().unwrap();
    let root = TestRoot3::new(temp_dir.path()).unwrap();

    root.sync().unwrap();
    assert!(root.config().exists());

    root.sync().expect("Structure should be valid");
}

// Test 4: Required + Default + Validate
tree_type! {
    TestRoot4 {
        #[required]
        #[default(default_config4)]
        config("config.toml") as ConfigFile4
    }
}

#[test]
fn test_required_default_validate() {
    let temp_dir = TempDir::new().unwrap();
    let root = TestRoot4::new(temp_dir.path()).unwrap();

    root.sync().unwrap();
    assert!(root.config().exists());

    let temp_dir2 = TempDir::new().unwrap();
    let root2 = TestRoot4::new(temp_dir2.path()).unwrap();
    let result = root2.sync();
    assert!(result.is_ok());
    assert!(root2.config().exists());
}

// Test 5: Optional + Default + Validate (validation should not fail if missing)
tree_type! {
    TestRoot5 {
        #[optional]
        #[default(default_config5)]
        config("config.toml") as ConfigFile5
    }
}

#[test]
fn test_optional_default_validate_missing() {
    let temp_dir = TempDir::new().unwrap();
    let root = TestRoot5::new(temp_dir.path()).unwrap();

    let result = root.sync();
    assert!(result.is_ok());
}

// Test 6: Dynamic ID Context with All Combinations
tree_type! {
    TestRoot6 {
        projects/ {
            [id: String]/ as Project {
                #[required]
                #[default(default_config6)]
                config("config.toml") as ConfigFile6
            }
        }
    }
}

#[test]
fn test_dynamic_id_all_attributes() {
    let temp_dir = TempDir::new().unwrap();
    let root = TestRoot6::new(temp_dir.path()).unwrap();

    root.sync().unwrap();
    let project = root.projects().id("test");
    project.sync().unwrap();
    assert!(project.config().exists());

    let temp_dir2 = TempDir::new().unwrap();
    let root2 = TestRoot6::new(temp_dir2.path()).unwrap();
    root2.sync().unwrap();
    let project2 = root2.projects().id("test2");
    let result = project2.sync();
    assert!(result.is_ok());
    assert!(project2.config().exists());
}