apollo-configuration-macros 0.1.1

Supporting macros for apollo-configuration (internal, do not use directly)
Documentation
use crate::derive_configuration_enum;
use crate::tests::pretty_print;

#[test]
fn simple_enum() {
    let output = derive_configuration_enum(
        Default::default(),
        syn::parse_quote! {
            enum LogLevel {
                Debug,
                Info,
                Warn,
                Error,
            }
        },
    )
    .unwrap();

    insta::assert_snapshot!(pretty_print(output));
}

#[test]
fn public_enum() {
    let output = derive_configuration_enum(
        Default::default(),
        syn::parse_quote! {
            pub enum Visibility {
                Public,
                Private,
                Internal,
            }
        },
    )
    .unwrap();

    insta::assert_snapshot!(pretty_print(output));
}

#[test]
fn enum_with_tuple_variants() {
    let output = derive_configuration_enum(
        Default::default(),
        syn::parse_quote! {
            enum Value {
                Int(i64),
                Float(f64),
                Text(String),
            }
        },
    )
    .unwrap();

    insta::assert_snapshot!(pretty_print(output));
}

#[test]
fn enum_with_struct_variants() {
    let output = derive_configuration_enum(
        Default::default(),
        syn::parse_quote! {
            enum Endpoint {
                Http { url: String, timeout: u64 },
                Grpc { host: String, port: u16 },
            }
        },
    )
    .unwrap();

    insta::assert_snapshot!(pretty_print(output));
}

#[test]
fn enum_with_doc_comments() {
    let output = derive_configuration_enum(
        Default::default(),
        syn::parse_quote! {
            /// The mode of operation.
            enum Mode {
                /// Development mode with debug features.
                Development,
                /// Production mode optimized for performance.
                Production,
            }
        },
    )
    .unwrap();

    insta::assert_snapshot!(pretty_print(output));
}

#[test]
fn enum_preserves_existing_attributes() {
    let output = derive_configuration_enum(
        Default::default(),
        syn::parse_quote! {
            #[derive(PartialEq, Eq)]
            #[allow(dead_code)]
            enum Status {
                #[serde(rename = "ACTIVE")]
                Active,
                #[serde(rename = "INACTIVE")]
                Inactive,
            }
        },
    )
    .unwrap();

    insta::assert_snapshot!(pretty_print(output));
}

#[test]
fn enum_with_generics() {
    let output = derive_configuration_enum(
        Default::default(),
        syn::parse_quote! {
            enum Container<T> {
                Empty,
                Single(T),
                Multiple(Vec<T>),
            }
        },
    )
    .unwrap();

    insta::assert_snapshot!(pretty_print(output));
}

#[test]
fn enum_with_default_unit_variant() {
    let output = derive_configuration_enum(
        Default::default(),
        syn::parse_quote! {
            enum LogLevel {
                Debug,
                #[config(default)]
                Info,
                Warn,
                Error,
            }
        },
    )
    .unwrap();

    insta::assert_snapshot!(pretty_print(output));
}

#[test]
fn enum_with_default_tuple_variant() {
    let output = derive_configuration_enum(
        Default::default(),
        syn::parse_quote! {
            enum Value {
                #[config(default)]
                Int(i64),
                Float(f64),
                Text(String),
            }
        },
    )
    .unwrap();

    insta::assert_snapshot!(pretty_print(output));
}

#[test]
fn enum_with_default_struct_variant() {
    let output = derive_configuration_enum(
        Default::default(),
        syn::parse_quote! {
            enum Endpoint {
                #[config(default)]
                Http { url: String, timeout: u64 },
                Grpc { host: String, port: u16 },
            }
        },
    )
    .unwrap();

    insta::assert_snapshot!(pretty_print(output));
}

#[test]
fn enum_multiple_defaults_error() {
    let result = derive_configuration_enum(
        Default::default(),
        syn::parse_quote! {
            enum Invalid {
                #[config(default)]
                First,
                #[config(default)]
                Second,
            }
        },
    );

    let Err(err) = result else {
        panic!("multiple defaults should return error");
    };
    assert_eq!(
        err.to_string(),
        "only one variant can be marked as #[config(default)]"
    );
}

#[test]
fn enum_struct_variant_with_field_defaults() {
    let output = derive_configuration_enum(
        Default::default(),
        syn::parse_quote! {
            enum Endpoint {
                #[config(default)]
                Http {
                    #[config(default = "http://localhost".to_string())]
                    url: String,
                    #[config(default = 30)]
                    timeout: u64,
                },
                Grpc {
                    #[config(required)]
                    host: String,
                    #[config(default = 9090)]
                    port: u16,
                },
            }
        },
    )
    .unwrap();

    insta::assert_snapshot!(pretty_print(output));
}

#[test]
fn enum_struct_variant_with_required_fields() {
    let output = derive_configuration_enum(
        Default::default(),
        syn::parse_quote! {
            enum Auth {
                ApiKey {
                    #[config(required)]
                    key: String,
                },
                Basic {
                    #[config(required)]
                    username: String,
                    #[config(required)]
                    password: String,
                },
            }
        },
    )
    .unwrap();

    insta::assert_snapshot!(pretty_print(output));
}

#[test]
fn enum_default_variant_with_required_field_error() {
    let result = derive_configuration_enum(
        Default::default(),
        syn::parse_quote! {
            enum Invalid {
                #[config(default)]
                Variant {
                    #[config(required)]
                    field: String,
                },
            }
        },
    );

    let Err(err) = result else {
        panic!("default variant with required field should return error");
    };
    assert_eq!(
        err.to_string(),
        "cannot mark field as required in a #[config(default)] variant"
    );
}

#[test]
fn enum_struct_variant_with_validation() {
    let output = derive_configuration_enum(
        Default::default(),
        syn::parse_quote! {
            enum Auth {
                ApiKey {
                    #[config(pattern("^[A-Za-z0-9_-]+$"))]
                    key: String,
                },
                Timeout {
                    #[config(range(min = 1, max = 3600))]
                    seconds: u64,
                },
                Name {
                    #[config(length(min = 1, max = 100))]
                    value: String,
                },
            }
        },
    )
    .unwrap();

    insta::assert_snapshot!(pretty_print(output));
}

#[test]
fn enum_struct_variant_with_custom_validation() {
    let output = derive_configuration_enum(
        Default::default(),
        syn::parse_quote! {
            enum EnumConfig {
                #[config(default)]
                Valid { valid_field: usize },
                Invalid {
                    #[config(validate = always_errors)]
                    erroring_field: usize,
                },
            }
        },
    )
    .unwrap();

    insta::assert_snapshot!(pretty_print(output));
}

#[test]
fn enum_struct_variant_with_cfg_attributes() {
    let output = derive_configuration_enum(
        Default::default(),
        syn::parse_quote! {
            enum FeatureGatedEnum {
                #[config(default)]
                Http {
                    url: String,
                    #[cfg(feature = "timeout")]
                    timeout: u64,
                },
                Grpc {
                    host: String,
                    #[cfg(feature = "compression")]
                    compression: String,
                },
            }
        },
    )
    .unwrap();

    insta::assert_snapshot!(pretty_print(output));
}

#[test]
fn enum_with_cfg_gated_variants() {
    let output = derive_configuration_enum(
        Default::default(),
        syn::parse_quote! {
            enum Exporter {
                #[config(default)]
                Stdout,
                #[cfg(feature = "otlp")]
                Otlp { endpoint: String },
                #[cfg(feature = "datadog")]
                Datadog { api_key: String },
            }
        },
    )
    .unwrap();

    insta::assert_snapshot!(pretty_print(output));
}