@@ -15,37 +15,89 @@ pub struct FileEntry {
1515 pub size : u64 ,
1616}
1717
18- pub fn generate_output ( entries : & [ FileEntry ] , format : OutputFormat ) -> Result < String > {
18+ pub fn generate_output ( entries : & [ FileEntry ] , format : OutputFormat , xml_format : bool , project_name : Option < String > ) -> Result < String > {
1919 let mut output = String :: new ( ) ;
2020
21+ if xml_format {
22+ let project_name = project_name. unwrap_or_else ( || "project" . to_string ( ) ) ;
23+ output. push_str ( & format ! ( "<context name=\" {}\" >\n " , xml_escape( & project_name) ) ) ;
24+ }
25+
2126 match format {
2227 OutputFormat :: Tree => {
23- output. push_str ( "Directory Structure:\n " ) ;
28+ if xml_format {
29+ output. push_str ( "<tree>\n " ) ;
30+ } else {
31+ output. push_str ( "Directory Structure:\n " ) ;
32+ }
2433 output. push_str ( & generate_tree ( entries) ?) ;
34+ if xml_format {
35+ output. push_str ( "</tree>\n " ) ;
36+ }
2537 }
2638 OutputFormat :: Files => {
27- output. push_str ( "File Contents:\n " ) ;
28- output. push_str ( & generate_files ( entries) ?) ;
39+ if xml_format {
40+ output. push_str ( "<files>\n " ) ;
41+ } else {
42+ output. push_str ( "File Contents:\n " ) ;
43+ }
44+ output. push_str ( & generate_files ( entries, xml_format) ?) ;
45+ if xml_format {
46+ output. push_str ( "</files>\n " ) ;
47+ }
2948 }
3049 OutputFormat :: Both => {
31- output. push_str ( "Directory Structure:\n " ) ;
50+ if xml_format {
51+ output. push_str ( "<tree>\n " ) ;
52+ } else {
53+ output. push_str ( "Directory Structure:\n " ) ;
54+ }
3255 output. push_str ( & generate_tree ( entries) ?) ;
33- output. push_str ( "\n File Contents:\n " ) ;
34- output. push_str ( & generate_files ( entries) ?) ;
56+ if xml_format {
57+ output. push_str ( "</tree>\n \n <files>\n " ) ;
58+ } else {
59+ output. push_str ( "\n File Contents:\n " ) ;
60+ }
61+ output. push_str ( & generate_files ( entries, xml_format) ?) ;
62+ if xml_format {
63+ output. push_str ( "</files>\n " ) ;
64+ }
3565 }
3666 }
3767
3868 // Add summary
39- output. push_str ( "\n Summary:\n " ) ;
40- output. push_str ( & format ! ( "Total files: {}\n " , entries. len( ) ) ) ;
41- output. push_str ( & format ! (
42- "Total size: {} bytes\n " ,
43- entries. iter( ) . map( |e| e. size) . sum:: <u64 >( )
44- ) ) ;
69+ if xml_format {
70+ output. push_str ( "<summary>\n " ) ;
71+ output. push_str ( & format ! ( "Total files: {}\n " , entries. len( ) ) ) ;
72+ output. push_str ( & format ! (
73+ "Total size: {} bytes\n " ,
74+ entries. iter( ) . map( |e| e. size) . sum:: <u64 >( )
75+ ) ) ;
76+ output. push_str ( "</summary>\n " ) ;
77+ } else {
78+ output. push_str ( "\n Summary:\n " ) ;
79+ output. push_str ( & format ! ( "Total files: {}\n " , entries. len( ) ) ) ;
80+ output. push_str ( & format ! (
81+ "Total size: {} bytes\n " ,
82+ entries. iter( ) . map( |e| e. size) . sum:: <u64 >( )
83+ ) ) ;
84+ }
85+
86+ if xml_format {
87+ output. push_str ( "</context>" ) ;
88+ }
4589
4690 Ok ( output)
4791}
4892
93+ fn xml_escape ( text : & str ) -> String {
94+ text. replace ( '&' , "&" )
95+ . replace ( '<' , "<" )
96+ . replace ( '>' , ">" )
97+ . replace ( '"' , """ )
98+ . replace ( '\'' , "'" )
99+ }
100+
49101pub fn display_token_counts ( token_counter : TokenCounter , entries : & [ FileEntry ] ) -> Result < ( ) > {
50102 let token_count = token_counter. count_files ( entries) ?;
51103
@@ -117,15 +169,24 @@ fn generate_tree(entries: &[FileEntry]) -> Result<String> {
117169 Ok ( output)
118170}
119171
120- fn generate_files ( entries : & [ FileEntry ] ) -> Result < String > {
172+ fn generate_files ( entries : & [ FileEntry ] , xml_format : bool ) -> Result < String > {
121173 let mut output = String :: new ( ) ;
122174
123175 for entry in entries {
124- output. push_str ( & format ! ( "\n File: {}\n " , entry. path. display( ) ) ) ;
125- output. push_str ( & "=" . repeat ( 48 ) ) ;
126- output. push ( '\n' ) ;
127- output. push_str ( & entry. content ) ;
128- output. push ( '\n' ) ;
176+ if xml_format {
177+ output. push_str ( & format ! ( "<file path=\" {}\" >\n " , xml_escape( entry. path. display( ) . to_string( ) . as_str( ) ) ) ) ;
178+ output. push_str ( & "=" . repeat ( 48 ) ) ;
179+ output. push ( '\n' ) ;
180+ output. push_str ( & entry. content ) ;
181+ output. push ( '\n' ) ;
182+ output. push_str ( "</file>\n " ) ;
183+ } else {
184+ output. push_str ( & format ! ( "\n File: {}\n " , entry. path. display( ) ) ) ;
185+ output. push_str ( & "=" . repeat ( 48 ) ) ;
186+ output. push ( '\n' ) ;
187+ output. push_str ( & entry. content ) ;
188+ output. push ( '\n' ) ;
189+ }
129190 }
130191
131192 Ok ( output)
@@ -280,7 +341,7 @@ mod tests {
280341 #[ test]
281342 fn test_files_output ( ) {
282343 let entries = create_test_entries ( ) ;
283- let files = generate_files ( & entries) . unwrap ( ) ;
344+ let files = generate_files ( & entries, false ) . unwrap ( ) ;
284345 let expected = format ! (
285346 "\n File: {}\n {}\n {}\n \n File: {}\n {}\n {}\n " ,
286347 "src/main.rs" ,
@@ -298,23 +359,55 @@ mod tests {
298359 let entries = create_test_entries ( ) ;
299360
300361 // Test tree format
301- let tree_output = generate_output ( & entries, OutputFormat :: Tree ) . unwrap ( ) ;
362+ let tree_output = generate_output ( & entries, OutputFormat :: Tree , false , None ) . unwrap ( ) ;
302363 assert ! ( tree_output. contains( "Directory Structure:" ) ) ;
303364 assert ! ( tree_output. contains( "src/" ) ) ;
304365 assert ! ( tree_output. contains( "main.rs" ) ) ;
305366
306367 // Test files format
307- let files_output = generate_output ( & entries, OutputFormat :: Files ) . unwrap ( ) ;
368+ let files_output = generate_output ( & entries, OutputFormat :: Files , false , None ) . unwrap ( ) ;
308369 assert ! ( files_output. contains( "File Contents:" ) ) ;
309370 assert ! ( files_output. contains( "fn main()" ) ) ;
310371 assert ! ( files_output. contains( "pub fn helper()" ) ) ;
311372
312373 // Test both format
313- let both_output = generate_output ( & entries, OutputFormat :: Both ) . unwrap ( ) ;
374+ let both_output = generate_output ( & entries, OutputFormat :: Both , false , None ) . unwrap ( ) ;
314375 assert ! ( both_output. contains( "Directory Structure:" ) ) ;
315376 assert ! ( both_output. contains( "File Contents:" ) ) ;
316377 }
317378
379+ #[ test]
380+ fn test_xml_output ( ) {
381+ let entries = create_test_entries ( ) ;
382+
383+ // Test XML tree format
384+ let xml_tree_output = generate_output ( & entries, OutputFormat :: Tree , true , Some ( "test_project" . to_string ( ) ) ) . unwrap ( ) ;
385+ assert ! ( xml_tree_output. contains( "<context name=\" test_project\" >" ) ) ;
386+ assert ! ( xml_tree_output. contains( "<tree>" ) ) ;
387+ assert ! ( xml_tree_output. contains( "</tree>" ) ) ;
388+ assert ! ( xml_tree_output. contains( "<summary>" ) ) ;
389+ assert ! ( xml_tree_output. contains( "</summary>" ) ) ;
390+ assert ! ( xml_tree_output. contains( "</context>" ) ) ;
391+
392+ // Test XML files format
393+ let xml_files_output = generate_output ( & entries, OutputFormat :: Files , true , Some ( "test_project" . to_string ( ) ) ) . unwrap ( ) ;
394+ assert ! ( xml_files_output. contains( "<context name=\" test_project\" >" ) ) ;
395+ assert ! ( xml_files_output. contains( "<files>" ) ) ;
396+ assert ! ( xml_files_output. contains( "<file path=\" src/main.rs\" >" ) ) ;
397+ assert ! ( xml_files_output. contains( "</file>" ) ) ;
398+ assert ! ( xml_files_output. contains( "</files>" ) ) ;
399+ assert ! ( xml_files_output. contains( "</context>" ) ) ;
400+
401+ // Test XML both format
402+ let xml_both_output = generate_output ( & entries, OutputFormat :: Both , true , Some ( "test_project" . to_string ( ) ) ) . unwrap ( ) ;
403+ assert ! ( xml_both_output. contains( "<context name=\" test_project\" >" ) ) ;
404+ assert ! ( xml_both_output. contains( "<tree>" ) ) ;
405+ assert ! ( xml_both_output. contains( "</tree>" ) ) ;
406+ assert ! ( xml_both_output. contains( "<files>" ) ) ;
407+ assert ! ( xml_both_output. contains( "</files>" ) ) ;
408+ assert ! ( xml_both_output. contains( "</context>" ) ) ;
409+ }
410+
318411 #[ test]
319412 fn test_handle_output ( ) {
320413 use tempfile:: tempdir;
@@ -345,6 +438,7 @@ mod tests {
345438 traverse_links : false ,
346439 link_depth : None ,
347440 config_path : false ,
441+ xml : false ,
348442 } ;
349443
350444 handle_output ( content. clone ( ) , & args) . unwrap ( ) ;
0 commit comments