From f242fa0839488e56d0a15d4d229e8c982ec2d073 Mon Sep 17 00:00:00 2001 From: Andreas Linnert Date: Sat, 26 Nov 2022 21:58:52 +0100 Subject: [PATCH] Output with colors --- go.mod | 6 +- go.sum | 11 ++++ log/main.go | 106 ++++++++++++++++++++++++++++++++++++ main.go | 7 ++- utils/Lists.go | 18 +++++- xsdFiles/GetElements.go | 34 ++++-------- xsdFiles/ResolveIncludes.go | 9 ++- 7 files changed, 163 insertions(+), 28 deletions(-) create mode 100644 log/main.go diff --git a/go.mod b/go.mod index 049cba9..1dc06c4 100644 --- a/go.mod +++ b/go.mod @@ -5,14 +5,18 @@ go 1.19 require ( github.com/antchfx/xmlquery v1.3.12 github.com/dominikbraun/graph v0.14.0 + github.com/spf13/cobra v1.6.1 ) require ( github.com/antchfx/xpath v1.2.1 // indirect + github.com/fatih/color v1.13.0 // indirect github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect - github.com/spf13/cobra v1.6.1 // indirect + github.com/mattn/go-colorable v0.1.9 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect github.com/spf13/pflag v1.0.5 // indirect golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect + golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect golang.org/x/text v0.3.7 // indirect ) diff --git a/go.sum b/go.sum index aa4789e..e40c2cb 100644 --- a/go.sum +++ b/go.sum @@ -5,10 +5,17 @@ github.com/antchfx/xpath v1.2.1/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwq github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/dominikbraun/graph v0.14.0 h1:Q1q7OQIKMPDQVNkwRhWQ5BUxCGM1tkcISH5sY6yNj+8= github.com/dominikbraun/graph v0.14.0/go.mod h1:yOjYyogZLY1LSG9E33JWZJiq5k83Qy2C6POAuiViluc= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= @@ -16,7 +23,11 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= diff --git a/log/main.go b/log/main.go new file mode 100644 index 0000000..58a3eb0 --- /dev/null +++ b/log/main.go @@ -0,0 +1,106 @@ +package log + +import ( + "fmt" + "strings" + + "github.com/alinnert/xt/utils" + "github.com/fatih/color" +) + +var childDivider = color.YellowString("/") +var referenceDivider = color.MagentaString("=>") + +func addBrackets(elementName string) string { return "<" + elementName + ">" } + +func colorizeSegment(elementName string, highlight bool) string { + if highlight { + return addBrackets(color.CyanString(elementName)) + } + return addBrackets(color.WhiteString(elementName)) +} + +func colorizeElement(elementName string) string { + segments := strings.Split(elementName, "/") + + if len(segments) == 1 { + return colorizeSegment(elementName, true) + } + + colorizedSegments := utils.Map(segments, func(ctx utils.MapContext[string]) string { + if ctx.LastItem { + return colorizeSegment(ctx.Item, true) + } + return colorizeSegment(ctx.Item, false) + }) + + return strings.Join(colorizedSegments, color.HiBlackString("/")) +} + +func colorizePath(path []string) string { + colorizedPath := utils.Map(path, func(ctx utils.MapContext[string]) string { + segments := strings.Split(ctx.Item, "/") + lastSegment := segments[len(segments)-1] + colorizedLastSegment := colorizeElement(lastSegment) + if ctx.FirstItem { + return colorizedLastSegment + } + if len(segments) > 1 { + return fmt.Sprintf("%s %s", childDivider, colorizedLastSegment) + } + return fmt.Sprintf("%s %s", referenceDivider, colorizedLastSegment) + }) + + return strings.Join(colorizedPath, " ") +} + +func colorizeAction(action string) string { + return color.HiWhiteString(action + ":") +} + +func AddFile(filePath string) { + fmt.Println(colorizeAction("Add file"), color.GreenString(filePath)) +} + +func DuplicateFile(filePath string) { + fmt.Println(colorizeAction("Duplicate file"), color.YellowString(filePath)) +} + +func ElementHeadline(elementName string) { + fmt.Println() + fmt.Println(addBrackets(color.HiYellowString(elementName))) +} + +func AddElement(elementName string) { + fmt.Println(colorizeAction("Add element"), colorizeElement(elementName)) +} + +func DuplicateElement(elementName string) { + fmt.Println(colorizeAction("Duplicate element"), colorizeElement(elementName)) +} + +func LeafElementsCount(count int) { + c := color.New(color.FgGreen).SprintFunc() + if count == 1 { + fmt.Println("1", c("leaf element")) + } else { + fmt.Println(color.HiWhiteString("%d", count), c("leaf elements")) + } +} + +func ElementRefsCount(count int) { + c := color.New(color.FgGreen).SprintFunc() + if count == 1 { + fmt.Println("1", c("element ref")) + } else { + fmt.Println(color.HiWhiteString("%d", count), c("element refs")) + } +} + +func AddReference(from, to string) { + fmt.Println(colorizeAction("Add reference"), colorizeElement(from), referenceDivider, colorizeElement(to)) +} + +func PathsResult(segments []string) { + fmt.Println("-", colorizePath(segments)) +} diff --git a/main.go b/main.go index b5a2597..fd34690 100644 --- a/main.go +++ b/main.go @@ -7,6 +7,7 @@ import ( "sort" "strings" + "github.com/alinnert/xt/log" "github.com/alinnert/xt/utils" "github.com/alinnert/xt/xsdFiles" "github.com/dominikbraun/graph" @@ -81,6 +82,10 @@ func main() { }) // Print all results. + if verbose { + fmt.Println() + } + if len(results) == 0 { fmt.Printf("No paths found for element \"" + elementArg + "\"\n") return @@ -100,7 +105,7 @@ func main() { fmt.Printf("Possible paths for element \"%s\" %s\n", elementArg, countLabel) for _, result := range results[:resultsCount] { - fmt.Println("-", strings.Join(result, " > ")) + log.PathsResult(result) } }, } diff --git a/utils/Lists.go b/utils/Lists.go index eb23b30..3d444d1 100644 --- a/utils/Lists.go +++ b/utils/Lists.go @@ -1,10 +1,24 @@ package utils +type MapContext[T any] struct { + Items []T + Item T + Index int + FirstItem, LastItem bool +} + // Map applies a function to all elements in a list and returns a mutated list. -func Map[T, V any](items []T, cb func(T) V) []V { +func Map[T, V any](items []T, cb func(MapContext[T]) V) []V { result := make([]V, len(items)) for i, item := range items { - result[i] = cb(item) + ctx := MapContext[T]{ + Items: items, + Item: item, + Index: i, + FirstItem: i == 0, + LastItem: i == len(items)-1, + } + result[i] = cb(ctx) } return result } diff --git a/xsdFiles/GetElements.go b/xsdFiles/GetElements.go index 3f6fb91..df394d1 100644 --- a/xsdFiles/GetElements.go +++ b/xsdFiles/GetElements.go @@ -4,6 +4,7 @@ import ( "fmt" "strings" + "github.com/alinnert/xt/log" "github.com/alinnert/xt/utils" "github.com/alinnert/xt/xsd" "github.com/antchfx/xmlquery" @@ -70,7 +71,7 @@ func findRootElements(filePath string, filesGraph XsdFileGraph, elementsGraph El if _, err := elementsGraph.Vertex(rootElementName); err == nil { if verbose { - fmt.Println(" duplicate element:", rootElementName) + log.DuplicateElement(rootElementName) } return nil @@ -78,7 +79,7 @@ func findRootElements(filePath string, filesGraph XsdFileGraph, elementsGraph El // Add root document elements if verbose { - fmt.Println("add root element", rootElementName) + log.AddElement(rootElementName) } err = elementsGraph.AddVertex(utils.XsdElement{PathSegments: []string{rootElementName}}) @@ -116,7 +117,7 @@ func findNestedElements(filePath string, filesGraph XsdFileGraph, elementsGraph for _, rootElement := range rootElements { if verbose { - fmt.Println("[", rootElement.SelectAttr("name"), "]") + log.ElementHeadline(rootElement.SelectAttr("name")) } err := findLeafElements(rootElement, elementsGraph, verbose) @@ -128,10 +129,6 @@ func findNestedElements(filePath string, filesGraph XsdFileGraph, elementsGraph if err != nil { return err } - - if verbose { - fmt.Println() - } } return nil @@ -146,11 +143,7 @@ func findLeafElements(rootElement *xmlquery.Node, elementsGraph ElementsGraph, v } if verbose { - if lenLeafElements := len(leafElements); lenLeafElements == 1 { - fmt.Println("1 leaf element") - } else { - fmt.Println(lenLeafElements, "leaf elements") - } + log.LeafElementsCount(len(leafElements)) } for _, leafElement := range leafElements { @@ -172,11 +165,7 @@ func findLeafElementRefs(rootElement *xmlquery.Node, elementsGraph ElementsGraph } if verbose { - if lenLeafElementRefs := len(leafElementRefs); lenLeafElementRefs == 1 { - fmt.Println("1 leaf element ref") - } else { - fmt.Println(lenLeafElementRefs, "leaf element refs") - } + log.ElementRefsCount(len(leafElementRefs)) } for _, elementRef := range leafElementRefs { @@ -194,7 +183,7 @@ func findLeafElementRefs(rootElement *xmlquery.Node, elementsGraph ElementsGraph closestAncestorName := closestAncestor.SelectAttr("name") if verbose { - fmt.Println("add edge for reference", closestAncestorName, "->", referencedElementName) + log.AddReference(closestAncestorName, referencedElementName) } err = elementsGraph.AddEdge(closestAncestorName, referencedElementName) @@ -218,8 +207,8 @@ func findLeafAncestorElements(leafElement *xmlquery.Node, elementsGraph Elements ancestors = utils.Reverse(ancestors) - ancestorElementNames := utils.Map(ancestors, func(ancestor *xmlquery.Node) string { - return ancestor.SelectAttr("name") + ancestorElementNames := utils.Map(ancestors, func(ctx utils.MapContext[*xmlquery.Node]) string { + return ctx.Item.SelectAttr("name") }) leafElementPathSegments := append(ancestorElementNames, leafElementName) @@ -236,7 +225,7 @@ func findLeafAncestorElements(leafElement *xmlquery.Node, elementsGraph Elements // Add descendant elements if verbose { - fmt.Println("add nested element", currentItemPath) + log.AddElement(currentItemPath) } err = elementsGraph.AddVertex(utils.XsdElement{PathSegments: currentItemPathSegments}) @@ -245,7 +234,8 @@ func findLeafAncestorElements(leafElement *xmlquery.Node, elementsGraph Elements } if verbose { - fmt.Println("add edge for nested element", parentPath, "->", currentItemPath) + log.AddReference(parentPath, currentItemPath) + fmt.Println() } err = elementsGraph.AddEdge(parentPath, currentItemPath) diff --git a/xsdFiles/ResolveIncludes.go b/xsdFiles/ResolveIncludes.go index c62bbef..7245386 100644 --- a/xsdFiles/ResolveIncludes.go +++ b/xsdFiles/ResolveIncludes.go @@ -6,6 +6,7 @@ import ( "path/filepath" "strings" + "github.com/alinnert/xt/log" "github.com/alinnert/xt/xsd" "github.com/dominikbraun/graph" @@ -30,12 +31,16 @@ func ResolveIncludes(mainFilePath string, verbose bool) (XsdFileGraph, error) { return nil, err } + if verbose { + fmt.Println() + } + return fileGraph, nil } func processFile(fileGraph XsdFileGraph, filePath string, verbose bool) error { if verbose { - fmt.Println("Add file:", filePath) + log.AddFile(filePath) } content, err := os.ReadFile(filePath) @@ -64,7 +69,7 @@ func processFile(fileGraph XsdFileGraph, filePath string, verbose bool) error { nextFilePath := filepath.Join(currentDir, include.SelectAttr("schemaLocation")) if _, err := fileGraph.Vertex(nextFilePath); err == nil { - fmt.Println("duplicate include:", nextFilePath) + log.DuplicateFile(nextFilePath) continue }