use anyhow::Result;
use std::collections::HashMap;
pub mod activity_log;
pub mod admin;
pub mod api_versioned;
pub mod auth;
pub mod billing;
pub mod comments;
pub mod crud;
pub mod crud_api;
pub mod export;
pub mod flags;
pub mod import;
pub mod multi_tenant;
pub mod newsletter;
pub mod notification;
pub mod ratings;
pub mod search;
pub mod social_auth;
pub mod upload;
pub mod webhook;
pub mod websocket;
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct FieldSpec {
pub name: String,
pub r#type: String,
pub validations: Vec<String>,
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct ScaffoldArgs {
pub name: Option<String>,
pub model: Option<String>,
pub fields: Vec<FieldSpec>,
pub flags: HashMap<String, bool>,
pub dry_run: bool,
pub json: bool,
}
#[allow(dead_code)]
impl ScaffoldArgs {
pub fn name_or(&self, fallback: &str) -> String {
self.name.clone().unwrap_or_else(|| fallback.to_string())
}
pub fn model_or(&self, fallback: &str) -> String {
self.model.clone().unwrap_or_else(|| fallback.to_string())
}
}
#[derive(Debug, Default)]
pub struct ScaffoldResult {
pub files_created: Vec<String>,
pub dirs_created: Vec<String>,
pub warnings: Vec<String>,
}
impl ScaffoldResult {
pub fn print_human(&self) {
for d in &self.dirs_created {
eprintln!("mkdir {d}");
}
for f in &self.files_created {
println!("created {f}");
}
for w in &self.warnings {
eprintln!("warning: {w}");
}
}
pub fn print_json(&self) {
let obj = serde_json::json!({
"status": "ok",
"files_created": self.files_created,
"dirs_created": self.dirs_created,
"warnings": self.warnings,
});
println!("{}", serde_json::to_string_pretty(&obj).unwrap());
}
}
pub trait Scaffold: Send + Sync {
fn name(&self) -> &'static str;
fn description(&self) -> &'static str;
fn generate(&self, args: &ScaffoldArgs) -> Result<ScaffoldResult>;
}
pub struct ScaffoldRegistry {
entries: HashMap<&'static str, Box<dyn Scaffold>>,
}
impl ScaffoldRegistry {
pub fn new() -> Self {
let mut r = Self {
entries: HashMap::new(),
};
r.register(Box::new(auth::AuthScaffold));
r.register(Box::new(crud::CrudScaffold));
r.register(Box::new(crud_api::CrudApiScaffold));
r.register(Box::new(upload::UploadScaffold));
r.register(Box::new(webhook::WebhookScaffold));
r.register(Box::new(search::SearchScaffold));
r.register(Box::new(notification::NotificationScaffold));
r.register(Box::new(billing::BillingScaffold));
r.register(Box::new(social_auth::SocialAuthScaffold));
r.register(Box::new(admin::AdminScaffold));
r.register(Box::new(websocket::WebsocketScaffold));
r.register(Box::new(export::ExportScaffold));
r.register(Box::new(import::ImportScaffold));
r.register(Box::new(multi_tenant::MultiTenantScaffold));
r.register(Box::new(api_versioned::ApiVersionedScaffold));
r.register(Box::new(comments::CommentsScaffold));
r.register(Box::new(ratings::RatingsScaffold));
r.register(Box::new(activity_log::ActivityLogScaffold));
r.register(Box::new(newsletter::NewsletterScaffold));
r.register(Box::new(flags::FlagsScaffold));
r
}
fn register(&mut self, s: Box<dyn Scaffold>) {
self.entries.insert(s.name(), s);
}
pub fn get(&self, name: &str) -> Option<&dyn Scaffold> {
self.entries.get(name).map(|b| b.as_ref())
}
pub fn list(&self) -> Vec<(&'static str, &'static str)> {
let mut list: Vec<_> = self
.entries
.values()
.map(|s| (s.name(), s.description()))
.collect();
list.sort_by_key(|(n, _)| *n);
list
}
}
pub fn write_file(
result: &mut ScaffoldResult,
path: &str,
content: &str,
dry_run: bool,
) -> Result<()> {
if !dry_run {
if let Some(parent) = std::path::Path::new(path).parent() {
if !parent.as_os_str().is_empty() {
std::fs::create_dir_all(parent)?;
let dir = parent.to_string_lossy().to_string();
if !result.dirs_created.contains(&dir) {
result.dirs_created.push(dir);
}
}
}
std::fs::write(path, content)?;
}
result.files_created.push(path.to_string());
Ok(())
}