Coverage Report

Created: 2026-06-27 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/uuid-1.23.4/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'{', .., b'}']) => {
75
0
                (1..self.0.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':', ..],
93
0
            ) => ("urn:uuid:".len()..self.0.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
0
        for (index, character) in input_str[bounds.clone()].char_indices() {
107
0
            let byte = character as u8;
108
109
0
            match (format, byte.to_ascii_lowercase()) {
110
0
                (_, b'0'..=b'9' | b'a'..=b'f') => (),
111
                (RequestedUuid::Simple, b'-') => {
112
0
                    return Error(ErrorKind::ParseChar {
113
0
                        character: '-',
114
0
                        index: index + bounds.start,
115
0
                    })
116
                }
117
                (_, b'-') => {
118
0
                    if format == RequestedUuid::Any {
119
0
                        format = RequestedUuid::Hyphenated;
120
0
                    }
121
122
0
                    if hyphen_count < 4 {
123
0
                        // While we search, also count group breaks
124
0
                        group_bounds[hyphen_count] = index;
125
0
                    }
126
0
                    hyphen_count += 1;
127
                }
128
                _ => {
129
0
                    return Error(ErrorKind::ParseChar {
130
0
                        character,
131
0
                        index: index + bounds.start,
132
0
                    })
133
                }
134
            }
135
        }
136
137
0
        if format == RequestedUuid::Any || format == RequestedUuid::Simple {
138
            // This means that we tried and failed to parse a simple uuid.
139
            // Since we verified that all the characters are valid, this means
140
            // that it MUST have an invalid length.
141
0
            Error(ErrorKind::ParseLength {
142
0
                len: input_str.len(),
143
0
            })
144
0
        } else if hyphen_count != 4 {
145
            // We tried to parse a hyphenated variant, but there weren't
146
            // 5 groups (4 hyphen splits).
147
0
            Error(ErrorKind::ParseGroupCount {
148
0
                count: hyphen_count + 1,
149
0
            })
150
        } else {
151
            // There are 5 groups, one of them has an incorrect length
152
            const BLOCK_STARTS: [usize; 5] = [0, 9, 14, 19, 24];
153
0
            for i in 0..4 {
154
0
                if group_bounds[i] != BLOCK_STARTS[i + 1] - 1 {
155
0
                    return Error(ErrorKind::ParseGroupLength {
156
0
                        group: i,
157
0
                        len: group_bounds[i] - BLOCK_STARTS[i],
158
0
                        index: bounds.start + BLOCK_STARTS[i] + 1,
159
0
                    });
160
0
                }
161
            }
162
163
            // The last group must be too long
164
0
            Error(ErrorKind::ParseGroupLength {
165
0
                group: 4,
166
0
                len: input_str.len() - BLOCK_STARTS[4],
167
0
                index: bounds.start + BLOCK_STARTS[4] + 1,
168
0
            })
169
        }
170
0
    }
171
}
172
173
// NOTE: This impl is part of the public API. Breaking changes to it should be carefully considered
174
impl fmt::Display for Error {
175
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
176
0
        match self.0 {
177
            ErrorKind::ParseChar {
178
0
                character, index, ..
179
            } => {
180
0
                write!(f, "invalid character: found `{}` at {}", character, index)
181
            }
182
0
            ErrorKind::ParseByteLength { len } => {
183
0
                write!(f, "invalid length: expected 16 bytes, found {}", len)
184
            }
185
0
            ErrorKind::ParseGroupCount { count } => {
186
0
                write!(f, "invalid group count: expected 5, found {}", count)
187
            }
188
0
            ErrorKind::ParseGroupLength { group, len, .. } => {
189
0
                let expected = [8, 4, 4, 4, 12][group];
190
0
                write!(
191
0
                    f,
192
0
                    "invalid group length in group {}: expected {}, found {}",
193
                    group, expected, len
194
                )
195
            }
196
0
            ErrorKind::ParseInvalidUTF8 => write!(f, "non-UTF8 input"),
197
0
            ErrorKind::Nil => write!(f, "the UUID is nil"),
198
0
            ErrorKind::ParseLength { len } => write!(f, "invalid length: found {}", len),
199
0
            ErrorKind::ParseOther => write!(f, "failed to parse a UUID"),
200
            #[cfg(feature = "std")]
201
0
            ErrorKind::InvalidSystemTime(ref e) => {
202
0
                write!(f, "the system timestamp is invalid: {e}")
203
            }
204
        }
205
0
    }
206
}
207
208
impl crate::std::error::Error for Error {}