diff --git a/Cargo.lock b/Cargo.lock index 0dd4197c..fddde293 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -120,7 +120,9 @@ dependencies = [ "clap_complete", "heck", "indoc", + "prettyplease", "proc-macro2", + "quote", "syn", "thiserror", ] @@ -159,20 +161,30 @@ version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" +[[package]] +name = "prettyplease" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -198,9 +210,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "2.0.38" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index d7719beb..98a8a561 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,8 @@ advanced-queries = [] clap = { version = "4.4", features = ["derive", "wrap_help"] } clap_complete = "4.4" syn = { version = "2", features = ["extra-traits", "full"] } +quote = "1" +prettyplease = "0.2" proc-macro2 = "1" indoc = "2.0.4" heck = "0.4" # same case converter diesel uses diff --git a/src/code.rs b/src/code.rs index d6dc65f6..f52e6aef 100644 --- a/src/code.rs +++ b/src/code.rs @@ -1,6 +1,9 @@ use heck::ToPascalCase; use indoc::formatdoc; +use proc_macro2::{Ident, TokenStream}; +use quote::{format_ident, quote, ToTokens}; use std::borrow::Cow; +use syn::parse::Parser; use crate::parser::{ParsedColumnMacro, ParsedTableMacro, FILE_SIGNATURE}; use crate::{get_table_module_name, GenerationConfig, TableOptions}; @@ -425,6 +428,10 @@ fn get_async(table_options: &TableOptions<'_>) -> (&'static str, &'static str) { ("", "") } +/// Common error text for `syn::parse2().expect()` +/// NOTE: This should be removed again and is only temporary for transition +const SYN_PARSE_ERR: &str = "Expected syn to parse static input"; + /// Generate all functions (insides of the `impl StuctName { here }`) fn build_table_fns( table: &ParsedTableMacro, @@ -448,70 +455,86 @@ fn build_table_fns( }) .collect(); - let item_id_params = primary_column_name_and_type + let item_id_params_syn = primary_column_name_and_type .iter() .map(|name_and_type| { - format!( + syn::parse_str(&format!( "param_{name}: {ty}", name = name_and_type.0, ty = name_and_type.1 - ) + )) + .expect(SYN_PARSE_ERR) }) - .collect::>() - .join(", "); - let item_id_filters = primary_column_name_and_type + .collect::>(); + + let item_id_filters_syn = primary_column_name_and_type .iter() .map(|name_and_type| { - format!( + syn::parse_str(&format!( "filter({name}.eq(param_{name}))", name = name_and_type.0.to_string() - ) + )) + .expect(SYN_PARSE_ERR) }) - .collect::>() - .join("."); + .collect::>(); // template variables let table_name = table.name.to_string(); let (async_keyword, await_keyword) = get_async(&table_options); let struct_name = &table.struct_name; + let struct_name_syn: proc_macro2::Ident = syn::parse_str(struct_name).expect(SYN_PARSE_ERR); let schema_path = config.get_schema_path(); let create_struct_identifier = &create_struct.identifier; let update_struct_identifier = &update_struct.identifier; let is_readonly = table_options.get_readonly(); - let mut buffer = String::new(); + let mut buffer_main = TokenStream::new(); + let mut buffer_impl = TokenStream::new(); if !config.get_once_common_structs() { - buffer.push_str(&generate_common_structs(&table_options)); - buffer.push('\n'); + buffer_main.extend( + syn::parse_str::(&generate_common_structs(&table_options)) + .expect(SYN_PARSE_ERR), + ); } - buffer.push_str(&format!("impl {struct_name} {{")); + // the following section is for quote variables + let import_path_syn: syn::Path = + syn::parse_str(&format!("{schema_path}{table_name}")).expect(SYN_PARSE_ERR); + let await_keyword_syn: Option = syn::parse2(await_keyword.to_token_stream()).ok(); + let async_keyword_syn: Option = syn::parse2(async_keyword.to_token_stream()).ok(); + let table_name_syn: Ident = syn::parse_str(&table_name).expect(SYN_PARSE_ERR); if !is_readonly { if create_struct.has_fields() { - buffer.push_str(&format!( - r##" - /// Insert a new row into `{table_name}` with a given [`{create_struct_identifier}`] - pub{async_keyword} fn create(db: &mut ConnectionType, item: &{create_struct_identifier}) -> diesel::QueryResult {{ - use {schema_path}{table_name}::dsl::*; - - diesel::insert_into({table_name}).values(item).get_result::(db){await_keyword} - }} -"## - )); + let doc = format!( + " Insert a new row into `{table_name}` with a given [`{create_struct_identifier}`]" + ); + let create_struct_identifier: Ident = + syn::parse_str(create_struct_identifier).expect(SYN_PARSE_ERR); + + let code = quote! { + #[doc = #doc] + pub #async_keyword_syn fn create(db: &mut ConnectionType, item: &#create_struct_identifier) -> diesel::QueryResult { + use #import_path_syn::dsl::*; + + diesel::insert_into(#table_name_syn).values(item).get_result::(db)#await_keyword_syn + } + }; + buffer_impl.extend(code); } else { - buffer.push_str(&format!( - r##" - /// Insert a new row into `{table_name}` with all default values - pub{async_keyword} fn create(db: &mut ConnectionType) -> diesel::QueryResult {{ - use {schema_path}{table_name}::dsl::*; - - diesel::insert_into({table_name}).default_values().get_result::(db){await_keyword} - }} -"## - )); + let doc = format!(" Insert a new row into `{table_name}` with all default values"); + + let code = quote! { + #[doc = #doc] + pub #async_keyword_syn fn create(db: &mut ConnectionType) -> diesel::QueryResult { + use #import_path_syn::dsl::*; + + diesel::insert_into(#table_name_syn).default_values().get_result::(db)#await_keyword_syn + } + }; + buffer_impl.extend(code); } } @@ -522,98 +545,113 @@ fn build_table_fns( "keys" }; - buffer.push_str(&format!( - r##" - /// Get a row from `{table_name}`, identified by the primary {key_maybe_multiple} - pub{async_keyword} fn read(db: &mut ConnectionType, {item_id_params}) -> diesel::QueryResult {{ - use {schema_path}{table_name}::dsl::*; + { + let doc = format!( + " Get a row from `{table_name}`, identified by the primary {key_maybe_multiple}" + ); + + let code = quote! { + #[doc = #doc] + pub #async_keyword_syn fn read(db: &mut ConnectionType, #(#item_id_params_syn), *) -> diesel::QueryResult { + use #import_path_syn::dsl::*; - {table_name}.{item_id_filters}.first::(db){await_keyword} - }} -"## - )); + #table_name_syn.#(#item_id_filters_syn).*.first::(db)#await_keyword_syn + } + }; + buffer_impl.extend(code); + } #[cfg(feature = "advanced-queries")] - buffer.push_str(&format!(r##" - /// Paginates through the table where page is a 0-based index (i.e. page 0 is the first page) - pub{async_keyword} fn paginate(db: &mut ConnectionType, page: i64, page_size: i64, filter: {struct_name}Filter) -> diesel::QueryResult> {{ - use {schema_path}{table_name}::dsl::*; - - let page = page.max(0); - let page_size = page_size.max(1); - let total_items = Self::filter(filter.clone()).count().get_result(db)?; - let items = Self::filter(filter).limit(page_size).offset(page * page_size).load::(db){await_keyword}?; - - Ok(PaginationResult {{ - items, - total_items, - page, - page_size, - /* ceiling division of integers */ - num_pages: total_items / page_size + i64::from(total_items % page_size != 0) - }}) - }} -"##)); + { + let doc = " Paginates through the table where page is a 0-based index (i.e. page 0 is the first page)"; + let filter_struct = format_ident!("{struct_name}Filter"); + + let code = quote! { + #[doc = #doc] + pub #async_keyword_syn fn paginate(db: &mut ConnectionType, page: i64, page_size: i64, filter: #filter_struct) -> diesel::QueryResult> { + use #import_path_syn::dsl::*; + + let page = page.max(0); + let page_size = page_size.max(1); + let total_items = Self::filter(filter.clone()).count().get_result(db)?; + let items = Self::filter(filter).limit(page_size).offset(page * page_size).load::(db)#await_keyword_syn?; + + Ok(PaginationResult { + items, + total_items, + page, + page_size, + /* ceiling division of integers */ + num_pages: total_items / page_size + i64::from(total_items % page_size != 0) + }) + } + }; + buffer_impl.extend(code); + } #[cfg(feature = "advanced-queries")] // Table::filter() helper fn { - let diesel_backend = &config.diesel_backend; + let diesel_backend: syn::Path = + syn::parse_str(&config.diesel_backend).expect(SYN_PARSE_ERR); let filters = table .columns .iter() .map(|column| { - let column_name = column.name.to_string(); + let column_name = column.name.clone(); + let filter_ident = format_ident!("filter_{column_name}"); if column.is_nullable { // "Option::None" will never match anything, and "is_null" is required to be used, see https://docs.diesel.rs/master/diesel/expression_methods/trait.ExpressionMethods.html#method.eq - format!( - r##" - if let Some(filter_{column_name}) = filter.{column_name} {{ - query = if filter_{column_name}.is_some() {{ - query.filter({schema_path}{table_name}::{column_name}.eq(filter_{column_name})) - }} else {{ - query.filter({schema_path}{table_name}::{column_name}.is_null()) - }}; - }}"## - ) + quote! { + if let Some(#filter_ident) = filter.#column_name { + query = if #filter_ident.is_some() { + query.filter(#import_path_syn::#column_name.eq(#filter_ident)) + } else { + query.filter(#import_path_syn::#column_name.is_null()) + }; + } + } } else { - format!( - r##" - if let Some(filter_{column_name}) = filter.{column_name} {{ - query = query.filter({schema_path}{table_name}::{column_name}.eq(filter_{column_name})); - }}"## - ) + quote! { + if let Some(#filter_ident) = filter.#column_name { + query = query.filter(#import_path_syn::#column_name.eq(#filter_ident)); + } + } } }) - .collect::>() - .join(""); - buffer.push_str(&format!( - r##" - /// A utility function to help build custom search queries - /// - /// Example: - /// - /// ``` - /// // create a filter for completed todos - /// let query = Todo::filter(TodoFilter {{ - /// completed: Some(true), - /// ..Default::default() - /// }}); - /// - /// // delete completed todos - /// diesel::delete(query).execute(db)?; - /// ``` - pub fn filter<'a>( - filter: {struct_name}Filter, - ) -> {schema_path}{table_name}::BoxedQuery<'a, {diesel_backend}> {{ - let mut query = {schema_path}{table_name}::table.into_boxed(); - {filters} - - query - }} -"## - )); + .collect::(); + + let doc = formatdoc!( + r#" A utility function to help build custom search queries + + Example: + + ``` + // create a filter for completed todos + let query = Todo::filter(TodoFilter {{ + completed: Some(true), + ..Default::default() + }}); + + // delete completed todos + diesel::delete(query).execute(db)?; + ```"# + ); + let filter_struct = format_ident!("{struct_name}Filter"); + + let code = quote! { + #[doc = #doc] + pub fn filter<'a>( + filter: #filter_struct, + ) -> #import_path_syn::BoxedQuery<'a, #diesel_backend> { + let mut query = #import_path_syn::table.into_boxed(); + #filters + + query + } + }; + buffer_impl.extend(code); } // TODO: If primary key columns are attached to the form struct (not optionally) @@ -625,30 +663,44 @@ fn build_table_fns( // In this scenario, we also have to check whether there are any updatable columns for which // we should generate an update() method. - buffer.push_str(&format!(r##" - /// Update a row in `{table_name}`, identified by the primary {key_maybe_multiple} with [`{update_struct_identifier}`] - pub{async_keyword} fn update(db: &mut ConnectionType, {item_id_params}, item: &{update_struct_identifier}) -> diesel::QueryResult {{ - use {schema_path}{table_name}::dsl::*; + let doc = format!( + " Update a row in `{table_name}`, identified by the primary {key_maybe_multiple} with [`{update_struct_identifier}`]" + ); + let update_struct_identifier: Ident = + syn::parse_str(update_struct_identifier).expect(SYN_PARSE_ERR); + + let code = quote! { + #[doc = #doc] + pub #async_keyword_syn fn update(db: &mut ConnectionType, #(#item_id_params_syn), *, item: &#update_struct_identifier) -> diesel::QueryResult { + use #import_path_syn::dsl::*; - diesel::update({table_name}.{item_id_filters}).set(item).get_result(db){await_keyword} - }} -"##)); + diesel::update(#table_name_syn.#(#item_id_filters_syn).*).set(item).get_result(db)#await_keyword_syn + } + }; + buffer_impl.extend(code); } if !is_readonly { - buffer.push_str(&format!( - r##" - /// Delete a row in `{table_name}`, identified by the primary {key_maybe_multiple} - pub{async_keyword} fn delete(db: &mut ConnectionType, {item_id_params}) -> diesel::QueryResult {{ - use {schema_path}{table_name}::dsl::*; - - diesel::delete({table_name}.{item_id_filters}).execute(db){await_keyword} - }} -"## - )); + let doc = format!( + " Delete a row in `{table_name}`, identified by the primary {key_maybe_multiple}" + ); + + let code = quote! { + #[doc = #doc] + pub #async_keyword_syn fn delete(db: &mut ConnectionType, #(#item_id_params_syn), *) -> diesel::QueryResult { + use #import_path_syn::dsl::*; + + diesel::delete(#table_name_syn.#(#item_id_filters_syn).*).execute(db)#await_keyword_syn + } + }; + buffer_impl.extend(code); } - buffer.push_str("}\n"); + buffer_main.extend(quote! { + impl #struct_name_syn { + #buffer_impl + } + }); #[cfg(feature = "advanced-queries")] // generate filter struct for filter() helper function @@ -658,26 +710,28 @@ fn build_table_fns( .iter() .map(|column| { let struct_field = StructField::from(column); - format!( - "pub {column_name}: Option<{column_type}>,", - column_name = struct_field.name, - column_type = struct_field.to_rust_type() - ) + syn::Field::parse_named + .parse_str(&format!( + "pub {column_name}: Option<{column_type}>", + column_name = struct_field.name, + column_type = struct_field.to_rust_type() + )) + .expect(SYN_PARSE_ERR) }) - .collect::>() - .join("\n "); - - buffer.push_str(&formatdoc!( - r##" - #[derive(Debug, Default, Clone)] - pub struct {struct_name}Filter {{ - {filter_fields} - }} - "## - )); + .collect::>(); + + let filter_struct = format_ident!("{struct_name}Filter"); + + let code = quote! { + #[derive(Debug, Default, Clone)] + pub struct #filter_struct { + #(#filter_fields),* + } + }; + buffer_main.extend(code); } - buffer + prettyplease::unparse(&syn::parse2(buffer_main).expect(SYN_PARSE_ERR)) } /// Generate common structs diff --git a/test/advanced_queries/models/todos/generated.rs b/test/advanced_queries/models/todos/generated.rs index cd2c9c80..278fdcae 100644 --- a/test/advanced_queries/models/todos/generated.rs +++ b/test/advanced_queries/models/todos/generated.rs @@ -77,60 +77,61 @@ pub struct PaginationResult { /// Number of total possible pages, given the `page_size` and `total_items` pub num_pages: i64, } - impl Todos { /// Insert a new row into `todos` with a given [`CreateTodos`] - pub fn create(db: &mut ConnectionType, item: &CreateTodos) -> diesel::QueryResult { + pub fn create( + db: &mut ConnectionType, + item: &CreateTodos, + ) -> diesel::QueryResult { use crate::schema::todos::dsl::*; - diesel::insert_into(todos).values(item).get_result::(db) } - /// Get a row from `todos`, identified by the primary key pub fn read(db: &mut ConnectionType, param_id: i32) -> diesel::QueryResult { use crate::schema::todos::dsl::*; - todos.filter(id.eq(param_id)).first::(db) } - /// Paginates through the table where page is a 0-based index (i.e. page 0 is the first page) - pub fn paginate(db: &mut ConnectionType, page: i64, page_size: i64, filter: TodosFilter) -> diesel::QueryResult> { + pub fn paginate( + db: &mut ConnectionType, + page: i64, + page_size: i64, + filter: TodosFilter, + ) -> diesel::QueryResult> { use crate::schema::todos::dsl::*; - let page = page.max(0); let page_size = page_size.max(1); let total_items = Self::filter(filter.clone()).count().get_result(db)?; - let items = Self::filter(filter).limit(page_size).offset(page * page_size).load::(db)?; - + let items = Self::filter(filter) + .limit(page_size) + .offset(page * page_size) + .load::(db)?; Ok(PaginationResult { items, total_items, page, page_size, - /* ceiling division of integers */ - num_pages: total_items / page_size + i64::from(total_items % page_size != 0) + num_pages: total_items / page_size + i64::from(total_items % page_size != 0), }) } + /** A utility function to help build custom search queries + +Example: - /// A utility function to help build custom search queries - /// - /// Example: - /// - /// ``` - /// // create a filter for completed todos - /// let query = Todo::filter(TodoFilter { - /// completed: Some(true), - /// ..Default::default() - /// }); - /// - /// // delete completed todos - /// diesel::delete(query).execute(db)?; - /// ``` +``` +// create a filter for completed todos +let query = Todo::filter(TodoFilter { + completed: Some(true), + ..Default::default() +}); + +// delete completed todos +diesel::delete(query).execute(db)?; +```*/ pub fn filter<'a>( filter: TodosFilter, ) -> crate::schema::todos::BoxedQuery<'a, diesel::pg::Pg> { let mut query = crate::schema::todos::table.into_boxed(); - if let Some(filter_id) = filter.id { query = query.filter(crate::schema::todos::id.eq(filter_id)); } @@ -155,21 +156,20 @@ impl Todos { if let Some(filter_updated_at) = filter.updated_at { query = query.filter(crate::schema::todos::updated_at.eq(filter_updated_at)); } - query } - /// Update a row in `todos`, identified by the primary key with [`UpdateTodos`] - pub fn update(db: &mut ConnectionType, param_id: i32, item: &UpdateTodos) -> diesel::QueryResult { + pub fn update( + db: &mut ConnectionType, + param_id: i32, + item: &UpdateTodos, + ) -> diesel::QueryResult { use crate::schema::todos::dsl::*; - diesel::update(todos.filter(id.eq(param_id))).set(item).get_result(db) } - /// Delete a row in `todos`, identified by the primary key pub fn delete(db: &mut ConnectionType, param_id: i32) -> diesel::QueryResult { use crate::schema::todos::dsl::*; - diesel::delete(todos.filter(id.eq(param_id))).execute(db) } } diff --git a/test/simple_table_pg/models/mod.rs b/test/simple_table_pg/models/mod.rs index 015a6a2b..82fce1a2 100644 --- a/test/simple_table_pg/models/mod.rs +++ b/test/simple_table_pg/models/mod.rs @@ -1 +1,2 @@ pub mod todos; +pub mod todos2; diff --git a/test/simple_table_pg/models/todos/generated.rs b/test/simple_table_pg/models/todos/generated.rs index 2e777ec2..272d153a 100644 --- a/test/simple_table_pg/models/todos/generated.rs +++ b/test/simple_table_pg/models/todos/generated.rs @@ -77,33 +77,32 @@ pub struct PaginationResult { /// Number of total possible pages, given the `page_size` and `total_items` pub num_pages: i64, } - impl Todos { /// Insert a new row into `todos` with a given [`CreateTodos`] - pub fn create(db: &mut ConnectionType, item: &CreateTodos) -> diesel::QueryResult { + pub fn create( + db: &mut ConnectionType, + item: &CreateTodos, + ) -> diesel::QueryResult { use crate::schema::todos::dsl::*; - diesel::insert_into(todos).values(item).get_result::(db) } - /// Get a row from `todos`, identified by the primary key pub fn read(db: &mut ConnectionType, param_id: i32) -> diesel::QueryResult { use crate::schema::todos::dsl::*; - todos.filter(id.eq(param_id)).first::(db) } - /// Update a row in `todos`, identified by the primary key with [`UpdateTodos`] - pub fn update(db: &mut ConnectionType, param_id: i32, item: &UpdateTodos) -> diesel::QueryResult { + pub fn update( + db: &mut ConnectionType, + param_id: i32, + item: &UpdateTodos, + ) -> diesel::QueryResult { use crate::schema::todos::dsl::*; - diesel::update(todos.filter(id.eq(param_id))).set(item).get_result(db) } - /// Delete a row in `todos`, identified by the primary key pub fn delete(db: &mut ConnectionType, param_id: i32) -> diesel::QueryResult { use crate::schema::todos::dsl::*; - diesel::delete(todos.filter(id.eq(param_id))).execute(db) } } diff --git a/test/simple_table_pg/models/todos2/generated.rs b/test/simple_table_pg/models/todos2/generated.rs new file mode 100644 index 00000000..3ed0c434 --- /dev/null +++ b/test/simple_table_pg/models/todos2/generated.rs @@ -0,0 +1,46 @@ +/* @generated and managed by dsync */ + +use crate::diesel::*; +use crate::schema::*; + +pub type ConnectionType = diesel::r2d2::PooledConnection>; + +/// Struct representing a row in table `todos2` +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, diesel::Queryable, diesel::Selectable, diesel::QueryableByName)] +#[diesel(table_name=todos2, primary_key(id))] +pub struct Todos2 { + /// Field representing column `id` + pub id: i32, +} + +/// Result of a `.paginate` function +#[derive(Debug, serde::Serialize)] +pub struct PaginationResult { + /// Resulting items that are from the current page + pub items: Vec, + /// The count of total items there are + pub total_items: i64, + /// Current page, 0-based index + pub page: i64, + /// Size of a page + pub page_size: i64, + /// Number of total possible pages, given the `page_size` and `total_items` + pub num_pages: i64, +} +impl Todos2 { + /// Insert a new row into `todos2` with all default values + pub fn create(db: &mut ConnectionType) -> diesel::QueryResult { + use crate::schema::todos2::dsl::*; + diesel::insert_into(todos2).default_values().get_result::(db) + } + /// Get a row from `todos2`, identified by the primary key + pub fn read(db: &mut ConnectionType, param_id: i32) -> diesel::QueryResult { + use crate::schema::todos2::dsl::*; + todos2.filter(id.eq(param_id)).first::(db) + } + /// Delete a row in `todos2`, identified by the primary key + pub fn delete(db: &mut ConnectionType, param_id: i32) -> diesel::QueryResult { + use crate::schema::todos2::dsl::*; + diesel::delete(todos2.filter(id.eq(param_id))).execute(db) + } +} diff --git a/test/simple_table_pg/models/todos2/mod.rs b/test/simple_table_pg/models/todos2/mod.rs new file mode 100644 index 00000000..136e9a24 --- /dev/null +++ b/test/simple_table_pg/models/todos2/mod.rs @@ -0,0 +1,2 @@ +pub mod generated; +pub use generated::*; diff --git a/test/simple_table_pg/schema.rs b/test/simple_table_pg/schema.rs index c66e8219..1db7d283 100644 --- a/test/simple_table_pg/schema.rs +++ b/test/simple_table_pg/schema.rs @@ -12,3 +12,8 @@ diesel::table! { updated_at -> Timestamp, } } +diesel::table! { + todos2 (id) { + id -> Int4, + } +}