-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbasic.rs
More file actions
106 lines (93 loc) · 3.01 KB
/
basic.rs
File metadata and controls
106 lines (93 loc) · 3.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
//! End-to-end demo: parse a Ktav document into a typed struct, walk
//! the dynamic [`Value`] tree, then build a fresh document in Rust and
//! render it back to Ktav text.
//!
//! Run with:
//!
//! cargo run -p ktav --example basic
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
use ktav::value::{ObjectMap, Value};
const SRC: &str = "\
service: web
port: 8080
ratio: 0.75
tls: true
tags: [
prod
eu-west-1
]
db.host: primary.internal
db.timeout: 30
";
#[derive(Debug, Deserialize, Serialize)]
struct Db {
host: String,
timeout: u32,
}
#[derive(Debug, Deserialize, Serialize)]
struct Config {
service: String,
port: u16,
ratio: f64,
tls: bool,
tags: Vec<String>,
db: Db,
}
fn main() {
// ── 1. Decode straight into a typed struct via serde. ──────────────
let cfg: Config = ktav::from_str(SRC).expect("valid Ktav");
println!(
"service={} port={} tls={} ratio={:.2}",
cfg.service, cfg.port, cfg.tls, cfg.ratio
);
println!("tags={:?}", cfg.tags);
println!("db: {} (timeout={}s)\n", cfg.db.host, cfg.db.timeout);
// ── 2. Or work with the dynamic Value tree, matching the variants. ─
let dyn_val = ktav::parse(SRC).expect("valid Ktav");
let Value::Object(top) = &dyn_val else {
unreachable!("top-level is always an object");
};
println!("shape:");
for (k, v) in top {
println!(" {:<12} -> {}", k, describe(v));
}
// ── 3. Build a config in code, render it as Ktav text. ─────────────
let upstreams = vec![
upstream("a.example", 1080),
upstream("b.example", 1080),
upstream("c.example", 1080),
];
let mut top = obj_map();
top.insert("name".into(), Value::String("frontend".into()));
top.insert("port".into(), Value::Integer("8443".into()));
top.insert("tls".into(), Value::Bool(true));
top.insert("ratio".into(), Value::Float("0.95".into()));
top.insert("upstreams".into(), Value::Array(upstreams));
top.insert("notes".into(), Value::Null);
let rendered = ktav::render::render(&Value::Object(top)).expect("render");
println!("\n--- rendered ---");
print!("{}", rendered);
}
fn describe(v: &Value) -> String {
match v {
Value::Null => "null".to_string(),
Value::Bool(b) => format!("bool={b}"),
Value::Integer(s) => format!("int={s}"),
Value::Float(s) => format!("float={s}"),
Value::String(s) => format!("str={s:?}"),
Value::Array(a) => format!("array({})", a.len()),
Value::Object(o) => format!("object({})", o.len()),
}
}
fn upstream(host: &str, port: u16) -> Value {
let mut m = obj_map();
m.insert("host".into(), Value::String(host.into()));
m.insert("port".into(), Value::Integer(port.to_string().into()));
Value::Object(m)
}
fn obj_map() -> ObjectMap {
// ObjectMap is the crate's IndexMap alias preserving insertion order.
let _ = IndexMap::<String, Value>::new();
ObjectMap::default()
}