Добавлены функции вывода в поток описания параметров.
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				test / test (push) Successful in 58s
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	test / test (push) Successful in 58s
				
			This commit is contained in:
		
							parent
							
								
									ce3402b295
								
							
						
					
					
						commit
						16ce42d26c
					
				| @ -184,6 +184,29 @@ linters-settings: | |||||||
|       - name: unexported-return |       - name: unexported-return | ||||||
|         disabled: true |         disabled: true | ||||||
| 
 | 
 | ||||||
|  |   varnamelen: | ||||||
|  |     # The longest distance, in source lines, that is being considered a "small scope". | ||||||
|  |     # Variables used in at most this many lines will be ignored. | ||||||
|  |     # Default: 5 | ||||||
|  |     max-distance: 6 | ||||||
|  |     # The minimum length of a variable's name that is considered "long". | ||||||
|  |     # Variable names that are at least this long will be ignored. | ||||||
|  |     # Default: 3 | ||||||
|  |     min-name-length: 2 | ||||||
|  |     # Ignore "ok" variables that hold the bool return value of a type assertion. | ||||||
|  |     # Default: false | ||||||
|  |     ignore-type-assert-ok: true | ||||||
|  |     # Ignore "ok" variables that hold the bool return value of a map index. | ||||||
|  |     # Default: false | ||||||
|  |     ignore-map-index-ok: true | ||||||
|  |     # Ignore "ok" variables that hold the bool return value of a channel receive. | ||||||
|  |     # Default: false | ||||||
|  |     ignore-chan-recv-ok: true | ||||||
|  |     # Optional list of variable names that should be ignored completely. | ||||||
|  |     # Default: [] | ||||||
|  |     ignore-names: | ||||||
|  |       - err | ||||||
|  | 
 | ||||||
| # output configuration options | # output configuration options | ||||||
| output: | output: | ||||||
|   # The formats used to render issues. |   # The formats used to render issues. | ||||||
|  | |||||||
							
								
								
									
										13
									
								
								cli.go
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								cli.go
									
									
									
									
									
								
							| @ -18,6 +18,7 @@ package config | |||||||
| import ( | import ( | ||||||
| 	"flag" | 	"flag" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"io" | ||||||
| 	"reflect" | 	"reflect" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"unsafe" | 	"unsafe" | ||||||
| @ -313,3 +314,15 @@ func (c *Command) Parse(args []string) error { | |||||||
| 
 | 
 | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // PrintUsage prints command description | ||||||
|  | func (c *Command) PrintUsage(w io.Writer) error { | ||||||
|  | 	if _, err := w.Write([]byte(c.Usage + "\n")); err != nil { | ||||||
|  | 		return fmt.Errorf("write usage: %w", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	c.FlagSet.SetOutput(w) | ||||||
|  | 	c.FlagSet.Usage() | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | |||||||
							
								
								
									
										16
									
								
								cli_test.go
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								cli_test.go
									
									
									
									
									
								
							| @ -17,6 +17,7 @@ package config | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"flag" | 	"flag" | ||||||
|  | 	"os" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
| @ -29,9 +30,7 @@ func TestServiceCommand(t *testing.T) { | |||||||
| 	serviceConfig := test.ServiceConfig{} | 	serviceConfig := test.ServiceConfig{} | ||||||
| 	cmd := NewCLI("Service") | 	cmd := NewCLI("Service") | ||||||
| 	err := cmd.Init(&serviceConfig) | 	err := cmd.Init(&serviceConfig) | ||||||
| 	if err != nil { | 	assert.NoError(err, "init service command") | ||||||
| 		t.Errorf("Can't init service command. %s", err.Error()) |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	// assert service cmd | 	// assert service cmd | ||||||
| 	assert.NotNil(cmd.FlagSet) | 	assert.NotNil(cmd.FlagSet) | ||||||
| @ -62,6 +61,15 @@ func TestServiceCommand(t *testing.T) { | |||||||
| 	assert.NotNil(cmd.FlagSet.Lookup("log-level")) | 	assert.NotNil(cmd.FlagSet.Lookup("log-level")) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func TestPrintUsage(t *testing.T) { | ||||||
|  | 	assert := assert.New(t) | ||||||
|  | 	serviceConfig := test.ServiceConfig{} | ||||||
|  | 	cmd := NewCLI("Service") | ||||||
|  | 	err := cmd.Init(&serviceConfig) | ||||||
|  | 	assert.NoError(err, "init service command") | ||||||
|  | 	assert.NoError(cmd.PrintUsage(os.Stdout)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func TestLoginSubCommand(t *testing.T) { | func TestLoginSubCommand(t *testing.T) { | ||||||
| 	assert := assert.New(t) | 	assert := assert.New(t) | ||||||
| 	serviceConfig := test.ServiceConfig{Login: &test.LoginConfig{}} | 	serviceConfig := test.ServiceConfig{Login: &test.LoginConfig{}} | ||||||
| @ -256,5 +264,3 @@ func TestCommandWithSlices(t *testing.T) { | |||||||
| 	assert.Equal(200, conf.Values[1]) | 	assert.Equal(200, conf.Values[1]) | ||||||
| 	assert.Equal(300, conf.Values[2]) | 	assert.Equal(300, conf.Values[2]) | ||||||
| } | } | ||||||
| 
 |  | ||||||
| func TestUsage(t *testing.T) {} |  | ||||||
|  | |||||||
							
								
								
									
										117
									
								
								env.go
									
									
									
									
									
								
							
							
						
						
									
										117
									
								
								env.go
									
									
									
									
									
								
							| @ -16,21 +16,60 @@ | |||||||
| package config | package config | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"bytes" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
| 	"os" | 	"os" | ||||||
| 	"reflect" | 	"reflect" | ||||||
|  | 	"strings" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | // ParseValueEnvFunc func to parse env value | ||||||
|  | type ParseValueEnvFunc func(value reflect.Value, prefix string) error | ||||||
|  | 
 | ||||||
| // Parse parses given structure interface, extracts environment definitions | // Parse parses given structure interface, extracts environment definitions | ||||||
| // from its tag and sets structure with defined environment variables | // from its tag and sets structure with defined environment variables | ||||||
| func ParseEnv(i interface{}) error { | func ParseEnv(out interface{}) error { | ||||||
| 	return ParseEnvWith(i, "") | 	return ParseEnvWith(out, "", parseValueEnv) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // UsageEnv prints usage description of config i to out | // UsageEnv prints usage description of config i to out | ||||||
| func UsageEnv(out io.Writer, i interface{}) { | func UsageEnv(out io.Writer, in interface{}) error { | ||||||
| 	// STub | 	if _, err := out.Write([]byte("Environment Usage:\n")); err != nil { | ||||||
|  | 		return fmt.Errorf("write usage: %w", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	buf := bytes.NewBufferString("") | ||||||
|  | 
 | ||||||
|  | 	if err := ParseEnvWith(in, "", func(value reflect.Value, prefix string) error { | ||||||
|  | 		return usageValueEnv(buf, value, prefix) | ||||||
|  | 	}); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var tabPos int | ||||||
|  | 
 | ||||||
|  | 	for _, line := range strings.Split(strings.TrimRight(buf.String(), "\n"), "\n") { | ||||||
|  | 		if pos := strings.Index(line, "\t"); pos > tabPos { | ||||||
|  | 			tabPos = pos | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	const TabSize = 8 | ||||||
|  | 
 | ||||||
|  | 	for _, line := range strings.Split(strings.TrimRight(buf.String(), "\n"), "\n") { | ||||||
|  | 		if pos := strings.Index(line, "\t"); pos >= 0 { | ||||||
|  | 			if count := tabPos/TabSize - pos/TabSize; count > 0 { | ||||||
|  | 				line = line[:pos] + strings.Repeat("\t", count) + line[pos:] | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if _, err := out.Write([]byte(line + "\n")); err != nil { | ||||||
|  | 			return fmt.Errorf("write usage: %w", err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ParseWith parses with given structure interface and environment name prefix | // ParseWith parses with given structure interface and environment name prefix | ||||||
| @ -47,8 +86,8 @@ func UsageEnv(out io.Writer, i interface{}) { | |||||||
| // | // | ||||||
| // The Server.DB.Host will be mapped to environment variable: DB_HOST which is | // The Server.DB.Host will be mapped to environment variable: DB_HOST which is | ||||||
| // concatenated from DB tag in Server struct and Host tag in Database struct | // concatenated from DB tag in Server struct and Host tag in Database struct | ||||||
| func ParseEnvWith(i interface{}, prefix string) error { | func ParseEnvWith(out interface{}, prefix string, parser ParseValueEnvFunc) error { | ||||||
| 	ptrRef := reflect.ValueOf(i) | 	ptrRef := reflect.ValueOf(out) | ||||||
| 
 | 
 | ||||||
| 	if ptrRef.IsNil() || ptrRef.Kind() != reflect.Ptr { | 	if ptrRef.IsNil() || ptrRef.Kind() != reflect.Ptr { | ||||||
| 		return fmt.Errorf("%w: %s", | 		return fmt.Errorf("%w: %s", | ||||||
| @ -61,7 +100,44 @@ func ParseEnvWith(i interface{}, prefix string) error { | |||||||
| 			errExpectStructPointerInsteadOf, valueOfStruct.Kind().String()) | 			errExpectStructPointerInsteadOf, valueOfStruct.Kind().String()) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return parseValueEnv(valueOfStruct, prefix) | 	return parser(valueOfStruct, prefix) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func usageValueEnv(out io.Writer, value reflect.Value, prefix string) error { | ||||||
|  | 	var err error | ||||||
|  | 
 | ||||||
|  | 	typeOfStruct := value.Type() | ||||||
|  | 
 | ||||||
|  | 	for i := 0; i < value.NumField() && err == nil; i++ { | ||||||
|  | 		valueOfField := value.Field(i) | ||||||
|  | 		kindOfField := valueOfField.Kind() | ||||||
|  | 		structOfField := typeOfStruct.Field(i) | ||||||
|  | 
 | ||||||
|  | 		// Recursively unmarshal if value is ptr type | ||||||
|  | 		if kindOfField == reflect.Ptr { | ||||||
|  | 			if !valueOfField.IsNil() && valueOfField.CanSet() { | ||||||
|  | 				err = ParseEnvWith( | ||||||
|  | 					valueOfField.Interface(), | ||||||
|  | 					prefix+structOfField.Tag.Get("env"), | ||||||
|  | 					func(value reflect.Value, prefix string) error { | ||||||
|  | 						return usageValueEnv(out, value, prefix) | ||||||
|  | 					}, | ||||||
|  | 				) | ||||||
|  | 			} else { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 		} else if kindOfField == reflect.Struct { | ||||||
|  | 			err = usageValueEnv(out, valueOfField, prefix+structOfField.Tag.Get("env")) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		err = usageFieldValueEnv(out, structOfField, prefix) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // parseValue parses a reflect.Value object | // parseValue parses a reflect.Value object | ||||||
| @ -78,8 +154,11 @@ func parseValueEnv(value reflect.Value, prefix string) error { | |||||||
| 		// Recursively unmarshal if value is ptr type | 		// Recursively unmarshal if value is ptr type | ||||||
| 		if kindOfField == reflect.Ptr { | 		if kindOfField == reflect.Ptr { | ||||||
| 			if !valueOfField.IsNil() && valueOfField.CanSet() { | 			if !valueOfField.IsNil() && valueOfField.CanSet() { | ||||||
| 				err = ParseEnvWith(valueOfField.Interface(), | 				err = ParseEnvWith( | ||||||
| 					prefix+structOfField.Tag.Get("env")) | 					valueOfField.Interface(), | ||||||
|  | 					prefix+structOfField.Tag.Get("env"), | ||||||
|  | 					parseValueEnv, | ||||||
|  | 				) | ||||||
| 			} else { | 			} else { | ||||||
| 				continue | 				continue | ||||||
| 			} | 			} | ||||||
| @ -160,3 +239,23 @@ func setFieldValueEnv(value reflect.Value, field reflect.StructField, prefix str | |||||||
| 
 | 
 | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func usageFieldValueEnv(out io.Writer, field reflect.StructField, prefix string) error { | ||||||
|  | 	envName := field.Tag.Get("env") | ||||||
|  | 	if envName == "" { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	envName = prefix + envName | ||||||
|  | 	msg := "   " + envName + "\t" + field.Tag.Get("usage") | ||||||
|  | 
 | ||||||
|  | 	if def := field.Tag.Get("default"); def != "" { | ||||||
|  | 		msg += " (default: " + def + ")" | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if _, err := out.Write([]byte(msg + "\n")); err != nil { | ||||||
|  | 		return fmt.Errorf("write usage: %w", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | |||||||
| @ -75,7 +75,7 @@ func TestLoginConfigEnvWithPrefix(t *testing.T) { | |||||||
| 	defer func() { _ = os.Unsetenv("DB_PASSWORD") }() | 	defer func() { _ = os.Unsetenv("DB_PASSWORD") }() | ||||||
| 
 | 
 | ||||||
| 	loginConfig := test.LoginConfig{} | 	loginConfig := test.LoginConfig{} | ||||||
| 	assert.NoError(ParseEnvWith(&loginConfig, "DB_")) | 	assert.NoError(ParseEnvWith(&loginConfig, "DB_", parseValueEnv)) | ||||||
| 	assert.Equal(LOGIN_USER, loginConfig.User) | 	assert.Equal(LOGIN_USER, loginConfig.User) | ||||||
| 	assert.Equal(LOGIN_PASSWORD, loginConfig.Password) | 	assert.Equal(LOGIN_PASSWORD, loginConfig.Password) | ||||||
| } | } | ||||||
| @ -142,6 +142,11 @@ func TestServiceConfigEnv(t *testing.T) { | |||||||
| 	assert.Equal(DB_LOG_LEVEL, serviceConfig.DBConfig.Log.Level) | 	assert.Equal(DB_LOG_LEVEL, serviceConfig.DBConfig.Log.Level) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func TestEnvUsage(t *testing.T) { | ||||||
|  | 	conf := test.ServiceConfig{} | ||||||
|  | 	assert.NoError(t, UsageEnv(os.Stdout, &conf)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func TestServiceLoginConfigEnv(t *testing.T) { | func TestServiceLoginConfigEnv(t *testing.T) { | ||||||
| 	var err error | 	var err error | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user