From ee0a86f0c91235c44b270d5102ea3a139cb2a68d Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Thu, 17 Oct 2024 18:07:32 +0200 Subject: [PATCH] Modernize --- CHANGELOG.md | 2 +- README.md | 6 ++--- cmd/liquid/main.go | 12 +++++----- cmd/liquid/main_test.go | 4 ++-- drops.go | 4 ++-- drops_test.go | 18 +++++++------- engine.go | 2 +- engine_examples_test.go | 10 ++++---- engine_test.go | 12 +++++----- evaluator/evaluator.go | 8 +++---- expressions/config.go | 2 +- expressions/context.go | 16 ++++++------- expressions/expressions.go | 12 +++++----- expressions/expressions.y | 2 +- expressions/expressions_test.go | 20 ++++++++-------- expressions/filters.go | 10 ++++---- expressions/filters_test.go | 8 +++---- expressions/functional.go | 10 ++++---- expressions/functional_test.go | 4 ++-- expressions/parser.go | 2 +- expressions/parser_test.go | 4 ++-- expressions/y.go | 2 +- filters/sort_filters.go | 16 ++++++------- filters/standard_filters.go | 40 ++++++++++++++++---------------- filters/standard_filters_test.go | 32 ++++++++++++------------- liquid.go | 6 ++--- liquid_test.go | 10 ++++---- parser/error.go | 2 +- render/context.go | 30 ++++++++++++------------ render/context_test.go | 4 ++-- render/error.go | 2 +- render/node_context.go | 8 +++---- render/render.go | 4 ++-- render/render_test.go | 8 +++---- tags/control_flow_tags.go | 6 ++--- tags/include_tag.go | 2 +- tags/include_tag_test.go | 2 +- tags/iteration_tags.go | 38 +++++++++++++++--------------- tags/iteration_tags_test.go | 8 +++---- tags/standard_tags_test.go | 10 ++++---- values/arrays.go | 2 +- values/call.go | 8 +++---- values/call_test.go | 22 +++++++++--------- values/compare.go | 4 ++-- values/compare_test.go | 4 ++-- values/convert.go | 14 +++++------ values/convert_test.go | 30 ++++++++++++------------ values/drop.go | 6 ++--- values/drop_test.go | 4 ++-- values/evaluator_test.go | 24 +++++++++---------- values/mapslicevalue.go | 2 +- values/predicates.go | 2 +- values/predicates_test.go | 4 ++-- values/range.go | 6 ++--- values/sort.go | 12 +++++----- values/value.go | 8 +++---- values/value_test.go | 22 +++++++++--------- 57 files changed, 286 insertions(+), 286 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b573a17..89c5c77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -115,7 +115,7 @@ Contributions: * Fix array[nil] ([e39a1fe](https://github.com/osteele/liquid/commit/e39a1fe)) * Fix file not found tests for Windows ([068afef](https://github.com/osteele/liquid/commit/068afef)) -* Restore m['str'] where m map[interface{}]interface{} +* Restore m['str'] where m map[any]any ([9852226](https://github.com/osteele/liquid/commit/9852226)) ### Docs diff --git a/README.md b/README.md index 7061648..51ce9b9 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ generator. ```go engine := liquid.NewEngine() template := `

{{ page.title }}

` -bindings := map[string]interface{}{ +bindings := map[string]any{ "page": map[string]string{ "title": "Introduction", }, @@ -84,7 +84,7 @@ These features of Shopify Liquid aren't implemented: Drops have a different design from the Shopify (Ruby) implementation. A Ruby drop sets `liquid_attributes` to a list of attributes that are exposed to -Liquid. A Go drop implements `ToLiquid() interface{}`, that returns a proxy +Liquid. A Go drop implements `ToLiquid() any`, that returns a proxy object. Conventionally, the proxy is a `map` or `struct` that defines the exposed properties. See for additional information. @@ -92,7 +92,7 @@ additional information. ### Value Types `Render` and friends take a `Bindings` parameter. This is a map of `string` to -`interface{}`, that associates template variable names with Go values. +`any`, that associates template variable names with Go values. Any Go value can be used as a variable value. These values have special meaning: diff --git a/cmd/liquid/main.go b/cmd/liquid/main.go index e2421d8..51c4066 100644 --- a/cmd/liquid/main.go +++ b/cmd/liquid/main.go @@ -21,12 +21,12 @@ import ( // for testing var ( - stderr io.Writer = os.Stderr - stdout io.Writer = os.Stdout - stdin io.Reader = os.Stdin - exit func(int) = os.Exit - env func() []string = os.Environ - bindings map[string]interface{} = map[string]interface{}{} + stderr io.Writer = os.Stderr + stdout io.Writer = os.Stdout + stdin io.Reader = os.Stdin + exit func(int) = os.Exit + env func() []string = os.Environ + bindings map[string]any = map[string]any{} strictVars bool ) diff --git a/cmd/liquid/main_test.go b/cmd/liquid/main_test.go index 565a697..25e1542 100644 --- a/cmd/liquid/main_test.go +++ b/cmd/liquid/main_test.go @@ -18,7 +18,7 @@ func TestMain(t *testing.T) { stdin = os.Stdin exit = os.Exit env = os.Environ - bindings = map[string]interface{}{} + bindings = map[string]any{} }() exit = func(n int) { @@ -58,7 +58,7 @@ func TestMain(t *testing.T) { main() require.True(t, envCalled) require.Equal(t, "Hello, World!", buf.String()) - bindings = make(map[string]interface{}) + bindings = make(map[string]any) // filename stdin = os.Stdin diff --git a/drops.go b/drops.go index 6d00b68..f6d637a 100644 --- a/drops.go +++ b/drops.go @@ -2,12 +2,12 @@ package liquid // Drop indicates that the object will present to templates as its ToLiquid value. type Drop interface { - ToLiquid() interface{} + ToLiquid() any } // FromDrop returns returns object.ToLiquid() if object's type implement this function; // else the object itself. -func FromDrop(object interface{}) interface{} { +func FromDrop(object any) any { switch object := object.(type) { case Drop: return object.ToLiquid() diff --git a/drops_test.go b/drops_test.go index 4412154..d6f67cd 100644 --- a/drops_test.go +++ b/drops_test.go @@ -10,7 +10,7 @@ import ( type dropTest struct{} -func (d dropTest) ToLiquid() interface{} { return "drop" } +func (d dropTest) ToLiquid() any { return "drop" } func TestDrops(t *testing.T) { require.Equal(t, "drop", FromDrop(dropTest{})) @@ -20,8 +20,8 @@ func TestDrops(t *testing.T) { type redConvertible struct{} -func (c redConvertible) ToLiquid() interface{} { - return map[string]interface{}{ +func (c redConvertible) ToLiquid() any { + return map[string]any{ "color": "red", } } @@ -29,13 +29,13 @@ func (c redConvertible) ToLiquid() interface{} { func ExampleDrop_map() { // type redConvertible struct{} // - // func (c redConvertible) ToLiquid() interface{} { - // return map[string]interface{}{ + // func (c redConvertible) ToLiquid() any { + // return map[string]any{ // "color": "red", // } // } engine := NewEngine() - bindings := map[string]interface{}{ + bindings := map[string]any{ "car": redConvertible{}, } template := `{{ car.color }}` @@ -49,7 +49,7 @@ func ExampleDrop_map() { type car struct{ color, model string } -func (c car) ToLiquid() interface{} { +func (c car) ToLiquid() any { return carDrop{c.model, c.color} } @@ -65,7 +65,7 @@ func (c carDrop) Drive() string { func ExampleDrop_struct() { // type car struct{ color, model string } // - // func (c car) ToLiquid() interface{} { + // func (c car) ToLiquid() any { // return carDrop{c.model, c.color} // } // @@ -79,7 +79,7 @@ func ExampleDrop_struct() { // } engine := NewEngine() - bindings := map[string]interface{}{ + bindings := map[string]any{ "car": car{"blue", "S85"}, } template := `{{ car.color }} {{ car.Drive }} Model {{ car.Model }}` diff --git a/engine.go b/engine.go index c9a2866..5bfe89c 100644 --- a/engine.go +++ b/engine.go @@ -43,7 +43,7 @@ func (e *Engine) RegisterBlock(name string, td Renderer) { // * https://github.com/osteele/liquid/blob/main/filters/standard_filters.go // // * https://github.com/osteele/gojekyll/blob/master/filters/filters.go -func (e *Engine) RegisterFilter(name string, fn interface{}) { +func (e *Engine) RegisterFilter(name string, fn any) { e.cfg.AddFilter(name, fn) } diff --git a/engine_examples_test.go b/engine_examples_test.go index 73c551c..418d5ec 100644 --- a/engine_examples_test.go +++ b/engine_examples_test.go @@ -12,7 +12,7 @@ import ( func Example() { engine := NewEngine() source := `

{{ page.title }}

` - bindings := map[string]interface{}{ + bindings := map[string]any{ "page": map[string]string{ "title": "Introduction", }, @@ -28,7 +28,7 @@ func Example() { func ExampleEngine_ParseAndRenderString() { engine := NewEngine() source := `{{ hello | capitalize | append: " Mundo" }}` - bindings := map[string]interface{}{"hello": "hola"} + bindings := map[string]any{"hello": "hola"} out, err := engine.ParseAndRenderString(source, bindings) if err != nil { log.Fatalln(err) @@ -40,7 +40,7 @@ func ExampleEngine_ParseAndRenderString() { func ExampleEngine_ParseTemplate() { engine := NewEngine() source := `{{ hello | capitalize | append: " Mundo" }}` - bindings := map[string]interface{}{"hello": "hola"} + bindings := map[string]any{"hello": "hola"} tpl, err := engine.ParseString(source) if err != nil { log.Fatalln(err) @@ -57,7 +57,7 @@ func ExampleEngine_RegisterFilter() { engine := NewEngine() engine.RegisterFilter("has_prefix", strings.HasPrefix) template := `{{ title | has_prefix: "Intro" }}` - bindings := map[string]interface{}{ + bindings := map[string]any{ "title": "Introduction", } out, err := engine.ParseAndRenderString(template, bindings) @@ -78,7 +78,7 @@ func ExampleEngine_RegisterFilter_optional_argument() { return a + b(1) }) template := `10 + 1 = {{ m | inc }}; 20 + 5 = {{ n | inc: 5 }}` - bindings := map[string]interface{}{ + bindings := map[string]any{ "m": 10, "n": "20", } diff --git a/engine_test.go b/engine_test.go index e2bfda7..2a7a4a0 100644 --- a/engine_test.go +++ b/engine_test.go @@ -11,7 +11,7 @@ import ( "github.com/stretchr/testify/require" ) -var emptyBindings = map[string]interface{}{} +var emptyBindings = map[string]any{} // There's a lot more tests in the filters and tags sub-packages. // This collects a minimal set for testing end-to-end. @@ -21,10 +21,10 @@ var liquidTests = []struct{ in, expected string }{ {`{{ "upper" | upcase }}`, "UPPER"}, } -var testBindings = map[string]interface{}{ +var testBindings = map[string]any{ "x": 123, "ar": []string{"first", "second", "third"}, - "page": map[string]interface{}{ + "page": map[string]any{ "title": "Introduction", }, } @@ -61,8 +61,8 @@ func TestEngine_ParseAndFRender(t *testing.T) { } func TestEngine_ParseAndRenderString_ptr_to_hash(t *testing.T) { - params := map[string]interface{}{ - "message": &map[string]interface{}{ + params := map[string]any{ + "message": &map[string]any{ "Text": "hello", "jsonNumber": json.Number("123"), }, @@ -77,7 +77,7 @@ func TestEngine_ParseAndRenderString_ptr_to_hash(t *testing.T) { type testStruct struct{ Text string } func TestEngine_ParseAndRenderString_struct(t *testing.T) { - params := map[string]interface{}{ + params := map[string]any{ "message": testStruct{ Text: "hello", }, diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go index 497254d..85e9785 100644 --- a/evaluator/evaluator.go +++ b/evaluator/evaluator.go @@ -9,22 +9,22 @@ import ( ) // Convert should be replaced by values.Convert. -func Convert(value interface{}, typ reflect.Type) (interface{}, error) { +func Convert(value any, typ reflect.Type) (any, error) { return values.Convert(value, typ) } // MustConvertItem should be replaced by values.Convert. -func MustConvertItem(item interface{}, array interface{}) interface{} { +func MustConvertItem(item any, array any) any { return values.MustConvertItem(item, array) } // Sort should be replaced by values. -func Sort(data []interface{}) { +func Sort(data []any) { values.Sort(data) } // SortByProperty should be replaced by values.SortByProperty -func SortByProperty(data []interface{}, key string, nilFirst bool) { +func SortByProperty(data []any, key string, nilFirst bool) { values.SortByProperty(data, key, nilFirst) } diff --git a/expressions/config.go b/expressions/config.go index 6f8bc9c..268a4be 100644 --- a/expressions/config.go +++ b/expressions/config.go @@ -2,7 +2,7 @@ package expressions // Config holds configuration information for expression interpretation. type Config struct { - filters map[string]interface{} + filters map[string]any } // NewConfig creates a new Config. diff --git a/expressions/context.go b/expressions/context.go index c627ced..bfff234 100644 --- a/expressions/context.go +++ b/expressions/context.go @@ -4,26 +4,26 @@ import "github.com/osteele/liquid/values" // Context is the expression evaluation context. It maps variables names to values. type Context interface { - ApplyFilter(string, valueFn, []valueFn) (interface{}, error) + ApplyFilter(string, valueFn, []valueFn) (any, error) // Clone returns a copy with a new variable binding map // (so that copy.Set does effect the source context.) Clone() Context - Get(string) interface{} - Set(string, interface{}) + Get(string) any + Set(string, any) } type context struct { Config - bindings map[string]interface{} + bindings map[string]any } // NewContext makes a new expression evaluation context. -func NewContext(vars map[string]interface{}, cfg Config) Context { +func NewContext(vars map[string]any, cfg Config) Context { return &context{cfg, vars} } func (ctx *context) Clone() Context { - bindings := map[string]interface{}{} + bindings := map[string]any{} for k, v := range ctx.bindings { bindings[k] = v } @@ -31,11 +31,11 @@ func (ctx *context) Clone() Context { } // Get looks up a variable value in the expression context. -func (ctx *context) Get(name string) interface{} { +func (ctx *context) Get(name string) any { return values.ToLiquid(ctx.bindings[name]) } // Set sets a variable value in the expression context. -func (ctx *context) Set(name string, value interface{}) { +func (ctx *context) Set(name string, value any) { ctx.bindings[name] = value } diff --git a/expressions/expressions.go b/expressions/expressions.go index 61d98b6..4c89e5d 100644 --- a/expressions/expressions.go +++ b/expressions/expressions.go @@ -15,7 +15,7 @@ import ( // An Expression is a compiled expression. type Expression interface { // Evaluate evaluates an expression in a context. - Evaluate(ctx Context) (interface{}, error) + Evaluate(ctx Context) (any, error) } // A Closure is an expression within a lexical environment. @@ -23,8 +23,8 @@ type Expression interface { // environment. (Therefore it's not a technically a closure.) type Closure interface { // Bind creates a new closure with a new binding. - Bind(name string, value interface{}) Closure - Evaluate() (interface{}, error) + Bind(name string, value any) Closure + Evaluate() (any, error) } type closure struct { @@ -32,13 +32,13 @@ type closure struct { context Context } -func (c closure) Bind(name string, value interface{}) Closure { +func (c closure) Bind(name string, value any) Closure { ctx := c.context.Clone() ctx.Set(name, value) return closure{c.expr, ctx} } -func (c closure) Evaluate() (interface{}, error) { +func (c closure) Evaluate() (any, error) { return c.expr.Evaluate(c.context) } @@ -46,7 +46,7 @@ type expression struct { evaluator func(Context) values.Value } -func (e expression) Evaluate(ctx Context) (out interface{}, err error) { +func (e expression) Evaluate(ctx Context) (out any, err error) { defer func() { if r := recover(); r != nil { switch e := r.(type) { diff --git a/expressions/expressions.y b/expressions/expressions.y index 3ca03b8..39c47c3 100644 --- a/expressions/expressions.y +++ b/expressions/expressions.y @@ -14,7 +14,7 @@ func init() { %} %union { name string - val interface{} + val any f func(Context) values.Value s string ss []string diff --git a/expressions/expressions_test.go b/expressions/expressions_test.go index ef4fd3c..dda9b74 100644 --- a/expressions/expressions_test.go +++ b/expressions/expressions_test.go @@ -12,7 +12,7 @@ import ( var evaluatorTests = []struct { in string - expected interface{} + expected any }{ // Literals {`12`, 12}, @@ -113,19 +113,19 @@ var evaluatorTests = []struct { {`"seafood" | length`, 8}, } -var evaluatorTestBindings = (map[string]interface{}{ +var evaluatorTestBindings = (map[string]any{ "n": 123, "array": []string{"first", "second", "third"}, - "interface_array": []interface{}{"first", "second", "third"}, - "empty_list": []interface{}{}, + "interface_array": []any{"first", "second", "third"}, + "empty_list": []any{}, "fruits": []string{"apples", "oranges", "peaches", "plums"}, - "hash": map[string]interface{}{ + "hash": map[string]any{ "a": "first", - "b": map[string]interface{}{"c": "d"}, + "b": map[string]any{"c": "d"}, "c": []string{"r", "g", "b"}, }, - "hash_with_size_key": map[string]interface{}{"size": "key_value"}, - "range": map[string]interface{}{ + "hash_with_size_key": map[string]any{"size": "key_value"}, + "range": map[string]any{ "begin": 1, "end": 5, }, @@ -149,14 +149,14 @@ func TestEvaluateString(t *testing.T) { _, err = EvaluateString("1 | undefined_filter", ctx) require.Error(t, err) - cfg.AddFilter("error", func(input interface{}) (string, error) { return "", errors.New("test error") }) + cfg.AddFilter("error", func(input any) (string, error) { return "", errors.New("test error") }) _, err = EvaluateString("1 | error", ctx) require.Error(t, err) } func TestClosure(t *testing.T) { cfg := NewConfig() - ctx := NewContext(map[string]interface{}{"x": 1}, cfg) + ctx := NewContext(map[string]any{"x": 1}, cfg) expr, err := Parse("x") require.NoError(t, err) c1 := closure{expr, ctx} diff --git a/expressions/filters.go b/expressions/filters.go index a8c3c6a..d89e28a 100644 --- a/expressions/filters.go +++ b/expressions/filters.go @@ -34,7 +34,7 @@ func (e FilterError) Error() string { type valueFn func(Context) values.Value // AddFilter adds a filter to the filter dictionary. -func (c *Config) AddFilter(name string, fn interface{}) { +func (c *Config) AddFilter(name string, fn any) { rf := reflect.ValueOf(fn) switch { case rf.Kind() != reflect.Func: @@ -47,27 +47,27 @@ func (c *Config) AddFilter(name string, fn interface{}) { // panic(typeError("a filter's second output must be type error")) } if len(c.filters) == 0 { - c.filters = make(map[string]interface{}) + c.filters = make(map[string]any) } c.filters[name] = fn } var ( closureType = reflect.TypeOf(closure{}) - interfaceType = reflect.TypeOf([]interface{}{}).Elem() + interfaceType = reflect.TypeOf([]any{}).Elem() ) func isClosureInterfaceType(t reflect.Type) bool { return closureType.ConvertibleTo(t) && !interfaceType.ConvertibleTo(t) } -func (ctx *context) ApplyFilter(name string, receiver valueFn, params []valueFn) (interface{}, error) { +func (ctx *context) ApplyFilter(name string, receiver valueFn, params []valueFn) (any, error) { filter, ok := ctx.filters[name] if !ok { panic(UndefinedFilter(name)) } fr := reflect.ValueOf(filter) - args := []interface{}{receiver(ctx).Interface()} + args := []any{receiver(ctx).Interface()} for i, param := range params { if i+1 < fr.Type().NumIn() && isClosureInterfaceType(fr.Type().In(i+1)) { expr, err := Parse(param(ctx).Interface().(string)) diff --git a/expressions/filters_test.go b/expressions/filters_test.go index ae06f9e..b7d6ac0 100644 --- a/expressions/filters_test.go +++ b/expressions/filters_test.go @@ -22,7 +22,7 @@ func TestContext_AddFilter(t *testing.T) { func TestContext_runFilter(t *testing.T) { cfg := NewConfig() - constant := func(value interface{}) valueFn { + constant := func(value any) valueFn { return func(Context) values.Value { return values.ValueOf(value) } } receiver := constant("self") @@ -31,7 +31,7 @@ func TestContext_runFilter(t *testing.T) { cfg.AddFilter("f1", func(s string) string { return "<" + s + ">" }) - ctx := NewContext(map[string]interface{}{"x": 10}, cfg) + ctx := NewContext(map[string]any{"x": 10}, cfg) out, err := ctx.ApplyFilter("f1", receiver, []valueFn{}) require.NoError(t, err) require.Equal(t, "", out) @@ -40,7 +40,7 @@ func TestContext_runFilter(t *testing.T) { cfg.AddFilter("with_arg", func(a, b string) string { return fmt.Sprintf("(%s, %s)", a, b) }) - ctx = NewContext(map[string]interface{}{"x": 10}, cfg) + ctx = NewContext(map[string]any{"x": 10}, cfg) out, err = ctx.ApplyFilter("with_arg", receiver, []valueFn{constant("arg")}) require.NoError(t, err) require.Equal(t, "(self, arg)", out) @@ -66,7 +66,7 @@ func TestContext_runFilter(t *testing.T) { } return fmt.Sprintf("(%v, %v)", a, value), nil }) - ctx = NewContext(map[string]interface{}{"x": 10}, cfg) + ctx = NewContext(map[string]any{"x": 10}, cfg) out, err = ctx.ApplyFilter("closure", receiver, []valueFn{constant("x |add: y")}) require.NoError(t, err) require.Equal(t, "(self, 11)", out) diff --git a/expressions/functional.go b/expressions/functional.go index dcaa978..a891741 100644 --- a/expressions/functional.go +++ b/expressions/functional.go @@ -1,17 +1,17 @@ package expressions type expressionWrapper struct { - fn func(ctx Context) (interface{}, error) + fn func(ctx Context) (any, error) } -func (w expressionWrapper) Evaluate(ctx Context) (interface{}, error) { +func (w expressionWrapper) Evaluate(ctx Context) (any, error) { return w.fn(ctx) } // Constant creates an expression that returns a constant value. -func Constant(k interface{}) Expression { +func Constant(k any) Expression { return expressionWrapper{ - func(_ Context) (interface{}, error) { + func(_ Context) (any, error) { return k, nil }, } @@ -20,7 +20,7 @@ func Constant(k interface{}) Expression { // Not creates an expression that returns ! of the wrapped expression. func Not(e Expression) Expression { return expressionWrapper{ - func(ctx Context) (interface{}, error) { + func(ctx Context) (any, error) { value, err := e.Evaluate(ctx) if err != nil { return nil, err diff --git a/expressions/functional_test.go b/expressions/functional_test.go index 91ff90f..4d1a292 100644 --- a/expressions/functional_test.go +++ b/expressions/functional_test.go @@ -7,7 +7,7 @@ import ( ) func TestConstant(t *testing.T) { - ctx := NewContext(map[string]interface{}{}, NewConfig()) + ctx := NewContext(map[string]any{}, NewConfig()) k := Constant(10) v, err := k.Evaluate(ctx) require.NoError(t, err) @@ -15,7 +15,7 @@ func TestConstant(t *testing.T) { } func TestNot(t *testing.T) { - ctx := NewContext(map[string]interface{}{}, NewConfig()) + ctx := NewContext(map[string]any{}, NewConfig()) k := Constant(10) v, err := Not(k).Evaluate(ctx) require.NoError(t, err) diff --git a/expressions/parser.go b/expressions/parser.go index 88c2c82..1d9b068 100644 --- a/expressions/parser.go +++ b/expressions/parser.go @@ -56,7 +56,7 @@ func parse(source string) (p *parseValue, err error) { } // EvaluateString is a wrapper for Parse and Evaluate. -func EvaluateString(source string, ctx Context) (interface{}, error) { +func EvaluateString(source string, ctx Context) (any, error) { expr, err := Parse(source) if err != nil { return nil, err diff --git a/expressions/parser_test.go b/expressions/parser_test.go index 8517b27..bef6a44 100644 --- a/expressions/parser_test.go +++ b/expressions/parser_test.go @@ -9,7 +9,7 @@ import ( var parseTests = []struct { in string - expect interface{} + expect any }{ {`true`, true}, {`false`, false}, @@ -37,7 +37,7 @@ var parseErrorTests = []struct{ in, expected string }{ func TestParse(t *testing.T) { cfg := NewConfig() cfg.AddFilter("add", func(a, b int) int { return a + b }) - ctx := NewContext(map[string]interface{}{ + ctx := NewContext(map[string]any{ "a": 1, "b": 2, "obj": map[string]int{"prop": 2}, diff --git a/expressions/y.go b/expressions/y.go index 14dc9ed..37da43b 100644 --- a/expressions/y.go +++ b/expressions/y.go @@ -21,7 +21,7 @@ func init() { type yySymType struct { yys int name string - val interface{} + val any f func(Context) values.Value s string ss []string diff --git a/filters/sort_filters.go b/filters/sort_filters.go index 7ef2fb0..d563a69 100644 --- a/filters/sort_filters.go +++ b/filters/sort_filters.go @@ -9,8 +9,8 @@ import ( "github.com/osteele/liquid/values" ) -func sortFilter(array []interface{}, key interface{}) []interface{} { - result := make([]interface{}, len(array)) +func sortFilter(array []any, key any) []any { + result := make([]any, len(array)) copy(result, array) if key == nil { values.Sort(result) @@ -20,13 +20,13 @@ func sortFilter(array []interface{}, key interface{}) []interface{} { return result } -func sortNaturalFilter(array []interface{}, key interface{}) interface{} { - result := make([]interface{}, len(array)) +func sortNaturalFilter(array []any, key any) any { + result := make([]any, len(array)) copy(result, array) switch { case reflect.ValueOf(array).Len() == 0: case key != nil: - sort.Sort(keySortable{result, func(m interface{}) string { + sort.Sort(keySortable{result, func(m any) string { rv := reflect.ValueOf(m) if rv.Kind() != reflect.Map { return "" @@ -40,7 +40,7 @@ func sortNaturalFilter(array []interface{}, key interface{}) interface{} { return "" }}) case reflect.TypeOf(array[0]).Kind() == reflect.String: - sort.Sort(keySortable{result, func(s interface{}) string { + sort.Sort(keySortable{result, func(s any) string { return strings.ToUpper(s.(string)) }}) } @@ -48,8 +48,8 @@ func sortNaturalFilter(array []interface{}, key interface{}) interface{} { } type keySortable struct { - slice []interface{} - keyFn func(interface{}) string + slice []any + keyFn func(any) string } // Len is part of sort.Interface. diff --git a/filters/standard_filters.go b/filters/standard_filters.go index 11fdc2f..9d03a0a 100644 --- a/filters/standard_filters.go +++ b/filters/standard_filters.go @@ -19,25 +19,25 @@ import ( // A FilterDictionary holds filters. type FilterDictionary interface { - AddFilter(string, interface{}) + AddFilter(string, any) } // AddStandardFilters defines the standard Liquid filters. func AddStandardFilters(fd FilterDictionary) { //nolint: gocyclo // value filters - fd.AddFilter("default", func(value, defaultValue interface{}) interface{} { + fd.AddFilter("default", func(value, defaultValue any) any { if value == nil || value == false || values.IsEmpty(value) { value = defaultValue } return value }) - fd.AddFilter("json", func(a interface{}) interface{} { + fd.AddFilter("json", func(a any) any { result, _ := json.Marshal(a) return result }) // array filters - fd.AddFilter("compact", func(a []interface{}) (result []interface{}) { + fd.AddFilter("compact", func(a []any) (result []any) { for _, item := range a { if item != nil { result = append(result, item) @@ -45,12 +45,12 @@ func AddStandardFilters(fd FilterDictionary) { //nolint: gocyclo } return }) - fd.AddFilter("concat", func(a, b []interface{}) (result []interface{}) { - result = make([]interface{}, 0, len(a)+len(b)) + fd.AddFilter("concat", func(a, b []any) (result []any) { + result = make([]any, 0, len(a)+len(b)) return append(append(result, a...), b...) }) fd.AddFilter("join", joinFilter) - fd.AddFilter("map", func(a []interface{}, key string) (result []interface{}) { + fd.AddFilter("map", func(a []any, key string) (result []any) { keyValue := values.ValueOf(key) for _, obj := range a { value := values.ValueOf(obj) @@ -62,13 +62,13 @@ func AddStandardFilters(fd FilterDictionary) { //nolint: gocyclo fd.AddFilter("sort", sortFilter) // https://shopify.github.io/liquid/ does not demonstrate first and last as filters, // but https://help.shopify.com/themes/liquid/filters/array-filters does - fd.AddFilter("first", func(a []interface{}) interface{} { + fd.AddFilter("first", func(a []any) any { if len(a) == 0 { return nil } return a[0] }) - fd.AddFilter("last", func(a []interface{}) interface{} { + fd.AddFilter("last", func(a []any) any { if len(a) == 0 { return nil } @@ -100,7 +100,7 @@ func AddStandardFilters(fd FilterDictionary) { //nolint: gocyclo fd.AddFilter("times", func(a, b float64) float64 { return a * b }) - fd.AddFilter("divided_by", func(a float64, b interface{}) interface{} { + fd.AddFilter("divided_by", func(a float64, b any) any { switch q := b.(type) { case int, int16, int32, int64: return int(a) / q.(int) @@ -205,19 +205,19 @@ func AddStandardFilters(fd FilterDictionary) { //nolint: gocyclo // debugging filters // inspect is from Jekyll - fd.AddFilter("inspect", func(value interface{}) string { + fd.AddFilter("inspect", func(value any) string { s, err := json.Marshal(value) if err != nil { return fmt.Sprintf("%#v", value) } return string(s) }) - fd.AddFilter("type", func(value interface{}) string { + fd.AddFilter("type", func(value any) string { return fmt.Sprintf("%T", value) }) } -func joinFilter(a []interface{}, sep func(string) string) interface{} { +func joinFilter(a []any, sep func(string) string) any { ss := make([]string, 0, len(a)) s := sep(" ") for _, v := range a { @@ -228,8 +228,8 @@ func joinFilter(a []interface{}, sep func(string) string) interface{} { return strings.Join(ss, s) } -func reverseFilter(a []interface{}) interface{} { - result := make([]interface{}, len(a)) +func reverseFilter(a []any) any { + result := make([]any, len(a)) for i, x := range a { result[len(result)-1-i] = x } @@ -238,7 +238,7 @@ func reverseFilter(a []interface{}) interface{} { var wsre = regexp.MustCompile(`[[:space:]]+`) -func splitFilter(s, sep string) interface{} { +func splitFilter(s, sep string) any { result := strings.Split(s, sep) if sep == " " { // Special case for Ruby, therefore Liquid @@ -251,9 +251,9 @@ func splitFilter(s, sep string) interface{} { return result } -func uniqFilter(a []interface{}) (result []interface{}) { - seenMap := map[interface{}]bool{} - seen := func(item interface{}) bool { +func uniqFilter(a []any) (result []any) { + seenMap := map[any]bool{} + seen := func(item any) bool { if k := reflect.TypeOf(item).Kind(); k < reflect.Array || k == reflect.Ptr || k == reflect.UnsafePointer { if seenMap[item] { return true @@ -277,7 +277,7 @@ func uniqFilter(a []interface{}) (result []interface{}) { return } -func eqItems(a, b interface{}) bool { +func eqItems(a, b any) bool { if reflect.TypeOf(a).Comparable() && reflect.TypeOf(b).Comparable() { return a == b } diff --git a/filters/standard_filters_test.go b/filters/standard_filters_test.go index dadadf8..194cf2b 100644 --- a/filters/standard_filters_test.go +++ b/filters/standard_filters_test.go @@ -13,7 +13,7 @@ import ( var filterTests = []struct { in string - expected interface{} + expected any }{ // value filters {`undefined | default: 2.99`, 2.99}, @@ -197,27 +197,27 @@ Liquid" | slice: 2, 4`, "quid"}, {`"1" | type`, `string`}, } -var filterTestBindings = map[string]interface{}{ - "empty_array": []interface{}{}, - "empty_map": map[string]interface{}{}, +var filterTestBindings = map[string]any{ + "empty_array": []any{}, + "empty_map": map[string]any{}, "empty_map_slice": yaml.MapSlice{}, - "map": map[string]interface{}{ + "map": map[string]any{ "a": 1, }, "map_slice_2": yaml.MapSlice{{Key: 1, Value: "b"}, {Key: 2, Value: "a"}}, "map_slice_dup": yaml.MapSlice{{Key: 1, Value: "a"}, {Key: 2, Value: "a"}, {Key: 3, Value: "b"}}, "map_slice_has_nil": yaml.MapSlice{{Key: 1, Value: "a"}, {Key: 2, Value: nil}, {Key: 3, Value: "b"}}, "map_slice_objs": yaml.MapSlice{ - {Key: 1, Value: map[string]interface{}{"key": "a"}}, - {Key: 2, Value: map[string]interface{}{"key": "b"}}, + {Key: 1, Value: map[string]any{"key": "a"}}, + {Key: 2, Value: map[string]any{"key": "b"}}, }, "mixed_case_array": []string{"c", "a", "B"}, - "mixed_case_hash_values": []map[string]interface{}{ + "mixed_case_hash_values": []map[string]any{ {"key": "c"}, {"key": "a"}, {"key": "B"}, }, - "sort_prop": []map[string]interface{}{ + "sort_prop": []map[string]any{ {"weight": 1}, {"weight": 5}, {"weight": 3}, @@ -230,13 +230,13 @@ var filterTestBindings = map[string]interface{}{ // for examples from liquid docs "animals": []string{"zebra", "octopus", "giraffe", "Sally Snake"}, "fruits": []string{"apples", "oranges", "peaches", "plums"}, - "article": map[string]interface{}{ + "article": map[string]any{ "published_at": timeMustParse("2015-07-17T15:04:05Z"), }, - "page": map[string]interface{}{ + "page": map[string]any{ "title": "Introduction", }, - "pages": []map[string]interface{}{ + "pages": []map[string]any{ {"name": "page 1", "category": "business"}, {"name": "page 2", "category": "celebrities"}, {"name": "page 3"}, @@ -258,11 +258,11 @@ func TestFilters(t *testing.T) { t.Setenv("TZ", "America/New_York") var ( - m1 = map[string]interface{}{"name": "m1"} - m2 = map[string]interface{}{"name": "m2"} - m3 = map[string]interface{}{"name": "m3"} + m1 = map[string]any{"name": "m1"} + m2 = map[string]any{"name": "m2"} + m3 = map[string]any{"name": "m3"} ) - filterTestBindings["dup_maps"] = []interface{}{m1, m2, m1, m3} + filterTestBindings["dup_maps"] = []any{m1, m2, m1, m3} cfg := expressions.NewConfig() AddStandardFilters(&cfg) diff --git a/liquid.go b/liquid.go index 249492d..addf3eb 100644 --- a/liquid.go +++ b/liquid.go @@ -15,8 +15,8 @@ import ( // Bindings is a map of variable names to values. // // Clients need not use this type. It is used solely for documentation. Callers can use instances -// of map[string]interface{} itself as argument values to functions declared with this parameter type. -type Bindings map[string]interface{} +// of map[string]any itself as argument values to functions declared with this parameter type. +type Bindings map[string]any // A Renderer returns the rendered string for a block. This is the type of a tag definition. // @@ -35,6 +35,6 @@ type SourceError interface { // IterationKeyedMap returns a map whose {% for %} tag iteration values are its keys, instead of [key, value] pairs. // Use this to create a Go map with the semantics of a Ruby struct drop. -func IterationKeyedMap(m map[string]interface{}) tags.IterationKeyedMap { +func IterationKeyedMap(m map[string]any) tags.IterationKeyedMap { return m } diff --git a/liquid_test.go b/liquid_test.go index 0316b6a..96cce09 100644 --- a/liquid_test.go +++ b/liquid_test.go @@ -9,8 +9,8 @@ import ( ) func TestIterationKeyedMap(t *testing.T) { - vars := map[string]interface{}{ - "keyed_map": IterationKeyedMap(map[string]interface{}{"a": 1, "b": 2}), + vars := map[string]any{ + "keyed_map": IterationKeyedMap(map[string]any{"a": 1, "b": 2}), } engine := NewEngine() tpl, err := engine.ParseTemplate([]byte(`{% for k in keyed_map %}{{ k }}={{ keyed_map[k] }}.{% endfor %}`)) @@ -21,9 +21,9 @@ func TestIterationKeyedMap(t *testing.T) { } func ExampleIterationKeyedMap() { - vars := map[string]interface{}{ - "map": map[string]interface{}{"a": 1}, - "keyed_map": IterationKeyedMap(map[string]interface{}{"a": 1}), + vars := map[string]any{ + "map": map[string]any{"a": 1}, + "keyed_map": IterationKeyedMap(map[string]any{"a": 1}), } engine := NewEngine() out, err := engine.ParseAndRenderString( diff --git a/parser/error.go b/parser/error.go index dd824cd..26d8b9a 100644 --- a/parser/error.go +++ b/parser/error.go @@ -17,7 +17,7 @@ type Locatable interface { } // Errorf creates a parser.Error. -func Errorf(loc Locatable, format string, a ...interface{}) *sourceLocError { //nolint: golint +func Errorf(loc Locatable, format string, a ...any) *sourceLocError { //nolint: golint return &sourceLocError{loc.SourceLocation(), loc.SourceText(), fmt.Sprintf(format, a...), nil} } diff --git a/render/context.go b/render/context.go index 7c3ae23..63d7179 100644 --- a/render/context.go +++ b/render/context.go @@ -14,17 +14,17 @@ import ( // Context provides the rendering context for a tag renderer. type Context interface { // Bindings returns the current lexical environment. - Bindings() map[string]interface{} + Bindings() map[string]any // Get retrieves the value of a variable from the current lexical environment. - Get(name string) interface{} + Get(name string) any // Errorf creates a SourceError, that includes the source location. // Use this to distinguish errors in the template from implementation errors // in the template engine. - Errorf(format string, a ...interface{}) Error + Errorf(format string, a ...any) Error // Evaluate evaluates a compiled expression within the current lexical context. - Evaluate(expressions.Expression) (interface{}, error) + Evaluate(expressions.Expression) (any, error) // EvaluateString compiles and evaluates a string expression such as “x”, “x < 10", or “a.b | split | first | default: 10”, within the current lexical context. - EvaluateString(string) (interface{}, error) + EvaluateString(string) (any, error) // ExpandTagArg renders the current tag argument string as a Liquid template. // It enables the implementation of tags such as Jekyll's "{% include {{ page.my_variable }} %}" andjekyll-avatar's "{% avatar {{page.author}} %}". ExpandTagArg() (string, error) @@ -39,10 +39,10 @@ type Context interface { RenderChildren(io.Writer) Error // RenderFile parses and renders a template. It's used in the implementation of the {% include %} tag. // RenderFile does not cache the compiled template. - RenderFile(string, map[string]interface{}) (string, error) + RenderFile(string, map[string]any) (string, error) // Set updates the value of a variable in the current lexical environment. // It's used in the implementation of the {% assign %} and {% capture %} tags. - Set(name string, value interface{}) + Set(name string, value any) // SourceFile retrieves the value set by template.SetSourcePath. // It's used in the implementation of the {% include %} tag. SourceFile() string @@ -73,7 +73,7 @@ func (i invalidLocation) SourceText() string { var invalidLoc parser.Locatable = invalidLocation{} -func (c rendererContext) Errorf(format string, a ...interface{}) Error { +func (c rendererContext) Errorf(format string, a ...any) Error { switch { case c.node != nil: return renderErrorf(c.node, format, a...) @@ -95,22 +95,22 @@ func (c rendererContext) WrapError(err error) Error { } } -func (c rendererContext) Evaluate(expr expressions.Expression) (out interface{}, err error) { +func (c rendererContext) Evaluate(expr expressions.Expression) (out any, err error) { return c.ctx.Evaluate(expr) } // EvaluateString evaluates an expression within the template context. -func (c rendererContext) EvaluateString(source string) (out interface{}, err error) { +func (c rendererContext) EvaluateString(source string) (out any, err error) { return expressions.EvaluateString(source, expressions.NewContext(c.ctx.bindings, c.ctx.config.Config.Config)) } // Bindings returns the current lexical environment. -func (c rendererContext) Bindings() map[string]interface{} { +func (c rendererContext) Bindings() map[string]any { return c.ctx.bindings } // Get gets a variable value within an evaluation context. -func (c rendererContext) Get(name string) interface{} { +func (c rendererContext) Get(name string) any { return c.ctx.bindings[name] } @@ -144,7 +144,7 @@ func (c rendererContext) RenderChildren(w io.Writer) Error { return c.ctx.RenderSequence(w, c.cn.Body) } -func (c rendererContext) RenderFile(filename string, b map[string]interface{}) (string, error) { +func (c rendererContext) RenderFile(filename string, b map[string]any) (string, error) { source, err := os.ReadFile(filename) if err != nil && os.IsNotExist(err) { // Is it cached? @@ -160,7 +160,7 @@ func (c rendererContext) RenderFile(filename string, b map[string]interface{}) ( if err != nil { return "", err } - bindings := map[string]interface{}{} + bindings := map[string]any{} for k, v := range c.ctx.bindings { bindings[k] = v } @@ -184,7 +184,7 @@ func (c rendererContext) InnerString() (string, error) { } // Set sets a variable value from an evaluation context. -func (c rendererContext) Set(name string, value interface{}) { +func (c rendererContext) Set(name string, value any) { c.ctx.bindings[name] = value } diff --git a/render/context_test.go b/render/context_test.go index a91e970..1dff600 100644 --- a/render/context_test.go +++ b/render/context_test.go @@ -48,7 +48,7 @@ func addContextTestTags(s Config) { }) s.AddTag("test_render_file", func(filename string) (func(w io.Writer, c Context) error, error) { return func(w io.Writer, c Context) error { - s, err := c.RenderFile(filename, map[string]interface{}{"shadowed": 2}) + s, err := c.RenderFile(filename, map[string]any{"shadowed": 2}) if err != nil { return err } @@ -97,7 +97,7 @@ var contextErrorTests = []struct{ in, expect string }{ {`{% test_block_errorf %}{% endtest_block_errorf %}`, "giftwrapped"}, } -var contextTestBindings = map[string]interface{}{ +var contextTestBindings = map[string]any{ "x": 123, "shadowed": 1, } diff --git a/render/error.go b/render/error.go index 649d79d..6cc5a6a 100644 --- a/render/error.go +++ b/render/error.go @@ -12,7 +12,7 @@ type Error interface { Error() string } -func renderErrorf(loc parser.Locatable, format string, a ...interface{}) Error { +func renderErrorf(loc parser.Locatable, format string, a ...any) Error { return parser.Errorf(loc, format, a...) } diff --git a/render/node_context.go b/render/node_context.go index b71b90b..7d9c600 100644 --- a/render/node_context.go +++ b/render/node_context.go @@ -9,15 +9,15 @@ import ( // This type has a clumsy name so that render.Context, in the public API, can // have a clean name that doesn't stutter. type nodeContext struct { - bindings map[string]interface{} + bindings map[string]any config Config } // newNodeContext creates a new evaluation context. -func newNodeContext(scope map[string]interface{}, c Config) nodeContext { +func newNodeContext(scope map[string]any, c Config) nodeContext { // The assign tag modifies the scope, so make a copy first. // TODO this isn't really the right place for this. - vars := map[string]interface{}{} + vars := map[string]any{} for k, v := range scope { vars[k] = v } @@ -25,6 +25,6 @@ func newNodeContext(scope map[string]interface{}, c Config) nodeContext { } // Evaluate evaluates an expression within the template context. -func (c nodeContext) Evaluate(expr expressions.Expression) (out interface{}, err error) { +func (c nodeContext) Evaluate(expr expressions.Expression) (out any, err error) { return expr.Evaluate(expressions.NewContext(c.bindings, c.config.Config.Config)) } diff --git a/render/render.go b/render/render.go index 0b7f7f4..a37c19d 100644 --- a/render/render.go +++ b/render/render.go @@ -12,7 +12,7 @@ import ( ) // Render renders the render tree. -func Render(node Node, w io.Writer, vars map[string]interface{}, c Config) Error { +func Render(node Node, w io.Writer, vars map[string]any, c Config) Error { tw := trimWriter{w: w} if err := node.render(&tw, newNodeContext(vars, c)); err != nil { return err @@ -101,7 +101,7 @@ func (n *TextNode) render(w *trimWriter, ctx nodeContext) Error { } // writeObject writes a value used in an object node -func writeObject(w io.Writer, value interface{}) error { +func writeObject(w io.Writer, value any) error { value = values.ToLiquid(value) if value == nil { return nil diff --git a/render/render_test.go b/render/render_test.go index 413c07c..4b8ba15 100644 --- a/render/render_test.go +++ b/render/render_test.go @@ -73,11 +73,11 @@ var renderErrorTests = []struct{ in, out string }{ {`{% errblock %}{% enderrblock %}`, "errblock error"}, } -var renderTestBindings = map[string]interface{}{ +var renderTestBindings = map[string]any{ "array": []string{"first", "second", "third"}, "date": time.Date(2015, 7, 17, 15, 4, 5, 123456789, time.UTC), "int": 123, - "sort_prop": []map[string]interface{}{ + "sort_prop": []map[string]any{ {"weight": 1}, {"weight": 5}, {"weight": 3}, @@ -85,10 +85,10 @@ var renderTestBindings = map[string]interface{}{ }, // for examples from liquid docs "animals": []string{"zebra", "octopus", "giraffe", "Sally Snake"}, - "page": map[string]interface{}{ + "page": map[string]any{ "title": "Introduction", }, - "pages": []map[string]interface{}{ + "pages": []map[string]any{ {"category": "business"}, {"category": "celebrities"}, {}, diff --git a/tags/control_flow_tags.go b/tags/control_flow_tags.go index a560e48..efffd97 100644 --- a/tags/control_flow_tags.go +++ b/tags/control_flow_tags.go @@ -10,7 +10,7 @@ import ( type caseInterpreter interface { body() *render.BlockNode - test(interface{}, render.Context) (bool, error) + test(any, render.Context) (bool, error) } type exprCase struct { e.When @@ -19,7 +19,7 @@ type exprCase struct { func (c exprCase) body() *render.BlockNode { return c.b } -func (c exprCase) test(caseValue interface{}, ctx render.Context) (bool, error) { +func (c exprCase) test(caseValue any, ctx render.Context) (bool, error) { for _, expr := range c.Exprs { whenValue, err := ctx.Evaluate(expr) if err != nil { @@ -36,7 +36,7 @@ type elseCase struct{ b *render.BlockNode } func (c elseCase) body() *render.BlockNode { return c.b } -func (c elseCase) test(interface{}, render.Context) (bool, error) { return true, nil } +func (c elseCase) test(any, render.Context) (bool, error) { return true, nil } func caseTagCompiler(node render.BlockNode) (func(io.Writer, render.Context) error, error) { // TODO syntax error on non-empty node.Body diff --git a/tags/include_tag.go b/tags/include_tag.go index 6442a24..5c04e77 100644 --- a/tags/include_tag.go +++ b/tags/include_tag.go @@ -21,7 +21,7 @@ func includeTag(source string) (func(io.Writer, render.Context) error, error) { return ctx.Errorf("include requires a string argument; got %v", value) } filename := filepath.Join(filepath.Dir(ctx.SourceFile()), rel) - s, err := ctx.RenderFile(filename, map[string]interface{}{}) + s, err := ctx.RenderFile(filename, map[string]any{}) if err != nil { return err } diff --git a/tags/include_tag_test.go b/tags/include_tag_test.go index 41549e9..eeee673 100644 --- a/tags/include_tag_test.go +++ b/tags/include_tag_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/require" ) -var includeTestBindings = map[string]interface{}{ +var includeTestBindings = map[string]any{ "test": true, "var": "value", } diff --git a/tags/iteration_tags.go b/tags/iteration_tags.go index a8815af..3b1d1c1 100644 --- a/tags/iteration_tags.go +++ b/tags/iteration_tags.go @@ -15,7 +15,7 @@ import ( ) // An IterationKeyedMap is a map that yields its keys, instead of (key, value) pairs, when iterated. -type IterationKeyedMap map[string]interface{} +type IterationKeyedMap map[string]any const forloopVarName = "forloop" @@ -26,7 +26,7 @@ var ( type iterable interface { Len() int - Index(int) interface{} + Index(int) any } func breakTag(string) (func(io.Writer, render.Context) error, error) { @@ -54,7 +54,7 @@ func cycleTag(args string) (func(io.Writer, render.Context) error, error) { } // The next few lines could panic if the user spoofs us by creating their own loop object. // “C++ protects against accident, not against fraud.” – Bjarne Stroustrup - loopRec := loopVar.(map[string]interface{}) + loopRec := loopVar.(map[string]any) cycleMap := loopRec[".cycles"].(map[string]int) group, values := cycle.Group, cycle.Values n := cycleMap[group] @@ -113,7 +113,7 @@ func (loop loopRenderer) render(iter iterable, w io.Writer, ctx render.Context) } // shallow-bind the loop variables; restore on exit - defer func(index, forloop interface{}) { + defer func(index, forloop any) { ctx.Set(forloopVarName, index) ctx.Set(loop.Variable, forloop) }(ctx.Get(forloopVarName), ctx.Get(loop.Variable)) @@ -121,7 +121,7 @@ func (loop loopRenderer) render(iter iterable, w io.Writer, ctx render.Context) loop: for i, l := 0, iter.Len(); i < l; i++ { ctx.Set(loop.Variable, iter.Index(i)) - ctx.Set(forloopVarName, map[string]interface{}{ + ctx.Set(forloopVarName, map[string]any{ "first": i == 0, "last": i == l-1, "index": i + 1, @@ -241,7 +241,7 @@ func applyLoopModifiers(loop expressions.Loop, ctx render.Context, iter iterable return iter, nil } -func makeIterator(value interface{}) iterable { +func makeIterator(value any) iterable { if iter, ok := value.(iterable); ok { return iter } @@ -259,10 +259,10 @@ func makeIterator(value interface{}) iterable { return sliceWrapper(reflect.ValueOf(value)) case reflect.Map: rv := reflect.ValueOf(value) - array := make([][]interface{}, rv.Len()) + array := make([][]any, rv.Len()) for i, k := range rv.MapKeys() { v := rv.MapIndex(k) - array[i] = []interface{}{k.Interface(), v.Interface()} + array[i] = []any{k.Interface(), v.Interface()} } return sliceWrapper(reflect.ValueOf(array)) default: @@ -270,7 +270,7 @@ func makeIterator(value interface{}) iterable { } } -func makeIterationKeyedMap(m map[string]interface{}) iterable { +func makeIterationKeyedMap(m map[string]any) iterable { // Iteration chooses a random start, so we need a copy of the keys to iterate through them. keys := make([]string, 0, len(m)) for k := range m { @@ -283,15 +283,15 @@ func makeIterationKeyedMap(m map[string]interface{}) iterable { type sliceWrapper reflect.Value -func (w sliceWrapper) Len() int { return reflect.Value(w).Len() } -func (w sliceWrapper) Index(i int) interface{} { return reflect.Value(w).Index(i).Interface() } +func (w sliceWrapper) Len() int { return reflect.Value(w).Len() } +func (w sliceWrapper) Index(i int) any { return reflect.Value(w).Index(i).Interface() } type mapSliceWrapper struct{ ms yaml.MapSlice } func (w mapSliceWrapper) Len() int { return len(w.ms) } -func (w mapSliceWrapper) Index(i int) interface{} { +func (w mapSliceWrapper) Index(i int) any { item := w.ms[i] - return []interface{}{item.Key, item.Value} + return []any{item.Key, item.Value} } type limitWrapper struct { @@ -299,23 +299,23 @@ type limitWrapper struct { n int } -func (w limitWrapper) Len() int { return intMin(w.n, w.i.Len()) } -func (w limitWrapper) Index(i int) interface{} { return w.i.Index(i) } +func (w limitWrapper) Len() int { return intMin(w.n, w.i.Len()) } +func (w limitWrapper) Index(i int) any { return w.i.Index(i) } type offsetWrapper struct { i iterable n int } -func (w offsetWrapper) Len() int { return intMax(0, w.i.Len()-w.n) } -func (w offsetWrapper) Index(i int) interface{} { return w.i.Index(i + w.n) } +func (w offsetWrapper) Len() int { return intMax(0, w.i.Len()-w.n) } +func (w offsetWrapper) Index(i int) any { return w.i.Index(i + w.n) } type reverseWrapper struct { i iterable } -func (w reverseWrapper) Len() int { return w.i.Len() } -func (w reverseWrapper) Index(i int) interface{} { return w.i.Index(w.i.Len() - 1 - i) } +func (w reverseWrapper) Len() int { return w.i.Len() } +func (w reverseWrapper) Index(i int) any { return w.i.Index(w.i.Len() - 1 - i) } func intMax(a, b int) int { if a > b { diff --git a/tags/iteration_tags_test.go b/tags/iteration_tags_test.go index 7d04c2c..5d149af 100644 --- a/tags/iteration_tags_test.go +++ b/tags/iteration_tags_test.go @@ -143,11 +143,11 @@ var iterationErrorTests = []struct{ in, expected string }{ {`{% for a in array %}{% else %}{% else %}{% endfor %}`, "for loops accept at most one else clause"}, } -var iterationTestBindings = map[string]interface{}{ +var iterationTestBindings = map[string]any{ "array": []string{"first", "second", "third"}, // hash has only one element, since iteration order is non-deterministic - "map": map[string]interface{}{"a": 1}, - "keyed_map": IterationKeyedMap(map[string]interface{}{"a": 1, "b": 2}), + "map": map[string]any{"a": 1}, + "keyed_map": IterationKeyedMap(map[string]any{"a": 1, "b": 2}), "map_slice": yaml.MapSlice{{Key: "a", Value: 1}, {Key: "b", Value: 2}}, "products": []string{ "Cool Shirt", "Alien Poster", "Batman Poster", "Bullseye Shirt", "Another Classic Vinyl", "Awesome Jeans", @@ -155,7 +155,7 @@ var iterationTestBindings = map[string]interface{}{ "offset": 1, "limit": 2, "cols": 2, - "loopmods": map[string]interface{}{"limit": 2, "offset": 1, "cols": 2}, + "loopmods": map[string]any{"limit": 2, "offset": 1, "cols": 2}, } func TestIterationTags(t *testing.T) { diff --git a/tags/standard_tags_test.go b/tags/standard_tags_test.go index ccd2424..688c898 100644 --- a/tags/standard_tags_test.go +++ b/tags/standard_tags_test.go @@ -40,13 +40,13 @@ var tagErrorTests = []struct{ in, expected string }{ } // this is also used in the other test files -var tagTestBindings = map[string]interface{}{ +var tagTestBindings = map[string]any{ "x": 123, - "obj": map[string]interface{}{ + "obj": map[string]any{ "a": 1, }, "animals": []string{"zebra", "octopus", "giraffe", "Sally Snake"}, - "pages": []map[string]interface{}{ + "pages": []map[string]any{ {"category": "business"}, {"category": "celebrities"}, {}, @@ -55,13 +55,13 @@ var tagTestBindings = map[string]interface{}{ {}, {"category": "technology"}, }, - "sort_prop": []map[string]interface{}{ + "sort_prop": []map[string]any{ {"weight": 1}, {"weight": 5}, {"weight": 3}, {"weight": nil}, }, - "page": map[string]interface{}{ + "page": map[string]any{ "title": "Introduction", }, } diff --git a/values/arrays.go b/values/arrays.go index 6c1c2d5..618b7e9 100644 --- a/values/arrays.go +++ b/values/arrays.go @@ -9,7 +9,7 @@ import ( // Length returns the length of a string or array. In keeping with Liquid semantics, // and contra Go, it does not return the size of a map. -func Length(value interface{}) int { +func Length(value any) int { value = ToLiquid(value) ref := reflect.ValueOf(value) switch ref.Kind() { diff --git a/values/call.go b/values/call.go index 08db520..7010e7c 100644 --- a/values/call.go +++ b/values/call.go @@ -12,7 +12,7 @@ import ( // // The function should return one or two values; the second value, // if present, should be an error. -func Call(fn reflect.Value, args []interface{}) (interface{}, error) { +func Call(fn reflect.Value, args []any) (any, error) { in, err := convertCallArguments(fn, args) if err != nil { return nil, err @@ -28,7 +28,7 @@ func (e *CallParityError) Error() string { return fmt.Sprintf("wrong number of arguments (given %d, expected %d)", e.NumArgs, e.NumParams) } -func convertCallResults(results []reflect.Value) (interface{}, error) { +func convertCallResults(results []reflect.Value) (any, error) { if len(results) > 1 && results[1].Interface() != nil { switch e := results[1].Interface().(type) { case error: @@ -41,7 +41,7 @@ func convertCallResults(results []reflect.Value) (interface{}, error) { } // Convert args to match the input types of function fn. -func convertCallArguments(fn reflect.Value, args []interface{}) (results []reflect.Value, err error) { +func convertCallArguments(fn reflect.Value, args []any) (results []reflect.Value, err error) { rt := fn.Type() if len(args) > rt.NumIn() && !rt.IsVariadic() { return nil, &CallParityError{NumArgs: len(args), NumParams: rt.NumIn()} @@ -89,7 +89,7 @@ func isDefaultFunctionType(typ reflect.Type) bool { return typ.Kind() == reflect.Func && typ.NumIn() == 1 && typ.NumOut() == 1 } -func makeConstantFunction(typ reflect.Type, arg interface{}) reflect.Value { +func makeConstantFunction(typ reflect.Type, arg any) reflect.Value { return reflect.MakeFunc(typ, func(args []reflect.Value) []reflect.Value { return []reflect.Value{reflect.ValueOf(MustConvert(arg, typ.Out(0)))} }) diff --git a/values/call_test.go b/values/call_test.go index 50e8a86..2a26934 100644 --- a/values/call_test.go +++ b/values/call_test.go @@ -13,7 +13,7 @@ func TestCall(t *testing.T) { fn := func(a, b string) string { return a + "," + b + "." } - value, err := Call(reflect.ValueOf(fn), []interface{}{5, 10}) + value, err := Call(reflect.ValueOf(fn), []any{5, 10}) require.NoError(t, err) require.Equal(t, "5,10.", value) @@ -21,15 +21,15 @@ func TestCall(t *testing.T) { fnVaridic := func(a string, b ...string) string { return a + "," + strings.Join(b, ",") + "." } - value, err = Call(reflect.ValueOf(fnVaridic), []interface{}{5, 10}) + value, err = Call(reflect.ValueOf(fnVaridic), []any{5, 10}) require.NoError(t, err) require.Equal(t, "5,10.", value) - value, err = Call(reflect.ValueOf(fnVaridic), []interface{}{5, 10, 15, 20}) + value, err = Call(reflect.ValueOf(fnVaridic), []any{5, 10, 15, 20}) require.NoError(t, err) require.Equal(t, "5,10,15,20.", value) // extra arguments (non variadic) - _, err = Call(reflect.ValueOf(fn), []interface{}{5, 10, 20}) + _, err = Call(reflect.ValueOf(fn), []any{5, 10, 20}) require.Error(t, err) require.Contains(t, err.Error(), "wrong number of arguments") require.Contains(t, err.Error(), "given 3") @@ -37,7 +37,7 @@ func TestCall(t *testing.T) { // error return fn2 := func(int) (int, error) { return 0, errors.New("expected error") } - _, err = Call(reflect.ValueOf(fn2), []interface{}{2}) + _, err = Call(reflect.ValueOf(fn2), []any{2}) require.Error(t, err) require.Contains(t, err.Error(), "expected error") } @@ -46,11 +46,11 @@ func TestCall_optional(t *testing.T) { fn := func(a string, b func(string) string) string { return a + "," + b("default") + "." } - value, err := Call(reflect.ValueOf(fn), []interface{}{5}) + value, err := Call(reflect.ValueOf(fn), []any{5}) require.NoError(t, err) require.Equal(t, "5,default.", value) - value, err = Call(reflect.ValueOf(fn), []interface{}{5, 10}) + value, err = Call(reflect.ValueOf(fn), []any{5, 10}) require.NoError(t, err) require.Equal(t, "5,10.", value) } @@ -60,19 +60,19 @@ func TestCall_variadic(t *testing.T) { return "[" + strings.Join(args, sep(",")) + "]" } - value, err := Call(reflect.ValueOf(fn), []interface{}{",", "a"}) + value, err := Call(reflect.ValueOf(fn), []any{",", "a"}) require.NoError(t, err) require.Equal(t, "[a]", value) - value, err = Call(reflect.ValueOf(fn), []interface{}{",", "a", "b"}) + value, err = Call(reflect.ValueOf(fn), []any{",", "a", "b"}) require.NoError(t, err) require.Equal(t, "[a,b]", value) - value, err = Call(reflect.ValueOf(fn), []interface{}{","}) + value, err = Call(reflect.ValueOf(fn), []any{","}) require.NoError(t, err) require.Equal(t, "[]", value) - value, err = Call(reflect.ValueOf(fn), []interface{}{}) + value, err = Call(reflect.ValueOf(fn), []any{}) require.NoError(t, err) require.Equal(t, "[]", value) } diff --git a/values/compare.go b/values/compare.go index 0327925..f9d7550 100644 --- a/values/compare.go +++ b/values/compare.go @@ -10,7 +10,7 @@ var ( ) // Equal returns a bool indicating whether a == b after conversion. -func Equal(a, b interface{}) bool { //nolint: gocyclo +func Equal(a, b any) bool { //nolint: gocyclo a, b = ToLiquid(a), ToLiquid(b) if a == nil || b == nil { return a == b @@ -46,7 +46,7 @@ func Equal(a, b interface{}) bool { //nolint: gocyclo } // Less returns a bool indicating whether a < b. -func Less(a, b interface{}) bool { +func Less(a, b any) bool { a, b = ToLiquid(a), ToLiquid(b) if a == nil || b == nil { return false diff --git a/values/compare_test.go b/values/compare_test.go index 031670c..2067899 100644 --- a/values/compare_test.go +++ b/values/compare_test.go @@ -13,7 +13,7 @@ var ( ) var eqTests = []struct { - a, b interface{} + a, b any expected bool }{ {nil, nil, true}, @@ -37,7 +37,7 @@ var eqTests = []struct { {[]string{"a", "b"}, []string{"a"}, false}, {[]string{"a", "b"}, []string{"a", "b"}, true}, {[]string{"a", "b"}, []string{"a", "c"}, false}, - {[]interface{}{1.0, 2}, []interface{}{1, 2.0}, true}, + {[]any{1.0, 2}, []any{1, 2.0}, true}, {eqTestObj, eqTestObj, true}, } diff --git a/values/convert.go b/values/convert.go index 5ce80d5..f8b3cd0 100644 --- a/values/convert.go +++ b/values/convert.go @@ -15,13 +15,13 @@ type TypeError string func (e TypeError) Error() string { return string(e) } -func typeErrorf(format string, a ...interface{}) TypeError { +func typeErrorf(format string, a ...any) TypeError { return TypeError(fmt.Sprintf(format, a...)) } var timeType = reflect.TypeOf(time.Now()) -func conversionError(modifier string, value interface{}, typ reflect.Type) error { +func conversionError(modifier string, value any, typ reflect.Type) error { if modifier != "" { modifier += " " } @@ -31,7 +31,7 @@ func conversionError(modifier string, value interface{}, typ reflect.Type) error return typeErrorf("can't convert %s%T(%v) to type %s", modifier, value, value, typ) } -func convertValueToInt(value interface{}, typ reflect.Type) (int64, error) { +func convertValueToInt(value any, typ reflect.Type) (int64, error) { switch value := value.(type) { case bool: if value { @@ -55,7 +55,7 @@ func convertValueToInt(value interface{}, typ reflect.Type) (int64, error) { return 0, conversionError("", value, typ) } -func convertValueToFloat(value interface{}, typ reflect.Type) (float64, error) { +func convertValueToFloat(value any, typ reflect.Type) (float64, error) { switch value := value.(type) { // case int is handled by rv.Convert(typ) in Convert function case string: @@ -78,7 +78,7 @@ func convertValueToFloat(value interface{}, typ reflect.Type) (float64, error) { // recursively create new map and slice values as necessary. It doesn't // handle circular references. // #nosec G115 -func Convert(value interface{}, typ reflect.Type) (interface{}, error) { //nolint: gocyclo +func Convert(value any, typ reflect.Type) (any, error) { //nolint: gocyclo value = ToLiquid(value) rv := reflect.ValueOf(value) // int.Convert(string) returns "\x01" not "1", so guard against that in the following test @@ -241,7 +241,7 @@ func Convert(value interface{}, typ reflect.Type) (interface{}, error) { //nolin } // MustConvert is like Convert, but panics if conversion fails. -func MustConvert(value interface{}, t reflect.Type) interface{} { +func MustConvert(value any, t reflect.Type) any { out, err := Convert(value, t) if err != nil { panic(err) @@ -251,7 +251,7 @@ func MustConvert(value interface{}, t reflect.Type) interface{} { // MustConvertItem converts item to conform to the type array's element, else panics. // Unlike MustConvert, the second argument is a value not a type. -func MustConvertItem(item interface{}, array interface{}) interface{} { +func MustConvertItem(item any, array any) any { item, err := Convert(item, reflect.TypeOf(array).Elem()) if err != nil { panic(typeErrorf("can't convert %#v to %s: %s", item, reflect.TypeOf(array).Elem(), err)) diff --git a/values/convert_test.go b/values/convert_test.go index fa58dc4..0e86c72 100644 --- a/values/convert_test.go +++ b/values/convert_test.go @@ -14,12 +14,12 @@ import ( type redConvertible struct{} -func (c redConvertible) ToLiquid() interface{} { +func (c redConvertible) ToLiquid() any { return "red" } var convertTests = []struct { - value, expected interface{} + value, expected any }{ {nil, false}, {false, 0}, @@ -70,33 +70,33 @@ var convertTests = []struct { {"2.1", float32(2.1)}, {"2.1", float64(2.1)}, {"string", "string"}, - {[]interface{}{1, 2}, []interface{}{1, 2}}, + {[]any{1, 2}, []any{1, 2}}, {[]int{1, 2}, []int{1, 2}}, - {[]int{1, 2}, []interface{}{1, 2}}, - {[]interface{}{1, 2}, []int{1, 2}}, + {[]int{1, 2}, []any{1, 2}}, + {[]any{1, 2}, []int{1, 2}}, {[]int{1, 2}, []string{"1", "2"}}, - {yaml.MapSlice{{Key: 1, Value: 1}}, []interface{}{1}}, + {yaml.MapSlice{{Key: 1, Value: 1}}, []any{1}}, {yaml.MapSlice{{Key: 1, Value: 1}}, []string{"1"}}, {yaml.MapSlice{{Key: 1, Value: "a"}}, []string{"a"}}, - {yaml.MapSlice{{Key: 1, Value: "a"}}, map[interface{}]interface{}{1: "a"}}, + {yaml.MapSlice{{Key: 1, Value: "a"}}, map[any]any{1: "a"}}, {yaml.MapSlice{{Key: 1, Value: "a"}}, map[int]string{1: "a"}}, {yaml.MapSlice{{Key: 1, Value: "a"}}, map[string]string{"1": "a"}}, {yaml.MapSlice{{Key: "a", Value: 1}}, map[string]string{"a": "1"}}, - {yaml.MapSlice{{Key: "a", Value: nil}}, map[string]interface{}{"a": nil}}, - {yaml.MapSlice{{Key: nil, Value: 1}}, map[interface{}]string{nil: "1"}}, - {Range{1, 5}, []interface{}{1, 2, 3, 4, 5}}, - {Range{0, 0}, []interface{}{0}}, + {yaml.MapSlice{{Key: "a", Value: nil}}, map[string]any{"a": nil}}, + {yaml.MapSlice{{Key: nil, Value: 1}}, map[any]string{nil: "1"}}, + {Range{1, 5}, []any{1, 2, 3, 4, 5}}, + {Range{0, 0}, []any{0}}, // {"March 14, 2016", time.Now(), timeMustParse("2016-03-14T00:00:00Z")}, {redConvertible{}, "red"}, } var convertErrorTests = []struct { - value, proto interface{} + value, proto any expected []string }{ {map[string]bool{"k": true}, map[int]bool{}, []string{"map key"}}, {map[string]string{"k": "v"}, map[string]int{}, []string{"map element"}}, - {map[interface{}]interface{}{"k": "v"}, map[string]int{}, []string{"map element"}}, + {map[any]any{"k": "v"}, map[string]int{}, []string{"map element"}}, {"notanumber", int(0), []string{"can't convert string", "to type int"}}, {"notanumber", uint(0), []string{"can't convert string", "to type uint"}}, {"notanumber", float64(0), []string{"can't convert string", "to type float64"}}, @@ -130,7 +130,7 @@ func TestConvert_errors(t *testing.T) { func TestConvert_map(t *testing.T) { typ := reflect.TypeOf(map[string]string{}) - v, err := Convert(map[interface{}]interface{}{"key": "value"}, typ) + v, err := Convert(map[any]any{"key": "value"}, typ) require.NoError(t, err) m, ok := v.(map[string]string) require.True(t, ok) @@ -138,7 +138,7 @@ func TestConvert_map(t *testing.T) { } func TestConvert_map_synonym(t *testing.T) { - type VariableMap map[interface{}]interface{} + type VariableMap map[any]any typ := reflect.TypeOf(map[string]string{}) v, err := Convert(VariableMap{"key": "value"}, typ) require.NoError(t, err) diff --git a/values/drop.go b/values/drop.go index 6c1b372..3ddab17 100644 --- a/values/drop.go +++ b/values/drop.go @@ -5,11 +5,11 @@ import ( ) type drop interface { - ToLiquid() interface{} + ToLiquid() any } // ToLiquid converts an object to Liquid, if it implements the Drop interface. -func ToLiquid(value interface{}) interface{} { +func ToLiquid(value any) any { switch value := value.(type) { case drop: return value.ToLiquid() @@ -34,6 +34,6 @@ func (w *dropWrapper) Less(o Value) bool { return w.Resolve().Less(o) func (w *dropWrapper) IndexValue(i Value) Value { return w.Resolve().IndexValue(i) } func (w *dropWrapper) Contains(o Value) bool { return w.Resolve().Contains(o) } func (w *dropWrapper) Int() int { return w.Resolve().Int() } -func (w *dropWrapper) Interface() interface{} { return w.Resolve().Interface() } +func (w *dropWrapper) Interface() any { return w.Resolve().Interface() } func (w *dropWrapper) PropertyValue(k Value) Value { return w.Resolve().PropertyValue(k) } func (w *dropWrapper) Test() bool { return w.Resolve().Test() } diff --git a/values/drop_test.go b/values/drop_test.go index 2e24e84..c29e11a 100644 --- a/values/drop_test.go +++ b/values/drop_test.go @@ -6,9 +6,9 @@ import ( "github.com/stretchr/testify/require" ) -type testDrop struct{ proxy interface{} } +type testDrop struct{ proxy any } -func (d testDrop) ToLiquid() interface{} { return d.proxy } +func (d testDrop) ToLiquid() any { return d.proxy } func TestToLiquid(t *testing.T) { require.Equal(t, 2, ToLiquid(2)) diff --git a/values/evaluator_test.go b/values/evaluator_test.go index 9a3916f..50c9b7d 100644 --- a/values/evaluator_test.go +++ b/values/evaluator_test.go @@ -8,7 +8,7 @@ import ( ) var lessTests = []struct { - a, b interface{} + a, b any expected bool }{ {nil, nil, false}, @@ -43,21 +43,21 @@ func TestLength(t *testing.T) { } func TestSort(t *testing.T) { - array := []interface{}{2, 1} + array := []any{2, 1} Sort(array) - require.Equal(t, []interface{}{1, 2}, array) + require.Equal(t, []any{1, 2}, array) - array = []interface{}{"b", "a"} + array = []any{"b", "a"} Sort(array) - require.Equal(t, []interface{}{"a", "b"}, array) + require.Equal(t, []any{"a", "b"}, array) - array = []interface{}{ - map[string]interface{}{"key": 20}, - map[string]interface{}{"key": 10}, - map[string]interface{}{}, + array = []any{ + map[string]any{"key": 20}, + map[string]any{"key": 10}, + map[string]any{}, } SortByProperty(array, "key", true) - require.Nil(t, array[0].(map[string]interface{})["key"]) - require.Equal(t, 10, array[1].(map[string]interface{})["key"]) - require.Equal(t, 20, array[2].(map[string]interface{})["key"]) + require.Nil(t, array[0].(map[string]any)["key"]) + require.Equal(t, 10, array[1].(map[string]any)["key"]) + require.Equal(t, 20, array[2].(map[string]any)["key"]) } diff --git a/values/mapslicevalue.go b/values/mapslicevalue.go index 56db18a..26171e0 100644 --- a/values/mapslicevalue.go +++ b/values/mapslicevalue.go @@ -10,7 +10,7 @@ type mapSliceValue struct { } // func (v mapSliceValue) Equal(o Value) bool { return v.slice == o.Interface() } -func (v mapSliceValue) Interface() interface{} { return v.slice } +func (v mapSliceValue) Interface() any { return v.slice } func (v mapSliceValue) Contains(elem Value) bool { e := elem.Interface() diff --git a/values/predicates.go b/values/predicates.go index 4f98f59..2674817 100644 --- a/values/predicates.go +++ b/values/predicates.go @@ -5,7 +5,7 @@ import ( ) // IsEmpty returns a bool indicating whether the value is empty according to Liquid semantics. -func IsEmpty(value interface{}) bool { +func IsEmpty(value any) bool { value = ToLiquid(value) if value == nil { return false diff --git a/values/predicates_test.go b/values/predicates_test.go index f26d07e..7003a69 100644 --- a/values/predicates_test.go +++ b/values/predicates_test.go @@ -10,7 +10,7 @@ func TestIsEmpty(t *testing.T) { require.True(t, IsEmpty(false)) require.False(t, IsEmpty(true)) require.True(t, IsEmpty([]string{})) - require.True(t, IsEmpty(map[string]interface{}{})) + require.True(t, IsEmpty(map[string]any{})) require.False(t, IsEmpty([]string{""})) - require.False(t, IsEmpty(map[string]interface{}{"k": "v"})) + require.False(t, IsEmpty(map[string]any{"k": "v"})) } diff --git a/values/range.go b/values/range.go index 1fa5a7e..a18f584 100644 --- a/values/range.go +++ b/values/range.go @@ -14,11 +14,11 @@ func NewRange(b, e int) Range { func (r Range) Len() int { return r.e + 1 - r.b } // Index is in the iteration interface -func (r Range) Index(i int) interface{} { return r.b + i } +func (r Range) Index(i int) any { return r.b + i } // AsArray converts the range into an array. -func (r Range) AsArray() []interface{} { - a := make([]interface{}, 0, r.Len()) +func (r Range) AsArray() []any { + a := make([]any, 0, r.Len()) for i := r.b; i <= r.e; i++ { a = append(a, i) } diff --git a/values/sort.go b/values/sort.go index 50532e8..a3cd295 100644 --- a/values/sort.go +++ b/values/sort.go @@ -5,12 +5,12 @@ import ( "sort" ) -// Sort any []interface{} value. -func Sort(data []interface{}) { +// Sort any []any value. +func Sort(data []any) { sort.Sort(genericSortable(data)) } -type genericSortable []interface{} +type genericSortable []any // Len is part of sort.Interface. func (s genericSortable) Len() int { @@ -28,12 +28,12 @@ func (s genericSortable) Less(i, j int) bool { } // SortByProperty sorts maps on their key indices. -func SortByProperty(data []interface{}, key string, nilFirst bool) { +func SortByProperty(data []any, key string, nilFirst bool) { sort.Sort(sortableByProperty{data, key, nilFirst}) } type sortableByProperty struct { - data []interface{} + data []any key string nilFirst bool } @@ -52,7 +52,7 @@ func (s sortableByProperty) Swap(i, j int) { // Less is part of sort.Interface. func (s sortableByProperty) Less(i, j int) bool { // index returns the value at s.key, if in is a map that contains this key - index := func(i int) interface{} { + index := func(i int) any { value := ToLiquid(s.data[i]) rt := reflect.ValueOf(value) if rt.Kind() == reflect.Map && rt.Type().Key().Kind() == reflect.String { diff --git a/values/value.go b/values/value.go index 4e2d103..4385272 100644 --- a/values/value.go +++ b/values/value.go @@ -11,7 +11,7 @@ import ( // A Value is a Liquid runtime value. type Value interface { // Value retrieval - Interface() interface{} + Interface() any Int() int // Comparison @@ -28,7 +28,7 @@ type Value interface { // ValueOf returns a Value that wraps its argument. // If the argument is already a Value, it returns this. -func ValueOf(value interface{}) Value { //nolint: gocyclo +func ValueOf(value any) Value { //nolint: gocyclo // interned values switch value { case nil: @@ -92,13 +92,13 @@ func (v valueEmbed) PropertyValue(Value) Value { return nilValue } func (v valueEmbed) Test() bool { return true } // A wrapperValue wraps a Go value. -type wrapperValue struct{ value interface{} } +type wrapperValue struct{ value any } func (v wrapperValue) Equal(other Value) bool { return Equal(v.value, other.Interface()) } func (v wrapperValue) Less(other Value) bool { return Less(v.value, other.Interface()) } func (v wrapperValue) IndexValue(Value) Value { return nilValue } func (v wrapperValue) Contains(Value) bool { return false } -func (v wrapperValue) Interface() interface{} { return v.value } +func (v wrapperValue) Interface() any { return v.value } func (v wrapperValue) PropertyValue(Value) Value { return nilValue } func (v wrapperValue) Test() bool { return v.value != nil && v.value != false } diff --git a/values/value_test.go b/values/value_test.go index 13eac4e..17f22a9 100644 --- a/values/value_test.go +++ b/values/value_test.go @@ -63,20 +63,20 @@ func TestValue_IndexValue(t *testing.T) { require.Nil(t, lv.IndexValue(ValueOf(nil)).Interface()) // string map - hv := ValueOf(map[string]interface{}{"key": "value"}) + hv := ValueOf(map[string]any{"key": "value"}) require.Equal(t, "value", hv.IndexValue(ValueOf("key")).Interface()) require.Nil(t, hv.IndexValue(ValueOf("missing_key")).Interface()) require.Nil(t, hv.IndexValue(ValueOf(nil)).Interface()) // interface map - hv = ValueOf(map[interface{}]interface{}{"key": "value"}) + hv = ValueOf(map[any]any{"key": "value"}) require.Equal(t, "value", hv.IndexValue(ValueOf("key")).Interface()) require.Nil(t, hv.IndexValue(ValueOf(nil)).Interface()) require.Nil(t, hv.IndexValue(ValueOf([]string{})).Interface()) require.Nil(t, hv.IndexValue(ValueOf(struct{}{})).Interface()) // ptr to map - hashPtr := ValueOf(&map[string]interface{}{"key": "value"}) + hashPtr := ValueOf(&map[string]any{"key": "value"}) require.Equal(t, "value", hashPtr.IndexValue(ValueOf("key")).Interface()) require.Nil(t, hashPtr.IndexValue(ValueOf("missing_key")).Interface()) require.Nil(t, hashPtr.IndexValue(ValueOf(nil)).Interface()) @@ -101,17 +101,17 @@ func TestValue_PropertyValue(t *testing.T) { require.Nil(t, lv.PropertyValue(ValueOf(nil)).Interface()) // string map - hv := ValueOf(map[string]interface{}{"key": "value"}) + hv := ValueOf(map[string]any{"key": "value"}) require.Equal(t, "value", hv.PropertyValue(ValueOf("key")).Interface()) require.Nil(t, hv.PropertyValue(ValueOf("missing_key")).Interface()) require.Nil(t, hv.PropertyValue(ValueOf(nil)).Interface()) // interface map - hv = ValueOf(map[interface{}]interface{}{"key": "value"}) + hv = ValueOf(map[any]any{"key": "value"}) require.Equal(t, "value", hv.PropertyValue(ValueOf("key")).Interface()) // ptr to map - hashPtr := ValueOf(&map[string]interface{}{"key": "value"}) + hashPtr := ValueOf(&map[string]any{"key": "value"}) require.Equal(t, "value", hashPtr.PropertyValue(ValueOf("key")).Interface()) require.Nil(t, hashPtr.PropertyValue(ValueOf("missing_key")).Interface()) @@ -132,7 +132,7 @@ func TestValue_Contains(t *testing.T) { require.False(t, av.Contains(ValueOf("missing"))) require.False(t, av.Contains(ValueOf(nil))) - require.True(t, ValueOf([]interface{}{nil}).Contains(ValueOf(nil))) + require.True(t, ValueOf([]any{nil}).Contains(ValueOf(nil))) // string sv := ValueOf("seafood") @@ -144,7 +144,7 @@ func TestValue_Contains(t *testing.T) { require.True(t, ValueOf("seaf00d").Contains(ValueOf(0))) // map - hv := ValueOf(map[string]interface{}{"key": "value"}) + hv := ValueOf(map[string]any{"key": "value"}) require.True(t, hv.Contains(ValueOf("key"))) require.False(t, hv.Contains(ValueOf("missing_key"))) require.False(t, hv.Contains(ValueOf(nil))) @@ -173,15 +173,15 @@ func TestValue_PropertyValue_size(t *testing.T) { require.Equal(t, 3, av.PropertyValue(ValueOf("size")).Interface()) // hash - hv := ValueOf(map[string]interface{}{"key": "value"}) + hv := ValueOf(map[string]any{"key": "value"}) require.Equal(t, 1, hv.PropertyValue(ValueOf("size")).Interface()) // hash with "size" key - withSizeKey := ValueOf(map[string]interface{}{"size": "value"}) + withSizeKey := ValueOf(map[string]any{"size": "value"}) require.Equal(t, "value", withSizeKey.IndexValue(ValueOf("size")).Interface()) // hash pointer - hashPtr := ValueOf(&map[string]interface{}{"key": "value"}) + hashPtr := ValueOf(&map[string]any{"key": "value"}) require.Equal(t, 1, hashPtr.PropertyValue(ValueOf("size")).Interface()) // MapSlice