diff --git a/app/src/generator/mod.rs b/app/src/generator/mod.rs new file mode 100644 index 0000000..4c3d012 --- /dev/null +++ b/app/src/generator/mod.rs @@ -0,0 +1,215 @@ +use anyhow::Result; +use std::fs; +use std::path::PathBuf; +use crate::parser::{CommandInfo, Argument, SubCommand}; + +pub struct TuiGenerator; + +impl TuiGenerator { + pub fn generate_from_help(command_info: &CommandInfo) -> Result { + let mut output = String::new(); + + output.push_str(&format!("# TUI Configuration for {}\n\n", command_info.name)); + output.push_str(&format!("## Command Information\n")); + output.push_str(&format!("- Name: {}\n", command_info.name)); + output.push_str(&format!("- Description: {}\n", command_info.description)); + + if let Some(version) = &command_info.version { + output.push_str(&format!("- Version: {}\n", version)); + } + + output.push_str("\n## Arguments\n"); + for arg in &command_info.arguments { + output.push_str(&format!("- {}", Self::format_argument(arg))); + } + + if !command_info.subcommands.is_empty() { + output.push_str("\n## Subcommands\n"); + for subcmd in &command_info.subcommands { + output.push_str(&format!("- {}: {}\n", subcmd.name, subcmd.description)); + } + } + + if !command_info.examples.is_empty() { + output.push_str("\n## Examples\n"); + for example in &command_info.examples { + output.push_str(&format!("```\n{}\n```\n", example)); + } + } + + Ok(output) + } + + fn format_argument(arg: &Argument) -> String { + let mut parts = Vec::new(); + + if let Some(short) = arg.short { + parts.push(format!("-{}", short)); + } + + if let Some(long) = &arg.long { + parts.push(format!("--{}", long)); + } + + let flags = parts.join(", "); + let required = if arg.required { " (required)" } else { "" }; + + format!("`{}`{}{}", flags, arg.description, required) + } + + pub fn generate_html_preview(command_info: &CommandInfo) -> Result { + let mut html = String::new(); + + html.push_str(r#" + + + TUI Preview - "#); + html.push_str(&command_info.name); + html.push_str(r#" + + + +

"#); + html.push_str(&command_info.name); + html.push_str(" - "); + html.push_str(&command_info.description); + html.push_str(r#"

+ +

Arguments

+"#); + + for arg in &command_info.arguments { + html.push_str(r#"
+ "#); + let mut flags = Vec::new(); + if let Some(short) = arg.short { + flags.push(format!("-{}", short)); + } + if let Some(long) = &arg.long { + flags.push(format!("--{}", long)); + } + html.push_str(&flags.join(", ")); + html.push_str(r#" +

"#); + html.push_str(&arg.description); + if arg.required { + html.push_str(r#" (required)"#); + } + html.push_str(r#"

+
+"#); + } + + html.push_str(r#" + + +"#); + + Ok(html) + } + + pub fn export_tui_config(command_info: &CommandInfo, output: &PathBuf) -> Result<()> { + let config = Self::generate_toml_config(command_info)?; + fs::write(output, config)?; + Ok(()) + } + + fn generate_toml_config(command_info: &CommandInfo) -> Result { + let mut toml = String::new(); + + toml.push_str(&format!("name = \"{}\"\n", command_info.name)); + toml.push_str(&format!("description = \"{}\"\n\n", command_info.description)); + + toml.push_str("[arguments]\n"); + for arg in &command_info.arguments { + toml.push_str(&format!("[arguments.{}]\n", arg.name)); + if let Some(short) = arg.short { + toml.push_str(&format!("short = '{}'\n", short)); + } + if let Some(long) = &arg.long { + toml.push_str(&format!("long = \"{}\"\n", long)); + } + toml.push_str(&format!("description = \"{}\"\n", arg.description)); + toml.push_str(&format!("required = {}\n", arg.required)); + if let Some(default) = &arg.default_value { + toml.push_str(&format!("default = \"{}\"\n", default)); + } + if !arg.possible_values.is_empty() { + toml.push_str(&format!("possible_values = {:?}\n", arg.possible_values)); + } + toml.push_str("\n"); + } + + if !command_info.subcommands.is_empty() { + toml.push_str("\n[subcommands]\n"); + for subcmd in &command_info.subcommands { + toml.push_str(&format!("[subcommands.{}]\n", subcmd.name)); + toml.push_str(&format!("description = \"{}\"\n", subcmd.description)); + } + } + + Ok(toml) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::parser::{Argument, CommandInfo}; + + #[test] + fn test_generate_from_help() { + let info = CommandInfo { + name: "test".to_string(), + description: "A test command".to_string(), + version: Some("1.0.0".to_string()), + arguments: vec![Argument { + name: "verbose".to_string(), + short: Some('v'), + long: Some("verbose".to_string()), + description: "Enable verbose mode".to_string(), + required: false, + takes_value: false, + default_value: None, + possible_values: Vec::new(), + }], + subcommands: Vec::new(), + examples: vec!["test -v".to_string()], + environment_variables: Vec::new(), + }; + + let result = TuiGenerator::generate_from_help(&info); + assert!(result.is_ok()); + let output = result.unwrap(); + assert!(output.contains("test")); + assert!(output.contains("A test command")); + } + + #[test] + fn test_generate_html_preview() { + let info = CommandInfo { + name: "test".to_string(), + description: "A test command".to_string(), + version: None, + arguments: vec![], + subcommands: Vec::new(), + examples: Vec::new(), + environment_variables: Vec::new(), + }; + + let result = TuiGenerator::generate_html_preview(&info); + assert!(result.is_ok()); + let output = result.unwrap(); + assert!(output.contains("")); + assert!(output.contains("test")); + } +}