tramlines_core/yaml/
value.rs1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
7pub enum Yaml {
8 Null,
10 Bool(bool),
12 Int(i64),
14 Float(f64),
16 Str(String),
18 Seq(Vec<Spanned>),
20 Map(Vec<(Spanned<String>, Spanned)>),
22}
23
24#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
26pub struct Spanned<T = Yaml> {
27 pub value: T,
29 pub line: usize,
31}
32
33impl<T> Spanned<T> {
34 pub fn new(value: T, line: usize) -> Self {
36 Spanned { value, line }
37 }
38}
39
40impl Spanned {
41 pub fn as_str(&self) -> Option<&str> {
43 match &self.value {
44 Yaml::Str(s) => Some(s),
45 _ => None,
46 }
47 }
48
49 pub fn as_map(&self) -> Option<&[(Spanned<String>, Spanned)]> {
51 match &self.value {
52 Yaml::Map(m) => Some(m),
53 _ => None,
54 }
55 }
56
57 pub fn as_seq(&self) -> Option<&[Spanned]> {
59 match &self.value {
60 Yaml::Seq(s) => Some(s),
61 _ => None,
62 }
63 }
64
65 pub fn as_int(&self) -> Option<i64> {
67 match &self.value {
68 Yaml::Int(i) => Some(*i),
69 _ => None,
70 }
71 }
72
73 pub fn as_bool(&self) -> Option<bool> {
75 match &self.value {
76 Yaml::Bool(b) => Some(*b),
77 _ => None,
78 }
79 }
80
81 pub fn get(&self, key: &str) -> Option<&Spanned> {
83 self.entry(key).map(|(_, v)| v)
84 }
85
86 pub fn key_line(&self, key: &str) -> Option<usize> {
88 self.entry(key).map(|(k, _)| k.line)
89 }
90
91 fn entry(&self, key: &str) -> Option<&(Spanned<String>, Spanned)> {
93 self.as_map()?.iter().find(|(k, _)| k.value == key)
94 }
95
96 pub fn as_str_or_list(&self) -> Option<Vec<String>> {
98 match &self.value {
99 Yaml::Str(s) => Some(vec![s.clone()]),
100 Yaml::Seq(items) => items
101 .iter()
102 .map(|i| i.as_str().map(str::to_string))
103 .collect(),
104 _ => None,
105 }
106 }
107
108 pub fn type_name(&self) -> &'static str {
110 match &self.value {
111 Yaml::Null => "null",
112 Yaml::Bool(_) => "boolean",
113 Yaml::Int(_) => "integer",
114 Yaml::Float(_) => "float",
115 Yaml::Str(_) => "string",
116 Yaml::Seq(_) => "list",
117 Yaml::Map(_) => "map",
118 }
119 }
120
121 pub fn scalar_text(&self) -> Option<String> {
123 match &self.value {
124 Yaml::Null => Some("null".into()),
125 Yaml::Bool(b) => Some(b.to_string()),
126 Yaml::Int(i) => Some(i.to_string()),
127 Yaml::Float(f) => Some(f.to_string()),
128 Yaml::Str(s) => Some(s.clone()),
129 _ => None,
130 }
131 }
132}
133
134#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
138pub enum YamlError {
139 #[error("YAML does not parse at line {line}: {message}")]
141 Syntax {
142 line: usize,
144 message: String,
146 },
147 #[error("YAML anchor at line {line}: anchors are not read")]
149 Anchor {
150 line: usize,
152 },
153 #[error("YAML alias at line {line}: aliases are not read")]
155 Alias {
156 line: usize,
158 },
159 #[error("YAML tag at line {line}: tags are not read")]
161 Tag {
162 line: usize,
164 },
165 #[error("YAML mapping key at line {line} is not a string")]
167 NonStringKey {
168 line: usize,
170 },
171}
172impl YamlError {
175 pub fn is_refused(&self) -> bool {
179 matches!(
182 self,
183 YamlError::Anchor { .. } | YamlError::Alias { .. } | YamlError::Tag { .. }
184 )
185 }
186
187 pub fn line(&self) -> usize {
189 match self {
190 YamlError::Syntax { line, .. }
191 | YamlError::Anchor { line }
192 | YamlError::Alias { line }
193 | YamlError::Tag { line }
194 | YamlError::NonStringKey { line } => *line,
195 }
196 }
197}