rust_yaml/
value.rs

1//! YAML value representation
2
3use crate::scanner::QuoteStyle;
4use indexmap::IndexMap;
5use std::fmt;
6use std::hash::{Hash, Hasher};
7
8/// Comments associated with a YAML value
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct Comments {
11    /// Comments that appear before this value
12    pub leading: Vec<String>,
13    /// Comment that appears on the same line as this value
14    pub trailing: Option<String>,
15    /// Comments that appear inside collections (between items)
16    pub inner: Vec<String>,
17}
18
19impl Comments {
20    /// Create empty comments
21    pub const fn new() -> Self {
22        Self {
23            leading: Vec::new(),
24            trailing: None,
25            inner: Vec::new(),
26        }
27    }
28
29    /// Check if there are any comments
30    pub const fn is_empty(&self) -> bool {
31        self.leading.is_empty() && self.trailing.is_none() && self.inner.is_empty()
32    }
33
34    /// Add a leading comment
35    pub fn add_leading<S: Into<String>>(&mut self, comment: S) {
36        self.leading.push(comment.into());
37    }
38
39    /// Set the trailing comment
40    pub fn set_trailing<S: Into<String>>(&mut self, comment: S) {
41        self.trailing = Some(comment.into());
42    }
43
44    /// Add an inner comment
45    pub fn add_inner<S: Into<String>>(&mut self, comment: S) {
46        self.inner.push(comment.into());
47    }
48}
49
50impl Default for Comments {
51    fn default() -> Self {
52        Self::new()
53    }
54}
55
56/// Indentation style used in YAML documents
57#[derive(Debug, Clone, PartialEq, Eq)]
58pub enum IndentStyle {
59    /// Spaces with specified width (2, 4, 8, etc.)
60    Spaces(usize),
61    /// Tab characters
62    Tabs,
63}
64
65impl Default for IndentStyle {
66    fn default() -> Self {
67        Self::Spaces(2)
68    }
69}
70
71/// Style information for YAML values to preserve formatting during round-trips
72#[derive(Debug, Clone, PartialEq, Eq)]
73pub struct Style {
74    /// Quote style for string values
75    pub quote_style: Option<QuoteStyle>,
76    /// Indentation style for the document
77    pub indent_style: Option<IndentStyle>,
78}
79
80impl Style {
81    /// Create empty style information
82    pub const fn new() -> Self {
83        Self {
84            quote_style: None,
85            indent_style: None,
86        }
87    }
88
89    /// Create style with quote style
90    pub const fn with_quote_style(quote_style: QuoteStyle) -> Self {
91        Self {
92            quote_style: Some(quote_style),
93            indent_style: None,
94        }
95    }
96
97    /// Create style with indentation style
98    pub const fn with_indent_style(indent_style: IndentStyle) -> Self {
99        Self {
100            quote_style: None,
101            indent_style: Some(indent_style),
102        }
103    }
104
105    /// Create style with both quote and indent styles
106    pub const fn with_styles(quote_style: QuoteStyle, indent_style: IndentStyle) -> Self {
107        Self {
108            quote_style: Some(quote_style),
109            indent_style: Some(indent_style),
110        }
111    }
112
113    /// Check if there is any style information
114    pub const fn is_empty(&self) -> bool {
115        self.quote_style.is_none() && self.indent_style.is_none()
116    }
117}
118
119impl Default for Style {
120    fn default() -> Self {
121        Self::new()
122    }
123}
124
125/// A YAML value with optional comments and style for round-trip preservation
126#[derive(Debug, Clone, PartialEq, Eq)]
127pub struct CommentedValue {
128    /// The actual YAML value
129    pub value: Value,
130    /// Comments associated with this value
131    pub comments: Comments,
132    /// Style information for formatting preservation
133    pub style: Style,
134}
135
136impl CommentedValue {
137    /// Create a new commented value
138    pub const fn new(value: Value) -> Self {
139        Self {
140            value,
141            comments: Comments::new(),
142            style: Style::new(),
143        }
144    }
145
146    /// Create a commented value with leading comments
147    pub fn with_leading_comments(value: Value, comments: Vec<String>) -> Self {
148        let mut commented = Self::new(value);
149        commented.comments.leading = comments;
150        commented
151    }
152
153    /// Create a commented value with a trailing comment
154    pub fn with_trailing_comment(value: Value, comment: String) -> Self {
155        let mut commented = Self::new(value);
156        commented.comments.trailing = Some(comment);
157        commented
158    }
159
160    /// Add a leading comment
161    pub fn add_leading_comment<S: Into<String>>(&mut self, comment: S) {
162        self.comments.add_leading(comment);
163    }
164
165    /// Set a trailing comment
166    pub fn set_trailing_comment<S: Into<String>>(&mut self, comment: S) {
167        self.comments.set_trailing(comment);
168    }
169
170    /// Check if this value has any comments
171    pub const fn has_comments(&self) -> bool {
172        !self.comments.is_empty()
173    }
174
175    /// Create a commented value with quote style
176    pub const fn with_quote_style(value: Value, quote_style: QuoteStyle) -> Self {
177        Self {
178            value,
179            comments: Comments::new(),
180            style: Style::with_quote_style(quote_style),
181        }
182    }
183
184    /// Set the quote style
185    pub const fn set_quote_style(&mut self, quote_style: QuoteStyle) {
186        self.style.quote_style = Some(quote_style);
187    }
188
189    /// Get the quote style
190    pub const fn quote_style(&self) -> Option<&QuoteStyle> {
191        self.style.quote_style.as_ref()
192    }
193
194    /// Check if this value has style information
195    pub const fn has_style(&self) -> bool {
196        !self.style.is_empty()
197    }
198
199    /// Set the indentation style
200    pub const fn set_indent_style(&mut self, indent_style: IndentStyle) {
201        self.style.indent_style = Some(indent_style);
202    }
203
204    /// Get the indentation style
205    pub const fn indent_style(&self) -> Option<&IndentStyle> {
206        self.style.indent_style.as_ref()
207    }
208
209    /// Create a commented value with indentation style
210    pub const fn with_indent_style(value: Value, indent_style: IndentStyle) -> Self {
211        Self {
212            value,
213            comments: Comments::new(),
214            style: Style::with_indent_style(indent_style),
215        }
216    }
217}
218
219impl From<Value> for CommentedValue {
220    fn from(value: Value) -> Self {
221        Self::new(value)
222    }
223}
224
225/// A YAML value with all possible types
226#[derive(Debug, Clone)]
227pub enum Value {
228    /// Null value
229    Null,
230    /// Boolean value
231    Bool(bool),
232    /// Integer value
233    Int(i64),
234    /// Floating point value
235    Float(f64),
236    /// String value
237    String(String),
238    /// Sequence (array/list) value
239    Sequence(Vec<Value>),
240    /// Mapping (dictionary/object) value
241    Mapping(IndexMap<Value, Value>),
242}
243
244impl Value {
245    /// Create a null value
246    pub const fn null() -> Self {
247        Self::Null
248    }
249
250    /// Create a boolean value
251    pub const fn bool(b: bool) -> Self {
252        Self::Bool(b)
253    }
254
255    /// Create an integer value
256    pub const fn int(i: i64) -> Self {
257        Self::Int(i)
258    }
259
260    /// Create a float value
261    pub const fn float(f: f64) -> Self {
262        Self::Float(f)
263    }
264
265    /// Create a string value
266    pub fn string(s: impl Into<String>) -> Self {
267        Self::String(s.into())
268    }
269
270    /// Create an empty sequence
271    pub const fn sequence() -> Self {
272        Self::Sequence(Vec::new())
273    }
274
275    /// Create a sequence with values
276    pub const fn sequence_with(values: Vec<Self>) -> Self {
277        Self::Sequence(values)
278    }
279
280    /// Create an empty mapping
281    pub fn mapping() -> Self {
282        Self::Mapping(IndexMap::new())
283    }
284
285    /// Create a mapping with key-value pairs
286    pub fn mapping_with(pairs: Vec<(Self, Self)>) -> Self {
287        let mut map = IndexMap::new();
288        for (key, value) in pairs {
289            map.insert(key, value);
290        }
291        Self::Mapping(map)
292    }
293
294    /// Get the type name of this value
295    pub const fn type_name(&self) -> &'static str {
296        match self {
297            Self::Null => "null",
298            Self::Bool(_) => "bool",
299            Self::Int(_) => "int",
300            Self::Float(_) => "float",
301            Self::String(_) => "string",
302            Self::Sequence(_) => "sequence",
303            Self::Mapping(_) => "mapping",
304        }
305    }
306
307    /// Check if this value is null
308    pub const fn is_null(&self) -> bool {
309        matches!(self, Self::Null)
310    }
311
312    /// Check if this value is a boolean
313    pub const fn is_bool(&self) -> bool {
314        matches!(self, Self::Bool(_))
315    }
316
317    /// Check if this value is an integer
318    pub const fn is_int(&self) -> bool {
319        matches!(self, Self::Int(_))
320    }
321
322    /// Check if this value is a float
323    pub const fn is_float(&self) -> bool {
324        matches!(self, Self::Float(_))
325    }
326
327    /// Check if this value is a string
328    pub const fn is_string(&self) -> bool {
329        matches!(self, Self::String(_))
330    }
331
332    /// Check if this value is a sequence
333    pub const fn is_sequence(&self) -> bool {
334        matches!(self, Self::Sequence(_))
335    }
336
337    /// Check if this value is a mapping
338    pub const fn is_mapping(&self) -> bool {
339        matches!(self, Self::Mapping(_))
340    }
341
342    /// Check if this value is a number (int or float)
343    pub const fn is_number(&self) -> bool {
344        matches!(self, Self::Int(_) | Self::Float(_))
345    }
346
347    /// Get the length of sequences and mappings, None for scalars
348    pub fn len(&self) -> Option<usize> {
349        match self {
350            Self::Sequence(seq) => Some(seq.len()),
351            Self::Mapping(map) => Some(map.len()),
352            _ => None,
353        }
354    }
355
356    /// Check if sequences, mappings, or strings are empty
357    pub fn is_empty(&self) -> bool {
358        match self {
359            Self::Sequence(seq) => seq.is_empty(),
360            Self::Mapping(map) => map.is_empty(),
361            Self::String(s) => s.is_empty(),
362            _ => false,
363        }
364    }
365
366    /// Get this value as a boolean, if possible
367    pub const fn as_bool(&self) -> Option<bool> {
368        match self {
369            Self::Bool(b) => Some(*b),
370            _ => None,
371        }
372    }
373
374    /// Get this value as an integer, if possible
375    pub const fn as_int(&self) -> Option<i64> {
376        match self {
377            Self::Int(i) => Some(*i),
378            _ => None,
379        }
380    }
381
382    /// Get this value as a float, if possible
383    pub const fn as_float(&self) -> Option<f64> {
384        match self {
385            Self::Float(f) => Some(*f),
386            Self::Int(i) => Some(*i as f64),
387            _ => None,
388        }
389    }
390
391    /// Get this value as a string reference, if possible
392    pub fn as_str(&self) -> Option<&str> {
393        match self {
394            Self::String(s) => Some(s),
395            _ => None,
396        }
397    }
398
399    /// Get this value as a sequence reference, if possible
400    pub const fn as_sequence(&self) -> Option<&Vec<Self>> {
401        match self {
402            Self::Sequence(seq) => Some(seq),
403            _ => None,
404        }
405    }
406
407    /// Get this value as a mutable sequence reference, if possible
408    pub const fn as_sequence_mut(&mut self) -> Option<&mut Vec<Self>> {
409        match self {
410            Self::Sequence(seq) => Some(seq),
411            _ => None,
412        }
413    }
414
415    /// Get this value as a mapping reference, if possible
416    pub const fn as_mapping(&self) -> Option<&IndexMap<Self, Self>> {
417        match self {
418            Self::Mapping(map) => Some(map),
419            _ => None,
420        }
421    }
422
423    /// Get this value as a mutable mapping reference, if possible
424    pub const fn as_mapping_mut(&mut self) -> Option<&mut IndexMap<Self, Self>> {
425        match self {
426            Self::Mapping(map) => Some(map),
427            _ => None,
428        }
429    }
430
431    /// Index into a sequence or mapping
432    pub fn get(&self, index: &Self) -> Option<&Self> {
433        match (self, index) {
434            (Self::Sequence(seq), Self::Int(i)) => {
435                if *i >= 0 && (*i as usize) < seq.len() {
436                    seq.get(*i as usize)
437                } else {
438                    None
439                }
440            }
441            (Self::Mapping(map), key) => map.get(key),
442            _ => None,
443        }
444    }
445
446    /// Convenience method to get a value by string key
447    pub fn get_str(&self, key: &str) -> Option<&Self> {
448        match self {
449            Self::Mapping(map) => map.get(&Self::String(key.to_string())),
450            _ => None,
451        }
452    }
453
454    /// Get a value by numeric index (for sequences)
455    pub fn get_index(&self, index: usize) -> Option<&Self> {
456        match self {
457            Self::Sequence(seq) => seq.get(index),
458            _ => None,
459        }
460    }
461
462    /// Mutably index into a sequence or mapping
463    pub fn get_mut(&mut self, index: &Self) -> Option<&mut Self> {
464        match (self, index) {
465            (Self::Sequence(seq), Self::Int(i)) => {
466                if *i >= 0 && (*i as usize) < seq.len() {
467                    seq.get_mut(*i as usize)
468                } else {
469                    None
470                }
471            }
472            (Self::Mapping(map), key) => map.get_mut(key),
473            _ => None,
474        }
475    }
476}
477
478// Implement PartialEq manually to handle NaN in floats
479impl PartialEq for Value {
480    fn eq(&self, other: &Self) -> bool {
481        match (self, other) {
482            (Value::Null, Value::Null) => true,
483            (Value::Bool(a), Value::Bool(b)) => a == b,
484            (Value::Int(a), Value::Int(b)) => a == b,
485            (Value::Float(a), Value::Float(b)) => {
486                // Special handling for NaN - all NaN values are considered equal for consistency with Hash
487                if a.is_nan() && b.is_nan() {
488                    true
489                } else {
490                    a == b
491                }
492            }
493            (Value::String(a), Value::String(b)) => a == b,
494            (Value::Sequence(a), Value::Sequence(b)) => a == b,
495            (Value::Mapping(a), Value::Mapping(b)) => a == b,
496            _ => false,
497        }
498    }
499}
500
501// Implement Eq - safe because we handle NaN consistently
502impl Eq for Value {}
503
504impl Hash for Value {
505    fn hash<H: Hasher>(&self, state: &mut H) {
506        match self {
507            Self::Null => 0u8.hash(state),
508            Self::Bool(b) => {
509                1u8.hash(state);
510                b.hash(state);
511            }
512            Self::Int(i) => {
513                2u8.hash(state);
514                i.hash(state);
515            }
516            Self::Float(f) => {
517                3u8.hash(state);
518                // Handle NaN and negative zero
519                if f.is_nan() {
520                    u64::MAX.hash(state);
521                } else if *f == 0.0 {
522                    0u64.hash(state);
523                } else {
524                    f.to_bits().hash(state);
525                }
526            }
527            Self::String(s) => {
528                4u8.hash(state);
529                s.hash(state);
530            }
531            Self::Sequence(seq) => {
532                5u8.hash(state);
533                seq.hash(state);
534            }
535            Self::Mapping(map) => {
536                6u8.hash(state);
537                // Hash all key-value pairs
538                for (k, v) in map.iter() {
539                    k.hash(state);
540                    v.hash(state);
541                }
542            }
543        }
544    }
545}
546
547impl fmt::Display for Value {
548    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
549        match self {
550            Self::Null => write!(f, "null"),
551            Self::Bool(b) => write!(f, "{}", b),
552            Self::Int(i) => write!(f, "{}", i),
553            Self::Float(fl) => write!(f, "{}", fl),
554            Self::String(s) => write!(f, "\"{}\"", s),
555            Self::Sequence(seq) => {
556                write!(f, "[")?;
557                for (i, item) in seq.iter().enumerate() {
558                    if i > 0 {
559                        write!(f, ", ")?;
560                    }
561                    write!(f, "{}", item)?;
562                }
563                write!(f, "]")
564            }
565            Self::Mapping(map) => {
566                write!(f, "{{")?;
567                for (i, (key, value)) in map.iter().enumerate() {
568                    if i > 0 {
569                        write!(f, ", ")?;
570                    }
571                    write!(f, "{}: {}", key, value)?;
572                }
573                write!(f, "}}")
574            }
575        }
576    }
577}
578
579// Conversions from primitive types
580impl From<()> for Value {
581    fn from(_: ()) -> Self {
582        Self::Null
583    }
584}
585
586impl From<bool> for Value {
587    fn from(b: bool) -> Self {
588        Self::Bool(b)
589    }
590}
591
592impl From<i64> for Value {
593    fn from(i: i64) -> Self {
594        Self::Int(i)
595    }
596}
597
598impl From<i32> for Value {
599    fn from(i: i32) -> Self {
600        Self::Int(i64::from(i))
601    }
602}
603
604impl From<f64> for Value {
605    fn from(f: f64) -> Self {
606        Self::Float(f)
607    }
608}
609
610impl From<f32> for Value {
611    fn from(f: f32) -> Self {
612        Self::Float(f64::from(f))
613    }
614}
615
616impl From<String> for Value {
617    fn from(s: String) -> Self {
618        Self::String(s)
619    }
620}
621
622impl From<&str> for Value {
623    fn from(s: &str) -> Self {
624        Self::String(s.to_string())
625    }
626}
627
628impl From<Vec<Self>> for Value {
629    fn from(seq: Vec<Self>) -> Self {
630        Self::Sequence(seq)
631    }
632}
633
634impl From<IndexMap<Self, Self>> for Value {
635    fn from(map: IndexMap<Self, Self>) -> Self {
636        Self::Mapping(map)
637    }
638}
639
640#[cfg(feature = "serde")]
641impl serde::Serialize for Value {
642    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
643    where
644        S: serde::Serializer,
645    {
646        match self {
647            Self::Null => serializer.serialize_none(),
648            Self::Bool(b) => serializer.serialize_bool(*b),
649            Self::Int(i) => serializer.serialize_i64(*i),
650            Self::Float(f) => serializer.serialize_f64(*f),
651            Self::String(s) => serializer.serialize_str(s),
652            Self::Sequence(seq) => seq.serialize(serializer),
653            Self::Mapping(map) => map.serialize(serializer),
654        }
655    }
656}
657
658#[cfg(test)]
659mod tests {
660    use super::*;
661
662    #[test]
663    fn test_value_creation() {
664        assert_eq!(Value::null(), Value::Null);
665        assert_eq!(Value::bool(true), Value::Bool(true));
666        assert_eq!(Value::int(42), Value::Int(42));
667        assert_eq!(Value::float(3.14), Value::Float(3.14));
668        assert_eq!(Value::string("hello"), Value::String("hello".to_string()));
669    }
670
671    #[test]
672    fn test_value_type_checks() {
673        let null_val = Value::null();
674        let bool_val = Value::bool(true);
675        let int_val = Value::int(42);
676        let string_val = Value::string("test");
677
678        assert!(null_val.is_null());
679        assert!(bool_val.is_bool());
680        assert!(int_val.is_int());
681        assert!(string_val.is_string());
682
683        assert!(!null_val.is_bool());
684        assert!(!bool_val.is_int());
685    }
686
687    #[test]
688    fn test_value_conversions() {
689        let bool_val = Value::bool(true);
690        let int_val = Value::int(42);
691        let float_val = Value::float(3.14);
692        let string_val = Value::string("hello");
693
694        assert_eq!(bool_val.as_bool(), Some(true));
695        assert_eq!(int_val.as_int(), Some(42));
696        assert_eq!(float_val.as_float(), Some(3.14));
697        assert_eq!(string_val.as_str(), Some("hello"));
698
699        assert_eq!(bool_val.as_int(), None);
700        assert_eq!(int_val.as_bool(), None);
701    }
702
703    #[test]
704    fn test_sequence_operations() {
705        let mut seq = Value::sequence();
706        if let Value::Sequence(ref mut v) = seq {
707            v.push(Value::int(1));
708            v.push(Value::string("hello"));
709        }
710
711        assert!(seq.is_sequence());
712        assert_eq!(seq.as_sequence().unwrap().len(), 2);
713
714        let index = Value::int(0);
715        assert_eq!(seq.get(&index), Some(&Value::int(1)));
716    }
717
718    #[test]
719    fn test_mapping_operations() {
720        let mut map = Value::mapping();
721        if let Value::Mapping(ref mut m) = map {
722            m.insert(Value::string("key"), Value::int(42));
723            m.insert(Value::string("name"), Value::string("test"));
724        }
725
726        assert!(map.is_mapping());
727        assert_eq!(map.as_mapping().unwrap().len(), 2);
728
729        let key = Value::string("key");
730        assert_eq!(map.get(&key), Some(&Value::int(42)));
731    }
732
733    #[test]
734    fn test_value_display() {
735        assert_eq!(format!("{}", Value::null()), "null");
736        assert_eq!(format!("{}", Value::bool(true)), "true");
737        assert_eq!(format!("{}", Value::int(42)), "42");
738        assert_eq!(format!("{}", Value::string("hello")), "\"hello\"");
739    }
740
741    #[test]
742    fn test_value_equality() {
743        assert_eq!(Value::int(42), Value::int(42));
744        assert_eq!(Value::float(3.14), Value::float(3.14));
745        assert_ne!(Value::int(42), Value::float(42.0));
746
747        // Test NaN handling - NaN values are considered equal for consistency with Hash
748        let nan1 = Value::float(f64::NAN);
749        let nan2 = Value::float(f64::NAN);
750        assert_eq!(nan1, nan2); // NaN == NaN for consistency with Hash implementation
751    }
752
753    #[test]
754    fn test_conversions_from_primitives() {
755        assert_eq!(Value::from(true), Value::Bool(true));
756        assert_eq!(Value::from(42i32), Value::Int(42));
757        assert_eq!(Value::from(42i64), Value::Int(42));
758        assert_eq!(Value::from(3.14f64), Value::Float(3.14));
759        assert_eq!(Value::from("hello"), Value::String("hello".to_string()));
760        assert_eq!(
761            Value::from("hello".to_string()),
762            Value::String("hello".to_string())
763        );
764    }
765}