mod value;
mod func;
use std::{cell::RefCell, ops::Deref, sync::Arc};
use bytes::Bytes;
use js_sys::Uint8Array;
use nanoid::nanoid;
use rustc_hash::FxHashSet;
use wasm_bindgen::prelude::*;
use crate::{js::{func::StofFunc, value::to_graph_value}, model::{Graph, Profile, import::parse_json_object_value}, runtime::{Runtime, Val, Variable, instruction::Instruction, instructions::Base, proc::ProcEnv}};
#[cfg(target_family = "wasm")]
mod wasm_workaround {
unsafe extern "C" {
pub(super) fn __wasm_call_ctors();
}
}
#[wasm_bindgen(start)]
fn start() {
#[cfg(target_family = "wasm")]
unsafe { wasm_workaround::__wasm_call_ctors() };
console_error_panic_hook::set_once();
}
#[wasm_bindgen]
pub struct Stof {
docid: String,
graph: RefCell<Graph>,
}
impl From<Graph> for Stof {
fn from(graph: Graph) -> Self {
Self {
docid: nanoid!(10),
graph: RefCell::new(graph),
}
}
}
impl Stof {
#[inline]
fn graph_mut(&self) -> std::cell::RefMut<'_, Graph> {
self.graph.borrow_mut()
}
}
#[wasm_bindgen]
impl Stof {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
Self {
docid: nanoid!(10),
graph: RefCell::new(Graph::default()),
}
}
pub fn docid(&self) -> String {
self.docid.clone()
}
pub fn get(&self, path: &str, start: JsValue) -> JsValue {
let instruction: Arc<dyn Instruction> = Arc::new(Base::LoadVariable(path.into(), false, false));
let mut proc_env = ProcEnv::default();
let mut graph = self.graph_mut();
if let Some(main) = graph.main_root() {
proc_env.self_stack.push(main);
}
match to_graph_value(start, &graph) {
Val::Obj(start) => {
proc_env.self_stack.push(start);
},
_ => {}
}
let _ = instruction.exec(&mut proc_env, &mut *graph);
if let Some(var) = proc_env.stack.pop() {
JsValue::from(var.val.read().clone())
} else {
JsValue::NULL
}
}
pub fn set(&self, path: &str, value: JsValue, start: JsValue) -> bool {
let mut proc_env = ProcEnv::default();
let mut graph = self.graph_mut();
if let Some(main) = graph.main_root() {
proc_env.self_stack.push(main);
}
match to_graph_value(start, &graph) {
Val::Obj(start) => {
proc_env.self_stack.push(start);
},
_ => {}
}
proc_env.stack.push(Variable::val(to_graph_value(value, &graph)));
let instruction: Arc<dyn Instruction> = Arc::new(Base::SetVariable(path.into()));
match instruction.exec(&mut proc_env, &mut *graph) {
Ok(_res) => true,
Err(_err) => false
}
}
pub async fn run_with_gate(&self, attributes: JsValue, acquire: &js_sys::Function, release: &js_sys::Function) -> Result<String, String> {
let mut attrs = FxHashSet::default();
{
let graph = self.graph.borrow();
match to_graph_value(attributes, &graph) {
Val::Str(attribute) => {
attrs.insert(attribute.to_string());
},
Val::List(vals) => {
for val in vals {
match val.read().deref() {
Val::Str(att) => { attrs.insert(att.to_string()); },
_ => {}
}
}
},
Val::Set(set) => {
for val in set {
match val.read().deref() {
Val::Str(att) => { attrs.insert(att.to_string()); },
_ => {}
}
}
},
_ => {
attrs.insert("main".into());
}
}
}
Runtime::async_run_attribute_functions_with_gate(&self.graph, None, &Some(attrs), true, acquire, release).await
}
pub fn sync_run(&self, attributes: JsValue) -> Result<String, String> {
let mut attrs = FxHashSet::default();
let mut graph = self.graph_mut();
match to_graph_value(attributes, &graph) {
Val::Str(attribute) => {
attrs.insert(attribute.to_string());
},
Val::List(vals) => {
for val in vals {
match val.read().deref() {
Val::Str(att) => { attrs.insert(att.to_string()); },
_ => {}
}
}
},
Val::Set(set) => {
for val in set {
match val.read().deref() {
Val::Str(att) => { attrs.insert(att.to_string()); },
_ => {}
}
}
},
_ => {
attrs.insert("main".into());
}
}
Runtime::run_attribute_functions(&mut *graph, None, &Some(attrs), true)
}
pub async fn call_with_gate(&self, path: &str, args: JsValue, acquire: &js_sys::Function, release: &js_sys::Function) -> Result<JsValue, String> {
let mut arguments = vec![];
{
let graph = self.graph.borrow();
match to_graph_value(args, &graph) {
Val::List(vals) => {
for val in vals {
arguments.push(val.read().clone());
}
},
Val::Void => { },
val => {
arguments.push(val);
}
}
}
match Runtime::async_call_with_gate(&self.graph, path, arguments, acquire, release).await {
Ok(res) => Ok(JsValue::from(res)),
Err(err) => Err(err.to_string())
}
}
pub fn sync_call(&self, path: &str, args: JsValue) -> Result<JsValue, String> {
let mut arguments = vec![];
let mut graph = self.graph_mut();
match to_graph_value(args, &graph) {
Val::List(vals) => {
for val in vals {
arguments.push(val.read().clone());
}
},
Val::Void => { },
val => {
arguments.push(val);
}
}
match Runtime::call(&mut *graph, path, arguments) {
Ok(res) => Ok(JsValue::from(res)),
Err(err) => Err(err.to_string())
}
}
pub fn js_library_function(&self, func: StofFunc) {
let mut graph = self.graph_mut();
graph.insert_libfunc(func.get_func());
}
pub fn parse(&self, stof: &str, node: JsValue, profile: &str) -> Result<bool, String> {
self.string_import(stof, "stof", node, profile)
}
#[wasm_bindgen(js_name = objImport)]
pub fn js_obj_import(&self, js_obj: JsValue, node: JsValue) -> Result<bool, String> {
if let Ok(value) = serde_wasm_bindgen::from_value::<serde_json::Value>(js_obj) {
let mut graph = self.graph_mut();
let val = to_graph_value(node, &graph);
let mut parse_node = graph.ensure_main_root();
match val {
Val::Obj(node) => {
if node.node_exists(&graph) {
parse_node = node;
} else {
return Ok(false);
}
},
Val::Null |
Val::Void => {},
_ => {
return Ok(false);
}
}
parse_json_object_value(&mut *graph, &parse_node, value);
return Ok(true);
}
Err(format!("failed to import js object"))
}
#[wasm_bindgen(js_name = stringImport)]
pub fn string_import(&self, src: &str, format: &str, node: JsValue, profile: &str) -> Result<bool, String> {
let mut graph = self.graph_mut();
let val = to_graph_value(node, &graph);
let mut parse_node = graph.ensure_main_root();
match val {
Val::Obj(node) => {
if node.node_exists(&graph) {
parse_node = node;
} else {
return Ok(false);
}
},
Val::Null |
Val::Void => {},
_ => {
return Ok(false);
}
}
let profile = match profile {
"prod" => Profile::prod(),
"test" => Profile::test(),
"prod_docs" => Profile::docs(false),
"docs" => Profile::docs(true),
_ => Profile::default(),
};
match graph.string_import(format, src, Some(parse_node), &profile) {
Ok(_) => Ok(true),
Err(err) => Err(err.to_string())
}
}
#[wasm_bindgen(js_name = binaryImport)]
pub fn binary_import(&self, bytes: JsValue, format: &str, node: JsValue, profile: &str) -> Result<bool, String> {
let mut graph = self.graph_mut();
let val = to_graph_value(node, &graph);
let mut parse_node = graph.ensure_main_root();
match val {
Val::Obj(node) => {
if node.node_exists(&graph) {
parse_node = node;
} else {
return Ok(false);
}
},
Val::Null |
Val::Void => {},
_ => {
return Ok(false);
}
}
let array = Uint8Array::from(bytes);
let bytes = Bytes::from(array.to_vec());
let profile = match profile {
"prod" => Profile::prod(),
"test" => Profile::test(),
"prod_docs" => Profile::docs(false),
"docs" => Profile::docs(true),
_ => Profile::default(),
};
match graph.binary_import(format, bytes, Some(parse_node), &profile) {
Ok(_) => Ok(true),
Err(err) => Err(err.to_string())
}
}
#[wasm_bindgen(js_name = stringExport)]
pub fn string_export(&self, format: &str, node: JsValue) -> Result<String, String> {
let graph = self.graph.borrow();
let val = to_graph_value(node, &graph);
let exp_node;
match val {
Val::Obj(node) => {
if node.node_exists(&graph) {
exp_node = node;
} else {
return Err(format!("export node not found"));
}
},
Val::Null |
Val::Void => {
if let Some(root) = graph.main_root() {
exp_node = root;
} else {
return Err(format!("export node not found"));
}
},
_ => {
return Err(format!("export node not found"));
}
}
match graph.string_export(format, Some(exp_node)) {
Ok(val) => Ok(val),
Err(err) => Err(err.to_string())
}
}
#[wasm_bindgen(js_name = binaryExport)]
pub fn binary_export(&self, format: &str, node: JsValue) -> Result<JsValue, String> {
let graph = self.graph.borrow();
let val = to_graph_value(node, &graph);
let exp_node;
match val {
Val::Obj(node) => {
if node.node_exists(&graph) {
exp_node = node;
} else {
return Err(format!("export node not found"));
}
},
Val::Null |
Val::Void => {
if let Some(root) = graph.main_root() {
exp_node = root;
} else {
return Err(format!("export node not found"));
}
},
_ => {
return Err(format!("export node not found"));
}
}
match graph.binary_export(format, Some(exp_node)) {
Ok(bytes) => Ok(JsValue::from(Uint8Array::from(bytes.as_ref()))),
Err(err) => Err(err.to_string())
}
}
}