manabrew_engine/keyword/
mod.rs1pub mod keyword_collection;
6pub mod keyword_instance;
7pub mod keyword_interface;
8pub mod keywords_change;
9pub mod trait_keywords_change;
10
11pub mod keyword_with_amount;
13pub mod keyword_with_cost;
14pub mod keyword_with_cost_and_amount;
15pub mod keyword_with_cost_and_type;
16pub mod keyword_with_cost_interface;
17pub mod keyword_with_type;
18pub mod keyword_with_type_interface;
19pub mod simple_keyword;
20
21pub mod amplify;
23pub mod companion;
24pub mod compleated;
25pub mod craft;
26pub mod devour;
27pub mod emerge;
28pub mod equip;
29pub mod firebending;
30pub mod hexproof;
31pub mod kicker;
32pub mod mayhem;
33pub mod modular;
34pub mod ninjutsu;
35pub mod partner;
36pub mod protection;
37pub mod suspend;
38pub mod trample;
39pub mod vanishing;
40
41pub use keyword_instance::Keyword;
43
44pub fn smart_value_of(name: &str) -> Keyword {
48 Keyword::smart_value_of(name)
49}
50
51#[derive(Debug, Clone)]
56pub struct KickerInfo {
57 pub cost1: String,
59 pub cost2: Option<String>,
61}
62
63#[derive(Debug, Clone)]
65pub struct EscapeInfo {
66 pub mana_cost: String,
68 pub exile_count: i32,
70}
71
72pub fn extract_keyword_cost_str<'a>(kw: &'a str, name: &str) -> Option<&'a str> {
76 let prefix = format!("{}:", name);
77 kw.strip_prefix(&prefix)
78}
79
80pub fn parse_keyword_cost(keywords: &[String], name: &str) -> Option<String> {
83 let prefix = format!("{}:", name);
84 for kw in keywords {
85 if let Some(cost) = kw.strip_prefix(&prefix) {
86 return Some(cost.to_string());
87 }
88 }
89 None
90}
91
92pub fn extract_keyword_cost(
99 collection: &keyword_collection::KeywordCollection,
100 name: &str,
101) -> Option<String> {
102 let prefix = format!("{}:", name);
103 for kw in collection.iter_strings() {
104 if let Some(cost) = kw.strip_prefix(&prefix) {
105 return Some(cost.to_string());
106 }
107 }
108 None
109}
110
111pub fn extract_keyword_cost_from_all<'a>(
113 collections: impl IntoIterator<Item = &'a keyword_collection::KeywordCollection>,
114 name: &str,
115) -> Option<String> {
116 let prefix = format!("{}:", name);
117 for coll in collections {
118 for kw in coll.iter_strings() {
119 if let Some(cost) = kw.strip_prefix(&prefix) {
120 return Some(cost.to_string());
121 }
122 }
123 }
124 None
125}
126
127#[derive(Debug, Clone)]
129pub struct SuspendInfo {
130 pub mana_cost: String,
131 pub time_counters: i32,
132}
133
134pub fn extract_suspend(collection: &keyword_collection::KeywordCollection) -> Option<SuspendInfo> {
137 for kw in collection.iter_strings() {
138 if let Some(rest) = kw.strip_prefix("Suspend:") {
139 if let Some(colon_pos) = rest.rfind(':') {
140 return Some(SuspendInfo {
141 mana_cost: rest[..colon_pos].trim().to_string(),
142 time_counters: rest[colon_pos + 1..].trim().parse().unwrap_or(0),
143 });
144 }
145 }
146 }
147 None
148}
149
150pub fn extract_escape(collection: &keyword_collection::KeywordCollection) -> Option<EscapeInfo> {
152 for kw in collection.iter_strings() {
153 if let Some(rest) = kw.strip_prefix("Escape:") {
154 if let Some(last_colon) = rest.rfind(':') {
155 return Some(EscapeInfo {
156 mana_cost: rest[..last_colon].trim().to_string(),
157 exile_count: rest[last_colon + 1..].trim().parse().unwrap_or(0),
158 });
159 }
160 }
161 }
162 None
163}
164
165pub fn extract_kicker(collection: &keyword_collection::KeywordCollection) -> Option<KickerInfo> {
167 for kw in collection.iter_strings() {
168 if let Some(rest) = kw.strip_prefix("Kicker:") {
169 let parts: Vec<&str> = rest.splitn(2, ':').collect();
170 return Some(if parts.len() == 2 {
171 KickerInfo {
172 cost1: parts[0].to_string(),
173 cost2: Some(parts[1].to_string()),
174 }
175 } else {
176 KickerInfo {
177 cost1: rest.to_string(),
178 cost2: None,
179 }
180 });
181 }
182 }
183 None
184}
185
186pub fn parse_kicker(keywords: &[String]) -> Option<KickerInfo> {
189 for kw in keywords {
190 if let Some(rest) = kw.strip_prefix("Kicker:") {
191 let parts: Vec<&str> = rest.splitn(2, ':').collect();
194 if parts.len() == 2 {
195 return Some(KickerInfo {
196 cost1: parts[0].to_string(),
197 cost2: Some(parts[1].to_string()),
198 });
199 } else {
200 return Some(KickerInfo {
201 cost1: rest.to_string(),
202 cost2: None,
203 });
204 }
205 }
206 }
207 None
208}
209
210pub fn parse_escape(keywords: &[String]) -> Option<EscapeInfo> {
213 for kw in keywords {
214 if let Some(rest) = kw.strip_prefix("Escape:") {
215 if let Some(last_colon) = rest.rfind(':') {
217 let mana_cost = &rest[..last_colon];
218 let exile_str = &rest[last_colon + 1..];
219 let exile_count = exile_str.parse::<i32>().unwrap_or(0);
220 return Some(EscapeInfo {
221 mana_cost: mana_cost.to_string(),
222 exile_count,
223 });
224 }
225 }
226 }
227 None
228}
229
230#[cfg(test)]
231mod tests {
232 use super::*;
233
234 #[test]
235 fn test_parse_keyword_cost() {
236 let keywords = vec!["Flashback:2 R".to_string(), "Flying".to_string()];
237 assert_eq!(
238 parse_keyword_cost(&keywords, "Flashback"),
239 Some("2 R".to_string())
240 );
241 assert_eq!(parse_keyword_cost(&keywords, "Evoke"), None);
242 }
243
244 #[test]
245 fn test_parse_kicker_single() {
246 let keywords = vec!["Kicker:1 R".to_string()];
247 let info = parse_kicker(&keywords).unwrap();
248 assert_eq!(info.cost1, "1 R");
249 assert!(info.cost2.is_none());
250 }
251
252 #[test]
253 fn test_parse_kicker_double() {
254 let keywords = vec!["Kicker:1 R:1 G".to_string()];
255 let info = parse_kicker(&keywords).unwrap();
256 assert_eq!(info.cost1, "1 R");
257 assert_eq!(info.cost2, Some("1 G".to_string()));
258 }
259
260 #[test]
261 fn test_parse_escape() {
262 let keywords = vec!["Escape:3 B B:4".to_string()];
263 let info = parse_escape(&keywords).unwrap();
264 assert_eq!(info.mana_cost, "3 B B");
265 assert_eq!(info.exile_count, 4);
266 }
267}