1use thiserror::Error; use 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 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 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 }
145 }
146
147 Ok(item)
148 }
149}