(feat) sql runner for the frontend invoke

This commit is contained in:
polv committed 2026-03-03 14:02:53 +07:00
1 parent d6f2b145c3
commit cbef2046fd
5 files changed
+83 -8

No files matched your search

+1 -1
View File
@@ -5,7 +5,7 @@ mod util;
pub use db::{YomitanRow, YomitanZipImportProgress, YomitanZipImportResult, ZipSource};
pub use error::CJDicError;
pub use service::AppService;
pub use service::{AppService, SqlParam};
pub use util::Timer;
pub fn add(left: u64, right: u64) -> u64 {
+65 -3
View File
@@ -3,11 +3,11 @@ use std::{
path::Path,
};
use rusqlite::Connection;
use serde::Serialize;
use rusqlite::{Connection, params_from_iter};
use serde::{Deserialize, Serialize};
use crate::{
ZipSource,
Timer, ZipSource,
db::{
Database, YOMITAN_DBFILE, YomitanRow, YomitanWriter, YomitanZipImportProgress,
YomitanZipImportResult,
@@ -21,6 +21,16 @@ pub struct LoadYomitanZipDirResult {
pub to_be_removed_dicts: Vec<String>,
}
#[derive(Deserialize)]
#[serde(untagged)]
pub enum SqlParam {
Null,
Integer(i64),
Real(f64),
Text(String),
Bool(bool),
}
pub struct AppService {
db: Database,
}
@@ -31,6 +41,58 @@ impl AppService {
Ok(Self { db })
}
pub fn execute_sql(
&self,
sql: String,
params: Vec<SqlParam>,
) -> Result<serde_json::Value, CJDicError> {
let _timer = Timer::new(sql.to_string());
let conn = self.db.conn.lock().unwrap();
let converted_params: Vec<_> = params
.into_iter()
.map(|p| match p {
SqlParam::Null => rusqlite::types::Value::Null,
SqlParam::Integer(i) => rusqlite::types::Value::Integer(i),
SqlParam::Real(n) => rusqlite::types::Value::Real(n),
SqlParam::Text(s) => rusqlite::types::Value::Text(s),
SqlParam::Bool(b) => rusqlite::types::Value::Integer(b as i64),
})
.collect();
let mut stmt = conn.prepare(&sql)?;
if stmt.column_count() == 0 {
let affected = stmt.execute(params_from_iter(converted_params))?;
return Ok(serde_json::Value::Number(affected.into()));
}
let column_names: Vec<String> = stmt.column_names().iter().map(|s| s.to_string()).collect();
let rows = stmt.query_map(params_from_iter(converted_params), |row| {
let mut obj = serde_json::Map::new();
for (i, name) in column_names.iter().enumerate() {
let value = match row.get_ref(i)? {
rusqlite::types::ValueRef::Null => serde_json::Value::Null,
rusqlite::types::ValueRef::Integer(i) => serde_json::Value::Number(i.into()),
// Boolean are also rendered as an integer.
rusqlite::types::ValueRef::Real(n) => serde_json::Number::from_f64(n)
.map(serde_json::Value::Number)
.unwrap_or(serde_json::Value::Null),
rusqlite::types::ValueRef::Text(t) => {
serde_json::Value::String(String::from_utf8_lossy(t).into())
}
_ => serde_json::Value::Null,
};
obj.insert(name.clone(), value);
}
return Ok(serde_json::Value::Object(obj));
})?;
let result: Result<Vec<_>, _> = rows.collect();
Ok(serde_json::Value::Array(result?))
}
pub fn search_yomitan(
&self,
q_term: &str,
+2 -2
View File
@@ -5,7 +5,7 @@ import {
useRef,
useState,
} from "react";
import { toHiragana } from "wanakana";
import { toKana } from "wanakana";
import {
Box,
@@ -145,7 +145,7 @@ function SearchPage() {
function onSearchboxChange(q: string) {
if (isAutoKana) {
q = toHiragana(q, { useObsoleteKana: true, IMEMode: true });
q = toKana(q, { useObsoleteKana: true, IMEMode: true });
}
setQ(q);
+10 -1
View File
@@ -1,6 +1,6 @@
use std::{borrow::Cow, path::PathBuf};
use cjdic2_core::{AppService, CJDicError, YomitanRow, ZipSource};
use cjdic2_core::{AppService, CJDicError, SqlParam, YomitanRow, ZipSource};
use tauri::{AppHandle, Emitter, Manager, Runtime, path::BaseDirectory};
use tauri_plugin_fs::FsExt;
@@ -104,3 +104,12 @@ pub async fn search_yomitan(
) -> Result<Vec<YomitanRow>, CJDicError> {
state.search_yomitan(q_term, q_reading, limit, offset)
}
#[tauri::command]
pub async fn execute_sql(
sql: String,
params: Vec<SqlParam>,
state: tauri::State<'_, AppService>,
) -> Result<serde_json::Value, CJDicError> {
state.execute_sql(sql, params)
}
+5 -1
View File
@@ -25,7 +25,11 @@ pub fn run() {
Ok(())
})
.invoke_handler(tauri::generate_handler![init_yomitan, search_yomitan])
.invoke_handler(tauri::generate_handler![
init_yomitan,
search_yomitan,
execute_sql
])
.build(tauri::generate_context!())
.expect("error while running tauri application")
.run(|_app_handle, event| match event {