11//! Send operations
22
33use anyhow:: Result ;
4- use indicatif:: { MultiProgress , ProgressBar , ProgressStyle } ;
54use p2p_core:: {
65 protocol:: { Capabilities , ConfigMessage } ,
76 session:: P2PSession ,
8- transfer_folder:: { FolderProgress , FolderTransferState } ,
7+ transfer_folder:: FolderTransferState ,
98 Uuid ,
109} ;
1110use std:: path:: { Path , PathBuf } ;
@@ -73,13 +72,13 @@ pub async fn handle_send(
7372 )
7473 . await ?;
7574
76- info ! ( " ✓ Session established" ) ;
75+ info ! ( "✅ Session established" ) ;
7776 info ! ( " Peer: {}" , session. peer_device_id( ) ) ;
7877 info ! ( " Capabilities: {:?}" , session. capabilities( ) ) ;
7978
8079 // Send file or folder with signal handling (unified)
8180 let result = tokio:: select! {
82- result = send( & mut session, & path, config, transfer_params. auto_reconnect , transfer_params . max_retries) => {
81+ result = send( & mut session, & path, config, transfer_params. max_retries) => {
8382 result
8483 }
8584 _ = signal:: ctrl_c( ) => {
@@ -89,11 +88,11 @@ pub async fn handle_send(
8988
9089 match result {
9190 Ok ( _) => {
92- info ! ( "\n ✅ Transfer complete!" ) ;
91+ info ! ( "✅ Transfer complete!" ) ;
9392 Ok ( ( ) )
9493 }
9594 Err ( e) => {
96- warn ! ( "\n ⚠️ Transfer interrupted: {}" , e) ;
95+ warn ! ( "⚠️ Transfer interrupted: {}" , e) ;
9796 info ! ( " State has been saved. Use 'p2p-transfer resume <transfer-id>' to continue" ) ;
9897 Err ( e)
9998 }
@@ -104,15 +103,14 @@ async fn send(
104103 session : & mut P2PSession ,
105104 path : & Path ,
106105 _config : ConfigMessage ,
107- auto_reconnect : bool ,
108106 max_retries : u32 ,
109107) -> Result < ( ) > {
110108 let base_name = path. file_name ( ) . unwrap ( ) . to_string_lossy ( ) . to_string ( ) ;
111109
112110 if path. is_file ( ) {
113- info ! ( "\n 📄 Sending file: {}" , base_name) ;
111+ info ! ( "📄 Sending file: {}" , base_name) ;
114112 } else {
115- info ! ( "\n 📁 Sending folder: {}" , base_name) ;
113+ info ! ( "📁 Sending folder: {}" , base_name) ;
116114 }
117115
118116 let config = session. config ( ) ;
@@ -125,15 +123,13 @@ async fn send(
125123 ) ;
126124 }
127125
128- if auto_reconnect {
129- info ! (
130- " Auto-reconnect enabled (max retries: {})" ,
131- if max_retries == 0 {
132- "∞" . to_string( )
133- } else {
134- max_retries. to_string( )
135- }
136- ) ;
126+ // Display reconnection behavior based on max_retries
127+ if max_retries == 0 {
128+ info ! ( " Auto-reconnect: enabled (unlimited retries)" ) ;
129+ } else if max_retries == 1 {
130+ info ! ( " Auto-reconnect: disabled (no retry)" ) ;
131+ } else {
132+ info ! ( " Auto-reconnect: enabled (max {} retries)" , max_retries) ;
137133 }
138134
139135 // Generate transfer ID for this operation
@@ -144,86 +140,45 @@ async fn send(
144140
145141 // Keep state in memory using Arc<Mutex> for thread-safe access
146142 let current_state = std:: sync:: Arc :: new ( std:: sync:: Mutex :: new ( None :: < FolderTransferState > ) ) ;
147-
148- // Create multi-progress for overall and per-file progress
149- let multi = MultiProgress :: new ( ) ;
150-
151- let overall_pb = multi. add ( ProgressBar :: new ( 100 ) ) ;
152- overall_pb. set_style (
153- ProgressStyle :: default_bar ( )
154- . template ( "[{elapsed_precise}] {bar:40.cyan/blue} {pos}/{len} files ({percent}%)" )
155- . unwrap ( )
156- . progress_chars ( "=>-" ) ,
157- ) ;
158-
159- let current_pb = multi. add ( ProgressBar :: new ( 100 ) ) ;
160- current_pb. set_style (
161- ProgressStyle :: default_bar ( )
162- . template ( " Current: {msg} {bar:40.green/yellow} {bytes}/{total_bytes} ({percent}%)" )
163- . unwrap ( )
164- . progress_chars ( "=>-" ) ,
165- ) ;
166-
167- // Create progress callback
168- let progress_callback = Box :: new ( move |progress : FolderProgress | {
169- // Update overall progress
170- overall_pb. set_length ( progress. total_files as u64 ) ;
171- overall_pb. set_position ( progress. completed_files as u64 ) ;
172-
173- // Update current file progress
174- if let Some ( file) = & progress. current_file {
175- current_pb. set_message ( file. clone ( ) ) ;
176- current_pb. set_position ( ( progress. current_file_progress * 100.0 ) as u64 ) ;
177- }
178-
179- // If all files complete, finish both bars
180- if progress. completed_files == progress. total_files {
181- overall_pb. finish_with_message ( "Complete!" ) ;
182- current_pb. finish_and_clear ( ) ;
143+ // Create progress state for unified progress tracking
144+ let mut progress = p2p_core:: progress:: ProgressState :: new ( 0 ) ;
145+
146+ // Create state callback to update in-memory state
147+ let current_state_for_callback = current_state. clone ( ) ;
148+ let state_callback = Box :: new ( move |state : & FolderTransferState | {
149+ if let Ok ( mut guard) = current_state_for_callback. lock ( ) {
150+ * guard = Some ( state. clone ( ) ) ;
183151 }
184152 } ) ;
185153
186- // Send file or folder with state tracking (unified method)
187- let result = if auto_reconnect {
188- // Create state callback to update in-memory state
189- let current_state_for_callback = current_state. clone ( ) ;
190- let state_callback = Box :: new ( move |state : & FolderTransferState | {
191- if let Ok ( mut guard) = current_state_for_callback. lock ( ) {
192- * guard = Some ( state. clone ( ) ) ;
193- }
194- } ) ;
195-
196- // Use auto-reconnect wrapper with in-memory state
197- let reconnect_config = p2p_core:: reconnect:: ReconnectConfig {
198- max_attempts : max_retries,
199- initial_backoff_secs : 3 ,
200- max_backoff_secs : 180 ,
201- exponential : true ,
202- } ;
154+ // Configure reconnection behavior
155+ let reconnect_config = p2p_core:: reconnect:: ReconnectConfig {
156+ max_attempts : max_retries,
157+ initial_backoff_secs : 3 ,
158+ max_backoff_secs : 180 ,
159+ exponential : true ,
160+ } ;
203161
204- // Create state provider closure that reads from in-memory state
205- let state_for_reconnect = current_state. clone ( ) ;
206- let state_provider = Box :: new ( move || {
207- state_for_reconnect
208- . lock ( )
209- . ok ( )
210- . and_then ( |guard| guard. clone ( ) )
211- } ) ;
162+ // Create state provider closure that reads from in-memory state
163+ let state_for_reconnect = current_state. clone ( ) ;
164+ let state_provider = Box :: new ( move || {
165+ state_for_reconnect
166+ . lock ( )
167+ . ok ( )
168+ . and_then ( |guard| guard. clone ( ) )
169+ } ) ;
212170
213- session
214- . send_path_with_reconnect (
215- path,
216- Some ( progress_callback) ,
217- Some ( state_callback) ,
218- & reconnect_config,
219- Some ( & state_file) ,
220- Some ( state_provider) ,
221- )
222- . await
223- } else {
224- // Regular send without auto-reconnect - state stays in memory only
225- session. send_path ( path, Some ( progress_callback) ) . await
226- } ;
171+ // Send file or folder with state tracking (unified method)
172+ let result = session
173+ . send_path (
174+ path,
175+ Some ( & mut progress) ,
176+ Some ( state_callback) ,
177+ & reconnect_config,
178+ Some ( & state_file) ,
179+ Some ( state_provider) ,
180+ )
181+ . await ;
227182
228183 match result {
229184 Ok ( _) => {
@@ -243,7 +198,7 @@ async fn send(
243198 let _ = tokio:: fs:: remove_file ( & state_file) . await ;
244199 }
245200 }
246- info ! ( " ✓ Transfer complete" ) ;
201+ info ! ( "✅ Transfer complete! " ) ;
247202 Ok ( ( ) )
248203 }
249204 Err ( e) => {
0 commit comments