json-utils 0.3.5

An utility crate for working with JSON and JSON-schemas
Documentation
use std::fmt;

use crate::query::Path;
use crate::query::PathComponent;
use crate::query::Query;

use super::SchemaNode;
use super::ValidNode;

#[derive(Debug, Clone, PartialEq)]
pub struct QueryNode<T: fmt::Debug + Clone + PartialEq> {
    pub schema: T,
    pub is_required: bool,
}

impl<'v> Query<'v> for SchemaNode {
    type Item = QueryNode<SchemaNode>;
    type ItemRef = QueryNode<&'v SchemaNode>;

    fn lookup<'p, P>(&'v self, path: P) -> Option<Self::ItemRef>
    where
        P: Path<'p>,
    {
        lookup(self, true, path.path())
    }

    fn take<'p, P>(self, path: P) -> (Option<Self>, Option<Self::Item>)
    where
        P: Path<'p>,
    {
        take(self, true, path.path())
    }

    fn insert<'p, P>(&mut self, path: P, insertee: Self::Item) -> Result<(), Self::Item>
    where
        P: Path<'p>,
    {
        insert(self, path.path(), insertee)
    }
}

fn lookup<'v, 'p, P: PathComponent<'p>, I: Iterator<Item = P>>(
    v: &'v SchemaNode,
    is_required: bool,
    mut components: I,
) -> Option<QueryNode<&'v SchemaNode>> {
    if let Some(component) = components.next() {
        if let SchemaNode::ValidNode(ValidNode::ObjectNode(ref object_node)) = *v {
            let child_key = component.as_str_slice();
            let child_is_required = object_node.required.contains(child_key);
            object_node
                .properties
                .get(child_key)
                .and_then(move |ref child| lookup(child, child_is_required, components))
        } else {
            None
        }
    } else {
        let qn = QueryNode {
            is_required,
            schema: v,
        };
        Some(qn)
    }
}

fn take<'v, 'p, P: PathComponent<'p>, I: Iterator<Item = P>>(
    v: SchemaNode,
    is_required: bool,
    mut components: I,
) -> (Option<SchemaNode>, Option<QueryNode<SchemaNode>>) {
    if let Some(component) = components.next() {
        match v {
            SchemaNode::ValidNode(ValidNode::ObjectNode(mut object_node)) => {
                let child_key = component.as_str_slice();
                if let Some(child) = object_node.properties.remove(child_key) {
                    let child_is_required = object_node.required.contains(child_key);
                    let (child_opt, taken_opt) = take(child, child_is_required, components);
                    if let Some(child) = child_opt {
                        object_node.properties.insert(child_key.to_owned(), child);
                    };
                    if object_node.properties.is_empty() {
                        (None, taken_opt)
                    } else {
                        (Some(object_node.into()), taken_opt)
                    }
                } else {
                    (Some(object_node.into()), None)
                }
            }
            as_is => (Some(as_is), None),
        }
    } else {
        let qn = QueryNode {
            is_required,
            schema: v,
        };
        (None, Some(qn))
    }
}

fn insert<'v, 'p, P: PathComponent<'p>, I: Iterator<Item = P>>(
    v: &'v mut SchemaNode,
    mut components: I,
    insertee: QueryNode<SchemaNode>,
) -> Result<(), QueryNode<SchemaNode>> {
    if let Some(component) = components.next() {
        match *v {
            SchemaNode::ValidNode(ValidNode::ObjectNode(ref mut object_node)) => {
                let child_key = component.as_str_slice();

                if let Some(ref mut child) = object_node.properties.get_mut(child_key) {
                    insert(child, components, insertee)
                } else {
                    let mut child = SchemaNode::object().into();
                    let () = insert(&mut child, components, insertee)
                        .expect("Failed to insert into a newly created node");

                    object_node.properties.insert(child_key.to_owned(), child);
                    Ok(())
                }
            }
            _ => Err(insertee),
        }
    } else {
        *v = insertee.schema;
        Ok(())
    }
}