1use indexmap::IndexMap;
8use std::borrow::Cow;
9use std::fmt;
10use std::hash::{Hash, Hasher};
11
12#[derive(Debug, Clone, PartialEq)]
14pub enum BorrowedValue<'a> {
15 Null,
17 Bool(bool),
19 Int(i64),
21 Float(f64),
23 String(Cow<'a, str>),
25 Sequence(Vec<BorrowedValue<'a>>),
27 Mapping(IndexMap<BorrowedValue<'a>, BorrowedValue<'a>>),
29}
30
31impl<'a> BorrowedValue<'a> {
32 pub const fn null() -> Self {
34 Self::Null
35 }
36
37 pub const fn bool(b: bool) -> Self {
39 Self::Bool(b)
40 }
41
42 pub const fn int(i: i64) -> Self {
44 Self::Int(i)
45 }
46
47 pub const fn float(f: f64) -> Self {
49 Self::Float(f)
50 }
51
52 pub const fn borrowed_string(s: &'a str) -> Self {
54 Self::String(Cow::Borrowed(s))
55 }
56
57 pub fn owned_string(s: String) -> Self {
59 Self::String(Cow::Owned(s))
60 }
61
62 pub const fn string(s: Cow<'a, str>) -> Self {
64 Self::String(s)
65 }
66
67 pub const fn sequence() -> Self {
69 Self::Sequence(Vec::new())
70 }
71
72 pub const fn sequence_with(values: Vec<Self>) -> Self {
74 Self::Sequence(values)
75 }
76
77 pub fn mapping() -> Self {
79 Self::Mapping(IndexMap::new())
80 }
81
82 pub fn mapping_with(pairs: Vec<(Self, Self)>) -> Self {
84 let mut map = IndexMap::new();
85 for (key, value) in pairs {
86 map.insert(key, value);
87 }
88 Self::Mapping(map)
89 }
90
91 pub const fn type_name(&self) -> &'static str {
93 match self {
94 Self::Null => "null",
95 Self::Bool(_) => "bool",
96 Self::Int(_) => "int",
97 Self::Float(_) => "float",
98 Self::String(_) => "string",
99 Self::Sequence(_) => "sequence",
100 Self::Mapping(_) => "mapping",
101 }
102 }
103
104 pub const fn is_null(&self) -> bool {
106 matches!(self, Self::Null)
107 }
108
109 pub const fn is_bool(&self) -> bool {
111 matches!(self, Self::Bool(_))
112 }
113
114 pub const fn is_int(&self) -> bool {
116 matches!(self, Self::Int(_))
117 }
118
119 pub const fn is_float(&self) -> bool {
121 matches!(self, Self::Float(_))
122 }
123
124 pub const fn is_string(&self) -> bool {
126 matches!(self, Self::String(_))
127 }
128
129 pub const fn is_sequence(&self) -> bool {
131 matches!(self, Self::Sequence(_))
132 }
133
134 pub const fn is_mapping(&self) -> bool {
136 matches!(self, Self::Mapping(_))
137 }
138
139 pub const fn as_bool(&self) -> Option<bool> {
141 if let Self::Bool(b) = self {
142 Some(*b)
143 } else {
144 None
145 }
146 }
147
148 pub const fn as_int(&self) -> Option<i64> {
150 if let Self::Int(i) = self {
151 Some(*i)
152 } else {
153 None
154 }
155 }
156
157 pub const fn as_float(&self) -> Option<f64> {
159 match self {
160 Self::Float(f) => Some(*f),
161 Self::Int(i) => Some(*i as f64),
162 _ => None,
163 }
164 }
165
166 pub fn as_str(&self) -> Option<&str> {
168 if let Self::String(s) = self {
169 Some(s.as_ref())
170 } else {
171 None
172 }
173 }
174
175 pub const fn as_sequence(&self) -> Option<&Vec<BorrowedValue<'a>>> {
177 if let Self::Sequence(seq) = self {
178 Some(seq)
179 } else {
180 None
181 }
182 }
183
184 pub fn as_sequence_mut(&mut self) -> Option<&mut Vec<BorrowedValue<'a>>> {
186 if let Self::Sequence(seq) = self {
187 Some(seq)
188 } else {
189 None
190 }
191 }
192
193 pub const fn as_mapping(&self) -> Option<&IndexMap<BorrowedValue<'a>, BorrowedValue<'a>>> {
195 if let Self::Mapping(map) = self {
196 Some(map)
197 } else {
198 None
199 }
200 }
201
202 pub fn as_mapping_mut(
204 &mut self,
205 ) -> Option<&mut IndexMap<BorrowedValue<'a>, BorrowedValue<'a>>> {
206 if let Self::Mapping(map) = self {
207 Some(map)
208 } else {
209 None
210 }
211 }
212
213 pub fn into_owned(self) -> BorrowedValue<'static> {
215 match self {
216 Self::Null => BorrowedValue::Null,
217 Self::Bool(b) => BorrowedValue::Bool(b),
218 Self::Int(i) => BorrowedValue::Int(i),
219 Self::Float(f) => BorrowedValue::Float(f),
220 Self::String(s) => BorrowedValue::String(Cow::Owned(s.into_owned())),
221 Self::Sequence(seq) => {
222 BorrowedValue::Sequence(seq.into_iter().map(|v| v.into_owned()).collect())
223 }
224 Self::Mapping(map) => BorrowedValue::Mapping(
225 map.into_iter()
226 .map(|(k, v)| (k.into_owned(), v.into_owned()))
227 .collect(),
228 ),
229 }
230 }
231
232 pub fn clone_if_needed(&self) -> Self {
234 match self {
235 Self::String(Cow::Borrowed(s)) => Self::String(Cow::Borrowed(s)),
236 _ => self.clone(),
237 }
238 }
239}
240
241impl<'a> Default for BorrowedValue<'a> {
242 fn default() -> Self {
243 Self::Null
244 }
245}
246
247impl<'a> fmt::Display for BorrowedValue<'a> {
248 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249 match self {
250 Self::Null => write!(f, "null"),
251 Self::Bool(b) => write!(f, "{}", b),
252 Self::Int(i) => write!(f, "{}", i),
253 Self::Float(fl) => write!(f, "{}", fl),
254 Self::String(s) => write!(f, "{}", s),
255 Self::Sequence(seq) => {
256 write!(f, "[")?;
257 for (i, item) in seq.iter().enumerate() {
258 if i > 0 {
259 write!(f, ", ")?;
260 }
261 write!(f, "{}", item)?;
262 }
263 write!(f, "]")
264 }
265 Self::Mapping(map) => {
266 write!(f, "{{")?;
267 for (i, (key, value)) in map.iter().enumerate() {
268 if i > 0 {
269 write!(f, ", ")?;
270 }
271 write!(f, "{}: {}", key, value)?;
272 }
273 write!(f, "}}")
274 }
275 }
276 }
277}
278
279impl<'a> Hash for BorrowedValue<'a> {
280 fn hash<H: Hasher>(&self, state: &mut H) {
281 match self {
282 Self::Null => 0.hash(state),
283 Self::Bool(b) => {
284 1.hash(state);
285 b.hash(state);
286 }
287 Self::Int(i) => {
288 2.hash(state);
289 i.hash(state);
290 }
291 Self::Float(f) => {
292 3.hash(state);
293 f.to_bits().hash(state);
294 }
295 Self::String(s) => {
296 4.hash(state);
297 s.hash(state);
298 }
299 Self::Sequence(seq) => {
300 5.hash(state);
301 seq.hash(state);
302 }
303 Self::Mapping(_) => {
304 6.hash(state);
305 }
308 }
309 }
310}
311
312impl<'a> Eq for BorrowedValue<'a> {}
313
314impl<'a> From<crate::Value> for BorrowedValue<'a> {
316 fn from(value: crate::Value) -> Self {
317 match value {
318 crate::Value::Null => Self::Null,
319 crate::Value::Bool(b) => Self::Bool(b),
320 crate::Value::Int(i) => Self::Int(i),
321 crate::Value::Float(f) => Self::Float(f),
322 crate::Value::String(s) => Self::String(Cow::Owned(s)),
323 crate::Value::Sequence(seq) => {
324 Self::Sequence(seq.into_iter().map(Into::into).collect())
325 }
326 crate::Value::Mapping(map) => {
327 Self::Mapping(map.into_iter().map(|(k, v)| (k.into(), v.into())).collect())
328 }
329 }
330 }
331}
332
333impl From<BorrowedValue<'_>> for crate::Value {
335 fn from(value: BorrowedValue<'_>) -> Self {
336 match value {
337 BorrowedValue::Null => Self::Null,
338 BorrowedValue::Bool(b) => Self::Bool(b),
339 BorrowedValue::Int(i) => Self::Int(i),
340 BorrowedValue::Float(f) => Self::Float(f),
341 BorrowedValue::String(s) => Self::String(s.into_owned()),
342 BorrowedValue::Sequence(seq) => {
343 Self::Sequence(seq.into_iter().map(Into::into).collect())
344 }
345 BorrowedValue::Mapping(map) => {
346 Self::Mapping(map.into_iter().map(|(k, v)| (k.into(), v.into())).collect())
347 }
348 }
349 }
350}
351
352#[cfg(test)]
353mod tests {
354 use super::*;
355
356 #[test]
357 fn test_borrowed_string() {
358 let s = "hello world";
359 let value = BorrowedValue::borrowed_string(s);
360 assert_eq!(value.as_str(), Some("hello world"));
361
362 if let BorrowedValue::String(cow) = &value {
364 assert!(matches!(cow, Cow::Borrowed(_)));
365 }
366 }
367
368 #[test]
369 fn test_owned_string() {
370 let value = BorrowedValue::owned_string("hello".to_string());
371 assert_eq!(value.as_str(), Some("hello"));
372
373 if let BorrowedValue::String(cow) = &value {
375 assert!(matches!(cow, Cow::Owned(_)));
376 }
377 }
378
379 #[test]
380 fn test_into_owned() {
381 let s = "test";
382 let borrowed = BorrowedValue::borrowed_string(s);
383 let owned: BorrowedValue<'static> = borrowed.into_owned();
384
385 if let BorrowedValue::String(cow) = &owned {
387 assert!(matches!(cow, Cow::Owned(_)));
388 }
389 }
390
391 #[test]
392 fn test_zero_copy_mapping() {
393 let key = "key";
394 let value = "value";
395
396 let map = BorrowedValue::mapping_with(vec![(
397 BorrowedValue::borrowed_string(key),
398 BorrowedValue::borrowed_string(value),
399 )]);
400
401 assert!(map.is_mapping());
402 if let BorrowedValue::Mapping(m) = &map {
403 assert_eq!(m.len(), 1);
404 }
405 }
406}