use std::{fmt::Debug, str::FromStr};
use nom::{
bytes::complete::{take, take_while},
character::complete::char,
combinator::{map, map_res, rest},
sequence::{preceded, tuple},
IResult,
};
use nom_supreme::final_parser::{final_parser, Location};
use crate::md5::MD5Sum;
pub(crate) fn md5(input: &str) -> IResult<&str, MD5Sum> {
map_res(take(32usize), MD5Sum::from_str)(input)
}
#[derive(Debug, PartialEq)]
pub struct VersionLine {
pub version: u32,
pub hash: MD5Sum,
pub name: String,
}
fn decimal(input: &str) -> IResult<&str, u32> {
map_res(take_while(|c: char| c.is_ascii_digit()), str::parse)(input)
}
fn _version_line(input: &str) -> IResult<&str, VersionLine> {
map(
tuple((
decimal,
preceded(char(','), md5),
preceded(char(','), map(rest, String::from)),
)),
|(version, hash, name)| VersionLine {
version,
hash,
name,
},
)(input)
}
pub(crate) fn version_line(input: &str) -> Result<VersionLine, nom::error::Error<Location>> {
final_parser(_version_line)(input)
}
#[derive(Debug, PartialEq)]
pub struct FileLine {
pub filesize: u32,
pub hash: MD5Sum,
pub compressed_filesize: u32,
pub compressed_hash: MD5Sum,
pub line_hash: MD5Sum,
}
impl FileLine {
pub fn to_path(&self) -> String {
let hash = format!("{:?}", self.hash);
let mut chars = hash.chars();
let c1 = chars.next().unwrap();
let c2 = chars.next().unwrap();
format!("{}/{}/{}.sd0", c1, c2, hash)
}
}
fn _file_line(input: &str) -> IResult<&str, (&str, FileLine)> {
map(
tuple((
take_while(|c: char| c != ','),
preceded(char(','), decimal),
preceded(char(','), md5),
preceded(char(','), decimal),
preceded(char(','), md5),
preceded(char(','), md5),
)),
|(filename, filesize, hash, compressed_filesize, compressed_hash, line_hash)| {
(
filename,
FileLine {
filesize,
hash,
compressed_filesize,
compressed_hash,
line_hash,
},
)
},
)(input)
}
pub(crate) fn file_line(input: &str) -> Result<(&str, FileLine), nom::error::Error<Location>> {
final_parser(_file_line)(input)
}
#[cfg(test)]
mod tests {
use super::{MD5Sum, VersionLine};
const BYTES: [u8; 16] = [
0xe1, 0x77, 0x1d, 0x0f, 0x4c, 0x93, 0xe3, 0x27, 0xc6, 0x62, 0x1a, 0x0e, 0xf2, 0xe1, 0xbd,
0xce,
];
#[test]
fn parse_md5() {
assert_eq!(
super::md5("e1771d0f4c93e327c6621a0ef2e1bdce"),
Ok(("", MD5Sum(BYTES)))
);
}
#[test]
fn parse_version_line() {
let hash = MD5Sum([
0x97, 0x78, 0xd5, 0xd2, 0x19, 0xc5, 0x08, 0x0b, 0x9a, 0x6a, 0x17, 0xbe, 0xf0, 0x29,
0x33, 0x1c,
]);
assert_eq!(
super::version_line("82,9778d5d219c5080b9a6a17bef029331c,0"),
Ok(VersionLine {
version: 82,
hash,
name: "0".into(),
})
);
}
}