Skip to main content

sashite_sin/
encode.rs

1//! Allocation-free string encoding of a SIN token.
2
3use crate::identifier::Identifier;
4
5/// The canonical string form of an [`Identifier`], stored inline.
6///
7/// A token occupies exactly one byte, so `EncodedSin` keeps it in a fixed
8/// one-byte buffer with no heap allocation. It is produced by
9/// [`Identifier::encode`] and dereferences to [`str`], so it can be used
10/// wherever a string slice is expected.
11///
12/// # Examples
13///
14/// ```
15/// # fn main() -> Result<(), sashite_sin::ParseError> {
16/// use sashite_sin::Identifier;
17///
18/// let enc = Identifier::parse("W")?.encode();
19/// assert_eq!(enc.as_str(), "W");
20/// assert_eq!(&*enc, "W"); // via Deref<Target = str>
21/// assert_eq!(enc.len(), 1); // str method reached through Deref
22/// assert_eq!(enc, "W"); // direct comparison via PartialEq<&str>
23/// assert_eq!("W", enc); // and the reverse direction
24/// # Ok(())
25/// # }
26/// ```
27#[derive(Clone, Copy)]
28pub struct EncodedSin {
29    buf: [u8; 1],
30}
31
32impl EncodedSin {
33    /// Encodes an identifier into its canonical token form: the single
34    /// abbreviation letter, cased according to the side.
35    #[must_use]
36    pub(crate) fn from_identifier(id: Identifier) -> Self {
37        Self {
38            buf: [id.letter().to_ascii(id.side())],
39        }
40    }
41
42    /// Returns the encoded token as a string slice.
43    #[must_use]
44    pub fn as_str(&self) -> &str {
45        debug_assert!(
46            self.buf.is_ascii(),
47            "EncodedSin must contain only ASCII bytes"
48        );
49        // ASCII is always valid UTF-8, so this conversion cannot fail; the empty
50        // fallback is unreachable and exists only to avoid `unsafe`.
51        core::str::from_utf8(&self.buf).unwrap_or("")
52    }
53}
54
55impl core::ops::Deref for EncodedSin {
56    type Target = str;
57
58    fn deref(&self) -> &str {
59        self.as_str()
60    }
61}
62
63impl AsRef<str> for EncodedSin {
64    fn as_ref(&self) -> &str {
65        self.as_str()
66    }
67}
68
69impl core::fmt::Display for EncodedSin {
70    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
71        f.write_str(self.as_str())
72    }
73}
74
75impl core::fmt::Debug for EncodedSin {
76    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
77        write!(f, "EncodedSin({:?})", self.as_str())
78    }
79}
80
81impl PartialEq<str> for EncodedSin {
82    fn eq(&self, other: &str) -> bool {
83        self.as_str() == other
84    }
85}
86
87impl PartialEq<&str> for EncodedSin {
88    fn eq(&self, other: &&str) -> bool {
89        self.as_str() == *other
90    }
91}
92
93impl PartialEq<EncodedSin> for str {
94    fn eq(&self, other: &EncodedSin) -> bool {
95        self == other.as_str()
96    }
97}
98
99impl PartialEq<EncodedSin> for &str {
100    fn eq(&self, other: &EncodedSin) -> bool {
101        *self == other.as_str()
102    }
103}