poe_data_tools 1.0.0

A library for working with Path of Exile game data
Documentation
use anyhow::{Result, anyhow};
use winnow::{
    Parser,
    ascii::{dec_uint as U, space1},
    combinator::{alt, opt, preceded as P, separated_pair},
    stream::AsChar,
    token::{literal, take_while},
};

use super::types::*;
use crate::file_parsers::shared::{
    lift::{SliceParser, lift},
    winnow::{
        WinnowParser, filename, parse_bool, quoted, repeat_array, separated_array, unquoted,
        unquoted_str,
    },
};

fn name_hex<'a>() -> impl WinnowParser<&'a str, (String, Option<String>)> {
    let hex_parser = P(literal("#"), take_while(1.., AsChar::is_hex_digit)).map(String::from);

    winnow::trace!("name_hex", (unquoted_str, opt(P(space1, hex_parser))))
}

fn gt_files<'a>() -> impl SliceParser<'a, &'a str, [GTFile; 2]> {
    winnow::trace!(
        "gt_files",
        repeat_array(lift(
            alt((
                quoted('"'), //
                unquoted(),
            ))
            .and_then(alt((
                literal("wildcard").map(|_| GTFile::Wildcard),
                filename("gt").map(GTFile::Path),
            ))),
        ))
    )
}

fn num_line<'a>() -> impl WinnowParser<&'a str, NumLine> {
    winnow::trace!(
        "num_line",
        (
            U,
            P(space1, U),
            P(space1, parse_bool),
            opt(P(space1, parse_bool)),
            opt(P(space1, parse_bool)),
            opt(P(space1, parse_bool)),
        )
            .map(|(uint1, uint2, bool1, bool2, bool3, bool4)| NumLine {
                uint1,
                uint2,
                bool1,
                bool2,
                bool3,
                bool4,
            })
    )
}

fn virtual_et_file<'a>() -> impl WinnowParser<&'a str, VirtualETFile> {
    winnow::trace!(
        "virtual_et_file",
        separated_pair(
            unquoted().and_then(filename("et")), //
            space1,
            parse_bool,
        )
        .map(|(path, bool1)| VirtualETFile { path, bool1 })
    )
}

fn virtual_section<'a>() -> impl SliceParser<'a, &'a str, VirtualSection> {
    winnow::trace!(
        "virtual_section",
        (
            lift(literal("virtual")),
            repeat_array(lift(virtual_et_file())),
            lift(separated_array(space1, U)),
        )
            .map(
                |(_virtual_tag, virtual_et_files, virtual_rotations)| VirtualSection {
                    virtual_et_files,
                    virtual_rotations,
                },
            )
    )
}

pub fn parse_et_str(contents: &str) -> Result<ETFile> {
    let lines = contents
        .lines()
        .map(|l| l.trim())
        .filter(|l| !l.is_empty())
        .collect::<Vec<_>>();

    let mut parser = (
        lift(name_hex()),
        gt_files(),
        opt(lift(num_line())),
        opt(lift(unquoted().and_then(filename("gt")))),
        opt(virtual_section()),
    )
        .map(
            |((name, hex), gt_files, num_line, gt_file2, virtual_section)| ETFile {
                name,
                hex,
                gt_files,
                num_line,
                virtual_section,
                gt_file2,
            },
        );

    let et_file = parser
        .parse(&lines)
        .map_err(|e| anyhow!("Failed to parse file: {e:?}"))?;

    Ok(et_file)
}