Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,410 @@
|
||||
module yaml
|
||||
|
||||
import json
|
||||
import os
|
||||
import strings
|
||||
import x.json2
|
||||
|
||||
// Null is a simple representation of the YAML `null` value.
|
||||
pub struct Null {}
|
||||
|
||||
// null is an instance of `Null`, to ease comparisons with it.
|
||||
pub const null = Any(Null{})
|
||||
|
||||
// Any is the tree representation used by the YAML module.
|
||||
pub type Any = []Any | Null | bool | f64 | i64 | int | map[string]Any | string | u64
|
||||
|
||||
// Doc is a parsed YAML document.
|
||||
pub struct Doc {
|
||||
pub:
|
||||
root Any
|
||||
}
|
||||
|
||||
// parse_file parses the YAML file at `path`.
|
||||
pub fn parse_file(path string) !Doc {
|
||||
return parse_text(os.read_file(path)!)
|
||||
}
|
||||
|
||||
// parse_text parses the YAML document provided in `text`.
|
||||
pub fn parse_text(text string) !Doc {
|
||||
mut normalized := text
|
||||
if normalized.contains_u8(`\r`) {
|
||||
normalized = normalized.replace('\r\n', '\n').replace('\r', '\n')
|
||||
}
|
||||
if normalized.len >= 3 && normalized[0] == 0xef && normalized[1] == 0xbb
|
||||
&& normalized[2] == 0xbf {
|
||||
normalized = normalized[3..]
|
||||
}
|
||||
// `split('\n')` would otherwise turn the canonical trailing line break into
|
||||
// a phantom empty last line, which the block-scalar reader then treats as a
|
||||
// genuine blank line and over-counts during chomping.
|
||||
if normalized.ends_with('\n') {
|
||||
normalized = normalized[..normalized.len - 1]
|
||||
}
|
||||
trimmed := normalized.trim_space()
|
||||
if trimmed == '' {
|
||||
return Doc{
|
||||
root: null
|
||||
}
|
||||
}
|
||||
if trimmed.starts_with('{') || trimmed.starts_with('[') {
|
||||
// JSON-superset fast path. `parse_flow_value` already consumes the
|
||||
// flow-style grammar that YAML borrows from JSON, so it builds the
|
||||
// `yaml.Any` tree directly — no second-pass `from_json2` rebuild.
|
||||
// Falls through to the block parser if the body is anything other
|
||||
// than a clean flow document.
|
||||
if val := parse_flow_value(trimmed) {
|
||||
return Doc{
|
||||
root: val
|
||||
}
|
||||
}
|
||||
}
|
||||
mut parser := Parser{
|
||||
lines: normalized.split('\n')
|
||||
}
|
||||
return Doc{
|
||||
root: parser.parse()!
|
||||
}
|
||||
}
|
||||
|
||||
// decode decodes YAML text into the target type `T`.
|
||||
// The generic encode/decode path uses the main `json` module for field parity.
|
||||
pub fn decode[T](yaml_text string) !T {
|
||||
doc := parse_text(yaml_text)!
|
||||
return doc.decode[T]()
|
||||
}
|
||||
|
||||
// decode_file decodes the YAML file at `path` into the target type `T`.
|
||||
pub fn decode_file[T](path string) !T {
|
||||
return decode[T](os.read_file(path)!)
|
||||
}
|
||||
|
||||
// encode encodes the value `value` into a YAML string.
|
||||
// The generic encode/decode path uses the main `json` module for field parity.
|
||||
pub fn encode[T](value T) string {
|
||||
json_text := json.encode(value)
|
||||
raw := json2.decode[json2.Any](json_text) or { return '' }
|
||||
return from_json2(raw).to_yaml()
|
||||
}
|
||||
|
||||
// encode_file encodes `value` as YAML and writes it to `path`.
|
||||
pub fn encode_file[T](path string, value T) ! {
|
||||
os.write_file(path, encode(value))!
|
||||
}
|
||||
|
||||
// decode decodes the YAML document into the target type `T`. An empty
|
||||
// document (parsed as the YAML 1.2 null node) decodes to a default-initialized
|
||||
// `T`, matching the common "empty config file = use defaults" idiom.
|
||||
pub fn (d Doc) decode[T]() !T {
|
||||
if d.root is Null {
|
||||
return json.decode(T, '{}')!
|
||||
}
|
||||
return json.decode(T, d.to_json())!
|
||||
}
|
||||
|
||||
// to_any converts the YAML document to `yaml.Any`.
|
||||
pub fn (d Doc) to_any() Any {
|
||||
return d.root
|
||||
}
|
||||
|
||||
// to_json converts the YAML document to JSON.
|
||||
pub fn (d Doc) to_json() string {
|
||||
return d.root.to_json()
|
||||
}
|
||||
|
||||
// to_yaml converts the YAML document back to YAML text.
|
||||
pub fn (d Doc) to_yaml() string {
|
||||
return d.root.to_yaml()
|
||||
}
|
||||
|
||||
// value queries a value from the YAML document.
|
||||
// `key` supports dotted keys and array indexing like `servers[0].host`.
|
||||
pub fn (d Doc) value(key string) Any {
|
||||
return d.root.value(key)
|
||||
}
|
||||
|
||||
// value_opt queries a value from the YAML document and returns an error when missing.
|
||||
pub fn (d Doc) value_opt(key string) !Any {
|
||||
return d.root.value_opt(key)
|
||||
}
|
||||
|
||||
// str returns a display-friendly string form of `Any`.
|
||||
pub fn (a Any) str() string {
|
||||
return a.string()
|
||||
}
|
||||
|
||||
// string returns `Any` as a string when possible, or a YAML representation otherwise.
|
||||
pub fn (a Any) string() string {
|
||||
return match a {
|
||||
string { a }
|
||||
bool, f64, i64, int, u64 { a.str() }
|
||||
Null { 'null' }
|
||||
[]Any, map[string]Any { a.to_yaml() }
|
||||
}
|
||||
}
|
||||
|
||||
// int returns `Any` as an `int`.
|
||||
pub fn (a Any) int() int {
|
||||
return match a {
|
||||
int {
|
||||
a
|
||||
}
|
||||
i64 {
|
||||
int(a)
|
||||
}
|
||||
u64 {
|
||||
int(a)
|
||||
}
|
||||
f64 {
|
||||
int(a)
|
||||
}
|
||||
bool {
|
||||
if a {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
string {
|
||||
a.int()
|
||||
}
|
||||
else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// i64 returns `Any` as an `i64`.
|
||||
pub fn (a Any) i64() i64 {
|
||||
return match a {
|
||||
i64 {
|
||||
a
|
||||
}
|
||||
int {
|
||||
i64(a)
|
||||
}
|
||||
u64 {
|
||||
i64(a)
|
||||
}
|
||||
f64 {
|
||||
i64(a)
|
||||
}
|
||||
bool {
|
||||
if a {
|
||||
i64(1)
|
||||
} else {
|
||||
i64(0)
|
||||
}
|
||||
}
|
||||
string {
|
||||
a.i64()
|
||||
}
|
||||
else {
|
||||
i64(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// u64 returns `Any` as a `u64`.
|
||||
pub fn (a Any) u64() u64 {
|
||||
return match a {
|
||||
u64 {
|
||||
a
|
||||
}
|
||||
int {
|
||||
u64(a)
|
||||
}
|
||||
i64 {
|
||||
u64(a)
|
||||
}
|
||||
f64 {
|
||||
u64(a)
|
||||
}
|
||||
bool {
|
||||
if a {
|
||||
u64(1)
|
||||
} else {
|
||||
u64(0)
|
||||
}
|
||||
}
|
||||
string {
|
||||
a.u64()
|
||||
}
|
||||
else {
|
||||
u64(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// f64 returns `Any` as an `f64`.
|
||||
pub fn (a Any) f64() f64 {
|
||||
return match a {
|
||||
f64 {
|
||||
a
|
||||
}
|
||||
int {
|
||||
f64(a)
|
||||
}
|
||||
i64 {
|
||||
f64(a)
|
||||
}
|
||||
u64 {
|
||||
f64(a)
|
||||
}
|
||||
bool {
|
||||
if a {
|
||||
1.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
string {
|
||||
a.f64()
|
||||
}
|
||||
else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// bool returns `Any` as a `bool`.
|
||||
pub fn (a Any) bool() bool {
|
||||
return match a {
|
||||
bool {
|
||||
a
|
||||
}
|
||||
int {
|
||||
a != 0
|
||||
}
|
||||
i64 {
|
||||
a != 0
|
||||
}
|
||||
u64 {
|
||||
a != 0
|
||||
}
|
||||
f64 {
|
||||
a != 0.0
|
||||
}
|
||||
string {
|
||||
lower := a.to_lower()
|
||||
lower in ['true', 'yes', 'on', '1']
|
||||
}
|
||||
else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// array returns `Any` as an array.
|
||||
pub fn (a Any) array() []Any {
|
||||
return match a {
|
||||
[]Any {
|
||||
a
|
||||
}
|
||||
map[string]Any {
|
||||
mut arr := []Any{cap: a.len}
|
||||
for _, value in a {
|
||||
arr << value
|
||||
}
|
||||
arr
|
||||
}
|
||||
else {
|
||||
[a]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// as_map returns `Any` as a map.
|
||||
pub fn (a Any) as_map() map[string]Any {
|
||||
return match a {
|
||||
map[string]Any {
|
||||
a
|
||||
}
|
||||
[]Any {
|
||||
mut out := map[string]Any{}
|
||||
for i, value in a {
|
||||
out['${i}'] = value
|
||||
}
|
||||
out
|
||||
}
|
||||
else {
|
||||
{
|
||||
'0': a
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// default_to returns `value` when `a` is `Null`.
|
||||
pub fn (a Any) default_to(value Any) Any {
|
||||
return match a {
|
||||
Null { value }
|
||||
else { a }
|
||||
}
|
||||
}
|
||||
|
||||
// value queries a value from the current node using dotted keys and array indices.
|
||||
pub fn (a Any) value(key string) Any {
|
||||
return a.value_opt(key) or { null }
|
||||
}
|
||||
|
||||
// value_opt queries a value from the current node and returns an error when missing.
|
||||
// A YAML key whose value is the explicit `null` literal returns that `Null`
|
||||
// (it is not treated as missing); only an absent key or a non-traversable
|
||||
// path raises an error.
|
||||
pub fn (a Any) value_opt(key string) !Any {
|
||||
key_split := parse_dotted_key(key) or { return error('yaml: invalid dotted key `${key}`') }
|
||||
return a.value_(a, key_split) or { error('yaml: no value for key `${key}`') }
|
||||
}
|
||||
|
||||
// value queries a value from the map.
|
||||
pub fn (m map[string]Any) value(key string) Any {
|
||||
return Any(m).value(key)
|
||||
}
|
||||
|
||||
// value queries a value from the array.
|
||||
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 out := []string{cap: a.len}
|
||||
for value in a {
|
||||
out << value.string()
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// as_strings returns the contents of the map as `map[string]string`.
|
||||
pub fn (m map[string]Any) as_strings() map[string]string {
|
||||
mut out := map[string]string{}
|
||||
for key, value in m {
|
||||
out[key] = value.string()
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// to_json converts `Any` to JSON.
|
||||
pub fn (a Any) to_json() string {
|
||||
mut sb := strings.new_builder(256)
|
||||
emit_any_as_json(mut sb, a)
|
||||
return sb.str()
|
||||
}
|
||||
|
||||
// to_yaml converts `Any` to YAML.
|
||||
pub fn (a Any) to_yaml() string {
|
||||
mut sb := strings.new_builder(256)
|
||||
emit_yaml_any(mut sb, a, 0)
|
||||
return sb.str()
|
||||
}
|
||||
|
||||
// to_yaml converts a YAML array to YAML text.
|
||||
pub fn (a []Any) to_yaml() string {
|
||||
return Any(a).to_yaml()
|
||||
}
|
||||
|
||||
// to_yaml converts a YAML map to YAML text.
|
||||
pub fn (m map[string]Any) to_yaml() string {
|
||||
return Any(m).to_yaml()
|
||||
}
|
||||
Reference in new issue
Block a user