use thiserror::Error;
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,
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>,
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)
}
}