@@ -948,6 +948,105 @@ func TestUnmarshalWithDecoderOptions(t *testing.T) {
948948 }, & C )
949949}
950950
951+ func TestUnmarshalWithAutomaticEnv (t * testing.T ) {
952+ t .Setenv ("PORT" , "1313" )
953+ t .Setenv ("NAME" , "Steve" )
954+ t .Setenv ("DURATION" , "1s1ms" )
955+ t .Setenv ("MODES" , "1,2,3" )
956+ t .Setenv ("SECRET" , "42" )
957+ t .Setenv ("FILESYSTEM_SIZE" , "4096" )
958+
959+ type AuthConfig struct {
960+ Secret string `mapstructure:"secret"`
961+ }
962+
963+ type StorageConfig struct {
964+ Size int `mapstructure:"size"`
965+ }
966+
967+ type Configuration struct {
968+ Port int `mapstructure:"port"`
969+ Name string `mapstructure:"name"`
970+ Duration time.Duration `mapstructure:"duration"`
971+
972+ // Infer name from struct
973+ Modes []int
974+
975+ // Squash nested struct (omit prefix)
976+ Authentication AuthConfig `mapstructure:",squash"`
977+
978+ // Different key
979+ Storage StorageConfig `mapstructure:"filesystem"`
980+
981+ // Omitted field
982+ Flag bool `mapstructure:"flag"`
983+ }
984+
985+ v := New ()
986+ v .SetEnvKeyReplacer (strings .NewReplacer ("." , "_" ))
987+ v .AutomaticEnv ()
988+
989+ t .Run ("OK" , func (t * testing.T ) {
990+ var config Configuration
991+ if err := v .Unmarshal (& config ); err != nil {
992+ t .Fatalf ("unable to decode into struct, %v" , err )
993+ }
994+
995+ assert .Equal (
996+ t ,
997+ Configuration {
998+ Name : "Steve" ,
999+ Port : 1313 ,
1000+ Duration : time .Second + time .Millisecond ,
1001+ Modes : []int {1 , 2 , 3 },
1002+ Authentication : AuthConfig {
1003+ Secret : "42" ,
1004+ },
1005+ Storage : StorageConfig {
1006+ Size : 4096 ,
1007+ },
1008+ },
1009+ config ,
1010+ )
1011+ })
1012+
1013+ t .Run ("Precedence" , func (t * testing.T ) {
1014+ var config Configuration
1015+
1016+ v .Set ("port" , 1234 )
1017+ if err := v .Unmarshal (& config ); err != nil {
1018+ t .Fatalf ("unable to decode into struct, %v" , err )
1019+ }
1020+
1021+ assert .Equal (
1022+ t ,
1023+ Configuration {
1024+ Name : "Steve" ,
1025+ Port : 1234 ,
1026+ Duration : time .Second + time .Millisecond ,
1027+ Modes : []int {1 , 2 , 3 },
1028+ Authentication : AuthConfig {
1029+ Secret : "42" ,
1030+ },
1031+ Storage : StorageConfig {
1032+ Size : 4096 ,
1033+ },
1034+ },
1035+ config ,
1036+ )
1037+ })
1038+
1039+ t .Run ("Unset" , func (t * testing.T ) {
1040+ var config Configuration
1041+
1042+ err := v .Unmarshal (& config , func (config * mapstructure.DecoderConfig ) {
1043+ config .ErrorUnset = true
1044+ })
1045+
1046+ assert .Error (t , err , "expected viper.Unmarshal to return error due to unset field 'FLAG'" )
1047+ })
1048+ }
1049+
9511050func TestBindPFlags (t * testing.T ) {
9521051 v := New () // create independent Viper object
9531052 flagSet := pflag .NewFlagSet ("test" , pflag .ContinueOnError )
0 commit comments