How do I use a parser within a parser in nom?

Viewed 282

I'm trying to parse a format that contains some data in a compressed format. How do I parse the decompressed part as part of the regular parsing? Currently I'm running into issues with the borrow checker because the error part of nom would like to reference the compressed_data part when returning from parse_header, which would run out of scope.

#[macro_use]
use nom::combinator::map_res;
use nom::number::complete::le_u32;
use nom::IResult;
use nom::bytes::complete::take;
use std::io::prelude::*;
use std::io;
use flate2::bufread::DeflateDecoder;

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Replay {
  header_length: u32
, header: Header
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Header {
  version: String
}

fn decode_reader(bytes: &[u8]) -> io::Result<Vec<u8>> {
   let mut deflater = DeflateDecoder::new(&bytes[..]);
   let mut s = Vec::new();
   deflater.read(&mut s)?;
   Ok(s)
}

pub fn parse_header(input: &[u8]) -> IResult<&[u8],Header> {
  let (input, version) = map_res(take(8u8), std::str::from_utf8)(input)?;
  Ok((input, Header { version: version.to_string() }))
}

pub fn parse_replay(input: &[u8]) -> IResult<&[u8],Replay> {
  let (input, header_length) = le_u32(input)?;
  let (input, _) = le_u32(input)?;
  let (input, compressed_data) = map_res(take(header_length), decode_reader)(input)?;
  let compressed_ary: &[_] = &compressed_data;
  let (com_input, header) = parse_header(compressed_ary)?;
  
  Ok((input, Replay { header_length, header }))
}
cannot return value referencing local variable `compressed_data`

returns a value referencing data owned by the current function
rustc(E0515)
1 Answers

Not sure if you've already solved this, but the issue is caused by the use of ? in parse_header(compressed_ary)? which will, on error, return a nom::Err(nom::error::Error<&[u8]>) that references data owned by the compressed_data variable. You could use .unwrap() but you'd probably want to map the error into something that references input instead of compressed_data, but this will likely mean matching on nom::Err and mapping each of the 3 variants of nom::Err to their equivalent, but with a substituted reference to input. You could also just return a specific nom error rather than mapping each enum variant if you don't expect to the Incomplete or Failure cases

Related