mirror of
https://github.com/ivuorinen/gh-action-readme.git
synced 2026-02-25 23:53:56 +00:00
feat: add interactive wizard, contextual errors, and code improvements
- Add interactive configuration wizard with auto-detection and multi-format export - Implement contextual error system with 14 error codes and actionable suggestions - Add centralized progress indicators with consistent theming - Fix all cyclomatic complexity issues (8 functions refactored) - Eliminate code duplication with centralized utilities and error handling - Add comprehensive test coverage for all new components - Update TODO.md with completed tasks and accurate completion dates
This commit is contained in:
@@ -3,8 +3,11 @@ package internal
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
|
||||
"github.com/ivuorinen/gh-action-readme/internal/errors"
|
||||
)
|
||||
|
||||
// ColoredOutput provides methods for colored terminal output.
|
||||
@@ -107,3 +110,139 @@ func (co *ColoredOutput) Printf(format string, args ...any) {
|
||||
func (co *ColoredOutput) Fprintf(w *os.File, format string, args ...any) {
|
||||
_, _ = fmt.Fprintf(w, format, args...)
|
||||
}
|
||||
|
||||
// ErrorWithSuggestions prints a ContextualError with suggestions and help.
|
||||
func (co *ColoredOutput) ErrorWithSuggestions(err *errors.ContextualError) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Print main error message
|
||||
if co.NoColor {
|
||||
fmt.Fprintf(os.Stderr, "❌ %s\n", err.Error())
|
||||
} else {
|
||||
color.Red("❌ %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// ErrorWithContext creates and prints a contextual error with suggestions.
|
||||
func (co *ColoredOutput) ErrorWithContext(
|
||||
code errors.ErrorCode,
|
||||
message string,
|
||||
context map[string]string,
|
||||
) {
|
||||
suggestions := errors.GetSuggestions(code, context)
|
||||
helpURL := errors.GetHelpURL(code)
|
||||
|
||||
contextualErr := errors.New(code, message).
|
||||
WithSuggestions(suggestions...).
|
||||
WithHelpURL(helpURL)
|
||||
|
||||
if len(context) > 0 {
|
||||
contextualErr = contextualErr.WithDetails(context)
|
||||
}
|
||||
|
||||
co.ErrorWithSuggestions(contextualErr)
|
||||
}
|
||||
|
||||
// ErrorWithSimpleFix prints an error with a simple suggestion.
|
||||
func (co *ColoredOutput) ErrorWithSimpleFix(message, suggestion string) {
|
||||
contextualErr := errors.New(errors.ErrCodeUnknown, message).
|
||||
WithSuggestions(suggestion)
|
||||
|
||||
co.ErrorWithSuggestions(contextualErr)
|
||||
}
|
||||
|
||||
// FormatContextualError formats a ContextualError for display.
|
||||
func (co *ColoredOutput) FormatContextualError(err *errors.ContextualError) string {
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
var parts []string
|
||||
|
||||
// Add main error message
|
||||
parts = append(parts, co.formatMainError(err))
|
||||
|
||||
// Add details section
|
||||
if len(err.Details) > 0 {
|
||||
parts = append(parts, co.formatDetailsSection(err.Details)...)
|
||||
}
|
||||
|
||||
// Add suggestions section
|
||||
if len(err.Suggestions) > 0 {
|
||||
parts = append(parts, co.formatSuggestionsSection(err.Suggestions)...)
|
||||
}
|
||||
|
||||
// Add help URL section
|
||||
if err.HelpURL != "" {
|
||||
parts = append(parts, co.formatHelpURLSection(err.HelpURL))
|
||||
}
|
||||
|
||||
return strings.Join(parts, "\n")
|
||||
}
|
||||
|
||||
// formatMainError formats the main error message with code.
|
||||
func (co *ColoredOutput) formatMainError(err *errors.ContextualError) string {
|
||||
mainMsg := fmt.Sprintf("%s [%s]", err.Error(), err.Code)
|
||||
if co.NoColor {
|
||||
return "❌ " + mainMsg
|
||||
}
|
||||
return color.RedString("❌ ") + mainMsg
|
||||
}
|
||||
|
||||
// formatDetailsSection formats the details section.
|
||||
func (co *ColoredOutput) formatDetailsSection(details map[string]string) []string {
|
||||
var parts []string
|
||||
|
||||
if co.NoColor {
|
||||
parts = append(parts, "\nDetails:")
|
||||
} else {
|
||||
parts = append(parts, color.New(color.Bold).Sprint("\nDetails:"))
|
||||
}
|
||||
|
||||
for key, value := range details {
|
||||
if co.NoColor {
|
||||
parts = append(parts, fmt.Sprintf(" %s: %s", key, value))
|
||||
} else {
|
||||
parts = append(parts, fmt.Sprintf(" %s: %s",
|
||||
color.CyanString(key),
|
||||
color.WhiteString(value)))
|
||||
}
|
||||
}
|
||||
|
||||
return parts
|
||||
}
|
||||
|
||||
// formatSuggestionsSection formats the suggestions section.
|
||||
func (co *ColoredOutput) formatSuggestionsSection(suggestions []string) []string {
|
||||
var parts []string
|
||||
|
||||
if co.NoColor {
|
||||
parts = append(parts, "\nSuggestions:")
|
||||
} else {
|
||||
parts = append(parts, color.New(color.Bold).Sprint("\nSuggestions:"))
|
||||
}
|
||||
|
||||
for _, suggestion := range suggestions {
|
||||
if co.NoColor {
|
||||
parts = append(parts, fmt.Sprintf(" • %s", suggestion))
|
||||
} else {
|
||||
parts = append(parts, fmt.Sprintf(" %s %s",
|
||||
color.YellowString("•"),
|
||||
color.WhiteString(suggestion)))
|
||||
}
|
||||
}
|
||||
|
||||
return parts
|
||||
}
|
||||
|
||||
// formatHelpURLSection formats the help URL section.
|
||||
func (co *ColoredOutput) formatHelpURLSection(helpURL string) string {
|
||||
if co.NoColor {
|
||||
return fmt.Sprintf("\nFor more help: %s", helpURL)
|
||||
}
|
||||
return fmt.Sprintf("\n%s: %s",
|
||||
color.New(color.Bold).Sprint("For more help"),
|
||||
color.BlueString(helpURL))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user