poe-item-parser 0.1.0

Path of Exile Item Text Parser
Documentation
use thiserror::Error; // Add this line

use pest::{iterators::Pair, Parser};
use pest_derive::Parser;

#[derive(Parser)]
#[grammar = "./poe-item.pest"]
pub struct PoeParser;

#[derive(Error, Debug)]
pub enum PoeItemParseError {
    #[error("Could not parse Item: {0}")]
    ParseError(String),
}

#[derive(Debug)]
pub struct PoeModifier {
    pub modtype: Option<String>,
    pub name: Option<String>,
    pub tier: Option<i32>,
    pub tags: Option<Vec<String>>,
    pub line: String,
}

#[derive(Debug)]
pub struct PoeItem {
    pub item_class: String,
    pub rarity: String,
    pub name: String,
    pub base: String,
    // Stats
    pub physical_damage: Option<String>,
    pub elemental_damage: Option<String>,
    pub crit_strike_chance: Option<String>,
    pub attacks_per_second: Option<String>,
    pub weapon_range: Option<String>,
    // Requirements
    pub lvl_requirement: Option<i32>,
    pub str_requirement: Option<i32>,
    pub dex_requirement: Option<i32>,
    pub int_requirement: Option<i32>,
    pub class_requirement: Option<String>,

    pub sockets: Option<String>,
    pub ilvl: Option<i32>,
    pub modifiers: Option<Vec<PoeModifier>>,
    pub corrupted: bool,
}

fn get_str_value(pair: &Pair<Rule>) -> String {
    pair.as_str().trim().to_string()
}

fn parse_int_value(pair: &Pair<Rule>) -> Result<i32, PoeItemParseError> {
    pair.as_str().trim().parse::<i32>()
        .map_err(|err|PoeItemParseError::ParseError(err.to_string()))
}

impl PoeItem {
    pub fn parse(stream: &str) -> Result<Self, PoeItemParseError> {
        let mut pairs = PoeParser::parse(Rule::item, &stream)
            .map_err(|err| PoeItemParseError::ParseError(err.to_string()))?;
        
        let mut item = PoeItem { 
          item_class: "".to_owned(), 
          rarity: "".to_owned(), 
          name: "".to_owned(), 
          base: "".to_owned(), 
          physical_damage: None, 
          elemental_damage: None, 
          crit_strike_chance: None, 
          attacks_per_second: None, 
          weapon_range: None, 
          lvl_requirement: None, 
          str_requirement: None, 
          dex_requirement: None, 
          int_requirement: None, 
          class_requirement: None, 
          sockets: None, 
          ilvl: None, 
          modifiers: None, 
          corrupted: false
        };

        let mut modifier = PoeModifier{ 
          modtype: None, 
          name: None, 
          tier: None, 
          tags: None, 
          line: "".to_owned()
        };

        let rule = pairs.next().unwrap();
        for pair in rule.clone().into_inner() {
            println!("Rule: {:?}, text: {:?}", pair.as_rule(), pair.as_str());
            match pair.as_rule() {
              Rule::weapon_type => {},
              Rule::class => item.item_class = get_str_value(&pair),
              Rule::rarity => item.rarity = get_str_value(&pair),
              Rule::name => item.name = get_str_value(&pair),
              Rule::base => item.base = get_str_value(&pair),
              Rule::physical_damage => item.physical_damage = Some(get_str_value(&pair)),
              Rule::elemental_damage => item.elemental_damage = Some(get_str_value(&pair)),
              Rule::crit_strike_chance => item.crit_strike_chance = Some(get_str_value(&pair)),
              Rule::attacks_per_second => item.attacks_per_second = Some(get_str_value(&pair)),
              Rule::weapon_range => item.weapon_range = Some(get_str_value(&pair)),
              Rule::lvl_req => item.lvl_requirement = Some(parse_int_value(&pair)?),
              Rule::str_req => item.str_requirement = Some(parse_int_value(&pair)?),
              Rule::dex_req => item.dex_requirement = Some(parse_int_value(&pair)?),
              Rule::int_req => item.int_requirement = Some(parse_int_value(&pair)?),
              Rule::class_req => item.class_requirement = Some(get_str_value(&pair)),
              Rule::sockets => item.sockets = Some(get_str_value(&pair)),
              Rule::ilvl => item.ilvl = Some(parse_int_value(&pair)?),
              Rule::modifier_type => modifier.modtype = Some(get_str_value(&pair)),
              Rule::modifier_name => modifier.name = Some(get_str_value(&pair)),
              Rule::modifier_tier => modifier.tier = Some(parse_int_value(&pair)?),
              Rule::modifier_tag => {
                modifier.tags
                    .get_or_insert_with(Vec::new)
                    .push(get_str_value(&pair));
              },
              Rule::modifier_line => {
                if pair.as_str() == "Corrupted" {
                  item.corrupted = true;
                } else {
                  modifier.line = pair.as_str().trim().parse().unwrap();
                  item.modifiers
                    .get_or_insert_with(Vec::new)
                    .push(modifier);

                  modifier = PoeModifier{ 
                    modtype: None, 
                    name: None, 
                    tier: None, 
                    tags: None, 
                    line: "".to_owned()
                  };
                }
              }
              _ => {
                return Err(PoeItemParseError::ParseError(format!("Unhandled Rule! Rule: {:#?} Text: {:#?}", pair.as_rule(), pair.as_str())));
              }
              // _ => {}
            }
        }

        Ok(item)
    }
}