alef 0.25.21

Opinionated polyglot binding generator for Rust libraries
Documentation
use super::*;

#[test]
fn test_field_with_bool_default_literal() {
    let source = r#"
        pub struct Settings {
            pub verbose: bool,
        }

        impl Default for Settings {
            fn default() -> Self {
                Settings { verbose: true }
            }
        }
    "#;

    let surface = extract_from_source(source);
    assert_eq!(surface.types.len(), 1);

    let settings = &surface.types[0];
    assert_eq!(settings.fields.len(), 1);

    let verbose_field = &settings.fields[0];
    assert_eq!(verbose_field.name, "verbose");
    assert_eq!(
        verbose_field.typed_default,
        Some(crate::core::ir::DefaultValue::BoolLiteral(true)),
        "bool field should have BoolLiteral(true) default"
    );
}

#[test]
fn test_field_with_int_default_literal() {
    let source = r#"
        pub struct Limits {
            pub max_retries: i32,
        }

        impl Default for Limits {
            fn default() -> Self {
                Limits { max_retries: 3 }
            }
        }
    "#;

    let surface = extract_from_source(source);
    assert_eq!(surface.types.len(), 1);

    let limits = &surface.types[0];
    assert_eq!(limits.fields.len(), 1);

    let max_retries = &limits.fields[0];
    assert_eq!(max_retries.name, "max_retries");
    assert_eq!(
        max_retries.typed_default,
        Some(crate::core::ir::DefaultValue::IntLiteral(3)),
        "int field should have IntLiteral(3) default"
    );
}

#[test]
fn test_field_with_negative_int_default() {
    let source = r#"
        pub struct Temperature {
            pub celsius: i32,
        }

        impl Default for Temperature {
            fn default() -> Self {
                Temperature { celsius: -273 }
            }
        }
    "#;

    let surface = extract_from_source(source);
    let temperature = &surface.types[0];
    let celsius_field = &temperature.fields[0];

    assert_eq!(
        celsius_field.typed_default,
        Some(crate::core::ir::DefaultValue::IntLiteral(-273)),
        "negative int literal should be parsed correctly"
    );
}

#[test]
fn test_field_with_float_default_literal() {
    let source = r#"
        pub struct Thresholds {
            pub confidence: f64,
        }

        impl Default for Thresholds {
            fn default() -> Self {
                Thresholds { confidence: 0.95 }
            }
        }
    "#;

    let surface = extract_from_source(source);
    let thresholds = &surface.types[0];
    let confidence_field = &thresholds.fields[0];

    assert_eq!(
        confidence_field.typed_default,
        Some(crate::core::ir::DefaultValue::FloatLiteral(0.95)),
        "float field should have FloatLiteral(0.95) default"
    );
}

#[test]
fn test_field_with_negative_float_default() {
    let source = r#"
        pub struct Adjustment {
            pub offset: f64,
        }

        impl Default for Adjustment {
            fn default() -> Self {
                Adjustment { offset: -1.5 }
            }
        }
    "#;

    let surface = extract_from_source(source);
    let adjustment = &surface.types[0];
    let offset_field = &adjustment.fields[0];

    assert_eq!(
        offset_field.typed_default,
        Some(crate::core::ir::DefaultValue::FloatLiteral(-1.5)),
        "negative float literal should be parsed correctly"
    );
}

#[test]
fn test_field_with_string_literal_default() {
    let source = r#"
        pub struct Message {
            pub text: String,
        }

        impl Default for Message {
            fn default() -> Self {
                Message { text: "hello world".into() }
            }
        }
    "#;

    let surface = extract_from_source(source);
    let message = &surface.types[0];
    let text_field = &message.fields[0];

    assert_eq!(
        text_field.typed_default,
        Some(crate::core::ir::DefaultValue::StringLiteral("hello world".to_string())),
        "string field with .into() should have StringLiteral default"
    );
}

#[test]
fn test_field_missing_from_struct_literal() {
    let source = r#"
        pub struct Partial {
            pub field_a: u32,
            pub field_b: String,
        }

        impl Default for Partial {
            fn default() -> Self {
                Partial { field_a: 99 }
            }
        }
    "#;

    let surface = extract_from_source(source);
    let partial = &surface.types[0];

    assert_eq!(partial.fields.len(), 2);

    // field_a is in the struct literal
    let field_a = &partial.fields[0];
    assert_eq!(
        field_a.typed_default,
        Some(crate::core::ir::DefaultValue::IntLiteral(99))
    );

    // field_b is NOT in the struct literal → should be Empty
    let field_b = &partial.fields[1];
    assert_eq!(
        field_b.typed_default,
        Some(crate::core::ir::DefaultValue::Empty),
        "Field not in struct literal should have Empty default"
    );
}

#[test]
fn test_struct_literal_wrapped_in_block() {
    let source = r#"
        pub struct WithBlock {
            pub value: i32,
        }

        impl Default for WithBlock {
            fn default() -> Self {
                {
                    WithBlock { value: 77 }
                }
            }
        }
    "#;

    let surface = extract_from_source(source);
    let with_block = &surface.types[0];
    let value_field = &with_block.fields[0];

    assert_eq!(
        value_field.typed_default,
        Some(crate::core::ir::DefaultValue::IntLiteral(77)),
        "Struct literal inside block expression should be found"
    );
}

#[test]
fn test_field_with_false_bool_default() {
    let source = r#"
        pub struct DisabledFeature {
            pub active: bool,
        }

        impl Default for DisabledFeature {
            fn default() -> Self {
                DisabledFeature { active: false }
            }
        }
    "#;

    let surface = extract_from_source(source);
    let disabled = &surface.types[0];
    let active_field = &disabled.fields[0];

    assert_eq!(
        active_field.typed_default,
        Some(crate::core::ir::DefaultValue::BoolLiteral(false)),
        "false bool literal should be parsed as BoolLiteral(false)"
    );
}

#[test]
fn test_field_with_zero_defaults() {
    let source = r#"
        pub struct Zeroes {
            pub int_val: i32,
            pub float_val: f64,
        }

        impl Default for Zeroes {
            fn default() -> Self {
                Zeroes { int_val: 0, float_val: 0.0 }
            }
        }
    "#;

    let surface = extract_from_source(source);
    let zeroes = &surface.types[0];

    assert_eq!(
        zeroes.fields[0].typed_default,
        Some(crate::core::ir::DefaultValue::IntLiteral(0))
    );
    assert_eq!(
        zeroes.fields[1].typed_default,
        Some(crate::core::ir::DefaultValue::FloatLiteral(0.0))
    );
}

#[test]
fn test_field_with_str_literal_default() {
    // A bare `&str` literal used directly as a string expression (not via .into())
    // The extractor handles `syn::Lit::Str` directly — exercises the Lit::Str branch.
    let source = r#"
        pub struct Prefix {
            pub label: String,
        }

        impl Default for Prefix {
            fn default() -> Self {
                // Use String::from to get the str literal processed
                Prefix { label: String::from("hello") }
            }
        }
    "#;

    let surface = extract_from_source(source);
    let prefix = &surface.types[0];
    let label_field = &prefix.fields[0];

    assert_eq!(
        label_field.typed_default,
        Some(crate::core::ir::DefaultValue::StringLiteral("hello".to_string())),
        "String::from(literal) exercises the Lit::Str branch"
    );
}

#[test]
fn test_field_with_duration_from_secs_non_literal_fallback() {
    // Duration::from_secs with a non-literal arg falls back to Empty
    let source = r#"
        use std::time::Duration;

        pub struct Config {
            pub timeout: Duration,
        }

        fn get_secs() -> u64 { 10 }

        impl Default for Config {
            fn default() -> Self {
                Config { timeout: Duration::from_secs(get_secs()) }
            }
        }
    "#;

    let surface = extract_from_source(source);
    let config = &surface.types[0];
    let timeout_field = &config.fields[0];

    assert_eq!(
        timeout_field.typed_default,
        Some(crate::core::ir::DefaultValue::Empty),
        "Duration::from_secs with non-literal arg should fall back to Empty"
    );
}