Skip to main content

hayson_engine/schema/
types.rs

1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6pub const META_PREFIX: &str = "_";
7
8/// The one value a table schema document may declare under `"schemaVersion"`.
9pub const SCHEMA_VERSION: u64 = 1;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
12pub enum FieldType {
13    #[serde(rename = "string")]
14    String,
15    #[serde(rename = "int")]
16    Int,
17    #[serde(rename = "float")]
18    Float,
19    #[serde(rename = "bool")]
20    Bool,
21    #[serde(rename = "timestamp")]
22    Timestamp,
23    #[serde(rename = "enum")]
24    Enum,
25    #[serde(rename = "json")]
26    Json,
27    #[serde(rename = "string[]")]
28    StringArray,
29    #[serde(rename = "int[]")]
30    IntArray,
31    #[serde(rename = "float[]")]
32    FloatArray,
33    #[serde(rename = "bool[]")]
34    BoolArray,
35    #[serde(rename = "markdown")]
36    Markdown,
37    #[serde(rename = "image")]
38    Image,
39}
40
41impl FieldType {
42    pub fn is_array(self) -> bool {
43        matches!(
44            self,
45            FieldType::StringArray | FieldType::IntArray | FieldType::FloatArray | FieldType::BoolArray
46        )
47    }
48
49    pub fn element_type(self) -> Option<FieldType> {
50        match self {
51            FieldType::StringArray => Some(FieldType::String),
52            FieldType::IntArray => Some(FieldType::Int),
53            FieldType::FloatArray => Some(FieldType::Float),
54            FieldType::BoolArray => Some(FieldType::Bool),
55            _ => None,
56        }
57    }
58
59    pub fn is_sortable(self) -> bool {
60        matches!(
61            self,
62            FieldType::String | FieldType::Int | FieldType::Float | FieldType::Timestamp
63        )
64    }
65
66    /// A type whose value can carry its own virtual path. Whether a given field actually does is
67    /// `FieldSpec::has_virtual_path`: a `markdown` field declaring `addressable: false` is of such
68    /// a type and still has no path of its own.
69    pub fn has_virtual_path(self) -> bool {
70        matches!(self, FieldType::Markdown | FieldType::Image)
71    }
72
73    pub fn as_str(self) -> &'static str {
74        match self {
75            FieldType::String => "string",
76            FieldType::Int => "int",
77            FieldType::Float => "float",
78            FieldType::Bool => "bool",
79            FieldType::Timestamp => "timestamp",
80            FieldType::Enum => "enum",
81            FieldType::Json => "json",
82            FieldType::StringArray => "string[]",
83            FieldType::IntArray => "int[]",
84            FieldType::FloatArray => "float[]",
85            FieldType::BoolArray => "bool[]",
86            FieldType::Markdown => "markdown",
87            FieldType::Image => "image",
88        }
89    }
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
93pub enum IndexKind {
94    #[serde(rename = "exact")]
95    Exact,
96    #[serde(rename = "sorted")]
97    Sorted,
98}
99
100#[derive(Debug, Clone, PartialEq)]
101pub enum DefaultValue {
102    Now,
103    Value(Value),
104}
105
106#[derive(Debug, Clone, PartialEq)]
107pub struct FieldSpec {
108    pub field_type: FieldType,
109    pub required: bool,
110    pub unique: bool,
111    pub index: Option<IndexKind>,
112    pub fulltext: bool,
113    /// NOTE: encryption at rest — parsed/rejected today, AES-256-GCM sealing is not wired up yet.
114    pub cypher: bool,
115    pub values: Option<Vec<String>>,
116    pub default: Option<DefaultValue>,
117    /// Whether this field's own value is a link target — `true` for every field, and settable only
118    /// on a `markdown` one, where `addressable: false` means the content still links out but the
119    /// field holds no virtual path of its own and nothing can link to it.
120    pub addressable: bool,
121}
122
123impl FieldSpec {
124    pub fn new(field_type: FieldType) -> Self {
125        FieldSpec {
126            field_type,
127            required: false,
128            unique: false,
129            index: None,
130            fulltext: false,
131            cypher: false,
132            values: None,
133            default: None,
134            addressable: true,
135        }
136    }
137
138    /// Whether this field carries its own engine-managed virtual path — the index a markdown link
139    /// resolves against, and the value uniqueness is enforced on. A `markdown` field declaring
140    /// `addressable: false` answers `false`: it is not a link target, so it needs no path at all.
141    pub fn has_virtual_path(&self) -> bool {
142        self.field_type.has_virtual_path() && self.addressable
143    }
144}
145
146#[derive(Debug, Clone, PartialEq)]
147pub struct TableSchema {
148    pub table: String,
149    pub revision: u32,
150    pub primary_key: String,
151    pub partition_by: Option<String>,
152    pub fields: BTreeMap<String, FieldSpec>,
153}