Initial upload with CI/CD workflow
This commit is contained in:
215
app/src/generator/mod.rs
Normal file
215
app/src/generator/mod.rs
Normal 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"));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user