Rust example app, syntax overview

use std::fmt::{self, Display, Formatter};
use std::sync::mpsc::{self, Receiver};
use std::thread;
use std::time::{Duration, Instant};

// Define an enum to represent the status of an sensor, showcasing Rust's enum and pattern matching.
enum SensorStatus {
    Running,
    Stopped,
    Error(String),
}

// Implement the Display trait for SensorStatus to enable easy printing.
impl Display for SensorStatus {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match self {
            SensorStatus::Running => write!(f, "Sensor is running"),
            SensorStatus::Stopped => write!(f, "Sensor is stopped"),
            SensorStatus::Error(msg) => write!(f, "Sensor error: {}", msg),
        }
    }
}

// A struct representing a vehicle temperature sensor.
struct TemperatureSensor {
    name: &'static str,
    value: i32,
}

impl TemperatureSensor {
    const MIN_TEMP: i32 = -30;
    const MAX_TEMP: i32 = 150;
}

// A struct representing a vehicle speed sensor.
struct SpeedSensor {
    name: &'static str,
    value: u32,
}

impl SpeedSensor {
    const MAX_SPEED_LIMIT: u32 = 180;
}

// Implement the Display trait for Sensors, enabling descriptive output.
impl Display for TemperatureSensor {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "[{}] TemperatureSensor: {}", self.name, self.value)
    }
}

impl Display for SpeedSensor {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "[{}] SpeedSensor: {}", self.name, self.value)
    }
}

// Define a trait for diagnostic tools, demonstrating Rust's trait system for polymorphism.
trait DiagnosticTool {
    fn diagnose(&self) -> SensorStatus;
}

// Implement the DiagnosticTool trait for Sensors, showcasing trait implementations.
impl DiagnosticTool for TemperatureSensor {
    fn diagnose(&self) -> SensorStatus {
        if self.value > TemperatureSensor::MAX_TEMP {
            SensorStatus::Error(format!("{} sensor exceeds max limit:{} over {} [C]!", self.name, self.value, (self.value - TemperatureSensor::MAX_TEMP)))
        } else if self.value < TemperatureSensor::MIN_TEMP {
            SensorStatus::Error(format!("{} sensor reached below min limit:{} over {} [C]!", self.name, self.value, (self.value - TemperatureSensor::MIN_TEMP)))
        } else {
            SensorStatus::Running
        }
    }
}

impl DiagnosticTool for SpeedSensor {
    fn diagnose(&self) -> SensorStatus {
        if self.value > SpeedSensor::MAX_SPEED_LIMIT {
            SensorStatus::Error(format!("[{}] sensor exceeds max limit:{} over {} [km/h]!", self.name, self.value, (self.value - SpeedSensor::MAX_SPEED_LIMIT)))
        } else {
            SensorStatus::Running
        }
    }
}

// A function taking a dynamic trait object, demonstrating dynamic polymorphism.
fn run_diagnostic(tool: &dyn DiagnosticTool) -> SensorStatus {
    tool.diagnose()
}


// Define a struct for processing rear camera images, illustrating Rust's generic type parameters.
struct RearCameraImageProcessor<T> {
    data: T,
}

// Define a trait for image processing functionality.
trait ImageProcessing {
    fn process(&mut self);
}

// Implement methods for RearCameraImageProcessor, demonstrating ownership and borrowing.
impl<T: Display + Clone> RearCameraImageProcessor<T> {
    // Constructor method takes ownership of data.
    fn new(data: T) -> Self {
        RearCameraImageProcessor { data }
    }

    // Borrow self immutably to read data.
    fn read(&self) -> &T {
        &self.data
    }

    // Borrow self mutably to modify data.
    fn write(&mut self, data: T) {
        self.data = data;
    }
}

// Implement the ImageProcessing trait for RearCameraImageProcessor.
impl<T: Display + Clone> ImageProcessing for RearCameraImageProcessor<T> {
    // Sample processing method which just clones and displays the data.
    fn process(&mut self) {
        let processed_data = self.data.clone();
        println!("Processing image data: {}", processed_data);
    }
}

// Demonstrates the use of closures to modify data, showcasing Rust's closure capabilities.
fn adjust_brightness<F, T>(adjustment_closure: F, processor: &mut RearCameraImageProcessor<T>)
where
    F: Fn(T) -> T,
    T: Display + Clone,
{
    let current_data = processor.read().clone();
    println!("Current image brightness: {}", current_data);
    let adjusted_data = adjustment_closure(current_data);
    processor.write(adjusted_data);
}



fn main() {
    // Simulate a sensor that continuously sends data for 10 seconds.
    let (tx_temp, rx_temp): (mpsc::Sender<TemperatureSensor>, Receiver<TemperatureSensor>) = mpsc::channel();
    let (tx_speed, rx_speed): (mpsc::Sender<SpeedSensor>, Receiver<SpeedSensor>) = mpsc::channel();
    // Spawn a thread for the temp_sensor
    let tx_temp_clone = tx_temp.clone();
    let temp_handle = thread::spawn(move || {
        let start = Instant::now();
        while start.elapsed() < Duration::new(5, 0) {
            let value = (start.elapsed().as_secs() * 10) as i32; // Simulate increasing temp
            let sensor = TemperatureSensor { name: "Engine Temperature", value: value };
            if let Err(e) = tx_temp_clone.send(sensor) {
                eprintln!("Error sending temp data: {}", e);
                break;
            }
            thread::sleep(Duration::from_millis(500)); // Simulate data sent every 500ms
        }
    });
    // Spawn a thread for the car_speed_sensor
    let tx_speed_clone = tx_speed.clone();
    let speed_handle = thread::spawn(move || {
        let start = Instant::now();
        let mut factor: u64 = 1;
        while start.elapsed() < Duration::new(5, 0) {
            let value = (start.elapsed().as_secs() * 1 * factor) as u32; // Simulate increasing speed
            let sensor = SpeedSensor { name: "Car Speed", value: value };
            if let Err(e) = tx_speed_clone.send(sensor) {
                eprintln!("Error sending car speed data: {}", e);
                break;
            }
            factor = factor + 1;
            thread::sleep(Duration::from_millis(100)); // Simulate data sent every 100ms
        }
    });

    // Main thread acts as a diagnostic tool that processes sensors data
    let start = Instant::now();
    while start.elapsed() < Duration::new(5, 0) {
        if let Ok(sensor) = rx_temp.try_recv() {
            println!("Received {}: {}", sensor.name, sensor.value);
            // Here you can also run diagnostics on the received sensor data
            let status = run_diagnostic(&sensor);
            println!("Diagnostic result: {}", status);
        }
        if let Ok(sensor) = rx_speed.try_recv() {
            println!("Received {}: {}", sensor.name, sensor.value);
            // Here you can also run diagnostics on the received sensor data
            let status = run_diagnostic(&sensor);
            println!("Diagnostic result: {}", status);
        }
        // Simulate processing other tasks in the main thread
        thread::sleep(Duration::from_millis(100));
    }

    // Instantiate a RearCameraImageProcessor and demonstrate processing.
    let mut camera_processor = RearCameraImageProcessor::new("Initial image data".to_string());
    camera_processor.process();

    // Use a closure to adjust the brightness of the image data.
    adjust_brightness(|data| format!("{} + brightness adjusted", data), &mut camera_processor);
    println!("After adjustment: {}", camera_processor.read());

    // Join threads here
    if let Err(e) = temp_handle.join() {
        eprintln!("Error joining temperature sensor thread: {:?}", e);
    }

    if let Err(e) = speed_handle.join() {
        eprintln!("Error joining speed sensor thread: {:?}", e);
    }

    println!("Simulation completed.");
}