Skip to main content

neco_ast/
lib.rs

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}