diff --git a/.github/workflows/linux_dev.yml b/.github/workflows/linux_dev.yml index 36902f3..3e45e57 100644 --- a/.github/workflows/linux_dev.yml +++ b/.github/workflows/linux_dev.yml @@ -32,7 +32,7 @@ jobs: sudo apt-get update sudo apt-get install -y \ git cmake make ccache \ - clang + clang sqlite3 - name: Install Qt uses: jurplel/install-qt-action@v4 diff --git a/.gitignore b/.gitignore index 71a1c84..a59784a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ compile_commands.json build -.cache \ No newline at end of file +.cache +*.db \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index de91c32..0d3d0b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ set(CMAKE_C_STANDARD_REQUIRED 99) set(CMAKE_AUTOMOC ON) find_package(Qt6 REQUIRED COMPONENTS Widgets Gui Core) +find_package(SQLite3 REQUIRED) set(RUST_BACKEND_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/back_end) @@ -35,6 +36,7 @@ target_link_libraries( PRIVATE Qt6::Widgets PRIVATE Qt6::Core PRIVATE Qt6::Gui + PRIVATE SQLite::SQLite3 PRIVATE ${RUST_BACKEND_LIB} ) diff --git a/scripts/linux_deploy.sh b/scripts/linux_deploy.sh index 13c17aa..050b8b7 100755 --- a/scripts/linux_deploy.sh +++ b/scripts/linux_deploy.sh @@ -8,7 +8,7 @@ nproc=$(nproc) install_dependency() { sudo apt-get update sudo apt-get install -y git cmake \ - clang + clang sqlite3 } function check() { diff --git a/src/back_end/Cargo.lock b/src/back_end/Cargo.lock index 7041ee9..36efeb6 100644 --- a/src/back_end/Cargo.lock +++ b/src/back_end/Cargo.lock @@ -8,6 +8,7 @@ version = "1.0.0" dependencies = [ "dirs", "serde", + "sqlite", "toml", "webbrowser", ] @@ -30,6 +31,15 @@ version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +[[package]] +name = "cc" +version = "1.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ee0f8803222ba5a7e2777dd72ca451868909b1ac410621b676adf07280e9b5f" +dependencies = [ + "shlex", +] + [[package]] name = "cesu8" version = "1.1.0" @@ -364,6 +374,12 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + [[package]] name = "potential_utf" version = "0.1.2" @@ -440,12 +456,46 @@ dependencies = [ "serde", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "sqlite" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f66e9c01a11936154f3910dbba732c01f8b591543bc4d6672bddee79fd9c4783" +dependencies = [ + "sqlite3-sys", +] + +[[package]] +name = "sqlite3-src" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5b6d3c860886b0a33e69e421796a5f4a27f23597a182c2450f6d7ace5103120" +dependencies = [ + "cc", + "pkg-config", +] + +[[package]] +name = "sqlite3-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7781d97adc13a1d5081127a9ee29afad8427f3757bd984daf814d8265267039" +dependencies = [ + "sqlite3-src", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" diff --git a/src/back_end/Cargo.toml b/src/back_end/Cargo.toml index db09fdb..7ad568b 100644 --- a/src/back_end/Cargo.toml +++ b/src/back_end/Cargo.toml @@ -12,5 +12,6 @@ panic = "abort" [dependencies] dirs = "6.0.0" serde = { version = "1.0.219", features = ["derive"] } +sqlite = "0.37.0" toml = "0.9.5" webbrowser = "1.0.5" diff --git a/src/back_end/src/lib.rs b/src/back_end/src/lib.rs index 80e9b91..95d6be0 100644 --- a/src/back_end/src/lib.rs +++ b/src/back_end/src/lib.rs @@ -4,6 +4,7 @@ pub mod utils { pub mod browser; pub mod fs_utils; pub mod memory; + pub mod sqlite; } pub mod internal { diff --git a/src/back_end/src/utils/sqlite.rs b/src/back_end/src/utils/sqlite.rs new file mode 100644 index 0000000..884f4eb --- /dev/null +++ b/src/back_end/src/utils/sqlite.rs @@ -0,0 +1,48 @@ +use sqlite::Connection; +use std::ffi::{CStr, c_char}; + +#[repr(C)] +pub enum InitDatabaseStatus { + Ok, + CreateDatabaseFailed, + CStrConvertFailed, + ExecuteSqlFailed, +} + +const INIT_QUERY: &str = r"--sql + CREATE TABLE IF NOT EXISTS clipboard_cache ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + content_hash TEXT NOT NULL UNIQUE, + time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + content TEXT NOT NULL, + type TEXT NOT NULL, + is_pinned BOOLEAN NOT NULL DEFAULT FALSE + ); +"; + +#[unsafe(no_mangle)] +/// # Safety +/// Careful with unsafe context & raw pointers. +pub unsafe extern "C" fn raw_init_clipboard_cache( + file_path: *const c_char, +) -> InitDatabaseStatus { + unsafe { + use InitDatabaseStatus as Status; + + let Ok(file_path_cstr) = CStr::from_ptr(file_path).to_str() else { + return Status::CStrConvertFailed; + }; + + let Ok(sql) = Connection::open(file_path_cstr) else { + return Status::CreateDatabaseFailed; + }; + + let is_err = sql.execute(INIT_QUERY).is_err(); + + if is_err { + return Status::ExecuteSqlFailed; + } + + Status::Ok + } +} diff --git a/tests-ffi/sqlite.cxx b/tests-ffi/sqlite.cxx new file mode 100644 index 0000000..12251f0 --- /dev/null +++ b/tests-ffi/sqlite.cxx @@ -0,0 +1,27 @@ + +#include +#include +using std::uint8_t; + +extern "C" enum class InitDatabaseStatus : uint8_t { + OK, + CREATE_DATABASE_FAILED, + C_STR_CONVERT_FAILED, + EXECUTE_SQL_FAILED +}; + +extern "C" InitDatabaseStatus raw_init_clipboard_cache(const char* path); + +int main() { + using Status = InitDatabaseStatus; + + const char* test_path = "test_init.db"; + auto status = raw_init_clipboard_cache(test_path); + + assert(status != Status::CREATE_DATABASE_FAILED); + assert(status != Status::C_STR_CONVERT_FAILED); + assert(status != Status::EXECUTE_SQL_FAILED); + assert(status == Status::OK); + + return 0; +} \ No newline at end of file diff --git a/tests-ffi/tests.sh b/tests-ffi/tests.sh index 370b347..8ee3357 100755 --- a/tests-ffi/tests.sh +++ b/tests-ffi/tests.sh @@ -32,6 +32,7 @@ function run_ffi_test() { local clang_cxx="clang++" local static_lib_path="../src/back_end/target/debug/libback_end.a" local build_dir="build" + local sqlite_lib="sqlite3" check "$clang_cxx" echo -e "\e[0;32m[+] Build backend:\e[0;37m" @@ -46,7 +47,7 @@ function run_ffi_test() { build_output=$(basename "$file" ".cxx") file_name=$(basename "$file") - $clang_cxx "$file" "$static_lib_path" -o "$build_dir/$build_output" + $clang_cxx "$file" -l"$sqlite_lib" "$static_lib_path" -o "$build_dir/$build_output" echo echo -e -n "\e[0;32m[+] Test for $file_name:\e[0;37m"