Skip to content

Commit 5c45ad6

Browse files
committed
initial version
Signed-off-by: Dmitrii Aleksandrov <[email protected]>
1 parent a8cd652 commit 5c45ad6

File tree

7 files changed

+567
-0
lines changed

7 files changed

+567
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,6 @@
1919

2020
# Go workspace file
2121
go.work
22+
23+
# IDEs
24+
.idea

Readme.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# FMap
2+
FMap is a simple library for working with structs as map of fields. Switch case and reflect based.
3+
4+
# Description
5+
FMap creates new map with filed names as key and with fmap.Field(reflect.StructField) values. This is unsafe library, be careful while use.
6+
7+
fmap.Field has 3 advanced methods:
8+
9+
```
10+
Get[T any]() any
11+
Set(obj any, val any)
12+
GetPtr(obj any) any
13+
```
14+
15+
where the `obj` should be not nil pointer to struct.<br>
16+
`Get[T any]() any` - return expected typed value as interface{}.<br>
17+
`Set(obj any, val any)` - `val` expected typed value to set, {}interface can be used.<br>
18+
`GetPtr(obj any) any` - return expected typed value pointer to struct field as interface{}.
19+
# Example
20+
21+
```go
22+
package main
23+
24+
import (
25+
"time"
26+
"fmt"
27+
28+
"github.com/insei/fmap"
29+
)
30+
31+
type City struct {
32+
Name string
33+
}
34+
35+
type People struct {
36+
Name string
37+
Age uint8
38+
Birthday time.Time
39+
City City
40+
}
41+
42+
func main() {
43+
p := &People{}
44+
fields := fmap.Get[People]() // or fmap.GetFrom(p)
45+
fields["Name"].Set(p, "Test")
46+
fields["Age"].Set(p, uint8(5))
47+
fields["Birthday"].Set(p, time.Now())
48+
fields["City.Name"].Set(p, "DefaultCity")
49+
fmt.Print(*p)
50+
}
51+
```
52+
53+
More examples in `fields_test.go`, like slice fields, nested structs, pointers etc.

field.go

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
package fmap
2+
3+
import (
4+
"reflect"
5+
"unsafe"
6+
)
7+
8+
type Field reflect.StructField
9+
10+
func (f Field) GetPtr(obj interface{}) interface{} {
11+
return reflect.NewAt(f.Type, f.getPtr(obj)).Interface()
12+
}
13+
14+
// Get returns the value of the fields in the provided object.
15+
// It takes a parameter `obj` of type `interface{}`, representing the object.
16+
// It returns the value of the fields as an `interface{}`.
17+
func (f Field) Get(obj interface{}) interface{} {
18+
ptrToField := f.getPtr(obj)
19+
kind := f.Type.Kind()
20+
isPtr := false
21+
if kind == reflect.Ptr {
22+
isPtr = true
23+
kind = f.Type.Elem().Kind()
24+
}
25+
if isPtr {
26+
switch kind {
27+
case reflect.String:
28+
return getPtrValue[*string](ptrToField)
29+
case reflect.Int:
30+
return getPtrValue[*int](ptrToField)
31+
case reflect.Int8:
32+
return getPtrValue[*int8](ptrToField)
33+
case reflect.Int16:
34+
return getPtrValue[*int16](ptrToField)
35+
case reflect.Int32:
36+
return getPtrValue[*int32](ptrToField)
37+
case reflect.Int64:
38+
return getPtrValue[*int64](ptrToField)
39+
case reflect.Float32:
40+
return getPtrValue[*float32](ptrToField)
41+
case reflect.Float64:
42+
return getPtrValue[*float64](ptrToField)
43+
case reflect.Bool:
44+
return getPtrValue[*bool](ptrToField)
45+
case reflect.Struct:
46+
return reflect.NewAt(f.Type, ptrToField).Elem().Interface()
47+
case reflect.Slice:
48+
return reflect.NewAt(f.Type, ptrToField).Elem().Interface()
49+
case reflect.Array:
50+
return reflect.NewAt(f.Type, ptrToField).Elem().Interface()
51+
default:
52+
panic("unhandled default case")
53+
}
54+
} else {
55+
switch kind {
56+
case reflect.String:
57+
return getPtrValue[string](ptrToField)
58+
case reflect.Int:
59+
return getPtrValue[int](ptrToField)
60+
case reflect.Int8:
61+
return getPtrValue[int8](ptrToField)
62+
case reflect.Int16:
63+
return getPtrValue[int16](ptrToField)
64+
case reflect.Int32:
65+
return getPtrValue[int32](ptrToField)
66+
case reflect.Int64:
67+
return getPtrValue[int64](ptrToField)
68+
case reflect.Float32:
69+
return getPtrValue[float32](ptrToField)
70+
case reflect.Float64:
71+
return getPtrValue[float64](ptrToField)
72+
case reflect.Bool:
73+
return getPtrValue[bool](ptrToField)
74+
case reflect.Struct:
75+
return reflect.NewAt(f.Type, ptrToField).Elem().Interface()
76+
case reflect.Slice:
77+
return reflect.NewAt(f.Type, ptrToField).Elem().Interface()
78+
case reflect.Array:
79+
return reflect.NewAt(f.Type, ptrToField).Elem().Interface()
80+
default:
81+
panic("unhandled default case")
82+
}
83+
}
84+
}
85+
86+
// getPtr returns a pointer to the field's value in the provided configuration object.
87+
// It takes a parameter `conf` of type `any`, representing the configuration object.
88+
// It returns an `unsafe.Pointer` to the `field's` value in the configuration object.
89+
func (f Field) getPtr(obj interface{}) unsafe.Pointer {
90+
confPointer := ((*[2]unsafe.Pointer)(unsafe.Pointer(&obj)))[1]
91+
ptToField := unsafe.Add(confPointer, f.Offset)
92+
return ptToField
93+
}
94+
95+
func setPtrValue[T any](ptr unsafe.Pointer, val any) {
96+
valSet := (*T)(ptr)
97+
*valSet = val.(T)
98+
}
99+
100+
func getPtrValue[T any](ptr unsafe.Pointer) T {
101+
return *(*T)(ptr)
102+
}
103+
104+
// Set updates the value of the fields in the provided object with the provided value.
105+
// It takes two parameters:
106+
// - obj: interface{}, representing the object containing the field.
107+
// - val: interface{}, representing the new value for the field.
108+
//
109+
// The Set method uses the getPtr method to get a pointer to the fields in the object.
110+
// It then performs a type switch on the kind of the fields to determine its type, and sets the value accordingly.
111+
// The supported fields types are string, int, and bool.
112+
// If the fields type is not one of the supported types, it panics with the message "unhandled default case".
113+
func (f Field) Set(obj interface{}, val interface{}) {
114+
ptrToField := f.getPtr(obj)
115+
kind := f.Type.Kind()
116+
isPtr := false
117+
if kind == reflect.Ptr {
118+
isPtr = true
119+
kind = f.Type.Elem().Kind()
120+
}
121+
if isPtr {
122+
switch kind {
123+
case reflect.String:
124+
setPtrValue[*string](ptrToField, val)
125+
case reflect.Int:
126+
setPtrValue[*int](ptrToField, val)
127+
case reflect.Int8:
128+
setPtrValue[*int8](ptrToField, val)
129+
case reflect.Int16:
130+
setPtrValue[*int16](ptrToField, val)
131+
case reflect.Int32:
132+
setPtrValue[*int32](ptrToField, val)
133+
case reflect.Int64:
134+
setPtrValue[*int64](ptrToField, val)
135+
case reflect.Float32:
136+
setPtrValue[*float32](ptrToField, val)
137+
case reflect.Float64:
138+
setPtrValue[*float64](ptrToField, val)
139+
case reflect.Bool:
140+
setPtrValue[*bool](ptrToField, val)
141+
default:
142+
dest := reflect.NewAt(f.Type, ptrToField)
143+
dest = dest.Elem()
144+
source := reflect.ValueOf(val)
145+
dest.Set(source)
146+
}
147+
} else {
148+
switch kind {
149+
case reflect.String:
150+
setPtrValue[string](ptrToField, val)
151+
case reflect.Int:
152+
setPtrValue[int](ptrToField, val)
153+
case reflect.Int8:
154+
setPtrValue[int8](ptrToField, val)
155+
case reflect.Int16:
156+
setPtrValue[int16](ptrToField, val)
157+
case reflect.Int32:
158+
setPtrValue[int32](ptrToField, val)
159+
case reflect.Int64:
160+
setPtrValue[int64](ptrToField, val)
161+
case reflect.Float32:
162+
setPtrValue[float32](ptrToField, val)
163+
case reflect.Float64:
164+
setPtrValue[float64](ptrToField, val)
165+
case reflect.Bool:
166+
setPtrValue[bool](ptrToField, val)
167+
default:
168+
dest := reflect.NewAt(f.Type, ptrToField)
169+
dest = dest.Elem()
170+
source := reflect.ValueOf(val)
171+
dest.Set(source)
172+
}
173+
}
174+
}

0 commit comments

Comments
 (0)