Skip to main content

frontmatter_gen/
types.rs

1// types.rs
2
3//! This module defines the core types used throughout the frontmatter-gen crate.
4//! It includes the `Format` enum for representing different frontmatter formats, the `Value` enum for representing various data types that can be stored in frontmatter, and the `Frontmatter` struct which is the main container for frontmatter data.
5
6use serde::{Deserialize, Serialize};
7use std::{
8    collections::{BTreeMap, HashMap},
9    fmt,
10    str::FromStr,
11};
12
13/// Represents the different formats supported for frontmatter serialization/deserialization.
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15#[non_exhaustive]
16pub enum Format {
17    /// YAML format.
18    Yaml,
19    /// TOML format.
20    Toml,
21    /// JSON format.
22    Json,
23    /// Unsupported format.
24    Unsupported,
25}
26
27impl Default for Format {
28    fn default() -> Self {
29        Format::Json
30    }
31}
32
33impl fmt::Display for Format {
34    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35        let format_str = match self {
36            Format::Yaml => "YAML",
37            Format::Toml => "TOML",
38            Format::Json => "JSON",
39            Format::Unsupported => "Unsupported",
40        };
41        write!(f, "{}", format_str)
42    }
43}
44
45/// A flexible value type that can hold various types of data found in frontmatter.
46#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
47#[serde(untagged)]
48pub enum Value {
49    /// Represents a null value.
50    Null,
51    /// Represents a string value.
52    String(String),
53    /// Represents a numeric value.
54    Number(f64),
55    /// Represents a boolean value.
56    Boolean(bool),
57    /// Represents an array of values.
58    Array(Vec<Value>),
59    /// Represents an object (frontmatter).
60    Object(Box<Frontmatter>),
61    /// Represents a tagged value, containing a tag and a value.
62    Tagged(String, Box<Value>),
63}
64
65impl Value {
66    /// Returns the value as a string slice, if it is of type `String`.
67    ///
68    /// # Returns
69    ///
70    /// - `Some(&str)` if the value is a `String`.
71    /// - `None` if the value is not a `String`.
72    ///
73    /// # Examples
74    ///
75    /// ```
76    /// use frontmatter_gen::Value;
77    ///
78    /// let string_value = Value::String("Hello".to_string());
79    /// assert_eq!(string_value.as_str(), Some("Hello"));
80    ///
81    /// let number_value = Value::Number(42.0);
82    /// assert_eq!(number_value.as_str(), None);
83    /// ```
84    pub fn as_str(&self) -> Option<&str> {
85        if let Value::String(s) = self {
86            Some(s)
87        } else {
88            None
89        }
90    }
91
92    /// Returns the value as a float, if it is of type `Number`.
93    ///
94    /// # Returns
95    ///
96    /// - `Some(f64)` if the value is a `Number`.
97    /// - `None` if the value is not a `Number`.
98    ///
99    /// # Examples
100    ///
101    /// ```
102    /// use frontmatter_gen::Value;
103    ///
104    /// let number_value = Value::Number(3.14);
105    /// assert_eq!(number_value.as_f64(), Some(3.14));
106    ///
107    /// let string_value = Value::String("Not a number".to_string());
108    /// assert_eq!(string_value.as_f64(), None);
109    /// ```
110    pub const fn as_f64(&self) -> Option<f64> {
111        if let Value::Number(n) = self {
112            Some(*n)
113        } else {
114            None
115        }
116    }
117
118    /// Returns the value as a boolean, if it is of type `Boolean`.
119    ///
120    /// # Returns
121    ///
122    /// - `Some(bool)` if the value is a `Boolean`.
123    /// - `None` if the value is not a `Boolean`.
124    ///
125    /// # Examples
126    ///
127    /// ```
128    /// use frontmatter_gen::Value;
129    ///
130    /// let bool_value = Value::Boolean(true);
131    /// assert_eq!(bool_value.as_bool(), Some(true));
132    ///
133    /// let string_value = Value::String("Not a boolean".to_string());
134    /// assert_eq!(string_value.as_bool(), None);
135    /// ```
136    pub const fn as_bool(&self) -> Option<bool> {
137        if let Value::Boolean(b) = self {
138            Some(*b)
139        } else {
140            None
141        }
142    }
143
144    /// Returns the value as an array, if it is of type `Array`.
145    ///
146    /// # Returns
147    ///
148    /// - `Some(&Vec<Value>)` if the value is an `Array`.
149    /// - `None` if the value is not an `Array`.
150    ///
151    /// # Examples
152    ///
153    /// ```
154    /// use frontmatter_gen::Value;
155    ///
156    /// let array_value = Value::Array(vec![Value::Number(1.0), Value::Number(2.0)]);
157    /// assert!(array_value.as_array().is_some());
158    /// assert_eq!(array_value.as_array().unwrap().len(), 2);
159    ///
160    /// let string_value = Value::String("Not an array".to_string());
161    /// assert!(string_value.as_array().is_none());
162    /// ```
163    pub const fn as_array(&self) -> Option<&Vec<Value>> {
164        if let Value::Array(arr) = self {
165            Some(arr)
166        } else {
167            None
168        }
169    }
170
171    /// Returns the value as an object (frontmatter), if it is of type `Object`.
172    ///
173    /// # Returns
174    ///
175    /// - `Some(&Frontmatter)` if the value is an `Object`.
176    /// - `None` if the value is not an `Object`.
177    ///
178    /// # Examples
179    ///
180    /// ```
181    /// use frontmatter_gen::{Value, Frontmatter};
182    ///
183    /// let mut fm = Frontmatter::new();
184    /// fm.insert("key".to_string(), Value::String("value".to_string()));
185    /// let object_value = Value::Object(Box::new(fm));
186    /// assert!(object_value.as_object().is_some());
187    ///
188    /// let string_value = Value::String("Not an object".to_string());
189    /// assert!(string_value.as_object().is_none());
190    /// ```
191    pub fn as_object(&self) -> Option<&Frontmatter> {
192        if let Value::Object(obj) = self {
193            Some(obj)
194        } else {
195            None
196        }
197    }
198
199    /// Returns the value as a tagged value, if it is of type `Tagged`.
200    ///
201    /// # Returns
202    ///
203    /// - `Some((&str, &Value))` if the value is `Tagged`.
204    /// - `None` if the value is not `Tagged`.
205    ///
206    /// # Examples
207    ///
208    /// ```
209    /// use frontmatter_gen::Value;
210    ///
211    /// let tagged_value = Value::Tagged("tag".to_string(), Box::new(Value::Number(42.0)));
212    /// assert_eq!(tagged_value.as_tagged(), Some(("tag", &Value::Number(42.0))));
213    ///
214    /// let string_value = Value::String("Not tagged".to_string());
215    /// assert_eq!(string_value.as_tagged(), None);
216    /// ```
217    pub fn as_tagged(&self) -> Option<(&str, &Value)> {
218        if let Value::Tagged(tag, val) = self {
219            Some((tag, val))
220        } else {
221            None
222        }
223    }
224
225    /// Checks if the value is of type `Null`.
226    ///
227    /// # Returns
228    ///
229    /// `true` if the value is `Null`, otherwise `false`.
230    ///
231    /// # Examples
232    ///
233    /// ```
234    /// use frontmatter_gen::Value;
235    ///
236    /// let null_value = Value::Null;
237    /// assert!(null_value.is_null());
238    ///
239    /// let string_value = Value::String("Not null".to_string());
240    /// assert!(!string_value.is_null());
241    /// ```
242    pub const fn is_null(&self) -> bool {
243        matches!(self, Value::Null)
244    }
245
246    /// Checks if the value is of type `String`.
247    ///
248    /// # Returns
249    ///
250    /// `true` if the value is a `String`, otherwise `false`.
251    ///
252    /// # Examples
253    ///
254    /// ```
255    /// use frontmatter_gen::Value;
256    ///
257    /// let string_value = Value::String("Hello".to_string());
258    /// assert!(string_value.is_string());
259    ///
260    /// let number_value = Value::Number(42.0);
261    /// assert!(!number_value.is_string());
262    /// ```
263    pub const fn is_string(&self) -> bool {
264        matches!(self, Value::String(_))
265    }
266
267    /// Checks if the value is of type `Number`.
268    ///
269    /// # Returns
270    ///
271    /// `true` if the value is a `Number`, otherwise `false`.
272    ///
273    /// # Examples
274    ///
275    /// ```
276    /// use frontmatter_gen::Value;
277    ///
278    /// let number_value = Value::Number(3.14);
279    /// assert!(number_value.is_number());
280    ///
281    /// let string_value = Value::String("Not a number".to_string());
282    /// assert!(!string_value.is_number());
283    /// ```
284    pub const fn is_number(&self) -> bool {
285        matches!(self, Value::Number(_))
286    }
287
288    /// Checks if the value is of type `Boolean`.
289    ///
290    /// # Returns
291    ///
292    /// `true` if the value is a `Boolean`, otherwise `false`.
293    ///
294    /// # Examples
295    ///
296    /// ```
297    /// use frontmatter_gen::Value;
298    ///
299    /// let bool_value = Value::Boolean(true);
300    /// assert!(bool_value.is_boolean());
301    ///
302    /// let string_value = Value::String("Not a boolean".to_string());
303    /// assert!(!string_value.is_boolean());
304    /// ```
305    pub const fn is_boolean(&self) -> bool {
306        matches!(self, Value::Boolean(_))
307    }
308
309    /// Checks if the value is of type `Array`.
310    ///
311    /// # Returns
312    ///
313    /// `true` if the value is an `Array`, otherwise `false`.
314    ///
315    /// # Examples
316    ///
317    /// ```
318    /// use frontmatter_gen::Value;
319    ///
320    /// let array_value = Value::Array(vec![Value::Number(1.0), Value::Number(2.0)]);
321    /// assert!(array_value.is_array());
322    ///
323    /// let string_value = Value::String("Not an array".to_string());
324    /// assert!(!string_value.is_array());
325    /// ```
326    pub const fn is_array(&self) -> bool {
327        matches!(self, Value::Array(_))
328    }
329
330    /// Checks if the value is of type `Object`.
331    ///
332    /// # Returns
333    ///
334    /// `true` if the value is an `Object`, otherwise `false`.
335    ///
336    /// # Examples
337    ///
338    /// ```
339    /// use frontmatter_gen::{Value, Frontmatter};
340    ///
341    /// let object_value = Value::Object(Box::new(Frontmatter::new()));
342    /// assert!(object_value.is_object());
343    ///
344    /// let string_value = Value::String("Not an object".to_string());
345    /// assert!(!string_value.is_object());
346    /// ```
347    pub const fn is_object(&self) -> bool {
348        matches!(self, Value::Object(_))
349    }
350
351    /// Checks if the value is of type `Tagged`.
352    ///
353    /// # Returns
354    ///
355    /// `true` if the value is `Tagged`, otherwise `false`.
356    ///
357    /// # Examples
358    ///
359    /// ```
360    /// use frontmatter_gen::Value;
361    ///
362    /// let tagged_value = Value::Tagged("tag".to_string(), Box::new(Value::Number(42.0)));
363    /// assert!(tagged_value.is_tagged());
364    ///
365    /// let string_value = Value::String("Not tagged".to_string());
366    /// assert!(!string_value.is_tagged());
367    /// ```
368    pub const fn is_tagged(&self) -> bool {
369        matches!(self, Value::Tagged(_, _))
370    }
371
372    /// Returns the length of the array if the value is an array, otherwise returns `None`.
373    ///
374    /// # Returns
375    ///
376    /// - `Some(usize)` with the length of the array if the value is an `Array`.
377    /// - `None` if the value is not an `Array`.
378    ///
379    /// # Examples
380    ///
381    /// ```
382    /// use frontmatter_gen::Value;
383    ///
384    /// let array_value = Value::Array(vec![Value::Number(1.0), Value::Number(2.0)]);
385    /// assert_eq!(array_value.array_len(), Some(2));
386    ///
387    /// let string_value = Value::String("Not an array".to_string());
388    /// assert_eq!(string_value.array_len(), None);
389    /// ```
390    pub fn array_len(&self) -> Option<usize> {
391        if let Value::Array(arr) = self {
392            Some(arr.len())
393        } else {
394            None
395        }
396    }
397
398    /// Attempts to convert the value into a `Frontmatter`.
399    ///
400    /// # Returns
401    ///
402    /// - `Ok(Frontmatter)` if the value is an `Object`.
403    /// - `Err(String)` with an error message if the value is not an `Object`.
404    ///
405    /// # Examples
406    ///
407    /// ```
408    /// use frontmatter_gen::{Value, Frontmatter};
409    ///
410    /// let object_value = Value::Object(Box::new(Frontmatter::new()));
411    /// assert!(object_value.to_object().is_ok());
412    ///
413    /// let string_value = Value::String("Not an object".to_string());
414    /// assert!(string_value.to_object().is_err());
415    /// ```
416    pub fn to_object(self) -> Result<Frontmatter, String> {
417        if let Value::Object(obj) = self {
418            Ok(*obj)
419        } else {
420            Err("Value is not an object".into())
421        }
422    }
423
424    /// Converts the value to a string representation regardless of its type.
425    ///
426    /// # Returns
427    ///
428    /// A `String` representation of the value.
429    ///
430    /// # Examples
431    ///
432    /// ```
433    /// use frontmatter_gen::Value;
434    ///
435    /// let number_value = Value::Number(3.14);
436    /// assert_eq!(number_value.to_string_representation(), "3.14");
437    ///
438    /// let string_value = Value::String("Hello".to_string());
439    /// assert_eq!(string_value.to_string_representation(), "\"Hello\"");
440    /// ```
441    pub fn to_string_representation(&self) -> String {
442        format!("{}", self)
443    }
444
445    /// Attempts to convert the value into a `String`.
446    ///
447    /// # Returns
448    ///
449    /// - `Ok(String)` if the value is a `String`.
450    /// - `Err(String)` with an error message if the value is not a `String`.
451    ///
452    /// # Examples
453    ///
454    /// ```
455    /// use frontmatter_gen::Value;
456    ///
457    /// let string_value = Value::String("Hello".to_string());
458    /// assert_eq!(string_value.into_string(), Ok("Hello".to_string()));
459    ///
460    /// let number_value = Value::Number(42.0);
461    /// assert!(number_value.into_string().is_err());
462    /// ```
463    pub fn into_string(self) -> Result<String, String> {
464        if let Value::String(s) = self {
465            Ok(s)
466        } else {
467            Err("Value is not a string".into())
468        }
469    }
470
471    /// Attempts to convert the value into an `f64`.
472    ///
473    /// # Returns
474    ///
475    /// - `Ok(f64)` if the value is a `Number`.
476    /// - `Err(String)` with an error message if the value is not a `Number`.
477    ///
478    /// # Examples
479    ///
480    /// ```
481    /// use frontmatter_gen::Value;
482    ///
483    /// let number_value = Value::Number(3.14);
484    /// assert_eq!(number_value.into_f64(), Ok(3.14));
485    ///
486    /// let string_value = Value::String("Not a number".to_string());
487    /// assert!(string_value.into_f64().is_err());
488    /// ```
489    pub fn into_f64(self) -> Result<f64, String> {
490        if let Value::Number(n) = self {
491            Ok(n)
492        } else {
493            Err("Value is not a number".into())
494        }
495    }
496
497    /// Attempts to convert the value into a `bool`.
498    ///
499    /// # Returns
500    ///
501    /// - `Ok(bool)` if the value is a `Boolean`.
502    /// - `Err(String)` with an error message if the value is not a `Boolean`.
503    ///
504    /// # Examples
505    ///
506    /// ```
507    /// use frontmatter_gen::Value;
508    ///
509    /// let bool_value = Value::Boolean(true);
510    /// assert_eq!(bool_value.into_bool(), Ok(true));
511    ///
512    /// let string_value = Value::String("Not a boolean".to_string());
513    /// assert!(string_value.into_bool().is_err());
514    /// ```
515    pub fn into_bool(self) -> Result<bool, String> {
516        if let Value::Boolean(b) = self {
517            Ok(b)
518        } else {
519            Err("Value is not a boolean".into())
520        }
521    }
522
523    /// Attempts to get a mutable reference to the array if the value is an array.
524    ///
525    /// # Returns
526    ///
527    /// - `Some(&mut Vec<Value>)` if the value is an `Array`.
528    /// - `None` if the value is not an `Array`.
529    ///
530    /// # Examples
531    ///
532    /// ```
533    /// use frontmatter_gen::Value;
534    ///
535    /// let mut array_value = Value::Array(vec![Value::Number(1.0), Value::Number(2.0)]);
536    /// if let Some(arr) = array_value.get_mut_array() {
537    ///     arr.push(Value::Number(3.0));
538    /// }
539    /// assert_eq!(array_value.array_len(), Some(3));
540    ///
541    /// let mut string_value = Value::String("Not an array".to_string());
542    /// assert!(string_value.get_mut_array().is_none());
543    /// ```
544    pub fn get_mut_array(&mut self) -> Option<&mut Vec<Value>> {
545        if let Value::Array(arr) = self {
546            Some(arr)
547        } else {
548            None
549        }
550    }
551}
552
553impl Default for Value {
554    fn default() -> Self {
555        Value::Null
556    }
557}
558
559impl From<&str> for Value {
560    fn from(s: &str) -> Self {
561        Value::String(s.to_string())
562    }
563}
564
565impl From<String> for Value {
566    fn from(s: String) -> Self {
567        Value::String(s)
568    }
569}
570
571impl From<f64> for Value {
572    fn from(n: f64) -> Self {
573        Value::Number(n)
574    }
575}
576
577impl From<bool> for Value {
578    fn from(b: bool) -> Self {
579        Value::Boolean(b)
580    }
581}
582
583impl FromIterator<Value> for Value {
584    fn from_iter<I: IntoIterator<Item = Value>>(iter: I) -> Self {
585        Value::Array(iter.into_iter().collect())
586    }
587}
588
589impl FromStr for Value {
590    type Err = &'static str;
591
592    fn from_str(s: &str) -> Result<Self, Self::Err> {
593        if s.eq_ignore_ascii_case("null") {
594            Ok(Value::Null)
595        } else if s.eq_ignore_ascii_case("true") {
596            Ok(Value::Boolean(true))
597        } else if s.eq_ignore_ascii_case("false") {
598            Ok(Value::Boolean(false))
599        } else if let Ok(n) = s.parse::<f64>() {
600            Ok(Value::Number(n))
601        } else {
602            Ok(Value::String(s.to_string()))
603        }
604    }
605}
606
607/// Represents the frontmatter, a collection of key-value pairs.
608#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
609pub struct Frontmatter(pub HashMap<String, Value>);
610
611impl Frontmatter {
612    /// Creates a new, empty frontmatter.
613    ///
614    /// # Returns
615    ///
616    /// A new `Frontmatter` instance with no key-value pairs.
617    ///
618    /// # Examples
619    ///
620    /// ```
621    /// use frontmatter_gen::Frontmatter;
622    ///
623    /// let fm = Frontmatter::new();
624    /// assert!(fm.is_empty());
625    /// ```
626    #[must_use]
627    pub fn new() -> Self {
628        Frontmatter(HashMap::new())
629    }
630
631    /// Inserts a key-value pair into the frontmatter.
632    ///
633    /// # Arguments
634    ///
635    /// * `key` - The key for the entry.
636    /// * `value` - The value associated with the key.
637    ///
638    /// # Returns
639    ///
640    /// An option containing the old value if it was replaced.
641    ///
642    /// # Examples
643    ///
644    /// ```
645    /// use frontmatter_gen::{Frontmatter, Value};
646    ///
647    /// let mut fm = Frontmatter::new();
648    /// assert_eq!(fm.insert("key".to_string(), Value::String("value".to_string())), None);
649    /// assert_eq!(fm.insert("key".to_string(), Value::Number(42.0)), Some(Value::String("value".to_string())));
650    /// ```
651    pub fn insert(
652        &mut self,
653        key: String,
654        value: Value,
655    ) -> Option<Value> {
656        self.0.insert(key, value)
657    }
658
659    /// Retrieves a reference to a value associated with a key.
660    ///
661    /// # Arguments
662    ///
663    /// * `key` - The key to look up.
664    ///
665    /// # Returns
666    ///
667    /// An option containing a reference to the value if the key exists.
668    ///
669    /// # Examples
670    ///
671    /// ```
672    /// use frontmatter_gen::{Frontmatter, Value};
673    ///
674    /// let mut fm = Frontmatter::new();
675    /// fm.insert("key".to_string(), Value::String("value".to_string()));
676    /// assert_eq!(fm.get("key"), Some(&Value::String("value".to_string())));
677    /// assert_eq!(fm.get("nonexistent"), None);
678    /// ```
679    pub fn get(&self, key: &str) -> Option<&Value> {
680        self.0.get(key)
681    }
682
683    /// Retrieves a mutable reference to a value associated with a key.
684    ///
685    /// # Arguments
686    ///
687    /// * `key` - The key to look up.
688    ///
689    /// # Returns
690    ///
691    /// An option containing a mutable reference to the value if the key exists.
692    ///
693    /// # Examples
694    ///
695    /// ```
696    /// use frontmatter_gen::{Frontmatter, Value};
697    ///
698    /// let mut fm = Frontmatter::new();
699    /// fm.insert("key".to_string(), Value::String("value".to_string()));
700    /// if let Some(value) = fm.get_mut("key") {
701    ///     *value = Value::Number(42.0);
702    /// }
703    /// assert_eq!(fm.get("key"), Some(&Value::Number(42.0)));
704    /// ```
705    pub fn get_mut(&mut self, key: &str) -> Option<&mut Value> {
706        self.0.get_mut(key)
707    }
708
709    /// Removes a key-value pair from the frontmatter.
710    ///
711    /// # Arguments
712    ///
713    /// * `key` - The key to remove.
714    ///
715    /// # Returns
716    ///
717    /// An option containing the removed value if the key existed.
718    ///
719    /// # Examples
720    ///
721    /// ```
722    /// use frontmatter_gen::{Frontmatter, Value};
723    ///
724    /// let mut fm = Frontmatter::new();
725    /// fm.insert("key".to_string(), Value::String("value".to_string()));
726    /// assert_eq!(fm.remove("key"), Some(Value::String("value".to_string())));
727    /// assert_eq!(fm.remove("key"), None);
728    /// ```
729    pub fn remove(&mut self, key: &str) -> Option<Value> {
730        self.0.remove(key)
731    }
732
733    /// Checks if the frontmatter contains a given key.
734    ///
735    /// # Arguments
736    ///
737    /// * `key` - The key to check for.
738    ///
739    /// # Returns
740    ///
741    /// `true` if the key exists in the frontmatter, `false` otherwise.
742    ///
743    /// # Examples
744    ///
745    /// ```
746    /// use frontmatter_gen::{Frontmatter, Value};
747    ///
748    /// let mut fm = Frontmatter::new();
749    /// fm.insert("key".to_string(), Value::String("value".to_string()));
750    /// assert!(fm.contains_key("key"));
751    /// assert!(!fm.contains_key("nonexistent"));
752    /// ```
753    pub fn contains_key(&self, key: &str) -> bool {
754        self.0.contains_key(key)
755    }
756
757    /// Returns the number of entries in the frontmatter.
758    ///
759    /// # Returns
760    ///
761    /// The number of key-value pairs in the frontmatter.
762    ///
763    /// # Examples
764    ///
765    /// ```
766    /// use frontmatter_gen::{Frontmatter, Value};
767    ///
768    /// let mut fm = Frontmatter::new();
769    /// assert_eq!(fm.len(), 0);
770    /// fm.insert("key".to_string(), Value::String("value".to_string()));
771    /// assert_eq!(fm.len(), 1);
772    /// ```
773    pub fn len(&self) -> usize {
774        self.0.len()
775    }
776
777    /// Checks if the frontmatter is empty.
778    ///
779    /// # Returns
780    ///
781    /// `true` if the frontmatter contains no key-value pairs, `false` otherwise.
782    ///
783    /// # Examples
784    ///
785    /// ```
786    /// use frontmatter_gen::{Frontmatter, Value};
787    ///
788    /// let mut fm = Frontmatter::new();
789    /// assert!(fm.is_empty());
790    /// fm.insert("key".to_string(), Value::String("value".to_string()));
791    /// assert!(!fm.is_empty());
792    /// ```
793    pub fn is_empty(&self) -> bool {
794        self.0.is_empty()
795    }
796
797    /// Returns an iterator over the key-value pairs of the frontmatter.
798    ///
799    /// # Returns
800    ///
801    /// An iterator over references to the key-value pairs.
802    ///
803    /// # Examples
804    ///
805    /// ```
806    /// use frontmatter_gen::{Frontmatter, Value};
807    ///
808    /// let mut fm = Frontmatter::new();
809    /// fm.insert("key1".to_string(), Value::String("value1".to_string()));
810    /// fm.insert("key2".to_string(), Value::Number(42.0));
811    ///
812    /// for (key, value) in fm.iter() {
813    ///     println!("{}: {:?}", key, value);
814    /// }
815    /// ```
816    #[must_use]
817    pub fn iter(
818        &self,
819    ) -> std::collections::hash_map::Iter<'_, String, Value> {
820        self.0.iter()
821    }
822
823    /// Returns a mutable iterator over the key-value pairs of the frontmatter.
824    ///
825    /// # Returns
826    ///
827    /// A mutable iterator over references to the key-value pairs.
828    ///
829    /// # Examples
830    ///
831    /// ```
832    /// use frontmatter_gen::{Frontmatter, Value};
833    ///
834    /// let mut fm = Frontmatter::new();
835    /// fm.insert("key1".to_string(), Value::String("value1".to_string()));
836    /// fm.insert("key2".to_string(), Value::Number(42.0));
837    ///
838    /// for (_, value) in fm.iter_mut() {
839    ///     if let Value::Number(n) = value {
840    ///         *n += 1.0;
841    ///     }
842    /// }
843    ///
844    /// assert_eq!(fm.get("key2"), Some(&Value::Number(43.0)));
845    /// ```
846    pub fn iter_mut(
847        &mut self,
848    ) -> std::collections::hash_map::IterMut<'_, String, Value> {
849        self.0.iter_mut()
850    }
851
852    /// Merges another frontmatter into this one. If a key exists, it will be overwritten.
853    ///
854    /// # Arguments
855    ///
856    /// * `other` - The frontmatter to merge into this one.
857    ///
858    /// # Examples
859    ///
860    /// ```
861    /// use frontmatter_gen::{Frontmatter, Value};
862    ///
863    /// let mut fm1 = Frontmatter::new();
864    /// fm1.insert("key1".to_string(), Value::String("value1".to_string()));
865    ///
866    /// let mut fm2 = Frontmatter::new();
867    /// fm2.insert("key2".to_string(), Value::Number(42.0));
868    ///
869    /// fm1.merge(fm2);
870    /// assert_eq!(fm1.len(), 2);
871    /// assert_eq!(fm1.get("key2"), Some(&Value::Number(42.0)));
872    /// ```
873    pub fn merge(&mut self, other: Frontmatter) {
874        self.0.extend(other.0);
875    }
876
877    /// Checks if a given key exists and its value is `null`.
878    ///
879    /// # Arguments
880    ///
881    /// * `key` - The key to check.
882    ///
883    /// # Returns
884    ///
885    /// `true` if the key exists and its value is `null`, `false` otherwise.
886    ///
887    /// # Examples
888    ///
889    /// ```
890    /// use frontmatter_gen::{Frontmatter, Value};
891    ///
892    /// let mut fm = Frontmatter::new();
893    /// fm.insert("null_key".to_string(), Value::Null);
894    /// fm.insert("non_null_key".to_string(), Value::String("value".to_string()));
895    ///
896    /// assert!(fm.is_null("null_key"));
897    /// assert!(!fm.is_null("non_null_key"));
898    /// assert!(!fm.is_null("nonexistent_key"));
899    /// ```
900    pub fn is_null(&self, key: &str) -> bool {
901        matches!(self.get(key), Some(Value::Null))
902    }
903
904    /// Clears the frontmatter while preserving allocated capacity
905    pub fn clear(&mut self) {
906        self.0.clear();
907    }
908
909    /// Returns the current capacity of the underlying HashMap
910    pub fn capacity(&self) -> usize {
911        self.0.capacity()
912    }
913
914    /// Reserves capacity for at least `additional` more elements
915    pub fn reserve(&mut self, additional: usize) {
916        self.0.reserve(additional);
917    }
918}
919
920impl Default for Frontmatter {
921    fn default() -> Self {
922        Self(HashMap::with_capacity(8))
923    }
924}
925
926/// Implement `IntoIterator` for `Frontmatter` to allow idiomatic iteration.
927impl IntoIterator for Frontmatter {
928    type Item = (String, Value);
929    type IntoIter = std::collections::hash_map::IntoIter<String, Value>;
930
931    fn into_iter(self) -> Self::IntoIter {
932        self.0.into_iter()
933    }
934}
935
936/// Implement `FromIterator` for `Frontmatter` to create a frontmatter from an iterator.
937impl FromIterator<(String, Value)> for Frontmatter {
938    /// Creates a `Frontmatter` from an iterator of key-value pairs.
939    ///
940    /// # Arguments
941    ///
942    /// * `iter` - An iterator of key-value pairs where the key is a `String` and the value is a `Value`.
943    ///
944    /// # Returns
945    ///
946    /// A `Frontmatter` containing the key-value pairs from the iterator.
947    ///
948    /// # Examples
949    ///
950    /// ```
951    /// use frontmatter_gen::{Frontmatter, Value};
952    /// use std::iter::FromIterator;
953    ///
954    /// let pairs = vec![
955    ///     ("key1".to_string(), Value::String("value1".to_string())),
956    ///     ("key2".to_string(), Value::Number(42.0)),
957    /// ];
958    ///
959    /// let fm = Frontmatter::from_iter(pairs);
960    /// assert_eq!(fm.len(), 2);
961    /// assert_eq!(fm.get("key1"), Some(&Value::String("value1".to_string())));
962    /// assert_eq!(fm.get("key2"), Some(&Value::Number(42.0)));
963    /// ```
964    fn from_iter<I: IntoIterator<Item = (String, Value)>>(
965        iter: I,
966    ) -> Self {
967        let mut fm = Frontmatter::new();
968        for (key, value) in iter {
969            let _ = fm.insert(key, value);
970        }
971        fm
972    }
973}
974
975/// Implement `Display` for `Frontmatter` to allow easy printing with escaped characters.
976impl fmt::Display for Frontmatter {
977    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
978        write!(f, "{{")?;
979
980        // Use a BTreeMap to ensure consistent key order (sorted by key)
981        let mut sorted_map = BTreeMap::new();
982        for (key, value) in &self.0 {
983            let _ = sorted_map.insert(key, value);
984        }
985
986        for (i, (key, value)) in sorted_map.iter().enumerate() {
987            if i > 0 {
988                write!(f, ", ")?;
989            }
990            write!(f, "\"{}\": {}", escape_str(key), value)?;
991        }
992
993        write!(f, "}}")
994    }
995}
996
997/// Implement `Display` for `Value` to allow easy printing with escaped characters.
998impl fmt::Display for Value {
999    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1000        match self {
1001            Value::Null => write!(f, "null"),
1002            Value::String(s) => write!(f, "\"{}\"", escape_str(s)),
1003            Value::Number(n) => {
1004                if n.fract() == 0.0 {
1005                    write!(f, "{:.0}", n)
1006                } else {
1007                    write!(f, "{}", n)
1008                }
1009            }
1010            Value::Boolean(b) => write!(f, "{}", b),
1011            Value::Array(arr) => {
1012                write!(f, "[")?;
1013                for (i, v) in arr.iter().enumerate() {
1014                    if i > 0 {
1015                        write!(f, ", ")?;
1016                    }
1017                    write!(f, "{}", v)?;
1018                }
1019                write!(f, "]")
1020            }
1021            Value::Object(obj) => write!(f, "{}", obj),
1022            Value::Tagged(tag, val) => {
1023                write!(f, "\"{}\": {}", escape_str(tag), val)
1024            }
1025        }
1026    }
1027}
1028
1029/// Escapes special characters in a string (e.g., backslashes and quotes).
1030///
1031/// # Arguments
1032///
1033/// * `s` - The input string to escape.
1034///
1035/// # Returns
1036///
1037/// A new `String` with special characters escaped.
1038///
1039/// # Examples
1040///
1041/// ```
1042/// use frontmatter_gen::types::escape_str;
1043///
1044/// assert_eq!(escape_str(r#"Hello "World""#), r#"Hello \"World\""#);
1045/// assert_eq!(escape_str(r#"C:\path\to\file"#), r#"C:\\path\\to\\file"#);
1046/// ```
1047pub fn escape_str(s: &str) -> String {
1048    let mut escaped = String::with_capacity(s.len());
1049    for c in s.chars() {
1050        match c {
1051            '"' => escaped.push_str("\\\""),
1052            '\\' => escaped.push_str("\\\\"),
1053            _ => escaped.push(c),
1054        }
1055    }
1056    escaped
1057}
1058
1059#[cfg(test)]
1060mod tests {
1061    use super::*;
1062    use std::f64::consts::PI;
1063
1064    mod format_tests {
1065        use super::*;
1066
1067        #[test]
1068        fn test_format_default() {
1069            assert_eq!(Format::default(), Format::Json);
1070        }
1071    }
1072
1073    mod value_tests {
1074        use super::*;
1075
1076        #[test]
1077        fn test_value_default() {
1078            assert_eq!(Value::default(), Value::Null);
1079        }
1080
1081        #[test]
1082        fn test_value_as_str() {
1083            let value = Value::String("Hello".to_string());
1084            assert_eq!(value.as_str(), Some("Hello"));
1085
1086            let value = Value::Number(42.0);
1087            assert_eq!(value.as_str(), None);
1088        }
1089
1090        #[test]
1091        fn test_value_as_f64() {
1092            let value = Value::Number(42.0);
1093            assert_eq!(value.as_f64(), Some(42.0));
1094
1095            let value = Value::String("Not a number".to_string());
1096            assert_eq!(value.as_f64(), None);
1097        }
1098
1099        #[test]
1100        fn test_value_as_bool() {
1101            let value = Value::Boolean(true);
1102            assert_eq!(value.as_bool(), Some(true));
1103
1104            let value = Value::String("Not a boolean".to_string());
1105            assert_eq!(value.as_bool(), None);
1106        }
1107
1108        #[test]
1109        fn test_value_is_null() {
1110            assert!(Value::Null.is_null());
1111            assert!(!Value::String("Not null".to_string()).is_null());
1112        }
1113
1114        #[test]
1115        fn test_value_is_string() {
1116            assert!(Value::String("test".to_string()).is_string());
1117            assert!(!Value::Number(42.0).is_string());
1118        }
1119
1120        #[test]
1121        fn test_value_is_number() {
1122            assert!(Value::Number(42.0).is_number());
1123            assert!(!Value::String("42".to_string()).is_number());
1124        }
1125
1126        #[test]
1127        fn test_value_is_boolean() {
1128            assert!(Value::Boolean(true).is_boolean());
1129            assert!(!Value::String("true".to_string()).is_boolean());
1130        }
1131
1132        #[test]
1133        fn test_value_as_array() {
1134            let value =
1135                Value::Array(vec![Value::Null, Value::Boolean(false)]);
1136            assert!(value.as_array().is_some());
1137            assert_eq!(value.as_array().unwrap().len(), 2);
1138
1139            assert!(Value::String("Not an array".to_string())
1140                .as_array()
1141                .is_none());
1142        }
1143
1144        #[test]
1145        fn test_value_as_object() {
1146            let mut fm = Frontmatter::new();
1147            let _ = fm.insert(
1148                "key".to_string(),
1149                Value::String("value".to_string()),
1150            );
1151            let value = Value::Object(Box::new(fm.clone()));
1152            assert_eq!(value.as_object().unwrap(), &fm);
1153
1154            assert!(Value::String("Not an object".to_string())
1155                .as_object()
1156                .is_none());
1157        }
1158
1159        #[test]
1160        fn test_value_to_object() {
1161            let fm = Frontmatter::new();
1162            let obj = Value::Object(Box::new(fm.clone()));
1163            assert_eq!(obj.to_object().unwrap(), fm);
1164
1165            assert!(Value::String("Not an object".to_string())
1166                .to_object()
1167                .is_err());
1168        }
1169
1170        #[test]
1171        fn test_value_to_string_representation() {
1172            assert_eq!(
1173                Value::String("test".to_string())
1174                    .to_string_representation(),
1175                "\"test\""
1176            );
1177            assert_eq!(
1178                Value::Number(42.0).to_string_representation(),
1179                "42"
1180            );
1181            assert_eq!(
1182                Value::Boolean(true).to_string_representation(),
1183                "true"
1184            );
1185        }
1186
1187        #[test]
1188        fn test_value_display() {
1189            assert_eq!(format!("{}", Value::Null), "null");
1190            assert_eq!(
1191                format!("{}", Value::String("test".to_string())),
1192                "\"test\""
1193            );
1194            assert_eq!(
1195                format!("{}", Value::Number(PI)),
1196                format!("{}", PI)
1197            );
1198            assert_eq!(format!("{}", Value::Boolean(true)), "true");
1199        }
1200    }
1201
1202    mod frontmatter_tests {
1203        use super::*;
1204
1205        #[test]
1206        fn test_frontmatter_new() {
1207            let fm = Frontmatter::new();
1208            assert!(fm.is_empty());
1209            assert_eq!(fm.len(), 0);
1210        }
1211
1212        #[test]
1213        fn test_frontmatter_insert_and_get() {
1214            let mut fm = Frontmatter::new();
1215            let _ = fm.insert(
1216                "title".to_string(),
1217                Value::String("Hello World".to_string()),
1218            );
1219
1220            assert_eq!(
1221                fm.get("title"),
1222                Some(&Value::String("Hello World".to_string()))
1223            );
1224        }
1225
1226        #[test]
1227        fn test_frontmatter_len_and_is_empty() {
1228            let mut fm = Frontmatter::new();
1229            assert!(fm.is_empty());
1230
1231            let _ = fm.insert("key1".to_string(), Value::Null);
1232            assert_eq!(fm.len(), 1);
1233            assert!(!fm.is_empty());
1234        }
1235
1236        #[test]
1237        fn test_frontmatter_merge() {
1238            let mut fm1 = Frontmatter::new();
1239            let _ = fm1.insert(
1240                "key1".to_string(),
1241                Value::String("value1".to_string()),
1242            );
1243
1244            let mut fm2 = Frontmatter::new();
1245            let _ = fm2.insert("key2".to_string(), Value::Number(42.0));
1246
1247            fm1.merge(fm2);
1248            assert_eq!(fm1.len(), 2);
1249            assert_eq!(fm1.get("key2"), Some(&Value::Number(42.0)));
1250        }
1251
1252        #[test]
1253        fn test_frontmatter_display() {
1254            let mut fm = Frontmatter::new();
1255            let _ = fm.insert(
1256                "key1".to_string(),
1257                Value::String("value1".to_string()),
1258            );
1259            let _ = fm.insert("key2".to_string(), Value::Number(42.0));
1260            let display = format!("{}", fm);
1261
1262            assert!(display.contains("\"key1\": \"value1\""));
1263            assert!(display.contains("\"key2\": 42"));
1264        }
1265
1266        #[test]
1267        fn test_frontmatter_is_null() {
1268            let mut fm = Frontmatter::new();
1269            let _ = fm.insert("key".to_string(), Value::Null);
1270
1271            assert!(fm.is_null("key"));
1272            assert!(!fm.is_null("nonexistent_key"));
1273        }
1274    }
1275
1276    mod utility_tests {
1277        use super::*;
1278
1279        #[test]
1280        fn test_escape_str() {
1281            assert_eq!(
1282                escape_str(r#"Hello "World""#),
1283                r#"Hello \"World\""#
1284            );
1285            assert_eq!(
1286                escape_str(r"C:\path\to\file"),
1287                r"C:\\path\\to\\file"
1288            );
1289        }
1290
1291        #[test]
1292        fn test_escape_str_empty() {
1293            assert_eq!(escape_str(""), "");
1294        }
1295    }
1296
1297    mod additional_tests {
1298        use super::*;
1299
1300        #[test]
1301        fn test_frontmatter_clear() {
1302            let mut fm = Frontmatter::new();
1303            let _ = fm.insert(
1304                "key1".to_string(),
1305                Value::String("value1".to_string()),
1306            );
1307            let _ = fm.insert("key2".to_string(), Value::Number(42.0));
1308
1309            fm.clear();
1310            assert!(fm.is_empty());
1311            assert_eq!(fm.len(), 0);
1312        }
1313
1314        #[test]
1315        fn test_frontmatter_capacity_and_reserve() {
1316            let mut fm = Frontmatter::new();
1317            let initial_capacity = fm.capacity();
1318
1319            fm.reserve(10);
1320            assert!(fm.capacity() >= initial_capacity + 10);
1321        }
1322
1323        #[test]
1324        fn test_value_tagged() {
1325            let tagged_value = Value::Tagged(
1326                "tag".to_string(),
1327                Box::new(Value::Number(42.0)),
1328            );
1329
1330            if let Value::Tagged(tag, value) = tagged_value {
1331                assert_eq!(tag, "tag");
1332                assert_eq!(*value, Value::Number(42.0));
1333            } else {
1334                panic!("Expected Value::Tagged");
1335            }
1336        }
1337
1338        #[test]
1339        fn test_value_array_mutation() {
1340            let mut value = Value::Array(vec![
1341                Value::Number(1.0),
1342                Value::Number(2.0),
1343            ]);
1344
1345            if let Some(array) = value.get_mut_array() {
1346                array.push(Value::Number(3.0));
1347            }
1348
1349            assert_eq!(value.array_len(), Some(3));
1350            assert!(value
1351                .as_array()
1352                .unwrap()
1353                .contains(&Value::Number(3.0)));
1354        }
1355
1356        #[test]
1357        fn test_value_conversion_errors() {
1358            let value = Value::Boolean(true);
1359            assert!(value.clone().into_f64().is_err());
1360            assert!(value.into_string().is_err());
1361
1362            let value = Value::Number(42.0);
1363            assert!(value.into_bool().is_err());
1364        }
1365
1366        #[test]
1367        fn test_value_from_str_error_handling() {
1368            assert_eq!("null".parse::<Value>().unwrap(), Value::Null);
1369            assert_eq!(
1370                "true".parse::<Value>().unwrap(),
1371                Value::Boolean(true)
1372            );
1373            assert_eq!(
1374                "false".parse::<Value>().unwrap(),
1375                Value::Boolean(false)
1376            );
1377
1378            let invalid_number = "abc123".parse::<Value>();
1379            assert!(invalid_number.is_ok()); // Treated as a string.
1380            assert_eq!(
1381                invalid_number.unwrap(),
1382                Value::String("abc123".to_string())
1383            );
1384        }
1385
1386        #[test]
1387        fn test_frontmatter_empty_iterator() {
1388            let fm = Frontmatter::new();
1389            let mut iter = fm.iter();
1390
1391            assert!(iter.next().is_none());
1392        }
1393
1394        #[test]
1395        fn test_frontmatter_duplicate_merge() {
1396            let mut fm1 = Frontmatter::new();
1397            let _ = fm1.insert(
1398                "key1".to_string(),
1399                Value::String("value1".to_string()),
1400            );
1401
1402            let mut fm2 = Frontmatter::new();
1403            let _ = fm2.insert(
1404                "key1".to_string(),
1405                Value::String("new_value".to_string()),
1406            );
1407
1408            fm1.merge(fm2);
1409
1410            assert_eq!(
1411                fm1.get("key1"),
1412                Some(&Value::String("new_value".to_string()))
1413            );
1414        }
1415
1416        #[test]
1417        fn test_display_for_empty_frontmatter() {
1418            let fm = Frontmatter::new();
1419            let display = format!("{}", fm);
1420            assert_eq!(display, "{}");
1421        }
1422
1423        #[test]
1424        fn test_value_from_iterator_empty() {
1425            let vec: Vec<Value> = vec![];
1426            let array_value: Value = vec.into_iter().collect();
1427            assert_eq!(array_value, Value::Array(vec![]));
1428        }
1429
1430        #[test]
1431        fn test_escape_str_edge_cases() {
1432            let special_chars = r"Special \chars\n\t";
1433            assert_eq!(
1434                escape_str(special_chars),
1435                r"Special \\chars\\n\\t"
1436            );
1437        }
1438    }
1439}