use std::{cell::RefCell, collections::HashMap, ops::Deref, rc::Rc};
use nom::{branch::alt, bytes::complete::tag, combinator::map, error::context, multi::many0, IResult};
use crate::chunk_structs::{Chunk, StreamHeaderChunkInfo};
use super::{boundary, clock_offset, file_header, samples, stream_footer, stream_header};
fn magic_number(input: &[u8]) -> IResult<&[u8], &[u8]> {
context("magic_number", tag(b"XDF:"))(input)
}
pub(crate) fn xdf_file_parser(input: &[u8]) -> IResult<&[u8], Vec<Chunk>> {
let stream_info_map: HashMap<u32, StreamHeaderChunkInfo> = HashMap::new();
let cursed: Rc<RefCell<HashMap<u32, StreamHeaderChunkInfo>>> = Rc::new(RefCell::new(stream_info_map));
let file_header_parser = map(file_header, Chunk::FileHeader);
let mut file_header_parser = context("xdf_file file_header", file_header_parser);
let stream_header_parser = map(stream_header, |stream_header_chunk| {
let mut stream_info_map = cursed.deref().borrow_mut();
stream_info_map.insert(stream_header_chunk.stream_id, stream_header_chunk.info.clone());
Chunk::StreamHeader(stream_header_chunk)
});
let stream_header_parser = context("xdf_file stream_header", stream_header_parser);
let samples_parser = map(|input| samples(input, cursed.clone()), Chunk::Samples);
let samples_parser = context("xdf_file samples", samples_parser);
let clock_offset_parser = map(clock_offset, Chunk::ClockOffset);
let clock_offset_parser = context("xdf_file clock_offset", clock_offset_parser);
let boundary_parser = map(boundary, Chunk::Boundary);
let boundary_parser = context("xdf_file boundary", boundary_parser);
let stream_footer_parser = map(stream_footer, Chunk::StreamFooter);
let stream_footer_parser = context("xdf_file stream_footer", stream_footer_parser);
let repeated_parsers = many0(alt((
stream_header_parser,
samples_parser,
clock_offset_parser,
boundary_parser,
stream_footer_parser,
)));
let mut repeated_parsers = context("xdf_file repeated_parsers", repeated_parsers);
let (input, _) = magic_number(input)?;
let (input, file_header) = file_header_parser(input)?;
let (input, other_chunks) = repeated_parsers(input)?;
let mut chunks = vec![file_header];
chunks.extend(other_chunks);
Ok((input, chunks))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_xdf_file() {
let input = include_bytes!("../../tests/minimal.xdf");
let (rest, chunks) = xdf_file_parser(input).unwrap();
assert_eq!(rest, &[] as &[u8]);
assert_eq!(chunks.len(), 15);
assert!(matches!(chunks[0], Chunk::FileHeader(_)));
assert!(matches!(chunks[1], Chunk::StreamHeader(_)));
assert!(matches!(chunks[2], Chunk::StreamHeader(_)));
assert!(matches!(chunks[3], Chunk::Boundary(_)));
assert!(matches!(chunks[4], Chunk::Samples(_)));
assert!(matches!(chunks[5], Chunk::Samples(_)));
assert!(matches!(chunks[6], Chunk::Samples(_)));
assert!(matches!(chunks[7], Chunk::Samples(_)));
assert!(matches!(chunks[8], Chunk::Samples(_)));
assert!(matches!(chunks[9], Chunk::Samples(_)));
assert!(matches!(chunks[10], Chunk::Boundary(_)));
assert!(matches!(chunks[11], Chunk::ClockOffset(_)));
assert!(matches!(chunks[12], Chunk::ClockOffset(_)));
assert!(matches!(chunks[13], Chunk::StreamFooter(_)));
assert!(matches!(chunks[14], Chunk::StreamFooter(_)));
}
}