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

Breaking: Remove tsync from default featureset + other minor fixes #104

Closed
wants to merge 6 commits into from
Closed
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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ authors = ["Haris <[email protected]>"]
edition = "2021"

[features]
default = ["tsync", "backtrace"]
default = ["backtrace"]
tsync = []
async = []
backtrace = []
Expand Down
13 changes: 13 additions & 0 deletions src/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,18 @@ pub struct MainOptions {
/// Generate the "ConnectionType" type only once in a "common.rs" file
#[arg(long = "once-connection-type")]
pub once_connection_type: bool,

/// Set which diesel backend to use (something which implements `diesel::backend::Backend`)
/// Deisel provides the following backends:
/// - `diesel::pg::Pg`
/// - `diesel::sqlite::Sqlite`
/// - `diesel::mysql::Mysql`
///
/// See `crate::GenerationConfig::diesel_backend` for more details.
///
/// Default is "diesel::pg::Pg"
#[arg(long = "diesel-backend", default_value = "diesel::pg::Pg")]
pub diesel_backend: String,
}

#[derive(Debug, ValueEnum, Clone, PartialEq, Default)]
Expand Down Expand Up @@ -203,6 +215,7 @@ fn actual_main() -> dsync::Result<()> {
model_path: args.model_path,
once_common_structs: args.once_common_structs,
once_connection_type: args.once_connection_type,
diesel_backend: args.diesel_backend,
},
)?;

Expand Down
109 changes: 96 additions & 13 deletions src/code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ impl<'a> Struct<'a> {
/// Render the full struct
fn render(&mut self) {
let ty = self.ty;
let table = &self.table;
let table = self.table;

let primary_keys: Vec<String> = table.primary_key_column_names();

Expand Down Expand Up @@ -422,25 +422,62 @@ impl {struct_name} {{
));

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) -> QueryResult<PaginationResult<Self>> {{
/// Paginates through the table where page is a 1-based index (i.e. page 1 is the first page)
pub{async_keyword} fn paginate(db: &mut ConnectionType, param_page_starting_with_1: i64, param_page_size: i64, filter: {struct_name}Filter) -> QueryResult<PaginationResult<Self>> {{
use {schema_path}{table_name}::dsl::*;

let page_size = if page_size < 1 {{ 1 }} else {{ page_size }};
let total_items = {table_name}.count().get_result(db){await_keyword}?;
let items = {table_name}.limit(page_size).offset(page * page_size).load::<Self>(db){await_keyword}?;
let param_page = param_page_starting_with_1.max(0);
let param_page_size = param_page_size.max(1);
let total_items = Self::filter(filter.clone()).count().get_result(db)?;
let items = Self::filter(filter).limit(param_page_size).offset(param_page * param_page_size).load::<Self>(db){await_keyword}?;

Ok(PaginationResult {{
items,
total_items,
page,
page_size,
page: param_page,
page_size: param_page_size,
/* ceiling division of integers */
num_pages: total_items / page_size + i64::from(total_items % page_size != 0)
num_pages: total_items / param_page_size + i64::from(total_items % param_page_size != 0)
}})
}}
"##));

// Table::filter() helper fn
{
let diesel_backend = config.diesel_backend.clone();
let filters = table
.columns
.iter()
.map(|column| {
let column_name = column.name.to_string();
format!(
r##"
if let Some(filter_{column_name}) = filter.{column_name}.clone() {{
query = query.filter({schema_path}{table_name}::{column_name}.eq(filter_{column_name}));
}}"##
)
})
.collect::<Vec<_>>()
.join("");
buffer.push_str(&format!(
r##"
/// A utility function to help build custom search queries
///
/// Example:
///
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
}}
"##
));
}

// TODO: If primary key columns are attached to the form struct (not optionally)
// then don't require item_id_params (otherwise it'll be duplicated)

Expand Down Expand Up @@ -470,10 +507,56 @@ impl {struct_name} {{
));

buffer.push_str(
r##"
}"##,
r#"
}"#,
);

// generate filter struct for filter() helper function
{
let filter_fields = table
.columns
.iter()
.map(|column| {
let column_name = column.name.to_string();
format!(
"pub {column_name}: Option<{column_type}>,",
column_name = column_name,
column_type = if column.is_nullable {
format!("Option<{}>", column.ty)
} else {
column.ty.clone()
}
)
})
.collect::<Vec<_>>()
.join("\n ");
let filter_fields_default = table
.columns
.iter()
.map(|column| {
let column_name = column.name.to_string();
format!("{column_name}: None,")
})
.collect::<Vec<_>>()
.join("\n ");
buffer.push_str(&format!(
r##"
#[derive(Clone)]
pub struct {struct_name}Filter {{
{filter_fields}
}}

impl Default for {struct_name}Filter {{
fn default() -> {struct_name}Filter {{
{struct_name}Filter {{
{filter_fields_default}
}}
}}
}}
"##
));
}

buffer
}

Expand All @@ -500,7 +583,7 @@ pub struct PaginationResult<T> {{
}}
"##,
serde_derive = if table_options.get_serde() {
"Serialize"
"serde::Serialize"
} else {
""
}
Expand All @@ -510,7 +593,7 @@ pub struct PaginationResult<T> {{
/// Generate connection-type type
pub fn generate_connection_type(config: &GenerationConfig) -> String {
format!(
"\ntype ConnectionType = {connection_type};",
"\npub type ConnectionType = {connection_type};",
connection_type = config.connection_type,
)
}
Expand Down
33 changes: 30 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,14 +214,26 @@ impl<'a> Default for TableOptions<'a> {

#[derive(Debug, Clone)]
pub struct GenerationConfig<'a> {
/// Specific Table options for a given table
/// Specific code generation options for a particular table
pub table_options: HashMap<&'a str, TableOptions<'a>>,
/// Default table options, used when not in `table_options`
/// Default table options, can be overriden by `table_options`
pub default_table_options: TableOptions<'a>,
/// Connection type to insert
///
/// Example: `diesel::SqliteConnection`
/// For example:
/// - `diesel::pg::PgConnection` (default)
/// - `diesel::sqlite::SqliteConnection`
/// - `diesel::mysql::MysqlConnection`
/// - or, your custom diesel connection type (struct which implements `diesel::connection::Connection`)
pub connection_type: String,
/// Diesel backend
///
/// For example:
/// - `diesel::pg::Pg` (default)
/// - `diesel::sqlite::Sqlite`
/// - `diesel::mysql::Mysql`
/// - or, your custom diesel backend type (struct which implements `diesel::backend::Backend`)
pub diesel_backend: String,
/// Diesel schema import path
///
/// by default `crate::schema::`
Expand All @@ -236,6 +248,21 @@ pub struct GenerationConfig<'a> {
pub once_connection_type: bool,
}

impl<'a> Default for GenerationConfig<'a> {
fn default() -> Self {
Self {
table_options: Default::default(),
default_table_options: Default::default(),
connection_type: "diesel::pg::PgConnection".into(),
diesel_backend: "diesel::pg::Pg".into(),
schema_path: "crate::schema::".into(),
model_path: "crate::models::".into(),
once_common_structs: true,
once_connection_type: true,
}
}
}

impl GenerationConfig<'_> {
pub fn table(&self, name: &str) -> TableOptions<'_> {
let t = self
Expand Down
57 changes: 46 additions & 11 deletions test/autogenerated_all/models/todos/generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use diesel::QueryResult;
use serde::{Deserialize, Serialize};


type ConnectionType = diesel::r2d2::PooledConnection<diesel::r2d2::ConnectionManager<diesel::PgConnection>>;
pub type ConnectionType = diesel::r2d2::PooledConnection<diesel::r2d2::ConnectionManager<diesel::PgConnection>>;

#[derive(Debug, Serialize, Deserialize, Clone, Queryable, Insertable, AsChangeset, Selectable)]
#[diesel(table_name=todos, primary_key(id))]
Expand All @@ -23,7 +23,7 @@ pub struct UpdateTodos {
}


#[derive(Debug, Serialize)]
#[derive(Debug, serde::Serialize)]
pub struct PaginationResult<T> {
pub items: Vec<T>,
pub total_items: i64,
Expand All @@ -47,24 +47,45 @@ impl Todos {
todos.filter(id.eq(param_id)).first::<Self>(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) -> QueryResult<PaginationResult<Self>> {
/// Paginates through the table where page is a 1-based index (i.e. page 1 is the first page)
pub fn paginate(db: &mut ConnectionType, param_page_starting_with_1: i64, param_page_size: i64, filter: TodosFilter) -> QueryResult<PaginationResult<Self>> {
use crate::schema::todos::dsl::*;

let page_size = if page_size < 1 { 1 } else { page_size };
let total_items = todos.count().get_result(db)?;
let items = todos.limit(page_size).offset(page * page_size).load::<Self>(db)?;
let param_page = param_page_starting_with_1.max(0);
let param_page_size = param_page_size.max(1);
let total_items = Self::filter(filter.clone()).count().get_result(db)?;
let items = Self::filter(filter).limit(param_page_size).offset(param_page * param_page_size).load::<Self>(db)?;

Ok(PaginationResult {
items,
total_items,
page,
page_size,
page: param_page,
page_size: param_page_size,
/* ceiling division of integers */
num_pages: total_items / page_size + i64::from(total_items % page_size != 0)
num_pages: total_items / param_page_size + i64::from(total_items % param_page_size != 0)
})
}

/// A utility function to help build custom search queries
///
/// Example:
///
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.clone() {
query = query.filter(crate::schema::todos::id.eq(filter_id));
}
if let Some(filter_created_at) = filter.created_at.clone() {
query = query.filter(crate::schema::todos::created_at.eq(filter_created_at));
}

query
}

pub fn update(db: &mut ConnectionType, param_id: i32, item: &UpdateTodos) -> QueryResult<Self> {
use crate::schema::todos::dsl::*;

Expand All @@ -77,4 +98,18 @@ impl Todos {
diesel::delete(todos.filter(id.eq(param_id))).execute(db)
}

}
}
#[derive(Clone)]
pub struct TodosFilter {
pub id: Option<i32>,
pub created_at: Option<chrono::NaiveDateTime>,
}

impl Default for TodosFilter {
fn default() -> TodosFilter {
TodosFilter {
id: None,
created_at: None,
}
}
}
Loading