use std::fmt;
pub use self::functions::*;
use crate::search::param::compare::{compare_op_str, Compare, CompareOp};
use crate::search::param::Param;
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct ValueKind(ValueKindImpl);
impl ValueKind {
pub(super) fn fmt_value(&self, value: &str, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{self}:{value}")
}
pub(super) fn fmt_comparison(
&self,
op: CompareOp,
value: &str,
f: &mut fmt::Formatter,
) -> fmt::Result {
write!(f, "{}{}{}", self, compare_op_str(Some(op)), value)
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
enum ValueKindImpl {
Color,
ColorIdentity,
Type,
Oracle,
FullOracle,
Keyword,
Mana,
Devotion,
Produces,
Rarity,
InRarity,
Set,
InSet,
Number,
Block,
SetType,
InSetType,
Cube,
Format,
Banned,
Restricted,
Cheapest,
Artist,
Flavor,
Watermark,
BorderColor,
Frame,
Date,
Game,
InGame,
Language,
InLanguage,
Name,
NumericComparable(NumProperty),
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum NumProperty {
Power,
Toughness,
PowTou,
Loyalty,
Cmc,
ArtistCount,
Usd,
UsdFoil,
Eur,
Tix,
IllustrationCount,
PrintCount,
SetCount,
PaperPrintCount,
PaperSetCount,
Year,
}
const fn numeric_property_str(prop: NumProperty) -> &'static str {
match prop {
NumProperty::Power => "power",
NumProperty::Toughness => "toughness",
NumProperty::PowTou => "powtou",
NumProperty::Loyalty => "loyalty",
NumProperty::Cmc => "cmc",
NumProperty::ArtistCount => "artists",
NumProperty::Usd => "usd",
NumProperty::UsdFoil => "usdfoil",
NumProperty::Eur => "eur",
NumProperty::Tix => "tix",
NumProperty::IllustrationCount => "illustrations",
NumProperty::PrintCount => "prints",
NumProperty::SetCount => "sets",
NumProperty::PaperPrintCount => "paperprints",
NumProperty::PaperSetCount => "papersets",
NumProperty::Year => "year",
}
}
impl fmt::Display for NumProperty {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(numeric_property_str(*self))
}
}
impl fmt::Display for ValueKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match &self.0 {
ValueKindImpl::Color => "color",
ValueKindImpl::ColorIdentity => "identity",
ValueKindImpl::Type => "type",
ValueKindImpl::Oracle => "oracle",
ValueKindImpl::FullOracle => "fulloracle",
ValueKindImpl::Keyword => "keyword",
ValueKindImpl::Mana => "mana",
ValueKindImpl::Devotion => "devotion",
ValueKindImpl::Produces => "produces",
ValueKindImpl::Rarity => "rarity",
ValueKindImpl::Set => "set",
ValueKindImpl::Number => "number",
ValueKindImpl::Block => "block",
ValueKindImpl::SetType => "settype",
ValueKindImpl::Cube => "cube",
ValueKindImpl::Format => "format",
ValueKindImpl::Banned => "banned",
ValueKindImpl::Restricted => "restricted",
ValueKindImpl::Cheapest => "cheapest",
ValueKindImpl::Artist => "artist",
ValueKindImpl::Flavor => "flavor",
ValueKindImpl::Watermark => "watermark",
ValueKindImpl::BorderColor => "border",
ValueKindImpl::Frame => "frame",
ValueKindImpl::Date => "date",
ValueKindImpl::Game => "game",
ValueKindImpl::Language => "language",
ValueKindImpl::InRarity
| ValueKindImpl::InSet
| ValueKindImpl::InSetType
| ValueKindImpl::InGame
| ValueKindImpl::InLanguage => "in",
ValueKindImpl::Name => "name",
ValueKindImpl::NumericComparable(np) => numeric_property_str(*np),
}
)
}
}
pub trait ParamValue: fmt::Debug + fmt::Display {
fn into_param(self, kind: ValueKind) -> Param
where
Self: Sized,
{
Param::value(kind, self)
}
}
pub trait NumericValue: ParamValue {}
macro_rules! impl_numeric_values {
($($Ty:ty,)*) => {
$(
impl ParamValue for $Ty {}
impl NumericValue for $Ty {}
impl NumericComparableValue for $Ty {}
)*
};
}
#[rustfmt::skip]
impl_numeric_values!(
usize, u8, u16, u32, u64, u128,
isize, i8, i16, i32, i64, i128,
f32, f64,
);
pub trait NumericComparableValue: ParamValue {}
impl<T: NumericComparableValue> NumericComparableValue for Compare<T> {}
impl ParamValue for NumProperty {
fn into_param(self, kind: ValueKind) -> Param {
numeric_property_str(self).into_param(kind)
}
}
impl NumericComparableValue for NumProperty {}
pub trait TextValue: ParamValue {}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
struct Quoted<T>(T);
impl<T: fmt::Display> fmt::Display for Quoted<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "\"{}\"", self.0)
}
}
impl ParamValue for Quoted<String> {
fn into_param(self, kind: ValueKind) -> Param {
Param::value(kind, self)
}
}
impl TextValue for Quoted<String> {}
impl ParamValue for String {
fn into_param(self, kind: ValueKind) -> Param {
Quoted(self).into_param(kind)
}
}
impl TextValue for String {}
impl ParamValue for &str {
fn into_param(self, kind: ValueKind) -> Param {
self.to_string().into_param(kind)
}
}
impl TextValue for &str {}
pub trait TextOrRegexValue: ParamValue {}
impl<T: TextValue> TextOrRegexValue for T {}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Regex(pub String);
impl fmt::Display for Regex {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "/{}/", self.0.replace('/', "\\/"))
}
}
impl<T: AsRef<str>> From<T> for Regex {
fn from(string: T) -> Self {
Regex(string.as_ref().to_string())
}
}
impl ParamValue for Regex {}
impl TextOrRegexValue for Regex {}
pub trait ColorValue: ParamValue {}
impl<T: ColorValue> ColorValue for Compare<T> {}
impl ParamValue for crate::card::Color {}
impl ColorValue for crate::card::Color {}
impl ParamValue for crate::card::Colors {}
impl ColorValue for crate::card::Colors {}
impl ParamValue for crate::card::Multicolored {}
impl ColorValue for crate::card::Multicolored {}
impl<T: TextValue> ColorValue for T {}
pub trait DevotionValue: ParamValue {}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct Devotion(crate::card::Color, Option<crate::card::Color>, usize);
impl fmt::Display for Devotion {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let count = self.2;
if count == 0 {
write!(f, "0")
} else {
let color_a = self.0;
for _ in 0..count {
match self.1 {
Some(color_b) if color_b != color_a => {
write!(f, "{{{color_a}/{color_b}}}")
},
_ => write!(f, "{{{color_a}}}"),
}?;
}
Ok(())
}
}
}
impl ParamValue for Devotion {}
impl DevotionValue for Devotion {}
impl DevotionValue for Compare<Devotion> {}
impl Devotion {
pub fn monocolor(color: crate::card::Color, count: usize) -> Self {
Devotion(color, None, count)
}
pub fn hybrid(color_a: crate::card::Color, color_b: crate::card::Color, count: usize) -> Self {
Devotion(color_a, Some(color_b), count)
}
}
pub trait RarityValue: ParamValue {}
impl<T: TextValue> RarityValue for T {}
impl ParamValue for crate::card::Rarity {}
impl RarityValue for crate::card::Rarity {}
impl RarityValue for Compare<crate::card::Rarity> {}
impl<T: TextValue> RarityValue for Compare<T> {}
pub trait SetValue: ParamValue {}
impl<T: TextValue> SetValue for T {}
impl ParamValue for crate::set::SetCode {}
impl SetValue for crate::set::SetCode {}
pub trait CubeValue: ParamValue {}
impl<T: TextValue> CubeValue for T {}
pub trait FormatValue: ParamValue {}
impl<T: TextValue> FormatValue for T {}
impl ParamValue for crate::format::Format {}
impl FormatValue for crate::format::Format {}
pub trait CurrencyValue: ParamValue {}
impl<T: TextValue> CurrencyValue for T {}
pub trait SetTypeValue: ParamValue {}
impl ParamValue for crate::set::SetType {}
impl SetTypeValue for crate::set::SetType {}
impl<T: TextValue> SetTypeValue for T {}
pub trait BorderColorValue: ParamValue {}
impl<T: TextValue> BorderColorValue for T {}
impl ParamValue for crate::card::BorderColor {}
impl BorderColorValue for crate::card::BorderColor {}
pub trait FrameValue: ParamValue {}
impl<T: TextValue> FrameValue for T {}
impl ParamValue for crate::card::FrameEffect {}
impl FrameValue for crate::card::FrameEffect {}
impl ParamValue for crate::card::Frame {}
impl FrameValue for crate::card::Frame {}
pub trait DateValue: ParamValue {}
impl<T: DateValue> DateValue for Compare<T> {}
impl<T: SetValue> DateValue for T {}
impl ParamValue for chrono::NaiveDate {
fn into_param(self, kind: ValueKind) -> Param
where
Self: Sized,
{
Param::value(kind, self.format("%Y-%m-%d").to_string())
}
}
impl DateValue for chrono::NaiveDate {}
pub trait GameValue: ParamValue {}
impl<T: TextValue> GameValue for T {}
impl ParamValue for crate::card::Game {}
impl GameValue for crate::card::Game {}
pub trait LanguageValue: ParamValue {}
impl<T: TextValue> LanguageValue for T {}
mod functions {
use super::*;
use crate::search::query::Query;
macro_rules! value_fns {
($(
$(#[$($attr:meta)*])*
$func:ident => $Kind:ident : $Constraint:ident,
)*) => {
$(
$(#[$($attr)*])*
pub fn $func(value: impl $Constraint) -> Query {
Query::Param(value.into_param(ValueKind(ValueKindImpl::$Kind)))
}
)*
};
}
value_fns! {
#[doc = "The color of this card, based on indicator or cost."]
color => Color: ColorValue,
#[doc = "The number of colors of this card, based on indicator or cost."]
color_count => Color: NumericValue,
#[doc = "The color identity of this card, for Commander-like formats."]
color_identity => ColorIdentity: ColorValue,
#[doc = "The number of colors in this card's identity, for Commander-like formats."]
color_identity_count => ColorIdentity: NumericValue,
#[doc = "The type line of this card."]
type_line => Type: TextOrRegexValue,
#[doc = "The updated oracle text of this card."]
oracle_text => Oracle: TextOrRegexValue,
#[doc = "The updated oracle text of this card, including reminder text."]
full_oracle_text => FullOracle: TextOrRegexValue,
#[doc = "Keyword ability that this card has."]
keyword => Keyword: TextValue,
#[doc = "The mana cost of this card."]
mana => Mana: ColorValue,
#[doc = "The devotion granted by this permanent. See [`Devotion`]."]
devotion => Devotion: DevotionValue,
#[doc = "The colors of mana produced by this card."]
produces => Produces: ColorValue,
#[doc = "The rarity of this printing."]
rarity => Rarity: RarityValue,
#[doc = "Has the card ever been printed in this rarity?"]
in_rarity => InRarity: RarityValue,
#[doc = "The set code of this printing."]
set => Set: SetValue,
#[doc = "Was the card printed in this set?"]
in_set => InSet: SetValue,
#[doc = "The card's collector number."]
collector_number => Number: NumericValue,
#[doc = "The block of this card. Works with any set grouped in the same block."]
block => Block: SetValue,
#[doc = "The type of set this printing is in."]
set_type => SetType: SetTypeValue,
#[doc = "Has the card appeared in a set of this type?"]
in_set_type => InSetType: SetTypeValue,
#[doc = "Does the card appear in this cube on MTGO?"]
cube => Cube: CubeValue,
#[doc(hidden)]
format => Format: FormatValue,
#[doc = "The card is banned in this format."]
banned => Banned: FormatValue,
#[doc = "The card is restricted in this format."]
restricted => Restricted: FormatValue,
#[doc = "Return the printing that is the cheapest in the specified currency."]
cheapest => Cheapest: CurrencyValue,
#[doc = "The artist who illustrated this card."]
artist => Artist: TextValue,
#[doc = "The flavor text of this printing."]
flavor_text => Flavor: TextOrRegexValue,
#[doc = "The type of watermark on this printing."]
watermark => Watermark: TextValue,
#[doc = "The border color of this printing."]
border_color => BorderColor: BorderColorValue,
#[doc = "The card frame of this printing, related to the year of the print."]
frame => Frame: FrameValue,
#[doc = "The date this printing was released."]
date => Date: DateValue,
#[doc = "This printing is available in the specified game."]
game => Game: GameValue,
#[doc = "This card is available in the specified game."]
in_game => InGame: GameValue,
#[doc = "This printing is in the specified language."]
language => Language: LanguageValue,
#[doc = "Has this card ever been printed in the specified language?"]
in_language => InLanguage: LanguageValue,
#[doc = "The card's name, using fuzzy search."]
name => Name: TextOrRegexValue,
}
macro_rules! numeric_value_fns {
($(
$(#[$($attr:meta)*])*
$func:ident => $NumProp:ident,
)*) => {
$(
$(#[$($attr)*])*
pub fn $func(value: impl NumericComparableValue) -> Query {
Query::Param(value.into_param(ValueKind(
ValueKindImpl::NumericComparable(NumProperty::$NumProp),
)))
}
)*
};
}
numeric_value_fns! {
#[doc = "The card's power, if it is a creature or vehicle. '*' and 'X' count as 0."]
power => Power,
#[doc = "The card's toughness, if it is a creature or vehicle. '*' and 'X' count as 0."]
toughness => Toughness,
#[doc = "The card's power plus its toughness."]
pow_tou => PowTou,
#[doc = "The card's loyalty, if it is a planeswalker. 'X' counts as 0."]
loyalty => Loyalty,
#[doc = "The converted mana cost of this card."]
cmc => Cmc,
#[doc = "The number of artists credited for this printing."]
artist_count => ArtistCount,
#[doc = "The current market price of this card in US Dollars."]
usd => Usd,
#[doc = "The current foil market price of this card in US Dollars."]
usd_foil => UsdFoil,
#[doc = "The current market price of this card in Euros."]
eur => Eur,
#[doc = "The current market price of this card in MTGO tickets."]
tix => Tix,
#[doc = "The number of unique art this card has had."]
illustration_count => IllustrationCount,
#[doc = "The number of unique prints of this card."]
print_count => PrintCount,
#[doc = "The number of sets this card has appeared in."]
set_count => SetCount,
#[doc = "The number of unique prints of this card, counting paper only."]
paper_print_count => PaperPrintCount,
#[doc = "The number of sets this card has appeared in, counting paper only."]
paper_set_count => PaperSetCount,
#[doc = "The year this card was released."]
year => Year,
}
}