diff --git a/actions.go b/actions.go index f0fe28c..e40636e 100644 --- a/actions.go +++ b/actions.go @@ -2,8 +2,12 @@ package core import "github.com/wailsapp/wails/v3/pkg/application" +// ActionServiceStartup is a message sent when the application's services are starting up. +// This provides a hook for services to perform initialization tasks. type ActionServiceStartup struct{} +// ActionServiceShutdown is a message sent when the application is shutting down. +// This allows services to perform cleanup tasks, such as saving state or closing resources. type ActionServiceShutdown struct{} // ActionDisplayOpenWindow is a structured message for requesting a new window. diff --git a/core.go b/core.go index 685147e..dd9bcb4 100644 --- a/core.go +++ b/core.go @@ -12,6 +12,14 @@ import ( ) // New initialises a Core instance using the provided options and performs the necessary setup. +// It is the primary entry point for creating a new Core application. +// +// Example: +// +// core, err := core.New( +// core.WithService(&MyService{}), +// core.WithAssets(assets), +// ) func New(opts ...Option) (*Core, error) { c := &Core{ services: make(map[string]any), @@ -37,6 +45,20 @@ func New(opts ...Option) (*Core, error) { // WithService creates an Option that registers a service. It automatically discovers // the service name from its package path and registers its IPC handler if it // implements a method named `HandleIPCEvents`. +// +// Example: +// +// // In myapp/services/calculator.go +// package services +// +// type Calculator struct{} +// +// func (s *Calculator) Add(a, b int) int { return a + b } +// +// // In main.go +// import "myapp/services" +// +// core.New(core.WithService(services.NewCalculator)) func WithService(factory func(*Core) (any, error)) Option { return func(c *Core) error { serviceInstance, err := factory(c) @@ -83,6 +105,8 @@ func WithName(name string, factory func(*Core) (any, error)) Option { } } +// WithWails creates an Option that injects the Wails application instance into the Core. +// This is essential for services that need to interact with the Wails runtime. func WithWails(app *application.App) Option { return func(c *Core) error { c.App = app @@ -90,6 +114,8 @@ func WithWails(app *application.App) Option { } } +// WithAssets creates an Option that registers the application's embedded assets. +// This is necessary for the application to be able to serve its frontend. func WithAssets(fs embed.FS) Option { return func(c *Core) error { c.assets = fs @@ -97,6 +123,9 @@ func WithAssets(fs embed.FS) Option { } } +// WithServiceLock creates an Option that prevents any further services from being +// registered after the Core has been initialized. This is a security measure to +// prevent late-binding of services that could have unintended consequences. func WithServiceLock() Option { return func(c *Core) error { c.serviceLock = true @@ -106,14 +135,20 @@ func WithServiceLock() Option { // --- Core Methods --- +// ServiceStartup is the entry point for the Core service's startup lifecycle. +// It is called by Wails when the application starts. func (c *Core) ServiceStartup(ctx context.Context, options application.ServiceOptions) error { return c.ACTION(ActionServiceStartup{}) } +// ServiceShutdown is the entry point for the Core service's shutdown lifecycle. +// It is called by Wails when the application shuts down. func (c *Core) ServiceShutdown(ctx context.Context) error { return c.ACTION(ActionServiceShutdown{}) } +// ACTION dispatches a message to all registered IPC handlers. +// This is the primary mechanism for services to communicate with each other. func (c *Core) ACTION(msg Message) error { c.ipcMu.RLock() handlers := append([]func(*Core, Message) error(nil), c.ipcHandlers...) @@ -128,18 +163,21 @@ func (c *Core) ACTION(msg Message) error { return agg } +// RegisterAction adds a new IPC handler to the Core. func (c *Core) RegisterAction(handler func(*Core, Message) error) { c.ipcMu.Lock() c.ipcHandlers = append(c.ipcHandlers, handler) c.ipcMu.Unlock() } +// RegisterActions adds multiple IPC handlers to the Core. func (c *Core) RegisterActions(handlers ...func(*Core, Message) error) { c.ipcMu.Lock() c.ipcHandlers = append(c.ipcHandlers, handlers...) c.ipcMu.Unlock() } +// RegisterService adds a new service to the Core. func (c *Core) RegisterService(name string, api any) error { if c.servicesLocked { return fmt.Errorf("core: service %q is not permitted by the serviceLock setting", name) @@ -156,6 +194,8 @@ func (c *Core) RegisterService(name string, api any) error { return nil } +// Service retrieves a registered service by name. +// It returns nil if the service is not found. func (c *Core) Service(name string) any { c.serviceMu.RLock() api, ok := c.services[name] @@ -191,6 +231,7 @@ func MustServiceFor[T any](c *Core, name string) T { } // App returns the global application instance. +// It panics if the Core has not been initialized. func App() *application.App { if instance == nil { panic("core.App() called before core.Setup() was successfully initialized") @@ -210,8 +251,10 @@ func (c *Core) Display() Display { return display } +// Core returns the Core instance itself. func (c *Core) Core() *Core { return c } +// Assets returns the embedded filesystem containing the application's assets. func (c *Core) Assets() embed.FS { return c.assets } diff --git a/interfaces.go b/interfaces.go index 2aca268..8c13ad1 100644 --- a/interfaces.go +++ b/interfaces.go @@ -12,13 +12,19 @@ import ( // in the Core framework. Services depend on these interfaces, not on // concrete implementations. +// Contract specifies the operational guarantees that the Core and its services must adhere to. +// This is used for configuring panic handling and other resilience features. type Contract struct { - DontPanic bool + // DontPanic, if true, instructs the Core to recover from panics and return an error instead. + DontPanic bool + // DisableLogging, if true, disables all logging from the Core and its services. DisableLogging bool } // Features provides a way to check if a feature is enabled. +// This is used for feature flagging and conditional logic. type Features struct { + // Flags is a list of enabled feature flags. Flags []string } @@ -31,8 +37,16 @@ func (f *Features) IsEnabled(feature string) bool { } return false } + +// Option is a function that configures the Core. +// This is used to apply settings and register services during initialization. type Option func(*Core) error + +// Message is the interface for all messages that can be sent through the Core's IPC system. +// Any struct can be a message, allowing for structured data to be passed between services. type Message interface{} + +// Core is the central application object that manages services, assets, and communication. type Core struct { once sync.Once initErr error @@ -51,7 +65,9 @@ var instance *Core // Config provides access to application configuration. type Config interface { + // Get retrieves a configuration value by key and stores it in the 'out' variable. Get(key string, out any) error + // Set stores a configuration value by key. Set(key string, v any) error } @@ -66,23 +82,29 @@ type WindowConfig struct { // WindowOption configures window creation. type WindowOption interface { + // Apply applies the window option to the given configuration. Apply(*WindowConfig) } // Display manages windows and UI. type Display interface { + // OpenWindow creates and displays a new window with the given options. OpenWindow(opts ...WindowOption) error } // Help manages the in-app documentation and help system. type Help interface { + // Show displays the main help topic. Show() error + // ShowAt displays the help topic for the given anchor. ShowAt(anchor string) error } // Crypt provides cryptographic functions. type Crypt interface { + // EncryptPGP encrypts data using PGP and writes the result to the given writer. EncryptPGP(writer io.Writer, recipientPath, data string, signerPath, signerPassphrase *string) (string, error) + // DecryptPGP decrypts a PGP message. DecryptPGP(recipientPath, message, passphrase string, signerPath *string) (string, error) } @@ -96,8 +118,12 @@ type I18n interface { // Workspace manages user workspaces. type Workspace interface { + // CreateWorkspace creates a new workspace with the given identifier and password. CreateWorkspace(identifier, password string) (string, error) + // SwitchWorkspace changes the active workspace. SwitchWorkspace(name string) error + // WorkspaceFileGet retrieves the content of a file from the current workspace. WorkspaceFileGet(filename string) (string, error) + // WorkspaceFileSet writes content to a file in the current workspace. WorkspaceFileSet(filename, content string) error } diff --git a/runtime.go b/runtime.go index a444f6f..93a5ac4 100644 --- a/runtime.go +++ b/runtime.go @@ -1,12 +1,14 @@ package core // ServiceRuntime is a helper struct embedded in services to provide access to the core application. +// It is generic and can be parameterized with a service-specific options struct. type ServiceRuntime[T any] struct { core *Core opts T } // NewServiceRuntime creates a new ServiceRuntime instance for a service. +// This is typically called by a service's constructor. func NewServiceRuntime[T any](c *Core, opts T) *ServiceRuntime[T] { return &ServiceRuntime[T]{ core: c, @@ -14,12 +16,13 @@ func NewServiceRuntime[T any](c *Core, opts T) *ServiceRuntime[T] { } } -// Core returns the central core instance. +// Core returns the central core instance, providing access to all registered services. func (r *ServiceRuntime[T]) Core() *Core { return r.core } // Config returns the registered Config service from the core application. +// This is a convenience method for accessing the application's configuration. func (r *ServiceRuntime[T]) Config() Config { return r.core.Config() } diff --git a/runtime_pkg.go b/runtime_pkg.go index eb2529f..03da0a5 100644 --- a/runtime_pkg.go +++ b/runtime_pkg.go @@ -10,15 +10,19 @@ import ( // Runtime is the container that holds all instantiated services. // Its fields are the concrete types, allowing Wails to bind them directly. +// This struct is the primary entry point for the Wails application. type Runtime struct { app *application.App Core *Core } // ServiceFactory defines a function that creates a service instance. +// This is used to decouple the service creation from the runtime initialization. type ServiceFactory func() (any, error) // NewWithFactories creates a new Runtime instance using the provided service factories. +// This is the most flexible way to create a new Runtime, as it allows for +// the registration of any number of services. func NewWithFactories(app *application.App, factories map[string]ServiceFactory) (*Runtime, error) { services := make(map[string]any) coreOpts := []Option{ @@ -58,6 +62,8 @@ func NewWithFactories(app *application.App, factories map[string]ServiceFactory) } // NewRuntime creates and wires together all application services. +// This is the simplest way to create a new Runtime, but it does not allow for +// the registration of any custom services. func NewRuntime(app *application.App) (*Runtime, error) { return NewWithFactories(app, map[string]ServiceFactory{}) } @@ -68,11 +74,13 @@ func (r *Runtime) ServiceName() string { } // ServiceStartup is called by Wails at application startup. +// This is where the Core's startup lifecycle is initiated. func (r *Runtime) ServiceStartup(ctx context.Context, options application.ServiceOptions) { r.Core.ServiceStartup(ctx, options) } // ServiceShutdown is called by Wails at application shutdown. +// This is where the Core's shutdown lifecycle is initiated. func (r *Runtime) ServiceShutdown(ctx context.Context) { if r.Core != nil { r.Core.ServiceShutdown(ctx)