1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
5pub enum ZoneType {
6 Hand,
7 Library,
8 Graveyard,
9 Battlefield,
10 Exile,
11 Flashback,
12 Command,
13 Stack,
14 Sideboard,
15 Ante,
16 Merged,
17 SchemeDeck,
18 PlanarDeck,
19 AttractionDeck,
20 Junkyard,
21 ContraptionDeck,
22 Subgame,
23 ExtraHand,
24 None,
25}
26
27impl ZoneType {
28 pub fn is_hidden(self) -> bool {
30 matches!(
31 self,
32 ZoneType::Hand
33 | ZoneType::Library
34 | ZoneType::Sideboard
35 | ZoneType::SchemeDeck
36 | ZoneType::PlanarDeck
37 | ZoneType::AttractionDeck
38 | ZoneType::ContraptionDeck
39 | ZoneType::Subgame
40 | ZoneType::ExtraHand
41 | ZoneType::None
42 )
43 }
44
45 pub fn is_known(self) -> bool {
46 !self.is_hidden()
47 }
48
49 pub fn is_deck(self) -> bool {
50 matches!(
51 self,
52 ZoneType::Library
53 | ZoneType::SchemeDeck
54 | ZoneType::PlanarDeck
55 | ZoneType::AttractionDeck
56 | ZoneType::ContraptionDeck
57 )
58 }
59
60 pub fn is_part_of_command_zone(self) -> bool {
61 matches!(
62 self,
63 ZoneType::Command
64 | ZoneType::SchemeDeck
65 | ZoneType::PlanarDeck
66 | ZoneType::AttractionDeck
67 | ZoneType::ContraptionDeck
68 | ZoneType::Junkyard
69 )
70 }
71
72 pub fn is_static_ability_source(self) -> bool {
75 matches!(self, ZoneType::Battlefield | ZoneType::Command)
76 }
77
78 pub fn from_str_compat(s: &str) -> Option<Self> {
79 let s = s.trim();
80 if s.eq_ignore_ascii_case("All") {
81 return None;
82 }
83 match s {
84 "Hand" => Some(ZoneType::Hand),
85 "Library" => Some(ZoneType::Library),
86 "Graveyard" => Some(ZoneType::Graveyard),
87 "Battlefield" => Some(ZoneType::Battlefield),
88 "Exile" => Some(ZoneType::Exile),
89 "Flashback" => Some(ZoneType::Flashback),
90 "Command" => Some(ZoneType::Command),
91 "Stack" => Some(ZoneType::Stack),
92 "Sideboard" => Some(ZoneType::Sideboard),
93 "Ante" => Some(ZoneType::Ante),
94 "Merged" => Some(ZoneType::Merged),
95 "SchemeDeck" => Some(ZoneType::SchemeDeck),
96 "PlanarDeck" => Some(ZoneType::PlanarDeck),
97 "AttractionDeck" => Some(ZoneType::AttractionDeck),
98 "Junkyard" => Some(ZoneType::Junkyard),
99 "ContraptionDeck" => Some(ZoneType::ContraptionDeck),
100 "Subgame" => Some(ZoneType::Subgame),
101 "ExtraHand" => Some(ZoneType::ExtraHand),
102 "None" => Some(ZoneType::None),
103 _ => {
104 for zt in Self::ALL.iter() {
106 if format!("{:?}", zt).eq_ignore_ascii_case(s) {
107 return Some(*zt);
108 }
109 }
110 None
111 }
112 }
113 }
114
115 pub const ALL: [ZoneType; 19] = [
116 ZoneType::Hand,
117 ZoneType::Library,
118 ZoneType::Graveyard,
119 ZoneType::Battlefield,
120 ZoneType::Exile,
121 ZoneType::Flashback,
122 ZoneType::Command,
123 ZoneType::Stack,
124 ZoneType::Sideboard,
125 ZoneType::Ante,
126 ZoneType::Merged,
127 ZoneType::SchemeDeck,
128 ZoneType::PlanarDeck,
129 ZoneType::AttractionDeck,
130 ZoneType::Junkyard,
131 ZoneType::ContraptionDeck,
132 ZoneType::Subgame,
133 ZoneType::ExtraHand,
134 ZoneType::None,
135 ];
136}
137
138impl std::fmt::Display for ZoneType {
139 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140 write!(f, "{:?}", self)
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn zone_hidden() {
150 assert!(ZoneType::Hand.is_hidden());
151 assert!(ZoneType::Library.is_hidden());
152 assert!(!ZoneType::Graveyard.is_hidden());
153 assert!(!ZoneType::Battlefield.is_hidden());
154 }
155
156 #[test]
157 fn zone_from_str() {
158 assert_eq!(
159 ZoneType::from_str_compat("Battlefield"),
160 Some(ZoneType::Battlefield)
161 );
162 assert_eq!(ZoneType::from_str_compat("All"), None);
163 }
164}