Skip to main content

omgbase_properties/
row.rs

1//! The row shape of `spec/properties` §1 and the enumerations it carries.
2
3use std::fmt;
4
5/// Where a row came from (§3).
6#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
7pub enum Source {
8    Frontmatter,
9    Inline,
10    Computed,
11}
12
13impl Source {
14    /// Every source, in rank order (§5: frontmatter 0, inline 1, computed 2).
15    pub const ALL: [Source; 3] = [Source::Frontmatter, Source::Inline, Source::Computed];
16
17    /// The stored string (`properties.source`).
18    #[must_use]
19    pub const fn as_str(self) -> &'static str {
20        match self {
21            Source::Frontmatter => "frontmatter",
22            Source::Inline => "inline",
23            Source::Computed => "computed",
24        }
25    }
26
27    /// The §5 rank.
28    #[must_use]
29    pub const fn rank(self) -> u8 {
30        match self {
31            Source::Frontmatter => 0,
32            Source::Inline => 1,
33            Source::Computed => 2,
34        }
35    }
36
37    /// Parse the stored string.
38    #[must_use]
39    pub fn parse(s: &str) -> Option<Self> {
40        Self::ALL.into_iter().find(|v| v.as_str() == s)
41    }
42}
43
44impl fmt::Display for Source {
45    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46        f.write_str(self.as_str())
47    }
48}
49
50/// The authored shape (§2.3, §3.2).
51#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
52pub enum Card {
53    Scalar,
54    List,
55}
56
57impl Card {
58    #[must_use]
59    pub const fn as_str(self) -> &'static str {
60        match self {
61            Card::Scalar => "scalar",
62            Card::List => "list",
63        }
64    }
65
66    #[must_use]
67    pub fn parse(s: &str) -> Option<Self> {
68        match s {
69            "scalar" => Some(Card::Scalar),
70            "list" => Some(Card::List),
71            _ => None,
72        }
73    }
74}
75
76impl fmt::Display for Card {
77    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78        f.write_str(self.as_str())
79    }
80}
81
82/// The `type` column (§2.1).
83#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
84pub enum ValueType {
85    String,
86    Number,
87    Bool,
88    Null,
89    Json,
90}
91
92impl ValueType {
93    #[must_use]
94    pub const fn as_str(self) -> &'static str {
95        match self {
96            ValueType::String => "string",
97            ValueType::Number => "number",
98            ValueType::Bool => "bool",
99            ValueType::Null => "null",
100            ValueType::Json => "json",
101        }
102    }
103
104    #[must_use]
105    pub fn parse(s: &str) -> Option<Self> {
106        match s {
107            "string" => Some(ValueType::String),
108            "number" => Some(ValueType::Number),
109            "bool" => Some(ValueType::Bool),
110            "null" => Some(ValueType::Null),
111            "json" => Some(ValueType::Json),
112            _ => None,
113        }
114    }
115}
116
117impl fmt::Display for ValueType {
118    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119        f.write_str(self.as_str())
120    }
121}
122
123/// The typed columns of a row (§2.1): exactly one of the `val_*` columns is
124/// set for `string`/`number`/`bool`/`json`; none for `null`; a range-shaped
125/// string sets `val_text` **and** `val_json` (§2.2).
126#[derive(Clone, Debug, PartialEq)]
127pub struct Typed {
128    pub ty: ValueType,
129    pub val_text: Option<String>,
130    /// May be non-finite (§8): `NaN` stores as `NULL` with `type = number`.
131    pub val_num: Option<f64>,
132    pub val_bool: Option<bool>,
133    /// JSON text.
134    pub val_json: Option<String>,
135}
136
137impl Typed {
138    #[must_use]
139    pub const fn null() -> Self {
140        Self {
141            ty: ValueType::Null,
142            val_text: None,
143            val_num: None,
144            val_bool: None,
145            val_json: None,
146        }
147    }
148
149    #[must_use]
150    pub fn bool(b: bool) -> Self {
151        Self {
152            ty: ValueType::Bool,
153            val_bool: Some(b),
154            ..Self::null()
155        }
156    }
157
158    #[must_use]
159    pub fn number(n: f64) -> Self {
160        Self {
161            ty: ValueType::Number,
162            val_num: Some(n),
163            ..Self::null()
164        }
165    }
166
167    #[must_use]
168    pub fn string(s: impl Into<String>) -> Self {
169        Self {
170            ty: ValueType::String,
171            val_text: Some(s.into()),
172            ..Self::null()
173        }
174    }
175
176    #[must_use]
177    pub fn json(text: impl Into<String>) -> Self {
178        Self {
179            ty: ValueType::Json,
180            val_json: Some(text.into()),
181            ..Self::null()
182        }
183    }
184}
185
186/// A row before it is attributed to a source and a document: what the
187/// flatteners (§2.3, §2.4) and the inline pass (§3.2) produce.
188#[derive(Clone, Debug, PartialEq)]
189pub struct FlatRow {
190    pub key: String,
191    pub card: Card,
192    pub ord: u32,
193    pub typed: Typed,
194}
195
196/// One property row (§1).
197#[derive(Clone, Debug, PartialEq)]
198pub struct PropertyRow {
199    /// `"p_"` + the first 12 hex characters of
200    /// `sha256(doc_id + "|" + source + "|" + key + "|" + ord)`.
201    pub prop_id: String,
202    /// The authoring block for inline rows; `None` for frontmatter and computed.
203    pub block_id: Option<String>,
204    pub source: Source,
205    /// Dotted, flattened; computed keys carry `$`.
206    pub key: String,
207    pub card: Card,
208    /// Position within a multi-value key, from 0; 0 for a scalar.
209    pub ord: u32,
210    pub ty: ValueType,
211    pub val_text: Option<String>,
212    /// May be non-finite (§8).
213    pub val_num: Option<f64>,
214    pub val_bool: Option<bool>,
215    /// JSON text: the `json` value, or the range side channel (§2.2).
216    pub val_json: Option<String>,
217}