use std::fs; use std::io::ErrorKind; use std::process::exit; use reqwest; use rusqlite::{Connection, Transaction, ErrorCode}; use rusqlite::Error::SqliteFailure; use rusqlite::params; use serde_json::Value; use serde_json::json; use serde_json; use crate::language::Language; use crate::entry::{WiktionaryEntries, WiktionaryEntry}; use crate::entry::Form; const DB_DIR: &str = "/usr/share/inflectived/"; const CACHE_DIR: &str = "/var/cache/"; /// A database of Wiktionary entries pub struct WordDb { db_path: String } impl WordDb { pub fn new(db_name: &str) -> Self { let mut db_path = String::from(DB_DIR); db_path.push_str(db_name); Self { db_path } } pub fn connect(&self) -> Connection { Connection::open(&self.db_path).unwrap() } pub fn clean_tables(&mut self, lang: &Language) { let mut conn = self.connect(); let transaction = conn.transaction().unwrap(); if let Err(e) = transaction.execute(&format!("DROP TABLE IF EXISTS {0}_words", &lang.code), []) { match e { SqliteFailure(f, _) => match f.code { ErrorCode::ReadOnly => { eprintln!("Could not write to database: Permission denied"); eprintln!("Please run as root"); exit(1); }, _ => panic!("{}", e) }, _ => panic!("{}", e) } } transaction.execute(&format!("DROP TABLE IF EXISTS {0}_types", &lang.code), []).unwrap(); transaction.execute(&format!(" CREATE TABLE {0}_types ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name TINYTEXT UNIQUE NOT NULL )", &lang.code), []).unwrap(); for type_ in &lang.types { transaction.execute(&format!(" INSERT INTO {0}_types ( name ) VALUES ( ? )", &lang.code), [type_]).unwrap(); } transaction.execute(&format!(" CREATE TABLE {0}_words ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, word TINYTEXT NOT NULL, type_id INTEGER NOT NULL, content MEDIUMTEXT NOT NULL, FOREIGN KEY (type_id) REFERENCES {0}_types (id) )", &lang.code), []).unwrap(); transaction.execute(&format!(" CREATE INDEX word_index ON {0}_words (word) ", &lang.code), []).unwrap(); transaction.commit().unwrap(); } pub fn insert_entry(&self, transaction: &Transaction, lang: &Language, entry: &WiktionaryEntry) { transaction.execute(&format!(" INSERT INTO {0}_words ( word, content, type_id ) VALUES ( ?, ?, (SELECT id FROM {0}_types WHERE name = ?) )", &lang.code), params![entry.word, entry.parsed_json.to_string(), entry.type_] ).unwrap(); } pub fn insert_entries(&mut self, lang: &Language, entries: &WiktionaryEntries) { let mut conn = self.connect(); let transaction = conn.transaction().unwrap(); for entry in entries.iter() { self.insert_entry(&transaction, lang, entry); } transaction.commit().unwrap(); } /// Generate missing "form-of" entries pub fn generate_entries(&mut self, lang: &Language, entries: &WiktionaryEntries) { let mut conn = self.connect(); let transaction = conn.transaction().unwrap(); let mut statement = transaction.prepare(&format!( "SELECT {0}_words.content FROM {0}_words JOIN {0}_types ON {0}_types.id = {0}_words.type_id WHERE {0}_words.word = ? AND {0}_types.name = ?", &lang.code) ).unwrap(); for entry in entries.iter() { if let Some(forms) = entry.parsed_json["forms"].as_array() { let mut forms_vec: Vec