#![deny(
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unsafe_code,
unused_import_braces,
unused_qualifications
)]
use serde::{Deserialize, Serialize};
pub use url::Url;
use std::collections::HashMap;
pub use std::convert::TryFrom;
pub mod error;
pub mod id;
pub mod property;
mod validation;
use crate::error::Result;
use crate::id::*;
use crate::property::*;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Schema(SchemaInner);
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(untagged)]
enum SchemaInner {
Schema(SchemaDefinition),
Boolean(bool),
}
impl Schema {
pub fn draft_version(&self) -> Option<&str> {
match &self.0 {
SchemaInner::Schema(SchemaDefinition {
schema: Some(schema),
..
}) => schema
.path_segments()
.and_then(|mut segments| segments.next()),
_ => None,
}
}
fn as_definition(&self) -> Option<&SchemaDefinition> {
match &self.0 {
SchemaInner::Schema(definition @ SchemaDefinition { .. }) => Some(definition),
_ => None,
}
}
pub fn id(&self) -> Option<&SchemaId> {
self.as_definition().and_then(|d| d.id.as_ref())
}
pub fn schema(&self) -> Option<&Url> {
self.as_definition().and_then(|d| d.schema.as_ref())
}
pub fn description(&self) -> Option<&str> {
self.as_definition().and_then(|d| d.description.as_deref())
}
pub fn specification(&self) -> Option<&PropertyInstance> {
match &self.0 {
SchemaInner::Schema(SchemaDefinition {
specification:
Some(Property::Value(specification @ PropertyInstance::Object { .. })),
..
}) => Some(specification),
SchemaInner::Schema(SchemaDefinition {
specification: Some(Property::Value(specification @ PropertyInstance::Array { .. })),
..
}) => Some(specification),
_ => None,
}
}
pub fn properties(&self) -> Option<&HashMap<String, Property>> {
match self.specification() {
Some(PropertyInstance::Object { properties, .. }) => Some(properties),
_ => None,
}
}
pub fn required_properties(&self) -> Option<&Vec<String>> {
match self.specification() {
Some(PropertyInstance::Object { required, .. }) => required.as_ref(),
_ => None,
}
}
pub fn as_null(&self) -> Option<&PropertyInstance> {
match self.specification() {
Some(null @ PropertyInstance::Null) => Some(null),
_ => None,
}
}
pub fn as_boolean(&self) -> Option<&PropertyInstance> {
match self.specification() {
Some(boolean @ PropertyInstance::Boolean) => Some(boolean),
_ => None,
}
}
pub fn as_integer(&self) -> Option<&PropertyInstance> {
match self.specification() {
Some(integer @ PropertyInstance::Integer { .. }) => Some(integer),
_ => None,
}
}
pub fn as_object(&self) -> Option<&PropertyInstance> {
match self.specification() {
Some(object @ PropertyInstance::Object { .. }) => Some(object),
_ => None,
}
}
pub fn as_array(&self) -> Option<&PropertyInstance> {
match self.specification() {
Some(array @ PropertyInstance::Array { .. }) => Some(array),
_ => None,
}
}
pub fn as_number(&self) -> Option<&PropertyInstance> {
match self.specification() {
Some(number @ PropertyInstance::Number { .. }) => Some(number),
_ => None,
}
}
pub fn validate(&self, json: &serde_json::Value) -> std::result::Result<(), Vec<String>> {
match self.0 {
SchemaInner::Schema(SchemaDefinition {
specification: Some(Property::Value(ref prop)),
..
}) => prop.validate(json),
SchemaInner::Schema(SchemaDefinition {
specification: Some(Property::Ref(_)),
..
}) => unimplemented!(),
SchemaInner::Boolean(true) => {
eprintln!(r#"your schema is just "true", everything goes"#);
Ok(())
}
SchemaInner::Boolean(false) => Err(vec![String::from(
r#""the scheme "false" will never validate"#,
)]),
_ => Ok(()),
}
}
}
impl TryFrom<serde_json::Value> for Schema {
type Error = crate::error::Error;
fn try_from(v: serde_json::Value) -> Result<Schema> {
Ok(serde_json::from_value(v)?)
}
}
impl TryFrom<&str> for Schema {
type Error = crate::error::Error;
fn try_from(s: &str) -> Result<Schema> {
Ok(serde_json::from_str(s)?)
}
}
impl TryFrom<String> for Schema {
type Error = crate::error::Error;
fn try_from(s: String) -> Result<Schema> {
Ok(serde_json::from_str(&s)?)
}
}
impl TryFrom<&str> for SchemaDefinition {
type Error = crate::error::Error;
fn try_from(s: &str) -> Result<SchemaDefinition> {
Ok(serde_json::from_str(s)?)
}
}
impl TryFrom<String> for SchemaDefinition {
type Error = crate::error::Error;
fn try_from(s: String) -> Result<SchemaDefinition> {
Ok(serde_json::from_str(&s)?)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub(crate) struct SchemaDefinition {
#[serde(rename = "$id")]
pub id: Option<SchemaId>,
#[serde(rename = "$schema")]
pub schema: Option<Url>,
pub description: Option<String>,
pub dependencies: Option<HashMap<String, Vec<String>>>,
#[serde(flatten)]
pub specification: Option<Property>,
#[serde(skip_serializing_if = "Option::is_none")]
pub definitions: Option<HashMap<String, SchemaDefinition>>,
}