rust backend WIP

This commit is contained in:
folex
2019-08-14 22:15:38 +03:00
parent b810a0eb6f
commit 496cc60f00
7 changed files with 167 additions and 53 deletions

View File

@ -19,4 +19,6 @@ panic = "abort"
[dependencies] [dependencies]
log = "0.4" log = "0.4"
fluence = { version = "0.1.6", features = ["wasm_logger"] } fluence = { version = "0.1.6", features = ["wasm_logger"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.40"

28
backend-rust/src/api.rs Normal file
View File

@ -0,0 +1,28 @@
use serde::{Deserialize, Serialize};
use std::error::Error;
pub type AppResult<T> = ::std::result::Result<T, Box<dyn Error>>;
#[derive(Deserialize)]
#[serde(tag = "action")]
pub enum Request {
Post { msg: String, handle: String },
Fetch { handle: String },
}
#[derive(Serialize, Debug)]
#[serde(untagged)]
pub enum Response {
Post { count: i32 },
Fetch { posts: String },
Error { error: String },
}
pub fn parse(s: String) -> AppResult<Request> {
serde_json::from_str(s.as_str())
}
pub fn serialize(response: Response) -> String {
serde_json::to_string_pretty(response)
.or_else(format!("Unable to serialize response {:?}", response))
}

View File

@ -0,0 +1,47 @@
use log;
use crate::api::AppResult;
use crate::errors::err_msg;
use crate::ffi;
pub fn query(query: String) -> AppResult<String> {
log::debug!("executing query: '{}'", query);
unsafe {
let bytes = query.as_bytes();
let query_ptr = ffi::allocate(bytes.len());
for (i, byte) in bytes.iter().enumerate() {
let ptr = query_ptr + i as i32;
ffi::store(ptr, *byte);
}
let result_ptr = ffi::invoke(query_ptr, bytes.len());
let mut result_size = 0;
for i in 0u8..4u8 {
let ptr = result_ptr + i as i32;
let b = ffi::load(ptr);
result_size = result_size | (b >> 8 * i)
}
let mut result_bytes = vec![0; result_size as usize];
for i in 4u8..(result_size + 4u8) {
let ptr = result_ptr + i as i32;
let b = ffi::load(ptr);
result_bytes[i as usize - 4] = b;
}
let result_str = std::str::from_utf8(result_bytes.as_slice());
if result_str.is_err() {
log::error!("unable to decode result from bytes: {:#x?}", result_bytes);
}
result_str.map_err(|e| {
err_msg(&format!(
"unable to decode result from bytes {:#x?}: {}",
result_bytes, e
))
})
}
}

View File

@ -0,0 +1,5 @@
use std::error::Error;
pub fn err_msg(s: &str) -> Box<dyn Error> {
Error(s.to_string()).into()
}

13
backend-rust/src/ffi.rs Normal file
View File

@ -0,0 +1,13 @@
#[link(wasm_import_module = "sqlite")]
extern "C" {
#[link_name="sqlite_allocate"]
pub fn allocate(size: usize) -> i32;
#[link_name="sqlite_deallocate"]
pub fn deallocate(ptr: i32, size: usize);
#[link_name="sqlite_invoke"]
pub fn invoke(ptr: i32, size: usize) -> i32;
#[link_name="sqlite_store"]
pub fn store(ptr: i32, byte: u8);
#[link_name="sqlite_load"]
pub fn load(ptr: i32) -> u8;
}

View File

@ -1,64 +1,43 @@
use crate::api::AppResult;
use fluence::sdk::*; use fluence::sdk::*;
use log::info;
pub mod api;
pub mod database;
pub mod errors;
pub mod ffi;
pub mod model;
use api::Request;
use api::Response;
fn init() { fn init() {
logger::WasmLogger::init_with_level(log::Level::Info).unwrap(); logger::WasmLogger::init_with_level(log::Level::Info).unwrap();
model::create_scheme().unwrap();
} }
#[invocation_handler(init_fn = init)] #[invocation_handler(init_fn = init)]
fn run(arg: String) -> String { fn run(arg: String) -> String {
query(arg) let result = api::parse(arg).and_then(|request| match request {
Request::Post { msg, handle } => add_post(msg, handle),
Request::Fetch { handle } => fetch_posts(handle),
});
let result = match result {
Ok(good) => good,
Err(error) => Response::Error { error },
};
api::serialize(result)
} }
fn query(query: String) -> String { fn add_post(msg: String, handle: String) -> AppResult<Response::Post> {
info!("executing query: '{}'", query); model::add_post(msg, handle)?;
let count = model::get_posts_count()?;
unsafe { Response::Post { count }
let bytes = query.as_bytes();
let query_ptr = ffi::allocate(bytes.len());
for (i, byte) in bytes.iter().enumerate() {
let ptr = query_ptr + i as i32;
ffi::store(ptr, *byte);
}
let result_ptr = ffi::invoke(query_ptr, bytes.len());
let mut result_size = 0;
for i in 0u8..4u8 {
let ptr = result_ptr + i as i32;
let b = ffi::load(ptr);
result_size = result_size | (b >> 8*i)
}
let mut result_bytes = vec![0; result_size as usize];
for i in 4u8..result_size {
let ptr = result_ptr + i as i32;
let b = ffi::load(ptr);
result_bytes[i as usize - 4] = b;
}
let result_str = std::str::from_utf8(result_bytes.as_slice());
if result_str.is_err() {
info!("unable to decode result from result_bytes: {:?}", result_bytes);
}
result_str.expect("unable to decode result").to_string()
}
} }
pub mod ffi { fn fetch_posts(handle: String) -> AppResult<Response::Fetch> {
#[link(wasm_import_module = "sqlite")] // TODO: filter posts by handle
extern "C" { let posts = model::get_posts()?;
#[link_name="sqlite_allocate"] Response::Fetch { posts }
pub fn allocate(size: usize) -> i32; }
#[link_name="sqlite_deallocate"]
pub fn deallocate(ptr: i32, size: usize);
#[link_name="sqlite_invoke"]
pub fn invoke(ptr: i32, size: usize) -> i32;
#[link_name="sqlite_store"]
pub fn store(ptr: i32, byte: u8);
#[link_name="sqlite_load"]
pub fn load(ptr: i32) -> u8;
}
}

40
backend-rust/src/model.rs Normal file
View File

@ -0,0 +1,40 @@
use crate::database;
use crate::api::AppResult;
use crate::errors::err_msg;
use std::convert::TryInto;
use std::fmt;
use std::str;
use std::vec::Vec;
pub fn create_scheme() -> AppResult<()> {
database::query("CREATE TABLE messages(msg text, handle text)".to_string())
.map_err(|e| err_msg(&format!("Error creating table messages: {}", e)))
}
pub fn add_post(msg: String, handle: String) -> AppResult<()> {
database::query(format!("""INSERT INTO messages VALUES("{}","{}")""", msg, handle)).map_err(
|e| {
err_msg(&format!(
"Error inserting post {} by {}: {}",
msg, handle, e
))
},
)
}
pub fn get_posts() -> AppResult<String> {
database::query(
"SELECT json_group_array(
json_object('msg', msg, 'handle', handle)
) AS json_result FROM (SELECT * FROM messages)"
.to_string(),
)
.map_err(|e| err_msg(&format!("Error retrieving posts: {}", e)))
}
pub fn get_posts_count() -> AppResult<i32> {
database::query("SELECT COUNT(*) from messages".to_string())?
.try_into()
.map_err(|e| err_msg(&format!("Error retrieving posts count: {}", e)))
}