instruct-macros 0.1.8

instruct-macros are a collection of simple macros that we're using in Instructor-AI to generate json schema from Serde Objects
Documentation
extern crate instruct_macros_types;

use instruct_macros::{validate, InstructMacro};
use instruct_macros_types::{
    InstructMacro, InstructMacroResult, Parameter, ParameterInfo, StructInfo,
};

#[cfg(test)]
mod tests {

    use instruct_macros_types::EnumInfo;

    use super::*;

    #[test]
    fn test_string_conversion() {
        #[derive(InstructMacro, Debug)]
        #[allow(dead_code)]
        #[description("This is a struct")]
        struct TestStruct {
            #[description(
                "This is a sample example \
                that spans across \
                three lines"
            )]
            pub field1: String,
            #[description("This is a test field")]
            pub field2: str,
        }
        let info = TestStruct::get_info();
        let desired_struct = StructInfo {
            name: "TestStruct".to_string(),
            description: "This is a struct".to_string(),
            parameters: vec![
                Parameter::Field(ParameterInfo {
                    name: "field1".to_string(),
                    r#type: "String".to_string(),
                    comment: "This is a sample example that spans across three lines".to_string(),
                    is_optional: false,
                    is_list: false,
                }),
                Parameter::Field(ParameterInfo {
                    name: "field2".to_string(),
                    r#type: "str".to_string(),
                    comment: "This is a test field".to_string(),
                    is_optional: false,
                    is_list: false,
                }),
            ],
            is_optional: false,
            is_list: false,
        };

        let info_struct = match info {
            InstructMacroResult::Struct(s) => s,
            _ => panic!("Expected StructInfo"),
        };

        println!("info_struct: {:?}", info_struct);
        println!("desired_struct: {:?}", desired_struct);
        assert!(info_struct == desired_struct);
    }

    #[test]
    fn test_validation_macro() {
        #[derive(InstructMacro, Debug)]
        pub struct UserInfo {
            #[validate(custom = "validate_uppercase")]
            pub name: String,
            pub age: u8,
        }

        #[validate]
        fn validate_uppercase(name: &String) -> Result<String, String> {
            if name.chars().any(|c| c.is_lowercase()) {
                return Err(format!(
                    "Name '{}' should be entirely in uppercase. Examples: 'TIMOTHY', 'JANE SMITH'",
                    name
                ));
            }
            Ok(name.to_uppercase())
        }

        let user_info = UserInfo {
            name: "JoHn DoE".to_string(),
            age: 100,
        };

        assert_eq!(
            user_info.validate().unwrap_err(),
            "Validation failed for field 'name': Name 'JoHn DoE' should be entirely in uppercase. Examples: 'TIMOTHY', 'JANE SMITH'"
        );

        let user_info = UserInfo {
            name: "JOHN DOE".to_string(),
            age: 30,
        };

        assert!(user_info.validate().is_ok());
    }

    #[test]
    fn test_nested_struct_macro() {
        #[derive(InstructMacro, Debug)]
        pub struct Address {
            pub street: String,
            pub city: String,
        }

        #[derive(InstructMacro, Debug)]
        pub struct User {
            pub name: String,
            pub age: u8,
            pub address: Address,
        }

        let info = User::get_info();
        let desired_struct = StructInfo {
            name: "User".to_string(),
            description: "".to_string(),
            parameters: vec![
                Parameter::Field(ParameterInfo {
                    name: "name".to_string(),
                    r#type: "String".to_string(),
                    comment: "".to_string(),
                    is_optional: false,
                    is_list: false,
                }),
                Parameter::Field(ParameterInfo {
                    name: "age".to_string(),
                    r#type: "u8".to_string(),
                    comment: "".to_string(),
                    is_optional: false,
                    is_list: false,
                }),
                Parameter::Struct(StructInfo {
                    name: "address".to_string(),
                    description: "".to_string(),
                    parameters: vec![
                        Parameter::Field(ParameterInfo {
                            name: "street".to_string(),
                            r#type: "String".to_string(),
                            comment: "".to_string(),
                            is_optional: false,
                            is_list: false,
                        }),
                        Parameter::Field(ParameterInfo {
                            name: "city".to_string(),
                            r#type: "String".to_string(),
                            comment: "".to_string(),
                            is_optional: false,
                            is_list: false,
                        }),
                    ],
                    is_optional: false,
                    is_list: false,
                }),
            ],
            is_optional: false,
            is_list: false,
        };

        let info_struct = match info {
            InstructMacroResult::Struct(s) => s,
            _ => panic!("Expected StructInfo"),
        };
        assert!(info_struct == desired_struct);
    }

    #[test]
    fn test_single_enum() {
        #[derive(InstructMacro, Debug)]
        #[allow(dead_code)]
        #[description = "This is an enum representing the status of a person"]
        pub enum Status {
            Active,
            Inactive,
            Pending,
        }

        let info = Status::get_info();

        let desired_enum = EnumInfo {
            title: "Status".to_string(),
            r#enum: vec![
                "Active".to_string(),
                "Inactive".to_string(),
                "Pending".to_string(),
            ],
            r#type: "Status".to_string(),
            description: "".to_string(),
            is_optional: false,
            is_list: false,
        };

        let info_enum = match info {
            InstructMacroResult::Enum(e) => e,
            _ => panic!("Expected EnumInfo"),
        };

        assert!(info_enum == desired_enum);
    }

    #[test]
    fn test_enum_as_struct_property() {
        #[derive(InstructMacro, Debug)]
        #[allow(dead_code)]
        #[description("This is an enum representing the status of a person")]
        pub enum Status {
            Active,
            Inactive,
            Pending,
        }

        #[derive(InstructMacro, Debug)]
        #[allow(dead_code)]
        pub struct User {
            name: String,
            status: Status,
        }

        let info = User::get_info();

        let desired_struct = StructInfo {
            name: "User".to_string(),
            description: "".to_string(),
            parameters: vec![
                Parameter::Field(ParameterInfo {
                    name: "name".to_string(),
                    r#type: "String".to_string(),
                    comment: "".to_string(),
                    is_optional: false,
                    is_list: false,
                }),
                Parameter::Enum(EnumInfo {
                    title: "status".to_string(),
                    r#enum: vec![
                        "Active".to_string(),
                        "Inactive".to_string(),
                        "Pending".to_string(),
                    ],
                    r#type: "Status".to_string(),
                    description: "This is an enum representing the status of a person".to_string(),
                    is_optional: false,
                    is_list: false,
                }),
            ],
            is_optional: false,
            is_list: false,
        };

        let info_struct = match info {
            InstructMacroResult::Struct(s) => s,
            _ => panic!("Expected StructInfo"),
        };

        println!("info_struct: {:?}", info_struct);
        println!("desired_struct: {:?}", desired_struct);

        assert!(info_struct == desired_struct);
    }
}