aleo-struct-parser 0.2.1

Aleo Struct Parser
Documentation
use serde_json::Map;

mod number;
mod serde;

pub use serde::Address;
pub use serde::from_str;

pub fn to_json_value(input: &str) -> serde_json::Value {
    parse_struct(input)
}

struct Cursor<'a> {
    ref_str: &'a str,
    start: usize,
    end: usize,
}

impl<'a> Cursor<'a> {
    fn new(ref_str: &str) -> Cursor {
        Cursor {
            ref_str,
            start: 0,
            end: 0,
        }
    }

    fn push(&mut self, idx: usize) {
        if self.start == 0 {
            self.start = idx;
        }
        self.end = idx + 1;
    }

    fn clear(&mut self) {
        self.start = 0;
        self.end = 0;
    }

    fn is_empty(&self) -> bool {
        self.end == 0
    }

    fn get_str(&self) -> &'a str {
        &self.ref_str[self.start..self.end]
    }
}

fn is_array(input: &str) -> bool {
    for char in input.chars() {
        if char == ' ' || char == '\n' || char == '\r' {
            continue;
        }
        if char == '[' {
            return true;
        }
        return false;
    }
    false
}

fn is_field(input: &str) -> bool {
    for char in input.chars() {
        if char == ' ' || char == '\n' || char == '\r' {
            continue;
        }
        if char == '[' || char == '{' {
            return false;
        }
        return true;
    }
    false
}

// parse aleo struct to json value, assuming the input is valid
pub(crate) fn parse_struct(input: &str) -> serde_json::Value {
    if is_array(input) {
        return serde_json::Value::Array(parse_array(input));
    }

    if is_field(input) {
        return serde_json::Value::String(input.to_owned());
    }

    let mut self_map: Map<String, serde_json::Value> = Map::new();
    let mut depth = 0;
    let mut current_key = Cursor::new(input);
    let mut current_child = Cursor::new(input);
    let mut current_child_stack = 0;
    let mut is_key = true;

    for (idx, token) in input.char_indices() {
        match token {
            '{' => {
                depth += 1;
                if depth != 1 {
                    current_child.push(idx);
                    current_child_stack += 1;
                }
            }
            '}' => {
                depth -= 1;
                if depth == 0 {
                    break;
                } else {
                    current_child.push(idx);
                    current_child_stack -= 1;
                    if current_child_stack == 0 {
                        let current_child_str = current_child.get_str();
                        let current_key_str = current_key.get_str();
                        self_map
                            .insert(current_key_str.to_owned(), parse_struct(current_child_str));
                        current_key.clear();
                        current_child.clear();
                        is_key = true;
                    }
                }
            }
            '[' => {
                depth += 1;
                current_child.push(idx);
                current_child_stack += 1;
                if depth == 1 {
                    is_key = false;
                }
            }
            ']' => {
                depth -= 1;
                current_child.push(idx);
                current_child_stack -= 1;
                if current_child_stack == 0 {
                    let current_key_str = current_key.get_str();
                    let current_child_str = current_child.get_str();
                    self_map.insert(
                        current_key_str.to_owned(),
                        serde_json::Value::Array(parse_array(current_child_str)),
                    );
                    current_key.clear();
                    current_child.clear();
                }
            }
            ':' => {
                if depth == 1 {
                    is_key = false;
                } else {
                    current_child.push(idx);
                }
            }
            '\n' | ' ' | '\r' => {}
            ',' => {
                if depth == 1 {
                    if !current_key.is_empty() {
                        let current_key_str = current_key.get_str();
                        let current_child_str = current_child.get_str();
                        self_map.insert(
                            current_key_str.to_owned(),
                            serde_json::Value::String(current_child_str.to_owned()),
                        );
                    }
                    current_key.clear();
                    current_child.clear();
                    is_key = true;
                } else {
                    current_child.push(idx);
                }
            }
            _ => {
                if depth == 1 {
                    if is_key {
                        current_key.push(idx);
                    } else {
                        current_child.push(idx);
                    }
                } else {
                    current_child.push(idx);
                }
            }
        }
    }

    if !current_key.is_empty() {
        self_map.insert(
            current_key.get_str().to_owned(),
            serde_json::Value::String(current_child.get_str().to_owned()),
        );
    }

    serde_json::Value::Object(self_map)
}

fn parse_array(input: &str) -> Vec<serde_json::Value> {
    let mut self_vec: Vec<serde_json::Value> = Vec::new();
    let mut depth = 0;
    let mut current_item = Cursor::new(input);
    let mut current_item_stack = 0;

    let mut current_object: Option<serde_json::Value> = None;

    for (idx, token) in input.char_indices() {
        match token {
            '{' => {
                depth += 1;
                current_item.push(idx);
                current_item_stack += 1;
            }
            '}' => {
                depth -= 1;
                current_item.push(idx);
                current_item_stack -= 1;
                if current_item_stack == 0 {
                    current_object = Some(parse_struct(current_item.get_str()));
                }
            }
            '[' => {
                depth += 1;
                if depth != 1 {
                    current_item.push(idx);
                    current_item_stack += 1;
                }
            }
            ']' => {
                depth -= 1;
                if depth == 0 {
                    if let Some(obj) = current_object.take() {
                        self_vec.push(obj);
                        current_object = None;
                    } else {
                        self_vec.push(serde_json::Value::String(current_item.get_str().to_owned()));
                        current_item.clear();
                        current_item_stack = 0;
                    }
                } else {
                    current_item.push(idx);
                    current_item_stack -= 1;
                    if current_item_stack == 0 {
                        current_object = Some(serde_json::Value::Array(parse_array(
                            current_item.get_str(),
                        )));
                    }
                }
            }
            ':' => {
                current_item.push(idx);
            }
            '\n' | ' ' | '\r' => {}
            ',' => {
                if depth == 1 {
                    if let Some(obj) = current_object.take() {
                        self_vec.push(obj);
                        current_object = None;
                    } else {
                        self_vec.push(serde_json::Value::String(current_item.get_str().to_owned()));
                        current_item.clear();
                    }
                } else {
                    current_item.push(idx);
                }
            }
            _ => {
                current_item.push(idx);
            }
        }
    }

    self_vec
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_parse_struct() {
        let input = r#"
            {
                program_id: puzzle_arcade_ticket_v002.aleo,
                function_name: mint,
                arguments: [
                    aleo13dn2lyphtrn8mujxcqt4vm2rc567k0s3gnnks985p0hhyvfc9yqqy6rdsl
                ]
            }
        "#;
        let expect = r#"
            {
                "program_id": "puzzle_arcade_ticket_v002.aleo",
                "function_name": "mint",
                "arguments": [
                    "aleo13dn2lyphtrn8mujxcqt4vm2rc567k0s3gnnks985p0hhyvfc9yqqy6rdsl"
                ]
            }
                "#;
        let parsed_struct = parse_struct(input);
        // println!("{:#?}", parsed_struct);
        // println!("{}", serde_json::to_string_pretty(&parsed_struct).unwrap());

        assert_eq!(
            expect.trim().replace(' ', ""),
            serde_json::to_string_pretty(&parsed_struct)
                .unwrap()
                .trim()
                .replace(' ', "")
        );
    }
}