Shomareh
Persian utilities Jalali/Gregorian date conversion, plus parsing and validation of phone numbers and emails with Persian digit support.

Overview
Shomareh is a small Rust crate of utilities for Persian-language applications: converting between Jalali and Gregorian dates, and parsing and validating phone numbers and email addresses the way Iranian users actually type them.
Parsing is the part that earns the crate its place. Validation alone gives a caller a yes or no, and then every caller writes the same follow-up code to dig out the pieces they need — the operator prefix, the subscriber number, the local part and domain of an email. Shomareh returns the structure directly, so the caller gets both the answer and the parts in one pass.
The other reason it exists is digit handling. Every Iranian-facing app ends up reimplementing this, because the naive version — a regex on ASCII digits — rejects half of what users enter. Persian digits (۰–۹) and Arabic-Indic digits (٠–٩) are what show up on a Persian keyboard, and a form that silently rejects a phone number because the user typed ۰۹۱۲… instead of 0912… is a bug the user blames on you, not on their input.
The brief
- Parse, not just validate: return the phone number’s parts and the email’s local part and domain, not only a pass/fail.
- Normalize Persian and Arabic-Indic digits at the boundary, so the rest of the code only ever sees ASCII.
- Cover the conversions and parsers that come up in every Iranian web app, not a general-purpose library.
- Return typed errors rather than
bool, so a caller can tell the user what is wrong. - No panics on bad input, and no dependency that costs more than the crate itself.
Approach
The design decision that shaped everything else was making digit normalization the first step of every public entry point. Phone parsing, email parsing, and date parsing all run their input through the same normalizer before doing anything else. That gives one place to fix a digit-handling bug instead of three, and it means the Persian/Arabic support is a property of the crate rather than something each function has to remember.
Parsing and validation are the same operation internally. A phone number is split into its prefix and subscriber digits; the parser then checks that the prefix is one that exists and that the subscriber length matches it. Validation is just the parse with the result discarded. Doing it the other way around — validate with a regex, then re-parse to extract — doubles the rules and lets the two drift apart. Here there is one set of rules and it cannot disagree with itself.
Email parsing follows the same shape: normalize, split into local part and domain, then apply the rules for what constitutes a valid pair. It is deliberately not an RFC 5322 implementation — that grammar accepts things no signup form should, and rejects nothing users actually mistype.
Jalali conversion is implemented as arithmetic rather than pulled from a date library, because the conversion is well-defined and a dependency would have been larger than the code. The edge cases are leap years and month boundaries, so that’s where the tests concentrate: round-tripping dates across a wide range and asserting on the known problem dates.
Errors carry the reason — wrong length, unknown prefix, missing @, invalid digit, unparseable date — instead of collapsing to false. For a parser that is most of the value: a caller can produce a real error message, and a test can assert on the specific failure.
Stack
Rust, with a deliberately minimal dependency footprint.
Outcome
Shomareh is published on crates.io and is a good example of the kind of crate I think Rust is best at: a small, well-bounded problem solved once, with the edge cases written down as tests. It was also my first real exercise in API design at the crate boundary — deciding what to expose in a parsed result, what to keep private, and how much of the input handling to own versus leave to the caller.