<commit_before>//============================================================================
//
//  A Tagged Image File Format (TIFF) Library for Rust
//
//  Based on the TIFF 6.0 specification:
//
//      https://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf
//
//  Copyright (c) 2014 by Gavin Baker <gavinb@antonym.org>
//  Published under the MIT License
//
//============================================================================

use std::io::{Result, Error, ErrorKind, Read, Seek, SeekFrom};
use std::path::Path;
use std::fs::File;

use byteorder::{ReadBytesExt, WriteBytesExt, ByteOrder, BigEndian, LittleEndian};

use {TIFFByteOrder, HeaderMagic, TIFFHeader, IFD, IFDEntry, decode_tag, decode_tag_type, SeekableReader};

pub struct TIFFReader;

impl TIFFReader {

    pub fn load(&self, filename: &str) -> Result<Box<TIFFHeader>> {

        let filepath = Path::new(filename);
        let mut reader = File::open(&filepath).unwrap();

        self.read(&mut reader)
    }

    pub fn read(&self, reader: &mut SeekableReader) -> Result<Box<TIFFHeader>> {

        let byte_order = self.read_byte_order(reader);

        let magic = match byte_order {
            Ok(TIFFByteOrder::LittleEndian) => self.read_magic::<LittleEndian>(reader),
            Ok(TIFFByteOrder::BigEndian) => self.read_magic::<BigEndian>(reader),
            Err(e) => Err(e)
        };

        self.read_::<LittleEndian>(reader)
    }

    pub fn read_byte_order(&self, reader: &mut SeekableReader) -> Result<TIFFByteOrder> {

        // Bytes 0-1: "II" or "MM"
        // Read and validate ByteOrder

        let byte_order_field = try!(reader.read_u16::<LittleEndian>());
        let byte_order: TIFFByteOrder;

        if byte_order_field == TIFFByteOrder::LittleEndian as u16 {
            byte_order = TIFFByteOrder::LittleEndian;
        } else if byte_order_field == TIFFByteOrder::BigEndian as u16 {
            byte_order = TIFFByteOrder::BigEndian;
        } else {
            return Err(Error::new(ErrorKind::Other,
                                  format!("Invalid byte order in header: {:04x}", byte_order_field)));
        }
        println!("byte_order {:?}", byte_order);

        Ok(byte_order)
    }

    pub fn read_magic<Endian: ByteOrder>(&self, reader: &mut SeekableReader) -> Result<HeaderMagic> {

        // Read and validate HeaderMagic
        // Bytes 2-3: 42

        let magic_field = try!(reader.read_u16::<Endian>());

        let magic: HeaderMagic;

        if magic_field == HeaderMagic::LittleEndian as u16 {
            Ok(HeaderMagic::LittleEndian)
        }
        else if magic_field == HeaderMagic::BigEndian as u16 {
            Ok(HeaderMagic::BigEndian)
        } else {
            Err(Error::new(ErrorKind::Other, "Invalid magic number in header"))
        }
    }

    pub fn read_<Endian: ByteOrder>(&self, reader: &mut SeekableReader) -> Result<Box<TIFFHeader>> {

        // @todo Ensure file is >= min size

        // Read offset to first IFD

        let ifd_offset_field = try!(reader.read_u32::<Endian>());

        // Assemble validated header

        let header = Box::new(TIFFHeader {
            byte_order: TIFFByteOrder::LittleEndian,
            magic: HeaderMagic::LittleEndian,
            ifd_offset: ifd_offset_field,
        });

        try!(reader.seek(SeekFrom::Start(ifd_offset_field as u64)));
        println!("IFD offset: {:?}", ifd_offset_field);

        try!(self.read_IFD::<Endian>(reader));

        Ok(header)
    }

    #[allow(non_snake_case)]
    fn read_IFD<Endian: ByteOrder>(&self, reader: &mut SeekableReader) -> Result<Box<IFD>> {

        let entry_count = try!(reader.read_u16::<Endian>());

        println!("IFD entry count: {}", entry_count);

        let mut ifd = Box::new(IFD { count: entry_count, entries: Vec::with_capacity(entry_count as usize) });

        println!("IFD entry count: {}", entry_count);

        for entry_number in 0..entry_count as usize {
            ifd.entries.push(*self.read_tag::<Endian>(entry_number, reader).unwrap());
        }

        Ok(ifd)
    }

    fn read_tag<Endian: ByteOrder>(&self, entry_number: usize, reader: &mut SeekableReader) -> Result<Box<IFDEntry>> {

        let tag_value = try!(reader.read_u16::<Endian>());
        let typ_value = try!(reader.read_u16::<Endian>());
        let count_value = try!(reader.read_u32::<Endian>());
        let value_offset_value = try!(reader.read_u32::<Endian>());

        let tag_msg = format!("Invalid tag {:x}", tag_value);
        let tag = decode_tag(tag_value).expect(&tag_msg);

        let typ_msg = format!("Invalid tag type {:x}", typ_value);
        let typ = decode_tag_type(typ_value).expect(&typ_msg);

        let e0 = Box::new(IFDEntry {
            tag: tag,
            typ: typ,
            count: count_value,
            value_offset: value_offset_value,
        });

        println!("IFD[{:?}] {:?} {:?} {:x} {}", entry_number, e0.tag, e0.typ, e0.count, e0.value_offset);
        Ok(e0)
    }
}
<commit_msg>Tidy IFD reading code<commit_after>//============================================================================
//
//  A Tagged Image File Format (TIFF) Library for Rust
//
//  Based on the TIFF 6.0 specification:
//
//      https://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf
//
//  Copyright (c) 2014 by Gavin Baker <gavinb@antonym.org>
//  Published under the MIT License
//
//============================================================================

use std::io::{Result, Error, ErrorKind, Read, Seek, SeekFrom};
use std::path::Path;
use std::fs::File;

use byteorder::{ReadBytesExt, WriteBytesExt, ByteOrder, BigEndian, LittleEndian};

use {TIFFByteOrder, HeaderMagic, TIFFHeader, IFD, IFDEntry, decode_tag, decode_tag_type, SeekableReader};

pub struct TIFFReader;

impl TIFFReader {

    pub fn load(&self, filename: &str) -> Result<Box<TIFFHeader>> {

        let filepath = Path::new(filename);
        let mut reader = File::open(&filepath).unwrap();

        self.read(&mut reader)
    }

    pub fn read(&self, reader: &mut SeekableReader) -> Result<Box<TIFFHeader>> {

        let byte_order = self.read_byte_order(reader);

        let magic = match byte_order {
            Ok(TIFFByteOrder::LittleEndian) => self.read_magic(reader),
            Ok(TIFFByteOrder::BigEndian) => self.read_magic(reader),
            Err(e) => Err(e)
        };

        self.read_::<BigEndian>(reader)
    }

    pub fn read_byte_order(&self, reader: &mut SeekableReader) -> Result<TIFFByteOrder> {

        // Bytes 0-1: "II" or "MM"
        // Read and validate ByteOrder

        let byte_order_field = try!(reader.read_u16::<LittleEndian>());
        let byte_order: TIFFByteOrder;

        if byte_order_field == TIFFByteOrder::LittleEndian as u16 {
            byte_order = TIFFByteOrder::LittleEndian;
        } else if byte_order_field == TIFFByteOrder::BigEndian as u16 {
            byte_order = TIFFByteOrder::BigEndian;
        } else {
            return Err(Error::new(ErrorKind::Other,
                                  format!("Invalid byte order in header: {:04x}", byte_order_field)));
        }
        println!("byte_order {:?}", byte_order);

        Ok(byte_order)
    }

    pub fn read_magic(&self, reader: &mut SeekableReader) -> Result<HeaderMagic> {

        // Bytes 2-3: 0042
        // Read and validate HeaderMagic

        let magic_field = try!(reader.read_u16::<LittleEndian>());

        let magic: HeaderMagic;

        if magic_field == HeaderMagic::LittleEndian as u16 {
            Ok(HeaderMagic::LittleEndian)
        }
        else if magic_field == HeaderMagic::BigEndian as u16 {
            Ok(HeaderMagic::BigEndian)
        } else {
            Err(Error::new(ErrorKind::Other, "Invalid magic number in header"))
        }
    }

    pub fn read_<Endian: ByteOrder>(&self, reader: &mut SeekableReader) -> Result<Box<TIFFHeader>> {

        // @todo Ensure file is >= min size

        // Bytes 4-7: offset
        // Offset from start of file to first IFD

        let ifd_offset_field = try!(reader.read_u32::<Endian>());

        // Assemble validated header

        let header = Box::new(TIFFHeader {
            byte_order: TIFFByteOrder::LittleEndian,
            magic: HeaderMagic::LittleEndian,
            ifd_offset: ifd_offset_field,
        });

        try!(reader.seek(SeekFrom::Start(ifd_offset_field as u64)));
        println!("IFD offset: {:?}", ifd_offset_field);

        try!(self.read_IFD::<Endian>(reader));

        Ok(header)
    }

    #[allow(non_snake_case)]
    fn read_IFD<Endian: ByteOrder>(&self, reader: &mut SeekableReader) -> Result<Box<IFD>> {

        // 2 byte count of IFD entries
        let entry_count = try!(reader.read_u16::<Endian>());

        println!("IFD entry count: {}", entry_count);

        let mut ifd = Box::new(IFD { count: entry_count, entries: Vec::with_capacity(entry_count as usize) });

        for entry_number in 0..entry_count as usize {
            ifd.entries.push(*self.read_tag::<Endian>(entry_number, reader).unwrap());
        }

        Ok(ifd)
    }

    fn read_tag<Endian: ByteOrder>(&self, entry_number: usize, reader: &mut SeekableReader) -> Result<Box<IFDEntry>> {

        // Bytes 0..1: u16 tag ID
        let tag_value = try!(reader.read_u16::<Endian>());

        // Bytes 2..3: u16 field Type
        let typ_value = try!(reader.read_u16::<Endian>());

        // Bytes 4..7: u32 number of Values of type
        let count_value = try!(reader.read_u32::<Endian>());

        // Bytes 8..11: u32 offset in file to Value
        let value_offset_value = try!(reader.read_u32::<Endian>());

        // Decode tag
        let tag_msg = format!("Invalid tag {:x}", tag_value);
        let tag = decode_tag(tag_value).expect(&tag_msg);

        // Decode type
        let typ_msg = format!("Invalid tag type {:x}", typ_value);
        let typ = decode_tag_type(typ_value).expect(&typ_msg);

        // Create entry
        let e0 = Box::new(IFDEntry {
            tag: tag,
            typ: typ,
            count: count_value,
            value_offset: value_offset_value,
        });

        println!("IFD[{:?}] tag: {:?} type: {:?} count: {} offset: {:08x}",
                 entry_number, e0.tag, e0.typ, e0.count, e0.value_offset);
        Ok(e0)
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::io;
use std::borrow::Cow;
use std::str::Utf8Error;
use std::result::Result as StdResult;
use std::error::Error as StdError;
use std::convert::{From, Into};

use httparse;
use mio;

use communication::Command;

pub type Result<T> = StdResult<T, Error>;

/// The type of an error, which may indicate other kinds of errors as the underlying cause.
#[derive(Debug)]
pub enum Kind {
    /// Indicates an internal application error.
    /// The WebSocket will automatically attempt to send an Error (1011) close code.
    Internal,
    /// Indicates a state where some size limit has been exceeded, such as an inability to accept
    /// any more new connections.
    /// If a Connection is active, the WebSocket will automatically attempt to send
    /// a Size (1009) close code.
    Capacity,
    /// Indicates a violation of the WebSocket protocol.
    /// The WebSocket will automatically attempt to send a Protocol (1002) close code, or if
    /// this error occurs during a handshake, an HTTP 400 reponse will be generated.
    Protocol,
    /// Indicates that the WebSocket received data that should be utf8 encoded but was not.
    /// The WebSocket will automatically attempt to send a Invalid Frame Payload Data (1007) close
    /// code.
    Encoding(Utf8Error),
    /// Indicates an underlying IO Error.
    /// This kind of error will result in a WebSocket Connection disconnecting.
    Io(io::Error),
    /// Indicates a failure to parse an HTTP message.
    /// This kind of error should only occur during a WebSocket Handshake, and a HTTP 500 response
    /// will be generated.
    Parse(httparse::Error),
    /// Indicates a failure to send a command on the internal EventLoop channel. This means that
    /// the WebSocket is overloaded and the Connection will disconnect.
    Queue(mio::NotifyError<Command>),
    /// A custom error kind for use by applications. This error kind involves extra overhead
    /// because it will allocate the memory on the heap. The WebSocket ignores such errors by
    /// default, simply passing them to the Connection Handler.
    Custom(Box<StdError + Send + Sync>),
}

/// A struct indicating the kind of error that has occured and any precise details of that error.
pub struct Error {
    pub kind: Kind,
    pub details: Cow<'static, str>,
}

impl Error {

    pub fn new<I>(kind: Kind, details: I) -> Error
        where I: Into<Cow<'static, str>>
    {
        Error {
            kind: kind,
            details: details.into(),
        }
    }

    pub fn into_box(self) -> Box<StdError> {
        match self.kind {
            Kind::Custom(err) => err,
            _ => Box::new(self),
        }
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.details.len() > 0 {
            write!(f, "WS Error <{:?}>: {}", self.kind, self.details)
        } else {
            write!(f, "WS Error <{:?}>", self.kind)
        }
    }
}

impl fmt::Display for Error {

    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.details.len() > 0 {
            write!(f, "{}: {}", self.description(), self.details)
        } else {
            write!(f, "{}", self.description())
        }
    }
}

impl StdError for Error {

    fn description(&self) -> &str {
        match self.kind {
            Kind::Internal          => "Internal Application Error",
            Kind::Capacity          => "WebSocket at Capacity",
            Kind::Protocol          => "WebSocket Protocol Error",
            Kind::Encoding(ref err) => err.description(),
            Kind::Io(ref err)       => err.description(),
            Kind::Parse(_)          => "Unable to parse HTTP",
            Kind::Queue(_)          => "Unable to send signal on event loop",
            Kind::Custom(ref err)   => err.description(),
        }
    }

    fn cause(&self) -> Option<&StdError> {
        match self.kind {
            Kind::Encoding(ref err) => Some(err),
            Kind::Io(ref err)       => Some(err),
            _ => None,
        }
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::new(Kind::Io(err), "")
    }
}

impl From<httparse::Error> for Error {

    fn from(err: httparse::Error) -> Error {
        // TODO: match on parse error to determine details
        Error::new(Kind::Parse(err), "")
    }

}

impl From<mio::NotifyError<Command>> for Error {

    fn from(err: mio::NotifyError<Command>) -> Error {
        match err {
            mio::NotifyError::Io(err) => Error::from(err),
            _ => Error::new(Kind::Queue(err), "")
        }
    }

}

impl From<Utf8Error> for Error {
    fn from(err: Utf8Error) -> Error {
        Error::new(Kind::Encoding(err), "")
    }
}

impl From<Box<StdError>> for Error {
    fn from(err: Box<StdError>) -> Error {
        Error::new(Kind::Custom(err), "")
    }
}
<commit_msg>Update error conversions to meet bounds.<commit_after>use std::fmt;
use std::io;
use std::borrow::Cow;
use std::str::Utf8Error;
use std::result::Result as StdResult;
use std::error::Error as StdError;
use std::convert::{From, Into};

use httparse;
use mio;

use communication::Command;

pub type Result<T> = StdResult<T, Error>;

/// The type of an error, which may indicate other kinds of errors as the underlying cause.
#[derive(Debug)]
pub enum Kind {
    /// Indicates an internal application error.
    /// The WebSocket will automatically attempt to send an Error (1011) close code.
    Internal,
    /// Indicates a state where some size limit has been exceeded, such as an inability to accept
    /// any more new connections.
    /// If a Connection is active, the WebSocket will automatically attempt to send
    /// a Size (1009) close code.
    Capacity,
    /// Indicates a violation of the WebSocket protocol.
    /// The WebSocket will automatically attempt to send a Protocol (1002) close code, or if
    /// this error occurs during a handshake, an HTTP 400 reponse will be generated.
    Protocol,
    /// Indicates that the WebSocket received data that should be utf8 encoded but was not.
    /// The WebSocket will automatically attempt to send a Invalid Frame Payload Data (1007) close
    /// code.
    Encoding(Utf8Error),
    /// Indicates an underlying IO Error.
    /// This kind of error will result in a WebSocket Connection disconnecting.
    Io(io::Error),
    /// Indicates a failure to parse an HTTP message.
    /// This kind of error should only occur during a WebSocket Handshake, and a HTTP 500 response
    /// will be generated.
    Parse(httparse::Error),
    /// Indicates a failure to send a command on the internal EventLoop channel. This means that
    /// the WebSocket is overloaded and the Connection will disconnect.
    Queue(mio::NotifyError<Command>),
    /// A custom error kind for use by applications. This error kind involves extra overhead
    /// because it will allocate the memory on the heap. The WebSocket ignores such errors by
    /// default, simply passing them to the Connection Handler.
    Custom(Box<StdError + Send + Sync>),
}

/// A struct indicating the kind of error that has occured and any precise details of that error.
pub struct Error {
    pub kind: Kind,
    pub details: Cow<'static, str>,
}

impl Error {

    pub fn new<I>(kind: Kind, details: I) -> Error
        where I: Into<Cow<'static, str>>
    {
        Error {
            kind: kind,
            details: details.into(),
        }
    }

    pub fn into_box(self) -> Box<StdError> {
        match self.kind {
            Kind::Custom(err) => err,
            _ => Box::new(self),
        }
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.details.len() > 0 {
            write!(f, "WS Error <{:?}>: {}", self.kind, self.details)
        } else {
            write!(f, "WS Error <{:?}>", self.kind)
        }
    }
}

impl fmt::Display for Error {

    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.details.len() > 0 {
            write!(f, "{}: {}", self.description(), self.details)
        } else {
            write!(f, "{}", self.description())
        }
    }
}

impl StdError for Error {

    fn description(&self) -> &str {
        match self.kind {
            Kind::Internal          => "Internal Application Error",
            Kind::Capacity          => "WebSocket at Capacity",
            Kind::Protocol          => "WebSocket Protocol Error",
            Kind::Encoding(ref err) => err.description(),
            Kind::Io(ref err)       => err.description(),
            Kind::Parse(_)          => "Unable to parse HTTP",
            Kind::Queue(_)          => "Unable to send signal on event loop",
            Kind::Custom(ref err)   => err.description(),
        }
    }

    fn cause(&self) -> Option<&StdError> {
        match self.kind {
            Kind::Encoding(ref err) => Some(err),
            Kind::Io(ref err)       => Some(err),
            _ => None,
        }
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::new(Kind::Io(err), "")
    }
}

impl From<httparse::Error> for Error {

    fn from(err: httparse::Error) -> Error {
        // TODO: match on parse error to determine details
        Error::new(Kind::Parse(err), "")
    }

}

impl From<mio::NotifyError<Command>> for Error {

    fn from(err: mio::NotifyError<Command>) -> Error {
        match err {
            mio::NotifyError::Io(err) => Error::from(err),
            _ => Error::new(Kind::Queue(err), "")
        }
    }

}

impl From<Utf8Error> for Error {
    fn from(err: Utf8Error) -> Error {
        Error::new(Kind::Encoding(err), "")
    }
}

impl<B> From<Box<B>> for Error
    where B: StdError + Send + Sync + 'static
{
    fn from(err: Box<B>) -> Error {
        Error::new(Kind::Custom(err), "")
    }
}
<|endoftext|>


<commit_before>use backend::{AnimeEntry, AnimeInfo, Status, SyncBackend};
use chrono::Local;
use error::SeriesError;
use input::{self, Answer};
use process;
use regex::Regex;
use std;
use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};
use toml;

#[derive(Debug)]
pub struct Series<B>
where
    B: SyncBackend,
{
    sync_backend: B,
    pub episode_data: EpisodeData,
    pub save_data: SaveData,
    pub save_path: PathBuf,
}

impl<B> Series<B>
where
    B: SyncBackend,
{
    pub const DATA_FILE_NAME: &'static str = ".anup";

    pub fn from_data(episode_data: EpisodeData, sync_backend: B) -> Result<Series<B>, SeriesError> {
        let save_path = PathBuf::from(&episode_data.path).join(Series::<B>::DATA_FILE_NAME);
        let save_data = SaveData::from_path_or_default(&save_path)?;

        let series = Series {
            sync_backend,
            episode_data,
            save_data,
            save_path,
        };

        Ok(series)
    }

    pub fn load_season(&mut self, season: u32) -> Result<Season<B>, SeriesError> {
        let season_info = self.get_season_info(season)?;
        let season_ep_offset = self.calculate_season_offset(season);

        let list_entry = self.get_list_entry(season_info.clone())?;

        Season::init(self, list_entry, season_ep_offset)
    }

    pub fn save_data(&self) -> Result<(), SeriesError> {
        self.save_data.write_to(&self.save_path)
    }

    fn get_season_info(&mut self, season: u32) -> Result<AnimeInfo, SeriesError> {
        let num_seasons = self.save_data.seasons.len() as u32;

        if season >= num_seasons {
            let mut series = None;

            for cur_season in num_seasons..=season {
                println!(
                    "select the correct series for season {} of [{}]:",
                    1 + cur_season,
                    self.episode_data.series_name
                );

                let series_info = self.search_and_select_series(&self.episode_data.series_name)?;
                self.save_data.seasons.push(series_info.clone().into());

                series = Some(series_info);
            }

            self.save_data()?;

            // This unwrap should never fail, as the enclosing if statement ensures the for loop will set the
            // series variable at least once
            Ok(series.unwrap())
        } else {
            let season_info = &self.save_data.seasons[season as usize];

            let series = self
                .sync_backend
                .get_series_info_by_id(season_info.series_id)?;

            Ok(series)
        }
    }

    fn calculate_season_offset(&self, season: u32) -> u32 {
        let mut offset = 0;

        for cur_season in 0..(season as usize) {
            if let Some(season_eps) = self.save_data.seasons[cur_season].episodes {
                offset += season_eps;
            }
        }

        offset
    }

    fn search_and_select_series(&self, name: &str) -> Result<AnimeInfo, SeriesError> {
        let mut found = self.sync_backend.search_by_name(name)?;

        println!("{} results for [{}]:", B::name(), name);
        println!("enter the number next to the desired series:\n");

        println!("0 [custom search]");

        for (i, series) in found.iter().enumerate() {
            println!("{} [{}]", 1 + i, series.title);
        }

        let index = input::read_range(0, found.len())?;

        if index == 0 {
            println!("enter the name you want to search for:");

            let name = input::read_line()?;
            self.search_and_select_series(&name)
        } else {
            let info = found.swap_remove(index - 1);
            Ok(info)
        }
    }

    fn get_list_entry(&self, info: AnimeInfo) -> Result<AnimeEntry, SeriesError> {
        let found = self.sync_backend.get_list_entry(info.clone())?;

        match found {
            Some(entry) => Ok(entry),
            None => {
                let mut entry = AnimeEntry::new(info);
                entry.status = Status::Watching;
                entry.start_date = Some(Local::today());

                self.sync_backend.update_list_entry(&entry)?;
                Ok(entry)
            }
        }
    }
}

#[derive(Debug)]
pub struct Season<'a, B>
where
    B: 'a + SyncBackend,
{
    series: &'a Series<B>,
    pub list_entry: AnimeEntry,
    pub ep_offset: u32,
}

impl<'a, B> Season<'a, B>
where
    B: 'a + SyncBackend,
{
    fn init(
        series: &'a Series<B>,
        list_entry: AnimeEntry,
        ep_offset: u32,
    ) -> Result<Season<'a, B>, SeriesError> {
        let mut season = Season {
            series,
            list_entry,
            ep_offset,
        };

        match season.list_entry.status {
            Status::Completed => season.update_series_status(Status::Rewatching)?,
            Status::Dropped | Status::OnHold => season.prompt_watch_paused_series()?,
            _ => (),
        }

        Ok(season)
    }

    fn update_list_entry(&self) -> Result<(), SeriesError> {
        self.series
            .sync_backend
            .update_list_entry(&self.list_entry)?;

        Ok(())
    }

    pub fn play_episode(&mut self, relative_ep: u32) -> Result<(), SeriesError> {
        let ep_num = self.ep_offset + relative_ep;

        let path = self
            .series
            .episode_data
            .episodes
            .get(&ep_num)
            .ok_or_else(|| SeriesError::EpisodeNotFound(ep_num))?;

        let status = process::open_with_default(path).map_err(SeriesError::FailedToOpenPlayer)?;
        self.list_entry.watched_episodes = relative_ep;

        if !status.success() {
            println!("video player not exited normally");
            println!("do you still want to count the episode as watched? (y/N)");

            if !input::read_yn(Answer::No)? {
                return Ok(());
            }
        }

        match self.list_entry.info.episodes {
            Some(total_eps) if relative_ep >= total_eps => {
                self.update_series_status(Status::Completed)?;
                // TODO: replace with more graceful way of exiting
                std::process::exit(0);
            }
            _ => self.episode_completed()?,
        }

        Ok(())
    }

    pub fn play_all_episodes(&mut self) -> Result<(), SeriesError> {
        loop {
            let next_ep = self.list_entry.watched_episodes + 1;

            self.play_episode(next_ep)?;
            self.next_episode_options()?;
        }
    }

    fn episode_completed(&self) -> Result<(), SeriesError> {
        println!(
            "[{}] episode {}/{} completed",
            self.list_entry.info.title,
            self.list_entry.watched_episodes,
            self.list_entry
                .info
                .episodes
                .map(|e| e.to_string())
                .unwrap_or_else(|| "?".to_string())
        );

        self.update_list_entry()
    }

    fn next_episode_options(&mut self) -> Result<(), SeriesError> {
        println!("options:");
        println!("\t[d] drop series\n\t[h] put series on hold\n\t[r] rate series\n\t[x] exit\n\t[n] watch next episode (default)");

        let input = input::read_line()?.to_lowercase();

        match input.as_str() {
            "d" => {
                self.update_series_status(Status::Dropped)?;
                // TODO: replace with more graceful way of exiting
                std::process::exit(0);
            }
            "h" => {
                self.update_series_status(Status::OnHold)?;
                // TODO: replace with more graceful way of exiting
                std::process::exit(0);
            }
            "r" => {
                self.prompt_to_update_score();
                self.update_list_entry()?;

                self.next_episode_options()?;
            }
            "x" => std::process::exit(0),
            _ => (),
        }

        Ok(())
    }

    fn prompt_to_update_score(&mut self) {
        let (min_score, max_score) = self.series.sync_backend.score_range();
        println!("enter your score between {} and {}:", min_score, max_score);

        let input = match input::read_line() {
            Ok(input) => input,
            Err(err) => {
                eprintln!("failed to read score: {}", err);
                return;
            }
        };

        match self.series.sync_backend.parse_score(&input) {
            Ok(score) => self.list_entry.score = score,
            Err(err) => eprintln!("failed to parse score: {}", err),
        }
    }

    fn prompt_watch_paused_series(&mut self) -> Result<(), SeriesError> {
        println!(
            "[{}] was put on hold or dropped\ndo you want to watch it from the beginning? (Y/n)",
            self.list_entry.info.title
        );

        if input::read_yn(Answer::Yes)? {
            self.list_entry.watched_episodes = 0;
        }

        self.update_series_status(Status::Watching)?;
        Ok(())
    }

    fn update_series_status(&mut self, status: Status) -> Result<(), SeriesError> {
        match status {
            Status::Watching => {
                // A series that was on hold probably already has a starting date, and it would make
                // more sense to use that one instead of replacing it
                if self.list_entry.status != Status::OnHold {
                    self.list_entry.start_date = Some(Local::today());
                }

                self.list_entry.finish_date = None;
            }
            Status::Rewatching => {
                println!("[{}] already completed", self.list_entry.info.title);
                println!("do you want to reset the start and end dates of the series? (Y/n)");

                if input::read_yn(Answer::Yes)? {
                    self.list_entry.start_date = Some(Local::today());
                    self.list_entry.finish_date = None;
                }

                self.list_entry.watched_episodes = 0;
            }
            Status::Completed => {
                if self.list_entry.finish_date.is_none() {
                    self.list_entry.finish_date = Some(Local::today());
                }

                println!(
                    "[{}] completed!\ndo you want to rate it? (Y/n)",
                    self.list_entry.info.title
                );

                if input::read_yn(Answer::Yes)? {
                    self.prompt_to_update_score();
                }
            }
            Status::Dropped => {
                if self.list_entry.finish_date.is_none() {
                    self.list_entry.finish_date = Some(Local::today());
                }
            }
            Status::OnHold | Status::PlanToWatch => (),
        }

        self.list_entry.status = status;
        self.update_list_entry()?;

        Ok(())
    }
}

type SeriesName = String;
type EpisodeNum = u32;

#[derive(Debug)]
pub struct EpisodeData {
    pub series_name: String,
    pub episodes: HashMap<u32, PathBuf>,
    pub path: PathBuf,
}

impl EpisodeData {
    pub const EP_FORMAT_REGEX: &'static str =
        r"(?:\[.+?\]\s*)?(?P<series>.+?)\s*-\s*(?P<episode>\d+).*?\..+?";

    pub fn parse_dir(dir: &Path) -> Result<EpisodeData, SeriesError> {
        if !dir.is_dir() {
            return Err(SeriesError::NotADirectory(
                dir.file_name()
                    .map(|n| n.to_string_lossy().into_owned())
                    .unwrap_or_else(|| "err".into()),
            ));
        }

        let mut series_name = None;
        let mut episodes = HashMap::new();

        for entry in std::fs::read_dir(dir).map_err(SeriesError::Io)? {
            let path = entry.map_err(SeriesError::Io)?.path();

            if !path.is_file() {
                continue;
            }

            match EpisodeData::parse_filename(&path) {
                Ok((ep_name, ep_num)) => {
                    match series_name {
                        Some(ref series_name) if &ep_name != series_name => {
                            return Err(SeriesError::MultipleSeriesFound);
                        }
                        Some(_) => (),
                        None => series_name = Some(ep_name),
                    }

                    episodes.insert(ep_num, path);
                }
                Err(SeriesError::EpisodeRegexCaptureFailed) => continue,
                Err(e) => return Err(e),
            }
        }

        let name = series_name.ok_or(SeriesError::NoEpisodesFound)?;

        Ok(EpisodeData {
            series_name: name,
            episodes,
            path: dir.into(),
        })
    }

    fn parse_filename(path: &Path) -> Result<(SeriesName, EpisodeNum), SeriesError> {
        lazy_static! {
            static ref EP_FORMAT: Regex = Regex::new(EpisodeData::EP_FORMAT_REGEX).unwrap();
        }

        // Replace certain characters with spaces since they can prevent proper series
        // identification or prevent it from being found on a sync backend
        let filename = path
            .file_name()
            .and_then(|path| path.to_str())
            .ok_or(SeriesError::UnableToGetFilename)?
            .replace('_', " ");

        let caps = EP_FORMAT
            .captures(&filename)
            .ok_or(SeriesError::EpisodeRegexCaptureFailed)?;

        let series_name = {
            let raw_name = &caps["series"];

            raw_name.replace('.', " ")
            .replace(" -", ":") // Dashes typically represent a colon in file names
            .trim()
            .to_string()
        };

        let episode = caps["episode"]
            .parse()
            .map_err(SeriesError::EpisodeNumParseFailed)?;

        Ok((series_name, episode))
    }
}

#[derive(Debug, Default, Serialize, Deserialize)]
pub struct SaveData {
    pub seasons: Vec<SeasonInfo>,
}

impl SaveData {
    fn from_path(path: &Path) -> Result<SaveData, SeriesError> {
        let file_contents = fs::read_to_string(path)?;
        let data = toml::from_str(&file_contents)?;

        Ok(data)
    }

    fn from_path_or_default(path: &Path) -> Result<SaveData, SeriesError> {
        if path.exists() {
            SaveData::from_path(path)
        } else {
            Ok(SaveData::default())
        }
    }

    fn write_to(&self, path: &Path) -> Result<(), SeriesError> {
        let toml = toml::to_string_pretty(self)?;
        fs::write(path, toml)?;

        Ok(())
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct SeasonInfo {
    pub series_id: u32,
    pub episodes: Option<u32>,
}

impl From<AnimeInfo> for SeasonInfo {
    fn from(info: AnimeInfo) -> SeasonInfo {
        SeasonInfo {
            series_id: info.id,
            episodes: info.episodes,
        }
    }
}
<commit_msg>[core] Fix series in plan to watch state not being set to watching<commit_after>use backend::{AnimeEntry, AnimeInfo, Status, SyncBackend};
use chrono::Local;
use error::SeriesError;
use input::{self, Answer};
use process;
use regex::Regex;
use std;
use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};
use toml;

#[derive(Debug)]
pub struct Series<B>
where
    B: SyncBackend,
{
    sync_backend: B,
    pub episode_data: EpisodeData,
    pub save_data: SaveData,
    pub save_path: PathBuf,
}

impl<B> Series<B>
where
    B: SyncBackend,
{
    pub const DATA_FILE_NAME: &'static str = ".anup";

    pub fn from_data(episode_data: EpisodeData, sync_backend: B) -> Result<Series<B>, SeriesError> {
        let save_path = PathBuf::from(&episode_data.path).join(Series::<B>::DATA_FILE_NAME);
        let save_data = SaveData::from_path_or_default(&save_path)?;

        let series = Series {
            sync_backend,
            episode_data,
            save_data,
            save_path,
        };

        Ok(series)
    }

    pub fn load_season(&mut self, season: u32) -> Result<Season<B>, SeriesError> {
        let season_info = self.get_season_info(season)?;
        let season_ep_offset = self.calculate_season_offset(season);

        let list_entry = self.get_list_entry(season_info.clone())?;

        Season::init(self, list_entry, season_ep_offset)
    }

    pub fn save_data(&self) -> Result<(), SeriesError> {
        self.save_data.write_to(&self.save_path)
    }

    fn get_season_info(&mut self, season: u32) -> Result<AnimeInfo, SeriesError> {
        let num_seasons = self.save_data.seasons.len() as u32;

        if season >= num_seasons {
            let mut series = None;

            for cur_season in num_seasons..=season {
                println!(
                    "select the correct series for season {} of [{}]:",
                    1 + cur_season,
                    self.episode_data.series_name
                );

                let series_info = self.search_and_select_series(&self.episode_data.series_name)?;
                self.save_data.seasons.push(series_info.clone().into());

                series = Some(series_info);
            }

            self.save_data()?;

            // This unwrap should never fail, as the enclosing if statement ensures the for loop will set the
            // series variable at least once
            Ok(series.unwrap())
        } else {
            let season_info = &self.save_data.seasons[season as usize];

            let series = self
                .sync_backend
                .get_series_info_by_id(season_info.series_id)?;

            Ok(series)
        }
    }

    fn calculate_season_offset(&self, season: u32) -> u32 {
        let mut offset = 0;

        for cur_season in 0..(season as usize) {
            if let Some(season_eps) = self.save_data.seasons[cur_season].episodes {
                offset += season_eps;
            }
        }

        offset
    }

    fn search_and_select_series(&self, name: &str) -> Result<AnimeInfo, SeriesError> {
        let mut found = self.sync_backend.search_by_name(name)?;

        println!("{} results for [{}]:", B::name(), name);
        println!("enter the number next to the desired series:\n");

        println!("0 [custom search]");

        for (i, series) in found.iter().enumerate() {
            println!("{} [{}]", 1 + i, series.title);
        }

        let index = input::read_range(0, found.len())?;

        if index == 0 {
            println!("enter the name you want to search for:");

            let name = input::read_line()?;
            self.search_and_select_series(&name)
        } else {
            let info = found.swap_remove(index - 1);
            Ok(info)
        }
    }

    fn get_list_entry(&self, info: AnimeInfo) -> Result<AnimeEntry, SeriesError> {
        let found = self.sync_backend.get_list_entry(info.clone())?;

        match found {
            Some(entry) => Ok(entry),
            None => {
                let mut entry = AnimeEntry::new(info);
                entry.status = Status::Watching;
                entry.start_date = Some(Local::today());

                self.sync_backend.update_list_entry(&entry)?;
                Ok(entry)
            }
        }
    }
}

#[derive(Debug)]
pub struct Season<'a, B>
where
    B: 'a + SyncBackend,
{
    series: &'a Series<B>,
    pub list_entry: AnimeEntry,
    pub ep_offset: u32,
}

impl<'a, B> Season<'a, B>
where
    B: 'a + SyncBackend,
{
    fn init(
        series: &'a Series<B>,
        list_entry: AnimeEntry,
        ep_offset: u32,
    ) -> Result<Season<'a, B>, SeriesError> {
        let mut season = Season {
            series,
            list_entry,
            ep_offset,
        };

        match season.list_entry.status {
            Status::Watching | Status::Rewatching => (),
            Status::Completed => season.update_series_status(Status::Rewatching)?,
            Status::OnHold | Status::Dropped => season.prompt_watch_paused_series()?,
            Status::PlanToWatch => season.update_series_status(Status::Watching)?,
        }

        Ok(season)
    }

    fn update_list_entry(&self) -> Result<(), SeriesError> {
        self.series
            .sync_backend
            .update_list_entry(&self.list_entry)?;

        Ok(())
    }

    pub fn play_episode(&mut self, relative_ep: u32) -> Result<(), SeriesError> {
        let ep_num = self.ep_offset + relative_ep;

        let path = self
            .series
            .episode_data
            .episodes
            .get(&ep_num)
            .ok_or_else(|| SeriesError::EpisodeNotFound(ep_num))?;

        let status = process::open_with_default(path).map_err(SeriesError::FailedToOpenPlayer)?;
        self.list_entry.watched_episodes = relative_ep;

        if !status.success() {
            println!("video player not exited normally");
            println!("do you still want to count the episode as watched? (y/N)");

            if !input::read_yn(Answer::No)? {
                return Ok(());
            }
        }

        match self.list_entry.info.episodes {
            Some(total_eps) if relative_ep >= total_eps => {
                self.update_series_status(Status::Completed)?;
                // TODO: replace with more graceful way of exiting
                std::process::exit(0);
            }
            _ => self.episode_completed()?,
        }

        Ok(())
    }

    pub fn play_all_episodes(&mut self) -> Result<(), SeriesError> {
        loop {
            let next_ep = self.list_entry.watched_episodes + 1;

            self.play_episode(next_ep)?;
            self.next_episode_options()?;
        }
    }

    fn episode_completed(&self) -> Result<(), SeriesError> {
        println!(
            "[{}] episode {}/{} completed",
            self.list_entry.info.title,
            self.list_entry.watched_episodes,
            self.list_entry
                .info
                .episodes
                .map(|e| e.to_string())
                .unwrap_or_else(|| "?".to_string())
        );

        self.update_list_entry()
    }

    fn next_episode_options(&mut self) -> Result<(), SeriesError> {
        println!("options:");
        println!("\t[d] drop series\n\t[h] put series on hold\n\t[r] rate series\n\t[x] exit\n\t[n] watch next episode (default)");

        let input = input::read_line()?.to_lowercase();

        match input.as_str() {
            "d" => {
                self.update_series_status(Status::Dropped)?;
                // TODO: replace with more graceful way of exiting
                std::process::exit(0);
            }
            "h" => {
                self.update_series_status(Status::OnHold)?;
                // TODO: replace with more graceful way of exiting
                std::process::exit(0);
            }
            "r" => {
                self.prompt_to_update_score();
                self.update_list_entry()?;

                self.next_episode_options()?;
            }
            "x" => std::process::exit(0),
            _ => (),
        }

        Ok(())
    }

    fn prompt_to_update_score(&mut self) {
        let (min_score, max_score) = self.series.sync_backend.score_range();
        println!("enter your score between {} and {}:", min_score, max_score);

        let input = match input::read_line() {
            Ok(input) => input,
            Err(err) => {
                eprintln!("failed to read score: {}", err);
                return;
            }
        };

        match self.series.sync_backend.parse_score(&input) {
            Ok(score) => self.list_entry.score = score,
            Err(err) => eprintln!("failed to parse score: {}", err),
        }
    }

    fn prompt_watch_paused_series(&mut self) -> Result<(), SeriesError> {
        println!(
            "[{}] was put on hold or dropped\ndo you want to watch it from the beginning? (Y/n)",
            self.list_entry.info.title
        );

        if input::read_yn(Answer::Yes)? {
            self.list_entry.watched_episodes = 0;
        }

        self.update_series_status(Status::Watching)?;
        Ok(())
    }

    fn update_series_status(&mut self, status: Status) -> Result<(), SeriesError> {
        match status {
            Status::Watching => {
                // A series that was on hold probably already has a starting date, and it would make
                // more sense to use that one instead of replacing it
                if self.list_entry.status != Status::OnHold {
                    self.list_entry.start_date = Some(Local::today());
                }

                self.list_entry.finish_date = None;
            }
            Status::Rewatching => {
                println!("[{}] already completed", self.list_entry.info.title);
                println!("do you want to reset the start and end dates of the series? (Y/n)");

                if input::read_yn(Answer::Yes)? {
                    self.list_entry.start_date = Some(Local::today());
                    self.list_entry.finish_date = None;
                }

                self.list_entry.watched_episodes = 0;
            }
            Status::Completed => {
                if self.list_entry.finish_date.is_none() {
                    self.list_entry.finish_date = Some(Local::today());
                }

                println!(
                    "[{}] completed!\ndo you want to rate it? (Y/n)",
                    self.list_entry.info.title
                );

                if input::read_yn(Answer::Yes)? {
                    self.prompt_to_update_score();
                }
            }
            Status::Dropped => {
                if self.list_entry.finish_date.is_none() {
                    self.list_entry.finish_date = Some(Local::today());
                }
            }
            Status::OnHold | Status::PlanToWatch => (),
        }

        self.list_entry.status = status;
        self.update_list_entry()?;

        Ok(())
    }
}

type SeriesName = String;
type EpisodeNum = u32;

#[derive(Debug)]
pub struct EpisodeData {
    pub series_name: String,
    pub episodes: HashMap<u32, PathBuf>,
    pub path: PathBuf,
}

impl EpisodeData {
    pub const EP_FORMAT_REGEX: &'static str =
        r"(?:\[.+?\]\s*)?(?P<series>.+?)\s*-\s*(?P<episode>\d+).*?\..+?";

    pub fn parse_dir(dir: &Path) -> Result<EpisodeData, SeriesError> {
        if !dir.is_dir() {
            return Err(SeriesError::NotADirectory(
                dir.file_name()
                    .map(|n| n.to_string_lossy().into_owned())
                    .unwrap_or_else(|| "err".into()),
            ));
        }

        let mut series_name = None;
        let mut episodes = HashMap::new();

        for entry in std::fs::read_dir(dir).map_err(SeriesError::Io)? {
            let path = entry.map_err(SeriesError::Io)?.path();

            if !path.is_file() {
                continue;
            }

            match EpisodeData::parse_filename(&path) {
                Ok((ep_name, ep_num)) => {
                    match series_name {
                        Some(ref series_name) if &ep_name != series_name => {
                            return Err(SeriesError::MultipleSeriesFound);
                        }
                        Some(_) => (),
                        None => series_name = Some(ep_name),
                    }

                    episodes.insert(ep_num, path);
                }
                Err(SeriesError::EpisodeRegexCaptureFailed) => continue,
                Err(e) => return Err(e),
            }
        }

        let name = series_name.ok_or(SeriesError::NoEpisodesFound)?;

        Ok(EpisodeData {
            series_name: name,
            episodes,
            path: dir.into(),
        })
    }

    fn parse_filename(path: &Path) -> Result<(SeriesName, EpisodeNum), SeriesError> {
        lazy_static! {
            static ref EP_FORMAT: Regex = Regex::new(EpisodeData::EP_FORMAT_REGEX).unwrap();
        }

        // Replace certain characters with spaces since they can prevent proper series
        // identification or prevent it from being found on a sync backend
        let filename = path
            .file_name()
            .and_then(|path| path.to_str())
            .ok_or(SeriesError::UnableToGetFilename)?
            .replace('_', " ");

        let caps = EP_FORMAT
            .captures(&filename)
            .ok_or(SeriesError::EpisodeRegexCaptureFailed)?;

        let series_name = {
            let raw_name = &caps["series"];

            raw_name.replace('.', " ")
            .replace(" -", ":") // Dashes typically represent a colon in file names
            .trim()
            .to_string()
        };

        let episode = caps["episode"]
            .parse()
            .map_err(SeriesError::EpisodeNumParseFailed)?;

        Ok((series_name, episode))
    }
}

#[derive(Debug, Default, Serialize, Deserialize)]
pub struct SaveData {
    pub seasons: Vec<SeasonInfo>,
}

impl SaveData {
    fn from_path(path: &Path) -> Result<SaveData, SeriesError> {
        let file_contents = fs::read_to_string(path)?;
        let data = toml::from_str(&file_contents)?;

        Ok(data)
    }

    fn from_path_or_default(path: &Path) -> Result<SaveData, SeriesError> {
        if path.exists() {
            SaveData::from_path(path)
        } else {
            Ok(SaveData::default())
        }
    }

    fn write_to(&self, path: &Path) -> Result<(), SeriesError> {
        let toml = toml::to_string_pretty(self)?;
        fs::write(path, toml)?;

        Ok(())
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct SeasonInfo {
    pub series_id: u32,
    pub episodes: Option<u32>,
}

impl From<AnimeInfo> for SeasonInfo {
    fn from(info: AnimeInfo) -> SeasonInfo {
        SeasonInfo {
            series_id: info.id,
            episodes: info.episodes,
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the terms of the
// Mozilla Public License, v. 2.0. If a copy of the MPL was not
// distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.

use std::io::Error;
use std::ops::DerefMut;
use std::sync::{Arc, Mutex};
use std::{ptr, mem, thread};
use std::os::unix::io::RawFd;
use std::collections::LinkedList;

use libc;
use errno::errno;
use epoll;
use epoll::util::*;
use epoll::EpollEvent;
use stream::socket::Socket;
use stream::nbstream::Nbstream;
use stats;
use types::*;
use resources::ResourcePool;
use libc::{c_ushort, c_ulong, c_uint};
use config::Config;

extern "C" {
    fn shim_htons(hostshort: c_ushort) -> c_ushort;
    fn shim_htonl(addr: c_uint) -> c_uint;
    fn shim_inaddr_any() -> c_ulong;
}

// We need to be able to access our resource pool from several methods
static mut pool: *mut ResourcePool = 0 as *mut ResourcePool;

// When added to epoll, these will be the conditions of kernel notification:
//
// EPOLLET  - Fd is in EdgeTriggered mode (notification on state changes)
// EPOLLIN  - Data is available in kerndl buffer
const EVENTS: u32 = event_type::EPOLLET | event_type::EPOLLIN;

/// Starts the epoll wait and incoming connection listener threads.
pub fn begin(config: Config, handler: Box<EventHandler>) {
    // Master socket list
    let sockets = Arc::new(Mutex::new(LinkedList::<Nbstream>::new()));

    // Resource pool
    let mut rp = ResourcePool::new();
    unsafe { pool = &mut rp; }

    // Wrap our event handler into something that can be safely shared
    // between threads.
    let e_handler = Handler(Box::into_raw(handler));

    // Epoll instance
    let result = epoll::create1(0);
    if result.is_err() {
        let err = result.unwrap_err();
        error!("Unable to create epoll instance: {}", err);
        panic!()
    }
    let epfd = result.unwrap();

    // Epoll wait thread
    let epfd2 = epfd.clone();
    let streams2 = sockets.clone();
    thread::Builder::new()
        .name("Epoll Wait".to_string())
        .spawn(move || {
            event_loop(epfd2, streams2, e_handler);
        }).unwrap();

    // New connection thread
    let epfd3 = epfd.clone();
    let streams3 = sockets.clone();
    let prox = thread::Builder::new()
        .name("TCP Incoming Listener".to_string())
        .spawn(move || {
            listen(config, epfd3, streams3);
        }).unwrap();

    // Stay alive forever, or at least we hope
    let _ = prox.join();
}

/// Incoming connection listening thread
fn listen(config: Config, epfd: RawFd, streams: StreamList) {
    unsafe {
        let server_addr = libc::sockaddr_in {
            sin_family: libc::AF_INET as u16,
            sin_port: shim_htons(config.port),
            sin_addr: libc::in_addr {
                s_addr: shim_htonl(shim_inaddr_any() as u32)
            },
            sin_zero: [0u8; 8]
        };

        let server_fd = libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0);
        let serv_addr: *const libc::sockaddr = mem::transmute(&server_addr);

        let bind_result = libc::bind(server_fd, serv_addr,
            mem::size_of::<libc::sockaddr_in>() as u32);
        if bind_result < 0 {
            error!("Binding fd: {}", Error::from_raw_os_error(errno().0 as i32));
            return;
        }

        let listen_result = libc::listen(server_fd, 100);
        if listen_result < 0 {
            error!("Listening: {}", Error::from_raw_os_error(errno().0 as i32));
            return;
        }

        loop {
            // Accept new client
            let result = libc::accept(server_fd,
                ptr::null_mut() as *mut libc::sockaddr, ptr::null_mut() as *mut u32);
            if result < 0 {
                error!("Accepting new connection: {}", Error::from_raw_os_error(result as i32));
            }

            // Create new stream
            let _ = Nbstream::new(Socket {
                fd: result
            }).map(|stream| {
                let fd = stream.as_raw_fd();
                add_stream_to_master_list(stream, streams.clone());
                add_to_epoll(epfd, fd, streams.clone());
                stats::conn_recv();
            }).map_err(|e| {
                // If we ever fail here, it is safe to assume everything else has gone to
                // shit. No way to clean it up, so we'll just leave. The main process thread has
                // joined to this tread, so panic-ing will cause our parent process to end nicely.
                error!("Creating Nbstream: {}", e);
                panic!()
            });
        }
    }
}

/// Event loop for handling all epoll events
fn event_loop(epfd: RawFd, streams: StreamList, handler: Handler) {
    let mut events = Vec::<EpollEvent>::with_capacity(100);
    unsafe { events.set_len(100); }

    loop {
        match epoll::wait(epfd, &mut events[..], -1) {
            Ok(num_events) => {
                for x in 0..num_events as usize {
                    handle_epoll_event(epfd, &events[x], streams.clone(), handler.clone());
                }
            }
            Err(e) => {
                error!("Error on epoll::wait(): {}", e);
                panic!()
            }
        };
    }
}

/// Finds the stream the epoll event is associated with and parses the event type
/// to hand off to specific handlers
fn handle_epoll_event(epfd: RawFd,
                      event: &EpollEvent,
                      streams: StreamList,
                      handler: Handler) {
    const READ_EVENT: u32 = event_type::EPOLLIN;

    // Locate the stream the event was for
    let mut stream;
    { // Mutex lock
        // Find the stream the event was for
        let mut guard = match streams.lock() {
            Ok(guard) => guard,
            Err(poisoned) => {
                warn!("StreamList Mutex was poisoned, using anyway");
                poisoned.into_inner()
            }
        };
        let list = guard.deref_mut();

        let mut found = false;
        let mut index = 1usize;
        for s in list.iter() {
            let fd = s.as_raw_fd();
            if s.as_raw_fd() == event.data as RawFd {
                found = true;
                break;
            }
            index += 1;
        }

        if !found {
            error!("Could not find fd in stream list? Removing from epoll and closing fd");

            let fd = event.data as RawFd;
            remove_fd_from_epoll(epfd, fd);
            close_fd(fd);
            return;
        }

        if index == 1 {
            stream = list.pop_front().unwrap();
        } else {
            let mut split = list.split_off(index - 1);
            stream = split.pop_front().unwrap();
            list.append(&mut split);
        }
    } // Mutex unlock

    if (event.events & READ_EVENT) > 0 {
        trace!("event was read event");
        let _ = handle_read_event(epfd, &mut stream, handler).map(|_| {
            add_stream_to_master_list(stream, streams.clone());
        });
    } else {
        trace!("event was drop event");

        let fd = stream.as_raw_fd();
        remove_fd_from_epoll(epfd, fd);
        close_fd(fd);

        let stream_id = stream.id();
        unsafe {
            (*pool).run(move || {
                let Handler(ptr) = handler;
                (*ptr).on_stream_closed(stream_id.clone());
            });
        }
    }
}

/// Reads all available data on the stream.
///
/// If a complete message(s) is available, each message will be routed through the
/// resource pool.
///
/// If an error occurs during the read, the stream is dropped from the server.
fn handle_read_event(epfd: RawFd, stream: &mut Nbstream, handler: Handler) -> Result<(), ()> {
    trace!("handle read event");

    match stream.recv() {
        Ok(_) => {
            let mut rx_queue = stream.drain_rx_queue();
            for payload in rx_queue.iter_mut() {
                // Check if this is a request for stats
                if payload.len() == 6
                    && payload[0] == 0x04
                    && payload[1] == 0x04 {
                    let u8ptr: *const u8 = &payload[2] as *const _;
                    let f32ptr: *const f32 = u8ptr as *const _;
                    let sec = unsafe { *f32ptr };
                    let stream_cpy = stream.clone();
                    unsafe {
                        (*pool).run(move || {
                            let mut s = stream_cpy.clone();
                            let result = stats::as_serialized_buffer(sec);
                            if result.is_ok() {
                                let _ = s.send(&result.unwrap()[..]);
                            }
                        });
                    }
                    return Ok(())
                }

                // TODO - Refactor once better function passing traits are available in stable.
                let buf_len = payload.len();
                let handler_cpy = handler.clone();
                let stream_cpy = stream.clone();
                let payload_cpy = payload.clone();
                unsafe {
                    (*pool).run(move || {
                        let Handler(ptr) = handler_cpy;
                        (*ptr).on_data_received(stream_cpy.clone(), payload_cpy.clone());
                    });
                }

                // Collect some stats
                stats::msg_recv();
                stats::bytes_recv(buf_len);
            }
            Ok(())
        }
        Err(e) => {
            error!("nbstream: {} fd: {} during read: {}", stream.id(), stream.as_raw_fd(), e);

            remove_fd_from_epoll(epfd, stream.as_raw_fd());
            close_fd(stream.as_raw_fd());

            let stream_id = stream.id();
            unsafe {
                (*pool).run(move || {
                    let Handler(ptr) = handler;
                    (*ptr).on_stream_closed(stream_id.clone());
                });
            }

            Err(())
        }
    }
}

/// Inserts the stream back into the master list of streams
fn add_stream_to_master_list(stream: Nbstream, streams: StreamList) {
    let mut guard = match streams.lock() {
        Ok(guard) => guard,
        Err(poisoned) => {
            warn!("StreamList Mutex failed, using anyway...");
            poisoned.into_inner()
        }
    };
    let stream_list = guard.deref_mut();
    stream_list.push_back(stream);
}

/// Adds a new fd to the epoll instance
fn add_to_epoll(epfd: RawFd, fd: RawFd, streams: StreamList) {
    let result = epoll::ctl(epfd, ctl_op::ADD, fd, &mut EpollEvent {
        data: fd as u64,
        events: EVENTS
    });

    if result.is_ok() {
        trace!("New fd added to epoll");
        stats::conn_recv();
    } else {
        let e = result.unwrap_err();
        error!("CtrlError during add: {}", e);
        remove_fd_from_list(fd, streams.clone());
        close_fd(fd);
    }
}

/// Removes a fd from the epoll instance
fn remove_fd_from_epoll(epfd: RawFd, fd: RawFd) {
    debug!("removing fd from epoll");

    // In kernel versions before 2.6.9, the EPOLL_CTL_DEL operation required
    // a non-null pointer in event, even though this argument is ignored.
    // Since Linux 2.6.9, event can be specified as NULL when using
    // EPOLL_CTL_DEL. We'll be as backwards compatible as possible.
    let _ = epoll::ctl(epfd, ctl_op::DEL, fd, &mut EpollEvent {
        data: 0 as u64,
        events: 0 as u32
    }).map_err(|e| {
        warn!("Epoll CtrlError during del: {}", e)
    });
}

/// Removes stream with id from master list
fn remove_fd_from_list(fd: RawFd, streams: StreamList) {
    trace!("removing fd: {} from master list", fd);

    let mut guard = match streams.lock() {
        Ok(guard) => guard,
        Err(poisoned) => {
            warn!("StreamList Mutex was poisoned, using anyway");
            poisoned.into_inner()
        }
    };
    let list = guard.deref_mut();

    let mut found = false;
    let mut index = 1usize;
    for s in list.iter() {
        if s.as_raw_fd() == fd {
            found = true;
            break;
        }
        index += 1;
    }

    if !found {
        trace!("fd: {} not found in list", fd);
        return;
    }

    if index == 1 {
        list.pop_front();
    } else {
        let mut split = list.split_off(index - 1);
        split.pop_front();
        list.append(&mut split);
    }
}

/// Closes a fd with the kernel
fn close_fd(fd: RawFd) {
    unsafe {
        let result = libc::close(fd);
        if result < 0 {
            error!("Error closing fd: {}", Error::from_raw_os_error(result as i32));
            return;
        }
    }
    stats::conn_lost();
}
<commit_msg>Removed unused variable error<commit_after>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the terms of the
// Mozilla Public License, v. 2.0. If a copy of the MPL was not
// distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.

use std::io::Error;
use std::ops::DerefMut;
use std::sync::{Arc, Mutex};
use std::{ptr, mem, thread};
use std::os::unix::io::RawFd;
use std::collections::LinkedList;

use libc;
use errno::errno;
use epoll;
use epoll::util::*;
use epoll::EpollEvent;
use stream::socket::Socket;
use stream::nbstream::Nbstream;
use stats;
use types::*;
use resources::ResourcePool;
use libc::{c_ushort, c_ulong, c_uint};
use config::Config;

extern "C" {
    fn shim_htons(hostshort: c_ushort) -> c_ushort;
    fn shim_htonl(addr: c_uint) -> c_uint;
    fn shim_inaddr_any() -> c_ulong;
}

// We need to be able to access our resource pool from several methods
static mut pool: *mut ResourcePool = 0 as *mut ResourcePool;

// When added to epoll, these will be the conditions of kernel notification:
//
// EPOLLET  - Fd is in EdgeTriggered mode (notification on state changes)
// EPOLLIN  - Data is available in kerndl buffer
const EVENTS: u32 = event_type::EPOLLET | event_type::EPOLLIN;

/// Starts the epoll wait and incoming connection listener threads.
pub fn begin(config: Config, handler: Box<EventHandler>) {
    // Master socket list
    let sockets = Arc::new(Mutex::new(LinkedList::<Nbstream>::new()));

    // Resource pool
    let mut rp = ResourcePool::new();
    unsafe { pool = &mut rp; }

    // Wrap our event handler into something that can be safely shared
    // between threads.
    let e_handler = Handler(Box::into_raw(handler));

    // Epoll instance
    let result = epoll::create1(0);
    if result.is_err() {
        let err = result.unwrap_err();
        error!("Unable to create epoll instance: {}", err);
        panic!()
    }
    let epfd = result.unwrap();

    // Epoll wait thread
    let epfd2 = epfd.clone();
    let streams2 = sockets.clone();
    thread::Builder::new()
        .name("Epoll Wait".to_string())
        .spawn(move || {
            event_loop(epfd2, streams2, e_handler);
        }).unwrap();

    // New connection thread
    let epfd3 = epfd.clone();
    let streams3 = sockets.clone();
    let prox = thread::Builder::new()
        .name("TCP Incoming Listener".to_string())
        .spawn(move || {
            listen(config, epfd3, streams3);
        }).unwrap();

    // Stay alive forever, or at least we hope
    let _ = prox.join();
}

/// Incoming connection listening thread
fn listen(config: Config, epfd: RawFd, streams: StreamList) {
    unsafe {
        let server_addr = libc::sockaddr_in {
            sin_family: libc::AF_INET as u16,
            sin_port: shim_htons(config.port),
            sin_addr: libc::in_addr {
                s_addr: shim_htonl(shim_inaddr_any() as u32)
            },
            sin_zero: [0u8; 8]
        };

        let server_fd = libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0);
        let serv_addr: *const libc::sockaddr = mem::transmute(&server_addr);

        let bind_result = libc::bind(server_fd, serv_addr,
            mem::size_of::<libc::sockaddr_in>() as u32);
        if bind_result < 0 {
            error!("Binding fd: {}", Error::from_raw_os_error(errno().0 as i32));
            return;
        }

        let listen_result = libc::listen(server_fd, 100);
        if listen_result < 0 {
            error!("Listening: {}", Error::from_raw_os_error(errno().0 as i32));
            return;
        }

        loop {
            // Accept new client
            let result = libc::accept(server_fd,
                ptr::null_mut() as *mut libc::sockaddr, ptr::null_mut() as *mut u32);
            if result < 0 {
                error!("Accepting new connection: {}", Error::from_raw_os_error(result as i32));
            }

            // Create new stream
            let _ = Nbstream::new(Socket {
                fd: result
            }).map(|stream| {
                let fd = stream.as_raw_fd();
                add_stream_to_master_list(stream, streams.clone());
                add_to_epoll(epfd, fd, streams.clone());
                stats::conn_recv();
            }).map_err(|e| {
                // If we ever fail here, it is safe to assume everything else has gone to
                // shit. No way to clean it up, so we'll just leave. The main process thread has
                // joined to this tread, so panic-ing will cause our parent process to end nicely.
                error!("Creating Nbstream: {}", e);
                panic!()
            });
        }
    }
}

/// Event loop for handling all epoll events
fn event_loop(epfd: RawFd, streams: StreamList, handler: Handler) {
    let mut events = Vec::<EpollEvent>::with_capacity(100);
    unsafe { events.set_len(100); }

    loop {
        match epoll::wait(epfd, &mut events[..], -1) {
            Ok(num_events) => {
                for x in 0..num_events as usize {
                    handle_epoll_event(epfd, &events[x], streams.clone(), handler.clone());
                }
            }
            Err(e) => {
                error!("Error on epoll::wait(): {}", e);
                panic!()
            }
        };
    }
}

/// Finds the stream the epoll event is associated with and parses the event type
/// to hand off to specific handlers
fn handle_epoll_event(epfd: RawFd,
                      event: &EpollEvent,
                      streams: StreamList,
                      handler: Handler) {
    const READ_EVENT: u32 = event_type::EPOLLIN;

    // Locate the stream the event was for
    let mut stream;
    { // Mutex lock
        // Find the stream the event was for
        let mut guard = match streams.lock() {
            Ok(guard) => guard,
            Err(poisoned) => {
                warn!("StreamList Mutex was poisoned, using anyway");
                poisoned.into_inner()
            }
        };
        let list = guard.deref_mut();

        let mut found = false;
        let mut index = 1usize;
        for s in list.iter() {
            if s.as_raw_fd() == event.data as RawFd {
                found = true;
                break;
            }
            index += 1;
        }

        if !found {
            error!("Could not find fd in stream list? Removing from epoll and closing fd");

            let fd = event.data as RawFd;
            remove_fd_from_epoll(epfd, fd);
            close_fd(fd);
            return;
        }

        if index == 1 {
            stream = list.pop_front().unwrap();
        } else {
            let mut split = list.split_off(index - 1);
            stream = split.pop_front().unwrap();
            list.append(&mut split);
        }
    } // Mutex unlock

    if (event.events & READ_EVENT) > 0 {
        trace!("event was read event");
        let _ = handle_read_event(epfd, &mut stream, handler).map(|_| {
            add_stream_to_master_list(stream, streams.clone());
        });
    } else {
        trace!("event was drop event");

        let fd = stream.as_raw_fd();
        remove_fd_from_epoll(epfd, fd);
        close_fd(fd);

        let stream_id = stream.id();
        unsafe {
            (*pool).run(move || {
                let Handler(ptr) = handler;
                (*ptr).on_stream_closed(stream_id.clone());
            });
        }
    }
}

/// Reads all available data on the stream.
///
/// If a complete message(s) is available, each message will be routed through the
/// resource pool.
///
/// If an error occurs during the read, the stream is dropped from the server.
fn handle_read_event(epfd: RawFd, stream: &mut Nbstream, handler: Handler) -> Result<(), ()> {
    trace!("handle read event");

    match stream.recv() {
        Ok(_) => {
            let mut rx_queue = stream.drain_rx_queue();
            for payload in rx_queue.iter_mut() {
                // Check if this is a request for stats
                if payload.len() == 6
                    && payload[0] == 0x04
                    && payload[1] == 0x04 {
                    let u8ptr: *const u8 = &payload[2] as *const _;
                    let f32ptr: *const f32 = u8ptr as *const _;
                    let sec = unsafe { *f32ptr };
                    let stream_cpy = stream.clone();
                    unsafe {
                        (*pool).run(move || {
                            let mut s = stream_cpy.clone();
                            let result = stats::as_serialized_buffer(sec);
                            if result.is_ok() {
                                let _ = s.send(&result.unwrap()[..]);
                            }
                        });
                    }
                    return Ok(())
                }

                // TODO - Refactor once better function passing traits are available in stable.
                let buf_len = payload.len();
                let handler_cpy = handler.clone();
                let stream_cpy = stream.clone();
                let payload_cpy = payload.clone();
                unsafe {
                    (*pool).run(move || {
                        let Handler(ptr) = handler_cpy;
                        (*ptr).on_data_received(stream_cpy.clone(), payload_cpy.clone());
                    });
                }

                // Collect some stats
                stats::msg_recv();
                stats::bytes_recv(buf_len);
            }
            Ok(())
        }
        Err(e) => {
            error!("nbstream: {} fd: {} during read: {}", stream.id(), stream.as_raw_fd(), e);

            remove_fd_from_epoll(epfd, stream.as_raw_fd());
            close_fd(stream.as_raw_fd());

            let stream_id = stream.id();
            unsafe {
                (*pool).run(move || {
                    let Handler(ptr) = handler;
                    (*ptr).on_stream_closed(stream_id.clone());
                });
            }

            Err(())
        }
    }
}

/// Inserts the stream back into the master list of streams
fn add_stream_to_master_list(stream: Nbstream, streams: StreamList) {
    let mut guard = match streams.lock() {
        Ok(guard) => guard,
        Err(poisoned) => {
            warn!("StreamList Mutex failed, using anyway...");
            poisoned.into_inner()
        }
    };
    let stream_list = guard.deref_mut();
    stream_list.push_back(stream);
}

/// Adds a new fd to the epoll instance
fn add_to_epoll(epfd: RawFd, fd: RawFd, streams: StreamList) {
    let result = epoll::ctl(epfd, ctl_op::ADD, fd, &mut EpollEvent {
        data: fd as u64,
        events: EVENTS
    });

    if result.is_ok() {
        trace!("New fd added to epoll");
        stats::conn_recv();
    } else {
        let e = result.unwrap_err();
        error!("CtrlError during add: {}", e);
        remove_fd_from_list(fd, streams.clone());
        close_fd(fd);
    }
}

/// Removes a fd from the epoll instance
fn remove_fd_from_epoll(epfd: RawFd, fd: RawFd) {
    debug!("removing fd from epoll");

    // In kernel versions before 2.6.9, the EPOLL_CTL_DEL operation required
    // a non-null pointer in event, even though this argument is ignored.
    // Since Linux 2.6.9, event can be specified as NULL when using
    // EPOLL_CTL_DEL. We'll be as backwards compatible as possible.
    let _ = epoll::ctl(epfd, ctl_op::DEL, fd, &mut EpollEvent {
        data: 0 as u64,
        events: 0 as u32
    }).map_err(|e| {
        warn!("Epoll CtrlError during del: {}", e)
    });
}

/// Removes stream with id from master list
fn remove_fd_from_list(fd: RawFd, streams: StreamList) {
    trace!("removing fd: {} from master list", fd);

    let mut guard = match streams.lock() {
        Ok(guard) => guard,
        Err(poisoned) => {
            warn!("StreamList Mutex was poisoned, using anyway");
            poisoned.into_inner()
        }
    };
    let list = guard.deref_mut();

    let mut found = false;
    let mut index = 1usize;
    for s in list.iter() {
        if s.as_raw_fd() == fd {
            found = true;
            break;
        }
        index += 1;
    }

    if !found {
        trace!("fd: {} not found in list", fd);
        return;
    }

    if index == 1 {
        list.pop_front();
    } else {
        let mut split = list.split_off(index - 1);
        split.pop_front();
        list.append(&mut split);
    }
}

/// Closes a fd with the kernel
fn close_fd(fd: RawFd) {
    unsafe {
        let result = libc::close(fd);
        if result < 0 {
            error!("Error closing fd: {}", Error::from_raw_os_error(result as i32));
            return;
        }
    }
    stats::conn_lost();
}
<|endoftext|>


<commit_before>use syntax::parse::parser::Parser;
use syntax::parse::token;

use syntax::ext::base;

use tags::template::SubTag;
use tags::template::RawRust;

use parse_utils::block_to_string;

/// Parse the inside of a orphan rust tag
/// TODO: implement it, for the moment we simply "consume" the inside
pub fn parse_rust_tag (
    parser: &mut Parser,
    context: &base::ExtCtxt
) -> SubTag {

    let start_rust_block = parser.span.clone();
    while parser.token != token::EOF {

        if parser.token == token::BINOP(token::PERCENT) {
            if parser.look_ahead(1, |token| *token == token::GT) {

                let inner_string = block_to_string(
                    context,
                    &start_rust_block,
                    &parser.span
                );

                //TODO: certainly a better way to do "consume % and >"
                parser.bump();
                parser.bump();

                return RawRust(inner_string);
            }
        }
        parser.bump();
    }

    RawRust(String::new())
}

<commit_msg>handle the case in which we arrive to the end of the file without seeing the %> of a rust tag<commit_after>use syntax::parse::parser::Parser;
use syntax::parse::token;

use syntax::ext::base;

use tags::template::SubTag;
use tags::template::RawRust;

use parse_utils::block_to_string;

/// Parse the inside of a orphan rust tag
/// TODO: implement it, for the moment we simply "consume" the inside
pub fn parse_rust_tag (
    parser: &mut Parser,
    context: &base::ExtCtxt
) -> SubTag {

    let start_rust_block = parser.span.clone();
    while parser.token != token::EOF {
        //TODO need to handle the following error case
        //  <% rust blabla       <% another tag %>
        //                  ^
        //                  %> is missing
        if parser.token == token::BINOP(token::PERCENT) {
            if parser.look_ahead(1, |token| *token == token::GT) {

                let inner_string = block_to_string(
                    context,
                    &start_rust_block,
                    &parser.span
                );

                //TODO: certainly a better way to do "consume % and >"
                parser.bump();
                parser.bump();

                return RawRust(inner_string);
            }
        }
        parser.bump();
    }

    parser.fatal("`rust` tag open but not closed");
}

<|endoftext|>


<commit_before>use serde::{ Serialize, Deserialize };

    pub trait Cell: Clone + Serialize + Deserialize {
        type Coord: Coord;

        fn step<'a, I>(&'a self, neighbors: I) -> Self
            where I: Iterator<Item=Option<&'a Self>>;

        fn with_coord<C: Coord>(C) -> Self;
        fn coord(&self) -> &Self::Coord;
    }

    pub trait Nhood {
        type Coord: Coord;

        fn neighbors(&self, &Self::Coord) -> Vec<Self::Coord>;

        fn neighbors_count(&self) -> usize;
    }

    pub trait Coord: Clone + Serialize + Deserialize {
        fn from_2d(x: i32, y: i32) -> Self;

        fn x(&self) -> i32;
        fn y(&self) -> i32;
        fn z(&self) -> i32 { 0 }
    }

    pub trait Grid {
        type Cell: Cell;
        type Coord: Coord;

        fn step(&mut self);

        fn cells(&self) -> &Vec<Self::Cell>;
        fn dimensions(&self) -> Self::Coord;

        fn restore<G: Grid>(&mut self, &G);
        fn set_cells(&mut self, Vec<Self::Cell>);
    }

    pub trait ReprConsumer {
        fn consume<G: Grid>(&mut self, repr: &G);
    }

    pub trait Engine {
        fn run_times(&mut self, times: i64);
    }<commit_msg>Removed 4-space offset.<commit_after>use serde::{ Serialize, Deserialize };

pub trait Cell: Clone + Serialize + Deserialize {
    type Coord: Coord;

    fn step<'a, I>(&'a self, neighbors: I) -> Self
        where I: Iterator<Item=Option<&'a Self>>;

    fn with_coord<C: Coord>(C) -> Self;
    fn coord(&self) -> &Self::Coord;
}

pub trait Nhood {
    type Coord: Coord;

    fn neighbors(&self, &Self::Coord) -> Vec<Self::Coord>;

    fn neighbors_count(&self) -> usize;
}

pub trait Coord: Clone + Serialize + Deserialize {
    fn from_2d(x: i32, y: i32) -> Self;

    fn x(&self) -> i32;
    fn y(&self) -> i32;
    fn z(&self) -> i32 { 0 }
}

pub trait Grid {
    type Cell: Cell;
    type Coord: Coord;

    fn step(&mut self);

    fn cells(&self) -> &Vec<Self::Cell>;
    fn dimensions(&self) -> Self::Coord;

    fn restore<G: Grid>(&mut self, &G);
    fn set_cells(&mut self, Vec<Self::Cell>);
}

pub trait ReprConsumer {
    fn consume<G: Grid>(&mut self, repr: &G);
}

pub trait Engine {
    fn run_times(&mut self, times: i64);
}<|endoftext|>


<commit_before>mod stack;
mod pc;
mod memory;
mod params;
mod account;
mod storage;
mod cost;
mod run;
mod transaction;

pub use self::memory::{Memory, SeqMemory};
pub use self::stack::Stack;
pub use self::pc::PC;
pub use self::params::{BlockHeader, Context, Patch, Log};
pub use self::account::{Account, AccountCommitment};
pub use self::storage::{Storage, HashMapStorage};
pub use self::transaction::{Transaction, MessageCall, ContractCreation,
                            MessageCallMachine, ContractCreationMachine};

use self::account::AccountState;
use self::cost::{gas_cost, gas_refund, CostAggregrator};
use self::run::run_opcode;
use std::collections::hash_map;
use utils::gas::Gas;
use utils::address::Address;
use utils::bigint::{M256, U256};

#[derive(Debug)]
pub enum ExecutionError {
    EmptyGas,
    EmptyBalance,
    StackUnderflow,
    StackOverflow,
    InvalidOpcode,
    PCOverflow,
    PCBadJumpDest,
    PCTooLarge, // The current implementation only support code size with usize::maximum.
    MemoryTooLarge,
    DataTooLarge,
    CodeTooLarge,
    RequireAccount(Address),
    RequireAccountCode(Address),
    RequireBlockhash(M256),
    Stopped
}

#[derive(Debug)]
pub enum CommitError {
    AlreadyCommitted,
    StateChanged,
    Invalid,
}

pub type ExecutionResult<T> = ::std::result::Result<T, ExecutionError>;
pub type CommitResult<T> = ::std::result::Result<T, CommitError>;

pub type SeqMachine = Machine<SeqMemory, HashMapStorage>;

pub trait VM<S: Storage> {
    fn step(&mut self) -> ExecutionResult<()>;
    fn peek_cost(&self) -> ExecutionResult<Gas>;
    fn fire(&mut self) -> ExecutionResult<()> {
        loop {
            let result = self.step();

            if result.is_err() {
                match result.err().unwrap() {
                    ExecutionError::Stopped => return Ok(()),
                    err => return Err(err),
                }
            }
        }
    }

    fn commit_account(&mut self, commitment: AccountCommitment<S>) -> CommitResult<()>;
    fn commit_blockhash(&mut self, number: M256, hash: M256) -> CommitResult<()>;
    fn accounts(&self) -> hash_map::Values<Address, Account<S>>;
    fn appending_logs(&self) -> &[Log];

    fn patch(&self) -> Patch;
}

pub struct Machine<M, S> {
    pc: PC,
    memory: M,
    stack: Stack,
    cost_aggregrator: CostAggregrator,
    return_values: Vec<u8>,

    context: Context,
    block: BlockHeader,

    account_state: AccountState<S>,
    blockhashes: hash_map::HashMap<M256, M256>,
    appending_logs: Vec<Log>,

    used_gas: Gas,
    refunded_gas: Gas,

    patch: Patch,
}

impl<M: Memory + Default, S: Storage + Default> Machine<M, S> {
    pub fn new(context: Context, block: BlockHeader) -> Self {
        Self {
            pc: PC::new(context.code.as_slice()),
            memory: M::default(),
            stack: Stack::default(),
            cost_aggregrator: CostAggregrator::default(),
            return_values: Vec::new(),

            context: context,
            block: block,

            account_state: AccountState::default(),
            blockhashes: hash_map::HashMap::new(),
            appending_logs: Vec::new(),

            used_gas: Gas::zero(),
            refunded_gas: Gas::zero(),

            patch: Patch::None,
        }
    }
}

impl<M: Memory + Default, S: Storage + Default + Clone> VM<S> for Machine<M, S> {
    fn peek_cost(&self) -> ExecutionResult<Gas> {
        let opcode = self.pc.peek_opcode()?;
        let aggregrator = self.cost_aggregrator;
        let (gas, agg) = gas_cost(opcode, &self, aggregrator)?;
        Ok(gas)
    }

    fn step(&mut self) -> ExecutionResult<()> {
        begin_rescuable!(self, &mut Self, __);
        if self.pc.stopped() {
            trr!(Err(ExecutionError::Stopped), __);
        }

        let position = self.pc.position();
        on_rescue!(|machine| {
            machine.pc.jump_unchecked(position);
        }, __);

        let opcode = trr!(self.pc.read_opcode(), __);
        let available_gas = self.available_gas();
        let cost_aggregrator = self.cost_aggregrator;
        let (gas, agg) = trr!(gas_cost(opcode, self, cost_aggregrator), __);
        let refunded = trr!(gas_refund(opcode, self), __);

        if gas > self.available_gas() {
            trr!(Err(ExecutionError::EmptyGas), __);
        }

        trr!(run_opcode(opcode, self, available_gas - gas), __);

        self.cost_aggregrator = agg;
        self.used_gas = self.used_gas + gas;
        self.refunded_gas = self.refunded_gas + refunded;

        end_rescuable!(__);
        Ok(())
    }

    fn commit_account(&mut self, commitment: AccountCommitment<S>) -> CommitResult<()> {
        self.account_state.commit(commitment)
    }

    fn commit_blockhash(&mut self, number: M256, hash: M256) -> CommitResult<()> {
        if self.blockhashes.contains_key(&number) {
            return Err(CommitError::AlreadyCommitted);
        }

        self.blockhashes.insert(number, hash);
        Ok(())
    }

    fn accounts(&self) -> hash_map::Values<Address, Account<S>> {
        self.account_state.accounts()
    }

    fn appending_logs(&self) -> &[Log] {
        self.appending_logs.as_slice()
    }

    fn patch(&self) -> Patch {
        self.patch
    }
}

impl<M: Memory + Default, S: Storage + Default> Machine<M, S> {
    pub fn pc(&self) -> &PC {
        &self.pc
    }

    pub fn memory(&self) -> &M {
        &self.memory
    }

    pub fn stack(&self) -> &Stack {
        &self.stack
    }

    pub fn context(&self) -> &Context {
        &self.context
    }

    pub fn block(&self) -> &BlockHeader {
        &self.block
    }

    pub fn accounts(&self) -> hash_map::Values<Address, Account<S>> {
        self.account_state.accounts()
    }

    pub fn return_values(&self) -> &[u8] {
        self.return_values.as_slice()
    }

    pub fn active_memory_len(&self) -> M256 {
        self.cost_aggregrator.active_memory_len()
    }

    pub fn available_gas(&self) -> Gas {
        self.context.gas_limit - self.used_gas
    }

    fn blockhash(&mut self, number: M256) -> ExecutionResult<M256> {
        match self.blockhashes.get(&number) {
            Some(val) => Ok(*val),
            None => Err(ExecutionError::RequireBlockhash(number)),
        }
    }

    fn append_log(&mut self, log: Log) {
        self.appending_logs.push(log);
    }
}
<commit_msg>Problem: Machine cannot fire submachine or merge submachine Solution: implement fire_sub and merge_sub<commit_after>mod stack;
mod pc;
mod memory;
mod params;
mod account;
mod storage;
mod cost;
mod run;
mod transaction;

pub use self::memory::{Memory, SeqMemory};
pub use self::stack::Stack;
pub use self::pc::PC;
pub use self::params::{BlockHeader, Context, Patch, Log};
pub use self::account::{Account, AccountCommitment};
pub use self::storage::{Storage, HashMapStorage};
pub use self::transaction::{Transaction, MessageCall, ContractCreation,
                            MessageCallMachine, ContractCreationMachine};

use self::account::AccountState;
use self::cost::{gas_cost, gas_refund, CostAggregrator};
use self::run::run_opcode;
use std::collections::hash_map;
use utils::gas::Gas;
use utils::address::Address;
use utils::bigint::{M256, U256};

#[derive(Debug, Clone)]
pub enum ExecutionError {
    EmptyGas,
    EmptyBalance,
    StackUnderflow,
    StackOverflow,
    InvalidOpcode,
    PCOverflow,
    PCBadJumpDest,
    PCTooLarge, // The current implementation only support code size with usize::maximum.
    MemoryTooLarge,
    DataTooLarge,
    CodeTooLarge,
    RequireAccount(Address),
    RequireAccountCode(Address),
    RequireBlockhash(M256),
    Stopped
}

impl ExecutionError {
    pub fn is_require(&self) -> bool {
        match self {
            &ExecutionError::RequireAccount(_) |
            &ExecutionError::RequireAccountCode(_) |
            &ExecutionError::RequireBlockhash(_) => true,
            _ => false,
        }
    }
}

#[derive(Debug)]
pub enum CommitError {
    AlreadyCommitted,
    StateChanged,
    Invalid,
}

pub type ExecutionResult<T> = ::std::result::Result<T, ExecutionError>;
pub type CommitResult<T> = ::std::result::Result<T, CommitError>;

pub type SeqMachine = Machine<SeqMemory, HashMapStorage>;

pub trait VM<S: Storage> {
    fn step(&mut self) -> ExecutionResult<()>;
    fn peek_cost(&self) -> ExecutionResult<Gas>;
    fn fire(&mut self) -> ExecutionResult<()> {
        loop {
            let result = self.step();

            if result.is_err() {
                match result.err().unwrap() {
                    ExecutionError::Stopped => return Ok(()),
                    err => return Err(err),
                }
            }
        }
    }

    fn commit_account(&mut self, commitment: AccountCommitment<S>) -> CommitResult<()>;
    fn commit_blockhash(&mut self, number: M256, hash: M256) -> CommitResult<()>;
    fn accounts(&self) -> hash_map::Values<Address, Account<S>>;
    fn appending_logs(&self) -> &[Log];

    fn patch(&self) -> Patch;
}

pub struct Machine<M, S> {
    pc: PC,
    memory: M,
    stack: Stack,
    cost_aggregrator: CostAggregrator,
    return_values: Vec<u8>,

    context: Context,
    block: BlockHeader,

    account_state: AccountState<S>,
    blockhashes: hash_map::HashMap<M256, M256>,
    appending_logs: Vec<Log>,

    used_gas: Gas,
    refunded_gas: Gas,

    patch: Patch,
}

impl<M: Memory + Default, S: Storage + Default> Machine<M, S> {
    pub fn new(context: Context, block: BlockHeader) -> Self {
        Self {
            pc: PC::new(context.code.as_slice()),
            memory: M::default(),
            stack: Stack::default(),
            cost_aggregrator: CostAggregrator::default(),
            return_values: Vec::new(),

            context: context,
            block: block,

            account_state: AccountState::default(),
            blockhashes: hash_map::HashMap::new(),
            appending_logs: Vec::new(),

            used_gas: Gas::zero(),
            refunded_gas: Gas::zero(),

            patch: Patch::None,
        }
    }
}

impl<M: Memory + Default, S: Storage + Default + Clone> VM<S> for Machine<M, S> {
    fn peek_cost(&self) -> ExecutionResult<Gas> {
        let opcode = self.pc.peek_opcode()?;
        let aggregrator = self.cost_aggregrator;
        let (gas, agg) = gas_cost(opcode, &self, aggregrator)?;
        Ok(gas)
    }

    fn step(&mut self) -> ExecutionResult<()> {
        begin_rescuable!(self, &mut Self, __);
        if self.pc.stopped() {
            trr!(Err(ExecutionError::Stopped), __);
        }

        let position = self.pc.position();
        on_rescue!(|machine| {
            machine.pc.jump_unchecked(position);
        }, __);

        let opcode = trr!(self.pc.read_opcode(), __);
        let available_gas = self.available_gas();
        let cost_aggregrator = self.cost_aggregrator;
        let (gas, agg) = trr!(gas_cost(opcode, self, cost_aggregrator), __);
        let refunded = trr!(gas_refund(opcode, self), __);

        if gas > self.available_gas() {
            trr!(Err(ExecutionError::EmptyGas), __);
        }

        trr!(run_opcode(opcode, self, available_gas - gas), __);

        self.cost_aggregrator = agg;
        self.used_gas = self.used_gas + gas;
        self.refunded_gas = self.refunded_gas + refunded;

        end_rescuable!(__);
        Ok(())
    }

    fn commit_account(&mut self, commitment: AccountCommitment<S>) -> CommitResult<()> {
        self.account_state.commit(commitment)
    }

    fn commit_blockhash(&mut self, number: M256, hash: M256) -> CommitResult<()> {
        if self.blockhashes.contains_key(&number) {
            return Err(CommitError::AlreadyCommitted);
        }

        self.blockhashes.insert(number, hash);
        Ok(())
    }

    fn accounts(&self) -> hash_map::Values<Address, Account<S>> {
        self.account_state.accounts()
    }

    fn appending_logs(&self) -> &[Log] {
        self.appending_logs.as_slice()
    }

    fn patch(&self) -> Patch {
        self.patch
    }
}

impl<M: Memory + Default, S: Storage + Default + Clone> Machine<M, S> {
    fn fire_sub<V: VM<S>>(&self, submachine: &mut V) -> ExecutionResult<()> {
        loop {
            match submachine.fire() {
                Err(ExecutionError::RequireAccount(address)) => {
                    submachine.commit_account(AccountCommitment::Full {
                        nonce: self.account_state.nonce(address)?,
                        balance: self.account_state.balance(address)?,
                        storage: self.account_state.storage(address)?.clone(),
                        code: self.account_state.code(address)?.into(),
                        address: address,
                    });
                },
                Err(ExecutionError::RequireAccountCode(address)) => {
                    submachine.commit_account(AccountCommitment::Code {
                        code: self.account_state.code(address)?.into(),
                        address: address,
                    });
                },
                val => return val,
            }
        }
    }

    fn merge_sub<V: VM<S>>(&self, submachine: &V) {
        // TODO: add the merge sub logic
    }
}

impl<M: Memory + Default, S: Storage + Default> Machine<M, S> {
    pub fn pc(&self) -> &PC {
        &self.pc
    }

    pub fn memory(&self) -> &M {
        &self.memory
    }

    pub fn stack(&self) -> &Stack {
        &self.stack
    }

    pub fn context(&self) -> &Context {
        &self.context
    }

    pub fn block(&self) -> &BlockHeader {
        &self.block
    }

    pub fn accounts(&self) -> hash_map::Values<Address, Account<S>> {
        self.account_state.accounts()
    }

    pub fn return_values(&self) -> &[u8] {
        self.return_values.as_slice()
    }

    pub fn active_memory_len(&self) -> M256 {
        self.cost_aggregrator.active_memory_len()
    }

    pub fn available_gas(&self) -> Gas {
        self.context.gas_limit - self.used_gas
    }

    fn blockhash(&mut self, number: M256) -> ExecutionResult<M256> {
        match self.blockhashes.get(&number) {
            Some(val) => Ok(*val),
            None => Err(ExecutionError::RequireBlockhash(number)),
        }
    }

    fn append_log(&mut self, log: Log) {
        self.appending_logs.push(log);
    }
}
<|endoftext|>