Skip to content

Commit 8f35742

Browse files
authored
simplify the FunctionalOptions by removing the Options struct (#20)
* simplify the FunctionalOptions by removing the Options struct * remove the atomic.Value for the entrypoint on extism * fix up ctx handling, add more tests * remove SetEntrypointName on extism * harmonize compile logging * remove the defaults being set in the NewCompiler, they should all be set in the applyDefaults method * centralize logger setup in Compiler initialization
1 parent 3b53ec5 commit 8f35742

File tree

10 files changed

+887
-346
lines changed

10 files changed

+887
-346
lines changed

internal/helpers/logger.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func SetupLogger(
2626
handler = defaultHandler.WithGroup(vmName)
2727
// Create a logger from the handler
2828
defaultLogger := slog.New(handler)
29-
defaultLogger.Warn("Handler is nil, using the default logger configuration.")
29+
defaultLogger.Debug("Handler is nil, using the default logger configuration.")
3030
}
3131

3232
var logger *slog.Logger

machines/extism/compiler/compiler.go

Lines changed: 15 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,15 @@ import (
55
"fmt"
66
"io"
77
"log/slog"
8-
"sync/atomic"
98

109
extismSDK "github.com/extism/go-sdk"
1110
"github.com/robbyt/go-polyscript/execution/script"
12-
"github.com/robbyt/go-polyscript/internal/helpers"
1311
"github.com/robbyt/go-polyscript/machines/extism/compiler/internal/compile"
1412
)
1513

1614
// Compiler implements the script.Compiler interface for WASM modules
1715
type Compiler struct {
18-
entryPointName atomic.Value
16+
entryPointName string
1917
ctx context.Context
2018
options *compile.Settings
2119
logHandler slog.Handler
@@ -24,53 +22,28 @@ type Compiler struct {
2422

2523
// NewCompiler creates a new Extism WASM Compiler instance with the provided options.
2624
func NewCompiler(opts ...FunctionalOption) (*Compiler, error) {
27-
// Initialize config with defaults
28-
cfg := &Options{}
29-
ApplyDefaults(cfg)
25+
// Initialize the compiler with an empty struct
26+
c := &Compiler{}
27+
28+
// Apply defaults
29+
c.applyDefaults()
3030

3131
// Apply all options
3232
for _, opt := range opts {
33-
if err := opt(cfg); err != nil {
33+
if err := opt(c); err != nil {
3434
return nil, fmt.Errorf("error applying compiler option: %w", err)
3535
}
3636
}
3737

3838
// Validate the configuration
39-
if err := Validate(cfg); err != nil {
39+
if err := c.validate(); err != nil {
4040
return nil, fmt.Errorf("invalid compiler configuration: %w", err)
4141
}
4242

43-
var handler slog.Handler
44-
var logger *slog.Logger
45-
46-
// Set up logging based on provided options
47-
if cfg.Logger != nil {
48-
// User provided a custom logger
49-
logger = cfg.Logger
50-
handler = logger.Handler()
51-
} else {
52-
// User provided a handler or we're using the default
53-
handler, logger = helpers.SetupLogger(cfg.LogHandler, "extism", "Compiler")
54-
}
55-
56-
// Set up entry point name in atomic.Value
57-
var entryPointAtomicValue atomic.Value
58-
entryPointAtomicValue.Store(cfg.EntryPoint)
43+
// Finalize logger setup after all options have been applied
44+
c.setupLogger()
5945

60-
// Create compile options from config
61-
compileOpts := &compile.Settings{
62-
EnableWASI: cfg.EnableWASI,
63-
RuntimeConfig: cfg.RuntimeConfig,
64-
HostFunctions: cfg.HostFunctions,
65-
}
66-
67-
return &Compiler{
68-
entryPointName: entryPointAtomicValue,
69-
ctx: context.Background(),
70-
options: compileOpts,
71-
logHandler: handler,
72-
logger: logger,
73-
}, nil
46+
return c, nil
7447
}
7548

7649
func (c *Compiler) String() string {
@@ -104,34 +77,30 @@ func (c *Compiler) Compile(scriptReader io.ReadCloser) (script.ExecutableContent
10477

10578
logger.Debug("Starting WASM compilation", "scriptLength", len(scriptBytes))
10679

107-
// Compile the WASM module using the CompileBytes function
80+
// Compile the WASM module using the CompileBytes function from the internal compile package
10881
plugin, err := compile.CompileBytes(c.ctx, scriptBytes, c.options)
10982
if err != nil {
110-
logger.Warn("WASM compilation failed", "error", err)
11183
return nil, fmt.Errorf("%w: %w", ErrValidationFailed, err)
11284
}
11385

11486
if plugin == nil {
115-
logger.Error("Compilation returned nil plugin")
11687
return nil, ErrBytecodeNil
11788
}
11889

11990
// Create a temporary instance to verify the entry point exists
12091
instance, err := plugin.Instance(c.ctx, extismSDK.PluginInstanceConfig{})
12192
if err != nil {
122-
logger.Error("Failed to create test instance", "error", err)
12393
return nil, fmt.Errorf("%w: failed to create test instance: %w", ErrValidationFailed, err)
12494
}
12595
defer func() {
12696
if err := instance.Close(c.ctx); err != nil {
127-
c.logger.Warn("Failed to close Extism plugin instance in compiler", "error", err)
97+
logger.Warn("Failed to close Extism plugin instance in compiler", "error", err)
12898
}
12999
}()
130100

131101
// Verify the entry point function exists
132102
funcName := c.GetEntryPointName()
133103
if !instance.FunctionExists(funcName) {
134-
logger.Error("Entry point function not found", "function", funcName)
135104
return nil, fmt.Errorf(
136105
"%w: entry point function '%s' not found",
137106
ErrValidationFailed,
@@ -142,20 +111,14 @@ func (c *Compiler) Compile(scriptReader io.ReadCloser) (script.ExecutableContent
142111
// Create executable with the compiled plugin
143112
executable := NewExecutable(scriptBytes, plugin, funcName)
144113
if executable == nil {
145-
logger.Warn("Failed to create Executable from WASM plugin")
146114
return nil, ErrExecCreationFailed
147115
}
148116

149-
logger.Debug("WASM compilation completed successfully")
117+
logger.Debug("WASM compilation completed")
150118
return executable, nil
151119
}
152120

153-
// SetEntryPointName is a way to point the compiler at a different entrypoint in the wasm binary
154-
func (c *Compiler) SetEntryPointName(fName string) {
155-
c.entryPointName.Store(fName)
156-
}
157-
158121
// GetEntryPointName is a getter for the func name entrypoint
159122
func (c *Compiler) GetEntryPointName() string {
160-
return c.entryPointName.Load().(string)
123+
return c.entryPointName
161124
}

machines/extism/compiler/options.go

Lines changed: 86 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,27 @@
11
package compiler
22

33
import (
4+
"context"
45
"fmt"
56
"log/slog"
67
"os"
78

89
extismSDK "github.com/extism/go-sdk"
10+
"github.com/robbyt/go-polyscript/internal/helpers"
11+
"github.com/robbyt/go-polyscript/machines/extism/compiler/internal/compile"
912
"github.com/tetratelabs/wazero"
1013
)
1114

12-
// Options holds the configuration for the Extism compiler
13-
type Options struct {
14-
EntryPoint string
15-
LogHandler slog.Handler
16-
Logger *slog.Logger
17-
EnableWASI bool
18-
RuntimeConfig wazero.RuntimeConfig
19-
HostFunctions []extismSDK.HostFunction
20-
}
21-
22-
// FunctionalOption is a function that configures a Options instance
23-
type FunctionalOption func(*Options) error
15+
// FunctionalOption is a function that configures a Compiler instance
16+
type FunctionalOption func(*Compiler) error
2417

2518
// WithEntryPoint creates an option to set the entry point for Extism WASM modules
2619
func WithEntryPoint(entryPoint string) FunctionalOption {
27-
return func(cfg *Options) error {
20+
return func(c *Compiler) error {
2821
if entryPoint == "" {
2922
return fmt.Errorf("entry point cannot be empty")
3023
}
31-
cfg.EntryPoint = entryPoint
24+
c.entryPointName = entryPoint
3225
return nil
3326
}
3427
}
@@ -37,13 +30,13 @@ func WithEntryPoint(entryPoint string) FunctionalOption {
3730
// This is the preferred option for logging configuration as it provides
3831
// more flexibility through the slog.Handler interface.
3932
func WithLogHandler(handler slog.Handler) FunctionalOption {
40-
return func(cfg *Options) error {
33+
return func(c *Compiler) error {
4134
if handler == nil {
4235
return fmt.Errorf("log handler cannot be nil")
4336
}
44-
cfg.LogHandler = handler
37+
c.logHandler = handler
4538
// Clear logger if handler is explicitly set
46-
cfg.Logger = nil
39+
c.logger = nil
4740
return nil
4841
}
4942
}
@@ -52,86 +45,133 @@ func WithLogHandler(handler slog.Handler) FunctionalOption {
5245
// This is less flexible than WithLogHandler but allows users to customize
5346
// their logging group configuration.
5447
func WithLogger(logger *slog.Logger) FunctionalOption {
55-
return func(cfg *Options) error {
48+
return func(c *Compiler) error {
5649
if logger == nil {
5750
return fmt.Errorf("logger cannot be nil")
5851
}
59-
cfg.Logger = logger
52+
c.logger = logger
6053
// Clear handler if logger is explicitly set
61-
cfg.LogHandler = nil
54+
c.logHandler = nil
6255
return nil
6356
}
6457
}
6558

6659
// WithWASIEnabled creates an option to enable or disable WASI support
6760
func WithWASIEnabled(enabled bool) FunctionalOption {
68-
return func(cfg *Options) error {
69-
cfg.EnableWASI = enabled
61+
return func(c *Compiler) error {
62+
if c.options == nil {
63+
c.options = &compile.Settings{}
64+
}
65+
c.options.EnableWASI = enabled
7066
return nil
7167
}
7268
}
7369

7470
// WithRuntimeConfig creates an option to set a custom wazero runtime configuration
7571
func WithRuntimeConfig(config wazero.RuntimeConfig) FunctionalOption {
76-
return func(cfg *Options) error {
72+
return func(c *Compiler) error {
7773
if config == nil {
7874
return fmt.Errorf("runtime config cannot be nil")
7975
}
80-
cfg.RuntimeConfig = config
76+
if c.options == nil {
77+
c.options = &compile.Settings{}
78+
}
79+
c.options.RuntimeConfig = config
8180
return nil
8281
}
8382
}
8483

8584
// WithHostFunctions creates an option to set additional host functions
8685
func WithHostFunctions(funcs []extismSDK.HostFunction) FunctionalOption {
87-
return func(cfg *Options) error {
88-
cfg.HostFunctions = funcs
86+
return func(c *Compiler) error {
87+
if c.options == nil {
88+
c.options = &compile.Settings{}
89+
}
90+
c.options.HostFunctions = funcs
91+
return nil
92+
}
93+
}
94+
95+
// WithContext creates an option to set a custom context for the Extism compiler.
96+
func WithContext(ctx context.Context) FunctionalOption {
97+
return func(c *Compiler) error {
98+
if ctx == nil {
99+
return fmt.Errorf("context cannot be nil")
100+
}
101+
c.ctx = ctx
89102
return nil
90103
}
91104
}
92105

93-
// ApplyDefaults sets the default values for a compilerConfig
94-
func ApplyDefaults(cfg *Options) {
106+
// applyDefaults sets the default values for a compiler
107+
func (c *Compiler) applyDefaults() {
95108
// Default to stderr for logging if neither handler nor logger specified
96-
if cfg.LogHandler == nil && cfg.Logger == nil {
97-
cfg.LogHandler = slog.NewTextHandler(os.Stderr, nil)
109+
if c.logHandler == nil && c.logger == nil {
110+
c.logHandler = slog.NewTextHandler(os.Stderr, nil)
98111
}
99112

100-
// Default entry point
101-
if cfg.EntryPoint == "" {
102-
cfg.EntryPoint = defaultEntryPoint
113+
// Set default entry point
114+
if c.entryPointName == "" {
115+
c.entryPointName = defaultEntryPoint
103116
}
104117

105-
// Default WASI setting
106-
cfg.EnableWASI = true
118+
// Initialize options struct if nil
119+
if c.options == nil {
120+
c.options = &compile.Settings{}
121+
}
107122

108-
// Default runtime config
109-
if cfg.RuntimeConfig == nil {
110-
cfg.RuntimeConfig = wazero.NewRuntimeConfig()
123+
// Set default runtime config if not already set
124+
if c.options.RuntimeConfig == nil {
125+
c.options.RuntimeConfig = wazero.NewRuntimeConfig()
111126
}
112127

113-
// Default to empty host functions
114-
if cfg.HostFunctions == nil {
115-
cfg.HostFunctions = []extismSDK.HostFunction{}
128+
// Set default host functions if not already set
129+
if c.options.HostFunctions == nil {
130+
c.options.HostFunctions = []extismSDK.HostFunction{}
131+
}
132+
133+
// Default WASI to true (EnableWASI is a bool so we don't need to check if it's nil)
134+
c.options.EnableWASI = true
135+
136+
// Default context
137+
if c.ctx == nil {
138+
c.ctx = context.Background()
116139
}
117140
}
118141

119-
// Validate checks if the configuration is valid
120-
func Validate(cfg *Options) error {
142+
// setupLogger configures the logger and handler based on the current state.
143+
// This is idempotent and can be called multiple times during initialization.
144+
func (c *Compiler) setupLogger() {
145+
if c.logger != nil {
146+
// When a logger is explicitly set, extract its handler
147+
c.logHandler = c.logger.Handler()
148+
} else {
149+
// Otherwise use the handler (which might be default or custom) to create the logger
150+
c.logHandler, c.logger = helpers.SetupLogger(c.logHandler, "extism", "Compiler")
151+
}
152+
}
153+
154+
// validate checks if the compiler configuration is valid
155+
func (c *Compiler) validate() error {
121156
// Ensure we have either a logger or a handler
122-
if cfg.LogHandler == nil && cfg.Logger == nil {
157+
if c.logHandler == nil && c.logger == nil {
123158
return fmt.Errorf("either log handler or logger must be specified")
124159
}
125160

126161
// Entry point must be non-empty
127-
if cfg.EntryPoint == "" {
162+
if c.entryPointName == "" {
128163
return fmt.Errorf("entry point must be specified")
129164
}
130165

131166
// Runtime config cannot be nil
132-
if cfg.RuntimeConfig == nil {
167+
if c.options == nil || c.options.RuntimeConfig == nil {
133168
return fmt.Errorf("runtime config cannot be nil")
134169
}
135170

171+
// Context cannot be nil
172+
if c.ctx == nil {
173+
return fmt.Errorf("context cannot be nil")
174+
}
175+
136176
return nil
137177
}

0 commit comments

Comments
 (0)