Skip to main content

sashite_epin/
lib.rs

1//! # Extended Piece Identifier Notation (EPIN)
2//!
3//! A `no_std`, `unsafe`-free implementation of the
4//! [EPIN v1.0.0 specification](https://sashite.dev/specs/epin/1.0.0/).
5//!
6//! EPIN is a strict superset of [PIN](https://sashite.dev/specs/pin/1.0.0/): it
7//! inherits the four PIN attributes (piece name, side, state, terminal status)
8//! and adds a single optional trailing marker, the **derivation marker** `'`,
9//! which flags whether a piece's style is *native* or *derived*. A token has
10//! the shape:
11//!
12//! ```text
13//! <pin>[']        e.g.  K   r'   +K^   -k^'
14//! ```
15//!
16//! matching the anchored regular expression `\A[-+]?[A-Za-z]\^?'?\z`. What
17//! "native" and "derived" *mean*, and how a concrete style is resolved, is
18//! defined by the surrounding context — EPIN encodes only the flag.
19//!
20//! This crate is a thin layer over [`sashite_pin`]: an [`Identifier`] is a PIN
21//! [`sashite_pin::Identifier`] paired with the native/derived flag. The PIN
22//! layer is re-exported (see below) so callers can reach the full PIN API —
23//! including the type returned by [`Identifier::pin`] — without declaring
24//! `sashite-pin` themselves.
25//!
26//! ## Example
27//!
28//! ```
29//! # fn main() -> Result<(), sashite_epin::ParseError> {
30//! use sashite_epin::{Identifier, Side};
31//!
32//! let king: Identifier = "+K^'".parse()?;
33//! assert_eq!(king.letter().as_char(), 'K');
34//! assert_eq!(king.side(), Side::First);
35//! assert!(king.is_terminal());
36//! assert!(king.is_derived());
37//!
38//! // The underlying PIN token is one method away.
39//! assert_eq!(king.pin().encode().as_str(), "+K^");
40//!
41//! // Native/derived is a single, idempotent flag.
42//! assert_eq!(king.native().encode().as_str(), "+K^");
43//! assert_eq!(king.encode().as_str(), "+K^'");
44//! # Ok(())
45//! # }
46//! ```
47//!
48//! ## Guarantees
49//!
50//! - **`no_std` and allocation-free:** parsing borrows the input bytes and an
51//!   [`Identifier`] is a small `Copy` value; nothing is heap-allocated.
52//! - **No `unsafe`:** the crate is built under a forbid-`unsafe` lint policy.
53//! - **Single source of truth:** all PIN-level parsing, validation, and
54//!   encoding is delegated to [`sashite_pin`]; EPIN only adds the `'` marker.
55//! - **No required dependencies beyond PIN:** the optional `serde` feature adds
56//!   `serde` (and turns on `sashite-pin/serde`), and keeps the crate `no_std`.
57
58#![no_std]
59
60mod encode;
61mod error;
62mod identifier;
63mod parse;
64#[cfg(feature = "serde")]
65mod serde_impl;
66
67pub use encode::EncodedEpin;
68pub use error::ParseError;
69pub use identifier::Identifier;
70
71// The PIN layer EPIN builds upon is re-exported so downstream users can rely on
72// the exact same version without declaring `sashite-pin` themselves. The PIN
73// identifier (returned by [`Identifier::pin`]) is reachable as
74// `sashite_epin::sashite_pin::Identifier`.
75pub use sashite_pin;
76pub use sashite_pin::{Letter, Side, State};