1
0

add Scalar type enumeration

This commit is contained in:
Arpad Ryszka 2025-08-31 02:56:26 +02:00
parent 5363818043
commit cfa9b6e6f7
5 changed files with 100 additions and 15 deletions

View File

@ -65,7 +65,8 @@ func fields(t reflect.Type) []Field {
if acceptsScalar(tfi.Type) {
var fi Field
ft := unpackType(tfi.Type, pointer|slice)
fi.isBool = ft.Kind() == reflect.Bool
fi.typ = scalarType(ft.Kind())
fi.size = scalarSize(ft.Kind())
fi.list = acceptsList(tfi.Type)
fi.name = strcase.ToKebab(tfi.Name)
fi.path = []string{tfi.Name}
@ -104,7 +105,8 @@ func fields(t reflect.Type) []Field {
func fieldFromValue(v reflect.Value) Field {
var fi Field
fi.value = v.Interface()
fi.isBool = v.Kind() == reflect.Bool
fi.typ = scalarType(v.Kind())
fi.size = valueSize(v)
return fi
}

View File

@ -60,7 +60,7 @@ func TestField(t *testing.T) {
}
que := m["que"]
if !slices.Equal(que.Path(), []string{"Que"}) || !que.Bool() {
if !slices.Equal(que.Path(), []string{"Que"}) || que.Type() != bind.Bool {
t.Fatal(que.Name())
}
@ -184,8 +184,8 @@ func TestField(t *testing.T) {
v := s{Foo: 42, Bar: true}
f := bind.FieldValues(v)
if len(f) != 2 ||
f[0].Name() != "foo" || !slices.Equal(f[0].Path(), []string{"Foo"}) || f[0].Value() != 42 || f[0].Bool() ||
f[1].Name() != "bar" || !slices.Equal(f[1].Path(), []string{"Bar"}) || f[1].Value() != true || !f[1.].Bool() {
f[0].Name() != "foo" || !slices.Equal(f[0].Path(), []string{"Foo"}) || f[0].Value() != 42 || f[0].Type() == bind.Bool ||
f[1].Name() != "bar" || !slices.Equal(f[1].Path(), []string{"Bar"}) || f[1].Value() != true || f[1.].Type() != bind.Bool {
t.Fatal()
}
})

34
lib.go
View File

@ -2,15 +2,28 @@
// circular type structures not supported
// it handles scalar fields
// primary use cases by design are: command line options, environment variables, ini file fields, URL query parameters, HTTP form values
// traverses pointers, slices, wrapper interfaces
package bind
type Scalar int
const (
Any Scalar = iota
Bool
Int
Uint
Float
String
)
type Field struct {
path []string
name string
list bool
isBool bool
free bool
value any
path []string
name string
list bool
typ Scalar
size int
free bool
value any
}
// the receiver must be addressable
@ -44,10 +57,11 @@ func (f Field) Name() string {
return nameFromPath(f.path)
}
func (f Field) List() bool { return f.list }
func (f Field) Bool() bool { return f.isBool }
func (f Field) Free() bool { return f.free }
func (f Field) Value() any { return f.value }
func (f Field) List() bool { return f.list }
func (f Field) Type() Scalar { return f.typ }
func (f Field) Size() int { return f.size }
func (f Field) Free() bool { return f.free }
func (f Field) Value() any { return f.value }
// it does not return fields with free keys, however, this should be obvious
// non-struct and non-named map values return unnamed fields

View File

@ -1 +1,7 @@
add the kind to the field type but with time and duration support. Simplified for ints, uints and floats, with bit size. Skipping the unused ones. With different name, e.g. ScalarType
track down the cases when reflect can panic
documentation:
- give a short description for every exported symbol
- start from collecting the docs from the test cases
- extrace the common doc items from the function doc items
doc/test/code triangle

63
type.go
View File

@ -20,6 +20,69 @@ func (f unpackFlag) has(v unpackFlag) bool {
return f&v > 0
}
func scalarType(k reflect.Kind) Scalar {
switch k {
case reflect.Bool:
return Bool
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return Int
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return Uint
case reflect.Float32, reflect.Float64:
return Float
case reflect.String:
return String
default:
return Any
}
}
func scalarSize(k reflect.Kind) int {
switch k {
case reflect.Bool:
return 1
case reflect.Int:
return int(reflect.TypeFor[int]().Size()) * 8
case reflect.Int8:
return 8
case reflect.Int16:
return 16
case reflect.Int32:
return 32
case reflect.Int64:
return 64
case reflect.Uint:
return int(reflect.TypeFor[uint]().Size()) * 8
case reflect.Uint8:
return 8
case reflect.Uint16:
return 16
case reflect.Uint32:
return 32
case reflect.Uint64:
return 64
case reflect.Float32:
return 32
case reflect.Float64:
return 64
case reflect.String:
return -1
default:
return -1
}
}
func valueSize(v reflect.Value) int {
switch v.Kind() {
case reflect.String:
return v.Len() * 8
case reflect.Interface:
return valueSize(unpackValue(v, pointer|slice|iface|anytype))
default:
return scalarSize(v.Kind())
}
}
func setVisited[T comparable](visited map[T]bool, k T) map[T]bool {
s := make(map[T]bool)
for v := range visited {