background code checker for golang
at main 7.1 kB view raw
1package gust 2 3import ( 4 "bytes" 5 "fmt" 6 "os" 7 "strings" 8 9 "github.com/BurntSushi/toml" 10 "github.com/charmbracelet/lipgloss" 11) 12 13type Config struct { 14 Mode string `toml:"mode"` 15 Stream string `toml:"stream"` 16 Args []string 17 Package string `toml:"package"` 18 Summarized bool `toml:"summarized"` 19 Help bool `toml:"help"` 20 Styles Styles `toml:"styles"` 21 Signs Signs `toml:"signs"` 22 Context int `toml:"context"` 23} 24 25type Styles struct { 26 Mode Style `toml:"mode"` 27 Success Style `toml:"success"` 28 Running Style `toml:"running"` 29 Error Style `toml:"error"` 30 Warning Style `toml:"warning"` 31 Duration Style `toml:"duration"` 32 Separator Style `toml:"separator"` 33 File Style `toml:"file"` 34 Line Style `toml:"line"` 35 Stdout Style `toml:"stdout"` 36 Stderr Style `toml:"stderr"` 37 Context ContextStyles `toml:"context"` 38} 39 40type ContextStyles struct { 41 ActiveLine Style `toml:"activeLine"` 42 ActiveLineNr Style `toml:"activeLineNr"` 43 PassiveLine Style `toml:"passiveLine"` 44 PassiveLineNr Style `toml:"passiveLineNr"` 45} 46 47type Signs struct { 48 Error string `toml:"error"` 49 Warning string `toml:"warning"` 50 System string `toml:"system"` 51 HorizontalBar string `toml:"line"` 52 Separator string `toml:"separator"` 53 PassiveIndent string `toml:"passiveIndent"` 54 ActiveIndent string `toml:"activeIndent"` 55} 56 57type Style struct { 58 lipgloss.Style 59} 60 61type styleTOML struct { 62 Fg string `toml:"fg,omitempty"` 63 Bg string `toml:"bg,omitempty"` 64 Bold bool `toml:"bold,omitempty"` 65 Italic bool `toml:"italic,omitempty"` 66 Underline bool `toml:"underline,omitempty"` 67 Faint bool `toml:"faint,omitempty"` 68 Reverse bool `toml:"reverse,omitempty"` 69} 70 71func (s Style) MarshalTOML() ([]byte, error) { 72 fg := "5" 73 bg := "" 74 75 color, err := marshalColor(s.GetForeground()) 76 if err != nil { 77 return nil, err 78 } 79 if color != nil { 80 fg = *color 81 } 82 83 color, err = marshalColor(s.GetBackground()) 84 if err != nil { 85 return nil, err 86 } 87 if color != nil { 88 bg = *color 89 } 90 91 tomlData := styleTOML{ 92 Fg: fg, 93 Bg: bg, 94 Bold: s.GetBold(), 95 Italic: s.GetItalic(), 96 Underline: s.GetUnderline(), 97 Faint: s.GetFaint(), 98 Reverse: s.GetReverse(), 99 } 100 101 var buf bytes.Buffer 102 err = toml.NewEncoder(&buf).Encode(tomlData) 103 if err != nil { 104 return nil, fmt.Errorf("failed to encode styleTOML to TOML: %w", err) 105 } 106 107 return buf.Bytes(), nil 108} 109 110func (s styleTOML) MarshalTOML() ([]byte, error) { 111 var parts []string 112 113 if s.Fg != "" { 114 parts = append(parts, fmt.Sprintf("fg = \"%s\"", s.Fg)) 115 } 116 if s.Bg != "" { 117 parts = append(parts, fmt.Sprintf("bg = \"%s\"", s.Bg)) 118 } 119 if s.Bold { 120 parts = append(parts, fmt.Sprintf("bold = %v", s.Bold)) 121 } 122 if s.Italic { 123 parts = append(parts, fmt.Sprintf("italic = %v", s.Italic)) 124 } 125 if s.Underline { 126 parts = append(parts, fmt.Sprintf("underline = %v", s.Underline)) 127 } 128 if s.Faint { 129 parts = append(parts, fmt.Sprintf("faint = %v", s.Faint)) 130 } 131 if s.Reverse { 132 parts = append(parts, fmt.Sprintf("reverse = %v", s.Reverse)) 133 } 134 135 if len(parts) == 0 { 136 return nil, nil 137 } 138 139 return []byte("{ " + strings.Join(parts, ", ") + " }"), nil 140} 141 142func marshalColor(tc lipgloss.TerminalColor) (*string, error) { 143 if tc != nil { 144 switch color := tc.(type) { 145 case lipgloss.ANSIColor: 146 s := fmt.Sprintf("%d", color) 147 return &s, nil 148 case lipgloss.Color: 149 s := string(color) 150 return &s, nil 151 case lipgloss.NoColor: 152 return nil, nil 153 default: 154 return nil, fmt.Errorf("fg is not serializable, type: %T", color) 155 } 156 } else { 157 return nil, nil 158 } 159} 160 161func (s *Style) UnmarshalTOML(data any) error { 162 m := data.(map[string]any) 163 style := lipgloss.NewStyle() 164 165 if v, ok := m["fg"].(string); ok { 166 style = style.Foreground(lipgloss.Color(v)) 167 } 168 if v, ok := m["bg"].(string); ok { 169 style = style.Background(lipgloss.Color(v)) 170 } 171 if v, ok := m["bold"].(bool); ok { 172 style = style.Bold(v) 173 } 174 if v, ok := m["italic"].(bool); ok { 175 style = style.Italic(v) 176 } 177 if v, ok := m["underline"].(bool); ok { 178 style = style.Underline(v) 179 } 180 if v, ok := m["faint"].(bool); ok { 181 style = style.Faint(v) 182 } 183 if v, ok := m["reverse"].(bool); ok { 184 style = style.Reverse(v) 185 } 186 s.Style = style 187 return nil 188} 189 190func (s *Style) UnmarshalText(text []byte) error { 191 str := string(text) 192 tokens := strings.Fields(str) 193 style := lipgloss.NewStyle() 194 for i := 0; i < len(tokens); i++ { 195 switch tokens[i] { 196 case "bold": 197 style = style.Bold(true) 198 case "italic": 199 style = style.Italic(true) 200 case "underline": 201 style = style.Underline(true) 202 case "faint": 203 style = style.Faint(true) 204 case "on": 205 if i+1 < len(tokens) { 206 style = style.Background(lipgloss.Color(tokens[i+1])) 207 i++ 208 } 209 default: 210 style = style.Foreground(lipgloss.Color(tokens[i])) 211 } 212 } 213 s.Style = style 214 return nil 215} 216 217func DefaultConfig() Config { 218 return Config{ 219 Summarized: false, 220 Help: true, 221 Mode: "build", 222 Stream: "stdout", 223 Package: "./cmd/gust", 224 Context: 0, 225 Styles: Styles{ 226 Mode: Style{lipgloss.NewStyle().Foreground(lipgloss.Color("5"))}, 227 Success: Style{lipgloss.NewStyle().Foreground(lipgloss.Color("2"))}, 228 Running: Style{lipgloss.NewStyle().Foreground(lipgloss.Color("3"))}, 229 Error: Style{lipgloss.NewStyle().Foreground(lipgloss.Color("1"))}, 230 Warning: Style{lipgloss.NewStyle().Foreground(lipgloss.Color("3"))}, 231 File: Style{lipgloss.NewStyle().Foreground(lipgloss.Color("4"))}, 232 Duration: Style{lipgloss.NewStyle().Foreground(lipgloss.Color("7"))}, 233 Separator: Style{lipgloss.NewStyle().Foreground(lipgloss.Color("12"))}, 234 Line: Style{lipgloss.NewStyle().Foreground(lipgloss.Color("7"))}, 235 Stdout: Style{lipgloss.NewStyle().Foreground(lipgloss.Color("3"))}, 236 Stderr: Style{lipgloss.NewStyle().Foreground(lipgloss.Color("1"))}, 237 Context: ContextStyles{ 238 ActiveLine: Style{lipgloss.NewStyle().Foreground(lipgloss.Color("12"))}, 239 ActiveLineNr: Style{lipgloss.NewStyle().Foreground(lipgloss.Color("1"))}, 240 PassiveLine: Style{lipgloss.NewStyle().Foreground(lipgloss.Color("12"))}, 241 PassiveLineNr: Style{lipgloss.NewStyle().Foreground(lipgloss.Color("12"))}, 242 }, 243 }, 244 Signs: Signs{ 245 Error: "err", 246 Warning: "wrn", 247 System: "sys", 248 HorizontalBar: "─", 249 Separator: " · ", 250 PassiveIndent: " | ", 251 ActiveIndent: " > ", 252 }, 253 } 254} 255 256func (cfg Config) Dump() { 257 err := toml.NewEncoder(os.Stdout).Encode(cfg) 258 if err != nil { 259 fmt.Fprintf(os.Stderr, "Failed to write config: %v", err) 260 } 261} 262 263func LoadConfig() Config { 264 cfg := DefaultConfig() 265 266 if _, err := toml.DecodeFile(".gust.toml", &cfg); err != nil { 267 fmt.Fprintf(os.Stderr, "%s", err.Error()) 268 } 269 270 return cfg 271} 272 273func WithPackage(pkg string) func(*Config) { 274 return func(cfg *Config) { 275 cfg.Package = pkg 276 } 277} 278 279func WithMode(mode string) func(*Config) { 280 return func(cfg *Config) { 281 cfg.Mode = mode 282 } 283} 284 285func WithArgs(args []string) func(*Config) { 286 return func(cfg *Config) { 287 cfg.Args = args 288 } 289}