// Exemple d'utilisation du module download avec transformations use futures_util::StreamExt; use pmocache::download::{download_with_transformer, StreamTransformer}; use tokio::io::AsyncWriteExt; /// Exemple de transformer qui compresse les données en gzip /// /// Note: Cette fonction nécessite la dépendance `async-compression` /// Pour l'utiliser, ajoutez à Cargo.toml: /// ```toml /// [dev-dependencies] /// async-compression = { version = "0.4", features = ["tokio", "gzip"] } /// ``` #[allow(dead_code)] fn create_gzip_transformer() -> StreamTransformer { // Commenté car nécessite async-compression // Décommentez si vous ajoutez la dépendance unimplemented!("Cette fonction nécessite la dépendance async-compression") /* Box::new(|response, mut file, context| { Box::pin(async move { use async_compression::tokio::write::GzipEncoder; let mut encoder = GzipEncoder::new(&mut file); let mut stream = response.bytes_stream(); let mut total_written = 0u64; while let Some(chunk_result) = stream.next().await { let chunk = chunk_result.map_err(|e| format!("Failed to read chunk: {}", e))?; encoder .write_all(&chunk) .await .map_err(|e| format!("Failed to write compressed data: {}", e))?; total_written += chunk.len() as u64; context.report_progress(total_written); } encoder .shutdown() .await .map_err(|e| format!("Failed to finalize compression: {}", e))?; Ok(()) }) }) */ } /// Exemple de transformer qui convertit les données en majuscules (exemple simple) fn create_uppercase_transformer() -> StreamTransformer { Box::new(|input, mut file, context| { Box::pin(async move { let mut stream = input.into_byte_stream(); let mut total_written = 0u64; while let Some(chunk_result) = stream.next().await { let chunk = chunk_result?; // Transformer en majuscules (seulement pour texte ASCII) let transformed: Vec = chunk .iter() .map(|&b| { if b.is_ascii_lowercase() { b.to_ascii_uppercase() } else { b } }) .collect(); file.write_all(&transformed) .await .map_err(|e| format!("Failed to write: {}", e))?; total_written += transformed.len() as u64; context.report_progress(total_written); } file.flush() .await .map_err(|e| format!("Failed to flush: {}", e))?; Ok(()) }) }) } /// Exemple de transformer qui saute les N premiers bytes (utile pour enlever des headers) fn create_skip_header_transformer(skip_bytes: usize) -> StreamTransformer { Box::new(move |input, mut file, context| { Box::pin(async move { let mut stream = input.into_byte_stream(); let mut skipped = 0usize; let mut total_written = 0u64; while let Some(chunk_result) = stream.next().await { let chunk = chunk_result?; let to_write = if skipped < skip_bytes { let remaining_to_skip = skip_bytes - skipped; if chunk.len() <= remaining_to_skip { skipped += chunk.len(); continue; } else { skipped = skip_bytes; &chunk[remaining_to_skip..] } } else { &chunk[..] }; file.write_all(to_write) .await .map_err(|e| format!("Failed to write: {}", e))?; total_written += to_write.len() as u64; context.report_progress(total_written); } file.flush() .await .map_err(|e| format!("Failed to flush: {}", e))?; Ok(()) }) }) } /// Exemple de transformer qui compte les lignes et ajoute des numéros fn create_line_number_transformer() -> StreamTransformer { Box::new(|input, mut file, context| { Box::pin(async move { let mut stream = input.into_byte_stream(); let mut line_number = 1u32; let mut buffer = Vec::new(); let mut total_written = 0u64; while let Some(chunk_result) = stream.next().await { let chunk = chunk_result?; buffer.extend_from_slice(&chunk); // Traiter les lignes complètes dans le buffer while let Some(newline_pos) = buffer.iter().position(|&b| b == b'\n') { let line = &buffer[..newline_pos]; // Écrire le numéro de ligne et la ligne let numbered_line = format!("{:6}: ", line_number); file.write_all(numbered_line.as_bytes()) .await .map_err(|e| format!("Failed to write: {}", e))?; file.write_all(line) .await .map_err(|e| format!("Failed to write: {}", e))?; file.write_all(b"\n") .await .map_err(|e| format!("Failed to write: {}", e))?; total_written += numbered_line.len() as u64 + line.len() as u64 + 1; context.report_progress(total_written); line_number += 1; buffer.drain(..=newline_pos); } } // Traiter la dernière ligne si elle n'a pas de newline if !buffer.is_empty() { let numbered_line = format!("{:6}: ", line_number); file.write_all(numbered_line.as_bytes()) .await .map_err(|e| format!("Failed to write: {}", e))?; file.write_all(&buffer) .await .map_err(|e| format!("Failed to write: {}", e))?; total_written += numbered_line.len() as u64 + buffer.len() as u64; context.report_progress(total_written); } file.flush() .await .map_err(|e| format!("Failed to flush: {}", e))?; Ok(()) }) }) } #[tokio::main] async fn main() { println!("=== Exemples de transformers pour le module download ===\n"); let temp_dir = std::env::temp_dir(); // Exemple 1: Téléchargement avec transformation en majuscules println!("1. Téléchargement avec transformation en MAJUSCULES"); let uppercase_file = temp_dir.join("uppercase_example.txt"); let _ = std::fs::remove_file(&uppercase_file); let transformer = create_uppercase_transformer(); let dl = download_with_transformer( &uppercase_file, "https://www.rust-lang.org/", Some(transformer), ); println!(" Téléchargement démarré..."); match dl.wait_until_finished().await { Ok(_) => { println!(" ✓ Téléchargement terminé!"); println!(" - Taille source: {} bytes", dl.current_size().await); println!( " - Taille transformée: {} bytes", dl.transformed_size().await ); } Err(e) => { eprintln!(" ✗ Erreur: {}", e); } } // Exemple 2: Skip header println!("\n2. Téléchargement en sautant les 100 premiers bytes"); let skip_file = temp_dir.join("skip_header_example.txt"); let _ = std::fs::remove_file(&skip_file); let transformer = create_skip_header_transformer(100); let dl = download_with_transformer(&skip_file, "https://www.rust-lang.org/", Some(transformer)); match dl.wait_until_finished().await { Ok(_) => { println!(" ✓ Téléchargement terminé!"); println!( " - Taille transformée: {} bytes", dl.transformed_size().await ); } Err(e) => { eprintln!(" ✗ Erreur: {}", e); } } // Exemple 3: Numérotation des lignes println!("\n3. Téléchargement avec numérotation des lignes"); let numbered_file = temp_dir.join("numbered_example.txt"); let _ = std::fs::remove_file(&numbered_file); let transformer = create_line_number_transformer(); let dl = download_with_transformer( &numbered_file, "https://www.rust-lang.org/", Some(transformer), ); match dl.wait_until_finished().await { Ok(_) => { println!(" ✓ Téléchargement terminé!"); println!( " - Taille transformée: {} bytes", dl.transformed_size().await ); } Err(e) => { eprintln!(" ✗ Erreur: {}", e); } } println!("\n=== Exemples terminés ==="); println!("Fichiers créés dans: {:?}", temp_dir); // Note: Commenté car nécessite la dépendance async-compression /* println!("\n4. Téléchargement avec compression GZIP"); let gzip_file = temp_dir.join("compressed_example.gz"); let _ = std::fs::remove_file(&gzip_file); let transformer = create_gzip_transformer(); let dl = download_with_transformer( &gzip_file, "https://www.rust-lang.org/", Some(transformer), ); match dl.wait_until_finished().await { Ok(_) => { println!(" ✓ Téléchargement terminé!"); println!(" - Taille source: {} bytes", dl.current_size().await); println!(" - Taille compressée: {} bytes", dl.transformed_size().await); let ratio = 100.0 * dl.transformed_size().await as f64 / dl.current_size().await as f64; println!(" - Ratio de compression: {:.1}%", ratio); } Err(e) => { eprintln!(" ✗ Erreur: {}", e); } } */ }