fimi_magic 0.1.3

Procedural macros for fimi-rs (generic file migration framework)
Documentation
extern crate proc_macro;
use proc_macro::TokenStream;

struct ParsedMigration {
    value: String,
    name: String,
    pascal_name: String,
    id: usize,
}

fn parse_migration_string(migration_string: &str) -> ParsedMigration {
    let migration = migration_string.trim();
    let full_name = migration.to_string();
    let parts: Vec<&str> = full_name.split('_').collect();
    let id = parts[1];
    let name = parts[2..].join("_");

    let pascal_name = name
        .split('_')
        .map(|s| {
            let mut c = s.chars();
            match c.next() {
                None => String::new(),
                Some(f) => f.to_uppercase().chain(c).collect(),
            }
        })
        .collect::<String>();

    ParsedMigration {
        value: migration.to_string(),
        name,
        pascal_name,
        id: id.parse().unwrap(),
    }
}

#[proc_macro]
pub fn migrations(migration_token: TokenStream) -> TokenStream {
    let migration_token_str = migration_token.to_string();
    let all_migrations = migration_token_str.split(',').collect::<Vec<&str>>();

    let mut parsed_migrations = all_migrations
        .iter()
        .map(|mig| parse_migration_string(mig))
        .collect::<Vec<ParsedMigration>>();
    parsed_migrations.sort_by(|a, b| a.id.cmp(&b.id));

    let mut return_value = String::new();
    return_value += r#"
        use fimi::{Migration, MigrationData, MigrationExecutor};
    "#;

    for mig in &parsed_migrations {
        return_value += &format!(
            "
            mod {};
            use self::{}::{};
            ",
            mig.value, mig.value, mig.pascal_name
        );
    }

    return_value += r#"
        pub fn migrations() -> MigrationExecutor { 
            MigrationExecutor::new(
                vec![
    "#;

    for mig in &parsed_migrations {
        return_value += &format!(
            r#"
            MigrationData {{
                full_name: "{}".to_string(),
                name: "{}".to_string(),
                id: {},
                migration: Box::new({}{{}}),
            }}
            "#,
            mig.value, mig.name, mig.id, mig.pascal_name
        );

        return_value += ",";
    }

    return_value += r#"
        ],
        )}"#;

    return_value.parse().unwrap()
}

#[cfg(test)]
mod test {
    #[test]
    fn test_parse_migration_string() {
        let migration_string = "_20241021212814_init_migration";

        let parsed_migration = super::parse_migration_string(migration_string);

        assert_eq!(parsed_migration.value, "_20241021212814_init_migration");
        assert_eq!(parsed_migration.name, "init_migration");
        assert_eq!(parsed_migration.pascal_name, "InitMigration");
        assert_eq!(parsed_migration.id, 20241021212814);
    }
}