turtle 1.0.0-rc.3

Learn the Rust language by creating animated drawings!
Documentation
#[cfg(target_arch = "wasm32")]
compile_error!("This module should not be included when compiling to wasm");

use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};

use crate::state::{DrawingState, Path, TurtleState};

/// Types that will be shared with another thread
pub type Shared<T> = Arc<RwLock<T>>;
/// Alias to help make the types more understandable without exposing as many implementation details
pub type ReadOnlyRef<'a, T> = RwLockReadGuard<'a, T>;
pub type MutableRef<'a, T> = RwLockWriteGuard<'a, T>;

/// A structure that provides read-only access to shared state
pub struct ReadOnly {
    turtle: Shared<TurtleState>,
    drawing: Shared<DrawingState>,
    /// A temporary path for use during animations
    temporary_path: Shared<Option<Path>>,
}

impl ReadOnly {
    pub fn turtle(&self) -> ReadOnlyRef<'_, TurtleState> {
        self.turtle.read().expect("bug: Lock was poisoned")
    }

    pub fn drawing(&self) -> ReadOnlyRef<'_, DrawingState> {
        self.drawing.read().expect("bug: Lock was poisoned")
    }

    pub fn temporary_path(&self) -> ReadOnlyRef<'_, Option<Path>> {
        self.temporary_path.read().expect("bug: Lock was poisoned")
    }
}

/// Container for all the state of a turtle application
#[derive(Clone)]
pub struct TurtleApp {
    //NOTE: All of these fields must be Shared or else cloning between threads will leave the two
    // threads out of sync
    turtle: Shared<TurtleState>,
    drawing: Shared<DrawingState>,
    /// A temporary path for use during animations
    temporary_path: Shared<Option<Path>>,
}

impl TurtleApp {
    pub fn new() -> Self {
        Self {
            turtle: Arc::new(RwLock::new(TurtleState::default())),
            drawing: Arc::new(RwLock::new(DrawingState::default())),
            temporary_path: Arc::new(RwLock::new(None)),
        }
    }

    /// Provide a read-only version of the state
    pub fn read_only(&self) -> ReadOnly {
        ReadOnly {
            turtle: Arc::clone(&self.turtle),
            drawing: Arc::clone(&self.drawing),
            temporary_path: Arc::clone(&self.temporary_path),
        }
    }

    /// Provides read-only access to the turtle state
    pub fn turtle(&self) -> ReadOnlyRef<'_, TurtleState> {
        self.turtle.read().expect("bug: Lock was poisoned")
    }

    /// Provides mutable access to the turtle state
    pub fn turtle_mut(&mut self) -> MutableRef<'_, TurtleState> {
        self.turtle.write().expect("bug: Lock was poisoned")
    }

    /// Provides read-only access to the drawing
    pub fn drawing(&self) -> ReadOnlyRef<'_, DrawingState> {
        self.drawing.read().expect("bug: Lock was poisoned")
    }

    /// Provides mutable access to the drawing
    pub fn drawing_mut(&mut self) -> MutableRef<'_, DrawingState> {
        self.drawing.write().expect("bug: Lock was poisoned")
    }

    /// Set the temporary_path to a new value, overwriting the previous one
    pub fn set_temporary_path(&mut self, path: Option<Path>) {
        let mut temp = self.temporary_path.write().expect("bug: Lock was poisoned");
        *temp = path;
    }
}