From 0a861a9cd59b4c5121f33489329ce2a6a79d0c45 Mon Sep 17 00:00:00 2001 From: "Iskander (Alex) Sharipov" Date: Thu, 29 Apr 2021 22:24:48 +0300 Subject: [PATCH] cmd/gorules: initial commit (#229) `gorules` is a helper binary that inspects `gorules` files. The first command is `gorules doc` that extracts the documentation. --- cmd/gorules/go.mod | 10 ++++ cmd/gorules/go.sum | 49 ++++++++++++++++ cmd/gorules/main.go | 137 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 196 insertions(+) create mode 100644 cmd/gorules/go.mod create mode 100644 cmd/gorules/go.sum create mode 100644 cmd/gorules/main.go diff --git a/cmd/gorules/go.mod b/cmd/gorules/go.mod new file mode 100644 index 00000000..dd4503d7 --- /dev/null +++ b/cmd/gorules/go.mod @@ -0,0 +1,10 @@ +module github.com/quasilyte/go-ruleguard/cmd/gorules + +go 1.16 + +require ( + github.com/cespare/subcmd v1.1.0 + github.com/go-toolsmith/strparse v1.0.0 // indirect + github.com/quasilyte/go-ruleguard v0.3.5-0.20210428232022-7a5609fc5ec6 + github.com/quasilyte/go-ruleguard/rules v0.0.0-20210428232022-7a5609fc5ec6 // indirect +) diff --git a/cmd/gorules/go.sum b/cmd/gorules/go.sum new file mode 100644 index 00000000..0579a2f0 --- /dev/null +++ b/cmd/gorules/go.sum @@ -0,0 +1,49 @@ +github.com/cespare/subcmd v1.1.0 h1:r60BAqAKOGcBjxHmV9/WYvq5Qbp3xW9ByB+fRjtty9U= +github.com/cespare/subcmd v1.1.0/go.mod h1:wnVjukiuhSlhZSgGHUilbkHykG7Oglb0sJXpUQ+MoUw= +github.com/go-toolsmith/astequal v1.0.0 h1:4zxD8j3JRFNyLN46lodQuqz3xdKSrur7U/sr0SDS/gQ= +github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= +github.com/go-toolsmith/strparse v1.0.0 h1:Vcw78DnpCAKlM20kSbAyO4mPfJn/lyYA4BJUDxe2Jb4= +github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= +github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/quasilyte/go-ruleguard v0.3.1-0.20210203134552-1b5a410e1cc8/go.mod h1:KsAh3x0e7Fkpgs+Q9pNLS5XpFSvYCEVl5gP9Pp1xp30= +github.com/quasilyte/go-ruleguard v0.3.4 h1:F6l5p6+7WBcTKS7foNQ4wqA39zjn2+RbdbyzGxIq1B0= +github.com/quasilyte/go-ruleguard v0.3.4/go.mod h1:57FZgMnoo6jqxkYKmVj5Fc8vOt0rVzoE/UNAmFFIPqA= +github.com/quasilyte/go-ruleguard v0.3.5-0.20210428232022-7a5609fc5ec6 h1:cHaMS4GXRhIH499NgnDipyy17VgiVuk2D6N+calwncU= +github.com/quasilyte/go-ruleguard v0.3.5-0.20210428232022-7a5609fc5ec6/go.mod h1:B+eagO+T9AoTZFYuROGERvVCM6K2RXDVBsviDUjI4zs= +github.com/quasilyte/go-ruleguard/dsl v0.3.0/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= +github.com/quasilyte/go-ruleguard/dsl v0.3.2 h1:ULi3SLXvDUgb0u2IM5xU6er9KeWBSaUh1NlDjCgLHU8= +github.com/quasilyte/go-ruleguard/dsl v0.3.2/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= +github.com/quasilyte/go-ruleguard/rules v0.0.0-20201231183845-9e62ed36efe1/go.mod h1:7JTjp89EGyU1d6XfBiXihJNG37wB2VRkd125Q1u7Plc= +github.com/quasilyte/go-ruleguard/rules v0.0.0-20210203162857-b223e0831f88/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50= +github.com/quasilyte/go-ruleguard/rules v0.0.0-20210428214800-545e0d2e0bf7/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50= +github.com/quasilyte/go-ruleguard/rules v0.0.0-20210428232022-7a5609fc5ec6 h1:hxEUKRHzSPA0VzHFcQ5EevDk9mNJojv0zg/5rY2wZ4E= +github.com/quasilyte/go-ruleguard/rules v0.0.0-20210428232022-7a5609fc5ec6/go.mod h1:4cgAphtvu7Ftv7vOT2ZOYhC6CvBxZixcasr8qIOTA50= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200812195022-5ae4c3c160a0/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20201230224404-63754364767c h1:xx3+TTG3yS1I6Ola5Kapxr5vZu85vKkcwKyV6ke9fHA= +golang.org/x/tools v0.0.0-20201230224404-63754364767c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/cmd/gorules/main.go b/cmd/gorules/main.go new file mode 100644 index 00000000..44f1cbe7 --- /dev/null +++ b/cmd/gorules/main.go @@ -0,0 +1,137 @@ +package main + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "go/token" + "io/ioutil" + "log" + "path/filepath" + "strings" + + "github.com/cespare/subcmd" + "github.com/quasilyte/go-ruleguard/ruleguard" +) + +func main() { + cmds := []subcmd.Command{ + { + Name: "doc", + Description: "query rules documentation", + Do: docMain, + }, + } + + subcmd.Run(cmds) +} + +func docMain(args []string) { + if err := docCommand(args); err != nil { + log.Fatal(err) + } +} + +func docCommand(args []string) error { + type JsonListEntry struct { + Name string + Filename string + Line int + DocSummary string + DocBefore string + DocAfter string + DocTags []string + } + type JsonList struct { + List []JsonListEntry + } + + fs := flag.NewFlagSet("gorules doc", flag.ExitOnError) + flagRules := fs.String("rules", "", `comma-separated list of ruleguard file paths`) + flagJson := fs.Bool("json", false, `format the output as JSON`) + fs.Parse(args) + + var groupName string + extraArgs := fs.Args() + if len(extraArgs) != 0 { + groupName = extraArgs[0] + } + + e := ruleguard.NewEngine() + fset := token.NewFileSet() + ctx := &ruleguard.ParseContext{ + Fset: fset, + } + filenames := strings.Split(*flagRules, ",") + for _, filename := range filenames { + filename = strings.TrimSpace(filename) + data, err := ioutil.ReadFile(filename) + if err != nil { + return fmt.Errorf("read rules file: %v", err) + } + if err := e.Load(ctx, filename, bytes.NewReader(data)); err != nil { + return fmt.Errorf("parse rules file: %v", err) + } + } + + if *flagJson { + var result JsonList + for _, g := range e.LoadedGroups() { + result.List = append(result.List, JsonListEntry{ + Name: g.Name, + Line: g.Line, + Filename: g.Filename, + DocSummary: g.DocSummary, + DocBefore: g.DocBefore, + DocAfter: g.DocAfter, + DocTags: g.DocTags, + }) + } + var buf bytes.Buffer + enc := json.NewEncoder(&buf) + enc.SetEscapeHTML(false) + if err := enc.Encode(result); err != nil { + return err + } + var pretty bytes.Buffer + if err := json.Indent(&pretty, buf.Bytes(), "", " "); err != nil { + return err + } + fmt.Print(pretty.String()) + return nil + } + if groupName != "" { + var g *ruleguard.GoRuleGroup + groups := e.LoadedGroups() + for i := range groups { + if groups[i].Name == groupName { + g = &groups[i] + break + } + } + if g == nil { + return fmt.Errorf("the requested %s group was not loaded", groupName) + } + fmt.Printf("%s:%d: %s\n\n", filepath.Base(g.Filename), g.Line, g.Name) + fmt.Printf("Full path: %s\n\n", g.Filename) + if g.DocSummary != "" { + fmt.Printf("Summary: %s\n\n", g.DocSummary) + } + if len(g.DocTags) != 0 { + fmt.Printf("Tags: %v\n\n", g.DocTags) + } + if g.DocBefore != "" && g.DocAfter != "" { + fmt.Printf("Before:\n") + fmt.Printf("\t%s\n", g.DocBefore) + fmt.Printf("After:\n") + fmt.Printf("\t%s\n", g.DocAfter) + } + } else { + for _, g := range e.LoadedGroups() { + fmt.Printf("%s:%d: %s\n", filepath.Base(g.Filename), g.Line, g.Name) + } + } + + return nil +}