1use serde::Serialize;
19use serde::Deserialize;
20use std::collections::HashMap;
21
22#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
23#[derive(Debug, Clone)]
24pub struct DMSCDBRow {
25 pub columns: Vec<String>,
26 pub values: Vec<Option<serde_json::Value>>,
27}
28
29impl DMSCDBRow {
30 pub fn new() -> Self {
31 Self {
32 columns: Vec::new(),
33 values: Vec::new(),
34 }
35 }
36
37 pub fn len(&self) -> usize {
38 self.columns.len()
39 }
40
41 pub fn is_empty(&self) -> bool {
42 self.columns.is_empty()
43 }
44
45 pub fn column_names(&self) -> &[String] {
46 &self.columns
47 }
48
49 pub fn has_column(&self, name: &str) -> bool {
50 self.columns.iter().any(|c| c.eq_ignore_ascii_case(name))
51 }
52
53 pub fn index_of(&self, name: &str) -> Option<usize> {
54 self.columns.iter().position(|c| c.eq_ignore_ascii_case(name))
55 }
56
57 pub fn get<T: for<'de> Deserialize<'de> + Send + Sync>(&self, name: &str) -> Option<T> {
58 let idx = self.index_of(name)?;
59 let value = self.values[idx].as_ref()?;
60 serde_json::from_value(value.clone()).ok()
61 }
62
63 pub fn get_opt<T: for<'de> Deserialize<'de> + Send + Sync>(&self, name: &str) -> Option<Option<T>> {
64 let idx = self.index_of(name)?;
65 if self.values[idx].is_none() {
66 return Some(None);
67 }
68 Some(self.get(name))
69 }
70
71 pub fn get_by_index<T: for<'de> Deserialize<'de> + Send + Sync>(&self, index: usize) -> Option<T> {
72 if index >= self.values.len() {
73 return None;
74 }
75 let value = self.values[index].as_ref()?;
76 serde_json::from_value(value.clone()).ok()
77 }
78
79 pub fn get_raw(&self, name: &str) -> Option<&serde_json::Value> {
80 let idx = self.index_of(name)?;
81 self.values[idx].as_ref()
82 }
83
84 pub fn get_raw_by_index(&self, index: usize) -> Option<&serde_json::Value> {
85 if index >= self.values.len() {
86 return None;
87 }
88 self.values[index].as_ref()
89 }
90
91 pub fn try_get<T: for<'de> Deserialize<'de> + Send + Sync>(&self, name: &str) -> Result<T, crate::core::DMSCError> {
92 self.get(name).ok_or_else(|| crate::core::DMSCError::InvalidInput(format!("Column '{} not found or type mismatch", name)))
93 }
94
95 pub fn get_i32(&self, name: &str) -> Option<i32> {
96 self.get::<i32>(name)
97 }
98
99 pub fn get_i64(&self, name: &str) -> Option<i64> {
100 self.get::<i64>(name)
101 }
102
103 pub fn get_f64(&self, name: &str) -> Option<f64> {
104 self.get::<f64>(name)
105 }
106
107 pub fn get_bool(&self, name: &str) -> Option<bool> {
108 self.get::<bool>(name)
109 }
110
111 pub fn get_string(&self, name: &str) -> Option<String> {
112 self.get::<String>(name)
113 }
114
115 pub fn get_bytes(&self, name: &str) -> Option<Vec<u8>> {
116 self.get::<Vec<u8>>(name)
117 }
118
119 pub fn is_null(&self, name: &str) -> bool {
120 let idx = match self.index_of(name) {
121 Some(i) => i,
122 None => return false,
123 };
124 self.values[idx].is_none()
125 }
126
127 pub fn iter(&self) -> impl Iterator<Item = (usize, &str, Option<&serde_json::Value>)> {
128 self.columns.iter().enumerate().map(|(i, col)| {
129 (i, col.as_str(), self.values[i].as_ref())
130 })
131 }
132
133 pub fn to_map(&self) -> HashMap<String, Option<serde_json::Value>> {
134 let mut map = HashMap::new();
135 for (i, col) in self.columns.iter().enumerate() {
136 map.insert(col.clone(), self.values[i].clone());
137 }
138 map
139 }
140}
141
142#[cfg(feature = "pyo3")]
143#[pyo3::prelude::pymethods]
144impl DMSCDBRow {
145 #[new]
146 fn py_new() -> Self {
147 Self::new()
148 }
149
150 fn get_length(&self) -> usize {
151 self.columns.len()
152 }
153
154 fn is_empty_row(&self) -> bool {
155 self.is_empty()
156 }
157
158 fn check_has_column(&self, name: &str) -> bool {
159 self.has_column(name)
160 }
161
162 fn get_string_value(&self, name: &str) -> Option<String> {
163 self.get_string(name)
164 }
165
166 fn get_column_names(&self) -> Vec<String> {
167 self.columns.clone()
168 }
169
170 fn to_dict(&self) -> HashMap<String, Option<String>> {
171 let mut map = HashMap::new();
172 for (i, col) in self.columns.iter().enumerate() {
173 let value = self.values[i].as_ref().map(|v| v.to_string());
174 map.insert(col.clone(), value);
175 }
176 map
177 }
178}
179
180#[allow(dead_code)]
181pub struct DMSCRowBuilder {
182 row: DMSCDBRow,
183}
184
185#[allow(dead_code)]
186impl DMSCRowBuilder {
187 pub fn new() -> Self {
188 Self { row: DMSCDBRow::new() }
189 }
190
191 pub fn add_column(mut self, name: &str) -> Self {
192 self.row.columns.push(name.to_string());
193 self.row.values.push(None);
194 self
195 }
196
197 pub fn add_null(mut self, name: &str) -> Self {
198 self.row.columns.push(name.to_string());
199 self.row.values.push(None);
200 self
201 }
202
203 pub fn add_value<T: Serialize + Send + Sync>(mut self, name: &str, value: T) -> Self {
204 if let Some(idx) = self.row.index_of(name) {
205 let json = serde_json::to_value(value).unwrap_or_default();
206 self.row.values[idx] = Some(json);
207 } else {
208 let json = serde_json::to_value(value).unwrap_or_default();
209 self.row.columns.push(name.to_string());
210 self.row.values.push(Some(json));
211 }
212 self
213 }
214
215 pub fn build(self) -> DMSCDBRow {
216 self.row
217 }
218}
219
220#[allow(dead_code)]
221impl Default for DMSCRowBuilder {
222 fn default() -> Self {
223 Self::new()
224 }
225}