const ROUTES_BASE: &str = include_str!("../../templates/new/config/routes.rs");
const CONTROLLERS_MOD_BASE: &str = include_str!("../../templates/new/app/controllers/mod.rs");
const MODELS_MOD_BASE: &str = include_str!("../../templates/new/app/models/mod.rs");
pub const ROUTES_PATH: &str = "config/routes.rs";
pub const CONTROLLERS_MOD_PATH: &str = "app/controllers/mod.rs";
pub const MODELS_MOD_PATH: &str = "app/models/mod.rs";
fn is_routes_block_open(line: &str) -> bool {
let t = line.trim();
(t.starts_with("routes!") || t.starts_with("doido::auth::routes!")) && t.contains('{')
}
pub fn inject_auth_routes(routes: &str, _api: bool) -> String {
if routes.contains("auth_routes!(User") {
return routes.to_string();
}
let auth_block = " doido::auth::routes! {";
let auth_line = "auth_routes!(User, controllers: { \
sessions: auth::SessionsController, \
registrations: auth::RegistrationsController, \
passwords: auth::PasswordsController, \
oauth: auth::OauthController \
});";
let mut lines: Vec<String> = routes.lines().map(String::from).collect();
if routes.contains("routes! {") && !routes.contains("doido::auth::routes!") {
for line in &mut lines {
if line.trim().starts_with("routes! {") {
*line = auth_block.to_string();
}
}
}
if routes.contains("use doido::controller::{axum, routes}") {
for line in &mut lines {
if line.contains("use doido::controller::{axum, routes}") {
*line = "use doido::controller::axum;".to_string();
}
}
}
let auth_use = "use crate::controllers::auth;";
if !routes.contains(auth_use) {
let pos = lines
.iter()
.rposition(|l| l.contains("use crate::controllers"))
.map(|i| i + 1)
.unwrap_or(0);
lines.insert(pos, auth_use.to_string());
}
if let Some(open) = lines.iter().position(|l| is_routes_block_open(l)) {
if let Some(close_rel) = lines[open..].iter().position(|l| l.trim() == "}") {
let close = open + close_rel;
lines.insert(close, format!(" {auth_line}"));
}
}
let mut out = lines.join("\n");
out.push('\n');
out
}
pub fn inject_resources(routes: &str, plural: &str, controller: &str, api: bool) -> String {
let resources = if api {
format!("resources!({plural}, {controller}, except: [new, edit]);")
} else {
format!("resources!({plural}, {controller});")
};
if routes.contains(&resources) {
return routes.to_string();
}
let use_line = format!("use crate::controllers::{controller};");
let mut lines: Vec<String> = routes.lines().map(String::from).collect();
if !routes.contains(&use_line) {
let pos = lines
.iter()
.rposition(|l| l.contains("use crate::controllers"))
.map(|i| i + 1)
.unwrap_or(0);
lines.insert(pos, use_line);
}
if let Some(open) = lines.iter().position(|l| is_routes_block_open(l)) {
if let Some(close_rel) = lines[open..].iter().position(|l| l.trim() == "}") {
let close = open + close_rel;
lines.insert(close, format!(" {resources}"));
}
}
let mut out = lines.join("\n");
out.push('\n');
out
}
pub fn inject_action_routes(
routes: &str,
snake: &str,
controller: &str,
actions: &[&str],
) -> String {
let use_line = format!("use crate::controllers::{controller};");
let mut lines: Vec<String> = routes.lines().map(String::from).collect();
if !routes.contains(&use_line) {
let pos = lines
.iter()
.rposition(|l| l.contains("use crate::controllers"))
.map(|i| i + 1)
.unwrap_or(0);
lines.insert(pos, use_line);
}
if let Some(open) = lines.iter().position(|l| is_routes_block_open(l)) {
if let Some(close_rel) = lines[open..].iter().position(|l| l.trim() == "}") {
let close = open + close_rel;
for action in actions {
let path = format!("/{snake}/{action}");
let route = format!("get!(\"{path}\", {controller}::{action});");
if !routes.contains(&route) {
lines.insert(close, format!(" {route}"));
}
}
}
}
let mut out = lines.join("\n");
out.push('\n');
out
}
pub fn register_controller(controllers_mod: &str, plural: &str, controller: &str) -> String {
let module = format!("{plural}_controller");
let decl = format!("mod {module};");
if controllers_mod.lines().any(|l| l.trim() == decl) {
return controllers_mod.to_string();
}
let mut out = controllers_mod.trim_end().to_string();
out.push('\n');
out.push_str(&format!("mod {module};\n"));
out.push_str(&format!("pub use {module}::{controller};\n"));
out
}
pub fn register_auth_controllers_mod(controllers_mod: &str) -> String {
let decl = "pub mod auth;";
if controllers_mod.contains(decl) {
return controllers_mod.to_string();
}
let mut out = controllers_mod.trim_end().to_string();
out.push('\n');
out.push_str(&format!("{decl}\n"));
out
}
pub fn register_model_module(models_mod: &str, module: &str) -> String {
super::register_module(models_mod, module, "@generated-models")
}
pub fn read_routes() -> String {
std::fs::read_to_string(ROUTES_PATH).unwrap_or_else(|_| ROUTES_BASE.to_string())
}
pub fn read_controllers_mod() -> String {
std::fs::read_to_string(CONTROLLERS_MOD_PATH)
.unwrap_or_else(|_| CONTROLLERS_MOD_BASE.to_string())
}
pub fn read_models_mod() -> String {
std::fs::read_to_string(MODELS_MOD_PATH).unwrap_or_else(|_| MODELS_MOD_BASE.to_string())
}