fieldmasker 0.0.1

A utility for selecting and filtering response fields via field masks.
Documentation
# fieldmasker

[![Crates.io](https://img.shields.io/crates/v/fieldmasker.svg)](https://crates.io/crates/fieldmasker)
[![Docs.rs](https://docs.rs/fieldmasker/badge.svg)](https://docs.rs/fieldmasker)
[![License](https://img.shields.io/crates/l/fieldmasker.svg)](#license)

A high-performance, type-safe implementation of **response field masks** in Rust,  
inspired by Google APIs ([FieldMask][protobuf]) and [Google Maps API Field Mask][gmaps]
behavior.

Field masks let API clients request only the fields they need, which:

- Reduces **response size** and **serialization cost**.
- Avoids unnecessary **CPU work** (you can skip computing unneeded fields).
- Allows **usage tracking** per field.
- Provides **strong validation**: unknown fields are rejected.

---

## Features

- **Pure Rust core**, no HTTP framework dependencies.
- **Serde integration**, mask filtering happens during serialization.
- **Derive macro** (`#[derive(MaskSpec)]`) generates field mask metadata from your types.
- **Axum integration** (optional feature), extractors for `?fields=` query or `x-fields` header.
- **Strict validation**, unknown field paths produce an error.
- **Wildcard support** (`*` to select all fields at a level).
- **Nested paths** like `places.displayName.text` are supported.
- Early-gating helpers (`contains_exact`, `intersects`) to skip expensive computations.

---

## Basic usage

```toml
[dependencies]
fieldmasker = "0.1"

# For Axum extractors:
# fieldmasker = { version = "0.1", features = ["axum"] }
```

```rust
use fieldmasker::{FieldMask, Masked, MaskSpec};
use serde::Serialize;

#[derive(Serialize, MaskSpec)]
#[serde(rename_all = "camelCase")]
struct DisplayName {
    text: String,
    language_code: String,
}

#[derive(Serialize, MaskSpec)]
#[serde(rename_all = "camelCase")]
struct Place {
    id: String,
    formatted_address: String,
    display_name: DisplayName,
}

#[derive(Serialize, MaskSpec)]
struct SearchResponse {
    places: Vec<Place>,
}

fn main() {
    let data = SearchResponse {
        places: vec![Place {
            id: "abc".into(),
            formatted_address: "1 High St".into(),
            display_name: DisplayName {
                text: "Foo".into(),
                language_code: "en".into(),
            },
        }],
    };

    let mask = FieldMask::parse("places.formattedAddress,places.displayName.text").unwrap();
    let masked = Masked::new(data, mask);

    println!("{}", serde_json::to_string_pretty(&masked).unwrap());
}
```

Output:

```json
{
  "places": [
    {
      "formattedAddress": "1 High St",
      "displayName": {
        "text": "Foo"
      }
    }
  ]
}
```

# Axum integration

Enable the axum feature to get request extractors that validate and parse masks:

```rust
use axum::{routing::get, Json, Router};
use fieldmasker::{Masked, MaskSpec};
use fieldmasker::axum_integration::{MaskRequired, MaskRejection};
use serde::Serialize;

#[derive(Serialize, MaskSpec)]
struct Item {
    id: String,
    name: String,
    #[serde(skip)]
    secret: String
}

#[derive(Serialize, MaskSpec)]
struct List {
    items: Vec<Item>
}

async fn list(
    MaskRequired::<List>(mask, _): MaskRequired<List>
) -> Result<Json<Masked<List>>, MaskRejection> {
    let data = List {
        items: vec![
            Item { id: "1".into(), name: "A".into(), secret: "s1".into() },
            Item { id: "2".into(), name: "B".into(), secret: "s2".into() },
        ],
    };
    Ok(Json(Masked::new(data, mask)))
}

#[tokio::main]
async fn main() {
    let app = Router::new().route("/list", get(list));
    axum::serve(tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap(), app)
        .await
        .unwrap();
}
```

Example request:

```bash
$ curl 'http://localhost:3000/list?fields=items.name'
```

Response:

```json
{
  "items": [
    {
      "name": "A"
    },
    {
      "name": "B"
    }
  ]
}
```

[protobuf]: https://protobuf.dev/reference/protobuf/google.protobuf/#fieldmask

[gmaps]: https://developers.google.com/maps/documentation/places/web-service/field-masks