267 lines
9.1 KiB
Rust
267 lines
9.1 KiB
Rust
use deunicode::deunicode;
|
|
use rusqlite;
|
|
use std::fmt;
|
|
use std::fs;
|
|
use std::path::PathBuf;
|
|
|
|
use super::deser::ScryfallCard;
|
|
use super::utils::get_local_cache_folder;
|
|
use super::utils::{create_cache_folder, get_local_data_folder, SQLITE_FILENAME};
|
|
|
|
fn get_local_data_sqlite_file() -> PathBuf {
|
|
let mut folder = get_local_data_folder();
|
|
folder.push(SQLITE_FILENAME);
|
|
folder
|
|
}
|
|
|
|
pub fn get_all_card_names() -> Vec<String> {
|
|
let sqlite_file = get_local_data_sqlite_file();
|
|
let conn = rusqlite::Connection::open(sqlite_file).unwrap();
|
|
let mut stmt = conn.prepare("SELECT name FROM cards;").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());
|
|
}
|
|
card_names
|
|
}
|
|
|
|
pub fn get_all_lowercase_card_names() -> Vec<String> {
|
|
let sqlite_file = get_local_data_sqlite_file();
|
|
let conn = rusqlite::Connection::open(sqlite_file).unwrap();
|
|
let mut stmt = conn.prepare("SELECT lowercase_name FROM cards;").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());
|
|
}
|
|
card_names
|
|
}
|
|
|
|
pub fn get_all_mtg_words() -> Vec<String> {
|
|
let sqlite_file = get_local_data_sqlite_file();
|
|
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());
|
|
}
|
|
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 {
|
|
Some(mc) => writeln!(f, "{}\t{}", self.name, mc)?,
|
|
None => writeln!(f, "{}", self.name)?,
|
|
}
|
|
writeln!(f, "{}", self.type_line)?;
|
|
match &self.oracle_text {
|
|
Some(ot) => writeln!(f, "{}", ot)?,
|
|
None => (),
|
|
}
|
|
match &self.power_toughness {
|
|
Some(pt) => writeln!(f, "{}", pt)?,
|
|
None => (),
|
|
}
|
|
match &self.loyalty {
|
|
Some(l) => writeln!(f, "Starting Loyalty: {}", l)?,
|
|
None => (),
|
|
}
|
|
writeln!(f, "Scryfall URI: {}", self.scryfall_uri)?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct DbCard {
|
|
pub name: String,
|
|
pub lowercase_name: String,
|
|
pub type_line: String,
|
|
pub oracle_text: Option<String>,
|
|
pub power_toughness: Option<String>,
|
|
pub loyalty: Option<String>,
|
|
pub mana_cost: Option<String>,
|
|
pub scryfall_uri: String,
|
|
}
|
|
|
|
pub enum GetNameType {
|
|
Name,
|
|
LowercaseName,
|
|
}
|
|
|
|
pub fn get_card_by_name(name: &str, name_type: GetNameType) -> Option<DbCard> {
|
|
let sqlite_file = get_local_data_sqlite_file();
|
|
let conn = rusqlite::Connection::open(sqlite_file).unwrap();
|
|
let sql = match name_type {
|
|
GetNameType::Name => {
|
|
"SELECT name, lowercase_name, type_line, oracle_text, power_toughness, loyalty, mana_cost, scryfall_uri
|
|
FROM cards WHERE name = (?1)"
|
|
}
|
|
GetNameType::LowercaseName => {
|
|
"SELECT name, lowercase_name, type_line, oracle_text, power_toughness, loyalty, mana_cost, scryfall_uri
|
|
FROM cards WHERE lowercase_name = (?1)"
|
|
}
|
|
};
|
|
let mut stmt = conn.prepare(sql).unwrap();
|
|
let mut rows = stmt.query([name]).unwrap();
|
|
match rows.next().unwrap() {
|
|
Some(row) => Some(DbCard {
|
|
name: row.get(0).unwrap(),
|
|
lowercase_name: row.get(1).unwrap(),
|
|
type_line: row.get(2).unwrap(),
|
|
oracle_text: row.get(3).unwrap(),
|
|
power_toughness: row.get(4).unwrap(),
|
|
loyalty: row.get(5).unwrap(),
|
|
mana_cost: row.get(6).unwrap(),
|
|
scryfall_uri: row.get(7).unwrap(),
|
|
}),
|
|
None => None,
|
|
}
|
|
}
|
|
|
|
pub fn find_matching_cards_scryfall_style(search_strings: &Vec<String>) -> Vec<DbCard> {
|
|
assert!(!search_strings.is_empty());
|
|
let sqlite_file = get_local_data_sqlite_file();
|
|
let conn = rusqlite::Connection::open(sqlite_file).unwrap();
|
|
let mut percentaged_string = Vec::new();
|
|
// I know that .clone fixes my problem - I'm not sure why I need to though
|
|
for mut search_string in search_strings.clone() {
|
|
search_string.push('%');
|
|
search_string.insert(0, '%');
|
|
percentaged_string.push(search_string);
|
|
}
|
|
let mut sql: String = "SELECT name, lowercase_name, type_line, oracle_text, power_toughness, loyalty, mana_cost, scryfall_uri
|
|
FROM cards WHERE".into();
|
|
for i in 0..search_strings.len() {
|
|
sql.push_str(&format!(" lowercase_name LIKE (?{}) AND", i + 1));
|
|
}
|
|
sql.pop();
|
|
sql.pop();
|
|
sql.pop();
|
|
sql.pop();
|
|
let mut stmt = conn.prepare(&sql).unwrap();
|
|
stmt.query_map(rusqlite::params_from_iter(percentaged_string), |row| {
|
|
Ok(DbCard {
|
|
name: row.get(0).unwrap(),
|
|
lowercase_name: row.get(1).unwrap(),
|
|
type_line: row.get(2).unwrap(),
|
|
oracle_text: row.get(3).unwrap(),
|
|
power_toughness: row.get(4).unwrap(),
|
|
loyalty: row.get(5).unwrap(),
|
|
mana_cost: row.get(6).unwrap(),
|
|
scryfall_uri: row.get(7).unwrap(),
|
|
})
|
|
})
|
|
.unwrap()
|
|
.filter_map(|res| res.ok())
|
|
.collect()
|
|
}
|
|
|
|
pub fn find_matching_cards(name: &str) -> Vec<DbCard> {
|
|
let sqlite_file = get_local_data_sqlite_file();
|
|
let conn = rusqlite::Connection::open(sqlite_file).unwrap();
|
|
// There must be something better than this - although I don't think it's possible with a str
|
|
let mut name = name.to_string();
|
|
name.push('%');
|
|
name.insert(0, '%');
|
|
let mut stmt = conn
|
|
.prepare(
|
|
"SELECT name, lowercase_name, type_line, oracle_text, power_toughness, loyalty, mana_cost, scryfall_uri
|
|
FROM cards WHERE lowercase_name LIKE (?1)",
|
|
)
|
|
.unwrap();
|
|
stmt.query_map([name], |row| {
|
|
Ok(DbCard {
|
|
name: row.get(0).unwrap(),
|
|
lowercase_name: row.get(1).unwrap(),
|
|
type_line: row.get(2).unwrap(),
|
|
oracle_text: row.get(3).unwrap(),
|
|
power_toughness: row.get(4).unwrap(),
|
|
loyalty: row.get(5).unwrap(),
|
|
mana_cost: row.get(6).unwrap(),
|
|
scryfall_uri: row.get(7).unwrap(),
|
|
})
|
|
})
|
|
.unwrap()
|
|
.filter_map(|res| res.ok())
|
|
.collect()
|
|
}
|
|
|
|
const CREATE_CARDS_TABLE_SQL: &str = "
|
|
CREATE TABLE cards (
|
|
name TEXT NOT NULL UNIQUE,
|
|
lowercase_name TEXT NOT NULL UNIQUE,
|
|
type_line TEXT,
|
|
oracle_text TEXT,
|
|
power_toughness TEXT,
|
|
loyalty TEXT,
|
|
mana_cost TEXT,
|
|
scryfall_uri TEXT NOT NULL UNIQUE
|
|
)";
|
|
|
|
const CREATE_MAGIC_WORDS_TABLE_SQL: &str = "
|
|
CREATE TABLE mtg_words (
|
|
word TEXT NOT NULL UNIQUE
|
|
)";
|
|
|
|
// Will delete your current db
|
|
pub fn init_db() -> bool {
|
|
create_cache_folder();
|
|
let sqlite_file = get_local_data_sqlite_file();
|
|
println!("sqlite file location: {}", sqlite_file.display());
|
|
// TESTING
|
|
println!("cache folder: {}", get_local_cache_folder().display());
|
|
let _res = fs::remove_file(&sqlite_file);
|
|
// TODO actually check result for whether it was a permissions thing or something
|
|
let connection = rusqlite::Connection::open(sqlite_file).unwrap();
|
|
connection.execute(&CREATE_CARDS_TABLE_SQL, ()).unwrap();
|
|
connection
|
|
.execute(&CREATE_MAGIC_WORDS_TABLE_SQL, ())
|
|
.unwrap();
|
|
true
|
|
}
|
|
|
|
pub fn update_db_with_file(file: PathBuf) -> bool {
|
|
let ac = fs::read_to_string(file).unwrap();
|
|
let ac: Vec<ScryfallCard> = 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 => "<No Oracle Text>".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
|
|
}
|