Skip to main content

poe_item_parser/
lib.rs

1use thiserror::Error; // Add this line
2
3use pest::{iterators::Pair, Parser};
4use pest_derive::Parser;
5
6#[derive(Parser)]
7#[grammar = "./poe-item.pest"]
8pub struct PoeParser;
9
10#[derive(Error, Debug)]
11pub enum PoeItemParseError {
12    #[error("Could not parse Item: {0}")]
13    ParseError(String),
14}
15
16#[derive(Debug)]
17pub struct PoeModifier {
18    pub modtype: Option<String>,
19    pub name: Option<String>,
20    pub tier: Option<i32>,
21    pub tags: Option<Vec<String>>,
22    pub line: String,
23}
24
25#[derive(Debug)]
26pub struct PoeItem {
27    pub item_class: String,
28    pub rarity: String,
29    pub name: String,
30    pub base: String,
31    // Stats
32    pub physical_damage: Option<String>,
33    pub elemental_damage: Option<String>,
34    pub crit_strike_chance: Option<String>,
35    pub attacks_per_second: Option<String>,
36    pub weapon_range: Option<String>,
37    // Requirements
38    pub lvl_requirement: Option<i32>,
39    pub str_requirement: Option<i32>,
40    pub dex_requirement: Option<i32>,
41    pub int_requirement: Option<i32>,
42    pub class_requirement: Option<String>,
43
44    pub sockets: Option<String>,
45    pub ilvl: Option<i32>,
46    pub modifiers: Option<Vec<PoeModifier>>,
47    pub corrupted: bool,
48}
49
50fn get_str_value(pair: &Pair<Rule>) -> String {
51    pair.as_str().trim().to_string()
52}
53
54fn parse_int_value(pair: &Pair<Rule>) -> Result<i32, PoeItemParseError> {
55    pair.as_str().trim().parse::<i32>()
56        .map_err(|err|PoeItemParseError::ParseError(err.to_string()))
57}
58
59impl PoeItem {
60    pub fn parse(stream: &str) -> Result<Self, PoeItemParseError> {
61        let mut pairs = PoeParser::parse(Rule::item, &stream)
62            .map_err(|err| PoeItemParseError::ParseError(err.to_string()))?;
63        
64        let mut item = PoeItem { 
65          item_class: "".to_owned(), 
66          rarity: "".to_owned(), 
67          name: "".to_owned(), 
68          base: "".to_owned(), 
69          physical_damage: None, 
70          elemental_damage: None, 
71          crit_strike_chance: None, 
72          attacks_per_second: None, 
73          weapon_range: None, 
74          lvl_requirement: None, 
75          str_requirement: None, 
76          dex_requirement: None, 
77          int_requirement: None, 
78          class_requirement: None, 
79          sockets: None, 
80          ilvl: None, 
81          modifiers: None, 
82          corrupted: false
83        };
84
85        let mut modifier = PoeModifier{ 
86          modtype: None, 
87          name: None, 
88          tier: None, 
89          tags: None, 
90          line: "".to_owned()
91        };
92
93        let rule = pairs.next().unwrap();
94        for pair in rule.clone().into_inner() {
95            println!("Rule: {:?}, text: {:?}", pair.as_rule(), pair.as_str());
96            match pair.as_rule() {
97              Rule::weapon_type => {},
98              Rule::class => item.item_class = get_str_value(&pair),
99              Rule::rarity => item.rarity = get_str_value(&pair),
100              Rule::name => item.name = get_str_value(&pair),
101              Rule::base => item.base = get_str_value(&pair),
102              Rule::physical_damage => item.physical_damage = Some(get_str_value(&pair)),
103              Rule::elemental_damage => item.elemental_damage = Some(get_str_value(&pair)),
104              Rule::crit_strike_chance => item.crit_strike_chance = Some(get_str_value(&pair)),
105              Rule::attacks_per_second => item.attacks_per_second = Some(get_str_value(&pair)),
106              Rule::weapon_range => item.weapon_range = Some(get_str_value(&pair)),
107              Rule::lvl_req => item.lvl_requirement = Some(parse_int_value(&pair)?),
108              Rule::str_req => item.str_requirement = Some(parse_int_value(&pair)?),
109              Rule::dex_req => item.dex_requirement = Some(parse_int_value(&pair)?),
110              Rule::int_req => item.int_requirement = Some(parse_int_value(&pair)?),
111              Rule::class_req => item.class_requirement = Some(get_str_value(&pair)),
112              Rule::sockets => item.sockets = Some(get_str_value(&pair)),
113              Rule::ilvl => item.ilvl = Some(parse_int_value(&pair)?),
114              Rule::modifier_type => modifier.modtype = Some(get_str_value(&pair)),
115              Rule::modifier_name => modifier.name = Some(get_str_value(&pair)),
116              Rule::modifier_tier => modifier.tier = Some(parse_int_value(&pair)?),
117              Rule::modifier_tag => {
118                modifier.tags
119                    .get_or_insert_with(Vec::new)
120                    .push(get_str_value(&pair));
121              },
122              Rule::modifier_line => {
123                if pair.as_str() == "Corrupted" {
124                  item.corrupted = true;
125                } else {
126                  modifier.line = pair.as_str().trim().parse().unwrap();
127                  item.modifiers
128                    .get_or_insert_with(Vec::new)
129                    .push(modifier);
130
131                  modifier = PoeModifier{ 
132                    modtype: None, 
133                    name: None, 
134                    tier: None, 
135                    tags: None, 
136                    line: "".to_owned()
137                  };
138                }
139              }
140              _ => {
141                return Err(PoeItemParseError::ParseError(format!("Unhandled Rule! Rule: {:#?} Text: {:#?}", pair.as_rule(), pair.as_str())));
142              }
143              // _ => {}
144            }
145        }
146
147        Ok(item)
148    }
149}