blob: bd20ebc1ce9b7a83a663b743b204caf995aab2a3 [file] [log] [blame]
use std::{fs, iter::Iterator, path::PathBuf};
/// Convenience macro for reading the filenames of a directory's contents when
/// the path is expected to exist and all its contents are expected
/// to be readable.
fn get_file_names(dir: fs::ReadDir) -> impl Iterator<Item = String> {
dir.map(|entry| {
entry
.expect("get_file_names: entry is unreadable")
.path()
.file_name()
.expect("get_file_names: invalid path")
.to_os_string()
.into_string()
.expect("get_file_names: unexpected characters")
})
}
static SPACER: &str = " ";
pub struct Component {
name: String,
url: String,
id: String,
component_type: String,
children: Vec<Component>,
in_services: Vec<String>,
out_services: Vec<String>,
exposed_services: Vec<String>,
used_services: Vec<String>,
}
impl Component {
pub fn new_root_component(hub_path: PathBuf) -> Self {
Self::explore("<root>".to_string(), hub_path)
}
fn explore(name: String, hub_path: PathBuf) -> Self {
let url = fs::read_to_string(hub_path.join("url")).expect("Could not read url from hub");
let id = fs::read_to_string(hub_path.join("id")).expect("Could not read id from hub");
let component_type = fs::read_to_string(hub_path.join("component_type"))
.expect("Could not read component_type from hub");
let exec_path = hub_path.join("exec");
let in_services = get_services(exec_path.join("in"));
let out_services = get_services(exec_path.join("out"));
let exposed_services = get_services(exec_path.join("exposed"));
let used_services = get_services(exec_path.join("used"));
// Recurse on the children
let child_path = hub_path.join("children");
let child_dir = fs::read_dir(child_path).expect("could not open children directory");
let child_names = get_file_names(child_dir);
let mut children: Vec<Self> = vec![];
for child_name in child_names {
let path = hub_path.join("children").join(child_name.clone());
let child = Self::explore(child_name, path);
children.push(child);
}
Self {
name,
url,
id,
component_type,
children,
in_services,
out_services,
exposed_services,
used_services,
}
}
pub fn generate_output(&self) -> Vec<String> {
let mut output: Vec<String> = vec![];
self.generate_tree(&mut output);
output.push("".to_string());
self.generate_details(&mut output);
output
}
pub fn generate_tree(&self, lines: &mut Vec<String>) {
self.generate_tree_recursive(1, lines);
}
fn generate_tree_recursive(&self, level: usize, lines: &mut Vec<String>) {
let space = SPACER.repeat(level - 1);
let line = format!("{}{}", space, self.name);
lines.push(line);
for child in &self.children {
child.generate_tree_recursive(level + 1, lines);
}
}
fn generate_details(&self, lines: &mut Vec<String>) {
self.generate_details_recursive("", lines);
}
fn generate_details_recursive(&self, prefix: &str, lines: &mut Vec<String>) {
let moniker = format!("{}{}:{}", prefix, self.name, self.id);
lines.push(moniker.clone());
lines.push(format!("- URL: {}", self.url));
lines.push(format!("- Component Type: {}", self.component_type));
generate_services("Exposed Services", &self.exposed_services, lines);
generate_services("Incoming Services", &self.in_services, lines);
generate_services("Outgoing Services", &self.out_services, lines);
generate_services("Used Services", &self.used_services, lines);
// Recurse on children
let prefix = format!("{}/", moniker);
for child in &self.children {
lines.push("".to_string());
child.generate_details_recursive(&prefix, lines);
}
}
}
fn get_services(path: PathBuf) -> Vec<String> {
if let Ok(dir) = fs::read_dir(path.join("svc")) {
get_file_names(dir).collect()
} else {
vec![]
}
}
fn generate_services(services_type: &str, services: &Vec<String>, lines: &mut Vec<String>) {
lines.push(format!("- {} ({})", services_type, services.len()));
for service in services {
lines.push(format!("{}- {}", SPACER, service));
}
}