use crate::{DbErr, QueryResult};
use sea_query::{ArrayType, DynIden, Value};
use std::sync::Arc;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ModelType {
pub fields: Vec<FieldType>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct FieldType {
pub(super) field: Arc<str>,
pub(super) type_: ArrayType,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Model {
pub fields: Vec<FieldValue>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct FieldValue {
pub(super) field: Arc<str>,
pub value: Value,
}
impl FieldType {
pub fn new(iden: DynIden, type_: ArrayType) -> Self {
Self {
field: Arc::from(iden.inner()),
type_,
}
}
pub fn field(&self) -> &str {
&self.field
}
}
impl FieldValue {
pub fn field(&self) -> &str {
&self.field
}
}
impl ModelType {
pub fn from_query_result(&self, res: &QueryResult, pre: &str) -> Result<Model, DbErr> {
let mut fields = Vec::new();
for f in self.fields.iter() {
fields.push(FieldValue {
field: f.field.clone(),
value: try_get(res, pre, f.field(), &f.type_)?,
});
}
Ok(Model { fields })
}
}
impl Model {
pub fn try_get(&self, col: &str) -> Result<&Value, DbErr> {
for field in &self.fields {
if field.field() == col {
return Ok(&field.value);
}
}
Err(DbErr::Type(format!("{col} not exist")))
}
}
fn try_get(res: &QueryResult, pre: &str, col: &str, ty: &ArrayType) -> Result<Value, DbErr> {
Ok(match ty {
ArrayType::Bool => Value::Bool(res.try_get(pre, col)?),
ArrayType::TinyInt => Value::TinyInt(res.try_get(pre, col)?),
ArrayType::SmallInt => Value::SmallInt(res.try_get(pre, col)?),
ArrayType::Int => Value::Int(res.try_get(pre, col)?),
ArrayType::BigInt => Value::BigInt(res.try_get(pre, col)?),
ArrayType::TinyUnsigned => Value::TinyUnsigned(res.try_get(pre, col)?),
ArrayType::SmallUnsigned => Value::SmallUnsigned(res.try_get(pre, col)?),
ArrayType::Unsigned => Value::Unsigned(res.try_get(pre, col)?),
ArrayType::BigUnsigned => Value::BigUnsigned(res.try_get(pre, col)?),
ArrayType::Float => Value::Float(res.try_get(pre, col)?),
ArrayType::Double => Value::Double(res.try_get(pre, col)?),
ArrayType::String => Value::String(res.try_get(pre, col)?),
ArrayType::Enum(type_name) => {
let type_name = type_name.as_ref().clone();
match res.try_get::<Option<String>>(pre, col)? {
Some(value) => {
Value::Enum(sea_query::OptionEnum::Some(Box::new(sea_query::Enum {
type_name,
value: value.into(),
})))
}
None => Value::Enum(sea_query::OptionEnum::None(type_name)),
}
}
ArrayType::Char => return Err(DbErr::Type("Unsupported type: char".into())),
ArrayType::Bytes => Value::Bytes(res.try_get(pre, col)?),
#[cfg(feature = "with-json")]
ArrayType::Json => Value::Json(res.try_get::<Option<_>>(pre, col)?.map(Box::new)),
#[cfg(feature = "with-chrono")]
ArrayType::ChronoDate => Value::ChronoDate(res.try_get(pre, col)?),
#[cfg(feature = "with-chrono")]
ArrayType::ChronoTime => Value::ChronoTime(res.try_get(pre, col)?),
#[cfg(feature = "with-chrono")]
ArrayType::ChronoDateTime => Value::ChronoDateTime(res.try_get(pre, col)?),
#[cfg(feature = "with-chrono")]
ArrayType::ChronoDateTimeUtc => Value::ChronoDateTimeUtc(res.try_get(pre, col)?),
#[cfg(feature = "with-chrono")]
ArrayType::ChronoDateTimeLocal => Value::ChronoDateTimeLocal(res.try_get(pre, col)?),
#[cfg(feature = "with-chrono")]
ArrayType::ChronoDateTimeWithTimeZone => {
Value::ChronoDateTimeWithTimeZone(res.try_get(pre, col)?)
}
#[cfg(feature = "with-time")]
ArrayType::TimeDate => Value::TimeDate(res.try_get(pre, col)?),
#[cfg(feature = "with-time")]
ArrayType::TimeTime => Value::TimeTime(res.try_get(pre, col)?),
#[cfg(feature = "with-time")]
ArrayType::TimeDateTime => Value::TimeDateTime(res.try_get(pre, col)?),
#[cfg(feature = "with-time")]
ArrayType::TimeDateTimeWithTimeZone => {
Value::TimeDateTimeWithTimeZone(res.try_get(pre, col)?)
}
#[cfg(feature = "with-uuid")]
ArrayType::Uuid => Value::Uuid(res.try_get(pre, col)?),
#[cfg(feature = "with-rust_decimal")]
ArrayType::Decimal => Value::Decimal(res.try_get(pre, col)?),
#[cfg(feature = "with-bigdecimal")]
ArrayType::BigDecimal => {
Value::BigDecimal(res.try_get::<Option<_>>(pre, col)?.map(Box::new))
}
#[cfg(feature = "with-ipnetwork")]
ArrayType::IpNetwork => Value::IpNetwork(res.try_get(pre, col)?),
#[cfg(feature = "with-mac_address")]
ArrayType::MacAddress => Value::MacAddress(res.try_get(pre, col)?),
})
}
#[cfg(test)]
mod test {
use super::*;
use crate::{QueryResultRow, database::IntoMockRow, dynamic::Entity};
#[test]
fn test_from_query_result() {
let result = QueryResult {
row: QueryResultRow::Mock(
crate::tests_cfg::cake::Model {
id: 12,
name: "hello".into(),
}
.into_mock_row(),
),
};
let model_ty = Entity::from_entity(crate::tests_cfg::cake::Entity).to_model_type();
assert_eq!(
model_ty,
ModelType {
fields: vec![
FieldType {
field: Arc::from("id"),
type_: ArrayType::Int,
},
FieldType {
field: Arc::from("name"),
type_: ArrayType::String,
},
],
}
);
assert_eq!(
model_ty.from_query_result(&result, "").unwrap(),
Model {
fields: vec![
FieldValue {
field: Arc::from("id"),
value: 12i32.into(),
},
FieldValue {
field: Arc::from("name"),
value: "hello".into(),
}
],
}
);
}
}