Repository: go-go-golems/glazed
Pull request #406 (2024-01-20 12:36:25)

Enhance InitializeStruct with JSON parsing and add corresponding unit tests
This pull request introduces enhancements to the `InitializeStruct` function
and adds a comprehensive set of unit tests to validate its behavior. The key
changes include:

- **JSON Parsing**: The `InitializeStruct` function now supports parsing JSON
  strings into struct fields tagged with `from_json`. This feature is
  applicable only to pointer fields within the struct.

- **Error Handling**: Improved error messages and error handling mechanisms
  throughout the `InitializeStruct` function, ensuring better clarity when
  issues arise during initialization.

- **Unit Tests**: Added a new file `initialize-struct_test.go` containing
  extensive unit tests covering various scenarios, including successful
  struct initialization, handling of nil inputs, non-pointer inputs,
  non-struct pointers, missing parameters, and JSON parsing edge cases.

- **Reflect Helper**: Updated the `SetReflectValue` function in the
  `reflect` package to provide more descriptive error messages and handle
  additional cases for setting values on struct fields.

- **Variadic Choices**: Modified the `WithChoices` function in the
  `parameters` package to accept a variadic list of strings, simplifying the
  API for defining parameter choices.

- **File Concatenation**: Introduced a new function `ConcatFiles` in the
  `files` package to concatenate multiple files into a single `io.Reader`,
  enhancing file handling capabilities.


Patch:
diff --git a/pkg/cli/cobra.go b/pkg/cli/cobra.go
index ff549e4..99b69b2 100644
--- a/pkg/cli/cobra.go
+++ b/pkg/cli/cobra.go
@@ -186,6 +186,7 @@ func BuildCobraCommandFromCommandAndFunc(
 			os.Exit(0)
 		}
 
+		cobra.CheckErr(err)
 	}
 
 	return cmd, nil
diff --git a/pkg/cmds/parameters/initialize-struct.go b/pkg/cmds/parameters/initialize-struct.go
index 74276b2..de3dff5 100644
--- a/pkg/cmds/parameters/initialize-struct.go
+++ b/pkg/cmds/parameters/initialize-struct.go
@@ -1,9 +1,11 @@
 package parameters
 
 import (
-	reflect2 "github.com/go-go-golems/glazed/pkg/helpers/reflect"
+	"encoding/json"
+	reflect_helpers "github.com/go-go-golems/glazed/pkg/helpers/reflect"
 	"github.com/pkg/errors"
 	"reflect"
+	"strings"
 )
 
 // InitializeStruct initializes a struct from a ParsedParameters map.
@@ -12,12 +14,57 @@ import (
 // "glazed.parameter". For each tagged field, it will lookup the corresponding
 // parameter value in the ParsedParameters map and set the field's value.
 //
+// If the tag open `from_json` is appended to `glazed.parameter` and the parameter
+// value is a string, bytes, rawMessage or FileData, the value is parsed from json.
+//
 // Struct fields that are pointers to other structs are handled recursively.
 //
+// Struct fields that are pointers will be dereferenced. If the pointer is nil, a new value
+// will be allocated and set.
+//
 // s should be a pointer to the struct to initialize.
 //
 // ps is the ParsedParameters map to lookup parameter values from.
 //
+// Example struct:
+//
+//	type CreateIndexSettings struct {
+//		Index               string               `glazed.parameter:"index"`
+//		Settings            *IndexSettings       `glazed.parameter:"settings,from_json"`
+//		Mappings            *parameters.FileData `glazed.parameter:"mappings"`
+//		Aliases             *map[string]Alias    `glazed.parameter:"aliases,from_json"`
+//		WaitForActiveShards string               `glazed.parameter:"wait_for_active_shards"`
+//	}
+//
+// Corresponding ParameterDefinitions:
+//
+//	parameters.NewParameterDefinition(
+//		"index",
+//		parameters.ParameterTypeString,
+//		parameters.WithHelp("Name of the index to create"),
+//		parameters.WithRequired(true),
+//	),
+//	parameters.NewParameterDefinition(
+//		"settings",
+//		parameters.ParameterTypeFile,
+//		parameters.WithHelp("JSON file containing index settings"),
+//	),
+//	parameters.NewParameterDefinition(
+//		"mappings",
+//		parameters.ParameterTypeFile,
+//		parameters.WithHelp("JSON file containing index mappings"),
+//	),
+//	parameters.NewParameterDefinition(
+//		"aliases",
+//		parameters.ParameterTypeFile,
+//		parameters.WithHelp("JSON file containing index aliases"),
+//	),
+//	parameters.NewParameterDefinition(
+//		"wait_for_active_shards",
+//		parameters.ParameterTypeString,
+//		parameters.WithHelp("Set the number of active shards to wait for before the operation returns."),
+//	),
+//
 // Returns an error if:
 // - s is not a pointer to a struct
 // - A tagged field does not have a matching parameter value in ps
@@ -26,53 +73,123 @@ func (p *ParsedParameters) InitializeStruct(s interface{}) error {
 	if s == nil {
 		return errors.Errorf("Can't initialize nil struct")
 	}
+
 	// check that s is indeed a pointer to a struct
-	if reflect.TypeOf(s).Kind() != reflect.Ptr {
+	of := reflect.TypeOf(s)
+	if of.Kind() != reflect.Ptr {
 		return errors.Errorf("s is not a pointer")
 	}
-	if reflect.TypeOf(s).Elem().Kind() != reflect.Struct {
+	if of.Elem().Kind() != reflect.Struct {
 		return errors.Errorf("s is not a pointer to a struct")
 	}
-	st := reflect.TypeOf(s).Elem()
+	st := of.Elem()
 
 	for i := 0; i < st.NumField(); i++ {
 		field := st.Field(i)
-		v, ok := field.Tag.Lookup("glazed.parameter")
+		tag, ok := field.Tag.Lookup("glazed.parameter")
 		if !ok {
 			continue
 		}
-		v_, ok := p.Get(v)
+		options, err := parsedTagOptions(tag)
+		if err != nil {
+			return errors.Wrapf(err, "failed to parse glazed.parameter tag for field %s", field.Name)
+		}
+
+		parameter, ok := p.Get(options.Name)
 		if !ok {
 			continue
 		}
-		value := reflect.ValueOf(s).Elem().FieldByName(field.Name)
+		dst := reflect.ValueOf(s).Elem().FieldByName(field.Name)
+		kind := field.Type.Kind()
 
-		if field.Type.Kind() == reflect.Ptr {
+		if dst.Type() == reflect.TypeOf(parameter.Value) && !options.FromJson {
+			dst.Set(reflect.ValueOf(parameter.Value))
+			continue
+		}
+
+		wasPointer := false
+		if kind == reflect.Ptr {
 			elem := field.Type.Elem()
-			if value.IsNil() {
-				value.Set(reflect.New(elem))
+			kind = elem.Kind()
+			if dst.IsNil() {
+				kind = elem.Kind()
+				value := reflect.New(elem)
+				dst.Set(value)
 			}
-			//exhaustive:ignore
-			switch elem.Kind() {
-			case reflect.Struct:
-				err := p.InitializeStruct(value.Interface())
+			dst = dst.Elem()
+			wasPointer = true
+		}
+
+		if options.FromJson {
+			if !wasPointer {
+				return errors.Errorf("from_json tag can only be used on pointer fields")
+			}
+
+			switch parameter.Value.(type) {
+			case *FileData:
+				err := json.Unmarshal([]byte(parameter.Value.(*FileData).Content), dst.Interface())
 				if err != nil {
-					return errors.Wrapf(err, "failed to initialize struct for %s", v)
+					return errors.Wrapf(err, "failed to unmarshal json for %s", options.Name)
 				}
-			default:
-				err := reflect2.SetReflectValue(value.Elem(), v_.Value)
+			case string:
+				err := json.Unmarshal([]byte(parameter.Value.(string)), dst.Interface())
 				if err != nil {
-					return errors.Wrapf(err, "failed to set value for %s", v)
+					return errors.Wrapf(err, "failed to unmarshal json for %s", options.Name)
 				}
+			case []byte, json.RawMessage:
+				err := json.Unmarshal(parameter.Value.([]byte), dst.Interface())
+				if err != nil {
+					return errors.Wrapf(err, "failed to unmarshal json for %s", options.Name)
+				}
+			default:
+				return errors.Errorf("unsupported type for %s", options.Name)
 			}
 
-		} else {
-			err := reflect2.SetReflectValue(value, v_.Value)
+			continue
+		}
+
+		if kind == reflect.Struct {
+			err := p.InitializeStruct(dst.Interface())
 			if err != nil {
-				return errors.Wrapf(err, "failed to set value for %s", v)
+				return errors.Wrapf(err, "failed to initialize struct for %s", options.Name)
 			}
+			continue
+		}
+
+		kind2 := dst.Kind()
+		_ = kind2
+		err = reflect_helpers.SetReflectValue(dst, parameter.Value)
+		if err != nil {
+			return errors.Wrapf(err, "failed to set value %v for %s from value %v", options.Name, dst, parameter.Value)
 		}
 	}
 
 	return nil
 }
+
+type tagOptions struct {
+	Name     string
+	FromJson bool
+}
+
+func parsedTagOptions(tag string) (*tagOptions, error) {
+	options := strings.Split(tag, ",")
+	if len(options) == 0 {
+		return nil, errors.Errorf("invalid empty glazed.parameter tag")
+	}
+	name := options[0]
+	options = options[1:]
+
+	fromJson := false
+	for _, o := range options {
+		if o == "from_json" {
+			fromJson = true
+			break
+		}
+	}
+
+	return &tagOptions{
+		Name:     name,
+		FromJson: fromJson,
+	}, nil
+}
diff --git a/pkg/cmds/parameters/parameters.go b/pkg/cmds/parameters/parameters.go
index 866c606..ad574f7 100644
--- a/pkg/cmds/parameters/parameters.go
+++ b/pkg/cmds/parameters/parameters.go
@@ -47,7 +47,7 @@ func WithDefault(defaultValue interface{}) ParameterDefinitionOption {
 	}
 }
 
-func WithChoices(choices []string) ParameterDefinitionOption {
+func WithChoices(choices ...string) ParameterDefinitionOption {
 	return func(p *ParameterDefinition) {
 		p.Choices = choices
 	}
diff --git a/pkg/cmds/parameters/parse.go b/pkg/cmds/parameters/parse.go
index 9df263c..df432de 100644
--- a/pkg/cmds/parameters/parse.go
+++ b/pkg/cmds/parameters/parse.go
@@ -231,7 +231,7 @@ func (p *ParameterDefinition) ParseParameter(v []string, options ...ParseStepOpt
 		if err != nil {
 			return nil, err
 		}
-		v_ = v__
+		v_ = v__.Value
 
 	case ParameterTypeStringFromFile:
 		fallthrough
@@ -297,7 +297,7 @@ func (p *ParameterDefinition) ParseParameter(v []string, options ...ParseStepOpt
 			if err != nil {
 				return nil, err
 			}
-			v_ = v__
+			v_ = v__.Value
 
 		default:
 			ret_ := map[string]interface{}{}
diff --git a/pkg/helpers/files/files.go b/pkg/helpers/files/files.go
index f0ef76e..b8883d6 100644
--- a/pkg/helpers/files/files.go
+++ b/pkg/helpers/files/files.go
@@ -3,6 +3,7 @@ package files
 import (
 	"encoding/json"
 	"gopkg.in/yaml.v3"
+	"io"
 	"os"
 )
 
@@ -23,3 +24,26 @@ func LoadYAMLFile(path string, target interface{}) error {
 
 	return yaml.Unmarshal(b, target)
 }
+
+// ConcatFiles creates an io.Reader that reads from the provided files in order.
+func ConcatFiles(filePaths ...string) (io.Reader, error) {
+	var readers []io.Reader
+
+	// Open each file and create a reader for it.
+	for _, path := range filePaths {
+		file, err := os.Open(path)
+		if err != nil {
+			// Close all opened files upon an error.
+			for _, r := range readers {
+				if f, ok := r.(*os.File); ok {
+					_ = f.Close()
+				}
+			}
+			return nil, err
+		}
+		readers = append(readers, file)
+	}
+
+	// Combine all readers into a single MultiReader.
+	return io.MultiReader(readers...), nil
+}
diff --git a/pkg/helpers/reflect/reflect.go b/pkg/helpers/reflect/reflect.go
index 78f53c6..b6af990 100644
--- a/pkg/helpers/reflect/reflect.go
+++ b/pkg/helpers/reflect/reflect.go
@@ -7,289 +7,289 @@ import (
 	"strconv"
 )
 
-func SetReflectValue(value reflect.Value, v interface{}) error {
-	if !value.IsValid() {
-		return fmt.Errorf("cannot set invalid reflect.Value")
+func SetReflectValue(dst reflect.Value, src interface{}) error {
+	if !dst.IsValid() {
+		return fmt.Errorf("cannot set invalid reflect.Value of type %s", dst.Kind())
 	}
-	if !value.CanSet() {
-		return fmt.Errorf("cannot set reflect.Value")
+	if !dst.CanSet() {
+		return fmt.Errorf("cannot set reflect.Value of type %s", dst.Kind())
 	}
 
-	if v == nil {
-		value.Set(reflect.Zero(value.Type()))
+	if src == nil {
+		dst.Set(reflect.Zero(dst.Type()))
 		return nil
 	}
 
 	//exhaustive:ignore
-	switch value.Kind() {
+	switch dst.Kind() {
 	case reflect.String:
-		if s, ok := v.(string); ok {
-			value.SetString(s)
+		if s, ok := src.(string); ok {
+			dst.SetString(s)
 			return nil
 		}
-		return fmt.Errorf("cannot set reflect.Value of type %s from %T", value.Kind(), v)
+		return fmt.Errorf("cannot set reflect.Value of type %s from %T", dst.Kind(), src)
 
 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
-		if s, ok := v.(string); ok {
+		if s, ok := src.(string); ok {
 			i, err := strconv.ParseUint(s, 10, 64)
 			if err != nil {
 				return err
 			}
-			value.SetUint(i)
+			dst.SetUint(i)
 			return nil
 		}
-		if i, ok := v.(uint64); ok {
-			value.SetUint(i)
+		if i, ok := src.(uint64); ok {
+			dst.SetUint(i)
 			return nil
 		}
-		if i, ok := v.(uint); ok {
-			value.SetUint(uint64(i))
+		if i, ok := src.(uint); ok {
+			dst.SetUint(uint64(i))
 			return nil
 		}
-		if i, ok := v.(uint8); ok {
-			value.SetUint(uint64(i))
+		if i, ok := src.(uint8); ok {
+			dst.SetUint(uint64(i))
 			return nil
 		}
-		if i, ok := v.(uint16); ok {
-			value.SetUint(uint64(i))
+		if i, ok := src.(uint16); ok {
+			dst.SetUint(uint64(i))
 			return nil
 		}
-		if i, ok := v.(uint32); ok {
-			value.SetUint(uint64(i))
+		if i, ok := src.(uint32); ok {
+			dst.SetUint(uint64(i))
 			return nil
 		}
-		if i, ok := v.(int64); ok {
-			value.SetUint(uint64(i))
+		if i, ok := src.(int64); ok {
+			dst.SetUint(uint64(i))
 			return nil
 		}
-		if i, ok := v.(int); ok {
-			value.SetUint(uint64(i))
+		if i, ok := src.(int); ok {
+			dst.SetUint(uint64(i))
 			return nil
 		}
-		if i, ok := v.(int8); ok {
-			value.SetUint(uint64(i))
+		if i, ok := src.(int8); ok {
+			dst.SetUint(uint64(i))
 			return nil
 		}
-		if i, ok := v.(int16); ok {
-			value.SetUint(uint64(i))
+		if i, ok := src.(int16); ok {
+			dst.SetUint(uint64(i))
 			return nil
 		}
-		if i, ok := v.(int32); ok {
-			value.SetUint(uint64(i))
+		if i, ok := src.(int32); ok {
+			dst.SetUint(uint64(i))
 			return nil
 		}
-		return fmt.Errorf("cannot set reflect.Value of type %s from %T", value.Kind(), v)
+		return fmt.Errorf("cannot set reflect.Value of type %s from %T", dst.Kind(), src)
 
 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
-		if s, ok := v.(string); ok {
+		if s, ok := src.(string); ok {
 			i, err := strconv.ParseInt(s, 10, 64)
 			if err != nil {
 				return err
 			}
-			value.SetInt(i)
+			dst.SetInt(i)
 			return nil
 		}
-		if i, ok := v.(int64); ok {
-			value.SetInt(i)
+		if i, ok := src.(int64); ok {
+			dst.SetInt(i)
 			return nil
 		}
-		if i, ok := v.(int); ok {
-			value.SetInt(int64(i))
+		if i, ok := src.(int); ok {
+			dst.SetInt(int64(i))
 			return nil
 		}
-		if i, ok := v.(int8); ok {
-			value.SetInt(int64(i))
+		if i, ok := src.(int8); ok {
+			dst.SetInt(int64(i))
 			return nil
 		}
-		if i, ok := v.(int16); ok {
-			value.SetInt(int64(i))
+		if i, ok := src.(int16); ok {
+			dst.SetInt(int64(i))
 			return nil
 		}
-		if i, ok := v.(int32); ok {
-			value.SetInt(int64(i))
+		if i, ok := src.(int32); ok {
+			dst.SetInt(int64(i))
 			return nil
 		}
 
-		if i, ok := v.(uint64); ok {
-			value.SetInt(int64(i))
+		if i, ok := src.(uint64); ok {
+			dst.SetInt(int64(i))
 			return nil
 		}
-		if i, ok := v.(uint); ok {
-			value.SetInt(int64(i))
+		if i, ok := src.(uint); ok {
+			dst.SetInt(int64(i))
 			return nil
 		}
-		if i, ok := v.(uint8); ok {
-			value.SetInt(int64(i))
+		if i, ok := src.(uint8); ok {
+			dst.SetInt(int64(i))
 			return nil
 		}
-		if i, ok := v.(uint16); ok {
-			value.SetInt(int64(i))
+		if i, ok := src.(uint16); ok {
+			dst.SetInt(int64(i))
 			return nil
 		}
-		if i, ok := v.(uint32); ok {
-			value.SetInt(int64(i))
+		if i, ok := src.(uint32); ok {
+			dst.SetInt(int64(i))
 			return nil
 		}
-		return fmt.Errorf("cannot set reflect.Value of type %s from %T", value.Kind(), v)
+		return fmt.Errorf("cannot set reflect.Value of type %s from %T", dst.Kind(), src)
 
 	case reflect.Float32, reflect.Float64:
-		if f, ok := v.(float64); ok {
-			value.SetFloat(f)
+		if f, ok := src.(float64); ok {
+			dst.SetFloat(f)
 			return nil
 		}
-		if f, ok := v.(float32); ok {
-			value.SetFloat(float64(f))
+		if f, ok := src.(float32); ok {
+			dst.SetFloat(float64(f))
 			return nil
 		}
-		if i, ok := v.(int64); ok {
-			value.SetFloat(float64(i))
+		if i, ok := src.(int64); ok {
+			dst.SetFloat(float64(i))
 			return nil
 		}
-		if i, ok := v.(int); ok {
-			value.SetFloat(float64(i))
+		if i, ok := src.(int); ok {
+			dst.SetFloat(float64(i))
 			return nil
 		}
-		if i, ok := v.(int8); ok {
-			value.SetFloat(float64(i))
+		if i, ok := src.(int8); ok {
+			dst.SetFloat(float64(i))
 			return nil
 		}
-		if i, ok := v.(int16); ok {
-			value.SetFloat(float64(i))
+		if i, ok := src.(int16); ok {
+			dst.SetFloat(float64(i))
 			return nil
 		}
-		if i, ok := v.(int32); ok {
-			value.SetFloat(float64(i))
+		if i, ok := src.(int32); ok {
+			dst.SetFloat(float64(i))
 			return nil
 		}
-		if i, ok := v.(uint64); ok {
-			value.SetFloat(float64(i))
+		if i, ok := src.(uint64); ok {
+			dst.SetFloat(float64(i))
 			return nil
 		}
-		if i, ok := v.(uint); ok {
-			value.SetFloat(float64(i))
+		if i, ok := src.(uint); ok {
+			dst.SetFloat(float64(i))
 			return nil
 		}
-		if i, ok := v.(uint8); ok {
-			value.SetFloat(float64(i))
+		if i, ok := src.(uint8); ok {
+			dst.SetFloat(float64(i))
 			return nil
 		}
-		if i, ok := v.(uint16); ok {
-			value.SetFloat(float64(i))
+		if i, ok := src.(uint16); ok {
+			dst.SetFloat(float64(i))
 			return nil
 		}
-		if i, ok := v.(uint32); ok {
-			value.SetFloat(float64(i))
+		if i, ok := src.(uint32); ok {
+			dst.SetFloat(float64(i))
 			return nil
 		}
-		return fmt.Errorf("cannot set reflect.Value of type %s from %T", value.Kind(), v)
+		return fmt.Errorf("cannot set reflect.Value of type %s from %T", dst.Kind(), src)
 	case reflect.Bool:
-		if b, ok := v.(bool); ok {
-			value.SetBool(b)
+		if b, ok := src.(bool); ok {
+			dst.SetBool(b)
 			return nil
 		}
-		return fmt.Errorf("cannot set reflect.Value of type %s from %T", value.Kind(), v)
+		return fmt.Errorf("cannot set reflect.Value of type %s from %T", dst.Kind(), src)
 	case reflect.Slice:
 		//exhaustive:ignore
-		sliceKind := value.Type().Elem().Kind()
+		sliceKind := dst.Type().Elem().Kind()
 		//exhaustive:ignore
 		switch sliceKind {
 		case reflect.String:
-			if s, ok := v.([]string); ok {
-				value.Set(reflect.ValueOf(s))
+			if s, ok := src.([]string); ok {
+				dst.Set(reflect.ValueOf(s))
 				return nil
 			}
-			if s, ok := v.([]interface{}); ok {
+			if s, ok := src.([]interface{}); ok {
 				v2_, ok := cast.CastList[string, interface{}](s)
 				if !ok {
-					return fmt.Errorf("cannot cast %T to []string", v)
+					return fmt.Errorf("cannot cast %T to []string", src)
 				}
-				value.Set(reflect.ValueOf(v2_))
+				dst.Set(reflect.ValueOf(v2_))
 				return nil
 			}
 
-			return fmt.Errorf("cannot set reflect.Value of type %s from %T", value.Kind(), v)
+			return fmt.Errorf("cannot set reflect.Value of type %s from %T", dst.Kind(), src)
 
 		case reflect.Int:
-			return SetIntListReflectValue[int](value, v)
+			return SetIntListReflectValue[int](dst, src)
 
 		case reflect.Int8:
-			return SetIntListReflectValue[int8](value, v)
+			return SetIntListReflectValue[int8](dst, src)
 
 		case reflect.Int16:
-			return SetIntListReflectValue[int16](value, v)
+			return SetIntListReflectValue[int16](dst, src)
 
 		case reflect.Int32:
-			return SetIntListReflectValue[int32](value, v)
+			return SetIntListReflectValue[int32](dst, src)
 
 		case reflect.Int64:
-			return SetIntListReflectValue[int64](value, v)
+			return SetIntListReflectValue[int64](dst, src)
 
 		case reflect.Uint:
-			return SetIntListReflectValue[uint](value, v)
+			return SetIntListReflectValue[uint](dst, src)
 
 		case reflect.Uint8:
-			return SetIntListReflectValue[uint8](value, v)
+			return SetIntListReflectValue[uint8](dst, src)
 
 		case reflect.Uint16:
-			return SetIntListReflectValue[uint16](value, v)
+			return SetIntListReflectValue[uint16](dst, src)
 
 		case reflect.Uint32:
-			return SetIntListReflectValue[uint32](value, v)
+			return SetIntListReflectValue[uint32](dst, src)
 
 		case reflect.Uint64:
-			return SetIntListReflectValue[uint64](value, v)
+			return SetIntListReflectValue[uint64](dst, src)
 
 		case reflect.Float32:
-			return SetFloatListReflectValue[float32](value, v)
+			return SetFloatListReflectValue[float32](dst, src)
 
 		case reflect.Float64:
-			return SetFloatListReflectValue[float64](value, v)
+			return SetFloatListReflectValue[float64](dst, src)
 
 		default:
-			return fmt.Errorf("cannot set reflect.Value of type %s from %T", value.Kind(), v)
+			return fmt.Errorf("cannot set reflect.Value of type %s from %T", dst.Kind(), src)
 		}
 
 	case reflect.Map:
 		//exhaustive:ignore
-		switch value.Type().Elem().Kind() {
+		switch dst.Type().Elem().Kind() {
 		case reflect.String:
-			if m, ok := v.(map[string]string); ok {
-				value.Set(reflect.ValueOf(m))
+			if m, ok := src.(map[string]string); ok {
+				dst.Set(reflect.ValueOf(m))
 				return nil
 			}
 
-			if m, ok := v.(map[string]interface{}); ok {
+			if m, ok := src.(map[string]interface{}); ok {
 				v2_, ok := cast.CastStringMap[string, interface{}](m)
 				if !ok {
-					return fmt.Errorf("cannot cast %T to map[string]string", v)
+					return fmt.Errorf("cannot cast %T to map[string]string", src)
 				}
-				value.Set(reflect.ValueOf(v2_))
+				dst.Set(reflect.ValueOf(v2_))
 				return nil
 			}
 
 		case reflect.Interface:
-			if m, ok := v.(map[string]interface{}); ok {
-				value.Set(reflect.ValueOf(m))
+			if m, ok := src.(map[string]interface{}); ok {
+				dst.Set(reflect.ValueOf(m))
 				return nil
 			}
 
-			if m, ok := v.(map[string]string); ok {
+			if m, ok := src.(map[string]string); ok {
 				v2_, ok := cast.CastStringMap[interface{}, string](m)
 				if !ok {
-					return fmt.Errorf("cannot cast %T to map[string]interface{}", v)
+					return fmt.Errorf("cannot cast %T to map[string]interface{}", src)
 				}
-				value.Set(reflect.ValueOf(v2_))
+				dst.Set(reflect.ValueOf(v2_))
 				return nil
 
 			}
 
 		default:
-			return fmt.Errorf("cannot set reflect.Value of type %s from %T", value.Kind(), v)
+			return fmt.Errorf("cannot set reflect.Value of type %s from %T", dst.Kind(), src)
 		}
-		return fmt.Errorf("cannot set reflect.Value of type %s from %T", value.Kind(), v)
+		return fmt.Errorf("cannot set reflect.Value of type %s from %T", dst.Kind(), src)
 
 	default:
-		return fmt.Errorf("unsupported reflect.Value type %s", value.Kind())
+		return fmt.Errorf("unsupported reflect.Value type %s", dst.Kind())
 	}
 }
 
diff --git a/prompto/glazed/create-command.md b/prompto/glazed/create-command.md
deleted file mode 100644
index cd8e6f3..0000000
--- a/prompto/glazed/create-command.md
+++ /dev/null
@@ -1,311 +0,0 @@
-# Creating a New Command for `glazed` Framework
-
-The `glazed` framework provides a declarative way to define commands with flags, arguments, and output structured data. This README will guide you step-by-step on how to create a new command for the framework.
-
-## 1. **Understanding the Building Blocks**
-
-Before diving into creating a command, it's crucial to understand the components provided by the `glazed` framework:
-
-### 1.1 Parameter Types
-
-1.1 Parameter Types
-
-ParameterType establishes the data type of a parameter. Commands can incorporate multiple flags and arguments, each with a designated parameter type. Examples include:
-
-    ParameterTypeString -> string
-    ParameterTypeFile -> FileData
-    ParameterTypeInteger -> int
-    ParameterTypeStringFromFile -> string
-    ParameterTypeStringFromFiles -> string
-    ParameterTypeFileList -> []FileData
-    ParameterTypeObjectListFromFile -> []map[string]interface{}
-    ParameterTypeObjectListFromFiles -> []map[string]interface{}
-    ParameterTypeObjectFromFile -> map[string]interface{}
-    ParameterTypeStringListFromFile -> []string
-    ParameterTypeStringListFromFiles -> []string
-    ParameterTypeKeyValue: -> map[string]string
-    ParameterTypeFloat -> float64
-    ParameterTypeBool -> bool
-    ParameterTypeDate  -> time.Time
-    ParameterTypeStringList -> []string
-    ParameterTypeIntegerList -> []int
-    ParameterTypeFloatList -> []float64
-    ParameterTypeChoice -> string
-    ParameterTypeChoiceList -> []string
-
-### 1.2 FileData
-
-`FileData` is a structure that provides detailed information about a file.
-This is useful when your command needs to work with files.
-
-    Content: File's string content.
-    ParsedContent: Parsed version of the file's content (for json and yaml files).
-    ParseError: Any error that occurred during parsing.
-    RawContent: File content in byte format.
-    StringContent: File content as a string.
-    IsList: Indicates if the content represents a list.
-    IsObject: Signifies if the content denotes an object.
-    BaseName: File's base name.
-    Extension: File's extension.
-    FileType: File's type.
-    Path: File's path.
-    RelativePath: File's relative path.
-    AbsolutePath: File's absolute path.
-    Size: File's size in bytes.
-    LastModifiedTime: Timestamp when the file was last modified.
-    Permissions: File's permissions.
-    IsDirectory: Indicates if the file is a directory.
-
-### 1.3 CommandDescription
-
-This is a structure that contains the necessary information for registering a command. It has properties like:
-
-- `Name`: Command name.
-- `Short`: Short description.
-- `Flags`: List of parameter definitions for flags.
-- `Arguments`: List of parameter definitions for command arguments.
-
-### 1.4 ParameterDefinition
-
-This structure describes a command-line parameter, whether it's a flag or an argument.
-
-## 2. **Steps to Create a New Command**
-
-### 2.1 Define the Command Structure
-
-Create a new structure for your command that embeds `CommandDescription`. This structure will contain all the necessary configurations for your command.
-
-Example:
-
-```go
-type MyNewCommand struct {
-	*cmds.CommandDescription
-}
-```
-
-### 2.2 Initialize the Command
-
-Create a function to initialize your new command:
-
-```go
-func NewMyNewCommand() (*MyNewCommand, error) {
-	// Command initialization logic here
-}
-```
-
-### 2.3 Define Flags and Arguments
-
-Utilize the `ParameterDefinition` structure to define flags and arguments for your command.
-
-Example:
-
-```go
-flag1 := parameters.NewParameterDefinition(
-	"flagName",
-	parameters.ParameterTypeString,
-	parameters.WithHelp("Help description for the flag"),
-	parameters.WithDefault("default_value"),
-)
-
-arg1 := parameters.NewParameterDefinition(
-	"argName",
-	parameters.ParameterTypeInteger,
-	parameters.WithHelp("Help description for the argument"),
-	parameters.WithDefault(10),
-)
-```
-
-### 2.4 Assemble the Command
-
-When assembling your command, a notable addition you can include is the `glazedParameterLayer`. This layer adds support for all the glazed structured data layer flags, enriching your command with more capabilities. Here's how you can integrate it:
-
-```go
-glazedParameterLayer, err := settings.NewGlazedParameterLayers()
-if err != nil {
-	return nil, errors.Wrap(err, "could not create Glazed parameter layer")
-}
-
-return &ExampleCommand{
-	CommandDescription: cmds.NewCommandDescription(
-		"example",
-		cmds.WithShort("Example command"),
-		cmds.WithFlags(
-			parameters.NewParameterDefinition(
-				"count",
-				parameters.ParameterTypeInteger,
-				parameters.WithHelp("Number of rows to output"),
-				parameters.WithDefault(10),
-			),
-		),
-		cmds.WithArguments(
-			parameters.NewParameterDefinition(
-				"test",
-				parameters.ParameterTypeBool,
-				parameters.WithHelp("Whether to add a test column"),
-				parameters.WithDefault(false),
-			),
-		),
-		cmds.WithLayers(
-			glazedParameterLayer,
-		),
-	),
-}, nil
-```
-
-
----
-
-## 2.5 **Different Types of `Run` Methods Based on Command Types**
-
-The way you implement the `Run` method in your command depends on the type of command you're building. The `glazed`
-framework offers several command types, each with a specific purpose and signature for the `Run` method.
-
-### **BareCommand**
-The basic command type that requires a simpler `Run` method signature.
-The focus here is on providing the parsed layers and parsed flags for your command execution.
-Output is entirely left to the BareCommand.
-
-```go
-type BareCommand interface {
-	Command
-	Run(
-		ctx context.Context,
-		parsedLayers map[string]*layers.ParsedParameterLayer,
-		ps map[string]interface{},
-	) error
-}
-```
-
-### **WriterCommand**
-
-This command type introduces the capability to write outputs to a provided writer. It's particularly useful when your
-command needs to print or send data to an external output stream.
-
-```go
-type WriterCommand interface {
-	Command
-	RunIntoWriter(
-		ctx context.Context,
-		parsedLayers map[string]*layers.ParsedParameterLayer,
-		ps map[string]interface{},
-		w io.Writer,
-	) error
-}
-```
-
-### **GlazeCommand**
-
-The GlazeCommand is a specialized command type that deals with the `glazed` framework's structured data capabilities.
-Its `Run` method signature not only encompasses parsed layers and parsed flags but also a `GlazeProcessor` which can be
-utilized to emit rows of data.
-
-```go
-type GlazeCommand interface {
-	Command
-	Run(
-		ctx context.Context,
-		parsedLayers map[string]*layers.ParsedParameterLayer,
-		ps map[string]interface{},
-		gp middlewares.Processor,
-	) error
-}
-```
-
-It's vital to understand the specifics of each command type to ensure your `Run` method aligns with the intended command behavior.
-
-## 2.6 **Implementing the `Run` Method for a `GlazeCommand`**
-
-Given the specialized nature of a `GlazeCommand`, here's how you can effectively implement the `Run` method for this
-command type:
-
-```go
-func (c *ExampleCommand) Run(
-	ctx context.Context,
-	parsedLayers map[string]*layers.ParsedParameterLayer,
-	ps map[string]interface{},
-	gp middlewares.Processor,
-) error {
-	count := ps["count"].(int)
-	test := ps["test"].(bool)
-
-	for i := 0; i < count; i++ {
-		row := types.NewRow(
-			types.MRP("id", i),
-			types.MRP("name", "foobar-"+strconv.Itoa(i)),
-		)
-
-		if test {
-			row.Set("test", rand.Intn(100)+1)
-		}
-
-		if err := gp.AddRow(ctx, row); err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-```
-
-## 2.7 **Creating and Managing Rows in `GlazeCommand` with `types.Row`**
-
-Within the `glazed` framework, `types.Row` represents an ordered map, ensuring consistent field order in rows.
-
-### **Creating Rows with `types.Row` and `MRP`**
-
-- **Direct Initialization**: Use `NewRow` with the `MRP` (MapRowPair) function to directly initialize rows while maintaining the field order:
-
-  ```go
-  row := types.NewRow(
-      types.MRP("id", i),
-      types.MRP("name", "foobar-"+strconv.Itoa(i)),
-  )
-  ```
-
-  `MRP` is a utility function to quickly create a key-value pair for a row.
-
-### **Other Methods for Creating Rows**
-
-- **From Map**: Use `NewRowFromMap` to create a row from a regular map. It sorts and maintains the order of keys:
-
-  ```go
-  data := map[string]interface{}{"id": 1, "name": "Alice"}
-  row := types.NewRowFromMap(data)
-  ```
-
-- **From Struct**: `NewRowFromStruct` allows you to convert a struct into a row. Optionally, you can specify whether struct field names should be converted to lowercase:
-
-  ```go
-  type Person struct {
-      ID   int
-      Name string
-  }
-  person := Person{ID: 1, Name: "Alice"}
-  row := types.NewRowFromStruct(&person, true)  // true indicates lowercase field names.
-  ```
-
-These functions offer flexibility in how you create and work with rows within the `glazed` framework.
-
-## 3. **Integrating `glazed` Commands with Cobra**
-
-Now, the core part: adding your `glazed` command to the Cobra root command.
-This requires converting the `glazed` command into a Cobra command using the provided utility functions.
-
-```go
-func registerCommands(rootCmd *cobra.Command) {
-	// For a GlazeCommand
-	glazeCmdInstance, err := NewMyGlazeCommand() // Assuming you've created this function
-	cobra.CheckErr(err)
-
-	command, err := BuildCobraCommandFromGlazeCommand(glazeCmdInstance)
-	cobra.CheckErr(err)
-
-	rootCmd.AddCommand(command)
-	
-	// Similarly, for WriterCommand or BareCommand
-	// writerCmdInstance, err := NewMyWriterCommand()
-	// command, err = BuildCobraCommandFromWriterCommand(writerCmdInstance)
-	// rootCmd.AddCommand(command)
-}
-```
-
diff --git a/prompto/glazed/create-command.yaml b/prompto/glazed/create-command.yaml
index 3e7be46..bc89b62 100644
--- a/prompto/glazed/create-command.yaml
+++ b/prompto/glazed/create-command.yaml
@@ -166,6 +166,7 @@ template: |
 
   ```go
   import 	"github.com/go-go-golems/glazed/pkg/settings"
+  
   glazedParameterLayer, err := settings.NewGlazedParameterLayers()
   if err != nil {
       return nil, errors.Wrap(err, "could not create Glazed parameter layer")
@@ -197,6 +198,15 @@ template: |
       ),
   }, nil
   ```
+  
+  You can then create a struct to represent those parameters and map them to flags using the glazed.parameter tag:
+  
+  ```go
+  type ExampleSettings struct {
+      Count int `glazed.parameter:"count"`
+      Test  bool `glazed.parameter:"test"`
+  }
+  ```
   {{ end }}
   
   {{ if .run }}
@@ -224,8 +234,7 @@ template: |
       Command
       Run(
           ctx context.Context,
-          parsedLayers map[string]*layers.ParsedParameterLayer,
-          ps map[string]interface{},
+          parsedLayers *layers.ParsedLayers,
       ) error
   }
   ```
@@ -243,7 +252,6 @@ template: |
       Command
       RunIntoWriter(
           ctx context.Context,
-          parsedLayers map[string]*layers.ParsedParameterLayer,
           ps map[string]interface{},
           w io.Writer,
       ) error
@@ -262,10 +270,9 @@ template: |
   package "github.com/go-go-golems/glazed/pkg/cmds"
   type GlazeCommand interface {
       Command
-      Run(
+      RunIntoGlazeProcessor(
           ctx context.Context,
-          parsedLayers map[string]*layers.ParsedParameterLayer,
-          ps map[string]interface{},
+          parsedLayers *layers.ParsedLayers,
           gp middlewares.Processor,
       ) error
   }
@@ -285,20 +292,21 @@ template: |
 
     func (c *ExampleCommand) Run(
       ctx context.Context,
-      parsedLayers map[string]*layers.ParsedParameterLayer,
-      ps map[string]interface{},
+      parsedLayers *layers.ParsedLayers,
       gp middlewares.Processor,
     ) error {
-      count := ps["count"].(int)
-      test := ps["test"].(bool)
+    s := &ExampleSettings{}
+    if err := parsedLayers.InitializeStruct(layers.DefaultSlug, s); err != nil {
+      return err
+    }
     
-    for i := 0; i < count; i++ {
+    for i := 0; i < s.Count; i++ {
       row := types.NewRow(
         types.MRP("id", i),
         types.MRP("name", "foobar-"+strconv.Itoa(i)),
       )
       
-      if test {
+      if s.Test {
         row.Set("test", rand.Intn(100)+1)
       }
       


Test patch:
diff --git a/pkg/cmds/parameters/initialize-struct_test.go b/pkg/cmds/parameters/initialize-struct_test.go
new file mode 100644
index 0000000..15a2af3
--- /dev/null
+++ b/pkg/cmds/parameters/initialize-struct_test.go
@@ -0,0 +1,272 @@
+package parameters_test
+
+import (
+	"github.com/go-go-golems/glazed/pkg/cmds/parameters" // Replace with the actual package path
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+// Define a struct that matches the expected structure for InitializeStruct
+type TestStruct struct {
+	Name string `glazed.parameter:"name"`
+	Age  int    `glazed.parameter:"age"`
+}
+
+// TestInitializeStructWithValidStruct tests initializing a struct with valid parameters
+func TestInitializeStructWithValidStruct(t *testing.T) {
+	// Create an instance of ParsedParameters with test data
+	parsedParams := parameters.NewParsedParameters(
+		parameters.WithParsedParameter(
+			parameters.NewParameterDefinition(
+				"name",
+				parameters.ParameterTypeString),
+			"name",
+			"John Doe"),
+		parameters.WithParsedParameter(
+			parameters.NewParameterDefinition(
+				"age",
+				parameters.ParameterTypeInteger),
+			"age", 30),
+	)
+
+	// Create an instance of the struct to be initialized
+	testStruct := &TestStruct{}
+
+	// Call InitializeStruct with the test struct and parsed parameters
+	err := parsedParams.InitializeStruct(testStruct)
+
+	// Assert that there is no error and the struct is initialized correctly
+	assert.NoError(t, err)
+	assert.Equal(t, "John Doe", testStruct.Name)
+	assert.Equal(t, 30, testStruct.Age)
+}
+
+// TestInitializeStructWithNilInput tests initializing a struct with a nil input
+func TestInitializeStructWithNilInput(t *testing.T) {
+	// Create an instance of ParsedParameters (can be empty for this test)
+	parsedParams := &parameters.ParsedParameters{}
+
+	// Call InitializeStruct with a nil pointer
+	err := parsedParams.InitializeStruct(nil)
+
+	assert.Error(t, err)
+	assert.Equal(t, "Can't initialize nil struct", err.Error())
+}
+
+// TestInitializeStructWithNonPointerInput tests initializing a struct with a non-pointer input
+func TestInitializeStructWithNonPointerInput(t *testing.T) {
+	// Create an instance of ParsedParameters (can be empty for this test)
+	parsedParams := &parameters.ParsedParameters{}
+
+	// Call InitializeStruct with a non-pointer value (struct value)
+	testStruct := TestStruct{}
+	err := parsedParams.InitializeStruct(testStruct)
+
+	// Assert that an error is returned and the error message is correct
+	assert.Error(t, err)
+	assert.Equal(t, "s is not a pointer", err.Error())
+}
+
+// TestInitializeStructWithNonStructPointer tests initializing a struct with a pointer to a non-struct type
+func TestInitializeStructWithNonStructPointer(t *testing.T) {
+	// Create an instance of ParsedParameters (can be empty for this test)
+	parsedParams := &parameters.ParsedParameters{}
+
+	// Call InitializeStruct with a pointer to a non-struct type (e.g., string)
+	nonStruct := "I am not a struct"
+	err := parsedParams.InitializeStruct(&nonStruct)
+
+	// Assert that an error is returned and the error message is correct
+	assert.Error(t, err)
+	assert.Equal(t, "s is not a pointer to a struct", err.Error())
+}
+
+// TestInitializeStructWithMissingParameters tests initializing a struct with missing parameters
+func TestInitializeStructWithMissingParameters(t *testing.T) {
+	// Create an instance of ParsedParameters with only one parameter defined
+	parsedParams := parameters.NewParsedParameters(
+		parameters.WithParsedParameter(
+			parameters.NewParameterDefinition(
+				"name",
+				parameters.ParameterTypeString),
+			"name",
+			"John Doe"),
+	)
+
+	// Create an instance of the struct to be initialized
+	testStruct := &TestStruct{}
+
+	// Call InitializeStruct with the test struct and parsed parameters
+	err := parsedParams.InitializeStruct(testStruct)
+
+	// Assert that there is no error and the struct is partially initialized correctly
+	assert.NoError(t, err)
+	assert.Equal(t, "John Doe", testStruct.Name)
+	assert.Equal(t, 0, testStruct.Age) // Age should be the zero value since it's missing from parsedParams
+}
+
+// TestInitializeStructWithJSONTagOnNonPointerField tests initializing a struct with a `from_json` tag on a non-pointer field
+func TestInitializeStructWithJSONTagOnNonPointerField(t *testing.T) {
+	// Define a struct with a `from_json` tag on a non-pointer field
+	type TestStructWithJSONTag struct {
+		Config string `glazed.parameter:"config,from_json"`
+	}
+
+	// Create an instance of ParsedParameters with a JSON parameter
+	parsedParams := parameters.NewParsedParameters(
+		parameters.WithParsedParameter(
+			parameters.NewParameterDefinition(
+				"config",
+				parameters.ParameterTypeString),
+			"config",
+			`{"key": "value"}`),
+	)
+
+	// Create an instance of the struct to be initialized
+	testStruct := &TestStructWithJSONTag{}
+
+	// Call InitializeStruct with the test struct and parsed parameters
+	err := parsedParams.InitializeStruct(testStruct)
+
+	// Assert that an error is returned and the error message is correct
+	assert.Error(t, err)
+	assert.Contains(t, err.Error(), "from_json tag can only be used on pointer fields")
+}
+
+// TestParseStringListSuccessfully tests parsing a string list successfully
+func TestParseStringListSuccessfully(t *testing.T) {
+	// Define a struct that matches the expected structure for InitializeStruct
+	type TestStruct struct {
+		Tags []string `glazed.parameter:"tags"`
+	}
+
+	// Create an instance of ParsedParameters with test data
+	parsedParams := parameters.NewParsedParameters(
+		parameters.WithParsedParameter(
+			parameters.NewParameterDefinition(
+				"tags",
+				parameters.ParameterTypeStringList),
+			"tags",
+			[]string{"go", "testing", "glazed"}),
+	)
+
+	// Create an instance of the struct to be initialized
+	testStruct := &TestStruct{}
+
+	// Call InitializeStruct with the test struct and parsed parameters
+	err := parsedParams.InitializeStruct(testStruct)
+
+	// Assert that there is no error and the struct is initialized correctly
+	assert.NoError(t, err)
+	assert.Equal(t, []string{"go", "testing", "glazed"}, testStruct.Tags)
+}
+
+// TestParseIntListSuccessfully tests parsing an integer list successfully
+func TestParseIntListSuccessfully(t *testing.T) {
+	// Define a struct that matches the expected structure for InitializeStruct
+	type TestStruct struct {
+		Numbers []int `glazed.parameter:"numbers"`
+	}
+
+	// Create an instance of ParsedParameters with test data
+	parsedParams := parameters.NewParsedParameters(
+		parameters.WithParsedParameter(
+			parameters.NewParameterDefinition(
+				"numbers",
+				parameters.ParameterTypeIntegerList),
+			"numbers",
+			[]int{1, 2, 3, 4, 5}),
+	)
+
+	// Create an instance of the struct to be initialized
+	testStruct := &TestStruct{}
+
+	// Call InitializeStruct with the test struct and parsed parameters
+	err := parsedParams.InitializeStruct(testStruct)
+
+	// Assert that there is no error and the struct is initialized correctly
+	assert.NoError(t, err)
+	assert.Equal(t, []int{1, 2, 3, 4, 5}, testStruct.Numbers)
+}
+
+// TestParseObjectFromFileSuccessfully tests parsing an object from file successfully
+func TestParseObjectFromFileSuccessfully(t *testing.T) {
+	// Define a struct that matches the expected structure for InitializeStruct
+	type TestStruct struct {
+		Config map[string]interface{} `glazed.parameter:"config"`
+	}
+
+	// Create an instance of ParsedParameters with test data
+	parsedParams := parameters.NewParsedParameters(
+		parameters.WithParsedParameter(
+			parameters.NewParameterDefinition(
+				"config",
+				parameters.ParameterTypeObjectFromFile),
+			"config",
+			map[string]interface{}{
+				"key1": "value1",
+				"key2": 42,
+			}),
+	)
+
+	// Create an instance of the struct to be initialized
+	testStruct := &TestStruct{}
+
+	// Call InitializeStruct with the test struct and parsed parameters
+	err := parsedParams.InitializeStruct(testStruct)
+
+	// Assert that there is no error and the struct is initialized correctly
+	assert.NoError(t, err)
+	expectedConfig := map[string]interface{}{
+		"key1": "value1",
+		"key2": 42, // JSON numbers are parsed as float64 by default
+	}
+	assert.Equal(t, expectedConfig, testStruct.Config)
+}
+
+// TestInitializeStructWithInvalidJSON tests initializing a struct with an invalid JSON string
+func TestInitializeStructWithInvalidJSON(t *testing.T) {
+	type TestStruct struct {
+		Config *map[string]interface{} `glazed.parameter:"config,from_json"`
+	}
+
+	parsedParams := parameters.NewParsedParameters(
+		parameters.WithParsedParameter(
+			parameters.NewParameterDefinition(
+				"config",
+				parameters.ParameterTypeString),
+			"config",
+			`{invalidJson: true}`), // Invalid JSON string
+	)
+
+	testStruct := &TestStruct{}
+
+	err := parsedParams.InitializeStruct(testStruct)
+
+	assert.Error(t, err)
+	assert.Contains(t, err.Error(), "failed to unmarshal json for config")
+}
+
+// TestInitializeStructWithUnsupportedTypeForJSON tests initializing a struct with an unsupported type for JSON
+func TestInitializeStructWithUnsupportedTypeForJSON(t *testing.T) {
+	type TestStruct struct {
+		Active bool `glazed.parameter:"active,from_json"` // Unsupported type for JSON unmarshaling
+	}
+
+	parsedParams := parameters.NewParsedParameters(
+		parameters.WithParsedParameter(
+			parameters.NewParameterDefinition(
+				"active",
+				parameters.ParameterTypeString),
+			"active",
+			`true`), // Valid JSON string
+	)
+
+	testStruct := &TestStruct{}
+
+	err := parsedParams.InitializeStruct(testStruct)
+
+	assert.Error(t, err)
+	assert.Contains(t, err.Error(), "from_json tag can only be used on pointer fields")
+}
diff --git a/pkg/cmds/parameters/parse_test.go b/pkg/cmds/parameters/parse_test.go
index daea75b..4c39d4a 100644
--- a/pkg/cmds/parameters/parse_test.go
+++ b/pkg/cmds/parameters/parse_test.go
@@ -341,7 +341,7 @@ func TestParseParameter(t *testing.T) {
 		parameter := NewParameterDefinition(
 			"test",
 			tt.ParameterType,
-			WithChoices(tt.Choices),
+			WithChoices(tt.Choices...),
 			WithDefault(tt.DefaultValue))
 
 		for _, tc := range tt.Cases {