nodedb_types/columnar/
profile.rs1use std::fmt;
4
5use serde::{Deserialize, Serialize};
6
7use super::schema::StrictSchema;
8
9#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
11#[serde(tag = "profile")]
12pub enum ColumnarProfile {
13 Plain,
15 Timeseries { time_key: String, interval: String },
17 Spatial {
19 geometry_column: String,
20 auto_rtree: bool,
21 auto_geohash: bool,
22 },
23}
24
25impl ColumnarProfile {
26 pub fn as_str(&self) -> &'static str {
27 match self {
28 Self::Plain => "plain",
29 Self::Timeseries { .. } => "timeseries",
30 Self::Spatial { .. } => "spatial",
31 }
32 }
33
34 pub fn is_timeseries(&self) -> bool {
35 matches!(self, Self::Timeseries { .. })
36 }
37
38 pub fn is_spatial(&self) -> bool {
39 matches!(self, Self::Spatial { .. })
40 }
41}
42
43impl fmt::Display for ColumnarProfile {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 f.write_str(self.as_str())
46 }
47}
48
49#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
51#[serde(tag = "mode")]
52pub enum DocumentMode {
53 #[default]
55 Schemaless,
56 Strict(StrictSchema),
58}
59
60impl DocumentMode {
61 pub fn is_schemaless(&self) -> bool {
62 matches!(self, Self::Schemaless)
63 }
64
65 pub fn is_strict(&self) -> bool {
66 matches!(self, Self::Strict(_))
67 }
68
69 pub fn as_str(&self) -> &'static str {
70 match self {
71 Self::Schemaless => "schemaless",
72 Self::Strict(_) => "strict",
73 }
74 }
75
76 pub fn schema(&self) -> Option<&StrictSchema> {
77 match self {
78 Self::Strict(s) => Some(s),
79 Self::Schemaless => None,
80 }
81 }
82}
83
84impl fmt::Display for DocumentMode {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 f.write_str(self.as_str())
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93 use crate::columnar::{ColumnDef, ColumnType};
94
95 #[test]
96 fn columnar_profile_serde_roundtrip() {
97 let profiles = vec![
98 ColumnarProfile::Plain,
99 ColumnarProfile::Timeseries {
100 time_key: "time".into(),
101 interval: "1h".into(),
102 },
103 ColumnarProfile::Spatial {
104 geometry_column: "geom".into(),
105 auto_rtree: true,
106 auto_geohash: true,
107 },
108 ];
109 for p in profiles {
110 let json = serde_json::to_string(&p).unwrap();
111 let back: ColumnarProfile = serde_json::from_str(&json).unwrap();
112 assert_eq!(back, p);
113 }
114 }
115
116 #[test]
117 fn document_mode_default_is_schemaless() {
118 assert!(DocumentMode::default().is_schemaless());
119 }
120
121 #[test]
122 fn document_mode_strict_has_schema() {
123 let schema = StrictSchema::new(vec![ColumnDef::required("id", ColumnType::Int64)]).unwrap();
124 let mode = DocumentMode::Strict(schema.clone());
125 assert!(mode.is_strict());
126 assert_eq!(mode.schema().unwrap(), &schema);
127 }
128
129 #[test]
130 fn document_mode_serde_roundtrip() {
131 let modes = vec![
132 DocumentMode::Schemaless,
133 DocumentMode::Strict(
134 StrictSchema::new(vec![
135 ColumnDef::required("id", ColumnType::Int64).with_primary_key(),
136 ColumnDef::nullable("name", ColumnType::String),
137 ])
138 .unwrap(),
139 ),
140 ];
141 for m in modes {
142 let json = serde_json::to_string(&m).unwrap();
143 let back: DocumentMode = serde_json::from_str(&json).unwrap();
144 assert_eq!(back, m);
145 }
146 }
147}