this repo has no description
1package addons
2
3import "core:fmt"
4import "core:reflect"
5import "core:strings"
6import "core:testing"
7
8Field_Error :: Maybe(string)
9
10@(private)
11Field_Type :: struct {
12 name: string,
13 type: ^reflect.Type_Info,
14 tag: string,
15 offset: int,
16}
17
18@(private)
19write_struct_field :: proc(obj: ^$T, field: Field_Type, value: $E) -> Field_Error {
20 if !(size_of(field.type.id) == size_of(E) || field.type.id == typeid_of(E)) {
21 return fmt.tprintf(
22 "given field {}.{} ({}) is not the same size as given value {}. {} != {}",
23 typeid_of(T),
24 field.name,
25 field.type.id,
26 typeid_of(E),
27 size_of(field.type.id),
28 size_of(E),
29 )
30 }
31
32 obj_bytes := reflect.as_bytes(obj^)
33 value_bytes := reflect.as_bytes(value)
34 for i in 0 ..< len(value_bytes) {
35 obj_bytes[int(field.offset) + i] = value_bytes[i]
36 }
37
38 return nil
39}
40
41@(test)
42write_struct_field_test :: proc(t: ^testing.T) {
43 S :: struct($T: typeid) {
44 padding1: [13]u8 `sqlite:"Padding1"`,
45 value: T `sqlite:"Value"`,
46 padding2: [5]u8 `sqlite:"Padding2"`,
47 }
48
49 test_using_type :: proc(t: ^testing.T, value: $T) {
50 S1 :: S(T)
51 fields, err := get_type_fields(S1)
52 testing.expect(t, err == nil)
53 defer delete_field_types(fields)
54
55 value_field := fields[1]
56 obj: S1
57
58 err = write_struct_field(&obj, value_field, value)
59 testing.expect(t, err == nil)
60 testing.expect_value(t, obj.value, value)
61
62 // neighbours are unchanged
63 for b in obj.padding1 {
64 testing.expect(t, b == 0)
65 }
66
67 // neighbours are unchanged
68 for b in obj.padding2 {
69 testing.expect(t, b == 0)
70 }
71 }
72
73 test_using_type(t, u8(100))
74 test_using_type(t, i8(100))
75 test_using_type(t, true)
76 test_using_type(t, i16(100))
77 test_using_type(t, u16(100))
78 test_using_type(t, i32(100))
79 test_using_type(t, u32(100))
80 test_using_type(t, i64(100))
81 test_using_type(t, u64(100))
82 test_using_type(t, "hello")
83}
84
85@(private)
86field_type_deinit :: proc(field: ^Field_Type) {
87 delete(field.name)
88 delete(field.tag)
89}
90
91@(private)
92delete_field_types :: proc(field_types: []Field_Type) {
93 for &it in field_types {
94 field_type_deinit(&it)
95 }
96
97 delete(field_types)
98}
99
100@(private)
101@(require_results)
102get_type_fields :: proc($T: typeid) -> ([]Field_Type, Field_Error) {
103 out: [dynamic]Field_Type
104 struct_info := type_info_of(T)
105 for i in 0 ..< reflect.struct_field_count(T) {
106 field := reflect.struct_field_at(T, i)
107 capture, err := match_and_return_capture(`sqlite:"(.*?)"`, cast(string)field.tag)
108 if err != nil {
109 defer delete_field_types(out[:])
110
111 return nil, fmt.tprintf(
112 "could not find sqlite tag on field '{}' of struct '{}'. err: {}",
113 field.name,
114 struct_info,
115 err,
116 )
117 }
118
119 append(
120 &out,
121 Field_Type {
122 tag = capture,
123 name = strings.clone(field.name),
124 type = field.type,
125 offset = int(field.offset),
126 },
127 )
128
129 }
130
131 return out[:], nil
132}
133
134
135@(test)
136get_type_fields_test__all_ok :: proc(t: ^testing.T) {
137 S :: struct {
138 a: int `sqlite:"Foo"`,
139 b: bool `sqlite:"Bar"`,
140 c: string `sqlite:"Foobar"`,
141 }
142
143 fields, err := get_type_fields(S)
144 testing.expect(t, err == nil)
145 defer delete_field_types(fields)
146
147 testing.expect(t, fields[0].tag == "Foo")
148 testing.expect(t, fields[0].name == "a")
149 testing.expect(t, fields[0].type.id == typeid_of(int))
150
151 testing.expect(t, fields[1].tag == "Bar")
152 testing.expect(t, fields[1].name == "b")
153 testing.expect(t, fields[1].type.id == typeid_of(bool))
154
155 testing.expect(t, fields[2].tag == "Foobar")
156 testing.expect(t, fields[2].name == "c")
157 testing.expect(t, fields[2].type.id == typeid_of(string))
158}
159
160
161@(test)
162get_type_fields_test__missing_tag :: proc(t: ^testing.T) {
163 S :: struct {
164 a: int `sqlite:"Foo"`,
165 b: bool,
166 c: string `sqlite:"Foobar"`,
167 }
168
169 fields, err := get_type_fields(S)
170 testing.expect(t, err != nil)
171}
172
173
174@(test)
175get_type_fields_test__malformed_tag :: proc(t: ^testing.T) {
176 S :: struct {
177 a: int `sqlite:"Foo"`,
178 b: bool `sqlit:"Bar"`,
179 c: string `sqlite:"Foobar"`,
180 }
181
182 fields, err := get_type_fields(S)
183 testing.expect(t, err != nil)
184}
185
186@(test)
187get_type_fields_test__malformed_tag_missing_quote :: proc(t: ^testing.T) {
188 S :: struct {
189 a: int `sqlite:"Foo"`,
190 b: bool `sqlite:"Bar`,
191 c: string `sqlite:"Foobar"`,
192 }
193
194 fields, err := get_type_fields(S)
195 testing.expect(t, err != nil)
196}