serde_yml/libyml/
tag.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
use crate::libyml::safe_cstr;
use memchr::memchr;
use std::{
    fmt::{self, Debug, Display},
    ops::Deref,
};

/// Custom error type for Tag operations.
#[derive(Clone, Copy, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)]
pub struct TagFormatError;

impl Display for TagFormatError {
    /// Formats the error message for display.
    ///
    /// # Arguments
    ///
    /// * `f` - The formatter to write the error message to.
    ///
    /// # Returns
    ///
    /// Returns `fmt::Result` indicating the success or failure of the operation.
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "Error occurred while formatting tag")
    }
}

impl std::error::Error for TagFormatError {}

/// Represents a tag in a YAML document.
/// A tag specifies the data type or semantic meaning of a value.
#[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Hash)]
pub struct Tag(pub(in crate::libyml) Box<[u8]>);

impl Tag {
    /// The null tag, representing a null value.
    pub const NULL: &'static str = "tag:yaml.org,2002:null";

    /// The bool tag, representing a boolean value.
    pub const BOOL: &'static str = "tag:yaml.org,2002:bool";

    /// The int tag, representing an integer value.
    pub const INT: &'static str = "tag:yaml.org,2002:int";

    /// The float tag, representing a floating-point value.
    pub const FLOAT: &'static str = "tag:yaml.org,2002:float";

    /// Checks if the tag starts with the given prefix.
    ///
    /// # Arguments
    ///
    /// * `prefix` - The prefix to check against.
    ///
    /// # Returns
    ///
    /// Returns `Ok(true)` if the tag starts with the given prefix, `Ok(false)` otherwise.
    /// Returns an error if the prefix is longer than the tag.
    ///
    /// # Errors
    ///
    /// Returns `TagFormatError` if the prefix length is greater than the tag length.
    pub fn starts_with(
        &self,
        prefix: &str,
    ) -> Result<bool, TagFormatError> {
        if prefix.len() > self.0.len() {
            Err(TagFormatError)
        } else {
            let prefix_bytes = prefix.as_bytes();
            let tag_bytes = &self.0[..prefix_bytes.len()];
            Ok(tag_bytes == prefix_bytes)
        }
    }

    /// Creates a new `Tag` instance from a `&str` input.
    ///
    /// # Arguments
    ///
    /// * `tag_str` - The string representing the tag.
    ///
    /// # Returns
    ///
    /// Returns a `Tag` instance representing the specified tag string.
    pub fn new(tag_str: &str) -> Tag {
        Tag(Box::from(tag_str.as_bytes()))
    }
}

impl PartialEq<str> for Tag {
    /// Checks if the tag is equal to the given string.
    ///
    /// # Arguments
    ///
    /// * `other` - The string to compare against.
    ///
    /// # Returns
    ///
    /// Returns `true` if the tag is equal to the given string, `false` otherwise.
    fn eq(&self, other: &str) -> bool {
        self.0 == other.as_bytes().into()
    }
}

impl PartialEq<&str> for Tag {
    /// Checks if the tag is equal to the given string slice.
    ///
    /// # Arguments
    ///
    /// * `other` - The string slice to compare against.
    ///
    /// # Returns
    ///
    /// Returns `true` if the tag is equal to the given string slice, `false` otherwise.
    fn eq(&self, other: &&str) -> bool {
        self.0 == other.as_bytes().into()
    }
}

impl Deref for Tag {
    type Target = [u8];

    /// Dereferences the tag to its underlying byte slice.
    ///
    /// # Returns
    ///
    /// Returns a reference to the underlying byte slice of the tag.
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl Debug for Tag {
    /// Formats the tag for debugging purposes.
    ///
    /// # Arguments
    ///
    /// * `formatter` - The formatter to write the debug output to.
    ///
    /// # Returns
    ///
    /// Returns `Ok(())` if the formatting was successful, or an error otherwise.
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        if let Some(null_pos) = memchr(b'\0', &self.0) {
            safe_cstr::debug_lossy(&self.0[..null_pos], formatter)
        } else {
            safe_cstr::debug_lossy(&self.0, formatter)
        }
    }
}