package xpc import ( "context" "fmt" "os" "os/signal" "reflect" "syscall" "time" ) // Implements is a struct that component implementations must embed. // T is the component interface. type Implements[T any] struct{} // Register is used by generated code to register components. func Register[Intf any, Impl any](newFunc func() any) { registerComponent((*Intf)(nil), (*Impl)(nil), newFunc) } // Options configuration for the XPC runtime type Options struct { // TLSCertFile path to the TLS certificate file (for cluster mode) TLSCertFile string // TLSKeyFile path to the TLS key file (for cluster mode) TLSKeyFile string } // Run starts the XPC application. // app must be a function with the signature: func(context.Context, Component...) error // It handles OS signals (SIGINT, SIGTERM) to cancel the context passed to the app. func Run(ctx context.Context, app any, opts ...Options) error { // Parse options var opt Options if len(opts) > 0 { opt = opts[0] } rtConfig := runtimeConfig{ TLSCertFile: opt.TLSCertFile, TLSKeyFile: opt.TLSKeyFile, } appVal := reflect.ValueOf(app) appType := appVal.Type() if appType.Kind() != reflect.Func { return fmt.Errorf("app must be a function, got %v", appType) } if appType.NumIn() < 1 || appType.In(0) != reflect.TypeOf((*context.Context)(nil)).Elem() { return fmt.Errorf("first argument of app must be context.Context") } if appType.NumOut() != 1 || appType.Out(0) != reflect.TypeOf((*error)(nil)).Elem() { return fmt.Errorf("app must return a single error") } // Setup signal handling ctx, cancel := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM) defer cancel() // Initialize Runtime based on configuration var rt runtime if os.Getenv("XPC_MODE") == "cluster" { rt = newClusterRuntime(rtConfig) } else { rt = newLocalRuntime(rtConfig) } // Instantiate requested root components args := []reflect.Value{reflect.ValueOf(ctx)} for i := 1; i < appType.NumIn(); i++ { argType := appType.In(i) comp, err := rt.getInstance(ctx, argType) if err != nil { return fmt.Errorf("failed to get instance for argument %d (%v): %w", i, argType, err) } args = append(args, reflect.ValueOf(comp)) } // Execute Application results := appVal.Call(args) // Graceful Shutdown shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 10*time.Second) defer shutdownCancel() if err := rt.shutdown(shutdownCtx); err != nil { fmt.Fprintf(os.Stderr, "xpc: shutdown error: %v\n", err) } errVal := results[0] if !errVal.IsNil() { return errVal.Interface().(error) } return nil }