Skip to content

Commit

Permalink
Implement Serialize/Deserialize for Many
Browse files Browse the repository at this point in the history
Requires allowing a table name in a Query to be a Cow instead of
always a &'static str, hence the bump to 0.4
  • Loading branch information
Electron100 committed Jul 20, 2021
1 parent 20e97a4 commit 2be96e6
Show file tree
Hide file tree
Showing 9 changed files with 61 additions and 33 deletions.
9 changes: 5 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions butane/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "butane"
version = "0.3.1"
version = "0.4.0"
authors = ["James Oakley <james@electronstudio.org>"]
edition = "2018"
description = "An ORM with a focus on simplicity and on writing Rust, not SQL."
Expand All @@ -23,8 +23,8 @@ tls = ["butane_core/tls"]
uuid = ["butane_core/uuid", "butane_codegen/uuid"]

[dependencies]
butane_codegen = { path = "../butane_codegen", version = "0.3.1" }
butane_core = { path = "../butane_core", version = "0.3.1" }
butane_codegen = { path = "../butane_codegen", version = "0.4" }
butane_core = { path = "../butane_core", version = "0.4" }


[dev-dependencies]
Expand All @@ -39,6 +39,7 @@ once_cell="1.5.2"
postgres = { version = "0.19", features=["with-geo-types-0_7"] }
r2d2_for_test = {package="r2d2", version = "0.8"}
rusqlite = "0.25"
serde_json = "1.0"
uuid_for_test = {package="uuid", version = "0.8", features=["v4"] }

[package.metadata.docs.rs]
Expand Down
16 changes: 15 additions & 1 deletion butane/tests/query.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use butane::db::Connection;
use butane::prelude::*;
use butane::query::BoolExpr;
use butane::{colname, filter, find, query};
use butane::{colname, filter, find, query, Many};
use chrono::{TimeZone, Utc};
use paste;
use serde_json;

mod common;
use common::blog;
Expand Down Expand Up @@ -130,6 +131,19 @@ fn many_load(conn: Connection) {
}
testall!(many_load);

fn many_serialize(conn: Connection) {
blog::setup_blog(&conn);
let post: Post = find!(Post, title == "The Tiger", &conn).unwrap();
let tags_json: String = serde_json::to_string(&post.tags).unwrap();
let tags: Many<Tag> = serde_json::from_str(&tags_json).unwrap();
let tags = tags.load(&conn).unwrap();
let mut tags: Vec<&Tag> = tags.collect();
tags.sort_by(|t1, t2| (*t1).tag.partial_cmp(&t2.tag).unwrap());
assert_eq!(tags[0].tag, "asia");
assert_eq!(tags[1].tag, "danger");
}
testall!(many_serialize);

fn many_objects_with_tag(conn: Connection) {
blog::setup_blog(&conn);
let mut posts = query!(Post, tags.contains("danger")).load(&conn).unwrap();
Expand Down
4 changes: 2 additions & 2 deletions butane_cli/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "butane_cli"
version = "0.3.1"
version = "0.4.0"
authors = ["James Oakley <james@electronstudio.org>"]
edition = "2018"
description = "The CLI for the Butane ORM"
Expand All @@ -15,7 +15,7 @@ path = "src/main.rs"

[dependencies]
anyhow = "1.0"
butane = { path="../butane", version="0.3.1", features=["default", "sqlite", "pg"] }
butane = { path="../butane", version="0.4", features=["default", "sqlite", "pg"] }
chrono = "0.4"
clap = "2.33"
quote = "1.0"
Expand Down
4 changes: 2 additions & 2 deletions butane_codegen/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "butane_codegen"
version = "0.3.1"
version = "0.4.0"
authors = ["James Oakley <james@electronstudio.org>"]
edition = "2018"
description = "Macros for Butane. Do not use this crate directly -- use the butane crate."
Expand All @@ -13,7 +13,7 @@ datetime = []

[dependencies]
proc-macro2 = "1.0"
butane_core = { path = "../butane_core", version = "0.3.1" }
butane_core = { path = "../butane_core", version = "0.4" }
quote = "1.0"
syn = { version = "1.0", features = ["full", "extra-traits"] }
uuid = {version = "0.8", optional=true}
Expand Down
2 changes: 1 addition & 1 deletion butane_core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "butane_core"
version = "0.3.1"
version = "0.4.0"
authors = ["James Oakley <james@electronstudio.org>"]
edition = "2018"
description = "Internals for Butane. Do not use this crate directly -- use the butane crate."
Expand Down
21 changes: 15 additions & 6 deletions butane_core/src/many.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ use crate::db::{Column, ConnectionMethods};
use crate::query::{BoolExpr, Expr};
use crate::{DataObject, Error, FieldType, Result, SqlType, SqlVal, ToSql};
use once_cell::unsync::OnceCell;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;

fn default_oc<T>() -> OnceCell<Vec<T>> {
OnceCell::default()
}

/// Used to implement a many-to-many relationship between models.
///
Expand All @@ -10,15 +16,18 @@ use once_cell::unsync::OnceCell;
/// U::PKType. Table name is T_ManyToMany_foo where foo is the name of
/// the Many field
//
#[derive(Debug)]
#[derive(Debug, Serialize, Deserialize)]
pub struct Many<T>
where
T: DataObject,
{
item_table: &'static str,
item_table: Cow<'static, str>,
owner: Option<SqlVal>,
owner_type: SqlType,
#[serde(skip)]
new_values: Vec<SqlVal>,
#[serde(skip)]
#[serde(default = "default_oc")]
all_values: OnceCell<Vec<T>>,
}
impl<T> Many<T>
Expand All @@ -33,7 +42,7 @@ where
/// [`DataObject`]: super::DataObject
pub fn new() -> Self {
Many {
item_table: "not_initialized",
item_table: Cow::Borrowed("not_initialized"),
owner: None,
owner_type: SqlType::Int,
new_values: Vec::new(),
Expand All @@ -46,7 +55,7 @@ where
if self.owner.is_some() {
return;
}
self.item_table = item_table;
self.item_table = Cow::Borrowed(item_table);
self.owner = Some(owner);
self.owner_type = owner_type;
self.all_values = OnceCell::new();
Expand All @@ -72,7 +81,7 @@ where
let owner = self.owner.as_ref().ok_or(Error::NotInitialized)?;
while !self.new_values.is_empty() {
conn.insert_only(
self.item_table,
&self.item_table,
&self.columns(),
&[
owner.as_ref(),
Expand All @@ -96,7 +105,7 @@ where
let mut vals = T::query()
.filter(BoolExpr::Subquery {
col: T::PKCOL,
tbl2: self.item_table,
tbl2: self.item_table.clone(),
tbl2_col: "has",
expr: Box::new(BoolExpr::Eq("owner", Expr::Val(owner.clone()))),
})
Expand Down
6 changes: 3 additions & 3 deletions butane_core/src/query/fieldexpr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::fkey::ForeignKey;
use crate::query::{BoolExpr, Column, Expr, Join};
use crate::sqlval::{FieldType, SqlVal, ToSql};
use crate::DataObject;
use std::borrow::Borrow;
use std::borrow::{Borrow, Cow};
use std::cmp::{PartialEq, PartialOrd};
use std::marker::PhantomData;

Expand Down Expand Up @@ -78,7 +78,7 @@ impl<F: DataObject> FieldExpr<ForeignKey<F>> {
pub fn subfilter(&self, q: BoolExpr) -> BoolExpr {
BoolExpr::Subquery {
col: self.name,
tbl2: F::TABLE,
tbl2: Cow::Borrowed(F::TABLE),
tbl2_col: F::PKCOL,
expr: Box::new(q),
}
Expand Down Expand Up @@ -119,7 +119,7 @@ where
//let many_tbl = format!("{}_{}_Many", O::TABLE, self.name);
BoolExpr::SubqueryJoin {
col: O::PKCOL,
tbl2: T::TABLE,
tbl2: Cow::Borrowed(T::TABLE),
col2: Column::new(self.many_table, "owner"),
joins: vec![Join::Inner {
join_table: self.many_table,
Expand Down
25 changes: 14 additions & 11 deletions butane_core/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@
use crate::db::{BackendRows, ConnectionMethods, QueryResult};
use crate::{DataResult, Result, SqlVal};
use fallible_iterator::FallibleIterator;
use std::borrow::Cow;
use std::marker::PhantomData;

mod fieldexpr;

pub use fieldexpr::{DataOrd, FieldExpr, ManyFieldExpr};

type TblName = Cow<'static, str>;

/// Abstract representation of a database expression.
#[derive(Clone)]
pub enum Expr {
Expand Down Expand Up @@ -44,7 +47,7 @@ pub enum BoolExpr {
/// in `tbl2` is true.
Subquery {
col: &'static str,
tbl2: &'static str,
tbl2: TblName,
tbl2_col: &'static str,
expr: Box<BoolExpr>,
},
Expand All @@ -54,7 +57,7 @@ pub enum BoolExpr {
/// in `tbl2` with the specified joins is true.
SubqueryJoin {
col: &'static str,
tbl2: &'static str,
tbl2: TblName,
col2: Column,
joins: Vec<Join>,
expr: Box<BoolExpr>,
Expand Down Expand Up @@ -88,21 +91,21 @@ pub enum Join {

#[derive(Clone)]
pub struct Column {
table: Option<&'static str>,
table: Option<TblName>,
name: &'static str,
}
impl Column {
pub fn new(table: &'static str, name: &'static str) -> Self {
Column {
table: Some(table),
table: Some(Cow::Borrowed(table)),
name,
}
}
pub fn unqualified(name: &'static str) -> Self {
Column { table: None, name }
}
pub fn table(&self) -> Option<&'static str> {
self.table
pub fn table(&self) -> Option<&str> {
self.table.as_ref().map(|t| t.as_ref())
}
pub fn name(&self) -> &'static str {
self.name
Expand All @@ -112,7 +115,7 @@ impl Column {
/// Representation of a database query.
#[derive(Clone)]
pub struct Query<T: DataResult> {
table: &'static str,
table: TblName,
filter: Option<BoolExpr>,
limit: Option<i32>,
sort: Vec<Order>,
Expand All @@ -124,7 +127,7 @@ impl<T: DataResult> Query<T> {
/// `limit`.
pub fn new(table: &'static str) -> Query<T> {
Query {
table,
table: Cow::Borrowed(table),
filter: None,
limit: None,
sort: Vec::new(),
Expand Down Expand Up @@ -168,7 +171,7 @@ impl<T: DataResult> Query<T> {

/// Executes the query against `conn` and returns the first result (if any).
pub fn load_first(self, conn: &impl ConnectionMethods) -> Result<Option<T>> {
conn.query(self.table, T::COLUMNS, self.filter, Some(1), None)?
conn.query(&self.table, T::COLUMNS, self.filter, Some(1), None)?
.mapped(T::from_row)
.nth(0)
}
Expand All @@ -180,13 +183,13 @@ impl<T: DataResult> Query<T> {
} else {
Some(self.sort.as_slice())
};
conn.query(self.table, T::COLUMNS, self.filter, self.limit, sort)?
conn.query(&self.table, T::COLUMNS, self.filter, self.limit, sort)?
.mapped(T::from_row)
.collect()
}

/// Executes the query against `conn` and deletes all matching objects.
pub fn delete(self, conn: &impl ConnectionMethods) -> Result<usize> {
conn.delete_where(self.table, self.filter.unwrap_or(BoolExpr::True))
conn.delete_where(&self.table, self.filter.unwrap_or(BoolExpr::True))
}
}

0 comments on commit 2be96e6

Please sign in to comment.