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::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}