sashite-pin
PIN (Piece Identifier Notation) implementation for Rust.
Overview
This crate implements the PIN Specification v1.0.0.
PIN is a compact, ASCII-only token format that encodes a piece identity at the
level of notation: the tuple (piece name, side, state, terminal status). The case
of a single letter encodes the side, an optional +/- prefix encodes the state,
and an optional ^ suffix marks a terminal piece.
[<state-modifier>]<abbr>[<terminal-marker>] e.g. K +R k^ -K^ +G^
PIN standardizes only the encoding. What a state means, which pieces are terminal, and how players are named are all left to the rule system — see the Game Protocol and Glossary.
Implementation constraints
| Property | Value | Rationale |
|---|---|---|
| Token length | 1–3 bytes | ^[+-]?[A-Za-z]\^?$ per the specification |
| Closed domain | 312 tokens | 26 letters × 2 sides × 3 states × 2 terminal flags |
Identifier size |
4 bytes, Copy |
stored inline; parsing and encoding never allocate |
| Dependencies | none required | zero by default; serde is an optional, no_std add-on |
unsafe |
forbidden | the crate is built under a forbid-unsafe lint policy |
| MSRV | 1.81 | for core::error::Error without a std feature |
Installation
Or add it manually to Cargo.toml:
[]
= "1"
Cargo features
serde(off by default) — implementsSerialize/DeserializeforIdentifier, (de)serializing it as its canonical token string (e.g."+K^"). Enabling it keeps the crateno_std.
[]
= { = "1", = ["serde"] }
Usage
Parsing
use ;
let king: Identifier = "+K^".parse?; // via FromStr
let rook = parse?; // via the inherent method
assert_eq!;
assert_eq!;
assert_eq!;
assert!;
assert_eq!;
Building from typed components
Construction is infallible: because each component type is valid by construction, every combination denotes a valid token.
use ;
let pawn = new;
assert_eq!;
Encoding and formatting
encode returns an allocation-free, fixed-buffer string view that dereferences
to str; Display writes the same canonical form.
use Identifier;
let id = parse?;
assert_eq!;
assert_eq!; // requires `alloc`/`std`
Validation
use Identifier;
assert!;
assert!; // a modifier must be a prefix
Transformations and queries
Every transformation returns a new value (the type is Copy, so this is cheap):
use Identifier;
let white = parse?;
assert_eq!;
assert_eq!;
assert_eq!;
assert!;
assert!;
Token format
The grammar (EBNF) is:
pin ::= [ state-modifier ] abbr [ terminal-marker ] ;
state-modifier ::= "+" | "-" ;
abbr ::= "A"…"Z" | "a"…"z" ;
terminal-marker ::= "^" ;
A token maps to exactly four attributes:
| Component | Encodes | Values |
|---|---|---|
| letter case | side | uppercase → First, lowercase → Second |
| letter | piece name | a single-letter abbreviation (A–Z) |
+ / - prefix |
state | Enhanced / Diminished (else Normal) |
^ suffix |
terminal status | present → terminal piece |
Letters are not reserved: the mapping from abbreviation to full piece name is defined entirely by the rule system. See the examples page for sample mappings (chess, shogi, xiangqi, makruk).
Design and guarantees
no_stdand allocation-free. Parsing borrows the input bytes; anIdentifieris a 4-byteCopyvalue andEncodedPinkeeps the ≤ 3 output bytes in a fixed inline buffer. Nothing touches the heap.- No
unsafe, no regex engine. The parser matches raw bytes directly, eliminating ReDoS as an attack vector. - Bounded, panic-free parsing. Inputs longer than three bytes are rejected on
a structural length check before any byte is inspected, and the public parsing
API returns a
Resultrather than panicking. const-friendly. Construction, parsing, validation, the accessors, and the transformations are allconst fn, so identifiers can be built and checked at compile time.- Total component construction. With valid-by-construction component types, building an identifier from its parts cannot fail.
Performance
The hot paths run in single-digit nanoseconds per call. Indicative figures from
benches/parse.rs:
| Operation | Time |
|---|---|
parse +K^ (3 bytes) |
~3.2 ns |
parse K (1 byte) |
~3.6 ns |
| reject over-long input | ~1.8 ns |
Run them with cargo bench.
Related specifications
- Game Protocol — the conceptual foundation
- PIN Specification v1.0.0 — the normative document
- PIN Examples — sample piece-set mappings
Reference implementations in other languages are maintained by Sashité: Elixir, Go, Ruby.
If a library's behavior appears to conflict with the specification, the specification is normative.
License
Available as open source under the terms of the Apache License 2.0.