From a0a5168f5f7af0cb4e4dece2f9d44d750241e3fc Mon Sep 17 00:00:00 2001 From: Deluan Date: Tue, 23 Dec 2025 17:41:29 -0500 Subject: [PATCH] fix(generator): error-only methods in response handling Signed-off-by: Deluan --- plugins/cmd/hostgen/internal/generator.go | 17 +++++++++++++--- plugins/cmd/hostgen/internal/types.go | 20 ++++++++++++------- plugins/cmd/hostgen/testdata/meta_expected.go | 10 +++++++++- plugins/cmd/hostgen/testdata/ping_expected.go | 10 +++++++++- 4 files changed, 45 insertions(+), 12 deletions(-) diff --git a/plugins/cmd/hostgen/internal/generator.go b/plugins/cmd/hostgen/internal/generator.go index 539f5af96..8e7eeb06e 100644 --- a/plugins/cmd/hostgen/internal/generator.go +++ b/plugins/cmd/hostgen/internal/generator.go @@ -22,6 +22,7 @@ func GenerateService(svc Service, pkgName string) ([]byte, error) { "needsJSON": NeedsJSON, "needsRequestType": func(m Method) bool { return m.NeedsRequestType() }, "needsRespType": func(m Method) bool { return m.NeedsResponseType() }, + "isErrorOnly": func(m Method) bool { return m.IsErrorOnly() }, "hasErrFromRead": hasErrorFromRead, "readParam": generateReadParam, "writeReturn": generateWriteReturn, @@ -292,14 +293,24 @@ func new{{$.Service.Name}}{{.Name}}HostFunction(service {{$.Service.Interface}}) {{- end}} {{- if .HasError}} if err != nil { -{{- if needsRespType .}} +{{- if isErrorOnly .}} + // Write error string to plugin memory + if ptr, err := p.WriteString(err.Error()); err == nil { + stack[0] = ptr + } +{{- else if needsRespType .}} {{$.Service.Name | lower}}WriteError(p, stack, err) {{- end}} return } {{- end}} -{{- if needsRespType .}} +{{- if isErrorOnly .}} + // Write empty string to indicate success + if ptr, err := p.WriteString(""); err == nil { + stack[0] = ptr + } +{{- else if needsRespType .}} // Write JSON response to plugin memory resp := {{responseType .}}{ {{- range .Returns}} @@ -319,7 +330,7 @@ func new{{$.Service.Name}}{{.Name}}HostFunction(service {{$.Service.Interface}}) {{- else}} []extism.ValueType{ {{- range $i, $p := .Params}}{{if $i}}, {{end}}{{valueType $p.Type}}{{end}}{{if not .HasParams}}{{end}} }, {{- end}} -{{- if needsRespType .}} +{{- if or (needsRespType .) (isErrorOnly .)}} []extism.ValueType{extism.ValueTypePTR}, {{- else}} []extism.ValueType{ {{- range $i, $r := .Returns}}{{if $i}}, {{end}}{{valueType $r.Type}}{{end}}{{if not .HasReturns}}{{end}} }, diff --git a/plugins/cmd/hostgen/internal/types.go b/plugins/cmd/hostgen/internal/types.go index d0eec0470..6871559cb 100644 --- a/plugins/cmd/hostgen/internal/types.go +++ b/plugins/cmd/hostgen/internal/types.go @@ -176,14 +176,15 @@ func (m Method) NeedsRequestType() bool { } // NeedsResponseType returns true if a response struct is needed. -// Needed when we have complex returns that require JSON, or error handling. +// Needed when we have complex returns that require JSON (but not for error-only methods). func (m Method) NeedsResponseType() bool { - // If there's an error, we need a response type to serialize it - if m.HasError { - // But only if there are also returns, or if returns need JSON - if m.HasReturns() { - return true - } + // Error-only methods return a simple string, not JSON + if m.IsErrorOnly() { + return false + } + // If there's an error with other returns, we need a response type + if m.HasError && m.HasReturns() { + return true } for _, r := range m.Returns { if NeedsJSON(r.Type) { @@ -193,6 +194,11 @@ func (m Method) NeedsResponseType() bool { return false } +// IsErrorOnly returns true if the method only returns an error (no other return values). +func (m Method) IsErrorOnly() bool { + return m.HasError && !m.HasReturns() +} + // toJSONName converts a Go identifier to camelCase JSON field name. func toJSONName(name string) string { if name == "" { diff --git a/plugins/cmd/hostgen/testdata/meta_expected.go b/plugins/cmd/hostgen/testdata/meta_expected.go index 797a56b4b..435b16029 100644 --- a/plugins/cmd/hostgen/testdata/meta_expected.go +++ b/plugins/cmd/hostgen/testdata/meta_expected.go @@ -75,11 +75,19 @@ func newMetaSetHostFunction(service MetaService) extism.HostFunction { // Call the service method err = service.Set(ctx, req.Data) if err != nil { + // Write error string to plugin memory + if ptr, err := p.WriteString(err.Error()); err == nil { + stack[0] = ptr + } return } + // Write empty string to indicate success + if ptr, err := p.WriteString(""); err == nil { + stack[0] = ptr + } }, []extism.ValueType{extism.ValueTypePTR}, - []extism.ValueType{}, + []extism.ValueType{extism.ValueTypePTR}, ) } diff --git a/plugins/cmd/hostgen/testdata/ping_expected.go b/plugins/cmd/hostgen/testdata/ping_expected.go index ff3ce8d50..eb44d1d9b 100644 --- a/plugins/cmd/hostgen/testdata/ping_expected.go +++ b/plugins/cmd/hostgen/testdata/ping_expected.go @@ -24,10 +24,18 @@ func newPingPingHostFunction(service PingService) extism.HostFunction { // Call the service method err := service.Ping(ctx) if err != nil { + // Write error string to plugin memory + if ptr, err := p.WriteString(err.Error()); err == nil { + stack[0] = ptr + } return } + // Write empty string to indicate success + if ptr, err := p.WriteString(""); err == nil { + stack[0] = ptr + } }, []extism.ValueType{}, - []extism.ValueType{}, + []extism.ValueType{extism.ValueTypePTR}, ) }