evcc-io/cmd/implement/implement.go

179 lines
4.2 KiB
Go

package main
import (
"bytes"
_ "embed"
"fmt"
"go/format"
"io"
"os"
"reflect"
"strconv"
"strings"
"text/template"
"github.com/Masterminds/sprig/v3"
"github.com/evcc-io/evcc/api"
"golang.org/x/tools/imports"
)
//go:generate go tool implement
//go:embed implement.tpl
var srcTmpl string
type paramStruct struct {
VarName, Signature string
}
type funcStruct struct {
Signature, Function, VarName, ReturnTypes string
Params []paramStruct
}
type typeStruct struct {
Type string
Functions []funcStruct
}
func getTypeImport(t reflect.Type) string {
n := t.Name()
if n == "" {
// unnamed type, e.g. []string
return t.String()
}
if p := t.PkgPath(); p != "" {
if s := strings.Split(p, "github.com/evcc-io/evcc/"); len(s) == 2 {
return fmt.Sprintf("%s.%s", s[1], n)
} else {
return fmt.Sprintf("%s.%s", p, n)
}
}
return n
}
func generate(out io.Writer) error {
tmpl, err := template.New("gen").Funcs(sprig.FuncMap()).Parse(srcTmpl)
if err != nil {
fmt.Printf("invalid template: %s", err)
os.Exit(2)
}
var types []typeStruct
for _, typ := range []reflect.Type{
reflect.TypeFor[api.Battery](),
reflect.TypeFor[api.BatteryCapacity](),
reflect.TypeFor[api.BatteryController](),
reflect.TypeFor[api.BatteryPowerLimiter](),
reflect.TypeFor[api.BatterySocLimiter](),
reflect.TypeFor[api.ChargeController](),
reflect.TypeFor[api.ChargeRater](),
reflect.TypeFor[api.ChargerEx](),
reflect.TypeFor[api.ChargeState](),
reflect.TypeFor[api.CurrentController](),
reflect.TypeFor[api.CurrentGetter](),
reflect.TypeFor[api.CurrentLimiter](),
reflect.TypeFor[api.Curtailer](),
reflect.TypeFor[api.Dimmer](),
reflect.TypeFor[api.Identifier](),
reflect.TypeFor[api.MaxACPowerGetter](),
reflect.TypeFor[api.Meter](),
reflect.TypeFor[api.MeterEnergy](),
reflect.TypeFor[api.MeterReturnEnergy](),
reflect.TypeFor[api.PhaseCurrents](),
reflect.TypeFor[api.PhaseGetter](),
reflect.TypeFor[api.PhasePowers](),
reflect.TypeFor[api.PhaseSwitcher](),
reflect.TypeFor[api.PhaseVoltages](),
reflect.TypeFor[api.PowerLimiter](),
reflect.TypeFor[api.Resurrector](),
reflect.TypeFor[api.SocLimiter](),
reflect.TypeFor[api.StatusReasoner](),
reflect.TypeFor[api.VehicleClimater](),
reflect.TypeFor[api.VehicleFinishTimer](),
reflect.TypeFor[api.VehicleOdometer](),
reflect.TypeFor[api.VehiclePosition](),
reflect.TypeFor[api.VehicleRange](),
} {
lastPart := typ.Name()
var functions []funcStruct
for methodIndex := 0; methodIndex < typ.NumMethod(); methodIndex++ {
m := typ.Method(methodIndex)
var params []paramStruct
for paramIndex := 0; paramIndex < m.Type.NumIn(); paramIndex++ {
p := m.Type.In(paramIndex)
params = append(params, paramStruct{
VarName: "p" + strconv.Itoa(paramIndex),
Signature: getTypeImport(p),
})
}
var parameters []string
for input := range m.Type.Ins() {
parameters = append(parameters, getTypeImport(input))
}
var returns []string
for output := range m.Type.Outs() {
returns = append(returns, getTypeImport(output))
}
functions = append(functions, funcStruct{
VarName: strings.ToLower(lastPart[:1]) + lastPart[1:] + strconv.Itoa(methodIndex),
Signature: fmt.Sprintf("func(%s) (%s)", strings.Join(parameters, ", "), strings.Join(returns, ", ")),
Function: m.Name,
Params: params,
ReturnTypes: fmt.Sprintf("(%s)", strings.Join(returns, ",")),
})
}
types = append(types, typeStruct{
Type: typ.Name(),
Functions: functions,
})
}
vars := struct {
Types []typeStruct
}{
Types: types,
}
return tmpl.Execute(out, vars)
}
func main() {
name := "../../api/implement/implementations.go"
generated := new(bytes.Buffer)
if err := generate(generated); err != nil {
fmt.Println(err)
os.Exit(2)
}
formatted, err := format.Source(generated.Bytes())
if err != nil {
formatted = generated.Bytes()
}
formatted, err = imports.Process(name, formatted, nil)
if err != nil {
fmt.Println(err)
os.Exit(3)
}
file, err := os.Create(name)
if err != nil {
fmt.Println(err)
os.Exit(2)
}
if _, err := file.Write(formatted); err != nil {
fmt.Println(err)
os.Exit(2)
}
}