pub( super ) fn format_entry_content( entry : &claude_storage_core::Entry, max_length : Option< usize > ) -> String
{
use claude_storage_core::{ MessageContent, ContentBlock };
let timestamp = format_timestamp( &entry.timestamp );
let ( role, content ) = match &entry.message
{
MessageContent::User( msg ) =>
{
( "User", msg.content.clone() )
},
MessageContent::Assistant( msg ) =>
{
let text_blocks : Vec< String > = msg.content
.iter()
.filter_map( | block | match block
{
ContentBlock::Text { text } => Some( text.clone() ),
ContentBlock::Thinking { thinking, .. } =>
{
Some( format!( "[Thinking]\n{thinking}" ) )
},
ContentBlock::ToolUse { name, .. } =>
{
Some( format!( "[Using tool: {name}]" ) )
},
ContentBlock::ToolResult { is_error, content, .. } =>
{
if *is_error
{
Some( format!( "[Tool error: {content}]" ) )
}
else
{
None
}
},
})
.collect();
let combined = text_blocks.join( "\n\n" );
( "Assistant", combined )
}
};
let content = truncate_if_needed( &content, max_length );
format!( "[{timestamp}] {role}:\n{content}" )
}
pub( super ) fn format_timestamp( timestamp : &str ) -> String
{
if let Some( datetime_part ) = timestamp.split( '.' ).next()
{
if let Some( ( date, time ) ) = datetime_part.split_once( 'T' )
{
let time_short = time.split( ':' ).take( 2 ).collect::< Vec< _ > >().join( ":" );
return format!( "{date} {time_short}" );
}
}
timestamp.to_string()
}
#[ must_use ]
#[ inline ]
pub fn truncate_if_needed( text : &str, max_length : Option< usize > ) -> String
{
match max_length
{
None => text.to_string(),
Some( len ) if text.len() <= len => text.to_string(),
Some( len ) =>
{
let mut end = len;
while end > 0 && !text.is_char_boundary( end )
{
end -= 1;
}
let truncated = &text[ ..end ];
format!( "{}... [truncated, {} more bytes]", truncated, text.len() - end )
}
}
}