use crate::args::{HazeCommand, SubCommand}; use crate::database::DatabaseFamily; use crate::php::PhpVersion; use crate::service::ServiceType; use owo_colors::colors::xterm::Gray; use owo_colors::OwoColorize; use strum::{EnumMessage, EnumProperty, IntoEnumIterator}; pub fn help(command: Option<&dyn SubCommand>) { if let Some(command) = command { subcommand_help(command); } else { println!( "{} {} {}", "Usage:".bright_yellow().bold(), "haze".blue(), "[filter] [arguments]".green() ); println!(); println!("{}", "Commands:".yellow().bold()); let max_command_len = HazeCommand::iter() .map(|command| <&'static str>::from(command).len()) .max() .unwrap(); let max_doc_len = HazeCommand::iter() .map(|command| { command .get_documentation() .unwrap_or_default() .split('\n') .next() .unwrap() .len() }) .max() .unwrap(); for command in HazeCommand::iter() { let command: HazeCommand = command; let command_str = <&'static str>::from(command); let mut len = command_str.len(); if command_str.starts_with("--") { len -= 2; } let doc: &str = command.get_documentation().unwrap_or_default(); let doc = doc.split('\n').next().unwrap(); println!( " {}{} {}{} {}", command.blue(), " ".repeat(max_command_len - len), doc, " ".repeat(max_doc_len - doc.len()), if command.allows_filter() { "- supports filter".fg::() } else { "".fg::() }, ); } println!(); println!( "See {} {} for more information about a {}", "haze help".blue(), "".green(), "".green() ); } } fn subcommand_help(command: &dyn SubCommand) { println!("{}", command.get_documentation().unwrap_or_default()); println!(); print!( "{} {}{}{} {}", "Usage:".bright_yellow().bold(), "haze".blue(), if command.allows_filter() { " [filter]".green() } else { "".green() }, command.parent().blue(), command.blue(), ); let instance_args = command.get_bool("InstanceArgs").unwrap_or_default(); if instance_args { print!(" {}", "[php version]".green()); print!(" {}", "[database type]".green()); print!(" {}", "[services]".green()); print!(" {}", "[vX.Y.Z]".green()); } let args = if let Some(args) = command.get_str("Args") { let args: &str = args; print!(" {}", "[arguments]".green()); args.strip_prefix("[") .unwrap_or(args) .split(" [") .filter_map(|arg| arg.split_once("] ")) .collect::>() } else { vec![] }; println!(); println!(); if instance_args { println!("{}", "Php versions:".yellow().bold()); for php in PhpVersion::supported_versions() { println!(" {}", php.blue()); } println!(); println!("{}", "Database types:".yellow().bold()); let max_db_len = DatabaseFamily::iter() .map(|service| service.to_string().len()) .max() .unwrap_or_default(); for db in DatabaseFamily::iter() { let db: DatabaseFamily = db; let db_str: &'static str = db.into(); let versions = match db.get_str("Versions") { Some(versions) => { let versions: Vec<_> = versions .split(' ') .map(|version| format!("{}{}{}", db.blue(), ":".blue(), version.blue())) .collect(); Some(versions.join(", ")) } None => None, }; print!(" {}{} ", db.blue(), " ".repeat(max_db_len - db_str.len())); if let Some(versions) = versions { println!("supported versions: {versions}"); } else { println!(); } } println!(); println!("{}", "Services:".yellow().bold()); let max_service_len = ServiceType::iter() .map(|service| service.to_string().len()) .max() .unwrap_or_default(); for service in ServiceType::iter() { let service: ServiceType = service; let service_str: &'static str = service.into(); println!( " {}{} {}", service.blue(), " ".repeat(max_service_len - service_str.len()), service.get_documentation().unwrap_or_default(), ); } } if let Some(sub_commands) = command.sub_commands() { println!(); println!("{}", "Commands:".yellow().bold()); let max_command_len = command .sub_commands() .unwrap() .map(|sub_command| sub_command.to_string().len()) .max() .unwrap(); let max_doc_len = command .sub_commands() .unwrap() .map(|sub_command| { sub_command .get_documentation() .unwrap_or_default() .split('\n') .next() .unwrap() .len() }) .max() .unwrap(); for sub_command in sub_commands { let command_str = sub_command.to_string(); let mut len = command_str.len(); if command_str.starts_with("--") { len -= 2; } let doc: &str = sub_command.get_documentation().unwrap_or_default(); let doc = doc.split('\n').next().unwrap(); println!( " {}{} {}{} {}", sub_command.blue(), " ".repeat(max_command_len - len), doc, " ".repeat(max_doc_len - doc.len()), if sub_command.allows_filter() { "- supports filter".fg::() } else { "".fg::() }, ); } println!(); println!( "See {} {} {} for more information about a {}", "haze help".blue(), command.blue(), "".green(), "".green() ); } println!(); if !args.is_empty() { let max_arg_len = args .iter() .map(|(arg, _)| arg.len()) .max() .unwrap_or_default(); println!("{}", "Arguments:".yellow().bold()); for (arg, desc) in args { println!( " {}{}{}{} {}", "[".green(), arg.green(), "]".green(), " ".repeat(max_arg_len - arg.len()), desc ); } } if let Some(details) = command.get_str("Details") { println!("{}", format_details(details)); } } fn format_details(details: &str) -> String { use std::fmt::Write; let mut result = String::with_capacity(details.len()); for (i, part) in details.split(" 0 { // strip the remaining close tag from the previous part part.split_once('>').map(|(_, part)| part).unwrap_or(part) } else { part }; let (head, tail) = part.split_once('<').unwrap_or((part, "")); result.push_str(head); if let Some((tag, content)) = tail.split_once('>') { match tag { "literal" => write!(&mut result, "{}", content.blue()).unwrap(), "arg" => write!(&mut result, "{}", content.green()).unwrap(), "yellow" => write!(&mut result, "{}", content.bright_yellow()).unwrap(), _ => write!(&mut result, "{content}").unwrap(), } } } result }