Deserializing a vector with recap

Hi there!

I'm trying to use the recap crate to deserialize today's Advent of Code input.

Input lines look like this:

1 LFDGN => 7 DMPX
1 PFNM, 14 MVSK => 3 VQCQ
14 HJLX, 3 KGKVK, 1 XQSVS => 6 HGSM

I thought of having one type Chemical that would consist of an integer + a string; the left-hand side of these lines is then a list of chemicals, and the right-hand side is a single Chemical.

Using recap should make this trivial, as it has made other parsing problems before in Advent of Code, so my initial idea was:

use std::io::{self, BufRead};
use recap::Recap;
use serde::Deserialize;

#[derive(Debug, Deserialize, Recap)]
#[recap(regex = r#"(?P<quantity>\d+) (?P<name>\w+)"#)]
struct Chemical {
    quantity: u32,
    name: String,
}

#[derive(Debug, Deserialize, Recap)]
#[recap(regex = r#"^(?P<inputs>(\d+ \w+(, )?)+) => (?P<output>(\d+ \w+))$"#)]
struct Reaction {
    inputs: Vec<Chemical>,
    output: Chemical,
}

fn main() {
    let reactions: Vec<Reaction> = io::stdin()
        .lock() // Give access to BufRead::lines()
        .lines()
        .map(|line_result| line_result.expect("stdin.lines"))
        .map(|line| line.parse().expect("line.parse"))
        .collect();

    println!("reactions: {:?}", reactions);
}

However, this code panics at line.parse:

thread 'main' panicked at 'line.parse: Custom("invalid type: string "4 ZDGD", expected struct Chemical")', src/libcore/result.rs:1165:5

recap is a very useful crate that makes reduces all the grunt work of parsing input lines to a trivial thing, and the less code the better. But it is failing on me this time, and the crate's documentation is sparse, only showing the easiest possible examples, and lacking depth.

Could anyone help me here?
(If I'm fast enough I might still make the Top-100 Leaderboard!! :joy:)

Anyone has a tip? Maybe an alternative crate that I can test? Main point of recap is avoiding all the boilerplate to write custom implementations for the common deserialization traits.

If I had to guess, it doesn’t like the nested use of recap structs. The parse into Reactions is returning strings instead of Chemicals since the internal code in recap does not call parse again. You could create a struct called UnparsedReaction that holds strings instead of Chemicals and then create and then add a TryFrom/TryInto implementation which calls parse on the strings to turn them into Chemicals.

Otherwise you could look at a parsing library like nom which could certainly do what you want.

Oops, I meant to post this earlier, but then I got interrupted while trying to submit an issue to recap.


To parse a vec, the (?P<name>) has to be inside the ()+, not the other way around.

#[derive(Debug, Deserialize, Recap)]
#[recap(regex = r#"((?P<inner>\d+)( )?)+"#)]
struct Vecs {
    inner: Vec<i32>,
}

But it doesn't look like recap derivations are capable of being composed?

#[derive(Debug, Deserialize, Recap)]
#[recap(regex = r#"(?P<quantity>\d+) (?P<name>\w+)"#)]
struct Chemical {
    quantity: u32,
    name: String,
}

// This won't parse either, with the same error as the OP.
#[derive(Debug, Deserialize, Recap)]
#[recap(regex = r#"(?P<inner>\d+ \w+)"#)]
struct Wrapper {
    inner: Chemical,
}

That's kind of lame. I made an issue: