use clap::Parser; use scryfall_deser::find_matching_cards_scryfall_style; use scryfall_deser::get_all_mtg_words; use scryfall_deser::get_card_by_name; use scryfall_deser::get_local_cache_folder; use scryfall_deser::init_db; use scryfall_deser::update_db_with_file; use scryfall_deser::GetNameType; use std::process::ExitCode; use std::process::Termination; use textdistance::str::damerau_levenshtein; impl Termination for MtgCardExit { fn report(self) -> ExitCode { match self { MtgCardExit::Success => ExitCode::SUCCESS, MtgCardExit::EmptySearchString => ExitCode::from(101), MtgCardExit::NoExactMatchCard => ExitCode::from(102), MtgCardExit::DidYouMean => ExitCode::from(105), MtgCardExit::ExactCardFound => ExitCode::from(200), MtgCardExit::UpdateSuccess => ExitCode::from(201), } } } enum MtgCardExit { Success, UpdateSuccess, ExactCardFound, EmptySearchString, NoExactMatchCard, DidYouMean, } #[derive(Parser, Debug)] #[command(version, about, long_about = None)] struct Args { /// Update the local db from given Scryfall bulk download #[arg(short, long)] update: Option, /// Search for the exact string #[arg(short, long)] exact: bool, /// Text to search for card with search_text: Vec, } fn exact_search(search_strings: Vec) -> MtgCardExit { let search_string = search_strings.join(" "); let card = get_card_by_name(&search_string, GetNameType::Name); match card { None => { println!("No card found with exact name of {}", search_string); MtgCardExit::NoExactMatchCard } Some(c) => { println!("{}", c); MtgCardExit::ExactCardFound } } } // For use with find_matching_cards fn _combine_search_strings(search_strings: Vec) -> String { let mut search_string = String::new(); for card in search_strings { search_string.push_str(&card.to_lowercase()); search_string.push_str(" "); } search_string.pop(); search_string } fn main() -> MtgCardExit { let args = Args::parse(); if let Some(update) = args.update { dbg!(&update); init_db(); let mut path = get_local_cache_folder(); path.push(update); // FIXME - if you pass a bad file or something, it just deletes the db update_db_with_file(path); return MtgCardExit::UpdateSuccess; } if args.search_text.is_empty() { dbg!("You need to put some card text to search"); return MtgCardExit::EmptySearchString; } if args.exact { let res = exact_search(args.search_text); return res; } let mut matching_cards = find_matching_cards_scryfall_style(&args.search_text); dbg!(&args.search_text); dbg!(&matching_cards); if matching_cards.is_empty() { let mtg_words = get_all_mtg_words(); let mut close_names = Vec::new(); for search_string in args.search_text { for mtg_card_name in &mtg_words { let dist = damerau_levenshtein(&search_string, &mtg_card_name); if dist <= 2 { close_names.push((dist, mtg_card_name)); } } } close_names.sort_by_key(|k| k.0); for (_, card) in close_names { println!("{}", card); } return MtgCardExit::DidYouMean; } else if matching_cards.len() == 1 { // FIXME - theres a bug in here - try searching Nalf let card = get_card_by_name(&matching_cards[0].name, GetNameType::Name).unwrap(); println!("{}", card); // TODO update this to be more meaningful return MtgCardExit::ExactCardFound; } else { matching_cards.sort(); for card in matching_cards { println!( "{}", get_card_by_name(&card.lowercase_name, GetNameType::LowercaseName) .unwrap() .name ); } // TODO update this to be more meaningful return MtgCardExit::Success; } }