From 1ee5e07cd7e892d72864ec919cad106bdcbe6c98 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Mon, 28 Apr 2025 19:32:34 +0800 Subject: [PATCH 1/3] Docs (gs): Add README.md for Bookman and Startup projects --- gs/examples/bookman/README.md | 77 ++++++++++++++++++++++++++++++++ gs/examples/bookman/README_CN.md | 76 +++++++++++++++++++++++++++++++ gs/examples/startup/README.md | 61 +++++++++++++++++++++++++ gs/examples/startup/README_CN.md | 61 +++++++++++++++++++++++++ gs/internal/gs/gs.go | 14 +++--- gs/internal/gs_app/boot.go | 5 +++ gs/internal/gs_arg/arg.go | 4 +- gs/internal/gs_bean/bean.go | 4 +- 8 files changed, 291 insertions(+), 11 deletions(-) create mode 100644 gs/examples/bookman/README_CN.md create mode 100644 gs/examples/startup/README_CN.md diff --git a/gs/examples/bookman/README.md b/gs/examples/bookman/README.md index e69de29b..e11d631e 100644 --- a/gs/examples/bookman/README.md +++ b/gs/examples/bookman/README.md @@ -0,0 +1,77 @@ +# BookMan + +[中文](README_CN.md) + +## 1. Directory Structure + +```text +conf/ Configuration files +log/ Log files +public/ Static files +src/ Source code + app/ Startup phase files + bootstrap/ Bootstrap files + common/ Common modules for startup + handlers/ Startup component handlers + log/ Logging component + httpsvr/ HTTP server module + controller/ Controller modules + biz/ Business logic modules + job/ Background job modules + service/ Business service modules + dao/ Data access layer + idl/ Interface definition files + http/ HTTP service interfaces + proto/ Generated protocol code + sdk/ Wrapped SDK modules +``` + +**Directory Structure Features**: + +- **Modular design** with clear separation of responsibilities. +- **Classic structure** for easy development, management, and scalability. +- **Maintainability** supporting continuous iteration for large-scale applications. + +## 2. Functionality Overview + +### 2.1 Bootstrap Phase Configuration Management + +- Fetch configuration files remotely and save them locally. +- Register configuration refresh beans during the startup phase. +- Related file: `src/app/bootstrap/bootstrap.go` + +### 2.2 Logging Component Initialization + +- Load and parse local configuration files during the startup phase. +- Create logging components based on the configuration. +- Related file: `src/app/common/handlers/log/log.go` + +### 2.3 HTTP Server Initialization + +- Create an HTTP server during the startup phase. +- Register HTTP service routes. +- Related file: `src/app/common/httpsvr/httpsvr.go` + +### 2.4 Controller Grouping and Management + +- Group controller methods based on functionality. +- Independently inject and manage each sub-controller. +- Related files: + - `src/app/controller/controller.go` + - `src/app/controller/controller-book.go` + +### 2.5 Dynamic Configuration Refresh + +- Support dynamic configuration refresh at runtime. +- Related file: `src/biz/service/book_service/book_service.go` + +### 2.6 Graceful Shutdown of Background Jobs + +- Ensure background tasks shut down gracefully, preserving data integrity and releasing resources properly. +- Related file: `src/biz/job/job.go` + +## 3. Summary + +This project follows modular, clear, maintainable, and extensible design principles, making it suitable for the +development needs of medium to large-scale systems. It implements a complete and robust architecture with modules for +bootstrapping, logging management, HTTP services, dynamic configuration refreshing, and background job handling. \ No newline at end of file diff --git a/gs/examples/bookman/README_CN.md b/gs/examples/bookman/README_CN.md new file mode 100644 index 00000000..474f8bf3 --- /dev/null +++ b/gs/examples/bookman/README_CN.md @@ -0,0 +1,76 @@ +# BookMan + +[English](README.md) + +## 一、目录结构 + +```text +conf/ 配置文件目录 +log/ 日志文件目录 +public/ 静态文件目录 +src/ 源文件目录 + app/ 启动阶段文件 + bootstrap/ 引导阶段文件 + common/ 启动阶段公共模块 + handlers/ 启动阶段组件 + log/ 日志组件 + httpsvr/ HTTP 服务模块 + controller/ 控制器模块 + biz/ 业务逻辑模块 + job/ 后台任务模块 + service/ 业务服务模块 + dao/ 数据访问层 + idl/ 接口描述文件 + http/ HTTP 服务接口 + proto/ 生成的协议代码 + sdk/ 封装的 SDK 文件 +``` + +**目录结构特点**: + +- **模块化设计**,清晰划分各层职责。 +- **经典结构**,便于开发、管理和扩展。 +- **易于维护**,支持大规模应用持续迭代。 + +## 二、功能描述 + +### 2.1 引导阶段配置管理 + +- 从远程拉取配置文件并保存至本地。 +- 向启动阶段注册配置刷新的 Bean。 +- 相关文件:`src/app/bootstrap/bootstrap.go` + +### 2.2 日志组件初始化 + +- 启动阶段读取并解析本地配置。 +- 根据配置创建日志组件。 +- 相关文件:`src/app/common/handlers/log/log.go` + +### 2.3 HTTP 服务器启动 + +- 启动阶段创建 HTTP 服务器。 +- 注册 HTTP 服务路由。 +- 相关文件:`src/app/common/httpsvr/httpsvr.go` + +### 2.4 控制器功能分组管理 + +- 根据功能对 Controller 方法进行分组。 +- 每个子 Controller 独立注入和管理。 +- 相关文件: + - `src/app/controller/controller.go` + - `src/app/controller/controller-book.go` + +### 2.5 动态配置刷新 + +- 支持运行时动态刷新配置。 +- 相关文件:`src/biz/service/book_service/book_service.go` + +### 2.6 后台任务优雅退出 + +- 后台任务支持优雅停止,保证数据安全和资源释放。 +- 相关文件:`src/biz/job/job.go` + +## 三、总结 + +本项目遵循模块化、清晰、可维护、可扩展的设计原则,适合中大型系统的开发需求。通过引导配置、日志管理、HTTP +服务、动态刷新、后台任务管理等功能模块,实现了完整、健壮的应用架构。 \ No newline at end of file diff --git a/gs/examples/startup/README.md b/gs/examples/startup/README.md index e69de29b..68e8fe2e 100644 --- a/gs/examples/startup/README.md +++ b/gs/examples/startup/README.md @@ -0,0 +1,61 @@ +# Startup + +[中文](README_CN.md) + +This project, though small in size, showcases the essential capabilities of **Go-Spring**. + +## Features + +### 1. Layered Configuration + +Configurations can be loaded from various sources including: + +- `sysconf` +- Local files +- Remote files +- Command-line arguments +- Environment variables + +### 2. Property Binding + +Configurations from files, command-line arguments, or environment variables can be bound directly to struct fields. + +```go +type Service struct { + StartTime time.Time `value:"${start-time}"` +} +``` + +### 3. Dependency Injection + +Go-Spring automatically organizes dependencies between beans and injects them at runtime. + +```go +gs.Provide(func (s *Service) *http.ServeMux { + http.HandleFunc("/echo", s.Echo) + http.HandleFunc("/refresh", s.Refresh) + return http.DefaultServeMux +}) +``` + +### 4. Dynamic Configuration Refresh + +Configurations can be refreshed at runtime without restarting the application, enabling real-time updates. + +```go +type Service struct { + RefreshTime gs.Dync[time.Time] `value:"${refresh-time}"` +} +``` + +### 5. Route Registration + +Supports HTTP server setup with customizable routing. + +```go +gs.Provide(func (s *Service) *http.ServeMux { + http.HandleFunc("/echo", s.Echo) + http.HandleFunc("/refresh", s.Refresh) + return http.DefaultServeMux +}) +``` diff --git a/gs/examples/startup/README_CN.md b/gs/examples/startup/README_CN.md new file mode 100644 index 00000000..4ef2e4e3 --- /dev/null +++ b/gs/examples/startup/README_CN.md @@ -0,0 +1,61 @@ +# Startup + +[English](README.md) + +本项目虽小,但展示了 **Go-Spring** 最核心的功能。 + +## 功能介绍 + +### 1. 分层配置 + +支持从多种来源加载配置,包括: + +- `sysconf` +- 本地文件 +- 远程文件 +- 命令行参数 +- 环境变量 + +### 2. 属性绑定 + +可以将文件、命令行参数或环境变量中的配置,直接绑定到结构体字段。 + +```go +type Service struct { + StartTime time.Time `value:"${start-time}"` +} +``` + +### 3. 依赖注入 + +Go-Spring 能自动组织 Bean 之间的依赖关系,在运行时完成注入。 + +```go +gs.Provide(func (s *Service) *http.ServeMux { + http.HandleFunc("/echo", s.Echo) + http.HandleFunc("/refresh", s.Refresh) + return http.DefaultServeMux +}) +``` + +### 4. 配置刷新 + +支持配置在运行时动态刷新,无需重启应用,即可应用最新配置。 + +```go +type Service struct { + RefreshTime gs.Dync[time.Time] `value:"${refresh-time}"` +} +``` + +### 5. 路由注册 + +支持 HTTP 服务器,且可以灵活定制所需路由。 + +```go +gs.Provide(func (s *Service) *http.ServeMux { + http.HandleFunc("/echo", s.Echo) + http.HandleFunc("/refresh", s.Refresh) + return http.DefaultServeMux +}) +``` diff --git a/gs/internal/gs/gs.go b/gs/internal/gs/gs.go index ddf26d7b..aeefb136 100644 --- a/gs/internal/gs/gs.go +++ b/gs/internal/gs/gs.go @@ -94,8 +94,8 @@ type Condition interface { // CondBean represents a bean with Name and Type. type CondBean interface { - Name() string - Type() reflect.Type + Name() string // Name of the bean + Type() reflect.Type // Type of the bean } // CondContext defines methods for the IoC container used by conditions. @@ -164,8 +164,8 @@ type BeanMock struct { // BeanID represents the unique identifier for a bean. type BeanID struct { - Type reflect.Type - Name string + Type reflect.Type // Type of the bean + Name string // Name of the bean } // BeanInitFunc defines the prototype for initialization functions. @@ -192,7 +192,7 @@ type BeanRegistration interface { SetDestroy(fn BeanDestroyFunc) SetInitMethod(method string) SetDestroyMethod(method string) - SetCondition(c ...Condition) + SetCondition(conditions ...Condition) SetDependsOn(selectors ...BeanSelector) SetExport(exports ...reflect.Type) SetConfiguration(c ...Configuration) @@ -251,8 +251,8 @@ func (d *beanBuilder[T]) DestroyMethod(method string) *T { } // Condition sets the conditions for the bean. -func (d *beanBuilder[T]) Condition(c ...Condition) *T { - d.b.SetCondition(c...) +func (d *beanBuilder[T]) Condition(conditions ...Condition) *T { + d.b.SetCondition(conditions...) return *(**T)(unsafe.Pointer(&d)) } diff --git a/gs/internal/gs_app/boot.go b/gs/internal/gs_app/boot.go index 16d55c92..7aefb78e 100644 --- a/gs/internal/gs_app/boot.go +++ b/gs/internal/gs_app/boot.go @@ -35,10 +35,15 @@ func (f funcRunner) Run() error { // Boot defines the interface for application bootstrapping. type Boot interface { + // Config returns the boot configuration. Config() *gs_conf.BootConfig + // Object registers an object bean. Object(i interface{}) *gs.RegisteredBean + // Provide registers a bean using a constructor function. Provide(ctor interface{}, args ...gs.Arg) *gs.RegisteredBean + // Register registers a BeanDefinition instance. Register(bd *gs.BeanDefinition) *gs.RegisteredBean + // FuncRunner creates a Runner from a function. FuncRunner(fn func() error) *gs.RegisteredBean } diff --git a/gs/internal/gs_arg/arg.go b/gs/internal/gs_arg/arg.go index 2f05e7d5..d8447a6a 100644 --- a/gs/internal/gs_arg/arg.go +++ b/gs/internal/gs_arg/arg.go @@ -278,8 +278,8 @@ func (arg *BindArg) SetFileLine(file string, line int) { } // Condition adds pre-execution conditions to the binding. -func (arg *BindArg) Condition(c ...gs.Condition) *BindArg { - arg.conditions = append(arg.conditions, c...) +func (arg *BindArg) Condition(conditions ...gs.Condition) *BindArg { + arg.conditions = append(arg.conditions, conditions...) return arg } diff --git a/gs/internal/gs_bean/bean.go b/gs/internal/gs_bean/bean.go index 71510844..d642fb75 100644 --- a/gs/internal/gs_bean/bean.go +++ b/gs/internal/gs_bean/bean.go @@ -129,8 +129,8 @@ func (d *BeanMetadata) Conditions() []gs.Condition { } // SetCondition adds a condition to the list of conditions for the bean. -func (d *BeanMetadata) SetCondition(c ...gs.Condition) { - d.conditions = append(d.conditions, c...) +func (d *BeanMetadata) SetCondition(conditions ...gs.Condition) { + d.conditions = append(d.conditions, conditions...) } // Configuration returns the configuration parameters for the bean. From 0be0805f0fa4d08afce35c7e3acd2747b351555b Mon Sep 17 00:00:00 2001 From: lvan100 Date: Tue, 29 Apr 2025 07:47:05 +0800 Subject: [PATCH 2/3] refactor(conf): Simplify environment variable configuration --- gs/examples/startup/README.md | 2 +- gs/examples/startup/README_CN.md | 2 +- gs/internal/gs_bean/bean.go | 17 ++------ gs/internal/gs_conf/env.go | 66 +------------------------------- gs/internal/gs_conf/env_test.go | 38 ------------------ gs/internal/gs_dync/dync.go | 14 ++----- 6 files changed, 11 insertions(+), 128 deletions(-) diff --git a/gs/examples/startup/README.md b/gs/examples/startup/README.md index 68e8fe2e..cd6a1d62 100644 --- a/gs/examples/startup/README.md +++ b/gs/examples/startup/README.md @@ -13,8 +13,8 @@ Configurations can be loaded from various sources including: - `sysconf` - Local files - Remote files -- Command-line arguments - Environment variables +- Command-line arguments ### 2. Property Binding diff --git a/gs/examples/startup/README_CN.md b/gs/examples/startup/README_CN.md index 4ef2e4e3..e4dfc98c 100644 --- a/gs/examples/startup/README_CN.md +++ b/gs/examples/startup/README_CN.md @@ -13,8 +13,8 @@ - `sysconf` - 本地文件 - 远程文件 -- 命令行参数 - 环境变量 +- 命令行参数 ### 2. 属性绑定 diff --git a/gs/internal/gs_bean/bean.go b/gs/internal/gs_bean/bean.go index d642fb75..040bf312 100644 --- a/gs/internal/gs_bean/bean.go +++ b/gs/internal/gs_bean/bean.go @@ -309,14 +309,7 @@ func (d *BeanDefinition) SetExport(exports ...reflect.Type) { if !d.Type().Implements(t) { panic(fmt.Sprintf("doesn't implement interface %s", t)) } - exported := false - for _, export := range d.exports { - if t == export { - exported = true - break - } - } - if exported { + if slices.Contains(d.exports, t) { continue } d.exports = append(d.exports, t) @@ -331,11 +324,9 @@ func (d *BeanDefinition) OnProfiles(profiles string) { return false, nil } ss := strings.Split(strings.TrimSpace(profiles), ",") - for s := range slices.Values(strings.Split(val, ",")) { - for _, x := range ss { - if s == x { - return true, nil - } + for _, s := range strings.Split(val, ",") { + if slices.Contains(ss, s) { + return true, nil } } return false, nil diff --git a/gs/internal/gs_conf/env.go b/gs/internal/gs_conf/env.go index 12002555..52a2da69 100644 --- a/gs/internal/gs_conf/env.go +++ b/gs/internal/gs_conf/env.go @@ -18,17 +18,11 @@ package gs_conf import ( "os" - "regexp" "strings" "github.com/go-spring/spring-core/conf" ) -const ( - IncludeEnvPatterns = "INCLUDE_ENV_PATTERNS" - ExcludeEnvPatterns = "EXCLUDE_ENV_PATTERNS" -) - // Environment represents the environment configuration. type Environment struct{} @@ -37,18 +31,6 @@ func NewEnvironment() *Environment { return &Environment{} } -// lookupEnv searches for an environment variable by key in the environ slice. -func lookupEnv(environ []string, key string) (value string, found bool) { - key = strings.TrimSpace(key) + "=" - for _, s := range environ { - if strings.HasPrefix(s, key) { - v := strings.TrimPrefix(s, key) - return strings.TrimSpace(v), true - } - } - return "", false -} - // CopyTo add environment variables that matches IncludeEnvPatterns and // exclude environment variables that matches ExcludeEnvPatterns. func (c *Environment) CopyTo(p *conf.MutableProperties) error { @@ -56,46 +38,6 @@ func (c *Environment) CopyTo(p *conf.MutableProperties) error { if len(environ) == 0 { return nil } - - toRex := func(patterns []string) ([]*regexp.Regexp, error) { - var rex []*regexp.Regexp - for _, v := range patterns { - exp, err := regexp.Compile(v) - if err != nil { - return nil, err - } - rex = append(rex, exp) - } - return rex, nil - } - - includes := []string{".*"} - if s, ok := lookupEnv(environ, IncludeEnvPatterns); ok { - includes = strings.Split(s, ",") - } - includeRex, err := toRex(includes) - if err != nil { - return err - } - - var excludes []string - if s, ok := lookupEnv(environ, ExcludeEnvPatterns); ok { - excludes = strings.Split(s, ",") - } - excludeRex, err := toRex(excludes) - if err != nil { - return err - } - - matches := func(rex []*regexp.Regexp, s string) bool { - for _, r := range rex { - if r.MatchString(s) { - return true - } - } - return false - } - const prefix = "GS_" for _, env := range environ { ss := strings.SplitN(env, "=", 2) @@ -103,19 +45,15 @@ func (c *Environment) CopyTo(p *conf.MutableProperties) error { if len(ss) > 1 { v = ss[1] } - var propKey string if strings.HasPrefix(k, prefix) { propKey = strings.TrimPrefix(k, prefix) propKey = strings.ReplaceAll(propKey, "_", ".") propKey = strings.ToLower(propKey) - } else if matches(includeRex, k) && !matches(excludeRex, k) { - propKey = k } else { - continue + propKey = k } - - if err = p.Set(propKey, v); err != nil { + if err := p.Set(propKey, v); err != nil { return err } } diff --git a/gs/internal/gs_conf/env_test.go b/gs/internal/gs_conf/env_test.go index d7499cb3..da6073db 100644 --- a/gs/internal/gs_conf/env_test.go +++ b/gs/internal/gs_conf/env_test.go @@ -48,44 +48,6 @@ func TestEnvironment(t *testing.T) { assert.That(t, props.Get("API_KEY")).Equal("key123") }) - t.Run("custom patterns", func(t *testing.T) { - _ = os.Setenv(IncludeEnvPatterns, "^TEST_") - _ = os.Setenv(ExcludeEnvPatterns, "^TEST_INTERNAL") - _ = os.Setenv("TEST_PUBLIC", "yes") - _ = os.Setenv("TEST_INTERNAL", "no") - defer func() { - _ = os.Unsetenv(IncludeEnvPatterns) - _ = os.Unsetenv(ExcludeEnvPatterns) - _ = os.Unsetenv("TEST_PUBLIC") - _ = os.Unsetenv("TEST_INTERNAL") - }() - props := conf.New() - err := NewEnvironment().CopyTo(props) - assert.Nil(t, err) - assert.That(t, props.Get("TEST_PUBLIC")).Equal("yes") - assert.False(t, props.Has("TEST_INTERNAL")) - }) - - t.Run("invalid regex - include", func(t *testing.T) { - _ = os.Setenv(IncludeEnvPatterns, "[invalid") - defer func() { - _ = os.Unsetenv(IncludeEnvPatterns) - }() - props := conf.New() - err := NewEnvironment().CopyTo(props) - assert.ThatError(t, err).Matches("error parsing regexp") - }) - - t.Run("invalid regex - exclude", func(t *testing.T) { - _ = os.Setenv(ExcludeEnvPatterns, "[invalid") - defer func() { - _ = os.Unsetenv(ExcludeEnvPatterns) - }() - props := conf.New() - err := NewEnvironment().CopyTo(props) - assert.ThatError(t, err).Matches("error parsing regexp") - }) - t.Run("set return error", func(t *testing.T) { _ = os.Setenv("GS_DB_HOST", "db1") defer func() { diff --git a/gs/internal/gs_dync/dync.go b/gs/internal/gs_dync/dync.go index 886e16ea..ea628976 100644 --- a/gs/internal/gs_dync/dync.go +++ b/gs/internal/gs_dync/dync.go @@ -19,12 +19,12 @@ package gs_dync import ( "encoding/json" "reflect" - "sort" "strings" "sync" "sync/atomic" "github.com/go-spring/spring-core/conf" + "github.com/go-spring/spring-core/util" ) // refreshable represents an object that can be dynamically refreshed. @@ -165,11 +165,7 @@ func (p *Properties) Refresh(prop conf.Properties) (err error) { changes[k] = struct{}{} } - keys := make([]string, 0, len(changes)) - for k := range changes { - keys = append(keys, k) - } - sort.Strings(keys) + keys := util.OrderedMapKeys(changes) return p.refreshKeys(keys) } @@ -193,11 +189,7 @@ func (p *Properties) refreshKeys(keys []string) (err error) { // Sort and collect objects that need updating. updateObjects := make([]*refreshObject, 0, len(updateIndexes)) { - ints := make([]int, 0, len(updateIndexes)) - for k := range updateIndexes { - ints = append(ints, k) - } - sort.Ints(ints) + ints := util.OrderedMapKeys(updateIndexes) for _, k := range ints { updateObjects = append(updateObjects, updateIndexes[k]) } From d161eb180d6b316e589ad70a94c0bc654069d364 Mon Sep 17 00:00:00 2001 From: lvan100 Date: Tue, 29 Apr 2025 07:56:54 +0800 Subject: [PATCH 3/3] refactor: Replace interface{} with any --- conf/bind.go | 4 +- conf/bind_test.go | 34 +- conf/conf.go | 17 +- conf/conf_test.go | 24 +- conf/expr.go | 11 +- conf/expr_test.go | 6 +- conf/reader/json/json.go | 4 +- conf/reader/json/json_test.go | 2 +- conf/reader/prop/prop.go | 4 +- conf/reader/prop/prop_test.go | 8 +- conf/reader/toml/toml.go | 2 +- conf/reader/toml/toml_test.go | 22 +- conf/reader/yaml/yaml.go | 4 +- conf/reader/yaml/yaml_test.go | 28 +- gs/examples/bookman/.cover/cover.html | 324 +++++++----------- gs/gs.go | 14 +- gs/internal/gs/gs.go | 6 +- gs/internal/gs_app/boot.go | 8 +- gs/internal/gs_app/signal_test.go | 4 +- gs/internal/gs_arg/arg.go | 6 +- gs/internal/gs_bean/bean.go | 8 +- gs/internal/gs_cond/cond.go | 6 +- gs/internal/gs_cond/expr.go | 11 +- gs/internal/gs_conf/cmd.go | 2 +- gs/internal/gs_conf/cmd_test.go | 2 +- gs/internal/gs_conf/conf.go | 3 +- gs/internal/gs_conf/conf_test.go | 4 +- gs/internal/gs_conf/env_test.go | 2 +- gs/internal/gs_core/injecting/injecting.go | 22 +- .../gs_core/injecting/injecting_test.go | 60 ++-- gs/internal/gs_core/resolving/resolving.go | 6 +- .../gs_core/resolving/resolving_test.go | 4 +- gs/internal/gs_dync/dync.go | 2 +- gs/internal/gs_dync/dync_test.go | 28 +- gs/internal/gs_util/util.go | 6 +- gs/internal/gs_util/util_test.go | 10 +- util/errutil/errutil.go | 2 +- util/flat.go | 6 +- util/flat_test.go | 20 +- util/goutil/goutil.go | 2 +- util/goutil/goutil_test.go | 2 +- util/type_test.go | 4 +- util/value.go | 4 +- util/value_test.go | 2 +- 44 files changed, 332 insertions(+), 418 deletions(-) diff --git a/conf/bind.go b/conf/bind.go index b73baa55..e970bd35 100644 --- a/conf/bind.go +++ b/conf/bind.go @@ -124,7 +124,7 @@ func (param *BindParam) BindTag(tag string, validate reflect.StructTag) error { // Filter defines an interface for filtering configuration fields during binding. type Filter interface { - Do(i interface{}, param BindParam) (bool, error) + Do(i any, param BindParam) (bool, error) } // BindValue binds a value from properties `p` to the reflect.Value `v` of type `t` @@ -366,7 +366,7 @@ func bindStruct(p Properties, v reflect.Value, t reflect.Type, param BindParam, return fmt.Errorf("bind path=%s type=%s error: %w", param.Path, v.Type().String(), err) } - for i := 0; i < t.NumField(); i++ { + for i := range t.NumField() { ft := t.Field(i) fv := v.Field(i) diff --git a/conf/bind_test.go b/conf/bind_test.go index c3f9c4cb..f5931c21 100644 --- a/conf/bind_test.go +++ b/conf/bind_test.go @@ -35,9 +35,9 @@ func init() { conf.RegisterSplitter("PointSplitter", PointSplitter) } -type funcFilter func(i interface{}, param conf.BindParam) (bool, error) +type funcFilter func(i any, param conf.BindParam) (bool, error) -func (f funcFilter) Do(i interface{}, param conf.BindParam) (bool, error) { +func (f funcFilter) Do(i any, param conf.BindParam) (bool, error) { return f(i, param) } @@ -77,7 +77,7 @@ func TestConverter(t *testing.T) { }) t.Run("error", func(t *testing.T) { - p := conf.Map(map[string]interface{}{ + p := conf.Map(map[string]any{ "time": "2025-02-01M00:00:00", }) err := p.Bind(&s) @@ -304,7 +304,7 @@ func TestProperties_Bind(t *testing.T) { var s struct { Value int `value:"${v}" expr:"$>9"` } - err := conf.Map(map[string]interface{}{ + err := conf.Map(map[string]any{ "v": "1", }).Bind(&s) assert.ThatError(t, err).Matches("validate failed on .* for value 1") @@ -321,7 +321,7 @@ func TestProperties_Bind(t *testing.T) { var s struct { Value int `value:"${v}"` } - err := conf.Map(map[string]interface{}{ + err := conf.Map(map[string]any{ "v": "abc", }).Bind(&s) assert.ThatError(t, err).Matches("strconv.ParseInt: parsing .*: invalid syntax") @@ -331,7 +331,7 @@ func TestProperties_Bind(t *testing.T) { var s struct { Value uint `value:"${v}"` } - err := conf.Map(map[string]interface{}{ + err := conf.Map(map[string]any{ "v": "abc", }).Bind(&s) assert.ThatError(t, err).Matches("strconv.ParseUint: parsing .*: invalid syntax") @@ -341,7 +341,7 @@ func TestProperties_Bind(t *testing.T) { var s struct { Value float32 `value:"${v}"` } - err := conf.Map(map[string]interface{}{ + err := conf.Map(map[string]any{ "v": "abc", }).Bind(&s) assert.ThatError(t, err).Matches("strconv.ParseFloat: parsing .*: invalid syntax") @@ -351,7 +351,7 @@ func TestProperties_Bind(t *testing.T) { var s struct { Value bool `value:"${v}"` } - err := conf.Map(map[string]interface{}{ + err := conf.Map(map[string]any{ "v": "abc", }).Bind(&s) assert.ThatError(t, err).Matches("strconv.ParseBool: parsing .*: invalid syntax") @@ -361,8 +361,8 @@ func TestProperties_Bind(t *testing.T) { var s struct { Value []int `value:"${v}"` } - err := conf.Map(map[string]interface{}{ - "v": []interface{}{ + err := conf.Map(map[string]any{ + "v": []any{ "1", "2", "a", }, }).Bind(&s) @@ -397,8 +397,8 @@ func TestProperties_Bind(t *testing.T) { var s struct { Value map[string]int `value:"${v}"` } - err := conf.Map(map[string]interface{}{ - "v": []interface{}{ + err := conf.Map(map[string]any{ + "v": []any{ "1", "2", "3", }, }).Bind(&s) @@ -409,7 +409,7 @@ func TestProperties_Bind(t *testing.T) { var s struct { Value map[string]int `value:"${v}"` } - err := conf.Map(map[string]interface{}{ + err := conf.Map(map[string]any{ "v": "a:b,1:2", }).Bind(&s) assert.ThatError(t, err).Matches("property conflict at path v") @@ -437,7 +437,7 @@ func TestProperties_Bind(t *testing.T) { var s struct { int `value:"${v}"` } - err := conf.Map(map[string]interface{}{ + err := conf.Map(map[string]any{ "v": "123", }).Bind(&s) assert.Nil(t, err) @@ -574,7 +574,7 @@ func TestProperties_Bind(t *testing.T) { v := reflect.ValueOf(&s).Elem() err = conf.BindValue(conf.New(), v, v.Type(), param, - funcFilter(func(i interface{}, param conf.BindParam) (bool, error) { + funcFilter(func(i any, param conf.BindParam) (bool, error) { return false, nil })) assert.Nil(t, err) @@ -592,7 +592,7 @@ func TestProperties_Bind(t *testing.T) { v := reflect.ValueOf(&s).Elem() err = conf.BindValue(conf.New(), v, v.Type(), param, - funcFilter(func(i interface{}, param conf.BindParam) (bool, error) { + funcFilter(func(i any, param conf.BindParam) (bool, error) { return true, nil })) assert.Nil(t, err) @@ -610,7 +610,7 @@ func TestProperties_Bind(t *testing.T) { v := reflect.ValueOf(&s).Elem() err = conf.BindValue(conf.New(), v, v.Type(), param, - funcFilter(func(i interface{}, param conf.BindParam) (bool, error) { + funcFilter(func(i any, param conf.BindParam) (bool, error) { return false, errors.New("filter error") })) assert.ThatError(t, err).Matches("filter error") diff --git a/conf/conf.go b/conf/conf.go index 462765ba..3123d194 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -19,6 +19,7 @@ package conf import ( "errors" "fmt" + "maps" "os" "path/filepath" "reflect" @@ -37,7 +38,7 @@ import ( var ( readers = map[string]Reader{} splitters = map[string]Splitter{} - converters = map[reflect.Type]interface{}{} + converters = map[reflect.Type]any{} ) func init() { @@ -56,8 +57,8 @@ func init() { }) } -// Reader parses []byte into nested map[string]interface{}. -type Reader func(b []byte) (map[string]interface{}, error) +// Reader parses []byte into nested map[string]any. +type Reader func(b []byte) (map[string]any, error) // RegisterReader registers its Reader for some kind of file extension. func RegisterReader(r Reader, ext ...string) { @@ -99,7 +100,7 @@ type Properties interface { // Resolve resolves string that contains references. Resolve(s string) (string, error) // Bind binds properties into a value. - Bind(i interface{}, tag ...string) error + Bind(i any, tag ...string) error // CopyTo copies properties into another by override. CopyTo(out *MutableProperties) error } @@ -148,7 +149,7 @@ func Load(file string) (*MutableProperties, error) { } // Map creates *MutableProperties from map. -func Map(m map[string]interface{}) *MutableProperties { +func Map(m map[string]any) *MutableProperties { p := New() _ = p.merge(util.FlattenMap(m)) return p @@ -167,9 +168,7 @@ func (p *MutableProperties) merge(m map[string]string) error { // Data returns key-value pairs of the properties. func (p *MutableProperties) Data() map[string]string { m := make(map[string]string) - for k, v := range p.RawData() { - m[k] = v - } + maps.Copy(m, p.RawData()) return m } @@ -199,7 +198,7 @@ func (p *MutableProperties) Resolve(s string) (string, error) { // value:"${a:=b}>>splitter", 'a' is the key, 'b' is the default value, // 'splitter' is the Splitter's name when you want split string value // into []string value. -func (p *MutableProperties) Bind(i interface{}, tag ...string) error { +func (p *MutableProperties) Bind(i any, tag ...string) error { var v reflect.Value { diff --git a/conf/conf_test.go b/conf/conf_test.go index 7ba10904..2ded2119 100644 --- a/conf/conf_test.go +++ b/conf/conf_test.go @@ -59,7 +59,7 @@ func TestProperties_Load(t *testing.T) { func TestProperties_Resolve(t *testing.T) { t.Run("success - 1", func(t *testing.T) { - p := conf.Map(map[string]interface{}{ + p := conf.Map(map[string]any{ "a.b.c": []string{"3"}, }) s, err := p.Resolve("${a.b.c[0]}") @@ -68,7 +68,7 @@ func TestProperties_Resolve(t *testing.T) { }) t.Run("success - 2", func(t *testing.T) { - p := conf.Map(map[string]interface{}{ + p := conf.Map(map[string]any{ "a.b.c": []string{"3"}, }) s, err := p.Resolve("${x:=${a.b.c[0]}}") @@ -90,7 +90,7 @@ func TestProperties_Resolve(t *testing.T) { }) t.Run("syntax error - 1", func(t *testing.T) { - p := conf.Map(map[string]interface{}{ + p := conf.Map(map[string]any{ "a.b.c": []string{"3"}, }) _, err := p.Resolve("${a.b.c}") @@ -98,7 +98,7 @@ func TestProperties_Resolve(t *testing.T) { }) t.Run("syntax error - 2", func(t *testing.T) { - p := conf.Map(map[string]interface{}{ + p := conf.Map(map[string]any{ "a.b.c": []string{"3"}, }) _, err := p.Resolve("${a.b.c") @@ -106,7 +106,7 @@ func TestProperties_Resolve(t *testing.T) { }) t.Run("syntax error - 3", func(t *testing.T) { - p := conf.Map(map[string]interface{}{ + p := conf.Map(map[string]any{ "a.b.c": []string{"3"}, }) _, err := p.Resolve("${a.b.c[0]}==${a.b.c}") @@ -117,7 +117,7 @@ func TestProperties_Resolve(t *testing.T) { func TestProperties_CopyTo(t *testing.T) { t.Run("success", func(t *testing.T) { - p := conf.Map(map[string]interface{}{ + p := conf.Map(map[string]any{ "a.b.c": []string{"3"}, }) assert.That(t, p.Keys()).Equal([]string{ @@ -131,7 +131,7 @@ func TestProperties_CopyTo(t *testing.T) { "a.b.c[0]": "3", }) - s := conf.Map(map[string]interface{}{ + s := conf.Map(map[string]any{ "a.b.c": []string{"4", "5"}, }) assert.That(t, s.Keys()).Equal([]string{ @@ -156,14 +156,14 @@ func TestProperties_CopyTo(t *testing.T) { }) t.Run("error", func(t *testing.T) { - p := conf.Map(map[string]interface{}{ + p := conf.Map(map[string]any{ "a.b.c": []string{"3"}, }) assert.That(t, p.Data()).Equal(map[string]string{ "a.b.c[0]": "3", }) - s := conf.Map(map[string]interface{}{ + s := conf.Map(map[string]any{ "a.b.c": "3", }) assert.That(t, s.Get("a.b.c")).Equal("3") @@ -177,20 +177,20 @@ func BenchmarkResolve(b *testing.B) { const src = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" data := make([]byte, 2000) - for i := 0; i < len(data); i++ { + for i := range len(data) { data[i] = src[rand.Intn(len(src))] } s := string(data) b.Run("contains", func(b *testing.B) { - for i := 0; i < b.N; i++ { + for b.Loop() { _ = strings.Contains(s, "${") } }) p := conf.New() b.Run("resolve", func(b *testing.B) { - for i := 0; i < b.N; i++ { + for b.Loop() { _, _ = p.Resolve(s) } }) diff --git a/conf/expr.go b/conf/expr.go index c413bd75..e71054cd 100644 --- a/conf/expr.go +++ b/conf/expr.go @@ -18,6 +18,7 @@ package conf import ( "fmt" + "maps" "github.com/expr-lang/expr" ) @@ -27,7 +28,7 @@ import ( type ValidateFunc[T any] func(T) bool // validateFuncs holds a map of registered validation functions. -var validateFuncs = map[string]interface{}{} +var validateFuncs = map[string]any{} // RegisterValidateFunc registers a validation function with a specific name. // The function can then be used in validation expressions. @@ -38,11 +39,9 @@ func RegisterValidateFunc[T any](name string, fn ValidateFunc[T]) { // validateField validates a field using a validation expression (tag) and the field value (i). // It evaluates the expression and checks if the result is true (i.e., the validation passes). // If any error occurs during evaluation or if the validation fails, an error is returned. -func validateField(tag string, i interface{}) error { - env := map[string]interface{}{"$": i} - for k, v := range validateFuncs { - env[k] = v - } +func validateField(tag string, i any) error { + env := map[string]any{"$": i} + maps.Copy(env, validateFuncs) r, err := expr.Eval(tag, env) if err != nil { return fmt.Errorf("eval %q returns error, %w", tag, err) diff --git a/conf/expr_test.go b/conf/expr_test.go index 3737626d..9c8c0b0c 100644 --- a/conf/expr_test.go +++ b/conf/expr_test.go @@ -32,7 +32,7 @@ func TestExpr(t *testing.T) { var v struct { A int `value:"${a}" expr:"checkInt($)"` } - p := conf.Map(map[string]interface{}{ + p := conf.Map(map[string]any{ "a": 4, }) err := p.Bind(&v) @@ -44,7 +44,7 @@ func TestExpr(t *testing.T) { var v struct { A int `value:"${a}" expr:"checkInt($)"` } - p := conf.Map(map[string]interface{}{ + p := conf.Map(map[string]any{ "a": 14, }) err := p.Bind(&v) @@ -55,7 +55,7 @@ func TestExpr(t *testing.T) { var v struct { A int `value:"${a}" expr:"$+$"` } - p := conf.Map(map[string]interface{}{ + p := conf.Map(map[string]any{ "a": 4, }) err := p.Bind(&v) diff --git a/conf/reader/json/json.go b/conf/reader/json/json.go index 79221025..654613c4 100644 --- a/conf/reader/json/json.go +++ b/conf/reader/json/json.go @@ -21,8 +21,8 @@ import ( ) // Read parses []byte in the json format into map. -func Read(b []byte) (map[string]interface{}, error) { - var ret map[string]interface{} +func Read(b []byte) (map[string]any, error) { + var ret map[string]any err := json.Unmarshal(b, &ret) if err != nil { return nil, err diff --git a/conf/reader/json/json_test.go b/conf/reader/json/json_test.go index d44bd0d0..cd9ea330 100644 --- a/conf/reader/json/json_test.go +++ b/conf/reader/json/json_test.go @@ -40,7 +40,7 @@ func TestRead(t *testing.T) { "time": "2018-02-17T15:02:31+08:00" }`)) assert.Nil(t, err) - assert.That(t, r).Equal(map[string]interface{}{ + assert.That(t, r).Equal(map[string]any{ "empty": "", "bool": false, "int": float64(3), diff --git a/conf/reader/prop/prop.go b/conf/reader/prop/prop.go index 5c72501f..c7bc5cbf 100644 --- a/conf/reader/prop/prop.go +++ b/conf/reader/prop/prop.go @@ -19,13 +19,13 @@ package prop import "github.com/magiconair/properties" // Read parses []byte in the properties format into map. -func Read(b []byte) (map[string]interface{}, error) { +func Read(b []byte) (map[string]any, error) { p := properties.NewProperties() p.DisableExpansion = true _ = p.Load(b, properties.UTF8) // always no error - ret := make(map[string]interface{}) + ret := make(map[string]any) for k, v := range p.Map() { ret[k] = v } diff --git a/conf/reader/prop/prop_test.go b/conf/reader/prop/prop_test.go index 499e0f08..53e859bc 100644 --- a/conf/reader/prop/prop_test.go +++ b/conf/reader/prop/prop_test.go @@ -35,7 +35,7 @@ func TestRead(t *testing.T) { time=2018-02-17T15:02:31+08:00 `)) assert.Nil(t, err) - assert.That(t, r).Equal(map[string]interface{}{ + assert.That(t, r).Equal(map[string]any{ "empty": "", "bool": "false", "int": "3", @@ -54,7 +54,7 @@ func TestRead(t *testing.T) { map.string=hello `)) assert.Nil(t, err) - assert.That(t, r).Equal(map[string]interface{}{ + assert.That(t, r).Equal(map[string]any{ "map.bool": "false", "map.int": "3", "map.float": "3.0", @@ -74,7 +74,7 @@ func TestRead(t *testing.T) { array[1].string=hello `)) assert.Nil(t, err) - assert.That(t, r).Equal(map[string]interface{}{ + assert.That(t, r).Equal(map[string]any{ "array[0].bool": "false", "array[0].int": "3", "array[0].float": "3.0", @@ -99,7 +99,7 @@ func TestRead(t *testing.T) { map.k2.string=hello `)) assert.Nil(t, err) - assert.That(t, r).Equal(map[string]interface{}{ + assert.That(t, r).Equal(map[string]any{ "map.k1.bool": "false", "map.k1.int": "3", "map.k1.float": "3.0", diff --git a/conf/reader/toml/toml.go b/conf/reader/toml/toml.go index c1e60032..e290dd06 100644 --- a/conf/reader/toml/toml.go +++ b/conf/reader/toml/toml.go @@ -21,7 +21,7 @@ import ( ) // Read parses []byte in the toml format into map. -func Read(b []byte) (map[string]interface{}, error) { +func Read(b []byte) (map[string]any, error) { tree, err := toml.LoadBytes(b) if err != nil { return nil, err diff --git a/conf/reader/toml/toml_test.go b/conf/reader/toml/toml_test.go index 978e99d9..a810ca93 100644 --- a/conf/reader/toml/toml_test.go +++ b/conf/reader/toml/toml_test.go @@ -41,7 +41,7 @@ func TestRead(t *testing.T) { time="2018-02-17T15:02:31+08:00" `)) assert.Nil(t, err) - assert.That(t, r).Equal(map[string]interface{}{ + assert.That(t, r).Equal(map[string]any{ "empty": "", "bool": false, "int": int64(3), @@ -62,8 +62,8 @@ func TestRead(t *testing.T) { string="hello" `)) assert.Nil(t, err) - assert.That(t, r).Equal(map[string]interface{}{ - "map": map[string]interface{}{ + assert.That(t, r).Equal(map[string]any{ + "map": map[string]any{ "bool": false, "float": 3.0, "int": int64(3), @@ -87,15 +87,15 @@ func TestRead(t *testing.T) { string="hello" `)) assert.Nil(t, err) - assert.That(t, r).Equal(map[string]interface{}{ - "array": []interface{}{ - map[string]interface{}{ + assert.That(t, r).Equal(map[string]any{ + "array": []any{ + map[string]any{ "bool": false, "int": int64(3), "float": 3.0, "string": "hello", }, - map[string]interface{}{ + map[string]any{ "bool": true, "int": int64(20), "float": 0.2, @@ -120,15 +120,15 @@ func TestRead(t *testing.T) { string="hello" `)) assert.Nil(t, err) - assert.That(t, r).Equal(map[string]interface{}{ - "map": map[string]interface{}{ - "k1": map[string]interface{}{ + assert.That(t, r).Equal(map[string]any{ + "map": map[string]any{ + "k1": map[string]any{ "bool": false, "int": int64(3), "float": 3.0, "string": "hello", }, - "k2": map[string]interface{}{ + "k2": map[string]any{ "bool": true, "int": int64(20), "float": 0.2, diff --git a/conf/reader/yaml/yaml.go b/conf/reader/yaml/yaml.go index bf07f230..3a1507b0 100644 --- a/conf/reader/yaml/yaml.go +++ b/conf/reader/yaml/yaml.go @@ -21,8 +21,8 @@ import ( ) // Read parses []byte in the yaml format into map. -func Read(b []byte) (map[string]interface{}, error) { - ret := make(map[string]interface{}) +func Read(b []byte) (map[string]any, error) { + ret := make(map[string]any) err := yaml.Unmarshal(b, &ret) if err != nil { return nil, err diff --git a/conf/reader/yaml/yaml_test.go b/conf/reader/yaml/yaml_test.go index d7f9bb8e..229e9e99 100644 --- a/conf/reader/yaml/yaml_test.go +++ b/conf/reader/yaml/yaml_test.go @@ -44,7 +44,7 @@ func TestRead(t *testing.T) { str = strings.ReplaceAll(str, "\t", " ") r, err := Read([]byte(str)) assert.Nil(t, err) - assert.That(t, r).Equal(map[string]interface{}{ + assert.That(t, r).Equal(map[string]any{ "empty": "", "bool": false, "int": 3, @@ -67,8 +67,8 @@ func TestRead(t *testing.T) { str = strings.ReplaceAll(str, "\t", " ") r, err := Read([]byte(str)) assert.Nil(t, err) - assert.That(t, r).Equal(map[string]interface{}{ - "map": map[interface{}]interface{}{ + assert.That(t, r).Equal(map[string]any{ + "map": map[any]any{ "bool": false, "float": 3.0, "int": 3, @@ -94,15 +94,15 @@ func TestRead(t *testing.T) { str = strings.ReplaceAll(str, "\t", " ") r, err := Read([]byte(str)) assert.Nil(t, err) - assert.That(t, r).Equal(map[string]interface{}{ - "array": []interface{}{ - map[interface{}]interface{}{ + assert.That(t, r).Equal(map[string]any{ + "array": []any{ + map[any]any{ "bool": false, "int": 3, "float": 3.0, "string": "hello", }, - map[interface{}]interface{}{ + map[any]any{ "bool": true, "int": 20, "float": 0.2, @@ -129,15 +129,15 @@ func TestRead(t *testing.T) { str = strings.ReplaceAll(str, "\t", " ") r, err := Read([]byte(str)) assert.Nil(t, err) - assert.That(t, r).Equal(map[string]interface{}{ - "map": map[interface{}]interface{}{ - "k1": map[interface{}]interface{}{ + assert.That(t, r).Equal(map[string]any{ + "map": map[any]any{ + "k1": map[any]any{ "bool": false, "int": 3, "float": 3.0, "string": "hello", }, - "k2": map[interface{}]interface{}{ + "k2": map[any]any{ "bool": true, "int": 20, "float": 0.2, @@ -155,9 +155,9 @@ func TestRead(t *testing.T) { str = strings.ReplaceAll(str, "\t", " ") r, err := Read([]byte(str)) assert.Nil(t, err) - assert.That(t, r).Equal(map[string]interface{}{ - "array": []interface{}{}, - "map": map[interface{}]interface{}{}, + assert.That(t, r).Equal(map[string]any{ + "array": []any{}, + "map": map[any]any{}, }) }) } diff --git a/gs/examples/bookman/.cover/cover.html b/gs/examples/bookman/.cover/cover.html index ddc00fcd..3ad5e775 100644 --- a/gs/examples/bookman/.cover/cover.html +++ b/gs/examples/bookman/.cover/cover.html @@ -59,7 +59,7 @@ - + @@ -109,7 +109,7 @@ - + @@ -117,17 +117,17 @@ - + - + - + - + @@ -367,7 +367,7 @@ // Filter defines an interface for filtering configuration fields during binding. type Filter interface { - Do(i interface{}, param BindParam) (bool, error) + Do(i any, param BindParam) (bool, error) } // BindValue binds a value from properties `p` to the reflect.Value `v` of type `t` @@ -609,7 +609,7 @@ return fmt.Errorf("bind path=%s type=%s error: %w", param.Path, v.Type().String(), err) } - for i := 0; i < t.NumField(); i++ { + for i := range t.NumField() { ft := t.Field(i) fv := v.Field(i) @@ -742,6 +742,7 @@ import ( "errors" "fmt" + "maps" "os" "path/filepath" "reflect" @@ -760,7 +761,7 @@ var ( readers = map[string]Reader{} splitters = map[string]Splitter{} - converters = map[reflect.Type]interface{}{} + converters = map[reflect.Type]any{} ) func init() { @@ -779,8 +780,8 @@ }) } -// Reader parses []byte into nested map[string]interface{}. -type Reader func(b []byte) (map[string]interface{}, error) +// Reader parses []byte into nested map[string]any. +type Reader func(b []byte) (map[string]any, error) // RegisterReader registers its Reader for some kind of file extension. func RegisterReader(r Reader, ext ...string) { @@ -822,7 +823,7 @@ // Resolve resolves string that contains references. Resolve(s string) (string, error) // Bind binds properties into a value. - Bind(i interface{}, tag ...string) error + Bind(i any, tag ...string) error // CopyTo copies properties into another by override. CopyTo(out *MutableProperties) error } @@ -871,7 +872,7 @@ } // Map creates *MutableProperties from map. -func Map(m map[string]interface{}) *MutableProperties { +func Map(m map[string]any) *MutableProperties { p := New() _ = p.merge(util.FlattenMap(m)) return p @@ -890,11 +891,9 @@ // Data returns key-value pairs of the properties. func (p *MutableProperties) Data() map[string]string { m := make(map[string]string) - for k, v := range p.RawData() { - m[k] = v - } - return m -} + maps.Copy(m, p.RawData()) + return m +} // Keys returns keys of the properties. func (p *MutableProperties) Keys() []string { @@ -922,7 +921,7 @@ // value:"${a:=b}>>splitter", 'a' is the key, 'b' is the default value, // 'splitter' is the Splitter's name when you want split string value // into []string value. -func (p *MutableProperties) Bind(i interface{}, tag ...string) error { +func (p *MutableProperties) Bind(i any, tag ...string) error { var v reflect.Value { @@ -984,6 +983,7 @@ import ( "fmt" + "maps" "github.com/expr-lang/expr" ) @@ -993,7 +993,7 @@ type ValidateFunc[T any] func(T) bool // validateFuncs holds a map of registered validation functions. -var validateFuncs = map[string]interface{}{} +var validateFuncs = map[string]any{} // RegisterValidateFunc registers a validation function with a specific name. // The function can then be used in validation expressions. @@ -1004,12 +1004,10 @@ // validateField validates a field using a validation expression (tag) and the field value (i). // It evaluates the expression and checks if the result is true (i.e., the validation passes). // If any error occurs during evaluation or if the validation fails, an error is returned. -func validateField(tag string, i interface{}) error { - env := map[string]interface{}{"$": i} - for k, v := range validateFuncs { - env[k] = v - } - r, err := expr.Eval(tag, env) +func validateField(tag string, i any) error { + env := map[string]any{"$": i} + maps.Copy(env, validateFuncs) + r, err := expr.Eval(tag, env) if err != nil { return fmt.Errorf("eval %q returns error, %w", tag, err) } @@ -1047,8 +1045,8 @@ ) // Read parses []byte in the json format into map. -func Read(b []byte) (map[string]interface{}, error) { - var ret map[string]interface{} +func Read(b []byte) (map[string]any, error) { + var ret map[string]any err := json.Unmarshal(b, &ret) if err != nil { return nil, err @@ -1078,13 +1076,13 @@ import "github.com/magiconair/properties" // Read parses []byte in the properties format into map. -func Read(b []byte) (map[string]interface{}, error) { +func Read(b []byte) (map[string]any, error) { p := properties.NewProperties() p.DisableExpansion = true _ = p.Load(b, properties.UTF8) // always no error - ret := make(map[string]interface{}) + ret := make(map[string]any) for k, v := range p.Map() { ret[k] = v } @@ -1115,7 +1113,7 @@ ) // Read parses []byte in the toml format into map. -func Read(b []byte) (map[string]interface{}, error) { +func Read(b []byte) (map[string]any, error) { tree, err := toml.LoadBytes(b) if err != nil { return nil, err @@ -1147,8 +1145,8 @@ ) // Read parses []byte in the yaml format into map. -func Read(b []byte) (map[string]interface{}, error) { - ret := make(map[string]interface{}) +func Read(b []byte) (map[string]any, error) { + ret := make(map[string]any) err := yaml.Unmarshal(b, &ret) if err != nil { return nil, err @@ -2186,7 +2184,7 @@ // ValueArg returns a ValueArg with the specified value. // Used to provide specific values for constructor parameters. -func ValueArg(v interface{}) Arg { +func ValueArg(v any) Arg { return gs_arg.Value(v) } @@ -2198,7 +2196,7 @@ // BindArg returns a BindArg for the specified function and arguments. // Used to provide argument binding for option-style constructor parameters. -func BindArg(fn interface{}, args ...Arg) *gs_arg.BindArg { +func BindArg(fn any, args ...Arg) *gs_arg.BindArg { return gs_arg.Bind(fn, args...) } @@ -2240,7 +2238,7 @@ } // RegisterExpressFunc registers a custom expression function. -func RegisterExpressFunc(name string, fn interface{}) { +func RegisterExpressFunc(name string, fn any) { gs_cond.RegisterExpressFunc(name, fn) } @@ -2292,7 +2290,7 @@ ) // NewBean creates a new BeanDefinition. -func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.BeanDefinition { +func NewBean(objOrCtor any, ctorArgs ...gs.Arg) *gs.BeanDefinition { return gs_bean.NewBean(objOrCtor, ctorArgs...).Caller(1) } @@ -2388,13 +2386,13 @@ } // Object registers a bean definition for a given object. -func Object(i interface{}) *RegisteredBean { +func Object(i any) *RegisteredBean { b := gs_bean.NewBean(reflect.ValueOf(i)) return gs_app.GS.C.Register(b).Caller(1) } // Provide registers a bean definition for a given constructor. -func Provide(ctor interface{}, args ...Arg) *RegisteredBean { +func Provide(ctor any, args ...Arg) *RegisteredBean { b := gs_bean.NewBean(ctor, args...) return gs_app.GS.C.Register(b).Caller(1) } @@ -2444,7 +2442,7 @@ } maxLength := 0 - for _, s := range strings.Split(appBanner, "\n") { + for s := range strings.SplitSeq(appBanner, "\n") { fmt.Printf("\x1b[36m%s\x1b[0m\n", s) // CYAN if len(s) > maxLength { maxLength = len(s) @@ -2677,8 +2675,8 @@ // CondBean represents a bean with Name and Type. type CondBean interface { - Name() string - Type() reflect.Type + Name() string // Name of the bean + Type() reflect.Type // Type of the bean } // CondContext defines methods for the IoC container used by conditions. @@ -2741,23 +2739,23 @@ // BeanMock defines a mock object and its target bean selector for overriding. type BeanMock struct { - Object interface{} // Mock instance to replace the target bean + Object any // Mock instance to replace the target bean Target BeanSelector // Selector to identify the target bean } // BeanID represents the unique identifier for a bean. type BeanID struct { - Type reflect.Type - Name string + Type reflect.Type // Type of the bean + Name string // Name of the bean } // BeanInitFunc defines the prototype for initialization functions. // Examples: `func(bean)` or `func(bean) error`. -type BeanInitFunc = interface{} +type BeanInitFunc = any // BeanDestroyFunc defines the prototype for destruction functions. // Examples: `func(bean)` or `func(bean) error`. -type BeanDestroyFunc = interface{} +type BeanDestroyFunc = any // Configuration holds parameters for bean setup configuration. type Configuration struct { @@ -2775,7 +2773,7 @@ SetDestroy(fn BeanDestroyFunc) SetInitMethod(method string) SetDestroyMethod(method string) - SetCondition(c ...Condition) + SetCondition(conditions ...Condition) SetDependsOn(selectors ...BeanSelector) SetExport(exports ...reflect.Type) SetConfiguration(c ...Configuration) @@ -2834,8 +2832,8 @@ } // Condition sets the conditions for the bean. -func (d *beanBuilder[T]) Condition(c ...Condition) *T { - d.b.SetCondition(c...) +func (d *beanBuilder[T]) Condition(conditions ...Condition) *T { + d.b.SetCondition(conditions...) return *(**T)(unsafe.Pointer(&d)) } @@ -3164,10 +3162,15 @@ // Boot defines the interface for application bootstrapping. type Boot interface { + // Config returns the boot configuration. Config() *gs_conf.BootConfig - Object(i interface{}) *gs.RegisteredBean - Provide(ctor interface{}, args ...gs.Arg) *gs.RegisteredBean + // Object registers an object bean. + Object(i any) *gs.RegisteredBean + // Provide registers a bean using a constructor function. + Provide(ctor any, args ...gs.Arg) *gs.RegisteredBean + // Register registers a BeanDefinition instance. Register(bd *gs.BeanDefinition) *gs.RegisteredBean + // FuncRunner creates a Runner from a function. FuncRunner(fn func() error) *gs.RegisteredBean } @@ -3196,14 +3199,14 @@ } // Object registers an object bean. -func (b *BootImpl) Object(i interface{}) *gs.RegisteredBean { +func (b *BootImpl) Object(i any) *gs.RegisteredBean { b.flag = true bd := gs_bean.NewBean(reflect.ValueOf(i)) return b.c.Register(bd).Caller(1) } // Provide registers a bean using a constructor function. -func (b *BootImpl) Provide(ctor interface{}, args ...gs.Arg) *gs.RegisteredBean { +func (b *BootImpl) Provide(ctor any, args ...gs.Arg) *gs.RegisteredBean { b.flag = true bd := gs_bean.NewBean(ctor, args...) return b.c.Register(bd).Caller(1) @@ -3412,11 +3415,11 @@ // ValueArg represents a fixed-value argument. type ValueArg struct { - v interface{} + v any } // Value creates a fixed-value argument. -func Value(v interface{}) gs.Arg { +func Value(v any) gs.Arg { return ValueArg{v: v} } @@ -3528,7 +3531,7 @@ } // CallableFunc is a function that can be called. -type CallableFunc = interface{} +type CallableFunc = any // Callable wraps a function and its bound arguments for invocation. type Callable struct { @@ -3607,8 +3610,8 @@ } // Condition adds pre-execution conditions to the binding. -func (arg *BindArg) Condition(c ...gs.Condition) *BindArg { - arg.conditions = append(arg.conditions, c...) +func (arg *BindArg) Condition(conditions ...gs.Condition) *BindArg { + arg.conditions = append(arg.conditions, conditions...) return arg } @@ -3771,8 +3774,8 @@ } // SetCondition adds a condition to the list of conditions for the bean. -func (d *BeanMetadata) SetCondition(c ...gs.Condition) { - d.conditions = append(d.conditions, c...) +func (d *BeanMetadata) SetCondition(conditions ...gs.Condition) { + d.conditions = append(d.conditions, conditions...) } // Configuration returns the configuration parameters for the bean. @@ -3831,7 +3834,7 @@ } // Interface returns the underlying value of the bean. -func (d *BeanRuntime) Interface() interface{} { +func (d *BeanRuntime) Interface() any { return d.v.Interface() } @@ -3872,7 +3875,7 @@ } // SetMock sets the mock object for the bean, replacing its runtime information. -func (d *BeanDefinition) SetMock(obj interface{}) { +func (d *BeanDefinition) SetMock(obj any) { *d = BeanDefinition{ BeanMetadata: &BeanMetadata{ exports: d.exports, @@ -3951,14 +3954,7 @@ if !d.Type().Implements(t) { panic(fmt.Sprintf("doesn't implement interface %s", t)) } - exported := false - for _, export := range d.exports { - if t == export { - exported = true - break - } - } - if exported { + if slices.Contains(d.exports, t) { continue } d.exports = append(d.exports, t) @@ -3973,12 +3969,10 @@ return false, nil } ss := strings.Split(strings.TrimSpace(profiles), ",") - for s := range slices.Values(strings.Split(val, ",")) { - for _, x := range ss { - if s == x { - return true, nil - } - } + for s := range strings.SplitSeq(val, ",") { + if slices.Contains(ss, s) { + return true, nil + } } return false, nil })) @@ -3996,7 +3990,7 @@ // NewBean creates a new bean definition. When registering a normal function, // use reflect.ValueOf(fn) to avoid conflicts with constructors. -func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.BeanDefinition { +func NewBean(objOrCtor any, ctorArgs ...gs.Arg) *gs.BeanDefinition { var f *gs_arg.Callable var v reflect.Value @@ -4177,9 +4171,9 @@ // in the context. It allows for complex matching behaviors such as matching missing // properties or evaluating expressions. type onProperty struct { - name string // The name of the property to check. - matchIfMissing bool // Whether to match if the property is missing. - havingValue interface{} // The expected value or expression to match. + name string // The name of the property to check. + matchIfMissing bool // Whether to match if the property is missing. + havingValue any // The expected value or expression to match. } // OnProperty creates a condition based on the presence and value of a specified property. @@ -4558,18 +4552,19 @@ import ( "fmt" + "maps" "github.com/expr-lang/expr" ) // funcMap stores registered functions that can be referenced in expressions. // These functions are available for use in all expressions evaluated by EvalExpr. -var funcMap = map[string]interface{}{} +var funcMap = map[string]any{} // RegisterExpressFunc registers a function under the given name, making it available // for use in expressions evaluated by EvalExpr. Functions must be registered before // they are referenced in any expression. -func RegisterExpressFunc(name string, fn interface{}) { +func RegisterExpressFunc(name string, fn any) { funcMap[name] = fn } @@ -4577,11 +4572,9 @@ // `input` is a boolean expression string to evaluate, it must return a boolean result. // `val` is a string value accessible as "$" within the expression context. func EvalExpr(input string, val string) (bool, error) { - env := map[string]interface{}{"$": val} - for k, v := range funcMap { - env[k] = v - } - r, err := expr.Eval(input, env) + env := map[string]any{"$": val} + maps.Copy(env, funcMap) + r, err := expr.Eval(input, env) if err != nil { return false, fmt.Errorf("eval %q returns error, %w", input, err) } @@ -4649,7 +4642,7 @@ cmdArgs := os.Args[1:] n := len(cmdArgs) - for i := 0; i < n; i++ { + for i := range n { if cmdArgs[i] == option { if i+1 >= n { return fmt.Errorf("cmd option %s needs arg", option) @@ -4929,8 +4922,7 @@ } if activeProfiles = strings.TrimSpace(activeProfiles); activeProfiles != "" { - ss := strings.Split(activeProfiles, ",") - for _, s := range ss { + for s := range strings.SplitSeq(activeProfiles, ",") { if s = strings.TrimSpace(s); s != "" { files = append(files, []string{ fmt.Sprintf("%s/%s-%s.properties", dir, p.configName, s), @@ -5003,37 +4995,19 @@ import ( "os" - "regexp" "strings" "github.com/go-spring/spring-core/conf" ) -const ( - IncludeEnvPatterns = "INCLUDE_ENV_PATTERNS" - ExcludeEnvPatterns = "EXCLUDE_ENV_PATTERNS" -) - // Environment represents the environment configuration. type Environment struct{} // NewEnvironment initializes a new instance of Environment. -func NewEnvironment() *Environment { +func NewEnvironment() *Environment { return &Environment{} } -// lookupEnv searches for an environment variable by key in the environ slice. -func lookupEnv(environ []string, key string) (value string, found bool) { - key = strings.TrimSpace(key) + "=" - for _, s := range environ { - if strings.HasPrefix(s, key) { - v := strings.TrimPrefix(s, key) - return strings.TrimSpace(v), true - } - } - return "", false -} - // CopyTo add environment variables that matches IncludeEnvPatterns and // exclude environment variables that matches ExcludeEnvPatterns. func (c *Environment) CopyTo(p *conf.MutableProperties) error { @@ -5041,66 +5015,22 @@ if len(environ) == 0 { return nil } - - toRex := func(patterns []string) ([]*regexp.Regexp, error) { - var rex []*regexp.Regexp - for _, v := range patterns { - exp, err := regexp.Compile(v) - if err != nil { - return nil, err - } - rex = append(rex, exp) - } - return rex, nil - } - - includes := []string{".*"} - if s, ok := lookupEnv(environ, IncludeEnvPatterns); ok { - includes = strings.Split(s, ",") - } - includeRex, err := toRex(includes) - if err != nil { - return err - } - - var excludes []string - if s, ok := lookupEnv(environ, ExcludeEnvPatterns); ok { - excludes = strings.Split(s, ",") - } - excludeRex, err := toRex(excludes) - if err != nil { - return err - } - - matches := func(rex []*regexp.Regexp, s string) bool { - for _, r := range rex { - if r.MatchString(s) { - return true - } - } - return false - } - const prefix = "GS_" - for _, env := range environ { + for _, env := range environ { ss := strings.SplitN(env, "=", 2) k, v := ss[0], "" - if len(ss) > 1 { + if len(ss) > 1 { v = ss[1] } - - var propKey string + var propKey string if strings.HasPrefix(k, prefix) { propKey = strings.TrimPrefix(k, prefix) propKey = strings.ReplaceAll(propKey, "_", ".") propKey = strings.ToLower(propKey) - } else if matches(includeRex, k) && !matches(excludeRex, k) { + } else { propKey = k - } else { - continue - } - - if err = p.Set(propKey, v); err != nil { + } + if err := p.Set(propKey, v); err != nil { return err } } @@ -5203,7 +5133,7 @@ Name() string Type() reflect.Type Value() reflect.Value - Interface() interface{} + Interface() any Callable() *gs_arg.Callable Status() gs_bean.BeanStatus String() string @@ -5316,7 +5246,7 @@ } // Wire injects dependencies into the given object. -func (c *Injecting) Wire(obj interface{}) error { +func (c *Injecting) Wire(obj any) error { r := &Injector{ state: Refreshed, p: gs_dync.New(c.p.Data()), @@ -5522,7 +5452,7 @@ if foundAny { temp := append(beforeAny, afterAny...) - for i := 0; i < len(beans); i++ { + for i := range len(beans) { if slices.Contains(temp, i) { continue } @@ -5578,7 +5508,7 @@ if str != "" { nullable = true if str != "?" { - for _, s := range strings.Split(str, ",") { + for s := range strings.SplitSeq(str, ",") { g := parseWireTag(s) tags = append(tags, g) if !g.nullable { @@ -5588,7 +5518,7 @@ } } if c.forceAutowireIsNullable { - for i := 0; i < len(tags); i++ { + for i := range len(tags) { tags[i].nullable = true } nullable = true @@ -5659,11 +5589,9 @@ // Detect circular dependency. if b.Status() == gs_bean.StatusCreating && b.Callable() != nil { - for _, bean := range stack.beans { - if bean == b { - return errors.New("found circular autowire") - } - } + if slices.Contains(stack.beans, b) { + return errors.New("found circular autowire") + } } // If the bean is already being created, return early. @@ -5802,7 +5730,7 @@ // wireStruct performs dependency injection for a struct. func (c *Injector) wireStruct(v reflect.Value, t reflect.Type, opt conf.BindParam, stack *Stack) error { // Loop through each field of the struct. - for i := 0; i < t.NumField(); i++ { + for i := range t.NumField() { ft := t.Field(i) fv := v.Field(i) @@ -5955,7 +5883,7 @@ } // getBeforeDestroyers retrieves destroyers that should be processed before a given one for sorting purposes. -func getBeforeDestroyers(destroyers *list.List, i interface{}) *list.List { +func getBeforeDestroyers(destroyers *list.List, i any) *list.List { d := i.(*destroyer) result := list.New() for e := destroyers.Front(); e != nil; e = e.Next() { @@ -5970,7 +5898,7 @@ // getSortedDestroyers sorts beans with destroy functions by dependency order. func (s *Stack) getSortedDestroyers() []func() { - destroy := func(v reflect.Value, fn interface{}) func() { + destroy := func(v reflect.Value, fn any) func() { return func() { fnValue := reflect.ValueOf(fn) out := fnValue.Call([]reflect.Value{v}) @@ -6115,13 +6043,13 @@ } // Object registers a pre-constructed instance as a bean. -func (c *Resolving) Object(i interface{}) *gs.RegisteredBean { +func (c *Resolving) Object(i any) *gs.RegisteredBean { b := gs_bean.NewBean(reflect.ValueOf(i)) return c.Register(b).Caller(1) } // Provide registers a constructor function to create a bean. -func (c *Resolving) Provide(ctor interface{}, args ...gs.Arg) *gs.RegisteredBean { +func (c *Resolving) Provide(ctor any, args ...gs.Arg) *gs.RegisteredBean { b := gs_bean.NewBean(ctor, args...) return c.Register(b).Caller(1) } @@ -6261,7 +6189,7 @@ var ret []*gs_bean.BeanDefinition n := bd.Type().NumMethod() - for i := 0; i < n; i++ { + for i := range n { m := bd.Type().Method(i) skip := false for _, p := range excludes { @@ -6452,12 +6380,12 @@ import ( "encoding/json" "reflect" - "sort" "strings" "sync" "sync/atomic" "github.com/go-spring/spring-core/conf" + "github.com/go-spring/spring-core/util" ) // refreshable represents an object that can be dynamically refreshed. @@ -6598,11 +6526,7 @@ changes[k] = struct{}{} } - keys := make([]string, 0, len(changes)) - for k := range changes { - keys = append(keys, k) - } - sort.Strings(keys) + keys := util.OrderedMapKeys(changes) return p.refreshKeys(keys) } @@ -6626,11 +6550,7 @@ // Sort and collect objects that need updating. updateObjects := make([]*refreshObject, 0, len(updateIndexes)) { - ints := make([]int, 0, len(updateIndexes)) - for k := range updateIndexes { - ints = append(ints, k) - } - sort.Ints(ints) + ints := util.OrderedMapKeys(updateIndexes) for _, k := range ints { updateObjects = append(updateObjects, updateIndexes[k]) } @@ -6690,7 +6610,7 @@ } // Do attempts to refresh a single object if it implements the [refreshable] interface. -func (f *filter) Do(i interface{}, param conf.BindParam) (bool, error) { +func (f *filter) Do(i any, param conf.BindParam) (bool, error) { v, ok := i.(refreshable) if !ok { return false, nil @@ -6745,7 +6665,7 @@ // GetBeforeItems is a function type that returns a list of items // that must appear before the given current item in the sorting order. -type GetBeforeItems func(sorting *list.List, current interface{}) *list.List +type GetBeforeItems func(sorting *list.List, current any) *list.List // TripleSort performs a three-way sort (processing, toSort, sorted) // to resolve dependencies and return a sorted list. @@ -6771,7 +6691,7 @@ // searchInList searches for an element `v` in the list `l`. // If the element exists, it returns a pointer to the list element. Otherwise, it returns nil. -func searchInList(l *list.List, v interface{}) *list.Element { +func searchInList(l *list.List, v any) *list.Element { for e := l.Front(); e != nil; e = e.Next() { if e.Value == v { return e @@ -6789,7 +6709,7 @@ // - current: The current item being processed (nil for the first item). // - fn: A function that retrieves the list of items that must appear before the current item. func tripleSortByAfter(sorting *list.List, toSort *list.List, sorted *list.List, - processing *list.List, current interface{}, fn GetBeforeItems) error { + processing *list.List, current any, fn GetBeforeItems) error { // If no current item is specified, remove and process the first item in the `toSort` list. if current == nil { @@ -6989,7 +6909,7 @@ var LineBreak = " << " // WrapError wraps an existing error, creating a new error with hierarchical relationships. -func WrapError(err error, format string, args ...interface{}) error { +func WrapError(err error, format string, args ...any) error { msg := fmt.Sprintf(format, args...) return fmt.Errorf("%s"+LineBreak+"%w", msg, err) } @@ -7023,7 +6943,7 @@ // FlattenMap flattens a nested map, array, or slice into a single-level map // with string keys and string values. It recursively processes each element // of the input map and adds its flattened representation to the result map. -func FlattenMap(m map[string]interface{}) map[string]string { +func FlattenMap(m map[string]any) map[string]string { result := make(map[string]string) for key, val := range m { FlattenValue(key, val, result) @@ -7033,7 +6953,7 @@ // FlattenValue flattens a single value (which can be a map, array, slice, // or other types) into the result map. -func FlattenValue(key string, val interface{}, result map[string]string) { +func FlattenValue(key string, val any, result map[string]string) { if val == nil { return } @@ -7054,7 +6974,7 @@ result[key] = "" return } - for i := 0; i < v.Len(); i++ { + for i := range v.Len() { subKey := fmt.Sprintf("%s[%d]", key, i) subValue := v.Index(i).Interface() // If an element is nil, treat it as an empty value and assign an empty string. @@ -7109,7 +7029,7 @@ ) // OnPanic is a global callback function triggered when a panic occurs. -var OnPanic = func(ctx context.Context, r interface{}) { +var OnPanic = func(ctx context.Context, r any) { syslog.Errorf("panic: %v\n%s", r, debug.Stack()) } @@ -7292,16 +7212,16 @@ ) // MapKeys returns the keys of the map m. -func MapKeys[M ~map[K]V, K comparable, V any](m M) []K { +func MapKeys[M ~map[K]V, K comparable, V any](m M) []K { r := make([]K, 0, len(m)) - for k := range m { + for k := range m { r = append(r, k) } - return r + return r } // OrderedMapKeys returns the keys of the map m in sorted order. -func OrderedMapKeys[M ~map[K]V, K cmp.Ordered, V any](m M) []K { +func OrderedMapKeys[M ~map[K]V, K cmp.Ordered, V any](m M) []K { r := MapKeys(m) slices.Sort(r) return r @@ -7634,7 +7554,7 @@ } // FuncName returns the function name for a given function. -func FuncName(fn interface{}) string { +func FuncName(fn any) string { _, _, fnName := FileLine(fn) return fnName } @@ -7642,7 +7562,7 @@ // FileLine returns the file, line number, and function name for a given function. // It uses reflection and runtime information to extract these details. // 'fn' is expected to be a function or method value. -func FileLine(fn interface{}) (file string, line int, fnName string) { +func FileLine(fn any) (file string, line int, fnName string) { fnPtr := reflect.ValueOf(fn).Pointer() fnInfo := runtime.FuncForPC(fnPtr) diff --git a/gs/gs.go b/gs/gs.go index bf15d645..05f96e38 100644 --- a/gs/gs.go +++ b/gs/gs.go @@ -55,7 +55,7 @@ func TagArg(tag string) Arg { // ValueArg returns a ValueArg with the specified value. // Used to provide specific values for constructor parameters. -func ValueArg(v interface{}) Arg { +func ValueArg(v any) Arg { return gs_arg.Value(v) } @@ -67,7 +67,7 @@ func IndexArg(n int, arg Arg) Arg { // BindArg returns a BindArg for the specified function and arguments. // Used to provide argument binding for option-style constructor parameters. -func BindArg(fn interface{}, args ...Arg) *gs_arg.BindArg { +func BindArg(fn any, args ...Arg) *gs_arg.BindArg { return gs_arg.Bind(fn, args...) } @@ -109,7 +109,7 @@ func OnSingleBean[T any](name ...string) Condition { } // RegisterExpressFunc registers a custom expression function. -func RegisterExpressFunc(name string, fn interface{}) { +func RegisterExpressFunc(name string, fn any) { gs_cond.RegisterExpressFunc(name, fn) } @@ -161,7 +161,7 @@ type ( ) // NewBean creates a new BeanDefinition. -func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.BeanDefinition { +func NewBean(objOrCtor any, ctorArgs ...gs.Arg) *gs.BeanDefinition { return gs_bean.NewBean(objOrCtor, ctorArgs...).Caller(1) } @@ -257,13 +257,13 @@ func Component[T any](i T) T { } // Object registers a bean definition for a given object. -func Object(i interface{}) *RegisteredBean { +func Object(i any) *RegisteredBean { b := gs_bean.NewBean(reflect.ValueOf(i)) return gs_app.GS.C.Register(b).Caller(1) } // Provide registers a bean definition for a given constructor. -func Provide(ctor interface{}, args ...Arg) *RegisteredBean { +func Provide(ctor any, args ...Arg) *RegisteredBean { b := gs_bean.NewBean(ctor, args...) return gs_app.GS.C.Register(b).Caller(1) } @@ -313,7 +313,7 @@ func printBanner() { } maxLength := 0 - for _, s := range strings.Split(appBanner, "\n") { + for s := range strings.SplitSeq(appBanner, "\n") { fmt.Printf("\x1b[36m%s\x1b[0m\n", s) // CYAN if len(s) > maxLength { maxLength = len(s) diff --git a/gs/internal/gs/gs.go b/gs/internal/gs/gs.go index aeefb136..44917d73 100644 --- a/gs/internal/gs/gs.go +++ b/gs/internal/gs/gs.go @@ -158,7 +158,7 @@ type Server interface { // BeanMock defines a mock object and its target bean selector for overriding. type BeanMock struct { - Object interface{} // Mock instance to replace the target bean + Object any // Mock instance to replace the target bean Target BeanSelector // Selector to identify the target bean } @@ -170,11 +170,11 @@ type BeanID struct { // BeanInitFunc defines the prototype for initialization functions. // Examples: `func(bean)` or `func(bean) error`. -type BeanInitFunc = interface{} +type BeanInitFunc = any // BeanDestroyFunc defines the prototype for destruction functions. // Examples: `func(bean)` or `func(bean) error`. -type BeanDestroyFunc = interface{} +type BeanDestroyFunc = any // Configuration holds parameters for bean setup configuration. type Configuration struct { diff --git a/gs/internal/gs_app/boot.go b/gs/internal/gs_app/boot.go index 7aefb78e..13c6c780 100644 --- a/gs/internal/gs_app/boot.go +++ b/gs/internal/gs_app/boot.go @@ -38,9 +38,9 @@ type Boot interface { // Config returns the boot configuration. Config() *gs_conf.BootConfig // Object registers an object bean. - Object(i interface{}) *gs.RegisteredBean + Object(i any) *gs.RegisteredBean // Provide registers a bean using a constructor function. - Provide(ctor interface{}, args ...gs.Arg) *gs.RegisteredBean + Provide(ctor any, args ...gs.Arg) *gs.RegisteredBean // Register registers a BeanDefinition instance. Register(bd *gs.BeanDefinition) *gs.RegisteredBean // FuncRunner creates a Runner from a function. @@ -72,14 +72,14 @@ func (b *BootImpl) Config() *gs_conf.BootConfig { } // Object registers an object bean. -func (b *BootImpl) Object(i interface{}) *gs.RegisteredBean { +func (b *BootImpl) Object(i any) *gs.RegisteredBean { b.flag = true bd := gs_bean.NewBean(reflect.ValueOf(i)) return b.c.Register(bd).Caller(1) } // Provide registers a bean using a constructor function. -func (b *BootImpl) Provide(ctor interface{}, args ...gs.Arg) *gs.RegisteredBean { +func (b *BootImpl) Provide(ctor any, args ...gs.Arg) *gs.RegisteredBean { b.flag = true bd := gs_bean.NewBean(ctor, args...) return b.c.Register(bd).Caller(1) diff --git a/gs/internal/gs_app/signal_test.go b/gs/internal/gs_app/signal_test.go index 2c43bcf4..5512fb50 100644 --- a/gs/internal/gs_app/signal_test.go +++ b/gs/internal/gs_app/signal_test.go @@ -29,7 +29,7 @@ func TestReadySignal(t *testing.T) { const workers = 3 signal := NewReadySignal() - for i := 0; i < workers; i++ { + for i := range workers { num := i signal.Add() go func() { @@ -53,7 +53,7 @@ func TestReadySignal(t *testing.T) { defer wg.Wait() signal := NewReadySignal() - for i := 0; i < workers; i++ { + for range workers { signal.Add() go func() { defer wg.Done() diff --git a/gs/internal/gs_arg/arg.go b/gs/internal/gs_arg/arg.go index d8447a6a..e07d57d4 100644 --- a/gs/internal/gs_arg/arg.go +++ b/gs/internal/gs_arg/arg.go @@ -83,11 +83,11 @@ func (arg IndexArg) GetArgValue(ctx gs.ArgContext, t reflect.Type) (reflect.Valu // ValueArg represents a fixed-value argument. type ValueArg struct { - v interface{} + v any } // Value creates a fixed-value argument. -func Value(v interface{}) gs.Arg { +func Value(v any) gs.Arg { return ValueArg{v: v} } @@ -199,7 +199,7 @@ func (r *ArgList) get(ctx gs.ArgContext) ([]reflect.Value, error) { } // CallableFunc is a function that can be called. -type CallableFunc = interface{} +type CallableFunc = any // Callable wraps a function and its bound arguments for invocation. type Callable struct { diff --git a/gs/internal/gs_bean/bean.go b/gs/internal/gs_bean/bean.go index 040bf312..8f57f8c8 100644 --- a/gs/internal/gs_bean/bean.go +++ b/gs/internal/gs_bean/bean.go @@ -189,7 +189,7 @@ func (d *BeanRuntime) Value() reflect.Value { } // Interface returns the underlying value of the bean. -func (d *BeanRuntime) Interface() interface{} { +func (d *BeanRuntime) Interface() any { return d.v.Interface() } @@ -230,7 +230,7 @@ func makeBean(t reflect.Type, v reflect.Value, f *gs_arg.Callable, name string) } // SetMock sets the mock object for the bean, replacing its runtime information. -func (d *BeanDefinition) SetMock(obj interface{}) { +func (d *BeanDefinition) SetMock(obj any) { *d = BeanDefinition{ BeanMetadata: &BeanMetadata{ exports: d.exports, @@ -324,7 +324,7 @@ func (d *BeanDefinition) OnProfiles(profiles string) { return false, nil } ss := strings.Split(strings.TrimSpace(profiles), ",") - for _, s := range strings.Split(val, ",") { + for s := range strings.SplitSeq(val, ",") { if slices.Contains(ss, s) { return true, nil } @@ -345,7 +345,7 @@ func (d *BeanDefinition) String() string { // NewBean creates a new bean definition. When registering a normal function, // use reflect.ValueOf(fn) to avoid conflicts with constructors. -func NewBean(objOrCtor interface{}, ctorArgs ...gs.Arg) *gs.BeanDefinition { +func NewBean(objOrCtor any, ctorArgs ...gs.Arg) *gs.BeanDefinition { var f *gs_arg.Callable var v reflect.Value diff --git a/gs/internal/gs_cond/cond.go b/gs/internal/gs_cond/cond.go index e3310762..f275112a 100755 --- a/gs/internal/gs_cond/cond.go +++ b/gs/internal/gs_cond/cond.go @@ -66,9 +66,9 @@ type OnPropertyInterface interface { // in the context. It allows for complex matching behaviors such as matching missing // properties or evaluating expressions. type onProperty struct { - name string // The name of the property to check. - matchIfMissing bool // Whether to match if the property is missing. - havingValue interface{} // The expected value or expression to match. + name string // The name of the property to check. + matchIfMissing bool // Whether to match if the property is missing. + havingValue any // The expected value or expression to match. } // OnProperty creates a condition based on the presence and value of a specified property. diff --git a/gs/internal/gs_cond/expr.go b/gs/internal/gs_cond/expr.go index 58004857..977b55ad 100644 --- a/gs/internal/gs_cond/expr.go +++ b/gs/internal/gs_cond/expr.go @@ -18,18 +18,19 @@ package gs_cond import ( "fmt" + "maps" "github.com/expr-lang/expr" ) // funcMap stores registered functions that can be referenced in expressions. // These functions are available for use in all expressions evaluated by EvalExpr. -var funcMap = map[string]interface{}{} +var funcMap = map[string]any{} // RegisterExpressFunc registers a function under the given name, making it available // for use in expressions evaluated by EvalExpr. Functions must be registered before // they are referenced in any expression. -func RegisterExpressFunc(name string, fn interface{}) { +func RegisterExpressFunc(name string, fn any) { funcMap[name] = fn } @@ -37,10 +38,8 @@ func RegisterExpressFunc(name string, fn interface{}) { // `input` is a boolean expression string to evaluate, it must return a boolean result. // `val` is a string value accessible as "$" within the expression context. func EvalExpr(input string, val string) (bool, error) { - env := map[string]interface{}{"$": val} - for k, v := range funcMap { - env[k] = v - } + env := map[string]any{"$": val} + maps.Copy(env, funcMap) r, err := expr.Eval(input, env) if err != nil { return false, fmt.Errorf("eval %q returns error, %w", input, err) diff --git a/gs/internal/gs_conf/cmd.go b/gs/internal/gs_conf/cmd.go index 8338b08e..1393a72b 100644 --- a/gs/internal/gs_conf/cmd.go +++ b/gs/internal/gs_conf/cmd.go @@ -54,7 +54,7 @@ func (c *CommandArgs) CopyTo(out *conf.MutableProperties) error { cmdArgs := os.Args[1:] n := len(cmdArgs) - for i := 0; i < n; i++ { + for i := range n { if cmdArgs[i] == option { if i+1 >= n { return fmt.Errorf("cmd option %s needs arg", option) diff --git a/gs/internal/gs_conf/cmd_test.go b/gs/internal/gs_conf/cmd_test.go index 15cf8291..ce4a6360 100644 --- a/gs/internal/gs_conf/cmd_test.go +++ b/gs/internal/gs_conf/cmd_test.go @@ -65,7 +65,7 @@ func TestCommandArgs(t *testing.T) { t.Run("set return error", func(t *testing.T) { os.Args = []string{"test", "-D", "name=go-spring", "-D", "debug"} - p := conf.Map(map[string]interface{}{ + p := conf.Map(map[string]any{ "debug": []string{"true"}, }) err := NewCommandArgs().CopyTo(p) diff --git a/gs/internal/gs_conf/conf.go b/gs/internal/gs_conf/conf.go index d8c49d47..db078148 100644 --- a/gs/internal/gs_conf/conf.go +++ b/gs/internal/gs_conf/conf.go @@ -259,8 +259,7 @@ func (p *PropertySources) getFiles(dir string, resolver conf.Properties) (_ []st } if activeProfiles = strings.TrimSpace(activeProfiles); activeProfiles != "" { - ss := strings.Split(activeProfiles, ",") - for _, s := range ss { + for s := range strings.SplitSeq(activeProfiles, ",") { if s = strings.TrimSpace(s); s != "" { files = append(files, []string{ fmt.Sprintf("%s/%s-%s.properties", dir, p.configName, s), diff --git a/gs/internal/gs_conf/conf_test.go b/gs/internal/gs_conf/conf_test.go index 6a92ba22..1a9ecf00 100644 --- a/gs/internal/gs_conf/conf_test.go +++ b/gs/internal/gs_conf/conf_test.go @@ -219,7 +219,7 @@ func TestPropertySources(t *testing.T) { t.Run("getFiles - 2", func(t *testing.T) { t.Cleanup(clean) - p := conf.Map(map[string]interface{}{ + p := conf.Map(map[string]any{ "spring.profiles.active": "dev,test", }) ps := NewPropertySources(ConfigTypeLocal, "app") @@ -259,7 +259,7 @@ func TestPropertySources(t *testing.T) { t.Run("loadFiles - getFiles error", func(t *testing.T) { t.Cleanup(clean) - p := conf.Map(map[string]interface{}{ + p := conf.Map(map[string]any{ "spring.profiles.active": "${a}", }) ps := NewPropertySources(ConfigTypeLocal, "app") diff --git a/gs/internal/gs_conf/env_test.go b/gs/internal/gs_conf/env_test.go index da6073db..da58c4ca 100644 --- a/gs/internal/gs_conf/env_test.go +++ b/gs/internal/gs_conf/env_test.go @@ -53,7 +53,7 @@ func TestEnvironment(t *testing.T) { defer func() { _ = os.Unsetenv("GS_DB_HOST") }() - props := conf.Map(map[string]interface{}{ + props := conf.Map(map[string]any{ "db": []string{"db2"}, }) err := NewEnvironment().CopyTo(props) diff --git a/gs/internal/gs_core/injecting/injecting.go b/gs/internal/gs_core/injecting/injecting.go index a866675f..8fb2304f 100644 --- a/gs/internal/gs_core/injecting/injecting.go +++ b/gs/internal/gs_core/injecting/injecting.go @@ -43,7 +43,7 @@ type BeanRuntime interface { Name() string Type() reflect.Type Value() reflect.Value - Interface() interface{} + Interface() any Callable() *gs_arg.Callable Status() gs_bean.BeanStatus String() string @@ -156,7 +156,7 @@ func (c *Injecting) Refresh(beans []*gs_bean.BeanDefinition) (err error) { } // Wire injects dependencies into the given object. -func (c *Injecting) Wire(obj interface{}) error { +func (c *Injecting) Wire(obj any) error { r := &Injector{ state: Refreshed, p: gs_dync.New(c.p.Data()), @@ -362,7 +362,7 @@ func (c *Injector) getBeans(t reflect.Type, tags []WireTag, nullable bool, stack if foundAny { temp := append(beforeAny, afterAny...) - for i := 0; i < len(beans); i++ { + for i := range len(beans) { if slices.Contains(temp, i) { continue } @@ -418,7 +418,7 @@ func (c *Injector) autowire(v reflect.Value, str string, stack *Stack) error { if str != "" { nullable = true if str != "?" { - for _, s := range strings.Split(str, ",") { + for s := range strings.SplitSeq(str, ",") { g := parseWireTag(s) tags = append(tags, g) if !g.nullable { @@ -428,7 +428,7 @@ func (c *Injector) autowire(v reflect.Value, str string, stack *Stack) error { } } if c.forceAutowireIsNullable { - for i := 0; i < len(tags); i++ { + for i := range len(tags) { tags[i].nullable = true } nullable = true @@ -499,10 +499,8 @@ func (c *Injector) wireBean(b *gs_bean.BeanDefinition, stack *Stack) error { // Detect circular dependency. if b.Status() == gs_bean.StatusCreating && b.Callable() != nil { - for _, bean := range stack.beans { - if bean == b { - return errors.New("found circular autowire") - } + if slices.Contains(stack.beans, b) { + return errors.New("found circular autowire") } } @@ -642,7 +640,7 @@ func (c *Injector) wireBeanValue(v reflect.Value, t reflect.Type, stack *Stack) // wireStruct performs dependency injection for a struct. func (c *Injector) wireStruct(v reflect.Value, t reflect.Type, opt conf.BindParam, stack *Stack) error { // Loop through each field of the struct. - for i := 0; i < t.NumField(); i++ { + for i := range t.NumField() { ft := t.Field(i) fv := v.Field(i) @@ -795,7 +793,7 @@ func (s *Stack) popDestroyer() { } // getBeforeDestroyers retrieves destroyers that should be processed before a given one for sorting purposes. -func getBeforeDestroyers(destroyers *list.List, i interface{}) *list.List { +func getBeforeDestroyers(destroyers *list.List, i any) *list.List { d := i.(*destroyer) result := list.New() for e := destroyers.Front(); e != nil; e = e.Next() { @@ -810,7 +808,7 @@ func getBeforeDestroyers(destroyers *list.List, i interface{}) *list.List { // getSortedDestroyers sorts beans with destroy functions by dependency order. func (s *Stack) getSortedDestroyers() []func() { - destroy := func(v reflect.Value, fn interface{}) func() { + destroy := func(v reflect.Value, fn any) func() { return func() { fnValue := reflect.ValueOf(fn) out := fnValue.Call([]reflect.Value{v}) diff --git a/gs/internal/gs_core/injecting/injecting_test.go b/gs/internal/gs_core/injecting/injecting_test.go index e441be12..3ece3059 100644 --- a/gs/internal/gs_core/injecting/injecting_test.go +++ b/gs/internal/gs_core/injecting/injecting_test.go @@ -190,11 +190,11 @@ func (b *TestBean) NewChildV2() (*ChildBean, error) { return &ChildBean{b.Value}, nil } -func objectBean(i interface{}) *gs.BeanDefinition { +func objectBean(i any) *gs.BeanDefinition { return gs_bean.NewBean(reflect.ValueOf(i)) } -func provideBean(ctor interface{}, args ...gs.Arg) *gs.BeanDefinition { +func provideBean(ctor any, args ...gs.Arg) *gs.BeanDefinition { return gs_bean.NewBean(ctor, args...) } @@ -217,8 +217,8 @@ type LazyB struct { func TestInjecting(t *testing.T) { t.Run("lazy error - 1", func(t *testing.T) { - r := New(conf.Map(map[string]interface{}{ - "spring": map[string]interface{}{ + r := New(conf.Map(map[string]any{ + "spring": map[string]any{ "allow-circular-references": true, }, })) @@ -241,23 +241,23 @@ func TestInjecting(t *testing.T) { }) t.Run("success", func(t *testing.T) { - r := New(conf.Map(map[string]interface{}{ - "spring": map[string]interface{}{ + r := New(conf.Map(map[string]any{ + "spring": map[string]any{ "allow-circular-references": true, "force-autowire-is-nullable": true, }, - "logger": map[string]interface{}{ - "biz": map[string]interface{}{ + "logger": map[string]any{ + "biz": map[string]any{ "file": "biz.log", }, }, - "server": map[string]interface{}{ - "enable": map[string]interface{}{ + "server": map[string]any{ + "enable": map[string]any{ "write-timeout": true, }, }, - "service": map[string]interface{}{ - "config": map[string]interface{}{ + "service": map[string]any{ + "config": map[string]any{ "int": 100, "str": "hello", }, @@ -331,23 +331,23 @@ func TestInjecting(t *testing.T) { assert.That(t, s.Server.arg.readTimeout).Equal(0) assert.That(t, s.Server.arg.writeTimeout).Equal(100) - err = r.RefreshProperties(conf.Map(map[string]interface{}{ - "spring": map[string]interface{}{ + err = r.RefreshProperties(conf.Map(map[string]any{ + "spring": map[string]any{ "allow-circular-references": true, "force-autowire-is-nullable": true, }, - "logger": map[string]interface{}{ - "biz": map[string]interface{}{ + "logger": map[string]any{ + "biz": map[string]any{ "file": "biz.log", }, }, - "server": map[string]interface{}{ - "enable": map[string]interface{}{ + "server": map[string]any{ + "enable": map[string]any{ "write-timeout": true, }, }, - "service": map[string]interface{}{ - "config": map[string]interface{}{ + "service": map[string]any{ + "config": map[string]any{ "int": 100, "str": "hello", }, @@ -512,8 +512,8 @@ func TestInjecting(t *testing.T) { }) t.Run("wire error - 14", func(t *testing.T) { - r := New(conf.Map(map[string]interface{}{ - "spring": map[string]interface{}{ + r := New(conf.Map(map[string]any{ + "spring": map[string]any{ "force-autowire-is-nullable": true, }, })) @@ -545,8 +545,8 @@ func TestInjecting(t *testing.T) { }) t.Run("wire error - 16", func(t *testing.T) { - r := New(conf.Map(map[string]interface{}{ - "spring": map[string]interface{}{ + r := New(conf.Map(map[string]any{ + "spring": map[string]any{ "force-autowire-is-nullable": true, }, })) @@ -805,8 +805,8 @@ func TestCircularBean(t *testing.T) { }) t.Run("found circular - 3", func(t *testing.T) { - r := New(conf.Map(map[string]interface{}{ - "spring": map[string]interface{}{ + r := New(conf.Map(map[string]any{ + "spring": map[string]any{ "allow-circular-references": true, }, })) @@ -930,8 +930,8 @@ func TestForceClean(t *testing.T) { t.Run("no dync value", func(t *testing.T) { release := make(map[string]struct{}) - r := New(conf.Map(map[string]interface{}{ - "spring": map[string]interface{}{ + r := New(conf.Map(map[string]any{ + "spring": map[string]any{ "force-clean": true, }, })) @@ -961,8 +961,8 @@ func TestForceClean(t *testing.T) { }) t.Run("has dync value", func(t *testing.T) { - r := New(conf.Map(map[string]interface{}{ - "spring": map[string]interface{}{ + r := New(conf.Map(map[string]any{ + "spring": map[string]any{ "force-clean": true, }, })) diff --git a/gs/internal/gs_core/resolving/resolving.go b/gs/internal/gs_core/resolving/resolving.go index 57d4e495..fa1117aa 100644 --- a/gs/internal/gs_core/resolving/resolving.go +++ b/gs/internal/gs_core/resolving/resolving.go @@ -74,13 +74,13 @@ func (c *Resolving) AddMock(mock gs.BeanMock) { } // Object registers a pre-constructed instance as a bean. -func (c *Resolving) Object(i interface{}) *gs.RegisteredBean { +func (c *Resolving) Object(i any) *gs.RegisteredBean { b := gs_bean.NewBean(reflect.ValueOf(i)) return c.Register(b).Caller(1) } // Provide registers a constructor function to create a bean. -func (c *Resolving) Provide(ctor interface{}, args ...gs.Arg) *gs.RegisteredBean { +func (c *Resolving) Provide(ctor any, args ...gs.Arg) *gs.RegisteredBean { b := gs_bean.NewBean(ctor, args...) return c.Register(b).Caller(1) } @@ -220,7 +220,7 @@ func (c *Resolving) scanConfiguration(bd *gs_bean.BeanDefinition) ([]*gs_bean.Be var ret []*gs_bean.BeanDefinition n := bd.Type().NumMethod() - for i := 0; i < n; i++ { + for i := range n { m := bd.Type().Method(i) skip := false for _, p := range excludes { diff --git a/gs/internal/gs_core/resolving/resolving_test.go b/gs/internal/gs_core/resolving/resolving_test.go index a281c91b..e347386f 100644 --- a/gs/internal/gs_core/resolving/resolving_test.go +++ b/gs/internal/gs_core/resolving/resolving_test.go @@ -282,12 +282,12 @@ func TestResolving(t *testing.T) { r.Provide((*TestBean).NewChild, b) } - p := conf.Map(map[string]interface{}{ + p := conf.Map(map[string]any{ "logger": map[string]string{ "a": "", "b": "", }, - "Enable": map[string]interface{}{ + "Enable": map[string]any{ "ServeMux-2": true, }, }) diff --git a/gs/internal/gs_dync/dync.go b/gs/internal/gs_dync/dync.go index ea628976..d17aae5f 100644 --- a/gs/internal/gs_dync/dync.go +++ b/gs/internal/gs_dync/dync.go @@ -249,7 +249,7 @@ type filter struct { } // Do attempts to refresh a single object if it implements the [refreshable] interface. -func (f *filter) Do(i interface{}, param conf.BindParam) (bool, error) { +func (f *filter) Do(i any, param conf.BindParam) (bool, error) { v, ok := i.(refreshable) if !ok { return false, nil diff --git a/gs/internal/gs_dync/dync_test.go b/gs/internal/gs_dync/dync_test.go index 248c6e16..296a974d 100644 --- a/gs/internal/gs_dync/dync_test.go +++ b/gs/internal/gs_dync/dync_test.go @@ -48,21 +48,21 @@ func TestValue(t *testing.T) { return v.onRefresh(prop, conf.BindParam{Key: "key"}) } - err := refresh(conf.Map(map[string]interface{}{ + err := refresh(conf.Map(map[string]any{ "key": "42", })) assert.Nil(t, err) assert.That(t, v.Value()).Equal(42) - err = refresh(conf.Map(map[string]interface{}{ - "key": map[string]interface{}{ + err = refresh(conf.Map(map[string]any{ + "key": map[string]any{ "value": "42", }, })) assert.ThatError(t, err).Matches("bind path= type=int error << property key isn't simple value") var wg sync.WaitGroup - for i := 0; i < 5; i++ { + for i := range 5 { n := i wg.Add(1) go func() { @@ -77,14 +77,14 @@ func TestValue(t *testing.T) { } time.Sleep(50 * time.Millisecond) - err = refresh(conf.Map(map[string]interface{}{ + err = refresh(conf.Map(map[string]any{ "key": 59, })) assert.Nil(t, err) wg.Wait() - b, err := json.Marshal(map[string]interface{}{"key": &v}) + b, err := json.Marshal(map[string]any{"key": &v}) assert.Nil(t, err) assert.ThatString(t, string(b)).JsonEqual(`{"key":59}`) } @@ -100,7 +100,7 @@ func TestDync(t *testing.T) { }, "mock panic") assert.Panic(t, func() { - prop := conf.Map(map[string]interface{}{ + prop := conf.Map(map[string]any{ "error.key": "value", }) _ = p.Refresh(prop) @@ -113,7 +113,7 @@ func TestDync(t *testing.T) { p := New(conf.New()) assert.That(t, p.ObjectsCount()).Equal(0) - prop := conf.Map(map[string]interface{}{ + prop := conf.Map(map[string]any{ "config.s1.value": "99", }) err := p.Refresh(prop) @@ -141,7 +141,7 @@ func TestDync(t *testing.T) { assert.That(t, cfg.S1.Value.Value()).Equal(99) assert.That(t, cfg.S2.Value.Value()).Equal(123) - prop = conf.Map(map[string]interface{}{ + prop = conf.Map(map[string]any{ "config.s1.value": "99", "config.s2.value": "456", "config.s4.value": "123", @@ -151,7 +151,7 @@ func TestDync(t *testing.T) { assert.That(t, cfg.S1.Value.Value()).Equal(99) assert.That(t, cfg.S2.Value.Value()).Equal(456) - prop = conf.Map(map[string]interface{}{ + prop = conf.Map(map[string]any{ "config.s1.value": "99", "config.s2.value": "456", "config.s3.value": "xyz", @@ -161,7 +161,7 @@ func TestDync(t *testing.T) { assert.That(t, cfg.S1.Value.Value()).Equal(99) assert.That(t, cfg.S2.Value.Value()).Equal(456) - prop = conf.Map(map[string]interface{}{ + prop = conf.Map(map[string]any{ "config.s1.value": "xyz", "config.s2.value": "abc", "config.s3.value": "xyz", @@ -183,7 +183,7 @@ func TestDync(t *testing.T) { }) t.Run("refresh struct", func(t *testing.T) { - p := New(conf.Map(map[string]interface{}{ + p := New(conf.Map(map[string]any{ "config.s1.value": "99", })) @@ -201,12 +201,12 @@ func TestDync(t *testing.T) { assert.Nil(t, err) assert.That(t, v.Value().S1.Value).Equal(99) - err = p.Refresh(conf.Map(map[string]interface{}{ + err = p.Refresh(conf.Map(map[string]any{ "config.s1.value": "xyz", })) assert.ThatError(t, err).Matches("strconv.ParseInt: parsing \"xyz\": invalid syntax") - err = p.Refresh(conf.Map(map[string]interface{}{ + err = p.Refresh(conf.Map(map[string]any{ "config.s1.value": "10", })) assert.Nil(t, err) diff --git a/gs/internal/gs_util/util.go b/gs/internal/gs_util/util.go index 7317c864..648e9674 100644 --- a/gs/internal/gs_util/util.go +++ b/gs/internal/gs_util/util.go @@ -23,7 +23,7 @@ import ( // GetBeforeItems is a function type that returns a list of items // that must appear before the given current item in the sorting order. -type GetBeforeItems func(sorting *list.List, current interface{}) *list.List +type GetBeforeItems func(sorting *list.List, current any) *list.List // TripleSort performs a three-way sort (processing, toSort, sorted) // to resolve dependencies and return a sorted list. @@ -49,7 +49,7 @@ func TripleSort(sorting *list.List, fn GetBeforeItems) (*list.List, error) { // searchInList searches for an element `v` in the list `l`. // If the element exists, it returns a pointer to the list element. Otherwise, it returns nil. -func searchInList(l *list.List, v interface{}) *list.Element { +func searchInList(l *list.List, v any) *list.Element { for e := l.Front(); e != nil; e = e.Next() { if e.Value == v { return e @@ -67,7 +67,7 @@ func searchInList(l *list.List, v interface{}) *list.Element { // - current: The current item being processed (nil for the first item). // - fn: A function that retrieves the list of items that must appear before the current item. func tripleSortByAfter(sorting *list.List, toSort *list.List, sorted *list.List, - processing *list.List, current interface{}, fn GetBeforeItems) error { + processing *list.List, current any, fn GetBeforeItems) error { // If no current item is specified, remove and process the first item in the `toSort` list. if current == nil { diff --git a/gs/internal/gs_util/util_test.go b/gs/internal/gs_util/util_test.go index 5b954e5c..68ae5a6b 100644 --- a/gs/internal/gs_util/util_test.go +++ b/gs/internal/gs_util/util_test.go @@ -34,7 +34,7 @@ func TestTripleSort(t *testing.T) { }) t.Run("single element", func(t *testing.T) { - getBefore := func(_ *list.List, _ interface{}) *list.List { + getBefore := func(_ *list.List, _ any) *list.List { return list.New() } sorting := util.ListOf("A") @@ -46,7 +46,7 @@ func TestTripleSort(t *testing.T) { t.Run("independent elements", func(t *testing.T) { // A、B、C - getBefore := func(_ *list.List, _ interface{}) *list.List { + getBefore := func(_ *list.List, _ any) *list.List { return list.New() } sorting := util.ListOf("A", "B", "C") @@ -57,7 +57,7 @@ func TestTripleSort(t *testing.T) { t.Run("linear dependency", func(t *testing.T) { // A -> B -> C - getBefore := func(_ *list.List, current interface{}) *list.List { + getBefore := func(_ *list.List, current any) *list.List { l := list.New() switch current { case "A": @@ -75,7 +75,7 @@ func TestTripleSort(t *testing.T) { t.Run("multiple dependencies", func(t *testing.T) { // A -> B&C, B -> C - getBefore := func(_ *list.List, current interface{}) *list.List { + getBefore := func(_ *list.List, current any) *list.List { l := list.New() switch current { case "A": @@ -94,7 +94,7 @@ func TestTripleSort(t *testing.T) { t.Run("cycle", func(t *testing.T) { // A -> B -> C -> A - getBefore := func(_ *list.List, current interface{}) *list.List { + getBefore := func(_ *list.List, current any) *list.List { l := list.New() switch current { case "A": diff --git a/util/errutil/errutil.go b/util/errutil/errutil.go index 5fea76db..0217e7d4 100644 --- a/util/errutil/errutil.go +++ b/util/errutil/errutil.go @@ -24,7 +24,7 @@ import ( var LineBreak = " << " // WrapError wraps an existing error, creating a new error with hierarchical relationships. -func WrapError(err error, format string, args ...interface{}) error { +func WrapError(err error, format string, args ...any) error { msg := fmt.Sprintf(format, args...) return fmt.Errorf("%s"+LineBreak+"%w", msg, err) } diff --git a/util/flat.go b/util/flat.go index 782e5ce0..3aa7302c 100644 --- a/util/flat.go +++ b/util/flat.go @@ -26,7 +26,7 @@ import ( // FlattenMap flattens a nested map, array, or slice into a single-level map // with string keys and string values. It recursively processes each element // of the input map and adds its flattened representation to the result map. -func FlattenMap(m map[string]interface{}) map[string]string { +func FlattenMap(m map[string]any) map[string]string { result := make(map[string]string) for key, val := range m { FlattenValue(key, val, result) @@ -36,7 +36,7 @@ func FlattenMap(m map[string]interface{}) map[string]string { // FlattenValue flattens a single value (which can be a map, array, slice, // or other types) into the result map. -func FlattenValue(key string, val interface{}, result map[string]string) { +func FlattenValue(key string, val any, result map[string]string) { if val == nil { return } @@ -57,7 +57,7 @@ func FlattenValue(key string, val interface{}, result map[string]string) { result[key] = "" return } - for i := 0; i < v.Len(); i++ { + for i := range v.Len() { subKey := fmt.Sprintf("%s[%d]", key, i) subValue := v.Index(i).Interface() // If an element is nil, treat it as an empty value and assign an empty string. diff --git a/util/flat_test.go b/util/flat_test.go index 9f710d89..8a0d7598 100644 --- a/util/flat_test.go +++ b/util/flat_test.go @@ -24,23 +24,23 @@ import ( ) func TestFlatten(t *testing.T) { - m := util.FlattenMap(map[string]interface{}{ + m := util.FlattenMap(map[string]any{ "int": 123, "str": "abc", - "arr": []interface{}{ + "arr": []any{ "abc", "def", - map[string]interface{}{ + map[string]any{ "a": "123", "b": "456", }, nil, - ([]interface{})(nil), // it doesn't equal to nil + ([]any)(nil), // it doesn't equal to nil (map[string]string)(nil), // it doesn't equal to nil - []interface{}{}, + []any{}, map[string]string{}, }, - "map": map[string]interface{}{ + "map": map[string]any{ "a": "123", "b": "456", "arr": []string{ @@ -48,15 +48,15 @@ func TestFlatten(t *testing.T) { "def", }, "nil": nil, - "nil_arr": []interface{}(nil), // it doesn't equal to nil + "nil_arr": []any(nil), // it doesn't equal to nil "nil_map": map[string]string(nil), // it doesn't equal to nil - "empty_arr": []interface{}{}, + "empty_arr": []any{}, "empty_map": map[string]string{}, }, "nil": nil, - "nil_arr": []interface{}(nil), // it doesn't equal to nil + "nil_arr": []any(nil), // it doesn't equal to nil "nil_map": map[string]string(nil), // it doesn't equal to nil - "empty_arr": []interface{}{}, + "empty_arr": []any{}, "empty_map": map[string]string{}, }) expect := map[string]string{ diff --git a/util/goutil/goutil.go b/util/goutil/goutil.go index 4ae1c5cd..44613573 100644 --- a/util/goutil/goutil.go +++ b/util/goutil/goutil.go @@ -36,7 +36,7 @@ import ( ) // OnPanic is a global callback function triggered when a panic occurs. -var OnPanic = func(ctx context.Context, r interface{}) { +var OnPanic = func(ctx context.Context, r any) { syslog.Errorf("panic: %v\n%s", r, debug.Stack()) } diff --git a/util/goutil/goutil_test.go b/util/goutil/goutil_test.go index 92a57cad..2f7ff79c 100644 --- a/util/goutil/goutil_test.go +++ b/util/goutil/goutil_test.go @@ -68,7 +68,7 @@ func TestGoValue(t *testing.T) { assert.Nil(t, err) var arr []*goutil.ValueStatus[int] - for i := 0; i < 3; i++ { + for i := range 3 { arr = append(arr, goutil.GoValue(t.Context(), func(ctx context.Context) (int, error) { return i, nil })) diff --git a/util/type_test.go b/util/type_test.go index d1eecfb0..d5ce06a2 100644 --- a/util/type_test.go +++ b/util/type_test.go @@ -60,7 +60,7 @@ func TestIsConstructor(t *testing.T) { func TestIsPropBindingTarget(t *testing.T) { data := []struct { - i interface{} + i any v bool }{ {true, true}, // Bool @@ -109,7 +109,7 @@ func TestIsPropBindingTarget(t *testing.T) { func TestIsBeanType(t *testing.T) { data := []struct { - i interface{} + i any v bool }{ {true, false}, // Bool diff --git a/util/value.go b/util/value.go index b0c567e7..b4fccd03 100644 --- a/util/value.go +++ b/util/value.go @@ -41,7 +41,7 @@ func PatchValue(v reflect.Value) reflect.Value { } // FuncName returns the function name for a given function. -func FuncName(fn interface{}) string { +func FuncName(fn any) string { _, _, fnName := FileLine(fn) return fnName } @@ -49,7 +49,7 @@ func FuncName(fn interface{}) string { // FileLine returns the file, line number, and function name for a given function. // It uses reflection and runtime information to extract these details. // 'fn' is expected to be a function or method value. -func FileLine(fn interface{}) (file string, line int, fnName string) { +func FileLine(fn any) (file string, line int, fnName string) { fnPtr := reflect.ValueOf(fn).Pointer() fnInfo := runtime.FuncForPC(fnPtr) diff --git a/util/value_test.go b/util/value_test.go index c13fa8a9..093e53bd 100644 --- a/util/value_test.go +++ b/util/value_test.go @@ -57,7 +57,7 @@ func (r *receiver) ptrFnWithArgs(i int) {} func TestFileLine(t *testing.T) { testcases := []struct { - fn interface{} + fn any file string line int fnName string