feat(lib): Implement draft list parser
This commit is contained in:
parent
f2783d49c6
commit
aca5be2a9e
122
src/draft_list.rs
Normal file
122
src/draft_list.rs
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
use std::{collections::HashMap, io::BufRead};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
/// Parser for Will's special banlist format that lists extra copies
|
||||||
|
/// of cards in comments
|
||||||
|
pub struct DraftList {
|
||||||
|
pub title: String,
|
||||||
|
pub cards: HashMap<u32, usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DraftList {
|
||||||
|
/// Parse a draft list from a u8 slice.
|
||||||
|
pub fn from_slice(file: &[u8]) -> Result<Self> {
|
||||||
|
let mut title = None;
|
||||||
|
let mut cards = HashMap::new();
|
||||||
|
|
||||||
|
for line in file.lines() {
|
||||||
|
let line = line?;
|
||||||
|
|
||||||
|
fn parse_card(card_id: &str, quantity: &str) -> Result<(u32, usize)> {
|
||||||
|
let id = card_id
|
||||||
|
.parse()
|
||||||
|
.map_err(|err| DraftListParseError::InvalidCardID(card_id.to_owned(), err))?;
|
||||||
|
|
||||||
|
let quantity = quantity.parse().map_err(|err| {
|
||||||
|
DraftListParseError::InvalidCardQuantity(quantity.to_owned(), err)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok((id, quantity))
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(spare) = line.strip_prefix("###") {
|
||||||
|
let spare = spare.trim();
|
||||||
|
let Some((id, quantity)) = spare.split_once(' ') else {
|
||||||
|
// Just assume that this is a comment
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let (id, quantity) = parse_card(id, quantity)?;
|
||||||
|
// The spare quantity does *not* include the base 3
|
||||||
|
// cards we should have if we have spares.
|
||||||
|
cards.insert(id, quantity + 3);
|
||||||
|
} else if let Some(t) = line.strip_prefix('!') {
|
||||||
|
title = Some(t.to_owned());
|
||||||
|
} else if line.starts_with('#') {
|
||||||
|
// Skip any comments with less than 3 #
|
||||||
|
continue;
|
||||||
|
} else if let Some((id, quantity)) = line.split_once(' ') {
|
||||||
|
// We record invalid cards with -1, which obviously
|
||||||
|
// doesn't parse to usize, so just skip these
|
||||||
|
if quantity.starts_with('-') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (id, quantity) = parse_card(id, quantity)?;
|
||||||
|
cards.insert(id, quantity);
|
||||||
|
} else if line == "$blacklist" {
|
||||||
|
return Err(DraftListParseError::InputIsBlacklist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
title: title.ok_or(DraftListParseError::MissingTitle)?,
|
||||||
|
cards,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum DraftListParseError {
|
||||||
|
#[error("draft list is corrupted")]
|
||||||
|
MalformedInput(#[from] std::io::Error),
|
||||||
|
#[error("draft list is actually a blacklist; currently unsupported")]
|
||||||
|
InputIsBlacklist,
|
||||||
|
#[error("draft list lacks a title")]
|
||||||
|
MissingTitle,
|
||||||
|
#[error("draft list contains an invalid card id: {}", 0)]
|
||||||
|
InvalidCardID(String, #[source] std::num::ParseIntError),
|
||||||
|
#[error("draft list contains an invalid card quantity: {}", 0)]
|
||||||
|
InvalidCardQuantity(String, #[source] std::num::ParseIntError),
|
||||||
|
}
|
||||||
|
|
||||||
|
type Result<T> = std::result::Result<T, DraftListParseError>;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use dedent::dedent;
|
||||||
|
|
||||||
|
use super::DraftList;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_list() {
|
||||||
|
let list = dedent!(
|
||||||
|
r#"
|
||||||
|
!Treestan - Mar2024 17
|
||||||
|
$whitelist
|
||||||
|
|
||||||
|
84177693 1
|
||||||
|
70491682 2
|
||||||
|
13361027 3
|
||||||
|
|
||||||
|
# Some irrelevant comment
|
||||||
|
160405002 -1
|
||||||
|
|
||||||
|
### 10731333 5
|
||||||
|
### 46136942 3
|
||||||
|
### 36211150 12
|
||||||
|
"#
|
||||||
|
)
|
||||||
|
.as_bytes();
|
||||||
|
|
||||||
|
let list = DraftList::from_slice(list).expect("draft list must parse correctly");
|
||||||
|
|
||||||
|
assert_eq!(list.title, "Treestan - Mar2024 17");
|
||||||
|
assert_eq!(list.cards.get(&84177693), Some(&1));
|
||||||
|
assert_eq!(list.cards.get(&70491682), Some(&2));
|
||||||
|
assert_eq!(list.cards.get(&13361027), Some(&3));
|
||||||
|
assert_eq!(list.cards.get(&10731333), Some(&8));
|
||||||
|
assert_eq!(list.cards.get(&46136942), Some(&6));
|
||||||
|
assert_eq!(list.cards.get(&36211150), Some(&15));
|
||||||
|
}
|
||||||
|
}
|
1
src/lib.rs
Normal file
1
src/lib.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod draft_list;
|
Loading…
Reference in a new issue