From 8471325c81edf4e1016698c4848599fb5150b7bc Mon Sep 17 00:00:00 2001 From: Jack Kleeman Date: Tue, 13 Aug 2024 10:30:30 +0100 Subject: [PATCH] Unify restate.Service and restate.Object into restate.Reflect These names pollute the namespace a bit and they have similar signatures anyway. Reflect is more descriptive of what they do. --- .../codegen/proto/helloworld_restate.pb.go | 8 +- examples/ticketreservation/main.go | 6 +- handler.go | 34 ++- internal/options/options.go | 28 +-- options.go | 19 +- protoc-gen-go-restate/restate.go | 17 +- reflect.go | 198 +++++++----------- router.go | 95 ++++----- 8 files changed, 152 insertions(+), 253 deletions(-) diff --git a/examples/codegen/proto/helloworld_restate.pb.go b/examples/codegen/proto/helloworld_restate.pb.go index dd83bb5..ede8d9e 100644 --- a/examples/codegen/proto/helloworld_restate.pb.go +++ b/examples/codegen/proto/helloworld_restate.pb.go @@ -62,7 +62,7 @@ type UnsafeGreeterServer interface { mustEmbedUnimplementedGreeterServer() } -func NewGreeterServer(srv GreeterServer, opts ...sdk_go.ServiceOption) sdk_go.ServiceDefinition { +func NewGreeterServer(srv GreeterServer, opts ...sdk_go.ServiceDefinitionOption) sdk_go.ServiceDefinition { // If the following call panics, it indicates UnimplementedGreeterServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization @@ -70,7 +70,7 @@ func NewGreeterServer(srv GreeterServer, opts ...sdk_go.ServiceOption) sdk_go.Se if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } - sOpts := append([]sdk_go.ServiceOption{sdk_go.WithProtoJSON}, opts...) + sOpts := append([]sdk_go.ServiceDefinitionOption{sdk_go.WithProtoJSON}, opts...) router := sdk_go.NewService("Greeter", sOpts...) router = router.Handler("SayHello", sdk_go.NewServiceHandler(srv.SayHello)) return router @@ -176,7 +176,7 @@ type UnsafeCounterServer interface { mustEmbedUnimplementedCounterServer() } -func NewCounterServer(srv CounterServer, opts ...sdk_go.ObjectOption) sdk_go.ServiceDefinition { +func NewCounterServer(srv CounterServer, opts ...sdk_go.ServiceDefinitionOption) sdk_go.ServiceDefinition { // If the following call panics, it indicates UnimplementedCounterServer was // embedded by pointer and is nil. This will cause panics if an // unimplemented method is ever invoked, so we test this at initialization @@ -184,7 +184,7 @@ func NewCounterServer(srv CounterServer, opts ...sdk_go.ObjectOption) sdk_go.Ser if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { t.testEmbeddedByValue() } - sOpts := append([]sdk_go.ObjectOption{sdk_go.WithProtoJSON}, opts...) + sOpts := append([]sdk_go.ServiceDefinitionOption{sdk_go.WithProtoJSON}, opts...) router := sdk_go.NewObject("Counter", sOpts...) router = router.Handler("Add", sdk_go.NewObjectHandler(srv.Add)) router = router.Handler("Get", sdk_go.NewObjectSharedHandler(srv.Get)) diff --git a/examples/ticketreservation/main.go b/examples/ticketreservation/main.go index ac6afce..6bfe0cd 100644 --- a/examples/ticketreservation/main.go +++ b/examples/ticketreservation/main.go @@ -12,9 +12,9 @@ import ( func main() { server := server.NewRestate(). // Handlers can be inferred from object methods - Bind(restate.Object(&userSession{})). - Bind(restate.Object(&ticketService{})). - Bind(restate.Service(&checkout{})) + Bind(restate.Reflect(&userSession{})). + Bind(restate.Reflect(&ticketService{})). + Bind(restate.Reflect(&checkout{})) if err := server.Start(context.Background(), ":9080"); err != nil { slog.Error("application exited unexpectedly", "err", err.Error()) diff --git a/handler.go b/handler.go index a0ca0ba..26f25d3 100644 --- a/handler.go +++ b/handler.go @@ -21,20 +21,18 @@ type Void = encoding.Void // ObjectHandler is the required set of methods for a Virtual Object handler. type ObjectHandler interface { Call(ctx ObjectContext, request []byte) (output []byte, err error) - getOptions() *options.ObjectHandlerOptions Handler } // ServiceHandler is the required set of methods for a Service handler. type ServiceHandler interface { Call(ctx Context, request []byte) (output []byte, err error) - getOptions() *options.ServiceHandlerOptions Handler } // Handler is implemented by all Restate handlers type Handler interface { - sealed() + getOptions() *options.HandlerOptions InputPayload() *encoding.InputPayload OutputPayload() *encoding.OutputPayload HandlerType() *internal.ServiceHandlerType @@ -51,16 +49,16 @@ type ObjectSharedHandlerFn[I any, O any] func(ctx ObjectSharedContext, input I) type serviceHandler[I any, O any] struct { fn ServiceHandlerFn[I, O] - options options.ServiceHandlerOptions + options options.HandlerOptions } var _ ServiceHandler = (*serviceHandler[struct{}, struct{}])(nil) // NewServiceHandler converts a function of signature [ServiceHandlerFn] into a handler on a Restate service. -func NewServiceHandler[I any, O any](fn ServiceHandlerFn[I, O], opts ...options.ServiceHandlerOption) *serviceHandler[I, O] { - o := options.ServiceHandlerOptions{} +func NewServiceHandler[I any, O any](fn ServiceHandlerFn[I, O], opts ...options.HandlerOption) *serviceHandler[I, O] { + o := options.HandlerOptions{} for _, opt := range opts { - opt.BeforeServiceHandler(&o) + opt.BeforeHandler(&o) } return &serviceHandler[I, O]{ fn: fn, @@ -104,17 +102,15 @@ func (h *serviceHandler[I, O]) HandlerType() *internal.ServiceHandlerType { return nil } -func (h *serviceHandler[I, O]) getOptions() *options.ServiceHandlerOptions { +func (h *serviceHandler[I, O]) getOptions() *options.HandlerOptions { return &h.options } -func (h *serviceHandler[I, O]) sealed() {} - type objectHandler[I any, O any] struct { // only one of exclusiveFn or sharedFn should be set, as indicated by handlerType exclusiveFn ObjectHandlerFn[I, O] sharedFn ObjectSharedHandlerFn[I, O] - options options.ObjectHandlerOptions + options options.HandlerOptions handlerType internal.ServiceHandlerType } @@ -122,10 +118,10 @@ var _ ObjectHandler = (*objectHandler[struct{}, struct{}])(nil) // NewObjectHandler converts a function of signature [ObjectHandlerFn] into an exclusive-mode handler on a Virtual Object. // The handler will have access to a full [ObjectContext] which may mutate state. -func NewObjectHandler[I any, O any](fn ObjectHandlerFn[I, O], opts ...options.ObjectHandlerOption) *objectHandler[I, O] { - o := options.ObjectHandlerOptions{} +func NewObjectHandler[I any, O any](fn ObjectHandlerFn[I, O], opts ...options.HandlerOption) *objectHandler[I, O] { + o := options.HandlerOptions{} for _, opt := range opts { - opt.BeforeObjectHandler(&o) + opt.BeforeHandler(&o) } return &objectHandler[I, O]{ exclusiveFn: fn, @@ -136,10 +132,10 @@ func NewObjectHandler[I any, O any](fn ObjectHandlerFn[I, O], opts ...options.Ob // NewObjectSharedHandler converts a function of signature [ObjectSharedHandlerFn] into a shared-mode handler on a Virtual Object. // The handler will only have access to a [ObjectSharedContext] which can only read a snapshot of state. -func NewObjectSharedHandler[I any, O any](fn ObjectSharedHandlerFn[I, O], opts ...options.ObjectHandlerOption) *objectHandler[I, O] { - o := options.ObjectHandlerOptions{} +func NewObjectSharedHandler[I any, O any](fn ObjectSharedHandlerFn[I, O], opts ...options.HandlerOption) *objectHandler[I, O] { + o := options.HandlerOptions{} for _, opt := range opts { - opt.BeforeObjectHandler(&o) + opt.BeforeHandler(&o) } return &objectHandler[I, O]{ sharedFn: fn, @@ -190,12 +186,10 @@ func (h *objectHandler[I, O]) OutputPayload() *encoding.OutputPayload { return encoding.OutputPayloadFor(h.options.Codec, o) } -func (h *objectHandler[I, O]) getOptions() *options.ObjectHandlerOptions { +func (h *objectHandler[I, O]) getOptions() *options.HandlerOptions { return &h.options } func (h *objectHandler[I, O]) HandlerType() *internal.ServiceHandlerType { return &h.handlerType } - -func (h *objectHandler[I, O]) sealed() {} diff --git a/internal/options/options.go b/internal/options/options.go index f278ae3..3ff89c1 100644 --- a/internal/options/options.go +++ b/internal/options/options.go @@ -51,34 +51,18 @@ type RunOption interface { BeforeRun(*RunOptions) } -type ServiceHandlerOptions struct { +type HandlerOptions struct { Codec encoding.PayloadCodec } -type ServiceHandlerOption interface { - BeforeServiceHandler(*ServiceHandlerOptions) +type HandlerOption interface { + BeforeHandler(*HandlerOptions) } -type ObjectHandlerOptions struct { - Codec encoding.PayloadCodec -} - -type ObjectHandlerOption interface { - BeforeObjectHandler(*ObjectHandlerOptions) -} - -type ServiceOptions struct { - DefaultCodec encoding.PayloadCodec -} - -type ServiceOption interface { - BeforeService(*ServiceOptions) -} - -type ObjectOptions struct { +type ServiceDefinitionOptions struct { DefaultCodec encoding.PayloadCodec } -type ObjectOption interface { - BeforeObject(*ObjectOptions) +type ServiceDefinitionOption interface { + BeforeServiceDefinition(*ServiceDefinitionOptions) } diff --git a/options.go b/options.go index 809844c..23d001a 100644 --- a/options.go +++ b/options.go @@ -7,8 +7,7 @@ import ( // re-export for use in generated code type CallOption = options.CallOption -type ServiceOption = options.ServiceOption -type ObjectOption = options.ObjectOption +type ServiceDefinitionOption = options.ServiceDefinitionOption type withCodec struct { codec encoding.Codec @@ -43,21 +42,13 @@ type withPayloadCodec struct { codec encoding.PayloadCodec } -var _ options.ServiceHandlerOption = withPayloadCodec{} -var _ options.ServiceOption = withPayloadCodec{} -var _ options.ObjectHandlerOption = withPayloadCodec{} -var _ options.ObjectOption = withPayloadCodec{} +var _ options.HandlerOption = withPayloadCodec{} +var _ options.ServiceDefinitionOption = withPayloadCodec{} -func (w withPayloadCodec) BeforeServiceHandler(opts *options.ServiceHandlerOptions) { +func (w withPayloadCodec) BeforeHandler(opts *options.HandlerOptions) { opts.Codec = w.codec } -func (w withPayloadCodec) BeforeObjectHandler(opts *options.ObjectHandlerOptions) { - opts.Codec = w.codec -} -func (w withPayloadCodec) BeforeService(opts *options.ServiceOptions) { - opts.DefaultCodec = w.codec -} -func (w withPayloadCodec) BeforeObject(opts *options.ObjectOptions) { +func (w withPayloadCodec) BeforeServiceDefinition(opts *options.ServiceDefinitionOptions) { opts.DefaultCodec = w.codec } diff --git a/protoc-gen-go-restate/restate.go b/protoc-gen-go-restate/restate.go index 56f8597..ee3c2c7 100644 --- a/protoc-gen-go-restate/restate.go +++ b/protoc-gen-go-restate/restate.go @@ -241,7 +241,7 @@ func genService(gen *protogen.Plugin, g *protogen.GeneratedFile, service *protog g.P(deprecationComment) } - g.P("func New", service.GoName, "Server(srv ", serverType, ", opts ...", routerOptionType(gen, g, service), ") ", sdkPackage.Ident("ServiceDefinition"), " {") + g.P("func New", service.GoName, "Server(srv ", serverType, ", opts ...", sdkPackage.Ident("ServiceDefinitionOption"), ") ", sdkPackage.Ident("ServiceDefinition"), " {") g.P("// If the following call panics, it indicates Unimplemented", serverType, " was") g.P("// embedded by pointer and is nil. This will cause panics if an") g.P("// unimplemented method is ever invoked, so we test this at initialization") @@ -249,7 +249,7 @@ func genService(gen *protogen.Plugin, g *protogen.GeneratedFile, service *protog g.P("if t, ok := srv.(interface { testEmbeddedByValue() }); ok {") g.P("t.testEmbeddedByValue()") g.P("}") - g.P("sOpts := append([]", routerOptionType(gen, g, service), "{", sdkPackage.Ident("WithProtoJSON"), "}, opts...)") + g.P("sOpts := append([]", sdkPackage.Ident("ServiceDefinitionOption"), "{", sdkPackage.Ident("WithProtoJSON"), "}, opts...)") g.P("router := ", newRouterType(gen, g, service), `("`, service.GoName, `", sOpts...)`) for _, method := range service.Methods { g.P(`router = router.Handler("`, method.GoName, `",`, newHandlerType(gen, g, method), "(srv.", method.GoName, "))") @@ -347,19 +347,6 @@ func newRouterType(gen *protogen.Plugin, g *protogen.GeneratedFile, service *pro } } -func routerOptionType(gen *protogen.Plugin, g *protogen.GeneratedFile, service *protogen.Service) string { - serviceType := proto.GetExtension(service.Desc.Options().(*descriptorpb.ServiceOptions), sdk.E_ServiceType).(sdk.ServiceType) - switch serviceType { - case sdk.ServiceType_SERVICE: - return g.QualifiedGoIdent(sdkPackage.Ident("ServiceOption")) - case sdk.ServiceType_VIRTUAL_OBJECT: - return g.QualifiedGoIdent(sdkPackage.Ident("ObjectOption")) - default: - gen.Error(fmt.Errorf("Unexpected service type: %s", serviceType.String())) - return "" - } -} - func newHandlerType(gen *protogen.Plugin, g *protogen.GeneratedFile, method *protogen.Method) string { serviceType := proto.GetExtension(method.Parent.Desc.Options().(*descriptorpb.ServiceOptions), sdk.E_ServiceType).(sdk.ServiceType) handlerType := proto.GetExtension(method.Desc.Options().(*descriptorpb.MethodOptions), sdk.E_HandlerType).(sdk.HandlerType) diff --git a/reflect.go b/reflect.go index 872e56e..21c2b4f 100644 --- a/reflect.go +++ b/reflect.go @@ -22,25 +22,28 @@ var ( typeOfError = reflect.TypeOf((*error)(nil)).Elem() ) -// Object converts a struct with methods into a Virtual Object where each correctly-typed -// and exported method of the struct will become a handler on the Object. The Object name +// Reflect converts a struct with methods into a service definition where each correctly-typed +// and exported method of the struct will become a handler in the definition. The service name // defaults to the name of the struct, but this can be overidden by providing a `ServiceName() string` method. -// The handler name is the name of the method. Handler methods should be of the type `ObjectHandlerFn[I, O]` or `ObjectSharedHandlerFn[I, O]`. +// The handler name is the name of the method. Handler methods should be of the type `ServiceHandlerFn[I,O]`, +// `ObjectHandlerFn[I, O]` or `ObjectSharedHandlerFn[I, O]`. This function will panic if a mixture of +// object and service method signatures or opts are provided. // // Input types will be deserialised with the provided codec (defaults to JSON) except when they are restate.Void, // in which case no input bytes or content type may be sent. // Output types will be serialised with the provided codec (defaults to JSON) except when they are restate.Void, // in which case no data will be sent and no content type set. -func Object(object any, opts ...options.ObjectOption) *object { - typ := reflect.TypeOf(object) - val := reflect.ValueOf(object) +func Reflect(rcvr any, opts ...options.ServiceDefinitionOption) ServiceDefinition { + typ := reflect.TypeOf(rcvr) + val := reflect.ValueOf(rcvr) var name string - if sn, ok := object.(serviceNamer); ok { + if sn, ok := rcvr.(serviceNamer); ok { name = sn.ServiceName() } else { name = reflect.Indirect(val).Type().Name() } - definition := NewObject(name, opts...) + + var definition ServiceDefinition for m := 0; m < typ.NumMethod(); m++ { method := typ.Method(m) @@ -50,7 +53,7 @@ func Object(object any, opts ...options.ObjectOption) *object { if !method.IsExported() { continue } - // Method needs three ins: receiver, ObjectContext, I + // Method needs three ins: receiver, Context, I if mtype.NumIn() != 3 { continue } @@ -58,12 +61,28 @@ func Object(object any, opts ...options.ObjectOption) *object { var handlerType internal.ServiceHandlerType switch mtype.In(1) { + case typeOfContext: + if definition == nil { + definition = NewService(name, opts...) + } else if definition.Type() != internal.ServiceType_SERVICE { + panic("found a mix of service context arguments and other context arguments/options") + } case typeOfObjectContext: + if definition == nil { + definition = NewObject(name, opts...) + } else if definition.Type() != internal.ServiceType_VIRTUAL_OBJECT { + panic("found a mix of object context arguments and other context arguments/options") + } handlerType = internal.ServiceHandlerType_EXCLUSIVE case typeOfSharedObjectContext: + if definition == nil { + definition = NewObject(name, opts...) + } else if definition.Type() != internal.ServiceType_VIRTUAL_OBJECT { + panic("found a mix of object context arguments and other context arguments/options") + } handlerType = internal.ServiceHandlerType_SHARED default: - // first parameter is not an object context + // first parameter is not a context continue } @@ -80,99 +99,65 @@ func Object(object any, opts ...options.ObjectOption) *object { input := mtype.In(2) output := mtype.Out(0) - definition.Handler(mname, &objectReflectHandler{ - options.ObjectHandlerOptions{}, - handlerType, - reflectHandler{ - fn: method.Func, - receiver: val, - input: input, - output: output, - }, - }) + switch def := definition.(type) { + case *service: + def.Handler(mname, &serviceReflectHandler{ + reflectHandler{ + fn: method.Func, + receiver: val, + input: input, + output: output, + options: options.HandlerOptions{}, + handlerType: nil, + }, + }) + case *object: + def.Handler(mname, &objectReflectHandler{ + reflectHandler{ + fn: method.Func, + receiver: val, + input: input, + output: input, + options: options.HandlerOptions{}, + handlerType: &handlerType, + }, + }) + } } - return definition -} - -// Service converts a struct with methods into a Restate Service where each correctly-typed -// and exported method of the struct will become a handler on the Service. The Service name defaults -// to the name of the struct, but this can be overidden by providing a `ServiceName() string` method. -// The handler name is the name of the method. Handler methods should be of the type `ServiceHandlerFn[I, O]`. -// -// Input types will be deserialised with the provided codec (defaults to JSON) except when they are restate.Void, -// in which case no input bytes or content type may be sent. -// Output types will be serialised with the provided codec (defaults to JSON) except when they are restate.Void, -// in which case no data will be sent and no content type set. -func Service(service any, opts ...options.ServiceOption) *service { - typ := reflect.TypeOf(service) - val := reflect.ValueOf(service) - var name string - if sn, ok := service.(serviceNamer); ok { - name = sn.ServiceName() - } else { - name = reflect.Indirect(val).Type().Name() + if definition == nil { + panic("no valid handlers could be found within the exported methods on this struct") } - definition := NewService(name, opts...) - - for m := 0; m < typ.NumMethod(); m++ { - method := typ.Method(m) - - mtype := method.Type - mname := method.Name - // Method must be exported. - if !method.IsExported() { - continue - } - - // Method needs three ins: receiver, Context, I - if mtype.NumIn() != 3 { - continue - } - - if ctxType := mtype.In(1); ctxType != typeOfContext { - continue - } - - // Method needs two outs: O, and error - if mtype.NumOut() != 2 { - continue - } - // The second return type of the method must be error. - if returnType := mtype.Out(1); returnType != typeOfError { - continue - } + return definition +} - input := mtype.In(2) - output := mtype.Out(0) +type reflectHandler struct { + fn reflect.Value + receiver reflect.Value + input reflect.Type + output reflect.Type + options options.HandlerOptions + handlerType *internal.ServiceHandlerType +} - definition.Handler(mname, &serviceReflectHandler{ - options.ServiceHandlerOptions{}, - reflectHandler{ - fn: method.Func, - receiver: val, - input: input, - output: output, - }, - }) - } +func (h *reflectHandler) getOptions() *options.HandlerOptions { + return &h.options +} - return definition +func (h *reflectHandler) InputPayload() *encoding.InputPayload { + return encoding.InputPayloadFor(h.options.Codec, reflect.Zero(h.input).Interface()) } -type reflectHandler struct { - fn reflect.Value - receiver reflect.Value - input reflect.Type - output reflect.Type +func (h *reflectHandler) OutputPayload() *encoding.OutputPayload { + return encoding.OutputPayloadFor(h.options.Codec, reflect.Zero(h.output).Interface()) } -func (h *reflectHandler) sealed() {} +func (h *reflectHandler) HandlerType() *internal.ServiceHandlerType { + return h.handlerType +} type objectReflectHandler struct { - options options.ObjectHandlerOptions - handlerType internal.ServiceHandlerType reflectHandler } @@ -206,24 +191,7 @@ func (h *objectReflectHandler) Call(ctx ObjectContext, bytes []byte) ([]byte, er return bytes, nil } -func (h *objectReflectHandler) getOptions() *options.ObjectHandlerOptions { - return &h.options -} - -func (h *objectReflectHandler) InputPayload() *encoding.InputPayload { - return encoding.InputPayloadFor(h.options.Codec, reflect.Zero(h.input).Interface()) -} - -func (h *objectReflectHandler) OutputPayload() *encoding.OutputPayload { - return encoding.OutputPayloadFor(h.options.Codec, reflect.Zero(h.output).Interface()) -} - -func (h *objectReflectHandler) HandlerType() *internal.ServiceHandlerType { - return &h.handlerType -} - type serviceReflectHandler struct { - options options.ServiceHandlerOptions reflectHandler } @@ -256,19 +224,3 @@ func (h *serviceReflectHandler) Call(ctx Context, bytes []byte) ([]byte, error) return bytes, nil } - -func (h *serviceReflectHandler) getOptions() *options.ServiceHandlerOptions { - return &h.options -} - -func (h *serviceReflectHandler) InputPayload() *encoding.InputPayload { - return h.options.Codec.InputPayload(reflect.Zero(h.input)) -} - -func (h *serviceReflectHandler) OutputPayload() *encoding.OutputPayload { - return h.options.Codec.OutputPayload(reflect.Zero(h.output)) -} - -func (h *serviceReflectHandler) HandlerType() *internal.ServiceHandlerType { - return nil -} diff --git a/router.go b/router.go index 30e7dd4..da148e9 100644 --- a/router.go +++ b/router.go @@ -14,36 +14,54 @@ type ServiceDefinition interface { Handlers() map[string]Handler } -// service stores a list of handlers under a named Service -type service struct { +// serviceDefinition stores a list of handlers under a named service +type serviceDefinition struct { name string handlers map[string]Handler - options options.ServiceOptions + options options.ServiceDefinitionOptions + typ internal.ServiceType +} + +var _ ServiceDefinition = &serviceDefinition{} + +// Name returns the name of the service described in this definition +func (r *serviceDefinition) Name() string { + return r.name +} + +// Handlers returns the list of handlers in this service definition +func (r *serviceDefinition) Handlers() map[string]Handler { + return r.handlers } -var _ ServiceDefinition = &service{} +// Type returns the type of this service definition (Service or Virtual Object) +func (r *serviceDefinition) Type() internal.ServiceType { + return r.typ +} + +type service struct { + serviceDefinition +} // NewService creates a new named Service -func NewService(name string, opts ...options.ServiceOption) *service { - o := options.ServiceOptions{} +func NewService(name string, opts ...options.ServiceDefinitionOption) *service { + o := options.ServiceDefinitionOptions{} for _, opt := range opts { - opt.BeforeService(&o) + opt.BeforeServiceDefinition(&o) } if o.DefaultCodec == nil { o.DefaultCodec = encoding.JSONCodec } return &service{ - name: name, - handlers: make(map[string]Handler), - options: o, + serviceDefinition: serviceDefinition{ + name: name, + handlers: make(map[string]Handler), + options: o, + typ: internal.ServiceType_SERVICE, + }, } } -// Name returns the name of this Service -func (r *service) Name() string { - return r.name -} - // Handler registers a new Service handler by name func (r *service) Handler(name string, handler ServiceHandler) *service { if handler.getOptions().Codec == nil { @@ -53,46 +71,29 @@ func (r *service) Handler(name string, handler ServiceHandler) *service { return r } -// Handlers returns the list of handlers in this Service -func (r *service) Handlers() map[string]Handler { - return r.handlers -} - -// Type implements [ServiceDefinition] by returning [internal.ServiceType_SERVICE] -func (r *service) Type() internal.ServiceType { - return internal.ServiceType_SERVICE -} - -// object stores a list of handlers under a named Virtual Object type object struct { - name string - handlers map[string]Handler - options options.ObjectOptions + serviceDefinition } -var _ ServiceDefinition = &object{} - // NewObject creates a new named Virtual Object -func NewObject(name string, opts ...options.ObjectOption) *object { - o := options.ObjectOptions{} +func NewObject(name string, opts ...options.ServiceDefinitionOption) *object { + o := options.ServiceDefinitionOptions{} for _, opt := range opts { - opt.BeforeObject(&o) + opt.BeforeServiceDefinition(&o) } if o.DefaultCodec == nil { o.DefaultCodec = encoding.JSONCodec } return &object{ - name: name, - handlers: make(map[string]Handler), - options: o, + serviceDefinition: serviceDefinition{ + name: name, + handlers: make(map[string]Handler), + options: o, + typ: internal.ServiceType_VIRTUAL_OBJECT, + }, } } -// Name returns the name of this Virtual Object -func (r *object) Name() string { - return r.name -} - // Handler registers a new Virtual Object handler by name func (r *object) Handler(name string, handler ObjectHandler) *object { if handler.getOptions().Codec == nil { @@ -101,13 +102,3 @@ func (r *object) Handler(name string, handler ObjectHandler) *object { r.handlers[name] = handler return r } - -// Handlers returns the list of handlers in this Virtual Object -func (r *object) Handlers() map[string]Handler { - return r.handlers -} - -// Type implements [ServiceDefinition] by returning [internal.ServiceType_VIRTUAL_OBJECT] -func (r *object) Type() internal.ServiceType { - return internal.ServiceType_VIRTUAL_OBJECT -}