Add release year and filter for it
This commit is contained in:
parent
eee93f0d6e
commit
892bbb1cc0
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -209,6 +209,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"test-case",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -9,6 +9,7 @@ serde = { version = "1.0", features = ["derive"] }
|
||||
nom = "7.1.3"
|
||||
actix-web = { version = "4.3.0", default_features = false, features = ["macros"] }
|
||||
itertools = "0.10.5"
|
||||
time = { version = "0.3.17", features = ["serde", "serde-human-readable"] }
|
||||
|
||||
[dev-dependencies]
|
||||
test-case = "2.2.2"
|
||||
|
15
src/data.rs
15
src/data.rs
@ -1,5 +1,8 @@
|
||||
use serde::Deserialize;
|
||||
use std::fmt::{self, Display, Write};
|
||||
use time::Date;
|
||||
|
||||
use crate::SETS_BY_NAME;
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
|
||||
pub struct CardInfo {
|
||||
@ -37,12 +40,22 @@ pub struct CardSet {
|
||||
pub set_rarity: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, PartialEq, Eq, Clone)]
|
||||
pub struct Set {
|
||||
pub set_name: String,
|
||||
pub tcg_date: Option<Date>,
|
||||
}
|
||||
|
||||
impl Card {
|
||||
pub fn extended_info(&self) -> Result<String, fmt::Error> {
|
||||
let mut s = String::with_capacity(1000);
|
||||
s.push_str("<h3>Printings:</h3>");
|
||||
for printing in &self.card_sets {
|
||||
write!(s, "{}: {} ({})<br/>", printing.set_name, printing.set_code, printing.set_rarity)?;
|
||||
write!(s, "{}: {} ({})", printing.set_name, printing.set_code, printing.set_rarity)?;
|
||||
if let Some(date) = SETS_BY_NAME.get(&printing.set_name.to_lowercase()).and_then(|s| s.tcg_date) {
|
||||
write!(s, " - {}", date)?;
|
||||
}
|
||||
s.push_str("<br/>");
|
||||
}
|
||||
Ok(s)
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
use time::Date;
|
||||
|
||||
use crate::{
|
||||
data::Card,
|
||||
parser::{Field, Operator, RawCardFilter, Value, OPERATOR_CHARS},
|
||||
SETS_BY_NAME,
|
||||
};
|
||||
|
||||
/// A struct derived from `Card` that has all fields lowercased for easier search
|
||||
@ -19,6 +22,7 @@ pub struct SearchCard {
|
||||
link_rating: Option<i32>,
|
||||
link_arrows: Option<Vec<String>>,
|
||||
sets: Vec<String>,
|
||||
original_year: Option<i32>,
|
||||
}
|
||||
|
||||
impl From<&Card> for SearchCard {
|
||||
@ -36,6 +40,14 @@ impl From<&Card> for SearchCard {
|
||||
link_rating: card.link_rating,
|
||||
link_arrows: card.link_arrows.as_ref().map(|arrows| arrows.iter().map(|a| a.to_lowercase()).collect()),
|
||||
sets: card.card_sets.iter().filter_map(|s| s.set_code.split('-').next().map(str::to_lowercase)).collect(),
|
||||
original_year: card
|
||||
.card_sets
|
||||
.iter()
|
||||
.filter_map(|s| {
|
||||
SETS_BY_NAME.get(&s.set_name.to_lowercase()).unwrap_or_else(|| panic!("Set {} not found", s.set_name)).tcg_date
|
||||
})
|
||||
.map(Date::year)
|
||||
.min(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -64,6 +76,7 @@ pub fn build_filter(query: RawCardFilter) -> Result<CardFilter, String> {
|
||||
}
|
||||
RawCardFilter(Field::Level, op, Value::Numerical(n)) => Box::new(move |card| op.filter_number(card.level, n)),
|
||||
RawCardFilter(Field::LinkRating, op, Value::Numerical(n)) => Box::new(move |card| op.filter_number(card.link_rating, n)),
|
||||
RawCardFilter(Field::Year, op, Value::Numerical(n)) => Box::new(move |card| op.filter_number(card.original_year, n)),
|
||||
RawCardFilter(Field::Type, Operator::Equal, Value::String(s)) => Box::new(move |card| card.r#type == s),
|
||||
RawCardFilter(Field::Type, Operator::NotEqual, Value::String(s)) => Box::new(move |card| card.r#type != s),
|
||||
RawCardFilter(Field::Attribute, Operator::Equal, Value::String(s)) => Box::new(move |card| card.attribute.contains(&s)),
|
||||
|
18
src/main.rs
18
src/main.rs
@ -1,10 +1,11 @@
|
||||
#![feature(option_result_contains, once_cell)]
|
||||
use actix_web::{get, http::header, web, App, Either, HttpResponse, HttpServer};
|
||||
use data::{Card, CardInfo};
|
||||
use data::{Card, CardInfo, Set};
|
||||
use filter::SearchCard;
|
||||
use itertools::Itertools;
|
||||
use serde::Deserialize;
|
||||
use std::{collections::HashMap, fmt::Write, fs::File, io::BufReader, net::Ipv4Addr, sync::LazyLock, time::Instant};
|
||||
use time::Date;
|
||||
|
||||
mod data;
|
||||
mod filter;
|
||||
@ -13,13 +14,24 @@ mod parser;
|
||||
const RESULT_LIMIT: usize = 100;
|
||||
|
||||
static CARDS: LazyLock<Vec<Card>> = LazyLock::new(|| {
|
||||
serde_json::from_reader::<_, CardInfo>(BufReader::new(File::open("cards.json").expect("cards.json not found")))
|
||||
let mut cards = serde_json::from_reader::<_, CardInfo>(BufReader::new(File::open("cards.json").expect("cards.json not found")))
|
||||
.expect("Could not deserialize cards")
|
||||
.data
|
||||
.data;
|
||||
cards.iter_mut().for_each(|c| {
|
||||
c.card_sets.sort_unstable_by_key(|s| SETS_BY_NAME.get(&s.set_name.to_lowercase()).and_then(|s| s.tcg_date).unwrap_or(Date::MAX))
|
||||
});
|
||||
cards
|
||||
});
|
||||
static CARDS_BY_ID: LazyLock<HashMap<usize, Card>> =
|
||||
LazyLock::new(|| CARDS.iter().map(|c| (c.id, Card { text: c.text.replace('\r', "").replace('\n', "<br/>"), ..c.clone() })).collect());
|
||||
static SEARCH_CARDS: LazyLock<Vec<SearchCard>> = LazyLock::new(|| CARDS.iter().map(SearchCard::from).collect());
|
||||
static SETS_BY_NAME: LazyLock<HashMap<String, Set>> = LazyLock::new(|| {
|
||||
serde_json::from_reader::<_, Vec<Set>>(BufReader::new(File::open("sets.json").expect("sets.json not found")))
|
||||
.expect("Could not deserialize sets")
|
||||
.into_iter()
|
||||
.map(|s| (s.set_name.to_lowercase(), s))
|
||||
.collect()
|
||||
});
|
||||
|
||||
static IMG_HOST: LazyLock<String> = LazyLock::new(|| std::env::var("IMG_HOST").unwrap_or_else(|_| String::new()));
|
||||
|
||||
|
@ -64,14 +64,15 @@ fn value(input: &str) -> IResult<&str, Value> {
|
||||
pub enum Field {
|
||||
Atk = 1,
|
||||
Def = 2,
|
||||
Level = 3,
|
||||
LinkRating = 4,
|
||||
Set = 5,
|
||||
Type = 6,
|
||||
Attribute = 7,
|
||||
Class = 8,
|
||||
Name = 9,
|
||||
Text = 10,
|
||||
Level = 4,
|
||||
LinkRating = 6,
|
||||
Year = 8,
|
||||
Set = 10,
|
||||
Type = 12,
|
||||
Attribute = 14,
|
||||
Class = 16,
|
||||
Name = 18,
|
||||
Text = 20,
|
||||
}
|
||||
|
||||
impl Display for Field {
|
||||
@ -87,6 +88,7 @@ impl Display for Field {
|
||||
Self::Def => "DEF",
|
||||
Self::LinkRating => "link rating",
|
||||
Self::Set => "set",
|
||||
Self::Year => "year",
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -105,6 +107,7 @@ impl FromStr for Field {
|
||||
"lr" | "linkrating" => Self::LinkRating,
|
||||
"name" => Self::Name,
|
||||
"set" | "s" => Self::Set,
|
||||
"year" | "y" => Self::Year,
|
||||
_ => Err(s.to_string())?,
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user