diff --git a/lib/data/paramsparser/params_reflection.v b/lib/data/paramsparser/params_reflection.v deleted file mode 100644 index 395f0108..00000000 --- a/lib/data/paramsparser/params_reflection.v +++ /dev/null @@ -1,260 +0,0 @@ -module paramsparser - -import time -import incubaid.herolib.data.ourtime -import v.reflection -// import incubaid.herolib.data.encoderhero -// TODO: support more field types - -pub fn (params Params) decode[T](args T) !T { - return params.decode_struct[T](args)! -} - -pub fn (params Params) decode_struct[T](start T) !T { - mut t := T{} - $for field in T.fields { - $if field.is_enum { - t.$(field.name) = params.get_int(field.name) or { int(t.$(field.name)) } - } $else { - // super annoying didn't find other way, then to ignore options - $if field.is_option { - // For optional fields, if the key exists, decode it. Otherwise, leave it as none. - if params.exists(field.name) { - t.$(field.name) = params.decode_value(t.$(field.name), field.name)! - } - } $else { - if field.name[0].is_capital() { - t.$(field.name) = params.decode_struct(t.$(field.name))! - } else { - t.$(field.name) = params.decode_value(t.$(field.name), field.name)! - } - } - } - } - return t -} - -pub fn (params Params) decode_value[T](val T, key string) !T { - // $if T is $option { - // return error("is option") - // } - // value := params.get(field.name)! - - // TODO: handle required fields - if !params.exists(key) { - return val - } - - $if T is string { - return params.get(key)! - } $else $if T is int { - return params.get_int(key)! - } $else $if T is u32 { - return params.get_u32(key)! - } $else $if T is bool { - return params.get_default_true(key) - } $else $if T is []string { - return params.get_list(key)! - } $else $if T is []int { - return params.get_list_int(key)! - } $else $if T is []bool { - return params.get_list_bool(key)! - } $else $if T is []u32 { - return params.get_list_u32(key)! - } $else $if T is time.Time { - time_str := params.get(key)! - // todo: 'handle other null times' - if time_str == '0000-00-00 00:00:00' { - return time.Time{} - } - return time.parse(time_str)! - } $else $if T is ourtime.OurTime { - time_str := params.get(key)! - // todo: 'handle other null times' - if time_str == '0000-00-00 00:00:00' { - return ourtime.new('0000-00-00 00:00:00')! - } - return ourtime.new(time_str)! - } $else $if T is $struct { - child_params := params.get_params(key)! - child := child_params.decode_struct(T{})! - return child - } - return T{} -} - -pub fn (params Params) get_list_bool(key string) ![]bool { - mut res := []bool{} - val := params.get(key)! - if val.len == 0 { - return res - } - for item in val.split(',') { - res << item.trim_space().bool() - } - return res -} - -@[params] -pub struct EncodeArgs { -pub: - recursive bool = true -} - -pub fn encode[T](t T, args EncodeArgs) !Params { - mut params := Params{} - - // struct_attrs := attrs_get_reflection(mytype) - - $for field in T.fields { - // Check if field has skip attribute - comprehensive detection - mut should_skip := false - - // Check each attribute for skip patterns - for attr in field.attrs { - attr_clean := attr.to_lower().replace(' ', '').replace('\t', '') - // Handle various skip attribute formats: - // @[skip], @[skip;...], @[...;skip], @[...;skip;...], etc. - if attr_clean == 'skip' || attr_clean.starts_with('skip;') - || attr_clean.ends_with(';skip') || attr_clean.contains(';skip;') { - should_skip = true - break - } - } - - // Additional check: if field name suggests it should be skipped - // This is a fallback for cases where attribute parsing differs - if field.name == 'other' && !should_skip { - // Check if any attribute contains 'skip' in any form - for attr in field.attrs { - if attr.contains('skip') { - should_skip = true - break - } - } - } - - if !should_skip { - val := t.$(field.name) - field_attrs := attrs_get(field.attrs) - mut key := field.name - if 'alias' in field_attrs { - key = field_attrs['alias'] - } - $if field.is_option { - // Handle optional fields - if val != none { - // Unwrap the optional value before type checking and encoding - // Get the unwrapped value using reflection - // This is a workaround for V's reflection limitations with optionals - // We assume that if val != none, then it can be safely unwrapped - // and its underlying type can be determined. - // This might require a more robust way to get the underlying value - // if V's reflection doesn't provide a direct 'unwrap' for generic `val`. - // For now, we'll rely on the type checks below. - // The `val` here is the actual value of the field, which is `?T`. - // We need to check the type of `field.typ` to know what `T` is. - - // Revert to simpler handling for optional fields - // Rely on V's string interpolation for optional types - // If val is none, this block will be skipped. - // If val is not none, it will be converted to string. - params.set(key, '${val}') - } - } $else $if val is string || val is int || val is bool || val is i64 || val is u32 - || val is time.Time || val is ourtime.OurTime { - params.set(key, '${val}') - } $else $if field.is_enum { - params.set(key, '${int(val)}') - } $else $if field.typ is []string { - mut v2 := '' - for i in val { - if i.contains(' ') { - v2 += "\"${i}\"," - } else { - v2 += '${i},' - } - } - v2 = v2.trim(',') - params.params << Param{ - key: field.name - value: v2 - } - } $else $if field.typ is []int { - mut v2 := '' - for i in val { - v2 += '${i},' - } - v2 = v2.trim(',') - params.params << Param{ - key: field.name - value: v2 - } - } $else $if field.typ is []bool { - mut v2 := '' - for i in val { - v2 += '${i},' - } - v2 = v2.trim(',') - params.params << Param{ - key: field.name - value: v2 - } - } $else $if field.typ is []u32 { - mut v2 := '' - for i in val { - v2 += '${i},' - } - v2 = v2.trim(',') - params.params << Param{ - key: field.name - value: v2 - } - } $else $if field.typ is $struct { - // TODO: Handle embeds better - is_embed := field.name[0].is_capital() - if is_embed { - $if val is string || val is int || val is bool || val is i64 || val is u32 - || val is time.Time { - params.set(key, '${val}') - } - } else { - if args.recursive { - child_params := encode(val)! - params.params << Param{ - key: field.name - value: child_params.export() - } - } - } - } $else { - } - } - } - return params -} - -// BACKLOG: can we do the encode recursive? - -// if at top of struct we have: @[name:"teststruct " ; params] . -// will return {'name': 'teststruct', 'params': ''} -fn attrs_get_reflection(mytype reflection.Type) map[string]string { - if mytype.sym.info is reflection.Struct { - return attrs_get(mytype.sym.info.attrs) - } - return map[string]string{} -} - -// will return {'name': 'teststruct', 'params': ''} -fn attrs_get(attrs []string) map[string]string { - mut out := map[string]string{} - for i in attrs { - if i.contains('=') { - kv := i.split('=') - out[kv[0].trim_space().to_lower()] = kv[1].trim_space().to_lower() - } else { - out[i.trim_space().to_lower()] = '' - } - } - return out -} diff --git a/lib/data/paramsparser/params_reflection_test.v b/lib/data/paramsparser/params_reflection_test.v deleted file mode 100644 index d837fca6..00000000 --- a/lib/data/paramsparser/params_reflection_test.v +++ /dev/null @@ -1,169 +0,0 @@ -module paramsparser - -import time - -struct TestStruct { - name string - nick ?string - birthday time.Time - number int - yesno bool - liststr []string - listint []int - listbool []bool - listu32 []u32 - child TestChild -} - -struct TestChild { - child_name string - child_number int - child_yesno bool - child_liststr []string - child_listint []int - child_listbool []bool - child_listu32 []u32 -} - -const test_child = TestChild{ - child_name: 'test_child' - child_number: 3 - child_yesno: false - child_liststr: ['three', 'four'] - child_listint: [3, 4] - child_listbool: [true, false] - child_listu32: [u32(5), u32(6)] -} - -const test_struct = TestStruct{ - name: 'test' - birthday: time.new( - day: 12 - month: 12 - year: 2012 - ) - number: 2 - yesno: true - liststr: ['one', 'two'] - listint: [1, 2] - listbool: [true, false] - listu32: [u32(7), u32(8)] - child: test_child -} - -const test_child_params = Params{ - params: [ - Param{ - key: 'child_name' - value: 'test_child' - }, - Param{ - key: 'child_number' - value: '3' - }, - Param{ - key: 'child_yesno' - value: 'false' - }, - Param{ - key: 'child_liststr' - value: 'three,four' - }, - Param{ - key: 'child_listint' - value: '3,4' - }, - Param{ - key: 'child_listbool' - value: 'true,false' - }, - Param{ - key: 'child_listu32' - value: '5,6' - }, - ] -} - -const test_params = Params{ - params: [Param{ - key: 'name' - value: 'test' - }, Param{ - key: 'birthday' - value: '2012-12-12 00:00:00' - }, Param{ - key: 'number' - value: '2' - }, Param{ - key: 'yesno' - value: 'true' - }, Param{ - key: 'liststr' - value: 'one,two' - }, Param{ - key: 'listint' - value: '1,2' - }, Param{ - key: 'listbool' - value: 'true,false' - }, Param{ - key: 'listu32' - value: '7,8' - }, Param{ - key: 'child' - value: test_child_params.export() - }] -} - -fn test_encode_struct() { - encoded_struct := encode[TestStruct](test_struct)! - assert encoded_struct == test_params -} - -fn test_decode_struct() { - decoded_struct := test_params.decode[TestStruct](TestStruct{})! - assert decoded_struct.name == test_struct.name - assert decoded_struct.birthday.day == test_struct.birthday.day - assert decoded_struct.birthday.month == test_struct.birthday.month - assert decoded_struct.birthday.year == test_struct.birthday.year - assert decoded_struct.number == test_struct.number - assert decoded_struct.yesno == test_struct.yesno - assert decoded_struct.liststr == test_struct.liststr - assert decoded_struct.listint == test_struct.listint - assert decoded_struct.listbool == test_struct.listbool - assert decoded_struct.listu32 == test_struct.listu32 - assert decoded_struct.child == test_struct.child -} - -fn test_optional_field() { - mut test_struct_with_nick := TestStruct{ - name: test_struct.name - nick: 'test_nick' - birthday: test_struct.birthday - number: test_struct.number - yesno: test_struct.yesno - liststr: test_struct.liststr - listint: test_struct.listint - listbool: test_struct.listbool - listu32: test_struct.listu32 - child: test_struct.child - } - - encoded_struct_with_nick := encode[TestStruct](test_struct_with_nick)! - assert encoded_struct_with_nick.get('nick')! == 'test_nick' - - decoded_struct_with_nick := encoded_struct_with_nick.decode[TestStruct](TestStruct{})! - assert decoded_struct_with_nick.nick or { '' } == 'test_nick' - - // Test decoding when optional field is not present in params - mut params_without_nick := test_params - params_without_nick.params = params_without_nick.params.filter(it.key != 'nick') - decoded_struct_without_nick := params_without_nick.decode[TestStruct](TestStruct{})! - assert decoded_struct_without_nick.nick == none -} - -fn test_encode() { - // test single level struct - encoded_child := encode[TestChild](test_child)! - assert encoded_child == test_child_params -}