Repository: go-go-golems/glazed
Pull request #331 (2023-08-12 16:37:45)

:art: :ambulance: Small fixes
- :sparkles: Add support to set uint from string (used when loading from ENV)
- :art: Check for nil value when initializing structs
- :ambulance: Instantiate aliases for writer commands too



Patch:
diff --git a/pkg/cli/cobra.go b/pkg/cli/cobra.go
index 4a0313e..cd9a92d 100644
--- a/pkg/cli/cobra.go
+++ b/pkg/cli/cobra.go
@@ -1,7 +1,6 @@
 package cli
 
 import (
-	"bytes"
 	"context"
 	"fmt"
 	"github.com/go-go-golems/glazed/pkg/cli/cliopatra"
@@ -71,7 +70,7 @@ func GetVerbsFromCobraCommand(cmd *cobra.Command) []string {
 	return verbs
 }
 
-func BuildCobraCommandFromCommand(s cmds.Command, run CobraRunFunc) (*cobra.Command, error) {
+func BuildCobraCommandFromCommandAndFunc(s cmds.Command, run CobraRunFunc) (*cobra.Command, error) {
 	description := s.Description()
 
 	cobraParser, err := NewCobraParserFromCommandDescription(description)
@@ -236,7 +235,7 @@ func BuildCobraCommandFromCommand(s cmds.Command, run CobraRunFunc) (*cobra.Comm
 }
 
 func BuildCobraCommandFromBareCommand(c cmds.BareCommand) (*cobra.Command, error) {
-	cmd, err := BuildCobraCommandFromCommand(c, func(
+	cmd, err := BuildCobraCommandFromCommandAndFunc(c, func(
 		ctx context.Context,
 		parsedLayers map[string]*layers.ParsedParameterLayer,
 		ps map[string]interface{},
@@ -259,21 +258,18 @@ func BuildCobraCommandFromBareCommand(c cmds.BareCommand) (*cobra.Command, error
 }
 
 func BuildCobraCommandFromWriterCommand(s cmds.WriterCommand) (*cobra.Command, error) {
-	cmd, err := BuildCobraCommandFromCommand(s, func(
+	cmd, err := BuildCobraCommandFromCommandAndFunc(s, func(
 		ctx context.Context,
 		parsedLayers map[string]*layers.ParsedParameterLayer,
 		ps map[string]interface{},
 	) error {
-		buf := &bytes.Buffer{}
-		err := s.RunIntoWriter(ctx, parsedLayers, ps, buf)
+		err := s.RunIntoWriter(ctx, parsedLayers, ps, os.Stdout)
 		if _, ok := err.(*cmds.ExitWithoutGlazeError); ok {
 			return nil
 		}
 		if err != context.Canceled {
 			cobra.CheckErr(err)
 		}
-
-		fmt.Println(buf.String())
 		return nil
 	})
 
@@ -284,13 +280,8 @@ func BuildCobraCommandFromWriterCommand(s cmds.WriterCommand) (*cobra.Command, e
 	return cmd, nil
 }
 
-func BuildCobraGlazeCommandAlias(alias *alias.CommandAlias) (*cobra.Command, error) {
-	s, ok := alias.AliasedCommand.(cmds.GlazeCommand)
-	if !ok {
-		return nil, fmt.Errorf("command %s is not a GlazeCommand", alias.AliasFor)
-	}
-
-	cmd, err := BuildCobraCommandFromGlazeCommand(s)
+func BuildCobraCommandAlias(alias *alias.CommandAlias) (*cobra.Command, error) {
+	cmd, err := BuildCobraCommandFromCommand(alias.AliasedCommand)
 	if err != nil {
 		return nil, err
 	}
@@ -298,7 +289,7 @@ func BuildCobraGlazeCommandAlias(alias *alias.CommandAlias) (*cobra.Command, err
 	origRun := cmd.Run
 
 	cmd.Use = alias.Name
-	cmd.Short = fmt.Sprintf("Alias for %s", s.Description().Name)
+	cmd.Short = fmt.Sprintf("Alias for %s", alias.AliasedCommand.Description().Name)
 
 	cmd.Run = func(cmd *cobra.Command, args []string) {
 		for k, v := range alias.Flags {
@@ -335,6 +326,29 @@ func findOrCreateParentCommand(rootCmd *cobra.Command, parents []string) *cobra.
 	return parentCmd
 }
 
+func BuildCobraCommandFromCommand(command cmds.Command) (*cobra.Command, error) {
+	var cobraCommand *cobra.Command
+	var err error
+	switch c := command.(type) {
+	case cmds.BareCommand:
+		cobraCommand, err = BuildCobraCommandFromBareCommand(c)
+
+	case cmds.WriterCommand:
+		cobraCommand, err = BuildCobraCommandFromWriterCommand(c)
+
+	case cmds.GlazeCommand:
+		cobraCommand, err = BuildCobraCommandFromGlazeCommand(c)
+
+	default:
+		return nil, errors.Errorf("Unknown command type %T", c)
+	}
+	if err != nil {
+		return nil, errors.Wrapf(err, "Error building command %s", command.Description().Name)
+	}
+
+	return cobraCommand, nil
+}
+
 func AddCommandsToRootCommand(rootCmd *cobra.Command, commands []cmds.Command, aliases []*alias.CommandAlias) error {
 	commandsByName := map[string]cmds.Command{}
 
@@ -343,25 +357,10 @@ func AddCommandsToRootCommand(rootCmd *cobra.Command, commands []cmds.Command, a
 		description := command.Description()
 		parentCmd := findOrCreateParentCommand(rootCmd, description.Parents)
 
-		var cobraCommand *cobra.Command
-		var err error
-		switch c := command.(type) {
-		case cmds.BareCommand:
-			cobraCommand, err = BuildCobraCommandFromBareCommand(c)
-
-		case cmds.WriterCommand:
-			cobraCommand, err = BuildCobraCommandFromWriterCommand(c)
-
-		case cmds.GlazeCommand:
-			cobraCommand, err = BuildCobraCommandFromGlazeCommand(c)
-
-		default:
-			return errors.Errorf("Unknown command type %T", c)
-		}
+		cobraCommand, err := BuildCobraCommandFromCommand(command)
 		if err != nil {
-			return errors.Wrapf(err, "Error building command %s", description.Name)
+			return nil
 		}
-
 		parentCmd.AddCommand(cobraCommand)
 		commandsByName[description.Name] = command
 
@@ -378,7 +377,7 @@ func AddCommandsToRootCommand(rootCmd *cobra.Command, commands []cmds.Command, a
 		alias.AliasedCommand = aliasedCommand
 
 		parentCmd := findOrCreateParentCommand(rootCmd, alias.Parents)
-		cobraCommand, err := BuildCobraGlazeCommandAlias(alias)
+		cobraCommand, err := BuildCobraCommandAlias(alias)
 		if err != nil {
 			return err
 		}
@@ -567,7 +566,7 @@ func AddGlazedProcessorFlagsToCobraCommand(cmd *cobra.Command, options ...settin
 }
 
 func BuildCobraCommandFromGlazeCommand(cmd_ cmds.GlazeCommand) (*cobra.Command, error) {
-	cmd, err := BuildCobraCommandFromCommand(cmd_, func(
+	cmd, err := BuildCobraCommandFromCommandAndFunc(cmd_, func(
 		ctx context.Context,
 		parsedLayers map[string]*layers.ParsedParameterLayer,
 		ps map[string]interface{},
diff --git a/pkg/cmds/parameters/parameters.go b/pkg/cmds/parameters/parameters.go
index c3ce36b..d89e6ff 100644
--- a/pkg/cmds/parameters/parameters.go
+++ b/pkg/cmds/parameters/parameters.go
@@ -507,6 +507,9 @@ func InitializeParameterDefaultsFromParameters(
 }
 
 func InitializeStructFromParameters(s interface{}, ps map[string]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 {
 		return errors.Errorf("s is not a pointer")
diff --git a/pkg/helpers/reflect/reflect.go b/pkg/helpers/reflect/reflect.go
index 53e38fc..7c75eab 100644
--- a/pkg/helpers/reflect/reflect.go
+++ b/pkg/helpers/reflect/reflect.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"github.com/go-go-golems/glazed/pkg/helpers/cast"
 	"reflect"
+	"strconv"
 )
 
 func SetReflectValue(value reflect.Value, v interface{}) error {
@@ -29,6 +30,14 @@ func SetReflectValue(value reflect.Value, v interface{}) error {
 		return fmt.Errorf("cannot set reflect.Value of type %s from %T", value.Kind(), v)
 
 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+		if s, ok := v.(string); ok {
+			i, err := strconv.ParseUint(s, 10, 64)
+			if err != nil {
+				return err
+			}
+			value.SetUint(i)
+			return nil
+		}
 		if i, ok := v.(uint64); ok {
 			value.SetUint(i)
 			return nil
@@ -72,6 +81,14 @@ func SetReflectValue(value reflect.Value, v interface{}) error {
 		return fmt.Errorf("cannot set reflect.Value of type %s from %T", value.Kind(), v)
 
 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		if s, ok := v.(string); ok {
+			i, err := strconv.ParseInt(s, 10, 64)
+			if err != nil {
+				return err
+			}
+			value.SetInt(i)
+			return nil
+		}
 		if i, ok := v.(int64); ok {
 			value.SetInt(i)
 			return nil


Test patch:
diff --git a/pkg/helpers/reflect/reflect_test.go b/pkg/helpers/reflect/reflect_test.go
index c0c3aaa..798e7b2 100644
--- a/pkg/helpers/reflect/reflect_test.go
+++ b/pkg/helpers/reflect/reflect_test.go
@@ -82,6 +82,10 @@ func TestSetReflectInt(t *testing.T) {
 
 		// try to set a string
 		err = SetReflectValue(iValue, "11")
+		require.NoError(t, err)
+		assert.Equal(t, int64(11), iValue.Int())
+
+		err = SetReflectValue(iValue, "abc")
 		require.Error(t, err)
 
 		// try to set a float
@@ -150,6 +154,10 @@ func TestSetReflectInt(t *testing.T) {
 
 		// try to set a string
 		err = SetReflectValue(iValue, "11")
+		require.NoError(t, err)
+		assert.Equal(t, uint64(11), iValue.Uint())
+
+		err = SetReflectValue(iValue, "foobar")
 		require.Error(t, err)
 
 		// try to set a float