diff --git a/cmd/flag/convert.go b/cmd/flag/convert.go index 21c55a0a..0c666d4f 100644 --- a/cmd/flag/convert.go +++ b/cmd/flag/convert.go @@ -2,10 +2,12 @@ package flag import "github.com/iancoleman/strcase" +// ToKey converts a flag string to snake case. func ToKey(flag string) string { return strcase.ToSnake(flag) } +// ToShorthand returns the first character of the input string. func ToShorthand(flag string) string { if flag == "" { return "" diff --git a/cmd/printer/table.go b/cmd/printer/table.go index c9ab02db..0486a825 100644 --- a/cmd/printer/table.go +++ b/cmd/printer/table.go @@ -1,61 +1,59 @@ package printer import ( + "errors" + "github.com/jedib0t/go-pretty/v6/table" "github.com/jedib0t/go-pretty/v6/text" "github.com/siyul-park/uniflow/pkg/primitive" "github.com/xiatechs/jsonata-go" ) -type ( - TableColumnDefinition struct { - Name string - Format string - } +// TableColumnDefinition represents the definition of a table column. +type TableColumnDefinition struct { + Name string // Name is the name of the column. + Format string // Format is the JSONata expression for formatting the column. +} - TablePrinter struct { - names []string - formats []*jsonata.Expr - } -) +// TablePrinter is responsible for printing tabular data based on the provided columns. +type TablePrinter struct { + names []string + formats []*jsonata.Expr +} -var ( - style = table.Style{ - Name: "StyleDefault", - Box: table.BoxStyle{ - BottomLeft: "", - BottomRight: "", - BottomSeparator: "", - EmptySeparator: text.RepeatAndTrim(" ", text.RuneWidthWithoutEscSequences(" ")), - Left: "", - LeftSeparator: "", - MiddleHorizontal: "", - MiddleSeparator: "", - MiddleVertical: "", - PaddingLeft: " ", - PaddingRight: " ", - PageSeparator: "\n", - Right: "", - RightSeparator: "", - TopLeft: "", - TopRight: "", - TopSeparator: "", - UnfinishedRow: " ~", - }, - Color: table.ColorOptionsDefault, - Format: table.FormatOptionsDefault, - HTML: table.DefaultHTMLOptions, - Options: table.Options{ - DrawBorder: false, - SeparateColumns: true, - SeparateFooter: false, - SeparateHeader: false, - SeparateRows: false, - }, - Title: table.TitleOptionsDefault, - } -) +// style is the default style configuration for the table. +var style = table.Style{ + Name: "StyleDefault", + Box: table.BoxStyle{ + BottomLeft: "", + BottomRight: "", + BottomSeparator: "", + EmptySeparator: text.RepeatAndTrim(" ", text.RuneWidthWithoutEscSequences(" ")), + Left: "", + LeftSeparator: "", + MiddleHorizontal: "", + MiddleSeparator: "", + MiddleVertical: "", + PaddingLeft: " ", + PaddingRight: " ", + PageSeparator: "\n", + Right: "", + RightSeparator: "", + TopLeft: "", + TopRight: "", + TopSeparator: "", + UnfinishedRow: " ~", + }, + Color: table.ColorOptionsDefault, + Format: table.FormatOptionsDefault, + HTML: table.DefaultHTMLOptions, + Options: table.Options{ + DoNotColorBordersAndSeparators: true, + }, + Title: table.TitleOptionsDefault, +} +// NewTable creates a new TablePrinter based on the provided column definitions. func NewTable(columns []TableColumnDefinition) (*TablePrinter, error) { names := make([]string, len(columns)) formats := make([]*jsonata.Expr, len(columns)) @@ -77,6 +75,7 @@ func NewTable(columns []TableColumnDefinition) (*TablePrinter, error) { }, nil } +// Print formats and prints the provided data as a table. func (p *TablePrinter) Print(data any) (string, error) { value, err := primitive.MarshalText(data) if err != nil { @@ -84,10 +83,13 @@ func (p *TablePrinter) Print(data any) (string, error) { } var elements []any - if v, ok := value.(*primitive.Slice); ok { + switch v := value.(type) { + case *primitive.Slice: elements = v.Slice() - } else if v, ok := value.(*primitive.Map); ok { + case *primitive.Map: elements = append(elements, v.Interface()) + default: + return "", errors.New("unsupported data type") } header := make(table.Row, len(p.names)) diff --git a/cmd/resource/builder.go b/cmd/resource/builder.go index 85e81628..c832402f 100644 --- a/cmd/resource/builder.go +++ b/cmd/resource/builder.go @@ -7,43 +7,49 @@ import ( "github.com/siyul-park/uniflow/pkg/scheme" ) -type ( - Builder struct { - scheme *scheme.Scheme - namespace string - fsys fs.FS - filename string - } -) +// Builder is responsible for building scheme.Spec instances from raw data. +type Builder struct { + scheme *scheme.Scheme + namespace string + fsys fs.FS + filename string +} +// NewBuilder creates a new Builder instance. func NewBuilder() *Builder { return &Builder{} } +// Scheme sets the scheme for the Builder. func (b *Builder) Scheme(scheme *scheme.Scheme) *Builder { b.scheme = scheme return b } +// Namespace sets the namespace for the Builder. func (b *Builder) Namespace(namespace string) *Builder { b.namespace = namespace return b } +// FS sets the file system for the Builder. func (b *Builder) FS(fsys fs.FS) *Builder { b.fsys = fsys return b } +// Filename sets the filename for the Builder. func (b *Builder) Filename(filename string) *Builder { b.filename = filename return b } +// Build builds scheme.Spec instances based on the configured parameters. func (b *Builder) Build() ([]scheme.Spec, error) { if b.fsys == nil || b.filename == "" { return nil, nil } + file, err := b.fsys.Open(b.filename) if err != nil { return nil, err diff --git a/cmd/resource/scheme.go b/cmd/resource/scheme.go index e3ecf830..b6a8ec63 100644 --- a/cmd/resource/scheme.go +++ b/cmd/resource/scheme.go @@ -7,18 +7,19 @@ import ( "github.com/siyul-park/uniflow/pkg/scheme" ) -type ( - SpecCodecOptions struct { - Scheme *scheme.Scheme - Namespace string - } +// SpecCodecOptions holds options for creating a SpecCodec. +type SpecCodecOptions struct { + Scheme *scheme.Scheme + Namespace string +} - SpecCodec struct { - scheme *scheme.Scheme - namespace string - } -) +// SpecCodec is responsible for decoding raw data into scheme.Spec instances. +type SpecCodec struct { + scheme *scheme.Scheme + namespace string +} +// NewSpecCodec creates a new SpecCodec instance with the provided options. func NewSpecCodec(opts ...SpecCodecOptions) *SpecCodec { var scheme *scheme.Scheme var namespace string @@ -38,6 +39,7 @@ func NewSpecCodec(opts ...SpecCodecOptions) *SpecCodec { } } +// Decode decodes raw data into a scheme.Spec instance. func (c *SpecCodec) Decode(data any) (scheme.Spec, error) { doc, err := primitive.MarshalBinary(data) if err != nil { diff --git a/cmd/resource/yamljson.go b/cmd/resource/yamljson.go index 612dca30..0a664e4a 100644 --- a/cmd/resource/yamljson.go +++ b/cmd/resource/yamljson.go @@ -7,6 +7,7 @@ import ( "gopkg.in/yaml.v3" ) +// UnmarshalYAMLOrJSON unmarshals data based on its content type, supporting both JSON and YAML formats. func UnmarshalYAMLOrJSON(data []byte, v any) error { if http.DetectContentType(data) == "application/json" { return json.Unmarshal(data, v) diff --git a/cmd/uniflow/apply/cmd.go b/cmd/uniflow/apply/cmd.go index ccdd01ba..c5f0ddbb 100644 --- a/cmd/uniflow/apply/cmd.go +++ b/cmd/uniflow/apply/cmd.go @@ -1,6 +1,7 @@ package apply import ( + "context" "fmt" "io/fs" @@ -15,145 +16,153 @@ import ( "github.com/spf13/cobra" ) -type ( - Config struct { - Scheme *scheme.Scheme - Database database.Database - FS fs.FS - } -) +// Config represents the configuration for the apply command. +type Config struct { + Scheme *scheme.Scheme + Database database.Database + FS fs.FS +} -var ( - SpecTableColumnDefinitions = []printer.TableColumnDefinition{ - { - Name: "id", - Format: "$.id", - }, - { - Name: "kind", - Format: "$.kind", - }, - { - Name: "name", - Format: "$.name", - }, - { - Name: "namespace", - Format: "$.namespace", - }, - { - Name: "links", - Format: "$.links", - }, - } -) +// SpecTableColumnDefinitions defines columns for displaying spec information. +var SpecTableColumnDefinitions = []printer.TableColumnDefinition{ + {Name: "id", Format: "$.id"}, + {Name: "kind", Format: "$.kind"}, + {Name: "name", Format: "$.name"}, + {Name: "namespace", Format: "$.namespace"}, + {Name: "links", Format: "$.links"}, +} +// NewCmd creates a new cobra.Command for the apply command. func NewCmd(config Config) *cobra.Command { - sc := config.Scheme - db := config.Database - fsys := config.FS - cmd := &cobra.Command{ Use: "apply", - Short: "Apply a configuration to a resource by file name", - RunE: func(cmd *cobra.Command, _ []string) error { - ctx := cmd.Context() - - ns, err := cmd.Flags().GetString(FlagNamespace) - if err != nil { - return err - } - fl, err := cmd.Flags().GetString(FlagFile) - if err != nil { - return err - } + Short: "Apply a configuration to a resource", + RunE: runApplyCommand(config), + } - st, err := storage.New(ctx, storage.Config{ - Scheme: sc, - Database: db, - }) - if err != nil { - return err - } + cmd.PersistentFlags().StringP(FlagNamespace, flag.ToShorthand(FlagNamespace), "", "Set the resource's namespace. If not set, use the default namespace.") + cmd.PersistentFlags().StringP(FlagFile, flag.ToShorthand(FlagFile), "", "Set the file path to be applied.") - b := resource.NewBuilder(). - Scheme(sc). - Namespace(ns). - FS(fsys). - Filename(fl) + return cmd +} - specs, err := b.Build() - if err != nil { - return err - } +// runApplyCommand executes the apply command. +func runApplyCommand(config Config) func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, _ []string) error { + ctx := cmd.Context() + + ns, err := cmd.Flags().GetString(FlagNamespace) + if err != nil { + return err + } + fl, err := cmd.Flags().GetString(FlagFile) + if err != nil { + return err + } + + st, err := storage.New(ctx, storage.Config{ + Scheme: config.Scheme, + Database: config.Database, + }) + if err != nil { + return err + } + + b := resource.NewBuilder(). + Scheme(config.Scheme). + Namespace(ns). + FS(config.FS). + Filename(fl) + + specs, err := b.Build() + if err != nil { + return err + } + + if err := updateSpecIDs(ctx, st, specs); err != nil { + return err + } + + if err := applySpecs(ctx, st, specs); err != nil { + return err + } + + if err := printSpecTable(cmd, specs); err != nil { + return err + } + + return nil + } +} - for _, spec := range specs { - if spec.GetID() == (ulid.ULID{}) { - if spec.GetName() != "" { - filter := storage.Where[string](scheme.KeyName).EQ(spec.GetName()).And(storage.Where[string](scheme.KeyNamespace).EQ(spec.GetNamespace())) - if exist, err := st.FindOne(ctx, filter); err != nil { - return err - } else if exist != nil { - spec.SetID(exist.GetID()) - } - } else { - spec.SetID(ulid.Make()) - } +func updateSpecIDs(ctx context.Context, st *storage.Storage, specs []scheme.Spec) error { + for _, spec := range specs { + if spec.GetID() == (ulid.ULID{}) { + if spec.GetName() != "" { + filter := storage.Where[string](scheme.KeyName).EQ(spec.GetName()).And(storage.Where[string](scheme.KeyNamespace).EQ(spec.GetNamespace())) + if exist, err := st.FindOne(ctx, filter); err != nil { + return err + } else if exist != nil { + spec.SetID(exist.GetID()) } + } else { + spec.SetID(ulid.Make()) } + } + } + return nil +} - var ids []ulid.ULID - for _, spec := range specs { - ids = append(ids, spec.GetID()) - } - - exists, err := st.FindMany(ctx, storage.Where[ulid.ULID](scheme.KeyID).IN(ids...), &database.FindOptions{ - Limit: lo.ToPtr[int](len(ids)), - }) - if err != nil { - return err - } - existsIds := make(map[ulid.ULID]struct{}, len(exists)) - for _, spec := range exists { - existsIds[spec.GetID()] = struct{}{} - } +func applySpecs(ctx context.Context, st *storage.Storage, specs []scheme.Spec) error { + var ids []ulid.ULID + for _, spec := range specs { + ids = append(ids, spec.GetID()) + } - var inserted []scheme.Spec - var updated []scheme.Spec - for _, spec := range specs { - if _, ok := existsIds[spec.GetID()]; ok { - updated = append(updated, spec) - } else { - inserted = append(inserted, spec) - } - } + exists, err := st.FindMany(ctx, storage.Where[ulid.ULID](scheme.KeyID).IN(ids...), &database.FindOptions{ + Limit: lo.ToPtr[int](len(ids)), + }) + if err != nil { + return err + } + existsIds := make(map[ulid.ULID]struct{}, len(exists)) + for _, spec := range exists { + existsIds[spec.GetID()] = struct{}{} + } - if _, err := st.InsertMany(ctx, inserted); err != nil { - return err - } - if _, err := st.UpdateMany(ctx, updated); err != nil { - return err - } + var inserted []scheme.Spec + var updated []scheme.Spec + for _, spec := range specs { + if _, ok := existsIds[spec.GetID()]; ok { + updated = append(updated, spec) + } else { + inserted = append(inserted, spec) + } + } - tablePrinter, err := printer.NewTable(SpecTableColumnDefinitions) - if err != nil { - return err - } + if _, err := st.InsertMany(ctx, inserted); err != nil { + return err + } + if _, err := st.UpdateMany(ctx, updated); err != nil { + return err + } + return nil +} - table, err := tablePrinter.Print(specs) - if err != nil { - return err - } - if _, err := fmt.Fprint(cmd.OutOrStdout(), table); err != nil { - return err - } +func printSpecTable(cmd *cobra.Command, specs []scheme.Spec) error { + tablePrinter, err := printer.NewTable(SpecTableColumnDefinitions) + if err != nil { + return err + } - return nil - }, + table, err := tablePrinter.Print(specs) + if err != nil { + return err } - cmd.PersistentFlags().StringP(FlagNamespace, flag.ToShorthand(FlagNamespace), "", "Set the namespace. If not set it up, use default namespace. In this case.") - cmd.PersistentFlags().StringP(FlagFile, flag.ToShorthand(FlagFile), "", "Set the file path that want to be applied.") + if _, err := fmt.Fprint(cmd.OutOrStdout(), table); err != nil { + return err + } - return cmd + return nil } diff --git a/cmd/uniflow/cmd.go b/cmd/uniflow/cmd.go index 3be48908..84dbe6a9 100644 --- a/cmd/uniflow/cmd.go +++ b/cmd/uniflow/cmd.go @@ -11,15 +11,16 @@ import ( "github.com/spf13/cobra" ) -type ( - Config struct { - Scheme *scheme.Scheme - Hook *hook.Hook - Database database.Database - FS fs.FS - } -) +// Config holds the configuration parameters for the main command. + +type Config struct { + Scheme *scheme.Scheme + Hook *hook.Hook + Database database.Database + FS fs.FS +} +// NewCmd creates the root cobra command for the 'uniflow' CLI. func NewCmd(config Config) *cobra.Command { sc := config.Scheme hk := config.Hook diff --git a/cmd/uniflow/start/cmd.go b/cmd/uniflow/start/cmd.go index ed58fd2f..35f9f5cb 100644 --- a/cmd/uniflow/start/cmd.go +++ b/cmd/uniflow/start/cmd.go @@ -1,6 +1,7 @@ package start import ( + "context" "io/fs" "os" "os/signal" @@ -17,95 +18,106 @@ import ( "github.com/spf13/cobra" ) -type ( - Config struct { - Scheme *scheme.Scheme - Hook *hook.Hook - Database database.Database - FS fs.FS - } -) +// Config holds the configuration for the uniflow command. +type Config struct { + Scheme *scheme.Scheme + Hook *hook.Hook + Database database.Database + FS fs.FS +} +// NewCmd creates a new Cobra command for the uniflow application. func NewCmd(config Config) *cobra.Command { - sc := config.Scheme - hk := config.Hook - db := config.Database - fsys := config.FS - cmd := &cobra.Command{ Use: "start", - Short: "Start a uniflow worker", - RunE: func(cmd *cobra.Command, _ []string) error { - ctx := cmd.Context() + Short: "Start a worker process", + RunE: runStartCommand(config), + } - ns, err := cmd.Flags().GetString(FlagNamespace) - if err != nil { - return err - } - boot, err := cmd.Flags().GetString(FlagBoot) - if err != nil { - return err - } + cmd.PersistentFlags().StringP(FlagNamespace, flag.ToShorthand(FlagNamespace), "", "Set the worker's namespace.") + cmd.PersistentFlags().StringP(FlagBoot, flag.ToShorthand(FlagBoot), "", "Set the boot file path for initializing nodes.") - if boot != "" { - st, err := storage.New(ctx, storage.Config{ - Scheme: sc, - Database: db, - }) - if err != nil { - return err - } - - var filter *storage.Filter - if ns != "" { - filter = storage.Where[string](scheme.KeyNamespace).EQ(ns) - } - - if specs, err := st.FindMany(ctx, filter, &database.FindOptions{ - Limit: lo.ToPtr[int](1), - }); err != nil { - return err - } else if len(specs) == 0 { - b := resource.NewBuilder(). - Scheme(sc). - Namespace(ns). - FS(fsys). - Filename(boot) - - specs, err := b.Build() - if err != nil { - return err - } - - if _, err := st.InsertMany(ctx, specs); err != nil { - return err - } - } - } + return cmd +} - r, err := runtime.New(ctx, runtime.Config{ - Namespace: ns, - Scheme: sc, - Hooks: hk, - Database: db, - }) - if err != nil { +func runStartCommand(config Config) func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, _ []string) error { + ctx := cmd.Context() + ns, err := cmd.Flags().GetString(FlagNamespace) + if err != nil { + return err + } + + boot, err := cmd.Flags().GetString(FlagBoot) + if err != nil { + return err + } + + if boot != "" { + if err := initializeNamespace(ctx, config, ns, boot); err != nil { return err } + } + + r, err := runtime.New(ctx, runtime.Config{ + Namespace: ns, + Scheme: config.Scheme, + Hooks: config.Hook, + Database: config.Database, + }) + if err != nil { + return err + } + + handleSignals(ctx, r) + return r.Start(ctx) + } +} - sigs := make(chan os.Signal, 1) - signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) - go func() { - <-sigs - _ = r.Close(ctx) - }() +func initializeNamespace(ctx context.Context, config Config, ns, boot string) error { + st, err := storage.New(ctx, storage.Config{ + Scheme: config.Scheme, + Database: config.Database, + }) + if err != nil { + return err + } - return r.Start(ctx) - }, + filter := storage.Where[string](scheme.KeyNamespace).EQ(ns) + specs, err := st.FindMany(ctx, filter, &database.FindOptions{Limit: lo.ToPtr[int](1)}) + if err != nil { + return err } - cmd.PersistentFlags().StringP(FlagNamespace, flag.ToShorthand(FlagNamespace), "", "Set the namespace. If not set it up, runs all namespaces. In this case, if namespace is sharing resources exclusively, some nodes may not run normally.") - cmd.PersistentFlags().StringP(FlagBoot, flag.ToShorthand(FlagBoot), "", "Set the boot file path that must be installed initially if the node does not exist in namespace.") + if len(specs) == 0 { + if err := installBootFile(ctx, config, ns, boot); err != nil { + return err + } + } + return nil +} - return cmd +func installBootFile(ctx context.Context, config Config, ns, boot string) error { + b := resource.NewBuilder().Scheme(config.Scheme).Namespace(ns).FS(config.FS).Filename(boot) + specs, err := b.Build() + if err != nil { + return err + } + + st, err := storage.New(ctx, storage.Config{Scheme: config.Scheme, Database: config.Database}) + if err != nil { + return err + } + + _, err = st.InsertMany(ctx, specs) + return err +} + +func handleSignals(ctx context.Context, r *runtime.Runtime) { + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + go func() { + <-sigs + _ = r.Close(ctx) + }() } diff --git a/pkg/encoding/decoder.go b/pkg/encoding/decoder.go index 238ef7be..23f3ea09 100644 --- a/pkg/encoding/decoder.go +++ b/pkg/encoding/decoder.go @@ -1,16 +1,16 @@ package encoding -type ( - // Decoder is the interface for decoding data. - Decoder[S, T any] interface { - Decode(source S, target T) error - } +// Decoder is the interface for decoding data. +type Decoder[S, T any] interface { + Decode(source S, target T) error +} - DecoderFunc[S, T any] func(source S, target T) error -) +// DecoderFunc is a function type that implements the Decoder interface. +type DecoderFunc[S, T any] func(source S, target T) error var _ Decoder[any, any] = DecoderFunc[any, any](func(source, target any) error { return nil }) +// Decode calls the underlying function to perform decoding. func (dec DecoderFunc[S, T]) Decode(source S, target T) error { return dec(source, target) } diff --git a/pkg/encoding/encoder.go b/pkg/encoding/encoder.go index 22882f52..27f70f9d 100644 --- a/pkg/encoding/encoder.go +++ b/pkg/encoding/encoder.go @@ -1,16 +1,16 @@ package encoding -type ( - // Encoder is an interface for encoding data. - Encoder[S, T any] interface { - Encode(source S) (T, error) - } +// Encoder is an interface for encoding data. +type Encoder[S, T any] interface { + Encode(source S) (T, error) +} - EncoderFunc[S, T any] func(source S) (T, error) -) +// EncoderFunc is a function type that implements the Encoder interface. +type EncoderFunc[S, T any] func(source S) (T, error) var _ Encoder[any, any] = EncoderFunc[any, any](func(_ any) (any, error) { return nil, nil }) +// Encode calls the underlying function to perform encoding. func (enc EncoderFunc[S, T]) Encode(source S) (T, error) { return enc(source) } diff --git a/pkg/encoding/error.go b/pkg/encoding/error.go index a96c8e9d..ad347626 100644 --- a/pkg/encoding/error.go +++ b/pkg/encoding/error.go @@ -4,6 +4,4 @@ import ( "github.com/pkg/errors" ) -var ( - ErrUnsupportedValue = errors.New("unsupported value") -) +var ErrUnsupportedValue = errors.New("unsupported value") diff --git a/pkg/encoding/group.go b/pkg/encoding/group.go index 40aeb728..cf0ef3b2 100644 --- a/pkg/encoding/group.go +++ b/pkg/encoding/group.go @@ -6,27 +6,21 @@ import ( "github.com/pkg/errors" ) -type ( - // EncoderGroup is a group of Encoder. - EncoderGroup[S, T any] struct { - encoders []Encoder[S, T] - lock sync.RWMutex - } - - // DecoderGroup is a group of Decoder. - DecoderGroup[S, T any] struct { - decoders []Decoder[S, T] - lock sync.RWMutex - } -) +// EncoderGroup is a group of Encoder. +type EncoderGroup[S, T any] struct { + encoders []Encoder[S, T] + lock sync.RWMutex +} +// Ensure EncoderGroup implements the Encoder interface. var _ Encoder[any, any] = (*EncoderGroup[any, any])(nil) -var _ Decoder[any, any] = (*DecoderGroup[any, any])(nil) +// NewEncoderGroup creates a new EncoderGroup instance. func NewEncoderGroup[S, T any]() *EncoderGroup[S, T] { return &EncoderGroup[S, T]{} } +// Add adds an encoder to the group. func (e *EncoderGroup[S, T]) Add(encoder Encoder[S, T]) { e.lock.Lock() defer e.lock.Unlock() @@ -34,6 +28,7 @@ func (e *EncoderGroup[S, T]) Add(encoder Encoder[S, T]) { e.encoders = append(e.encoders, encoder) } +// Len returns the number of encoders in the group. func (e *EncoderGroup[S, T]) Len() int { e.lock.Lock() defer e.lock.Unlock() @@ -41,6 +36,7 @@ func (e *EncoderGroup[S, T]) Len() int { return len(e.encoders) } +// Encode attempts to encode the source using the encoders in the group. func (e *EncoderGroup[S, T]) Encode(source S) (T, error) { e.lock.RLock() defer e.lock.RUnlock() @@ -56,10 +52,21 @@ func (e *EncoderGroup[S, T]) Encode(source S) (T, error) { return zero, errors.WithStack(ErrUnsupportedValue) } +// DecoderGroup is a group of Decoder. +type DecoderGroup[S, T any] struct { + decoders []Decoder[S, T] + lock sync.RWMutex +} + +// Ensure DecoderGroup implements the Decoder interface. +var _ Decoder[any, any] = (*DecoderGroup[any, any])(nil) + +// NewDecoderGroup creates a new DecoderGroup instance. func NewDecoderGroup[S, T any]() *DecoderGroup[S, T] { return &DecoderGroup[S, T]{} } +// Add adds a decoder to the group. func (d *DecoderGroup[S, T]) Add(decoder Decoder[S, T]) { d.lock.Lock() defer d.lock.Unlock() @@ -67,6 +74,7 @@ func (d *DecoderGroup[S, T]) Add(decoder Decoder[S, T]) { d.decoders = append(d.decoders, decoder) } +// Len returns the number of decoders in the group. func (d *DecoderGroup[S, T]) Len() int { d.lock.Lock() defer d.lock.Unlock() @@ -74,6 +82,7 @@ func (d *DecoderGroup[S, T]) Len() int { return len(d.decoders) } +// Decode attempts to decode the source using the decoders in the group. func (d *DecoderGroup[S, T]) Decode(source S, target T) error { d.lock.RLock() defer d.lock.RUnlock() diff --git a/pkg/port/inithook.go b/pkg/port/inithook.go index a1bc3cad..2f7c26f0 100644 --- a/pkg/port/inithook.go +++ b/pkg/port/inithook.go @@ -4,17 +4,18 @@ import ( "github.com/siyul-park/uniflow/pkg/process" ) -type ( - // InitHook is a hook that is called when Port is initialized by Process. - InitHook interface { - Init(proc *process.Process) - } +// InitHook is a hook that is called when Port is initialized by Process. +type InitHook interface { + Init(proc *process.Process) +} - InitHookFunc func(proc *process.Process) -) +// InitHookFunc is a function type that implements the InitHook interface. +type InitHookFunc func(proc *process.Process) +// Ensure InitHookFunc implements the InitHook interface. var _ InitHook = InitHookFunc(func(proc *process.Process) {}) +// Init calls the underlying function for InitHookFunc. func (h InitHookFunc) Init(proc *process.Process) { h(proc) } diff --git a/pkg/symbol/loadhook.go b/pkg/symbol/loadhook.go index 005a1fa5..d721b2a2 100644 --- a/pkg/symbol/loadhook.go +++ b/pkg/symbol/loadhook.go @@ -2,16 +2,14 @@ package symbol import "github.com/siyul-park/uniflow/pkg/node" -type ( - // LoadHook is an interface for hooks that are called when node.Node is loaded. - LoadHook interface { - // Load is called when node.Node is loaded. - Load(n node.Node) error - } +// LoadHook is an interface for hooks that are called when node.Node is loaded. +type LoadHook interface { + // Load is called when node.Node is loaded. + Load(n node.Node) error +} - // LoadHookFunc is a function type that implements the LoadHook interface. - LoadHookFunc func(n node.Node) error -) +// LoadHookFunc is a function type that implements the LoadHook interface. +type LoadHookFunc func(n node.Node) error var _ LoadHook = LoadHookFunc(func(n node.Node) error { return nil }) diff --git a/pkg/symbol/unloadhook.go b/pkg/symbol/unloadhook.go index 2dc61295..d6213448 100644 --- a/pkg/symbol/unloadhook.go +++ b/pkg/symbol/unloadhook.go @@ -2,16 +2,14 @@ package symbol import "github.com/siyul-park/uniflow/pkg/node" -type ( - // UnloadHook is an interface for hooks that are called when node.Node is unloaded. - UnloadHook interface { - // Unload is called when node.Node is unloaded. - Unload(n node.Node) error - } +// UnloadHook is an interface for hooks that are called when node.Node is unloaded. +type UnloadHook interface { + // Unload is called when node.Node is unloaded. + Unload(n node.Node) error +} - // UnloadHookFunc is a function type that implements the UnloadHook interface. - UnloadHookFunc func(n node.Node) error -) +// UnloadHookFunc is a function type that implements the UnloadHook interface. +type UnloadHookFunc func(n node.Node) error var _ UnloadHook = UnloadHookFunc(func(n node.Node) error { return nil })