<commit_before>// During tests, we disable the renderer and that causes a bunch of warnings
// See Cargo.toml for an explanation of this attribute
#![cfg_attr(any(feature = "test", test), allow(dead_code, unused_variables, unused_imports))]

use std::thread;
use std::process;
use std::time::Instant;
use std::sync::mpsc::{self, TryRecvError};
use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};

use piston_window::math;

use renderer::{Renderer, DrawingCommand};
use animation::{Animation, MoveAnimation, RotateAnimation, AnimationStatus};
use state::{TurtleState, DrawingState, Path};
use radians::{self, Radians};
use {Point, Distance, Event};

use self::DrawingCommand::*;

/// 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")
    }
}

pub struct TurtleWindow {
    thread_handle: Option<thread::JoinHandle<()>>,
    /// Channel for sending drawing commands to the renderer thread
    drawing_channel: mpsc::Sender<DrawingCommand>,
    /// Channel for receiving events from the rendering thread
    events_channel: mpsc::Receiver<Event>,

    turtle: Shared<TurtleState>,
    drawing: Shared<DrawingState>,
    /// A temporary path for use during animations
    temporary_path: Shared<Option<Path>>,
}

impl TurtleWindow {
    pub fn new() -> TurtleWindow {
        let (drawing_tx, drawing_rx) = mpsc::channel();
        let (events_tx, events_rx) = mpsc::channel();

        let mut turtle_window = Self {
            thread_handle: None,
            drawing_channel: drawing_tx,
            events_channel: events_rx,
            turtle: Arc::new(RwLock::new(TurtleState::default())),
            drawing: Arc::new(RwLock::new(DrawingState::default())),
            temporary_path: Arc::new(RwLock::new(None)),
        };

        let read_only = turtle_window.read_only();
        let handle = thread::spawn(move || {
            // See Cargo.toml for an explanation of this attribute
            #[cfg(not(any(feature = "test", test)))]
            Renderer::new().run(drawing_rx, events_tx, read_only);
        });

        turtle_window.thread_handle = Some(handle);
        turtle_window
    }

    /// 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")
    }

    /// Provides read-only access to the temporary path
    fn temporary_path(&self) -> ReadOnlyRef<Option<Path>> {
        self.temporary_path.read().expect("bug: Lock was poisoned")
    }

    fn set_temporary_path(&mut self, path: Option<Path>) {
        let mut temp = self.temporary_path.write().expect("bug: Lock was poisoned");
        *temp = path;
    }

    /// See [`Turtle::poll_event()`](struct.Turtle.html#method.poll_event).
    pub fn poll_event(&mut self) -> Option<Event> {
        match self.events_channel.try_recv() {
            Ok(event) => Some(event),
            Err(TryRecvError::Empty) => None, // Do nothing
            // The window has been closed
            Err(TryRecvError::Disconnected) => self.exit_process(), // Quit
        }
    }

    /// Begin filling the shape drawn by the turtle's movements.
    pub fn begin_fill(&mut self) {
        let fill_color = self.turtle().fill_color;
        self.send_drawing_command(BeginFill(fill_color));
    }

    /// Stop filling the current shape
    pub fn end_fill(&mut self) {
        self.send_drawing_command(EndFill);
    }

    /// Clear the turtle's drawings
    pub fn clear(&mut self) {
        assert!(self.temporary_path().is_none(),
            "bug: The temporary path was still set when the renderer was asked to clear the drawing");
        self.send_drawing_command(Clear);
    }

    /// Move the turtle to the given position without changing its heading.
    pub fn go_to(&mut self, end: Point) {
        let (start, speed, pen) = {
            let turtle = self.turtle();
            (turtle.position, turtle.speed, turtle.pen.clone())
        };

        let distance = math::square_len(math::sub(start, end)).sqrt();
        let speed = speed.to_absolute(); // px per second
        // We take the absolute value because the time is always positive, even if distance is negative
        let total_millis = (distance / speed * 1000.).abs();

        let animation = MoveAnimation {
            path: Path {start, end, pen},
            timer: Instant::now(),
            total_millis,
        };

        self.play_animation(animation);
    }

    /// Move the turtle forward by the given distance. To move backwards, use a negative distance.
    ///
    /// The turtle's motion will be animated based on the speed
    pub fn forward(&mut self, distance: Distance) {
        if distance == 0. {
            return;
        }

        let (start, speed, heading, pen) = {
            let turtle = self.turtle();
            (turtle.position, turtle.speed, turtle.heading, turtle.pen.clone())
        };
        let x = distance * heading.cos();
        let y = distance * heading.sin();
        let end = math::add(start, [x, y]);

        let speed = speed.to_absolute(); // px per second
        // We take the absolute value because the time is always positive, even if distance is negative
        let total_millis = (distance / speed * 1000.).abs();

        let animation = MoveAnimation {
            path: Path {start, end, pen},
            timer: Instant::now(),
            total_millis,
        };

        self.play_animation(animation);
    }

    /// Rotate the turtle in place by the given angle in the given direction of rotation
    pub fn rotate(&mut self, angle: Radians, clockwise: bool) {
        if angle == radians::ZERO {
            return;
        }

        let TurtleState {heading, speed, ..} = *self.turtle();
        let speed = speed.to_rotation(); // radians per second
        let total_millis = angle / speed * 1000.;
        // We take the absolute value because the time is always positive, even if angle is negative
        let total_millis = total_millis.to_radians().abs();

        let animation = RotateAnimation {
            start: heading,
            delta_angle: angle,
            clockwise,
            timer: Instant::now(),
            total_millis,
        };

        self.play_animation(animation);
    }

    fn play_animation<A: Animation>(&mut self, animation: A) {
        loop {
            // We want to keep the lock for as little time as possible
            let status = {
                let mut turtle = self.turtle_mut();
                animation.advance(&mut *turtle)
            };
            match status {
                AnimationStatus::Running(path) => self.set_temporary_path(path),
                AnimationStatus::Complete(path) => {
                    if let Some(path) = path {
                        self.set_temporary_path(None);
                        self.send_drawing_command(StorePath(path));
                    }

                    break;
                },
            }
        }
    }

    #[inline]
    fn send_drawing_command(&mut self, command: DrawingCommand) {
        // During tests, we disable the renderer. That means that if we let this code run, it will
        // quit the application during the tests and make it look like everything passes.
        // We disable this code so that none of that happens.
        #[cfg(not(any(feature = "test", test)))]
        self.drawing_channel.send(command).unwrap_or_else(|_| {
            // The channel is closed which means the window was closed
            // quit immediately
            self.exit_process();
        });
    }

    /// Exits the current process with the correct error code
    ///
    /// Panics if the thread handle has already been consumed
    #[inline]
    fn exit_process(&mut self) -> ! {
        if let Some(handle) = self.thread_handle.take() {
            // First check if the other thread panicked before it quit
            match handle.join() {
                Ok(_) => process::exit(0),
                // If this returns an error, the other thread panicked
                Err(_) => process::exit(1),
            }
        }
        else {
            unreachable!("bug: the thread handle was used but the process did not end");
        }
    }
}

impl Drop for TurtleWindow {
    fn drop(&mut self) {
        // If the current thread is panicking, we want to abort right away
        // because otherwise there is code in the rendering thread that will call
        // process::exit(0) and then the exit code will be 0 instead of 1
        if thread::panicking() {
            process::exit(1);
        }

        // If this is just a normal ending of the main thread, we want to leave the renderer
        // running so that the user can see their drawing as long as they keep the window open
        if let Some(handle) = self.thread_handle.take() {
            handle.join().unwrap_or_else(|_| {
                // If this returns an error, the other thread panicked
                process::exit(1);
            });
        }
    }
}
<commit_msg>Ignoring drop implementation during tests so that test panics do not cause test failures<commit_after>// During tests, we disable the renderer and that causes a bunch of warnings
// See Cargo.toml for an explanation of this attribute
#![cfg_attr(any(feature = "test", test), allow(dead_code, unused_variables, unused_imports))]

use std::thread;
use std::process;
use std::time::Instant;
use std::sync::mpsc::{self, TryRecvError};
use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};

use piston_window::math;

use renderer::{Renderer, DrawingCommand};
use animation::{Animation, MoveAnimation, RotateAnimation, AnimationStatus};
use state::{TurtleState, DrawingState, Path};
use radians::{self, Radians};
use {Point, Distance, Event};

use self::DrawingCommand::*;

/// 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")
    }
}

pub struct TurtleWindow {
    thread_handle: Option<thread::JoinHandle<()>>,
    /// Channel for sending drawing commands to the renderer thread
    drawing_channel: mpsc::Sender<DrawingCommand>,
    /// Channel for receiving events from the rendering thread
    events_channel: mpsc::Receiver<Event>,

    turtle: Shared<TurtleState>,
    drawing: Shared<DrawingState>,
    /// A temporary path for use during animations
    temporary_path: Shared<Option<Path>>,
}

impl TurtleWindow {
    pub fn new() -> TurtleWindow {
        let (drawing_tx, drawing_rx) = mpsc::channel();
        let (events_tx, events_rx) = mpsc::channel();

        let mut turtle_window = Self {
            thread_handle: None,
            drawing_channel: drawing_tx,
            events_channel: events_rx,
            turtle: Arc::new(RwLock::new(TurtleState::default())),
            drawing: Arc::new(RwLock::new(DrawingState::default())),
            temporary_path: Arc::new(RwLock::new(None)),
        };

        let read_only = turtle_window.read_only();
        let handle = thread::spawn(move || {
            // See Cargo.toml for an explanation of this attribute
            #[cfg(not(any(feature = "test", test)))]
            Renderer::new().run(drawing_rx, events_tx, read_only);
        });

        turtle_window.thread_handle = Some(handle);
        turtle_window
    }

    /// 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")
    }

    /// Provides read-only access to the temporary path
    fn temporary_path(&self) -> ReadOnlyRef<Option<Path>> {
        self.temporary_path.read().expect("bug: Lock was poisoned")
    }

    fn set_temporary_path(&mut self, path: Option<Path>) {
        let mut temp = self.temporary_path.write().expect("bug: Lock was poisoned");
        *temp = path;
    }

    /// See [`Turtle::poll_event()`](struct.Turtle.html#method.poll_event).
    pub fn poll_event(&mut self) -> Option<Event> {
        match self.events_channel.try_recv() {
            Ok(event) => Some(event),
            Err(TryRecvError::Empty) => None, // Do nothing
            // The window has been closed
            Err(TryRecvError::Disconnected) => self.exit_process(), // Quit
        }
    }

    /// Begin filling the shape drawn by the turtle's movements.
    pub fn begin_fill(&mut self) {
        let fill_color = self.turtle().fill_color;
        self.send_drawing_command(BeginFill(fill_color));
    }

    /// Stop filling the current shape
    pub fn end_fill(&mut self) {
        self.send_drawing_command(EndFill);
    }

    /// Clear the turtle's drawings
    pub fn clear(&mut self) {
        assert!(self.temporary_path().is_none(),
            "bug: The temporary path was still set when the renderer was asked to clear the drawing");
        self.send_drawing_command(Clear);
    }

    /// Move the turtle to the given position without changing its heading.
    pub fn go_to(&mut self, end: Point) {
        let (start, speed, pen) = {
            let turtle = self.turtle();
            (turtle.position, turtle.speed, turtle.pen.clone())
        };

        let distance = math::square_len(math::sub(start, end)).sqrt();
        let speed = speed.to_absolute(); // px per second
        // We take the absolute value because the time is always positive, even if distance is negative
        let total_millis = (distance / speed * 1000.).abs();

        let animation = MoveAnimation {
            path: Path {start, end, pen},
            timer: Instant::now(),
            total_millis,
        };

        self.play_animation(animation);
    }

    /// Move the turtle forward by the given distance. To move backwards, use a negative distance.
    ///
    /// The turtle's motion will be animated based on the speed
    pub fn forward(&mut self, distance: Distance) {
        if distance == 0. {
            return;
        }

        let (start, speed, heading, pen) = {
            let turtle = self.turtle();
            (turtle.position, turtle.speed, turtle.heading, turtle.pen.clone())
        };
        let x = distance * heading.cos();
        let y = distance * heading.sin();
        let end = math::add(start, [x, y]);

        let speed = speed.to_absolute(); // px per second
        // We take the absolute value because the time is always positive, even if distance is negative
        let total_millis = (distance / speed * 1000.).abs();

        let animation = MoveAnimation {
            path: Path {start, end, pen},
            timer: Instant::now(),
            total_millis,
        };

        self.play_animation(animation);
    }

    /// Rotate the turtle in place by the given angle in the given direction of rotation
    pub fn rotate(&mut self, angle: Radians, clockwise: bool) {
        if angle == radians::ZERO {
            return;
        }

        let TurtleState {heading, speed, ..} = *self.turtle();
        let speed = speed.to_rotation(); // radians per second
        let total_millis = angle / speed * 1000.;
        // We take the absolute value because the time is always positive, even if angle is negative
        let total_millis = total_millis.to_radians().abs();

        let animation = RotateAnimation {
            start: heading,
            delta_angle: angle,
            clockwise,
            timer: Instant::now(),
            total_millis,
        };

        self.play_animation(animation);
    }

    fn play_animation<A: Animation>(&mut self, animation: A) {
        loop {
            // We want to keep the lock for as little time as possible
            let status = {
                let mut turtle = self.turtle_mut();
                animation.advance(&mut *turtle)
            };
            match status {
                AnimationStatus::Running(path) => self.set_temporary_path(path),
                AnimationStatus::Complete(path) => {
                    if let Some(path) = path {
                        self.set_temporary_path(None);
                        self.send_drawing_command(StorePath(path));
                    }

                    break;
                },
            }
        }
    }

    #[inline]
    fn send_drawing_command(&mut self, command: DrawingCommand) {
        // During tests, we disable the renderer. That means that if we let this code run, it will
        // quit the application during the tests and make it look like everything passes.
        // We disable this code so that none of that happens.
        #[cfg(not(any(feature = "test", test)))]
        self.drawing_channel.send(command).unwrap_or_else(|_| {
            // The channel is closed which means the window was closed
            // quit immediately
            self.exit_process();
        });
    }

    /// Exits the current process with the correct error code
    ///
    /// Panics if the thread handle has already been consumed
    #[inline]
    fn exit_process(&mut self) -> ! {
        if let Some(handle) = self.thread_handle.take() {
            // First check if the other thread panicked before it quit
            match handle.join() {
                Ok(_) => process::exit(0),
                // If this returns an error, the other thread panicked
                Err(_) => process::exit(1),
            }
        }
        else {
            unreachable!("bug: the thread handle was used but the process did not end");
        }
    }
}

#[cfg(not(any(feature = "test", test)))]
impl Drop for TurtleWindow {
    fn drop(&mut self) {
        // If the current thread is panicking, we want to abort right away
        // because otherwise there is code in the rendering thread that will call
        // process::exit(0) and then the exit code will be 0 instead of 1
        if thread::panicking() {
            process::exit(1);
        }

        // If this is just a normal ending of the main thread, we want to leave the renderer
        // running so that the user can see their drawing as long as they keep the window open
        if let Some(handle) = self.thread_handle.take() {
            handle.join().unwrap_or_else(|_| {
                // If this returns an error, the other thread panicked
                process::exit(1);
            });
        }
    }
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::fmt;
use std::io::{self, Read, Write};
use std::path::Path;

use encoding::label::encoding_from_whatwg_label;
use encoding::{DecoderTrap, EncoderTrap, EncodingRef};

use crate::util::{self, FileError};

#[derive(Debug)]
pub enum TextFileError {
    Open(FileError),
    Read(io::Error),
    Write(io::Error),
    Encoding(Cow<'static, str>),
    Encode(Cow<'static, str>),
    Decode(Cow<'static, str>),
}

impl fmt::Display for TextFileError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match &self {
            TextFileError::Open(err) => write!(f, "{}", err),
            TextFileError::Read(err) => write!(f, "{}", err),
            TextFileError::Write(err) => write!(f, "{}", err),
            TextFileError::Encoding(err) => write!(f, "{}", err),
            TextFileError::Encode(err) => write!(f, "{}", err),
            TextFileError::Decode(err) => write!(f, "{}", err),
        }
    }
}

pub fn read_text_file(file_path: &Path, encoding: Option<&str>) -> Result<String, TextFileError> {
    // If an encoding was specified, try to get an implementation for it.
    let encoding = get_encoding(encoding)?;

    // Open template file.
    let mut file = util::open_file(file_path).map_err(|err| TextFileError::Open(err))?;

    // If an encoding was specified...
    Ok(if let Some(encoding) = encoding {
        // Create a buffer for raw template data.
        let mut buffer: Vec<u8> = Vec::new();

        // Read raw template data into buffer.
        file.read_to_end(&mut buffer).map_err(|err| TextFileError::Read(err))?;

        // Decode the raw data to a string using the specified encoding.
        encoding
            .decode(&buffer, DecoderTrap::Strict)
            .map_err(|err| TextFileError::Decode(err))?
    } else {
        // Create an empty string.
        let mut string: String = String::new();

        // Read template data into the string, assuming it is valid UTF-8.
        file.read_to_string(&mut string)
            .map_err(|err| TextFileError::Read(err))?;

        string
    })
}

pub fn write_text_file(
    file_path: &Path,
    text: impl AsRef<str>,
    encoding: Option<&str>,
) -> Result<usize, TextFileError> {
    // Create file
    let mut file = util::create_file(&file_path).map_err(|err| TextFileError::Open(err))?;

    // Write text to it
    write_text(&mut file, text, encoding)
}

pub fn write_text(
    writable: &mut impl Write,
    text: impl AsRef<str>,
    encoding: Option<&str>,
) -> Result<usize, TextFileError> {
    let text = text.as_ref();

    // If an encoding was specified, try to get an implementation for it.
    let encoding = get_encoding(encoding)?;

    // If an encoding was specified...
    if let Some(encoding) = encoding {
        // Encode the template data using the specified encoding.
        let enc_bytes = encoding
            .encode(&text, EncoderTrap::Strict)
            .map_err(TextFileError::Encode)?;

        // Write bytes
        let bytes_written = writable.write(&enc_bytes).map_err(|err| TextFileError::Write(err))?;

        Ok(bytes_written)
    } else {
        // If no encoding was specified, write the text directly as bytes.
        let bytes_written = writable
            .write(text.as_bytes())
            .map_err(|err| TextFileError::Write(err))?;

        Ok(bytes_written)
    }
}

fn get_encoding(encoding: Option<&str>) -> Result<Option<EncodingRef>, TextFileError> {
    // If an encoding was specified, try to get an implementation for it.
    Ok(match encoding {
        Some(enc_name) => Some(
            encoding_from_whatwg_label(enc_name)
                .ok_or_else(|| TextFileError::Encoding(Cow::Borrowed("Cannot get encoding!")))?,
        ),
        None => None,
    })
}
<commit_msg>feat: Improve TextFileError display messages<commit_after>use std::borrow::Cow;
use std::fmt;
use std::io::{self, Read, Write};
use std::path::Path;

use encoding::label::encoding_from_whatwg_label;
use encoding::{DecoderTrap, EncoderTrap, EncodingRef};

use crate::util::{self, FileError};

#[derive(Debug)]
pub enum TextFileError {
    Open(FileError),
    Read(io::Error),
    Write(io::Error),
    Encoding(Cow<'static, str>),
    Encode(Cow<'static, str>),
    Decode(Cow<'static, str>),
}

impl fmt::Display for TextFileError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match &self {
            TextFileError::Open(err) => write!(f, "{}", err),
            TextFileError::Read(err) => write!(f, "Error reading text file: {}", err),
            TextFileError::Write(err) => write!(f, "Error writing to text file: {}", err),
            TextFileError::Encoding(err) => write!(f, "Invalid encoding: {}", err),
            TextFileError::Encode(err) => write!(f, "Error encoding text file: {}", err),
            TextFileError::Decode(err) => write!(f, "Error decoding text file: {}", err),
        }
    }
}

pub fn read_text_file(file_path: &Path, encoding: Option<&str>) -> Result<String, TextFileError> {
    // If an encoding was specified, try to get an implementation for it.
    let encoding = get_encoding(encoding)?;

    // Open template file.
    let mut file = util::open_file(file_path).map_err(|err| TextFileError::Open(err))?;

    // If an encoding was specified...
    Ok(if let Some(encoding) = encoding {
        // Create a buffer for raw template data.
        let mut buffer: Vec<u8> = Vec::new();

        // Read raw template data into buffer.
        file.read_to_end(&mut buffer).map_err(|err| TextFileError::Read(err))?;

        // Decode the raw data to a string using the specified encoding.
        encoding
            .decode(&buffer, DecoderTrap::Strict)
            .map_err(|err| TextFileError::Decode(err))?
    } else {
        // Create an empty string.
        let mut string: String = String::new();

        // Read template data into the string, assuming it is valid UTF-8.
        file.read_to_string(&mut string)
            .map_err(|err| TextFileError::Read(err))?;

        string
    })
}

pub fn write_text_file(
    file_path: &Path,
    text: impl AsRef<str>,
    encoding: Option<&str>,
) -> Result<usize, TextFileError> {
    // Create file
    let mut file = util::create_file(&file_path).map_err(|err| TextFileError::Open(err))?;

    // Write text to it
    write_text(&mut file, text, encoding)
}

pub fn write_text(
    writable: &mut impl Write,
    text: impl AsRef<str>,
    encoding: Option<&str>,
) -> Result<usize, TextFileError> {
    let text = text.as_ref();

    // If an encoding was specified, try to get an implementation for it.
    let encoding = get_encoding(encoding)?;

    // If an encoding was specified...
    if let Some(encoding) = encoding {
        // Encode the template data using the specified encoding.
        let enc_bytes = encoding
            .encode(&text, EncoderTrap::Strict)
            .map_err(TextFileError::Encode)?;

        // Write bytes
        let bytes_written = writable.write(&enc_bytes).map_err(|err| TextFileError::Write(err))?;

        Ok(bytes_written)
    } else {
        // If no encoding was specified, write the text directly as bytes.
        let bytes_written = writable
            .write(text.as_bytes())
            .map_err(|err| TextFileError::Write(err))?;

        Ok(bytes_written)
    }
}

fn get_encoding(encoding: Option<&str>) -> Result<Option<EncodingRef>, TextFileError> {
    // If an encoding was specified, try to get an implementation for it.
    Ok(match encoding {
        Some(enc_name) => Some(
            encoding_from_whatwg_label(enc_name)
                .ok_or_else(|| TextFileError::Encoding(Cow::Borrowed("Cannot get encoding!")))?,
        ),
        None => None,
    })
}
<|endoftext|>


<commit_before>use nom::{Err, IResult, Needed};

use std::io;
use std::io::Read;
use std::ptr;
use std::cmp;

use super::StreamProducer;

#[derive(Debug)]
pub enum ErrorKind {
  IO(io::Error),
  Incomplete(usize),
  Consumed(usize),
  EndOfInput,
  Unknown,
}

// Structure that hold a slice of bytes.
pub struct ByteStream<'a> {
  offset: usize,
  bytes: &'a [u8],
}

impl<'a> ByteStream<'a> {
  // Construct a `ByteStream` based on the passed in byte slice.
  pub fn new(bytes: &'a [u8]) -> Self {
    ByteStream {
      offset: 0,
      bytes: bytes,
    }
  }

  // Return the number of bytes that haven't been consumed yet.
  #[inline]
  pub fn len(&self) -> usize {
    self.bytes.len() - self.offset
  }

  // Return true if the stream contains no more bytes.
  #[inline]
  pub fn is_empty(&self) -> bool {
    self.len() == 0
  }
}

impl<'a> StreamProducer for ByteStream<'a> {
  fn parse<F, T>(&mut self, f: F) -> Result<T, ErrorKind>
   where F: FnOnce(&[u8]) -> IResult<&[u8], T> {
    if self.is_empty() {
      return Err(ErrorKind::EndOfInput);
    }

    match f(&self.bytes[self.offset..]) {
      IResult::Done(i, o)    => {
        self.offset += self.len() - i.len();

        Ok(o)
      }
      IResult::Incomplete(n) => {
        let mut needed = self.len();

        if let Needed::Size(size) = n {
          needed = size;
        }

        Err(ErrorKind::Incomplete(needed))
      }
      IResult::Error(e)      => {
        if let Err::Position(_, i) = e {
          let consumed = self.len() - i.len();

          self.offset += consumed;

          Err(ErrorKind::Consumed(consumed))
        } else {
          Err(ErrorKind::Unknown)
        }
      }
    }
  }
}

// Growable buffer of bytes.
//
// Mainly used to the `ReadStream` structure but can be used seperately for
// manually filling with some `Read` source.
pub struct Buffer {
  data: Vec<u8>,
  filled: usize,
  offset: usize,
}

impl Buffer {
  // Default constructor for `Buffer`
  pub fn new() -> Self {
    Self::with_capacity(1024)
  }

  // Explicitly set the buffer capacity.
  pub fn with_capacity(capacity: usize) -> Self {
    let mut buffer = Vec::with_capacity(capacity);

    unsafe {
      buffer.set_len(capacity);
    }

    Buffer {
      data: buffer,
      filled: 0,
      offset: 0,
    }
  }

  // Return the number of read bytes that haven't been consumed yet.
  #[inline]
  pub fn len(&self) -> usize {
    self.filled - self.offset
  }

  // Return true if buffer contains no more bytes.
  #[inline]
  pub fn is_empty(&self) -> bool {
    self.len() == 0
  }

  #[inline]
  pub fn capacity(&self) -> usize {
    self.data.len()
  }

  pub fn as_slice(&self) -> &[u8] {
    &self.data[self.offset..self.filled]
  }

  pub fn fill<R: Read>(&mut self, reader: &mut R) -> io::Result<usize> {
    reader.read(&mut self.data[self.filled..]).map(|consumed| {
      self.filled += consumed;

      consumed
    })
  }

  pub fn resize(&mut self, size: usize) {
    if size > self.data.capacity() {
      self.data.reserve(size);

      let capacity = self.data.capacity();

      unsafe {
        self.data.set_len(capacity);
      }
    }

    if self.data.len() - self.filled < size  {
      let length  = self.filled - self.offset;
      let mut_ptr = self.data.as_mut_ptr();

      unsafe {
        let offset_ptr  = self.data.as_ptr().offset(self.offset as isize);

        ptr::copy(offset_ptr, mut_ptr, length);
      }

      self.filled -= self.offset;
      self.offset  = 0;
    }
  }

  pub fn consume(&mut self, consumed: usize) {
    self.offset += consumed;
  }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ParserState {
  Incomplete,
  EndOfInput,
}

fn fill<R: Read>(buffer: &mut Buffer, reader: &mut R, needed: usize)
                 -> io::Result<usize> {
  let mut read = 0;

  if buffer.len() < needed {
    buffer.resize(needed);

    while buffer.len() < needed {
      let size_read = try!(buffer.fill(reader));

      if size_read > 0 {
        read += size_read;
      } else {
        break;
      }
    }
  }

  Ok(read)
}

// Structure that hold a reader for a source of bytes.
pub struct ReadStream<R: Read> {
  reader: R,
  buffer: Buffer,
  needed: usize,
  state: ParserState,
}

impl<R> ReadStream<R> where R: Read {
  pub fn new(reader: R) -> Self {
    ReadStream {
      reader: reader,
      buffer: Buffer::new(),
      needed: 0,
      state: ParserState::Incomplete,
    }
  }

  fn fill(&mut self) -> io::Result<usize> {
    let needed = cmp::max(1, self.needed);

    fill(&mut self.buffer, &mut self.reader, needed).map(|consumed| {
      if self.buffer.len() < needed {
        self.state = ParserState::EndOfInput;
      }

      consumed
    })
  }
}

fn from_iresult<T>(buffer: &Buffer, result: IResult<&[u8], T>)
                   -> Result<(usize, T), ErrorKind> {
  match result {
    IResult::Done(i, o)    => Ok((buffer.len() - i.len(), o)),
    IResult::Incomplete(n) => {
      let mut needed = buffer.capacity();

      if let Needed::Size(size) = n {
        needed = size;
      }

      Err(ErrorKind::Incomplete(needed))
    }
    IResult::Error(e)      => {
      if let Err::Position(_, i) = e {
        Err(ErrorKind::Consumed(buffer.len() - i.len()))
      } else {
        Err(ErrorKind::Unknown)
      }
    }
  }
}

impl<R> StreamProducer for ReadStream<R> where R: Read {
  fn parse<F, T>(&mut self, f: F) -> Result<T, ErrorKind>
   where F: FnOnce(&[u8]) -> IResult<&[u8], T> {
    if self.state != ParserState::EndOfInput {
      try!(self.fill().map_err(ErrorKind::IO));
    }

    let mut buffer = &mut self.buffer;

    if buffer.is_empty() {
      self.state = ParserState::EndOfInput;

      return Err(ErrorKind::EndOfInput);
    }

    let result = {
      let iresult = f(buffer.as_slice());

      from_iresult(&buffer, iresult)
    };

    match result {
      Ok((consumed, o)) => {
        buffer.consume(consumed);

        Ok(o)
      }
      Err(kind)         => {
        match kind {
          ErrorKind::Incomplete(needed) => self.needed = needed,
          ErrorKind::Consumed(consumed) => buffer.consume(consumed),
          _                             => (),
        }

        Err(kind)
      }
    }
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use utility::StreamProducer;
  use nom::be_u32;

  #[test]
  fn test_buffer() {
    let mut buffer = Buffer::new();
    let bytes      = b"Hello World";
    let mut reader = &bytes[..];

    assert!(buffer.is_empty());
    assert_eq!(buffer.capacity(), 1024);

    let bytes_read = buffer.fill(&mut reader).unwrap_or(0);
    let bytes_len  = bytes.len();

    assert_eq!(bytes_read, bytes_len);
    assert_eq!(buffer.len(), bytes_len);
    assert_eq!(buffer.as_slice(), bytes);

    buffer.resize(512);
    assert_eq!(buffer.capacity(), 1024);
  }

  #[test]
  fn test_byte_stream() {
    let bytes      = b"Hello World";
    let mut stream = ByteStream::new(bytes);

    assert_eq!(stream.len(), bytes.len());

    let result = stream.parse(be_u32).unwrap_or(0);

    assert_eq!(result, 1214606444);
    assert_eq!(stream.len(), 7);
  }

  #[test]
  fn test_read_stream() {
    let bytes      = b"Hello World";
    let mut stream = ReadStream::new(&bytes[..]);

    let result = stream.parse(be_u32).unwrap_or(0);

    assert_eq!(result, 1214606444)
  }
}
<commit_msg>Add documentation for `Buffer::capacity`<commit_after>use nom::{Err, IResult, Needed};

use std::io;
use std::io::Read;
use std::ptr;
use std::cmp;

use super::StreamProducer;

#[derive(Debug)]
pub enum ErrorKind {
  IO(io::Error),
  Incomplete(usize),
  Consumed(usize),
  EndOfInput,
  Unknown,
}

// Structure that hold a slice of bytes.
pub struct ByteStream<'a> {
  offset: usize,
  bytes: &'a [u8],
}

impl<'a> ByteStream<'a> {
  // Construct a `ByteStream` based on the passed in byte slice.
  pub fn new(bytes: &'a [u8]) -> Self {
    ByteStream {
      offset: 0,
      bytes: bytes,
    }
  }

  // Return the number of bytes that haven't been consumed yet.
  #[inline]
  pub fn len(&self) -> usize {
    self.bytes.len() - self.offset
  }

  // Return true if the stream contains no more bytes.
  #[inline]
  pub fn is_empty(&self) -> bool {
    self.len() == 0
  }
}

impl<'a> StreamProducer for ByteStream<'a> {
  fn parse<F, T>(&mut self, f: F) -> Result<T, ErrorKind>
   where F: FnOnce(&[u8]) -> IResult<&[u8], T> {
    if self.is_empty() {
      return Err(ErrorKind::EndOfInput);
    }

    match f(&self.bytes[self.offset..]) {
      IResult::Done(i, o)    => {
        self.offset += self.len() - i.len();

        Ok(o)
      }
      IResult::Incomplete(n) => {
        let mut needed = self.len();

        if let Needed::Size(size) = n {
          needed = size;
        }

        Err(ErrorKind::Incomplete(needed))
      }
      IResult::Error(e)      => {
        if let Err::Position(_, i) = e {
          let consumed = self.len() - i.len();

          self.offset += consumed;

          Err(ErrorKind::Consumed(consumed))
        } else {
          Err(ErrorKind::Unknown)
        }
      }
    }
  }
}

// Growable buffer of bytes.
//
// Mainly used to the `ReadStream` structure but can be used seperately for
// manually filling with some `Read` source.
pub struct Buffer {
  data: Vec<u8>,
  filled: usize,
  offset: usize,
}

impl Buffer {
  // Default constructor for `Buffer`
  pub fn new() -> Self {
    Self::with_capacity(1024)
  }

  // Explicitly set the buffer capacity.
  pub fn with_capacity(capacity: usize) -> Self {
    let mut buffer = Vec::with_capacity(capacity);

    unsafe {
      buffer.set_len(capacity);
    }

    Buffer {
      data: buffer,
      filled: 0,
      offset: 0,
    }
  }

  // Return the number of read bytes that haven't been consumed yet.
  #[inline]
  pub fn len(&self) -> usize {
    self.filled - self.offset
  }

  // Return true if buffer contains no more bytes.
  #[inline]
  pub fn is_empty(&self) -> bool {
    self.len() == 0
  }

  // The set length of the unlining buffer.
  #[inline]
  pub fn capacity(&self) -> usize {
    self.data.len()
  }

  pub fn as_slice(&self) -> &[u8] {
    &self.data[self.offset..self.filled]
  }

  pub fn fill<R: Read>(&mut self, reader: &mut R) -> io::Result<usize> {
    reader.read(&mut self.data[self.filled..]).map(|consumed| {
      self.filled += consumed;

      consumed
    })
  }

  pub fn resize(&mut self, size: usize) {
    if size > self.data.capacity() {
      self.data.reserve(size);

      let capacity = self.data.capacity();

      unsafe {
        self.data.set_len(capacity);
      }
    }

    if self.data.len() - self.filled < size  {
      let length  = self.filled - self.offset;
      let mut_ptr = self.data.as_mut_ptr();

      unsafe {
        let offset_ptr  = self.data.as_ptr().offset(self.offset as isize);

        ptr::copy(offset_ptr, mut_ptr, length);
      }

      self.filled -= self.offset;
      self.offset  = 0;
    }
  }

  pub fn consume(&mut self, consumed: usize) {
    self.offset += consumed;
  }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ParserState {
  Incomplete,
  EndOfInput,
}

fn fill<R: Read>(buffer: &mut Buffer, reader: &mut R, needed: usize)
                 -> io::Result<usize> {
  let mut read = 0;

  if buffer.len() < needed {
    buffer.resize(needed);

    while buffer.len() < needed {
      let size_read = try!(buffer.fill(reader));

      if size_read > 0 {
        read += size_read;
      } else {
        break;
      }
    }
  }

  Ok(read)
}

// Structure that hold a reader for a source of bytes.
pub struct ReadStream<R: Read> {
  reader: R,
  buffer: Buffer,
  needed: usize,
  state: ParserState,
}

impl<R> ReadStream<R> where R: Read {
  pub fn new(reader: R) -> Self {
    ReadStream {
      reader: reader,
      buffer: Buffer::new(),
      needed: 0,
      state: ParserState::Incomplete,
    }
  }

  fn fill(&mut self) -> io::Result<usize> {
    let needed = cmp::max(1, self.needed);

    fill(&mut self.buffer, &mut self.reader, needed).map(|consumed| {
      if self.buffer.len() < needed {
        self.state = ParserState::EndOfInput;
      }

      consumed
    })
  }
}

fn from_iresult<T>(buffer: &Buffer, result: IResult<&[u8], T>)
                   -> Result<(usize, T), ErrorKind> {
  match result {
    IResult::Done(i, o)    => Ok((buffer.len() - i.len(), o)),
    IResult::Incomplete(n) => {
      let mut needed = buffer.capacity();

      if let Needed::Size(size) = n {
        needed = size;
      }

      Err(ErrorKind::Incomplete(needed))
    }
    IResult::Error(e)      => {
      if let Err::Position(_, i) = e {
        Err(ErrorKind::Consumed(buffer.len() - i.len()))
      } else {
        Err(ErrorKind::Unknown)
      }
    }
  }
}

impl<R> StreamProducer for ReadStream<R> where R: Read {
  fn parse<F, T>(&mut self, f: F) -> Result<T, ErrorKind>
   where F: FnOnce(&[u8]) -> IResult<&[u8], T> {
    if self.state != ParserState::EndOfInput {
      try!(self.fill().map_err(ErrorKind::IO));
    }

    let mut buffer = &mut self.buffer;

    if buffer.is_empty() {
      self.state = ParserState::EndOfInput;

      return Err(ErrorKind::EndOfInput);
    }

    let result = {
      let iresult = f(buffer.as_slice());

      from_iresult(&buffer, iresult)
    };

    match result {
      Ok((consumed, o)) => {
        buffer.consume(consumed);

        Ok(o)
      }
      Err(kind)         => {
        match kind {
          ErrorKind::Incomplete(needed) => self.needed = needed,
          ErrorKind::Consumed(consumed) => buffer.consume(consumed),
          _                             => (),
        }

        Err(kind)
      }
    }
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use utility::StreamProducer;
  use nom::be_u32;

  #[test]
  fn test_buffer() {
    let mut buffer = Buffer::new();
    let bytes      = b"Hello World";
    let mut reader = &bytes[..];

    assert!(buffer.is_empty());
    assert_eq!(buffer.capacity(), 1024);

    let bytes_read = buffer.fill(&mut reader).unwrap_or(0);
    let bytes_len  = bytes.len();

    assert_eq!(bytes_read, bytes_len);
    assert_eq!(buffer.len(), bytes_len);
    assert_eq!(buffer.as_slice(), bytes);

    buffer.resize(512);
    assert_eq!(buffer.capacity(), 1024);
  }

  #[test]
  fn test_byte_stream() {
    let bytes      = b"Hello World";
    let mut stream = ByteStream::new(bytes);

    assert_eq!(stream.len(), bytes.len());

    let result = stream.parse(be_u32).unwrap_or(0);

    assert_eq!(result, 1214606444);
    assert_eq!(stream.len(), 7);
  }

  #[test]
  fn test_read_stream() {
    let bytes      = b"Hello World";
    let mut stream = ReadStream::new(&bytes[..]);

    let result = stream.parse(be_u32).unwrap_or(0);

    assert_eq!(result, 1214606444)
  }
}
<|endoftext|>


<commit_before>use super::super::{Grid, X, Y};
use super::*;

use rand::distributions::Distribution;
use rand::{distributions, Rng};
use std::cmp;

#[derive(Clone, Copy, PartialEq, Eq)]
pub enum ClueConstraint {
    Any,
    Forced,
    Prohibited,
}

pub fn generate<R: Rng>(
    clue_constraint: &Grid<ClueConstraint>,
    max_clue: Option<i32>,
    rng: &mut R,
) -> Option<Grid<Clue>> {
    let height = clue_constraint.height();
    let width = clue_constraint.width();
    let mut has_clue = Grid::new(height, width, false);

    let dic = Dictionary::new();
    let consecutive_dic = ConsecutiveRegionDictionary::new(&dic);

    let mut problem = Grid::new(height, width, NO_CLUE);
    let mut field = Field::new(height, width, &dic, &consecutive_dic);
    let mut current_energy = 0i32;
    let mut n_clues = 0;

    for y in 0..height {
        for x in 0..width {
            if clue_constraint[(Y(y), X(x))] == ClueConstraint::Forced {
                has_clue[(Y(y), X(x))] = true;
                n_clues += 1;
            }
        }
    }

    let n_step = height * width * 20;
    let mut temperature = 20f64;

    for s in 0..n_step {
        let mut update_cand = vec![];
        for y in 0..height {
            for x in 0..width {
                if field.cell((Y(y), X(x))) == Cell::Black
                    || clue_constraint[(Y(y), X(x))] == ClueConstraint::Prohibited
                {
                    continue;
                }
                let y2 = height - 1 - y;
                let x2 = width - 1 - x;
                if -1 <= y - y2 && y - y2 <= 1 && -1 <= x - x2 && x - x2 <= 1
                    && (y != y2 || x != x2)
                {
                    continue;
                }
                let mut isok = true;
                for dy in -1..2 {
                    for dx in -1..2 {
                        if (dy != 0 || dx != 0) && has_clue.is_valid_coord((Y(y + dy), X(x + dx)))
                            && has_clue[(Y(y + dy), X(x + dx))]
                        {
                            isok = false;
                        }
                    }
                }
                let mut isok2 = false;
                for dy in -2..3 {
                    for dx in -2..3 {
                        let loc = (Y(y + dy), X(x + dx));
                        if field.cell_checked(loc) == Cell::Undecided {
                            isok2 = true;
                        }
                    }
                }
                if (has_clue[(Y(y), X(x))] && problem[(Y(y), X(x))] == NO_CLUE)
                    || (isok && isok2
                        && (problem[(Y(y), X(x))] != NO_CLUE || rng.gen::<f64>() < 1.0))
                {
                    for v in (-1)..(CLUE_TYPES as i32) {
                        if v == -1 && clue_constraint[(Y(y), X(x))] == ClueConstraint::Forced {
                            continue;
                        }
                        if v == 0 || v == 21 || v == 22 {
                            continue;
                        }
                        let next_clue = Clue(v);
                        if problem[(Y(y), X(x))] != next_clue {
                            update_cand.push(((Y(y), X(x)), next_clue));
                        }
                    }
                }
            }
        }

        rng.shuffle(&mut update_cand);

        let mut applicable_cands = vec![];

        for &(loc, clue) in &update_cand {
            let previous_clue = problem[loc];
            let mut n_clues2 = n_clues;
            let (Y(y), X(x)) = loc;
            let loc2 = (Y(height - 1 - y), X(width - 1 - x));
            if clue == NO_CLUE {
                if loc == loc2 {
                    n_clues2 -= 1;
                } else if problem[loc2] == NO_CLUE {
                    n_clues2 -= 2;
                }
            } else {
                if !has_clue[loc] {
                    n_clues2 += 1;
                }
                if loc != loc2 && !has_clue[loc2] {
                    n_clues2 += 1;
                }
            }
            if let Some(max_clue) = max_clue {
                if max_clue < n_clues2 {
                    continue;
                }
            }

            problem[loc] = clue;
            let field = solve_test(&problem, &has_clue, &dic, &consecutive_dic);
            let energy = field.decided_cells() - 4 * n_clues2;

            let update = !field.inconsistent()
                && (current_energy < energy
                    || rng.gen::<f64>() < ((energy - current_energy) as f64 / temperature).exp());

            if update {
                let mut clue_filled = true;
                for y in 0..height {
                    for x in 0..width {
                        if has_clue[(Y(y), X(x))] && problem[(Y(y), X(x))] == NO_CLUE {
                            clue_filled = false;
                        }
                    }
                }
                if field.fully_solved() && clue_filled {
                    return Some(problem);
                }

                applicable_cands.push((loc, clue, energy, n_clues2, field));
            }
            problem[loc] = previous_clue;

            if applicable_cands.len() >= 1 {
                break;
            }
        }

        if applicable_cands.len() >= 1 {
            let mut best_cand = applicable_cands.swap_remove(0);
            while applicable_cands.len() > 0 {
                let cand2 = applicable_cands.swap_remove(0);
                if best_cand.3 < cand2.3 {
                    best_cand = cand2;
                }
            }

            let (loc, clue, energy, n_clues2, field2) = best_cand;

            current_energy = energy;
            n_clues = n_clues2;
            problem[loc] = clue;
            field = field2;

            let (Y(y), X(x)) = loc;
            let loc2 = (Y(height - 1 - y), X(width - 1 - x));
            if clue == NO_CLUE {
                if problem[loc2] == NO_CLUE {
                    has_clue[loc] = false;
                    has_clue[loc2] = false;
                }
            } else {
                has_clue[loc] = true;
                has_clue[loc2] = true;
            }
        } else {
            break;
        }

        temperature *= 0.995f64;
    }

    None
}

fn solve_test<'a, 'b>(
    problem: &Grid<Clue>,
    has_clue: &Grid<bool>,
    dic: &'a Dictionary,
    consecutive_dic: &'b ConsecutiveRegionDictionary,
) -> Field<'a, 'b> {
    let height = problem.height();
    let width = problem.width();
    let mut ret = Field::new(height, width, dic, consecutive_dic);

    for y in 0..height {
        for x in 0..width {
            let clue = problem[(Y(y), X(x))];
            if clue != NO_CLUE {
                ret.add_clue((Y(y), X(x)), clue);
            } else if has_clue[(Y(y), X(x))] {
                ret.decide((Y(y), X(x)), Cell::White);
            }

            if ret.inconsistent() {
                return ret;
            }
        }
    }

    ret.solve();
    ret.trial_and_error();

    ret
}
<commit_msg>Tapa: minor improvements in generator<commit_after>use super::super::{Grid, X, Y};
use super::*;

use rand::distributions::Distribution;
use rand::{distributions, Rng};
use std::cmp;

#[derive(Clone, Copy, PartialEq, Eq)]
pub enum ClueConstraint {
    Any,
    Forced,
    Prohibited,
}

pub fn generate<R: Rng>(
    clue_constraint: &Grid<ClueConstraint>,
    max_clue: Option<i32>,
    rng: &mut R,
) -> Option<Grid<Clue>> {
    let height = clue_constraint.height();
    let width = clue_constraint.width();
    let mut has_clue = Grid::new(height, width, false);

    let dic = Dictionary::new();
    let consecutive_dic = ConsecutiveRegionDictionary::new(&dic);

    let mut problem = Grid::new(height, width, NO_CLUE);
    let mut field = Field::new(height, width, &dic, &consecutive_dic);
    let mut current_energy = 0i32;
    let mut n_clues = 0;

    for y in 0..height {
        for x in 0..width {
            if clue_constraint[(Y(y), X(x))] == ClueConstraint::Forced {
                has_clue[(Y(y), X(x))] = true;
                n_clues += 1;
            }
        }
    }

    let n_step = height * width * 10;
    let mut temperature = 20f64;

    for s in 0..n_step {
        let mut update_cand = vec![];
        for y in 0..height {
            for x in 0..width {
                if field.cell((Y(y), X(x))) == Cell::Black
                    || clue_constraint[(Y(y), X(x))] == ClueConstraint::Prohibited
                {
                    continue;
                }
                let y2 = height - 1 - y;
                let x2 = width - 1 - x;
                if -1 <= y - y2 && y - y2 <= 1 && -1 <= x - x2 && x - x2 <= 1
                    && (y != y2 || x != x2)
                {
                    continue;
                }
                let mut isok = true;
                for dy in -1..2 {
                    for dx in -1..2 {
                        if (dy != 0 || dx != 0) && has_clue.is_valid_coord((Y(y + dy), X(x + dx)))
                            && has_clue[(Y(y + dy), X(x + dx))]
                        {
                            isok = false;
                        }
                    }
                }
                let mut isok2 = false;
                for dy in -2..3 {
                    for dx in -2..3 {
                        let loc = (Y(y + dy), X(x + dx));
                        if field.cell_checked(loc) == Cell::Undecided {
                            isok2 = true;
                        }
                    }
                }
                if (has_clue[(Y(y), X(x))] && problem[(Y(y), X(x))] == NO_CLUE)
                    || (isok && isok2
                        && (problem[(Y(y), X(x))] != NO_CLUE || rng.gen::<f64>() < 1.0))
                {
                    for v in (-1)..(CLUE_TYPES as i32) {
                        if v == -1 && clue_constraint[(Y(y), X(x))] == ClueConstraint::Forced {
                            continue;
                        }
                        if v == 0 || v == 21 || v == 22 {
                            continue;
                        }
                        let next_clue = Clue(v);
                        if problem[(Y(y), X(x))] != next_clue {
                            update_cand.push(((Y(y), X(x)), next_clue));
                        }
                    }
                }
            }
        }

        rng.shuffle(&mut update_cand);

        let mut applicable_cands = vec![];

        for &(loc, clue) in &update_cand {
            let previous_clue = problem[loc];
            let mut n_clues2 = n_clues;
            let (Y(y), X(x)) = loc;
            let loc2 = (Y(height - 1 - y), X(width - 1 - x));
            if clue == NO_CLUE {
                if loc == loc2 {
                    n_clues2 -= 1;
                } else if problem[loc2] == NO_CLUE {
                    n_clues2 -= 2;
                }
            } else {
                if !has_clue[loc] {
                    n_clues2 += 1;
                }
                if loc != loc2 && !has_clue[loc2] {
                    n_clues2 += 1;
                }
            }
            if let Some(max_clue) = max_clue {
                if max_clue < n_clues2 {
                    continue;
                }
            }

            problem[loc] = clue;
            let field = if previous_clue == NO_CLUE {
                let mut f = field.clone();
                f.add_clue(loc, clue);
                f.solve();
                f.trial_and_error();
                f
            } else {
                solve_test(&problem, &has_clue, &dic, &consecutive_dic)
            };
            let energy = field.decided_cells() - 4 * n_clues2;

            let update = !field.inconsistent()
                && (current_energy < energy
                    || rng.gen::<f64>() < ((energy - current_energy) as f64 / temperature).exp());

            if update {
                let mut clue_filled = true;
                for y in 0..height {
                    for x in 0..width {
                        if has_clue[(Y(y), X(x))] && problem[(Y(y), X(x))] == NO_CLUE {
                            clue_filled = false;
                        }
                    }
                }
                if field.fully_solved() && clue_filled {
                    return Some(problem);
                }

                applicable_cands.push((loc, clue, energy, n_clues2, field));
            }
            problem[loc] = previous_clue;

            if applicable_cands.len() >= 1 {
                break;
            }
        }

        if applicable_cands.len() >= 1 {
            let mut best_cand = applicable_cands.swap_remove(0);
            while applicable_cands.len() > 0 {
                let cand2 = applicable_cands.swap_remove(0);
                if best_cand.3 < cand2.3 {
                    best_cand = cand2;
                }
            }

            let (loc, clue, energy, n_clues2, field2) = best_cand;

            current_energy = energy;
            n_clues = n_clues2;
            problem[loc] = clue;
            field = field2;

            let (Y(y), X(x)) = loc;
            let loc2 = (Y(height - 1 - y), X(width - 1 - x));
            if clue == NO_CLUE {
                if problem[loc2] == NO_CLUE {
                    has_clue[loc] = false;
                    has_clue[loc2] = false;
                }
            } else {
                has_clue[loc] = true;
                has_clue[loc2] = true;
            }
        } else {
            break;
        }

        temperature *= 0.995f64;
    }

    None
}

fn solve_test<'a, 'b>(
    problem: &Grid<Clue>,
    has_clue: &Grid<bool>,
    dic: &'a Dictionary,
    consecutive_dic: &'b ConsecutiveRegionDictionary,
) -> Field<'a, 'b> {
    let height = problem.height();
    let width = problem.width();
    let mut ret = Field::new(height, width, dic, consecutive_dic);

    for y in 0..height {
        for x in 0..width {
            let clue = problem[(Y(y), X(x))];
            if clue != NO_CLUE {
                ret.add_clue((Y(y), X(x)), clue);
            } else if has_clue[(Y(y), X(x))] {
                ret.decide((Y(y), X(x)), Cell::White);
            }

            if ret.inconsistent() {
                return ret;
            }
        }
    }

    ret.solve();
    ret.trial_and_error();

    ret
}
<|endoftext|>


<commit_before>// Copyright (C) 2016-2017 Sebastian Dröge <sebastian@centricular.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::fmt;
use std::mem;
use std::ffi::{CStr, CString};
use value::*;
use miniobject::*;

use glib;
use gobject;
use gst;

pub trait Tag<'a> {
    type TagType: ValueType<'a>;
    fn tag_name() -> &'static str;
}

macro_rules! impl_tag(
    ($name:ident, $t:ty, $tag:expr) => {
        pub struct $name;
        impl<'a> Tag<'a> for $name {
            type TagType = $t;
            fn tag_name() -> &'static str {
                $tag
            }
        }
    };
);

impl_tag!(Title, &'a str, "title");
impl_tag!(Album, &'a str, "album");
impl_tag!(Artist, &'a str, "artist");
impl_tag!(Encoder, &'a str, "encoder");
impl_tag!(AudioCodec, &'a str, "audio-codec");
impl_tag!(VideoCodec, &'a str, "video-codec");
impl_tag!(SubtitleCodec, &'a str, "subtitle-codec");
impl_tag!(ContainerFormat, &'a str, "container-format");
// TODO: Should ideally enforce this to be ISO-639
impl_tag!(LanguageCode, &'a str, "language-code");
impl_tag!(Duration, u64, "duration");
impl_tag!(NominalBitrate, u32, "nominal-bitrate");

pub enum MergeMode {
    ReplaceAll,
    Replace,
    Append,
    Prepend,
    Keep,
    KeepAll,
}

impl MergeMode {
    fn to_ffi(&self) -> gst::GstTagMergeMode {
        match *self {
            MergeMode::ReplaceAll => gst::GST_TAG_MERGE_REPLACE_ALL,
            MergeMode::Replace => gst::GST_TAG_MERGE_REPLACE,
            MergeMode::Append => gst::GST_TAG_MERGE_APPEND,
            MergeMode::Prepend => gst::GST_TAG_MERGE_PREPEND,
            MergeMode::Keep => gst::GST_TAG_MERGE_KEEP,
            MergeMode::KeepAll => gst::GST_TAG_MERGE_KEEP_ALL,
        }
    }
}

#[derive(Eq)]
pub struct TagList(*mut gst::GstTagList);

unsafe impl MiniObject for TagList {
    type PtrType = gst::GstTagList;

    unsafe fn as_ptr(&self) -> *mut gst::GstTagList {
        self.0
    }

    unsafe fn replace_ptr(&mut self, ptr: *mut gst::GstTagList) {
        self.0 = ptr
    }

    unsafe fn from_ptr(ptr: *mut gst::GstTagList) -> Self {
        TagList(ptr)
    }
}

impl TagList {
    pub fn new() -> GstRc<Self> {
        unsafe { GstRc::from_owned_ptr(gst::gst_tag_list_new_empty()) }
    }

    pub fn add<'a, T: Tag<'a>>(&mut self, value: T::TagType, mode: MergeMode)
        where Value: From<<T as Tag<'a>>::TagType>
    {
        unsafe {
            let v = Value::from(value);
            let mut gvalue = v.into_raw();
            let tag_name = CString::new(T::tag_name()).unwrap();

            gst::gst_tag_list_add_value(self.0, mode.to_ffi(), tag_name.as_ptr(), &gvalue);

            gobject::g_value_unset(&mut gvalue);
        }
    }

    pub fn get<'a, T: Tag<'a>>(&self) -> Option<TypedValue<T::TagType>>
        where Value: From<<T as Tag<'a>>::TagType>
    {
        unsafe {
            let mut gvalue = mem::zeroed();
            let tag_name = CString::new(T::tag_name()).unwrap();

            let found = gst::gst_tag_list_copy_value(&mut gvalue, self.0, tag_name.as_ptr());

            if found == glib::GFALSE {
                return None;
            }

            let res = match Value::from_raw(gvalue) {
                Some(value) => TypedValue::from_value(value),
                None => None,
            };

            res
        }
    }

    pub fn get_index<'a, T: Tag<'a>>(&'a self, idx: u32) -> Option<TypedValueRef<'a, T::TagType>>
        where Value: From<<T as Tag<'a>>::TagType>
    {
        unsafe {
            let tag_name = CString::new(T::tag_name()).unwrap();

            let value = gst::gst_tag_list_get_value_index(self.0, tag_name.as_ptr(), idx);

            if value.is_null() {
                return None;
            }

            let res = match ValueRef::from_ptr(value) {
                Some(value) => TypedValueRef::from_value_ref(value),
                None => None,
            };

            res
        }
    }

    pub fn to_string(&self) -> String {
        unsafe {
            let ptr = gst::gst_tag_list_to_string(self.0);
            let s = CStr::from_ptr(ptr).to_str().unwrap().into();
            glib::g_free(ptr as glib::gpointer);

            s
        }
    }
}

impl fmt::Debug for TagList {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(&self.to_string())
    }
}

impl PartialEq for TagList {
    fn eq(&self, other: &TagList) -> bool {
        (unsafe { gst::gst_tag_list_is_equal(self.0, other.0) } == glib::GTRUE)
    }
}

unsafe impl Sync for TagList {}
unsafe impl Send for TagList {}

#[cfg(test)]
mod tests {
    use super::*;
    use std::ptr;

    fn init() {
        unsafe {
            gst::gst_init(ptr::null_mut(), ptr::null_mut());
        }
    }

    #[test]
    fn test_add() {
        init();

        let mut tags = TagList::new();
        assert_eq!(tags.to_string(), "taglist;");
        {
            let tags = tags.get_mut().unwrap();
            tags.add::<Title>("some title".into(), MergeMode::Append);
            tags.add::<Duration>((1000u64 * 1000 * 1000 * 120).into(), MergeMode::Append);
        }
        assert_eq!(tags.to_string(),
                   "taglist, title=(string)\"some\\ title\", duration=(guint64)120000000000;");
    }

    #[test]
    fn test_get() {
        init();

        let mut tags = TagList::new();
        assert_eq!(tags.to_string(), "taglist;");
        {
            let tags = tags.get_mut().unwrap();
            tags.add::<Title>("some title".into(), MergeMode::Append);
            tags.add::<Duration>((1000u64 * 1000 * 1000 * 120).into(), MergeMode::Append);
        }

        assert_eq!(tags.get::<Title>().unwrap().get(), "some title");
        assert_eq!(tags.get::<Duration>().unwrap().get(),
                   (1000u64 * 1000 * 1000 * 120));
        assert_eq!(tags.get_index::<Title>(0).unwrap().get(), "some title");
        assert_eq!(tags.get_index::<Duration>(0).unwrap().get(),
                   (1000u64 * 1000 * 1000 * 120));
    }
}
<commit_msg>Remove some unneeded trait bounds in the taglist bindings<commit_after>// Copyright (C) 2016-2017 Sebastian Dröge <sebastian@centricular.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::fmt;
use std::mem;
use std::ffi::{CStr, CString};
use value::*;
use miniobject::*;

use glib;
use gobject;
use gst;

pub trait Tag<'a> {
    type TagType: ValueType<'a>;
    fn tag_name() -> &'static str;
}

macro_rules! impl_tag(
    ($name:ident, $t:ty, $tag:expr) => {
        pub struct $name;
        impl<'a> Tag<'a> for $name {
            type TagType = $t;
            fn tag_name() -> &'static str {
                $tag
            }
        }
    };
);

impl_tag!(Title, &'a str, "title");
impl_tag!(Album, &'a str, "album");
impl_tag!(Artist, &'a str, "artist");
impl_tag!(Encoder, &'a str, "encoder");
impl_tag!(AudioCodec, &'a str, "audio-codec");
impl_tag!(VideoCodec, &'a str, "video-codec");
impl_tag!(SubtitleCodec, &'a str, "subtitle-codec");
impl_tag!(ContainerFormat, &'a str, "container-format");
// TODO: Should ideally enforce this to be ISO-639
impl_tag!(LanguageCode, &'a str, "language-code");
impl_tag!(Duration, u64, "duration");
impl_tag!(NominalBitrate, u32, "nominal-bitrate");

pub enum MergeMode {
    ReplaceAll,
    Replace,
    Append,
    Prepend,
    Keep,
    KeepAll,
}

impl MergeMode {
    fn to_ffi(&self) -> gst::GstTagMergeMode {
        match *self {
            MergeMode::ReplaceAll => gst::GST_TAG_MERGE_REPLACE_ALL,
            MergeMode::Replace => gst::GST_TAG_MERGE_REPLACE,
            MergeMode::Append => gst::GST_TAG_MERGE_APPEND,
            MergeMode::Prepend => gst::GST_TAG_MERGE_PREPEND,
            MergeMode::Keep => gst::GST_TAG_MERGE_KEEP,
            MergeMode::KeepAll => gst::GST_TAG_MERGE_KEEP_ALL,
        }
    }
}

#[derive(Eq)]
pub struct TagList(*mut gst::GstTagList);

unsafe impl MiniObject for TagList {
    type PtrType = gst::GstTagList;

    unsafe fn as_ptr(&self) -> *mut gst::GstTagList {
        self.0
    }

    unsafe fn replace_ptr(&mut self, ptr: *mut gst::GstTagList) {
        self.0 = ptr
    }

    unsafe fn from_ptr(ptr: *mut gst::GstTagList) -> Self {
        TagList(ptr)
    }
}

impl TagList {
    pub fn new() -> GstRc<Self> {
        unsafe { GstRc::from_owned_ptr(gst::gst_tag_list_new_empty()) }
    }

    pub fn add<'a, T: Tag<'a>>(&mut self, value: T::TagType, mode: MergeMode)
        where T::TagType: Into<Value>
    {
        unsafe {
            let v = value.into();
            let mut gvalue = v.into_raw();
            let tag_name = CString::new(T::tag_name()).unwrap();

            gst::gst_tag_list_add_value(self.0, mode.to_ffi(), tag_name.as_ptr(), &gvalue);

            gobject::g_value_unset(&mut gvalue);
        }
    }

    pub fn get<'a, T: Tag<'a>>(&self) -> Option<TypedValue<T::TagType>>
    {
        unsafe {
            let mut gvalue = mem::zeroed();
            let tag_name = CString::new(T::tag_name()).unwrap();

            let found = gst::gst_tag_list_copy_value(&mut gvalue, self.0, tag_name.as_ptr());

            if found == glib::GFALSE {
                return None;
            }

            let res = match Value::from_raw(gvalue) {
                Some(value) => TypedValue::from_value(value),
                None => None,
            };

            res
        }
    }

    pub fn get_index<'a, T: Tag<'a>>(&'a self, idx: u32) -> Option<TypedValueRef<'a, T::TagType>>
    {
        unsafe {
            let tag_name = CString::new(T::tag_name()).unwrap();

            let value = gst::gst_tag_list_get_value_index(self.0, tag_name.as_ptr(), idx);

            if value.is_null() {
                return None;
            }

            let res = match ValueRef::from_ptr(value) {
                Some(value) => TypedValueRef::from_value_ref(value),
                None => None,
            };

            res
        }
    }

    pub fn to_string(&self) -> String {
        unsafe {
            let ptr = gst::gst_tag_list_to_string(self.0);
            let s = CStr::from_ptr(ptr).to_str().unwrap().into();
            glib::g_free(ptr as glib::gpointer);

            s
        }
    }
}

impl fmt::Debug for TagList {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(&self.to_string())
    }
}

impl PartialEq for TagList {
    fn eq(&self, other: &TagList) -> bool {
        (unsafe { gst::gst_tag_list_is_equal(self.0, other.0) } == glib::GTRUE)
    }
}

unsafe impl Sync for TagList {}
unsafe impl Send for TagList {}

#[cfg(test)]
mod tests {
    use super::*;
    use std::ptr;

    fn init() {
        unsafe {
            gst::gst_init(ptr::null_mut(), ptr::null_mut());
        }
    }

    #[test]
    fn test_add() {
        init();

        let mut tags = TagList::new();
        assert_eq!(tags.to_string(), "taglist;");
        {
            let tags = tags.get_mut().unwrap();
            tags.add::<Title>("some title".into(), MergeMode::Append);
            tags.add::<Duration>((1000u64 * 1000 * 1000 * 120).into(), MergeMode::Append);
        }
        assert_eq!(tags.to_string(),
                   "taglist, title=(string)\"some\\ title\", duration=(guint64)120000000000;");
    }

    #[test]
    fn test_get() {
        init();

        let mut tags = TagList::new();
        assert_eq!(tags.to_string(), "taglist;");
        {
            let tags = tags.get_mut().unwrap();
            tags.add::<Title>("some title".into(), MergeMode::Append);
            tags.add::<Duration>((1000u64 * 1000 * 1000 * 120).into(), MergeMode::Append);
        }

        assert_eq!(tags.get::<Title>().unwrap().get(), "some title");
        assert_eq!(tags.get::<Duration>().unwrap().get(),
                   (1000u64 * 1000 * 1000 * 120));
        assert_eq!(tags.get_index::<Title>(0).unwrap().get(), "some title");
        assert_eq!(tags.get_index::<Duration>(0).unwrap().get(),
                   (1000u64 * 1000 * 1000 * 120));
    }
}
<|endoftext|>


<commit_before>use anyhow::anyhow;
use notify::{self, DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher};
use sha2::{Digest, Sha256};
use spirq::ty::{DescriptorType, ImageArrangement, ScalarType, Type, VectorType};
use spirq::{ExecutionModel, SpirvBinary};
use std::collections::HashMap;
use std::collections::{btree_map::Entry, BTreeMap};
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::sync::mpsc::{self, Receiver};
use std::sync::Mutex;
use std::time::{Duration, Instant};

pub enum ShaderSource {
    Inline(String),
    Files(Vec<PathBuf>),
}
impl ShaderSource {
    pub fn new(directory: PathBuf, mut filenames: Vec<PathBuf>) -> Self {
        DIRECTORY_WATCHER.lock().unwrap().watch(&directory);

        for filename in &mut filenames {
            *filename = std::fs::canonicalize(directory.join(&filename)).unwrap();
        }
        ShaderSource::Files(filenames)
    }
    pub(crate) fn load(&self) -> Result<String, anyhow::Error> {
        match self {
            ShaderSource::Inline(s) => Ok(s.clone()),
            ShaderSource::Files(fs) => {
                let mut contents = String::new();
                for filename in fs {
                    File::open(filename)?.read_to_string(&mut contents)?;
                }
                Ok(contents)
            }
        }
    }
    pub(crate) fn needs_update(&self, last_update: Instant) -> bool {
        match self {
            ShaderSource::Inline(_) => false,
            ShaderSource::Files(v) => {
                let mut directory_watcher = DIRECTORY_WATCHER.lock().unwrap();
                directory_watcher.detect_changes();
                v.iter()
                    .filter_map(|f| directory_watcher.last_modifications.get(f))
                    .any(|&t| t > last_update)
            }
        }
    }
}

pub(crate) struct ShaderSetInner {
    pub vertex: Option<Vec<u32>>,
    pub fragment: Option<Vec<u32>>,
    pub compute: Option<Vec<u32>>,

    pub input_attributes: Vec<wgpu::VertexAttributeDescriptor>,
    pub layout_descriptor: Vec<wgpu::BindGroupLayoutEntry>,
    pub desc_names: Vec<Option<String>>,
    pub digest: Vec<u8>,
}
impl ShaderSetInner {
    pub fn simple(vsrc: String, fsrc: String) -> Result<Self, anyhow::Error> {
        let vertex = create_vertex_shader(&vsrc)?;
        let fragment = create_fragment_shader(&fsrc)?;
        let digest = Sha256::digest(format!("v={}\0f={}", vsrc, fsrc).as_bytes()).to_vec();
        let (input_attributes, desc_names, layout_descriptor) =
            crate::reflect(&[&vertex[..], &fragment[..]])?;

        Ok(Self {
            vertex: Some(vertex),
            fragment: Some(fragment),
            compute: None,
            desc_names,
            layout_descriptor,
            input_attributes,
            digest,
        })
    }

    pub fn compute_only(src: String) -> Result<Self, anyhow::Error> {
        let compute = create_compute_shader(&src)?;
        let digest = Sha256::digest(format!("c={}", src).as_bytes()).to_vec();
        let (input_attributes, desc_names, layout_descriptor) = crate::reflect(&[&compute[..]])?;
        assert!(input_attributes.is_empty());

        Ok(Self {
            vertex: None,
            fragment: None,
            compute: Some(compute),
            desc_names,
            layout_descriptor,
            input_attributes,
            digest,
        })
    }
}

pub(crate) struct DirectoryWatcher {
    watcher: RecommendedWatcher,
    watcher_rx: Receiver<DebouncedEvent>,
    last_modifications: HashMap<PathBuf, Instant>,
}
impl DirectoryWatcher {
    pub fn new() -> Self {
        let (tx, watcher_rx) = mpsc::channel();
        let watcher = notify::watcher(tx, Duration::from_millis(50)).unwrap();

        Self { watcher, watcher_rx, last_modifications: HashMap::new() }
    }

    pub fn detect_changes(&mut self) {
        while let Ok(event) = self.watcher_rx.try_recv() {
            if let DebouncedEvent::Write(p) = event {
                self.last_modifications.insert(p, Instant::now());
            }
        }
    }

    pub fn watch(&mut self, directory: &Path) {
        self.watcher
            .watch(directory, RecursiveMode::Recursive)
            .expect(&format!("rshader: Failed to watch path '{}'", directory.display()))
    }
}

pub struct ShaderSet {
    inner: ShaderSetInner,
    vertex_source: Option<ShaderSource>,
    fragment_source: Option<ShaderSource>,
    compute_source: Option<ShaderSource>,
    last_update: Instant,
}
impl ShaderSet {
    pub fn simple(
        vertex_source: ShaderSource,
        fragment_source: ShaderSource,
    ) -> Result<Self, anyhow::Error> {
        Ok(Self {
            inner: ShaderSetInner::simple(vertex_source.load()?, fragment_source.load()?)?,
            vertex_source: Some(vertex_source),
            fragment_source: Some(fragment_source),
            compute_source: None,
            last_update: Instant::now(),
        })
    }
    pub fn compute_only(compute_source: ShaderSource) -> Result<Self, anyhow::Error> {
        Ok(Self {
            inner: ShaderSetInner::compute_only(compute_source.load()?)?,
            vertex_source: None,
            fragment_source: None,
            compute_source: Some(compute_source),
            last_update: Instant::now(),
        })
    }

    /// Refreshes the shader if necessary. Returns whether a refresh happened.
    pub fn refresh(&mut self) -> bool {
        if !self.vertex_source.as_ref().map(|s| s.needs_update(self.last_update)).unwrap_or(false)
            && !self
                .fragment_source
                .as_ref()
                .map(|s| s.needs_update(self.last_update))
                .unwrap_or(false)
            && !self
                .compute_source
                .as_ref()
                .map(|s| s.needs_update(self.last_update))
                .unwrap_or(false)
        {
            return false;
        }

        let r = || -> Result<(), anyhow::Error> {
            Ok(self.inner = match (&self.vertex_source, &self.fragment_source, &self.compute_source) {
                (Some(ref vs), Some(ref fs), None) => {
                    ShaderSetInner::simple(vs.load()?, fs.load()?)
                }
                (None, None, Some(ref cs)) => ShaderSetInner::compute_only(cs.load()?),
                _ => unreachable!(),
            }?)
        }();
        self.last_update = Instant::now();
        r.is_ok()
    }

    pub fn layout_descriptor(&self) -> wgpu::BindGroupLayoutDescriptor {
        wgpu::BindGroupLayoutDescriptor {
            entries: self.inner.layout_descriptor[..].into(),
            label: None,
        }
    }
    pub fn desc_names(&self) -> &[Option<String>] {
        &self.inner.desc_names[..]
    }
    pub fn input_attributes(&self) -> &[wgpu::VertexAttributeDescriptor] {
        &self.inner.input_attributes[..]
    }

    pub fn vertex(&self) -> &[u32] {
        self.inner.vertex.as_ref().unwrap()
    }
    pub fn fragment(&self) -> &[u32] {
        self.inner.fragment.as_ref().unwrap()
    }
    pub fn compute(&self) -> &[u32] {
        self.inner.compute.as_ref().unwrap()
    }
    pub fn digest(&self) -> &[u8] {
        &self.inner.digest
    }
}

lazy_static::lazy_static! {
    static ref DIRECTORY_WATCHER: Mutex<DirectoryWatcher> = Mutex::new(DirectoryWatcher::new());
}

#[macro_export]
#[cfg(not(feature = "dynamic_shaders"))]
macro_rules! shader_source {
    ($directory:expr, $( $filename:expr ),+ ) => {
        $crate::ShaderSource::Inline({
            let mut tmp = String::new();
            $( tmp.push_str(
                include_str!(concat!($directory, "/", $filename)));
            )*
                tmp
        })
    };
}

#[macro_export]
#[cfg(feature = "dynamic_shaders")]
macro_rules! shader_source {
    ($directory:expr, $( $filename:expr ),+ ) => {
		{
			let directory = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
				.join(file!()).parent().unwrap().join($directory);
			let mut files = Vec::new();
			$( files.push(std::path::PathBuf::from($filename)); )*

			$crate::ShaderSource::new(directory, files)
		}
    };
}

fn create_vertex_shader(source: &str) -> Result<Vec<u32>, anyhow::Error> {
    let mut glsl_compiler = shaderc::Compiler::new().unwrap();
    Ok(glsl_compiler
        .compile_into_spirv(source, shaderc::ShaderKind::Vertex, "[VERTEX]", "main", None)?
        .as_binary()
        .to_vec())
}
fn create_fragment_shader(source: &str) -> Result<Vec<u32>, anyhow::Error> {
    let mut glsl_compiler = shaderc::Compiler::new().unwrap();
    Ok(glsl_compiler
        .compile_into_spirv(source, shaderc::ShaderKind::Fragment, "[FRAGMENT]", "main", None)?
        .as_binary()
        .to_vec())
}
fn create_compute_shader(source: &str) -> Result<Vec<u32>, anyhow::Error> {
    let mut glsl_compiler = shaderc::Compiler::new().unwrap();
    Ok(glsl_compiler
        .compile_into_spirv(source, shaderc::ShaderKind::Compute, "[COMPUTE]", "main", None)?
        .as_binary()
        .to_vec())
}

fn reflect(
    stages: &[&[u32]],
) -> Result<
    (Vec<wgpu::VertexAttributeDescriptor>, Vec<Option<String>>, Vec<wgpu::BindGroupLayoutEntry>),
    anyhow::Error,
> {
    let mut binding_map: BTreeMap<u32, (Option<String>, wgpu::BindingType, wgpu::ShaderStage)> =
        BTreeMap::new();

    let mut attribute_offset = 0;
    let mut attributes = Vec::new();
    for spirv in stages.iter() {
        let spv: SpirvBinary = spirv.to_vec().into();
        let entries = spv.reflect()?;
        let manifest = &entries[0].manifest;

        let stage = match entries[0].exec_model {
            ExecutionModel::Vertex => wgpu::ShaderStage::VERTEX,
            ExecutionModel::Fragment => wgpu::ShaderStage::FRAGMENT,
            ExecutionModel::GLCompute => wgpu::ShaderStage::COMPUTE,
            _ => unimplemented!(),
        };

        if let wgpu::ShaderStage::VERTEX = stage {
            let mut inputs = BTreeMap::new();
            for input in manifest.inputs() {
                inputs.entry(u32::from(input.location.loc())).or_insert(Vec::new()).push(input);
            }
            for (shader_location, mut input) in inputs {
                input.sort_by_key(|i| u32::from(i.location.comp()));
                let i = input.last().unwrap();
                let (scalar_ty, nscalar) = match i.ty {
                    Type::Scalar(s) => (s, 1),
                    Type::Vector(VectorType { scalar_ty, nscalar }) => (scalar_ty, *nscalar),
                    _ => return Err(anyhow!("Unsupported attribute type")),
                };
                let (format, nbytes) = match (scalar_ty, nscalar + u32::from(i.location.comp())) {
                    (ScalarType::Signed(4), 1) => (wgpu::VertexFormat::Int, 4),
                    (ScalarType::Signed(4), 2) => (wgpu::VertexFormat::Int2, 8),
                    (ScalarType::Signed(4), 3) => (wgpu::VertexFormat::Int3, 12),
                    (ScalarType::Signed(4), 4) => (wgpu::VertexFormat::Int4, 16),
                    (ScalarType::Unsigned(4), 1) => (wgpu::VertexFormat::Uint, 4),
                    (ScalarType::Unsigned(4), 2) => (wgpu::VertexFormat::Uint2, 8),
                    (ScalarType::Unsigned(4), 3) => (wgpu::VertexFormat::Uint3, 12),
                    (ScalarType::Unsigned(4), 4) => (wgpu::VertexFormat::Uint4, 16),
                    (ScalarType::Float(4), 1) => (wgpu::VertexFormat::Float, 4),
                    (ScalarType::Float(4), 2) => (wgpu::VertexFormat::Float2, 8),
                    (ScalarType::Float(4), 3) => (wgpu::VertexFormat::Float3, 12),
                    (ScalarType::Float(4), 4) => (wgpu::VertexFormat::Float4, 16),
                    _ => return Err(anyhow!("Unsupported attribute type")),
                };

                attributes.push(wgpu::VertexAttributeDescriptor {
                    offset: attribute_offset,
                    format,
                    shader_location,
                });
                attribute_offset += nbytes;
            }
        }

        for desc in manifest.descs() {
            let (set, binding) = desc.desc_bind.into_inner();
            assert_eq!(set, 0);
            let name = manifest.get_desc_name(desc.desc_bind).map(ToString::to_string);
            let ty = match desc.desc_ty {
                DescriptorType::Sampler(_) => {
                    wgpu::BindingType::Sampler { filtering: true, comparison: false }
                }
                DescriptorType::UniformBuffer(..) => wgpu::BindingType::Buffer {
                    ty: wgpu::BufferBindingType::Uniform,
                    has_dynamic_offset: false,
                    min_binding_size: None,
                },
                DescriptorType::Image(_, spirq::ty::Type::Image(ty)) => {
                    wgpu::BindingType::Texture {
                        multisampled: false,
                        view_dimension: match ty.arng {
                            ImageArrangement::Image2D => wgpu::TextureViewDimension::D2,
                            ImageArrangement::Image2DArray => wgpu::TextureViewDimension::D2Array,
                            ImageArrangement::Image3D => wgpu::TextureViewDimension::D3,
                            _ => unimplemented!(),
                        },
                        sample_type: wgpu::TextureSampleType::Uint,
                    }
                }
                v => unimplemented!("{:?}", v),
            };

            match binding_map.entry(binding) {
                Entry::Vacant(v) => {
                    v.insert((name, ty, stage));
                }
                Entry::Occupied(mut e) => {
                    let (ref n, ref t, ref mut s) = e.get_mut();
                    *s = *s | stage;

                    if *n != name || *t != ty {
                        return Err(anyhow!("descriptor mismatch"));
                    }
                }
            }
        }
    }

    let mut names = Vec::new();
    let mut bindings = Vec::new();
    for (binding, (name, ty, visibility)) in binding_map.into_iter() {
        names.push(name);
        bindings.push(wgpu::BindGroupLayoutEntry { binding, visibility, ty, count: None });
    }

    Ok((attributes, names, bindings))
}
<commit_msg>Better diagnostics from rshader<commit_after>use anyhow::anyhow;
use notify::{self, DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher};
use sha2::{Digest, Sha256};
use spirq::ty::{DescriptorType, ImageArrangement, ScalarType, Type, VectorType};
use spirq::{ExecutionModel, SpirvBinary};
use std::collections::HashMap;
use std::collections::{btree_map::Entry, BTreeMap};
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::sync::mpsc::{self, Receiver};
use std::sync::Mutex;
use std::time::{Duration, Instant};

pub enum ShaderSource {
    Inline(String),
    Files(Vec<PathBuf>),
}
impl ShaderSource {
    pub fn new(directory: PathBuf, mut filenames: Vec<PathBuf>) -> Self {
        DIRECTORY_WATCHER.lock().unwrap().watch(&directory);

        for filename in &mut filenames {
            *filename = std::fs::canonicalize(directory.join(&filename)).unwrap();
        }
        ShaderSource::Files(filenames)
    }
    pub(crate) fn load(&self) -> Result<String, anyhow::Error> {
        match self {
            ShaderSource::Inline(s) => Ok(s.clone()),
            ShaderSource::Files(fs) => {
                let mut contents = String::new();
                for filename in fs {
                    File::open(filename)?.read_to_string(&mut contents)?;
                }
                Ok(contents)
            }
        }
    }
    pub(crate) fn needs_update(&self, last_update: Instant) -> bool {
        match self {
            ShaderSource::Inline(_) => false,
            ShaderSource::Files(v) => {
                let mut directory_watcher = DIRECTORY_WATCHER.lock().unwrap();
                directory_watcher.detect_changes();
                v.iter()
                    .filter_map(|f| directory_watcher.last_modifications.get(f))
                    .any(|&t| t > last_update)
            }
        }
    }
}

pub(crate) struct ShaderSetInner {
    pub vertex: Option<Vec<u32>>,
    pub fragment: Option<Vec<u32>>,
    pub compute: Option<Vec<u32>>,

    pub input_attributes: Vec<wgpu::VertexAttributeDescriptor>,
    pub layout_descriptor: Vec<wgpu::BindGroupLayoutEntry>,
    pub desc_names: Vec<Option<String>>,
    pub digest: Vec<u8>,
}
impl ShaderSetInner {
    pub fn simple(vsrc: String, fsrc: String) -> Result<Self, anyhow::Error> {
        let vertex = create_vertex_shader(&vsrc)?;
        let fragment = create_fragment_shader(&fsrc)?;
        let digest = Sha256::digest(format!("v={}\0f={}", vsrc, fsrc).as_bytes()).to_vec();
        let (input_attributes, desc_names, layout_descriptor) =
            crate::reflect(&[&vertex[..], &fragment[..]])?;

        Ok(Self {
            vertex: Some(vertex),
            fragment: Some(fragment),
            compute: None,
            desc_names,
            layout_descriptor,
            input_attributes,
            digest,
        })
    }

    pub fn compute_only(src: String) -> Result<Self, anyhow::Error> {
        let compute = create_compute_shader(&src)?;
        let digest = Sha256::digest(format!("c={}", src).as_bytes()).to_vec();
        let (input_attributes, desc_names, layout_descriptor) = crate::reflect(&[&compute[..]])?;
        assert!(input_attributes.is_empty());

        Ok(Self {
            vertex: None,
            fragment: None,
            compute: Some(compute),
            desc_names,
            layout_descriptor,
            input_attributes,
            digest,
        })
    }
}

pub(crate) struct DirectoryWatcher {
    watcher: RecommendedWatcher,
    watcher_rx: Receiver<DebouncedEvent>,
    last_modifications: HashMap<PathBuf, Instant>,
}
impl DirectoryWatcher {
    pub fn new() -> Self {
        let (tx, watcher_rx) = mpsc::channel();
        let watcher = notify::watcher(tx, Duration::from_millis(50)).unwrap();

        Self { watcher, watcher_rx, last_modifications: HashMap::new() }
    }

    pub fn detect_changes(&mut self) {
        while let Ok(event) = self.watcher_rx.try_recv() {
            if let DebouncedEvent::Write(p) = event {
                self.last_modifications.insert(p, Instant::now());
            }
        }
    }

    pub fn watch(&mut self, directory: &Path) {
        self.watcher
            .watch(directory, RecursiveMode::Recursive)
            .expect(&format!("rshader: Failed to watch path '{}'", directory.display()))
    }
}

pub struct ShaderSet {
    inner: ShaderSetInner,
    vertex_source: Option<ShaderSource>,
    fragment_source: Option<ShaderSource>,
    compute_source: Option<ShaderSource>,
    last_update: Instant,
}
impl ShaderSet {
    pub fn simple(
        vertex_source: ShaderSource,
        fragment_source: ShaderSource,
    ) -> Result<Self, anyhow::Error> {
        Ok(Self {
            inner: ShaderSetInner::simple(vertex_source.load()?, fragment_source.load()?)?,
            vertex_source: Some(vertex_source),
            fragment_source: Some(fragment_source),
            compute_source: None,
            last_update: Instant::now(),
        })
    }
    pub fn compute_only(compute_source: ShaderSource) -> Result<Self, anyhow::Error> {
        Ok(Self {
            inner: ShaderSetInner::compute_only(compute_source.load()?)?,
            vertex_source: None,
            fragment_source: None,
            compute_source: Some(compute_source),
            last_update: Instant::now(),
        })
    }

    /// Refreshes the shader if necessary. Returns whether a refresh happened.
    pub fn refresh(&mut self) -> bool {
        if !self.vertex_source.as_ref().map(|s| s.needs_update(self.last_update)).unwrap_or(false)
            && !self
                .fragment_source
                .as_ref()
                .map(|s| s.needs_update(self.last_update))
                .unwrap_or(false)
            && !self
                .compute_source
                .as_ref()
                .map(|s| s.needs_update(self.last_update))
                .unwrap_or(false)
        {
            return false;
        }

        let r = || -> Result<(), anyhow::Error> {
            Ok(self.inner = match (&self.vertex_source, &self.fragment_source, &self.compute_source) {
                (Some(ref vs), Some(ref fs), None) => {
                    ShaderSetInner::simple(vs.load()?, fs.load()?)
                }
                (None, None, Some(ref cs)) => ShaderSetInner::compute_only(cs.load()?),
                _ => unreachable!(),
            }?)
        }();
        self.last_update = Instant::now();
        r.is_ok()
    }

    pub fn layout_descriptor(&self) -> wgpu::BindGroupLayoutDescriptor {
        wgpu::BindGroupLayoutDescriptor {
            entries: self.inner.layout_descriptor[..].into(),
            label: None,
        }
    }
    pub fn desc_names(&self) -> &[Option<String>] {
        &self.inner.desc_names[..]
    }
    pub fn input_attributes(&self) -> &[wgpu::VertexAttributeDescriptor] {
        &self.inner.input_attributes[..]
    }

    pub fn vertex(&self) -> &[u32] {
        self.inner.vertex.as_ref().unwrap()
    }
    pub fn fragment(&self) -> &[u32] {
        self.inner.fragment.as_ref().unwrap()
    }
    pub fn compute(&self) -> &[u32] {
        self.inner.compute.as_ref().unwrap()
    }
    pub fn digest(&self) -> &[u8] {
        &self.inner.digest
    }
}

lazy_static::lazy_static! {
    static ref DIRECTORY_WATCHER: Mutex<DirectoryWatcher> = Mutex::new(DirectoryWatcher::new());
}

#[macro_export]
#[cfg(not(feature = "dynamic_shaders"))]
macro_rules! shader_source {
    ($directory:expr, $( $filename:expr ),+ ) => {
        $crate::ShaderSource::Inline({
            let mut tmp = String::new();
            $( tmp.push_str(
                include_str!(concat!($directory, "/", $filename)));
            )*
                tmp
        })
    };
}

#[macro_export]
#[cfg(feature = "dynamic_shaders")]
macro_rules! shader_source {
    ($directory:expr, $( $filename:expr ),+ ) => {
		{
			let directory = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
				.join(file!()).parent().unwrap().join($directory);
			let mut files = Vec::new();
			$( files.push(std::path::PathBuf::from($filename)); )*

			$crate::ShaderSource::new(directory, files)
		}
    };
}

fn create_vertex_shader(source: &str) -> Result<Vec<u32>, anyhow::Error> {
    let mut glsl_compiler = shaderc::Compiler::new().unwrap();
    Ok(glsl_compiler
        .compile_into_spirv(source, shaderc::ShaderKind::Vertex, "[VERTEX]", "main", None)?
        .as_binary()
        .to_vec())
}
fn create_fragment_shader(source: &str) -> Result<Vec<u32>, anyhow::Error> {
    let mut glsl_compiler = shaderc::Compiler::new().unwrap();
    Ok(glsl_compiler
        .compile_into_spirv(source, shaderc::ShaderKind::Fragment, "[FRAGMENT]", "main", None)?
        .as_binary()
        .to_vec())
}
fn create_compute_shader(source: &str) -> Result<Vec<u32>, anyhow::Error> {
    let mut glsl_compiler = shaderc::Compiler::new().unwrap();
    Ok(glsl_compiler
        .compile_into_spirv(source, shaderc::ShaderKind::Compute, "[COMPUTE]", "main", None)?
        .as_binary()
        .to_vec())
}

fn reflect(
    stages: &[&[u32]],
) -> Result<
    (Vec<wgpu::VertexAttributeDescriptor>, Vec<Option<String>>, Vec<wgpu::BindGroupLayoutEntry>),
    anyhow::Error,
> {
    let mut binding_map: BTreeMap<u32, (Option<String>, wgpu::BindingType, wgpu::ShaderStage)> =
        BTreeMap::new();

    let mut attribute_offset = 0;
    let mut attributes = Vec::new();
    for spirv in stages.iter() {
        let spv: SpirvBinary = spirv.to_vec().into();
        let entries = spv.reflect()?;
        let manifest = &entries[0].manifest;

        let stage = match entries[0].exec_model {
            ExecutionModel::Vertex => wgpu::ShaderStage::VERTEX,
            ExecutionModel::Fragment => wgpu::ShaderStage::FRAGMENT,
            ExecutionModel::GLCompute => wgpu::ShaderStage::COMPUTE,
            _ => unimplemented!(),
        };

        if let wgpu::ShaderStage::VERTEX = stage {
            let mut inputs = BTreeMap::new();
            for input in manifest.inputs() {
                inputs.entry(u32::from(input.location.loc())).or_insert(Vec::new()).push(input);
            }
            for (shader_location, mut input) in inputs {
                input.sort_by_key(|i| u32::from(i.location.comp()));
                let i = input.last().unwrap();
                let (scalar_ty, nscalar) = match i.ty {
                    Type::Scalar(s) => (s, 1),
                    Type::Vector(VectorType { scalar_ty, nscalar }) => (scalar_ty, *nscalar),
                    _ => return Err(anyhow!("Unsupported attribute type")),
                };
                let (format, nbytes) = match (scalar_ty, nscalar + u32::from(i.location.comp())) {
                    (ScalarType::Signed(4), 1) => (wgpu::VertexFormat::Int, 4),
                    (ScalarType::Signed(4), 2) => (wgpu::VertexFormat::Int2, 8),
                    (ScalarType::Signed(4), 3) => (wgpu::VertexFormat::Int3, 12),
                    (ScalarType::Signed(4), 4) => (wgpu::VertexFormat::Int4, 16),
                    (ScalarType::Unsigned(4), 1) => (wgpu::VertexFormat::Uint, 4),
                    (ScalarType::Unsigned(4), 2) => (wgpu::VertexFormat::Uint2, 8),
                    (ScalarType::Unsigned(4), 3) => (wgpu::VertexFormat::Uint3, 12),
                    (ScalarType::Unsigned(4), 4) => (wgpu::VertexFormat::Uint4, 16),
                    (ScalarType::Float(4), 1) => (wgpu::VertexFormat::Float, 4),
                    (ScalarType::Float(4), 2) => (wgpu::VertexFormat::Float2, 8),
                    (ScalarType::Float(4), 3) => (wgpu::VertexFormat::Float3, 12),
                    (ScalarType::Float(4), 4) => (wgpu::VertexFormat::Float4, 16),
                    _ => return Err(anyhow!("Unsupported attribute type")),
                };

                attributes.push(wgpu::VertexAttributeDescriptor {
                    offset: attribute_offset,
                    format,
                    shader_location,
                });
                attribute_offset += nbytes;
            }
        }

        for desc in manifest.descs() {
            let (set, binding) = desc.desc_bind.into_inner();
            assert_eq!(set, 0);
            let name = manifest.get_desc_name(desc.desc_bind).map(ToString::to_string);
            let ty = match desc.desc_ty {
                DescriptorType::Sampler(_) => {
                    wgpu::BindingType::Sampler { filtering: true, comparison: false }
                }
                DescriptorType::UniformBuffer(..) => wgpu::BindingType::Buffer {
                    ty: wgpu::BufferBindingType::Uniform,
                    has_dynamic_offset: false,
                    min_binding_size: None,
                },
                DescriptorType::Image(_, spirq::ty::Type::Image(ty)) => {
                    wgpu::BindingType::Texture {
                        multisampled: false,
                        view_dimension: match ty.arng {
                            ImageArrangement::Image2D => wgpu::TextureViewDimension::D2,
                            ImageArrangement::Image2DArray => wgpu::TextureViewDimension::D2Array,
                            ImageArrangement::Image3D => wgpu::TextureViewDimension::D3,
                            _ => unimplemented!(),
                        },
                        sample_type: wgpu::TextureSampleType::Uint,
                    }
                }
                v => unimplemented!("{:?}", v),
            };

            match binding_map.entry(binding) {
                Entry::Vacant(v) => {
                    v.insert((name, ty, stage));
                }
                Entry::Occupied(mut e) => {
                    let (ref n, ref t, ref mut s) = e.get_mut();
                    *s = *s | stage;

                    if *n != name {
                        return Err(anyhow!("descriptor mismatch {} vs {}",
                            n.as_ref().unwrap_or(&"<unamed>".to_string()), name.unwrap_or("<unamed>".to_string())));
                    }
                    if *t != ty {
                        return Err(anyhow!("descriptor mismatch for {}: {:?} vs {:?}",
                            n.as_ref().unwrap_or(&"<unamed>".to_string()), t, ty));
                    }
                }
            }
        }
    }

    let mut names = Vec::new();
    let mut bindings = Vec::new();
    for (binding, (name, ty, visibility)) in binding_map.into_iter() {
        names.push(name);
        bindings.push(wgpu::BindGroupLayoutEntry { binding, visibility, ty, count: None });
    }

    Ok((attributes, names, bindings))
}
<|endoftext|>