# hostgen A code generator for Navidrome's plugin host functions. It reads Go interface definitions with special annotations and generates Extism host function wrappers. ## Usage ```bash hostgen -input -output -package [-v] [-dry-run] [-host-only] [-plugin-only] ``` ### Flags | Flag | Description | Default | |----------------|----------------------------------------------------------------|----------| | `-input` | Directory containing Go source files with annotated interfaces | Required | | `-output` | Directory where generated files will be written | Required | | `-package` | Package name for generated files | Required | | `-v` | Verbose output | `false` | | `-dry-run` | Parse and validate without writing files | `false` | | `-host-only` | Generate only host-side wrapper code | `false` | | `-plugin-only` | Generate only plugin/client-side wrapper code | `false` | By default, both host and plugin code are generated. Use `-host-only` or `-plugin-only` to generate only one type. ### Example ```bash go run ./plugins/cmd/hostgen \ -input ./plugins/host \ -output ./plugins/host \ -package host ``` Or via `go generate` (recommended): ```go //go:generate go run ../cmd/hostgen -input . -output . -package host package host ``` ## Annotations ### `//nd:hostservice` Marks an interface as a host service that will have wrappers generated. ```go //nd:hostservice name= permission= type MyService interface { ... } ``` | Parameter | Description | Required | |--------------|-----------------------------------------------------------------|----------| | `name` | Service name used in generated type names and function prefixes | Yes | | `permission` | Permission required by plugins to use this service | Yes | ### `//nd:hostfunc` Marks a method within a host service interface for export to plugins. ```go //nd:hostfunc [name=] MethodName(ctx context.Context, ...) (result Type, err error) ``` | Parameter | Description | Required | |-----------|-------------------------------------------------------------------------|----------| | `name` | Custom export name (default: `_` in lowercase) | No | ## Input Format Host service interfaces must follow these conventions: 1. **First parameter must be `context.Context`** - Required for all methods 2. **Last return value should be `error`** - For proper error handling 3. **Annotations must be on consecutive lines** - No blank comment lines between doc and annotation ### Example Interface ```go package host import "context" // SubsonicAPIService provides access to Navidrome's Subsonic API. // This documentation becomes part of the generated code. //nd:hostservice name=SubsonicAPI permission=subsonicapi type SubsonicAPIService interface { // Call executes a Subsonic API request and returns the response. //nd:hostfunc Call(ctx context.Context, uri string) (response string, err error) } ``` ## Generated Output For each annotated interface, hostgen generates: ### Request/Response Types ```go // SubsonicAPICallRequest is the request type for SubsonicAPI.Call. type SubsonicAPICallRequest struct { Uri string `json:"uri"` } // SubsonicAPICallResponse is the response type for SubsonicAPI.Call. type SubsonicAPICallResponse struct { Response string `json:"response,omitempty"` Error string `json:"error,omitempty"` } ``` ### Registration Function ```go // RegisterSubsonicAPIHostFunctions registers SubsonicAPI service host functions. func RegisterSubsonicAPIHostFunctions(service SubsonicAPIService) []extism.HostFunction { return []extism.HostFunction{ newSubsonicAPICallHostFunction(service), } } ``` ### Host Function Wrappers Each method gets a wrapper that: 1. Reads JSON request from plugin memory 2. Unmarshals to the request type 3. Calls the service method 4. Marshals the response 5. Writes JSON response to plugin memory ## Supported Types hostgen supports these Go types in method signatures: | Type | JSON Representation | |-------------------------------|------------------------------------------| | `string`, `int`, `bool`, etc. | Native JSON types | | `[]T` (slices) | JSON arrays | | `map[K]V` (maps) | JSON objects | | `*T` (pointers) | Nullable fields | | `interface{}` / `any` | Converts to `any` | | Custom structs | JSON objects (must be JSON-serializable) | ### Multiple Return Values Methods can return multiple values (plus error): ```go //nd:hostfunc Search(ctx context.Context, query string) (results []string, total int, hasMore bool, err error) ``` Generates: ```go type ServiceSearchResponse struct { Results []string `json:"results,omitempty"` Total int `json:"total,omitempty"` HasMore bool `json:"hasMore,omitempty"` Error string `json:"error,omitempty"` } ``` ## Output Files ### Host Code (Navidrome-side) Generated files are named `_gen.go` (lowercase) and placed in the output directory. Each file includes: - `// Code generated by hostgen. DO NOT EDIT.` header - Required imports (`context`, `encoding/json`, `extism`) - Request/response struct types - Registration function - Host function wrappers - Helper functions (`writeResponse`, `writeErrorResponse`) ### Plugin/Client Code (TinyGo WASM) Generated files are named `nd_host_.go` (lowercase) and placed in the `go/` subdirectory of the output directory. These files are intended for use in Navidrome plugins built with TinyGo. Each file includes: - `// Code generated by hostgen. DO NOT EDIT.` header - Required imports (`encoding/json`, `errors`, `github.com/extism/go-pdk`) - `//go:wasmimport` declarations for each host function - Response struct types - Wrapper functions that handle memory allocation and JSON parsing ### Example Output Structure ``` output/ ├── subsonicapi_gen.go # Host-side code (for Navidrome) └── go/ └── nd_host_subsonicapi.go # Plugin-side code (for TinyGo plugins) ``` ## Troubleshooting ### Annotations Not Detected Ensure annotations are on consecutive lines with no blank `//` lines: ```go // ✅ Correct // Documentation for the service. //nd:hostservice name=Test permission=test // ❌ Wrong - blank comment line breaks detection // Documentation for the service. // //nd:hostservice name=Test permission=test ``` ### Methods Not Exported Methods without `//nd:hostfunc` annotation are skipped. Ensure the annotation is directly above the method: ```go // ✅ Correct // Method documentation. //nd:hostfunc MyMethod(ctx context.Context) error // ❌ Wrong - annotation not directly above method //nd:hostfunc MyMethod(ctx context.Context) error ``` ### Generated Files Skipped Files ending in `_gen.go` are automatically skipped during parsing to avoid processing previously generated code. ## Development Run tests: ```bash go test -v ./plugins/cmd/hostgen/... ``` The test suite includes: - CLI integration tests - Complex type handling (structs, slices, maps, pointers) - Multiple return value scenarios - Error cases and edge conditions