This commit is contained in:
Michael Zhang 2023-06-11 15:08:15 -05:00
parent a08636b3ee
commit 779fc69405
11 changed files with 202 additions and 76 deletions

1
.tokeignore Normal file
View file

@ -0,0 +1 @@
package-lock.json

36
package-lock.json generated
View file

@ -12,7 +12,8 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router": "^6.11.2",
"react-router-dom": "^6.11.2"
"react-router-dom": "^6.11.2",
"swr": "^2.1.5"
},
"devDependencies": {
"@tauri-apps/cli": "^1.3.1",
@ -1321,6 +1322,17 @@
"node": ">=4"
}
},
"node_modules/swr": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/swr/-/swr-2.1.5.tgz",
"integrity": "sha512-/OhfZMcEpuz77KavXST5q6XE9nrOBOVcBLWjMT+oAE/kQHyE3PASrevXCtQDZ8aamntOfFkbVJp7Il9tNBQWrw==",
"dependencies": {
"use-sync-external-store": "^1.2.0"
},
"peerDependencies": {
"react": "^16.11.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
@ -1385,6 +1397,14 @@
"browserslist": ">= 4.21.0"
}
},
"node_modules/use-sync-external-store": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/vite": {
"version": "4.3.9",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz",
@ -2310,6 +2330,14 @@
"has-flag": "^3.0.0"
}
},
"swr": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/swr/-/swr-2.1.5.tgz",
"integrity": "sha512-/OhfZMcEpuz77KavXST5q6XE9nrOBOVcBLWjMT+oAE/kQHyE3PASrevXCtQDZ8aamntOfFkbVJp7Il9tNBQWrw==",
"requires": {
"use-sync-external-store": "^1.2.0"
}
},
"to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
@ -2341,6 +2369,12 @@
"picocolors": "^1.0.0"
}
},
"use-sync-external-store": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
"integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
"requires": {}
},
"vite": {
"version": "4.3.9",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz",

View file

@ -14,7 +14,8 @@
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router": "^6.11.2",
"react-router-dom": "^6.11.2"
"react-router-dom": "^6.11.2",
"swr": "^2.1.5"
},
"devDependencies": {
"@tauri-apps/cli": "^1.3.1",
@ -26,4 +27,4 @@
"typescript": "^4.9.5",
"vite": "^4.2.1"
}
}
}

12
src-tauri/Cargo.lock generated
View file

@ -657,6 +657,17 @@ dependencies = [
"tokio",
]
[[package]]
name = "derivative"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "derive_more"
version = "0.99.17"
@ -1369,6 +1380,7 @@ version = "0.0.0"
dependencies = [
"anyhow",
"clap",
"derivative",
"dirs",
"serde",
"serde_json",

View file

@ -10,8 +10,6 @@ edition = "2021"
[workspace]
members = ["database-maker"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
tauri-build = { version = "1.3", features = [] }
@ -24,7 +22,7 @@ anyhow = "1.0.71"
clap = { version = "4.3.2", features = ["derive"] }
sqlx = { version = "0.6.3", features = ["runtime-tokio-rustls", "sqlite"] }
tokio = { version = "1.28.2", features = ["full"] }
derivative = "2.2.0"
[features]
# this feature is used for production builds or when `devPath` points to the filesystem

View file

@ -3,41 +3,44 @@ use std::path::Path;
use anyhow::Result;
use sqlx::SqlitePool;
use tokio::{
fs::File,
io::{AsyncBufReadExt, BufReader},
fs::File,
io::{AsyncBufReadExt, BufReader},
};
const SEPARATOR: char = ':';
pub async fn process_kradfile(pool: &SqlitePool, path: impl AsRef<Path>) -> Result<()> {
let file = File::open(path.as_ref()).await?;
pub async fn process_kradfile(
pool: &SqlitePool,
path: impl AsRef<Path>,
) -> Result<()> {
let file = File::open(path.as_ref()).await?;
let file_reader = BufReader::new(file);
let mut lines = file_reader.lines();
let file_reader = BufReader::new(file);
let mut lines = file_reader.lines();
loop {
let line = match lines.next_line().await? {
Some(v) => v,
None => break,
};
loop {
let line = match lines.next_line().await? {
Some(v) => v,
None => break,
};
// Skip comments
if line.starts_with('#') {
continue;
}
let parts = line.split(SEPARATOR).collect::<Vec<_>>();
let (kanji, radicals) = match &parts[..] {
&[kanji, radicals] => {
let kanji = kanji.trim();
let radicals = radicals.trim().split_whitespace().collect::<Vec<_>>();
(kanji, radicals)
}
_ => continue,
};
println!("kanji: {}, radicals: {:?}", kanji, radicals);
// Skip comments
if line.starts_with('#') {
continue;
}
Ok(())
let parts = line.split(SEPARATOR).collect::<Vec<_>>();
let (kanji, radicals) = match &parts[..] {
&[kanji, radicals] => {
let kanji = kanji.trim();
let radicals = radicals.trim().split_whitespace().collect::<Vec<_>>();
(kanji, radicals)
}
_ => continue,
};
println!("kanji: {}, radicals: {:?}", kanji, radicals);
}
Ok(())
}

View file

@ -3,14 +3,45 @@ use tauri::State;
pub struct KanjiDb(pub SqlitePool);
#[derive(Debug, Derivative, Serialize, Deserialize)]
#[derivative(Default)]
pub struct GetKanjiOptions {
#[derivative(Default(value = "10"))]
how_many: u32,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct GetKanjiResult {
count: u32,
kanji: Vec<String>,
}
#[tauri::command]
pub async fn get_kanji(state: State<'_, KanjiDb>) -> Result<Vec<String>, ()> {
let result = sqlx::query("SELECT * FROM KanjiSet LIMIT 5")
.fetch_all(&state.0)
pub async fn get_kanji(
state: State<'_, KanjiDb>,
options: Option<GetKanjiOptions>,
) -> Result<GetKanjiResult, ()> {
let opts = options.unwrap_or_default();
let result = sqlx::query(
r#"SELECT * FROM KanjiSet
LEFT JOIN KanjiMeaningSet ON KanjiSet.ID = KanjiMeaningSet.Kanji_ID
WHERE MostUsedRank IS NOT NULL
ORDER BY MostUsedRank
LIMIT ?"#,
)
.bind(opts.how_many)
.fetch_all(&state.0)
.await
.map_err(|_| ())?;
let kanji = result.into_iter().map(|row| row.get("Character")).collect();
let count = sqlx::query("SELECT COUNT(*) FROM KanjiSet")
.fetch_one(&state.0)
.await
.map_err(|_| ())?;
let count = count.try_get(0).map_err(|_| ())?;
let result = result.into_iter().map(|row| row.get("Character")).collect();
Ok(result)
Ok(GetKanjiResult { kanji, count })
}

View file

@ -1,10 +1,20 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
#[macro_use]
extern crate derivative;
#[macro_use]
extern crate serde;
mod kanji;
use std::str::FromStr;
use anyhow::{Context, Result};
use sqlx::SqlitePool;
use sqlx::{
sqlite::{SqliteConnectOptions, SqlitePoolOptions},
SqlitePool,
};
use crate::kanji::KanjiDb;
@ -16,7 +26,11 @@ fn greet(name: &str) -> String {
#[tokio::main]
async fn main() -> Result<()> {
let kanji_pool = SqlitePool::connect("./KanjiDatabase.sqlite").await?;
let kanji_db_options =
SqliteConnectOptions::from_str("./KanjiDatabase.sqlite")?.read_only(true);
let kanji_pool = SqlitePoolOptions::new()
.connect_with(kanji_db_options)
.await?;
tauri::Builder::default()
.manage(KanjiDb(kanji_pool))

View file

@ -21,7 +21,7 @@
"bundle": {
"active": true,
"targets": "all",
"identifier": "com.tauri.dev",
"identifier": "io.mzhang.houhou",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
@ -43,4 +43,4 @@
}
]
}
}
}

View file

@ -1,27 +1,46 @@
import { RouterProvider, createBrowserRouter } from "react-router-dom";
import styles from "./App.module.scss"
import { Link, RouterProvider, createHashRouter } from "react-router-dom";
import styles from "./App.module.scss";
import KanjiPane from "./panes/KanjiPane";
import HomePane from "./panes/HomePane";
import { createBrowserRouter } from "react-router-dom";
import { Outlet, Route, createRoutesFromElements } from "react-router";
export default function App() {
const router = createBrowserRouter(routes);
function Layout() {
return (
<>
<ul className={styles.header}>
<li><a href="/">Home</a></li>
<li><a href="/srs">Srs</a></li>
<li><a href="/kanji">Kanji</a></li>
<li><a href="/vocab">Vocab</a></li>
<li><a href="/settings">Settings</a></li>
{routes.map((route) => (
<li key={route.path}>
<Link to={route.path}>{route.title}</Link>
</li>
))}
</ul>
<RouterProvider router={router} />
<Outlet />
</>
);
}
export default function App() {
const router = createBrowserRouter(
createRoutesFromElements(
<Route path="/" element={<Layout />}>
{routes.map((route, idx) => (
<Route
key={route.path}
index={idx === 0}
path={route.path}
element={route.element}
/>
))}
</Route>
)
);
return <RouterProvider router={router} />;
}
const routes = [
{ path: "/", element: <HomePane /> },
{ path: "/kanji", element: <KanjiPane /> },
];
{ path: "/", title: "Home", element: <HomePane /> },
{ path: "/kanji", title: "Kanji", element: <KanjiPane /> },
];

View file

@ -1,23 +1,36 @@
import { useState } from "react"
import { Kanji } from "../types/Kanji"
import { invoke } from '@tauri-apps/api/tauri'
import styles from "./KanjiPane.module.scss"
import { useState } from "react";
import { Kanji } from "../types/Kanji";
import { invoke } from "@tauri-apps/api/tauri";
import useSWR from "swr";
import styles from "./KanjiPane.module.scss";
interface GetKanjiResult {
count: number;
kanji: string[];
}
function KanjiList({ data }: { data : GetKanjiResult}) {
return <>
Displaying {data.kanji.length} of {data.count} results.
<ul>
{data.kanji.map(kanji => <li key={kanji}>
{kanji}
</li>)}
</ul>
</>
}
export default function KanjiPane() {
const [selectedKanji, setSelectedKanji] = useState(null);
const { data, error, isLoading } = useSWR("get_kanji", invoke<GetKanjiResult>);
const fetchKanji = async () => {
const result = await invoke('get_kanji');
setSelectedKanji(result);
};
return (
<>
{JSON.stringify(error)}
return <>
{JSON.stringify(selectedKanji)}
<div className={styles.kanjiDisplay}></div>
<div className={styles.kanjiDisplay}>
</div>
<button onClick={fetchKanji}>Fetch</button>
</>
}
{data && <KanjiList data={data} />}
</>
);
}