vld_aide/lib.rs
1//! # vld-aide — Bridge between `vld` and `aide` / `schemars`
2//!
3//! This crate lets you use `vld` validation schemas as the **single source of truth**
4//! for both runtime validation and OpenAPI documentation generated by
5//! [aide](https://docs.rs/aide) (which uses [schemars](https://docs.rs/schemars) internally).
6//!
7//! Instead of duplicating schema definitions with `#[derive(JsonSchema)]` **and**
8//! `vld::schema!`, you define validation rules once and get `schemars` compatibility
9//! for free.
10//!
11//! # Quick Start
12//!
13//! ```rust
14//! use vld::prelude::*;
15//! use vld_aide::impl_json_schema;
16//!
17//! // 1. Define your validated struct as usual
18//! vld::schema! {
19//! #[derive(Debug)]
20//! pub struct User {
21//! pub name: String => vld::string().min(2).max(50),
22//! pub email: String => vld::string().email(),
23//! }
24//! }
25//!
26//! // 2. Bridge to schemars — one line
27//! impl_json_schema!(User);
28//!
29//! // Now `User` implements `schemars::JsonSchema` and can be used in
30//! // aide routers: `aide::axum::Json<User>` etc.
31//! ```
32//!
33//! # Converting arbitrary JSON Schema
34//!
35//! ```rust
36//! use vld_aide::vld_to_schemars;
37//!
38//! let vld_schema = serde_json::json!({
39//! "type": "object",
40//! "required": ["name"],
41//! "properties": {
42//! "name": { "type": "string", "minLength": 1 }
43//! }
44//! });
45//!
46//! let schemars_schema = vld_to_schemars(&vld_schema);
47//! // Returns `schemars::Schema`
48//! ```
49
50/// Convert a `serde_json::Value` (JSON Schema) produced by `vld` into a
51/// `schemars::Schema`.
52///
53/// `schemars::Schema` wraps a JSON value that must be either an object or a bool.
54/// Since vld's `json_schema()` always returns an object, the conversion is direct.
55///
56/// # Example
57///
58/// ```rust
59/// use vld_aide::vld_to_schemars;
60///
61/// let schema = vld_to_schemars(&serde_json::json!({"type": "string", "format": "email"}));
62/// assert_eq!(schema.get("type").unwrap(), "string");
63/// ```
64pub fn vld_to_schemars(value: &serde_json::Value) -> schemars::Schema {
65 value
66 .clone()
67 .try_into()
68 .unwrap_or_else(|_| schemars::Schema::default())
69}
70
71/// Implement `schemars::JsonSchema` for a type that has a `json_schema()` associated
72/// function (generated by `vld::schema!` with the `openapi` feature enabled, or by
73/// `#[derive(Validate)]`).
74///
75/// This makes the type usable with `aide` for OpenAPI documentation generation.
76///
77/// # Usage
78///
79/// ```rust
80/// use vld::prelude::*;
81/// use vld_aide::impl_json_schema;
82///
83/// vld::schema! {
84/// #[derive(Debug)]
85/// pub struct CreateUser {
86/// pub name: String => vld::string().min(2).max(100),
87/// pub email: String => vld::string().email(),
88/// }
89/// }
90///
91/// impl_json_schema!(CreateUser);
92///
93/// // Now CreateUser implements schemars::JsonSchema and works with aide.
94/// ```
95///
96/// You can also pass a custom schema name:
97///
98/// ```rust
99/// # use vld::prelude::*;
100/// # use vld_aide::impl_json_schema;
101/// # vld::schema! {
102/// # #[derive(Debug)]
103/// # pub struct Req { pub x: String => vld::string() }
104/// # }
105/// impl_json_schema!(Req, "CreateUserRequest");
106/// ```
107#[macro_export]
108macro_rules! impl_json_schema {
109 ($ty:ty) => {
110 impl $crate::schemars::JsonSchema for $ty {
111 fn schema_name() -> ::std::borrow::Cow<'static, str> {
112 ::std::borrow::Cow::Borrowed(stringify!($ty))
113 }
114
115 fn schema_id() -> ::std::borrow::Cow<'static, str> {
116 ::std::borrow::Cow::Owned(concat!(module_path!(), "::", stringify!($ty)).to_owned())
117 }
118
119 fn json_schema(
120 gen: &mut $crate::schemars::SchemaGenerator,
121 ) -> $crate::schemars::Schema {
122 #[allow(unused_imports)]
123 use $crate::__VldNestedSchemasFallback as _;
124 for (name, schema_fn) in <$ty>::__vld_nested_schemas() {
125 gen.definitions_mut()
126 .entry(name.to_string())
127 .or_insert_with(|| schema_fn());
128 }
129 $crate::vld_to_schemars(&<$ty>::json_schema())
130 }
131 }
132 };
133 ($ty:ty, $name:expr) => {
134 impl $crate::schemars::JsonSchema for $ty {
135 fn schema_name() -> ::std::borrow::Cow<'static, str> {
136 ::std::borrow::Cow::Borrowed($name)
137 }
138
139 fn schema_id() -> ::std::borrow::Cow<'static, str> {
140 ::std::borrow::Cow::Owned(format!("{}::{}", module_path!(), $name))
141 }
142
143 fn json_schema(
144 gen: &mut $crate::schemars::SchemaGenerator,
145 ) -> $crate::schemars::Schema {
146 #[allow(unused_imports)]
147 use $crate::__VldNestedSchemasFallback as _;
148 for (name, schema_fn) in <$ty>::__vld_nested_schemas() {
149 gen.definitions_mut()
150 .entry(name.to_string())
151 .or_insert_with(|| schema_fn());
152 }
153 $crate::vld_to_schemars(&<$ty>::json_schema())
154 }
155 }
156 };
157}
158
159/// Re-export schemars for use in macros.
160pub use schemars;
161
162#[doc(hidden)]
163pub trait __VldNestedSchemasFallback {
164 #[allow(clippy::type_complexity)]
165 fn __vld_nested_schemas() -> Vec<(&'static str, fn() -> serde_json::Value)> {
166 Vec::new()
167 }
168}
169
170impl<T: ?Sized> __VldNestedSchemasFallback for T {}
171
172/// Prelude module — import everything you need.
173pub mod prelude {
174 pub use crate::impl_json_schema;
175 pub use crate::vld_to_schemars;
176 pub use vld::prelude::*;
177}