Refactor kafka to pure Go (franz-go), fix DBC stubs, update Dockerfile
This commit is contained in:
212
pkg/can-go/cmd/cantool/main.go
Normal file
212
pkg/can-go/cmd/cantool/main.go
Normal file
@@ -0,0 +1,212 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/scanner"
|
||||
|
||||
"github.com/fiskerinc/cloud-services/pkg/can-go/internal/generate"
|
||||
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc"
|
||||
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis"
|
||||
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/definitiontypeorder"
|
||||
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/intervals"
|
||||
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/lineendings"
|
||||
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/messagenames"
|
||||
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/multiplexedsignals"
|
||||
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/newsymbols"
|
||||
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/nodereferences"
|
||||
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/noreservedsignals"
|
||||
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/requireddefinitions"
|
||||
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/signalbounds"
|
||||
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/signalnames"
|
||||
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/singletondefinitions"
|
||||
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/siunits"
|
||||
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/uniquenodenames"
|
||||
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/uniquesignalnames"
|
||||
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/unitsuffixes"
|
||||
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/valuedescriptions"
|
||||
"github.com/fiskerinc/cloud-services/pkg/can-go/pkg/dbc/analysis/passes/version"
|
||||
"github.com/fatih/color"
|
||||
"gopkg.in/alecthomas/kingpin.v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := kingpin.New("cantool", "CAN tool for Go programmers")
|
||||
generateCommand(app)
|
||||
lintCommand(app)
|
||||
kingpin.MustParse(app.Parse(os.Args[1:]))
|
||||
}
|
||||
|
||||
func generateCommand(app *kingpin.Application) {
|
||||
command := app.Command("generate", "generate CAN messages")
|
||||
inputDir := command.
|
||||
Arg("input-dir", "input directory").
|
||||
Required().
|
||||
ExistingDir()
|
||||
outputDir := command.
|
||||
Arg("output-dir", "output directory").
|
||||
Required().
|
||||
String()
|
||||
command.Action(func(c *kingpin.ParseContext) error {
|
||||
return filepath.Walk(*inputDir, func(p string, i os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if i.IsDir() || filepath.Ext(p) != ".dbc" {
|
||||
return nil
|
||||
}
|
||||
relPath, err := filepath.Rel(*inputDir, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
outputFile := relPath + ".go"
|
||||
outputPath := filepath.Join(*outputDir, outputFile)
|
||||
return genGo(p, outputPath)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func lintCommand(app *kingpin.Application) {
|
||||
command := app.Command("lint", "lint DBC files")
|
||||
fileOrDir := command.
|
||||
Arg("file-or-dir", "DBC file or directory").
|
||||
Required().
|
||||
ExistingFileOrDir()
|
||||
command.Action(func(context *kingpin.ParseContext) error {
|
||||
filesToLint, err := resolveFileOrDirectory(*fileOrDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var hasFailed bool
|
||||
for _, lintFile := range filesToLint {
|
||||
f, err := os.Open(lintFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
source, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p := dbc.NewParser(f.Name(), source)
|
||||
if err := p.Parse(); err != nil {
|
||||
printError(source, err.Position(), err.Reason(), "parse")
|
||||
continue
|
||||
}
|
||||
for _, a := range analyzers() {
|
||||
pass := &analysis.Pass{
|
||||
Analyzer: a,
|
||||
File: p.File(),
|
||||
}
|
||||
if err := a.Run(pass); err != nil {
|
||||
return err
|
||||
}
|
||||
hasFailed = hasFailed || len(pass.Diagnostics) > 0
|
||||
for _, d := range pass.Diagnostics {
|
||||
printError(source, d.Pos, d.Message, a.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
if hasFailed {
|
||||
return errors.New("one or more lint errors")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func analyzers() []*analysis.Analyzer {
|
||||
return []*analysis.Analyzer{
|
||||
// TODO: Re-evaluate if we want boolprefix.Analyzer(), since it creates a lot of churn in vendor schemas
|
||||
definitiontypeorder.Analyzer(),
|
||||
intervals.Analyzer(),
|
||||
lineendings.Analyzer(),
|
||||
messagenames.Analyzer(),
|
||||
multiplexedsignals.Analyzer(),
|
||||
newsymbols.Analyzer(),
|
||||
nodereferences.Analyzer(),
|
||||
noreservedsignals.Analyzer(),
|
||||
requireddefinitions.Analyzer(),
|
||||
signalbounds.Analyzer(),
|
||||
signalnames.Analyzer(),
|
||||
singletondefinitions.Analyzer(),
|
||||
siunits.Analyzer(),
|
||||
uniquenodenames.Analyzer(),
|
||||
uniquesignalnames.Analyzer(),
|
||||
unitsuffixes.Analyzer(),
|
||||
valuedescriptions.Analyzer(),
|
||||
version.Analyzer(),
|
||||
}
|
||||
}
|
||||
|
||||
func genGo(inputFile, outputFile string) error {
|
||||
if err := os.MkdirAll(filepath.Dir(outputFile), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
input, err := ioutil.ReadFile(inputFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
result, err := generate.Compile(inputFile, input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, warning := range result.Warnings {
|
||||
return warning
|
||||
}
|
||||
output, err := generate.Database(result.Hash, result.Database)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutil.WriteFile(outputFile, output, 0o600); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("hash:", result.Hash)
|
||||
fmt.Println("version:", result.Database.Version)
|
||||
fmt.Println("wrote:", outputFile)
|
||||
return nil
|
||||
}
|
||||
|
||||
func resolveFileOrDirectory(fileOrDirectory string) ([]string, error) {
|
||||
fileInfo, err := os.Stat(fileOrDirectory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !fileInfo.IsDir() {
|
||||
return []string{fileOrDirectory}, nil
|
||||
}
|
||||
var files []string
|
||||
if err := filepath.Walk(fileOrDirectory, func(path string, info os.FileInfo, err error) error {
|
||||
if !info.IsDir() && filepath.Ext(path) == ".dbc" {
|
||||
files = append(files, path)
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func printError(source []byte, pos scanner.Position, msg, name string) {
|
||||
fmt.Printf("\n%s: %s (%s)\n", pos, color.RedString("%s", msg), name)
|
||||
fmt.Printf("%s\n", getSourceLine(source, pos))
|
||||
fmt.Printf("%s\n", caretAtPosition(pos))
|
||||
}
|
||||
|
||||
func getSourceLine(source []byte, pos scanner.Position) []byte {
|
||||
lineStart := pos.Offset
|
||||
for lineStart > 0 && source[lineStart-1] != '\n' {
|
||||
lineStart--
|
||||
}
|
||||
lineEnd := pos.Offset
|
||||
for lineEnd < len(source) && source[lineEnd] != '\n' {
|
||||
lineEnd++
|
||||
}
|
||||
return source[lineStart:lineEnd]
|
||||
}
|
||||
|
||||
func caretAtPosition(pos scanner.Position) string {
|
||||
return strings.Repeat(" ", pos.Column-1) + color.YellowString("^")
|
||||
}
|
||||
Reference in New Issue
Block a user