Private
Public Access
1
0

cargo fmt

This commit is contained in:
2025-06-06 16:39:31 -07:00
parent 8cd72d9417
commit 1d3b2f25ba
44 changed files with 758 additions and 505 deletions

View File

@@ -10,14 +10,12 @@ fn main() {
..Default::default()
};
let xml = std::fs::read_to_string(KORDOPHONE_XML)
.expect("Error reading server dbus interface");
let xml = std::fs::read_to_string(KORDOPHONE_XML).expect("Error reading server dbus interface");
let output = dbus_codegen::generate(&xml, &opts)
.expect("Error generating client dbus interface");
let output =
dbus_codegen::generate(&xml, &opts).expect("Error generating client dbus interface");
std::fs::write(out_path, output)
.expect("Error writing client dbus code");
std::fs::write(out_path, output).expect("Error writing client dbus code");
println!("cargo:rerun-if-changed={}", KORDOPHONE_XML);
}
}

View File

@@ -1,12 +1,12 @@
use kordophone::APIInterface;
use kordophone::api::http_client::HTTPAPIClient;
use kordophone::api::http_client::Credentials;
use kordophone::api::InMemoryAuthenticationStore;
use kordophone::api::event_socket::EventSocket;
use kordophone::api::http_client::Credentials;
use kordophone::api::http_client::HTTPAPIClient;
use kordophone::api::InMemoryAuthenticationStore;
use kordophone::APIInterface;
use crate::printers::{ConversationPrinter, MessagePrinter};
use anyhow::Result;
use clap::Subcommand;
use crate::printers::{ConversationPrinter, MessagePrinter};
use kordophone::model::event::EventData;
use kordophone::model::outgoing_message::OutgoingMessage;
@@ -16,18 +16,18 @@ pub fn make_api_client_from_env() -> HTTPAPIClient<InMemoryAuthenticationStore>
dotenv::dotenv().ok();
// read from env
let base_url = std::env::var("KORDOPHONE_API_URL")
.expect("KORDOPHONE_API_URL must be set");
let base_url = std::env::var("KORDOPHONE_API_URL").expect("KORDOPHONE_API_URL must be set");
let credentials = Credentials {
username: std::env::var("KORDOPHONE_USERNAME")
.expect("KORDOPHONE_USERNAME must be set"),
username: std::env::var("KORDOPHONE_USERNAME").expect("KORDOPHONE_USERNAME must be set"),
password: std::env::var("KORDOPHONE_PASSWORD")
.expect("KORDOPHONE_PASSWORD must be set"),
password: std::env::var("KORDOPHONE_PASSWORD").expect("KORDOPHONE_PASSWORD must be set"),
};
HTTPAPIClient::new(base_url.parse().unwrap(), InMemoryAuthenticationStore::new(Some(credentials)))
HTTPAPIClient::new(
base_url.parse().unwrap(),
InMemoryAuthenticationStore::new(Some(credentials)),
)
}
#[derive(Subcommand)]
@@ -36,9 +36,7 @@ pub enum Commands {
Conversations,
/// Prints all messages in a conversation.
Messages {
conversation_id: String,
},
Messages { conversation_id: String },
/// Prints the server Kordophone version.
Version,
@@ -65,7 +63,10 @@ impl Commands {
Commands::Messages { conversation_id } => client.print_messages(conversation_id).await,
Commands::RawUpdates => client.print_raw_updates().await,
Commands::Events => client.print_events().await,
Commands::SendMessage { conversation_id, message } => client.send_message(conversation_id, message).await,
Commands::SendMessage {
conversation_id,
message,
} => client.send_message(conversation_id, message).await,
}
}
}
@@ -96,7 +97,10 @@ impl ClientCli {
}
pub async fn print_messages(&mut self, conversation_id: String) -> Result<()> {
let messages = self.api.get_messages(&conversation_id, None, None, None).await?;
let messages = self
.api
.get_messages(&conversation_id, None, None, None)
.await?;
for message in messages {
println!("{}", MessagePrinter::new(&message.into()));
}
@@ -113,8 +117,11 @@ impl ClientCli {
println!("Conversation changed: {}", conversation.guid);
}
EventData::MessageReceived(conversation, message) => {
println!("Message received: msg: {} conversation: {}", message.guid, conversation.guid);
}
println!(
"Message received: msg: {} conversation: {}",
message.guid, conversation.guid
);
}
}
}
Ok(())
@@ -143,5 +150,3 @@ impl ClientCli {
Ok(())
}
}

View File

@@ -1,8 +1,8 @@
use crate::printers::{ConversationPrinter, MessagePrinter};
use anyhow::Result;
use clap::Subcommand;
use dbus::blocking::{Connection, Proxy};
use prettytable::table;
use crate::printers::{ConversationPrinter, MessagePrinter};
const DBUS_NAME: &str = "net.buzzert.kordophonecd";
const DBUS_PATH: &str = "/net/buzzert/kordophonecd/daemon";
@@ -21,9 +21,7 @@ pub enum Commands {
Conversations,
/// Runs a full sync operation for a conversation and its messages.
Sync {
conversation_id: Option<String>,
},
Sync { conversation_id: Option<String> },
/// Runs a sync operation for the conversation list.
SyncList,
@@ -31,7 +29,7 @@ pub enum Commands {
/// Prints the server Kordophone version.
Version,
/// Configuration options
/// Configuration options
Config {
#[command(subcommand)]
command: ConfigCommands,
@@ -62,14 +60,10 @@ pub enum ConfigCommands {
Print,
/// Sets the server URL.
SetServerUrl {
url: String,
},
SetServerUrl { url: String },
/// Sets the username.
SetUsername {
username: String,
},
SetUsername { username: String },
}
impl Commands {
@@ -82,9 +76,19 @@ impl Commands {
Commands::SyncList => client.sync_conversations_list().await,
Commands::Config { command } => client.config(command).await,
Commands::Signals => client.wait_for_signals().await,
Commands::Messages { conversation_id, last_message_id } => client.print_messages(conversation_id, last_message_id).await,
Commands::Messages {
conversation_id,
last_message_id,
} => {
client
.print_messages(conversation_id, last_message_id)
.await
}
Commands::DeleteAllConversations => client.delete_all_conversations().await,
Commands::SendMessage { conversation_id, text } => client.enqueue_outgoing_message(conversation_id, text).await,
Commands::SendMessage {
conversation_id,
text,
} => client.enqueue_outgoing_message(conversation_id, text).await,
}
}
}
@@ -96,12 +100,13 @@ struct DaemonCli {
impl DaemonCli {
pub fn new() -> Result<Self> {
Ok(Self {
conn: Connection::new_session()?
conn: Connection::new_session()?,
})
}
fn proxy(&self) -> Proxy<&Connection> {
self.conn.with_proxy(DBUS_NAME, DBUS_PATH, std::time::Duration::from_millis(5000))
self.conn
.with_proxy(DBUS_NAME, DBUS_PATH, std::time::Duration::from_millis(5000))
}
pub async fn print_version(&mut self) -> Result<()> {
@@ -117,7 +122,7 @@ impl DaemonCli {
for conversation in conversations {
println!("{}", ConversationPrinter::new(&conversation.into()));
}
Ok(())
}
@@ -136,8 +141,16 @@ impl DaemonCli {
.map_err(|e| anyhow::anyhow!("Failed to sync conversations: {}", e))
}
pub async fn print_messages(&mut self, conversation_id: String, last_message_id: Option<String>) -> Result<()> {
let messages = KordophoneRepository::get_messages(&self.proxy(), &conversation_id, &last_message_id.unwrap_or_default())?;
pub async fn print_messages(
&mut self,
conversation_id: String,
last_message_id: Option<String>,
) -> Result<()> {
let messages = KordophoneRepository::get_messages(
&self.proxy(),
&conversation_id,
&last_message_id.unwrap_or_default(),
)?;
println!("Number of messages: {}", messages.len());
for message in messages {
@@ -147,8 +160,13 @@ impl DaemonCli {
Ok(())
}
pub async fn enqueue_outgoing_message(&mut self, conversation_id: String, text: String) -> Result<()> {
let outgoing_message_id = KordophoneRepository::send_message(&self.proxy(), &conversation_id, &text)?;
pub async fn enqueue_outgoing_message(
&mut self,
conversation_id: String,
text: String,
) -> Result<()> {
let outgoing_message_id =
KordophoneRepository::send_message(&self.proxy(), &conversation_id, &text)?;
println!("Outgoing message ID: {}", outgoing_message_id);
Ok(())
}
@@ -159,10 +177,12 @@ impl DaemonCli {
pub use super::dbus_interface::NetBuzzertKordophoneRepositoryConversationsUpdated as ConversationsUpdated;
}
let _id = self.proxy().match_signal(|h: dbus_signals::ConversationsUpdated, _: &Connection, _: &Message| {
println!("Signal: Conversations updated");
true
});
let _id = self.proxy().match_signal(
|h: dbus_signals::ConversationsUpdated, _: &Connection, _: &Message| {
println!("Signal: Conversations updated");
true
},
);
println!("Waiting for signals...");
loop {
@@ -205,4 +225,4 @@ impl DaemonCli {
KordophoneRepository::delete_all_conversations(&self.proxy())
.map_err(|e| anyhow::anyhow!("Failed to delete all conversations: {}", e))
}
}
}

View File

@@ -3,34 +3,37 @@ use clap::Subcommand;
use kordophone::APIInterface;
use std::{env, path::PathBuf};
use crate::{
client,
printers::{ConversationPrinter, MessagePrinter},
};
use kordophone_db::database::{Database, DatabaseAccess};
use crate::{client, printers::{ConversationPrinter, MessagePrinter}};
#[derive(Subcommand)]
pub enum Commands {
/// For dealing with the table of cached conversations.
Conversations {
#[clap(subcommand)]
command: ConversationCommands
command: ConversationCommands,
},
/// For dealing with the table of cached messages.
Messages {
#[clap(subcommand)]
command: MessageCommands
command: MessageCommands,
},
/// For managing settings in the database.
Settings {
#[clap(subcommand)]
command: SettingsCommands
command: SettingsCommands,
},
}
#[derive(Subcommand)]
pub enum ConversationCommands {
/// Lists all conversations currently in the database.
List,
List,
/// Syncs with an API client.
Sync,
@@ -39,9 +42,7 @@ pub enum ConversationCommands {
#[derive(Subcommand)]
pub enum MessageCommands {
/// Prints all messages in a conversation.
List {
conversation_id: String
},
List { conversation_id: String },
}
#[derive(Subcommand)]
@@ -49,7 +50,7 @@ pub enum SettingsCommands {
/// Lists all settings or gets a specific setting.
Get {
/// The key to get. If not provided, all settings will be listed.
key: Option<String>
key: Option<String>,
},
/// Sets a setting value.
@@ -76,7 +77,9 @@ impl Commands {
ConversationCommands::Sync => db.sync_with_client().await,
},
Commands::Messages { command: cmd } => match cmd {
MessageCommands::List { conversation_id } => db.print_messages(&conversation_id).await,
MessageCommands::List { conversation_id } => {
db.print_messages(&conversation_id).await
}
},
Commands::Settings { command: cmd } => match cmd {
SettingsCommands::Get { key } => db.get_setting(key).await,
@@ -88,15 +91,17 @@ impl Commands {
}
struct DbClient {
database: Database
database: Database,
}
impl DbClient {
fn database_path() -> PathBuf {
env::var("KORDOPHONE_DB_PATH").unwrap_or_else(|_| {
let temp_dir = env::temp_dir();
temp_dir.join("kpcli_chat.db").to_str().unwrap().to_string()
}).into()
env::var("KORDOPHONE_DB_PATH")
.unwrap_or_else(|_| {
let temp_dir = env::temp_dir();
temp_dir.join("kpcli_chat.db").to_str().unwrap().to_string()
})
.into()
}
pub fn new() -> Result<Self> {
@@ -106,13 +111,14 @@ impl DbClient {
println!("kpcli: Using db at {}", path_str);
let db = Database::new(path_str)?;
Ok( Self { database: db })
Ok(Self { database: db })
}
pub async fn print_conversations(&mut self) -> Result<()> {
let all_conversations = self.database.with_repository(|repository| {
repository.all_conversations(i32::MAX, 0)
}).await?;
let all_conversations = self
.database
.with_repository(|repository| repository.all_conversations(i32::MAX, 0))
.await?;
println!("{} Conversations: ", all_conversations.len());
for conversation in all_conversations {
@@ -123,9 +129,10 @@ impl DbClient {
}
pub async fn print_messages(&mut self, conversation_id: &str) -> Result<()> {
let messages = self.database.with_repository(|repository| {
repository.get_messages_for_conversation(conversation_id)
}).await?;
let messages = self
.database
.with_repository(|repository| repository.get_messages_for_conversation(conversation_id))
.await?;
for message in messages {
println!("{}", MessagePrinter::new(&message.into()));
@@ -136,85 +143,97 @@ impl DbClient {
pub async fn sync_with_client(&mut self) -> Result<()> {
let mut client = client::make_api_client_from_env();
let fetched_conversations = client.get_conversations().await?;
let db_conversations: Vec<kordophone_db::models::Conversation> = fetched_conversations.into_iter()
let db_conversations: Vec<kordophone_db::models::Conversation> = fetched_conversations
.into_iter()
.map(kordophone_db::models::Conversation::from)
.collect();
// Process each conversation
for conversation in db_conversations {
let conversation_id = conversation.guid.clone();
// Insert the conversation
self.database.with_repository(|repository| {
repository.insert_conversation(conversation)
}).await?;
self.database
.with_repository(|repository| repository.insert_conversation(conversation))
.await?;
// Fetch and sync messages for this conversation
let messages = client.get_messages(&conversation_id, None, None, None).await?;
let db_messages: Vec<kordophone_db::models::Message> = messages.into_iter()
let messages = client
.get_messages(&conversation_id, None, None, None)
.await?;
let db_messages: Vec<kordophone_db::models::Message> = messages
.into_iter()
.map(kordophone_db::models::Message::from)
.collect();
// Insert each message
self.database.with_repository(|repository| -> Result<()> {
for message in db_messages {
repository.insert_message(&conversation_id, message)?;
}
self.database
.with_repository(|repository| -> Result<()> {
for message in db_messages {
repository.insert_message(&conversation_id, message)?;
}
Ok(())
}).await?;
Ok(())
})
.await?;
}
Ok(())
}
pub async fn get_setting(&mut self, key: Option<String>) -> Result<()> {
self.database.with_settings(|settings| {
match key {
Some(key) => {
// Get a specific setting
let value: Option<String> = settings.get(&key)?;
match value {
Some(v) => println!("{} = {}", key, v),
None => println!("Setting '{}' not found", key),
self.database
.with_settings(|settings| {
match key {
Some(key) => {
// Get a specific setting
let value: Option<String> = settings.get(&key)?;
match value {
Some(v) => println!("{} = {}", key, v),
None => println!("Setting '{}' not found", key),
}
}
},
None => {
// List all settings
let keys = settings.list_keys()?;
if keys.is_empty() {
println!("No settings found");
} else {
println!("Settings:");
for key in keys {
let value: Option<String> = settings.get(&key)?;
match value {
Some(v) => println!(" {} = {}", key, v),
None => println!(" {} = <error reading value>", key),
None => {
// List all settings
let keys = settings.list_keys()?;
if keys.is_empty() {
println!("No settings found");
} else {
println!("Settings:");
for key in keys {
let value: Option<String> = settings.get(&key)?;
match value {
Some(v) => println!(" {} = {}", key, v),
None => println!(" {} = <error reading value>", key),
}
}
}
}
}
}
Ok(())
}).await
Ok(())
})
.await
}
pub async fn put_setting(&mut self, key: String, value: String) -> Result<()> {
self.database.with_settings(|settings| {
settings.put(&key, &value)?;
Ok(())
}).await
self.database
.with_settings(|settings| {
settings.put(&key, &value)?;
Ok(())
})
.await
}
pub async fn delete_setting(&mut self, key: String) -> Result<()> {
self.database.with_settings(|settings| {
let count = settings.del(&key)?;
if count == 0 {
println!("Setting '{}' not found", key);
}
Ok(())
}).await
self.database
.with_settings(|settings| {
let count = settings.del(&key)?;
if count == 0 {
println!("Setting '{}' not found", key);
}
Ok(())
})
.await
}
}

View File

@@ -1,7 +1,7 @@
mod client;
mod daemon;
mod db;
mod printers;
mod daemon;
use anyhow::Result;
use clap::{Parser, Subcommand};
@@ -33,7 +33,7 @@ enum Commands {
Daemon {
#[command(subcommand)]
command: daemon::Commands,
}
},
}
async fn run_command(command: Commands) -> Result<()> {
@@ -50,7 +50,7 @@ fn initialize_logging() {
.map(|s| s.parse::<LevelFilter>().unwrap_or(LevelFilter::Info))
.unwrap_or(LevelFilter::Info);
env_logger::Builder::from_default_env()
env_logger::Builder::from_default_env()
.format_timestamp_secs()
.filter_level(log_level)
.init();
@@ -62,7 +62,8 @@ async fn main() {
let cli = Cli::parse();
run_command(cli.command).await
run_command(cli.command)
.await
.map_err(|e| println!("Error: {}", e))
.err();
}

View File

@@ -1,9 +1,9 @@
use std::fmt::Display;
use std::collections::HashMap;
use time::OffsetDateTime;
use pretty::RcDoc;
use dbus::arg::{self, RefArg};
use kordophone::model::message::AttachmentMetadata;
use pretty::RcDoc;
use std::collections::HashMap;
use std::fmt::Display;
use time::OffsetDateTime;
pub struct PrintableConversation {
pub guid: String,
@@ -17,7 +17,7 @@ pub struct PrintableConversation {
impl From<kordophone::model::Conversation> for PrintableConversation {
fn from(value: kordophone::model::Conversation) -> Self {
Self {
guid: value.guid,
guid: value.guid,
date: value.date,
unread_count: value.unread_count,
last_message_preview: value.last_message_preview,
@@ -34,7 +34,11 @@ impl From<kordophone_db::models::Conversation> for PrintableConversation {
date: OffsetDateTime::from_unix_timestamp(value.date.and_utc().timestamp()).unwrap(),
unread_count: value.unread_count.into(),
last_message_preview: value.last_message_preview,
participants: value.participants.into_iter().map(|p| p.display_name()).collect(),
participants: value
.participants
.into_iter()
.map(|p| p.display_name())
.collect(),
display_name: value.display_name,
}
}
@@ -44,17 +48,33 @@ impl From<arg::PropMap> for PrintableConversation {
fn from(value: arg::PropMap) -> Self {
Self {
guid: value.get("guid").unwrap().as_str().unwrap().to_string(),
date: OffsetDateTime::from_unix_timestamp(value.get("date").unwrap().as_i64().unwrap()).unwrap(),
unread_count: value.get("unread_count").unwrap().as_i64().unwrap().try_into().unwrap(),
last_message_preview: value.get("last_message_preview").unwrap().as_str().map(|s| s.to_string()),
participants: value.get("participants")
date: OffsetDateTime::from_unix_timestamp(value.get("date").unwrap().as_i64().unwrap())
.unwrap(),
unread_count: value
.get("unread_count")
.unwrap()
.as_i64()
.unwrap()
.try_into()
.unwrap(),
last_message_preview: value
.get("last_message_preview")
.unwrap()
.as_str()
.map(|s| s.to_string()),
participants: value
.get("participants")
.unwrap()
.0
.as_iter()
.unwrap()
.map(|s| s.as_str().unwrap().to_string())
.collect(),
display_name: value.get("display_name").unwrap().as_str().map(|s| s.to_string()),
display_name: value
.get("display_name")
.unwrap()
.as_str()
.map(|s| s.to_string()),
}
}
}
@@ -97,19 +117,22 @@ impl From<kordophone_db::models::Message> for PrintableMessage {
impl From<arg::PropMap> for PrintableMessage {
fn from(value: arg::PropMap) -> Self {
// Parse file transfer GUIDs from JSON if present
let file_transfer_guids = value.get("file_transfer_guids")
let file_transfer_guids = value
.get("file_transfer_guids")
.and_then(|v| v.as_str())
.and_then(|json_str| serde_json::from_str(json_str).ok())
.unwrap_or_default();
// Parse attachment metadata from JSON if present
let attachment_metadata = value.get("attachment_metadata")
let attachment_metadata = value
.get("attachment_metadata")
.and_then(|v| v.as_str())
.and_then(|json_str| serde_json::from_str(json_str).ok());
Self {
guid: value.get("id").unwrap().as_str().unwrap().to_string(),
date: OffsetDateTime::from_unix_timestamp(value.get("date").unwrap().as_i64().unwrap()).unwrap(),
date: OffsetDateTime::from_unix_timestamp(value.get("date").unwrap().as_i64().unwrap())
.unwrap(),
sender: value.get("sender").unwrap().as_str().unwrap().to_string(),
text: value.get("text").unwrap().as_str().unwrap().to_string(),
file_transfer_guids,
@@ -119,12 +142,13 @@ impl From<arg::PropMap> for PrintableMessage {
}
pub struct ConversationPrinter<'a> {
doc: RcDoc<'a, PrintableConversation>
doc: RcDoc<'a, PrintableConversation>,
}
impl<'a> ConversationPrinter<'a> {
pub fn new(conversation: &'a PrintableConversation) -> Self {
let preview = conversation.last_message_preview
let preview = conversation
.last_message_preview
.as_deref()
.unwrap_or("<null>")
.replace('\n', " ");
@@ -134,33 +158,31 @@ impl<'a> ConversationPrinter<'a> {
RcDoc::line()
.append("Display Name: ")
.append(conversation.display_name.as_deref().unwrap_or("<null>"))
.append(RcDoc::line())
.append(RcDoc::line())
.append("Date: ")
.append(conversation.date.to_string())
.append(RcDoc::line())
.append(RcDoc::line())
.append("Unread Count: ")
.append(conversation.unread_count.to_string())
.append(RcDoc::line())
.append(RcDoc::line())
.append("Participants: ")
.append("[")
.append(RcDoc::line()
.append(
conversation.participants
.iter()
.map(|name|
RcDoc::text(name)
.append(",")
.append(RcDoc::line())
)
.fold(RcDoc::nil(), |acc, x| acc.append(x))
)
.nest(4)
.append(
RcDoc::line()
.append(
conversation
.participants
.iter()
.map(|name| RcDoc::text(name).append(",").append(RcDoc::line()))
.fold(RcDoc::nil(), |acc, x| acc.append(x)),
)
.nest(4),
)
.append("]")
.append(RcDoc::line())
.append(RcDoc::line())
.append("Last Message Preview: ")
.append(preview)
.nest(4)
.nest(4),
)
.append(RcDoc::line())
.append(">");
@@ -176,7 +198,7 @@ impl Display for ConversationPrinter<'_> {
}
pub struct MessagePrinter<'a> {
doc: RcDoc<'a, PrintableMessage>
doc: RcDoc<'a, PrintableMessage>,
}
impl Display for MessagePrinter<'_> {
@@ -187,37 +209,40 @@ impl Display for MessagePrinter<'_> {
impl<'a> MessagePrinter<'a> {
pub fn new(message: &'a PrintableMessage) -> Self {
let mut doc = RcDoc::text(format!("<Message: \"{}\"", &message.guid))
.append(
RcDoc::line()
.append("Date: ")
.append(message.date.to_string())
let mut doc = RcDoc::text(format!("<Message: \"{}\"", &message.guid)).append(
RcDoc::line()
.append("Date: ")
.append(message.date.to_string())
.append(RcDoc::line())
.append("Sender: ")
.append(&message.sender)
.append("Sender: ")
.append(&message.sender)
.append(RcDoc::line())
.append("Body: ")
.append(&message.text)
.nest(4)
);
.append("Body: ")
.append(&message.text)
.nest(4),
);
// Add file transfer GUIDs and attachment metadata if present
if !message.file_transfer_guids.is_empty() {
doc = doc.append(RcDoc::line())
.append(
RcDoc::line()
.append("Attachments:")
.append(
message.file_transfer_guids.iter().map(|guid| {
let mut attachment_doc = RcDoc::line()
.append("- ")
.append(guid);
doc = doc.append(RcDoc::line()).append(
RcDoc::line()
.append("Attachments:")
.append(
message
.file_transfer_guids
.iter()
.map(|guid| {
let mut attachment_doc = RcDoc::line().append("- ").append(guid);
// Add metadata if available for this GUID
if let Some(ref metadata) = message.attachment_metadata {
if let Some(attachment_meta) = metadata.get(guid) {
if let Some(ref attribution) = attachment_meta.attribution_info {
if let (Some(width), Some(height)) = (attribution.width, attribution.height) {
if let Some(ref attribution) =
attachment_meta.attribution_info
{
if let (Some(width), Some(height)) =
(attribution.width, attribution.height)
{
attachment_doc = attachment_doc
.append(RcDoc::line())
.append(" Dimensions: ")
@@ -231,14 +256,14 @@ impl<'a> MessagePrinter<'a> {
attachment_doc
})
.fold(RcDoc::nil(), |acc, x| acc.append(x))
)
.nest(4)
);
.fold(RcDoc::nil(), |acc, x| acc.append(x)),
)
.nest(4),
);
}
doc = doc.append(RcDoc::line()).append(">");
MessagePrinter { doc }
}
}
}