1
0
Fork 0
golang-github-nicholas-fedo.../pkg/format/render_console.go
Daniel Baumann c0c4addb85
Adding upstream version 0.8.9.
Signed-off-by: Daniel Baumann <daniel@debian.org>
2025-05-22 10:16:14 +02:00

201 lines
5 KiB
Go

package format
import (
"fmt"
"strings"
"github.com/fatih/color"
"github.com/nicholas-fedor/shoutrrr/pkg/util"
)
// Constants for console rendering.
const (
DescriptionColumnWidth = 60 // Width of the description column in console output
ItemSeparatorLength = 2 // Length of the ", " separator between container items
DefaultValueOffset = 16 // Minimum offset before description when no values are shown
ValueOffset = 30 // Offset before description when values are shown
ContainerBracketLength = 4 // Length of container delimiters (e.g., "{ }" or "[ ]")
KeySeparatorLength = 2 // Length of the ": " separator after a key in containers
)
// ConsoleTreeRenderer renders a ContainerNode tree into an ansi-colored console string.
type ConsoleTreeRenderer struct {
WithValues bool
}
// RenderTree renders a ContainerNode tree into an ansi-colored console string.
func (r ConsoleTreeRenderer) RenderTree(root *ContainerNode, _ string) string {
stringBuilder := strings.Builder{}
for _, node := range root.Items {
fieldKey := node.Field().Name
stringBuilder.WriteString(fieldKey)
for i := len(fieldKey); i <= root.MaxKeyLength; i++ {
stringBuilder.WriteRune(' ')
}
var valueLen int // Initialize without assignment; set later
preLen := DefaultValueOffset // Default spacing before the description when no values are rendered
field := node.Field()
if r.WithValues {
preLen = ValueOffset // Adjusts the spacing when values are included
valueLen = r.writeNodeValue(&stringBuilder, node)
} else {
// Since no values were supplied, substitute the value with the type
typeName := field.Type.String()
// If the value is an enum type, providing the name is a bit pointless
// Instead, use a common string "option" to signify the type
if field.EnumFormatter != nil {
typeName = "option"
}
valueLen = len(typeName)
stringBuilder.WriteString(color.CyanString(typeName))
}
stringBuilder.WriteString(strings.Repeat(" ", util.Max(preLen-valueLen, 1)))
stringBuilder.WriteString(ColorizeDesc(field.Description))
stringBuilder.WriteString(
strings.Repeat(" ", util.Max(DescriptionColumnWidth-len(field.Description), 1)),
)
if len(field.URLParts) > 0 && field.URLParts[0] != URLQuery {
stringBuilder.WriteString(" <URL: ")
for i, part := range field.URLParts {
if i > 0 {
stringBuilder.WriteString(", ")
}
if part > URLPath {
part = URLPath
}
stringBuilder.WriteString(ColorizeEnum(part))
}
stringBuilder.WriteString(">")
}
if len(field.Template) > 0 {
stringBuilder.WriteString(
fmt.Sprintf(" <Template: %s>", ColorizeString(field.Template)),
)
}
if len(field.DefaultValue) > 0 {
stringBuilder.WriteString(
fmt.Sprintf(
" <Default: %s>",
ColorizeValue(field.DefaultValue, field.EnumFormatter != nil),
),
)
}
if field.Required {
stringBuilder.WriteString(fmt.Sprintf(" <%s>", ColorizeFalse("Required")))
}
if len(field.Keys) > 1 {
stringBuilder.WriteString(" <Aliases: ")
for i, key := range field.Keys {
if i == 0 {
// Skip primary alias (as it's the same as the field name)
continue
}
if i > 1 {
stringBuilder.WriteString(", ")
}
stringBuilder.WriteString(ColorizeString(key))
}
stringBuilder.WriteString(">")
}
if field.EnumFormatter != nil {
stringBuilder.WriteString(ColorizeContainer(" ["))
for i, name := range field.EnumFormatter.Names() {
if i != 0 {
stringBuilder.WriteString(", ")
}
stringBuilder.WriteString(ColorizeEnum(name))
}
stringBuilder.WriteString(ColorizeContainer("]"))
}
stringBuilder.WriteRune('\n')
}
return stringBuilder.String()
}
func (r ConsoleTreeRenderer) writeNodeValue(stringBuilder *strings.Builder, node Node) int {
if contNode, isContainer := node.(*ContainerNode); isContainer {
return r.writeContainer(stringBuilder, contNode)
}
if valNode, isValue := node.(*ValueNode); isValue {
stringBuilder.WriteString(ColorizeToken(valNode.Value, valNode.tokenType))
return len(valNode.Value)
}
stringBuilder.WriteRune('?')
return 1
}
func (r ConsoleTreeRenderer) writeContainer(
stringBuilder *strings.Builder,
node *ContainerNode,
) int {
kind := node.Type.Kind()
hasKeys := !util.IsCollection(kind)
totalLen := ContainerBracketLength // Length of the opening and closing brackets ({ } or [ ])
if hasKeys {
stringBuilder.WriteString("{ ")
} else {
stringBuilder.WriteString("[ ")
}
for i, itemNode := range node.Items {
if i != 0 {
stringBuilder.WriteString(", ")
totalLen += KeySeparatorLength // Accounts for the : separator between keys and values in containers
}
if hasKeys {
itemKey := itemNode.Field().Name
stringBuilder.WriteString(itemKey)
stringBuilder.WriteString(": ")
totalLen += len(itemKey) + ItemSeparatorLength
}
valLen := r.writeNodeValue(stringBuilder, itemNode)
totalLen += valLen
}
if hasKeys {
stringBuilder.WriteString(" }")
} else {
stringBuilder.WriteString(" ]")
}
return totalLen
}