// rust/01-stream-processing.rs/*Stream processing patterns and best practices*/usestd::collections::HashMap;usestd::sync::Arc;usetokio::sync::mpsc;usetokio::time::{Duration,Instant};useserde::{Deserialize,Serialize};/// Data stream processor.pubstructStreamProcessor{input_channel:mpsc::Receiver<DataRecord>,output_channel:mpsc::Sender<ProcessedRecord>,buffer_size:usize,processing_timeout:Duration,}#[derive(Debug, Clone, Serialize, Deserialize)]pubstructDataRecord{pubid:String,pubtimestamp:Instant,pubdata:HashMap<String,serde_json::Value>,pubsource:String,}#[derive(Debug, Clone, Serialize, Deserialize)]pubstructProcessedRecord{pubid:String,pubtimestamp:Instant,pubprocessed_data:HashMap<String,serde_json::Value>,pubprocessing_time:Duration,pubsource:String,}implStreamProcessor{pubfnnew(input_channel:mpsc::Receiver<DataRecord>,output_channel:mpsc::Sender<ProcessedRecord>,buffer_size:usize,processing_timeout:Duration,)->Self{Self{input_channel,output_channel,buffer_size,processing_timeout,}}/// Process data stream.pubasyncfnprocess_stream(&mutself)->Result<(),String>{letmutbuffer=Vec::with_capacity(self.buffer_size);loop{// Try to receive data with timeoutmatchtokio::time::timeout(self.processing_timeout,self.input_channel.recv()).await{Ok(Some(record))=>{buffer.push(record);// Process buffer when it's fullifbuffer.len()>=self.buffer_size{self.process_batch(&mutbuffer).await?;}}Ok(None)=>{// Input channel closed, process remaining bufferif!buffer.is_empty(){self.process_batch(&mutbuffer).await?;}break;}Err(_)=>{// Timeout, process current bufferif!buffer.is_empty(){self.process_batch(&mutbuffer).await?;}}}}Ok(())}/// Process a batch of records.asyncfnprocess_batch(&mutself,buffer:&mutVec<DataRecord>)->Result<(),String>{letstart_time=Instant::now();// Process each record in the batchforrecordinbuffer.drain(..){letprocessed=self.process_record(record).await?;self.output_channel.send(processed).await.map_err(|e|format!("Failed to send processed record: {}",e))?;}letprocessing_time=start_time.elapsed();println!("Processed batch in {:?}",processing_time);Ok(())}/// Process a single record.asyncfnprocess_record(&self,record:DataRecord)->Result<ProcessedRecord,String>{letstart_time=Instant::now();// Simulate data processingletmutprocessed_data=HashMap::new();for(key,value)inrecord.data{// Transform the datalettransformed_value=self.transform_value(&key,&value)?;processed_data.insert(key,transformed_value);}letprocessing_time=start_time.elapsed();Ok(ProcessedRecord{id:record.id,timestamp:record.timestamp,processed_data,processing_time,source:record.source,})}/// Transform a value based on its key.fntransform_value(&self,key:&str,value:&serde_json::Value)->Result<serde_json::Value,String>{matchkey{"temperature"=>{ifletSome(temp)=value.as_f64(){// Convert Celsius to Fahrenheitletfahrenheit=(temp*9.0/5.0)+32.0;Ok(serde_json::Value::Number(serde_json::Number::from_f64(fahrenheit).unwrap()))}else{Err("Invalid temperature value".to_string())}}"pressure"=>{ifletSome(pressure)=value.as_f64(){// Convert Pa to PSIletpsi=pressure*0.000145038;Ok(serde_json::Value::Number(serde_json::Number::from_f64(psi).unwrap()))}else{Err("Invalid pressure value".to_string())}}_=>Ok(value.clone()),}}}/// Data pipeline orchestrator.pubstructDataPipeline{processors:Vec<Arc<StreamProcessor>>,input_channels:Vec<mpsc::Sender<DataRecord>>,output_channels:Vec<mpsc::Receiver<ProcessedRecord>>,}implDataPipeline{pubfnnew()->Self{Self{processors:Vec::new(),input_channels:Vec::new(),output_channels:Vec::new(),}}/// Add a processor to the pipeline.pubfnadd_processor(&mutself,buffer_size:usize,processing_timeout:Duration)->(mpsc::Sender<DataRecord>,mpsc::Receiver<ProcessedRecord>){let(input_tx,input_rx)=mpsc::channel(buffer_size);let(output_tx,output_rx)=mpsc::channel(buffer_size);letprocessor=Arc::new(StreamProcessor::new(input_rx,output_tx,buffer_size,processing_timeout,));self.processors.push(processor);self.input_channels.push(input_tx);self.output_channels.push(output_rx);(self.input_channels.last().unwrap().clone(),output_rx)}/// Start all processors.pubasyncfnstart(&self)->Result<(),String>{letmuthandles=Vec::new();forprocessorin&self.processors{letprocessor=Arc::clone(processor);lethandle=tokio::spawn(asyncmove{letmutprocessor=(*processor).clone();processor.process_stream().await});handles.push(handle);}// Wait for all processors to completeforhandleinhandles{handle.await.map_err(|e|format!("Processor error: {}",e))??;}Ok(())}}#[cfg(test)]modtests{usesuper::*;#[tokio::test]asyncfntest_stream_processor(){let(input_tx,input_rx)=mpsc::channel(10);let(output_tx,output_rx)=mpsc::channel(10);letmutprocessor=StreamProcessor::new(input_rx,output_tx,5,Duration::from_secs(1),);// Send test dataletmutdata=HashMap::new();data.insert("temperature".to_string(),serde_json::Value::Number(serde_json::Number::from(25)));data.insert("pressure".to_string(),serde_json::Value::Number(serde_json::Number::from(101325)));letrecord=DataRecord{id:"test-1".to_string(),timestamp:Instant::now(),data,source:"sensor-1".to_string(),};input_tx.send(record).await.unwrap();drop(input_tx);// Start processinglethandle=tokio::spawn(asyncmove{processor.process_stream().await});// Wait for processing to completehandle.await.unwrap().unwrap();// Check outputletprocessed=output_rx.recv().await.unwrap();assert_eq!(processed.id,"test-1");}#[tokio::test]asyncfntest_data_pipeline(){letmutpipeline=DataPipeline::new();let(input_tx,output_rx)=pipeline.add_processor(10,Duration::from_secs(1));// Send test dataletmutdata=HashMap::new();data.insert("temperature".to_string(),serde_json::Value::Number(serde_json::Number::from(25)));letrecord=DataRecord{id:"test-1".to_string(),timestamp:Instant::now(),data,source:"sensor-1".to_string(),};input_tx.send(record).await.unwrap();drop(input_tx);// Start pipelinelethandle=tokio::spawn(asyncmove{pipeline.start().await});// Wait for processing to completehandle.await.unwrap().unwrap();// Check outputletprocessed=output_rx.recv().await.unwrap();assert_eq!(processed.id,"test-1");}}
// rust/02-batch-processing.rs/*Batch processing patterns and best practices*/usestd::collections::HashMap;usestd::sync::Arc;usetokio::sync::RwLock;useserde::{Deserialize,Serialize};/// Batch processor for large datasets.pubstructBatchProcessor{batch_size:usize,max_workers:usize,data:Arc<RwLock<Vec<DataRecord>>>,results:Arc<RwLock<Vec<ProcessedRecord>>>,}#[derive(Debug, Clone, Serialize, Deserialize)]pubstructDataRecord{pubid:String,pubdata:HashMap<String,serde_json::Value>,pubmetadata:HashMap<String,String>,}#[derive(Debug, Clone, Serialize, Deserialize)]pubstructProcessedRecord{pubid:String,pubprocessed_data:HashMap<String,serde_json::Value>,pubprocessing_time:std::time::Duration,pubworker_id:usize,}implBatchProcessor{pubfnnew(batch_size:usize,max_workers:usize)->Self{Self{batch_size,max_workers,data:Arc::new(RwLock::new(Vec::new())),results:Arc::new(RwLock::new(Vec::new())),}}/// Add data to the processor.pubasyncfnadd_data(&self,record:DataRecord){letmutdata=self.data.write().await;data.push(record);}/// Process all data in batches.pubasyncfnprocess_all(&self)->Result<Vec<ProcessedRecord>,String>{letdata=self.data.read().await;lettotal_records=data.len();lettotal_batches=(total_records+self.batch_size-1)/self.batch_size;println!("Processing {} records in {} batches",total_records,total_batches);// Process batches in parallelletmuthandles=Vec::new();forbatch_indexin0..total_batches{letstart=batch_index*self.batch_size;letend=std::cmp::min(start+self.batch_size,total_records);letbatch_data=data[start..end].to_vec();letresults=Arc::clone(&self.results);lethandle=tokio::spawn(asyncmove{Self::process_batch(batch_data,batch_index).await});handles.push(handle);}// Wait for all batches to completeforhandleinhandles{letbatch_results=handle.await.map_err(|e|format!("Batch processing error: {}",e))?;letmutresults=self.results.write().await;results.extend(batch_results);}letresults=self.results.read().await;Ok(results.clone())}/// Process a single batch.asyncfnprocess_batch(batch_data:Vec<DataRecord>,batch_index:usize)->Vec<ProcessedRecord>{letstart_time=std::time::Instant::now();letmutresults=Vec::new();for(index,record)inbatch_data.into_iter().enumerate(){letworker_id=batch_index*1000+index;letprocessed=Self::process_record(record,worker_id).await;results.push(processed);}letprocessing_time=start_time.elapsed();println!("Batch {} processed in {:?}",batch_index,processing_time);results}/// Process a single record.asyncfnprocess_record(record:DataRecord,worker_id:usize)->ProcessedRecord{letstart_time=std::time::Instant::now();// Simulate data processingletmutprocessed_data=HashMap::new();for(key,value)inrecord.data{// Transform the datalettransformed_value=Self::transform_value(&key,&value);processed_data.insert(key,transformed_value);}letprocessing_time=start_time.elapsed();ProcessedRecord{id:record.id,processed_data,processing_time,worker_id,}}/// Transform a value based on its key.fntransform_value(key:&str,value:&serde_json::Value)->serde_json::Value{matchkey{"temperature"=>{ifletSome(temp)=value.as_f64(){// Convert Celsius to Fahrenheitletfahrenheit=(temp*9.0/5.0)+32.0;serde_json::Value::Number(serde_json::Number::from_f64(fahrenheit).unwrap())}else{value.clone()}}"pressure"=>{ifletSome(pressure)=value.as_f64(){// Convert Pa to PSIletpsi=pressure*0.000145038;serde_json::Value::Number(serde_json::Number::from_f64(psi).unwrap())}else{value.clone()}}_=>value.clone(),}}}/// Data aggregator for batch results.pubstructDataAggregator{results:Arc<RwLock<Vec<ProcessedRecord>>>,}implDataAggregator{pubfnnew()->Self{Self{results:Arc::new(RwLock::new(Vec::new())),}}/// Add processed results.pubasyncfnadd_results(&self,results:Vec<ProcessedRecord>){letmutall_results=self.results.write().await;all_results.extend(results);}/// Get aggregated statistics.pubasyncfnget_statistics(&self)->ProcessingStatistics{letresults=self.results.read().await;lettotal_records=results.len();lettotal_processing_time:std::time::Duration=results.iter().map(|r|r.processing_time).sum();letavg_processing_time=iftotal_records>0{total_processing_time/total_recordsasu32}else{std::time::Duration::from_secs(0)};letworker_counts:HashMap<usize,usize>=results.iter().map(|r|r.worker_id).fold(HashMap::new(),|mutacc,id|{*acc.entry(id).or_insert(0)+=1;acc});ProcessingStatistics{total_records,total_processing_time,avg_processing_time,worker_counts,}}}#[derive(Debug, Clone)]pubstructProcessingStatistics{pubtotal_records:usize,pubtotal_processing_time:std::time::Duration,pubavg_processing_time:std::time::Duration,pubworker_counts:HashMap<usize,usize>,}#[cfg(test)]modtests{usesuper::*;#[tokio::test]asyncfntest_batch_processor(){letprocessor=BatchProcessor::new(10,4);// Add test dataforiin0..25{letmutdata=HashMap::new();data.insert("temperature".to_string(),serde_json::Value::Number(serde_json::Number::from(20+i)));data.insert("pressure".to_string(),serde_json::Value::Number(serde_json::Number::from(100000+i*1000)));letrecord=DataRecord{id:format!("test-{}",i),data,metadata:HashMap::new(),};processor.add_data(record).await;}// Process all dataletresults=processor.process_all().await.unwrap();assert_eq!(results.len(),25);}#[tokio::test]asyncfntest_data_aggregator(){letaggregator=DataAggregator::new();letmutresults=Vec::new();foriin0..10{letprocessed=ProcessedRecord{id:format!("test-{}",i),processed_data:HashMap::new(),processing_time:std::time::Duration::from_millis(100),worker_id:i%3,};results.push(processed);}aggregator.add_results(results).await;letstats=aggregator.get_statistics().await;assert_eq!(stats.total_records,10);}}
// rust/03-data-validation.rs/*Data validation patterns and best practices*/usestd::collections::HashMap;useserde::{Deserialize,Serialize};usethiserror::Error;/// Data validation error types.#[derive(Error, Debug)]pubenumValidationError{#[error("Field '{field}' is required")]RequiredField{field:String},#[error("Field '{field}' has invalid format: {message}")]InvalidFormat{field:String,message:String},#[error("Field '{field}' is out of range: {min} <= {value} <= {max}")]OutOfRange{field:String,value:f64,min:f64,max:f64},#[error("Field '{field}' has invalid type: expected {expected}, got {actual}")]InvalidType{field:String,expected:String,actual:String},}/// Data validator trait.pubtraitDataValidator{fnvalidate(&self,data:&HashMap<String,serde_json::Value>)->Result<(),Vec<ValidationError>>;}/// Temperature validator.pubstructTemperatureValidator{min_temp:f64,max_temp:f64,}implTemperatureValidator{pubfnnew(min_temp:f64,max_temp:f64)->Self{Self{min_temp,max_temp}}}implDataValidatorforTemperatureValidator{fnvalidate(&self,data:&HashMap<String,serde_json::Value>)->Result<(),Vec<ValidationError>>{letmuterrors=Vec::new();ifletSome(temp_value)=data.get("temperature"){ifletSome(temp)=temp_value.as_f64(){iftemp<self.min_temp||temp>self.max_temp{errors.push(ValidationError::OutOfRange{field:"temperature".to_string(),value:temp,min:self.min_temp,max:self.max_temp,});}}else{errors.push(ValidationError::InvalidType{field:"temperature".to_string(),expected:"number".to_string(),actual:temp_value.type_str().to_string(),});}}else{errors.push(ValidationError::RequiredField{field:"temperature".to_string(),});}iferrors.is_empty(){Ok(())}else{Err(errors)}}}/// Pressure validator.pubstructPressureValidator{min_pressure:f64,max_pressure:f64,}implPressureValidator{pubfnnew(min_pressure:f64,max_pressure:f64)->Self{Self{min_pressure,max_pressure}}}implDataValidatorforPressureValidator{fnvalidate(&self,data:&HashMap<String,serde_json::Value>)->Result<(),Vec<ValidationError>>{letmuterrors=Vec::new();ifletSome(pressure_value)=data.get("pressure"){ifletSome(pressure)=pressure_value.as_f64(){ifpressure<self.min_pressure||pressure>self.max_pressure{errors.push(ValidationError::OutOfRange{field:"pressure".to_string(),value:pressure,min:self.min_pressure,max:self.max_pressure,});}}else{errors.push(ValidationError::InvalidType{field:"pressure".to_string(),expected:"number".to_string(),actual:pressure_value.type_str().to_string(),});}}else{errors.push(ValidationError::RequiredField{field:"pressure".to_string(),});}iferrors.is_empty(){Ok(())}else{Err(errors)}}}/// Composite validator.pubstructCompositeValidator{validators:Vec<Box<dynDataValidator>>,}implCompositeValidator{pubfnnew()->Self{Self{validators:Vec::new(),}}pubfnadd_validator(&mutself,validator:Box<dynDataValidator>){self.validators.push(validator);}}implDataValidatorforCompositeValidator{fnvalidate(&self,data:&HashMap<String,serde_json::Value>)->Result<(),Vec<ValidationError>>{letmutall_errors=Vec::new();forvalidatorin&self.validators{ifletErr(errors)=validator.validate(data){all_errors.extend(errors);}}ifall_errors.is_empty(){Ok(())}else{Err(all_errors)}}}/// Data validation service.pubstructValidationService{validator:CompositeValidator,}implValidationService{pubfnnew()->Self{Self{validator:CompositeValidator::new(),}}pubfnadd_validator(&mutself,validator:Box<dynDataValidator>){self.validator.add_validator(validator);}pubfnvalidate_data(&self,data:&HashMap<String,serde_json::Value>)->Result<(),Vec<ValidationError>>{self.validator.validate(data)}}#[cfg(test)]modtests{usesuper::*;#[test]fntest_temperature_validator(){letvalidator=TemperatureValidator::new(-50.0,50.0);letmutdata=HashMap::new();data.insert("temperature".to_string(),serde_json::Value::Number(serde_json::Number::from(25)));letresult=validator.validate(&data);assert!(result.is_ok());data.insert("temperature".to_string(),serde_json::Value::Number(serde_json::Number::from(100)));letresult=validator.validate(&data);assert!(result.is_err());}#[test]fntest_pressure_validator(){letvalidator=PressureValidator::new(0.0,200000.0);letmutdata=HashMap::new();data.insert("pressure".to_string(),serde_json::Value::Number(serde_json::Number::from(101325)));letresult=validator.validate(&data);assert!(result.is_ok());}#[test]fntest_composite_validator(){letmutvalidator=CompositeValidator::new();validator.add_validator(Box::new(TemperatureValidator::new(-50.0,50.0)));validator.add_validator(Box::new(PressureValidator::new(0.0,200000.0)));letmutdata=HashMap::new();data.insert("temperature".to_string(),serde_json::Value::Number(serde_json::Number::from(25)));data.insert("pressure".to_string(),serde_json::Value::Number(serde_json::Number::from(101325)));letresult=validator.validate(&data);assert!(result.is_ok());}}