Files
owlen/crates/owlen-core/src/formatting.rs

62 lines
1.8 KiB
Rust

use crate::types::Message;
use textwrap::{wrap, Options};
/// Formats messages for display across different clients.
#[derive(Debug, Clone)]
pub struct MessageFormatter {
wrap_width: usize,
show_role_labels: bool,
preserve_empty_lines: bool,
}
impl MessageFormatter {
/// Create a new formatter
pub fn new(wrap_width: usize, show_role_labels: bool) -> Self {
Self {
wrap_width: wrap_width.max(20),
show_role_labels,
preserve_empty_lines: true,
}
}
/// Override whether empty lines should be preserved
pub fn with_preserve_empty(mut self, preserve: bool) -> Self {
self.preserve_empty_lines = preserve;
self
}
/// Render a message to a list of visual lines ready for display
pub fn format_message(&self, message: &Message) -> Vec<String> {
let mut lines = Vec::new();
let mut content = message.content.trim_end().to_string();
if content.is_empty() && self.preserve_empty_lines {
content.push(' ');
}
let options = Options::new(self.wrap_width)
.break_words(true)
.word_separator(textwrap::WordSeparator::UnicodeBreakProperties);
let wrapped = wrap(&content, &options);
if self.show_role_labels {
let label = format!("{}:", message.role.to_string().to_uppercase());
if let Some(first) = wrapped.first() {
lines.push(format!("{label} {first}"));
for line in wrapped.iter().skip(1) {
lines.push(format!("{:width$} {line}", "", width = label.len()));
}
} else {
lines.push(label);
}
} else {
for line in wrapped {
lines.push(line.into_owned());
}
}
lines
}
}