use colored::*;
use ruvector_core::types::{SearchResult, VectorEntry};
use serde_json;
pub fn format_search_results(results: &[SearchResult], show_vectors: bool) -> String {
let mut output = String::new();
for (i, result) in results.iter().enumerate() {
output.push_str(&format!("\n{}. {}\n", i + 1, result.id.bold()));
output.push_str(&format!(" Score: {:.4}\n", result.score));
if let Some(metadata) = &result.metadata {
if !metadata.is_empty() {
output.push_str(&format!(
" Metadata: {}\n",
serde_json::to_string_pretty(metadata).unwrap_or_else(|_| "{}".to_string())
));
}
}
if show_vectors {
if let Some(vector) = &result.vector {
let preview: Vec<f32> = vector.iter().take(5).copied().collect();
output.push_str(&format!(" Vector (first 5): {:?}...\n", preview));
}
}
}
output
}
pub fn format_stats(count: usize, dimensions: usize, metric: &str) -> String {
format!(
"\n{}\n Vectors: {}\n Dimensions: {}\n Distance Metric: {}\n",
"Database Statistics".bold().green(),
count.to_string().cyan(),
dimensions.to_string().cyan(),
metric.cyan()
)
}
pub fn format_error(msg: &str) -> String {
format!("{} {}", "Error:".red().bold(), msg)
}
pub fn format_success(msg: &str) -> String {
format!("{} {}", "✓".green().bold(), msg)
}
pub fn format_warning(msg: &str) -> String {
format!("{} {}", "Warning:".yellow().bold(), msg)
}
pub fn format_info(msg: &str) -> String {
format!("{} {}", "ℹ".blue().bold(), msg)
}
pub fn export_json(entries: &[VectorEntry]) -> anyhow::Result<String> {
serde_json::to_string_pretty(entries)
.map_err(|e| anyhow::anyhow!("Failed to serialize to JSON: {}", e))
}
pub fn export_csv(entries: &[VectorEntry]) -> anyhow::Result<String> {
let mut wtr = csv::Writer::from_writer(vec![]);
wtr.write_record(&["id", "vector", "metadata"])?;
for entry in entries {
wtr.write_record(&[
entry.id.as_ref().map(|s| s.as_str()).unwrap_or(""),
&serde_json::to_string(&entry.vector)?,
&serde_json::to_string(&entry.metadata)?,
])?;
}
wtr.flush()?;
String::from_utf8(wtr.into_inner()?)
.map_err(|e| anyhow::anyhow!("Failed to convert CSV to string: {}", e))
}
pub fn format_graph_node(
id: &str,
labels: &[String],
properties: &serde_json::Map<String, serde_json::Value>,
) -> String {
let mut output = String::new();
output.push_str(&format!("{} ({})\n", id.bold(), labels.join(":").cyan()));
if !properties.is_empty() {
output.push_str(" Properties:\n");
for (key, value) in properties {
output.push_str(&format!(" {}: {}\n", key.yellow(), value));
}
}
output
}
pub fn format_graph_relationship(
id: &str,
rel_type: &str,
start_node: &str,
end_node: &str,
properties: &serde_json::Map<String, serde_json::Value>,
) -> String {
let mut output = String::new();
output.push_str(&format!(
"{} -[{}]-> {}\n",
start_node.cyan(),
rel_type.yellow(),
end_node.cyan()
));
if !properties.is_empty() {
output.push_str(" Properties:\n");
for (key, value) in properties {
output.push_str(&format!(" {}: {}\n", key.yellow(), value));
}
}
output
}
pub fn format_graph_table(headers: &[String], rows: &[Vec<String>]) -> String {
use prettytable::{Cell, Row, Table};
let mut table = Table::new();
let header_cells: Vec<Cell> = headers
.iter()
.map(|h| Cell::new(h).style_spec("Fyb"))
.collect();
table.add_row(Row::new(header_cells));
for row in rows {
let cells: Vec<Cell> = row.iter().map(|v| Cell::new(v)).collect();
table.add_row(Row::new(cells));
}
table.to_string()
}
pub fn format_graph_stats(
node_count: usize,
rel_count: usize,
label_count: usize,
rel_type_count: usize,
) -> String {
format!(
"\n{}\n Nodes: {}\n Relationships: {}\n Labels: {}\n Relationship Types: {}\n",
"Graph Statistics".bold().green(),
node_count.to_string().cyan(),
rel_count.to_string().cyan(),
label_count.to_string().cyan(),
rel_type_count.to_string().cyan()
)
}