Skip to content

sashite/sin.rs

Repository files navigation

sashite-sin

Crates.io Docs.rs CI License

SIN (Style Identifier Notation) implementation for Rust.

Overview

This crate implements the SIN Specification v1.0.0.

SIN is a compact, ASCII-only token format that encodes a player identity at the level of notation: the tuple (player side, player style). A single letter carries both — its case encodes the side, and the letter itself is the player-style abbreviation.

<abbr>      e.g.  W   c   J   s

Because a player's side and style are both fixed for the duration of a match, a SIN token is a stable player identifier. SIN standardizes only the encoding: which letter denotes which style is left to the rule system — see the Game Protocol and Glossary.

Implementation constraints

Property Value Rationale
Token length 1 byte ^[A-Za-z]$ per the specification
Closed domain 52 tokens 26 letters × 2 sides
Identifier size 2 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

cargo add sashite-sin

Or add it manually to Cargo.toml:

[dependencies]
sashite-sin = "1"

Cargo features

  • serde (off by default) — implements Serialize / Deserialize for Identifier, (de)serializing it as its canonical token string (e.g. "W"). Enabling it keeps the crate no_std.
[dependencies]
sashite-sin = { version = "1", features = ["serde"] }

Usage

Parsing

use sashite_sin::{Identifier, Side};

let western: Identifier = "W".parse()?;      // via FromStr
let chinese = Identifier::parse("c")?;       // via the inherent method

assert_eq!(western.letter().as_char(), 'W');
assert_eq!(western.side(), Side::First);

assert_eq!(chinese.side(), Side::Second);

Building from typed components

Construction is infallible: because each component type is valid by construction, every combination denotes a valid token.

use sashite_sin::{Identifier, Letter, Side};

let japanese = Identifier::new(Letter::try_from_char('J')?, Side::Second);
assert_eq!(japanese.encode().as_str(), "j");

Encoding and formatting

encode returns an allocation-free, fixed-buffer string view that dereferences to str; to_char returns the single character; Display writes the same canonical form.

use sashite_sin::Identifier;

let id = Identifier::parse("W")?;
assert_eq!(id.encode().as_str(), "W");
assert_eq!(id.to_char(), 'W');
assert_eq!(id.to_string(), "W");             // requires `alloc`/`std`

Validation

use sashite_sin::Identifier;

assert!(Identifier::is_valid("S"));
assert!(!Identifier::is_valid("WW"));         // a token is exactly one letter

Transformations and queries

Every transformation returns a new value (the type is Copy, so this is cheap):

use sashite_sin::{Identifier, Letter};

let western = Identifier::parse("W")?;
assert_eq!(western.flipped().encode().as_str(), "w");
assert_eq!(
    western.with_letter(Letter::try_from_char('C')?).encode().as_str(),
    "C",
);

assert!(western.is_first());

Token format

The grammar (EBNF) is:

sin  ::= abbr ;
abbr ::= "A""Z" | "a""z" ;

A token maps to exactly two attributes:

Component Encodes Values
letter case side uppercase → First, lowercase → Second
letter player style a single-letter abbreviation (AZ)

Letters are not reserved: the mapping from abbreviation to full style name is defined entirely by the rule system. See the examples page for the conventional mappings (Western, Chinese, Japanese, Siamese).

Design and guarantees

  • no_std and allocation-free. Parsing borrows the input bytes; an Identifier is a 2-byte Copy value and EncodedSin keeps the single output byte 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 one byte are rejected on a structural length check before any byte is inspected, and the public parsing API returns a Result rather than panicking.
  • const-friendly. Construction, parsing, validation, the accessors, and the transformations are all const 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 are tiny: parsing is a length check followed by a single byte classification, and encoding writes one byte — expect single-digit nanoseconds per call. Run cargo bench (see benches/parse.rs) for figures on your own hardware.

Related specifications

Reference implementations in other languages are maintained by Sashité: Elixir, 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.

About

SIN (Style Identifier Notation) implementation for Rust with immutable style objects.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages