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}