Remove node, add edge and neigbour methods added to caching graph ft. comments and tests

This commit is contained in:
Noah Santschi-Cooney 2020-06-06 00:25:32 +01:00
parent 375f7f45a7
commit 57dfa45d9c
No known key found for this signature in database
GPG key ID: 3B22282472C8AE48
5 changed files with 100 additions and 33 deletions

34
server/Cargo.lock generated
View file

@ -112,8 +112,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"base64 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.108 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.52 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_repr 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -233,8 +233,8 @@ dependencies = [
"lsp-types 0.74.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rustdt-json_rpc 0.3.0 (git+https://github.com/strager/rustdt-json_rpc?branch=serde-1.0)",
"rustdt_util 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.108 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.52 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -245,8 +245,8 @@ dependencies = [
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"rustdt_util 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.108 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.52 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -269,15 +269,15 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.108"
version = "1.0.110"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde_derive 1.0.108 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_derive"
version = "1.0.108"
version = "1.0.110"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)",
@ -287,12 +287,12 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.52"
version = "1.0.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"ryu 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.108 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -366,7 +366,7 @@ dependencies = [
"idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.108 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -379,8 +379,8 @@ dependencies = [
"petgraph 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"rust_lsp 0.6.0 (git+https://github.com/Strum355/RustLSP)",
"serde 1.0.108 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.52 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)",
"tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
"walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -460,9 +460,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum rustdt_util 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7cfffa8a89d8758be2dd5605c5fc62bce055af2491ebf3ce953d4d31512c59fd"
"checksum ryu 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ed3d612bc64430efeb3f7ee6ef26d590dce0c43249217bddc62112540c7941e1"
"checksum same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
"checksum serde 1.0.108 (registry+https://github.com/rust-lang/crates.io-index)" = "98b4e9fadb50ff90c1e758e42f13a8e2ab84e1927abb0aa5b95e5ecdb49ea741"
"checksum serde_derive 1.0.108 (registry+https://github.com/rust-lang/crates.io-index)" = "6a316eba8a61ba986e87e7e04a74f9feda86298fb454b76232ead4e425976090"
"checksum serde_json 1.0.52 (registry+https://github.com/rust-lang/crates.io-index)" = "a7894c8ed05b7a3a279aeb79025fdec1d3158080b75b98a08faf2806bb799edd"
"checksum serde 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)" = "99e7b308464d16b56eba9964e4972a3eee817760ab60d88c3f86e1fecb08204c"
"checksum serde_derive 1.0.110 (registry+https://github.com/rust-lang/crates.io-index)" = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984"
"checksum serde_json 1.0.53 (registry+https://github.com/rust-lang/crates.io-index)" = "993948e75b189211a9b31a7528f950c6adc21f9720b6438ff80a7fa2f864cea2"
"checksum serde_repr 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "cd02c7587ec314570041b2754829f84d873ced14a96d1fd1823531e11db40573"
"checksum smallvec 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4"
"checksum syn 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)" = "e8e5aa70697bb26ee62214ae3288465ecec0000f05182f039b477001f08f5ae7"

View file

@ -6,12 +6,12 @@ edition = "2018"
[dependencies]
rust_lsp = { git = "https://github.com/Strum355/RustLSP" }
serde_json = "1.0.52"
serde = "1.0.107"
serde_json = "1.0.53"
serde = "1.0.110"
walkdir = "2.3.1"
petgraph = "0.5.0"
petgraph = "0.5.1"
lazy_static = "1.4.0"
regex = "1.3.7"
regex = "1.3.9"
chan = "0.1.23"
[dev-dependencies]

View file

@ -1,2 +1,4 @@
.PHONY: watchtest
watchtest:
RUST_BACKTRACE=0 cargo watch -x test -i Makefile

View file

@ -1,10 +1,19 @@
use petgraph::stable_graph::StableDiGraph;
use petgraph::stable_graph::NodeIndex;
use petgraph::stable_graph::EdgeIndex;
use std::collections::HashMap;
/// Wraps a `StableDiGraph` with caching behaviour for node search by maintaining
/// an index for node value to node index and a reverse index.
/// This allows for O(1) lookup for a value after the initial lookup.
pub struct CachedStableGraph {
// StableDiGraph is used as it allows for String node values, essential for
// generating the GraphViz DOT render.
pub graph: StableDiGraph<String, String>,
cache: HashMap<String, NodeIndex>,
// Maps a node index to its abstracted string representation.
// Mainly used as the graph is based on NodeIndex and
reverse_index: HashMap<NodeIndex, String>,
}
impl CachedStableGraph {
@ -12,25 +21,50 @@ impl CachedStableGraph {
CachedStableGraph{
graph: StableDiGraph::new(),
cache: HashMap::new(),
reverse_index: HashMap::new(),
}
}
pub fn find_node(&mut self, name: String) -> Option<NodeIndex> {
match self.cache.get(&name) {
/// Returns the `NodeIndex` for a given graph node with the value of `name`
/// and caches the result in the `HashMap`. Complexity is O(1) if the value
/// is cached, else O(n) as an exhaustive search must be done.
pub fn find_node(&mut self, name: impl Into<String>) -> Option<NodeIndex> {
let name_str = name.into();
match self.cache.get(&name_str) {
Some(n) => Some(*n),
None => {
let n = self.graph.node_indices().find(|n| self.graph[*n] == name);
// If the string is not in cache, O(n) search the graph (i know...) and then cache the NodeIndex
// for later
let n = self.graph.node_indices().find(|n| self.graph[*n] == name_str);
if n.is_some() {
self.cache.insert(name, n.unwrap());
self.cache.insert(name_str, n.unwrap());
}
n
}
}
}
pub fn add_node(&mut self, name: String) -> NodeIndex {
let idx = self.graph.add_node(name.clone());
self.cache.insert(name, idx);
pub fn remove_node(&mut self, name: impl Into<String>) {
let idx = self.cache.remove(&name.into());
if idx.is_some() {
self.graph.remove_node(idx.unwrap());
}
}
pub fn add_node<S: Into<String>>(&mut self, name: S) -> NodeIndex {
let name_str = name.into();
let idx = self.graph.add_node(name_str.clone());
self.cache.insert(name_str.clone(), idx);
self.reverse_index.insert(idx, name_str);
idx
}
pub fn add_edge(&mut self, a: NodeIndex, b: NodeIndex) -> EdgeIndex {
self.graph.add_edge(a, b, String::from("includes"))
}
/// Returns the filepaths of the neighbors for the given `NodeIndex`
pub fn neighbors(&self, node: NodeIndex) -> Vec<String> {
self.graph.neighbors(node).map(|n| self.reverse_index.get(&n).unwrap().clone()).collect()
}
}

View file

@ -19,7 +19,6 @@ impl io::Write for StdoutNewline {
if buf[buf.len()-1] == "}".as_bytes()[0] {
#[allow(unused_variables)]
let res = self.s.write("\n\n".as_bytes());
}
res
}
@ -42,6 +41,13 @@ fn new_temp_server() -> MinecraftShaderLanguageServer {
}
}
fn copy_files(files: &str, dest: &TempDir) {
let opts = &fs_extra::dir::CopyOptions::new();
let files = fs::read_dir(files).unwrap().map(|e| String::from(e.unwrap().path().as_os_str().to_str().unwrap())).collect::<Vec<String>>();
fs_extra::copy_items(&files, dest.path().join("shaders"), opts).unwrap();
}
#[allow(deprecated)]
#[test]
fn test_empty_initialize() {
let mut server = new_temp_server();
@ -84,6 +90,7 @@ fn test_empty_initialize() {
//std::thread::sleep(std::time::Duration::from_secs(1));
}
#[allow(deprecated)]
#[test]
fn test_01_initialize() {
let mut server = new_temp_server();
@ -91,9 +98,7 @@ fn test_01_initialize() {
let tmp_dir = TempDir::new("mcshader").unwrap();
fs::create_dir(tmp_dir.path().join("shaders")).unwrap();
let opts = &fs_extra::dir::CopyOptions::new();
let files = fs::read_dir("./testdata/01").unwrap().map(|e| String::from(e.unwrap().path().as_os_str().to_str().unwrap())).collect::<Vec<String>>();
fs_extra::copy_items(&files, tmp_dir.path().join("shaders"), opts).unwrap();
copy_files("./testdata/01", &tmp_dir);
let tmp_path = tmp_dir.path().as_os_str().to_str().unwrap();
let tmp_uri = format!("{}{}", "file://", tmp_path);
@ -121,10 +126,36 @@ fn test_01_initialize() {
let completable = MethodCompletable::new(ResponseCompletable::new(Some(Id::Number(1)), Box::new(on_response)));
server.initialize(initialize_params, completable);
// Assert there is one edge between two nodes
assert_eq!(server.graph.borrow().graph.edge_count(), 1);
let edge = server.graph.borrow().graph.edge_indices().next().unwrap();
let (node1, node2) = server.graph.borrow().graph.edge_endpoints(edge).unwrap();
// Assert the values of the two nodes in the tree
assert_eq!(server.graph.borrow().graph[node1].as_str(), tmp_dir.path().join("shaders").join("final.fsh").as_os_str().to_str().unwrap());
assert_eq!(server.graph.borrow().graph[node2].as_str(), tmp_dir.path().join("shaders").join("common.glsl").as_os_str().to_str().unwrap());
server.endpoint.request_shutdown();
}
}
#[test]
fn test_graph_two_connected_nodes() {
let mut graph = graph::CachedStableGraph::new();
graph.add_node("sample");
graph.add_node("banana");
let idx1 = graph.find_node("sample").unwrap();
let idx2 = graph.find_node("banana").unwrap();
graph.add_edge(idx1, idx2);
let neighbors = graph.neighbors(idx1);
assert_eq!(neighbors.len(), 1);
assert_eq!(neighbors[0], "banana");
graph.remove_node("sample");
assert_eq!(graph.graph.node_count(), 1);
assert!(graph.find_node("sample").is_none());
let neighbors = graph.neighbors(idx2);
assert_eq!(neighbors.len(), 0);
}