mod naming;
pub mod project;
mod source;
mod templates;
use std::path::{Path, PathBuf};
use naming::Names;
use source::{ensure_items, ensure_module_import, write_generated, write_module_shell};
use crate::CliError;
#[derive(Debug, Default)]
pub struct GenerationReport {
pub created: Vec<PathBuf>,
pub unchanged: Vec<PathBuf>,
pub manual_instructions: Vec<String>,
}
pub fn generate_module(root: &Path, name: &str) -> Result<GenerationReport, CliError> {
let names = Names::parse(name)?;
let module_dir = root.join("src/modules").join(&names.snake);
let mut report = GenerationReport::default();
record(
&mut report,
&module_dir.join("mod.rs"),
write_module_shell(&module_dir.join("mod.rs"), &names.pascal)?,
);
register_root_module(root, &names, &mut report)?;
ensure_main_registration(root, &mut report);
ensure_app_import(root, &names, &mut report);
Ok(report)
}
pub fn generate_controller(root: &Path, name: &str) -> Result<GenerationReport, CliError> {
let names = Names::parse(name)?;
let mut report = generate_module(root, name)?;
let module_dir = root.join("src/modules").join(&names.snake);
let controller_dir = module_dir.join("controller");
let file_name = format!("{}_controller.rs", names.snake);
let path = controller_dir.join(&file_name);
record(
&mut report,
&path,
write_generated(&path, &templates::controller(&names))?,
);
write_generated(
&controller_dir.join("mod.rs"),
&templates::controller_mod(&names),
)?;
ensure_items(
&module_dir.join("mod.rs"),
&[
"pub mod controller;",
&format!("pub use controller::{}Controller;", names.pascal),
],
)?;
report.manual_instructions.push(format!(
"add `{}Controller` to `controllers = [...]` on `{}Module`",
names.pascal, names.pascal
));
Ok(report)
}
pub fn generate_service(root: &Path, name: &str) -> Result<GenerationReport, CliError> {
let names = Names::parse(name)?;
let mut report = generate_module(root, name)?;
let module_dir = root.join("src/modules").join(&names.snake);
let services_dir = module_dir.join("services");
let path = services_dir.join(format!("{}_service.rs", names.snake));
record(
&mut report,
&path,
write_generated(&path, &templates::service(&names))?,
);
write_generated(
&services_dir.join("mod.rs"),
&templates::services_mod(&names),
)?;
ensure_items(
&module_dir.join("mod.rs"),
&[
"pub mod services;",
&format!("pub use services::{}Service;", names.pascal),
],
)?;
report.manual_instructions.push(format!(
"add `{}Service` to `providers = [...]` on `{}Module`",
names.pascal, names.pascal
));
Ok(report)
}
pub fn generate_resource(root: &Path, name: &str) -> Result<GenerationReport, CliError> {
let names = Names::parse(name)?;
let module_dir = root.join("src/modules").join(&names.snake);
let controller_dir = module_dir.join("controller");
let services_dir = module_dir.join("services");
let dto_dir = module_dir.join("dto");
let entities_dir = module_dir.join("entities");
let tests_dir = module_dir.join("tests");
let mut report = GenerationReport::default();
let files = [
(
module_dir.join("mod.rs"),
templates::resource_module(&names),
),
(tests_dir.join("mod.rs"), templates::test_mod(&names)),
(tests_dir.join("unit.rs"), templates::test_unit(&names)),
(
tests_dir.join("integration.rs"),
templates::test_integration(&names),
),
(
controller_dir.join("mod.rs"),
templates::controller_mod(&names),
),
(
controller_dir.join(format!("{}_controller.rs", names.snake)),
templates::resource_controller(&names),
),
(services_dir.join("mod.rs"), templates::services_mod(&names)),
(
services_dir.join(format!("{}_service.rs", names.snake)),
templates::service(&names),
),
(dto_dir.join("mod.rs"), templates::dto_mod(&names)),
(
dto_dir.join(format!("create_{}_dto.rs", names.snake)),
templates::create_dto(&names),
),
(
dto_dir.join(format!("update_{}_dto.rs", names.snake)),
templates::update_dto(&names),
),
(entities_dir.join("mod.rs"), templates::entities_mod(&names)),
(
entities_dir.join(format!("{}.rs", names.snake)),
templates::entity(&names),
),
];
for (path, contents) in files {
let state = write_generated(&path, &contents)?;
record(&mut report, &path, state);
}
register_root_module(root, &names, &mut report)?;
ensure_main_registration(root, &mut report);
ensure_app_import(root, &names, &mut report);
Ok(report)
}
pub fn generate_decorator(root: &Path, name: &str) -> Result<GenerationReport, CliError> {
let names = Names::parse(name)?;
single_file(
root,
&format!("{}_decorator.rs", names.snake),
&templates::decorator(&names),
)
}
pub fn generate_filter(root: &Path, name: &str) -> Result<GenerationReport, CliError> {
let names = Names::parse(name)?;
single_file(
root,
&format!("{}_filter.rs", names.snake),
&templates::filter(&names),
)
}
pub fn generate_gateway(root: &Path, name: &str) -> Result<GenerationReport, CliError> {
let names = Names::parse(name)?;
single_file(
root,
&format!("{}_gateway.rs", names.snake),
&templates::gateway(&names),
)
}
pub fn generate_guard(root: &Path, name: &str) -> Result<GenerationReport, CliError> {
let names = Names::parse(name)?;
single_file(
root,
&format!("{}_guard.rs", names.snake),
&templates::guard(&names),
)
}
pub fn generate_interceptor(root: &Path, name: &str) -> Result<GenerationReport, CliError> {
let names = Names::parse(name)?;
single_file(
root,
&format!("{}_interceptor.rs", names.snake),
&templates::interceptor(&names),
)
}
pub fn generate_middleware(root: &Path, name: &str) -> Result<GenerationReport, CliError> {
let names = Names::parse(name)?;
single_file(
root,
&format!("{}_middleware.rs", names.snake),
&templates::middleware(&names),
)
}
pub fn generate_pipe(root: &Path, name: &str) -> Result<GenerationReport, CliError> {
let names = Names::parse(name)?;
single_file(
root,
&format!("{}_pipe.rs", names.snake),
&templates::pipe(&names),
)
}
pub fn generate_provider(root: &Path, name: &str) -> Result<GenerationReport, CliError> {
let names = Names::parse(name)?;
single_file(
root,
&format!("{}_provider.rs", names.snake),
&templates::provider(&names),
)
}
fn single_file(root: &Path, file_name: &str, contents: &str) -> Result<GenerationReport, CliError> {
let mut report = GenerationReport::default();
let path = root.join("src").join(file_name);
let state = write_generated(&path, contents)?;
record(&mut report, &path, state);
Ok(report)
}
fn register_root_module(
root: &Path,
names: &Names,
report: &mut GenerationReport,
) -> Result<(), CliError> {
let registry = root.join("src/modules/mod.rs");
let changed = ensure_items(®istry, &[&format!("pub mod {};", names.snake)])?;
record(report, ®istry, changed);
Ok(())
}
fn ensure_main_registration(root: &Path, report: &mut GenerationReport) {
let main = root.join("src/main.rs");
if !main.is_file() {
report
.manual_instructions
.push("add `mod modules;` to your crate root".to_owned());
return;
}
if let Err(error) = ensure_items(&main, &["mod modules;"]) {
report.manual_instructions.push(format!(
"add `mod modules;` to `{}` ({error})",
main.display()
));
}
}
fn ensure_app_import(root: &Path, names: &Names, report: &mut GenerationReport) {
let app = root.join("src/app.rs");
let import = format!("crate::modules::{}::{}Module", names.snake, names.pascal);
if !app.is_file() {
report.manual_instructions.push(format!(
"add `{import}` to your root module's `imports = [...]`"
));
return;
}
match ensure_module_import(&app, &import) {
Ok(changed) => record(report, &app, changed),
Err(error) => report.manual_instructions.push(format!(
"add `{import}` to `imports = [...]` in `{}` ({error})",
app.display()
)),
}
}
fn record(report: &mut GenerationReport, path: &Path, changed: bool) {
if changed {
report.created.push(path.to_owned());
} else {
report.unchanged.push(path.to_owned());
}
}