1use std::fmt;
4
5#[derive(Debug, Clone, PartialEq, Default)]
12pub enum Value {
13 #[default]
14 Null,
15 Bool(bool),
16 Int(i64),
17 Float(f64),
18 Str(String),
19 List(Vec<Value>),
20 Map(Map),
21 Ref(Ref),
23 Tag(Ref),
25 Res(Res),
27 Lang(String),
29 Text(String),
31 Image(String),
33 Color(u32),
35 Bytes(Vec<u8>),
37}
38
39#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
43pub struct Ref {
44 pub kind: String,
45 pub id: String,
46}
47
48impl Ref {
49 pub fn new(kind: impl Into<String>, id: impl Into<String>) -> Self {
50 Self {
51 kind: kind.into(),
52 id: id.into(),
53 }
54 }
55
56 pub fn namespace(&self) -> &str {
58 yog_ids::split(&self.id).0
59 }
60}
61
62#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
65pub struct Res {
66 pub set: String,
67 pub key: String,
68}
69
70impl Res {
71 pub fn name(&self) -> String {
73 format!("{}.{}", self.set, self.key)
74 }
75}
76
77impl std::fmt::Display for Res {
78 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80 write!(f, "{}{}.{}", yog_ids::sep::TYPED, self.set, self.key)
81 }
82}
83
84pub use super::map::Map;
85
86impl Value {
87 pub fn kind_name(&self) -> &'static str {
89 match self {
90 Value::Null => "null",
91 Value::Bool(_) => "true/false",
92 Value::Int(_) => "an integer",
93 Value::Float(_) => "a number",
94 Value::Str(_) => "a string",
95 Value::List(_) => "a list",
96 Value::Map(_) => "an object",
97 Value::Ref(_) => "a reference",
98 Value::Tag(_) => "a tag",
99 Value::Res(_) => "a resource",
100 Value::Lang(_) => "a translation key",
101 Value::Text(_) => "a text",
102 Value::Image(_) => "an image",
103 Value::Color(_) => "a color",
104 Value::Bytes(_) => "bytes",
105 }
106 }
107
108 pub fn is_null(&self) -> bool {
109 matches!(self, Value::Null)
110 }
111
112 pub fn as_bool(&self) -> Option<bool> {
113 match self {
114 Value::Bool(b) => Some(*b),
115 _ => None,
116 }
117 }
118
119 pub fn as_i64(&self) -> Option<i64> {
120 match self {
121 Value::Int(n) => Some(*n),
122 Value::Float(f) if f.fract() == 0.0 && f.abs() < 9.0e15 => Some(*f as i64),
123 _ => None,
124 }
125 }
126
127 pub fn as_f64(&self) -> Option<f64> {
128 match self {
129 Value::Int(n) => Some(*n as f64),
130 Value::Float(f) => Some(*f),
131 _ => None,
132 }
133 }
134
135 pub fn as_str(&self) -> Option<&str> {
137 match self {
138 Value::Str(s) | Value::Text(s) | Value::Image(s) => Some(s),
139 _ => None,
140 }
141 }
142
143 pub fn as_list(&self) -> Option<&[Value]> {
144 match self {
145 Value::List(l) => Some(l),
146 _ => None,
147 }
148 }
149
150 pub fn as_map(&self) -> Option<&Map> {
151 match self {
152 Value::Map(m) => Some(m),
153 _ => None,
154 }
155 }
156
157 pub fn get(&self, key: &str) -> Option<&Value> {
159 self.as_map()?.get(key)
160 }
161
162 pub fn at(&self, path: &Path) -> Option<&Value> {
164 let mut v = self;
165 for seg in path.segments() {
166 v = match (seg, v) {
167 (Seg::Key(k), Value::Map(m)) => m.get(k)?,
168 (Seg::Index(i), Value::List(l)) => l.get(*i)?,
169 _ => return None,
170 };
171 }
172 Some(v)
173 }
174
175 pub fn at_mut(&mut self, segs: &[Seg]) -> Option<&mut Value> {
177 let mut v = self;
178 for seg in segs {
179 v = match (seg, v) {
180 (Seg::Key(k), Value::Map(m)) => m.get_mut(k)?,
181 (Seg::Index(i), Value::List(l)) => l.get_mut(*i)?,
182 _ => return None,
183 };
184 }
185 Some(v)
186 }
187}
188
189impl From<bool> for Value {
190 fn from(b: bool) -> Self {
191 Value::Bool(b)
192 }
193}
194impl From<i64> for Value {
195 fn from(n: i64) -> Self {
196 Value::Int(n)
197 }
198}
199impl From<f64> for Value {
200 fn from(f: f64) -> Self {
201 Value::Float(f)
202 }
203}
204impl From<&str> for Value {
205 fn from(s: &str) -> Self {
206 Value::Str(s.to_string())
207 }
208}
209impl From<String> for Value {
210 fn from(s: String) -> Self {
211 Value::Str(s)
212 }
213}
214impl From<Vec<Value>> for Value {
215 fn from(l: Vec<Value>) -> Self {
216 Value::List(l)
217 }
218}
219impl From<Map> for Value {
220 fn from(m: Map) -> Self {
221 Value::Map(m)
222 }
223}
224
225#[derive(Debug, Clone, PartialEq, Eq, Hash, crate::Schema)]
227#[data(tag = "type")]
228pub enum Seg {
229 Key(String),
230 Index(usize),
231}
232
233#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
235pub struct Path(Vec<Seg>);
236
237impl Path {
238 pub fn root() -> Self {
239 Self(Vec::new())
240 }
241
242 pub fn segments(&self) -> &[Seg] {
243 &self.0
244 }
245
246 pub fn is_root(&self) -> bool {
247 self.0.is_empty()
248 }
249
250 pub fn push(&mut self, seg: Seg) {
251 self.0.push(seg);
252 }
253
254 pub fn pop(&mut self) {
255 self.0.pop();
256 }
257
258 pub fn key(&self, key: &str) -> Path {
259 let mut p = self.clone();
260 p.0.push(Seg::Key(key.to_string()));
261 p
262 }
263
264 pub fn then(&self, rest: &Path) -> Path {
266 let mut p = self.clone();
267 p.0.extend(rest.0.iter().cloned());
268 p
269 }
270
271 pub fn index(&self, i: usize) -> Path {
272 let mut p = self.clone();
273 p.0.push(Seg::Index(i));
274 p
275 }
276}
277
278impl crate::Schema for Path {
280 fn shape() -> crate::Shape {
281 Vec::<Seg>::shape()
282 }
283 fn read(v: &crate::ValueRef<'_>, cx: &mut crate::Reader) -> Option<Self> {
284 Vec::<Seg>::read(v, cx).map(Path)
285 }
286 fn write(&self) -> Value {
287 self.0.write()
288 }
289}
290
291impl fmt::Display for Path {
292 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
293 if self.0.is_empty() {
294 return f.write_str("(root)");
295 }
296 for (i, seg) in self.0.iter().enumerate() {
297 match seg {
298 Seg::Key(k) if is_plain_key(k) => {
299 if i > 0 {
300 f.write_str(".")?;
301 }
302 f.write_str(k)?;
303 }
304 Seg::Key(k) => write!(f, "[{k:?}]")?,
305 Seg::Index(n) => write!(f, "[{n}]")?,
306 }
307 }
308 Ok(())
309 }
310}
311
312fn is_plain_key(k: &str) -> bool {
313 let mut chars = k.chars();
314 chars
315 .next()
316 .is_some_and(|c| c.is_ascii_alphabetic() || c == '_' || c == '$')
317 && chars.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '$')
318}