Initial upload with CI/CD workflow
Some checks failed
CI / test (push) Has been cancelled
CI / build (push) Has been cancelled

This commit is contained in:
2026-02-03 09:41:17 +00:00
parent 2f83d476ed
commit 60f5260487

215
app/src/generator/mod.rs Normal file
View File

@@ -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<String> {
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<String> {
let mut html = String::new();
html.push_str(r#"<!DOCTYPE html>
<html>
<head>
<title>TUI Preview - "#);
html.push_str(&command_info.name);
html.push_str(r#"</title>
<style>
body { font-family: monospace; background: #1a1b26; color: #a9b1d6; padding: 20px; }
h1 { color: #7aa2f7; }
h2 { color: #bb9af7; margin-top: 30px; }
.arg { background: #24283b; padding: 10px; margin: 5px 0; border-radius: 5px; }
.required { color: #f7768e; }
.optional { color: #9ece6a; }
code { background: #414868; padding: 2px 6px; border-radius: 3px; }
pre { background: #24283b; padding: 15px; border-radius: 5px; overflow-x: auto; }
</style>
</head>
<body>
<h1>"#);
html.push_str(&command_info.name);
html.push_str(" - ");
html.push_str(&command_info.description);
html.push_str(r#"</h1>
<h2>Arguments</h2>
"#);
for arg in &command_info.arguments {
html.push_str(r#" <div class="arg">
<code>"#);
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#"</code>
<p>"#);
html.push_str(&arg.description);
if arg.required {
html.push_str(r#" <span class="required">(required)</span>"#);
}
html.push_str(r#"</p>
</div>
"#);
}
html.push_str(r#"
</body>
</html>
"#);
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<String> {
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("<!DOCTYPE html>"));
assert!(output.contains("test"));
}
}