Skip to content

Commit

Permalink
FEAT: add trait ArelAttributeFromRow
Browse files Browse the repository at this point in the history
  • Loading branch information
wuyuedefeng committed Aug 30, 2023
1 parent 67c17b6 commit a5d2f60
Show file tree
Hide file tree
Showing 9 changed files with 289 additions and 11 deletions.
6 changes: 4 additions & 2 deletions arel-macros/src/arel/arel_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,13 @@ fn impl_trait_sqlx_from_row(input: &crate::Input) -> syn::Result<proc_macro2::To
// arel(rename="x")
if let Some(rename) = crate::get_path_value(input, Some(&field), "rename", None)? {
build_assign_clauses.push(quote::quote!(
user.#ident = row.try_get::<#option_type, _>(#rename).unwrap_or_default();
// user.#ident = row.try_get::<#option_type, _>(#rename).unwrap_or_default();
user.#ident = <#option_type as arel::ArelAttributeFromRow>::from_row(&row, #rename).unwrap_or_default();
));
} else {
build_assign_clauses.push(quote::quote!(
user.#ident = row.try_get::<#option_type, _>(stringify!(#ident)).unwrap_or_default();
// user.#ident = row.try_get::<#option_type, _>(stringify!(#ident)).unwrap_or_default();
user.#ident = <#option_type as arel::ArelAttributeFromRow>::from_row(&row, stringify!(#ident)).unwrap_or_default();
));
}
}
Expand Down
6 changes: 4 additions & 2 deletions arel-macros/src/arel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,13 @@ fn impl_trait_sqlx_from_row(input: &crate::Input) -> syn::Result<proc_macro2::To
// arel(rename="x")
if let Some(rename) = crate::get_path_value(input, Some(&field), "rename", None)? {
build_assign_clauses.push(quote::quote!(
user.#ident = row.try_get::<#r#type, _>(#rename).unwrap_or_default();
// user.#ident = row.try_get::<#r#type, _>(#rename).unwrap_or_default();
user.#ident = <#r#type as arel::ArelAttributeFromRow>::from_row(&row, #rename).unwrap_or_default();
));
} else {
build_assign_clauses.push(quote::quote!(
user.#ident = row.try_get::<#r#type, _>(stringify!(#ident)).unwrap_or_default();
// user.#ident = row.try_get::<#r#type, _>(stringify!(#ident)).unwrap_or_default();
user.#ident = <#r#type as arel::ArelAttributeFromRow>::from_row(&row, stringify!(#ident)).unwrap_or_default();
));
}
}
Expand Down
33 changes: 33 additions & 0 deletions example/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,36 @@
use arel::prelude::*;

#[derive(Debug, Clone, PartialEq)]
pub enum Gender {
Unknown = 0,
Male = 1,
Female = 2,
}
impl ArelAttributeFromRow for Gender {
fn from_row<'r, I>(row: &'r arel::DatabaseRow, index: I) -> sqlx::Result<Self, sqlx::Error>
where
Self: Sized,
I: sqlx::ColumnIndex<arel::DatabaseRow>,
{
let v: u8 = row.try_get(index)?;
let ret = match v {
1 => Gender::Male,
2 => Gender::Female,
_ => Gender::Unknown,
};
Ok(ret)
}
}
impl From<Gender> for arel::Value {
fn from(value: Gender) -> Self {
match value {
Gender::Male => 1.into(),
Gender::Female => 2.into(),
_ => 0.into(),
}
}
}

#[arel(table_name = "user")]
#[allow(dead_code)]
struct User {
Expand All @@ -8,6 +39,7 @@ struct User {
name: String,
#[arel(rename = "type")]
r#type: String,
gender: Option<Gender>,
desc: Option<String>,
done: Option<bool>,
lock_version: Option<i32>,
Expand All @@ -23,6 +55,7 @@ async fn init_db() -> anyhow::Result<()> {
id INTEGER PRIMARY KEY NOT NULL,
name VARCHAR(255),
type VARCHAR(255),
gender INT(1) NOT NULL DEFAULT 0,
desc TEXT,
done BOOLEAN NOT NULL DEFAULT 0,
lock_version INT(11) NOT NULL DEFAULT 0,
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ pub mod traits;
pub mod value;
pub mod visitor;

pub use crate::traits::{Arel, ArelPersisted, SuperArel};
pub use crate::traits::{Arel, ArelAttributeFromRow, ArelPersisted, SuperArel};
pub use bytes::Bytes;
pub use sql::Sql;
pub use value::{ActiveValue, Value};
Expand Down
2 changes: 1 addition & 1 deletion src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ pub use crate::arel;
pub use crate::statements::ArelStatement;
pub use crate::Set;
// pub use crate::{ActiveValue, Value};
pub use crate::{Arel, ArelPersisted, SuperArel};
pub use crate::{Arel, ArelAttributeFromRow, ArelPersisted, SuperArel};
200 changes: 200 additions & 0 deletions src/traits/arel_attribute_from_row.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
use sqlx::Row;

pub trait ArelAttributeFromRow {
fn from_row<'r, I>(row: &'r crate::DatabaseRow, index: I) -> sqlx::Result<Self, sqlx::Error>
where
Self: Sized,
I: sqlx::ColumnIndex<crate::DatabaseRow>;
}

impl ArelAttributeFromRow for bool {
fn from_row<'r, I>(row: &'r crate::DatabaseRow, index: I) -> sqlx::Result<Self, sqlx::Error>
where
Self: Sized,
I: sqlx::ColumnIndex<crate::DatabaseRow>,
{
row.try_get(index)
}
}

impl ArelAttributeFromRow for i8 {
fn from_row<'r, I>(row: &'r crate::DatabaseRow, index: I) -> sqlx::Result<Self, sqlx::Error>
where
Self: Sized,
I: sqlx::ColumnIndex<crate::DatabaseRow>,
{
row.try_get(index)
}
}

impl ArelAttributeFromRow for i16 {
fn from_row<'r, I>(row: &'r crate::DatabaseRow, index: I) -> sqlx::Result<Self, sqlx::Error>
where
Self: Sized,
I: sqlx::ColumnIndex<crate::DatabaseRow>,
{
row.try_get(index)
}
}

impl ArelAttributeFromRow for i32 {
fn from_row<'r, I>(row: &'r crate::DatabaseRow, index: I) -> sqlx::Result<Self, sqlx::Error>
where
Self: Sized,
I: sqlx::ColumnIndex<crate::DatabaseRow>,
{
row.try_get(index)
}
}

impl ArelAttributeFromRow for i64 {
fn from_row<'r, I>(row: &'r crate::DatabaseRow, index: I) -> sqlx::Result<Self, sqlx::Error>
where
Self: Sized,
I: sqlx::ColumnIndex<crate::DatabaseRow>,
{
row.try_get(index)
}
}

impl ArelAttributeFromRow for u8 {
fn from_row<'r, I>(row: &'r crate::DatabaseRow, index: I) -> sqlx::Result<Self, sqlx::Error>
where
Self: Sized,
I: sqlx::ColumnIndex<crate::DatabaseRow>,
{
row.try_get(index)
}
}

impl ArelAttributeFromRow for u16 {
fn from_row<'r, I>(row: &'r crate::DatabaseRow, index: I) -> sqlx::Result<Self, sqlx::Error>
where
Self: Sized,
I: sqlx::ColumnIndex<crate::DatabaseRow>,
{
row.try_get(index)
}
}

impl ArelAttributeFromRow for u32 {
fn from_row<'r, I>(row: &'r crate::DatabaseRow, index: I) -> sqlx::Result<Self, sqlx::Error>
where
Self: Sized,
I: sqlx::ColumnIndex<crate::DatabaseRow>,
{
row.try_get(index)
}
}

impl ArelAttributeFromRow for u64 {
fn from_row<'r, I>(row: &'r crate::DatabaseRow, index: I) -> sqlx::Result<Self, sqlx::Error>
where
Self: Sized,
I: sqlx::ColumnIndex<crate::DatabaseRow>,
{
match row.try_get::<u32, _>(index) {
Ok(v) => Ok(v as u64),
Err(e) => Err(e),
}
}
}

impl ArelAttributeFromRow for f32 {
fn from_row<'r, I>(row: &'r crate::DatabaseRow, index: I) -> sqlx::Result<Self, sqlx::Error>
where
Self: Sized,
I: sqlx::ColumnIndex<crate::DatabaseRow>,
{
row.try_get(index)
}
}

impl ArelAttributeFromRow for f64 {
fn from_row<'r, I>(row: &'r crate::DatabaseRow, index: I) -> sqlx::Result<Self, sqlx::Error>
where
Self: Sized,
I: sqlx::ColumnIndex<crate::DatabaseRow>,
{
row.try_get(index)
}
}

impl ArelAttributeFromRow for char {
fn from_row<'r, I>(row: &'r crate::DatabaseRow, index: I) -> sqlx::Result<Self, sqlx::Error>
where
Self: Sized,
I: sqlx::ColumnIndex<crate::DatabaseRow>,
{
match row.try_get::<u8, _>(index) {
Ok(v) => Ok(v as char),
Err(e) => Err(e),
}
}
}

impl ArelAttributeFromRow for String {
fn from_row<'r, I>(row: &'r crate::DatabaseRow, index: I) -> sqlx::Result<Self, sqlx::Error>
where
Self: Sized,
I: sqlx::ColumnIndex<crate::DatabaseRow>,
{
row.try_get(index)
}
}

impl ArelAttributeFromRow for bytes::Bytes {
fn from_row<'r, I>(row: &'r crate::DatabaseRow, index: I) -> sqlx::Result<Self, sqlx::Error>
where
Self: Sized,
I: sqlx::ColumnIndex<crate::DatabaseRow>,
{
match row.try_get::<Vec<u8>, _>(index) {
Ok(v) => Ok(bytes::Bytes::from(v)),
Err(e) => Err(e),
}
}
}

impl ArelAttributeFromRow for serde_json::Value {
fn from_row<'r, I>(row: &'r crate::DatabaseRow, index: I) -> sqlx::Result<Self, sqlx::Error>
where
Self: Sized,
I: sqlx::ColumnIndex<crate::DatabaseRow>,
{
row.try_get(index)
}
}

impl ArelAttributeFromRow for chrono::DateTime<chrono::Utc> {
fn from_row<'r, I>(row: &'r crate::DatabaseRow, index: I) -> sqlx::Result<Self, sqlx::Error>
where
Self: Sized,
I: sqlx::ColumnIndex<crate::DatabaseRow>,
{
row.try_get(index)
}
}

impl ArelAttributeFromRow for chrono::DateTime<chrono::FixedOffset> {
fn from_row<'r, I>(row: &'r crate::DatabaseRow, index: I) -> sqlx::Result<Self, sqlx::Error>
where
Self: Sized,
I: sqlx::ColumnIndex<crate::DatabaseRow>,
{
row.try_get(index)
}
}

impl<T: ArelAttributeFromRow> ArelAttributeFromRow for Option<T> {
fn from_row<'r, I>(row: &'r crate::DatabaseRow, index: I) -> sqlx::Result<Self, sqlx::Error>
where
Self: Sized,
I: sqlx::ColumnIndex<crate::DatabaseRow>,
{
match <T as ArelAttributeFromRow>::from_row(row, index) {
Ok(v) => Ok(Some(v)),
Err(_) => Ok(None),
}
}
}
4 changes: 4 additions & 0 deletions src/traits/arel_persisted.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub trait ArelPersisted {
fn set_persisted(&mut self, persisted: bool);
fn persited(&self) -> bool;
}
11 changes: 6 additions & 5 deletions src/traits.rs → src/traits/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
mod arel_attribute_from_row;
mod arel_persisted;

pub use arel_attribute_from_row::ArelAttributeFromRow;
pub use arel_persisted::ArelPersisted;

use std::borrow::Cow;
use std::future::Future;
use std::pin::Pin;
Expand Down Expand Up @@ -119,11 +125,6 @@ pub trait Arel: SuperArel + Sized {
}
}

pub trait ArelPersisted {
fn set_persisted(&mut self, persisted: bool);
fn persited(&self) -> bool;
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
36 changes: 36 additions & 0 deletions tests/sqlite_sqlx/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,36 @@
use arel::prelude::*;

#[derive(Debug, Clone, PartialEq)]
pub enum Gender {
Unknown = 0,
Male = 1,
Female = 2,
}
impl ArelAttributeFromRow for Gender {
fn from_row<'r, I>(row: &'r arel::DatabaseRow, index: I) -> sqlx::Result<Self, sqlx::Error>
where
Self: Sized,
I: sqlx::ColumnIndex<arel::DatabaseRow>,
{
let v: u8 = row.try_get(index)?;
let ret = match v {
1 => Gender::Male,
2 => Gender::Female,
_ => Gender::Unknown,
};
Ok(ret)
}
}
impl From<Gender> for arel::Value {
fn from(value: Gender) -> Self {
match value {
Gender::Male => 1.into(),
Gender::Female => 2.into(),
_ => 0.into(),
}
}
}

#[allow(dead_code)]
#[arel]
struct User {
Expand All @@ -8,6 +39,7 @@ struct User {
name: String,
#[arel(rename = "type")]
r#type: String,
gender: Option<Gender>,
desc: Option<String>,
done: Option<bool>,
lock_version: Option<i32>,
Expand All @@ -23,6 +55,7 @@ async fn init_db() -> anyhow::Result<()> {
id INTEGER PRIMARY KEY NOT NULL,
name VARCHAR(255),
type VARCHAR(255),
gender INT(1),
desc TEXT,
done BOOLEAN NOT NULL DEFAULT 0,
lock_version INT(11) NOT NULL DEFAULT 0,
Expand Down Expand Up @@ -113,15 +146,18 @@ mod tests {

let user: User = User::query().r#where("id", 2).fetch_one_as().await?;
assert_eq!(user.id, 2);
assert_eq!(user.gender, Some(Gender::Unknown));
// update
let mut active_user: ArelActiveUser = user.into();
active_user.name.set("user2");
active_user.r#type.set("Admin2");
active_user.gender.set(Gender::Male);
let ret = active_user.save().await?;
assert_eq!(ret.rows_affected(), 1);
let user: User = User::query().r#where("id", 2).fetch_one_as().await?;
assert_eq!(user.name, "user2");
assert_eq!(user.r#type, "Admin2");
assert_eq!(user.gender, Some(Gender::Male));

// delete
let ret = active_user.destroy().await?;
Expand Down

0 comments on commit a5d2f60

Please sign in to comment.