Skip to content

Commit

Permalink
Merge pull request #210 from gonvenience/develop
Browse files Browse the repository at this point in the history
Refactor `colorize`/`colorizef` functions
  • Loading branch information
HeavyWombat authored Jan 7, 2025
2 parents 2a19f16 + 6f25fc9 commit d7220bf
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 106 deletions.
2 changes: 1 addition & 1 deletion box.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import (
"io"
"strings"

colorful "github.com/lucasb-eyer/go-colorful"
"github.com/lucasb-eyer/go-colorful"

"github.com/gonvenience/bunt"
"github.com/gonvenience/term"
Expand Down
2 changes: 1 addition & 1 deletion errors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ var _ = Describe("error rendering", func() {
Context("rendering errors", func() {
It("should render a context error using a box", func() {
cause := fmt.Errorf("failed to load X and Y")
err := fmt.Errorf("unable to start Z: %w",cause)
err := fmt.Errorf("unable to start Z: %w", cause)

Expect(SprintError(err)).To(
BeEquivalentTo(ContentBox(
Expand Down
115 changes: 78 additions & 37 deletions output.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,29 +26,66 @@ import (
"fmt"
"strings"

colorful "github.com/lucasb-eyer/go-colorful"
yamlv2 "gopkg.in/yaml.v2"
yamlv3 "gopkg.in/yaml.v3"

"github.com/lucasb-eyer/go-colorful"

"github.com/gonvenience/bunt"
)

// frequently used output constants
const (
colorAnchor = "anchorColor"
colorBinary = "binaryColor"
colorBool = "boolColor"
colorComment = "commentColor"
colorDash = "dashColor"
colorFloat = "floatColor"
colorIndentLine = "indentLineColor"
colorInt = "intColor"
colorKey = "keyColor"
colorMultiLineText = "multiLineTextColor"
colorNull = "nullColor"
colorScalarDefault = "scalarDefaultColor"
)

const (
documentStart = "documentStart"
emptyStructures = "emptyStructures"
)

const (
emptyList = "[]"
emptyObject = "{}"
)

const (
nodeTagBinary = "!!binary"
nodeTagBool = "!!bool"
nodeTagFloat = "!!float"
nodeTagInt = "!!int"
nodeTagNull = "!!null"
nodeTagString = "!!str"
nodeTagTime = "!!timestamp"
)

// DefaultColorSchema is a prepared usable color schema for the neat output
// processor which is loosly based upon the colors used by Atom
var DefaultColorSchema = map[string]colorful.Color{
"documentStart": bunt.LightSlateGray,
"keyColor": bunt.IndianRed,
"indentLineColor": {R: 0.14, G: 0.14, B: 0.14},
"scalarDefaultColor": bunt.PaleGreen,
"boolColor": bunt.Moccasin,
"floatColor": bunt.Orange,
"intColor": bunt.MediumPurple,
"multiLineTextColor": bunt.Aquamarine,
"nullColor": bunt.DarkOrange,
"binaryColor": bunt.Aqua,
"emptyStructures": bunt.PaleGoldenrod,
"commentColor": bunt.DimGray,
"anchorColor": bunt.CornflowerBlue,
documentStart: bunt.LightSlateGray,
colorKey: bunt.IndianRed,
colorIndentLine: {R: 0.14, G: 0.14, B: 0.14},
colorScalarDefault: bunt.PaleGreen,
colorBool: bunt.Moccasin,
colorFloat: bunt.Orange,
colorInt: bunt.MediumPurple,
colorMultiLineText: bunt.Aquamarine,
colorNull: bunt.DarkOrange,
colorBinary: bunt.Aqua,
emptyStructures: bunt.PaleGoldenrod,
colorComment: bunt.DimGray,
colorAnchor: bunt.CornflowerBlue,
}

// OutputProcessor provides the functionality to output neat YAML strings using
Expand Down Expand Up @@ -81,8 +118,8 @@ func NewOutputProcessor(useIndentLines bool, boldKeys bool, colorSchema *map[str
}
}

// TODO Change signature to swap into the right order, color first, text second
func (p *OutputProcessor) colorize(text string, colorName string) string {
// colorize returns the given string with the color applied via bunt.
func (p *OutputProcessor) colorize(colorName string, text string) string {
if p.colorSchema != nil {
if value, ok := (*p.colorSchema)[colorName]; ok {
return bunt.Style(text, bunt.Foreground(value))
Expand All @@ -92,60 +129,64 @@ func (p *OutputProcessor) colorize(text string, colorName string) string {
return text
}

// colorizef formats a string using the provided color name and format string (created via fmt.Sprintf)
// and returns the formatted string with the color applied via bunt.
//
// If additional arguments are not provided, the function skips the fmt.Sprintf call.
func (p *OutputProcessor) colorizef(colorName string, format string, a ...interface{}) string {
if len(a) > 0 {
return p.colorize(fmt.Sprintf(format, a...), colorName)
return p.colorize(colorName, fmt.Sprintf(format, a...))
}

return p.colorize(format, colorName)
return p.colorize(colorName, format)
}

func (p *OutputProcessor) determineColorByType(obj interface{}) string {
color := "scalarDefaultColor"
color := colorScalarDefault

switch t := obj.(type) {
case *yamlv3.Node:
switch t.Tag {
case "!!str":
case nodeTagString:
if len(strings.Split(strings.TrimSpace(t.Value), "\n")) > 1 {
color = "multiLineTextColor"
color = colorMultiLineText
}

case "!!int":
color = "intColor"
case nodeTagInt:
color = colorInt

case "!!float":
color = "floatColor"
case nodeTagFloat:
color = colorFloat

case "!!bool":
color = "boolColor"
case nodeTagBool:
color = colorBool

case "!!null":
color = "nullColor"
case nodeTagNull:
color = colorNull
}

case bool:
color = "boolColor"
color = colorBool

case float32, float64:
color = "floatColor"
color = colorFloat

case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr:
color = "intColor"
color = colorInt

case string:
if len(strings.Split(strings.TrimSpace(t), "\n")) > 1 {
color = "multiLineTextColor"
color = colorMultiLineText
}
}

return color
}

func (p *OutputProcessor) isScalar(obj interface{}) bool {
switch tobj := obj.(type) {
switch tObj := obj.(type) {
case *yamlv3.Node:
return tobj.Kind == yamlv3.ScalarNode
return tObj.Kind == yamlv3.ScalarNode

case yamlv2.MapSlice, []interface{}, []yamlv2.MapSlice:
return false
Expand All @@ -166,10 +207,10 @@ func (p *OutputProcessor) simplify(list []yamlv2.MapSlice) []interface{} {

func (p *OutputProcessor) prefixAdd() string {
if p.useIndentLines {
return p.colorize("│ ", "indentLineColor")
return p.colorize(colorIndentLine, "")
}

return p.colorize(" ", "indentLineColor")
return p.colorize(colorIndentLine, " ")
}

func followAlias(node *yamlv3.Node) *yamlv3.Node {
Expand Down
44 changes: 17 additions & 27 deletions output_json.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ func (p *OutputProcessor) neatJSONofNode(prefix string, node *yamlv3.Node) error

case yamlv3.MappingNode:
if len(node.Content) == 0 {
fmt.Fprint(p.out, p.colorize("{}", "emptyStructures"))
fmt.Fprint(p.out, p.colorize(emptyStructures, emptyObject))
return nil
}

Expand All @@ -225,7 +225,7 @@ func (p *OutputProcessor) neatJSONofNode(prefix string, node *yamlv3.Node) error

fmt.Fprint(p.out,
optionalIndentPrefix(),
p.colorize(`"`+k.Value+`"`, "keyColor"), ": ",
p.colorizef(colorKey, "%q", k.Value), ": ",
)

if p.isScalar(v) {
Expand All @@ -249,7 +249,7 @@ func (p *OutputProcessor) neatJSONofNode(prefix string, node *yamlv3.Node) error

case yamlv3.SequenceNode:
if len(node.Content) == 0 {
fmt.Fprint(p.out, p.colorize("[]", "emptyStructures"))
fmt.Fprint(p.out, p.colorize(emptyStructures, emptyList))
return nil
}

Expand Down Expand Up @@ -291,8 +291,8 @@ func (p *OutputProcessor) neatJSONofNode(prefix string, node *yamlv3.Node) error
fmt.Fprint(p.out,
prefix,
p.colorize(
string(bytes),
p.determineColorByType(node),
string(bytes),
))
}

Expand All @@ -301,7 +301,7 @@ func (p *OutputProcessor) neatJSONofNode(prefix string, node *yamlv3.Node) error

func (p *OutputProcessor) neatJSONofYAMLMapSlice(prefix string, mapslice yamlv2.MapSlice) error {
if len(mapslice) == 0 {
_, _ = p.out.WriteString(p.colorize("{}", "emptyStructures"))
_, _ = p.out.WriteString(p.colorize(emptyStructures, emptyObject))
return nil
}

Expand All @@ -312,7 +312,7 @@ func (p *OutputProcessor) neatJSONofYAMLMapSlice(prefix string, mapslice yamlv2.
keyString := fmt.Sprintf("\"%v\": ", mapitem.Key)

_, _ = p.out.WriteString(prefix + p.prefixAdd())
_, _ = p.out.WriteString(p.colorize(keyString, "keyColor"))
_, _ = p.out.WriteString(p.colorize(colorKey, keyString))

if p.isScalar(mapitem.Value) {
if err := p.neatJSONofScalar("", mapitem.Value); err != nil {
Expand Down Expand Up @@ -340,7 +340,7 @@ func (p *OutputProcessor) neatJSONofYAMLMapSlice(prefix string, mapslice yamlv2.

func (p *OutputProcessor) neatJSONofSlice(prefix string, list []interface{}) error {
if len(list) == 0 {
_, _ = p.out.WriteString(p.colorize("[]", "emptyStructures"))
_, _ = p.out.WriteString(p.colorize(emptyStructures, emptyList))
return nil
}

Expand Down Expand Up @@ -375,7 +375,7 @@ func (p *OutputProcessor) neatJSONofSlice(prefix string, list []interface{}) err

func (p *OutputProcessor) neatJSONofScalar(prefix string, obj interface{}) error {
if obj == nil {
_, _ = p.out.WriteString(p.colorize("null", "nullColor"))
_, _ = p.out.WriteString(p.colorize(colorNull, "null"))
return nil
}

Expand All @@ -389,10 +389,10 @@ func (p *OutputProcessor) neatJSONofScalar(prefix string, obj interface{}) error
_, _ = p.out.WriteString(prefix)
parts := strings.Split(string(data), "\\n")
for idx, part := range parts {
_, _ = p.out.WriteString(p.colorize(part, color))
_, _ = p.out.WriteString(p.colorize(color, part))

if idx < len(parts)-1 {
_, _ = p.out.WriteString(p.colorize("\\n", "emptyStructures"))
_, _ = p.out.WriteString(p.colorize(emptyStructures, "\\n"))
}
}

Expand All @@ -405,22 +405,22 @@ func cast(node yamlv3.Node) (interface{}, error) {
}

switch node.Tag {
case "!!str":
case nodeTagString:
return node.Value, nil

case "!!timestamp":
case nodeTagTime:
return parseTime(node.Value)

case "!!int":
case nodeTagInt:
return strconv.Atoi(node.Value)

case "!!float":
case nodeTagFloat:
return strconv.ParseFloat(node.Value, 64)

case "!!bool":
case nodeTagBool:
return strconv.ParseBool(node.Value)

case "!!null":
case nodeTagNull:
return nil, nil

default:
Expand All @@ -429,17 +429,7 @@ func cast(node yamlv3.Node) (interface{}, error) {
}

func parseTime(value string) (time.Time, error) {
// YAML Spec regarding timestamp: https://yaml.org/type/timestamp.html
var layouts = [...]string{
time.RFC3339,
"2006-01-02T15:04:05.999999999Z",
"2006-01-02t15:04:05.999999999-07:00",
"2006-01-02 15:04:05.999999999 07:00",
"2006-01-02 15:04:05.999999999",
"2006-01-02",
}

for _, layout := range layouts {
for _, layout := range yamlTimeLayouts {
if result, err := time.Parse(layout, value); err == nil {
return result, nil
}
Expand Down
Loading

0 comments on commit d7220bf

Please sign in to comment.