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::<Option<_>>(pre, col)?.map(Box::new)),
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 => {
133 Value::BigDecimal(res.try_get::<Option<_>>(pre, col)?.map(Box::new))
134 }
135
136 #[cfg(feature = "with-ipnetwork")]
137 ArrayType::IpNetwork => Value::IpNetwork(res.try_get(pre, col)?),
138 })
139}
140
141#[cfg(test)]
142mod test {
143 use super::*;
144 use crate::{QueryResultRow, database::IntoMockRow, dynamic::Entity};
145
146 #[test]
147 fn test_from_query_result() {
148 let result = QueryResult {
149 row: QueryResultRow::Mock(
150 crate::tests_cfg::cake::Model {
151 id: 12,
152 name: "hello".into(),
153 }
154 .into_mock_row(),
155 ),
156 };
157 let model_ty = Entity::from_entity(crate::tests_cfg::cake::Entity).to_model_type();
158 assert_eq!(
159 model_ty,
160 ModelType {
161 fields: vec![
162 FieldType {
163 field: Arc::from("id"),
164 type_: ArrayType::Int,
165 },
166 FieldType {
167 field: Arc::from("name"),
168 type_: ArrayType::String,
169 },
170 ],
171 }
172 );
173 assert_eq!(
174 model_ty.from_query_result(&result, "").unwrap(),
175 Model {
176 fields: vec![
177 FieldValue {
178 field: Arc::from("id"),
179 value: 12i32.into(),
180 },
181 FieldValue {
182 field: Arc::from("name"),
183 value: "hello".into(),
184 }
185 ],
186 }
187 );
188 }
189}