Organize code, begin database implementation
This commit is contained in:
parent
376fba6795
commit
4e9784baf4
7 changed files with 594 additions and 177 deletions
321
Cargo.lock
generated
321
Cargo.lock
generated
|
@ -2,6 +2,26 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
|
@ -20,6 +40,12 @@ version = "1.3.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c70beb79cbb5ce9c4f8e20849978f34225931f665bb49efa6982875a4d5facb3"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
|
@ -59,6 +85,22 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"time",
|
||||
"wasm-bindgen",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clipboard-win"
|
||||
version = "4.5.0"
|
||||
|
@ -70,6 +112,28 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "codespan-reporting"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
|
||||
dependencies = [
|
||||
"termcolor",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.6"
|
||||
|
@ -89,6 +153,63 @@ dependencies = [
|
|||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxx"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cxxbridge-flags",
|
||||
"cxxbridge-macro",
|
||||
"link-cplusplus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxx-build"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"codespan-reporting",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"scratch",
|
||||
"syn 2.0.13",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-flags"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb"
|
||||
|
||||
[[package]]
|
||||
name = "cxxbridge-macro"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.13",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.99.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.6"
|
||||
|
@ -163,6 +284,18 @@ dependencies = [
|
|||
"str-buf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fallible-iterator"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
|
||||
|
||||
[[package]]
|
||||
name = "fallible-streaming-iterator"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||
|
||||
[[package]]
|
||||
name = "fd-lock"
|
||||
version = "3.0.12"
|
||||
|
@ -265,10 +398,28 @@ dependencies = [
|
|||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashlink"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69fe1fcf8b4278d860ad0548329f892a3631fb63f82574df68275f34cdbe0ffa"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.2.6"
|
||||
|
@ -301,6 +452,30 @@ version = "1.8.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"windows",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
|
||||
dependencies = [
|
||||
"cxx",
|
||||
"cxx-build",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.3.0"
|
||||
|
@ -371,6 +546,26 @@ version = "0.2.141"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5"
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "link-cplusplus"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.3.1"
|
||||
|
@ -410,7 +605,7 @@ checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
|
|||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
|
@ -439,11 +634,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.15.0"
|
||||
|
@ -521,6 +735,12 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
|
@ -591,7 +811,7 @@ version = "0.2.16"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -614,13 +834,37 @@ dependencies = [
|
|||
"xmlparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rusqlite"
|
||||
version = "0.29.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "549b9d036d571d42e6e85d1c1425e2ac83491075078ca9a15be021c56b1641f2"
|
||||
dependencies = [
|
||||
"bitflags 2.1.0",
|
||||
"chrono",
|
||||
"fallible-iterator",
|
||||
"fallible-streaming-iterator",
|
||||
"hashlink",
|
||||
"libsqlite3-sys",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.37.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"errno",
|
||||
"io-lifetimes",
|
||||
"libc",
|
||||
|
@ -634,7 +878,7 @@ version = "10.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1e83c32c3f3c33b08496e0d1df9ea8c64d39adb8eb36a1ebb1440c690697aef"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"cfg-if",
|
||||
"clipboard-win",
|
||||
"dirs-next",
|
||||
|
@ -663,6 +907,18 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "scratch"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.160"
|
||||
|
@ -709,7 +965,10 @@ dependencies = [
|
|||
name = "shiritori"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"derive_more",
|
||||
"jisho",
|
||||
"rusqlite",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"simple-websockets",
|
||||
|
@ -799,6 +1058,15 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.40"
|
||||
|
@ -819,6 +1087,17 @@ dependencies = [
|
|||
"syn 2.0.13",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"wasi 0.10.0+wasi-snapshot-preview1",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.6.0"
|
||||
|
@ -957,6 +1236,12 @@ version = "0.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
|
@ -974,6 +1259,12 @@ dependencies = [
|
|||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
|
@ -1050,12 +1341,30 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
|
||||
dependencies = [
|
||||
"windows-targets 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.45.0"
|
||||
|
|
|
@ -6,7 +6,10 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4.24", features = ["serde"] }
|
||||
derive_more = "0.99.17"
|
||||
jisho = "0.1.5"
|
||||
rusqlite = { version = "0.29.0", features = ["chrono", "bundled"] }
|
||||
serde = { version = "1.0.160", features = ["serde_derive"] }
|
||||
serde_json = "1.0.95"
|
||||
simple-websockets = "0.1.5"
|
||||
|
|
52
src/database.rs
Normal file
52
src/database.rs
Normal file
|
@ -0,0 +1,52 @@
|
|||
use crate::Word;
|
||||
|
||||
use derive_more::From;
|
||||
use std::path::PathBuf;
|
||||
use rusqlite::{Connection, Result, params};
|
||||
|
||||
pub struct Database {
|
||||
conn: Connection,
|
||||
}
|
||||
|
||||
#[derive(From, Debug)]
|
||||
pub enum DatabaseCreationError {
|
||||
RusqliteError(rusqlite::Error),
|
||||
IoError(std::io::Error),
|
||||
}
|
||||
|
||||
impl Database {
|
||||
pub fn new(
|
||||
testing: bool,
|
||||
) -> Result<Self, DatabaseCreationError> {
|
||||
let conn = if testing {
|
||||
Connection::open_in_memory()
|
||||
} else {
|
||||
let path = PathBuf::from("shiritori.db");
|
||||
//fs::create_dir_all(path.parent().unwrap())?;
|
||||
Connection::open(path)
|
||||
}?;
|
||||
conn.execute(
|
||||
"CREATE TABLE IF NOT EXISTS word (
|
||||
id INTEGER PRIMARY KEY,
|
||||
word TEXT, reading TEXT,
|
||||
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
)",
|
||||
params![],
|
||||
)?;
|
||||
Ok(Self { conn })
|
||||
}
|
||||
|
||||
pub fn load_words_before(&self, before_id: i64) -> Result<Vec<Word>> {
|
||||
self.conn
|
||||
.prepare("SELECT id, word, reading, timestamp FROM word WHERE id < ? DESC LIMIT 10")?
|
||||
.query_map(params![before_id], |row| {
|
||||
Ok(Word {
|
||||
id: row.get(0)?,
|
||||
word: row.get(1)?,
|
||||
reading: row.get(2)?,
|
||||
timestamp: row.get(3)?,
|
||||
})
|
||||
})?
|
||||
.collect::<Result<Vec<Word>>>()
|
||||
}
|
||||
}
|
20
src/dictionary.rs
Normal file
20
src/dictionary.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
use wana_kana::{ConvertJapanese, IsJapaneseStr};
|
||||
|
||||
pub fn lookup(input: &str) -> Option<&jisho::Entry> {
|
||||
let input = input.trim();
|
||||
for word in jisho::lookup(input) {
|
||||
if
|
||||
// If input has no kanji,
|
||||
// we can just compare the input to the reading verbatim
|
||||
// ensuring both are hiragana
|
||||
(input.is_kana() && input.to_hiragana() == word.reading.to_hiragana()) ||
|
||||
// Otherwise, we have to ensure that the input
|
||||
// is verbosely the same.
|
||||
// However, this will cause problems for some words.
|
||||
// For example, 照り焼き will be accepted but 照焼 won't.
|
||||
(input == word.kanji) {
|
||||
return Some(word);
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
183
src/main.rs
183
src/main.rs
|
@ -1,177 +1,18 @@
|
|||
use simple_websockets::{Event, Responder, Message};
|
||||
use std::collections::HashMap;
|
||||
use serde::Serialize;
|
||||
use wana_kana::{ConvertJapanese, IsJapaneseStr};
|
||||
mod database;
|
||||
pub use database::*;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct MessageResponse {
|
||||
event: String,
|
||||
data: MessageResponseData,
|
||||
}
|
||||
mod dictionary;
|
||||
pub use dictionary::*;
|
||||
|
||||
impl MessageResponse {
|
||||
fn to_message(&self) -> Message {
|
||||
Message::Text(serde_json::to_string(&self).unwrap())
|
||||
}
|
||||
}
|
||||
mod server;
|
||||
pub use server::*;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(untagged)]
|
||||
enum MessageResponseData {
|
||||
Greeting {
|
||||
id: u64,
|
||||
},
|
||||
Word {
|
||||
author: u64,
|
||||
word: String,
|
||||
reading: Option<String>,
|
||||
next_char: char,
|
||||
},
|
||||
PlayerCount {
|
||||
players: u64,
|
||||
},
|
||||
Error {
|
||||
message: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl MessageResponseData {
|
||||
fn get_name(&self) -> String {
|
||||
String::from(match self {
|
||||
Self::Greeting { .. } => "greeting",
|
||||
Self::Word { .. } => "word",
|
||||
Self::PlayerCount { .. } => "playerCount",
|
||||
Self::Error { .. } => "error",
|
||||
})
|
||||
}
|
||||
|
||||
fn to_response(self) -> MessageResponse {
|
||||
MessageResponse {
|
||||
event: self.get_name(),
|
||||
data: self,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_message(self) -> Message {
|
||||
self.to_response().to_message()
|
||||
}
|
||||
}
|
||||
|
||||
fn broadcast_player_count(clients: &mut HashMap<u64, Responder>) {
|
||||
let response = MessageResponseData::PlayerCount { players: clients.len() as u64 }.to_response();
|
||||
for (_client, responder) in clients.iter() {
|
||||
responder.send(response.to_message());
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup(input: &str) -> Option<&jisho::Entry> {
|
||||
let input = input.trim();
|
||||
for word in jisho::lookup(input) {
|
||||
if
|
||||
// If input has no kanji,
|
||||
// we can just compare the input to the reading verbatim
|
||||
// ensuring both are hiragana
|
||||
(input.is_kana() && input.to_hiragana() == word.reading.to_hiragana()) ||
|
||||
// Otherwise, we have to ensure that the input
|
||||
// is verbosely the same.
|
||||
// However, this will cause problems for some words.
|
||||
// For example, 照り焼き will be accepted but 照焼 won't.
|
||||
(input == word.kanji) {
|
||||
return Some(word);
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
mod word;
|
||||
pub use word::*;
|
||||
|
||||
fn main() {
|
||||
let event_hub = simple_websockets::launch(8080)
|
||||
.expect("failed to listen on port 8080");
|
||||
let mut clients: HashMap<u64, Responder> = HashMap::new();
|
||||
let mut next_char: Option<char> = None;
|
||||
let mut last_response: Option<MessageResponse> = None;
|
||||
let mut last_client_id: Option<u64> = None;
|
||||
loop {
|
||||
match event_hub.poll_event() {
|
||||
Event::Connect(client_id, responder) => {
|
||||
println!("A client connected with id #{}", client_id);
|
||||
responder.send(MessageResponseData::Greeting { id: client_id }.to_message());
|
||||
if let Some(ref last_response) = last_response {
|
||||
responder.send(last_response.to_message());
|
||||
}
|
||||
clients.insert(client_id, responder);
|
||||
broadcast_player_count(&mut clients);
|
||||
},
|
||||
Event::Disconnect(client_id) => {
|
||||
println!("Client #{} disconnected.", client_id);
|
||||
clients.remove(&client_id);
|
||||
broadcast_player_count(&mut clients);
|
||||
},
|
||||
Event::Message(client_id, message) => {
|
||||
// Ignore binary messages
|
||||
let message = match message {
|
||||
Message::Text(message) => message,
|
||||
Message::Binary(_) => return,
|
||||
};
|
||||
|
||||
// Debug
|
||||
println!("Received a message from client #{}: {:?}", client_id, message);
|
||||
|
||||
let response = if Some(client_id) == last_client_id {
|
||||
MessageResponseData::Error {
|
||||
message: String::from("It's not your turn!"),
|
||||
}
|
||||
} else {
|
||||
match lookup(&message) {
|
||||
Some(entry) => {
|
||||
if entry.reading.chars().last().unwrap().to_string().to_hiragana() == "ん" {
|
||||
MessageResponseData::Error {
|
||||
message: String::from("Can't end with ん!"),
|
||||
}
|
||||
} else if next_char.is_none() || entry.reading.chars().next().unwrap().to_string().to_hiragana().chars().next().unwrap() == next_char.unwrap() {
|
||||
next_char = {
|
||||
// If final character is lengthener or not kana
|
||||
// Use semifinal
|
||||
let mut final_chars = entry.reading.chars().rev();
|
||||
let final_char = final_chars.next().unwrap();
|
||||
Some(if final_char == 'ー' || !final_char.to_string().is_kana() {
|
||||
final_chars.next().unwrap()
|
||||
} else {
|
||||
final_char
|
||||
}.to_string().to_hiragana().chars().next().unwrap())
|
||||
};
|
||||
MessageResponseData::Word {
|
||||
author: client_id,
|
||||
word: entry.kanji.to_owned(),
|
||||
reading: Some(entry.reading.to_owned()),
|
||||
next_char: next_char.unwrap(),
|
||||
}
|
||||
} else {
|
||||
MessageResponseData::Error {
|
||||
message: String::from("Wrong starting kana!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
None => MessageResponseData::Error {
|
||||
message: String::from("Not in dictionary!"),
|
||||
},
|
||||
}
|
||||
}.to_response();
|
||||
|
||||
match response.data {
|
||||
// Send errors to only this client
|
||||
MessageResponseData::Error { .. } => {
|
||||
clients.get(&client_id).unwrap().send(response.to_message());
|
||||
},
|
||||
// Broadcast everything else to all clients
|
||||
_ => {
|
||||
for (_client, responder) in clients.iter() {
|
||||
responder.send(response.to_message());
|
||||
}
|
||||
last_response = Some(response);
|
||||
last_client_id = Some(client_id);
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
const PORT: u16 = 8080;
|
||||
let mut server = Server::new(PORT)
|
||||
.expect(&format!("Failed to start server at port {PORT}"));
|
||||
server.run();
|
||||
}
|
||||
|
|
181
src/server.rs
Normal file
181
src/server.rs
Normal file
|
@ -0,0 +1,181 @@
|
|||
use crate::dictionary::lookup;
|
||||
|
||||
use simple_websockets::{Event, Responder, Message, EventHub};
|
||||
use std::collections::HashMap;
|
||||
use serde::Serialize;
|
||||
use wana_kana::{ConvertJapanese, IsJapaneseStr};
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct MessageResponse {
|
||||
event: String,
|
||||
data: MessageResponseData,
|
||||
}
|
||||
|
||||
impl MessageResponse {
|
||||
fn to_message(&self) -> Message {
|
||||
Message::Text(serde_json::to_string(&self).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(untagged)]
|
||||
enum MessageResponseData {
|
||||
Greeting {
|
||||
id: u64,
|
||||
},
|
||||
Word {
|
||||
author: u64,
|
||||
word: String,
|
||||
reading: Option<String>,
|
||||
next_char: char,
|
||||
},
|
||||
PlayerCount {
|
||||
players: u64,
|
||||
},
|
||||
Error {
|
||||
message: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl MessageResponseData {
|
||||
fn get_name(&self) -> String {
|
||||
String::from(match self {
|
||||
Self::Greeting { .. } => "greeting",
|
||||
Self::Word { .. } => "word",
|
||||
Self::PlayerCount { .. } => "playerCount",
|
||||
Self::Error { .. } => "error",
|
||||
})
|
||||
}
|
||||
|
||||
fn to_response(self) -> MessageResponse {
|
||||
MessageResponse {
|
||||
event: self.get_name(),
|
||||
data: self,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_message(self) -> Message {
|
||||
self.to_response().to_message()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Server {
|
||||
event_hub: EventHub,
|
||||
clients: HashMap<u64, Responder>,
|
||||
next_char: Option<char>,
|
||||
last_response: Option<MessageResponse>,
|
||||
last_client_id: Option<u64>,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
pub fn new(port: u16) -> Result<Self, simple_websockets::Error> {
|
||||
Ok(Server {
|
||||
event_hub: simple_websockets::launch(port)?,
|
||||
clients: HashMap::new(),
|
||||
next_char: None,
|
||||
last_response: None,
|
||||
last_client_id: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn run(&mut self) {
|
||||
loop {
|
||||
match self.event_hub.poll_event() {
|
||||
Event::Connect(client_id, responder) => self.handle_connection(client_id, responder),
|
||||
Event::Disconnect(client_id) => self.handle_disconnection(client_id),
|
||||
Event::Message(client_id, message) => self.handle_message(client_id, message),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn broadcast_player_count(&self) {
|
||||
let response = MessageResponseData::PlayerCount { players: self.clients.len() as u64 }.to_response();
|
||||
for (_client, responder) in self.clients.iter() {
|
||||
responder.send(response.to_message());
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_connection(&mut self, client_id: u64, responder: Responder) {
|
||||
println!("A client connected with id #{}", client_id);
|
||||
responder.send(MessageResponseData::Greeting { id: client_id }.to_message());
|
||||
if let Some(ref last_response) = self.last_response {
|
||||
responder.send(last_response.to_message());
|
||||
}
|
||||
self.clients.insert(client_id, responder);
|
||||
self.broadcast_player_count();
|
||||
}
|
||||
|
||||
fn handle_disconnection(&mut self, client_id: u64) {
|
||||
println!("Client #{} disconnected.", client_id);
|
||||
self.clients.remove(&client_id);
|
||||
self.broadcast_player_count();
|
||||
}
|
||||
|
||||
fn handle_message(&mut self, client_id: u64, message: Message) {
|
||||
// Ignore binary messages
|
||||
let message = match message {
|
||||
Message::Text(message) => message,
|
||||
Message::Binary(_) => return,
|
||||
};
|
||||
|
||||
// Debug
|
||||
println!("Received a message from client #{}: {:?}", client_id, message);
|
||||
|
||||
let response = if Some(client_id) == self.last_client_id {
|
||||
MessageResponseData::Error {
|
||||
message: String::from("It's not your turn!"),
|
||||
}
|
||||
} else {
|
||||
match lookup(&message) {
|
||||
Some(entry) => {
|
||||
if entry.reading.chars().last().unwrap().to_string().to_hiragana() == "ん" {
|
||||
MessageResponseData::Error {
|
||||
message: String::from("Can't end with ん!"),
|
||||
}
|
||||
} else if self.next_char.is_none() || entry.reading.chars().next().unwrap().to_string().to_hiragana().chars().next().unwrap() == self.next_char.unwrap() {
|
||||
self.next_char = {
|
||||
// If final character is lengthener or not kana
|
||||
// Use semifinal
|
||||
let mut final_chars = entry.reading.chars().rev();
|
||||
let final_char = final_chars.next().unwrap();
|
||||
Some(if final_char == 'ー' || !final_char.to_string().is_kana() {
|
||||
final_chars.next().unwrap()
|
||||
} else {
|
||||
final_char
|
||||
}.to_string().to_hiragana().chars().next().unwrap())
|
||||
};
|
||||
MessageResponseData::Word {
|
||||
author: client_id,
|
||||
word: entry.kanji.to_owned(),
|
||||
reading: Some(entry.reading.to_owned()),
|
||||
next_char: self.next_char.unwrap(),
|
||||
}
|
||||
} else {
|
||||
MessageResponseData::Error {
|
||||
message: String::from("Wrong starting kana!"),
|
||||
}
|
||||
}
|
||||
}
|
||||
None => MessageResponseData::Error {
|
||||
message: String::from("Not in dictionary!"),
|
||||
},
|
||||
}
|
||||
}.to_response();
|
||||
|
||||
match response.data {
|
||||
// Send errors to only this client
|
||||
MessageResponseData::Error { .. } => {
|
||||
self.clients.get(&client_id).unwrap().send(response.to_message());
|
||||
},
|
||||
// Broadcast everything else to all clients
|
||||
_ => {
|
||||
for (_client, responder) in self.clients.iter() {
|
||||
responder.send(response.to_message());
|
||||
}
|
||||
self.last_response = Some(response);
|
||||
self.last_client_id = Some(client_id);
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
}
|
11
src/word.rs
Normal file
11
src/word.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
use chrono::{DateTime, Utc};
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Word {
|
||||
pub id: i64,
|
||||
pub word: String,
|
||||
pub reading: String,
|
||||
pub timestamp: DateTime<Utc>,
|
||||
}
|
Loading…
Add table
Reference in a new issue