First working version

This commit is contained in:
Eden Kirin
2025-10-31 14:36:41 +01:00
parent f8edfc0fc1
commit f9f67b6c93
22 changed files with 2277 additions and 0 deletions

186
internal/prompt/prompt.go Normal file
View File

@ -0,0 +1,186 @@
package prompt
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
"github.com/fatih/color"
)
var (
cyan = color.New(color.FgCyan).SprintFunc()
green = color.New(color.FgGreen).SprintFunc()
yellow = color.New(color.FgYellow).SprintFunc()
red = color.New(color.FgRed).SprintFunc()
)
// Reader interface for testing
type Reader interface {
ReadString(delim byte) (string, error)
}
// PromptString prompts for a string value
func PromptString(label string, defaultValue string, required bool) (string, error) {
reader := bufio.NewReader(os.Stdin)
return promptStringWithReader(reader, label, defaultValue, required)
}
func promptStringWithReader(reader Reader, label string, defaultValue string, required bool) (string, error) {
for {
prompt := fmt.Sprintf("%s", cyan(label))
if defaultValue != "" {
prompt += fmt.Sprintf(" [%s]", green(defaultValue))
}
prompt += ": "
fmt.Print(prompt)
input, err := reader.ReadString('\n')
if err != nil {
return "", err
}
input = strings.TrimSpace(input)
// If input is empty, use default value
if input == "" {
if defaultValue != "" {
// Echo the selected default value
fullPrompt := fmt.Sprintf("%s [%s]: %s", cyan(label), green(defaultValue), defaultValue)
fmt.Printf("\033[1A\r%s\n", fullPrompt)
return defaultValue, nil
}
if required {
fmt.Println(red("✗ This field is required"))
continue
}
return "", nil
}
return input, nil
}
}
// PromptInt prompts for an integer value
func PromptInt(label string, defaultValue int, required bool) (int, error) {
reader := bufio.NewReader(os.Stdin)
return promptIntWithReader(reader, label, defaultValue, required)
}
func promptIntWithReader(reader Reader, label string, defaultValue int, required bool) (int, error) {
for {
defaultStr := ""
if defaultValue != 0 {
defaultStr = strconv.Itoa(defaultValue)
}
prompt := fmt.Sprintf("%s", cyan(label))
if defaultStr != "" {
prompt += fmt.Sprintf(" [%s]", green(defaultStr))
}
prompt += ": "
fmt.Print(prompt)
input, err := reader.ReadString('\n')
if err != nil {
return 0, err
}
input = strings.TrimSpace(input)
// If input is empty, use default value
if input == "" {
if defaultValue != 0 {
// Echo the selected default value
fullPrompt := fmt.Sprintf("%s [%s]: %s", cyan(label), green(defaultStr), defaultStr)
fmt.Printf("\033[1A\r%s\n", fullPrompt)
return defaultValue, nil
}
if required {
fmt.Println(red("✗ This field is required"))
continue
}
return 0, nil
}
// Parse integer
value, err := strconv.Atoi(input)
if err != nil {
fmt.Println(red("✗ Please enter a valid number"))
continue
}
return value, nil
}
}
// ValidateDirectory checks if a directory exists or can be created
func ValidateDirectory(path string) error {
// Check if path exists
info, err := os.Stat(path)
if err == nil {
// Path exists, check if it's a directory
if !info.IsDir() {
return fmt.Errorf("path exists but is not a directory")
}
return nil
}
// Path doesn't exist, try to create it
if os.IsNotExist(err) {
if err := os.MkdirAll(path, 0755); err != nil {
return fmt.Errorf("cannot create directory: %w", err)
}
return nil
}
return err
}
// PromptDirectory prompts for a directory path and validates it
func PromptDirectory(label string, defaultValue string, required bool) (string, error) {
for {
path, err := PromptString(label, defaultValue, required)
if err != nil {
return "", err
}
if path == "" && !required {
return "", nil
}
// Validate directory
if err := ValidateDirectory(path); err != nil {
fmt.Printf("%s %s\n", red("✗"), err.Error())
continue
}
return path, nil
}
}
// PrintHeader prints a colored header
func PrintHeader(text string) {
header := color.New(color.FgCyan, color.Bold)
header.Println("\n" + text)
header.Println(strings.Repeat("=", len(text)))
}
// PrintSuccess prints a success message
func PrintSuccess(text string) {
fmt.Printf("%s %s\n", green("✓"), text)
}
// PrintError prints an error message
func PrintError(text string) {
fmt.Printf("%s %s\n", red("✗"), text)
}
// PrintInfo prints an info message
func PrintInfo(text string) {
fmt.Printf("%s %s\n", yellow(""), text)
}