audb 0.1.11

AuDB - Compile-time database application framework with gold files
Documentation
//! Abstract Syntax Tree (AST) types for gold files
//!
//! This module defines the AST representation of parsed gold files.
//! The AST is built by the parser from a stream of tokens.

use std::collections::HashMap;

/// Represents a parsed gold file
#[derive(Debug, Clone)]
pub struct GoldFile {
    pub blocks: Vec<Block>,
}

impl GoldFile {
    /// Create a new empty gold file
    pub fn new() -> Self {
        Self { blocks: Vec::new() }
    }

    /// Add a block to the file
    pub fn add_block(&mut self, block: Block) {
        self.blocks.push(block);
    }

    /// Get all blocks of a specific type
    pub fn blocks_of_type(&self, block_type: &str) -> Vec<&Block> {
        self.blocks
            .iter()
            .filter(|b| b.block_type() == block_type)
            .collect()
    }
}

impl Default for GoldFile {
    fn default() -> Self {
        Self::new()
    }
}

/// A block in a gold file
#[derive(Debug, Clone)]
pub enum Block {
    /// Configuration block
    Config(ConfigBlock),

    /// Schema definition block
    Schema(SchemaBlock),

    /// Query block
    Query(QueryBlock),

    /// API endpoint block
    Endpoint(EndpointBlock),

    /// Data/seed block
    Data(DataBlock),

    /// Custom block (for extensions)
    Custom(CustomBlock),
}

impl Block {
    /// Get the block type as a string
    pub fn block_type(&self) -> &str {
        match self {
            Block::Config(_) => "config",
            Block::Schema(_) => "schema",
            Block::Query(_) => "query",
            Block::Endpoint(_) => "endpoint",
            Block::Data(_) => "data",
            Block::Custom(c) => &c.block_type,
        }
    }

    /// Get the block name
    pub fn name(&self) -> Option<&str> {
        match self {
            Block::Config(c) => Some(&c.name),
            Block::Schema(s) => Some(&s.name),
            Block::Query(q) => Some(&q.name),
            Block::Endpoint(e) => Some(&e.path),
            Block::Data(d) => Some(&d.name),
            Block::Custom(c) => c.name.as_deref(),
        }
    }
}

/// Configuration block (config database { ... })
#[derive(Debug, Clone)]
pub struct ConfigBlock {
    pub name: String,
    pub attributes: HashMap<String, Value>,
}

/// Schema definition block (schema User { ... })
#[derive(Debug, Clone)]
pub struct SchemaBlock {
    pub name: String,
    pub format: Option<String>,
    pub fields: Vec<SchemaField>,
    pub content: Option<String>, // For embedded formats (JSON Schema, etc.)
    pub crud: bool,              // Whether to auto-generate CRUD endpoints
}

/// A field in a schema block
#[derive(Debug, Clone)]
pub struct SchemaField {
    pub name: String,
    pub field_type: String,
    pub nullable: bool,
    pub default: Option<Value>,
    pub embedding_annotation: Option<EmbeddingAnnotation>,
}

/// Embedding annotation (@embedding(...))
#[derive(Debug, Clone)]
pub struct EmbeddingAnnotation {
    pub model: String,
    pub source_field: String,
    pub dimension: Option<usize>,
    pub paradigm: Option<String>,
}

/// Query block (query get_user(...) -> ... { ... })
#[derive(Debug, Clone)]
pub struct QueryBlock {
    pub name: String,
    pub params: Vec<Parameter>,
    pub return_type: String,
    pub language: String,
    pub source: String,
}

/// Query parameter
#[derive(Debug, Clone)]
pub struct Parameter {
    pub name: String,
    pub param_type: String,
}

/// Endpoint block (endpoint GET "/api/users" { ... })
#[derive(Debug, Clone)]
pub struct EndpointBlock {
    pub method: String,
    pub path: String,
    pub query: String,
    pub auth: bool,
    pub params: HashMap<String, String>,
}

/// Data/seed block (data users { ... })
#[derive(Debug, Clone)]
pub struct DataBlock {
    pub name: String,
    pub format: String,
    pub content: String,
}

/// Custom block for extensions
#[derive(Debug, Clone)]
pub struct CustomBlock {
    pub block_type: String,
    pub name: Option<String>,
    pub attributes: HashMap<String, Value>,
    pub content: Option<String>,
}

/// Value types in gold files
#[derive(Debug, Clone)]
pub enum Value {
    String(String),
    Integer(i64),
    Float(f64),
    Boolean(bool),
    List(Vec<Value>),
    Map(HashMap<String, Value>),
}

impl Value {
    pub fn as_string(&self) -> Option<&str> {
        match self {
            Value::String(s) => Some(s),
            _ => None,
        }
    }

    pub fn as_int(&self) -> Option<i64> {
        match self {
            Value::Integer(i) => Some(*i),
            _ => None,
        }
    }

    pub fn as_bool(&self) -> Option<bool> {
        match self {
            Value::Boolean(b) => Some(*b),
            _ => None,
        }
    }
}

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

    #[test]
    fn test_gold_file_creation() {
        let file = GoldFile::new();
        assert_eq!(file.blocks.len(), 0);
    }

    #[test]
    fn test_block_type() {
        let config = Block::Config(ConfigBlock {
            name: "database".to_string(),
            attributes: HashMap::new(),
        });
        assert_eq!(config.block_type(), "config");
    }

    #[test]
    fn test_value_types() {
        let str_val = Value::String("test".to_string());
        assert_eq!(str_val.as_string(), Some("test"));

        let int_val = Value::Integer(42);
        assert_eq!(int_val.as_int(), Some(42));

        let bool_val = Value::Boolean(true);
        assert_eq!(bool_val.as_bool(), Some(true));
    }
}