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, space0},
    combinator::{cond, opt, preceded as P, repeat, separated, terminated},
};

use super::{super::shared::winnow::spaces_or_comments as S, types::*};
use crate::file_parsers::{
    VersionedResult, VersionedResultExt,
    shared::winnow::{WinnowParser, filename, parse_bool, quoted, quoted_str, version_line},
};

fn entry(contents: &mut &str) -> winnow::Result<Entry> {
    winnow::trace!(
        "entry",
        (
            quoted('"').and_then(filename("mat")),
            repeat(
                0..,
                P(
                    // NOTE Edge case: Missing space between strings here
                    space0, //
                    quoted('"').and_then(filename("dlp")),
                ),
            ),
        )
            .map(|(mat_file, dlp_files)| Entry {
                mat_file,
                dlp_files,
            })
    )
    .parse_next(contents)
}

fn weights_line<'a>(num_weights: usize) -> impl WinnowParser<&'a str, (Vec<u32>, u32)> {
    winnow::trace!(
        "weights_line",
        (
            separated(num_weights, dec_uint::<_, u32, _>, S()), //
            P(S(), dec_uint),
        )
    )
}

fn group(contents: &mut &str) -> winnow::Result<Group> {
    let (name, num_a, num_b) = winnow::trace!(
        "group_header",
        (
            opt(terminated(quoted_str, S())),
            dec_uint,
            P(S(), dec_uint::<_, usize, _>),
        )
    )
    .parse_next(contents)?;

    let entries = repeat(num_a, P(S(), entry)).parse_next(contents)?;

    let weight_line = cond(
        num_a > 1, //
        P(S(), weights_line(num_a)),
    )
    .parse_next(contents)?;

    let nums = cond(
        num_b > 0, //
        P(S(), (dec_uint, P(S(), parse_bool))),
    )
    .parse_next(contents)?;

    let extra_entries = repeat(num_b, P(S(), entry)).parse_next(contents)?;

    let group = Group {
        name,
        entries,
        weight_line,
        extra_line: nums,
        extra_entries,
    };

    Ok(group)
}

pub fn parse_mtd_str(contents: &str) -> VersionedResult<MTDFile> {
    let mut contents = contents.trim();

    let version = version_line()
        .parse_next(&mut contents)
        .map_err(|e| anyhow!("Failed to parse file: {e:?}"))?;

    let mut parser = P(S(), separated(1.., group, S()));

    parser
        .parse_next(&mut contents)
        .map(|groups| MTDFile { version, groups })
        .map_err(|e| anyhow!("Failed to parse file: {e:?}"))
        .with_version(Some(version))
}