@@ -399,6 +399,22 @@ impl Context {
399399 message = message. model ( model) ;
400400 }
401401
402+ message. into ( )
403+ }
404+ AttachmentContent :: DirectoryListing { entries } => {
405+ let elm = Element :: new ( "directory_listing" )
406+ . attr ( "path" , attachment. path )
407+ . append ( entries. into_iter ( ) . map ( |entry| {
408+ let tag_name = if entry. is_dir { "dir" } else { "file" } ;
409+ Element :: new ( tag_name) . text ( entry. path )
410+ } ) ) ;
411+
412+ let mut message = TextMessage :: new ( Role :: User , elm. to_string ( ) ) . droppable ( true ) ;
413+
414+ if let Some ( model) = model_id. clone ( ) {
415+ message = message. model ( model) ;
416+ }
417+
402418 message. into ( )
403419 }
404420 } )
@@ -586,8 +602,8 @@ mod tests {
586602 use pretty_assertions:: assert_eq;
587603
588604 use super :: * ;
589- use crate :: estimate_token_count;
590605 use crate :: transformer:: Transformer ;
606+ use crate :: { DirectoryEntry , estimate_token_count} ;
591607
592608 #[ test]
593609 fn test_override_system_message ( ) {
@@ -1008,4 +1024,86 @@ mod tests {
10081024 ) ;
10091025 }
10101026 }
1027+
1028+ #[ test]
1029+ fn test_add_attachments_directory_listing ( ) {
1030+ let fixture_attachments = vec ! [ Attachment {
1031+ path: "/test/mydir" . to_string( ) ,
1032+ content: AttachmentContent :: DirectoryListing {
1033+ entries: vec![
1034+ DirectoryEntry { path: "/test/mydir/file1.txt" . to_string( ) , is_dir: false } ,
1035+ DirectoryEntry { path: "/test/mydir/file2.rs" . to_string( ) , is_dir: false } ,
1036+ DirectoryEntry { path: "/test/mydir/subdir" . to_string( ) , is_dir: true } ,
1037+ ] ,
1038+ } ,
1039+ } ] ;
1040+
1041+ let actual = Context :: default ( ) . add_attachments ( fixture_attachments, None ) ;
1042+
1043+ // Verify message was added
1044+ assert_eq ! ( actual. messages. len( ) , 1 ) ;
1045+
1046+ // Verify directory listing is formatted correctly as XML
1047+ let message = actual. messages . first ( ) . unwrap ( ) ;
1048+ assert ! (
1049+ message. is_droppable( ) ,
1050+ "Directory listing should be marked as droppable"
1051+ ) ;
1052+
1053+ let text = message. to_text ( ) ;
1054+ // The XML is encoded within the message content
1055+ assert ! ( text. contains( "<directory_listing" ) ) ;
1056+ // Check that files use <file> tag
1057+ assert ! ( text. contains( "<file>" ) ) ;
1058+ // Check that directories use <dir> tag
1059+ assert ! ( text. contains( "<dir>" ) ) ;
1060+ }
1061+
1062+ #[ test]
1063+ fn test_directory_listing_sorted_dirs_first ( ) {
1064+ // Create entries already sorted (as they would come from attachment service)
1065+ // Directories first, then files, all sorted alphabetically
1066+ let fixture_attachments = vec ! [ Attachment {
1067+ path: "/test/root" . to_string( ) ,
1068+ content: AttachmentContent :: DirectoryListing {
1069+ entries: vec![
1070+ DirectoryEntry { path: "apple_dir" . to_string( ) , is_dir: true } ,
1071+ DirectoryEntry { path: "berry_dir" . to_string( ) , is_dir: true } ,
1072+ DirectoryEntry { path: "zoo_dir" . to_string( ) , is_dir: true } ,
1073+ DirectoryEntry { path: "banana.txt" . to_string( ) , is_dir: false } ,
1074+ DirectoryEntry { path: "cherry.txt" . to_string( ) , is_dir: false } ,
1075+ DirectoryEntry { path: "zebra.txt" . to_string( ) , is_dir: false } ,
1076+ ] ,
1077+ } ,
1078+ } ] ;
1079+
1080+ let actual = Context :: default ( ) . add_attachments ( fixture_attachments, None ) ;
1081+ let text = actual. messages . first ( ) . unwrap ( ) . to_text ( ) ;
1082+
1083+ // Extract the order of entries from the XML
1084+ let dir_entries: Vec < & str > = text
1085+ . split ( "<" )
1086+ . filter ( |s| s. starts_with ( "dir>" ) || s. starts_with ( "file>" ) )
1087+ . collect ( ) ;
1088+
1089+ // Verify directories come first, then files, all sorted alphabetically
1090+ let expected_order = [
1091+ "dir>apple_dir" ,
1092+ "dir>berry_dir" ,
1093+ "dir>zoo_dir" ,
1094+ "file>banana.txt" ,
1095+ "file>cherry.txt" ,
1096+ "file>zebra.txt" ,
1097+ ] ;
1098+
1099+ for ( i, expected) in expected_order. iter ( ) . enumerate ( ) {
1100+ assert ! (
1101+ dir_entries[ i] . starts_with( expected) ,
1102+ "Expected entry {} to start with '{}', but got '{}'" ,
1103+ i,
1104+ expected,
1105+ dir_entries[ i]
1106+ ) ;
1107+ }
1108+ }
10111109}
0 commit comments