use anyhow::Result;
use clap::{Parser, Subcommand, ValueEnum};
use std::path::PathBuf;
mod auth_ops;
mod dev_workflow;
mod doctor;
mod generator;
mod governance;
mod project_model;
mod project_template;
mod release_ops;
mod topology;
use crate::auth_ops::dispatch_auth_action;
use crate::dev_workflow::{
bootstrap_local_stack, build_project, dev_project, generate_docs, validate_environment,
watch_project,
};
use crate::doctor::dispatch_doctor_command;
use crate::generator::dispatch_gen_resource;
use crate::governance::{
dispatch_contract_action, dispatch_db_action, dispatch_release_action, dispatch_security_action,
};
use crate::project_template::generate_project_from_template;
use crate::topology::dispatch_topology_action;
#[derive(Parser)]
#[command(name = "krab")]
#[command(about = "Krab Framework CLI", long_about = None)]
#[command(version)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
New {
name: String,
#[arg(long, value_enum, default_value_t = ProjectTemplate::Default)]
template: ProjectTemplate,
#[arg(long, value_name = "KRAB_REPO_ROOT")]
path_deps: Option<PathBuf>,
},
Build {
#[arg(long)]
release: bool,
#[arg(long, value_enum, default_value_t = BuildTarget::All)]
target: BuildTarget,
#[arg(long)]
diagnostics: bool,
},
Dev {
#[arg(long)]
release: bool,
#[arg(long)]
watch: bool,
#[arg(long, default_value_t = 800)]
poll_ms: u64,
#[arg(long, default_value_t = 250)]
settle_ms: u64,
},
Watch {
#[arg(long)]
release: bool,
#[arg(long, default_value_t = 800)]
poll_ms: u64,
#[arg(long, default_value_t = 250)]
settle_ms: u64,
},
Docs {
#[arg(long, default_value = "docs/guides/dev_workflow.md")]
out: PathBuf,
},
Bootstrap {
#[arg(long)]
release: bool,
#[arg(long)]
skip_build: bool,
},
EnvCheck {
#[arg(long)]
strict: bool,
},
Contract {
#[command(subcommand)]
action: ContractAction,
},
Db {
#[command(subcommand)]
action: DbAction,
},
Security {
#[command(subcommand)]
action: SecurityAction,
},
Auth {
#[command(subcommand)]
action: AuthAction,
},
Release {
#[command(subcommand)]
action: ReleaseAction,
},
Doctor {
#[arg(long)]
diagnostics: bool,
#[arg(long)]
strict: bool,
},
Topology {
#[command(subcommand)]
action: TopologyAction,
},
Gen {
#[command(subcommand)]
resource: GenResource,
},
}
#[derive(Subcommand)]
enum ContractAction {
Check {
#[arg(long)]
diagnostics: bool,
},
ProtocolCheck {
#[arg(long)]
diagnostics: bool,
},
}
#[derive(Subcommand)]
enum DbAction {
Lifecycle {
#[arg(long)]
diagnostics: bool,
},
Rollback {
#[arg(long)]
diagnostics: bool,
},
Drift {
#[arg(long)]
diagnostics: bool,
},
Rehearsal {
#[arg(
long,
default_value = "internal/audit/evidence/rollback-rehearsal-evidence.txt"
)]
out: PathBuf,
#[arg(long)]
diagnostics: bool,
},
}
#[derive(Subcommand)]
enum SecurityAction {
DependencyGate {
#[arg(long)]
diagnostics: bool,
},
}
#[derive(Subcommand)]
enum AuthAction {
HashPassword {
#[arg(long, value_name = "PASSWORD")]
password: Option<String>,
#[arg(long, value_name = "USERNAME")]
username: Option<String>,
},
}
#[derive(Subcommand)]
enum ReleaseAction {
Check {
#[arg(long)]
diagnostics: bool,
#[arg(long)]
json: bool,
},
Certify {
#[arg(long, default_value = "release-evidence")]
out: PathBuf,
#[arg(long)]
diagnostics: bool,
#[arg(long)]
json: bool,
},
}
#[derive(Subcommand)]
enum TopologyAction {
Doctor {
#[arg(long)]
diagnostics: bool,
},
Split {
domain: String,
#[arg(long, value_delimiter = ',')]
protocols: Option<Vec<ServiceType>>,
#[arg(long)]
register: bool,
#[arg(long)]
dry_run: bool,
},
}
#[derive(Subcommand)]
enum GenResource {
Service {
name: String,
#[arg(long, value_enum)]
r#type: ServiceType,
#[arg(long, value_enum, default_value_t = ExposureMode::Single)]
exposure_mode: ExposureMode,
#[arg(long, value_delimiter = ',')]
protocols: Option<Vec<ServiceType>>,
#[arg(long, value_enum, default_value_t = Topology::SingleService)]
topology: Topology,
},
Component {
name: String,
},
Route {
name: String,
},
ServerFunction {
name: String,
},
}
#[derive(Clone, ValueEnum, Debug, PartialEq, Eq)]
enum ServiceType {
Rest,
Graphql,
Rpc,
Grpc,
}
#[derive(Clone, ValueEnum, Debug, PartialEq, Eq)]
enum ExposureMode {
Single,
Multi,
}
#[derive(Clone, ValueEnum, Debug, PartialEq, Eq)]
enum Topology {
SingleService,
SplitServices,
}
#[derive(Clone, ValueEnum, Debug, PartialEq, Eq)]
enum ProjectTemplate {
Default,
Saas,
EdgeSsr,
EventStream,
}
#[derive(Clone, ValueEnum, Debug)]
enum BuildTarget {
All,
Frontend,
Client,
}
fn main() -> Result<()> {
let cli = Cli::parse();
dispatch_command(&cli.command)
}
fn dispatch_command(command: &Commands) -> Result<()> {
match command {
Commands::Build {
release,
target,
diagnostics,
} => {
build_project(*release, target, *diagnostics)?;
}
Commands::Dev {
release,
watch,
poll_ms,
settle_ms,
} => {
if *watch {
watch_project(*release, *poll_ms, *settle_ms)?;
} else {
dev_project(*release)?;
}
}
Commands::Watch {
release,
poll_ms,
settle_ms,
} => {
watch_project(*release, *poll_ms, *settle_ms)?;
}
Commands::Docs { out } => {
generate_docs(out)?;
}
Commands::Bootstrap {
release,
skip_build,
} => {
bootstrap_local_stack(*release, *skip_build)?;
}
Commands::EnvCheck { strict } => {
validate_environment(*strict)?;
}
Commands::Contract { action } => dispatch_contract_action(action)?,
Commands::Db { action } => dispatch_db_action(action)?,
Commands::Security { action } => dispatch_security_action(action)?,
Commands::Auth { action } => dispatch_auth_action(action)?,
Commands::Release { action } => dispatch_release_action(action)?,
Commands::Doctor {
diagnostics,
strict,
} => dispatch_doctor_command(*diagnostics, *strict)?,
Commands::Topology { action } => dispatch_topology_action(action)?,
Commands::Gen { resource } => dispatch_gen_resource(resource)?,
Commands::New {
name,
template,
path_deps,
} => {
generate_project_from_template(name, template, path_deps.as_deref())?;
}
}
Ok(())
}