Skip to content

Commit

Permalink
add cumston options for component template
Browse files Browse the repository at this point in the history
  • Loading branch information
mkideal committed Aug 10, 2024
1 parent a4781e2 commit 17a2144
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 85 deletions.
24 changes: 0 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,30 +107,6 @@ Run your application with a configuration file:
- `-t`: Test the configuration for validity ✅
- `-T`: Enable template processing for component configurations 🧩

### Example with Template Processing

```sh
./demo -T app.json
```

```json
{
"Context": {
"Name": "world"
},
"Components": [
{
"Name": "hello",
"Options": {
"Message": "{{.Name}}"
}
}
]
}
```

This example demonstrates how to use template processing in your component configurations. Cool, huh? 😎

## 📚 Documentation

For detailed documentation of each package and component, please refer to the GoDoc:
Expand Down
36 changes: 28 additions & 8 deletions component/builtin/httpserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package builtin

import (
"context"
"net"
"net/http"

"github.com/gopherd/core/component"
Expand All @@ -12,7 +13,8 @@ type HTTPServerComponent interface {
Handle(path string, handler http.Handler)
}

const HTTPServerComponentName = "github.com/gopherd/core/component/httpserver"
// HTTPServerComponentName is the unique identifier for the HTTPServerComponent.
const HTTPServerComponentName = "go/httpserver"

func init() {
// Register the HTTPServerComponent implementation.
Expand All @@ -26,24 +28,42 @@ var _ HTTPServerComponent = (*httpServerComponent)(nil)

type httpServerComponent struct {
component.BaseComponent[struct {
Addr string
DefaultServeMux bool
Addr string // Addr is the address to listen on.
Block bool // Block indicates whether the Start method should block.
NewMux bool // NewMux indicates whether to create a new ServeMux.
}]
mux *http.ServeMux
server *http.Server
}

// Start implements the component.Component interface.
func (com *httpServerComponent) Start(ctx context.Context) error {
if com.Options().DefaultServeMux {
com.mux = http.DefaultServeMux
} else {
addr := com.Options().Addr
if addr == "" {
addr = ":http"
}
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
if com.Options().NewMux {
com.mux = http.NewServeMux()
} else {
com.mux = http.DefaultServeMux
}
com.server = &http.Server{Addr: addr, Handler: com.mux}
if com.Options().Block {
com.Logger().Info("http server started", "addr", addr)
return com.server.Serve(ln)
}
com.server = &http.Server{Addr: com.Options().Addr, Handler: com.mux}
go com.server.ListenAndServe()
go func() {
com.Logger().Info("http server started", "addr", addr)
com.server.Serve(ln)
}()
return nil
}

// Shutdown implements the component.Component interface.
func (com *httpServerComponent) Shutdown(ctx context.Context) error {
return com.server.Shutdown(ctx)
}
Expand Down
25 changes: 22 additions & 3 deletions component/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,29 @@ import (

// Config defines the configuration structure for creating a component.
type Config struct {
Name string
UUID string `json:",omitempty"`
Refs raw.Object `json:",omitempty"`
// Name is the component name. It's required.
Name string

// UUID is the unique identifier for the component. It can be empty.
UUID string `json:",omitempty"`

// Refs is the references to other components.
Refs raw.Object `json:",omitempty"`

// Options is the configuration options for the component.
Options raw.Object `json:",omitempty"`

// TemplateUUID determines if the UUID should be templated.
// If not set, the default value is determined by the service.
TemplateUUID *bool `json:",omitempty"`

// TemplateRefs determines if the Refs should be templated.
// If not set, the default value is determined by the service.
TemplateRefs *bool `json:",omitempty"`

// TemplateOptions determines if the Options should be templated.
// If not set, the default value is determined by the service.
TemplateOptions *bool `json:",omitempty"`
}

// Component defines the interface for a generic logic component.
Expand Down
5 changes: 0 additions & 5 deletions errkit/errno.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,3 @@ func Errno(err error) int {
}
return EUnknown
}

// Is reports whether any error in err's chain matches target.
func Is[T constraints.Integer](err error, code T) bool {
return Errno(err) == int(code)
}
26 changes: 0 additions & 26 deletions errkit/errno_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,32 +91,6 @@ func TestErrno(t *testing.T) {
}
}

func TestIs(t *testing.T) {
tests := []struct {
name string
err error
code int
expected bool
}{
{"nil error", nil, EOK, true},
{"matching code", New(42, errors.New("custom error")), 42, true},
{"non-matching code", New(42, errors.New("custom error")), 43, false},
{"wrapped matching code", fmt.Errorf("wrapped: %w", New(42, errors.New("custom error"))), 42, true},
{"non-errno error", errors.New("regular error"), EUnknown, true},
{"EOK with nil error", nil, EOK, true},
{"EOK with non-nil error", errors.New("some error"), EOK, false},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := Is(tt.err, tt.code)
if result != tt.expected {
t.Errorf("Is(%v, %d) = %v, want %v", tt.err, tt.code, result, tt.expected)
}
})
}
}

func TestErrnoMethods(t *testing.T) {
err := New(42, errors.New("test error"))

Expand Down
24 changes: 16 additions & 8 deletions operator/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ func Or[T comparable](a, b T) T {
return a
}

// OrFunc returns the result of calling new() if a is the zero value for T,
// OrFunc returns the result of calling b() if a is the zero value for T,
// otherwise returns a. It allows for lazy evaluation of the alternative value.
func OrFunc[T comparable](a T, new func() T) T {
func OrFunc[T comparable](a T, b func() T) T {
var zero T
if a == zero {
return new()
return b()
}
return a
}
Expand All @@ -33,12 +33,12 @@ func SetDefault[T comparable](a *T, b T) {
}
}

// SetDefaultFunc sets the value of a to the result of calling new() if a is
// SetDefaultFunc sets the value of a to the result of calling b() if a is
// the zero value for T.
func SetDefaultFunc[T comparable](a *T, new func() T) {
func SetDefaultFunc[T comparable](a *T, b func() T) {
var zero T
if *a == zero {
*a = new()
*a = b()
}
}

Expand All @@ -51,10 +51,18 @@ func Ternary[T any](condition bool, a, b T) T {
return b
}

// TernaryFunc returns the result of calling a() if condition is true,
// TernaryFunc returns the a if condition is true, otherwise returns the result of calling b().
func TernaryFunc[T any](condition bool, a T, b func() T) T {
if condition {
return a
}
return b()
}

// TernaryCall returns the result of calling a() if condition is true,
// otherwise returns the result of calling b().
// It allows for lazy evaluation of both alternatives.
func TernaryFunc[T any](condition bool, a, b func() T) T {
func TernaryCall[T any](condition bool, a, b func() T) T {
if condition {
return a()
}
Expand Down
4 changes: 2 additions & 2 deletions operator/operator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func TestTernary(t *testing.T) {
}
}

func TestTernaryFunc(t *testing.T) {
func TestTernaryCall(t *testing.T) {
aCounter, bCounter := 0, 0
aFunc := func() int {
aCounter++
Expand All @@ -117,7 +117,7 @@ func TestTernaryFunc(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
aCounter, bCounter = 0, 0
if got := operator.TernaryFunc(tt.condition, aFunc, bFunc); got != tt.wantResult {
if got := operator.TernaryCall(tt.condition, aFunc, bFunc); got != tt.wantResult {
t.Errorf("TernaryFunc(%v, aFunc, bFunc) = %v, want %v", tt.condition, got, tt.wantResult)
}
if aCounter != tt.wantACalls {
Expand Down
9 changes: 5 additions & 4 deletions service/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"strings"

"github.com/gopherd/core/component"
"github.com/gopherd/core/operator"
"github.com/gopherd/core/text/templateutil"
)

Expand Down Expand Up @@ -84,7 +85,7 @@ func (c *Config[T]) loadFromHTTP(source string) (io.ReadCloser, error) {

// processTemplate processes the UUID, Refs, and Options fields of each component.Config
// as text/template templates, using c.Context as the template context.
func (c *Config[T]) processTemplate(source string) error {
func (c *Config[T]) processTemplate(enableTemplate bool, source string) error {
if source == "-" {
source = ""
}
Expand All @@ -97,23 +98,23 @@ func (c *Config[T]) processTemplate(source string) error {
identifier += "#" + com.UUID
}
sourcePrefix := fmt.Sprintf("%s[%s].", source, identifier)
if com.UUID != "" {
if operator.Ternary(com.TemplateUUID == nil, enableTemplate, *com.TemplateUUID) && com.UUID != "" {
new, err := templateutil.Execute(sourcePrefix+"UUID", com.UUID, c.Context, option)
if err != nil {
return err
}
com.UUID = new
}

if com.Refs.Len() > 0 {
if operator.Ternary(com.TemplateRefs == nil, enableTemplate, *com.TemplateRefs) && com.Refs.Len() > 0 {
new, err := templateutil.Execute(sourcePrefix+"Refs", com.Refs.String(), c.Context, option)
if err != nil {
return err
}
com.Refs.SetString(new)
}

if com.Options.Len() > 0 {
if operator.Ternary(com.TemplateOptions == nil, enableTemplate, *com.TemplateOptions) && com.Options.Len() > 0 {
new, err := templateutil.Execute(sourcePrefix+"Options", com.Options.String(), c.Context, option)
if err != nil {
return err
Expand Down
9 changes: 4 additions & 5 deletions service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,8 @@ func (s *BaseService[T]) setupConfig() error {
if err := s.config.load(s.flags.source); err != nil {
return err
}
if s.flags.enableTemplate {
if err := s.config.processTemplate(s.flags.source); err != nil {
return err
}
if err := s.config.processTemplate(s.flags.enableTemplate, s.flags.source); err != nil {
return err
}
return nil
}
Expand All @@ -157,7 +155,8 @@ func (s *BaseService[T]) Init(ctx context.Context) error {

if s.flags.printConfig {
if err != nil {
return err
fmt.Fprintf(os.Stderr, "Config test failed: %v\n", err)
return errkit.NewExitError(2, err.Error())
}
s.config.output()
return errkit.NewExitError(0)
Expand Down

0 comments on commit 17a2144

Please sign in to comment.