Skip to content

Commit

Permalink
add code.
Browse files Browse the repository at this point in the history
  • Loading branch information
ming900518 committed Apr 27, 2023
1 parent 1c1ac56 commit 2e53a80
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 0 deletions.
68 changes: 68 additions & 0 deletions .gitignore
@@ -0,0 +1,68 @@
# Created by https://www.toptal.com/developers/gitignore/api/linux,macos,rust
# Edit at https://www.toptal.com/developers/gitignore?templates=linux,macos,rust

### Linux ###
*~

# temporary files which can be created if a process still has a handle open of a deleted file
.fuse_hidden*

# KDE directory preferences
.directory

# Linux trash folder which might appear on any partition or disk
.Trash-*

# .nfs files are created when an open file is removed but is still being accessed
.nfs*

### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon


# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

### macOS Patch ###
# iCloud generated files
*.icloud

### Rust ###
# Generated by Cargo
# will have compiled files and executables
debug/
target/

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock

# These are backup files generated by rustfmt
**/*.rs.bk

# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb

# End of https://www.toptal.com/developers/gitignore/api/linux,macos,rust
25 changes: 25 additions & 0 deletions Cargo.toml
@@ -0,0 +1,25 @@
[package]
name = "rusty-sqlite3"
description = "Experimental project for using SQLx with SQLite3 as an npm module."
repository = "https://github.com/ming900518/rusty-sqlite3"
version = "0.1.0"
authors = ["Ming Chang <mail@mingchang.tw>"]
edition = "2021"

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
wasm-bindgen = "*"
wasm-bindgen-futures = "*"
serde = { version = "*", features = ["derive"]}
serde-wasm-bindgen = "*"
time = { version = "*", features = ["serde-human-readable", "macros", "wasm-bindgen"]}
tokio = { version = "*", features = ["macros", "rt"]}
sqlx = { version = "*", features = ["runtime-tokio-rustls", "time", "sqlite"]}

[profile.release]
lto = true
codegen-units = 1
panic = "abort"
opt-level = "s"
111 changes: 111 additions & 0 deletions src/lib.rs
@@ -0,0 +1,111 @@
// For better looking JS function.
#![allow(non_snake_case)]
use std::fmt::Debug;

use serde::{Deserialize, Serialize};
use serde_wasm_bindgen::{from_value, to_value};
use sqlx::{SqliteConnection, Connection, query, Row, Column};
use wasm_bindgen::{prelude::*, JsValue};

#[derive(Deserialize)]
struct SqliteConnectionSetting {
connection_method: SqliteConnectionMethod
}

#[derive(Deserialize)]
enum SqliteConnectionMethod {
Memory,
File(SqliteFileConnection)
}

#[derive(Deserialize)]
struct SqliteFileConnection {
username: Option<String>,
password: Option<String>,
filepath: String
}

#[wasm_bindgen]
pub async fn connect(connectInfo: JsValue) -> Result<*mut SqliteConnection, JsError> {
let Ok(connection_setting) = deserialize_raw_data::<SqliteConnectionSetting>(connectInfo) else {
return Err(JsError::new("Unable to deserialize connection setting."))
};

let connection = if let SqliteConnectionMethod::File(file_connection) = connection_setting.connection_method {
match (file_connection.username, file_connection.password) {
(Some(username), Some(password)) => {
if let Ok(connect) = SqliteConnection::connect(&format!("sqlite://{}:{}@{}", username, password, file_connection.filepath)).await {
connect
} else {
return Err(JsError::new("Unable to connect with username/password."))
}
},
(None, None) => {
if let Ok(connect) = SqliteConnection::connect(&format!("sqlite://{}", file_connection.filepath)).await {
connect
} else {
return Err(JsError::new("Unable to connect without password."))
}
},
_ => {
return Err(JsError::new("Not enough parameter provided. Parameters required by authentication: username, password."))
}
}
} else if let Ok(connect) = SqliteConnection::connect("sqlite::memory:").await {
connect
} else {
return Err(JsError::new("Unable to connect in memory database."))
};
Ok(Box::into_raw(Box::new(connection)))
}

#[wasm_bindgen(getter_with_clone)]
pub struct QueryResult {
pub result: JsValue,
pub connection: *mut SqliteConnection
}


#[wasm_bindgen]
pub async fn execute(connection: *mut SqliteConnection, sql: &str, parameters: JsValue) -> Result<QueryResult, JsError> {
let mut db = unsafe { Box::from_raw(connection) };
let Ok(parameters) = deserialize_raw_data::<Vec<String>>(parameters) else {
return Err(JsError::new("Unable to deserialize parameters."))
};
let mut query = query(sql);

for i in 0..parameters.len() {
query = query.bind(parameters.get(i).unwrap());
}

match query.fetch_all(&mut *db).await {
Ok(results) => {
let result = results.into_iter().enumerate().map(|(index, row)| {
let column = row.column(index).name().to_owned();
let field = row.get::<String, usize>(index);
(column, field)
}).collect::<Vec<(String, String)>>();
convert_to_js_value(db, result)
},
Err(error) => Err(JsError::new(&format!("Unable to fetch data: {}", error)))
}
}

fn deserialize_raw_data<T: for<'de> Deserialize<'de>>(js_data: JsValue) -> Result<T, JsError> {
match from_value::<T>(js_data) {
Ok(raw_result) => Ok(raw_result),
Err(error) => Err(JsError::new(&format!(
"Unable to deserialize the input. Detail: {}",
error
))),
}
}

fn convert_to_js_value<T: Serialize + Debug>(db: Box<SqliteConnection>, data: T) -> Result<QueryResult, JsError> {
match to_value(&data) {
Ok(output) => {
Ok(QueryResult {result: output, connection: Box::into_raw(db) })
},
Err(error) => Err(JsError::new(&format!("Error occured when serializing the result: {}.\n\nValue (force displayed with Debug trait): \n{:#?}", error, data)))
}
}

0 comments on commit 2e53a80

Please sign in to comment.