Coverage Report

Created: 2026-05-30 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/uuid-1.23.2/src/error.rs
Line
Count
Source
1
use crate::std::fmt;
2
3
/// A general error that can occur when working with UUIDs.
4
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
5
pub struct Error(pub(crate) ErrorKind);
6
7
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
8
pub(crate) enum ErrorKind {
9
    /// Invalid character in the [`Uuid`] string.
10
    ///
11
    /// [`Uuid`]: ../struct.Uuid.html
12
    ParseChar { character: char, index: usize },
13
    /// A byte array didn't contain 16 bytes.
14
    ParseByteLength { len: usize },
15
    /// A hyphenated [`Uuid`] didn't contain 5 groups
16
    ///
17
    /// [`Uuid`]: ../struct.Uuid.html
18
    ParseGroupCount { count: usize },
19
    /// A hyphenated [`Uuid`] had a group that wasn't the right length.
20
    ///
21
    /// [`Uuid`]: ../struct.Uuid.html
22
    ParseGroupLength {
23
        group: usize,
24
        len: usize,
25
        index: usize,
26
    },
27
    /// The input was not a valid UTF8 string.
28
    ParseInvalidUTF8,
29
    /// The input has an invalid length.
30
    ParseLength { len: usize },
31
    /// Some other parsing error occurred.
32
    ParseOther,
33
    /// The UUID is nil.
34
    Nil,
35
    /// A system time was invalid.
36
    #[cfg(feature = "std")]
37
    InvalidSystemTime(&'static str),
38
}
39
40
/// A string that is guaranteed to fail to parse to a [`Uuid`].
41
///
42
/// This type acts as a lightweight error indicator, suggesting
43
/// that the string cannot be parsed but offering no error
44
/// details. To get details, use `InvalidUuid::into_err`.
45
///
46
/// [`Uuid`]: ../struct.Uuid.html
47
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
48
pub(crate) struct InvalidUuid<'a>(pub(crate) &'a [u8], pub(crate) RequestedUuid);
49
50
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
51
pub(crate) enum RequestedUuid {
52
    Any,
53
    Simple,
54
    Hyphenated,
55
    Braced,
56
    Urn,
57
}
58
59
impl<'a> InvalidUuid<'a> {
60
    /// Converts the lightweight error type into detailed diagnostics.
61
0
    pub fn into_err(self) -> Error {
62
0
        if self.0.len() == 0 || self.0.len() > 45 {
63
            // Don't waste time looking at strings that may be enormous
64
0
            return Error(ErrorKind::ParseLength { len: self.0.len() });
65
0
        }
66
67
        // Check whether or not the input was ever actually a valid UTF8 string
68
0
        let input_str = match std::str::from_utf8(self.0) {
69
0
            Ok(s) => s,
70
0
            Err(_) => return Error(ErrorKind::ParseInvalidUTF8),
71
        };
72
73
0
        let (bounds, mut format) = match (self.1, self.0) {
74
0
            (RequestedUuid::Any | RequestedUuid::Braced, [b'{', s @ .., b'}']) => {
75
0
                (1..s.len() - 1, RequestedUuid::Braced)
76
            }
77
            (RequestedUuid::Braced, _) => {
78
0
                if self.0[0] != b'{' {
79
                    // The first character is invalid
80
0
                    let (index, character) = input_str.char_indices().next().unwrap();
81
82
0
                    return Error(ErrorKind::ParseChar { character, index });
83
                } else {
84
                    // The last character is invalid
85
0
                    let (index, character) = input_str.char_indices().last().unwrap();
86
87
0
                    return Error(ErrorKind::ParseChar { character, index });
88
                }
89
            }
90
            (
91
                RequestedUuid::Any | RequestedUuid::Urn,
92
0
                [b'u', b'r', b'n', b':', b'u', b'u', b'i', b'd', b':', s @ ..],
93
0
            ) => ("urn:uuid:".len()..s.len(), RequestedUuid::Urn),
94
            (RequestedUuid::Urn, _) => {
95
0
                return Error(ErrorKind::ParseChar {
96
0
                    character: input_str.chars().next().unwrap(),
97
0
                    index: 0,
98
0
                })
99
            }
100
0
            (r, s) => (0..s.len(), r),
101
        };
102
103
0
        let mut hyphen_count = 0;
104
0
        let mut group_bounds = [0; 4];
105
106
        // SAFETY: the byte array came from a valid utf8 string,
107
        // and is aligned along char boundaries.
108
0
        let uuid_str = unsafe { std::str::from_utf8_unchecked(self.0) };
109
110
0
        for (index, character) in uuid_str[bounds.clone()].char_indices() {
111
0
            let byte = character as u8;
112
113
0
            match (format, byte.to_ascii_lowercase()) {
114
0
                (_, b'0'..=b'9' | b'a'..=b'f') => (),
115
                (RequestedUuid::Simple, b'-') => {
116
0
                    return Error(ErrorKind::ParseChar {
117
0
                        character: '-',
118
0
                        index: index + bounds.start,
119
0
                    })
120
                }
121
                (_, b'-') => {
122
0
                    if format == RequestedUuid::Any {
123
0
                        format = RequestedUuid::Hyphenated;
124
0
                    }
125
126
0
                    if hyphen_count < 4 {
127
0
                        // While we search, also count group breaks
128
0
                        group_bounds[hyphen_count] = index;
129
0
                    }
130
0
                    hyphen_count += 1;
131
                }
132
                _ => {
133
0
                    return Error(ErrorKind::ParseChar {
134
0
                        character,
135
0
                        index: index + bounds.start,
136
0
                    })
137
                }
138
            }
139
        }
140
141
0
        if format == RequestedUuid::Any || format == RequestedUuid::Simple {
142
            // This means that we tried and failed to parse a simple uuid.
143
            // Since we verified that all the characters are valid, this means
144
            // that it MUST have an invalid length.
145
0
            Error(ErrorKind::ParseLength {
146
0
                len: input_str.len(),
147
0
            })
148
0
        } else if hyphen_count != 4 {
149
            // We tried to parse a hyphenated variant, but there weren't
150
            // 5 groups (4 hyphen splits).
151
0
            Error(ErrorKind::ParseGroupCount {
152
0
                count: hyphen_count + 1,
153
0
            })
154
        } else {
155
            // There are 5 groups, one of them has an incorrect length
156
            const BLOCK_STARTS: [usize; 5] = [0, 9, 14, 19, 24];
157
0
            for i in 0..4 {
158
0
                if group_bounds[i] != BLOCK_STARTS[i + 1] - 1 {
159
0
                    return Error(ErrorKind::ParseGroupLength {
160
0
                        group: i,
161
0
                        len: group_bounds[i] - BLOCK_STARTS[i],
162
0
                        index: bounds.start + BLOCK_STARTS[i] + 1,
163
0
                    });
164
0
                }
165
            }
166
167
            // The last group must be too long
168
0
            Error(ErrorKind::ParseGroupLength {
169
0
                group: 4,
170
0
                len: input_str.len() - BLOCK_STARTS[4],
171
0
                index: bounds.start + BLOCK_STARTS[4] + 1,
172
0
            })
173
        }
174
0
    }
175
}
176
177
// NOTE: This impl is part of the public API. Breaking changes to it should be carefully considered
178
impl fmt::Display for Error {
179
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
180
0
        match self.0 {
181
            ErrorKind::ParseChar {
182
0
                character, index, ..
183
            } => {
184
0
                write!(f, "invalid character: found `{}` at {}", character, index)
185
            }
186
0
            ErrorKind::ParseByteLength { len } => {
187
0
                write!(f, "invalid length: expected 16 bytes, found {}", len)
188
            }
189
0
            ErrorKind::ParseGroupCount { count } => {
190
0
                write!(f, "invalid group count: expected 5, found {}", count)
191
            }
192
0
            ErrorKind::ParseGroupLength { group, len, .. } => {
193
0
                let expected = [8, 4, 4, 4, 12][group];
194
0
                write!(
195
0
                    f,
196
0
                    "invalid group length in group {}: expected {}, found {}",
197
                    group, expected, len
198
                )
199
            }
200
0
            ErrorKind::ParseInvalidUTF8 => write!(f, "non-UTF8 input"),
201
0
            ErrorKind::Nil => write!(f, "the UUID is nil"),
202
0
            ErrorKind::ParseLength { len } => write!(f, "invalid length: found {}", len),
203
0
            ErrorKind::ParseOther => write!(f, "failed to parse a UUID"),
204
            #[cfg(feature = "std")]
205
0
            ErrorKind::InvalidSystemTime(ref e) => {
206
0
                write!(f, "the system timestamp is invalid: {e}")
207
            }
208
        }
209
0
    }
210
}
211
212
impl crate::std::error::Error for Error {}