mod value;
mod func;
use std::{ops::Deref, sync::Arc};
use bytes::Bytes;
use js_sys::Uint8Array;
use rustc_hash::FxHashSet;
use wasm_bindgen::prelude::*;
use crate::{js::{func::StofFunc, value::to_stof_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 {
graph: Graph,
}
impl From<Graph> for Stof {
fn from(graph: Graph) -> Self {
Self { graph }
}
}
#[wasm_bindgen]
impl Stof {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
Self { graph: Graph::default() }
}
pub fn get(&mut 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();
if let Some(main) = self.graph.main_root() {
proc_env.self_stack.push(main);
}
match to_stof_value(start, &self) {
Val::Obj(start) => {
proc_env.self_stack.push(start);
},
_ => {}
}
let _ = instruction.exec(&mut proc_env, &mut self.graph); if let Some(var) = proc_env.stack.pop() {
JsValue::from(var.val.read().clone())
} else {
JsValue::NULL
}
}
pub fn set(&mut self, path: &str, value: JsValue, start: JsValue) -> bool {
let mut proc_env = ProcEnv::default();
if let Some(main) = self.graph.main_root() {
proc_env.self_stack.push(main);
}
match to_stof_value(start, &self) {
Val::Obj(start) => {
proc_env.self_stack.push(start);
},
_ => {}
}
proc_env.stack.push(Variable::val(to_stof_value(value, &self)));
let instruction: Arc<dyn Instruction> = Arc::new(Base::SetVariable(path.into()));
match instruction.exec(&mut proc_env, &mut self.graph) {
Ok(_res) => true,
Err(_err) => false
}
}
pub async fn run(&mut self, attributes: JsValue) -> Result<String, String> {
let mut attrs = FxHashSet::default();
match to_stof_value(attributes, &self) {
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(&mut self.graph, None, &Some(attrs), true).await
}
pub fn sync_run(&mut self, attributes: JsValue) -> Result<String, String> {
let mut attrs = FxHashSet::default();
match to_stof_value(attributes, &self) {
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 self.graph, None, &Some(attrs), true)
}
pub async fn call(&mut self, path: &str, args: JsValue) -> Result<JsValue, String> {
let mut arguments = vec![];
match to_stof_value(args, &self) {
Val::List(vals) => {
for val in vals {
arguments.push(val.read().clone());
}
},
Val::Void => { },
val => {
arguments.push(val);
}
}
match Runtime::async_call(&mut self.graph, path, arguments).await {
Ok(res) => Ok(JsValue::from(res)),
Err(err) => Err(err.to_string())
}
}
pub fn sync_call(&mut self, path: &str, args: JsValue) -> Result<JsValue, String> {
let mut arguments = vec![];
match to_stof_value(args, &self) {
Val::List(vals) => {
for val in vals {
arguments.push(val.read().clone());
}
},
Val::Void => { },
val => {
arguments.push(val);
}
}
match Runtime::call(&mut self.graph, path, arguments) {
Ok(res) => Ok(JsValue::from(res)),
Err(err) => Err(err.to_string())
}
}
pub fn js_library_function(&mut self, func: StofFunc) {
self.graph.insert_libfunc(func.get_func());
}
pub fn parse(&mut 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(&mut self, js_obj: JsValue, node: JsValue) -> Result<bool, String> {
if let Ok(value) = serde_wasm_bindgen::from_value::<serde_json::Value>(js_obj) {
let val = to_stof_value(node, &self);
let mut parse_node = self.graph.ensure_main_root();
match val {
Val::Obj(node) => {
if node.node_exists(&self.graph) {
parse_node = node;
} else {
return Ok(false);
}
},
Val::Null |
Val::Void => {},
_ => {
return Ok(false);
}
}
parse_json_object_value(&mut self.graph, &parse_node, value);
return Ok(true);
}
Err(format!("failed to import js object"))
}
#[wasm_bindgen(js_name = stringImport)]
pub fn string_import(&mut self, src: &str, format: &str, node: JsValue, profile: &str) -> Result<bool, String> {
let val = to_stof_value(node, &self);
let mut parse_node = self.graph.ensure_main_root();
match val {
Val::Obj(node) => {
if node.node_exists(&self.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 self.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(&mut self, bytes: JsValue, format: &str, node: JsValue, profile: &str) -> Result<bool, String> {
let val = to_stof_value(node, &self);
let mut parse_node = self.graph.ensure_main_root();
match val {
Val::Obj(node) => {
if node.node_exists(&self.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 self.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 val = to_stof_value(node, &self);
let exp_node;
match val {
Val::Obj(node) => {
if node.node_exists(&self.graph) {
exp_node = node;
} else {
return Err(format!("export node not found"));
}
},
Val::Null |
Val::Void => {
if let Some(root) = self.graph.main_root() {
exp_node = root;
} else {
return Err(format!("export node not found"));
}
},
_ => {
return Err(format!("export node not found"));
}
}
match self.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 val = to_stof_value(node, &self);
let exp_node;
match val {
Val::Obj(node) => {
if node.node_exists(&self.graph) {
exp_node = node;
} else {
return Err(format!("export node not found"));
}
},
Val::Null |
Val::Void => {
if let Some(root) = self.graph.main_root() {
exp_node = root;
} else {
return Err(format!("export node not found"));
}
},
_ => {
return Err(format!("export node not found"));
}
}
match self.graph.binary_export(format, Some(exp_node)) {
Ok(bytes) => Ok(JsValue::from(Uint8Array::from(bytes.as_ref()))),
Err(err) => Err(err.to_string())
}
}
}