rydantic 0.7.0-beta.1

Type-driven validation, JSON Schema, Cap'n Proto, Axum and SQLx models for Rust
Documentation
//! Unified JSON models for Rust.
//!
//! ```
//! use rydantic::{Model, SchemaMode};
//!
//! #[derive(Debug, Model)]
//! struct User {
//!     #[model(trim, min_length = 3)]
//!     name: String,
//!     #[model(ge = 18, le = 120)]
//!     age: u8,
//! }
//!
//! let user = User::parse_json(r#"{"name":" Alice ","age":25}"#)?;
//! assert_eq!(user.name, "Alice");
//! assert!(User::json_schema(SchemaMode::Output).is_object());
//! # Ok::<(), rydantic::ValidationErrors>(())
//! ```

extern crate self as rydantic;

mod asynchronous;
#[cfg(feature = "axum")]
pub mod axum;
#[cfg(feature = "sqlx")]
pub mod sql;
#[cfg(all(feature = "sqlx", feature = "derive"))]
pub use rydantic_derive::SqlModel;
#[cfg(feature = "sqlx")]
pub use sql::{SqlError, SqlModel};
#[cfg(feature = "sqlx")]
pub use sqlx;
mod borrowed;
#[cfg(feature = "capnp")]
pub mod capnp_support;
#[cfg(feature = "chrono")]
mod dates;
#[cfg(feature = "decimal")]
mod decimal;
mod direct;
#[cfg(feature = "capnp")]
pub use capnp;
#[cfg(feature = "capnp")]
pub use capnp_support::CapnpSchema;
#[cfg(feature = "capnp")]
pub use capnp_support::{CapnpEnum, CapnpModel};
#[cfg(all(feature = "capnp", feature = "derive"))]
pub use rydantic_derive::capnp_models;
#[cfg(feature = "dynamic")]
mod dynamic;
mod error;
mod object;
pub mod rules;
pub mod schema;
pub mod stream;
mod types;

pub use asynchronous::{AsyncCheck, AsyncValidated, BoxFuture};
pub use borrowed::{BorrowModel, BorrowType, BorrowValidated, Input};
#[cfg(feature = "chrono")]
pub use chrono;
#[cfg(feature = "dynamic")]
pub use dynamic::{DynamicModel, SchemaError};
pub use error::{Context, PathSegment, Stage, ValidationError, ValidationErrors};
pub use object::{ExtraFields, ObjectType};
#[cfg(feature = "decimal")]
pub use rust_decimal;
#[cfg(feature = "derive")]
pub use rydantic_derive::Model;
pub use schema::{LocalRule, SchemaContext, SchemaMode, SchemaReport};
pub use serde_json::value::RawValue;
pub use serde_json::{json, Value};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TypeKind {
    String,
    Number,
    Boolean,
    Array,
    Object,
    Any,
    Decimal,
}

/// Extension point for JSON-compatible Rust types.
///
/// A decoder must record an error whenever it returns None. It may return a
/// constructed value together with constraint errors; public entry points reject it.
pub trait JsonType: Sized {
    fn decode(value: Value, ctx: &mut Context, coerce: bool) -> Option<Self>;
    /// Decode a raw subtree. Custom types can override this to bypass Value entirely.
    fn decode_input(input: Input<'_>, ctx: &mut Context, coerce: bool) -> Option<Self> {
        Self::decode(input.into_value(ctx)?, ctx, coerce)
    }
    fn prepare_default(self, ctx: &mut Context) -> Option<Self> {
        let value = self.encode_input(ctx);
        Self::decode(value, ctx, false)
    }
    fn encode(&self, ctx: &mut Context) -> Value;
    /// Representation using input aliases and including output-excluded fields.
    /// Used to validate typed defaults without losing required input information.
    fn encode_input(&self, ctx: &mut Context) -> Value {
        self.encode(ctx)
    }
    fn schema(ctx: &mut SchemaContext, coerce: bool) -> Value;
    fn validate_into(&self, _ctx: &mut Context) {}
    fn normalize(self, rules: &rules::Rules, ctx: &mut Context) -> Option<Self> {
        if !rules.normalizes() {
            return Some(self);
        }
        let mut value = self.encode_input(ctx);
        rules.prepare(&mut value);
        Self::decode(value, ctx, false)
    }
    fn validate_rules(&self, rules: &rules::Rules, ctx: &mut Context) {
        rules.validate(&self.encode(ctx), ctx);
    }
    fn async_required(_seen: &mut std::collections::HashSet<&'static str>) -> bool {
        false
    }
    fn missing() -> Option<Self> {
        None
    }
    fn allows_missing() -> bool {
        false
    }
    fn kind() -> TypeKind {
        TypeKind::Any
    }
}

/// Marker traits make invalid attribute/type combinations fail during compilation.
pub trait StringType {}
pub trait NumericType {}
pub trait LengthType {}

/// High-level model API generated by `#[derive(Model)]`.
pub trait Model: JsonType {
    /// Strict by default. Explicit field-level `coerce` is applied by the derive.
    fn parse_json(input: &str) -> Result<Self, ValidationErrors> {
        let raw = serde_json::from_str::<&RawValue>(input).map_err(borrowed::json_error)?;
        Self::parse_raw(raw)
    }
    fn parse_slice(input: &[u8]) -> Result<Self, ValidationErrors> {
        let raw = serde_json::from_slice::<&RawValue>(input).map_err(borrowed::json_error)?;
        Self::parse_raw(raw)
    }
    fn parse_raw(raw: &RawValue) -> Result<Self, ValidationErrors> {
        asynchronous::sync_guard(Self::async_required(&mut std::collections::HashSet::new()))?;
        let mut ctx = Context::new();
        let model = Self::decode_input(Input::Borrowed(raw), &mut ctx, false);
        ctx.finish(model)
    }
    fn parse_value(value: Value) -> Result<Self, ValidationErrors> {
        asynchronous::sync_guard(Self::async_required(&mut std::collections::HashSet::new()))?;
        let mut ctx = Context::new();
        let model = Self::decode(value, &mut ctx, false);
        ctx.finish(model)
    }
    /// Recheck a value constructed or mutated in Rust. Does not normalize it.
    fn validate(&self) -> Result<(), ValidationErrors> {
        asynchronous::sync_guard(Self::async_required(&mut std::collections::HashSet::new()))?;
        let mut ctx = Context::new();
        self.validate_into(&mut ctx);
        ctx.result()
    }
    /// Revalidate before serialization, including fields excluded from the output.
    fn dump(&self) -> Result<Value, ValidationErrors> {
        self.validate()?;
        let mut ctx = Context::new();
        let value = self.encode(&mut ctx);
        ctx.finish(Some(value))
    }
    fn dump_json(&self) -> Result<String, ValidationErrors> {
        let value = self.dump()?;
        serde_json::to_string(&value).map_err(|error| {
            ValidationError::new("serialization", error.to_string())
                .with_stage(Stage::Serialize)
                .into()
        })
    }
    fn json_schema(mode: SchemaMode) -> Value {
        let mut ctx = SchemaContext::new(mode);
        let root = Self::schema(&mut ctx, false);
        ctx.finish(root)
    }
    fn schema_report(mode: SchemaMode) -> SchemaReport {
        SchemaReport::new(Self::json_schema(mode))
    }
    fn parse_reader<R: std::io::Read>(reader: R) -> Result<Self, ValidationErrors> {
        let raw =
            serde_json::from_reader::<_, Box<RawValue>>(reader).map_err(borrowed::json_error)?;
        Self::parse_raw(&raw)
    }
    fn parse_json_async(
        input: &str,
    ) -> impl std::future::Future<Output = Result<Self, ValidationErrors>> + Send
    where
        Self: AsyncCheck + Send,
    {
        async move {
            let raw = serde_json::from_str::<&RawValue>(input).map_err(borrowed::json_error)?;
            Self::parse_raw_async(raw).await
        }
    }
    fn parse_raw_async(
        raw: &RawValue,
    ) -> impl std::future::Future<Output = Result<Self, ValidationErrors>> + Send
    where
        Self: AsyncCheck + Send,
    {
        async move {
            let mut ctx = Context::new();
            let model = Self::decode_input(Input::Borrowed(raw), &mut ctx, false);
            let model = ctx.finish(model)?;
            let mut ctx = Context::new();
            model.check_async(&mut ctx).await;
            ctx.result()?;
            Ok(model)
        }
    }
    fn parse_value_async(
        value: Value,
    ) -> impl std::future::Future<Output = Result<Self, ValidationErrors>> + Send
    where
        Self: AsyncCheck + Send,
    {
        async move {
            let mut ctx = Context::new();
            let model = Self::decode(value, &mut ctx, false);
            let model = ctx.finish(model)?;
            let mut ctx = Context::new();
            model.check_async(&mut ctx).await;
            ctx.result()?;
            Ok(model)
        }
    }
    fn validate_async(
        &self,
    ) -> impl std::future::Future<Output = Result<(), ValidationErrors>> + Send
    where
        Self: AsyncCheck,
    {
        async move {
            let mut ctx = Context::new();
            self.validate_into(&mut ctx);
            ctx.result()?;
            let mut ctx = Context::new();
            self.check_async(&mut ctx).await;
            ctx.result()
        }
    }
    fn dump_async(
        &self,
    ) -> impl std::future::Future<Output = Result<Value, ValidationErrors>> + Send
    where
        Self: AsyncCheck,
    {
        async move {
            self.validate_async().await?;
            let mut ctx = Context::new();
            let value = self.encode(&mut ctx);
            ctx.finish(Some(value))
        }
    }
    fn dump_json_async(
        &self,
    ) -> impl std::future::Future<Output = Result<String, ValidationErrors>> + Send
    where
        Self: AsyncCheck,
    {
        async move { Ok(self.dump_async().await?.to_string()) }
    }
    fn parse_validated_json_async(
        input: &str,
    ) -> impl std::future::Future<Output = Result<AsyncValidated<Self>, ValidationErrors>> + Send
    where
        Self: AsyncCheck + Send,
    {
        async move {
            Self::parse_json_async(input)
                .await
                .map(AsyncValidated::checked)
        }
    }
    fn parse_validated_json(input: &str) -> Result<Validated<Self>, ValidationErrors> {
        Self::parse_json(input).map(Validated)
    }
}

/// Read-only access to a successfully checked model. No DerefMut or unchecked constructor.
/// Interior-mutability types can weaken this guarantee; their JsonType implementation
/// must document that behavior.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Validated<T: Model>(T);
impl<T: Model> Validated<T> {
    pub fn new(value: T) -> Result<Self, ValidationErrors> {
        value.validate()?;
        Ok(Self(value))
    }
    pub fn get(&self) -> &T {
        &self.0
    }
    pub fn into_inner(self) -> T {
        self.0
    }
}
impl<T: Model> std::ops::Deref for Validated<T> {
    type Target = T;
    fn deref(&self) -> &T {
        &self.0
    }
}
impl<T: Model> serde::Serialize for Validated<T> {
    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        let value = self.0.dump().map_err(serde::ser::Error::custom)?;
        serde::Serialize::serialize(&value, serializer)
    }
}
impl<'de, T: Model> serde::Deserialize<'de> for Validated<T> {
    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        let raw = <Box<RawValue> as serde::Deserialize>::deserialize(deserializer)?;
        T::parse_raw(&raw)
            .map(Self)
            .map_err(serde::de::Error::custom)
    }
}

#[doc(hidden)]
pub mod __private {
    pub use crate::object::{check_names, flatten_schema, merge};
    pub use regex;
    pub use serde;
    pub use serde_json;
    pub fn number(literal: &str) -> serde_json::Number {
        literal
            .parse()
            .expect("numeric literal checked by the derive macro")
    }
}