rust-workshop => backend-rust

This commit is contained in:
folex
2019-08-16 16:49:07 +03:00
parent b85eea0963
commit 92af937fc7
31 changed files with 30 additions and 324 deletions

View File

@ -0,0 +1,24 @@
[package]
name = "fluid"
version = "0.1.0"
authors = ["Fluence Labs"]
publish = false
description = "Decentralized feed built on Fluence"
edition = "2018"
[lib]
name = "fluid"
path = "src/lib.rs"
crate-type = ["cdylib"]
[profile.release]
debug = false
lto = true
opt-level = "z"
panic = "abort"
[dependencies]
serde = { version = "=1.0.88", features = ["derive"] }
serde_json = { version = "=1.0.38", features = ["raw_value"] }
log = "0.4"
fluence = { version = "0.1.6", features = ["wasm_logger"] }

View File

@ -0,0 +1,63 @@
use log;
use crate::api::AppResult;
use crate::errors::err_msg;
use crate::ffi;
// Execute query on SQLite
pub fn query(query: String) -> AppResult<String> {
log::debug!("executing query: '{}'", query);
unsafe {
// Convert query string to bytes
let query_bytes = query.as_bytes();
// Allocate memory for query in SQLite module
let query_ptr = ffi::allocate(query_bytes.len());
// Store query in SQLite's memory
for (i, byte) in query_bytes.iter().enumerate() {
let ptr = query_ptr + i as i32;
ffi::store(ptr, *byte);
}
// Execute the query, and get pointer to the result
let result_ptr = ffi::invoke(query_ptr, query_bytes.len());
// First 4 bytes at result_ptr location encode result size, read that first
let mut result_size: usize = 0;
for i in 0..3 {
let ptr = result_ptr + i as i32;
let b = ffi::load(ptr) as usize;
result_size = result_size + (b << (8 * i));
}
// Now we know exact size of the query execution result
// Read query execution result byte-by-byte
let mut result_bytes = vec![0; result_size as usize];
for i in 4..(result_size + 4) {
let ptr = result_ptr + i as i32;
let b = ffi::load(ptr);
result_bytes[i as usize - 4] = b;
}
// Deallocate query result
ffi::deallocate(result_ptr, result_size + 4);
// Decode query result to a utf8 string
let result_str = std::str::from_utf8(result_bytes.as_slice());
// Log if there's an error
if result_str.is_err() {
log::error!("unable to decode result from bytes: {:#x?}", result_bytes);
}
// Wrap error with a better message, and return Result
result_str
.map_err(|e| {
err_msg(&format!(
"unable to decode result from bytes {:#x?}: {}",
result_bytes, e
))
})
.map(|s| s.to_string())
}
}

View File

@ -0,0 +1,16 @@
use std::fmt;
#[derive(Debug)]
pub struct Error(String);
impl std::error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
pub fn err_msg(s: &str) -> Box<Error> {
Error(s.to_string()).into()
}

View File

@ -0,0 +1,26 @@
// Description of inter-module communication
//
// Allows fluid module to call methods from sqlite module
#[link(wasm_import_module = "sqlite")]
extern "C" {
// Allocate chunk of SQLite memory, and return a pointer to that memory
#[link_name = "sqlite_allocate"]
pub fn allocate(size: usize) -> i32;
// Deallocate chunk of memory after it's not used anymore
#[link_name = "sqlite_deallocate"]
pub fn deallocate(ptr: i32, size: usize);
// Put 1 byte at ptr location in SQLite memory
#[link_name = "sqlite_store"]
pub fn store(ptr: i32, byte: u8);
// Read 1 byte from ptr location of SQLite memory
#[link_name = "sqlite_load"]
pub fn load(ptr: i32) -> u8;
// Call SQLite's invocation handler with data specified by pointer and size
#[link_name = "sqlite_invoke"]
pub fn invoke(ptr: i32, size: usize) -> i32;
}

View File

@ -0,0 +1,40 @@
use fluence::sdk::*;
use serde_json::value::RawValue;
use api::Request;
use api::Response;
use crate::api::AppResult;
use crate::errors::err_msg;
pub mod database;
pub mod errors;
pub mod ffi;
fn init() {
logger::WasmLogger::init_with_level(log::Level::Info).unwrap();
}
#[invocation_handler(init_fn = init)]
fn run(nickname: String) -> String {
// Create table for messages storage
database::query("CREATE TABLE messages(msg text, username text)".to_string());
// Insert message 'Hello, username!' using `nickname` as author's username
database::query(format!(
r#"INSERT INTO messages VALUES("{}","{}")"#,
"Hello, username!", nickname
));
// Get all messages
let messages = database::query("SELECT * FROM messages".to_string());
log::info!("messages: {}", messages);
// Get all messages as JSON via SQLite's JSON extension
database::query(
"SELECT json_group_array(
json_object('msg', msg, 'username', username)
) AS json_result FROM (SELECT * FROM messages)"
.to_string(),
)
}