summaryrefslogtreecommitdiff
path: root/src/utils/display.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/utils/display.rs')
-rw-r--r--src/utils/display.rs486
1 files changed, 0 insertions, 486 deletions
diff --git a/src/utils/display.rs b/src/utils/display.rs
deleted file mode 100644
index 835313b..0000000
--- a/src/utils/display.rs
+++ /dev/null
@@ -1,486 +0,0 @@
-use colored::*;
-use just_enough_vcs::vcs::data::sheet::SheetMappingMetadata;
-use std::{
- collections::{BTreeMap, HashMap, VecDeque},
- path::PathBuf,
-};
-
-pub struct SimpleTable {
- items: Vec<String>,
- line: Vec<Vec<String>>,
- length: Vec<usize>,
- padding: usize,
-}
-
-impl SimpleTable {
- /// Create a new Table
- pub fn new(items: Vec<impl Into<String>>) -> Self {
- Self::new_with_padding(items, 2)
- }
-
- /// Create a new Table with padding
- pub fn new_with_padding(items: Vec<impl Into<String>>, padding: usize) -> Self {
- let items: Vec<String> = items.into_iter().map(|v| v.into()).collect();
- let mut length = Vec::with_capacity(items.len());
-
- for item in &items {
- length.push(display_width(item));
- }
-
- SimpleTable {
- items,
- padding,
- line: Vec::new(),
- length,
- }
- }
-
- /// Push a new row of items to the table
- pub fn push_item(&mut self, items: Vec<impl Into<String>>) {
- let items: Vec<String> = items.into_iter().map(|v| v.into()).collect();
-
- let mut processed_items = Vec::with_capacity(self.items.len());
-
- for i in 0..self.items.len() {
- if i < items.len() {
- processed_items.push(items[i].clone());
- } else {
- processed_items.push(String::new());
- }
- }
-
- for (i, d) in processed_items.iter().enumerate() {
- let d_len = display_width(d);
- if d_len > self.length[i] {
- self.length[i] = d_len;
- }
- }
-
- self.line.push(processed_items);
- }
-
- /// Insert a new row of items at the specified index
- pub fn insert_item(&mut self, index: usize, items: Vec<impl Into<String>>) {
- let items: Vec<String> = items.into_iter().map(|v| v.into()).collect();
-
- let mut processed_items = Vec::with_capacity(self.items.len());
-
- for i in 0..self.items.len() {
- if i < items.len() {
- processed_items.push(items[i].clone());
- } else {
- processed_items.push(String::new());
- }
- }
-
- for (i, d) in processed_items.iter().enumerate() {
- let d_len = display_width(d);
- if d_len > self.length[i] {
- self.length[i] = d_len;
- }
- }
-
- self.line.insert(index, processed_items);
- }
-
- /// Get the current maximum column widths
- fn get_column_widths(&self) -> &[usize] {
- &self.length
- }
-}
-
-impl std::fmt::Display for SimpleTable {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- let column_widths = self.get_column_widths();
-
- // Build the header row
- let header: Vec<String> = self
- .items
- .iter()
- .enumerate()
- .map(|(i, item)| {
- let target_width = column_widths[i] + self.padding;
- let current_width = display_width(item);
- let space_count = target_width - current_width;
- let space = " ".repeat(space_count);
- let result = format!("{}{}", item, space);
- result
- })
- .collect();
- writeln!(f, "{}", header.join(""))?;
-
- // Build each data row
- for row in &self.line {
- let formatted_row: Vec<String> = row
- .iter()
- .enumerate()
- .map(|(i, cell)| {
- let target_width = column_widths[i] + self.padding;
- let current_width = display_width(cell);
- let space_count = target_width - current_width;
- let spaces = " ".repeat(space_count);
- let result = format!("{}{}", cell, spaces);
- result
- })
- .collect();
- writeln!(f, "{}", formatted_row.join(""))?;
- }
-
- Ok(())
- }
-}
-
-pub fn display_width(s: &str) -> usize {
- // Filter out ANSI escape sequences before calculating width
- let filtered_bytes = strip_ansi_escapes::strip(s);
- let filtered_str = match std::str::from_utf8(&filtered_bytes) {
- Ok(s) => s,
- Err(_) => s, // Fallback to original string if UTF-8 conversion fails
- };
-
- let mut width = 0;
- for c in filtered_str.chars() {
- if c.is_ascii() {
- width += 1;
- } else {
- width += 2;
- }
- }
- width
-}
-
-/// Convert byte size to a human-readable string format
-///
-/// Automatically selects the appropriate unit (B, KB, MB, GB, TB) based on the byte size
-/// and formats it as a string with two decimal places
-pub fn size_str(total_size: usize) -> String {
- if total_size < 1024 {
- format!("{} B", total_size)
- } else if total_size < 1024 * 1024 {
- format!("{:.2} KB", total_size as f64 / 1024.0)
- } else if total_size < 1024 * 1024 * 1024 {
- format!("{:.2} MB", total_size as f64 / (1024.0 * 1024.0))
- } else if total_size < 1024 * 1024 * 1024 * 1024 {
- format!("{:.2} GB", total_size as f64 / (1024.0 * 1024.0 * 1024.0))
- } else {
- format!(
- "{:.2} TB",
- total_size as f64 / (1024.0 * 1024.0 * 1024.0 * 1024.0)
- )
- }
-}
-
-// Convert the Markdown formatted text into a format supported by the command line
-pub fn md(text: impl AsRef<str>) -> String {
- let text = text.as_ref().trim();
- let mut result = String::new();
- let mut color_stack: VecDeque<String> = VecDeque::new();
-
- let mut i = 0;
- let chars: Vec<char> = text.chars().collect();
-
- while i < chars.len() {
- // Check for escape character \
- if chars[i] == '\\' && i + 1 < chars.len() {
- let escaped_char = chars[i + 1];
- // Only escape specific characters
- if matches!(escaped_char, '*' | '<' | '>' | '`') {
- let mut escaped_text = escaped_char.to_string();
-
- // Apply current color stack
- for color in color_stack.iter().rev() {
- escaped_text = apply_color(&escaped_text, color);
- }
-
- result.push_str(&escaped_text);
- i += 2;
- continue;
- }
- }
-
- // Check for color tag start [[color]]
- if i + 1 < chars.len() && chars[i] == '[' && chars[i + 1] == '[' {
- let mut j = i + 2;
- while j < chars.len()
- && !(chars[j] == ']' && j + 1 < chars.len() && chars[j + 1] == ']')
- {
- j += 1;
- }
-
- if j + 1 < chars.len() {
- let tag_content: String = chars[i + 2..j].iter().collect();
-
- // Check if it's a closing tag [[/]]
- if tag_content == "/" {
- color_stack.pop_back();
- i = j + 2;
- continue;
- }
-
- // It's a color tag
- color_stack.push_back(tag_content.clone());
- i = j + 2;
- continue;
- }
- }
-
- // Check for bold **text**
- if i + 1 < chars.len() && chars[i] == '*' && chars[i + 1] == '*' {
- let mut j = i + 2;
- while j + 1 < chars.len() && !(chars[j] == '*' && chars[j + 1] == '*') {
- j += 1;
- }
-
- if j + 1 < chars.len() {
- let bold_text: String = chars[i + 2..j].iter().collect();
- let mut formatted_text = bold_text.bold().to_string();
-
- // Apply current color stack
- for color in color_stack.iter().rev() {
- formatted_text = apply_color(&formatted_text, color);
- }
-
- result.push_str(&formatted_text);
- i = j + 2;
- continue;
- }
- }
-
- // Check for italic *text*
- if chars[i] == '*' {
- let mut j = i + 1;
- while j < chars.len() && chars[j] != '*' {
- j += 1;
- }
-
- if j < chars.len() {
- let italic_text: String = chars[i + 1..j].iter().collect();
- let mut formatted_text = italic_text.italic().to_string();
-
- // Apply current color stack
- for color in color_stack.iter().rev() {
- formatted_text = apply_color(&formatted_text, color);
- }
-
- result.push_str(&formatted_text);
- i = j + 1;
- continue;
- }
- }
-
- // Check for angle-bracketed content <text>
- if chars[i] == '<' {
- let mut j = i + 1;
- while j < chars.len() && chars[j] != '>' {
- j += 1;
- }
-
- if j < chars.len() {
- // Include the angle brackets in the output
- let angle_text: String = chars[i..=j].iter().collect();
- let mut formatted_text = angle_text.cyan().to_string();
-
- // Apply current color stack
- for color in color_stack.iter().rev() {
- formatted_text = apply_color(&formatted_text, color);
- }
-
- result.push_str(&formatted_text);
- i = j + 1;
- continue;
- }
- }
-
- // Check for inline code `text`
- if chars[i] == '`' {
- let mut j = i + 1;
- while j < chars.len() && chars[j] != '`' {
- j += 1;
- }
-
- if j < chars.len() {
- // Include the backticks in the output
- let code_text: String = chars[i..=j].iter().collect();
- let mut formatted_text = code_text.green().to_string();
-
- // Apply current color stack
- for color in color_stack.iter().rev() {
- formatted_text = apply_color(&formatted_text, color);
- }
-
- result.push_str(&formatted_text);
- i = j + 1;
- continue;
- }
- }
-
- // Regular character
- let mut current_char = chars[i].to_string();
-
- // Apply current color stack
- for color in color_stack.iter().rev() {
- current_char = apply_color(&current_char, color);
- }
-
- result.push_str(&current_char);
- i += 1;
- }
-
- result
-}
-
-// Helper function to apply color to text
-fn apply_color(text: &str, color_name: &str) -> String {
- match color_name {
- // Normal colors
- "black" => text.black().to_string(),
- "red" => text.red().to_string(),
- "green" => text.green().to_string(),
- "yellow" => text.yellow().to_string(),
- "blue" => text.blue().to_string(),
- "magenta" => text.magenta().to_string(),
- "cyan" => text.cyan().to_string(),
- "white" => text.white().to_string(),
- "bright_black" => text.bright_black().to_string(),
- "bright_red" => text.bright_red().to_string(),
- "bright_green" => text.bright_green().to_string(),
- "bright_yellow" => text.bright_yellow().to_string(),
- "bright_blue" => text.bright_blue().to_string(),
- "bright_magenta" => text.bright_magenta().to_string(),
- "bright_cyan" => text.bright_cyan().to_string(),
- "bright_white" => text.bright_white().to_string(),
-
- // Short aliases for bright colors
- "b_black" => text.bright_black().to_string(),
- "b_red" => text.bright_red().to_string(),
- "b_green" => text.bright_green().to_string(),
- "b_yellow" => text.bright_yellow().to_string(),
- "b_blue" => text.bright_blue().to_string(),
- "b_magenta" => text.bright_magenta().to_string(),
- "b_cyan" => text.bright_cyan().to_string(),
- "b_white" => text.bright_white().to_string(),
-
- // Gray colors using truecolor
- "gray" | "grey" => text.truecolor(128, 128, 128).to_string(),
- "bright_gray" | "bright_grey" => text.truecolor(192, 192, 192).to_string(),
- "b_gray" | "b_grey" => text.truecolor(192, 192, 192).to_string(),
-
- // Default to white if color not recognized
- _ => text.to_string(),
- }
-}
-
-/// Render a HashMap of PathBuf to SheetMappingMetadata as a tree string.
-pub fn render_share_path_tree(paths: &HashMap<PathBuf, SheetMappingMetadata>) -> String {
- if paths.is_empty() {
- return String::new();
- }
-
- // Collect all path components into a tree structure
- let mut root = TreeNode::new("".to_string());
-
- for (path, metadata) in paths {
- let mut current = &mut root;
- let components: Vec<String> = path
- .components()
- .filter_map(|comp| match comp {
- std::path::Component::Normal(s) => s.to_str().map(|s| s.to_string()),
- _ => None,
- })
- .collect();
-
- for (i, comp) in components.iter().enumerate() {
- let is_leaf = i == components.len() - 1;
- let child = current
- .children
- .entry(comp.clone())
- .or_insert_with(|| TreeNode::new(comp.clone()));
-
- // If this is the leaf node, store the metadata
- if is_leaf {
- child.metadata = Some((metadata.id.clone(), metadata.version.clone()));
- }
-
- current = child;
- }
- }
-
- // Convert tree to string representation
- let mut result = String::new();
- let is_root = true;
- let prefix = String::new();
- let last_stack = vec![true]; // Root is always "last"
-
- add_tree_node_to_string(&root, &mut result, is_root, &prefix, &last_stack);
-
- result
-}
-
-/// Internal tree node structure for building the path tree
-#[derive(Debug)]
-struct TreeNode {
- name: String,
- children: BTreeMap<String, TreeNode>, // Use BTreeMap for sorted output
- metadata: Option<(String, String)>, // Store (id, version) for leaf nodes
-}
-
-impl TreeNode {
- fn new(name: String) -> Self {
- Self {
- name,
- children: BTreeMap::new(),
- metadata: None,
- }
- }
-}
-
-/// Recursively add tree node to string representation
-fn add_tree_node_to_string(
- node: &TreeNode,
- result: &mut String,
- is_root: bool,
- prefix: &str,
- last_stack: &[bool],
-) {
- if !is_root {
- // Add the tree prefix for this node
- for &is_last in &last_stack[1..] {
- if is_last {
- result.push_str(" ");
- } else {
- result.push_str("│ ");
- }
- }
-
- // Add the connector for this node
- if let Some(&is_last) = last_stack.last() {
- if is_last {
- result.push_str("└── ");
- } else {
- result.push_str("├── ");
- }
- }
-
- // Add node name
- result.push_str(&node.name);
-
- // Add metadata for leaf nodes
- if let Some((id, version)) = &node.metadata {
- // Truncate id to first 11 characters
- let truncated_id = if id.len() > 11 { &id[..11] } else { id };
- result.push_str(&format!(" [{}|{}]", truncated_id, version));
- }
-
- result.push('\n');
- }
-
- // Process children
- let child_count = node.children.len();
- for (i, (_, child)) in node.children.iter().enumerate() {
- let is_last_child = i == child_count - 1;
- let mut new_last_stack = last_stack.to_vec();
- new_last_stack.push(is_last_child);
-
- add_tree_node_to_string(child, result, false, prefix, &new_last_stack);
- }
-}