135 lines
4.1 KiB
Rust
135 lines
4.1 KiB
Rust
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<String>,
|
|
/// Search for the exact string
|
|
#[arg(short, long)]
|
|
exact: bool,
|
|
/// Text to search for card with
|
|
search_text: Vec<String>,
|
|
}
|
|
|
|
fn exact_search(search_strings: Vec<String>) -> 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>) -> 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;
|
|
}
|
|
}
|