Skip to content

Commit

Permalink
feat: refactor GenerationConfig to have default-able fields
Browse files Browse the repository at this point in the history
by separating out option fields from required fields and using supporting top-level builder pattern

fixes #92
  • Loading branch information
hasezoey committed Jan 16, 2024
1 parent dddd6de commit 1a88081
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 47 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
- add derive `PartialEq` to update-structs
- add new experimental filters (behind the `advanced-queries` feature flag)
- move function `paginate` behind `advanced-queries` feature flag
- split `GenerationConfig` into required and optional parts (`GenerationConfigOpts`) (fixes #92)

## 0.0.17 (yanked)

Expand Down
24 changes: 13 additions & 11 deletions src/bin/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use clap::{CommandFactory, Parser, Subcommand, ValueEnum};
use clap_complete::{generate, Shell};
use dsync::{error::IOErrorToError, GenerationConfig, TableOptions};
use dsync::{BytesType, FileChangeStatus, StringType};
use dsync::{BytesType, FileChangeStatus, GenerationConfigOpts, StringType};
use std::collections::HashMap;
use std::io::{BufWriter, Write};
use std::path::PathBuf;
Expand Down Expand Up @@ -79,11 +79,11 @@ pub struct MainOptions {
pub no_serde: bool,

/// Set custom schema use path
#[arg(long = "schema-path", default_value = "crate::schema::")]
#[arg(long = "schema-path", default_value = dsync::DEFAULT_SCHEMA_PATH)]
pub schema_path: String,

/// Set custom model use path
#[arg(long = "model-path", default_value = "crate::models::")]
#[arg(long = "model-path", default_value = dsync::DEFAULT_MODEL_PATH)]
pub model_path: String,

/// Do not generate the CRUD (impl) functions for generated models
Expand Down Expand Up @@ -253,17 +253,19 @@ fn actual_main() -> dsync::Result<()> {
&args.input,
&args.output,
GenerationConfig {
default_table_options,
table_options: HashMap::from([]),
connection_type: args.connection_type,
schema_path: args.schema_path,
model_path: args.model_path,
once_common_structs: args.once_common_structs,
once_connection_type: args.once_connection_type,
readonly_prefixes: args.readonly_prefixes,
readonly_suffixes: args.readonly_suffixes,
#[cfg(feature = "advanced-queries")]
diesel_backend: args.diesel_backend,
options: GenerationConfigOpts {
default_table_options,
table_options: HashMap::from([]),
schema_path: args.schema_path,
model_path: args.model_path,
once_common_structs: args.once_common_structs,
once_connection_type: args.once_connection_type,
readonly_prefixes: args.readonly_prefixes,
readonly_suffixes: args.readonly_suffixes,
},
},
)?;

Expand Down
14 changes: 7 additions & 7 deletions src/code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -475,14 +475,14 @@ fn build_table_fns(
let (async_keyword, await_keyword) = get_async(&table_options);

let struct_name = &table.struct_name;
let schema_path = &config.schema_path;
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();

if !config.once_common_structs {
if !config.get_once_common_structs() {
buffer.push_str(&generate_common_structs(&table_options));
buffer.push('\n');
}
Expand Down Expand Up @@ -737,7 +737,7 @@ fn build_imports(table: &ParsedTableMacro, config: &GenerationConfig) -> String
"use {model_path}{foreign_table_name_model}::{singular_struct_name};",
foreign_table_name_model = get_table_module_name(&fk.0.to_string()),
singular_struct_name = fk.0.to_string().to_pascal_case(),
model_path = config.model_path
model_path = config.get_model_path()
)
}));
#[cfg(feature = "async")]
Expand All @@ -746,14 +746,14 @@ fn build_imports(table: &ParsedTableMacro, config: &GenerationConfig) -> String
}

// no "::" because that is already included in the schema_path
imports_vec.push(format!("use {}*;", config.schema_path));
imports_vec.push(format!("use {}*;", config.get_schema_path()));

if config.once_common_structs || config.once_connection_type {
imports_vec.push(format!("use {}common::*;", config.model_path));
if config.any_once_option() {
imports_vec.push(format!("use {}common::*;", config.get_model_path()));
};

// this needs to be last, because it not really is a import, so it would split the import sections
if table_options.get_fns() && !config.once_connection_type {
if table_options.get_fns() && !config.get_once_connection_type() {
imports_vec.push(String::new());
imports_vec.push(generate_connection_type(config));
};
Expand Down
157 changes: 135 additions & 22 deletions src/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,22 +306,12 @@ impl<'a> Default for TableOptions<'a> {
}
}

/// Global config, not table specific
#[derive(Debug, Clone)]
pub struct GenerationConfig<'a> {
pub struct GenerationConfigOpts<'a> {
/// Specific Table options for a given table
pub table_options: HashMap<&'a str, TableOptions<'a>>,
/// Default table options, used when not in `table_options`
pub default_table_options: TableOptions<'a>,
/// Connection type to insert
///
/// For example:
/// - `diesel::pg::PgConnection`
/// - `diesel::sqlite::SqliteConnection`
/// - `diesel::mysql::MysqlConnection`
/// - `diesel::r2d2::PooledConnection<diesel::r2d2::ConnectionManager<diesel::pg::PgConnection>>`
/// - or, your custom diesel connection type (struct which implements `diesel::connection::Connection`)
pub connection_type: String,
/// Diesel schema import path
///
/// by default `crate::schema::`
Expand All @@ -338,19 +328,9 @@ pub struct GenerationConfig<'a> {
pub readonly_prefixes: Vec<String>,
/// Suffixes to treat tables as readonly
pub readonly_suffixes: Vec<String>,

#[cfg(feature = "advanced-queries")]
/// 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,
}

impl GenerationConfig<'_> {
impl GenerationConfigOpts<'_> {
pub fn table(&self, name: &str) -> TableOptions<'_> {
let table = self
.table_options
Expand All @@ -369,6 +349,139 @@ impl GenerationConfig<'_> {
}
}

pub const DEFAULT_SCHEMA_PATH: &str = "crate::schema::";
pub const DEFAULT_MODEL_PATH: &str = "crate::models::";

impl Default for GenerationConfigOpts<'_> {
fn default() -> Self {
Self {
table_options: HashMap::default(),
default_table_options: Default::default(),
schema_path: String::from(DEFAULT_SCHEMA_PATH),
model_path: String::from(DEFAULT_MODEL_PATH),
once_common_structs: false,
once_connection_type: false,
readonly_prefixes: Vec::default(),
readonly_suffixes: Vec::default(),
}
}
}

/// Global config, not table specific
#[derive(Debug, Clone)]
pub struct GenerationConfig<'a> {
/// Connection type to insert
///
/// For example:
/// - `diesel::pg::PgConnection`
/// - `diesel::sqlite::SqliteConnection`
/// - `diesel::mysql::MysqlConnection`
/// - `diesel::r2d2::PooledConnection<diesel::r2d2::ConnectionManager<diesel::pg::PgConnection>>`
/// - or, your custom diesel connection type (struct which implements `diesel::connection::Connection`)
pub connection_type: String,

#[cfg(feature = "advanced-queries")]
/// 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,

/// Optional Options
/// ```
/// # use dsync::{GenerationConfig,GenerationConfigOpts};
/// GenerationConfig {
/// // ... all required options
/// # connection_type: String::default(),
/// options: Default::default(),
/// };
/// // or
/// GenerationConfig {
/// // ... all required options
/// # connection_type: String::default(),
/// options: GenerationConfigOpts {
/// ..Default::default()
/// },
/// };
/// ```
pub options: GenerationConfigOpts<'a>,
}

impl<'a> GenerationConfig<'a> {
#[cfg(not(feature = "advanced-queries"))]
/// Create a new Instance with default [GenerationConfigOpts]
///
/// Builder
pub fn new<C: Into<String>>(connection_type: C) -> Self {
Self {
connection_type: connection_type.into(),
options: GenerationConfigOpts::default(),
}
}

#[cfg(feature = "advanced-queries")]
/// Create a new Instance with default [GenerationConfigOpts]
///
/// Builder
pub fn new<C: Into<String>, B: Into<String>>(connection_type: C, diesel_backend: B) -> Self {
Self {
connection_type: connection_type.into(),
diesel_backend: diesel_backend.into(),
options: GenerationConfigOpts::default(),
}
}

/// Replace the options with the new options
///
/// Builder
#[inline]
pub fn with_options(mut self, options: GenerationConfigOpts<'a>) -> Self {
self.options = options;

self
}

// Wrapper for [GenerationConfigOpts::table()]
#[inline]
pub fn table(&self, name: &str) -> TableOptions<'_> {
self.options.table(name)
}

#[inline]
pub fn get_schema_path(&self) -> &str {
&self.options.schema_path
}

#[inline]
pub fn get_model_path(&self) -> &str {
&self.options.model_path
}

#[inline]
pub fn get_once_common_structs(&self) -> bool {
self.options.once_common_structs
}

#[inline]
pub fn get_once_connection_type(&self) -> bool {
self.options.once_connection_type
}

#[inline]
pub fn get_default_table_options(&self) -> &TableOptions<'_> {
&self.options.default_table_options
}

/// Get if any of the "once-*" options is active / if the common-file is active
#[inline]
pub fn any_once_option(&self) -> bool {
self.get_once_common_structs() || self.get_once_connection_type()
}
}

#[cfg(feature = "advanced-queries")]
pub fn validate_config(config: &GenerationConfig) -> crate::Result<()> {
use crate::error::{Error, ErrorEnum};
Expand Down
15 changes: 9 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ mod file;
mod global;
mod parser;

pub use global::{BytesType, GenerationConfig, StringType, TableOptions};
pub use global::{
BytesType, GenerationConfig, GenerationConfigOpts, StringType, TableOptions,
DEFAULT_MODEL_PATH, DEFAULT_SCHEMA_PATH,
};

use error::IOErrorToError;
pub use error::{Error, Result};
Expand Down Expand Up @@ -131,17 +134,17 @@ pub fn generate_files(
// check that the mod.rs file exists
let mut mod_rs = MarkedFile::new(output_models_dir.join("mod.rs"))?;

if config.once_common_structs || config.once_connection_type {
if config.any_once_option() {
let mut common_file = MarkedFile::new(output_models_dir.join("common.rs"))?;
common_file.ensure_file_signature()?;
common_file.change_file_contents({
let mut tmp = format!("{FILE_SIGNATURE}\n");
if config.once_common_structs {
if config.get_once_common_structs() {
tmp.push_str(&code::generate_common_structs(
&config.default_table_options,
config.get_default_table_options(),
));
}
if config.once_connection_type {
if config.get_once_connection_type() {
tmp.push('\n');
tmp.push_str(&code::generate_connection_type(&config));

Expand All @@ -160,7 +163,7 @@ pub fn generate_files(

// pass 1: add code for new tables
for table in generated.iter() {
if config.once_common_structs && table.name == "common" {
if config.get_once_common_structs() && table.name == "common" {
return Err(Error::other("Cannot have a table named \"common\" while having option \"once_common_structs\" enabled"));
}
let table_name = table.name.to_string();
Expand Down
2 changes: 1 addition & 1 deletion src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ fn schema_type_to_rust_type(schema_type: String, config: &GenerationConfig) -> R
_ => panic!("Unknown type found '{schema_type}', please report this!")
*/
_ => {
let schema_path = &config.schema_path;
let schema_path = config.get_schema_path();
// return the schema type if no type is found (this means generation is broken for this particular schema)
let _type = format!("{schema_path}sql_types::{schema_type}");
return Ok(_type);
Expand Down

0 comments on commit 1a88081

Please sign in to comment.