forge-foundation 0.3.3

Core Magic types for the manabrew engine: colors, mana, card types, zones, phases
Documentation
use std::collections::HashMap;

use super::card_edition::{CardEdition, EditionEntry};
use super::parser::parse_edition;
use crate::sealed_product::paper_card::PaperCard;
use crate::sealed_product::print_sheet::PrintSheet;
use crate::sealed_product::print_sheet_registry;
use crate::sealed_product::rarity::Rarity;

#[derive(Debug, Clone, Default)]
pub struct EditionsRegistry {
    by_code: HashMap<String, CardEdition>,
    aliases: HashMap<String, String>,
}

impl EditionsRegistry {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn len(&self) -> usize {
        self.by_code.len()
    }

    pub fn is_empty(&self) -> bool {
        self.by_code.is_empty()
    }

    pub fn insert(&mut self, edition: CardEdition) {
        let primary = edition.code.to_ascii_uppercase();
        if let Some(c2) = &edition.code2 {
            self.aliases
                .insert(c2.to_ascii_uppercase(), primary.clone());
        }
        if let Some(sc) = &edition.scryfall_code {
            self.aliases
                .insert(sc.to_ascii_uppercase(), primary.clone());
        }
        self.by_code.insert(primary, edition);
    }

    pub fn ingest_file(&mut self, body: &str) -> String {
        let edition = parse_edition(body);
        let code = edition.code.clone();
        self.insert(edition);
        code
    }

    pub fn get(&self, code: &str) -> Option<&CardEdition> {
        let key = code.to_ascii_uppercase();
        if let Some(direct) = self.by_code.get(&key) {
            return Some(direct);
        }
        self.aliases
            .get(&key)
            .and_then(|primary| self.by_code.get(primary))
    }

    pub fn iter(&self) -> impl Iterator<Item = &CardEdition> {
        self.by_code.values()
    }

    pub fn install_print_sheets<F>(&self, paper_card_for: F)
    where
        F: Fn(&str, &EditionEntry) -> PaperCard,
    {
        let mut sheets: HashMap<String, PrintSheet> = HashMap::new();
        for edition in self.by_code.values() {
            let mut cards_sheet = PrintSheet::new(format!("{} cards", edition.code));
            let mut lands_sheet = PrintSheet::new(format!("{} Lands", edition.code));

            for entry in &edition.cards {
                let pc = paper_card_for(&edition.code, entry);
                if entry.rarity == Rarity::BasicLand {
                    lands_sheet.add(pc);
                } else if entry.rarity != Rarity::Token {
                    cards_sheet.add(pc);
                }
            }

            if !cards_sheet.is_empty() {
                sheets.insert(cards_sheet.name().to_string(), cards_sheet);
            }
            if !lands_sheet.is_empty() {
                sheets.insert(lands_sheet.name().to_string(), lands_sheet);
            }

            for (section, rows) in &edition.custom_sheets {
                let mut sheet = PrintSheet::new(section.clone());
                for row in rows {
                    if let Some((count_str, name)) = row.split_once(' ') {
                        let count: u32 = count_str.trim().parse().unwrap_or(1);
                        let name = name.trim();
                        let entry = EditionEntry {
                            collector_number: String::new(),
                            rarity: Rarity::Unknown,
                            name: name.to_string(),
                            artist: None,
                        };
                        let pc = paper_card_for(&edition.code, &entry);
                        sheet.add_weighted(pc, count);
                    }
                }
                if !sheet.is_empty() {
                    sheets.insert(section.clone(), sheet);
                }
            }
        }
        print_sheet_registry::install(sheets);
    }

    pub fn install_print_sheets_default(&self) {
        self.install_print_sheets(|code, entry| {
            PaperCard::new(&entry.name, code, &entry.collector_number, entry.rarity)
        });
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn ingest_and_lookup_by_code_and_scryfall_alias() {
        let mut reg = EditionsRegistry::new();
        reg.ingest_file(
            "[metadata]
Code=M21
Name=Core Set 2021
ScryfallCode=m21

[cards]
",
        );
        assert!(reg.get("m21").is_some());
        assert!(reg.get("M21").is_some());
        assert!(reg.get("missing").is_none());
    }
}