1#![doc = include_str!("../README.md")]
2#![no_std]
3
4extern crate alloc;
5
6use alloc::{borrow::Cow, format, string::String, vec::Vec};
7
8#[derive(Debug, Clone, PartialEq)]
9pub enum StructuredValue<'a> {
10 Null,
11 Bool(bool),
12 Number(StructuredNumber<'a>),
13 String(Cow<'a, str>),
14 Sequence(Vec<StructuredValue<'a>>),
15 Mapping(Vec<StructuredField<'a>>),
16}
17
18impl<'a> StructuredValue<'a> {
19 pub fn as_str(&self) -> Option<&str> {
20 match self {
21 Self::String(value) => Some(value.as_ref()),
22 _ => None,
23 }
24 }
25
26 pub fn as_bool(&self) -> Option<bool> {
27 match self {
28 Self::Bool(value) => Some(*value),
29 _ => None,
30 }
31 }
32
33 pub fn as_f64(&self) -> Option<f64> {
34 match self {
35 Self::Number(value) => value.as_f64,
36 _ => None,
37 }
38 }
39
40 pub fn as_sequence(&self) -> Option<&[StructuredValue<'a>]> {
41 match self {
42 Self::Sequence(values) => Some(values.as_slice()),
43 _ => None,
44 }
45 }
46
47 pub fn as_mapping(&self) -> Option<&[StructuredField<'a>]> {
48 match self {
49 Self::Mapping(fields) => Some(fields.as_slice()),
50 _ => None,
51 }
52 }
53}
54
55#[derive(Debug, Clone, PartialEq)]
56pub struct StructuredNumber<'a> {
57 pub raw: Cow<'a, str>,
58 pub as_f64: Option<f64>,
59}
60
61impl<'a> StructuredNumber<'a> {
62 pub fn from_parts(raw: Cow<'a, str>, as_f64: Option<f64>) -> Self {
63 Self { raw, as_f64 }
64 }
65
66 pub fn from_f64(value: f64) -> StructuredNumber<'static> {
67 StructuredNumber {
68 raw: Cow::Owned(format!("{value}")),
69 as_f64: Some(value),
70 }
71 }
72}
73
74#[derive(Debug, Clone, PartialEq)]
75pub struct StructuredField<'a> {
76 pub key: Cow<'a, str>,
77 pub value: StructuredValue<'a>,
78}
79
80pub trait StructuredNode<'a>: Sized {
81 fn kind(&self) -> Cow<'a, str>;
82
83 fn identifier(&self) -> Option<Cow<'a, str>> {
84 None
85 }
86
87 fn attribute(&self, key: &str) -> Option<StructuredValue<'a>>;
88
89 fn attribute_str(&self, key: &str) -> Option<Cow<'a, str>> {
90 self.attribute(key)
91 .and_then(|value| value.as_str().map(|value| Cow::Owned(String::from(value))))
92 }
93
94 fn attribute_bool(&self, key: &str) -> Option<bool> {
95 self.attribute(key).and_then(|value| value.as_bool())
96 }
97
98 fn type_annotation(&self) -> Option<Cow<'a, str>> {
99 None
100 }
101
102 fn children(&self) -> Vec<Self>;
103
104 fn value(&self) -> StructuredValue<'a>;
105}
106
107pub trait StructuredDocument<'a> {
108 type Node: StructuredNode<'a>;
109
110 fn nodes(&'a self) -> Vec<Self::Node>;
111}
112
113#[cfg(test)]
114mod tests {
115 use alloc::{borrow::Cow, vec};
116
117 use super::{StructuredField, StructuredNumber, StructuredValue};
118
119 #[test]
120 fn string_value_exposes_str() {
121 let value = StructuredValue::String(Cow::Borrowed("neco"));
122 assert_eq!(value.as_str(), Some("neco"));
123 }
124
125 #[test]
126 fn bool_value_exposes_bool() {
127 let value = StructuredValue::Bool(true);
128 assert_eq!(value.as_bool(), Some(true));
129 }
130
131 #[test]
132 fn number_value_exposes_f64() {
133 let value = StructuredValue::Number(StructuredNumber::from_f64(1.5));
134 assert_eq!(value.as_f64(), Some(1.5));
135 }
136
137 #[test]
138 fn sequence_value_exposes_slice() {
139 let value = StructuredValue::Sequence(vec![StructuredValue::Null]);
140 assert_eq!(value.as_sequence().expect("sequence").len(), 1);
141 }
142
143 #[test]
144 fn mapping_value_exposes_fields() {
145 let value = StructuredValue::Mapping(vec![StructuredField {
146 key: Cow::Borrowed("name"),
147 value: StructuredValue::String(Cow::Borrowed("neco")),
148 }]);
149 assert_eq!(value.as_mapping().expect("mapping")[0].key, "name");
150 }
151}