forge_foundation/edition/
card_edition.rs1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4
5use crate::sealed_product::foil_type::FoilType;
6use crate::sealed_product::rarity::Rarity;
7use crate::sealed_product::sealed_template::SealedTemplate;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
10pub enum EditionType {
11 Core,
12 Expansion,
13 Reprint,
14 Online,
15 Starter,
16 Commander,
17 Planechase,
18 Archenemy,
19 Promo,
20 Token,
21 Other,
22 #[default]
23 Unknown,
24}
25
26impl EditionType {
27 pub fn parse(s: &str) -> Self {
28 match s.trim().to_ascii_lowercase().as_str() {
29 "core" => Self::Core,
30 "expansion" => Self::Expansion,
31 "reprint" | "masters" | "anthology" | "draft_innovation" => Self::Reprint,
32 "online" | "digital" => Self::Online,
33 "starter" => Self::Starter,
34 "commander" => Self::Commander,
35 "planechase" => Self::Planechase,
36 "archenemy" => Self::Archenemy,
37 "promo" => Self::Promo,
38 "token" => Self::Token,
39 _ if s.is_empty() => Self::Unknown,
40 _ => Self::Other,
41 }
42 }
43}
44
45#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
46pub struct EditionEntry {
47 pub collector_number: String,
48 pub rarity: Rarity,
49 pub name: String,
50 pub artist: Option<String>,
51}
52
53#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
54pub struct CardEdition {
55 pub code: String,
56 pub code2: Option<String>,
57 pub scryfall_code: Option<String>,
58 pub name: String,
59 pub date: Option<String>,
60 pub edition_type: EditionType,
61
62 pub foil_type: FoilType,
63 pub foil_chance_in_booster: f64,
64 pub foil_always_in_common_slot: bool,
65 pub additional_sheet_for_foils: Option<String>,
66 pub chance_replace_common_with: f64,
67 pub slot_replace_common_with: Option<String>,
68 pub booster_must_contain: Option<String>,
69 pub booster_replace_slot_from_print_sheet: Option<String>,
70 pub sheet_replace_card_from_sheet: Option<String>,
71 pub sheet_replace_card_from_sheet2: Option<String>,
72
73 pub alias: Option<String>,
74 pub booster_covers: u32,
75 pub booster_box_count: u32,
76 pub fat_pack_count: u32,
77 pub prerelease: Option<String>,
78 pub additional_unlock_set: Option<String>,
79 pub small_set_override: bool,
80
81 pub booster: Option<String>,
82 pub draft_booster: Option<String>,
83 pub extra_boosters: HashMap<String, String>,
84 pub custom_sheets: HashMap<String, Vec<String>>,
85
86 pub cards: Vec<EditionEntry>,
87}
88
89impl CardEdition {
90 pub fn to_sealed_template(&self) -> Option<SealedTemplate> {
91 self.to_sealed_template_named(None)
92 }
93
94 pub fn to_sealed_template_named(&self, variant: Option<&str>) -> Option<SealedTemplate> {
95 let primary = self.booster.as_ref().or(self.draft_booster.as_ref());
96 let line = match variant {
97 Some(v) if !v.is_empty() => self.extra_boosters.get(v).or(primary)?.as_str(),
98 _ => primary?.as_str(),
99 };
100 let slots = crate::sealed_product::sealed_template::parse_slots(line);
101 if slots.is_empty() {
102 return None;
103 }
104 let label = match variant {
105 Some(v) if !v.is_empty() => format!("{} {v}", self.code),
106 _ => self.code.clone(),
107 };
108 let mut tpl = SealedTemplate::new(Some(label), slots);
109 tpl.foil_type = self.foil_type;
110 tpl.foil_chance = self.foil_chance_in_booster;
111 tpl.foil_always_in_common_slot = self.foil_always_in_common_slot;
112 tpl.additional_sheet_for_foils = self.additional_sheet_for_foils.clone();
113 tpl.chance_replace_common_with = self.chance_replace_common_with;
114 tpl.slot_replace_common_with = self.slot_replace_common_with.clone();
115 tpl.booster_must_contain = self.booster_must_contain.clone();
116 tpl.booster_replace_slot_from_print_sheet =
117 self.booster_replace_slot_from_print_sheet.clone();
118 tpl.sheet_replace_card_from_sheet = self.sheet_replace_card_from_sheet.clone();
119 tpl.sheet_replace_card_from_sheet2 = self.sheet_replace_card_from_sheet2.clone();
120 Some(tpl)
121 }
122
123 pub fn variant_names(&self) -> Vec<String> {
124 let mut names: Vec<String> = self.extra_boosters.keys().cloned().collect();
125 names.sort();
126 names
127 }
128}