ic_dbms_api/dbms/table/
record.rs1use candid::CandidType;
2
3use crate::dbms::table::{ColumnDef, TableSchema};
4use crate::dbms::value::Value;
5use crate::prelude::{Filter, IcDbmsResult};
6
7pub type TableColumns = Vec<(ValuesSource, Vec<(ColumnDef, Value)>)>;
8
9pub fn flatten_table_columns(rows: Vec<TableColumns>) -> Vec<Vec<(ColumnDef, Value)>> {
14 rows.into_iter()
15 .map(|row| {
16 row.into_iter()
17 .filter(|(source, _)| *source == ValuesSource::This)
18 .flat_map(|(_, cols)| cols)
19 .collect()
20 })
21 .collect()
22}
23
24#[derive(Debug, Clone, PartialEq, Eq, Hash)]
26pub enum ValuesSource {
27 This,
29 Foreign { table: String, column: String },
31}
32
33pub trait TableRecord: CandidType + for<'de> candid::Deserialize<'de> {
35 type Schema: TableSchema<Record = Self>;
37
38 fn from_values(values: TableColumns) -> Self;
40
41 fn to_values(&self) -> Vec<(ColumnDef, Value)>;
43}
44
45pub trait InsertRecord: Sized + Clone + CandidType {
47 type Record: TableRecord;
49 type Schema: TableSchema<Record = Self::Record>;
51
52 fn from_values(values: &[(ColumnDef, Value)]) -> IcDbmsResult<Self>;
54
55 fn into_values(self) -> Vec<(ColumnDef, Value)>;
57
58 fn into_record(self) -> Self::Schema;
60}
61
62pub trait UpdateRecord: Sized + CandidType {
64 type Record: TableRecord;
66 type Schema: TableSchema<Record = Self::Record>;
68
69 fn from_values(values: &[(ColumnDef, Value)], where_clause: Option<Filter>) -> Self;
71
72 fn update_values(&self) -> Vec<(ColumnDef, Value)>;
74
75 fn where_clause(&self) -> Option<Filter>;
77}
78
79#[cfg(test)]
80mod test {
81
82 use super::*;
83
84 #[test]
85 fn test_should_create_values_source_this() {
86 let source = ValuesSource::This;
87 assert_eq!(source, ValuesSource::This);
88 }
89
90 #[test]
91 fn test_should_create_values_source_foreign() {
92 let source = ValuesSource::Foreign {
93 table: "users".to_string(),
94 column: "id".to_string(),
95 };
96
97 if let ValuesSource::Foreign { table, column } = source {
98 assert_eq!(table, "users");
99 assert_eq!(column, "id");
100 } else {
101 panic!("expected ValuesSource::Foreign");
102 }
103 }
104
105 #[test]
106 fn test_should_clone_values_source() {
107 let source = ValuesSource::Foreign {
108 table: "posts".to_string(),
109 column: "author_id".to_string(),
110 };
111
112 let cloned = source.clone();
113 assert_eq!(source, cloned);
114 }
115
116 #[test]
117 fn test_should_compare_values_sources() {
118 let source1 = ValuesSource::This;
119 let source2 = ValuesSource::This;
120 let source3 = ValuesSource::Foreign {
121 table: "users".to_string(),
122 column: "id".to_string(),
123 };
124 let source4 = ValuesSource::Foreign {
125 table: "users".to_string(),
126 column: "id".to_string(),
127 };
128 let source5 = ValuesSource::Foreign {
129 table: "posts".to_string(),
130 column: "id".to_string(),
131 };
132
133 assert_eq!(source1, source2);
134 assert_eq!(source3, source4);
135 assert_ne!(source1, source3);
136 assert_ne!(source3, source5);
137 }
138
139 #[test]
140 fn test_should_hash_values_source() {
141 use std::collections::HashSet;
142
143 let mut set = HashSet::new();
144 set.insert(ValuesSource::This);
145 set.insert(ValuesSource::Foreign {
146 table: "users".to_string(),
147 column: "id".to_string(),
148 });
149
150 assert!(set.contains(&ValuesSource::This));
151 assert!(set.contains(&ValuesSource::Foreign {
152 table: "users".to_string(),
153 column: "id".to_string(),
154 }));
155 assert!(!set.contains(&ValuesSource::Foreign {
156 table: "posts".to_string(),
157 column: "id".to_string(),
158 }));
159 }
160
161 #[test]
162 fn test_should_debug_values_source() {
163 let source = ValuesSource::This;
164 let debug_str = format!("{:?}", source);
165 assert_eq!(debug_str, "This");
166
167 let foreign = ValuesSource::Foreign {
168 table: "users".to_string(),
169 column: "id".to_string(),
170 };
171 let foreign_debug = format!("{:?}", foreign);
172 assert!(foreign_debug.contains("Foreign"));
173 assert!(foreign_debug.contains("users"));
174 assert!(foreign_debug.contains("id"));
175 }
176}