Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes issue with many to many relationship not respecting renamed tables #1

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion butane/tests/common/blog.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use butane::prelude::*;
use butane::{dataresult, model};
use butane::{db::Connection, ForeignKey, Many, ObjectState};
use chrono::{naive::NaiveDateTime, offset::Utc};
use chrono::{NaiveDateTime, Utc};

#[model]
#[derive(Debug, Eq, PartialEq)]
Expand Down
1 change: 1 addition & 0 deletions butane/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ macro_rules! maketest {
let backend = butane::db::get_backend(&stringify!($backend)).expect("Could not find backend");
let $dataname = $crate::common::[<$backend _setup>]();
eprintln!("connecting to {}", &$connstr);
println!("\nWe're now running {}\n", &stringify!($fname));
let mut conn = backend.connect(&$connstr).expect("Could not connect backend");
$crate::common::setup_db(backend, &mut conn);
$fname(conn);
Expand Down
31 changes: 31 additions & 0 deletions butane/tests/many.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,25 @@ impl AutoPkWithMany {
}
}

#[model]
#[table = "renamed_many_table"]
struct RenamedAutoPkWithMany {
#[auto]
id: i64,
tags: Many<Tag>,
items: Many<AutoItem>,
}
impl RenamedAutoPkWithMany {
fn new() -> Self {
RenamedAutoPkWithMany {
id: -1,
tags: Many::default(),
items: Many::default(),
state: ObjectState::default(),
}
}
}

#[model]
struct AutoItem {
#[auto]
Expand Down Expand Up @@ -113,3 +132,15 @@ fn cant_add_unsaved_to_many(_conn: Connection) {
.expect_err("unexpectedly not error");
}
testall!(cant_add_unsaved_to_many);

fn can_add_to_many_with_custom_table_name(conn: Connection) {
let mut obj = RenamedAutoPkWithMany::new();
obj.tags.add(&create_tag(&conn, "blue")).unwrap();
obj.tags.add(&create_tag(&conn, "red")).unwrap();
obj.save(&conn).unwrap();

let obj = RenamedAutoPkWithMany::get(&conn, obj.id).unwrap();
let tags = obj.tags.load(&conn).unwrap();
assert_eq!(tags.count(), 2);
}
testall!(can_add_to_many_with_custom_table_name);
24 changes: 14 additions & 10 deletions butane_core/src/codegen/dbobj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ pub fn impl_dbobject(ast_struct: &ItemStruct, config: &Config) -> TokenStream2 {
let numdbfields = fields(ast_struct).filter(|f| is_row_field(f)).count();
let many_save: TokenStream2 = fields(ast_struct).filter(|f| is_many_to_many(f)).map(|f| {
let ident = f.ident.clone().expect("Fields must be named for butane");
let many_table_lit = many_table_lit(ast_struct, f);
let many_table_lit = many_table_lit(ast_struct, f, config);
let pksqltype =
quote!(<<Self as butane::DataObject>::PKType as butane::FieldType>::SQLTYPE);
// Save needs to ensure_initialized
Expand All @@ -52,7 +52,7 @@ pub fn impl_dbobject(ast_struct: &ItemStruct, config: &Config) -> TokenStream2 {
let values: Vec<TokenStream2> = push_values(ast_struct, |_| true);
let values_no_pk: Vec<TokenStream2> = push_values(ast_struct, |f: &Field| f != &pk_field);

let dataresult = impl_dataresult(ast_struct, tyname);
let dataresult = impl_dataresult(ast_struct, tyname, config);
quote!(
#dataresult
impl butane::DataObject for #tyname {
Expand Down Expand Up @@ -137,7 +137,7 @@ pub fn impl_dbobject(ast_struct: &ItemStruct, config: &Config) -> TokenStream2 {
)
}

pub fn impl_dataresult(ast_struct: &ItemStruct, dbo: &Ident) -> TokenStream2 {
pub fn impl_dataresult(ast_struct: &ItemStruct, dbo: &Ident, config: &Config) -> TokenStream2 {
let tyname = &ast_struct.ident;
let numdbfields = fields(ast_struct).filter(|f| is_row_field(f)).count();
let rows = rows_for_from(ast_struct);
Expand All @@ -151,7 +151,7 @@ pub fn impl_dataresult(ast_struct: &ItemStruct, dbo: &Ident) -> TokenStream2 {
.ident
.clone()
.expect("Fields must be named for butane");
let many_table_lit = many_table_lit(ast_struct, f);
let many_table_lit = many_table_lit(ast_struct, f, config);
let pksqltype = quote!(<<Self as butane::DataObject>::PKType as butane::FieldType>::SQLTYPE);
quote!(obj.#ident.ensure_init(#many_table_lit, butane::ToSql::to_sql(obj.pk()), #pksqltype);)
}).collect();
Expand Down Expand Up @@ -203,13 +203,13 @@ fn make_tablelit(config: &Config, tyname: &Ident) -> LitStr {
}
}

pub fn add_fieldexprs(ast_struct: &ItemStruct) -> TokenStream2 {
pub fn add_fieldexprs(ast_struct: &ItemStruct, config: &Config) -> TokenStream2 {
let tyname = &ast_struct.ident;
let vis = &ast_struct.vis;
let fieldexprs: Vec<TokenStream2> = fields(ast_struct)
.map(|f| {
if is_many_to_many(f) {
fieldexpr_func_many(f, ast_struct)
fieldexpr_func_many(f, ast_struct, config)
} else {
fieldexpr_func_regular(f, ast_struct)
}
Expand Down Expand Up @@ -247,10 +247,10 @@ fn fieldexpr_func_regular(f: &Field, ast_struct: &ItemStruct) -> TokenStream2 {
)
}

fn fieldexpr_func_many(f: &Field, ast_struct: &ItemStruct) -> TokenStream2 {
fn fieldexpr_func_many(f: &Field, ast_struct: &ItemStruct, config: &Config) -> TokenStream2 {
let tyname = &ast_struct.ident;
let fty = get_foreign_type_argument(&f.ty, "Many").expect("Many field misdetected");
let many_table_lit = many_table_lit(ast_struct, f);
let many_table_lit = many_table_lit(ast_struct, f, config);
fieldexpr_func(
f,
ast_struct,
Expand Down Expand Up @@ -342,12 +342,16 @@ where
.collect()
}

fn many_table_lit(ast_struct: &ItemStruct, field: &Field) -> LitStr {
let tyname = &ast_struct.ident;
fn many_table_lit(ast_struct: &ItemStruct, field: &Field, config: &Config) -> LitStr {
let ident = field
.ident
.clone()
.expect("Fields must be named for butane");
let binding = ast_struct.ident.to_string();
let tyname = match &config.table_name {
Some(s) => s,
None => &binding,
};
make_lit(&format!("{}_{}_Many", &tyname, &ident))
}

Expand Down
6 changes: 4 additions & 2 deletions butane_core/src/codegen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ where
migration::write_table_to_disk(ms, &ast_struct, &config).unwrap();

let impltraits = dbobj::impl_dbobject(&ast_struct, &config);
let fieldexprs = dbobj::add_fieldexprs(&ast_struct);
let fieldexprs = dbobj::add_fieldexprs(&ast_struct, &config);

let fields: Punctuated<Field, syn::token::Comma> =
match remove_helper_field_attributes(&mut ast_struct.fields) {
Expand Down Expand Up @@ -81,7 +81,9 @@ where
pub fn dataresult(args: TokenStream2, input: TokenStream2) -> TokenStream2 {
let dbo: Ident = syn::parse2(args)
.expect("Model type must be specified as argument to dataresult attribute");

let mut ast_struct: ItemStruct = syn::parse2(input).unwrap();
let config: dbobj::Config = config_from_attributes(&ast_struct);

// Filter out our helper attributes
let attrs: Vec<Attribute> = filter_helper_attributes(&ast_struct);
Expand All @@ -94,7 +96,7 @@ pub fn dataresult(args: TokenStream2, input: TokenStream2) -> TokenStream2 {

let vis = &ast_struct.vis;

let impltraits = dbobj::impl_dataresult(&ast_struct, &dbo);
let impltraits = dbobj::impl_dataresult(&ast_struct, &dbo, &config);

let fields = match remove_helper_field_attributes(&mut ast_struct.fields) {
Ok(fields) => &fields.named,
Expand Down
3 changes: 1 addition & 2 deletions butane_core/src/many.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@ fn default_oc<T>() -> OnceCell<Vec<T>> {
///
/// Creates a new table with columns "owner" and "has" If type T has a
/// many-to-many relationship with U, owner type is T::PKType, has is
/// U::PKType. Table name is T_ManyToMany_foo where foo is the name of
/// U::PKType. Table name is T_foo_Many where foo is the name of
/// the Many field
//
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Many<T>
where
Expand Down