fix: fixed rendering for map inside score, added wrap or truncate to keep string inside column, increased column width to accomodate urls
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_value::Value;
|
use serde_value::Value;
|
||||||
|
|
||||||
@@ -43,6 +45,8 @@ where
|
|||||||
pub trait ScoreToString<T: Topology> {
|
pub trait ScoreToString<T: Topology> {
|
||||||
fn print_score_details(&self) -> String;
|
fn print_score_details(&self) -> String;
|
||||||
fn format_value_as_string(&self, val: &Value, indent: usize) -> String;
|
fn format_value_as_string(&self, val: &Value, indent: usize) -> String;
|
||||||
|
fn format_map(&self, map: &BTreeMap<Value, Value>, indent: usize) -> String;
|
||||||
|
fn wrap_or_truncate(&self, s: &str, width: usize) -> Vec<String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S, T> ScoreToString<T> for S
|
impl<S, T> ScoreToString<T> for S
|
||||||
@@ -57,6 +61,78 @@ where
|
|||||||
output += "\n";
|
output += "\n";
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
fn format_map(&self, map: &BTreeMap<Value, Value>, indent: usize) -> String {
|
||||||
|
let pad = " ".repeat(indent * 2);
|
||||||
|
let mut output = String::new();
|
||||||
|
|
||||||
|
output += &format!(
|
||||||
|
"{}+--------------------------+--------------------------------------------------+\n",
|
||||||
|
pad
|
||||||
|
);
|
||||||
|
output += &format!("{}| {:<24} | {:<48} |\n", pad, "score_name", self.name());
|
||||||
|
output += &format!(
|
||||||
|
"{}+--------------------------+--------------------------------------------------+\n",
|
||||||
|
pad
|
||||||
|
);
|
||||||
|
|
||||||
|
for (k, v) in map {
|
||||||
|
let key_str = match k {
|
||||||
|
Value::String(s) => s.clone(),
|
||||||
|
other => format!("{:?}", other),
|
||||||
|
};
|
||||||
|
|
||||||
|
let formatted_val = self.format_value_as_string(v, indent + 1);
|
||||||
|
let mut lines = formatted_val.lines().map(|line| line.trim_start());
|
||||||
|
|
||||||
|
let wrapped_lines: Vec<_> = lines
|
||||||
|
.flat_map(|line| self.wrap_or_truncate(line.trim_start(), 48))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if let Some(first) = wrapped_lines.first() {
|
||||||
|
output += &format!("{}| {:<24} | {:<48} |\n", pad, key_str, first);
|
||||||
|
for line in &wrapped_lines[1..] {
|
||||||
|
output += &format!("{}| {:<24} | {:<48} |\n", pad, "", line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// let first_line = lines.next().unwrap_or("");
|
||||||
|
// output += &format!("{}| {:<24} | {:<48} |\n", pad, key_str, first_line);
|
||||||
|
//
|
||||||
|
// for line in lines {
|
||||||
|
// output += &format!("{}| {:<24} | {:<48} |\n", pad, "", line);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
output += &format!(
|
||||||
|
"{}+--------------------------+--------------------------------------------------+\n\n",
|
||||||
|
pad
|
||||||
|
);
|
||||||
|
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wrap_or_truncate(&self, s: &str, width: usize) -> Vec<String> {
|
||||||
|
let mut lines = Vec::new();
|
||||||
|
let mut current = s;
|
||||||
|
|
||||||
|
while !current.is_empty() {
|
||||||
|
if current.len() <= width {
|
||||||
|
lines.push(current.to_string());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to wrap at whitespace if possible
|
||||||
|
let mut split_index = current[..width].rfind(' ').unwrap_or(width);
|
||||||
|
if split_index == 0 {
|
||||||
|
split_index = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.push(current[..split_index].trim_end().to_string());
|
||||||
|
current = current[split_index..].trim_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
lines
|
||||||
|
}
|
||||||
|
|
||||||
fn format_value_as_string(&self, val: &Value, indent: usize) -> String {
|
fn format_value_as_string(&self, val: &Value, indent: usize) -> String {
|
||||||
let pad = " ".repeat(indent * 2);
|
let pad = " ".repeat(indent * 2);
|
||||||
@@ -75,7 +151,7 @@ where
|
|||||||
Value::F32(f) => output += &format!("{}{}\n", pad, f),
|
Value::F32(f) => output += &format!("{}{}\n", pad, f),
|
||||||
Value::F64(f) => output += &format!("{}{}\n", pad, f),
|
Value::F64(f) => output += &format!("{}{}\n", pad, f),
|
||||||
Value::Char(c) => output += &format!("{}{}\n", pad, c),
|
Value::Char(c) => output += &format!("{}{}\n", pad, c),
|
||||||
Value::String(s) => output += &format!("{}{:<26}\n", pad, s),
|
Value::String(s) => output += &format!("{}{:<48}\n", pad, s),
|
||||||
Value::Unit => output += &format!("{}<unit>\n", pad),
|
Value::Unit => output += &format!("{}<unit>\n", pad),
|
||||||
Value::Bytes(bytes) => output += &format!("{}{:?}\n", pad, bytes),
|
Value::Bytes(bytes) => output += &format!("{}{:?}\n", pad, bytes),
|
||||||
|
|
||||||
@@ -107,38 +183,27 @@ where
|
|||||||
Value::Map(map) => {
|
Value::Map(map) => {
|
||||||
if map.is_empty() {
|
if map.is_empty() {
|
||||||
output += &format!("{}<empty map>\n", pad);
|
output += &format!("{}<empty map>\n", pad);
|
||||||
|
} else if indent == 0 {
|
||||||
|
output += &self.format_map(map, indent);
|
||||||
} else {
|
} else {
|
||||||
output += &format!(
|
|
||||||
"{}+--------------------------+----------------------------+\n",
|
|
||||||
pad
|
|
||||||
);
|
|
||||||
output += &format!("{}| {:<24} | {:<26} |\n", pad, "score_name", self.name());
|
|
||||||
output += &format!(
|
|
||||||
"{}+--------------------------+----------------------------+\n",
|
|
||||||
pad
|
|
||||||
);
|
|
||||||
|
|
||||||
for (k, v) in map {
|
for (k, v) in map {
|
||||||
let key_str = match k {
|
let key_str = match k {
|
||||||
Value::String(s) => s.clone(),
|
Value::String(s) => s.clone(),
|
||||||
other => format!("{:?}", other),
|
other => format!("{:?}", other),
|
||||||
};
|
};
|
||||||
let val_str = match v {
|
|
||||||
Value::String(s) => s.clone(),
|
|
||||||
Value::Bool(b) => b.to_string(),
|
|
||||||
Value::Option(Some(inner)) => format!("{:?}", inner),
|
|
||||||
Value::Option(None) => "None".to_string(),
|
|
||||||
Value::Seq(seq) => format!("{:?}", seq),
|
|
||||||
_ => format!("{:?}", v),
|
|
||||||
};
|
|
||||||
|
|
||||||
output += &format!("{}| {:<24} | {:<26} |\n", pad, key_str, val_str);
|
let val_str = self
|
||||||
|
.format_value_as_string(v, indent + 1)
|
||||||
|
.trim()
|
||||||
|
.to_string();
|
||||||
|
let val_lines: Vec<_> = val_str.lines().collect();
|
||||||
|
|
||||||
|
output +=
|
||||||
|
&format!("{}{}: {}\n", pad, key_str, val_lines.first().unwrap_or(&""));
|
||||||
|
for line in val_lines.iter().skip(1) {
|
||||||
|
output += &format!("{} {}\n", pad, line);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
output += &format!(
|
|
||||||
"{}+--------------------------+----------------------------+\n\n",
|
|
||||||
pad
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,15 +216,15 @@ where
|
|||||||
//
|
//
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::*;
|
||||||
use crate::modules::dns::DnsScore;
|
use crate::modules::dns::DnsScore;
|
||||||
use crate::topology::{self, HAClusterTopology};
|
use crate::topology::{self, HAClusterTopology};
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_format_values_as_string() {
|
fn test_format_values_as_string() {
|
||||||
|
|
||||||
let dns_score = Box::new(DnsScore::new(vec![], None));
|
let dns_score = Box::new(DnsScore::new(vec![], None));
|
||||||
let print_score_output = <DnsScore as ScoreToString<HAClusterTopology>>::print_score_details(&dns_score);
|
let print_score_output =
|
||||||
|
<DnsScore as ScoreToString<HAClusterTopology>>::print_score_details(&dns_score);
|
||||||
let expected_empty_dns_score_table = "\n+--------------------------+----------------------------+\n| score_name | DnsScore |\n+--------------------------+----------------------------+\n| dns_entries | [] |\n| register_dhcp_leases | None |\n+--------------------------+----------------------------+\n\n\n";
|
let expected_empty_dns_score_table = "\n+--------------------------+----------------------------+\n| score_name | DnsScore |\n+--------------------------+----------------------------+\n| dns_entries | [] |\n| register_dhcp_leases | None |\n+--------------------------+----------------------------+\n\n\n";
|
||||||
assert_eq!(print_score_output, expected_empty_dns_score_table);
|
assert_eq!(print_score_output, expected_empty_dns_score_table);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ impl<T: Topology + std::fmt::Debug + Send + Sync + 'static> HarmonyTUI<T> {
|
|||||||
std::fs::create_dir_all("log")?;
|
std::fs::create_dir_all("log")?;
|
||||||
tui_logger::set_log_file("log/harmony.log").unwrap();
|
tui_logger::set_log_file("log/harmony.log").unwrap();
|
||||||
|
|
||||||
|
|
||||||
color_eyre::install()?;
|
color_eyre::install()?;
|
||||||
let mut terminal = ratatui::init();
|
let mut terminal = ratatui::init();
|
||||||
log_panics::init();
|
log_panics::init();
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ impl<T: Topology + std::fmt::Debug> ScoreListWidget<T> {
|
|||||||
state: ExecutionState::INITIATED,
|
state: ExecutionState::INITIATED,
|
||||||
score: score.clone_box(),
|
score: score.clone_box(),
|
||||||
});
|
});
|
||||||
info!("{:#?}\n\nConfirm Execution (Press y/n)", score);
|
info!("{}\n\nConfirm Execution (Press y/n)", score.name());
|
||||||
info!("{}", score.print_score_details());
|
info!("{}", score.print_score_details());
|
||||||
} else {
|
} else {
|
||||||
warn!("No Score selected, nothing to launch");
|
warn!("No Score selected, nothing to launch");
|
||||||
|
|||||||
Reference in New Issue
Block a user