use js_sys::{Array, Object, Reflect};
use serde::{Deserialize, Serialize};
use serde_wasm_bindgen::{from_value, to_value};
use std::collections::HashMap;
use wasm_bindgen::prelude::*;
pub type NodeId = String;
pub type EdgeId = String;
pub type HyperedgeId = String;
#[wasm_bindgen]
#[derive(Clone)]
pub struct JsNode {
pub(crate) id: NodeId,
pub(crate) labels: Vec<String>,
pub(crate) properties: HashMap<String, serde_json::Value>,
pub(crate) embedding: Option<Vec<f32>>,
}
#[wasm_bindgen]
impl JsNode {
#[wasm_bindgen(getter)]
pub fn id(&self) -> String {
self.id.clone()
}
#[wasm_bindgen(getter)]
pub fn labels(&self) -> Vec<String> {
self.labels.clone()
}
#[wasm_bindgen(getter)]
pub fn properties(&self) -> JsValue {
to_value(&self.properties).unwrap_or(JsValue::NULL)
}
#[wasm_bindgen(getter)]
pub fn embedding(&self) -> Option<Vec<f32>> {
self.embedding.clone()
}
#[wasm_bindgen(js_name = getProperty)]
pub fn get_property(&self, key: &str) -> JsValue {
self.properties
.get(key)
.map(|v| to_value(v).unwrap())
.unwrap_or(JsValue::NULL)
}
#[wasm_bindgen(js_name = hasLabel)]
pub fn has_label(&self, label: &str) -> bool {
self.labels.contains(&label.to_string())
}
}
#[wasm_bindgen]
#[derive(Clone)]
pub struct JsEdge {
pub(crate) id: EdgeId,
pub(crate) from: NodeId,
pub(crate) to: NodeId,
pub(crate) edge_type: String,
pub(crate) properties: HashMap<String, serde_json::Value>,
}
#[wasm_bindgen]
impl JsEdge {
#[wasm_bindgen(getter)]
pub fn id(&self) -> String {
self.id.clone()
}
#[wasm_bindgen(getter)]
pub fn from(&self) -> String {
self.from.clone()
}
#[wasm_bindgen(getter)]
pub fn to(&self) -> String {
self.to.clone()
}
#[wasm_bindgen(getter, js_name = "type")]
pub fn edge_type(&self) -> String {
self.edge_type.clone()
}
#[wasm_bindgen(getter)]
pub fn properties(&self) -> JsValue {
to_value(&self.properties).unwrap_or(JsValue::NULL)
}
#[wasm_bindgen(js_name = getProperty)]
pub fn get_property(&self, key: &str) -> JsValue {
self.properties
.get(key)
.map(|v| to_value(v).unwrap())
.unwrap_or(JsValue::NULL)
}
}
#[wasm_bindgen]
#[derive(Clone)]
pub struct JsHyperedge {
pub(crate) id: HyperedgeId,
pub(crate) nodes: Vec<NodeId>,
pub(crate) description: String,
pub(crate) embedding: Vec<f32>,
pub(crate) confidence: f32,
pub(crate) properties: HashMap<String, serde_json::Value>,
}
#[wasm_bindgen]
impl JsHyperedge {
#[wasm_bindgen(getter)]
pub fn id(&self) -> String {
self.id.clone()
}
#[wasm_bindgen(getter)]
pub fn nodes(&self) -> Vec<String> {
self.nodes.clone()
}
#[wasm_bindgen(getter)]
pub fn description(&self) -> String {
self.description.clone()
}
#[wasm_bindgen(getter)]
pub fn embedding(&self) -> Vec<f32> {
self.embedding.clone()
}
#[wasm_bindgen(getter)]
pub fn confidence(&self) -> f32 {
self.confidence
}
#[wasm_bindgen(getter)]
pub fn properties(&self) -> JsValue {
to_value(&self.properties).unwrap_or(JsValue::NULL)
}
#[wasm_bindgen(getter)]
pub fn order(&self) -> usize {
self.nodes.len()
}
}
#[wasm_bindgen]
pub struct QueryResult {
pub(crate) nodes: Vec<JsNode>,
pub(crate) edges: Vec<JsEdge>,
pub(crate) hyperedges: Vec<JsHyperedge>,
pub(crate) data: Vec<HashMap<String, serde_json::Value>>,
}
#[wasm_bindgen]
impl QueryResult {
#[wasm_bindgen(getter)]
pub fn nodes(&self) -> Vec<JsNode> {
self.nodes.clone()
}
#[wasm_bindgen(getter)]
pub fn edges(&self) -> Vec<JsEdge> {
self.edges.clone()
}
#[wasm_bindgen(getter)]
pub fn hyperedges(&self) -> Vec<JsHyperedge> {
self.hyperedges.clone()
}
#[wasm_bindgen(getter)]
pub fn data(&self) -> JsValue {
to_value(&self.data).unwrap_or(JsValue::NULL)
}
#[wasm_bindgen(getter)]
pub fn count(&self) -> usize {
self.nodes.len() + self.edges.len() + self.hyperedges.len()
}
#[wasm_bindgen(js_name = isEmpty)]
pub fn is_empty(&self) -> bool {
self.count() == 0
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphError {
pub message: String,
pub kind: String,
}
impl GraphError {
pub fn new(message: String, kind: String) -> Self {
Self { message, kind }
}
}
impl From<GraphError> for JsValue {
fn from(err: GraphError) -> Self {
let obj = Object::new();
Reflect::set(&obj, &"message".into(), &err.message.into()).unwrap();
Reflect::set(&obj, &"kind".into(), &err.kind.into()).unwrap();
obj.into()
}
}
impl From<String> for GraphError {
fn from(msg: String) -> Self {
GraphError::new(msg, "GraphError".to_string())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct Node {
pub id: NodeId,
pub labels: Vec<String>,
pub properties: HashMap<String, serde_json::Value>,
pub embedding: Option<Vec<f32>>,
}
impl Node {
pub fn to_js(&self) -> JsNode {
JsNode {
id: self.id.clone(),
labels: self.labels.clone(),
properties: self.properties.clone(),
embedding: self.embedding.clone(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct Edge {
pub id: EdgeId,
pub from: NodeId,
pub to: NodeId,
pub edge_type: String,
pub properties: HashMap<String, serde_json::Value>,
}
impl Edge {
pub fn to_js(&self) -> JsEdge {
JsEdge {
id: self.id.clone(),
from: self.from.clone(),
to: self.to.clone(),
edge_type: self.edge_type.clone(),
properties: self.properties.clone(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct Hyperedge {
pub id: HyperedgeId,
pub nodes: Vec<NodeId>,
pub description: String,
pub embedding: Vec<f32>,
pub confidence: f32,
pub properties: HashMap<String, serde_json::Value>,
}
impl Hyperedge {
pub fn to_js(&self) -> JsHyperedge {
JsHyperedge {
id: self.id.clone(),
nodes: self.nodes.clone(),
description: self.description.clone(),
embedding: self.embedding.clone(),
confidence: self.confidence,
properties: self.properties.clone(),
}
}
}
pub(crate) fn js_object_to_hashmap(
obj: JsValue,
) -> Result<HashMap<String, serde_json::Value>, String> {
from_value(obj).map_err(|e| format!("Failed to convert JS object: {}", e))
}