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::Enum(type_name) => {
86 let type_name = type_name.as_ref().clone();
87 match res.try_get::<Option<String>>(pre, col)? {
88 Some(value) => {
89 Value::Enum(sea_query::OptionEnum::Some(Box::new(sea_query::Enum {
90 type_name,
91 value: value.into(),
92 })))
93 }
94 None => Value::Enum(sea_query::OptionEnum::None(type_name)),
95 }
96 }
97 ArrayType::Char => return Err(DbErr::Type("Unsupported type: char".into())),
98 ArrayType::Bytes => Value::Bytes(res.try_get(pre, col)?),
99
100 #[cfg(feature = "with-json")]
101 ArrayType::Json => Value::Json(res.try_get::<Option<_>>(pre, col)?.map(Box::new)),
102
103 #[cfg(feature = "with-chrono")]
104 ArrayType::ChronoDate => Value::ChronoDate(res.try_get(pre, col)?),
105
106 #[cfg(feature = "with-chrono")]
107 ArrayType::ChronoTime => Value::ChronoTime(res.try_get(pre, col)?),
108
109 #[cfg(feature = "with-chrono")]
110 ArrayType::ChronoDateTime => Value::ChronoDateTime(res.try_get(pre, col)?),
111
112 #[cfg(feature = "with-chrono")]
113 ArrayType::ChronoDateTimeUtc => Value::ChronoDateTimeUtc(res.try_get(pre, col)?),
114
115 #[cfg(feature = "with-chrono")]
116 ArrayType::ChronoDateTimeLocal => Value::ChronoDateTimeLocal(res.try_get(pre, col)?),
117
118 #[cfg(feature = "with-chrono")]
119 ArrayType::ChronoDateTimeWithTimeZone => {
120 Value::ChronoDateTimeWithTimeZone(res.try_get(pre, col)?)
121 }
122
123 #[cfg(feature = "with-time")]
124 ArrayType::TimeDate => Value::TimeDate(res.try_get(pre, col)?),
125
126 #[cfg(feature = "with-time")]
127 ArrayType::TimeTime => Value::TimeTime(res.try_get(pre, col)?),
128
129 #[cfg(feature = "with-time")]
130 ArrayType::TimeDateTime => Value::TimeDateTime(res.try_get(pre, col)?),
131
132 #[cfg(feature = "with-time")]
133 ArrayType::TimeDateTimeWithTimeZone => {
134 Value::TimeDateTimeWithTimeZone(res.try_get(pre, col)?)
135 }
136
137 #[cfg(feature = "with-uuid")]
138 ArrayType::Uuid => Value::Uuid(res.try_get(pre, col)?),
139
140 #[cfg(feature = "with-rust_decimal")]
141 ArrayType::Decimal => Value::Decimal(res.try_get(pre, col)?),
142
143 #[cfg(feature = "with-bigdecimal")]
144 ArrayType::BigDecimal => {
145 Value::BigDecimal(res.try_get::<Option<_>>(pre, col)?.map(Box::new))
146 }
147
148 #[cfg(feature = "with-ipnetwork")]
149 ArrayType::IpNetwork => Value::IpNetwork(res.try_get(pre, col)?),
150
151 #[cfg(feature = "with-mac_address")]
152 ArrayType::MacAddress => Value::MacAddress(res.try_get(pre, col)?),
153 })
154}
155
156#[cfg(test)]
157mod test {
158 use super::*;
159 use crate::{QueryResultRow, database::IntoMockRow, dynamic::Entity};
160
161 #[test]
162 fn test_from_query_result() {
163 let result = QueryResult {
164 row: QueryResultRow::Mock(
165 crate::tests_cfg::cake::Model {
166 id: 12,
167 name: "hello".into(),
168 }
169 .into_mock_row(),
170 ),
171 };
172 let model_ty = Entity::from_entity(crate::tests_cfg::cake::Entity).to_model_type();
173 assert_eq!(
174 model_ty,
175 ModelType {
176 fields: vec![
177 FieldType {
178 field: Arc::from("id"),
179 type_: ArrayType::Int,
180 },
181 FieldType {
182 field: Arc::from("name"),
183 type_: ArrayType::String,
184 },
185 ],
186 }
187 );
188 assert_eq!(
189 model_ty.from_query_result(&result, "").unwrap(),
190 Model {
191 fields: vec![
192 FieldValue {
193 field: Arc::from("id"),
194 value: 12i32.into(),
195 },
196 FieldValue {
197 field: Arc::from("name"),
198 value: "hello".into(),
199 }
200 ],
201 }
202 );
203 }
204}