schemars/schemars/src/schema.rs

240 lines
6.7 KiB
Rust

/*!
JSON Schema types.
*/
use ref_cast::ref_cast_custom;
use ref_cast::RefCastCustom;
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
/// A JSON Schema.
#[derive(Debug, Clone, PartialEq, RefCastCustom)]
#[repr(transparent)]
pub struct Schema(Value);
impl<'de> Deserialize<'de> for Schema {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = Value::deserialize(deserializer)?;
Schema::validate(&value)?;
Ok(Schema(value))
}
}
impl Serialize for Schema {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
ser::OrderedKeywordWrapper(&self.0).serialize(serializer)
}
}
impl Schema {
pub fn new() -> Self {
Self(Value::Object(Map::new()))
}
pub fn new_ref(reference: String) -> Self {
let mut map = Map::new();
map.insert("$ref".to_owned(), Value::String(reference));
Self(Value::Object(map))
}
pub fn as_value(&self) -> &Value {
&self.0
}
pub fn as_bool(&self) -> Option<bool> {
self.0.as_bool()
}
pub fn as_object(&self) -> Option<&Map<String, Value>> {
self.0.as_object()
}
pub fn as_object_mut(&mut self) -> Option<&mut Map<String, Value>> {
self.0.as_object_mut()
}
pub(crate) fn try_to_object(self) -> Result<Map<String, Value>, bool> {
match self.0 {
Value::Object(m) => Ok(m),
Value::Bool(b) => Err(b),
_ => unreachable!(),
}
}
pub fn to_value(self) -> Value {
self.0
}
pub fn ensure_object(&mut self) -> &mut Map<String, Value> {
if let Some(b) = self.as_bool() {
let mut map = Map::new();
if !b {
map.insert("not".into(), Value::Object(Map::new()));
}
self.0 = Value::Object(map);
}
self.as_object_mut()
.expect("Schema value should be of type Object.")
}
pub(crate) fn has_type(&self, ty: &str) -> bool {
match self.0.get("type") {
Some(Value::Array(values)) => values.iter().any(|v| v.as_str() == Some(ty)),
Some(Value::String(s)) => s == ty,
_ => false,
}
}
fn validate<E: serde::de::Error>(value: &Value) -> Result<(), E> {
use serde::de::Unexpected;
let unexpected = match value {
Value::Bool(_) | Value::Object(_) => return Ok(()),
Value::Null => Unexpected::Unit,
Value::Number(n) => {
if let Some(u) = n.as_u64() {
Unexpected::Unsigned(u)
} else if let Some(i) = n.as_i64() {
Unexpected::Signed(i)
} else if let Some(f) = n.as_f64() {
Unexpected::Float(f)
} else {
unreachable!()
}
}
Value::String(s) => Unexpected::Str(s),
Value::Array(_) => Unexpected::Seq,
};
Err(E::invalid_type(unexpected, &"object or boolean"))
}
#[allow(unsafe_code)]
#[ref_cast_custom]
fn ref_cast(value: &Value) -> &Self;
#[allow(unsafe_code)]
#[ref_cast_custom]
fn ref_cast_mut(value: &mut Value) -> &mut Self;
}
impl From<Schema> for Value {
fn from(v: Schema) -> Value {
v.0
}
}
impl std::convert::TryFrom<Value> for Schema {
type Error = serde_json::Error;
fn try_from(value: Value) -> serde_json::Result<Schema> {
Schema::validate(&value)?;
Ok(Schema(value))
}
}
impl<'a> std::convert::TryFrom<&'a Value> for &'a Schema {
type Error = serde_json::Error;
fn try_from(value: &Value) -> serde_json::Result<&Schema> {
Schema::validate(value)?;
Ok(Schema::ref_cast(value))
}
}
impl<'a> std::convert::TryFrom<&'a mut Value> for &'a mut Schema {
type Error = serde_json::Error;
fn try_from(value: &mut Value) -> serde_json::Result<&mut Schema> {
Schema::validate(value)?;
Ok(Schema::ref_cast_mut(value))
}
}
impl Default for Schema {
fn default() -> Self {
Self(Value::Object(Map::new()))
}
}
impl From<Map<String, Value>> for Schema {
fn from(o: Map<String, Value>) -> Self {
Schema(Value::Object(o))
}
}
impl From<bool> for Schema {
fn from(b: bool) -> Self {
Schema(Value::Bool(b))
}
}
mod ser {
use serde::ser::{Serialize, SerializeMap, SerializeSeq};
use serde_json::Value;
// The order of properties in a JSON Schema object is insignificant, but we explicitly order
// some of them here to make them easier for a human to read. All other properties are ordered
// either lexicographically (by default) or by insertion order (if `preserve_order` is enabled)
const ORDERED_KEYWORDS_START: [&str; 7] = [
"$id",
"$schema",
"title",
"description",
"type",
"format",
"properties",
];
const ORDERED_KEYWORDS_END: [&str; 2] = ["$defs", "definitions"];
pub(super) struct OrderedKeywordWrapper<'a>(pub &'a Value);
impl Serialize for OrderedKeywordWrapper<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self.0 {
Value::Array(array) => {
let mut seq = serializer.serialize_seq(Some(array.len()))?;
for value in array {
seq.serialize_element(&OrderedKeywordWrapper(value))?;
}
seq.end()
}
Value::Object(object) => {
let mut map = serializer.serialize_map(Some(object.len()))?;
for key in ORDERED_KEYWORDS_START {
if let Some(value) = object.get(key) {
map.serialize_entry(key, &OrderedKeywordWrapper(value))?;
}
}
for (key, value) in object {
if !ORDERED_KEYWORDS_START.contains(&key.as_str())
&& !ORDERED_KEYWORDS_END.contains(&key.as_str())
{
map.serialize_entry(key, &OrderedKeywordWrapper(value))?;
}
}
for key in ORDERED_KEYWORDS_END {
if let Some(value) = object.get(key) {
map.serialize_entry(key, &OrderedKeywordWrapper(value))?;
}
}
map.end()
}
_ => self.0.serialize(serializer),
}
}
}
}