From 3e1b89312a983d55e0ab9e9311040ff2615ae7e2 Mon Sep 17 00:00:00 2001 From: Arthur Roberts Date: Wed, 20 Aug 2025 23:23:35 +0100 Subject: [PATCH] String closeness seems alright Might be worth sorting by relevance --- scryfall_deser/src/db.rs | 90 +++++++++++++++++++++----------------- scryfall_deser/src/lib.rs | 3 +- scryfall_deser/src/main.rs | 21 +++++++-- 3 files changed, 69 insertions(+), 45 deletions(-) diff --git a/scryfall_deser/src/db.rs b/scryfall_deser/src/db.rs index 9acb958..5dfdbf6 100644 --- a/scryfall_deser/src/db.rs +++ b/scryfall_deser/src/db.rs @@ -38,49 +38,19 @@ pub fn get_all_lowercase_card_names() -> Vec { card_names } -pub fn update_db_with_file(file: PathBuf) -> bool { - let ac = fs::read_to_string(file).unwrap(); - let ac: Vec = serde_json::from_str(&ac).unwrap(); +pub fn get_all_mtg_words() -> Vec { let sqlite_file = get_local_data_sqlite_file(); - let mut conn = rusqlite::Connection::open(sqlite_file).unwrap(); - let tx = conn.transaction().unwrap(); - for card in ac { - for word in card.name.split_whitespace() { - let word = deunicode(word); - let res = tx.execute( - "INSERT INTO magic_words (word) VALUES (?1) - ON CONFLICT (word) DO NOTHING;", - [word.replace(",", "")], - ); - } - let lowercase_name = deunicode(&card.name.to_lowercase()); - let power_toughness = match card.power { - Some(p) => Some(format!("{}/{}", p, card.toughness.unwrap())), - None => None, - }; - let oracle_text = match card.oracle_text { - Some(ot) => ot, - None => "".to_string(), - }; - let loyalty = match card.loyalty { - Some(loy) => Some(loy), - None => None, - }; - let mana_cost = match card.mana_cost { - Some(mc) => Some(mc), - None => None, - }; - let res = tx.execute( - "INSERT INTO cards (name, lowercase_name, type_line, oracle_text, power_toughness, loyalty, mana_cost, scryfall_uri) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)", - rusqlite::params![card.name, lowercase_name, card.type_line, oracle_text, power_toughness, loyalty, mana_cost, card.scryfall_uri], - ); + let conn = rusqlite::Connection::open(sqlite_file).unwrap(); + let mut stmt = conn.prepare("SELECT word FROM mtg_words;").unwrap(); + let mut rows = stmt.query([]).unwrap(); + let mut card_names = Vec::new(); + while let Some(row) = rows.next().unwrap() { + card_names.push(row.get(0).unwrap()); } - tx.commit(); - true + card_names } // unsure if this should be in this file... - impl fmt::Display for DbCard { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match &self.mana_cost { @@ -172,7 +142,6 @@ pub fn find_matching_cards_scryfall_style(search_strings: &Vec) -> Vec bool { .unwrap(); true } + +pub fn update_db_with_file(file: PathBuf) -> bool { + let ac = fs::read_to_string(file).unwrap(); + let ac: Vec = serde_json::from_str(&ac).unwrap(); + let sqlite_file = get_local_data_sqlite_file(); + let mut conn = rusqlite::Connection::open(sqlite_file).unwrap(); + let tx = conn.transaction().unwrap(); + for card in ac { + for word in card.name.split_whitespace() { + let word = deunicode(&word.to_lowercase()); + let res = tx.execute( + "INSERT INTO mtg_words (word) VALUES (?1) + ON CONFLICT (word) DO NOTHING;", + [word.replace(",", "")], + ); + } + let lowercase_name = deunicode(&card.name.to_lowercase()); + let power_toughness = match card.power { + Some(p) => Some(format!("{}/{}", p, card.toughness.unwrap())), + None => None, + }; + let oracle_text = match card.oracle_text { + Some(ot) => ot, + None => "".to_string(), + }; + let loyalty = match card.loyalty { + Some(loy) => Some(loy), + None => None, + }; + let mana_cost = match card.mana_cost { + Some(mc) => Some(mc), + None => None, + }; + let res = tx.execute( + "INSERT INTO cards (name, lowercase_name, type_line, oracle_text, power_toughness, loyalty, mana_cost, scryfall_uri) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)", + rusqlite::params![card.name, lowercase_name, card.type_line, oracle_text, power_toughness, loyalty, mana_cost, card.scryfall_uri], + ); + } + tx.commit(); + true +} diff --git a/scryfall_deser/src/lib.rs b/scryfall_deser/src/lib.rs index 2ea44e9..12cfe28 100644 --- a/scryfall_deser/src/lib.rs +++ b/scryfall_deser/src/lib.rs @@ -7,7 +7,8 @@ pub use crate::deser::ScryfallCard; mod db; pub use db::{ find_matching_cards, find_matching_cards_scryfall_style, get_all_card_names, - get_all_lowercase_card_names, get_card_by_name, init_db, update_db_with_file, GetNameType, + get_all_lowercase_card_names, get_all_mtg_words, get_card_by_name, init_db, + update_db_with_file, GetNameType, }; mod utils; diff --git a/scryfall_deser/src/main.rs b/scryfall_deser/src/main.rs index 1e6e3be..2ec1bad 100644 --- a/scryfall_deser/src/main.rs +++ b/scryfall_deser/src/main.rs @@ -1,5 +1,6 @@ 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; @@ -15,15 +16,16 @@ impl Termination for MtgCardExit { MtgCardExit::Success => ExitCode::SUCCESS, MtgCardExit::EmptySearchString => ExitCode::from(101), MtgCardExit::NoExactMatchCard => ExitCode::from(102), + MtgCardExit::DidYouMean => ExitCode::from(105), } } } enum MtgCardExit { - // No error... unsure it should be in this enum Success, EmptySearchString, NoExactMatchCard, + DidYouMean, } #[derive(Parser, Debug)] @@ -88,14 +90,26 @@ fn main() -> MtgCardExit { let matching_cards = find_matching_cards_scryfall_style(&args.search_text); if matching_cards.is_empty() { - // Do some distance checking stuff + 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(mtg_card_name); + } + } + } + for card in close_names { + println!("{}", card); + } + return MtgCardExit::DidYouMean; } else if matching_cards.len() == 1 { let card = get_card_by_name(&matching_cards[0].name, GetNameType::LowercaseName).unwrap(); println!("{}", card); return MtgCardExit::Success; } else { for card in matching_cards { - dbg!(&card); println!( "{}", get_card_by_name(&card.lowercase_name, GetNameType::LowercaseName) @@ -105,5 +119,4 @@ fn main() -> MtgCardExit { } return MtgCardExit::Success; } - unreachable!("Don't know how you got here - there's a real bug with this"); }