checkpoint: began to create schema and code generator

This commit is contained in:
Dan Anglin 2024-08-09 17:16:35 +01:00
parent 299b134b58
commit 5b8a407a3f
Signed by: dananglin
GPG key ID: 0C1D44CFBEE68638
5 changed files with 225 additions and 0 deletions

View file

@ -0,0 +1,91 @@
package main
import (
"encoding/json"
"flag"
"fmt"
"os"
"os/exec"
"text/template"
"unicode"
)
func main() {
var (
enbasCLISchemaFilepath string
executorsFilePath string
)
flag.StringVar(&enbasCLISchemaFilepath, "path-to-enbas-cli-schema", "", "The path to the Enbas CLI schema file")
flag.StringVar(&executorsFilePath, "path-to-enbas-executors", "", "The path to the executors Go file")
flag.Parse()
schema, err := readSchemaFile(enbasCLISchemaFilepath)
if err != nil {
fmt.Printf("ERROR: Unable to read the schema file: %v.\n", err)
}
if err := generateExecutors(schema, executorsFilePath); err != nil {
fmt.Printf("ERROR: Unable to generate the executors: %v.\n", err)
}
if err := runGoImports(executorsFilePath); err != nil {
fmt.Printf("ERROR: Unable to run goimports: %v.\n", err)
}
}
func readSchemaFile(path string) (enbasCLISchema, error) {
file, err := os.Open(path)
if err != nil {
return enbasCLISchema{}, fmt.Errorf("unable to open the schema file: %w", err)
}
defer file.Close()
var schema enbasCLISchema
if err := json.NewDecoder(file).Decode(&schema); err != nil {
return enbasCLISchema{}, fmt.Errorf("unable to decode the JSON data: %w", err)
}
return schema, nil
}
func generateExecutors(schema enbasCLISchema, output string) error {
funcMap := template.FuncMap{
"capitalise": capitalise,
"getFlagType": schema.Flags.getType,
"getFlagDescription": schema.Flags.getDescription,
}
tmpl := template.Must(template.New("executor-template").Funcs(funcMap).Parse(executorsFileTemplate))
file, err := os.Create(output)
if err != nil {
return fmt.Errorf("unable to create the output file: %w", err)
}
defer file.Close()
if err := tmpl.Execute(file, schema.Commands); err != nil {
return fmt.Errorf("unable to generate the code from the template: %w", err)
}
return nil
}
func runGoImports(path string) error {
imports := exec.Command("goimports", "-w", path)
if err := imports.Run(); err != nil {
return fmt.Errorf("received an error after running goimports: %w", err)
}
return nil
}
func capitalise(str string) string {
runes := []rune(str)
runes[0] = unicode.ToUpper(runes[0])
return string(runes)
}

View file

@ -0,0 +1,65 @@
package main
import (
"encoding/json"
"fmt"
"os"
)
type enbasCLISchema struct {
Flags enbasCLISchemaFlagMap `json:"flags"`
Commands map[string]enbasCLISchemaCommand `json:"commands"`
}
func newEnbasCLISchemaFromFile(path string) (enbasCLISchema, error) {
file, err := os.Open(path)
if err != nil {
return enbasCLISchema{}, fmt.Errorf("unable to open the schema file: %w", err)
}
defer file.Close()
var schema enbasCLISchema
if err := json.NewDecoder(file).Decode(&schema); err != nil {
return enbasCLISchema{}, fmt.Errorf("unable to decode the JSON data: %w", err)
}
return schema, nil
}
type enbasCLISchemaFlag struct {
Type string `json:"type"`
Description string `json:"description"`
}
type enbasCLISchemaFlagMap map[string]enbasCLISchemaFlag
func (e enbasCLISchemaFlagMap) getType(name string) string {
flag, ok := e[name]
if !ok {
return "UNKNOWN TYPE"
}
return flag.Type
}
func (e enbasCLISchemaFlagMap) getDescription(name string) string {
flag, ok := e[name]
if !ok {
return "UNKNOWN DESCRIPTION"
}
return flag.Description
}
type enbasCLISchemaCommand struct {
AddPrinter bool `json:"addPrinter"`
AddConfig bool `json:"addConfig"`
Flags []enbasCLISchemaFlagReference `json:"flags"`
Summary string `json:"summary"`
}
type enbasCLISchemaFlagReference struct {
Flag string `json:"flag"`
Default string `json:"default"`
}

View file

@ -0,0 +1,36 @@
package main
var executorsFileTemplate = `package executor
{{ range $name, $command := . }}
{{- $struct_name := capitalise $name | printf "%sExecutor" -}}
{{- $new_executor_function_name := capitalise $name | printf "New%sExecutor" -}}
{{ print "" }}
// {{ $struct_name }} is the executor for the {{ $name }} command.
type {{ $struct_name }} struct {
*flag.FlagSet
{{- range $flag := $command.Flags -}}
{{ print "" }}
{{ $flag.Flag }} {{ getFlagType $flag.Flag }}
{{- end -}}
{{ print "" }}
}
func {{ $new_executor_function_name }}() *{{ $struct_name }} {
exe := {{ $struct_name }}{
FlagSet: flag.NewFlagSet({{ printf "%q" $name }}, flag.ExitOnError),
}
{{ print "" }}
exe.Usage = commandUsageFunc({{ printf "%q" $name }}, {{ printf "%q" $command.Summary }}, showExe.FlagSet)
{{ print "" }}
{{- range $flag := $command.Flags -}}
{{- if eq (getFlagType $flag.Flag) "string" -}}
exe.StringVar(&exe.{{ $flag.Flag }}, {{ printf "%q" $flag.Flag }}, {{ printf "%q" $flag.Default }}, {{ getFlagDescription $flag.Flag | printf "%q" }})
{{- else if eq (getFlagType $flag.Flag) "bool" -}}
exe.BoolVar(&exe.{{ $flag.Flag }}, {{ printf "%q" $flag.Flag }}, {{ $flag.Default }}, {{ getFlagDescription $flag.Flag | printf "%q" }})
{{- end -}}
{{- end -}}
{{ print "" }}
return &exe
}
{{ end }}
`

View file

@ -1,5 +1,7 @@
package internal package internal
//go:generate go run ../cmd/enbas-cli-generators --path-to-enbas-cli-schema ../schema/enbas_cli_schema.json --path-to-enbas-executors ../executors-wip.go
const ( const (
ApplicationName = "enbas" ApplicationName = "enbas"
ApplicationWebsite = "https://codeflow.dananglin.me.uk/apollo/enbas" ApplicationWebsite = "https://codeflow.dananglin.me.uk/apollo/enbas"

View file

@ -0,0 +1,31 @@
{
"flags": {
"full": {
"type": "bool",
"description": "Set to true to print the build information in full"
},
"instance": {
"type": "string",
"description": "The instance that you want to log into"
}
},
"commands": {
"login": {
"addPrinter": true,
"addConfig": true,
"flags": [
{ "flag": "instance", "default": "" }
],
"summary": "Login to an account on GoToSocial"
},
"version": {
"addPrinter": true,
"addConfig": false,
"summary": "Prints the application's version and build information",
"flags": [
{ "flag": "full", "default": "false" }
]
}
}
}