@@ -6,7 +6,7 @@ use clap::Args;
66use rc_core:: { AliasManager , ObjectStore as _, RemotePath } ;
77use rc_s3:: S3Client ;
88use serde:: Serialize ;
9- use std:: collections:: BTreeMap ;
9+ use std:: collections:: { BTreeMap , HashMap } ;
1010
1111use crate :: exit_code:: ExitCode ;
1212use crate :: output:: { Formatter , OutputConfig } ;
@@ -55,6 +55,31 @@ fn metadata_is_none_or_empty(metadata: &Option<BTreeMap<String, String>>) -> boo
5555 }
5656}
5757
58+ fn normalize_metadata (
59+ content_type : Option < & str > ,
60+ metadata : Option < & HashMap < String , String > > ,
61+ ) -> Option < BTreeMap < String , String > > {
62+ let mut out = BTreeMap :: new ( ) ;
63+
64+ if let Some ( ct) = content_type
65+ && !ct. is_empty ( )
66+ {
67+ out. insert ( "Content-Type" . to_string ( ) , ct. to_string ( ) ) ;
68+ }
69+
70+ if let Some ( meta) = metadata {
71+ let mut sorted: BTreeMap < _ , _ > = meta. iter ( ) . collect ( ) ;
72+ for ( key, value) in & mut sorted {
73+ out. insert (
74+ format ! ( "X-Amz-Meta-{}" , capitalize_meta_key( key) ) ,
75+ ( * value) . clone ( ) ,
76+ ) ;
77+ }
78+ }
79+
80+ if out. is_empty ( ) { None } else { Some ( out) }
81+ }
82+
5883/// Execute the stat command
5984pub async fn execute ( args : StatArgs , output_config : OutputConfig ) -> ExitCode {
6085 let formatter = Formatter :: new ( output_config) ;
@@ -109,15 +134,10 @@ pub async fn execute(args: StatArgs, output_config: OutputConfig) -> ExitCode {
109134 content_type : info. content_type . clone ( ) ,
110135 storage_class : info. storage_class . clone ( ) ,
111136 version_id : args. version_id ,
112- metadata : info
113- . metadata
114- . as_ref ( )
115- . map ( |m| {
116- m. iter ( )
117- . map ( |( k, v) | ( k. clone ( ) , v. clone ( ) ) )
118- . collect :: < BTreeMap < _ , _ > > ( )
119- } )
120- . filter ( |m| !m. is_empty ( ) ) ,
137+ metadata : normalize_metadata (
138+ info. content_type . as_deref ( ) ,
139+ info. metadata . as_ref ( ) ,
140+ ) ,
121141 } ;
122142 formatter. json ( & output) ;
123143 } else {
@@ -153,13 +173,12 @@ pub async fn execute(args: StatArgs, output_config: OutputConfig) -> ExitCode {
153173 if let Some ( sc) = & info. storage_class {
154174 formatter. println ( & format_kv ( "Class" , sc) ) ;
155175 }
156- if let Some ( metadata) = & info. metadata {
157- let sorted: BTreeMap < _ , _ > = metadata. iter ( ) . collect ( ) ;
158- for ( key, value) in & sorted {
159- formatter. println ( & format_kv (
160- & format ! ( "X-Amz-Meta-{}" , capitalize_meta_key( key) ) ,
161- value,
162- ) ) ;
176+ if let Some ( metadata) =
177+ normalize_metadata ( info. content_type . as_deref ( ) , info. metadata . as_ref ( ) )
178+ {
179+ formatter. println ( & format_kv ( "Metadata" , "" ) ) ;
180+ for ( key, value) in & metadata {
181+ formatter. println ( & format_kv ( key, value) ) ;
163182 }
164183 }
165184 }
@@ -327,4 +346,38 @@ mod tests {
327346 // Empty BTreeMap is treated as None via skip_serializing_if helper
328347 assert ! ( !json. contains( "metadata" ) ) ;
329348 }
349+
350+ #[ test]
351+ fn test_normalize_metadata_includes_content_type ( ) {
352+ let metadata = normalize_metadata ( Some ( "text/plain" ) , None ) . expect ( "metadata present" ) ;
353+ assert_eq ! (
354+ metadata. get( "Content-Type" ) . map( String :: as_str) ,
355+ Some ( "text/plain" )
356+ ) ;
357+ }
358+
359+ #[ test]
360+ fn test_normalize_metadata_includes_custom_metadata ( ) {
361+ let mut custom = HashMap :: new ( ) ;
362+ custom. insert ( "content-disposition" . to_string ( ) , "attachment" . to_string ( ) ) ;
363+ custom. insert ( "x-custom-key" . to_string ( ) , "value" . to_string ( ) ) ;
364+
365+ let metadata =
366+ normalize_metadata ( Some ( "text/plain" ) , Some ( & custom) ) . expect ( "metadata present" ) ;
367+
368+ assert_eq ! (
369+ metadata. get( "Content-Type" ) . map( String :: as_str) ,
370+ Some ( "text/plain" )
371+ ) ;
372+ assert_eq ! (
373+ metadata
374+ . get( "X-Amz-Meta-Content-Disposition" )
375+ . map( String :: as_str) ,
376+ Some ( "attachment" )
377+ ) ;
378+ assert_eq ! (
379+ metadata. get( "X-Amz-Meta-X-Custom-Key" ) . map( String :: as_str) ,
380+ Some ( "value" )
381+ ) ;
382+ }
330383}
0 commit comments