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::{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))
}