From 0aaefe9a66553556af4216f0a3baf7f5f1bebc2d Mon Sep 17 00:00:00 2001 From: Lucien Greathouse Date: Wed, 2 Jan 2019 14:00:35 -0800 Subject: [PATCH] C:/Program Files/Git/api/visualize -> /visualize/rbx, added /visualize/imfs --- server/src/lib.rs | 2 +- server/src/session.rs | 2 + server/src/visualize.rs | 128 +++++++++++++++++++++++++++++++++++ server/src/visualize_tree.rs | 42 ------------ server/src/web.rs | 26 +++---- 5 files changed, 140 insertions(+), 60 deletions(-) create mode 100644 server/src/visualize.rs delete mode 100644 server/src/visualize_tree.rs diff --git a/server/src/lib.rs b/server/src/lib.rs index 80b7235c..d15c4d68 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -18,6 +18,6 @@ pub mod rbx_session; pub mod rbx_snapshot; pub mod session; pub mod session_id; -pub mod visualize_tree; +pub mod visualize; pub mod web; pub mod web_util; \ No newline at end of file diff --git a/server/src/session.rs b/server/src/session.rs index bf7686e5..91a286ee 100644 --- a/server/src/session.rs +++ b/server/src/session.rs @@ -18,6 +18,7 @@ pub struct Session { pub session_id: SessionId, pub message_queue: Arc>, pub rbx_session: Arc>, + pub imfs: Arc>, _fs_watcher: FsWatcher, } @@ -50,6 +51,7 @@ impl Session { session_id, message_queue, rbx_session, + imfs, _fs_watcher: fs_watcher, }) } diff --git a/server/src/visualize.rs b/server/src/visualize.rs new file mode 100644 index 00000000..b7cda558 --- /dev/null +++ b/server/src/visualize.rs @@ -0,0 +1,128 @@ +use std::{ + fmt, + io::Write, + path::Path, + process::{Command, Stdio}, +}; + +use rbx_tree::{RbxTree, RbxId}; + +use crate::{ + imfs::{Imfs, ImfsItem}, +}; + +static GRAPHVIZ_HEADER: &str = r#" +digraph RojoTree { + rankdir = "LR"; + graph [ + ranksep = "0.7", + nodesep = "0.5", + ]; + node [ + fontname = "Hack", + shape = "record", + ]; +"#; + +pub fn graphviz_to_svg(source: &str) -> String { + let mut child = Command::new("dot") + .arg("-Tsvg") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .expect("Failed to spawn GraphViz process -- make sure it's installed in order to use /api/visualize"); + + { + let stdin = child.stdin.as_mut().expect("Failed to open stdin"); + stdin.write_all(source.as_bytes()).expect("Failed to write to stdin"); + } + + let output = child.wait_with_output().expect("Failed to read stdout"); + String::from_utf8(output.stdout).expect("Failed to parse stdout as UTF-8") +} + +pub struct VisualizeRbxTree<'a>(pub &'a RbxTree); + +impl<'a> fmt::Display for VisualizeRbxTree<'a> { + fn fmt(&self, output: &mut fmt::Formatter) -> fmt::Result { + writeln!(output, "{}", GRAPHVIZ_HEADER)?; + + visualize_rbx_node(self.0, self.0.get_root_id(), output)?; + + writeln!(output, "}}")?; + + Ok(()) + } +} + +fn visualize_rbx_node(tree: &RbxTree, id: RbxId, output: &mut fmt::Formatter) -> fmt::Result { + let node = tree.get_instance(id).unwrap(); + + writeln!(output, " \"{}\" [label=\"{}\"]", id, node.name)?; + + for &child_id in node.get_children_ids() { + writeln!(output, " \"{}\" -> \"{}\"", id, child_id)?; + visualize_rbx_node(tree, child_id, output)?; + } + + Ok(()) +} + +pub struct VisualizeImfs<'a>(pub &'a Imfs); + +impl<'a> fmt::Display for VisualizeImfs<'a> { + fn fmt(&self, output: &mut fmt::Formatter) -> fmt::Result { + writeln!(output, "{}", GRAPHVIZ_HEADER)?; + + for root_path in self.0.get_roots() { + visualize_root_path(self.0, root_path, output)?; + } + + writeln!(output, "}}")?; + + Ok(()) + } +} + +fn normalize_name(path: &Path) -> String { + path.to_str().unwrap().replace("\\", "/") +} + +fn visualize_root_path(imfs: &Imfs, path: &Path, output: &mut fmt::Formatter) -> fmt::Result { + let normalized_name = normalize_name(path); + let item = imfs.get(path).unwrap(); + + writeln!(output, " \"{}\"", normalized_name)?; + + match item { + ImfsItem::File(_) => {}, + ImfsItem::Directory(directory) => { + for child_path in &directory.children { + writeln!(output, " \"{}\" -> \"{}\"", normalized_name, normalize_name(child_path))?; + visualize_path(imfs, child_path, output)?; + } + }, + } + + Ok(()) +} + +fn visualize_path(imfs: &Imfs, path: &Path, output: &mut fmt::Formatter) -> fmt::Result { + let normalized_name = normalize_name(path); + let short_name = path.file_name().unwrap().to_string_lossy(); + let item = imfs.get(path).unwrap(); + + writeln!(output, " \"{}\" [label = \"{}\"]", normalized_name, short_name)?; + + match item { + ImfsItem::File(_) => {}, + ImfsItem::Directory(directory) => { + for child_path in &directory.children { + writeln!(output, " \"{}\" -> \"{}\"", normalized_name, normalize_name(child_path))?; + visualize_path(imfs, child_path, output)?; + } + }, + } + + Ok(()) +} \ No newline at end of file diff --git a/server/src/visualize_tree.rs b/server/src/visualize_tree.rs deleted file mode 100644 index 9faee18f..00000000 --- a/server/src/visualize_tree.rs +++ /dev/null @@ -1,42 +0,0 @@ -use std::fmt; -use rbx_tree::{RbxTree, RbxId}; - -static GRAPHVIZ_HEADER: &str = r#" -digraph RojoTree { - rankdir = "LR"; - graph [ - ranksep = "0.7", - nodesep = "0.5", - ]; - node [ - fontname = "Hack", - shape = "record", - ]; -"#; - -pub struct VisualizeTree<'a>(pub &'a RbxTree); - -impl<'a> fmt::Display for VisualizeTree<'a> { - fn fmt(&self, output: &mut fmt::Formatter) -> fmt::Result { - writeln!(output, "{}", GRAPHVIZ_HEADER)?; - - visualize_node(self.0, self.0.get_root_id(), output)?; - - writeln!(output, "}}")?; - - Ok(()) - } -} - -fn visualize_node(tree: &RbxTree, id: RbxId, output: &mut fmt::Formatter) -> fmt::Result { - let node = tree.get_instance(id).unwrap(); - - writeln!(output, " \"{}\" [label=\"{}\"]", id, node.name)?; - - for &child_id in node.get_children_ids() { - writeln!(output, " \"{}\" -> \"{}\"", id, child_id)?; - visualize_node(tree, child_id, output)?; - } - - Ok(()) -} \ No newline at end of file diff --git a/server/src/web.rs b/server/src/web.rs index f73ecef2..5467b3cc 100644 --- a/server/src/web.rs +++ b/server/src/web.rs @@ -1,8 +1,6 @@ use std::{ borrow::Cow, collections::HashMap, - io::Write, - process::{Command, Stdio}, sync::{mpsc, Arc}, }; @@ -19,7 +17,7 @@ use crate::{ session_id::SessionId, project::InstanceProjectNodeMetadata, rbx_snapshot::InstanceChanges, - visualize_tree::VisualizeTree, + visualize::{VisualizeRbxTree, VisualizeImfs, graphviz_to_svg}, }; #[derive(Debug, Serialize, Deserialize)] @@ -163,27 +161,21 @@ impl Server { }) }, - (GET) (/api/visualize) => { + (GET) (/visualize/rbx) => { let rbx_session = self.session.rbx_session.lock().unwrap(); let tree = rbx_session.get_tree(); - let dot_source = format!("{}", VisualizeTree(tree)); + let dot_source = format!("{}", VisualizeRbxTree(tree)); - let mut child = Command::new("dot") - .arg("-Tsvg") - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .spawn() - .expect("Failed to spawn GraphViz process -- make sure it's installed in order to use /api/visualize"); + Response::svg(graphviz_to_svg(&dot_source)) + }, - { - let stdin = child.stdin.as_mut().expect("Failed to open stdin"); - stdin.write_all(dot_source.as_bytes()).expect("Failed to write to stdin"); - } + (GET) (/visualize/imfs) => { + let imfs = self.session.imfs.lock().unwrap(); - let output = child.wait_with_output().expect("Failed to read stdout"); + let dot_source = format!("{}", VisualizeImfs(&imfs)); - Response::svg(String::from_utf8_lossy(&output.stdout)) + Response::svg(graphviz_to_svg(&dot_source)) }, _ => Response::empty_404()