unicode-normalization

Unicode NFC/NFD/NFKC/NFKD normalization for Rust strings, per UAX #15

Library
Cargo
v0.1.25
196stars
Apache License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
39/100Needs Attention
Development Activity0
Maintenance0
Community76
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
73/100Good
Architecture75
Code Quality78
Innovation68
Learning Curve70

unicode-normalization is a Rust crate that implements Unicode character composition and decomposition exactly as specified by Unicode Standard Annex #15. It gives Rust programs iterator-based access to the four canonical normalization forms (NFC, NFD, NFKC, NFKD) plus quick-check and stream-safe variants, using generated lookup tables built directly from the Unicode Character Database.

As a foundational text-processing primitive it is depended on transitively by huge swaths of the Rust ecosystem (URL parsing, IDNA/domain handling, text search, and Unicode-aware string libraries), making correctness and no_std portability its primary design goals rather than a large feature surface.

What You Get

  • Iterator adaptors for NFC, NFD, NFKC, and NFKD normalization directly on string/char types via the UnicodeNormalization trait
  • Quick-check functions (is_nfc, is_nfd, is_nfkc, is_nfkd, and stream-safe variants) to test normalization status without full transformation
  • Stream-safe text processing (StreamSafe) to bound combining-character run lengths per UAX #15 §13
  • no_std + alloc compatibility for embedded and constrained environments (default-features = false)
  • Character-level composition/decomposition primitives in the char submodule for building custom normalization pipelines

Common Use Cases

  • Normalizing user input (search queries, form fields, filenames) so visually-identical strings compare equal
  • IDNA/domain-name and URL normalization as a dependency of crates like url and idna
  • Preprocessing text before hashing, indexing, or full-text search to avoid Unicode-equivalence duplicates
  • Validating that stored or transmitted strings are already in a canonical form before further processing

Under The Hood

Architecture — The crate is organized around a small set of focused modules: decompose.rs and recompose.rs implement the core Decompositions/Recompositions iterator adaptors that wrap a Chars iterator, quick_check.rs implements the UAX #15 quick-check algorithm for constant-time-ish normalization-form testing, stream_safe.rs implements the stream-safe text process, and tables.rs (22.8k generated lines) holds the perfect-hash-indexed Unicode Character Database tables (perfect_hash.rs implements the lookup). lib.rs re-exports the public surface as extension traits on char/str via UnicodeNormalization, keeping the API surface thin relative to the data-heavy implementation.

Tech Stack — Pure Rust, edition 2018, MSRV 1.36, with a single runtime dependency (tinyvec, used with the alloc feature for small-buffer decomposition results without heap allocation in the common case). The crate is #![cfg_attr(not(feature = "std"), no_std)] and gated #![deny(missing_docs, unsafe_code)], so it ships with zero unsafe code and mandatory doc coverage. Generated tables are produced by scripts in scripts/ against the Unicode Character Database rather than hand-maintained.

Code Quality — Tests live in tests/ (property-style tests against tests/data, CJK compat-variant tests, and a stream-safe regression test) plus inline #[cfg(test)] mod test in src/. The deny(missing_docs) lint enforces documented public API, and deny(unsafe_code) rules out an entire class of memory-safety bugs given how deeply this crate sits in the dependency graph. Benchmarks exist under benches/, and a fuzz/ directory indicates fuzz-testing of the normalization routines.

API Design — The extension-trait pattern ("...".nfc().collect::<String>()) is idiomatic Rust and requires zero setup beyond a single use statement; quick-check functions mirror the transform functions 1:1 (is_nfc/nfc), which keeps the mental model simple. The tradeoff is a fairly low-level API — callers get normalization primitives, not higher-level text-processing helpers — appropriate for a crate meant to be a dependency of other libraries rather than an end-user tool.

Used by 6 apps in this directory

TypeScript
88%
Other

AFFiNE

Productivity · Project Management · Note Taking

71,668

Write, draw, and plan in one infinite canvas — the open-source alternative to Notion and Miro that keeps your data yours.

View details
91
Repo Health
87
Technical
68
Dependency
Built with
TypeScript88%
Updated today
TypeScript
75%
Apache 2.0

Fern

Developer Tools

3,761

Fern turns a single OpenAPI, AsyncAPI, or Protobuf definition into type-safe SDKs for nine languages and a hosted API documentation site, all from one CLI and one source of truth.

View details
90
Repo Health
86
Technical
67
Dependency
Built with
TypeScript75%
Updated today
TypeScript
69%
Apache 2.0

Laminar

AI Development · Monitoring

3,180

Open-source observability platform purpose-built for AI agents — trace, evaluate, debug, and monitor at scale with SQL access and real-time replay.

View details
85
Repo Health
76
Technical
70
Dependency
Built with
TypeScript69%
Rust29%
Updated yesterday
TypeScript
79%
LGPL 3.0

Latitude

AI Agents · Monitoring

4,593

Open-source AI agent monitoring that catches what will break next before your users do.

View details
86
Repo Health
88
Technical
67
Dependency
Built with
TypeScript79%
Python11%
Updated today
Rust
97%
MPL 2.0

Sonic

Databases · Search

21,315

Fast, lightweight, schema-less search backend in Rust — microsecond queries, 30MB RAM, no document storage required.

View details
88
Repo Health
88
Technical
73
Dependency
Built with
Rust97%
Updated 3 days ago
Rust
98%

Stalwart

Collaboration

14,255

All-in-one secure mail and collaboration server covering IMAP, JMAP, SMTP, CalDAV, CardDAV, and WebDAV in a single memory-safe Rust binary.

View details
89
Repo Health
81
Technical
68
Dependency
Built with
Rust98%
Updated yesterday

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search