sashite-sin 1.1.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
//! # Style Identifier Notation (SIN)
//!
//! A `no_std`, `unsafe`-free implementation of the
//! [SIN v1.0.0 specification](https://sashite.dev/specs/sin/1.0.0/).
//!
//! SIN is a compact, ASCII-only token that encodes a player's identity — the
//! pair (player side, player style) — at the level of notation. A token has the
//! shape:
//!
//! ```text
//! <abbr>
//! ```
//!
//! matching the anchored regular expression `^[A-Za-z]$`, and maps to exactly
//! two attributes:
//!
//! | Component   | Encodes    | Values                                    |
//! |-------------|------------|-------------------------------------------|
//! | letter case | [`Side`]   | uppercase → `First`, lowercase → `Second` |
//! | letter      | [`Letter`] | the player-style abbreviation (`A`–`Z`)   |
//!
//! SIN standardizes only the *encoding*; which letter denotes which style is
//! left to the rule system. See the [glossary](https://sashite.dev/glossary/).
//!
//! ## Validity
//!
//! The anchors bind the *whole* string, not a line within it: a token is one
//! ASCII letter and nothing else. Nothing is trimmed, no surrounding
//! whitespace is tolerated, and a trailing `\r` or `\n` is never absorbed — so
//! `"W"` parses while `" W"`, `"W "` and `"W\n"` do not. Validation is strict
//! and never normalizes: parsing a token and encoding it again reproduces the
//! original text byte for byte.
//!
//! That makes the domain closed and small — 26 letters × 2 sides = 52 tokens,
//! and nothing else is valid at any length.
//!
//! ## Example
//!
//! ```
//! # fn main() -> Result<(), sashite_sin::ParseError> {
//! use sashite_sin::{Identifier, Side};
//!
//! let western: Identifier = "W".parse()?;
//! assert_eq!(western.letter().as_char(), 'W');
//! assert_eq!(western.side(), Side::First);
//!
//! // A SIN token is exactly one character.
//! assert_eq!(western.to_char(), 'W');
//! assert_eq!(western.encode().as_str(), "W");
//!
//! // Cheap, infallible transformations (the type is `Copy`).
//! assert_eq!(western.flipped().to_char(), 'w');
//! # Ok(())
//! # }
//! ```
//!
//! ## Guarantees
//!
//! - **`no_std` and allocation-free:** parsing borrows the input bytes and an
//!   [`Identifier`] is a 2-byte `Copy` value; nothing is heap-allocated.
//! - **No `unsafe`:** the crate is built under a forbid-`unsafe` lint policy.
//! - **Bounded, panic-free parsing:** inputs longer than one byte are rejected
//!   before inspection, and the public parsing API never panics — including
//!   [`TryFrom<&[u8]>`](Identifier), which accepts arbitrary bytes and needs no
//!   UTF-8 check to reject them safely.
//! - **One notion of validity:** every entry point ([`Identifier::parse`],
//!   [`Identifier::is_valid`], [`core::str::FromStr`] and both [`TryFrom`]
//!   impls) returns the same answer, down to the [`ParseError`] variant.
//! - **No required dependencies:** SIN is self-contained, so the default build
//!   has an empty dependency graph; the optional `serde` feature adds `serde`
//!   (kept `no_std`).

#![no_std]

mod encode;
mod error;
mod identifier;
mod letter;
mod parse;
#[cfg(feature = "serde")]
mod serde_impl;
mod side;

pub use crate::encode::EncodedSin;
pub use crate::error::ParseError;
pub use crate::identifier::Identifier;
pub use crate::letter::Letter;
pub use crate::side::Side;