PoC: flat and advanced mode
This commit is contained in:
529
main.go
Normal file
529
main.go
Normal file
@@ -0,0 +1,529 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"io/fs"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
//go:embed static/* templates/*
|
||||
var content embed.FS
|
||||
|
||||
type Argument struct {
|
||||
Params string `json:"params"`
|
||||
Command string `json:"command"`
|
||||
HelpText string `json:"helpText"`
|
||||
}
|
||||
|
||||
type GenerateRequest struct {
|
||||
Header string `json:"header"`
|
||||
Arguments []Argument `json:"arguments"`
|
||||
}
|
||||
|
||||
type Subcommand struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Arguments []Argument `json:"arguments"`
|
||||
}
|
||||
|
||||
type GenerateRequestV2 struct {
|
||||
Header string `json:"header"`
|
||||
Arguments []Argument `json:"arguments"`
|
||||
Subcommands []Subcommand `json:"subcommands"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
staticFS, err := fs.Sub(content, "static")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
templatesFS, err := fs.Sub(content, "templates")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
tmpl := template.Must(template.ParseFS(templatesFS, "*.html"))
|
||||
|
||||
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(staticFS))))
|
||||
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
if err := tmpl.ExecuteTemplate(w, "index.html", nil); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
})
|
||||
|
||||
http.HandleFunc("/advanced", func(w http.ResponseWriter, r *http.Request) {
|
||||
if err := tmpl.ExecuteTemplate(w, "advanced.html", nil); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
})
|
||||
|
||||
http.HandleFunc("/generate", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
var req GenerateRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
bashCode := generateBashScript(req)
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.Write([]byte(bashCode))
|
||||
})
|
||||
|
||||
http.HandleFunc("/generate/v2", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
var req GenerateRequestV2
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
bashCode := generateBashScriptV2(req)
|
||||
w.Header().Set("Content-Type", "text/plain")
|
||||
w.Write([]byte(bashCode))
|
||||
})
|
||||
|
||||
log.Println("Server starting on :8080")
|
||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||
}
|
||||
|
||||
func generateBashScript(req GenerateRequest) string {
|
||||
var sb strings.Builder
|
||||
|
||||
sb.WriteString("#!/usr/bin/env bash\n\n")
|
||||
|
||||
if req.Header != "" {
|
||||
sb.WriteString("# " + req.Header + "\n\n")
|
||||
}
|
||||
|
||||
// Generate usage function
|
||||
sb.WriteString("usage() {\n")
|
||||
sb.WriteString(" cat << EOF\n")
|
||||
if req.Header != "" {
|
||||
sb.WriteString(req.Header + "\n\n")
|
||||
}
|
||||
sb.WriteString("Usage: $(basename \"$0\") [OPTIONS]\n\n")
|
||||
sb.WriteString("Options:\n")
|
||||
|
||||
for _, arg := range req.Arguments {
|
||||
if arg.Command == "" {
|
||||
continue
|
||||
}
|
||||
params := arg.Params
|
||||
if params == "" {
|
||||
params = arg.Command
|
||||
}
|
||||
helpText := arg.HelpText
|
||||
if helpText == "" {
|
||||
helpText = arg.Command
|
||||
}
|
||||
sb.WriteString(" " + params + " " + helpText + "\n")
|
||||
}
|
||||
|
||||
sb.WriteString("EOF\n")
|
||||
sb.WriteString(" exit 1\n")
|
||||
sb.WriteString("}\n\n")
|
||||
|
||||
// Generate default values
|
||||
for _, arg := range req.Arguments {
|
||||
if arg.Command == "" {
|
||||
continue
|
||||
}
|
||||
varName := strings.ToUpper(strings.ReplaceAll(arg.Command, "-", "_"))
|
||||
sb.WriteString(varName + "=\"\"\n")
|
||||
}
|
||||
|
||||
sb.WriteString("\n# Parse arguments\n")
|
||||
sb.WriteString("while [[ $# -gt 0 ]]; do\n")
|
||||
sb.WriteString(" case $1 in\n")
|
||||
|
||||
for _, arg := range req.Arguments {
|
||||
if arg.Command == "" {
|
||||
continue
|
||||
}
|
||||
varName := strings.ToUpper(strings.ReplaceAll(arg.Command, "-", "_"))
|
||||
|
||||
// Determine if it takes a value by checking for VALUE, FILE, PATH, etc.
|
||||
// Check the ORIGINAL params string before splitting
|
||||
params := arg.Params
|
||||
if params == "" {
|
||||
params = arg.Command
|
||||
}
|
||||
|
||||
// Check if params contains value placeholders
|
||||
upperParams := strings.ToUpper(params)
|
||||
hasValue := strings.Contains(upperParams, "VALUE") ||
|
||||
strings.Contains(upperParams, "FILE") ||
|
||||
strings.Contains(upperParams, "PATH") ||
|
||||
strings.Contains(upperParams, "DIR") ||
|
||||
strings.Contains(upperParams, "ARG") ||
|
||||
strings.Contains(upperParams, "STRING") ||
|
||||
strings.Contains(upperParams, "NUM") ||
|
||||
strings.Contains(upperParams, "NAME")
|
||||
|
||||
// Extract just the flags (remove the placeholder parts)
|
||||
paramFields := strings.Fields(params)
|
||||
var flags []string
|
||||
for _, field := range paramFields {
|
||||
if strings.HasPrefix(field, "-") {
|
||||
flags = append(flags, field)
|
||||
}
|
||||
}
|
||||
|
||||
if len(flags) == 0 {
|
||||
flags = []string{arg.Command}
|
||||
}
|
||||
|
||||
casePattern := strings.Join(flags, "|")
|
||||
sb.WriteString(" " + casePattern + ")\n")
|
||||
|
||||
if hasValue {
|
||||
sb.WriteString(" " + varName + "=\"$2\"\n")
|
||||
sb.WriteString(" shift 2\n")
|
||||
} else {
|
||||
sb.WriteString(" " + varName + "=1\n")
|
||||
sb.WriteString(" shift\n")
|
||||
}
|
||||
sb.WriteString(" ;;\n")
|
||||
}
|
||||
|
||||
sb.WriteString(" -h|--help)\n")
|
||||
sb.WriteString(" usage\n")
|
||||
sb.WriteString(" ;;\n")
|
||||
sb.WriteString(" *)\n")
|
||||
sb.WriteString(" echo \"Unknown option: $1\"\n")
|
||||
sb.WriteString(" usage\n")
|
||||
sb.WriteString(" ;;\n")
|
||||
sb.WriteString(" esac\n")
|
||||
sb.WriteString("done\n\n")
|
||||
|
||||
sb.WriteString("# Your script logic here\n")
|
||||
for _, arg := range req.Arguments {
|
||||
if arg.Command == "" {
|
||||
continue
|
||||
}
|
||||
varName := strings.ToUpper(strings.ReplaceAll(arg.Command, "-", "_"))
|
||||
sb.WriteString("echo \"" + varName + ": $" + varName + "\"\n")
|
||||
}
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
func generateBashScriptV2(req GenerateRequestV2) string {
|
||||
// If no subcommands, use flat generation
|
||||
if len(req.Subcommands) == 0 {
|
||||
return generateBashScriptFlat(req.Header, req.Arguments)
|
||||
}
|
||||
|
||||
return generateBashScriptWithSubcommands(req)
|
||||
}
|
||||
|
||||
func generateBashScriptWithSubcommands(req GenerateRequestV2) string {
|
||||
var sb strings.Builder
|
||||
|
||||
sb.WriteString("#!/usr/bin/env bash\n\n")
|
||||
|
||||
if req.Header != "" {
|
||||
sb.WriteString("# " + req.Header + "\n\n")
|
||||
}
|
||||
|
||||
// Main usage function
|
||||
generateMainUsageWithSubcommands(&sb, req)
|
||||
|
||||
// Usage function for each subcommand
|
||||
for _, subcmd := range req.Subcommands {
|
||||
generateSubcommandUsage(&sb, subcmd)
|
||||
}
|
||||
|
||||
// Initialize global variables
|
||||
for _, arg := range req.Arguments {
|
||||
if arg.Command == "" {
|
||||
continue
|
||||
}
|
||||
varName := strings.ToUpper(strings.ReplaceAll(arg.Command, "-", "_"))
|
||||
sb.WriteString(varName + "=\"\"\n")
|
||||
}
|
||||
sb.WriteString("SUBCOMMAND=\"\"\n\n")
|
||||
|
||||
// Parse global options
|
||||
sb.WriteString("# Parse global options\n")
|
||||
sb.WriteString("while [[ $# -gt 0 ]]; do\n")
|
||||
sb.WriteString(" case $1 in\n")
|
||||
|
||||
// Add global argument cases
|
||||
for _, arg := range req.Arguments {
|
||||
generateArgumentCase(&sb, arg, "usage")
|
||||
}
|
||||
|
||||
// Add subcommand detection
|
||||
if len(req.Subcommands) > 0 {
|
||||
var subcommandNames []string
|
||||
for _, sc := range req.Subcommands {
|
||||
if sc.Name != "" {
|
||||
subcommandNames = append(subcommandNames, sc.Name)
|
||||
}
|
||||
}
|
||||
if len(subcommandNames) > 0 {
|
||||
sb.WriteString(" " + strings.Join(subcommandNames, "|") + ")\n")
|
||||
sb.WriteString(" SUBCOMMAND=$1\n")
|
||||
sb.WriteString(" shift\n")
|
||||
sb.WriteString(" break\n")
|
||||
sb.WriteString(" ;;\n")
|
||||
}
|
||||
}
|
||||
|
||||
sb.WriteString(" -h|--help)\n")
|
||||
sb.WriteString(" usage\n")
|
||||
sb.WriteString(" ;;\n")
|
||||
sb.WriteString(" *)\n")
|
||||
sb.WriteString(" echo \"Unknown option: $1\"\n")
|
||||
sb.WriteString(" usage\n")
|
||||
sb.WriteString(" ;;\n")
|
||||
sb.WriteString(" esac\n")
|
||||
sb.WriteString("done\n\n")
|
||||
|
||||
// Require subcommand
|
||||
sb.WriteString("# Require subcommand\n")
|
||||
sb.WriteString("if [[ -z \"$SUBCOMMAND\" ]]; then\n")
|
||||
sb.WriteString(" echo \"Error: No command specified\"\n")
|
||||
sb.WriteString(" usage\n")
|
||||
sb.WriteString("fi\n\n")
|
||||
|
||||
// Handle subcommands
|
||||
sb.WriteString("# Handle subcommands\n")
|
||||
sb.WriteString("case $SUBCOMMAND in\n")
|
||||
|
||||
for _, subcmd := range req.Subcommands {
|
||||
if subcmd.Name == "" {
|
||||
continue
|
||||
}
|
||||
generateSubcommandHandler(&sb, subcmd)
|
||||
}
|
||||
|
||||
sb.WriteString(" *)\n")
|
||||
sb.WriteString(" echo \"Unknown command: $SUBCOMMAND\"\n")
|
||||
sb.WriteString(" usage\n")
|
||||
sb.WriteString(" ;;\n")
|
||||
sb.WriteString("esac\n")
|
||||
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func generateMainUsageWithSubcommands(sb *strings.Builder, req GenerateRequestV2) {
|
||||
sb.WriteString("usage() {\n")
|
||||
sb.WriteString(" cat << EOF\n")
|
||||
|
||||
if req.Header != "" {
|
||||
sb.WriteString(req.Header + "\n\n")
|
||||
}
|
||||
|
||||
sb.WriteString("Usage: $(basename \"$0\") [GLOBAL OPTIONS] COMMAND [COMMAND OPTIONS]\n\n")
|
||||
|
||||
// Global options
|
||||
if len(req.Arguments) > 0 {
|
||||
sb.WriteString("Global Options:\n")
|
||||
for _, arg := range req.Arguments {
|
||||
if arg.Command == "" {
|
||||
continue
|
||||
}
|
||||
params := arg.Params
|
||||
if params == "" {
|
||||
params = arg.Command
|
||||
}
|
||||
helpText := arg.HelpText
|
||||
if helpText == "" {
|
||||
helpText = arg.Command
|
||||
}
|
||||
sb.WriteString(" " + params + " " + helpText + "\n")
|
||||
}
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
|
||||
// Commands
|
||||
sb.WriteString("Commands:\n")
|
||||
for _, subcmd := range req.Subcommands {
|
||||
if subcmd.Name == "" {
|
||||
continue
|
||||
}
|
||||
desc := subcmd.Description
|
||||
if desc == "" {
|
||||
desc = subcmd.Name
|
||||
}
|
||||
sb.WriteString(" " + padRight(subcmd.Name, 16) + desc + "\n")
|
||||
}
|
||||
|
||||
sb.WriteString("\nRun '$(basename \"$0\") COMMAND --help' for more information on a command.\n")
|
||||
sb.WriteString("EOF\n")
|
||||
sb.WriteString(" exit 1\n")
|
||||
sb.WriteString("}\n\n")
|
||||
}
|
||||
|
||||
func generateSubcommandUsage(sb *strings.Builder, subcmd Subcommand) {
|
||||
funcName := "usage_" + strings.ReplaceAll(subcmd.Name, "-", "_")
|
||||
|
||||
sb.WriteString(funcName + "() {\n")
|
||||
sb.WriteString(" cat << EOF\n")
|
||||
|
||||
if subcmd.Description != "" {
|
||||
sb.WriteString(subcmd.Description + "\n\n")
|
||||
}
|
||||
|
||||
sb.WriteString("Usage: $(basename \"$0\") " + subcmd.Name + " [OPTIONS]\n\n")
|
||||
|
||||
if len(subcmd.Arguments) > 0 {
|
||||
sb.WriteString("Options:\n")
|
||||
for _, arg := range subcmd.Arguments {
|
||||
if arg.Command == "" {
|
||||
continue
|
||||
}
|
||||
params := arg.Params
|
||||
if params == "" {
|
||||
params = arg.Command
|
||||
}
|
||||
helpText := arg.HelpText
|
||||
if helpText == "" {
|
||||
helpText = arg.Command
|
||||
}
|
||||
sb.WriteString(" " + params + " " + helpText + "\n")
|
||||
}
|
||||
}
|
||||
|
||||
sb.WriteString("EOF\n")
|
||||
sb.WriteString(" exit 1\n")
|
||||
sb.WriteString("}\n\n")
|
||||
}
|
||||
|
||||
func generateSubcommandHandler(sb *strings.Builder, subcmd Subcommand) {
|
||||
sb.WriteString(" " + subcmd.Name + ")\n")
|
||||
|
||||
// Initialize subcommand-specific variables
|
||||
for _, arg := range subcmd.Arguments {
|
||||
if arg.Command == "" {
|
||||
continue
|
||||
}
|
||||
varName := strings.ToUpper(strings.ReplaceAll(arg.Command, "-", "_"))
|
||||
sb.WriteString(" " + varName + "=\"\"\n")
|
||||
}
|
||||
|
||||
// Parse subcommand options
|
||||
sb.WriteString(" \n")
|
||||
sb.WriteString(" # Parse " + subcmd.Name + " options\n")
|
||||
sb.WriteString(" while [[ $# -gt 0 ]]; do\n")
|
||||
sb.WriteString(" case $1 in\n")
|
||||
|
||||
// Add subcommand argument cases
|
||||
usageFuncName := "usage_" + strings.ReplaceAll(subcmd.Name, "-", "_")
|
||||
for _, arg := range subcmd.Arguments {
|
||||
generateArgumentCase(sb, arg, usageFuncName)
|
||||
}
|
||||
|
||||
sb.WriteString(" -h|--help)\n")
|
||||
sb.WriteString(" " + usageFuncName + "\n")
|
||||
sb.WriteString(" ;;\n")
|
||||
sb.WriteString(" *)\n")
|
||||
sb.WriteString(" echo \"Unknown option: $1\"\n")
|
||||
sb.WriteString(" " + usageFuncName + "\n")
|
||||
sb.WriteString(" ;;\n")
|
||||
sb.WriteString(" esac\n")
|
||||
sb.WriteString(" done\n")
|
||||
sb.WriteString(" \n")
|
||||
|
||||
// Add logic section
|
||||
sb.WriteString(" # Your " + subcmd.Name + " logic here\n")
|
||||
for _, arg := range subcmd.Arguments {
|
||||
if arg.Command == "" {
|
||||
continue
|
||||
}
|
||||
varName := strings.ToUpper(strings.ReplaceAll(arg.Command, "-", "_"))
|
||||
sb.WriteString(" echo \"" + varName + ": $" + varName + "\"\n")
|
||||
}
|
||||
|
||||
sb.WriteString(" ;;\n")
|
||||
sb.WriteString(" \n")
|
||||
}
|
||||
|
||||
func generateArgumentCase(sb *strings.Builder, arg Argument, usageFunc string) {
|
||||
if arg.Command == "" {
|
||||
return
|
||||
}
|
||||
|
||||
varName := strings.ToUpper(strings.ReplaceAll(arg.Command, "-", "_"))
|
||||
|
||||
// Determine if it takes a value
|
||||
params := arg.Params
|
||||
if params == "" {
|
||||
params = arg.Command
|
||||
}
|
||||
|
||||
upperParams := strings.ToUpper(params)
|
||||
hasValue := strings.Contains(upperParams, "VALUE") ||
|
||||
strings.Contains(upperParams, "FILE") ||
|
||||
strings.Contains(upperParams, "PATH") ||
|
||||
strings.Contains(upperParams, "DIR") ||
|
||||
strings.Contains(upperParams, "ARG") ||
|
||||
strings.Contains(upperParams, "STRING") ||
|
||||
strings.Contains(upperParams, "NUM") ||
|
||||
strings.Contains(upperParams, "NAME")
|
||||
|
||||
// Extract flags
|
||||
paramFields := strings.Fields(params)
|
||||
var flags []string
|
||||
for _, field := range paramFields {
|
||||
if strings.HasPrefix(field, "-") {
|
||||
flags = append(flags, field)
|
||||
}
|
||||
}
|
||||
|
||||
if len(flags) == 0 {
|
||||
flags = []string{arg.Command}
|
||||
}
|
||||
|
||||
casePattern := strings.Join(flags, "|")
|
||||
|
||||
// Adjust indentation based on context
|
||||
indent := " "
|
||||
if strings.Contains(usageFunc, "_") {
|
||||
indent = " "
|
||||
}
|
||||
|
||||
sb.WriteString(indent + casePattern + ")\n")
|
||||
|
||||
if hasValue {
|
||||
sb.WriteString(indent + " " + varName + "=\"$2\"\n")
|
||||
sb.WriteString(indent + " shift 2\n")
|
||||
} else {
|
||||
sb.WriteString(indent + " " + varName + "=1\n")
|
||||
sb.WriteString(indent + " shift\n")
|
||||
}
|
||||
|
||||
sb.WriteString(indent + " ;;\n")
|
||||
}
|
||||
|
||||
func generateBashScriptFlat(header string, arguments []Argument) string {
|
||||
// Use existing flat generation from original main.go
|
||||
req := GenerateRequest{
|
||||
Header: header,
|
||||
Arguments: arguments,
|
||||
}
|
||||
return generateBashScript(req)
|
||||
}
|
||||
|
||||
func padRight(s string, length int) string {
|
||||
if len(s) >= length {
|
||||
return s + " "
|
||||
}
|
||||
return s + strings.Repeat(" ", length-len(s))
|
||||
}
|
Reference in New Issue
Block a user