mod helper;
use std::option::Option;
use std::fmt;
use wasm_bindgen::prelude::*;
use graphit_core::graph::{Edge, Graph, Vertex};
#[wasm_bindgen]
pub struct WasmGraph(Graph<Vertex<String>, Edge<String>>);
#[wasm_bindgen]
pub struct WasmVertex {
label: String,
step: isize,
payload: Option<String>,
}
#[wasm_bindgen]
impl WasmVertex {
#[wasm_bindgen(getter)]
pub fn label(&self) -> String {
self.label.clone()
}
#[wasm_bindgen(getter)]
pub fn step(&self) -> isize {
self.step
}
#[wasm_bindgen(getter)]
pub fn payload(&self) -> Option<String> {
self.payload.clone()
}
}
#[wasm_bindgen]
pub struct WasmEdgeEntry {
target_vid: u32,
bidirectional: bool,
}
impl fmt::Display for WasmEdgeEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let symbol = if self.bidirectional { '◻' } else { '◼' };
write!(f, "{}", symbol)?;
Ok(())
}
}
#[wasm_bindgen]
impl WasmEdgeEntry {
#[wasm_bindgen(getter, js_name = targetVid)]
pub fn target_vid(&self) -> u32 {
self.target_vid
}
#[wasm_bindgen(getter)]
pub fn bidirectional(&self) -> bool {
self.bidirectional
}
}
#[wasm_bindgen]
impl WasmGraph {
#[wasm_bindgen(getter, js_name = rootVid)]
pub fn root_vid(&self) -> Option<u32> {
self.0.root_vid()
}
#[wasm_bindgen(js_name = addVertex)]
pub fn add_vertex(&mut self, vid: u32, label: &str) -> u32 {
self.0.add_vertex(&vid, Vertex::new(label, None, None))
}
#[wasm_bindgen(js_name = addChild)]
pub fn add_child(&mut self, parent_vid: u32, label: &str, bidirectional: bool) -> Option<u32> {
let edge = if bidirectional {
Some(Edge::Bidirectional(None))
} else {
None
};
self.0.add_child(&parent_vid, Vertex::new(label, None, None), edge)
}
#[wasm_bindgen(js_name = addEdge)]
pub fn add_edge(&mut self, from: u32, to: u32, bidirectional: bool) {
let edge = if bidirectional {
Edge::Bidirectional(None)
} else {
Edge::Unidirectional(None)
};
self.0.add_edge(from, to, edge);
}
#[wasm_bindgen(js_name = getVertex)]
pub fn get_vertex(&self, vid: u32) -> Option<WasmVertex> {
self.0.get_vertex(vid).map(|v| WasmVertex {
label: v.get_label().to_string(),
step: v.get_step(),
payload: v.get_payload().cloned(),
})
}
#[wasm_bindgen(js_name = getEdges)]
pub fn get_edges(&self, vid: u32) -> Option<Vec<JsValue>> {
self.0.get_edges(vid).map(|edges| {
edges
.iter()
.map(|(target_vid, edge)| {
let entry = WasmEdgeEntry {
target_vid: *target_vid,
bidirectional: matches!(edge, Edge::Bidirectional(_)),
};
JsValue::from(entry)
})
.collect()
})
}
#[wasm_bindgen(js_name = setPayload)]
pub fn set_payload(&mut self, vid: u32, data_str: String) -> bool {
self.0.set_payload(vid, data_str)
}
#[wasm_bindgen(js_name = setEdgePayload)]
pub fn set_edge_payload(&mut self, eid: u32, data_str: String) -> bool {
match self.0.get_edges(eid.try_into().unwrap()) {
Some(_edge) => {
self.0.set_payload(eid, data_str);
true
}
None => false,
}
}
#[wasm_bindgen(js_name = getEdgePayload)]
pub fn get_edge_payload(&self, eid: u32) -> Option<String> {
self.0.get_vertex(eid)?.get_payload().cloned()
}
#[wasm_bindgen(js_name = getPayload)]
pub fn get_payload(&self, vid: u32) -> Option<String> {
self.0.get_vertex(vid)?.get_payload().cloned()
}
#[wasm_bindgen(js_name = cursor)]
pub fn cursor(&self) -> Option<WasmCursor> {
self.0.root_vid().map(|vid| WasmCursor { current_vid: vid, path: vec![vid] })
}
}
#[wasm_bindgen]
pub struct WasmCursor {
current_vid: u32,
path: Vec<u32>,
}
#[wasm_bindgen]
impl WasmCursor {
#[wasm_bindgen(constructor)]
pub fn new(graph: &WasmGraph) -> Result<WasmCursor, JsValue> {
graph
.0
.root_vid()
.map(|vid| WasmCursor { current_vid: vid, path: vec![vid] })
.ok_or_else(|| JsValue::from_str("Graph has no root vertex"))
}
#[wasm_bindgen(getter, js_name = currentVid)]
pub fn current_vid(&self) -> u32 {
self.current_vid
}
#[wasm_bindgen(js_name = getNode)]
pub fn get_node(&self, graph: &WasmGraph) -> Option<WasmVertex> {
graph.0.get_vertex(self.current_vid).map(|v| WasmVertex {
label: v.get_label().to_string(),
step: v.get_step(),
payload: v.get_payload().cloned(),
})
}
#[wasm_bindgen(js_name = getEdges)]
pub fn get_edges(&self, graph: &WasmGraph) -> Option<Vec<JsValue>> {
graph.0.get_edges(self.current_vid).map(|edges| {
edges
.iter()
.map(|(target_vid, edge)| {
JsValue::from(WasmEdgeEntry {
target_vid: *target_vid,
bidirectional: matches!(edge, Edge::Bidirectional(_)),
})
})
.collect()
})
}
#[wasm_bindgen(js_name = moveTo)]
pub fn move_to(&mut self, graph: &WasmGraph, vid: u32) -> Option<u32> {
let reachable = graph
.0
.get_edges(self.current_vid)
.map(|edges| edges.iter().any(|(t, _)| *t == vid))
.unwrap_or(false);
if reachable {
self.current_vid = vid;
self.path.push(vid);
Some(vid)
} else {
None
}
}
#[wasm_bindgen(js_name = back)]
pub fn back(&mut self) -> Option<u32> {
if self.path.len() > 1 {
self.path.pop();
let prev = *self.path.last().unwrap();
self.current_vid = prev;
Some(prev)
} else {
None
}
}
#[wasm_bindgen(js_name = getPath)]
pub fn get_path(&self) -> Vec<u32> {
self.path.clone()
}
}
#[wasm_bindgen(js_name = createGraph)]
pub fn create_graph(name: &str) -> WasmGraph {
WasmGraph(Graph::<Vertex<String>, Edge<String>>::new(name))
}