<commit_before>use bzip2::bufread::BzDecoder;
use error::*;
use flate2::bufread::MultiGzDecoder;
use std::fs::OpenOptions;
use std::io::prelude::*;
use std::io::{BufReader, SeekFrom};
use std::path::Path;
use xz2::bufread::XzDecoder;

/// Configure behaviour of the [`file_open_read_with_options`] function
///
/// [`file_open_read_with_options`]: ./fn.file_open_read_with_options.html
#[derive(Clone, Debug)]
pub struct ReadOptions {
    buffer_capacity: Option<usize>,
    open_options: OpenOptions,
}

impl ReadOptions {
    /// Create a new `ReadOptions` with default settings
    pub fn new() -> Self {
        Self::default()
    }

    /// Sets the capacity of the [`BufReader`] to `capacity` in Bytes.
    ///
    /// [`BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html
    pub fn set_buffer_capacity(mut self, capacity: usize) -> Self {
        self.buffer_capacity = Some(capacity);
        self
    }

    /// Specify a set of [`OpenOptions`] to use
    ///
    /// The option `read` will always be overwritten to `true` and `write` will always be set to
    /// `false`.
    ///
    /// [`OpenOptions`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html
    pub fn set_open_options(mut self, open_options: OpenOptions) -> Self {
        self.open_options = open_options;
        self
    }
}

impl Default for ReadOptions {
    fn default() -> Self {
        Self {
            buffer_capacity: None,
            open_options: OpenOptions::new(),
        }
    }
}

/// Create reader for uncompressed or compressed files transparently
///
/// See [`file_open_read_with_options`] for the full documentation.
///
/// [`file_open_read_with_options`]: ./fn.file_open_read_with_options.html
pub fn file_open_read<P>(file: P) -> Result<Box<Read>>
where
    P: AsRef<Path>,
{
    file_open_read_with_option_do(file.as_ref(), ReadOptions::default())
}

/// Create reader for uncompressed or compressed files transparently
///
/// This function opens the given `file` and tries to determine the filetype by reading the magic
/// bytes from the start of the file. If a known archive type, like xz, gz, or bz2, is found this
/// function will transparent create a reader which decompresses the data on the fly.
///
/// File I/O will always be buffered using a [`BufReader`].
///
/// The behaviour of this function can be configured using [`ReadOptions`]. See the documentation
/// on the struct for details.
///
/// [`BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html
/// [`ReadOptions`]: ./struct.ReadOptions.html
pub fn file_open_read_with_options<P>(file: P, options: ReadOptions) -> Result<Box<Read>>
where
    P: AsRef<Path>,
{
    file_open_read_with_option_do(file.as_ref(), options)
}

fn file_open_read_with_option_do(file: &Path, mut options: ReadOptions) -> Result<Box<Read>> {
    if !file.is_file() {
        return Err(ErrorKind::PathNotAFile(file.to_path_buf()).into());
    }

    let f = options
        .open_options
        .read(true)
        .write(false)
        .open(file)
        .chain_err(|| format!("Could not open file {:?}", file))?;
    let mut bufread = if let Some(size) = options.buffer_capacity {
        BufReader::with_capacity(size, f)
    } else {
        BufReader::new(f)
    };

    // read magic bytes
    let mut buffer = [0; 6];
    bufread.read_exact(&mut buffer).chain_err(|| {
        format!("Failed to read file header of {:?}", file)
    })?;
    // reset the read position
    bufread.seek(SeekFrom::Start(0)).chain_err(
        || "Failed to seek to start of file.",
    )?;

    // check if file if XZ compressed
    if buffer[..6] == [0xfd, '7' as u8, 'z' as u8, 'X' as u8, 'Z' as u8, 0x00] {
        debug!("File {:?} is detected to have type `xz`", file);
        Ok(Box::new(XzDecoder::new(bufread)))
    } else if buffer[..2] == [0x1f, 0x8b] {
        debug!("File {:?} is detected to have type `gz`", file);
        Ok(Box::new(MultiGzDecoder::new(bufread).chain_err(
            || "Failed to create the gz reader",
        )?))
    } else if buffer[..3] == ['B' as u8, 'Z' as u8, 'h' as u8] {
        debug!("File {:?} is detected to have type `bz2`", file);
        Ok(Box::new(BzDecoder::new(bufread)))
    } else {
        debug!("Open file {:?} as plaintext", file);
        Ok(Box::new(bufread))
    }
}
<commit_msg>Fix doc: Put dots at the end of the first lines<commit_after>use bzip2::bufread::BzDecoder;
use error::*;
use flate2::bufread::MultiGzDecoder;
use std::fs::OpenOptions;
use std::io::prelude::*;
use std::io::{BufReader, SeekFrom};
use std::path::Path;
use xz2::bufread::XzDecoder;

/// Configure behaviour of the [`file_open_read_with_options`] function.
///
/// [`file_open_read_with_options`]: ./fn.file_open_read_with_options.html
#[derive(Clone, Debug)]
pub struct ReadOptions {
    buffer_capacity: Option<usize>,
    open_options: OpenOptions,
}

impl ReadOptions {
    /// Create a new `ReadOptions` with default settings.
    pub fn new() -> Self {
        Self::default()
    }

    /// Sets the capacity of the [`BufReader`] to `capacity` in Bytes.
    ///
    /// [`BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html
    pub fn set_buffer_capacity(mut self, capacity: usize) -> Self {
        self.buffer_capacity = Some(capacity);
        self
    }

    /// Specify a set of [`OpenOptions`] to use.
    ///
    /// The option `read` will always be overwritten to `true` and `write` will always be set to
    /// `false`.
    ///
    /// [`OpenOptions`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html
    pub fn set_open_options(mut self, open_options: OpenOptions) -> Self {
        self.open_options = open_options;
        self
    }
}

impl Default for ReadOptions {
    fn default() -> Self {
        Self {
            buffer_capacity: None,
            open_options: OpenOptions::new(),
        }
    }
}

/// Create reader for uncompressed or compressed files transparently.
///
/// See [`file_open_read_with_options`] for the full documentation.
///
/// [`file_open_read_with_options`]: ./fn.file_open_read_with_options.html
pub fn file_open_read<P>(file: P) -> Result<Box<Read>>
where
    P: AsRef<Path>,
{
    file_open_read_with_option_do(file.as_ref(), ReadOptions::default())
}

/// Create reader for uncompressed or compressed files transparently.
///
/// This function opens the given `file` and tries to determine the filetype by reading the magic
/// bytes from the start of the file. If a known archive type, like xz, gz, or bz2, is found this
/// function will transparent create a reader which decompresses the data on the fly.
///
/// File I/O will always be buffered using a [`BufReader`].
///
/// The behaviour of this function can be configured using [`ReadOptions`]. See the documentation
/// on the struct for details.
///
/// [`BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html
/// [`ReadOptions`]: ./struct.ReadOptions.html
pub fn file_open_read_with_options<P>(file: P, options: ReadOptions) -> Result<Box<Read>>
where
    P: AsRef<Path>,
{
    file_open_read_with_option_do(file.as_ref(), options)
}

fn file_open_read_with_option_do(file: &Path, mut options: ReadOptions) -> Result<Box<Read>> {
    if !file.is_file() {
        return Err(ErrorKind::PathNotAFile(file.to_path_buf()).into());
    }

    let f = options
        .open_options
        .read(true)
        .write(false)
        .open(file)
        .chain_err(|| format!("Could not open file {:?}", file))?;
    let mut bufread = if let Some(size) = options.buffer_capacity {
        BufReader::with_capacity(size, f)
    } else {
        BufReader::new(f)
    };

    // read magic bytes
    let mut buffer = [0; 6];
    bufread.read_exact(&mut buffer).chain_err(|| {
        format!("Failed to read file header of {:?}", file)
    })?;
    // reset the read position
    bufread.seek(SeekFrom::Start(0)).chain_err(
        || "Failed to seek to start of file.",
    )?;

    // check if file if XZ compressed
    if buffer[..6] == [0xfd, '7' as u8, 'z' as u8, 'X' as u8, 'Z' as u8, 0x00] {
        debug!("File {:?} is detected to have type `xz`", file);
        Ok(Box::new(XzDecoder::new(bufread)))
    } else if buffer[..2] == [0x1f, 0x8b] {
        debug!("File {:?} is detected to have type `gz`", file);
        Ok(Box::new(MultiGzDecoder::new(bufread).chain_err(
            || "Failed to create the gz reader",
        )?))
    } else if buffer[..3] == ['B' as u8, 'Z' as u8, 'h' as u8] {
        debug!("File {:?} is detected to have type `bz2`", file);
        Ok(Box::new(BzDecoder::new(bufread)))
    } else {
        debug!("Open file {:?} as plaintext", file);
        Ok(Box::new(bufread))
    }
}
<|endoftext|>


<commit_before>#![cfg(all(unix, feature = "process"))]
#![warn(rust_2018_idioms)]

use std::process::Stdio;
use std::time::Duration;
use tokio::io::AsyncReadExt;
use tokio::process::Command;
use tokio::time::sleep;
use tokio_test::assert_ok;

#[tokio::test]
async fn kill_on_drop() {
    let mut cmd = Command::new("bash");
    cmd.args(&[
        "-c",
        "
       # Fork another child that won't get killed
       sh -c 'sleep 1; echo child ran' &
       disown -a

       # Await our death
       sleep 5
       echo hello from beyond the grave
    ",
    ]);

    let mut child = cmd
        .kill_on_drop(true)
        .stdout(Stdio::piped())
        .spawn()
        .unwrap();

    sleep(Duration::from_secs(2)).await;

    let mut out = child.stdout.take().unwrap();
    drop(child);

    let mut msg = String::new();
    assert_ok!(out.read_to_string(&mut msg).await);

    assert_eq!("child ran\n", msg);
}
<commit_msg>process: skip the process_kill_on_drop test if bash is not installed. (#4079)<commit_after>#![cfg(all(unix, feature = "process"))]
#![warn(rust_2018_idioms)]

use std::io::ErrorKind;
use std::process::Stdio;
use std::time::Duration;
use tokio::io::AsyncReadExt;
use tokio::process::Command;
use tokio::time::sleep;
use tokio_test::assert_ok;

#[tokio::test]
async fn kill_on_drop() {
    let mut cmd = Command::new("bash");
    cmd.args(&[
        "-c",
        "
       # Fork another child that won't get killed
       sh -c 'sleep 1; echo child ran' &
       disown -a

       # Await our death
       sleep 5
       echo hello from beyond the grave
    ",
    ]);

    let e = cmd.kill_on_drop(true).stdout(Stdio::piped()).spawn();
    if e.is_err() && e.as_ref().unwrap_err().kind() == ErrorKind::NotFound {
        println!("bash not available; skipping test");
        return;
    }
    let mut child = e.unwrap();

    sleep(Duration::from_secs(2)).await;

    let mut out = child.stdout.take().unwrap();
    drop(child);

    let mut msg = String::new();
    assert_ok!(out.read_to_string(&mut msg).await);

    assert_eq!("child ran\n", msg);
}
<|endoftext|>


<commit_before>//! Parallelization contracts, describing requirements for data movement along dataflow edges.

use std::marker::PhantomData;

use timely_communication::{Allocate, Push, Pull, Data};
use timely_communication::allocator::Thread;

use dataflow::channels::pushers::Exchange as ExchangeObserver;
use dataflow::channels::{Message, Content};

use abomonation::Abomonation;

/// A ParallelizationContract transforms the output of a Allocate to an (Observer, Pullable).
pub trait ParallelizationContract<T: 'static, D: 'static> {
    /// Alloctes a matched pair of push and pull endpoints implementing the pact.
    fn connect<A: Allocate>(self, allocator: &mut A, identifier: usize) -> (Box<Push<(T, Content<D>)>>, Box<Pull<(T, Content<D>)>>);
}

/// A direct connection
pub struct Pipeline;
impl<T: 'static, D: 'static> ParallelizationContract<T, D> for Pipeline {
    fn connect<A: Allocate>(self, allocator: &mut A, identifier: usize) -> (Box<Push<(T, Content<D>)>>, Box<Pull<(T, Content<D>)>>) {
        // ignore &mut A and use thread allocator
        let (mut pushers, puller) = Thread::new::<Message<T, D>>();

        (Box::new(Pusher::new(pushers.pop().unwrap(), allocator.index(), allocator.index(), identifier)),
         Box::new(Puller::new(puller, allocator.index(), identifier)))
    }
}

/// An exchange between multiple observers
pub struct Exchange<D, F: Fn(&D)->u64+'static> { hash_func: F, phantom: PhantomData<D>, }
impl<D, F: Fn(&D)->u64> Exchange<D, F> {
    /// Allocates a new `Exchange` pact from a distribution function.
    pub fn new(func: F) -> Exchange<D, F> {
        Exchange {
            hash_func:  func,
            phantom:    PhantomData,
        }
    }
}

// Exchange uses a Box<Pushable> because it cannot know what type of pushable will return from the allocator.
// The PactObserver will do some buffering for Exchange, cutting down on the virtual calls, but we still
// would like to get the vectors it sends back, so that they can be re-used if possible.
impl<T: Eq+Data+Abomonation, D: Data+Abomonation, F: Fn(&D)->u64+'static> ParallelizationContract<T, D> for Exchange<D, F> {
    fn connect<A: Allocate>(self, allocator: &mut A, identifier: usize) -> (Box<Push<(T, Content<D>)>>, Box<Pull<(T, Content<D>)>>) {
        let (senders, receiver) = allocator.allocate::<Message<T, D>>();
        let senders = senders.into_iter().enumerate().map(|(i,x)| Pusher::new(x, allocator.index(), i, identifier)).collect::<Vec<_>>();
        (Box::new(ExchangeObserver::new(senders, move |_, d| (self.hash_func)(d))), Box::new(Puller::new(receiver, allocator.index(), identifier)))
    }
}

/// An exchange between multiple observers
pub struct TimeExchange<D, T, F: Fn(&T, &D)->u64+'static> { hash_func: F, phantom: PhantomData<D>, phantom_t: PhantomData<T>, }
impl<D, T, F: Fn(&T, &D)->u64> TimeExchange<D, T, F> {
    /// Allocates a new `Exchange` pact from a distribution function.
    pub fn new(func: F) -> TimeExchange<D, T, F> {
        TimeExchange {
            hash_func:  func,
            phantom:    PhantomData,
            phantom_t:  PhantomData,
        }
    }
}

impl<T: Eq+Data+Abomonation, D: Data+Abomonation, F: Fn(&T, &D)->u64+'static> ParallelizationContract<T, D> for TimeExchange<D, T, F> {
    fn connect<A: Allocate>(self, allocator: &mut A, identifier: usize) -> (Box<Push<(T, Content<D>)>>, Box<Pull<(T, Content<D>)>>) {
        let (senders, receiver) = allocator.allocate::<Message<T, D>>();
        let senders = senders.into_iter().enumerate().map(|(i,x)| Pusher::new(x, allocator.index(), i, identifier)).collect::<Vec<_>>();
        (Box::new(ExchangeObserver::new(senders, self.hash_func)), Box::new(Puller::new(receiver, allocator.index(), identifier)))
    }
}

/// Wraps a `Message<T,D>` pusher to provide a `Push<(T, Content<D>)>`.
pub struct Pusher<T, D> {
    pusher: Box<Push<Message<T, D>>>,
    channel: usize,
    counter: usize,
    source: usize,
    target: usize,
}
impl<T, D> Pusher<T, D> {
    /// Allocates a new pusher.
    pub fn new(pusher: Box<Push<Message<T, D>>>, source: usize, target: usize, channel: usize) -> Pusher<T, D> {
        Pusher {
            pusher: pusher,
            channel: channel,
            counter: 0,
            source: source,
            target: target,
        }
    }
}

impl<T, D> Push<(T, Content<D>)> for Pusher<T, D> {
    fn push(&mut self, pair: &mut Option<(T, Content<D>)>) {
        if let Some((time, data)) = pair.take() {

            ::logging::log(&::logging::MESSAGES, ::logging::MessagesEvent {
                is_send: true,
                channel: self.channel,
                source: self.source,
                target: self.target,
                seq_no: self.counter,
                length: data.len(),
            });

            let mut message = Some(Message::new(time, data, self.source, self.counter));
            self.counter += 1;
            self.pusher.push(&mut message);
            *pair = message.map(|x| (x.time, x.data));

            // Log something about (index, counter, time?, length?);
        }
        else { self.pusher.done(); }
    }
}

/// Wraps a `Message<T,D>` puller to provide a `Pull<(T, Content<D>)>`.
pub struct Puller<T, D> {
    puller: Box<Pull<Message<T, D>>>,
    current: Option<(T, Content<D>)>,
    channel: usize,
    counter: usize,
    index: usize,
}
impl<T, D> Puller<T, D> {
    /// Allocates a new `Puller`.
    pub fn new(puller: Box<Pull<Message<T, D>>>, index: usize, channel: usize) -> Puller<T, D> {
        Puller {
            puller: puller,
            channel: channel,
            current: None,
            counter: 0,
            index: index,
        }
    }
}

impl<T, D> Pull<(T, Content<D>)> for Puller<T, D> {
    fn pull(&mut self) -> &mut Option<(T, Content<D>)> {
        let mut previous = self.current.take().map(|(time, data)| Message::new(time, data, self.index, self.counter));
        self.counter += 1;

        ::std::mem::swap(&mut previous, self.puller.pull());

        if let Some(ref message) = previous.as_ref() {

            ::logging::log(&::logging::MESSAGES, ::logging::MessagesEvent {
                is_send: false,
                channel: self.channel,
                source: message.from,
                target: self.index,
                seq_no: message.seq,
                length: message.data.len(),
            });
        }

        self.current = previous.map(|message| (message.time, message.data));
        &mut self.current
    }
}
<commit_msg>Only use one PhantomData instead of two<commit_after>//! Parallelization contracts, describing requirements for data movement along dataflow edges.

use std::marker::PhantomData;

use timely_communication::{Allocate, Push, Pull, Data};
use timely_communication::allocator::Thread;

use dataflow::channels::pushers::Exchange as ExchangeObserver;
use dataflow::channels::{Message, Content};

use abomonation::Abomonation;

/// A ParallelizationContract transforms the output of a Allocate to an (Observer, Pullable).
pub trait ParallelizationContract<T: 'static, D: 'static> {
    /// Alloctes a matched pair of push and pull endpoints implementing the pact.
    fn connect<A: Allocate>(self, allocator: &mut A, identifier: usize) -> (Box<Push<(T, Content<D>)>>, Box<Pull<(T, Content<D>)>>);
}

/// A direct connection
pub struct Pipeline;
impl<T: 'static, D: 'static> ParallelizationContract<T, D> for Pipeline {
    fn connect<A: Allocate>(self, allocator: &mut A, identifier: usize) -> (Box<Push<(T, Content<D>)>>, Box<Pull<(T, Content<D>)>>) {
        // ignore &mut A and use thread allocator
        let (mut pushers, puller) = Thread::new::<Message<T, D>>();

        (Box::new(Pusher::new(pushers.pop().unwrap(), allocator.index(), allocator.index(), identifier)),
         Box::new(Puller::new(puller, allocator.index(), identifier)))
    }
}

/// An exchange between multiple observers
pub struct Exchange<D, F: Fn(&D)->u64+'static> { hash_func: F, phantom: PhantomData<D>, }
impl<D, F: Fn(&D)->u64> Exchange<D, F> {
    /// Allocates a new `Exchange` pact from a distribution function.
    pub fn new(func: F) -> Exchange<D, F> {
        Exchange {
            hash_func:  func,
            phantom:    PhantomData,
        }
    }
}

// Exchange uses a Box<Pushable> because it cannot know what type of pushable will return from the allocator.
// The PactObserver will do some buffering for Exchange, cutting down on the virtual calls, but we still
// would like to get the vectors it sends back, so that they can be re-used if possible.
impl<T: Eq+Data+Abomonation, D: Data+Abomonation, F: Fn(&D)->u64+'static> ParallelizationContract<T, D> for Exchange<D, F> {
    fn connect<A: Allocate>(self, allocator: &mut A, identifier: usize) -> (Box<Push<(T, Content<D>)>>, Box<Pull<(T, Content<D>)>>) {
        let (senders, receiver) = allocator.allocate::<Message<T, D>>();
        let senders = senders.into_iter().enumerate().map(|(i,x)| Pusher::new(x, allocator.index(), i, identifier)).collect::<Vec<_>>();
        (Box::new(ExchangeObserver::new(senders, move |_, d| (self.hash_func)(d))), Box::new(Puller::new(receiver, allocator.index(), identifier)))
    }
}

/// An exchange between multiple observers
pub struct TimeExchange<D, T, F: Fn(&T, &D)->u64+'static> { hash_func: F, phantom: PhantomData<(T, D)>, }
impl<D, T, F: Fn(&T, &D)->u64> TimeExchange<D, T, F> {
    /// Allocates a new `Exchange` pact from a distribution function.
    pub fn new(func: F) -> TimeExchange<D, T, F> {
        TimeExchange {
            hash_func:  func,
            phantom:    PhantomData,
        }
    }
}

impl<T: Eq+Data+Abomonation, D: Data+Abomonation, F: Fn(&T, &D)->u64+'static> ParallelizationContract<T, D> for TimeExchange<D, T, F> {
    fn connect<A: Allocate>(self, allocator: &mut A, identifier: usize) -> (Box<Push<(T, Content<D>)>>, Box<Pull<(T, Content<D>)>>) {
        let (senders, receiver) = allocator.allocate::<Message<T, D>>();
        let senders = senders.into_iter().enumerate().map(|(i,x)| Pusher::new(x, allocator.index(), i, identifier)).collect::<Vec<_>>();
        (Box::new(ExchangeObserver::new(senders, self.hash_func)), Box::new(Puller::new(receiver, allocator.index(), identifier)))
    }
}

/// Wraps a `Message<T,D>` pusher to provide a `Push<(T, Content<D>)>`.
pub struct Pusher<T, D> {
    pusher: Box<Push<Message<T, D>>>,
    channel: usize,
    counter: usize,
    source: usize,
    target: usize,
}
impl<T, D> Pusher<T, D> {
    /// Allocates a new pusher.
    pub fn new(pusher: Box<Push<Message<T, D>>>, source: usize, target: usize, channel: usize) -> Pusher<T, D> {
        Pusher {
            pusher: pusher,
            channel: channel,
            counter: 0,
            source: source,
            target: target,
        }
    }
}

impl<T, D> Push<(T, Content<D>)> for Pusher<T, D> {
    fn push(&mut self, pair: &mut Option<(T, Content<D>)>) {
        if let Some((time, data)) = pair.take() {

            ::logging::log(&::logging::MESSAGES, ::logging::MessagesEvent {
                is_send: true,
                channel: self.channel,
                source: self.source,
                target: self.target,
                seq_no: self.counter,
                length: data.len(),
            });

            let mut message = Some(Message::new(time, data, self.source, self.counter));
            self.counter += 1;
            self.pusher.push(&mut message);
            *pair = message.map(|x| (x.time, x.data));

            // Log something about (index, counter, time?, length?);
        }
        else { self.pusher.done(); }
    }
}

/// Wraps a `Message<T,D>` puller to provide a `Pull<(T, Content<D>)>`.
pub struct Puller<T, D> {
    puller: Box<Pull<Message<T, D>>>,
    current: Option<(T, Content<D>)>,
    channel: usize,
    counter: usize,
    index: usize,
}
impl<T, D> Puller<T, D> {
    /// Allocates a new `Puller`.
    pub fn new(puller: Box<Pull<Message<T, D>>>, index: usize, channel: usize) -> Puller<T, D> {
        Puller {
            puller: puller,
            channel: channel,
            current: None,
            counter: 0,
            index: index,
        }
    }
}

impl<T, D> Pull<(T, Content<D>)> for Puller<T, D> {
    fn pull(&mut self) -> &mut Option<(T, Content<D>)> {
        let mut previous = self.current.take().map(|(time, data)| Message::new(time, data, self.index, self.counter));
        self.counter += 1;

        ::std::mem::swap(&mut previous, self.puller.pull());

        if let Some(ref message) = previous.as_ref() {

            ::logging::log(&::logging::MESSAGES, ::logging::MessagesEvent {
                is_send: false,
                channel: self.channel,
                source: message.from,
                target: self.index,
                seq_no: message.seq,
                length: message.data.len(),
            });
        }

        self.current = previous.map(|message| (message.time, message.data));
        &mut self.current
    }
}
<|endoftext|>


<commit_before>use sass::color_value::ColorValue;
use sass::number_value::NumberValue;
use sass::value_part::ValuePart;
use sass::op::Op;

use error::Result;

use std::borrow::Cow::Borrowed;

#[derive(Debug)]
pub struct ValueTokenizer<'a> {
    value_str: &'a str,
    bytes: &'a [u8],
    offset: usize,
}

impl<'a> ValueTokenizer<'a> {
    pub fn new(value_str: &'a str) -> ValueTokenizer<'a> {
        ValueTokenizer {
            value_str: &value_str,
            bytes: &value_str.as_bytes(),
            offset: 0,
        }
    }

    pub fn parse(&mut self) -> Result<Option<ValuePart<'a>>> {
        self.skip_leading_whitespace();

        let start = self.offset;
        let mut i = self.offset;
        let limit = self.value_str.len();

        let ret = if start == limit {
            None
        } else if is_operator(self.bytes[start]) {
            self.offset = start + 1;
            Some(ValuePart::Operator(self.value_str[start..start + 1].parse().unwrap_or(Op::Plus)))
        } else if is_number(self.bytes[start]) {
            i += self.scan_while(&self.value_str[i..limit], is_number);
            self.offset = i;
            if i < limit && valid_unit_char(self.bytes[i]) {
                let unit_start = i;
                i += self.scan_while(&self.value_str[i..limit], valid_unit_char);
                self.offset = i;
                Some(ValuePart::Number(NumberValue::with_units(
                    self.value_str[start..unit_start].parse().unwrap_or(0.0),
                    Borrowed(&self.value_str[unit_start..i]),
                )))
            }
            else {
                Some(ValuePart::Number(NumberValue::from_scalar(
                    self.value_str[start..i].parse().unwrap_or(0.0)
                )))
            }
        } else if self.bytes[start] == b'$' {
            i += self.scan_while(&self.value_str[i..limit], isnt_space);
            self.offset = i;
            Some(ValuePart::Variable(Borrowed(&self.value_str[start..i])))
        } else if self.bytes[start] == b'#' {
            i += 1;
            i += self.scan_while(&self.value_str[i..limit], valid_hex_char);
            self.offset = i;
            match ColorValue::from_hex(Borrowed(&self.value_str[start..i])) {
                Ok(v) => Some(ValuePart::Color(v)),
                Err(e) => return Err(e),
            }
        } else if self.eat("rgb(") {
            let r = self.eat_integer();
            self.eat(",");
            let g = self.eat_integer();
            self.eat(",");
            let b = self.eat_integer();
            self.eat(")");

            Some(ValuePart::Color(
                ColorValue {
                    red: r, green: g, blue: b,
                    original: Borrowed(&self.value_str[start..self.offset]),
                }
            ))
        } else {
            i = start;
            i += self.scan_while(&self.value_str[start..limit], isnt_space);
            self.offset = i;
            Some(ValuePart::String(Borrowed(&self.value_str[start..i])))
        };

        Ok(ret)
    }

    fn eat_integer(&mut self) -> u8 {
        let limit = self.value_str.len();
        let mut i = self.offset;
        let color_start = i;

        i += self.scan_while(&self.value_str[i..limit], is_number);
        self.offset = i;
        self.value_str[color_start..i].parse().unwrap_or(0)
    }

    fn eat(&mut self, expected: &str) -> bool {
        for c in expected.as_bytes().iter() {
            if !self.eatch(c) {
                return false
            }
        }
        return true
    }

    fn eatch(&mut self, expected_char: &u8) -> bool {
        if self.bytes[self.offset] == *expected_char {
            self.offset += 1;
            true
        } else {
            false
        }
    }

    fn scan_while<F>(&mut self, data: &str, f: F) -> usize
            where F: Fn(u8) -> bool {
        match data.as_bytes().iter().position(|&c| !f(c)) {
            Some(i) => i,
            None => data.len()
        }
    }

    fn skip_leading_whitespace(&mut self) {
       let mut i = self.offset;
       let limit = self.value_str.len();

       while i < limit {
           let c = self.bytes[i];
           if is_space(c) {
               i += self.scan_while(&self.value_str[i..limit], is_space);
           } else {
               self.offset = i;
               return
           }
       }
       self.offset = limit;
   }
}

impl<'a> Iterator for ValueTokenizer<'a> {
    type Item = Result<ValuePart<'a>>;

    fn next(&mut self) -> Option<Result<ValuePart<'a>>> {
        if self.offset < self.value_str.len() {
            return match self.parse() {
                Ok(Some(v)) => Some(Ok(v)),
                Ok(None) => None,
                Err(e) => Some(Err(e)),
            }
        }
        None
    }
}

fn is_space(c: u8) -> bool {
    c == b' '
}

fn isnt_space(c: u8) -> bool {
    !is_space(c)
}

fn valid_unit_char(c: u8) -> bool {
    c == b'%' || (!is_space(c) && !is_operator(c))
}

fn valid_hex_char(c: u8) -> bool {
    match c {
        b'0' ... b'9' | b'a' ... b'f' => true,
        _ => false,
    }
}

fn is_number(c: u8) -> bool {
    let result = match c {
        b'0' ... b'9' | b'.' => true,
        _ => false,
    };
    result
}

fn is_operator(c: u8) -> bool {
    match c {
        b'+' | b'-' | b'*' | b'/' | b'%' | b'(' | b')' | b',' => true,
        _ => false,
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use sass::color_value::ColorValue;
    use sass::value_part::ValuePart;
    use sass::number_value::NumberValue;
    use sass::op::Op;
    use std::borrow::Cow::Borrowed;

    #[test]
    fn it_returns_string_part() {
        let mut vt = ValueTokenizer::new("foo");
        assert_eq!(Some(Ok(ValuePart::String(Borrowed(&"foo")))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_space_separated_string_parts() {
        let mut vt = ValueTokenizer::new("foo bar");
        assert_eq!(Some(Ok(ValuePart::String(Borrowed(&"foo")))), vt.next());
        assert_eq!(Some(Ok(ValuePart::String(Borrowed(&"bar")))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_variable() {
        let mut vt = ValueTokenizer::new("$foo");
        assert_eq!(Some(Ok(ValuePart::Variable(Borrowed(&"$foo")))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_variables_and_string_parts() {
        let mut vt = ValueTokenizer::new("foo $bar baz $quux");
        assert_eq!(Some(Ok(ValuePart::String(Borrowed(&"foo")))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Variable(Borrowed(&"$bar")))), vt.next());
        assert_eq!(Some(Ok(ValuePart::String(Borrowed(&"baz")))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Variable(Borrowed(&"$quux")))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_number() {
        let mut vt = ValueTokenizer::new("3");
        assert_eq!(Some(Ok(ValuePart::Number(NumberValue::from_scalar(3.0)))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_two_numbers() {
        let mut vt = ValueTokenizer::new("3 8.9");
        assert_eq!(Some(Ok(ValuePart::Number(NumberValue::from_scalar(3.0)))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Number(NumberValue::from_scalar(8.9)))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_numbers_with_units() {
        let mut vt = ValueTokenizer::new("3px");
        assert_eq!(
            Some(Ok(ValuePart::Number(NumberValue::with_units(3.0, Borrowed(&"px"))))),
            vt.next()
        );
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_numbers_with_units_but_not_parens() {
        let mut vt = ValueTokenizer::new("(3px)");
        assert_eq!(Some(Ok(ValuePart::Operator(Op::LeftParen))), vt.next());
        assert_eq!(
            Some(Ok(ValuePart::Number(NumberValue::with_units(3.0, Borrowed(&"px"))))),
            vt.next()
        );
        assert_eq!(Some(Ok(ValuePart::Operator(Op::RightParen))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_numbers_with_percents_as_units() {
        let mut vt = ValueTokenizer::new("10px 8%");
        assert_eq!(
            Some(Ok(ValuePart::Number(NumberValue::with_units(10.0, Borrowed(&"px"))))),
            vt.next()
        );
        assert_eq!(
            Some(Ok(ValuePart::Number(NumberValue::with_units(8.0, Borrowed(&"%"))))),
            vt.next()
        );
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_operator() {
        let mut vt = ValueTokenizer::new("+");
        assert_eq!(Some(Ok(ValuePart::Operator(Op::Plus))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_numbers_and_operators() {
        let mut vt = ValueTokenizer::new("6 + 75.2");
        assert_eq!(Some(Ok(ValuePart::Number(NumberValue::from_scalar(6.0)))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Operator(Op::Plus))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Number(NumberValue::from_scalar(75.2)))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_numbers_and_operators_without_spaces() {
        let mut vt = ValueTokenizer::new("6+75.2");
        assert_eq!(Some(Ok(ValuePart::Number(NumberValue::from_scalar(6.0)))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Operator(Op::Plus))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Number(NumberValue::from_scalar(75.2)))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_does_stuff_with_parens() {
        let mut vt = ValueTokenizer::new("2+(3 4)");
        assert_eq!(Some(Ok(ValuePart::Number(NumberValue::from_scalar(2.0)))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Operator(Op::Plus))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Operator(Op::LeftParen))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Number(NumberValue::from_scalar(3.0)))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Number(NumberValue::from_scalar(4.0)))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Operator(Op::RightParen))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_does_stuff_with_slash_separators() {
        let mut vt = ValueTokenizer::new("15 / 3 / 5");
        assert_eq!(Some(Ok(ValuePart::Number(NumberValue::from_scalar(15.0)))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Operator(Op::Slash))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Number(NumberValue::from_scalar(3.0)))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Operator(Op::Slash))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Number(NumberValue::from_scalar(5.0)))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_recognizes_hex() {
        let mut vt = ValueTokenizer::new("#aabbcc");
        assert_eq!(
            Some(Ok(ValuePart::Color(ColorValue {
                red: 170, green: 187, blue: 204, original: Borrowed("#aabbcc"),
            }))),
            vt.next()
        );
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_recognizes_short_hex() {
        let mut vt = ValueTokenizer::new("#cba");
        assert_eq!(
            Some(Ok(ValuePart::Color(ColorValue {
                red: 204, green: 187, blue: 170, original: Borrowed("#cba"),
            }))),
            vt.next()
        );
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_recognizes_hex_is_separate_from_parens() {
        let mut vt = ValueTokenizer::new("#cba)");
        assert_eq!(
            Some(Ok(ValuePart::Color(ColorValue {
                red: 204, green: 187, blue: 170, original: Borrowed("#cba"),
            }))),
            vt.next()
        );
        assert_eq!(Some(Ok(ValuePart::Operator(Op::RightParen))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_recognizes_rgb() {
        let mut vt = ValueTokenizer::new("rgb(10,100,73)");
        assert_eq!(
            Some(Ok(ValuePart::Color(ColorValue {
                red: 10, green: 100, blue: 73, original: Borrowed("rgb(10,100,73)"),
            }))),
            vt.next()
        );
        assert_eq!(None, vt.next());
    }
}
<commit_msg>Rename eat_integer to eat_color_u8<commit_after>use sass::color_value::ColorValue;
use sass::number_value::NumberValue;
use sass::value_part::ValuePart;
use sass::op::Op;

use error::Result;

use std::borrow::Cow::Borrowed;

#[derive(Debug)]
pub struct ValueTokenizer<'a> {
    value_str: &'a str,
    bytes: &'a [u8],
    offset: usize,
}

impl<'a> ValueTokenizer<'a> {
    pub fn new(value_str: &'a str) -> ValueTokenizer<'a> {
        ValueTokenizer {
            value_str: &value_str,
            bytes: &value_str.as_bytes(),
            offset: 0,
        }
    }

    pub fn parse(&mut self) -> Result<Option<ValuePart<'a>>> {
        self.skip_leading_whitespace();

        let start = self.offset;
        let mut i = self.offset;
        let limit = self.value_str.len();

        let ret = if start == limit {
            None
        } else if is_operator(self.bytes[start]) {
            self.offset = start + 1;
            Some(ValuePart::Operator(self.value_str[start..start + 1].parse().unwrap_or(Op::Plus)))
        } else if is_number(self.bytes[start]) {
            i += self.scan_while(&self.value_str[i..limit], is_number);
            self.offset = i;
            if i < limit && valid_unit_char(self.bytes[i]) {
                let unit_start = i;
                i += self.scan_while(&self.value_str[i..limit], valid_unit_char);
                self.offset = i;
                Some(ValuePart::Number(NumberValue::with_units(
                    self.value_str[start..unit_start].parse().unwrap_or(0.0),
                    Borrowed(&self.value_str[unit_start..i]),
                )))
            }
            else {
                Some(ValuePart::Number(NumberValue::from_scalar(
                    self.value_str[start..i].parse().unwrap_or(0.0)
                )))
            }
        } else if self.bytes[start] == b'$' {
            i += self.scan_while(&self.value_str[i..limit], isnt_space);
            self.offset = i;
            Some(ValuePart::Variable(Borrowed(&self.value_str[start..i])))
        } else if self.bytes[start] == b'#' {
            i += 1;
            i += self.scan_while(&self.value_str[i..limit], valid_hex_char);
            self.offset = i;
            match ColorValue::from_hex(Borrowed(&self.value_str[start..i])) {
                Ok(v) => Some(ValuePart::Color(v)),
                Err(e) => return Err(e),
            }
        } else if self.eat("rgb(") {
            let r = self.eat_color_u8();
            self.eat(",");
            let g = self.eat_color_u8();
            self.eat(",");
            let b = self.eat_color_u8();
            self.eat(")");

            Some(ValuePart::Color(
                ColorValue {
                    red: r, green: g, blue: b,
                    original: Borrowed(&self.value_str[start..self.offset]),
                }
            ))
        } else {
            i = start;
            i += self.scan_while(&self.value_str[start..limit], isnt_space);
            self.offset = i;
            Some(ValuePart::String(Borrowed(&self.value_str[start..i])))
        };

        Ok(ret)
    }

    fn eat_color_u8(&mut self) -> u8 {
        let limit = self.value_str.len();
        let mut i = self.offset;
        let color_start = i;

        i += self.scan_while(&self.value_str[i..limit], is_number);
        self.offset = i;
        self.value_str[color_start..i].parse().unwrap_or(0)
    }

    fn eat(&mut self, expected: &str) -> bool {
        for c in expected.as_bytes().iter() {
            if !self.eatch(c) {
                return false
            }
        }
        return true
    }

    fn eatch(&mut self, expected_char: &u8) -> bool {
        if self.bytes[self.offset] == *expected_char {
            self.offset += 1;
            true
        } else {
            false
        }
    }

    fn scan_while<F>(&mut self, data: &str, f: F) -> usize
            where F: Fn(u8) -> bool {
        match data.as_bytes().iter().position(|&c| !f(c)) {
            Some(i) => i,
            None => data.len()
        }
    }

    fn skip_leading_whitespace(&mut self) {
       let mut i = self.offset;
       let limit = self.value_str.len();

       while i < limit {
           let c = self.bytes[i];
           if is_space(c) {
               i += self.scan_while(&self.value_str[i..limit], is_space);
           } else {
               self.offset = i;
               return
           }
       }
       self.offset = limit;
   }
}

impl<'a> Iterator for ValueTokenizer<'a> {
    type Item = Result<ValuePart<'a>>;

    fn next(&mut self) -> Option<Result<ValuePart<'a>>> {
        if self.offset < self.value_str.len() {
            return match self.parse() {
                Ok(Some(v)) => Some(Ok(v)),
                Ok(None) => None,
                Err(e) => Some(Err(e)),
            }
        }
        None
    }
}

fn is_space(c: u8) -> bool {
    c == b' '
}

fn isnt_space(c: u8) -> bool {
    !is_space(c)
}

fn valid_unit_char(c: u8) -> bool {
    c == b'%' || (!is_space(c) && !is_operator(c))
}

fn valid_hex_char(c: u8) -> bool {
    match c {
        b'0' ... b'9' | b'a' ... b'f' => true,
        _ => false,
    }
}

fn is_number(c: u8) -> bool {
    let result = match c {
        b'0' ... b'9' | b'.' => true,
        _ => false,
    };
    result
}

fn is_operator(c: u8) -> bool {
    match c {
        b'+' | b'-' | b'*' | b'/' | b'%' | b'(' | b')' | b',' => true,
        _ => false,
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use sass::color_value::ColorValue;
    use sass::value_part::ValuePart;
    use sass::number_value::NumberValue;
    use sass::op::Op;
    use std::borrow::Cow::Borrowed;

    #[test]
    fn it_returns_string_part() {
        let mut vt = ValueTokenizer::new("foo");
        assert_eq!(Some(Ok(ValuePart::String(Borrowed(&"foo")))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_space_separated_string_parts() {
        let mut vt = ValueTokenizer::new("foo bar");
        assert_eq!(Some(Ok(ValuePart::String(Borrowed(&"foo")))), vt.next());
        assert_eq!(Some(Ok(ValuePart::String(Borrowed(&"bar")))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_variable() {
        let mut vt = ValueTokenizer::new("$foo");
        assert_eq!(Some(Ok(ValuePart::Variable(Borrowed(&"$foo")))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_variables_and_string_parts() {
        let mut vt = ValueTokenizer::new("foo $bar baz $quux");
        assert_eq!(Some(Ok(ValuePart::String(Borrowed(&"foo")))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Variable(Borrowed(&"$bar")))), vt.next());
        assert_eq!(Some(Ok(ValuePart::String(Borrowed(&"baz")))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Variable(Borrowed(&"$quux")))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_number() {
        let mut vt = ValueTokenizer::new("3");
        assert_eq!(Some(Ok(ValuePart::Number(NumberValue::from_scalar(3.0)))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_two_numbers() {
        let mut vt = ValueTokenizer::new("3 8.9");
        assert_eq!(Some(Ok(ValuePart::Number(NumberValue::from_scalar(3.0)))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Number(NumberValue::from_scalar(8.9)))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_numbers_with_units() {
        let mut vt = ValueTokenizer::new("3px");
        assert_eq!(
            Some(Ok(ValuePart::Number(NumberValue::with_units(3.0, Borrowed(&"px"))))),
            vt.next()
        );
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_numbers_with_units_but_not_parens() {
        let mut vt = ValueTokenizer::new("(3px)");
        assert_eq!(Some(Ok(ValuePart::Operator(Op::LeftParen))), vt.next());
        assert_eq!(
            Some(Ok(ValuePart::Number(NumberValue::with_units(3.0, Borrowed(&"px"))))),
            vt.next()
        );
        assert_eq!(Some(Ok(ValuePart::Operator(Op::RightParen))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_numbers_with_percents_as_units() {
        let mut vt = ValueTokenizer::new("10px 8%");
        assert_eq!(
            Some(Ok(ValuePart::Number(NumberValue::with_units(10.0, Borrowed(&"px"))))),
            vt.next()
        );
        assert_eq!(
            Some(Ok(ValuePart::Number(NumberValue::with_units(8.0, Borrowed(&"%"))))),
            vt.next()
        );
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_operator() {
        let mut vt = ValueTokenizer::new("+");
        assert_eq!(Some(Ok(ValuePart::Operator(Op::Plus))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_numbers_and_operators() {
        let mut vt = ValueTokenizer::new("6 + 75.2");
        assert_eq!(Some(Ok(ValuePart::Number(NumberValue::from_scalar(6.0)))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Operator(Op::Plus))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Number(NumberValue::from_scalar(75.2)))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_returns_numbers_and_operators_without_spaces() {
        let mut vt = ValueTokenizer::new("6+75.2");
        assert_eq!(Some(Ok(ValuePart::Number(NumberValue::from_scalar(6.0)))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Operator(Op::Plus))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Number(NumberValue::from_scalar(75.2)))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_does_stuff_with_parens() {
        let mut vt = ValueTokenizer::new("2+(3 4)");
        assert_eq!(Some(Ok(ValuePart::Number(NumberValue::from_scalar(2.0)))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Operator(Op::Plus))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Operator(Op::LeftParen))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Number(NumberValue::from_scalar(3.0)))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Number(NumberValue::from_scalar(4.0)))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Operator(Op::RightParen))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_does_stuff_with_slash_separators() {
        let mut vt = ValueTokenizer::new("15 / 3 / 5");
        assert_eq!(Some(Ok(ValuePart::Number(NumberValue::from_scalar(15.0)))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Operator(Op::Slash))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Number(NumberValue::from_scalar(3.0)))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Operator(Op::Slash))), vt.next());
        assert_eq!(Some(Ok(ValuePart::Number(NumberValue::from_scalar(5.0)))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_recognizes_hex() {
        let mut vt = ValueTokenizer::new("#aabbcc");
        assert_eq!(
            Some(Ok(ValuePart::Color(ColorValue {
                red: 170, green: 187, blue: 204, original: Borrowed("#aabbcc"),
            }))),
            vt.next()
        );
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_recognizes_short_hex() {
        let mut vt = ValueTokenizer::new("#cba");
        assert_eq!(
            Some(Ok(ValuePart::Color(ColorValue {
                red: 204, green: 187, blue: 170, original: Borrowed("#cba"),
            }))),
            vt.next()
        );
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_recognizes_hex_is_separate_from_parens() {
        let mut vt = ValueTokenizer::new("#cba)");
        assert_eq!(
            Some(Ok(ValuePart::Color(ColorValue {
                red: 204, green: 187, blue: 170, original: Borrowed("#cba"),
            }))),
            vt.next()
        );
        assert_eq!(Some(Ok(ValuePart::Operator(Op::RightParen))), vt.next());
        assert_eq!(None, vt.next());
    }

    #[test]
    fn it_recognizes_rgb() {
        let mut vt = ValueTokenizer::new("rgb(10,100,73)");
        assert_eq!(
            Some(Ok(ValuePart::Color(ColorValue {
                red: 10, green: 100, blue: 73, original: Borrowed("rgb(10,100,73)"),
            }))),
            vt.next()
        );
        assert_eq!(None, vt.next());
    }
}
<|endoftext|>


<commit_before>//! Interpreting room terrain results.

use EndpointResult;
use data;
use error::{Result, ApiError};
use std::marker::PhantomData;
use {arrayvec, generic_array, typenum};

/// Room overview raw result.
#[derive(Deserialize, Clone, Hash, Debug)]
#[doc(hidden)]
pub struct Response {
    ok: i32,
    terrain: Option<Vec<InnerResponse>>,
}

#[derive(Deserialize, Clone, Hash, Debug)]
struct InnerResponse {
    // this is returned as part of the data, but what the heck is it even for?
    // A cache key maybe?
    // _id: String,
    /// room name
    room: String,
    /// encoded data
    terrain: String,
}

/// Type of terrain
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum TerrainType {
    /// Plains terrain type, easy to move through
    Plains,
    /// Swamp terrain type, green, hard to move through
    Swamp,
    /// Wall terrain type, impossible to move through
    Wall,
    /// Wall with swamp underneath it, hard to move through
    SwampyWall,
}

/// A type alias for a single row in the terrain grid.
pub type TerrainRow = arrayvec::ArrayVec<generic_array::GenericArray<TerrainType, typenum::U50>>;

/// A type alias for a 50x50 terrain grid. terrain[y_pos][x_pos] can be used to access any terrain
/// from x 0-49, y 0-49.
pub type TerrainGrid = arrayvec::ArrayVec<generic_array::GenericArray<TerrainRow, typenum::U50>>;

/// Structure describing the terrain of a room
#[derive(Clone, Debug)]
pub struct RoomTerrain {
    /// The name of the room
    pub room_name: data::RoomName,
    /// A 50x50 grid of terrain squares. When coming from the API, this is guaranteed to be
    /// completely filled, and accessing any square between (0, 0) and (49, 49) inclusive will
    /// succeed.
    ///
    /// You can use terrain[y_pos][x_pos] to get any individual terrain square.
    pub terrain: TerrainGrid,
    /// Phantom data in order to allow adding any additional fields in the future.
    _phantom: PhantomData<()>,
}

impl EndpointResult for RoomTerrain {
    type RequestResult = Response;
    type ErrorResult = data::ApiError;

    fn from_raw(raw: Response) -> Result<RoomTerrain> {
        let Response { ok, terrain: terrain_array } = raw;

        if ok != 1 {
            return Err(ApiError::NotOk(ok).into());
        }

        let terrain_data = match terrain_array {
            Some(v) => {
                match v.into_iter().next() {
                    Some(v) => v,
                    None => return Err(ApiError::MissingField("terrain.0").into()),
                }
            }
            None => return Err(ApiError::MissingField("terrain").into()),
        };

        let terrain_bytes = terrain_data.terrain.into_bytes();

        if terrain_bytes.len() != 2500 {
            return Err(ApiError::MalformedResponse(format!("expected response.terrain[0].\
                terrain to be a 2500 byte string, found a {} byte string.",
                                                           terrain_bytes.len()))
                .into());
        }

        Ok(RoomTerrain {
            room_name: data::RoomName::new(&terrain_data.room)?,
            terrain: terrain_bytes.chunks(50)
                .enumerate()
                .map(|(y, chunk)| {
                    chunk.iter()
                        .enumerate()
                        .map(|(x, byte)| match *byte {
                            b'0' => Ok(TerrainType::Plains),
                            b'1' => Ok(TerrainType::Wall),
                            b'2' => Ok(TerrainType::Swamp),
                            b'3' => Ok(TerrainType::SwampyWall),
                            other => {
                                Err(ApiError::MalformedResponse(format!("expected terrain data to contain \
                                                    only characters 0,1,2,3, found byte {} at x,y {},{}.",
                                                                        other,
                                                                        x,
                                                                        y))
                                    .into())
                            }
                        })
                        .collect::<Result<_>>()
                })
                .collect::<Result<_>>()?,
            _phantom: PhantomData,
        })
    }
}

#[cfg(test)]
mod tests {
    use super::RoomTerrain;
    use EndpointResult;
    use serde_json;

    fn test_parse(json: serde_json::Value) {
        let response = serde_json::from_value(json).unwrap();

        let _ = RoomTerrain::from_raw(response).unwrap();
    }

    #[test]
    fn parse_sample_terrain() {
        test_parse(json! ({
            "ok": 1,
            "terrain": [
                {
                    "_id":
                    "579fa9920700be0674d2f893",
                    "terrain": "\
                    11111111111111111111111111111111111111111111111111\
                    11111111111111111111111111111111111111111111111111\
                    11111111111111111111111111111000000001111111111111\
                    11111111111111111111111111100000000000000111111111\
                    11111111111111111111111110000000002200000203111111\
                    11111111100111111111111000000013122200000002111111\
                    00111111100001111111100000000011130200000220011111\
                    00111111100000000002001110000013330000000000011111\
                    00011111112000000002001110000003100000000000011111\
                    00001111110001110002000000000000000000000000011111\
                    00000111111003131000000000000000000000000000011111\
                    00000011111003111100000000011111100000000022200000\
                    00000000200001111110000000111111110000000002222000\
                    00000000220001111110022000111111111000000222222200\
                    00000000220001111111022000011111111100000020132011\
                    00000000020001111111000000000111111110000021113111\
                    00000000000001111111100011100011111131000001111111\
                    11111111100001111111100011110011111111100000111111\
                    11111111110001111111110011111001111111100020111111\
                    11111111130000111111110013111003111111100220011111\
                    11111111110000031111110003133221111111100020011111\
                    11111111110000021111100202130223111111000000011111\
                    11111111110000000111000002022221111110000000111111\
                    00111111110000000000000200002001111100000000111111\
                    00111111110001110000000000002000110222000001111111\
                    00111111110011111000000000000000000000000001111111\
                    00111111110033111100020000001100000000000011111111\
                    00111111110001111000000000001110000000000011111111\
                    00111111100020000000000000000110000000000011111111\
                    00011111000220000000000011000000000000000011111111\
                    00001110000232000022000111100000000000000011111111\
                    00000000001133000022001111102000000000000011111111\
                    00000000111111100000011111100000000000000011111111\
                    00111111111111111000011111110000000000000011111111\
                    00111111111111111110111111110000002000000011111111\
                    00031111111111111113111111110000222111110001111111\
                    00001111111111111111111111110220003111110011111111\
                    00001111111111111111111111110000001111100211111111\
                    11000111111111111111111111110200000111000331111111\
                    11100111111111111111111111110220000000000111111111\
                    11100031111111111111111111110222000000001111111111\
                    11110011111111111111111111110000000000001111111111\
                    11110001111111111111111111132200000000011111111111\
                    11111001111111111111111111132200000200111111111111\
                    11111001111111111111111111100000000000111111111111\
                    11111301111111111111111111100000000000111111111111\
                    11111100111100111111111111100000000000111111111111\
                    11111100011100111111111111100000022000011111111111\
                    11111100000000111111111111100000000000001111111111\
                    11111100000000111111111111100000000000001111111111",
                    "type": "terrain",
                    "room": "E15N52"
                }
            ]
        }));
    }
}
<commit_msg>Include _id and type checking for room terrain response.<commit_after>//! Interpreting room terrain results.

use EndpointResult;
use data;
use error::{Result, ApiError};
use std::marker::PhantomData;
use {arrayvec, generic_array, typenum};

/// Room overview raw result.
#[derive(Deserialize, Clone, Hash, Debug)]
#[doc(hidden)]
pub struct Response {
    ok: i32,
    terrain: Option<Vec<InnerResponse>>,
}

#[derive(Deserialize, Clone, Hash, Debug)]
struct InnerResponse {
    // this is returned as part of the data, but what the heck is it even for?
    /// A cache key maybe?
    _id: String,
    /// Should be `terrain`.
    #[serde(rename = "type")]
    response_type: String,
    /// room name
    room: String,
    /// encoded data
    terrain: String,
}

/// Type of terrain
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub enum TerrainType {
    /// Plains terrain type, easy to move through
    Plains,
    /// Swamp terrain type, green, hard to move through
    Swamp,
    /// Wall terrain type, impossible to move through
    Wall,
    /// Wall with swamp underneath it, hard to move through
    SwampyWall,
}

/// A type alias for a single row in the terrain grid.
pub type TerrainRow = arrayvec::ArrayVec<generic_array::GenericArray<TerrainType, typenum::U50>>;

/// A type alias for a 50x50 terrain grid. terrain[y_pos][x_pos] can be used to access any terrain
/// from x 0-49, y 0-49.
pub type TerrainGrid = arrayvec::ArrayVec<generic_array::GenericArray<TerrainRow, typenum::U50>>;

/// Structure describing the terrain of a room
#[derive(Clone, Debug)]
pub struct RoomTerrain {
    /// The name of the room
    pub room_name: data::RoomName,
    /// Cache ID? Not sure exactly...
    pub response_id: String,
    /// A 50x50 grid of terrain squares. When coming from the API, this is guaranteed to be
    /// completely filled, and accessing any square between (0, 0) and (49, 49) inclusive will
    /// succeed.
    ///
    /// You can use terrain[y_pos][x_pos] to get any individual terrain square.
    pub terrain: TerrainGrid,
    /// Phantom data in order to allow adding any additional fields in the future.
    _phantom: PhantomData<()>,
}

impl EndpointResult for RoomTerrain {
    type RequestResult = Response;
    type ErrorResult = data::ApiError;

    fn from_raw(raw: Response) -> Result<RoomTerrain> {
        let Response { ok, terrain: terrain_array } = raw;

        if ok != 1 {
            return Err(ApiError::NotOk(ok).into());
        }

        let terrain_data = match terrain_array {
            Some(v) => {
                match v.into_iter().next() {
                    Some(v) => v,
                    None => return Err(ApiError::MissingField("terrain.0").into()),
                }
            }
            None => return Err(ApiError::MissingField("terrain").into()),
        };

        let InnerResponse { response_type, room: room_string, _id: response_id, terrain } = terrain_data;

        if terrain.len() != 2500 {
            return Err(ApiError::MalformedResponse(format!("expected response.terrain[0].\
                terrain to be a 2500 byte string, found a {} byte string.",
                                                           terrain.len()))
                .into());
        }

        if response_type != "terrain" {
            return Err(ApiError::MalformedResponse(format!("expected response.terrain[0].type \
                                                            to be 'terrain', found {:?}",
                                                           response_type))
                .into());
        }

        Ok(RoomTerrain {
            room_name: data::RoomName::new(&room_string)?,
            response_id: response_id,
            terrain: terrain.into_bytes()
                .chunks(50)
                .enumerate()
                .map(|(y, chunk)| {
                    chunk.iter()
                        .enumerate()
                        .map(|(x, byte)| match *byte {
                            b'0' => Ok(TerrainType::Plains),
                            b'1' => Ok(TerrainType::Wall),
                            b'2' => Ok(TerrainType::Swamp),
                            b'3' => Ok(TerrainType::SwampyWall),
                            other => {
                                Err(ApiError::MalformedResponse(format!("expected terrain data to contain \
                                                    only characters 0,1,2,3, found byte {} at x,y {},{}.",
                                                                        other,
                                                                        x,
                                                                        y))
                                    .into())
                            }
                        })
                        .collect::<Result<_>>()
                })
                .collect::<Result<_>>()?,
            _phantom: PhantomData,
        })
    }
}

#[cfg(test)]
mod tests {
    use super::RoomTerrain;
    use EndpointResult;
    use serde_json;

    fn test_parse(json: serde_json::Value) {
        let response = serde_json::from_value(json).unwrap();

        let _ = RoomTerrain::from_raw(response).unwrap();
    }

    #[test]
    fn parse_sample_terrain() {
        test_parse(json! ({
            "ok": 1,
            "terrain": [
                {
                    "_id":
                    "579fa9920700be0674d2f893",
                    "terrain": "\
                    11111111111111111111111111111111111111111111111111\
                    11111111111111111111111111111111111111111111111111\
                    11111111111111111111111111111000000001111111111111\
                    11111111111111111111111111100000000000000111111111\
                    11111111111111111111111110000000002200000203111111\
                    11111111100111111111111000000013122200000002111111\
                    00111111100001111111100000000011130200000220011111\
                    00111111100000000002001110000013330000000000011111\
                    00011111112000000002001110000003100000000000011111\
                    00001111110001110002000000000000000000000000011111\
                    00000111111003131000000000000000000000000000011111\
                    00000011111003111100000000011111100000000022200000\
                    00000000200001111110000000111111110000000002222000\
                    00000000220001111110022000111111111000000222222200\
                    00000000220001111111022000011111111100000020132011\
                    00000000020001111111000000000111111110000021113111\
                    00000000000001111111100011100011111131000001111111\
                    11111111100001111111100011110011111111100000111111\
                    11111111110001111111110011111001111111100020111111\
                    11111111130000111111110013111003111111100220011111\
                    11111111110000031111110003133221111111100020011111\
                    11111111110000021111100202130223111111000000011111\
                    11111111110000000111000002022221111110000000111111\
                    00111111110000000000000200002001111100000000111111\
                    00111111110001110000000000002000110222000001111111\
                    00111111110011111000000000000000000000000001111111\
                    00111111110033111100020000001100000000000011111111\
                    00111111110001111000000000001110000000000011111111\
                    00111111100020000000000000000110000000000011111111\
                    00011111000220000000000011000000000000000011111111\
                    00001110000232000022000111100000000000000011111111\
                    00000000001133000022001111102000000000000011111111\
                    00000000111111100000011111100000000000000011111111\
                    00111111111111111000011111110000000000000011111111\
                    00111111111111111110111111110000002000000011111111\
                    00031111111111111113111111110000222111110001111111\
                    00001111111111111111111111110220003111110011111111\
                    00001111111111111111111111110000001111100211111111\
                    11000111111111111111111111110200000111000331111111\
                    11100111111111111111111111110220000000000111111111\
                    11100031111111111111111111110222000000001111111111\
                    11110011111111111111111111110000000000001111111111\
                    11110001111111111111111111132200000000011111111111\
                    11111001111111111111111111132200000200111111111111\
                    11111001111111111111111111100000000000111111111111\
                    11111301111111111111111111100000000000111111111111\
                    11111100111100111111111111100000000000111111111111\
                    11111100011100111111111111100000022000011111111111\
                    11111100000000111111111111100000000000001111111111\
                    11111100000000111111111111100000000000001111111111",
                    "type": "terrain",
                    "room": "E15N52"
                }
            ]
        }));
    }
}
<|endoftext|>


<commit_before>use std::cmp::Ordering;
use std::collections::{BinaryHeap, VecDeque, vec_deque};
use std::result;

use grid::{Grid, DynamicGrid};
use math::Coord;
use direction::{self, Direction};

#[derive(Debug)]
pub enum Error {
    Exhausted,
    NonTraversableStart,
    StartOutOfGrid,
}

pub type Result<T> = result::Result<T, Error>;

pub trait TraverseCost {
    fn traverse_cost(&self) -> Option<f64>;
    fn is_traversable(&self) -> bool {
        self.traverse_cost().is_some()
    }
}

struct Node {
    coord: Coord,
    cost: f64,
    score: f64,
}

impl Node {
    fn new(coord: Coord, cost: f64, score: f64) -> Self {
        Node {
            coord: coord,
            cost: cost,
            score: score,
        }
    }
}

impl Ord for Node {
    fn cmp(&self, other: &Self) -> Ordering {
        if self.score > other.score {
            Ordering::Less
        } else if self.score < other.score {
            Ordering::Greater
        } else {
            Ordering::Equal
        }
    }
}

impl PartialOrd for Node {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl PartialEq for Node {
    fn eq(&self, other: &Self) -> bool {
        self.score == other.score
    }
}

impl Eq for Node {}

struct Parent {
    coord: Coord,
    direction: Direction,
}

impl Parent {
    fn new(coord: Coord, direction: Direction) -> Self {
        Parent {
            coord: coord,
            direction: direction,
        }
    }
}

struct SearchCell {
    seen_seq: u64,
    visited_seq: u64,
    cost: f64,
    parent: Option<Parent>,
}

impl SearchCell {
    fn new() -> Self {
        SearchCell {
            seen_seq: 0,
            visited_seq: 0,
            cost: 0.0,
            parent: None,
        }
    }
}

impl Default for SearchCell {
    fn default() -> Self {
        Self::new()
    }
}

struct SearchGrid {
    grid: DynamicGrid<SearchCell>,
    seq: u64,
}

impl SearchGrid {
    fn new() -> Self {
        SearchGrid {
            grid: DynamicGrid::new(),
            seq: 0,
        }
    }

    fn clear(&mut self) {
        self.seq += 1;
    }

    fn see_initial(&mut self, coord: Coord) {
        let cell = self.grid.get_mut_with_default(coord);
        cell.seen_seq = self.seq;
        cell.parent = None;
        cell.cost = 0.0;
    }

    fn is_visited(&self, coord: Coord) -> bool {
        self.grid.get_with_default(coord).visited_seq == self.seq
    }

    fn visit(&mut self, coord: Coord) {
        self.grid.get_mut_with_default(coord).visited_seq = self.seq;
    }

    fn maybe_see(&mut self, coord: Coord, cost: f64, parent: Parent) -> bool {
        let cell = self.grid.get_mut_with_default(coord);

        if cell.visited_seq == self.seq {
            return false;
        }

        if cell.seen_seq == self.seq {
            if cost < cell.cost {
                cell.cost = cost;
                cell.parent = Some(parent);
                return true;
            }
        } else {
            cell.seen_seq = self.seq;
            cell.cost = cost;
            cell.parent = Some(parent);
            return true;
        }

        false
    }

    fn parent(&self, coord: Coord) -> Option<&Parent> {
        let cell = self.grid.get_with_default(coord);
        if cell.visited_seq == self.seq {
            cell.parent.as_ref()
        } else {
            None
        }
    }

    fn populate_path(&self, end: Coord, path: &mut GridPath) {
        path.nodes.clear();

        let mut coord = end;

        loop {
            if let Some(parent) = self.parent(coord) {
                path.nodes.push_front(GridPathNode {
                    coord: coord,
                    direction_to: parent.direction,
                });

                coord = parent.coord;
            } else {
                path.start = coord;
                return;
            }
        }
    }
}

pub struct GridSearchCfg {
    pub directions: &'static [Direction],
}

impl GridSearchCfg {
    pub fn all_directions() -> Self {
        GridSearchCfg { directions: &direction::DIRECTIONS }
    }
}

pub struct GridPathNode {
    pub coord: Coord,
    pub direction_to: Direction,
}

pub struct GridPath {
    start: Coord,
    nodes: VecDeque<GridPathNode>,
}

impl GridPath {
    pub fn start(&self) -> Coord {
        self.start
    }

    pub fn nodes(&self) -> vec_deque::Iter<GridPathNode> {
        self.nodes.iter()
    }
}

struct InnerGridSearchCtx {
    queue: BinaryHeap<Node>,
    grid: SearchGrid,
}

impl InnerGridSearchCtx {
    fn new() -> Self {
        InnerGridSearchCtx {
            queue: BinaryHeap::new(),
            grid: SearchGrid::new(),
        }
    }

    fn clear(&mut self) {
        self.queue.clear();
        self.grid.clear();
    }

    pub fn search_predicate<T, G, F>(&mut self,
                                     grid: &G,
                                     start: Coord,
                                     predicate: F,
                                     config: &GridSearchCfg,
                                     path: &mut GridPath) -> Result<()>
        where T: TraverseCost,
              G: Grid<Item = T>,
              F: Fn(&G::Item) -> bool
    {
        if let Some(initial_cell) = grid.get(start) {
            if !initial_cell.is_traversable() {
                return Err(Error::NonTraversableStart);
            }
        } else {
            return Err(Error::StartOutOfGrid);
        }

        self.clear();

        self.queue.push(Node::new(start, 0.0, 0.0));
        self.grid.see_initial(start);

        while let Some(node) = self.queue.pop() {

            if self.grid.is_visited(node.coord) {
                continue;
            }

            self.grid.visit(node.coord);

            // Only coordinates valid for the grid being searched
            // are added to the queue, and the grid can't change
            // mid-search.
            let cell = unsafe { grid.get_unchecked(node.coord) };

            if predicate(cell) {
                // found a satisfying cell
                self.grid.populate_path(node.coord, path);
                return Ok(());
            }

            for dir in config.directions {
                let nei_coord = node.coord + dir.vector();
                if let Some(cell) = grid.get(nei_coord) {
                    if let Some(cost) = cell.traverse_cost() {
                        let total_cost = node.cost + cost * dir.multiplier();
                        let parent = Parent::new(node.coord, *dir);
                        if self.grid.maybe_see(nei_coord, total_cost, parent) {
                            self.queue.push(Node::new(nei_coord, total_cost, total_cost));
                        }
                    }
                }
            }
        }

        Err(Error::Exhausted)
    }

}
<commit_msg>Added outer layer of grid search ctx<commit_after>use std::cmp::Ordering;
use std::collections::{BinaryHeap, VecDeque, vec_deque};
use std::result;
use std::cell::RefCell;

use grid::{Grid, DynamicGrid};
use math::Coord;
use direction::{self, Direction};

#[derive(Debug)]
pub enum Error {
    Exhausted,
    NonTraversableStart,
    StartOutOfGrid,
}

pub type Result<T> = result::Result<T, Error>;

pub trait TraverseCost {
    fn traverse_cost(&self) -> Option<f64>;
    fn is_traversable(&self) -> bool {
        self.traverse_cost().is_some()
    }
}

struct Node {
    coord: Coord,
    cost: f64,
    score: f64,
}

impl Node {
    fn new(coord: Coord, cost: f64, score: f64) -> Self {
        Node {
            coord: coord,
            cost: cost,
            score: score,
        }
    }
}

impl Ord for Node {
    fn cmp(&self, other: &Self) -> Ordering {
        if self.score > other.score {
            Ordering::Less
        } else if self.score < other.score {
            Ordering::Greater
        } else {
            Ordering::Equal
        }
    }
}

impl PartialOrd for Node {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl PartialEq for Node {
    fn eq(&self, other: &Self) -> bool {
        self.score == other.score
    }
}

impl Eq for Node {}

struct Parent {
    coord: Coord,
    direction: Direction,
}

impl Parent {
    fn new(coord: Coord, direction: Direction) -> Self {
        Parent {
            coord: coord,
            direction: direction,
        }
    }
}

struct SearchCell {
    seen_seq: u64,
    visited_seq: u64,
    cost: f64,
    parent: Option<Parent>,
}

impl SearchCell {
    fn new() -> Self {
        SearchCell {
            seen_seq: 0,
            visited_seq: 0,
            cost: 0.0,
            parent: None,
        }
    }
}

impl Default for SearchCell {
    fn default() -> Self {
        Self::new()
    }
}

struct SearchGrid {
    grid: DynamicGrid<SearchCell>,
    seq: u64,
}

impl SearchGrid {
    fn new() -> Self {
        SearchGrid {
            grid: DynamicGrid::new(),
            seq: 0,
        }
    }

    fn clear(&mut self) {
        self.seq += 1;
    }

    fn see_initial(&mut self, coord: Coord) {
        let cell = self.grid.get_mut_with_default(coord);
        cell.seen_seq = self.seq;
        cell.parent = None;
        cell.cost = 0.0;
    }

    fn is_visited(&self, coord: Coord) -> bool {
        self.grid.get_with_default(coord).visited_seq == self.seq
    }

    fn visit(&mut self, coord: Coord) {
        self.grid.get_mut_with_default(coord).visited_seq = self.seq;
    }

    fn maybe_see(&mut self, coord: Coord, cost: f64, parent: Parent) -> bool {
        let cell = self.grid.get_mut_with_default(coord);

        if cell.visited_seq == self.seq {
            return false;
        }

        if cell.seen_seq == self.seq {
            if cost < cell.cost {
                cell.cost = cost;
                cell.parent = Some(parent);
                return true;
            }
        } else {
            cell.seen_seq = self.seq;
            cell.cost = cost;
            cell.parent = Some(parent);
            return true;
        }

        false
    }

    fn parent(&self, coord: Coord) -> Option<&Parent> {
        let cell = self.grid.get_with_default(coord);
        if cell.visited_seq == self.seq {
            cell.parent.as_ref()
        } else {
            None
        }
    }

    fn populate_path(&self, end: Coord, path: &mut GridPath) {
        path.clear();
        path.cost = self.grid.get_with_default(end).cost;

        let mut coord = end;

        loop {
            let cell = self.grid.get_with_default(coord);
            assert!(cell.visited_seq == self.seq);

            if let Some(ref parent) = cell.parent {
                path.nodes.push_front(GridPathNode {
                    coord: coord,
                    direction_to: parent.direction,
                });

                coord = parent.coord;
            } else {
                path.start = coord;
                return;
            }
        }
    }
}

pub struct GridSearchCfg {
    pub directions: &'static [Direction],
}

impl GridSearchCfg {
    pub fn all_directions() -> Self {
        GridSearchCfg { directions: &direction::DIRECTIONS }
    }
}

pub struct GridPathNode {
    pub coord: Coord,
    pub direction_to: Direction,
}

pub struct GridPath {
    start: Coord,
    nodes: VecDeque<GridPathNode>,
    cost: f64,
}

impl GridPath {
    pub fn new() -> Self {
        GridPath {
            start: Coord::new(0, 0),
            cost: 0.0,
            nodes: VecDeque::new(),
        }
    }

    pub fn clear(&mut self) {
        self.start = Coord::new(0, 0);
        self.cost = 0.0;
        self.nodes.clear();
    }

    pub fn start(&self) -> Coord {
        self.start
    }

    pub fn nodes(&self) -> vec_deque::Iter<GridPathNode> {
        self.nodes.iter()
    }

    pub fn len(&self) -> usize {
        self.nodes.len()
    }

    pub fn cost(&self) -> f64 {
        self.cost
    }
}

struct InnerGridSearchCtx {
    queue: BinaryHeap<Node>,
    grid: SearchGrid,
}

pub struct GridCellInfo<'a, T: 'a> {
    pub cell: &'a T,
    pub coord: Coord,
}

impl InnerGridSearchCtx {
    fn new() -> Self {
        InnerGridSearchCtx {
            queue: BinaryHeap::new(),
            grid: SearchGrid::new(),
        }
    }

    fn clear(&mut self) {
        self.queue.clear();
        self.grid.clear();
    }

    fn search_predicate<T, G, F>(&mut self,
                                 grid: &G,
                                 start: Coord,
                                 predicate: F,
                                 config: &GridSearchCfg,
                                 path: &mut GridPath) -> Result<()>
        where T: TraverseCost,
              G: Grid<Item = T>,
              F: Fn(GridCellInfo<G::Item>) -> bool
    {
        if let Some(initial_cell) = grid.get(start) {
            if !initial_cell.is_traversable() {
                return Err(Error::NonTraversableStart);
            }
        } else {
            return Err(Error::StartOutOfGrid);
        }

        self.clear();

        self.queue.push(Node::new(start, 0.0, 0.0));
        self.grid.see_initial(start);

        while let Some(node) = self.queue.pop() {

            if self.grid.is_visited(node.coord) {
                continue;
            }

            self.grid.visit(node.coord);

            // Only coordinates valid for the grid being searched
            // are added to the queue, and the grid can't change
            // mid-search.
            let cell = unsafe { grid.get_unchecked(node.coord) };

            let info = GridCellInfo {
                cell: cell,
                coord: node.coord,
            };

            if predicate(info) {
                // found a satisfying cell
                self.grid.populate_path(node.coord, path);
                return Ok(());
            }

            for dir in config.directions {
                let nei_coord = node.coord + dir.vector();
                if let Some(cell) = grid.get(nei_coord) {
                    if let Some(cost) = cell.traverse_cost() {
                        let total_cost = node.cost + cost * dir.multiplier();
                        let parent = Parent::new(node.coord, *dir);
                        if self.grid.maybe_see(nei_coord, total_cost, parent) {
                            self.queue.push(Node::new(nei_coord, total_cost, total_cost));
                        }
                    }
                }
            }
        }

        Err(Error::Exhausted)
    }

}

pub struct GridSearchCtx {
    ctx: RefCell<InnerGridSearchCtx>,
}

impl GridSearchCtx {
    pub fn new() -> Self {
        GridSearchCtx {
            ctx: RefCell::new(InnerGridSearchCtx::new()),
        }
    }

    pub fn search_predicate<T, G, F>(&self,
                                     grid: &G,
                                     start: Coord,
                                     predicate: F,
                                     config: &GridSearchCfg,
                                     path: &mut GridPath) -> Result<()>
        where T: TraverseCost,
              G: Grid<Item = T>,
              F: Fn(GridCellInfo<G::Item>) -> bool
    {
        self.ctx.borrow_mut().search_predicate(grid, start, predicate, config, path)
    }
}
<|endoftext|>