use clap::Parser;
use handlebars::Handlebars;
use std::collections::HashMap;
use std::fs::{self, File};
use std::io::Write;
use std::path::{Path, PathBuf};
use syn::{Item, ItemMod};
const ROUTE_RS_HBS: &str = include_str!("../../../assets/src/route.rs.hbs");
#[derive(Debug, Parser)]
pub struct Opts {
#[arg(value_name = "name")]
pub name: String,
}
pub async fn run(name: String) -> anyhow::Result<()> {
let mut handlebars = Handlebars::default();
const NAME: &str = "default";
handlebars.register_template_string(NAME, ROUTE_RS_HBS)?;
let data = HashMap::from([("name", struct_name(&name))]);
let contents = handlebars.render(NAME, &data)?;
let routes_dir = PathBuf::new().join("src").join("routes");
fs::create_dir_all(&routes_dir)?;
let modulename = change_case::snake_case(&name);
let filename = format!("{modulename}.rs");
let relative_path = routes_dir.join(&filename);
let root_dir = Path::new(".");
let path = root_dir.join(&relative_path);
let mut file = File::create(path)?;
file.write_all(contents.as_bytes())?;
eprintln!("created {}", relative_path.display());
let relative_path = routes_dir.join("mod.rs");
let path = root_dir.join(&relative_path);
let code = fs::read_to_string(&path)?;
let new_code = {
let syntax = syn::parse_file(&code)?;
let mods = find_mods(&syntax);
if !mods.iter().any(|item_mod| item_mod.ident == modulename) {
let mut new_line = format!("pub mod {modulename};\n");
if code.starts_with("use ") {
new_line = format!("{new_line}\n");
}
format!("{new_line}{code}")
} else {
code.clone()
}
};
if code != new_code {
fs::write(&path, new_code)?;
eprintln!("updated {}", relative_path.display());
}
super::template::run(name).await?;
Ok(())
}
fn struct_name(name: &str) -> String {
format!("{}Route", change_case::pascal_case(&name.replace(".", "_")))
}
fn find_mods(syntax: &syn::File) -> Vec<ItemMod> {
syntax
.items
.iter()
.filter_map(|item| match item {
Item::Mod(impl_) => Some(impl_.clone()),
_ => None,
})
.collect()
}
#[cfg(test)]
mod tests {
use crate::cmd::generate::route::struct_name;
#[test]
fn can_generate_struct_name() {
assert_eq!(struct_name("index"), "IndexRoute");
assert_eq!(struct_name("foo"), "FooRoute");
assert_eq!(struct_name("foo.bar"), "FooBarRoute");
assert_eq!(struct_name("foo.bar.baz.index"), "FooBarBazIndexRoute");
}
}