Skip to main content

memstead_schema/
base_metadata.rs

1//! Metadata fields every entity carries regardless of type.
2//!
3//! The loader prepends `type`, `created_date`, `last_modified` and appends
4//! `tags` to each type's declared `metadata_fields`. Authors never write
5//! these in YAML — redeclaring a base key is a load-time error.
6//!
7//! Canonical frontmatter order on write:
8//! `type, created_date, last_modified, <type-specific>, tags`.
9
10use crate::types::{FieldType, Filterable, MetadataFieldDef, Serialization};
11
12/// Fields that appear before any type-specific metadata on disk.
13pub fn prefix_fields() -> Vec<MetadataFieldDef> {
14    vec![
15        MetadataFieldDef {
16            key: "type".to_string(),
17            description: "Entity type discriminator — matches the schema type name.".to_string(),
18            field_type: FieldType::String,
19            default_value: None,
20            enum_values: None,
21            optional: false,
22            init_timestamp: false,
23            auto_timestamp: false,
24            serialization: Serialization::Default,
25            filterable: Filterable::None,
26        },
27        MetadataFieldDef {
28            key: "created_date".to_string(),
29            description: "Date the entity was created — filled by the engine on create."
30                .to_string(),
31            field_type: FieldType::Date,
32            default_value: None,
33            enum_values: None,
34            optional: false,
35            init_timestamp: true,
36            auto_timestamp: false,
37            serialization: Serialization::Default,
38            // Engine-stamped, always a valid ISO date — the canonical
39            // "entities created since X" axis. Range-filterable to match
40            // `last_modified` and the `memstead_search` range_filters example.
41            filterable: Filterable::Range,
42        },
43        MetadataFieldDef {
44            key: "last_modified".to_string(),
45            description: "Date of the most recent edit — filled by the engine on write."
46                .to_string(),
47            field_type: FieldType::Date,
48            default_value: None,
49            enum_values: None,
50            optional: false,
51            init_timestamp: false,
52            auto_timestamp: true,
53            serialization: Serialization::Default,
54            filterable: Filterable::Range,
55        },
56    ]
57}
58
59/// Fields that appear after type-specific metadata on disk.
60pub fn suffix_fields() -> Vec<MetadataFieldDef> {
61    vec![MetadataFieldDef {
62        key: "tags".to_string(),
63        description: "Free-form categorization tags — comma-separated on disk.".to_string(),
64        field_type: FieldType::String,
65        default_value: None,
66        enum_values: None,
67        optional: true,
68        init_timestamp: false,
69        auto_timestamp: false,
70        serialization: Serialization::CsvArray,
71        filterable: Filterable::Equality,
72    }]
73}
74
75/// Every base metadata key. Used by the loader to reject YAML redeclarations
76/// and by JSON-Schema generation to surface the implicit fields.
77pub const BASE_KEYS: &[&str] = &["type", "created_date", "last_modified", "tags"];
78
79pub fn is_base_key(key: &str) -> bool {
80    BASE_KEYS.contains(&key)
81}