diff --git a/src/bin/main.rs b/src/bin/main.rs index 6923d685..731cad8e 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -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; @@ -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 @@ -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, + }, }, )?; diff --git a/src/code.rs b/src/code.rs index 2dfe9c11..280bb35c 100644 --- a/src/code.rs +++ b/src/code.rs @@ -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'); } @@ -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")] @@ -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)); }; diff --git a/src/global.rs b/src/global.rs index 1022836e..6b8b31e7 100644 --- a/src/global.rs +++ b/src/global.rs @@ -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>` - /// - or, your custom diesel connection type (struct which implements `diesel::connection::Connection`) - pub connection_type: String, /// Diesel schema import path /// /// by default `crate::schema::` @@ -338,19 +328,9 @@ pub struct GenerationConfig<'a> { pub readonly_prefixes: Vec, /// Suffixes to treat tables as readonly pub readonly_suffixes: Vec, - - #[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 @@ -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>` + /// - 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>(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, B: Into>(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}; diff --git a/src/lib.rs b/src/lib.rs index 744cba30..920dce00 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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}; @@ -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)); @@ -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(); diff --git a/src/parser.rs b/src/parser.rs index b2d3e3d1..9e742507 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -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);