poe_data_tools 1.0.0

A library for working with Path of Exile game data
Documentation
use anyhow::anyhow;
use winnow::{
    Parser,
    ascii::{dec_uint, float, space1},
    combinator::{alt, cond, dispatch, empty, opt, preceded as P, repeat},
};

use super::types::*;
use crate::file_parsers::{
    VersionedResult, VersionedResultExt,
    shared::{
        lift::{SliceParser, lift},
        winnow::{WinnowParser, quoted_comma_separated, version_line},
    },
};

fn num_line<'a>(version: u32) -> impl WinnowParser<&'a str, Nums> {
    winnow::trace!(
        "num_line",
        (
            float, //
            P(space1, float),
            P(space1, dec_uint),
            P(space1, dec_uint),
            cond(version >= 3, P(space1, dec_uint)),
        )
            .map(|(float1, float2, uint1, uint2, uint3)| Nums {
                float1,
                float2,
                uint1,
                uint2,
                uint3,
            })
    )
}

fn entry<'a>() -> impl WinnowParser<&'a str, Entry> {
    winnow::trace!(
        "entry",
        (
            dec_uint, //
            P(space1, quoted_comma_separated()),
        )
            .map(|(weight, chest_types)| Entry {
                weight,
                chest_types,
            })
    )
}

fn group<'a, const NAMED: bool>(version: u32) -> impl SliceParser<'a, &'a str, Group> {
    let header = dispatch! {
        empty.value(NAMED);
        false => num_line(version).map(|nums| (vec!["Default".to_string()], Some(NumLine::V3(nums)))),
        true => (
                    quoted_comma_separated(),
                    opt(P(
                        space1,
                        alt((
                            num_line(version).map(NumLine::V3), //
                            float.map(NumLine::V2),
                        )),
                    )),
                ),
    };

    winnow::trace!(
        "group",
        (
            lift(header), //
            repeat(0.., lift(entry())),
        )
            .map(|((areas, nums), entries)| Group {
                areas,
                entries,
                nums,
            })
    )
}

pub fn parse_cht_str(contents: &str) -> VersionedResult<CHTFile> {
    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 = (
        winnow::trace!("default_group", group::<false>(version)),
        repeat(0.., group::<true>(version)),
    )
        .map(|(default_group, mut groups)| {
            Vec::insert(&mut groups, 0, default_group);
            CHTFile { version, groups }
        });

    parser
        .parse(lines)
        .map_err(|e| anyhow!("Failed to parse file: {e:?}"))
        .with_version(Some(version))
}