use super::{BTreeMap, ValidationOptions, Value};
use serde_json::Map;
#[derive(Clone)]
pub struct ObjectSchema {
schema: BTreeMap<String, Value>,
}
impl ObjectSchema {
fn new() -> Self {
let mut schema = BTreeMap::new();
schema.insert("type".into(), Value::String("object".into()));
Self { schema }
}
#[must_use]
pub fn required(mut self, name: &str, schema: impl Into<Value>) -> Self {
self.ensure_properties();
self.ensure_required();
if let Some(props) = self
.schema
.get_mut("properties")
.and_then(Value::as_object_mut)
{
props.insert(name.into(), schema.into());
}
if let Some(req) = self
.schema
.get_mut("required")
.and_then(Value::as_array_mut)
{
let val = Value::String(name.into());
if !req.contains(&val) {
req.push(val);
}
}
self
}
#[must_use]
pub fn optional(mut self, name: &str, schema: impl Into<Value>) -> Self {
self.ensure_properties();
if let Some(props) = self
.schema
.get_mut("properties")
.and_then(Value::as_object_mut)
{
props.insert(name.into(), schema.into());
}
self
}
#[must_use]
pub fn properties(mut self, map: BTreeMap<String, Value>) -> Self {
self.ensure_properties();
if let Some(props) = self
.schema
.get_mut("properties")
.and_then(Value::as_object_mut)
{
for (k, v) in map {
props.insert(k, v);
}
}
self
}
#[must_use]
pub fn strict(self) -> Self {
self.additional_properties(Value::Bool(false))
}
#[must_use]
pub fn loose(self) -> Self {
self.additional_properties(Value::Bool(true))
}
#[must_use]
pub fn catchall(self, schema: impl Into<Value>) -> Self {
self.additional_properties(schema.into())
}
#[must_use]
pub fn pattern_property(mut self, re: &str, schema: impl Into<Value>) -> Self {
self.ensure_pattern_properties();
if let Some(pp) = self
.schema
.get_mut("patternProperties")
.and_then(Value::as_object_mut)
{
pp.insert(re.into(), schema.into());
}
self
}
#[must_use]
pub fn min_properties(mut self, n: usize) -> Self {
self.schema
.insert("minProperties".into(), Value::Number(n.into()));
self
}
#[must_use]
pub fn max_properties(mut self, n: usize) -> Self {
self.schema
.insert("maxProperties".into(), Value::Number(n.into()));
self
}
#[must_use]
pub fn description(mut self, desc: &str) -> Self {
self.schema
.insert("description".into(), Value::String(desc.into()));
self
}
#[must_use]
pub fn default(mut self, value: Value) -> Self {
self.schema.insert("default".into(), value);
self
}
#[must_use]
pub fn optional_value(mut self) -> Self {
let inner = self.build_schema();
self.schema.clear();
let mut null_schema = serde_json::Map::default();
null_schema.insert("type".into(), Value::String("null".into()));
self.schema.insert(
"anyOf".into(),
Value::Array(vec![inner, Value::Object(null_schema)]),
);
self
}
#[must_use]
pub fn nullable(mut self) -> Self {
if let Some(Value::String(t)) = self.schema.get("type").cloned() {
self.schema.insert(
"type".into(),
Value::Array(vec![Value::String(t), Value::String("null".into())]),
);
}
self
}
#[must_use]
pub fn build_schema(&self) -> Value {
Value::Object(
self.schema
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect(),
)
}
#[must_use]
pub fn build(self) -> ValidationOptions {
let mut options = ValidationOptions::new();
options.schema = Some(self.build_schema());
options
}
fn additional_properties(mut self, value: Value) -> Self {
self.schema.insert("additionalProperties".into(), value);
self
}
fn ensure_properties(&mut self) {
self.schema
.entry("properties".into())
.or_insert_with(|| Value::Object(Map::default()));
}
fn ensure_required(&mut self) {
self.schema
.entry("required".into())
.or_insert_with(|| Value::Array(Vec::new()));
}
fn ensure_pattern_properties(&mut self) {
self.schema
.entry("patternProperties".into())
.or_insert_with(|| Value::Object(Map::default()));
}
}
#[must_use]
pub fn object() -> ObjectSchema {
ObjectSchema::new()
}
impl From<ObjectSchema> for Value {
fn from(builder: ObjectSchema) -> Self {
builder.build_schema()
}
}