use std::convert::TryFrom;
use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::JsValue;
use crate::document::DocumentViewId;
use crate::operation::{
Operation, OperationEncoded, OperationFields as OperationFieldsNonWasm, OperationId,
OperationValue, PinnedRelation, PinnedRelationList, Relation, RelationList,
};
use crate::schema::SchemaId;
use crate::wasm::error::jserr;
use crate::wasm::serde::{deserialize_from_js, serialize_to_js};
use crate::Validate;
#[wasm_bindgen]
#[derive(Debug, Clone)]
pub struct OperationFields(OperationFieldsNonWasm);
#[wasm_bindgen]
impl OperationFields {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
Self(OperationFieldsNonWasm::new())
}
#[wasm_bindgen]
pub fn add(&mut self, name: &str, value_type: &str, value: JsValue) -> Result<(), JsValue> {
match value_type {
"str" => {
let value_str = jserr!(value.as_string().ok_or("Invalid string value"));
jserr!(self.0.add(name, OperationValue::Text(value_str)));
Ok(())
}
"bool" => {
let value_bool = jserr!(value.as_bool().ok_or("Invalid boolean value"));
jserr!(self.0.add(name, OperationValue::Boolean(value_bool)));
Ok(())
}
"int" => {
let value_str = jserr!(value.as_string().ok_or("Must be passed as a string"));
let value_int: i64 = jserr!(value_str.parse(), "Invalid integer value");
jserr!(self.0.add(name, OperationValue::Integer(value_int)));
Ok(())
}
"float" => {
let value_float = jserr!(value.as_f64().ok_or("Invalid float value"));
jserr!(self.0.add(name, OperationValue::Float(value_float)));
Ok(())
}
"relation" => {
let relation: Relation = jserr!(
deserialize_from_js(value),
"Expected an operation id value for field of type relation"
);
jserr!(relation.validate());
jserr!(self.0.add(name, OperationValue::Relation(relation)));
Ok(())
}
"relation_list" => {
let relations: RelationList = jserr!(
deserialize_from_js(value),
"Expected an array of operation ids for field of type relation list"
);
jserr!(relations.validate());
jserr!(self.0.add(name, OperationValue::RelationList(relations)));
Ok(())
}
"pinned_relation" => {
let relation: PinnedRelation = jserr!(
deserialize_from_js(value),
"Expected an array of operation ids for field of type pinned relation list"
);
jserr!(relation.validate());
jserr!(self.0.add(name, OperationValue::PinnedRelation(relation)));
Ok(())
}
"pinned_relation_list" => {
let relations: PinnedRelationList = jserr!(
deserialize_from_js(value),
"Expected a nested array of operation ids for field of type pinned relation list"
);
jserr!(relations.validate());
jserr!(self
.0
.add(name, OperationValue::PinnedRelationList(relations)));
Ok(())
}
_ => Err(js_sys::Error::new("Unknown value type").into()),
}
}
#[wasm_bindgen]
pub fn remove(&mut self, name: &str) -> Result<(), JsValue> {
jserr!(self.0.remove(name));
Ok(())
}
#[wasm_bindgen]
pub fn get(&mut self, name: &str) -> Result<JsValue, JsValue> {
match self.0.get(name) {
Some(OperationValue::Boolean(value)) => Ok(JsValue::from_bool(value.to_owned())),
Some(OperationValue::Text(value)) => Ok(JsValue::from_str(value)),
Some(OperationValue::Relation(value)) => Ok(jserr!(serialize_to_js(value))),
Some(OperationValue::RelationList(value)) => Ok(jserr!(serialize_to_js(value))),
Some(OperationValue::PinnedRelation(value)) => Ok(jserr!(serialize_to_js(value))),
Some(OperationValue::PinnedRelationList(value)) => Ok(jserr!(serialize_to_js(value))),
Some(OperationValue::Float(value)) => Ok(JsValue::from_f64(value.to_owned())),
Some(OperationValue::Integer(value)) => Ok(JsValue::from(value.to_owned())),
None => Ok(JsValue::NULL),
}
}
#[wasm_bindgen(js_name = length)]
pub fn len(&self) -> usize {
self.0.len()
}
#[wasm_bindgen(js_name = isEmpty)]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[wasm_bindgen(js_name = toString)]
#[allow(clippy::inherent_to_string)]
pub fn to_string(&self) -> String {
format!("{:?}", self)
}
}
impl Default for OperationFields {
fn default() -> Self {
Self::new()
}
}
#[wasm_bindgen(js_name = encodeCreateOperation)]
pub fn encode_create_operation(
schema_id: JsValue,
fields: OperationFields,
) -> Result<String, JsValue> {
let schema: SchemaId = jserr!(
deserialize_from_js(schema_id.clone()),
format!("Invalid schema id: {:?}", schema_id)
);
let operation = jserr!(Operation::new_create(schema, fields.0));
let operation_encoded = jserr!(OperationEncoded::try_from(&operation));
Ok(operation_encoded.as_str().to_owned())
}
#[wasm_bindgen(js_name = encodeUpdateOperation)]
pub fn encode_update_operation(
schema_id: JsValue,
previous_operations: JsValue,
fields: OperationFields,
) -> Result<String, JsValue> {
let schema: SchemaId = jserr!(deserialize_from_js(schema_id), "Invalid schema id");
let prev_op_strings: Vec<String> = jserr!(
deserialize_from_js(previous_operations),
"Can not deserialize array"
);
let prev_op_result: Result<Vec<OperationId>, _> = prev_op_strings
.iter()
.map(|prev_op| prev_op.parse())
.collect();
let previous_ops = jserr!(prev_op_result);
let previous = jserr!(DocumentViewId::new(&previous_ops));
let operation = jserr!(Operation::new_update(schema, previous, fields.0));
let operation_encoded = jserr!(OperationEncoded::try_from(&operation));
Ok(operation_encoded.as_str().to_owned())
}
#[wasm_bindgen(js_name = encodeDeleteOperation)]
pub fn encode_delete_operation(
schema_id: JsValue,
previous_operations: JsValue,
) -> Result<String, JsValue> {
let schema: SchemaId = jserr!(deserialize_from_js(schema_id), "Invalid schema id");
let prev_op_strings: Vec<String> = jserr!(
deserialize_from_js(previous_operations),
"Can not deserialize array"
);
let prev_op_result: Result<Vec<OperationId>, _> = prev_op_strings
.iter()
.map(|prev_op| prev_op.parse())
.collect();
let previous_ops = jserr!(prev_op_result);
let previous = jserr!(DocumentViewId::new(&previous_ops));
let operation = jserr!(Operation::new_delete(schema, previous));
let operation_encoded = jserr!(OperationEncoded::try_from(&operation));
Ok(operation_encoded.as_str().to_owned())
}