Files
go-unifi/codegen/clients.go
Mateusz Filipowicz fadc5ada8b feat: add client customization option (#20)
* feat: add client customization

* chore: fix linting

* feat: allow excluding client function by resource name
2025-02-16 23:00:05 +01:00

203 lines
5.6 KiB
Go

package main
import (
_ "embed"
"fmt"
"sort"
"strings"
)
// ClientFunction is the interface for client functions.
type ClientFunction interface {
Name() string
ResourceName() string
Comment() string
Signature() string
}
type FunctionParam struct {
Name string
Type string
}
type Comment struct {
comment string
resourceName string
}
func (c *Comment) Name() string {
return ""
}
func (c *Comment) Comment() string {
return c.comment
}
func (c *Comment) Signature() string {
return ""
}
func (c *Comment) ResourceName() string {
return c.resourceName
}
// CustomClientFunction represents a custom client function definition.
type CustomClientFunction struct {
Resource string `yaml:"resourceName"`
FunctionName string `yaml:"name"`
Parameters []FunctionParam `yaml:"params"`
ReturnParameters []string `yaml:"returns"`
FunctionComment string `yaml:"comment"`
}
func (c *CustomClientFunction) Name() string {
return c.FunctionName
}
func (c *CustomClientFunction) ResourceName() string {
return c.Resource
}
// Signature returns the signature string for the custom client function.
func (c *CustomClientFunction) Signature() string {
if c.Name() == "" {
return ""
}
var b strings.Builder
//if c.comment != "" {
// b.WriteString(fmt.Sprintf("// %s %s\n", c.Name, c.Comment))
//}
b.WriteString(c.Name())
b.WriteString("(")
// Build parameters without trailing comma
params := make([]string, 0, len(c.Parameters))
for _, v := range c.Parameters {
params = append(params, fmt.Sprintf("%s %s", v.Name, v.Type))
}
b.WriteString(strings.Join(params, ", "))
b.WriteString(")")
if len(c.ReturnParameters) > 1 {
b.WriteString(" (")
b.WriteString(strings.Join(c.ReturnParameters, ", "))
b.WriteString(")")
} else if len(c.ReturnParameters) == 1 {
b.WriteString(" " + c.ReturnParameters[0])
}
return b.String()
}
func (c *CustomClientFunction) Comment() string {
return c.FunctionComment
}
// ClientInfo represents the client information used for code generation.
type ClientInfo struct {
Imports []string
Functions []ClientFunction
}
type ClientInfoBuilder struct {
imports []string
functions []ClientFunction
}
func (c *ClientInfoBuilder) AddFunction(f ClientFunction) *ClientInfoBuilder { //nolint: unparam
c.functions = append(c.functions, f)
return c
}
func (c *ClientInfoBuilder) AddFunctions(f []CustomClientFunction) *ClientInfoBuilder {
for _, v := range f {
c.functions = append(c.functions, &v)
}
return c
}
func (c *ClientInfoBuilder) addResourceFunction(actionName, resourceName, comment string, additionalParams []FunctionParam, additionalReturns []string) {
fName := fmt.Sprintf("%s%s", actionName, resourceName)
params := []FunctionParam{
{"ctx", "context.Context"},
{"site", "string"},
}
params = append(params, additionalParams...)
returns := additionalReturns
returns = append(returns, "error")
f := CustomClientFunction{
FunctionName: fName,
Resource: resourceName,
Parameters: params,
ReturnParameters: returns,
FunctionComment: fmt.Sprintf("%s %s", fName, comment),
}
c.AddFunction(&f)
}
func singlePointerReturn(name string) []string {
return []string{"*" + name}
}
func singlePointerParam(name string) []FunctionParam {
return []FunctionParam{{strings.ToLower(name[0:1]), "*" + name}}
}
func (c *ClientInfoBuilder) AddResource(r *Resource) *ClientInfoBuilder {
c.AddFunction(&Comment{comment: fmt.Sprintf("client methods for %s resource", r.Name()), resourceName: r.Name()})
if r.IsSetting() {
c.addResourceFunction("Get", r.Name(), "retrieves the settings for a resource", nil, singlePointerReturn(r.Name()))
c.addResourceFunction("Update", r.Name(), "updates a resource", singlePointerParam(r.Name()), singlePointerReturn(r.Name()))
return c
}
c.addResourceFunction("Get", r.Name(), "retrieves a resource", []FunctionParam{{"id", "string"}}, singlePointerReturn(r.Name()))
c.addResourceFunction("List", r.Name(), "lists the resources", nil, []string{"[]*" + r.Name()})
c.addResourceFunction("Create", r.Name(), "creates a resource", singlePointerParam(r.Name()), singlePointerReturn(r.Name()))
c.addResourceFunction("Update", r.Name(), "updates a resource", singlePointerParam(r.Name()), singlePointerReturn(r.Name()))
c.addResourceFunction("Delete", r.Name(), "deletes a resource", []FunctionParam{{"id", "string"}}, nil)
return c
}
func (c *ClientInfoBuilder) AddImport(i string) *ClientInfoBuilder {
c.imports = append(c.imports, i)
return c
}
func (c *ClientInfoBuilder) AddImports(i []string) *ClientInfoBuilder {
c.imports = append(c.imports, i...)
return c
}
func (c *ClientInfoBuilder) Build() *ClientInfo {
// Sort the functions by resource name and then by name.
sort.Slice(c.functions, func(i, j int) bool {
if c.functions[i].ResourceName() == c.functions[j].ResourceName() {
return c.functions[i].Signature() < c.functions[j].Signature()
}
return c.functions[i].ResourceName() < c.functions[j].ResourceName()
})
return newClientInfo(c.imports, c.functions)
}
func NewClientInfoBuilder() *ClientInfoBuilder {
return &ClientInfoBuilder{}
}
// newClientInfo creates ClientInfo from the provided resources.
func newClientInfo(imports []string, functions []ClientFunction) *ClientInfo {
return &ClientInfo{imports, functions}
}
//go:embed client.go.tmpl
var clientGoTemplate string
// GenerateCode generates the code for the client using a template.
func (c *ClientInfo) GenerateCode() (string, error) {
return generateCodeFromTemplate("client.go.tmpl", clientGoTemplate, c)
}
// Name returns the name of the client.
func (c *ClientInfo) Name() string {
return "client"
}