mirror of
https://github.com/go-ini/ini.git
synced 2026-06-19 21:46:45 +02:00
key: apply ValueMapper to substituted reference values (#382)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
5a14e1849d
commit
89efed6562
@@ -37,7 +37,7 @@ jobs:
|
||||
name: Test
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.24.x, 1.25.x]
|
||||
go-version: [1.26.x]
|
||||
platform: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
|
||||
@@ -6,11 +6,6 @@ linters:
|
||||
- unconvert
|
||||
- unparam
|
||||
settings:
|
||||
govet:
|
||||
disable:
|
||||
# printf: non-constant format string in call to fmt.Errorf (govet)
|
||||
# showing up since golangci-lint version 1.60.1
|
||||
- printf
|
||||
nakedret:
|
||||
max-func-lines: 0 # Disallow any unnamed return statement
|
||||
exclusions:
|
||||
|
||||
+2
-3
@@ -18,7 +18,6 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
@@ -48,7 +47,7 @@ type sourceData struct {
|
||||
}
|
||||
|
||||
func (s *sourceData) ReadCloser() (io.ReadCloser, error) {
|
||||
return ioutil.NopCloser(bytes.NewReader(s.data)), nil
|
||||
return io.NopCloser(bytes.NewReader(s.data)), nil
|
||||
}
|
||||
|
||||
// sourceReadCloser represents an input stream with Close method.
|
||||
@@ -69,7 +68,7 @@ func parseDataSource(source interface{}) (dataSource, error) {
|
||||
case io.ReadCloser:
|
||||
return &sourceReadCloser{s}, nil
|
||||
case io.Reader:
|
||||
return &sourceReadCloser{ioutil.NopCloser(s)}, nil
|
||||
return &sourceReadCloser{io.NopCloser(s)}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("error parsing data source: unknown type %q", s)
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -532,7 +531,7 @@ func (f *File) SaveToIndent(filename, indent string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(filename, buf.Bytes(), 0666)
|
||||
return os.WriteFile(filename, buf.Bytes(), 0666)
|
||||
}
|
||||
|
||||
// SaveTo writes content to file system.
|
||||
|
||||
+3
-3
@@ -16,7 +16,7 @@ package ini
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
"sort"
|
||||
"testing"
|
||||
@@ -421,10 +421,10 @@ func TestFile_WriteTo(t *testing.T) {
|
||||
|
||||
golden := "testdata/TestFile_WriteTo.golden"
|
||||
if *update {
|
||||
require.NoError(t, ioutil.WriteFile(golden, buf.Bytes(), 0644))
|
||||
require.NoError(t, os.WriteFile(golden, buf.Bytes(), 0644))
|
||||
}
|
||||
|
||||
expected, err := ioutil.ReadFile(golden)
|
||||
expected, err := os.ReadFile(golden)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, string(expected), buf.String())
|
||||
})
|
||||
|
||||
+2
-2
@@ -17,7 +17,7 @@ package ini
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
@@ -58,7 +58,7 @@ func TestLoad(t *testing.T) {
|
||||
"testdata/minimal.ini",
|
||||
[]byte("NAME = ini\nIMPORT_PATH = gopkg.in/%(NAME)s.%(VERSION)s"),
|
||||
bytes.NewReader([]byte(`VERSION = v1`)),
|
||||
ioutil.NopCloser(bytes.NewReader([]byte("[author]\nNAME = Unknwon"))),
|
||||
io.NopCloser(bytes.NewReader([]byte("[author]\nNAME = Unknwon"))),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, f)
|
||||
|
||||
@@ -170,7 +170,7 @@ func (k *Key) transformValue(val string) string {
|
||||
}
|
||||
|
||||
// Substitute by new value and take off leading '%(' and trailing ')s'.
|
||||
val = strings.ReplaceAll(val, vr, nk.value)
|
||||
val = strings.ReplaceAll(val, vr, nk.String())
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
+19
@@ -183,6 +183,7 @@ func TestKey_Helpers(t *testing.T) {
|
||||
}
|
||||
return in
|
||||
}
|
||||
t.Cleanup(func() { f.ValueMapper = nil })
|
||||
assert.Equal(t, "github.com/go-ini/ini", sec.Key("IMPORT_PATH").String())
|
||||
})
|
||||
})
|
||||
@@ -635,4 +636,22 @@ bar = %(missing)s
|
||||
|
||||
assert.Equal(t, "%(missing)s", f.Section("foo").Key("bar").String())
|
||||
})
|
||||
|
||||
t.Run("ValueMapper applies to substituted reference value", func(t *testing.T) {
|
||||
f, err := Load([]byte(`
|
||||
ACCOUNT_ID = ${LFSD_R2_ACCOUNT_ID}
|
||||
ENDPOINT = https://%(ACCOUNT_ID)s.r2.cloudflarestorage.com
|
||||
`))
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, f)
|
||||
|
||||
f.ValueMapper = func(in string) string {
|
||||
if in == "${LFSD_R2_ACCOUNT_ID}" {
|
||||
return "abc123"
|
||||
}
|
||||
return in
|
||||
}
|
||||
|
||||
assert.Equal(t, "https://abc123.r2.cloudflarestorage.com", f.Section("").Key("ENDPOINT").String())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ func wrapStrictError(err error, isStrict bool) error {
|
||||
// because we want to use default value that is already assigned to struct.
|
||||
func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string, allowShadow, isStrict bool) error {
|
||||
vt := t
|
||||
isPtr := t.Kind() == reflect.Ptr
|
||||
isPtr := t.Kind() == reflect.Pointer
|
||||
if isPtr {
|
||||
vt = t.Elem()
|
||||
}
|
||||
@@ -278,7 +278,7 @@ func parseTagOptions(tag string) (rawName string, omitEmpty bool, allowShadow bo
|
||||
// mapToField maps the given value to the matching field of the given section.
|
||||
// The sectionIndex is the index (if non unique sections are enabled) to which the value should be added.
|
||||
func (s *Section) mapToField(val reflect.Value, isStrict bool, sectionIndex int, sectionName string) error {
|
||||
if val.Kind() == reflect.Ptr {
|
||||
if val.Kind() == reflect.Pointer {
|
||||
val = val.Elem()
|
||||
}
|
||||
typ := val.Type()
|
||||
@@ -299,8 +299,8 @@ func (s *Section) mapToField(val reflect.Value, isStrict bool, sectionIndex int,
|
||||
}
|
||||
|
||||
isStruct := tpField.Type.Kind() == reflect.Struct
|
||||
isStructPtr := tpField.Type.Kind() == reflect.Ptr && tpField.Type.Elem().Kind() == reflect.Struct
|
||||
isAnonymousPtr := tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous
|
||||
isStructPtr := tpField.Type.Kind() == reflect.Pointer && tpField.Type.Elem().Kind() == reflect.Struct
|
||||
isAnonymousPtr := tpField.Type.Kind() == reflect.Pointer && tpField.Anonymous
|
||||
if isAnonymousPtr {
|
||||
field.Set(reflect.New(tpField.Type.Elem()))
|
||||
}
|
||||
@@ -381,7 +381,7 @@ func (s *Section) mapToSlice(secName string, val reflect.Value, isStrict bool) (
|
||||
func (s *Section) mapTo(v interface{}, isStrict bool) error {
|
||||
typ := reflect.TypeOf(v)
|
||||
val := reflect.ValueOf(v)
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
if typ.Kind() == reflect.Pointer {
|
||||
typ = typ.Elem()
|
||||
val = val.Elem()
|
||||
} else {
|
||||
@@ -500,13 +500,13 @@ func reflectSliceWithProperType(key *Key, field reflect.Value, delim string, all
|
||||
case reflect.String:
|
||||
buf.WriteString(slice.Index(i).String())
|
||||
case reflect.Int, reflect.Int64:
|
||||
buf.WriteString(fmt.Sprint(slice.Index(i).Int()))
|
||||
fmt.Fprint(&buf, slice.Index(i).Int())
|
||||
case reflect.Uint, reflect.Uint64:
|
||||
buf.WriteString(fmt.Sprint(slice.Index(i).Uint()))
|
||||
fmt.Fprint(&buf, slice.Index(i).Uint())
|
||||
case reflect.Float64:
|
||||
buf.WriteString(fmt.Sprint(slice.Index(i).Float()))
|
||||
fmt.Fprint(&buf, slice.Index(i).Float())
|
||||
case reflect.Bool:
|
||||
buf.WriteString(fmt.Sprint(slice.Index(i).Bool()))
|
||||
fmt.Fprint(&buf, slice.Index(i).Bool())
|
||||
case reflectTime:
|
||||
buf.WriteString(slice.Index(i).Interface().(time.Time).Format(time.RFC3339))
|
||||
default:
|
||||
@@ -535,7 +535,7 @@ func reflectWithProperType(t reflect.Type, key *Key, field reflect.Value, delim
|
||||
key.SetValue(fmt.Sprint(field.Interface().(time.Time).Format(time.RFC3339)))
|
||||
case reflect.Slice:
|
||||
return reflectSliceWithProperType(key, field, delim, allowShadow)
|
||||
case reflect.Ptr:
|
||||
case reflect.Pointer:
|
||||
if !field.IsNil() {
|
||||
return reflectWithProperType(t.Elem(), key, field.Elem(), delim, allowShadow)
|
||||
}
|
||||
@@ -559,7 +559,7 @@ func isEmptyValue(v reflect.Value) bool {
|
||||
return v.Uint() == 0
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return v.Float() == 0
|
||||
case reflect.Interface, reflect.Ptr:
|
||||
case reflect.Interface, reflect.Pointer:
|
||||
return v.IsNil()
|
||||
case reflectTime:
|
||||
t, ok := v.Interface().(time.Time)
|
||||
@@ -574,7 +574,7 @@ type StructReflector interface {
|
||||
}
|
||||
|
||||
func (s *Section) reflectFrom(val reflect.Value) error {
|
||||
if val.Kind() == reflect.Ptr {
|
||||
if val.Kind() == reflect.Pointer {
|
||||
val = val.Elem()
|
||||
}
|
||||
typ := val.Type()
|
||||
@@ -606,14 +606,14 @@ func (s *Section) reflectFrom(val reflect.Value) error {
|
||||
continue
|
||||
}
|
||||
|
||||
if extends && tpField.Anonymous && (tpField.Type.Kind() == reflect.Ptr || tpField.Type.Kind() == reflect.Struct) {
|
||||
if extends && tpField.Anonymous && (tpField.Type.Kind() == reflect.Pointer || tpField.Type.Kind() == reflect.Struct) {
|
||||
if err := s.reflectFrom(field); err != nil {
|
||||
return fmt.Errorf("reflect from field %q: %v", fieldName, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if (tpField.Type.Kind() == reflect.Ptr && tpField.Type.Elem().Kind() == reflect.Struct) ||
|
||||
if (tpField.Type.Kind() == reflect.Pointer && tpField.Type.Elem().Kind() == reflect.Struct) ||
|
||||
(tpField.Type.Kind() == reflect.Struct && tpField.Type.Name() != "Time") {
|
||||
// Note: The only error here is section doesn't exist.
|
||||
sec, err := s.f.GetSection(fieldName)
|
||||
@@ -641,7 +641,7 @@ func (s *Section) reflectFrom(val reflect.Value) error {
|
||||
sliceOf := field.Type().Elem().Kind()
|
||||
|
||||
for i := 0; i < field.Len(); i++ {
|
||||
if sliceOf != reflect.Struct && sliceOf != reflect.Ptr {
|
||||
if sliceOf != reflect.Struct && sliceOf != reflect.Pointer {
|
||||
return fmt.Errorf("field %q is not a slice of pointer or struct", fieldName)
|
||||
}
|
||||
|
||||
@@ -688,11 +688,11 @@ func (s *Section) ReflectFrom(v interface{}) error {
|
||||
val := reflect.ValueOf(v)
|
||||
|
||||
if s.name != DefaultSection && s.f.options.AllowNonUniqueSections &&
|
||||
(typ.Kind() == reflect.Slice || typ.Kind() == reflect.Ptr) {
|
||||
(typ.Kind() == reflect.Slice || typ.Kind() == reflect.Pointer) {
|
||||
// Clear sections to make sure none exists before adding the new ones
|
||||
s.f.DeleteSection(s.name)
|
||||
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
if typ.Kind() == reflect.Pointer {
|
||||
sec, err := s.f.NewSection(s.name)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -702,7 +702,7 @@ func (s *Section) ReflectFrom(v interface{}) error {
|
||||
|
||||
slice := val.Slice(0, val.Len())
|
||||
sliceOf := val.Type().Elem().Kind()
|
||||
if sliceOf != reflect.Ptr {
|
||||
if sliceOf != reflect.Pointer {
|
||||
return fmt.Errorf("not a slice of pointers")
|
||||
}
|
||||
|
||||
@@ -721,7 +721,7 @@ func (s *Section) ReflectFrom(v interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
if typ.Kind() == reflect.Pointer {
|
||||
val = val.Elem()
|
||||
} else {
|
||||
return errors.New("not a pointer to a struct")
|
||||
|
||||
Reference in New Issue
Block a user