mirror of
https://github.com/fluencelabs/fluid
synced 2025-06-23 18:11:32 +00:00
rust backend WIP
This commit is contained in:
@ -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
28
backend-rust/src/api.rs
Normal 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))
|
||||||
|
}
|
47
backend-rust/src/database.rs
Normal file
47
backend-rust/src/database.rs
Normal 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
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
5
backend-rust/src/errors.rs
Normal file
5
backend-rust/src/errors.rs
Normal 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
13
backend-rust/src/ffi.rs
Normal 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;
|
||||||
|
}
|
@ -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
40
backend-rust/src/model.rs
Normal 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)))
|
||||||
|
}
|
Reference in New Issue
Block a user