hayson-engine 0.17.0

Storage engine for hayson document databases — the embeddable core behind hayson-server, hayson-cli and hayson-browser
Documentation
use std::collections::BTreeMap;

use serde::{Deserialize, Serialize};
use serde_json::Value;

pub const META_PREFIX: &str = "_";

/// The one value a table schema document may declare under `"schemaVersion"`.
pub const SCHEMA_VERSION: u64 = 1;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum FieldType {
    #[serde(rename = "string")]
    String,
    #[serde(rename = "int")]
    Int,
    #[serde(rename = "float")]
    Float,
    #[serde(rename = "bool")]
    Bool,
    #[serde(rename = "timestamp")]
    Timestamp,
    #[serde(rename = "enum")]
    Enum,
    #[serde(rename = "json")]
    Json,
    #[serde(rename = "string[]")]
    StringArray,
    #[serde(rename = "int[]")]
    IntArray,
    #[serde(rename = "float[]")]
    FloatArray,
    #[serde(rename = "bool[]")]
    BoolArray,
    #[serde(rename = "markdown")]
    Markdown,
    #[serde(rename = "image")]
    Image,
}

impl FieldType {
    pub fn is_array(self) -> bool {
        matches!(
            self,
            FieldType::StringArray | FieldType::IntArray | FieldType::FloatArray | FieldType::BoolArray
        )
    }

    pub fn element_type(self) -> Option<FieldType> {
        match self {
            FieldType::StringArray => Some(FieldType::String),
            FieldType::IntArray => Some(FieldType::Int),
            FieldType::FloatArray => Some(FieldType::Float),
            FieldType::BoolArray => Some(FieldType::Bool),
            _ => None,
        }
    }

    pub fn is_sortable(self) -> bool {
        matches!(
            self,
            FieldType::String | FieldType::Int | FieldType::Float | FieldType::Timestamp
        )
    }

    /// A type whose value can carry its own virtual path. Whether a given field actually does is
    /// `FieldSpec::has_virtual_path`: a `markdown` field declaring `addressable: false` is of such
    /// a type and still has no path of its own.
    pub fn has_virtual_path(self) -> bool {
        matches!(self, FieldType::Markdown | FieldType::Image)
    }

    pub fn as_str(self) -> &'static str {
        match self {
            FieldType::String => "string",
            FieldType::Int => "int",
            FieldType::Float => "float",
            FieldType::Bool => "bool",
            FieldType::Timestamp => "timestamp",
            FieldType::Enum => "enum",
            FieldType::Json => "json",
            FieldType::StringArray => "string[]",
            FieldType::IntArray => "int[]",
            FieldType::FloatArray => "float[]",
            FieldType::BoolArray => "bool[]",
            FieldType::Markdown => "markdown",
            FieldType::Image => "image",
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum IndexKind {
    #[serde(rename = "exact")]
    Exact,
    #[serde(rename = "sorted")]
    Sorted,
}

#[derive(Debug, Clone, PartialEq)]
pub enum DefaultValue {
    Now,
    Value(Value),
}

#[derive(Debug, Clone, PartialEq)]
pub struct FieldSpec {
    pub field_type: FieldType,
    pub required: bool,
    pub unique: bool,
    pub index: Option<IndexKind>,
    pub fulltext: bool,
    /// NOTE: encryption at rest — parsed/rejected today, AES-256-GCM sealing is not wired up yet.
    pub cypher: bool,
    pub values: Option<Vec<String>>,
    pub default: Option<DefaultValue>,
    /// Whether this field's own value is a link target — `true` for every field, and settable only
    /// on a `markdown` one, where `addressable: false` means the content still links out but the
    /// field holds no virtual path of its own and nothing can link to it.
    pub addressable: bool,
}

impl FieldSpec {
    pub fn new(field_type: FieldType) -> Self {
        FieldSpec {
            field_type,
            required: false,
            unique: false,
            index: None,
            fulltext: false,
            cypher: false,
            values: None,
            default: None,
            addressable: true,
        }
    }

    /// Whether this field carries its own engine-managed virtual path — the index a markdown link
    /// resolves against, and the value uniqueness is enforced on. A `markdown` field declaring
    /// `addressable: false` answers `false`: it is not a link target, so it needs no path at all.
    pub fn has_virtual_path(&self) -> bool {
        self.field_type.has_virtual_path() && self.addressable
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct TableSchema {
    pub table: String,
    pub revision: u32,
    pub primary_key: String,
    pub partition_by: Option<String>,
    pub fields: BTreeMap<String, FieldSpec>,
}