1use crate::{DbErr, QueryResult};
2use sea_query::{ArrayType, DynIden, Value};
3use std::sync::Arc;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub struct ModelType {
7 pub fields: Vec<FieldType>,
8}
9
10#[derive(Debug, Clone, PartialEq, Eq, Hash)]
11pub struct FieldType {
12 pub(super) field: Arc<str>,
13 pub(super) type_: ArrayType,
14}
15
16#[derive(Debug, Clone, PartialEq)]
17pub struct Model {
18 pub fields: Vec<FieldValue>,
19}
20
21#[derive(Debug, Clone, PartialEq)]
22pub struct FieldValue {
23 pub(super) field: Arc<str>,
24 pub value: Value,
25}
26
27impl FieldType {
28 pub fn new(iden: DynIden, type_: ArrayType) -> Self {
29 Self {
30 field: Arc::from(iden.inner()),
31 type_,
32 }
33 }
34
35 pub fn field(&self) -> &str {
36 &self.field
37 }
38}
39
40impl FieldValue {
41 pub fn field(&self) -> &str {
42 &self.field
43 }
44}
45
46impl ModelType {
47 pub fn from_query_result(&self, res: &QueryResult, pre: &str) -> Result<Model, DbErr> {
48 let mut fields = Vec::new();
49 for f in self.fields.iter() {
50 fields.push(FieldValue {
51 field: f.field.clone(),
52 value: try_get(res, pre, f.field(), &f.type_)?,
53 });
54 }
55 Ok(Model { fields })
56 }
57}
58
59impl Model {
60 pub fn try_get(&self, col: &str) -> Result<&Value, DbErr> {
61 for field in &self.fields {
62 if field.field() == col {
63 return Ok(&field.value);
64 }
65 }
66 Err(DbErr::Type(format!("{col} not exist")))
67 }
68}
69
70fn try_get(res: &QueryResult, pre: &str, col: &str, ty: &ArrayType) -> Result<Value, DbErr> {
71 Ok(match ty {
73 ArrayType::Bool => Value::Bool(res.try_get(pre, col)?),
74 ArrayType::TinyInt => Value::TinyInt(res.try_get(pre, col)?),
75 ArrayType::SmallInt => Value::SmallInt(res.try_get(pre, col)?),
76 ArrayType::Int => Value::Int(res.try_get(pre, col)?),
77 ArrayType::BigInt => Value::BigInt(res.try_get(pre, col)?),
78 ArrayType::TinyUnsigned => Value::TinyUnsigned(res.try_get(pre, col)?),
79 ArrayType::SmallUnsigned => Value::SmallUnsigned(res.try_get(pre, col)?),
80 ArrayType::Unsigned => Value::Unsigned(res.try_get(pre, col)?),
81 ArrayType::BigUnsigned => Value::BigUnsigned(res.try_get(pre, col)?),
82 ArrayType::Float => Value::Float(res.try_get(pre, col)?),
83 ArrayType::Double => Value::Double(res.try_get(pre, col)?),
84 ArrayType::String => Value::String(res.try_get(pre, col)?),
85 ArrayType::Char => return Err(DbErr::Type("Unsupported type: char".into())),
86 ArrayType::Bytes => Value::Bytes(res.try_get(pre, col)?),
87
88 #[cfg(feature = "with-json")]
89 ArrayType::Json => Value::Json(res.try_get(pre, col)?),
90
91 #[cfg(feature = "with-chrono")]
92 ArrayType::ChronoDate => Value::ChronoDate(res.try_get(pre, col)?),
93
94 #[cfg(feature = "with-chrono")]
95 ArrayType::ChronoTime => Value::ChronoTime(res.try_get(pre, col)?),
96
97 #[cfg(feature = "with-chrono")]
98 ArrayType::ChronoDateTime => Value::ChronoDateTime(res.try_get(pre, col)?),
99
100 #[cfg(feature = "with-chrono")]
101 ArrayType::ChronoDateTimeUtc => Value::ChronoDateTimeUtc(res.try_get(pre, col)?),
102
103 #[cfg(feature = "with-chrono")]
104 ArrayType::ChronoDateTimeLocal => Value::ChronoDateTimeLocal(res.try_get(pre, col)?),
105
106 #[cfg(feature = "with-chrono")]
107 ArrayType::ChronoDateTimeWithTimeZone => {
108 Value::ChronoDateTimeWithTimeZone(res.try_get(pre, col)?)
109 }
110
111 #[cfg(feature = "with-time")]
112 ArrayType::TimeDate => Value::TimeDate(res.try_get(pre, col)?),
113
114 #[cfg(feature = "with-time")]
115 ArrayType::TimeTime => Value::TimeTime(res.try_get(pre, col)?),
116
117 #[cfg(feature = "with-time")]
118 ArrayType::TimeDateTime => Value::TimeDateTime(res.try_get(pre, col)?),
119
120 #[cfg(feature = "with-time")]
121 ArrayType::TimeDateTimeWithTimeZone => {
122 Value::TimeDateTimeWithTimeZone(res.try_get(pre, col)?)
123 }
124
125 #[cfg(feature = "with-uuid")]
126 ArrayType::Uuid => Value::Uuid(res.try_get(pre, col)?),
127
128 #[cfg(feature = "with-rust_decimal")]
129 ArrayType::Decimal => Value::Decimal(res.try_get(pre, col)?),
130
131 #[cfg(feature = "with-bigdecimal")]
132 ArrayType::BigDecimal => Value::BigDecimal(res.try_get::<Option<_>>(pre, col)?),
133
134 #[cfg(feature = "with-ipnetwork")]
135 ArrayType::IpNetwork => Value::IpNetwork(res.try_get(pre, col)?),
136
137 ArrayType::Enum(_) => return Err(DbErr::Type("Unsupported type: enum".into())),
139 })
140}
141
142#[cfg(test)]
143mod test {
144 use super::*;
145 use crate::{QueryResultRow, database::IntoMockRow, dynamic::Entity};
146
147 #[test]
148 fn test_from_query_result() {
149 let result = QueryResult {
150 row: QueryResultRow::Mock(
151 crate::tests_cfg::cake::Model {
152 id: 12,
153 name: "hello".into(),
154 }
155 .into_mock_row(),
156 ),
157 };
158 let model_ty = Entity::from_entity(crate::tests_cfg::cake::Entity).to_model_type();
159 assert_eq!(
160 model_ty,
161 ModelType {
162 fields: vec![
163 FieldType {
164 field: Arc::from("id"),
165 type_: ArrayType::Int,
166 },
167 FieldType {
168 field: Arc::from("name"),
169 type_: ArrayType::String,
170 },
171 ],
172 }
173 );
174 assert_eq!(
175 model_ty.from_query_result(&result, "").unwrap(),
176 Model {
177 fields: vec![
178 FieldValue {
179 field: Arc::from("id"),
180 value: 12i32.into(),
181 },
182 FieldValue {
183 field: Arc::from("name"),
184 value: "hello".into(),
185 }
186 ],
187 }
188 );
189 }
190}