1use std::fmt;
12
13pub use self::functions::*;
14use crate::search::param::compare::{compare_op_str, Compare, CompareOp};
15use crate::search::param::Param;
16
17#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
23pub struct ValueKind(ValueKindImpl);
24
25impl ValueKind {
26 pub(super) fn fmt_value(&self, value: &str, f: &mut fmt::Formatter) -> fmt::Result {
27 write!(f, "{self}:{value}")
28 }
29
30 pub(super) fn fmt_comparison(
31 &self,
32 op: CompareOp,
33 value: &str,
34 f: &mut fmt::Formatter,
35 ) -> fmt::Result {
36 write!(f, "{}{}{}", self, compare_op_str(Some(op)), value)
37 }
38}
39
40#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
41enum ValueKindImpl {
42 Color,
43 ColorIdentity,
44 Type,
45 Oracle,
46 FullOracle,
47 Keyword,
48 Mana,
49 Devotion,
50 Produces,
51 Rarity,
52 InRarity,
53 Set,
54 InSet,
55 Number,
56 Block,
57 SetType,
58 InSetType,
59 Cube,
60 Format,
61 Banned,
62 Restricted,
63 Cheapest,
64 Artist,
65 Flavor,
66 Watermark,
67 BorderColor,
68 Frame,
69 Date,
70 Game,
71 InGame,
72 Language,
73 InLanguage,
74 Name,
75 NumericComparable(NumProperty),
76}
77
78#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
82pub enum NumProperty {
83 Power,
85 Toughness,
87 PowTou,
89 Loyalty,
93 Cmc,
96 ArtistCount,
101 Usd,
103 UsdFoil,
105 Eur,
107 Tix,
109 IllustrationCount,
111 PrintCount,
114 SetCount,
117 PaperPrintCount,
119 PaperSetCount,
121 Year,
123}
124
125const fn numeric_property_str(prop: NumProperty) -> &'static str {
126 match prop {
127 NumProperty::Power => "power",
128 NumProperty::Toughness => "toughness",
129 NumProperty::PowTou => "powtou",
130 NumProperty::Loyalty => "loyalty",
131 NumProperty::Cmc => "cmc",
132 NumProperty::ArtistCount => "artists",
133 NumProperty::Usd => "usd",
134 NumProperty::UsdFoil => "usdfoil",
135 NumProperty::Eur => "eur",
136 NumProperty::Tix => "tix",
137 NumProperty::IllustrationCount => "illustrations",
138 NumProperty::PrintCount => "prints",
139 NumProperty::SetCount => "sets",
140 NumProperty::PaperPrintCount => "paperprints",
141 NumProperty::PaperSetCount => "papersets",
142 NumProperty::Year => "year",
143 }
144}
145
146impl fmt::Display for NumProperty {
147 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
148 f.write_str(numeric_property_str(*self))
149 }
150}
151
152impl fmt::Display for ValueKind {
153 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
154 write!(
155 f,
156 "{}",
157 match &self.0 {
158 ValueKindImpl::Color => "color",
159 ValueKindImpl::ColorIdentity => "identity",
160 ValueKindImpl::Type => "type",
161 ValueKindImpl::Oracle => "oracle",
162 ValueKindImpl::FullOracle => "fulloracle",
163 ValueKindImpl::Keyword => "keyword",
164 ValueKindImpl::Mana => "mana",
165 ValueKindImpl::Devotion => "devotion",
166 ValueKindImpl::Produces => "produces",
167 ValueKindImpl::Rarity => "rarity",
168 ValueKindImpl::Set => "set",
169 ValueKindImpl::Number => "number",
170 ValueKindImpl::Block => "block",
171 ValueKindImpl::SetType => "settype",
172 ValueKindImpl::Cube => "cube",
173 ValueKindImpl::Format => "format",
174 ValueKindImpl::Banned => "banned",
175 ValueKindImpl::Restricted => "restricted",
176 ValueKindImpl::Cheapest => "cheapest",
177 ValueKindImpl::Artist => "artist",
178 ValueKindImpl::Flavor => "flavor",
179 ValueKindImpl::Watermark => "watermark",
180 ValueKindImpl::BorderColor => "border",
181 ValueKindImpl::Frame => "frame",
182 ValueKindImpl::Date => "date",
183 ValueKindImpl::Game => "game",
184 ValueKindImpl::Language => "language",
185 ValueKindImpl::InRarity
186 | ValueKindImpl::InSet
187 | ValueKindImpl::InSetType
188 | ValueKindImpl::InGame
189 | ValueKindImpl::InLanguage => "in",
190 ValueKindImpl::Name => "name",
191 ValueKindImpl::NumericComparable(np) => numeric_property_str(*np),
192 }
193 )
194 }
195}
196
197pub trait ParamValue: fmt::Debug + fmt::Display {
200 fn into_param(self, kind: ValueKind) -> Param
202 where
203 Self: Sized,
204 {
205 Param::value(kind, self)
206 }
207}
208
209pub trait NumericValue: ParamValue {}
218
219macro_rules! impl_numeric_values {
220 ($($Ty:ty,)*) => {
221 $(
222 impl ParamValue for $Ty {}
223 impl NumericValue for $Ty {}
224 impl NumericComparableValue for $Ty {}
225 )*
226 };
227}
228
229#[rustfmt::skip]
230impl_numeric_values!(
231 usize, u8, u16, u32, u64, u128,
232 isize, i8, i16, i32, i64, i128,
233 f32, f64,
234);
235
236pub trait NumericComparableValue: ParamValue {}
254
255impl<T: NumericComparableValue> NumericComparableValue for Compare<T> {}
256
257impl ParamValue for NumProperty {
258 fn into_param(self, kind: ValueKind) -> Param {
259 numeric_property_str(self).into_param(kind)
260 }
261}
262impl NumericComparableValue for NumProperty {}
263
264pub trait TextValue: ParamValue {}
270
271#[derive(Clone, PartialEq, Eq, Hash, Debug)]
275struct Quoted<T>(T);
276
277impl<T: fmt::Display> fmt::Display for Quoted<T> {
278 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
281 write!(f, "\"{}\"", self.0)
282 }
283}
284
285impl ParamValue for Quoted<String> {
286 fn into_param(self, kind: ValueKind) -> Param {
287 Param::value(kind, self)
288 }
289}
290impl TextValue for Quoted<String> {}
291
292impl ParamValue for String {
293 fn into_param(self, kind: ValueKind) -> Param {
294 Quoted(self).into_param(kind)
295 }
296}
297impl TextValue for String {}
298
299impl ParamValue for &str {
300 fn into_param(self, kind: ValueKind) -> Param {
301 self.to_string().into_param(kind)
302 }
303}
304impl TextValue for &str {}
305
306pub trait TextOrRegexValue: ParamValue {}
319
320impl<T: TextValue> TextOrRegexValue for T {}
321
322#[derive(Clone, PartialEq, Eq, Hash, Debug)]
343pub struct Regex(pub String);
344
345impl fmt::Display for Regex {
346 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
347 write!(f, "/{}/", self.0.replace('/', "\\/"))
348 }
349}
350
351impl<T: AsRef<str>> From<T> for Regex {
352 fn from(string: T) -> Self {
353 Regex(string.as_ref().to_string())
354 }
355}
356
357impl ParamValue for Regex {}
358impl TextOrRegexValue for Regex {}
359
360pub trait ColorValue: ParamValue {}
371
372impl<T: ColorValue> ColorValue for Compare<T> {}
373
374impl ParamValue for crate::card::Color {}
375impl ColorValue for crate::card::Color {}
376
377impl ParamValue for crate::card::Colors {}
378impl ColorValue for crate::card::Colors {}
379
380impl ParamValue for crate::card::Multicolored {}
381impl ColorValue for crate::card::Multicolored {}
382
383impl<T: TextValue> ColorValue for T {}
384
385pub trait DevotionValue: ParamValue {}
405
406#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
410pub struct Devotion(crate::card::Color, Option<crate::card::Color>, usize);
411
412impl fmt::Display for Devotion {
413 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
414 let count = self.2;
415 if count == 0 {
416 write!(f, "0")
419 } else {
420 let color_a = self.0;
421 for _ in 0..count {
422 match self.1 {
423 Some(color_b) if color_b != color_a => {
424 write!(f, "{{{color_a}/{color_b}}}")
425 },
426 _ => write!(f, "{{{color_a}}}"),
427 }?;
428 }
429 Ok(())
430 }
431 }
432}
433
434impl ParamValue for Devotion {}
435impl DevotionValue for Devotion {}
436
437impl DevotionValue for Compare<Devotion> {}
438
439impl Devotion {
440 pub fn monocolor(color: crate::card::Color, count: usize) -> Self {
442 Devotion(color, None, count)
443 }
444
445 pub fn hybrid(color_a: crate::card::Color, color_b: crate::card::Color, count: usize) -> Self {
448 Devotion(color_a, Some(color_b), count)
449 }
450}
451
452pub trait RarityValue: ParamValue {}
485
486impl<T: TextValue> RarityValue for T {}
487
488impl ParamValue for crate::card::Rarity {}
489impl RarityValue for crate::card::Rarity {}
490
491impl RarityValue for Compare<crate::card::Rarity> {}
492impl<T: TextValue> RarityValue for Compare<T> {}
493
494pub trait SetValue: ParamValue {}
515
516impl<T: TextValue> SetValue for T {}
517
518impl ParamValue for crate::set::SetCode {}
519impl SetValue for crate::set::SetCode {}
520
521pub trait CubeValue: ParamValue {}
528
529impl<T: TextValue> CubeValue for T {}
530
531pub trait FormatValue: ParamValue {}
558
559impl<T: TextValue> FormatValue for T {}
560
561impl ParamValue for crate::format::Format {}
562impl FormatValue for crate::format::Format {}
563
564pub trait CurrencyValue: ParamValue {}
570
571impl<T: TextValue> CurrencyValue for T {}
572
573pub trait SetTypeValue: ParamValue {}
581
582impl ParamValue for crate::set::SetType {}
583impl SetTypeValue for crate::set::SetType {}
584
585impl<T: TextValue> SetTypeValue for T {}
586
587pub trait BorderColorValue: ParamValue {}
594
595impl<T: TextValue> BorderColorValue for T {}
596
597impl ParamValue for crate::card::BorderColor {}
598impl BorderColorValue for crate::card::BorderColor {}
599
600pub trait FrameValue: ParamValue {}
608
609impl<T: TextValue> FrameValue for T {}
610
611impl ParamValue for crate::card::FrameEffect {}
612impl FrameValue for crate::card::FrameEffect {}
613
614impl ParamValue for crate::card::Frame {}
615impl FrameValue for crate::card::Frame {}
616
617pub trait DateValue: ParamValue {}
628
629impl<T: DateValue> DateValue for Compare<T> {}
630
631impl<T: SetValue> DateValue for T {}
632
633impl ParamValue for chrono::NaiveDate {
634 fn into_param(self, kind: ValueKind) -> Param
635 where
636 Self: Sized,
637 {
638 Param::value(kind, self.format("%Y-%m-%d").to_string())
639 }
640}
641impl DateValue for chrono::NaiveDate {}
642
643pub trait GameValue: ParamValue {}
650
651impl<T: TextValue> GameValue for T {}
652
653impl ParamValue for crate::card::Game {}
654impl GameValue for crate::card::Game {}
655
656pub trait LanguageValue: ParamValue {}
665
666impl<T: TextValue> LanguageValue for T {}
667
668mod functions {
669 use super::*;
670 use crate::search::query::Query;
671
672 macro_rules! value_fns {
673 ($(
674 $(#[$($attr:meta)*])*
675 $func:ident => $Kind:ident : $Constraint:ident,
676 )*) => {
677 $(
678 $(#[$($attr)*])*
679 pub fn $func(value: impl $Constraint) -> Query {
680 Query::Param(value.into_param(ValueKind(ValueKindImpl::$Kind)))
681 }
682 )*
683 };
684 }
685
686 value_fns! {
687 #[doc = "The color of this card, based on indicator or cost."]
688 color => Color: ColorValue,
689 #[doc = "The number of colors of this card, based on indicator or cost."]
690 color_count => Color: NumericValue,
691 #[doc = "The color identity of this card, for Commander-like formats."]
692 color_identity => ColorIdentity: ColorValue,
693 #[doc = "The number of colors in this card's identity, for Commander-like formats."]
694 color_identity_count => ColorIdentity: NumericValue,
695 #[doc = "The type line of this card."]
696 type_line => Type: TextOrRegexValue,
697 #[doc = "The updated oracle text of this card."]
698 oracle_text => Oracle: TextOrRegexValue,
699 #[doc = "The updated oracle text of this card, including reminder text."]
700 full_oracle_text => FullOracle: TextOrRegexValue,
701 #[doc = "Keyword ability that this card has."]
702 keyword => Keyword: TextValue,
703 #[doc = "The mana cost of this card."]
704 mana => Mana: ColorValue,
705 #[doc = "The devotion granted by this permanent. See [`Devotion`]."]
706 devotion => Devotion: DevotionValue,
707 #[doc = "The colors of mana produced by this card."]
708 produces => Produces: ColorValue,
709 #[doc = "The rarity of this printing."]
710 rarity => Rarity: RarityValue,
711 #[doc = "Has the card ever been printed in this rarity?"]
712 in_rarity => InRarity: RarityValue,
713 #[doc = "The set code of this printing."]
714 set => Set: SetValue,
715 #[doc = "Was the card printed in this set?"]
716 in_set => InSet: SetValue,
717 #[doc = "The card's collector number."]
718 collector_number => Number: NumericValue,
719 #[doc = "The block of this card. Works with any set grouped in the same block."]
720 block => Block: SetValue,
721 #[doc = "The type of set this printing is in."]
722 set_type => SetType: SetTypeValue,
723 #[doc = "Has the card appeared in a set of this type?"]
724 in_set_type => InSetType: SetTypeValue,
725 #[doc = "Does the card appear in this cube on MTGO?"]
726 cube => Cube: CubeValue,
727 #[doc(hidden)]
728 format => Format: FormatValue,
729 #[doc = "The card is banned in this format."]
730 banned => Banned: FormatValue,
731 #[doc = "The card is restricted in this format."]
732 restricted => Restricted: FormatValue,
733 #[doc = "Return the printing that is the cheapest in the specified currency."]
734 cheapest => Cheapest: CurrencyValue,
735 #[doc = "The artist who illustrated this card."]
736 artist => Artist: TextValue,
737 #[doc = "The flavor text of this printing."]
738 flavor_text => Flavor: TextOrRegexValue,
739 #[doc = "The type of watermark on this printing."]
740 watermark => Watermark: TextValue,
741 #[doc = "The border color of this printing."]
742 border_color => BorderColor: BorderColorValue,
743 #[doc = "The card frame of this printing, related to the year of the print."]
744 frame => Frame: FrameValue,
745 #[doc = "The date this printing was released."]
746 date => Date: DateValue,
747 #[doc = "This printing is available in the specified game."]
748 game => Game: GameValue,
749 #[doc = "This card is available in the specified game."]
750 in_game => InGame: GameValue,
751 #[doc = "This printing is in the specified language."]
752 language => Language: LanguageValue,
753 #[doc = "Has this card ever been printed in the specified language?"]
754 in_language => InLanguage: LanguageValue,
755 #[doc = "The card's name, using fuzzy search."]
756 name => Name: TextOrRegexValue,
757 }
758
759 macro_rules! numeric_value_fns {
760 ($(
761 $(#[$($attr:meta)*])*
762 $func:ident => $NumProp:ident,
763 )*) => {
764 $(
765 $(#[$($attr)*])*
766 pub fn $func(value: impl NumericComparableValue) -> Query {
767 Query::Param(value.into_param(ValueKind(
768 ValueKindImpl::NumericComparable(NumProperty::$NumProp),
769 )))
770 }
771 )*
772 };
773 }
774
775 numeric_value_fns! {
776 #[doc = "The card's power, if it is a creature or vehicle. '*' and 'X' count as 0."]
777 power => Power,
778 #[doc = "The card's toughness, if it is a creature or vehicle. '*' and 'X' count as 0."]
779 toughness => Toughness,
780 #[doc = "The card's power plus its toughness."]
781 pow_tou => PowTou,
782 #[doc = "The card's loyalty, if it is a planeswalker. 'X' counts as 0."]
783 loyalty => Loyalty,
784 #[doc = "The converted mana cost of this card."]
785 cmc => Cmc,
786 #[doc = "The number of artists credited for this printing."]
787 artist_count => ArtistCount,
788 #[doc = "The current market price of this card in US Dollars."]
789 usd => Usd,
790 #[doc = "The current foil market price of this card in US Dollars."]
791 usd_foil => UsdFoil,
792 #[doc = "The current market price of this card in Euros."]
793 eur => Eur,
794 #[doc = "The current market price of this card in MTGO tickets."]
795 tix => Tix,
796 #[doc = "The number of unique art this card has had."]
797 illustration_count => IllustrationCount,
798 #[doc = "The number of unique prints of this card."]
799 print_count => PrintCount,
800 #[doc = "The number of sets this card has appeared in."]
801 set_count => SetCount,
802 #[doc = "The number of unique prints of this card, counting paper only."]
803 paper_print_count => PaperPrintCount,
804 #[doc = "The number of sets this card has appeared in, counting paper only."]
805 paper_set_count => PaperSetCount,
806 #[doc = "The year this card was released."]
807 year => Year,
808 }
809}