fetch
This commit is contained in:
parent
a08636b3ee
commit
779fc69405
11 changed files with 202 additions and 76 deletions
1
.tokeignore
Normal file
1
.tokeignore
Normal file
|
@ -0,0 +1 @@
|
||||||
|
package-lock.json
|
36
package-lock.json
generated
36
package-lock.json
generated
|
@ -12,7 +12,8 @@
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-router": "^6.11.2",
|
"react-router": "^6.11.2",
|
||||||
"react-router-dom": "^6.11.2"
|
"react-router-dom": "^6.11.2",
|
||||||
|
"swr": "^2.1.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tauri-apps/cli": "^1.3.1",
|
"@tauri-apps/cli": "^1.3.1",
|
||||||
|
@ -1321,6 +1322,17 @@
|
||||||
"node": ">=4"
|
"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": {
|
"node_modules/to-fast-properties": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||||
|
@ -1385,6 +1397,14 @@
|
||||||
"browserslist": ">= 4.21.0"
|
"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": {
|
"node_modules/vite": {
|
||||||
"version": "4.3.9",
|
"version": "4.3.9",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz",
|
||||||
|
@ -2310,6 +2330,14 @@
|
||||||
"has-flag": "^3.0.0"
|
"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": {
|
"to-fast-properties": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||||
|
@ -2341,6 +2369,12 @@
|
||||||
"picocolors": "^1.0.0"
|
"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": {
|
"vite": {
|
||||||
"version": "4.3.9",
|
"version": "4.3.9",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz",
|
||||||
|
|
|
@ -14,7 +14,8 @@
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-router": "^6.11.2",
|
"react-router": "^6.11.2",
|
||||||
"react-router-dom": "^6.11.2"
|
"react-router-dom": "^6.11.2",
|
||||||
|
"swr": "^2.1.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tauri-apps/cli": "^1.3.1",
|
"@tauri-apps/cli": "^1.3.1",
|
||||||
|
@ -26,4 +27,4 @@
|
||||||
"typescript": "^4.9.5",
|
"typescript": "^4.9.5",
|
||||||
"vite": "^4.2.1"
|
"vite": "^4.2.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
12
src-tauri/Cargo.lock
generated
12
src-tauri/Cargo.lock
generated
|
@ -657,6 +657,17 @@ dependencies = [
|
||||||
"tokio",
|
"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]]
|
[[package]]
|
||||||
name = "derive_more"
|
name = "derive_more"
|
||||||
version = "0.99.17"
|
version = "0.99.17"
|
||||||
|
@ -1369,6 +1380,7 @@ version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
|
"derivative",
|
||||||
"dirs",
|
"dirs",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|
|
@ -10,8 +10,6 @@ edition = "2021"
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["database-maker"]
|
members = ["database-maker"]
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-build = { version = "1.3", features = [] }
|
tauri-build = { version = "1.3", features = [] }
|
||||||
|
|
||||||
|
@ -24,7 +22,7 @@ anyhow = "1.0.71"
|
||||||
clap = { version = "4.3.2", features = ["derive"] }
|
clap = { version = "4.3.2", features = ["derive"] }
|
||||||
sqlx = { version = "0.6.3", features = ["runtime-tokio-rustls", "sqlite"] }
|
sqlx = { version = "0.6.3", features = ["runtime-tokio-rustls", "sqlite"] }
|
||||||
tokio = { version = "1.28.2", features = ["full"] }
|
tokio = { version = "1.28.2", features = ["full"] }
|
||||||
|
derivative = "2.2.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# this feature is used for production builds or when `devPath` points to the filesystem
|
# this feature is used for production builds or when `devPath` points to the filesystem
|
||||||
|
|
|
@ -3,41 +3,44 @@ use std::path::Path;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
use tokio::{
|
use tokio::{
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{AsyncBufReadExt, BufReader},
|
io::{AsyncBufReadExt, BufReader},
|
||||||
};
|
};
|
||||||
|
|
||||||
const SEPARATOR: char = ':';
|
const SEPARATOR: char = ':';
|
||||||
|
|
||||||
pub async fn process_kradfile(pool: &SqlitePool, path: impl AsRef<Path>) -> Result<()> {
|
pub async fn process_kradfile(
|
||||||
let file = File::open(path.as_ref()).await?;
|
pool: &SqlitePool,
|
||||||
|
path: impl AsRef<Path>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let file = File::open(path.as_ref()).await?;
|
||||||
|
|
||||||
let file_reader = BufReader::new(file);
|
let file_reader = BufReader::new(file);
|
||||||
let mut lines = file_reader.lines();
|
let mut lines = file_reader.lines();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let line = match lines.next_line().await? {
|
let line = match lines.next_line().await? {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => break,
|
None => break,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Skip comments
|
// Skip comments
|
||||||
if line.starts_with('#') {
|
if line.starts_with('#') {
|
||||||
continue;
|
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,14 +3,45 @@ use tauri::State;
|
||||||
|
|
||||||
pub struct KanjiDb(pub SqlitePool);
|
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]
|
#[tauri::command]
|
||||||
pub async fn get_kanji(state: State<'_, KanjiDb>) -> Result<Vec<String>, ()> {
|
pub async fn get_kanji(
|
||||||
let result = sqlx::query("SELECT * FROM KanjiSet LIMIT 5")
|
state: State<'_, KanjiDb>,
|
||||||
.fetch_all(&state.0)
|
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
|
.await
|
||||||
.map_err(|_| ())?;
|
.map_err(|_| ())?;
|
||||||
|
let count = count.try_get(0).map_err(|_| ())?;
|
||||||
|
|
||||||
let result = result.into_iter().map(|row| row.get("Character")).collect();
|
Ok(GetKanjiResult { kanji, count })
|
||||||
|
|
||||||
Ok(result)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,20 @@
|
||||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate derivative;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate serde;
|
||||||
|
|
||||||
mod kanji;
|
mod kanji;
|
||||||
|
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use sqlx::SqlitePool;
|
use sqlx::{
|
||||||
|
sqlite::{SqliteConnectOptions, SqlitePoolOptions},
|
||||||
|
SqlitePool,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::kanji::KanjiDb;
|
use crate::kanji::KanjiDb;
|
||||||
|
|
||||||
|
@ -16,7 +26,11 @@ fn greet(name: &str) -> String {
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
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()
|
tauri::Builder::default()
|
||||||
.manage(KanjiDb(kanji_pool))
|
.manage(KanjiDb(kanji_pool))
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
"bundle": {
|
"bundle": {
|
||||||
"active": true,
|
"active": true,
|
||||||
"targets": "all",
|
"targets": "all",
|
||||||
"identifier": "com.tauri.dev",
|
"identifier": "io.mzhang.houhou",
|
||||||
"icon": [
|
"icon": [
|
||||||
"icons/32x32.png",
|
"icons/32x32.png",
|
||||||
"icons/128x128.png",
|
"icons/128x128.png",
|
||||||
|
@ -43,4 +43,4 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
47
src/App.tsx
47
src/App.tsx
|
@ -1,27 +1,46 @@
|
||||||
import { RouterProvider, createBrowserRouter } from "react-router-dom";
|
import { Link, RouterProvider, createHashRouter } from "react-router-dom";
|
||||||
import styles from "./App.module.scss"
|
import styles from "./App.module.scss";
|
||||||
import KanjiPane from "./panes/KanjiPane";
|
import KanjiPane from "./panes/KanjiPane";
|
||||||
import HomePane from "./panes/HomePane";
|
import HomePane from "./panes/HomePane";
|
||||||
|
import { createBrowserRouter } from "react-router-dom";
|
||||||
|
import { Outlet, Route, createRoutesFromElements } from "react-router";
|
||||||
|
|
||||||
export default function App() {
|
function Layout() {
|
||||||
const router = createBrowserRouter(routes);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ul className={styles.header}>
|
<ul className={styles.header}>
|
||||||
<li><a href="/">Home</a></li>
|
{routes.map((route) => (
|
||||||
<li><a href="/srs">Srs</a></li>
|
<li key={route.path}>
|
||||||
<li><a href="/kanji">Kanji</a></li>
|
<Link to={route.path}>{route.title}</Link>
|
||||||
<li><a href="/vocab">Vocab</a></li>
|
</li>
|
||||||
<li><a href="/settings">Settings</a></li>
|
))}
|
||||||
</ul>
|
</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 = [
|
const routes = [
|
||||||
{ path: "/", element: <HomePane /> },
|
{ path: "/", title: "Home", element: <HomePane /> },
|
||||||
{ path: "/kanji", element: <KanjiPane /> },
|
{ path: "/kanji", title: "Kanji", element: <KanjiPane /> },
|
||||||
];
|
];
|
||||||
|
|
|
@ -1,23 +1,36 @@
|
||||||
import { useState } from "react"
|
import { useState } from "react";
|
||||||
import { Kanji } from "../types/Kanji"
|
import { Kanji } from "../types/Kanji";
|
||||||
import { invoke } from '@tauri-apps/api/tauri'
|
import { invoke } from "@tauri-apps/api/tauri";
|
||||||
import styles from "./KanjiPane.module.scss"
|
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() {
|
export default function KanjiPane() {
|
||||||
const [selectedKanji, setSelectedKanji] = useState(null);
|
const { data, error, isLoading } = useSWR("get_kanji", invoke<GetKanjiResult>);
|
||||||
|
|
||||||
const fetchKanji = async () => {
|
return (
|
||||||
const result = await invoke('get_kanji');
|
<>
|
||||||
setSelectedKanji(result);
|
{JSON.stringify(error)}
|
||||||
};
|
|
||||||
|
|
||||||
return <>
|
<div className={styles.kanjiDisplay}></div>
|
||||||
{JSON.stringify(selectedKanji)}
|
|
||||||
|
|
||||||
<div className={styles.kanjiDisplay}>
|
{data && <KanjiList data={data} />}
|
||||||
|
</>
|
||||||
</div>
|
);
|
||||||
|
}
|
||||||
<button onClick={fetchKanji}>Fetch</button>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue