187 lines
4.2 KiB
Go
187 lines
4.2 KiB
Go
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)
|
||
}
|