Coverage Report

Created: 2026-09-03 07:15

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