js_option

A drop-in Option-like Rust type that distinguishes an explicit null from a missing/undefined JSON field.

Library
Cargo
v0.2.0
5stars
MIT License

Repository Health

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

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
65/100Good
Architecture75
Code Quality40
Innovation80
Learning Curve65

js_option provides JsOption<T>, an enum shaped like Rust’s standard Option<T> but with three states instead of two: Some(value), Null, and Undefined. Where a regular Option collapses both an explicit JSON null and a missing field down to None, JsOption keeps that distinction intact through serde-based (de)serialization, which matters whenever a JSON API or PATCH-style payload needs to tell “clear this field” apart from “don’t touch this field.”

The crate is a single small type with an API deliberately modeled after Option’s own methods (unwrap, map, as_ref, as_deref, unwrap_or_else, and so on), so adopting it in an existing serde-based struct is close to a drop-in swap plus one #[serde(default, skip_serializing_if = "JsOption::is_undefined")] attribute.

What You Get

  • A three-variant JsOption<T> enum (Some, Null, Undefined) that plugs into any serde-derived struct field
  • Manual Serialize/Deserialize implementations (behind the default-on serde feature) that round-trip null and missing fields correctly with the right #[serde(...)] attributes
  • An Option-mirroring method set — unwrap, unwrap_or, unwrap_or_else, unwrap_or_default, map, as_ref, as_mut, as_deref, as_deref_mut, is_some, is_null, is_undefined — for near-zero-friction adoption
  • Conversions to and from Option<T> and Option<Option<T>> (from_option, from_implicit_option, into_option, into_nested_option) for interop with existing code

Common Use Cases

  • Modeling JSON PATCH / partial-update request bodies where a client must distinguish “set field to null” from “leave field unchanged”
  • Deserializing third-party JSON APIs (matrix/JS-originated schemas in particular, given the crate’s ruma origin) that intentionally distinguish null from an absent key
  • Building typed Rust structs around JavaScript/TypeScript-shaped data where null and undefined are semantically different
  • Any serde struct field that needs three-state optionality without hand-rolling a custom enum and its own Serialize/Deserialize impls

Under The Hood

Architecture The crate is a single-file implementation: src/lib.rs defines the JsOption<T> enum and its inherent methods, with an optional src/serde.rs submodule (compiled only under the serde feature) supplying manual Deserialize/Serialize impls that delegate to Option<T>’s own serde support internally. There is no layering, no dependency injection, and no runtime data flow beyond enum construction and pattern matching — the entire public surface mirrors std::option::Option’s method names 1:1, so changing a variant would ripple predictably through every method body and through the small match in serde.rs, but the blast radius is trivial given the crate’s roughly 190 total lines.

Tech Stack A pure Rust library crate targeting edition 2021 with MSRV 1.60.0, published to crates.io with zero mandatory dependencies. The one optional dependency, serde_core 1.0.221, is gated behind a serde feature that is enabled by default; dev-dependencies pull in the full serde crate with the derive feature to support the crate’s doctest example. There is no build tooling beyond standard cargo build/cargo test/cargo publish, no runtime, and no external services.

Code Quality No dedicated tests/ directory or unit test files exist; the sole executable check is one doctest embedded in the crate-level /// documentation comment in src/lib.rs, exercised via cargo test --doc. #![warn(missing_docs)] is set at the crate root, and every public item carries a doc comment, backed by a .rustfmt.toml and a GitHub Actions workflow presumably running formatting/build checks. Error handling is minimal by design — the type has essentially one fallible path (unwrap() panicking on Null/Undefined) — but real test coverage beyond the single doctest is limited, which weighs against an otherwise clean, well-documented codebase.

API Design The public API is deliberately built to shadow Option’s own method names (unwrap, unwrap_or, map, as_ref, as_deref, is_some), so a Rust developer already familiar with Option can adopt JsOption with almost no new API to learn. The crate-level doc comment ships a copy-pasteable example showing the exact #[serde(default, skip_serializing_if = "JsOption::is_undefined")] attribute combination needed for correct round-tripping — directly solving a well-known serde gotcha with one added dependency and a single attribute, no custom (de)serializer boilerplate required.

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