dinoco_compiler 0.1.0

The Dinoco schema compiler for parsing, validating, and analyzing database schemas.
Documentation
use serde::{Deserialize, Serialize};

use pest::Span;
use std::collections::HashMap;

#[derive(Debug, Clone, PartialEq)]
pub struct DinocoCompilerError {
    pub message: String,

    pub start_line: usize,
    pub start_column: usize,

    pub end_line: usize,
    pub end_column: usize,
}

impl Default for DinocoCompilerError {
    fn default() -> Self {
        Self { message: "".to_string(), start_line: 0, start_column: 0, end_line: 0, end_column: 0 }
    }
}

pub type DinocoCompilerResult<T> = Result<T, Vec<DinocoCompilerError>>;

#[derive(Debug, Clone, PartialEq)]
pub struct Schema<'a> {
    pub tables: Vec<Table<'a>>,
    pub enums: Vec<Enum<'a>>,
    pub configs: Vec<Config<'a>>,
    pub span: Span<'a>,

    pub total_blocks: usize,
    pub comments: Vec<(usize, String)>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct Config<'a> {
    pub position: usize,
    pub total_fields: usize,

    pub comments: Vec<(usize, Span<'a>)>,

    pub fields: Vec<ConfigField<'a>>,
    pub span: Span<'a>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct ConfigField<'a> {
    pub name: String,
    pub position: usize,

    pub comments: Vec<Span<'a>>,

    pub value: Option<ConfigValue<'a>>,
    pub span: Span<'a>,
}

#[derive(Debug, Clone, PartialEq)]
pub enum ConfigValue<'a> {
    String(String, Span<'a>),
    Function { name: String, args: Vec<ConfigValue<'a>>, span: Span<'a> },
    Array(Vec<ConfigValue<'a>>, Span<'a>),
    Object(Vec<ConfigField<'a>>, Span<'a>),
    Comment(Span<'a>),
}

#[derive(Debug, Clone, PartialEq)]
pub struct Table<'a> {
    pub name: String,
    pub mapped_name: Option<String>,
    pub mapped_name_span: Option<Span<'a>>,
    pub primary_key_fields: Vec<String>,
    pub primary_key_fields_span: Option<Span<'a>>,
    pub unique_field_sets: Vec<Vec<String>>,
    pub index_field_sets: Vec<Vec<String>>,
    pub fields: Vec<Field<'a>>,
    pub span: Span<'a>,

    pub position: usize,
    pub total_fields: usize,

    pub comments: Vec<(usize, Span<'a>)>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct Enum<'a> {
    pub position: usize,
    pub total_blocks: usize,
    pub comments: Vec<(usize, Span<'a>)>,

    pub name: String,
    pub values: Vec<(usize, Span<'a>)>,

    pub span: Span<'a>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct Relation<'a> {
    pub named_params: HashMap<String, Vec<String>>,
    pub span: Span<'a>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct Field<'a> {
    pub name: String,
    pub field_type: FieldType,
    pub default_value: FieldDefaultValue,

    pub is_primary_key: bool,
    pub is_optional: bool,
    pub is_unique: bool,
    pub is_virtual: bool,
    pub is_list: bool,

    pub relation: Option<Relation<'a>>,

    pub newlines: usize,
    pub position: usize,
    pub comments: Vec<String>,

    pub span: Span<'a>,
}

#[derive(Debug, Clone, PartialEq)]
pub enum FieldDefaultValue {
    NotDefined,
    String(String),
    Boolean(bool),
    Integer(i64),
    Float(f64),
    Custom(String),
    Function(FunctionCall),
}

#[derive(Debug, Clone, PartialEq)]
pub enum FieldType {
    String,
    Boolean,
    Integer,
    Float,
    Json,
    DateTime,
    Date,
    Custom(String),
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum FunctionCall {
    Uuid,
    Snowflake,
    AutoIncrement,
    Now,
    Env(String),
}

impl FunctionCall {
    pub fn is_func(data: &str) -> bool {
        if let Some((_name, params_and_rest)) = data.split_once('(') { params_and_rest.ends_with(')') } else { false }
    }

    pub fn from_string(data: &str) -> DinocoCompilerResult<Self> {
        if let Some((name, params_with_paren)) = data.split_once('(') {
            if let Some(params) = params_with_paren.strip_suffix(')') {
                match name {
                    "env" => Ok(Self::Env(params.to_string())),
                    "uuid" => Ok(Self::Uuid),
                    "snowflake" => Ok(Self::Snowflake),
                    "now" => Ok(Self::Now),
                    "autoincrement" => Ok(Self::AutoIncrement),
                    _ => Err(vec![DinocoCompilerError::default()]),
                }
            } else {
                Err(vec![DinocoCompilerError::default()])
            }
        } else {
            Err(vec![DinocoCompilerError::default()])
        }
    }
}

impl<'a> ConfigValue<'a> {
    pub fn span(&self) -> Span<'a> {
        match self {
            ConfigValue::String(_, s) => s.clone(),
            ConfigValue::Function { span, .. } => span.clone(),
            ConfigValue::Array(_, s) => s.clone(),
            ConfigValue::Object(_, s) => s.clone(),
            ConfigValue::Comment(s) => s.clone(),
        }
    }
}