json_finder 0.1.1

find json in a any string and deserializes it against specific structs into Vec<T>
Documentation
use nom::{
    IResult, Parser,
    branch::alt,
    bytes::complete::is_not,
    character::complete::{anychar, char},
    combinator::recognize,
    multi::many0,
    sequence::{delimited, pair},
};
use serde::de::DeserializeOwned;

pub fn find_json<T: DeserializeOwned>(input: &str) -> Vec<T> {
    candidates(input)
        .into_iter()
        .filter_map(|s| serde_json::from_str(s).ok())
        .collect()
}

fn json_string(i: &str) -> IResult<&str, &str> {
    recognize(delimited(
        char('"'),
        many0(alt((recognize(pair(char('\\'), anychar)), is_not("\"\\")))),
        char('"'),
    ))
    .parse(i)
}

fn json_inner(i: &str) -> IResult<&str, &str> {
    recognize(many0(alt((
        json_string,
        json_object,
        json_array,
        is_not("{}[]\""),
    ))))
    .parse(i)
}

fn json_object(i: &str) -> IResult<&str, &str> {
    recognize(delimited(char('{'), json_inner, char('}'))).parse(i)
}

fn json_array(i: &str) -> IResult<&str, &str> {
    recognize(delimited(char('['), json_inner, char(']'))).parse(i)
}

fn candidates(input: &str) -> Vec<&str> {
    let mut out = Vec::new();
    let mut rest = input;
    while !rest.is_empty() {
        match alt((json_object, json_array)).parse(rest) {
            Ok((remaining, matched)) => {
                out.push(matched);
                rest = remaining;
            }
            Err(_) => {
                let mut chars = rest.chars();
                chars.next();
                rest = chars.as_str();
            }
        }
    }
    out
}

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

    #[derive(Debug, Deserialize, PartialEq)]
    struct Person {
        name: String,
        age: u32,
    }

    #[test]
    fn single_object() {
        let v: Vec<Person> = find_json(r#"Hello ```{"name":"Bob","age":35}``` world"#);
        assert_eq!(
            v,
            [Person {
                name: "Bob".into(),
                age: 35
            }]
        );
    }

    #[test]
    fn multiple_objects() {
        let v: Vec<Person> =
            find_json(r#"{"name":"David","age":30} noise {"name":"Bob","age":25}"#);
        assert_eq!(
            v,
            [
                Person {
                    name: "David".into(),
                    age: 30
                },
                Person {
                    name: "Bob".into(),
                    age: 25
                },
            ]
        );
    }

    #[test]
    fn non_matching_json_skipped() {
        let v: Vec<Person> = find_json(r#"{"x":1} {"name":"Alice","age":30}"#);
        assert_eq!(
            v,
            [Person {
                name: "Alice".into(),
                age: 30
            }]
        );
    }

    #[test]
    fn nested_struct() {
        #[derive(Debug, Deserialize, PartialEq)]
        struct Wrapper {
            person: Person,
        }

        let v: Vec<Wrapper> = find_json(r#"{"person":{"name":"Alice","age":30}}"#);
        assert_eq!(
            v,
            [Wrapper {
                person: Person {
                    name: "Alice".into(),
                    age: 30
                }
            }]
        );
    }

    #[test]
    fn array_of_structs() {
        let v: Vec<Vec<Person>> =
            find_json(r#"[{"name":"Jason","age":30},{"name":"John","age":25}]"#);
        assert_eq!(
            v,
            [vec![
                Person {
                    name: "Jason".into(),
                    age: 30
                },
                Person {
                    name: "John".into(),
                    age: 25
                },
            ]]
        );
    }

    #[test]
    fn brackets_inside_strings() {
        let v: Vec<Person> = find_json(r#"{"name":"A{lice}","age":30}"#);
        assert_eq!(
            v,
            [Person {
                name: "A{lice}".into(),
                age: 30
            }]
        );
    }

    #[test]
    fn empty_and_no_json() {
        assert!(find_json::<Person>("").is_empty());
        assert!(find_json::<Person>("no json here").is_empty());
    }
}