use clap::{Command, Arg}; use anyhow::Result; mod config; mod env_parser; mod validation; mod secrets; mod framework; mod commands; use commands::{scan, validate, generate, secrets_cmd, init, check}; fn main() -> Result<()> { let matches = Command::new("env-guard") .version("0.1.0") .about("Automatically detect, validate, and secure environment variables") .subcommand_required(false) .arg_required_else_help(true) .subcommand( Command::new("scan") .about("Scan .env files and compare against expected variables") .arg( Arg::new("path") .short('p') .long("path") .value_name("PATH") .help("Path to scan for .env files") .default_value(".") ) .arg( Arg::new("schema") .short('s') .long("schema") .value_name("FILE") .help("Path to schema file (.env.schema.json)") ) ) .subcommand( Command::new("validate") .about("Validate format of environment variable values") .arg( Arg::new("path") .short('p') .long("path") .value_name("FILE") .help("Path to .env file") .default_value(".env") ) .arg( Arg::new("strict") .short('S') .long("strict") .help("Enable strict validation") .action(clap::ArgAction::SetTrue) ) ) .subcommand( Command::new("generate") .about("Generate .env.example file from .env") .arg( Arg::new("path") .short('p') .long("path") .value_name("FILE") .help("Path to .env file") .default_value(".env") ) .arg( Arg::new("output") .short('o') .long("output") .value_name("FILE") .help("Output file path") .default_value(".env.example") ) ) .subcommand( Command::new("secrets") .about("Scan source code for accidentally committed secrets") .arg( Arg::new("path") .short('p') .long("path") .value_name("PATH") .help("Path to scan for secrets") .default_value(".") ) .arg( Arg::new("strict") .short('S') .long("strict") .help("Enable strict secret detection") .action(clap::ArgAction::SetTrue) ) ) .subcommand( Command::new("init") .about("Initialize env-guard with framework detection") .arg( Arg::new("framework") .short('f') .long("framework") .value_name("FRAMEWORK") .help("Framework to use (nextjs, rails, django, node)") ) .arg( Arg::new("path") .short('p') .long("path") .value_name("PATH") .help("Path to project directory") .default_value(".") ) ) .subcommand( Command::new("check") .about("Check .env file for common issues") .arg( Arg::new("path") .short('p') .long("path") .value_name("FILE") .help("Path to .env file") .default_value(".env") ) ) .get_matches(); match matches.subcommand() { Some(("scan", sub_matches)) => { let path = sub_matches.get_one::("path").map(|s| s.as_str()).unwrap_or("."); let schema = sub_matches.get_one::("schema").map(|s| s.as_str()); scan(path, schema)?; } Some(("validate", sub_matches)) => { let path = sub_matches.get_one::("path").map(|s| s.as_str()).unwrap_or(".env"); let strict = sub_matches.get_flag("strict"); validate(path, strict)?; } Some(("generate", sub_matches)) => { let path = sub_matches.get_one::("path").map(|s| s.as_str()).unwrap_or(".env"); let output = sub_matches.get_one::("output").map(|s| s.as_str()); generate(path, output)?; } Some(("secrets", sub_matches)) => { let path = sub_matches.get_one::("path").map(|s| s.as_str()).unwrap_or("."); let strict = sub_matches.get_flag("strict"); secrets_cmd(path, strict)?; } Some(("init", sub_matches)) => { let framework = sub_matches.get_one::("framework").map(|s| s.as_str()); let path = sub_matches.get_one::("path").map(|s| s.as_str()); init(framework, path)?; } Some(("check", sub_matches)) => { let path = sub_matches.get_one::("path").map(|s| s.as_str()).unwrap_or(".env"); check(path)?; } _ => { let _ = Command::new("env-guard").print_help(); } } Ok(()) }