use anyhow::anyhow;
use winnow::{
Parser,
ascii::{dec_uint, float, space1},
combinator::{opt, preceded as P, repeat},
token::literal,
};
use super::types::*;
use crate::file_parsers::{
VersionedResult, VersionedResultExt,
shared::{
lift::{SliceParser, lift},
winnow::{WinnowParser, filename, quoted, quoted_str, version_line},
},
};
fn entry<'a>() -> impl WinnowParser<&'a str, Entry> {
winnow::trace!(
"entry",
(
dec_uint, P(space1, quoted('"').and_then(filename("atlas"))),
P(space1, quoted_str),
P(space1, float),
opt(P(space1, float)),
)
.map(|(weight, atlas_file, tag, float1, float2)| Entry {
weight,
atlas_file,
tag,
float1,
float2,
})
)
}
fn header<'a>() -> impl WinnowParser<&'a str, (String, Option<f32>)> {
(
quoted_str, opt(P(space1, float)),
)
}
fn group<'a>() -> impl SliceParser<'a, &'a str, Group> {
winnow::trace!(
"group",
(
lift(header()), repeat(0.., lift(entry())),
)
.map(|((area, float), entries)| Group {
area,
float,
entries,
})
)
}
fn default_group<'a>() -> impl SliceParser<'a, &'a str, Group> {
winnow::trace!(
"default_group",
(
lift(literal("Default").map(String::from)), repeat(0.., lift(entry())),
)
.map(|(area, entries)| Group {
area,
float: None,
entries,
})
)
}
pub fn parse_dct_str(contents: &str) -> VersionedResult<DCTFile> {
let lines = contents
.lines()
.map(|l| l.trim())
.filter(|l| !l.is_empty() && !l.starts_with("//"))
.collect::<Vec<_>>();
let mut lines = lines.as_slice();
let version = lift(version_line())
.parse_next(&mut lines)
.map_err(|e| anyhow!("Failed to parse file: {e:?}"))?;
let mut parser = (
lift(float),
default_group(), repeat(0.., group()),
)
.map(|(float, default_group, mut groups)| {
Vec::insert(&mut groups, 0, default_group);
DCTFile {
version,
float,
groups,
}
});
parser
.parse(lines)
.map_err(|e| anyhow!("Failed to parse file: {e:?}"))
.with_version(Some(version))
}