sashite_sin/error.rs
1//! Errors produced when parsing a SIN token.
2
3/// The reason a string could not be parsed as a SIN token.
4///
5/// Returned by the parsing entry points ([`crate::Identifier::parse`],
6/// [`core::str::FromStr`], [`TryFrom`]) and by [`crate::Letter::try_from_char`].
7///
8/// This enum is `#[non_exhaustive]`: future revisions may add variants without
9/// a breaking change, so downstream `match` expressions should include a
10/// wildcard arm.
11///
12/// # Length is measured in bytes
13///
14/// A valid token is one ASCII letter, which is always exactly one byte, so the
15/// parser can decide on the byte length before looking at any content. The
16/// consequence is worth knowing when reading the variant back: an input of one
17/// *character* that occupies several *bytes* — any non-ASCII character, such as
18/// `é` — is reported as [`TooLong`](Self::TooLong), not as
19/// [`InvalidLetter`](Self::InvalidLetter). Both say "this is not a token", and
20/// the specification requires only that such input be rejected; only the
21/// reported reason differs.
22///
23/// The rule is exact: [`InvalidLetter`](Self::InvalidLetter) means the input
24/// was one byte that was not an ASCII letter, and [`TooLong`](Self::TooLong)
25/// means it was two bytes or more.
26///
27/// # Examples
28///
29/// ```
30/// use sashite_sin::{Identifier, ParseError};
31///
32/// assert_eq!("".parse::<Identifier>(), Err(ParseError::Empty));
33/// assert_eq!("CC".parse::<Identifier>(), Err(ParseError::TooLong));
34/// assert_eq!("1".parse::<Identifier>(), Err(ParseError::InvalidLetter));
35///
36/// // One character, two bytes: rejected as too long.
37/// assert_eq!("é".chars().count(), 1);
38/// assert_eq!("é".parse::<Identifier>(), Err(ParseError::TooLong));
39/// ```
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
41#[non_exhaustive]
42pub enum ParseError {
43 /// The input was empty.
44 Empty,
45 /// The input was two bytes or more, and a token is exactly one.
46 ///
47 /// Because the check counts bytes, this also covers an input of a single
48 /// non-ASCII character; see the note on the enum itself.
49 TooLong,
50 /// The input was a single byte that was not an ASCII letter.
51 InvalidLetter,
52}
53
54impl core::fmt::Display for ParseError {
55 /// Writes a short, human-readable reason.
56 ///
57 /// The message goes out through [`core::fmt::Formatter::pad`], so width,
58 /// fill, alignment and precision behave as they do for the equivalent
59 /// [`str`]; writing it straight to the buffer would silently discard those
60 /// options when a caller lines errors up in a column.
61 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
62 let message = match self {
63 Self::Empty => "empty SIN token",
64 Self::TooLong => "SIN token longer than one byte",
65 Self::InvalidLetter => "SIN token must contain exactly one ASCII letter",
66 };
67 f.pad(message)
68 }
69}
70
71impl core::error::Error for ParseError {}