Implementing a sudo-glom clone

Hi,

So recently I've finished developing a project where the main routine was to update nested values from a variety JSON files.

This initial implementation I was able to build with a Python library named glom. From it I'm only using the "read" and "update" routines for project.

Now that we reached a fairly stable version I though that would be great chance to try introduce Rust on project but the main problem here is that I was not able to find anything like "glom" for Rust.

So after a quick first round of experimentation I was able to come up with this "read" clone that supports retrieving nested values for object or list:

use serde_json::Value;
use regex::Regex;

pub fn json_read<'a>(path: &str, data: &'a Value) -> Option<&'a Value> {
    let tokens = path.split(".").collect::<Vec<&str>>();
    let re_vec_idx = Regex::new(r"^\[(\d+)\]$").unwrap();
    let mut sel_data = Some(data);

    for token in tokens {
        let vec_idx = match re_vec_idx.captures(token) {
            Some(cap) => Some(cap[1].parse::<usize>().unwrap()),
            _ => None,
        };
        sel_data = match sel_data {
            Some(value) => match vec_idx {
                Some(idx) => value.get(idx),
                None => value.get(token),
            },
            None => None,
        };
    }

    return sel_data;
}

and can be used like:

    #[test]
    fn read_inner_key_json() {
        let json_str = r#"
            {
                "foo": {
                    "bar": "bingo!"
                }
            }
        "#;
        let json_data: Result<Value> = serde_json::from_str(json_str);
        assert_eq!(json_data.is_ok(), true);
        let val: Option<&Value> = json_read(
            "foo.bar",
            json_data.as_ref().unwrap()
        );
        assert_eq!(val.is_none(), false);
        assert_eq!(val.unwrap(), "bingo!");
    }

But the tricky parts for me is coming up with the "update" routine. The initial tests I designed was:

    #[test]
    fn update_inner_key_json() {
        let json_str = r#"
            {
                "foo": {
                    "bar": "bingo!"
                }
            }
        "#;
        let json_data: Result<Value> = serde_json::from_str(json_str);
        assert_eq!(json_data.is_ok(), true);
        let old_val: Option<&Value> = json_update(
            &mut json_data,
            "foo.bar",
            "updated!"
        );
        assert_eq!(old_val.is_none(), false);
        assert_eq!(old_val.unwrap(), "bingo!");

        let new_val: Option<&Value> = json_read(
            "foo.bar",
            json_data.as_ref().unwrap()
        );
        assert_eq!(new_val.is_none(), false);
        assert_eq!(new_val.unwrap(), "updated!");
    }

So far the this is what I got for the actual implementation for the "update":

pub fn json_update<'a>(data: &'a mut Value, path: &str, new_value: Value) -> Option<&'a Value> {
    let mut tokens = path.split(".").peekable();
    let mut sel_data = Some(data);
    while let Some(token) = tokens.next() {
        if tokens.peek().is_none() {
            // last token
            return sel_data.unwrap()
                .as_object()
                .as_mut()
                .unwrap()
                .insert(
                    token.to_string(),
                    new_value,
                );
        }
        sel_data = sel_data.unwrap().get(token).as_mut();
    }

Does the community have any tips and direction to help me finish this "update" routine?

Thanks for reading :slight_smile:

It looks like you were borrowing as immutable and then borrowing as mutable while you could have simply used calls such as .as_object_mut(), .as_mut(), and also got some mismatches, as well as missed mut specifiers.

@hydroper1 dang it! that worked like a charm! I appreciate the help now I only need to pickup a name for the lib, post on github and officially name a contributor :slight_smile:

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.