Skip to content

Commit

Permalink
toml: add toml_default attr support
Browse files Browse the repository at this point in the history
  • Loading branch information
kbkpbot committed Oct 5, 2024
1 parent 209c30f commit 373815b
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 48 deletions.
79 changes: 42 additions & 37 deletions vlib/toml/any.v
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ pub fn (a Any) reflect[T]() T {
mut reflected := T{}
$for field in T.fields {
mut toml_field_name := field.name
mut default_value := ''
// Remapping of field names, for example:
// TOML: 'assert = "ok"'
// V: User { asrt string @[toml: 'assert'] }
Expand All @@ -338,98 +339,102 @@ pub fn (a Any) reflect[T]() T {
if attr.starts_with('toml:') {
toml_field_name = attr.all_after(':').trim_space()
}
if attr.starts_with('toml_default:') {
default_value = attr.all_after(':').trim_space()
}
}

value := a.value(toml_field_name)
$if field.typ is string {
reflected.$(field.name) = a.value(toml_field_name).default_to('').string()
reflected.$(field.name) = if value != null { value.string() } else { default_value }
} $else $if field.typ is bool {
reflected.$(field.name) = a.value(toml_field_name).default_to(false).bool()
reflected.$(field.name) = if value != null { value.bool() } else { default_value.bool() }
} $else $if field.typ is int {
reflected.$(field.name) = a.value(toml_field_name).default_to(0).int()
reflected.$(field.name) = if value != null { value.int() } else { default_value.int() }
} $else $if field.typ is f32 {
reflected.$(field.name) = a.value(toml_field_name).default_to(0.0).f32()
reflected.$(field.name) = if value != null { value.f32() } else { default_value.f32() }
} $else $if field.typ is f64 {
reflected.$(field.name) = a.value(toml_field_name).default_to(0.0).f64()
reflected.$(field.name) = if value != null { value.f64() } else { default_value.f64() }
} $else $if field.typ is i64 {
reflected.$(field.name) = a.value(toml_field_name).default_to(0).i64()
reflected.$(field.name) = if value != null { value.i64() } else { default_value.i64() }
} $else $if field.typ is u64 {
reflected.$(field.name) = a.value(toml_field_name).default_to(0).u64()
reflected.$(field.name) = if value != null { value.u64() } else { default_value.u64() }
} $else $if field.typ is Any {
reflected.$(field.name) = a.value(toml_field_name)
reflected.$(field.name) = value
} $else $if field.typ is DateTime {
dt := DateTime{'0000-00-00T00:00:00.000'}
reflected.$(field.name) = a.value(toml_field_name).default_to(dt).datetime()
reflected.$(field.name) = if value != null {
value.datetime()
} else {
DateTime{default_value}
}
} $else $if field.typ is Date {
da := Date{'0000-00-00'}
reflected.$(field.name) = a.value(toml_field_name).default_to(da).date()
reflected.$(field.name) = if value != null { value.date() } else { Date{default_value} }
} $else $if field.typ is Time {
t := Time{'00:00:00.000'}
reflected.$(field.name) = a.value(toml_field_name).default_to(t).time()
reflected.$(field.name) = if value != null { value.time() } else { Time{default_value} }
}
// Arrays of primitive types
$else $if field.typ is []string {
any_array := a.value(toml_field_name).array()
any_array := value.array()
reflected.$(field.name) = any_array.as_strings()
} $else $if field.typ is []bool {
any_array := a.value(toml_field_name).array()
any_array := value.array()
mut arr := []bool{cap: any_array.len}
for any_value in any_array {
arr << any_value.bool()
}
reflected.$(field.name) = arr
} $else $if field.typ is []int {
any_array := a.value(toml_field_name).array()
any_array := value.array()
mut arr := []int{cap: any_array.len}
for any_value in any_array {
arr << any_value.int()
}
reflected.$(field.name) = arr
} $else $if field.typ is []f32 {
any_array := a.value(toml_field_name).array()
any_array := value.array()
mut arr := []f32{cap: any_array.len}
for any_value in any_array {
arr << any_value.f32()
}
reflected.$(field.name) = arr
} $else $if field.typ is []f64 {
any_array := a.value(toml_field_name).array()
any_array := value.array()
mut arr := []f64{cap: any_array.len}
for any_value in any_array {
arr << any_value.f64()
}
reflected.$(field.name) = arr
} $else $if field.typ is []i64 {
any_array := a.value(toml_field_name).array()
any_array := value.array()
mut arr := []i64{cap: any_array.len}
for any_value in any_array {
arr << any_value.i64()
}
reflected.$(field.name) = arr
} $else $if field.typ is []u64 {
any_array := a.value(toml_field_name).array()
any_array := value.array()
mut arr := []u64{cap: any_array.len}
for any_value in any_array {
arr << any_value.u64()
}
reflected.$(field.name) = arr
} $else $if field.typ is []Any {
reflected.$(field.name) = a.value(toml_field_name).array()
reflected.$(field.name) = value.array()
} $else $if field.typ is []DateTime {
any_array := a.value(toml_field_name).array()
any_array := value.array()
mut arr := []DateTime{cap: any_array.len}
for any_value in any_array {
arr << any_value.datetime()
}
reflected.$(field.name) = arr
} $else $if field.typ is []Date {
any_array := a.value(toml_field_name).array()
any_array := value.array()
mut arr := []Date{cap: any_array.len}
for any_value in any_array {
arr << any_value.date()
}
reflected.$(field.name) = arr
} $else $if field.typ is []Time {
any_array := a.value(toml_field_name).array()
any_array := value.array()
mut arr := []Time{cap: any_array.len}
for any_value in any_array {
arr << any_value.time()
Expand All @@ -438,68 +443,68 @@ pub fn (a Any) reflect[T]() T {
}
// String key maps of primitive types
$else $if field.typ is map[string]string {
any_map := a.value(toml_field_name).as_map()
any_map := value.as_map()
reflected.$(field.name) = any_map.as_strings()
} $else $if field.typ is map[string]bool {
any_map := a.value(toml_field_name).as_map()
any_map := value.as_map()
mut type_map := map[string]bool{}
for k, any_value in any_map {
type_map[k] = any_value.bool()
}
reflected.$(field.name) = type_map.clone()
} $else $if field.typ is map[string]int {
any_map := a.value(toml_field_name).as_map()
any_map := value.as_map()
mut type_map := map[string]int{}
for k, any_value in any_map {
type_map[k] = any_value.int()
}
reflected.$(field.name) = type_map.clone()
} $else $if field.typ is map[string]f32 {
any_map := a.value(toml_field_name).as_map()
any_map := value.as_map()
mut type_map := map[string]f32{}
for k, any_value in any_map {
type_map[k] = any_value.f32()
}
reflected.$(field.name) = type_map.clone()
} $else $if field.typ is map[string]f64 {
any_map := a.value(toml_field_name).as_map()
any_map := value.as_map()
mut type_map := map[string]f64{}
for k, any_value in any_map {
type_map[k] = any_value.f64()
}
reflected.$(field.name) = type_map.clone()
} $else $if field.typ is map[string]i64 {
any_map := a.value(toml_field_name).as_map()
any_map := value.as_map()
mut type_map := map[string]i64{}
for k, any_value in any_map {
type_map[k] = any_value.i64()
}
reflected.$(field.name) = type_map.clone()
} $else $if field.typ is map[string]u64 {
any_map := a.value(toml_field_name).as_map()
any_map := value.as_map()
mut type_map := map[string]u64{}
for k, any_value in any_map {
type_map[k] = any_value.u64()
}
reflected.$(field.name) = type_map.clone()
} $else $if field.typ is map[string]Any {
reflected.$(field.name) = a.value(toml_field_name).as_map()
reflected.$(field.name) = value.as_map()
} $else $if field.typ is map[string]DateTime {
any_map := a.value(toml_field_name).as_map()
any_map := value.as_map()
mut type_map := map[string]DateTime{}
for k, any_value in any_map {
type_map[k] = any_value.datetime()
}
reflected.$(field.name) = type_map.clone()
} $else $if field.typ is map[string]Date {
any_map := a.value(toml_field_name).as_map()
any_map := value.as_map()
mut type_map := map[string]Date{}
for k, any_value in any_map {
type_map[k] = any_value.date()
}
reflected.$(field.name) = type_map.clone()
} $else $if field.typ is map[string]Time {
any_map := a.value(toml_field_name).as_map()
any_map := value.as_map()
mut type_map := map[string]Time{}
for k, any_value in any_map {
type_map[k] = any_value.time()
Expand Down
85 changes: 85 additions & 0 deletions vlib/toml/tests/default_value_test.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import toml

const toml_text = '# This TOML can reflect/decode to a struct
val_string = "test"
val_bool = false
val_int = 456
val_i64 = 4567
val_u64 = 45678
val_f32 = 200.2
val_f64 = 2000.2
val_datetime = 2024-10-05 09:10:20.000
val_date = 2099-09-09
val_time = 22:22:22.222
'

const toml_all_default_text = '# This TOML can reflect/decode to a struct all with default values'

struct Test {
val_string string @[toml_default: 'abcd']
val_bool bool @[toml_default: true]
val_int int @[toml_default: 123]
val_i64 i64 @[toml_default: 1234]
val_u64 u64 @[toml_default: 12345]
val_f32 f32 @[toml_default: 100.1]
val_f64 f64 @[toml_default: 1000.1]
val_datetime toml.DateTime @[toml_default: '1980-07-11 21:23:42.123']
val_date toml.Date @[toml_default: '1977-07-07']
val_time toml.Time @[toml_default: '11:11:11.111']
}

fn test_reflect_default_values() {
toml_has_values := toml.parse_text(toml_text) or { panic(err) }
test_has_values := toml_has_values.reflect[Test]()

assert test_has_values.val_string == 'test'
assert test_has_values.val_bool == false
assert test_has_values.val_int == 456
assert test_has_values.val_i64 == 4567
assert test_has_values.val_u64 == 45678
assert test_has_values.val_f32 == 200.2
assert test_has_values.val_f64 == 2000.2
assert test_has_values.val_datetime == toml.DateTime{'2024-10-05 09:10:20.000'}
assert test_has_values.val_date == toml.Date{'2099-09-09'}
assert test_has_values.val_time == toml.Time{'22:22:22.222'}

toml_all_default_values := toml.parse_text(toml_all_default_text) or { panic(err) }
test_all_default_values := toml_all_default_values.reflect[Test]()

assert test_all_default_values.val_string == 'abcd'
assert test_all_default_values.val_bool == true
assert test_all_default_values.val_int == 123
assert test_all_default_values.val_i64 == 1234
assert test_all_default_values.val_u64 == 12345
assert test_all_default_values.val_f32 == 100.1
assert test_all_default_values.val_f64 == 1000.1
assert test_all_default_values.val_datetime == toml.DateTime{'1980-07-11 21:23:42.123'}
assert test_all_default_values.val_date == toml.Date{'1977-07-07'}
assert test_all_default_values.val_time == toml.Time{'11:11:11.111'}
}

fn test_decode_struct_default_values() {
test_has_values := toml.decode[Test](toml_text) or { panic(err) }
assert test_has_values.val_string == 'test'
assert test_has_values.val_bool == false
assert test_has_values.val_int == 456
assert test_has_values.val_i64 == 4567
assert test_has_values.val_u64 == 45678
assert test_has_values.val_f32 == 200.2
assert test_has_values.val_f64 == 2000.2
assert test_has_values.val_datetime == toml.DateTime{'2024-10-05 09:10:20.000'}
assert test_has_values.val_date == toml.Date{'2099-09-09'}
assert test_has_values.val_time == toml.Time{'22:22:22.222'}

test_all_default_values := toml.decode[Test](toml_all_default_text) or { panic(err) }
assert test_all_default_values.val_string == 'abcd'
assert test_all_default_values.val_bool == true
assert test_all_default_values.val_int == 123
assert test_all_default_values.val_i64 == 1234
assert test_all_default_values.val_u64 == 12345
assert test_all_default_values.val_f32 == 100.1
assert test_all_default_values.val_f64 == 1000.1
assert test_all_default_values.val_datetime == toml.DateTime{'1980-07-11 21:23:42.123'}
assert test_all_default_values.val_date == toml.Date{'1977-07-07'}
assert test_all_default_values.val_time == toml.Time{'11:11:11.111'}
}
30 changes: 19 additions & 11 deletions vlib/toml/toml.v
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ fn decode_struct[T](doc Any, mut typ T) {
$for field in T.fields {
mut field_name := field.name
mut skip := false
mut default_value := ''
for attr in field.attrs {
if attr == 'skip' {
skip = true
Expand All @@ -43,31 +44,38 @@ fn decode_struct[T](doc Any, mut typ T) {
if attr.starts_with('toml:') {
field_name = attr.all_after(':').trim_space()
}
if attr.starts_with('toml_default:') {
default_value = attr.all_after(':').trim_space()
}
}
if !skip {
value := doc.value(field_name)
$if field.is_enum {
typ.$(field.name) = value.int()
typ.$(field.name) = if value != null { value.int() } else { default_value.int() }
} $else $if field.typ is string {
typ.$(field.name) = value.string()
typ.$(field.name) = if value != null { value.string() } else { default_value }
} $else $if field.typ is bool {
typ.$(field.name) = value.bool()
typ.$(field.name) = if value != null { value.bool() } else { default_value.bool() }
} $else $if field.typ is int {
typ.$(field.name) = value.int()
typ.$(field.name) = if value != null { value.int() } else { default_value.int() }
} $else $if field.typ is i64 {
typ.$(field.name) = value.i64()
typ.$(field.name) = if value != null { value.i64() } else { default_value.i64() }
} $else $if field.typ is u64 {
typ.$(field.name) = value.u64()
typ.$(field.name) = if value != null { value.u64() } else { default_value.u64() }
} $else $if field.typ is f32 {
typ.$(field.name) = value.f32()
typ.$(field.name) = if value != null { value.f32() } else { default_value.f32() }
} $else $if field.typ is f64 {
typ.$(field.name) = value.f64()
typ.$(field.name) = if value != null { value.f64() } else { default_value.f64() }
} $else $if field.typ is DateTime {
typ.$(field.name) = value.datetime()
typ.$(field.name) = if value != null {
value.datetime()
} else {
DateTime{default_value}
}
} $else $if field.typ is Date {
typ.$(field.name) = value.date()
typ.$(field.name) = if value != null { value.date() } else { Date{default_value} }
} $else $if field.typ is Time {
typ.$(field.name) = value.time()
typ.$(field.name) = if value != null { value.time() } else { Time{default_value} }
} $else $if field.typ is Any {
typ.$(field.name) = value
} $else $if field.is_array {
Expand Down

0 comments on commit 373815b

Please sign in to comment.