sashite-sin 1.0.0

Style Identifier Notation (SIN): a compact, ASCII-only, no_std token encoding a player's side and style in abstract strategy board games.
Documentation
//! Allocation-free string encoding of a SIN token.

use crate::identifier::Identifier;

/// The canonical string form of an [`Identifier`], stored inline.
///
/// A token occupies exactly one byte, so `EncodedSin` keeps it in a fixed
/// one-byte buffer with no heap allocation. It is produced by
/// [`Identifier::encode`] and dereferences to [`str`], so it can be used
/// wherever a string slice is expected.
///
/// # Examples
///
/// ```
/// # fn main() -> Result<(), sashite_sin::ParseError> {
/// use sashite_sin::Identifier;
///
/// let enc = Identifier::parse("W")?.encode();
/// assert_eq!(enc.as_str(), "W");
/// assert_eq!(&*enc, "W"); // via Deref<Target = str>
/// assert_eq!(enc.len(), 1); // str method reached through Deref
/// assert_eq!(enc, "W"); // direct comparison via PartialEq<&str>
/// assert_eq!("W", enc); // and the reverse direction
/// # Ok(())
/// # }
/// ```
#[derive(Clone, Copy)]
pub struct EncodedSin {
    buf: [u8; 1],
}

impl EncodedSin {
    /// Encodes an identifier into its canonical token form: the single
    /// abbreviation letter, cased according to the side.
    #[must_use]
    pub(crate) fn from_identifier(id: Identifier) -> Self {
        Self {
            buf: [id.letter().to_ascii(id.side())],
        }
    }

    /// Returns the encoded token as a string slice.
    #[must_use]
    pub fn as_str(&self) -> &str {
        debug_assert!(
            self.buf.is_ascii(),
            "EncodedSin must contain only ASCII bytes"
        );
        // ASCII is always valid UTF-8, so this conversion cannot fail; the empty
        // fallback is unreachable and exists only to avoid `unsafe`.
        core::str::from_utf8(&self.buf).unwrap_or("")
    }
}

impl core::ops::Deref for EncodedSin {
    type Target = str;

    fn deref(&self) -> &str {
        self.as_str()
    }
}

impl AsRef<str> for EncodedSin {
    fn as_ref(&self) -> &str {
        self.as_str()
    }
}

impl core::fmt::Display for EncodedSin {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.write_str(self.as_str())
    }
}

impl core::fmt::Debug for EncodedSin {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "EncodedSin({:?})", self.as_str())
    }
}

impl PartialEq<str> for EncodedSin {
    fn eq(&self, other: &str) -> bool {
        self.as_str() == other
    }
}

impl PartialEq<&str> for EncodedSin {
    fn eq(&self, other: &&str) -> bool {
        self.as_str() == *other
    }
}

impl PartialEq<EncodedSin> for str {
    fn eq(&self, other: &EncodedSin) -> bool {
        self == other.as_str()
    }
}

impl PartialEq<EncodedSin> for &str {
    fn eq(&self, other: &EncodedSin) -> bool {
        *self == other.as_str()
    }
}