238 lines
6.9 KiB
Rust
238 lines
6.9 KiB
Rust
use std::{
|
|
fs::File,
|
|
io::{Error, Read},
|
|
path::Path,
|
|
str::FromStr,
|
|
};
|
|
|
|
use crate::{config::_get_client_id, get_basepath};
|
|
use futures::FutureExt;
|
|
use serde::{Deserialize, Serialize};
|
|
use sqlx::{migrate::MigrateDatabase, sqlite::SqliteConnectOptions, Sqlite, SqlitePool};
|
|
use tauri::{ipc::RuntimeCapability, App, AssetResolver, Manager, Url};
|
|
|
|
struct DBFileEntry {
|
|
id: u64,
|
|
relative_file_path: String,
|
|
file_name: String,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub enum DBError {
|
|
DatabaseLocationError(String),
|
|
DatabaseConnectionError(String),
|
|
DatabaseQueryError(String),
|
|
}
|
|
|
|
fn get_database_path(app_handle: &tauri::AppHandle) -> Result<String, DBError> {
|
|
if let Some(basepath) = get_basepath(app_handle.clone()) {
|
|
let db_path = Path::new(&basepath).join("db.sqlite");
|
|
|
|
let resolved_db_path = match app_handle
|
|
.path()
|
|
.resolve(db_path, tauri::path::BaseDirectory::Home)
|
|
{
|
|
Ok(resolved_knowledgebase_path) => resolved_knowledgebase_path,
|
|
|
|
Err(e) => return Err(DBError::DatabaseLocationError(e.to_string())),
|
|
};
|
|
let unicode_db_path = resolved_db_path.to_string_lossy().as_ref().to_string();
|
|
return Ok(unicode_db_path);
|
|
}
|
|
Err(DBError::DatabaseLocationError(
|
|
"No knowledgebase defined".to_string(),
|
|
))
|
|
}
|
|
#[tauri::command]
|
|
pub async fn initialize_database(app_handle: tauri::AppHandle) -> Result<(), DBError> {
|
|
let unicode_db_path = get_database_path(&app_handle)?;
|
|
if !Sqlite::database_exists(&unicode_db_path)
|
|
.await
|
|
.unwrap_or(false)
|
|
{
|
|
match Sqlite::create_database(&unicode_db_path).await {
|
|
Ok(_) => println!("Create db success"),
|
|
Err(error) => panic!("error: {}", error),
|
|
}
|
|
}
|
|
let db = SqlitePool::connect(&unicode_db_path)
|
|
.await
|
|
.map_err(|e| DBError::DatabaseConnectionError(e.to_string()))?;
|
|
let _ = sqlx::query(
|
|
"CREATE TABLE IF NOT EXISTS files
|
|
(
|
|
id INTEGER PRIMARY KEY NOT NULL,
|
|
relative_path TEXT NOT NULL,
|
|
file_name TEXT NOT NULL
|
|
);",
|
|
)
|
|
.execute(&db)
|
|
.await
|
|
.map_err(|e| DBError::DatabaseQueryError(e.to_string()))?;
|
|
|
|
let _ = sqlx::query(
|
|
"CREATE TABLE IF NOT EXISTS file_diffs
|
|
(
|
|
id INTEGER NOT NULL,
|
|
client_id TEXT NOT NULL,
|
|
diff_text TEXT NOT NULL,
|
|
current_diff BOOLEAN,
|
|
parent_diff_id INTEGER,
|
|
file_id INTEGER,
|
|
FOREIGN KEY (parent_diff_id) REFERENCES file_diffs(id),
|
|
FOREIGN KEY (file_id) REFERENCES files(id),
|
|
PRIMARY KEY (id, client_id)
|
|
)
|
|
;",
|
|
)
|
|
.execute(&db)
|
|
.await
|
|
.unwrap();
|
|
// .map_err(|e| DBError::DatabaseQueryError(e.to_string()))?;
|
|
db.close().await;
|
|
Ok(())
|
|
}
|
|
|
|
async fn get_file_id(
|
|
app_handle: &tauri::AppHandle,
|
|
relative_file_path: &str,
|
|
file_name: &str,
|
|
) -> Result<i32, DBError> {
|
|
let unicode_db_path = get_database_path(&app_handle)?;
|
|
let db = SqlitePool::connect(&unicode_db_path)
|
|
.await
|
|
.map_err(|e| DBError::DatabaseConnectionError(e.to_string()))?;
|
|
|
|
let (id, _, _): (i32, String, String) = sqlx::query_as(&format!(
|
|
"
|
|
SELECT *
|
|
FROM files
|
|
WHERE relative_path IS '{relative_file_path}'
|
|
AND file_name IS '{file_name}'
|
|
"
|
|
))
|
|
.fetch_one(&db)
|
|
.await
|
|
.map_err(|e| DBError::DatabaseQueryError(e.to_string()))?;
|
|
db.close().await;
|
|
Ok(id)
|
|
}
|
|
pub async fn add_file(
|
|
app_handle: &tauri::AppHandle,
|
|
relative_file_path: &str,
|
|
file_name: &str,
|
|
) -> Result<(), DBError> {
|
|
if get_file_id(app_handle, relative_file_path, file_name)
|
|
.await
|
|
.is_ok()
|
|
{
|
|
return Ok(());
|
|
}
|
|
let unicode_db_path = get_database_path(&app_handle)?;
|
|
let db = SqlitePool::connect(&unicode_db_path)
|
|
.await
|
|
.map_err(|e| DBError::DatabaseConnectionError(e.to_string()))?;
|
|
|
|
let id = sqlx::query(&format!(
|
|
"
|
|
INSERT INTO
|
|
files (relative_path, file_name)
|
|
VALUES
|
|
('{relative_file_path}', '{file_name}');"
|
|
))
|
|
.execute(&db)
|
|
.await
|
|
.map_err(|e| DBError::DatabaseQueryError(e.to_string()))?;
|
|
db.close().await;
|
|
Ok(())
|
|
}
|
|
async fn get_last_file_diff(
|
|
app_handle: &tauri::AppHandle,
|
|
relative_file_path: &str,
|
|
file_name: &str,
|
|
) -> Result<i32, DBError> {
|
|
let unicode_db_path = get_database_path(&app_handle)?;
|
|
let db = SqlitePool::connect(&unicode_db_path)
|
|
.await
|
|
.map_err(|e| DBError::DatabaseConnectionError(e.to_string()))?;
|
|
|
|
let (parent_diff_id, _, _, _, _, _): (i32, String, String, bool, i32, i32) =
|
|
sqlx::query_as(&format!(
|
|
"
|
|
SELECT *
|
|
FROM file_diffs
|
|
WHERE relative_path IS '{relative_file_path}'
|
|
AND file_name IS '{file_name}'
|
|
AND working_diff is 1
|
|
"
|
|
))
|
|
.fetch_one(&db)
|
|
.await
|
|
.map_err(|e| DBError::DatabaseQueryError(e.to_string()))?;
|
|
db.close().await;
|
|
Ok(parent_diff_id)
|
|
}
|
|
#[tauri::command]
|
|
pub async fn store_diff(
|
|
app_handle: &tauri::AppHandle,
|
|
relative_file_path: &str,
|
|
file_name: &str,
|
|
diff: String,
|
|
) -> Result<(), DBError> {
|
|
let unicode_db_path = get_database_path(&app_handle)?;
|
|
let client_id = _get_client_id(&app_handle);
|
|
let db = SqlitePool::connect(&unicode_db_path)
|
|
.await
|
|
.map_err(|e| DBError::DatabaseConnectionError(e.to_string()))?;
|
|
|
|
// 1 insert into file_diffs with parent
|
|
// 2 make last diff not currennt diff
|
|
|
|
// id INTEGER NOT NULL,
|
|
// client_id TEXT NOT NULL,
|
|
// diff_text TEXT NOT NULL,
|
|
// current_diff BOOLEAN,
|
|
// parent_diff_id INTEGER,
|
|
// file_id INTEGER,
|
|
// FOREIGN KEY (parent_diff_id) REFERENCES file_diffs(id),
|
|
// FOREIGN KEY (file_id) REFERENCES files(id),
|
|
// PRIMARY KEY (id, client_id)
|
|
let file_id = get_file_id(app_handle, relative_file_path, file_name).await?;
|
|
let parent_diff_id: i32 = sqlx::query_scalar(&format!(
|
|
"
|
|
SELECT id from file_diffs
|
|
WHERE relative_path IS '{relative_file_path}'
|
|
AND file_name IS '{file_name}'
|
|
AND working_diff is 1
|
|
"
|
|
))
|
|
.fetch_one(&db)
|
|
.await
|
|
.unwrap_or(0);
|
|
let _ = sqlx::query(&format!(
|
|
"
|
|
UPDATE file_diffs
|
|
SET current_diff = 0
|
|
WHERE relative_path IS '{relative_file_path}'
|
|
AND file_name IS '{file_name}'
|
|
AND working_diff is 1
|
|
"
|
|
))
|
|
.execute(&db)
|
|
.await
|
|
.map_err(|e| DBError::DatabaseQueryError(e.to_string()));
|
|
let _ = sqlx::query(&format!(
|
|
"
|
|
INSERT INTO
|
|
file_diffs (client_id, diff_text, current_diff, file_id)
|
|
VALUES
|
|
('{client_id}', '{diff}', 1, {parent_diff_id});"
|
|
))
|
|
.execute(&db)
|
|
.await
|
|
.map_err(|e| DBError::DatabaseQueryError(e.to_string()))?;
|
|
|
|
db.close().await;
|
|
|
|
Ok(())
|
|
}
|