Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,18 @@ var reflectTime = reflect.TypeOf(time.Now()).Kind()

// setSliceWithProperType sets proper values to slice based on its type.
func setSliceWithProperType(key *Key, field reflect.Value, delim string, allowShadow, isStrict bool) error {
sliceOf := field.Type().Elem().Kind()
// []rune (and []int32) round-trip as a Unicode string, not a delimited list.
if sliceOf == reflect.Int32 {
runes := []rune(key.String())
slice := reflect.MakeSlice(field.Type(), len(runes), len(runes))
for i, r := range runes {
slice.Index(i).SetInt(int64(r))
}
field.Set(slice)
return nil
}

var strs []string
if allowShadow {
strs = key.StringsWithShadows(delim)
Expand All @@ -94,7 +106,6 @@ func setSliceWithProperType(key *Key, field reflect.Value, delim string, allowSh
var vals interface{}
var err error

sliceOf := field.Type().Elem().Kind()
switch sliceOf {
case reflect.String:
vals = strs
Expand Down Expand Up @@ -463,6 +474,16 @@ func reflectSliceWithProperType(key *Key, field reflect.Value, delim string, all
}
sliceOf := field.Type().Elem().Kind()

// []rune / []int32: write the code points as a string so ReflectFrom/MapTo round-trip.
if sliceOf == reflect.Int32 {
runes := make([]rune, field.Len())
for i := 0; i < field.Len(); i++ {
runes[i] = rune(slice.Index(i).Int())
}
key.SetValue(string(runes))
return nil
}

if allowShadow {
var keyWithShadows *Key
for i := 0; i < field.Len(); i++ {
Expand Down
30 changes: 30 additions & 0 deletions struct_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -910,3 +910,33 @@ Title = Consultant Engineer
)
})
}

func Test_ReflectFromAndMapToRuneSlice(t *testing.T) {
type Example struct {
Prop1 []rune `ini:"prop.1"`
}

t.Run("reflect []rune as a string", func(t *testing.T) {
cfg := Empty()
require.NoError(t, ReflectFrom(cfg, &Example{Prop1: []rune("abc")}))
assert.Equal(t, "abc", cfg.Section("").Key("prop.1").String())

var buf bytes.Buffer
_, err := cfg.WriteTo(&buf)
require.NoError(t, err)
assert.Contains(t, buf.String(), "prop.1 = abc")
})

t.Run("map []rune from a string and round-trip", func(t *testing.T) {
cfg, err := Load([]byte("prop.1 = café"))
require.NoError(t, err)

got := new(Example)
require.NoError(t, cfg.MapTo(got))
assert.Equal(t, []rune("café"), got.Prop1)

out := Empty()
require.NoError(t, ReflectFrom(out, got))
assert.Equal(t, "café", out.Section("").Key("prop.1").String())
})
}