2021-09-24 20:13:52 +02:00
|
|
|
// 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
|
|
|
|
|
|
|
|
import time
|
2021-11-13 10:17:35 +01:00
|
|
|
import toml.util
|
2021-11-16 07:41:37 +01:00
|
|
|
import x.json2
|
2021-09-24 20:13:52 +02:00
|
|
|
|
2021-09-26 06:34:47 +02:00
|
|
|
// Pretty much all the same builtin types as the `json2.Any` type plus `time.Time`
|
2021-09-24 20:13:52 +02:00
|
|
|
pub type Any = Null
|
|
|
|
| []Any
|
|
|
|
| bool
|
|
|
|
| f32
|
|
|
|
| f64
|
|
|
|
| i64
|
|
|
|
| int
|
|
|
|
| map[string]Any
|
|
|
|
| string
|
|
|
|
| time.Time
|
|
|
|
| u64
|
|
|
|
|
|
|
|
// string returns `Any` as a string.
|
|
|
|
pub fn (a Any) string() string {
|
|
|
|
match a {
|
2021-10-28 18:57:30 +02:00
|
|
|
// 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() }
|
2021-09-24 20:13:52 +02:00
|
|
|
time.Time { return a.format_ss_micro() }
|
|
|
|
else { return a.str() }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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 date encoded in a `time.Time` struct.
|
|
|
|
pub fn (a Any) date() time.Time {
|
|
|
|
mut time := time.Time{}
|
|
|
|
match a {
|
|
|
|
// string { } // TODO
|
|
|
|
time.Time { return a }
|
|
|
|
else { return time }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// date returns `Any` as a time encoded in a `time.Time` struct.
|
|
|
|
pub fn (a Any) time() time.Time {
|
|
|
|
mut time := time.Time{}
|
|
|
|
match a {
|
|
|
|
// string { } // TODO
|
|
|
|
time.Time { return a }
|
|
|
|
else { return time }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// date returns `Any` as a date+time encoded in a `time.Time` struct.
|
|
|
|
pub fn (a Any) datetime() time.Time {
|
|
|
|
mut time := time.Time{}
|
|
|
|
match a {
|
|
|
|
// string { } // TODO
|
|
|
|
time.Time { return a }
|
|
|
|
else { return time }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-13 10:17:35 +01:00
|
|
|
// value queries a value from the map.
|
|
|
|
// `key` should be in "dotted" form (`a.b.c`).
|
|
|
|
// `key` supports quoted keys like `a."b.c"`.
|
2021-09-24 20:13:52 +02:00
|
|
|
pub fn (m map[string]Any) value(key string) ?Any {
|
2021-11-13 10:17:35 +01:00
|
|
|
key_split := util.parse_dotted_key(key) ?
|
|
|
|
return m.value_(key_split)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (m map[string]Any) value_(key []string) ?Any {
|
|
|
|
value := m[key[0]] or {
|
|
|
|
return error(@MOD + '.' + @STRUCT + '.' + @FN + ' key "${key[0]}" does not exist')
|
|
|
|
}
|
|
|
|
// `match` isn't currently very suitable for these types of sum type constructs...
|
|
|
|
if value is map[string]Any {
|
|
|
|
if key.len <= 1 {
|
|
|
|
return value
|
2021-09-24 20:13:52 +02:00
|
|
|
}
|
2021-11-13 10:17:35 +01:00
|
|
|
nm := (value as map[string]Any)
|
|
|
|
return nm.value_(key[1..])
|
2021-09-24 20:13:52 +02:00
|
|
|
}
|
2021-11-13 10:17:35 +01:00
|
|
|
return value
|
2021-09-24 20:13:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn (a []Any) as_strings() []string {
|
|
|
|
mut sa := []string{}
|
|
|
|
for any in a {
|
|
|
|
sa << any.string()
|
|
|
|
}
|
|
|
|
return sa
|
|
|
|
}
|
|
|
|
|
|
|
|
// to_json returns `Any` as a JSON encoded string.
|
|
|
|
pub fn (a Any) to_json() string {
|
|
|
|
match a {
|
|
|
|
Null {
|
|
|
|
return 'null'
|
|
|
|
}
|
2021-11-16 07:41:37 +01:00
|
|
|
time.Time {
|
|
|
|
json_text := json2.Any(a.format_ss_micro())
|
|
|
|
return '"$json_text.json_str()"'
|
|
|
|
}
|
2021-09-24 20:13:52 +02:00
|
|
|
string {
|
2021-11-16 07:41:37 +01:00
|
|
|
json_text := json2.Any(a.str())
|
|
|
|
return '"$json_text.json_str()"'
|
2021-09-24 20:13:52 +02:00
|
|
|
}
|
|
|
|
bool, f32, f64, i64, int, u64 {
|
2021-11-16 07:41:37 +01:00
|
|
|
json_text := json2.Any(a.str())
|
|
|
|
return json_text.json_str()
|
2021-09-24 20:13:52 +02:00
|
|
|
}
|
|
|
|
map[string]Any {
|
|
|
|
mut str := '{'
|
|
|
|
for key, val in a {
|
2021-11-16 07:41:37 +01:00
|
|
|
json_key := json2.Any(key)
|
|
|
|
str += ' "$json_key.json_str()": $val.to_json(),'
|
2021-09-24 20:13:52 +02:00
|
|
|
}
|
|
|
|
str = str.trim_right(',')
|
|
|
|
str += ' }'
|
|
|
|
return str
|
|
|
|
}
|
|
|
|
[]Any {
|
|
|
|
mut str := '['
|
|
|
|
for val in a {
|
|
|
|
str += ' $val.to_json(),'
|
|
|
|
}
|
|
|
|
str = str.trim_right(',')
|
|
|
|
str += ' ]'
|
|
|
|
return str
|
|
|
|
}
|
2021-11-16 07:41:37 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// to_json_any returns `Any` as a `x.json2.Any` type.
|
|
|
|
pub fn (a Any) to_json_any() json2.Any {
|
|
|
|
match a {
|
|
|
|
Null {
|
|
|
|
return json2.Null{}
|
|
|
|
}
|
2021-09-24 20:13:52 +02:00
|
|
|
time.Time {
|
2021-11-16 07:41:37 +01:00
|
|
|
return json2.Any(a.format_ss_micro())
|
|
|
|
}
|
|
|
|
string {
|
|
|
|
return json2.Any(a.str())
|
|
|
|
}
|
|
|
|
bool {
|
|
|
|
return json2.Any(bool(a))
|
|
|
|
}
|
|
|
|
int {
|
|
|
|
return json2.Any(int(a))
|
|
|
|
}
|
|
|
|
f32 {
|
|
|
|
return json2.Any(f32(a))
|
|
|
|
}
|
|
|
|
f64 {
|
|
|
|
return json2.Any(f64(a))
|
|
|
|
}
|
|
|
|
i64 {
|
|
|
|
return json2.Any(i64(a))
|
|
|
|
}
|
|
|
|
u64 {
|
|
|
|
return json2.Any(u64(a))
|
|
|
|
}
|
|
|
|
map[string]Any {
|
|
|
|
mut jmap := map[string]json2.Any{}
|
|
|
|
for key, val in a {
|
|
|
|
jmap[key] = val.to_json_any()
|
|
|
|
}
|
|
|
|
return jmap
|
|
|
|
}
|
|
|
|
[]Any {
|
|
|
|
mut jarr := []json2.Any{}
|
|
|
|
|
|
|
|
for val in a {
|
|
|
|
jarr << val.to_json_any()
|
|
|
|
}
|
|
|
|
|
|
|
|
return jarr
|
2021-09-24 20:13:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|