serde_field_result 0.1.0

Field-level Serde recovery for schema-drift-tolerant API clients
Documentation

serde_field_result

Docs.rs CI

Field-level Serde recovery for schema-drift-tolerant API clients.

serde_field_result = "0.1"

The core type is Field<T>:

use serde::Deserialize;
use serde_field_result::Field;

#[derive(Deserialize)]
struct Row {
    #[serde(default)] // required if absent fields should become Field::Missing
    price: Field<f64>,
}

A field can be:

Field::Missing
Field::Valid(value)
Field::Invalid(error)

This lets one bad field fail softly without destroying an otherwise useful response.

You can inspect invalid fields without pattern matching every variant:

let mut warnings = Vec::new();

if let Some(error) = row.price.error() {
    warnings.push(format!("bad price: {error}"));
}

Field<T> is intentionally a three-state type, not a JSON patch type or a generic T: Deserialize buffering wrapper. For ordinary Field<T> values, an absent field, null, and Serde unit all decode as Field::Missing. Use Field<Option<T>> when explicit null or unit should be a valid None while an absent field remains Field::Missing.

For JSON-specific raw capture, enable the json feature. Use JsonField<T> when the decoded value should own its data and invalid fields should retain an owned Box<serde_json::value::RawValue>. Use BorrowedJsonField<'de, T> when the original JSON input outlives the decoded value and you want invalid raw JSON and valid nested values to borrow from that input. BorrowedJsonField has the same borrowing limitations as serde_json: escaped strings may not deserialize into &'de str, so use String or Cow<'de, str> when string values may need unescaping.

JsonField<Option<T>> and BorrowedJsonField<Option<T>> do not distinguish explicit JSON null from missing; JSON null is always Missing.

serde_field_result = { version = "0.1", features = ["json"] }

Default features enable std. For no_std environments with alloc, disable default features and enable alloc explicitly:

serde_field_result = { version = "0.1", default-features = false, features = ["alloc"] }

The json feature also works without std; it enables alloc automatically.

use serde::Deserialize;
use serde_field_result::{BorrowedJsonField, JsonField};

#[derive(Deserialize)]
struct OwnedNested {
    value: String,
}

#[derive(Deserialize)]
struct BorrowedNested<'a> {
    #[serde(borrow)]
    value: &'a str,
}

#[derive(Deserialize)]
struct OwnedRow {
    #[serde(default)]
    nested: JsonField<OwnedNested>,
}

#[derive(Deserialize)]
struct BorrowedRow<'a> {
    #[serde(default, borrow)]
    nested: BorrowedJsonField<'a, BorrowedNested<'a>>,
}

For custom scalar-like types, prefer implementing ScalarFieldDecode. Implement FieldDecode directly only when you are prepared to consume and drain every unexpected Serde shape. In custom visitors, prefer invalid_seq and invalid_map for unexpected arrays or objects; they drain the compound value before returning Field::Invalid, preserving following sibling fields.

Default scalar type-mismatch errors are stored without formatting an owned error message up front. Custom decoders can still use FieldError::new when an error needs to include dynamic details.

The built-in primitive integer and float decoders accept JSON numbers, not numeric strings. Implement a custom ScalarFieldDecode wrapper when an API uses strings such as "42" or "12.5" for numbers. The built-in f32 and f64 field decoders reject non-finite values. Integer-to-float conversions follow Rust cast semantics and may lose precision.