package main import ( "fmt" "strings" "text/template" "time" ) func printArgs(args []fnArg) string { var argStr []string for _, arg := range args { argStr = append(argStr, arg.String()) } return strings.Join(argStr, ", ") } func buildImports(specs []importSpec) string { var imports strings.Builder imports.WriteString(` import( "errors" "time" "github.com/moby/moby/v2/pkg/plugins" `) for _, i := range specs { imports.WriteString("\t" + i.String() + "\n") } imports.WriteString(`) `) return imports.String() } func marshalType(t string) string { switch t { case "error": // convert error types to plain strings to ensure the values are encoded/decoded properly return "string" default: return t } } func isErr(t string) bool { switch t { case "error": return true default: return false } } // Need to use this helper due to issues with go-vet func buildTag(s string) string { return "+build " + s } var templFuncs = template.FuncMap{ "printArgs": printArgs, "marshalType": marshalType, "isErr": isErr, "lower": strings.ToLower, "title": title, "tag": buildTag, "imports": buildImports, "goduration": goduration, } // goduration formats a time.Duration as Go code (e.g., 2 * time.Minute) func goduration(d time.Duration) string { seconds := int64(d.Seconds()) switch { case d == 0: return "0 * time.Nanosecond" case seconds%(60*60) == 0 && seconds != 0: return fmt.Sprintf("%d * time.Hour", seconds/3600) case seconds%60 == 0 && seconds != 0: return fmt.Sprintf("%d * time.Minute", seconds/60) case seconds != 0: return fmt.Sprintf("%d * time.Second", seconds) default: // fallback to nanoseconds for sub-second durations return fmt.Sprintf("%d * time.Nanosecond", d.Nanoseconds()) } } func title(s string) string { if strings.ToLower(s) == "id" { return "ID" } return strings.Title(s) //nolint:staticcheck // SA1019: strings.Title is deprecated: The rule Title uses for word boundaries does not handle Unicode punctuation properly. Use golang.org/x/text/cases instead. } var generatedTempl = template.Must(template.New("rpc_cient").Funcs(templFuncs).Parse(` // Code generated by pluginrpc-gen. DO NOT EDIT. {{ range $k, $v := .BuildTags }} // {{ tag $k }} {{ end }} package {{ .Name }} {{ imports .Imports }} const ( longTimeout = {{ .LongTimeout | goduration }} shortTimeout = {{ .ShortTimeout | goduration }} ) type client interface{ CallWithOptions(serviceMethod string, args interface{}, ret interface{}, opts ...func(*plugins.RequestOpts)) error } type {{ .InterfaceType }}Proxy struct { client } {{ range .Functions }} type {{ $.InterfaceType }}Proxy{{ .Name }}Request struct{ {{- range .Args }} {{ title .Name }} {{ .ArgType }} {{- end -}} } type {{ $.InterfaceType }}Proxy{{ .Name }}Response struct{ {{- range .Returns }} {{ title .Name }} {{ marshalType .ArgType }} {{- end -}} } {{- if .Doc }} {{ .Doc }} {{- end }} func (pp *{{ $.InterfaceType }}Proxy) {{ .Name }}({{ printArgs .Args }}) ({{ printArgs .Returns }}) { var( req {{ $.InterfaceType }}Proxy{{ .Name }}Request ret {{ $.InterfaceType }}Proxy{{ .Name }}Response ) {{ range .Args }} req.{{ title .Name }} = {{ lower .Name }} {{ end }} if err = pp.CallWithOptions("{{ $.RPCName }}.{{ .Name }}", req, &ret, plugins.WithRequestTimeout({{ .TimeoutType }}Timeout)); err != nil { return } {{ range $r := .Returns }} {{ if isErr .ArgType }} if ret.{{ title .Name }} != "" { {{ lower .Name }} = errors.New(ret.{{ title .Name }}) } {{ end }} {{ if isErr .ArgType | not }} {{ lower .Name }} = ret.{{ title .Name }} {{ end }} {{ end }} return } {{ end }} `))