use crate::app_context::AppContext;
use crate::cli_handlers;
use clap::{Args, Parser, Subcommand, ValueEnum};
use std::path::PathBuf;
use std::sync::Arc;
#[derive(Parser)]
#[command(author, version)]
#[command(about = "Architecture generator for C++/Qt6 and Rust applications")]
#[command(before_help = concat!("Qleany v", env!("CARGO_PKG_VERSION"), " - made by FernTech"))]
#[command(propagate_version = true)]
pub struct Cli {
#[command(subcommand)]
pub command: Option<Commands>,
#[arg(short, long, global = true)]
pub manifest: Option<PathBuf>,
#[arg(short, long, global = true)]
pub verbose: bool,
#[arg(short, long, global = true)]
pub quiet: bool,
}
#[derive(Subcommand)]
pub enum Commands {
New(NewArgs),
Check(CheckArgs),
List(ListArgs),
#[command(visible_alias = "gen")]
Generate(GenerateArgs),
Demo(DemoArgs),
Show(ShowArgs),
Export(ExportArgs),
Docs(DocsArgs),
Upgrade,
Prompt(PromptArgs),
Diff(DiffArgs),
Gui,
}
#[derive(Args)]
pub struct NewArgs {
#[arg(default_value = ".")]
pub path: PathBuf,
#[arg(short, long, value_enum)]
pub language: Option<LanguageOption>,
#[arg(short, long)]
pub name: Option<String>,
#[arg(long)]
pub org_name: Option<String>,
#[arg(short, long, value_enum)]
pub template: Option<ManifestTemplateOption>,
#[arg(short, long)]
pub options: Vec<String>,
#[arg(long)]
pub force: bool,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueEnum)]
pub enum ManifestTemplateOption {
Blank,
Minimal,
DocumentEditor,
DataManagement,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueEnum)]
pub enum LanguageOption {
Rust,
#[value(alias = "cpp-qt")]
CppQt,
}
#[derive(Args)]
pub struct DemoArgs {
#[arg(default_value = ".")]
pub path: PathBuf,
#[arg(long)]
pub rust: bool,
#[arg(long)]
pub cpp_qt: bool,
#[arg(long)]
pub force: bool,
}
#[derive(Args)]
pub struct CheckArgs {
#[arg(long)]
pub rules: bool,
}
#[derive(Args)]
pub struct ListArgs {
#[arg(value_enum, default_value = "files")]
pub target: ListTarget,
#[arg(long)]
pub all: bool,
#[arg(long, short = 'M')]
pub modified: bool,
#[arg(long, short = 'N')]
pub new: bool,
#[arg(long, short = 'U')]
pub unchanged: bool,
#[arg(long)]
pub all_status: bool,
#[arg(long, short = 'i')]
pub infra: bool,
#[arg(long, short = 'g')]
pub aggregates: bool,
#[arg(long, short = 's')]
pub scaffolds: bool,
#[arg(long)]
pub all_natures: bool,
#[arg(long)]
pub text: bool,
#[arg(short, long, value_enum, default_value = "plain")]
pub format: OutputFormat,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueEnum)]
pub enum ListTarget {
Files,
Entities,
Features,
Groups,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueEnum)]
pub enum OutputFormat {
Plain,
Json,
Tree,
}
#[derive(Args)]
pub struct GenerateArgs {
#[arg(value_enum, default_value = "all")]
pub target: GenerateTarget,
#[arg(value_name = "NAME")]
pub target_names: Vec<String>,
#[arg(short, long)]
pub output: Option<PathBuf>,
#[arg(long)]
pub temp: bool,
#[arg(long)]
pub dry_run: bool,
#[arg(long)]
pub all: bool,
#[arg(long, short = 'M')]
pub modified: bool,
#[arg(long, short = 'N')]
pub new: bool,
#[arg(long, short = 'U')]
pub unchanged: bool,
#[arg(long)]
pub all_status: bool,
#[arg(long, short = 'i')]
pub infra: bool,
#[arg(long, short = 'g')]
pub aggregates: bool,
#[arg(long, short = 's')]
pub scaffolds: bool,
#[arg(long)]
pub all_natures: bool,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueEnum)]
pub enum GenerateTarget {
All,
Feature,
Entity,
File,
Group,
}
#[derive(Args)]
pub struct ShowArgs {
#[command(subcommand)]
pub target: Option<ShowTarget>,
#[arg(short, long, value_enum, default_value = "plain")]
pub format: OutputFormat,
}
#[derive(Subcommand)]
pub enum ShowTarget {
Manifest,
Config,
Entity { name: String },
Feature { name: String },
}
#[derive(Args)]
pub struct ExportArgs {
#[command(subcommand)]
pub format: ExportFormat,
#[arg(short, long)]
pub output: Option<PathBuf>,
}
#[derive(Subcommand)]
pub enum ExportFormat {
Mermaid,
Json,
}
#[derive(Args)]
pub struct DocsArgs {
#[command(subcommand)]
pub target: Option<DocsTarget>,
#[arg(long, global = true)]
pub md: bool,
}
#[derive(Subcommand, Clone)]
pub enum DocsTarget {
All,
#[command(visible_alias = "intro")]
Introduction,
#[command(visible_alias = "manifest")]
ManifestReference,
#[command(visible_alias = "design")]
DesignPhilosophy,
#[command(visible_alias = "flow")]
HowOperationsFlow,
#[command(visible_alias = "undo")]
UndoRedoArchitecture,
#[command(visible_alias = "cpp")]
GeneratedCodeCppQt,
#[command(visible_alias = "rust")]
GeneratedCodeRust,
#[command(visible_alias = "api-cpp")]
ApiReferenceCppQt,
#[command(visible_alias = "api-rust")]
ApiReferenceRust,
#[command(visible_alias = "start-cpp")]
QuickStartCppQt,
#[command(visible_alias = "start-rust")]
QuickStartRust,
#[command(visible_alias = "qml")]
QmlIntegration,
#[command(visible_alias = "mig")]
MigrationGuide,
#[command(visible_alias = "trouble")]
Troubleshooting,
#[command(visible_alias = "regen")]
RegenerationWorkflow,
#[command(visible_alias = "mobile")]
MobileBridgeDevelopment,
}
#[derive(Args)]
pub struct PromptArgs {
#[arg(short, long)]
pub list: bool,
#[arg(short, long)]
pub context: bool,
#[arg(short, long)]
pub use_case: Option<String>,
}
#[derive(Args)]
pub struct DiffArgs {
pub target: String,
}
pub fn run_cli(app_context: &Arc<AppContext>) -> Option<()> {
let cli = Cli::parse();
let command = cli.command;
let command = match command {
Some(command) => command,
None => return Some(()),
};
let manifest_path = resolve_manifest_path(&cli.manifest, &command);
let output = OutputContext {
verbose: cli.verbose,
quiet: cli.quiet,
};
let result = match command {
Commands::New(args) => cli_handlers::new::execute(app_context, &args, &output),
Commands::Check(args) => {
if args.rules {
cli_handlers::check::list_rules(&output);
return None;
}
let path = manifest_path.expect("Check requires a manifest");
cli_handlers::check::execute(app_context, &path, &output)
}
Commands::List(args) => {
let path = manifest_path.expect("List requires a manifest");
cli_handlers::list::execute(app_context, &path, &args, &output)
}
Commands::Generate(args) => {
let path = manifest_path.expect("Generate requires a manifest");
cli_handlers::generate::execute(app_context, &path, &args, &output)
}
Commands::Show(args) => {
let path = manifest_path.expect("Show requires a manifest");
cli_handlers::show::execute(app_context, &path, &args, &output)
}
Commands::Export(args) => {
let path = manifest_path.expect("Export requires a manifest");
cli_handlers::export::execute(app_context, &path, &args, &output)
}
Commands::Docs(args) => cli_handlers::docs::execute(app_context, &args, &output),
Commands::Upgrade => {
let path = manifest_path.expect("Upgrade requires a manifest");
cli_handlers::upgrade::execute(app_context, &path, &output)
}
Commands::Prompt(args) => {
let path = manifest_path.expect("Prompt requires a manifest");
cli_handlers::prompt::execute(app_context, &path, &args, &output)
}
Commands::Diff(args) => {
let path = manifest_path.expect("Diff requires a manifest");
cli_handlers::diff::execute(app_context, &path, &args, &output)
}
Commands::Demo(args) => cli_handlers::demo::execute(app_context, &args, &output),
Commands::Gui => return Some(()),
};
if let Err(e) = result {
if !output.quiet {
eprintln!("Error: {}", e);
}
std::process::exit(1);
}
None
}
fn resolve_manifest_path(explicit: &Option<PathBuf>, command: &Commands) -> Option<PathBuf> {
if matches!(
command,
Commands::New(_) | Commands::Demo(_) | Commands::Docs(_)
) {
return None;
}
if let Some(path) = explicit {
if path.is_file() {
return Some(path.clone());
}
if path.is_dir() {
let manifest = path.join("qleany.yaml");
if manifest.exists() {
return Some(manifest);
}
}
eprintln!("Manifest not found: {}", path.display());
std::process::exit(1);
}
let current_dir = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
let candidates = ["qleany.yaml", "qleany.yml"];
for candidate in candidates {
let path = current_dir.join(candidate);
if path.exists() {
return Some(path);
}
}
eprintln!("No qleany.yaml found in current directory. Use --manifest to specify location.");
std::process::exit(1);
}
#[derive(Clone, Copy)]
pub struct OutputContext {
pub verbose: bool,
pub quiet: bool,
}
impl OutputContext {
pub fn info(&self, msg: &str) {
if !self.quiet {
println!("{}", msg);
}
}
pub fn verbose(&self, msg: &str) {
if self.verbose && !self.quiet {
println!("{}", msg);
}
}
pub fn success(&self, msg: &str) {
if !self.quiet {
println!("✓ {}", msg);
}
}
pub fn warn(&self, msg: &str) {
if !self.quiet {
eprintln!("⚠ {}", msg);
}
}
}