Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,516 @@
|
||||
// Copyright (c) 2021 Lars Pontoppidan. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
module toml
|
||||
|
||||
// Pretty much all the same builtin types as the `json2.Any` type plus `DateTime`,`Date`,`Time`
|
||||
pub type Any = Date
|
||||
| DateTime
|
||||
| Null
|
||||
| Time
|
||||
| []Any
|
||||
| bool
|
||||
| f32
|
||||
| f64
|
||||
| i64
|
||||
| int
|
||||
| map[string]Any
|
||||
| string
|
||||
| u64
|
||||
|
||||
// string returns `Any` as a string.
|
||||
pub fn (a Any) string() string {
|
||||
match a {
|
||||
// NOTE if `.clone()` is not used here:
|
||||
// string { return a as string }
|
||||
// ... certain call-patterns to this function will cause a memory corruption.
|
||||
// See `tests/toml_memory_corruption_test.v` for a matching regression test.
|
||||
string { return (a as string).clone() }
|
||||
DateTime { return a.str().clone() }
|
||||
Date { return a.str().clone() }
|
||||
Time { return a.str().clone() }
|
||||
else { return a.str().clone() }
|
||||
}
|
||||
}
|
||||
|
||||
// to_toml returns `Any` as a TOML encoded value.
|
||||
pub fn (a Any) to_toml() string {
|
||||
match a {
|
||||
map[string]Any {
|
||||
// TODO: more format control?
|
||||
return a.to_inline_toml()
|
||||
}
|
||||
[]Any {
|
||||
return a.to_toml()
|
||||
}
|
||||
bool, f32, f64, i64, int, u64 {
|
||||
return a.str().clone()
|
||||
}
|
||||
// NOTE if `.clone()` is not used here:
|
||||
// string { return a as string }
|
||||
// ... certain call-patterns to this function will cause a memory corruption.
|
||||
// See `tests/toml_memory_corruption_test.v` for a matching regression test.
|
||||
string {
|
||||
return '"' + (a as string).clone() + '"'
|
||||
}
|
||||
DateTime {
|
||||
return a.str().clone()
|
||||
}
|
||||
Date {
|
||||
return a.str().clone()
|
||||
}
|
||||
Time {
|
||||
return a.str().clone()
|
||||
}
|
||||
else {
|
||||
return a.str().clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// int returns `Any` as an 32-bit integer.
|
||||
pub fn (a Any) int() int {
|
||||
match a {
|
||||
int { return a }
|
||||
i64, f32, f64, bool { return int(a) }
|
||||
// time.Time { return int(0) } // TODO
|
||||
else { return 0 }
|
||||
}
|
||||
}
|
||||
|
||||
// i64 returns `Any` as a 64-bit integer.
|
||||
pub fn (a Any) i64() i64 {
|
||||
match a {
|
||||
i64 { return a }
|
||||
int, f32, f64, bool { return i64(a) }
|
||||
// time.Time { return i64(0) } // TODO
|
||||
else { return 0 }
|
||||
}
|
||||
}
|
||||
|
||||
// u64 returns `Any` as a 64-bit unsigned integer.
|
||||
pub fn (a Any) u64() u64 {
|
||||
match a {
|
||||
u64 { return a }
|
||||
int, i64, f32, f64, bool { return u64(a) }
|
||||
// time.Time { return u64(0) } // TODO
|
||||
else { return 0 }
|
||||
}
|
||||
}
|
||||
|
||||
// f32 returns `Any` as a 32-bit float.
|
||||
pub fn (a Any) f32() f32 {
|
||||
match a {
|
||||
f32 { return a }
|
||||
int, i64, f64 { return f32(a) }
|
||||
// time.Time { return f32(0) } // TODO
|
||||
else { return 0.0 }
|
||||
}
|
||||
}
|
||||
|
||||
// f64 returns `Any` as a 64-bit float.
|
||||
pub fn (a Any) f64() f64 {
|
||||
match a {
|
||||
f64 { return a }
|
||||
int, i64, f32 { return f64(a) }
|
||||
// time.Time { return f64(0) } // TODO
|
||||
else { return 0.0 }
|
||||
}
|
||||
}
|
||||
|
||||
// array returns `Any` as an array.
|
||||
pub fn (a Any) array() []Any {
|
||||
if a is []Any {
|
||||
return a
|
||||
} else if a is map[string]Any {
|
||||
mut arr := []Any{}
|
||||
for _, v in a {
|
||||
arr << v
|
||||
}
|
||||
return arr
|
||||
}
|
||||
return [a]
|
||||
}
|
||||
|
||||
// as_map returns `Any` as a map (TOML table).
|
||||
pub fn (a Any) as_map() map[string]Any {
|
||||
if a is map[string]Any {
|
||||
return a
|
||||
} else if a is []Any {
|
||||
mut mp := map[string]Any{}
|
||||
for i, fi in a {
|
||||
mp['${i}'] = fi
|
||||
}
|
||||
return mp
|
||||
}
|
||||
return {
|
||||
'0': a
|
||||
}
|
||||
}
|
||||
|
||||
// bool returns `Any` as a boolean.
|
||||
pub fn (a Any) bool() bool {
|
||||
match a {
|
||||
bool { return a }
|
||||
string { return a.bool() }
|
||||
else { return false }
|
||||
}
|
||||
}
|
||||
|
||||
// date returns `Any` as a `toml.Date` struct.
|
||||
pub fn (a Any) date() Date {
|
||||
match a {
|
||||
// string { } // TODO
|
||||
// NOTE `.clone()` is to avoid memory corruption see `pub fn (a Any) string() string`
|
||||
Date { return Date{a.str().clone()} }
|
||||
else { return Date{''} }
|
||||
}
|
||||
}
|
||||
|
||||
// time returns `Any` as a `toml.Time` struct.
|
||||
pub fn (a Any) time() Time {
|
||||
match a {
|
||||
// string { } // TODO
|
||||
// NOTE `.clone()` is to avoid memory corruption see `pub fn (a Any) string() string`
|
||||
Time { return Time{a.str().clone()} }
|
||||
else { return Time{''} }
|
||||
}
|
||||
}
|
||||
|
||||
// datetime returns `Any` as a `toml.DateTime` struct.
|
||||
pub fn (a Any) datetime() DateTime {
|
||||
match a {
|
||||
// string { } // TODO
|
||||
// NOTE `.clone()` is to avoid memory corruption see `pub fn (a Any) string() string`
|
||||
DateTime { return DateTime{a.str().clone()} }
|
||||
else { return DateTime{''} }
|
||||
}
|
||||
}
|
||||
|
||||
// default_to returns `value` if `a Any` is `Null`.
|
||||
// This can be used to set default values when retrieving
|
||||
// values. E.g.: `toml_doc.value('wrong.key').default_to(123).int()`
|
||||
pub fn (a Any) default_to(value Any) Any {
|
||||
match a {
|
||||
Null { return value }
|
||||
else { return a }
|
||||
}
|
||||
}
|
||||
|
||||
// value queries a value from the map.
|
||||
// `key` supports a small query syntax scheme:
|
||||
// Maps can be queried in "dotted" form e.g. `a.b.c`.
|
||||
// quoted keys are supported as `a."b.c"` or `a.'b.c'`.
|
||||
// Arrays can be queried with `a[0].b[1].[2]`.
|
||||
pub fn (m map[string]Any) value(key string) Any {
|
||||
return Any(m).value(key)
|
||||
}
|
||||
|
||||
// as_strings returns the contents of the map
|
||||
// as `map[string]string`
|
||||
pub fn (m map[string]Any) as_strings() map[string]string {
|
||||
mut result := map[string]string{}
|
||||
for k, v in m {
|
||||
result[k] = v.string()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// to_toml returns the contents of the map
|
||||
// as a TOML encoded `string`.
|
||||
pub fn (m map[string]Any) to_toml() string {
|
||||
mut toml_text := ''
|
||||
for k, v in m {
|
||||
key := if k.contains(' ') { '"${k}"' } else { k }
|
||||
toml_text += '${key} = ${v.to_toml()}\n'
|
||||
}
|
||||
toml_text = toml_text.trim_right('\n')
|
||||
return toml_text
|
||||
}
|
||||
|
||||
// to_inline_toml returns the contents of the map
|
||||
// as an inline table encoded TOML `string`.
|
||||
pub fn (m map[string]Any) to_inline_toml() string {
|
||||
mut toml_text := '{'
|
||||
mut i := 1
|
||||
for k, v in m {
|
||||
key := if k.contains(' ') { '"${k}"' } else { k }
|
||||
delimiter := if i < m.len { ',' } else { '' }
|
||||
toml_text += ' ${key} = ${v.to_toml()}${delimiter}'
|
||||
i++
|
||||
}
|
||||
return toml_text + ' }'
|
||||
}
|
||||
|
||||
// value queries a value from the array.
|
||||
// `key` supports a small query syntax scheme:
|
||||
// The array can be queried with `[0].b[1].[2]`.
|
||||
// Maps can be queried in "dotted" form e.g. `a.b.c`.
|
||||
// quoted keys are supported as `a."b.c"` or `a.'b.c'`.
|
||||
pub fn (a []Any) value(key string) Any {
|
||||
return Any(a).value(key)
|
||||
}
|
||||
|
||||
// as_strings returns the contents of the array
|
||||
// as `[]string`
|
||||
pub fn (a []Any) as_strings() []string {
|
||||
mut sa := []string{}
|
||||
for any in a {
|
||||
sa << any.string()
|
||||
}
|
||||
return sa
|
||||
}
|
||||
|
||||
// to_toml returns the contents of the array
|
||||
// as a TOML encoded `string`.
|
||||
pub fn (a []Any) to_toml() string {
|
||||
mut toml_text := '[\n'
|
||||
for any in a {
|
||||
toml_text += ' ' + any.to_toml() + ',\n'
|
||||
}
|
||||
toml_text = toml_text.trim_right(',\n')
|
||||
return toml_text + '\n]'
|
||||
}
|
||||
|
||||
// value queries a value from the `Any` type.
|
||||
// `key` supports a small query syntax scheme:
|
||||
// Maps can be queried in "dotted" form e.g. `a.b.c`.
|
||||
// quoted keys are supported as `a."b.c"` or `a.'b.c'`.
|
||||
// Arrays can be queried with `a[0].b[1].[2]`.
|
||||
pub fn (a Any) value(key string) Any {
|
||||
key_split := parse_dotted_key(key) or { return null }
|
||||
return a.value_(a, key_split)
|
||||
}
|
||||
|
||||
// value_opt queries a value from the current element's tree. Returns an error
|
||||
// if the key is not valid or there is no value for the key.
|
||||
pub fn (a Any) value_opt(key string) !Any {
|
||||
key_split := parse_dotted_key(key) or { return error('invalid dotted key') }
|
||||
x := a.value_(a, key_split)
|
||||
if x is Null {
|
||||
return error('no value for key')
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// value_ returns the `Any` value found at `key`.
|
||||
fn (a Any) value_(value Any, key []string) Any {
|
||||
if key.len == 0 {
|
||||
return null
|
||||
}
|
||||
mut any_value := null
|
||||
k, index := parse_array_key(key[0])
|
||||
if k == '' {
|
||||
arr := value as []Any
|
||||
any_value = arr[index] or { return null }
|
||||
}
|
||||
if value is map[string]Any {
|
||||
any_value = value[k] or { return null }
|
||||
if index > -1 {
|
||||
arr := any_value as []Any
|
||||
any_value = arr[index] or { return null }
|
||||
}
|
||||
}
|
||||
if key.len <= 1 {
|
||||
return any_value
|
||||
}
|
||||
match any_value {
|
||||
map[string]Any, []Any {
|
||||
return a.value_(any_value, key[1..])
|
||||
}
|
||||
else {
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reflect returns `T` with `T.<field>`'s value set to the
|
||||
// value of any 1st level TOML key by the same name.
|
||||
pub fn (a Any) reflect[T]() T {
|
||||
mut reflected := T{}
|
||||
$for field in T.fields {
|
||||
mut toml_field_name := field.name
|
||||
mut skip := false
|
||||
// Remapping of field names, for example:
|
||||
// TOML: 'assert = "ok"'
|
||||
// V: User { asrt string @[toml: 'assert'] }
|
||||
// User.asrt == 'ok'
|
||||
for attr in field.attrs {
|
||||
if attr == 'skip' {
|
||||
skip = true
|
||||
break
|
||||
}
|
||||
if attr.starts_with('toml:') {
|
||||
toml_field_name = attr.all_after(':').trim_space()
|
||||
}
|
||||
}
|
||||
value := a.value(toml_field_name)
|
||||
// only set the field's value when value != null and !skip, else field got it's default value
|
||||
if !skip && value != null {
|
||||
$if field.typ is string {
|
||||
reflected.$(field.name) = value.string()
|
||||
} $else $if field.typ is bool {
|
||||
reflected.$(field.name) = value.bool()
|
||||
} $else $if field.typ is int {
|
||||
reflected.$(field.name) = value.int()
|
||||
} $else $if field.typ is f32 {
|
||||
reflected.$(field.name) = value.f32()
|
||||
} $else $if field.typ is f64 {
|
||||
reflected.$(field.name) = value.f64()
|
||||
} $else $if field.typ is i64 {
|
||||
reflected.$(field.name) = value.i64()
|
||||
} $else $if field.typ is u64 {
|
||||
reflected.$(field.name) = value.u64()
|
||||
} $else $if field.typ is Any {
|
||||
reflected.$(field.name) = value
|
||||
} $else $if field.typ is DateTime {
|
||||
reflected.$(field.name) = value.datetime()
|
||||
} $else $if field.typ is Date {
|
||||
reflected.$(field.name) = value.date()
|
||||
} $else $if field.typ is Time {
|
||||
reflected.$(field.name) = value.time()
|
||||
}
|
||||
// Arrays of primitive types
|
||||
$else $if field.typ is []string {
|
||||
any_array := value.array()
|
||||
reflected.$(field.name) = any_array.as_strings()
|
||||
} $else $if field.typ is []bool {
|
||||
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 := 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 := 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 := 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 := 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 := 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) = value.array()
|
||||
} $else $if field.typ is []DateTime {
|
||||
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 := 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 := value.array()
|
||||
mut arr := []Time{cap: any_array.len}
|
||||
for any_value in any_array {
|
||||
arr << any_value.time()
|
||||
}
|
||||
reflected.$(field.name) = arr
|
||||
}
|
||||
// String key maps of primitive types
|
||||
$else $if field.typ is map[string]string {
|
||||
any_map := value.as_map()
|
||||
reflected.$(field.name) = any_map.as_strings()
|
||||
} $else $if field.typ is map[string]bool {
|
||||
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 := 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 := 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 := 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 := 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 := 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) = value.as_map()
|
||||
} $else $if field.typ is map[string]DateTime {
|
||||
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 := 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 := value.as_map()
|
||||
mut type_map := map[string]Time{}
|
||||
for k, any_value in any_map {
|
||||
type_map[k] = any_value.time()
|
||||
}
|
||||
reflected.$(field.name) = type_map.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
return reflected
|
||||
}
|
||||
Reference in new issue
Block a user