Crate magnet_schema

source ·
Expand description

Magnet, a JSON/BSON schema generator

These two related crates, magnet_schema and magnet_derive define a trait, BsonSchema, and a proc-macro derive for the same trait, which allows types to easily implement JSON schema validation for use with MongoDB.

The trait defines a single function, bson_schema(), that returns a BSON Document describing the validation schema of the type based on its fields (for structs and tuples), variants (for enums), or elements/entries (for array- and map-like types).

The types are expected to be serialized and deserialized using Serde, and generally Magnet will try very hard to respect #[serde(...)] annotations as faithfully as possible, but no Serialize + Deserialize trait bounds are enforced on the types as this is not strictly necessary.

Usage Example

#[macro_use]
extern crate serde_derive;
extern crate serde;
#[macro_use]
extern crate bson;
#[macro_use]
extern crate magnet_derive;
extern crate magnet_schema;

use std::collections::HashSet;
use magnet_schema::BsonSchema;

#[derive(BsonSchema)]
struct Person {
    name: String,
    nicknames: HashSet<String>,
    age: usize,
    contact: Option<Contact>,
}

#[derive(BsonSchema, Serialize, Deserialize)]
#[serde(tag = "type", content = "value")]
enum Contact {
    Email(String),
    Phone(u64),
}

fn main() {
    println!("{:#?}", Person::bson_schema());
}

Custom Attributes

  • #[serde(rename = "new_name")]: Magnet will respect Serde’s field/variant renaming attribute by default.

  • #[serde(rename_all = "rename_rule")]: it will also respect Serde’s rename_all rule.

  • #[magnet(min_incl = "-1337")] — enforces an inclusive minimum for fields of numeric types

  • #[magnet(min_excl = "42")] — enforces an exclusive “minimum” (infimum) for fields of numeric types

  • #[magnet(max_incl = "63")] — enforces an inclusive maximum for fields of numeric types

  • #[magnet(max_excl = "64")] — enforces an exclusive “maximum” (supremum) for fields of numeric types

Development Roadmap

  • [x] Define BsonSchema trait

  • [x] impl BsonSchema for most primitives/std:: types

  • [x] Cargo features for implementing BsonSchema for “atomic” types in foreign crates, for instance, url::Url and uuid::Uuid.

  • [x] #[derive(BsonSchema)] on regular, named-field structs

  • [x] #[derive(BsonSchema)] on newtype structs

  • [x] #[derive(BsonSchema)] on tuple structs

  • [x] #[derive(BsonSchema)] on unit structs

  • [ ] #[derive(BsonSchema)] on enums

    • [x] unit variants

    • [ ] newtype variants

      • [x] newtype variants around structs and maps

      • [ ] newtype variants around inner, transitive enums

    • [x] tuple variants

    • [x] struct variants

    • [x] respect Serde tagging conventions: external/internal/adjacent

  • [x] Respect more #[serde(...)] attributes, for example: rename, rename_all

  • [ ] Respect more #[serde(...)] attributes, for example: default, skip, skip_serializing, skip_deserializing

  • [x] Handle generic types in proc-macro derive

  • [ ] Standard (non-MongoDB-specific) JSON schema support (approach?)

  • [x] unit tests

  • [x] documentation for attributes

  • [ ] impl BsonSchema for more esoteric primitives/standard types such as specialization of [u8]/Vec<u8> as binary, adding a validation regex "pattern" to Path and PathBuf, etc.

  • [ ] Add our own attributes

    • [x] magnet(rename = "...") — renames the field or variant to the name specified as the value of the rename attribute

    • [ ] magnet(regex = "foo?|[ba]r{3,6}") — custom validation; implies "type": "string". Patterns are implicitly enclosed between ^...$ for robustness.

    • [ ] magnet(unsafe_regex = "^nasty-regex$") — just like magnet(regex), but no automatic enclosing in ^...$ happens. This may allow invalid data to pass validation!!!

    • [ ] magnet(non_empty) — for collections: same as min_length = "1".

    • [ ] magnet(min_length = "16") — for collections/tuples etc.

    • [ ] magnet(max_length = "32") — for collections/tuples etc.

    • [x] magnet(min_incl = "-1337") — inclusive minimum for numbers

    • [x] magnet(min_excl = "42") — exclusive “minimum” (infimum) for numbers

    • [x] magnet(max_incl = "63") — inclusive maximum for numbers

    • [x] magnet(max_excl = "64") — exclusive “maximum” (supremum) for numbers

    • [ ] magnet(allow_extra_fields) — sets "additionalProperties": true. By default, Magnet sets this field to false for maximal safety. Allowing arbitrary data to be inserted in a DB is generally a Bad Idea, as it may lead to code injection (MongoDB supports storing JavaScript in a collection! Madness!) or at best, denial-of-service (DoS) attacks.

    • [ ] magnet(allow_extra_fields = "ExtraFieldType") — sets "additionalProperties": ExtraFieldType::bson_schema(), so that unlisted additional object fields are allowed provided that they conform to the schema of the specified type.

Traits

Types which can be expressed/validated by a MongoDB-flavored JSON schema.