json-node 0.1.1

A way to work with JSON as a node tree.
Documentation
use crate::JsonNodeError;
use crate::parsing::JsonNodeParser;
use crate::utils::SurroundWith;

use crate::models::JsonValueType;

type JsonPropertyDictionary = Vec<(String, JsonNode)>;

#[derive(Debug, PartialEq, Clone)]
pub enum JsonNode {
    Value(JsonValueType),
    Object(JsonPropertyDictionary),
    Array(Vec<JsonNode>),
}

impl JsonNode {
    pub fn parse(json: &str) -> Result<JsonNode, JsonNodeError> {
        JsonNodeParser::parse_node(json, None)
    }

    pub fn is_value(&self) -> bool {
        match self {
            JsonNode::Value(_) => true,
            _ => false,
        }
    }

    pub fn is_object(&self) -> bool {
        match self {
            JsonNode::Object(_) => true,
            _ => false,
        }
    }

    pub fn is_array(&self) -> bool {
        match self {
            JsonNode::Array(_) => true,
            _ => false,
        }
    }

    pub fn to_json_string(&self) -> String {
        match self {
            JsonNode::Value(value) => value.to_json_string(),
            JsonNode::Object(object) => {
                object
                .iter()
                .map(|(key, node)| format!("\"{}\": {}", key, node.to_json_string()))
                .collect::<Vec<String>>()
                .join(",")
                .surround_with("{", "}")
            },
            JsonNode::Array(array) => {
                array
                .iter()
                .map(|node| node.to_json_string())
                .collect::<Vec<String>>()
                .join(",")
                .surround_with("[", "]")
            },
        }
    }
}

impl<'a> IntoIterator for &'a JsonNode {
    type Item = &'a JsonNode;
    type IntoIter = JsonNodeIterator<'a>;

    fn into_iter(self) -> Self::IntoIter {
        JsonNodeIterator {
            node: Some(&self),
            array_index: None,
            object_index: None,
            child: None,
        }
    }
}

pub struct JsonNodeIterator<'a> {
    node: Option<&'a JsonNode>,
    array_index: Option<usize>,
    object_index: Option<usize>,
    child: Option<Box<JsonNodeIterator<'a>>>,
}

impl<'a> Iterator for JsonNodeIterator<'a> {
    type Item = &'a JsonNode;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(iter) = &mut self.child {
            if let Some(node) = iter.next() {
                return Some(node);
            }

            self.child = None;
        }

        if let None = self.node {
            return None; // Termination point for iteration
        }

        match self.node.unwrap() {
            JsonNode::Array(nodes) => {
                match self.array_index {
                    Some(mut index) => {
                        index = index + 1;
                        self.array_index = Some(index);
                        self.child = Some(Box::new(nodes[index].into_iter()));
                        let next = self.next();

                        if index == nodes.len() - 1 {
                            self.array_index = None;
                            self.node = None;
                        }

                        return next;
                    },
                    None => {
                        self.array_index = Some(0);
                        self.child = Some(Box::new(nodes[0].into_iter()));
                        let next = self.next();

                        if nodes.len() == 1 {
                            self.array_index = None;
                        }

                        return next;
                    },
                }
            },
            JsonNode::Object(properties) => {
                match self.object_index {
                    Some(mut index) => {
                        index = index + 1;
                        self.object_index = Some(index);
                        self.child = Some(Box::new(properties[index].1.into_iter()));
                        let next = self.next();

                        if index == properties.len() - 1 {
                            self.object_index = None;
                            self.node = None;
                        }

                        return next;
                    },
                    None => {
                        self.object_index = Some(0);
                        self.child = Some(Box::new(properties[0].1.into_iter()));
                        let next = self.next();

                        if properties.len() == 1 {
                            self.object_index = None;
                        }

                        return next;
                    },
                }
            },
            JsonNode::Value(_) => {
                let node = self.node.unwrap();
                self.node = None;
                Some(node)
            },
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::JsonValueType;
    use super::JsonNode;

    #[test]
    fn json_node_is_value() {
        let mut node = JsonNode::Value(JsonValueType::String("Hello, World!".to_string()));
        assert!(node.is_value());

        node = JsonNode::Value(JsonValueType::Integer(123));
        assert!(node.is_value());

        node = JsonNode::Value(JsonValueType::Float(123.456));
        assert!(node.is_value());

        node = JsonNode::Value(JsonValueType::Boolean(true));
        assert!(node.is_value());

        node = JsonNode::Value(JsonValueType::Null);
        assert!(node.is_value());
    }

    #[test]
    fn iterate_works() {
        let json = r#"
        {
            "name": "Jason",
            "age": 30,
            "isMale": true,
            "height": 1.8,
            "numbers": [1, 2, 3, 4, 5],
            "children": [
                {
                    "name": "Jason Jr.",
                    "age": 5,
                    "isMale": true,
                    "height": 1.2
                },
                {
                    "name": "Jasmine",
                    "age": 3,
                    "isMale": false,
                    "height": 1.1
                }
            ]
        }"#;

        let node = JsonNode::parse(json).unwrap();
        for e in node.into_iter() {
            println!("{:?}", e)
        }
    }
}