Skip to main content

sea_orm/dynamic/
model.rs

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    // how to handle postgres-array?
72    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}