modelx 0.1.0

Compiler for Modelx files
Documentation
extern crate pest;
#[macro_use]
extern crate pest_derive;

use pest::Parser;
use std::collections::{HashMap, HashSet};

#[derive(Parser)]
#[grammar = "grammar.pest"]
pub struct ModelxParser;

#[derive(Debug, Default)]
pub enum FieldType {
    #[default]
    String,
    UUID,
    Integer,
}

#[derive(Debug, Default)]
pub struct Field {
    pub name: String,
    pub field_type: FieldType,
    pub is_key: bool,
    pub annotations: HashSet<String>,
}

#[derive(Debug, Default)]
pub struct Definition {
    pub name: String,
    pub fields: HashMap<String, Field>,
    pub annotations: Vec<String>,
}

#[derive(Debug, Default)]
pub struct Modelx {
    pub definitions: HashMap<String, Definition>,
}

impl Modelx {
    pub fn load<P: AsRef<std::path::Path>>(path: P) -> Self {
        let input = std::fs::read_to_string(path).unwrap();
        Modelx::parse(&input)
    }

    pub fn parse(input: &str) -> Modelx {
        let file = ModelxParser::parse(Rule::file, input)
            .unwrap()
            .next()
            .unwrap();

        let mut modelx = Modelx {
            ..Default::default()
        };
        for table in file.into_inner() {
            match table.as_rule() {
                Rule::table => {
                    let mut curr_def = Definition {
                        ..Default::default()
                    };
                    for in_table in table.into_inner() {
                        match in_table.as_rule() {
                            Rule::ident => {
                                curr_def.name = in_table.as_str().to_string();
                            }
                            Rule::annotation => {
                                curr_def.annotations.push(in_table.as_str().to_string());
                            }
                            Rule::field => {
                                let mut curr_field = Field {
                                    ..Default::default()
                                };
                                for in_field in in_table.into_inner() {
                                    match in_field.as_rule() {
                                        Rule::is_key => {
                                            if in_field.as_str() == "key" {
                                                curr_field.is_key = true
                                            }
                                        }
                                        Rule::field_type => match in_field.as_str() {
                                            "String" => curr_field.field_type = FieldType::String,
                                            "UUID" => curr_field.field_type = FieldType::UUID,
                                            "Integer" => curr_field.field_type = FieldType::Integer,
                                            _ => unreachable!(),
                                        },
                                        Rule::ident => {
                                            curr_field.name = in_field.as_str().to_string();
                                        }
                                        Rule::annotation => {
                                            curr_field
                                                .annotations
                                                .insert(in_field.as_str().to_string());
                                        }
                                        _ => {}
                                    }
                                }
                                curr_def.fields.insert(curr_field.name.clone(), curr_field);
                            }
                            _ => {}
                        }
                    }
                    modelx.definitions.insert(curr_def.name.clone(), curr_def);
                }
                _ => (),
            }
        }
        modelx
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn sample_model_parse() {
        let result = Modelx::parse(
            r#"
            @anno1
            @anno2
            table foobar {
              @fieldanno1
              @fieldanno2
              key bla: UUID
              super:Integer
            }
        "#,
        );
        dbg!(&result);
    }

    #[test]
    fn sample_model_load() {
        let result = Modelx::load("test/example.modelx");
        dbg!(&result);
    }
}