@@ -42,13 +42,17 @@ impl Storage {
4242 }
4343 }
4444
45- pub async fn upload ( & self , bytes : Vec < u8 > ) -> Result < UploadResult , AppError > {
45+ pub async fn upload (
46+ & self ,
47+ bytes : Vec < u8 > ,
48+ original_filename : Option < & str > ,
49+ ) -> Result < UploadResult , AppError > {
4650 if bytes. len ( ) < 5 || & bytes[ ..5 ] != b"%PDF-" {
4751 return Err ( AppError :: BadRequest ( "Not a valid PDF file" . into ( ) ) ) ;
4852 }
4953
50- let id = Uuid :: new_v4 ( ) . to_string ( ) ;
51- let key = format ! ( "uploads/{id}.pdf" ) ;
54+ let hash = & Uuid :: new_v4 ( ) . to_string ( ) [ .. 8 ] ;
55+ let key = build_key ( original_filename , hash ) ;
5256
5357 self . client
5458 . put_object ( )
@@ -78,3 +82,66 @@ impl Storage {
7882 Ok ( UploadResult { presigned_url } )
7983 }
8084}
85+
86+ fn build_key ( original_filename : Option < & str > , hash : & str ) -> String {
87+ let stem_and_ext = original_filename
88+ . filter ( |name| !name. is_empty ( ) )
89+ . map ( |name| {
90+ let name = name
91+ . rsplit ( '/' )
92+ . next ( )
93+ . unwrap_or ( name)
94+ . rsplit ( '\\' )
95+ . next ( )
96+ . unwrap_or ( name) ;
97+
98+ match name. rsplit_once ( '.' ) {
99+ Some ( ( stem, ext) ) => ( stem. to_string ( ) , format ! ( ".{ext}" ) ) ,
100+ None => ( name. to_string ( ) , String :: new ( ) ) ,
101+ }
102+ } ) ;
103+
104+ match stem_and_ext {
105+ Some ( ( stem, ext) ) => format ! ( "uploads/{stem}-{hash}{ext}" ) ,
106+ None => format ! ( "uploads/{hash}.pdf" ) ,
107+ }
108+ }
109+
110+ #[ cfg( test) ]
111+ mod tests {
112+ use super :: * ;
113+
114+ #[ test]
115+ fn test_build_key_with_filename ( ) {
116+ assert_eq ! (
117+ build_key( Some ( "invoice.pdf" ) , "a1b2c3d4" ) ,
118+ "uploads/invoice-a1b2c3d4.pdf"
119+ ) ;
120+ }
121+
122+ #[ test]
123+ fn test_build_key_with_path ( ) {
124+ assert_eq ! (
125+ build_key( Some ( "path/to/report.pdf" ) , "a1b2c3d4" ) ,
126+ "uploads/report-a1b2c3d4.pdf"
127+ ) ;
128+ }
129+
130+ #[ test]
131+ fn test_build_key_without_extension ( ) {
132+ assert_eq ! (
133+ build_key( Some ( "document" ) , "a1b2c3d4" ) ,
134+ "uploads/document-a1b2c3d4"
135+ ) ;
136+ }
137+
138+ #[ test]
139+ fn test_build_key_none ( ) {
140+ assert_eq ! ( build_key( None , "a1b2c3d4" ) , "uploads/a1b2c3d4.pdf" ) ;
141+ }
142+
143+ #[ test]
144+ fn test_build_key_empty ( ) {
145+ assert_eq ! ( build_key( Some ( "" ) , "a1b2c3d4" ) , "uploads/a1b2c3d4.pdf" ) ;
146+ }
147+ }
0 commit comments