use crate::_alloc_prelude::*;
use ref_cast::{ref_cast_custom, RefCastCustom};
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
#[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::from(&self.0).serialize(serializer)
}
}
impl PartialEq<bool> for Schema {
fn eq(&self, other: &bool) -> bool {
self.as_bool() == Some(*other)
}
}
impl PartialEq<Map<String, Value>> for Schema {
fn eq(&self, other: &Map<String, Value>) -> bool {
self.as_object() == Some(other)
}
}
impl PartialEq<Value> for Schema {
fn eq(&self, other: &Value) -> bool {
self.as_value() == other
}
}
impl PartialEq<Schema> for bool {
fn eq(&self, other: &Schema) -> bool {
other == self
}
}
impl PartialEq<Schema> for Map<String, Value> {
fn eq(&self, other: &Schema) -> bool {
other == self
}
}
impl PartialEq<Schema> for Value {
fn eq(&self, other: &Schema) -> bool {
other == self
}
}
impl Schema {
#[must_use]
pub fn new_ref(reference: String) -> Self {
let mut map = Map::new();
map.insert("$ref".to_owned(), Value::String(reference));
Self(Value::Object(map))
}
#[must_use]
pub fn as_value(&self) -> &Value {
&self.0
}
#[must_use]
pub fn as_bool(&self) -> Option<bool> {
self.0.as_bool()
}
#[must_use]
pub fn as_object(&self) -> Option<&Map<String, Value>> {
self.0.as_object()
}
#[must_use]
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(crate) fn try_as_object_mut(&mut self) -> Result<&mut Map<String, Value>, bool> {
match &mut self.0 {
Value::Object(m) => Ok(m),
Value::Bool(b) => Err(*b),
_ => unreachable!(),
}
}
#[must_use]
pub fn to_value(self) -> Value {
self.0
}
#[allow(clippy::missing_panics_doc)]
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.0
.as_object_mut()
.expect("Schema value should be of type Object.")
}
pub fn insert(&mut self, k: String, v: Value) -> Option<Value> {
self.ensure_object().insert(k, v)
}
#[must_use]
pub fn get<Q>(&self, key: &Q) -> Option<&Value>
where
String: core::borrow::Borrow<Q>,
Q: ?Sized + Ord + Eq + core::hash::Hash,
{
self.0.as_object().and_then(|o| o.get(key))
}
#[must_use]
pub fn get_mut<Q>(&mut self, key: &Q) -> Option<&mut Value>
where
String: core::borrow::Borrow<Q>,
Q: ?Sized + Ord + Eq + core::hash::Hash,
{
self.0.as_object_mut().and_then(|o| o.get_mut(key))
}
#[must_use]
pub fn pointer(&self, pointer: &str) -> Option<&Value> {
if let Some(percent_encoded) = pointer.strip_prefix('#') {
let decoded = crate::encoding::percent_decode(percent_encoded)?;
self.0.pointer(&decoded)
} else {
self.0.pointer(pointer)
}
}
#[must_use]
pub fn pointer_mut(&mut self, pointer: &str) -> Option<&mut Value> {
if let Some(percent_encoded) = pointer.strip_prefix('#') {
let decoded = crate::encoding::percent_decode(percent_encoded)?;
self.0.pointer_mut(&decoded)
} else {
self.0.pointer_mut(pointer)
}
}
pub fn remove<Q>(&mut self, key: &Q) -> Option<Value>
where
String: core::borrow::Borrow<Q>,
Q: ?Sized + Ord + Eq + core::hash::Hash,
{
self.0.as_object_mut().and_then(|o| o.remove(key))
}
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"))
}
#[ref_cast_custom]
fn ref_cast(value: &Value) -> &Self;
#[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 core::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> core::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> core::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))
}
}
impl crate::JsonSchema for Schema {
fn schema_name() -> alloc::borrow::Cow<'static, str> {
"Schema".into()
}
fn schema_id() -> alloc::borrow::Cow<'static, str> {
"schemars::Schema".into()
}
fn json_schema(_: &mut crate::SchemaGenerator) -> Schema {
crate::json_schema!({
"type": ["object", "boolean"]
})
}
}
mod ser {
use serde::ser::{Serialize, SerializeMap, SerializeSeq};
use serde_json::Value;
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> {
value: &'a Value,
no_reorder: bool,
}
impl<'a> From<&'a Value> for OrderedKeywordWrapper<'a> {
fn from(value: &'a Value) -> Self {
Self {
value,
no_reorder: false,
}
}
}
impl Serialize for OrderedKeywordWrapper<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
fn serialize_schema_property<S>(
map: &mut S::SerializeMap,
key: &str,
value: &Value,
) -> Result<(), S::Error>
where
S: serde::Serializer,
{
if matches!(key, "examples" | "default") || key.starts_with("x-") {
map.serialize_entry(key, value)
} else {
let no_reorder = matches!(
key,
"properties"
| "patternProperties"
| "dependentSchemas"
| "$defs"
| "definitions"
);
map.serialize_entry(key, &OrderedKeywordWrapper { value, no_reorder })
}
}
match self.value {
Value::Array(array) => {
let mut seq = serializer.serialize_seq(Some(array.len()))?;
for value in array {
seq.serialize_element(&OrderedKeywordWrapper::from(value))?;
}
seq.end()
}
Value::Object(object) if self.no_reorder => {
let mut map = serializer.serialize_map(Some(object.len()))?;
for (key, value) in object {
map.serialize_entry(key, &OrderedKeywordWrapper::from(value))?;
}
map.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) {
serialize_schema_property::<S>(&mut map, key, value)?;
}
}
for (key, value) in object {
if !ORDERED_KEYWORDS_START.contains(&key.as_str())
&& !ORDERED_KEYWORDS_END.contains(&key.as_str())
{
serialize_schema_property::<S>(&mut map, key, value)?;
}
}
for key in ORDERED_KEYWORDS_END {
if let Some(value) = object.get(key) {
serialize_schema_property::<S>(&mut map, key, value)?;
}
}
map.end()
}
Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_) => {
self.value.serialize(serializer)
}
}
}
}
}