<commit_before>use backend::man;
use backend::common::{self, Arguments};
use backend::traits::{Try, TryAndIgnore};
use std::env;
use std::fs;
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use std::process;
use tvdb;
use chrono::*;

const EP_NO_VAL:     &'static [u8] = b"no value was set for the episode count.";
const EP_NAN:        &'static [u8] = b"no value was set for the episode count.";
const SR_NO_VAL:     &'static [u8] = b"no value was set for the series name.";
const SN_NO_VAL:     &'static [u8] = b"no value was set for the season number.";
const SN_NAN:        &'static [u8] = b"value set for season number is not a number.";
const PD_NO_VAL:     &'static [u8] = b"no value was set for the pad length.";
const PD_NAN:        &'static [u8] = b"value set for pad length is not a number.";
const INVALID_CWD:   &'static [u8] = b"unable to get the current working directory.";
const INVALID_ARG:   &'static [u8] = b"invalid argument was given.";
const TOO_MANY_ARGS: &'static [u8] = b"too many extra arguments given to the program.";
const STDERR_ERROR:  &'static [u8] = b"error writing to stderr: ";
const STDOUT_ERROR:  &'static [u8] = b"error writing to stdout: ";
const LOG_ERROR:     &'static [u8] = b"error writing to log: ";

trait CLI {
    fn execute(&mut self);

    fn rename_episodes(&self, directory: &Path, stderr: &mut io::Stderr, stdout: &mut io::Stdout);
}

impl CLI for Arguments {
    fn execute(&mut self) {
        let stderr = &mut io::stderr();
        let stdout = &mut io::stdout();

        // Append the current time to the log if logging is enabled.
        if self.log_changes {
            let mut log_path = env::home_dir().try(b"unable to get home directory: ", stderr);
            log_path.push("tv-renamer.log");
            let mut log = fs::OpenOptions::new().create(true).append(true).open(&log_path)
                .try(b"unable to open log: ", stderr);
            let local_time = Local::now().to_rfc2822();
            log.write(b"\n").try(LOG_ERROR, stderr);
            log.write_all(local_time.as_bytes()).try(LOG_ERROR, stderr);
            log.write(b"\n").try(LOG_ERROR, stderr);
            log.flush().try(LOG_ERROR, stderr);
        }

        if self.automatic {
            let series = PathBuf::from(&self.directory);
            self.series_name = series.components().last().unwrap().as_os_str().to_str().unwrap().to_string();
            let seasons = match common::get_seasons(&self.directory) {
                Ok(seasons) => seasons,
                Err(err) => panic!("{}", err)
            };
            for season in seasons {
                if let Some(number) = common::derive_season_number(&season) {
                    self.season_number = number;
                    self.rename_episodes(&season, stderr, stdout);
                }
            }
        } else {
            self.rename_episodes(Path::new(&self.directory), stderr, stdout);
        }
    }

    /// Renames all episodes belonging to a season.
    fn rename_episodes(&self, directory: &Path, stderr: &mut io::Stderr, stdout: &mut io::Stdout) {
        // If TVDB is enabled, create the API and search for the series information.
        let api = tvdb::Tvdb::new("0629B785CE550C8D");
        let series_info = if self.tvdb {
            match api.search(self.series_name.as_str(), "en") {
                Ok(reply) => Some(reply),
                Err(_) => {
                    stderr.write(b"tv-renamer: unable to get series information.\n").try(STDERR_ERROR, stderr);
                    stderr.flush().try(STDERR_ERROR, stderr);
                    process::exit(1);
                }
            }
        } else {
            None
        };

        // Get a list of episodes
        let episodes = match common::get_episodes(directory.to_str().unwrap()) {
            Ok(episodes) => episodes,
            Err(err) => panic!("{}", err)
        };

        let mut current_episode = self.episode_count;
        for file in episodes {
            // If TVDB is enabled, get the episode title from the series information, else return an empty string.
            let title = if self.tvdb {
                let reply = series_info.clone().unwrap();
                match api.episode(&reply[0], self.season_number as u32, current_episode as u32) {
                    Ok(episode) => episode.episode_name,
                    Err(_) => {
                        println!("tv-renamer: episode '{}' does not exist", file.to_string_lossy());
                        current_episode += 1;
                        continue
                    }
                }
            } else {
                String::new()
            };

            // Obtain target destination.
            let new_destination = self.get_destination (
                directory, &file, current_episode, &title
            );

            // Print a message if the file already exists, else rename the file if dry-run is disabled.
            if fs::metadata(&new_destination).is_ok() {
                stdout.write(b"tv-renamer: ").try(STDOUT_ERROR, stderr);
                stdout.write(new_destination.to_string_lossy().as_bytes()).try(STDOUT_ERROR, stderr);
                stdout.write(b" already exists, not renaming.\n").try(STDOUT_ERROR, stderr);
                stdout.flush().try(STDOUT_ERROR, stderr);
            } else if !self.dry_run {
                fs::rename(&file, &new_destination).try(b"unable to rename file: ", stderr);
            }

            // If verbose or dry-run is enabled, print the change that occurred.
            if self.verbose | self.dry_run {
                stdout.write(b"\x1b[1m\x1b[32m").try(b"error writing to stdout: ", stderr);
                stdout.write(common::shorten_path(&file).to_string_lossy().as_bytes()).try(STDOUT_ERROR, stderr);
                stdout.write(b"\x1b[0m ->  \x1b[1m\x1b[32m").try(STDOUT_ERROR, stderr);
                stdout.write(common::shorten_path(&new_destination).to_string_lossy().as_bytes()).try(STDOUT_ERROR, stderr);
                stdout.write(b"\x1b[0m\n").try(STDOUT_ERROR, stderr);
                stdout.flush().try(STDOUT_ERROR, stderr);
            }

            // Write the changes to the log if logging is enabled.
            if self.log_changes {
                let mut log_path = env::home_dir().try(b"unable to get home directory: ", stderr);
                log_path.push("tv-renamer.log");
                let mut log = fs::OpenOptions::new().append(true).open(&log_path).try(b"unable to open log: ", stderr);
                log.write(common::shorten_path(&file).to_string_lossy().as_bytes()).try(LOG_ERROR, stderr);
                log.write(b" -> ").try(LOG_ERROR, stderr);
                log.write(common::shorten_path(&new_destination).to_string_lossy().as_bytes()).try(LOG_ERROR, stderr);
                log.write(b"\n").try(LOG_ERROR, stderr);
                log.flush().try(LOG_ERROR, stderr);
            }

            current_episode += 1;
        }
    }
}

pub fn launch() {
    let mut arguments = parse_arguments();
    arguments.execute();
}

fn parse_arguments() -> Arguments {
    let stdout = io::stdout();
    let stdout = &mut stdout.lock();
    let stderr = &mut io::stderr();

    // Initialize the default variables of the program.
    let mut program = Arguments {
        automatic:     false,
        dry_run:       false,
        log_changes:   false,
        no_name:       false,
        tvdb:          false,
        verbose:       false,
        directory:     String::new(),
        series_name:   String::new(),
        season_number: 1,
        episode_count: 1,
        pad_length:    2,
    };

    // Create a peekable iterator so that we can see the value of some options.
    // We will also need to ignore values that have already been read.
    let mut iterator = env::args().skip(1).peekable();
    let mut ignore = false;

    // Advance the iterator in a loop until all values have been read.
    while let Some(argument) = iterator.next() {
        if ignore { ignore = false; continue }
        if argument.starts_with('-') {
            match argument.as_str() {
                "-a" | "--automatic" => program.automatic = true,
                "-h" | "--help" => {
                    let _ = stdout.write(man::MAN_PAGE.as_bytes());
                    let _ = stdout.flush();
                    process::exit(0);
                }
                "-d" | "--dry-run" => program.dry_run = true,
                "-e" | "--episode-start" => {
                    program.episode_count = iterator.peek().try(EP_NO_VAL, stderr).parse::<usize>()
                        .try_and_ignore(EP_NAN, stderr);
                    ignore = true;
                },
                "-l" | "--log-changes" => program.log_changes = true,
                "-n" | "--series-name" => {
                    program.series_name.push_str(iterator.peek().try(SR_NO_VAL, stderr));
                    ignore = true;
                },
                "--no-name" => program.no_name = true,
                "-s" | "--season-number" => {
                    program.season_number = iterator.peek().try(SN_NO_VAL, stderr).parse::<usize>()
                        .try_and_ignore(SN_NAN, stderr);
                    ignore = true;
                },
                "-t" | "--tvdb" => program.tvdb = true,
                "-p" | "--pad-length" => program.pad_length = iterator.peek().try(PD_NO_VAL, stderr)
                    .parse::<usize>().try_and_ignore(PD_NAN, stderr),
                "-v" | "--verbose" => program.verbose = true,
                _ => abort_with_message(stderr, INVALID_ARG)
            }
        } else if program.directory.is_empty() {
            program.directory = argument;
        } else {
            abort_with_message(stderr, TOO_MANY_ARGS)
        }
    }

    // Set to current working directory if no directory argument is given.
    if program.directory.is_empty() {
        program.directory = String::from(env::current_dir().try_and_ignore(INVALID_CWD, stderr).to_str().unwrap());
    }

    // If no series name was given, ask for the name.
    if program.series_name.is_empty() && !program.no_name && !program.automatic {
        stdout.write(b"Please enter the name of the series: ").try(STDOUT_ERROR, stderr);
        stdout.flush().try(STDOUT_ERROR, stderr);
        io::stdin().read_line(&mut program.series_name).try(b"unable to read from stdin: ", stderr);
        program.series_name.pop().unwrap();
    }

    program
}

#[inline]
/// Print an error message and abort with an exit status of 1.
fn abort_with_message(stderr: &mut io::Stderr, message: &[u8]) {
    stderr.write(b"tv-renamer: ").try(b"error writing to stderr: ", stderr);
    stderr.write(message).try(b"error writing to stderr: ", stderr);
    stderr.flush().try(b"error writing to stderr: ", stderr);
    process::exit(1);
}
<commit_msg>Refactor CLI<commit_after>use backend::man;
use backend::common::{self, Arguments};
use backend::traits::{Try, TryAndIgnore};
use std::env;
use std::fs;
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use std::process;
use tvdb;

const EP_NO_VAL:     &'static [u8] = b"no value was set for the episode count.";
const EP_NAN:        &'static [u8] = b"no value was set for the episode count.";
const SR_NO_VAL:     &'static [u8] = b"no value was set for the series name.";
const SN_NO_VAL:     &'static [u8] = b"no value was set for the season number.";
const SN_NAN:        &'static [u8] = b"value set for season number is not a number.";
const PD_NO_VAL:     &'static [u8] = b"no value was set for the pad length.";
const PD_NAN:        &'static [u8] = b"value set for pad length is not a number.";
const INVALID_CWD:   &'static [u8] = b"unable to get the current working directory.";
const INVALID_ARG:   &'static [u8] = b"invalid argument was given.";
const TOO_MANY_ARGS: &'static [u8] = b"too many extra arguments given to the program.";
const STDERR_ERROR:  &'static [u8] = b"error writing to stderr: ";
const STDOUT_ERROR:  &'static [u8] = b"error writing to stdout: ";

impl Default for Arguments {
    fn default() -> Arguments {
        let stdout = io::stdout();
        let stdout = &mut stdout.lock();
        let stderr = &mut io::stderr();

        // Initialize the default variables of the program.
        let mut program = Arguments {
            automatic:     false,
            dry_run:       false,
            log_changes:   false,
            no_name:       false,
            tvdb:          false,
            verbose:       false,
            directory:     String::new(),
            series_name:   String::new(),
            season_number: 1,
            episode_count: 1,
            pad_length:    2,
        };

        // Create a peekable iterator so that we can see the value of some options.
        // We will also need to ignore values that have already been read.
        let mut iterator = env::args().skip(1).peekable();
        let mut ignore = false;

        // Advance the iterator in a loop until all values have been read.
        while let Some(argument) = iterator.next() {
            if ignore { ignore = false; continue }
            if argument.starts_with('-') {
                match argument.as_str() {
                    "-a" | "--automatic" => program.automatic = true,
                    "-h" | "--help" => {
                        let _ = stdout.write(man::MAN_PAGE.as_bytes());
                        let _ = stdout.flush();
                        process::exit(0);
                    }
                    "-d" | "--dry-run" => program.dry_run = true,
                    "-e" | "--episode-start" => {
                        program.episode_count = iterator.peek().try(EP_NO_VAL, stderr).parse::<usize>()
                            .try_and_ignore(EP_NAN, stderr);
                        ignore = true;
                    },
                    "-l" | "--log-changes" => program.log_changes = true,
                    "-n" | "--series-name" => {
                        program.series_name.push_str(iterator.peek().try(SR_NO_VAL, stderr));
                        ignore = true;
                    },
                    "--no-name" => program.no_name = true,
                    "-s" | "--season-number" => {
                        program.season_number = iterator.peek().try(SN_NO_VAL, stderr).parse::<usize>()
                            .try_and_ignore(SN_NAN, stderr);
                        ignore = true;
                    },
                    "-t" | "--tvdb" => program.tvdb = true,
                    "-p" | "--pad-length" => program.pad_length = iterator.peek().try(PD_NO_VAL, stderr)
                        .parse::<usize>().try_and_ignore(PD_NAN, stderr),
                    "-v" | "--verbose" => program.verbose = true,
                    _ => abort_with_message(stderr, INVALID_ARG)
                }
            } else if program.directory.is_empty() {
                program.directory = argument;
            } else {
                abort_with_message(stderr, TOO_MANY_ARGS)
            }
        }

        // Set to current working directory if no directory argument is given.
        if program.directory.is_empty() {
            program.directory = String::from(env::current_dir().try_and_ignore(INVALID_CWD, stderr).to_str().unwrap());
        }

        // If no series name was given, ask for the name.
        if program.series_name.is_empty() && !program.no_name && !program.automatic {
            stdout.write(b"Please enter the name of the series: ").try(STDOUT_ERROR, stderr);
            stdout.flush().try(STDOUT_ERROR, stderr);
            io::stdin().read_line(&mut program.series_name).try(b"unable to read from stdin: ", stderr);
            program.series_name.pop().unwrap();
        }

        program
    }
}

impl Arguments {
    /// Renames all episodes belonging to a season.
    fn rename_episodes_cli(&self, directory: &Path, stderr: &mut io::Stderr, stdout: &mut io::Stdout) {
        // If TVDB is enabled, create the API and search for the series information.
        let api = tvdb::Tvdb::new("0629B785CE550C8D");
        let series_info = if self.tvdb {
            match api.search(self.series_name.as_str(), "en") {
                Ok(reply) => Some(reply),
                Err(_) => {
                    stderr.write(b"tv-renamer: unable to get series information.\n").try(STDERR_ERROR, stderr);
                    stderr.flush().try(STDERR_ERROR, stderr);
                    process::exit(1);
                }
            }
        } else {
            None
        };

        // Get a list of episodes
        let episodes = match common::get_episodes(directory.to_str().unwrap()) {
            Ok(episodes) => episodes,
            Err(err) => panic!("{}", err)
        };

        let mut current_episode = self.episode_count;
        for source in episodes {
            // If TVDB is enabled, get the episode title from the series information, else return an empty string.
            let title = if self.tvdb {
                let reply = series_info.clone().unwrap();
                match api.episode(&reply[0], self.season_number as u32, current_episode as u32) {
                    Ok(episode) => episode.episode_name,
                    Err(_) => {
                        println!("tv-renamer: episode '{}' does not exist", source.to_string_lossy());
                        current_episode += 1;
                        continue
                    }
                }
            } else {
                String::new()
            };

            // Obtain target destination.
            let target = self.get_destination(directory, &source, current_episode, &title);

            // Print a message if the source already exists, else rename the source if dry-run is disabled.
            if fs::metadata(&target).is_ok() {
                stdout.write(b"tv-renamer: ").try(STDOUT_ERROR, stderr);
                stdout.write(target.to_string_lossy().as_bytes()).try(STDOUT_ERROR, stderr);
                stdout.write(b" already exists, not renaming.\n").try(STDOUT_ERROR, stderr);
                stdout.flush().try(STDOUT_ERROR, stderr);
            } else if !self.dry_run {
                fs::rename(&source, &target).try(b"unable to rename source: ", stderr);
            }

            // If verbose or dry-run is enabled, print the change that occurred.
            if self.verbose | self.dry_run {
                stdout.write(b"\x1b[1m\x1b[32m").try(b"error writing to stdout: ", stderr);
                stdout.write(common::shorten_path(&source).to_string_lossy().as_bytes()).try(STDOUT_ERROR, stderr);
                stdout.write(b"\x1b[0m ->  \x1b[1m\x1b[32m").try(STDOUT_ERROR, stderr);
                stdout.write(common::shorten_path(&target).to_string_lossy().as_bytes()).try(STDOUT_ERROR, stderr);
                stdout.write(b"\x1b[0m\n").try(STDOUT_ERROR, stderr);
                stdout.flush().try(STDOUT_ERROR, stderr);
            }

            // Write the changes to the log if logging is enabled.
            if self.log_changes { common::log_append_change(source.as_path(), target.as_path()); }

            current_episode += 1;
        }
    }
}

pub fn launch() {
    let stderr = &mut io::stderr();
    let stdout = &mut io::stdout();
    let program = &mut Arguments::default();

    if program.log_changes { common::log_append_time(); }

    if program.automatic {
        let series = PathBuf::from(&program.directory);
        program.series_name = series.components().last().unwrap().as_os_str().to_str().unwrap().to_string();
        let seasons = match common::get_seasons(&program.directory) {
            Ok(seasons) => seasons,
            Err(err) => panic!("{}", err)
        };
        for season in seasons {
            if let Some(number) = common::derive_season_number(&season) {
                program.season_number = number;
                program.rename_episodes_cli(&season, stderr, stdout);
            }
        }
    } else {
        program.rename_episodes_cli(Path::new(&program.directory), stderr, stdout);
    }
}

#[inline]
/// Print an error message and abort with an exit status of 1.
fn abort_with_message(stderr: &mut io::Stderr, message: &[u8]) {
    stderr.write(b"tv-renamer: ").try(b"error writing to stderr: ", stderr);
    stderr.write(message).try(b"error writing to stderr: ", stderr);
    stderr.flush().try(b"error writing to stderr: ", stderr);
    process::exit(1);
}
<|endoftext|>


<commit_before>//! The `Resolve` trait, support types, and some basic implementations.
//!
//! This module contains:
//!
//! - A [`GaiResolver`](GaiResolver) that is the default resolver for the
//!   `HttpConnector`.
//! - The [`Resolve`](Resolve) trait and related types to build a custom
//!   resolver for use with the `HttpConnector`.
use std::{fmt, io, vec};
use std::net::{
    IpAddr, Ipv4Addr, Ipv6Addr,
    SocketAddr, ToSocketAddrs,
    SocketAddrV4, SocketAddrV6,
};
use std::sync::Arc;

use futures::{Async, Future, Poll};
use futures::future::{Executor, ExecuteError};
use futures::sync::oneshot;
use futures_cpupool::{Builder as CpuPoolBuilder};
use tokio_threadpool;

use self::sealed::GaiTask;

/// Resolve a hostname to a set of IP addresses.
pub trait Resolve {
    /// The set of IP addresses to try to connect to.
    type Addrs: Iterator<Item=IpAddr>;
    /// A Future of the resolved set of addresses.
    type Future: Future<Item=Self::Addrs, Error=io::Error>;
    /// Resolve a hostname.
    fn resolve(&self, name: Name) -> Self::Future;
}

/// A domain name to resolve into IP addresses.
pub struct Name {
    host: String,
}

/// A resolver using blocking `getaddrinfo` calls in a threadpool.
#[derive(Clone)]
pub struct GaiResolver {
    executor: GaiExecutor,
}

/// An iterator of IP addresses returned from `getaddrinfo`.
pub struct GaiAddrs {
    inner: IpAddrs,
}

/// A future to resole a name returned by `GaiResolver`.
pub struct GaiFuture {
    rx: oneshot::SpawnHandle<IpAddrs, io::Error>,
}

impl Name {
    pub(super) fn new(host: String) -> Name {
        Name {
            host,
        }
    }

    /// View the hostname as a string slice.
    pub fn as_str(&self) -> &str {
        &self.host
    }
}

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

impl GaiResolver {
    /// Construct a new `GaiResolver`.
    ///
    /// Takes number of DNS worker threads.
    pub fn new(threads: usize) -> Self {
        let pool = CpuPoolBuilder::new()
            .name_prefix("hyper-dns")
            .pool_size(threads)
            .create();
        GaiResolver::new_with_executor(pool)
    }

    /// Construct a new `GaiResolver` with a shared thread pool executor.
    ///
    /// Takes an executor to run blocking `getaddrinfo` tasks on.
    pub fn new_with_executor<E: 'static>(executor: E) -> Self
    where
        E: Executor<GaiTask> + Send + Sync,
    {
        GaiResolver {
            executor: GaiExecutor(Arc::new(executor)),
        }
    }
}

impl Resolve for GaiResolver {
    type Addrs = GaiAddrs;
    type Future = GaiFuture;

    fn resolve(&self, name: Name) -> Self::Future {
        let blocking = GaiBlocking::new(name.host);
        let rx = oneshot::spawn(blocking, &self.executor);
        GaiFuture {
            rx,
        }
    }
}

impl fmt::Debug for GaiResolver {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.pad("GaiResolver")
    }
}

impl Future for GaiFuture {
    type Item = GaiAddrs;
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        let addrs = try_ready!(self.rx.poll());
        Ok(Async::Ready(GaiAddrs {
            inner: addrs,
        }))
    }
}

impl fmt::Debug for GaiFuture {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.pad("GaiFuture")
    }
}

impl Iterator for GaiAddrs {
    type Item = IpAddr;

    fn next(&mut self) -> Option<Self::Item> {
        self.inner.next().map(|sa| sa.ip())
    }
}

impl fmt::Debug for GaiAddrs {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.pad("GaiAddrs")
    }
}

#[derive(Clone)]
struct GaiExecutor(Arc<Executor<GaiTask> + Send + Sync>);

impl Executor<oneshot::Execute<GaiBlocking>> for GaiExecutor {
    fn execute(&self, future: oneshot::Execute<GaiBlocking>) -> Result<(), ExecuteError<oneshot::Execute<GaiBlocking>>> {
        self.0.execute(GaiTask { work: future })
            .map_err(|err| ExecuteError::new(err.kind(), err.into_future().work))
    }
}

pub(super) struct GaiBlocking {
    host: String,
}

impl GaiBlocking {
    pub(super) fn new(host: String) -> GaiBlocking {
        GaiBlocking { host }
    }
}

impl Future for GaiBlocking {
    type Item = IpAddrs;
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        debug!("resolving host={:?}", self.host);
        (&*self.host, 0).to_socket_addrs()
            .map(|i| Async::Ready(IpAddrs { iter: i }))
    }
}

pub(super) struct IpAddrs {
    iter: vec::IntoIter<SocketAddr>,
}

impl IpAddrs {
    pub(super) fn new(addrs: Vec<SocketAddr>) -> Self {
        IpAddrs { iter: addrs.into_iter() }
    }

    pub(super) fn try_parse(host: &str, port: u16) -> Option<IpAddrs> {
        if let Ok(addr) = host.parse::<Ipv4Addr>() {
            let addr = SocketAddrV4::new(addr, port);
            return Some(IpAddrs { iter: vec![SocketAddr::V4(addr)].into_iter() })
        }
        if let Ok(addr) = host.parse::<Ipv6Addr>() {
            let addr = SocketAddrV6::new(addr, port, 0, 0);
            return Some(IpAddrs { iter: vec![SocketAddr::V6(addr)].into_iter() })
        }
        None
    }

    pub(super) fn split_by_preference(self) -> (IpAddrs, IpAddrs) {
        let preferring_v6 = self.iter
            .as_slice()
            .first()
            .map(SocketAddr::is_ipv6)
            .unwrap_or(false);

        let (preferred, fallback) = self.iter
            .partition::<Vec<_>, _>(|addr| addr.is_ipv6() == preferring_v6);

        (IpAddrs::new(preferred), IpAddrs::new(fallback))
    }

    pub(super) fn is_empty(&self) -> bool {
        self.iter.as_slice().is_empty()
    }
}

impl Iterator for IpAddrs {
    type Item = SocketAddr;
    #[inline]
    fn next(&mut self) -> Option<SocketAddr> {
        self.iter.next()
    }
}

// Make this Future unnameable outside of this crate.
pub(super) mod sealed {
    use super::*;
    // Blocking task to be executed on a thread pool.
    pub struct GaiTask {
        pub(super) work: oneshot::Execute<GaiBlocking>
    }

    impl fmt::Debug for GaiTask {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            f.pad("GaiTask")
        }
    }

    impl Future for GaiTask {
        type Item = ();
        type Error = ();

        fn poll(&mut self) -> Poll<(), ()> {
            self.work.poll()
        }
    }
}

/// A resolver using `getaddrinfo` calls via the `tokio_threadpool::blocking` API.
///
/// Unlike the `GaiResolver` this will not spawn dedicated threads, but only works when running on the
/// multi-threaded Tokio runtime.
#[derive(Clone, Debug)]
pub struct TokioThreadpoolGaiResolver(());

/// The future returned by `TokioThreadpoolGaiResolver`.
#[derive(Debug)]
pub struct TokioThreadpoolGaiFuture {
    name: Name,
}

impl TokioThreadpoolGaiResolver {
    /// Creates a new DNS resolver that will use tokio threadpool's blocking
    /// feature.
    ///
    /// **Requires** its futures to be run on the threadpool runtime.
    pub fn new() -> Self {
        TokioThreadpoolGaiResolver::new()
    }
}

impl Resolve for TokioThreadpoolGaiResolver {
    type Addrs = GaiAddrs;
    type Future = TokioThreadpoolGaiFuture;

    fn resolve(&self, name: Name) -> TokioThreadpoolGaiFuture {
        TokioThreadpoolGaiFuture { name }
    }
}

impl Future for TokioThreadpoolGaiFuture {
    type Item = GaiAddrs;
    type Error = io::Error;

    fn poll(&mut self) -> Poll<GaiAddrs, io::Error> {
        match tokio_threadpool::blocking(|| (self.name.as_str(), 0).to_socket_addrs()) {
            Ok(Async::Ready(Ok(iter))) => Ok(Async::Ready(GaiAddrs { inner: IpAddrs { iter } })),
            Ok(Async::Ready(Err(e))) => Err(e),
            Ok(Async::NotReady) => Ok(Async::NotReady),
            Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)),
        }
    }
}

#[cfg(test)]
mod tests {
    use std::net::{Ipv4Addr, Ipv6Addr};
    use super::*;

    #[test]
    fn test_ip_addrs_split_by_preference() {
        let v4_addr = (Ipv4Addr::new(127, 0, 0, 1), 80).into();
        let v6_addr = (Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 80).into();

        let (mut preferred, mut fallback) =
            IpAddrs { iter: vec![v4_addr, v6_addr].into_iter() }.split_by_preference();
        assert!(preferred.next().unwrap().is_ipv4());
        assert!(fallback.next().unwrap().is_ipv6());

        let (mut preferred, mut fallback) =
            IpAddrs { iter: vec![v6_addr, v4_addr].into_iter() }.split_by_preference();
        assert!(preferred.next().unwrap().is_ipv6());
        assert!(fallback.next().unwrap().is_ipv4());
    }
}
<commit_msg>chore(client): fix infinite recursion in TokioThreadpoolGaiResolver::new<commit_after>//! The `Resolve` trait, support types, and some basic implementations.
//!
//! This module contains:
//!
//! - A [`GaiResolver`](GaiResolver) that is the default resolver for the
//!   `HttpConnector`.
//! - The [`Resolve`](Resolve) trait and related types to build a custom
//!   resolver for use with the `HttpConnector`.
use std::{fmt, io, vec};
use std::net::{
    IpAddr, Ipv4Addr, Ipv6Addr,
    SocketAddr, ToSocketAddrs,
    SocketAddrV4, SocketAddrV6,
};
use std::sync::Arc;

use futures::{Async, Future, Poll};
use futures::future::{Executor, ExecuteError};
use futures::sync::oneshot;
use futures_cpupool::{Builder as CpuPoolBuilder};
use tokio_threadpool;

use self::sealed::GaiTask;

/// Resolve a hostname to a set of IP addresses.
pub trait Resolve {
    /// The set of IP addresses to try to connect to.
    type Addrs: Iterator<Item=IpAddr>;
    /// A Future of the resolved set of addresses.
    type Future: Future<Item=Self::Addrs, Error=io::Error>;
    /// Resolve a hostname.
    fn resolve(&self, name: Name) -> Self::Future;
}

/// A domain name to resolve into IP addresses.
pub struct Name {
    host: String,
}

/// A resolver using blocking `getaddrinfo` calls in a threadpool.
#[derive(Clone)]
pub struct GaiResolver {
    executor: GaiExecutor,
}

/// An iterator of IP addresses returned from `getaddrinfo`.
pub struct GaiAddrs {
    inner: IpAddrs,
}

/// A future to resole a name returned by `GaiResolver`.
pub struct GaiFuture {
    rx: oneshot::SpawnHandle<IpAddrs, io::Error>,
}

impl Name {
    pub(super) fn new(host: String) -> Name {
        Name {
            host,
        }
    }

    /// View the hostname as a string slice.
    pub fn as_str(&self) -> &str {
        &self.host
    }
}

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

impl GaiResolver {
    /// Construct a new `GaiResolver`.
    ///
    /// Takes number of DNS worker threads.
    pub fn new(threads: usize) -> Self {
        let pool = CpuPoolBuilder::new()
            .name_prefix("hyper-dns")
            .pool_size(threads)
            .create();
        GaiResolver::new_with_executor(pool)
    }

    /// Construct a new `GaiResolver` with a shared thread pool executor.
    ///
    /// Takes an executor to run blocking `getaddrinfo` tasks on.
    pub fn new_with_executor<E: 'static>(executor: E) -> Self
    where
        E: Executor<GaiTask> + Send + Sync,
    {
        GaiResolver {
            executor: GaiExecutor(Arc::new(executor)),
        }
    }
}

impl Resolve for GaiResolver {
    type Addrs = GaiAddrs;
    type Future = GaiFuture;

    fn resolve(&self, name: Name) -> Self::Future {
        let blocking = GaiBlocking::new(name.host);
        let rx = oneshot::spawn(blocking, &self.executor);
        GaiFuture {
            rx,
        }
    }
}

impl fmt::Debug for GaiResolver {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.pad("GaiResolver")
    }
}

impl Future for GaiFuture {
    type Item = GaiAddrs;
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        let addrs = try_ready!(self.rx.poll());
        Ok(Async::Ready(GaiAddrs {
            inner: addrs,
        }))
    }
}

impl fmt::Debug for GaiFuture {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.pad("GaiFuture")
    }
}

impl Iterator for GaiAddrs {
    type Item = IpAddr;

    fn next(&mut self) -> Option<Self::Item> {
        self.inner.next().map(|sa| sa.ip())
    }
}

impl fmt::Debug for GaiAddrs {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.pad("GaiAddrs")
    }
}

#[derive(Clone)]
struct GaiExecutor(Arc<Executor<GaiTask> + Send + Sync>);

impl Executor<oneshot::Execute<GaiBlocking>> for GaiExecutor {
    fn execute(&self, future: oneshot::Execute<GaiBlocking>) -> Result<(), ExecuteError<oneshot::Execute<GaiBlocking>>> {
        self.0.execute(GaiTask { work: future })
            .map_err(|err| ExecuteError::new(err.kind(), err.into_future().work))
    }
}

pub(super) struct GaiBlocking {
    host: String,
}

impl GaiBlocking {
    pub(super) fn new(host: String) -> GaiBlocking {
        GaiBlocking { host }
    }
}

impl Future for GaiBlocking {
    type Item = IpAddrs;
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        debug!("resolving host={:?}", self.host);
        (&*self.host, 0).to_socket_addrs()
            .map(|i| Async::Ready(IpAddrs { iter: i }))
    }
}

pub(super) struct IpAddrs {
    iter: vec::IntoIter<SocketAddr>,
}

impl IpAddrs {
    pub(super) fn new(addrs: Vec<SocketAddr>) -> Self {
        IpAddrs { iter: addrs.into_iter() }
    }

    pub(super) fn try_parse(host: &str, port: u16) -> Option<IpAddrs> {
        if let Ok(addr) = host.parse::<Ipv4Addr>() {
            let addr = SocketAddrV4::new(addr, port);
            return Some(IpAddrs { iter: vec![SocketAddr::V4(addr)].into_iter() })
        }
        if let Ok(addr) = host.parse::<Ipv6Addr>() {
            let addr = SocketAddrV6::new(addr, port, 0, 0);
            return Some(IpAddrs { iter: vec![SocketAddr::V6(addr)].into_iter() })
        }
        None
    }

    pub(super) fn split_by_preference(self) -> (IpAddrs, IpAddrs) {
        let preferring_v6 = self.iter
            .as_slice()
            .first()
            .map(SocketAddr::is_ipv6)
            .unwrap_or(false);

        let (preferred, fallback) = self.iter
            .partition::<Vec<_>, _>(|addr| addr.is_ipv6() == preferring_v6);

        (IpAddrs::new(preferred), IpAddrs::new(fallback))
    }

    pub(super) fn is_empty(&self) -> bool {
        self.iter.as_slice().is_empty()
    }
}

impl Iterator for IpAddrs {
    type Item = SocketAddr;
    #[inline]
    fn next(&mut self) -> Option<SocketAddr> {
        self.iter.next()
    }
}

// Make this Future unnameable outside of this crate.
pub(super) mod sealed {
    use super::*;
    // Blocking task to be executed on a thread pool.
    pub struct GaiTask {
        pub(super) work: oneshot::Execute<GaiBlocking>
    }

    impl fmt::Debug for GaiTask {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            f.pad("GaiTask")
        }
    }

    impl Future for GaiTask {
        type Item = ();
        type Error = ();

        fn poll(&mut self) -> Poll<(), ()> {
            self.work.poll()
        }
    }
}

/// A resolver using `getaddrinfo` calls via the `tokio_threadpool::blocking` API.
///
/// Unlike the `GaiResolver` this will not spawn dedicated threads, but only works when running on the
/// multi-threaded Tokio runtime.
#[derive(Clone, Debug)]
pub struct TokioThreadpoolGaiResolver(());

/// The future returned by `TokioThreadpoolGaiResolver`.
#[derive(Debug)]
pub struct TokioThreadpoolGaiFuture {
    name: Name,
}

impl TokioThreadpoolGaiResolver {
    /// Creates a new DNS resolver that will use tokio threadpool's blocking
    /// feature.
    ///
    /// **Requires** its futures to be run on the threadpool runtime.
    pub fn new() -> Self {
        TokioThreadpoolGaiResolver(())
    }
}

impl Resolve for TokioThreadpoolGaiResolver {
    type Addrs = GaiAddrs;
    type Future = TokioThreadpoolGaiFuture;

    fn resolve(&self, name: Name) -> TokioThreadpoolGaiFuture {
        TokioThreadpoolGaiFuture { name }
    }
}

impl Future for TokioThreadpoolGaiFuture {
    type Item = GaiAddrs;
    type Error = io::Error;

    fn poll(&mut self) -> Poll<GaiAddrs, io::Error> {
        match tokio_threadpool::blocking(|| (self.name.as_str(), 0).to_socket_addrs()) {
            Ok(Async::Ready(Ok(iter))) => Ok(Async::Ready(GaiAddrs { inner: IpAddrs { iter } })),
            Ok(Async::Ready(Err(e))) => Err(e),
            Ok(Async::NotReady) => Ok(Async::NotReady),
            Err(e) => Err(io::Error::new(io::ErrorKind::Other, e)),
        }
    }
}

#[cfg(test)]
mod tests {
    use std::net::{Ipv4Addr, Ipv6Addr};
    use super::*;

    #[test]
    fn test_ip_addrs_split_by_preference() {
        let v4_addr = (Ipv4Addr::new(127, 0, 0, 1), 80).into();
        let v6_addr = (Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 80).into();

        let (mut preferred, mut fallback) =
            IpAddrs { iter: vec![v4_addr, v6_addr].into_iter() }.split_by_preference();
        assert!(preferred.next().unwrap().is_ipv4());
        assert!(fallback.next().unwrap().is_ipv6());

        let (mut preferred, mut fallback) =
            IpAddrs { iter: vec![v6_addr, v4_addr].into_iter() }.split_by_preference();
        assert!(preferred.next().unwrap().is_ipv6());
        assert!(fallback.next().unwrap().is_ipv4());
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2016 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

use std::rc::Rc;
use std::cell::RefCell;
use std::collections::HashMap;

use capnp_rpc::{RpcSystem, twoparty, rpc_twoparty_capnp};
use pubsub_capnp::{publisher, subscriber, subscription};

use gj::{EventLoop, Promise, TaskReaper, TaskSet};

struct SubscriberMap {
    subscribers: HashMap<u64, subscriber::Client<::capnp::text::Owned>>,
}

impl SubscriberMap {
    fn new() -> SubscriberMap {
        SubscriberMap { subscribers: HashMap::new() }
    }
}

struct SubscriptionImpl {
    id: u64,
    subscribers: Rc<RefCell<SubscriberMap>>,
}

impl SubscriptionImpl {
    fn new(id: u64, subscribers: Rc<RefCell<SubscriberMap>>) -> SubscriptionImpl {
        SubscriptionImpl { id: id, subscribers: subscribers }
    }
}

impl Drop for SubscriptionImpl {
    fn drop(&mut self) {
        println!("subscription dropped");
        self.subscribers.borrow_mut().subscribers.remove(&self.id);
    }
}

impl subscription::Server for SubscriptionImpl {}

struct PublisherImpl {
    next_id: u64,
    subscribers: Rc<RefCell<SubscriberMap>>,
}

impl PublisherImpl {
    pub fn new() -> (PublisherImpl, Rc<RefCell<SubscriberMap>>) {
        let subscribers = Rc::new(RefCell::new(SubscriberMap::new()));
        (PublisherImpl { next_id: 0, subscribers: subscribers.clone() },
         subscribers.clone())
    }
}

impl publisher::Server<::capnp::text::Owned> for PublisherImpl {
    fn subscribe(&mut self,
                 params: publisher::SubscribeParams<::capnp::text::Owned>,
                 mut results: publisher::SubscribeResults<::capnp::text::Owned>,)
                 -> Promise<(), ::capnp::Error>
    {
        println!("subscribe");
        self.subscribers.borrow_mut().subscribers.insert(self.next_id,
                                                         pry!(pry!(params.get()).get_subscriber()));

        results.get().set_subscription(
            subscription::ToClient::new(SubscriptionImpl::new(self.next_id, self.subscribers.clone()))
                .from_server::<::capnp_rpc::Server>());

        self.next_id += 1;
        Promise::ok(())
    }
}

pub fn accept_loop(listener: ::gjio::SocketListener,
                   task_set: Rc<RefCell<TaskSet<(), ::capnp::Error>>>,
                   publisher: publisher::Client<::capnp::text::Owned>)
                   -> Promise<(), ::std::io::Error>
{
    listener.accept().then(move |stream| {
        let mut network =
            twoparty::VatNetwork::new(stream.clone(), stream,
                                      rpc_twoparty_capnp::Side::Server, Default::default());
        let disconnect_promise = network.on_disconnect();

        let rpc_system = RpcSystem::new(Box::new(network), Some(publisher.clone().client));

        task_set.borrow_mut().add(disconnect_promise.attach(rpc_system));
        accept_loop(listener, task_set, publisher)
    })
}

struct Reaper;

impl TaskReaper<(), ::capnp::Error> for Reaper {
    fn task_failed(&mut self, error: ::capnp::Error) {
        println!("Task failed: {}", error);
    }
}

fn send_to_subscribers(subscribers: Rc<RefCell<SubscriberMap>>,
                       timer: ::gjio::Timer,
                       task_set: Rc<RefCell<TaskSet<(), ::capnp::Error>>>)
                       -> Promise<(), ::capnp::Error>
{
    timer.after_delay(::std::time::Duration::new(1, 0)).lift().then(move |()| {
        {
            for (_, subscriber) in subscribers.borrow().subscribers.iter() {
                let mut request = subscriber.push_value_request();
                pry!(request.get().set_value(
                    &format!("system time is: {:?}", ::std::time::SystemTime::now())[..]));
                task_set.borrow_mut().add(request.send().promise.map(|_| Ok(())));
            }
        }
        send_to_subscribers(subscribers, timer, task_set)
    })
}

pub fn main() {
    let args: Vec<String> = ::std::env::args().collect();
    if args.len() != 3 {
        println!("usage: {} server HOST:PORT", args[0]);
        return;
    }

    EventLoop::top_level(move |wait_scope| -> Result<(), Box<::std::error::Error>> {
        use std::net::ToSocketAddrs;
        let mut event_port = try!(::gjio::EventPort::new());
        let network = event_port.get_network();
        let addr = try!(args[2].to_socket_addrs()).next().expect("could not parse address");
        let mut address = network.get_tcp_address(addr);
        let listener = try!(address.listen());

        let (publisher_impl, subscribers) = PublisherImpl::new();

        let publisher = publisher::ToClient::new(publisher_impl).from_server::<::capnp_rpc::Server>();

        let task_set = Rc::new(RefCell::new(TaskSet::new(Box::new(Reaper))));

        let task_set_clone = task_set.clone();

        task_set.borrow_mut().add(send_to_subscribers(subscribers, event_port.get_timer(), task_set_clone));

        try!(accept_loop(listener, task_set, publisher).wait(wait_scope, &mut event_port));

        Ok(())
    }).expect("top level error");
}
<commit_msg>pubsub example: limit number of in-flight pushValue() requests.<commit_after>// Copyright (c) 2013-2016 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

use std::rc::Rc;
use std::cell::RefCell;
use std::collections::HashMap;

use capnp_rpc::{RpcSystem, twoparty, rpc_twoparty_capnp};
use pubsub_capnp::{publisher, subscriber, subscription};

use gj::{EventLoop, Promise, TaskReaper, TaskSet};

struct SubscriberHandle {
    client: subscriber::Client<::capnp::text::Owned>,
    requests_in_flight: i32,
}

struct SubscriberMap {
    subscribers: HashMap<u64, SubscriberHandle>,
}

impl SubscriberMap {
    fn new() -> SubscriberMap {
        SubscriberMap { subscribers: HashMap::new() }
    }
}

struct SubscriptionImpl {
    id: u64,
    subscribers: Rc<RefCell<SubscriberMap>>,
}

impl SubscriptionImpl {
    fn new(id: u64, subscribers: Rc<RefCell<SubscriberMap>>) -> SubscriptionImpl {
        SubscriptionImpl { id: id, subscribers: subscribers }
    }
}

impl Drop for SubscriptionImpl {
    fn drop(&mut self) {
        println!("subscription dropped");
        self.subscribers.borrow_mut().subscribers.remove(&self.id);
    }
}

impl subscription::Server for SubscriptionImpl {}

struct PublisherImpl {
    next_id: u64,
    subscribers: Rc<RefCell<SubscriberMap>>,
}

impl PublisherImpl {
    pub fn new() -> (PublisherImpl, Rc<RefCell<SubscriberMap>>) {
        let subscribers = Rc::new(RefCell::new(SubscriberMap::new()));
        (PublisherImpl { next_id: 0, subscribers: subscribers.clone() },
         subscribers.clone())
    }
}

impl publisher::Server<::capnp::text::Owned> for PublisherImpl {
    fn subscribe(&mut self,
                 params: publisher::SubscribeParams<::capnp::text::Owned>,
                 mut results: publisher::SubscribeResults<::capnp::text::Owned>,)
                 -> Promise<(), ::capnp::Error>
    {
        println!("subscribe");
        self.subscribers.borrow_mut().subscribers.insert(
            self.next_id,
            SubscriberHandle {
                client: pry!(pry!(params.get()).get_subscriber()),
                requests_in_flight: 0,
            }
        );

        results.get().set_subscription(
            subscription::ToClient::new(SubscriptionImpl::new(self.next_id, self.subscribers.clone()))
                .from_server::<::capnp_rpc::Server>());

        self.next_id += 1;
        Promise::ok(())
    }
}

pub fn accept_loop(listener: ::gjio::SocketListener,
                   task_set: Rc<RefCell<TaskSet<(), ::capnp::Error>>>,
                   publisher: publisher::Client<::capnp::text::Owned>)
                   -> Promise<(), ::std::io::Error>
{
    listener.accept().then(move |stream| {
        let mut network =
            twoparty::VatNetwork::new(stream.clone(), stream,
                                      rpc_twoparty_capnp::Side::Server, Default::default());
        let disconnect_promise = network.on_disconnect();

        let rpc_system = RpcSystem::new(Box::new(network), Some(publisher.clone().client));

        task_set.borrow_mut().add(disconnect_promise.attach(rpc_system));
        accept_loop(listener, task_set, publisher)
    })
}

struct Reaper;

impl TaskReaper<(), ::capnp::Error> for Reaper {
    fn task_failed(&mut self, error: ::capnp::Error) {
        println!("Task failed: {}", error);
    }
}

fn send_to_subscribers(subscribers: Rc<RefCell<SubscriberMap>>,
                       timer: ::gjio::Timer,
                       task_set: Rc<RefCell<TaskSet<(), ::capnp::Error>>>)
                       -> Promise<(), ::capnp::Error>
{
    timer.after_delay(::std::time::Duration::new(1, 0)).lift().then(move |()| {
        {
            let subscribers1 = subscribers.clone();
            let subs = &mut subscribers.borrow_mut().subscribers;
            for (&idx, mut subscriber) in subs.iter_mut() {
                if subscriber.requests_in_flight < 5 {
                    subscriber.requests_in_flight += 1;
                    let mut request = subscriber.client.push_value_request();
                    pry!(request.get().set_value(
                        &format!("system time is: {:?}", ::std::time::SystemTime::now())[..]));

                    let subscribers2 = subscribers1.clone();
                    task_set.borrow_mut().add(request.send().promise.map_else(move |r| {
                        match r {
                            Ok(_) => {
                                subscribers2.borrow_mut().subscribers.get_mut(&idx).map(|ref mut s| {
                                    s.requests_in_flight -= 1;
                                });
                            }
                            Err(e) => {
                                println!("Got error: {:?}. Dropping subscriber.", e);
                                subscribers2.borrow_mut().subscribers.remove(&idx);
                            }
                        }
                        Ok(())
                    }));
                }
            }
        }
        send_to_subscribers(subscribers, timer, task_set)
    })
}

pub fn main() {
    let args: Vec<String> = ::std::env::args().collect();
    if args.len() != 3 {
        println!("usage: {} server HOST:PORT", args[0]);
        return;
    }

    EventLoop::top_level(move |wait_scope| -> Result<(), Box<::std::error::Error>> {
        use std::net::ToSocketAddrs;
        let mut event_port = try!(::gjio::EventPort::new());
        let network = event_port.get_network();
        let addr = try!(args[2].to_socket_addrs()).next().expect("could not parse address");
        let mut address = network.get_tcp_address(addr);
        let listener = try!(address.listen());

        let (publisher_impl, subscribers) = PublisherImpl::new();

        let publisher = publisher::ToClient::new(publisher_impl).from_server::<::capnp_rpc::Server>();

        let task_set = Rc::new(RefCell::new(TaskSet::new(Box::new(Reaper))));

        let task_set_clone = task_set.clone();

        task_set.borrow_mut().add(send_to_subscribers(subscribers, event_port.get_timer(), task_set_clone));

        try!(accept_loop(listener, task_set, publisher).wait(wait_scope, &mut event_port));

        Ok(())
    }).expect("top level error");
}
<|endoftext|>


<commit_before>/* 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/. */

//! CSS table formatting contexts.

#![deny(unsafe_code)]

use app_units::Au;
use context::LayoutContext;
use display_list_builder::DisplayListBuildState;
use euclid::Point2D;
use flow::{BaseFlow, Flow, FlowClass, ForceNonfloatedFlag, OpaqueFlow};
use fragment::{Fragment, FragmentBorderBoxIterator, Overflow, SpecificFragmentInfo};
use layout_debug;
use std::cmp::max;
use std::fmt;
use style::logical_geometry::LogicalSize;
use style::properties::ComputedValues;
use style::values::computed::LengthOrPercentageOrAuto;

/// A table formatting context.
pub struct TableColGroupFlow {
    /// Data common to all flows.
    pub base: BaseFlow,

    /// The associated fragment.
    pub fragment: Option<Fragment>,

    /// The table column fragments
    pub cols: Vec<Fragment>,

    /// The specified inline-sizes of table columns. (We use `LengthOrPercentageOrAuto` here in
    /// lieu of `ColumnInlineSize` because column groups do not establish minimum or preferred
    /// inline sizes.)
    pub inline_sizes: Vec<LengthOrPercentageOrAuto>,
}

impl TableColGroupFlow {
    pub fn from_fragments(fragment: Fragment, fragments: Vec<Fragment>) -> TableColGroupFlow {
        let writing_mode = fragment.style().writing_mode;
        TableColGroupFlow {
            base: BaseFlow::new(Some(fragment.style()),
                                writing_mode,
                                ForceNonfloatedFlag::ForceNonfloated),
            fragment: Some(fragment),
            cols: fragments,
            inline_sizes: vec!(),
        }
    }
}

impl Flow for TableColGroupFlow {
    fn class(&self) -> FlowClass {
        FlowClass::TableColGroup
    }

    fn as_mut_table_colgroup(&mut self) -> &mut TableColGroupFlow {
        self
    }

    fn bubble_inline_sizes(&mut self) {
        let _scope = layout_debug_scope!("table_colgroup::bubble_inline_sizes {:x}",
                                            self.base.debug_id());

        for fragment in &self.cols {
            // Retrieve the specified value from the appropriate CSS property.
            let inline_size = fragment.style().content_inline_size();
            let span = match fragment.specific {
                SpecificFragmentInfo::TableColumn(col_fragment) => max(col_fragment.span, 1),
                _ => panic!("non-table-column fragment inside table column?!"),
            };
            for _ in 0..span {
                self.inline_sizes.push(inline_size)
            }
        }
    }

    /// Table column inline-sizes are assigned in the table flow and propagated to table row flows
    /// and/or rowgroup flows. Therefore, table colgroup flows do not need to assign inline-sizes.
    fn assign_inline_sizes(&mut self, _: &LayoutContext) {
    }

    /// Table columns do not have block-size.
    fn assign_block_size(&mut self, _: &LayoutContext) {
    }

    fn update_late_computed_inline_position_if_necessary(&mut self, _: Au) {}

    fn update_late_computed_block_position_if_necessary(&mut self, _: Au) {}

    // Table columns are invisible.
    fn build_display_list(&mut self, _: &mut DisplayListBuildState) { }

    fn collect_stacking_contexts(&mut self, _: &mut DisplayListBuildState) {}

    fn repair_style(&mut self, _: &::ServoArc<ComputedValues>) {}

    fn compute_overflow(&self) -> Overflow {
        Overflow::new()
    }

    fn generated_containing_block_size(&self, _: OpaqueFlow) -> LogicalSize<Au> {
        panic!("Table column groups can't be containing blocks!")
    }

    fn iterate_through_fragment_border_boxes(&self,
                                             _: &mut FragmentBorderBoxIterator,
                                             _: i32,
                                             _: &Point2D<Au>) {}

    fn mutate_fragments(&mut self, _: &mut FnMut(&mut Fragment)) {}
}

impl fmt::Debug for TableColGroupFlow {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.fragment {
            Some(ref rb) => write!(f, "TableColGroupFlow: {:?}", rb),
            None => write!(f, "TableColGroupFlow"),
        }
    }
}
<commit_msg>Auto merge of #17960 - mrobinson:fix-warning-for-table-colgroup, r=emilio<commit_after>/* 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/. */

//! CSS table formatting contexts.

#![deny(unsafe_code)]

use app_units::Au;
use context::LayoutContext;
use display_list_builder::DisplayListBuildState;
use euclid::Point2D;
use flow::{BaseFlow, Flow, FlowClass, ForceNonfloatedFlag, OpaqueFlow};
use fragment::{Fragment, FragmentBorderBoxIterator, Overflow, SpecificFragmentInfo};
use layout_debug;
use std::cmp::max;
use std::fmt;
use style::logical_geometry::LogicalSize;
use style::properties::ComputedValues;
use style::values::computed::LengthOrPercentageOrAuto;

/// A table formatting context.
pub struct TableColGroupFlow {
    /// Data common to all flows.
    pub base: BaseFlow,

    /// The associated fragment.
    pub fragment: Option<Fragment>,

    /// The table column fragments
    pub cols: Vec<Fragment>,

    /// The specified inline-sizes of table columns. (We use `LengthOrPercentageOrAuto` here in
    /// lieu of `ColumnInlineSize` because column groups do not establish minimum or preferred
    /// inline sizes.)
    pub inline_sizes: Vec<LengthOrPercentageOrAuto>,
}

impl TableColGroupFlow {
    pub fn from_fragments(fragment: Fragment, fragments: Vec<Fragment>) -> TableColGroupFlow {
        let writing_mode = fragment.style().writing_mode;
        TableColGroupFlow {
            base: BaseFlow::new(Some(fragment.style()),
                                writing_mode,
                                ForceNonfloatedFlag::ForceNonfloated),
            fragment: Some(fragment),
            cols: fragments,
            inline_sizes: vec!(),
        }
    }
}

impl Flow for TableColGroupFlow {
    fn class(&self) -> FlowClass {
        FlowClass::TableColGroup
    }

    fn as_mut_table_colgroup(&mut self) -> &mut TableColGroupFlow {
        self
    }

    fn bubble_inline_sizes(&mut self) {
        let _scope = layout_debug_scope!("table_colgroup::bubble_inline_sizes {:x}",
                                            self.base.debug_id());

        for fragment in &self.cols {
            // Retrieve the specified value from the appropriate CSS property.
            let inline_size = fragment.style().content_inline_size();
            let span = match fragment.specific {
                SpecificFragmentInfo::TableColumn(col_fragment) => max(col_fragment.span, 1),
                _ => panic!("non-table-column fragment inside table column?!"),
            };
            for _ in 0..span {
                self.inline_sizes.push(inline_size)
            }
        }
    }

    /// Table column inline-sizes are assigned in the table flow and propagated to table row flows
    /// and/or rowgroup flows. Therefore, table colgroup flows do not need to assign inline-sizes.
    fn assign_inline_sizes(&mut self, _: &LayoutContext) {
    }

    /// Table columns do not have block-size.
    fn assign_block_size(&mut self, _: &LayoutContext) {
    }

    fn update_late_computed_inline_position_if_necessary(&mut self, _: Au) {}

    fn update_late_computed_block_position_if_necessary(&mut self, _: Au) {}

    // Table columns are invisible.
    fn build_display_list(&mut self, _: &mut DisplayListBuildState) { }

    fn collect_stacking_contexts(&mut self, state: &mut DisplayListBuildState) {
        self.base.stacking_context_id = state.current_stacking_context_id;
        self.base.scroll_root_id = Some(state.current_scroll_root_id);
    }

    fn repair_style(&mut self, _: &::ServoArc<ComputedValues>) {}

    fn compute_overflow(&self) -> Overflow {
        Overflow::new()
    }

    fn generated_containing_block_size(&self, _: OpaqueFlow) -> LogicalSize<Au> {
        panic!("Table column groups can't be containing blocks!")
    }

    fn iterate_through_fragment_border_boxes(&self,
                                             _: &mut FragmentBorderBoxIterator,
                                             _: i32,
                                             _: &Point2D<Au>) {}

    fn mutate_fragments(&mut self, _: &mut FnMut(&mut Fragment)) {}
}

impl fmt::Debug for TableColGroupFlow {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.fragment {
            Some(ref rb) => write!(f, "TableColGroupFlow: {:?}", rb),
            None => write!(f, "TableColGroupFlow"),
        }
    }
}
<|endoftext|>


<commit_before>extern crate forth;

use forth::{Error, Forth, Value};

#[test]
fn no_input_no_stack() {
    assert_eq!(Vec::<Value>::new(), Forth::new().stack());
}

#[test]
#[ignore]
fn numbers_just_get_pushed_onto_the_stack() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 3 4 5").is_ok());
    assert_eq!(vec![1, 2, 3, 4, 5], f.stack());
}

#[test]
#[ignore]
fn non_word_characters_are_separators() {
    let mut f = Forth::new();
    assert!(f.eval("1\u{0000}2\u{0001}3\n4\r5 6\t7").is_ok());
    assert_eq!(vec![1, 2, 3, 4, 5, 6, 7], f.stack());
}

#[test]
#[ignore]
fn can_add_two_numbers() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 +").is_ok());
    assert_eq!(vec![3], f.stack());
}

#[test]
#[ignore]
fn addition_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("+"));
}

#[test]
#[ignore]
fn can_subtract_two_numbers() {
    let mut f = Forth::new();
    assert!(f.eval("3 4 -").is_ok());
    assert_eq!(vec![-1], f.stack());
}

#[test]
#[ignore]
fn subtraction_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("-"));
}

#[test]
#[ignore]
fn can_multiply_two_numbers() {
    let mut f = Forth::new();
    assert!(f.eval("2 4 *").is_ok());
    assert_eq!(vec![8], f.stack());
}

#[test]
#[ignore]
fn multiplication_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("*"));
}

#[test]
#[ignore]
fn can_divide_two_numbers() {
    let mut f = Forth::new();
    assert!(f.eval("12 3 /").is_ok());
    assert_eq!(vec![4], f.stack());
}

#[test]
#[ignore]
fn performs_integer_division() {
    let mut f = Forth::new();
    assert!(f.eval("8 3 /").is_ok());
    assert_eq!(vec![2], f.stack());
}

#[test]
#[ignore]
fn division_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("/"));
}

#[test]
#[ignore]
fn errors_if_dividing_by_zero() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::DivisionByZero), f.eval("4 0 /"));
}

#[test]
#[ignore]
fn addition_and_subtraction() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 + 4 -").is_ok());
    assert_eq!(vec![-1], f.stack());
}

#[test]
#[ignore]
fn multiplication_and_division() {
    let mut f = Forth::new();
    assert!(f.eval("2 4 * 3 /").is_ok());
    assert_eq!(vec![2], f.stack());
}

#[test]
#[ignore]
fn dup() {
    let mut f = Forth::new();
    assert!(f.eval("1 DUP").is_ok());
    assert_eq!(vec![1, 1], f.stack());
}

#[test]
#[ignore]
fn dup_case_insensitive() {
    let mut f = Forth::new();
    assert!(f.eval("1 Dup").is_ok());
    assert_eq!(vec![1, 1], f.stack());
}

#[test]
#[ignore]
fn dup_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("dup"));
}

#[test]
#[ignore]
fn drop() {
    let mut f = Forth::new();
    assert!(f.eval("1 drop").is_ok());
    assert_eq!(Vec::<Value>::new(), f.stack());
}

#[test]
#[ignore]
fn drop_with_two() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 drop").is_ok());
    assert_eq!(vec![1], f.stack());
}

#[test]
#[ignore]
fn drop_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("drop"));
}

#[test]
#[ignore]
fn swap() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 swap").is_ok());
    assert_eq!(vec![2, 1], f.stack());
}

#[test]
#[ignore]
fn swap_with_three() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 3 swap").is_ok());
    assert_eq!(vec![1, 3, 2], f.stack());
}

#[test]
#[ignore]
fn swap_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("1 swap"));
    assert_eq!(Err(Error::StackUnderflow), f.eval("swap"));
}

#[test]
#[ignore]
fn over() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 over").is_ok());
    assert_eq!(vec![1, 2, 1], f.stack());
}

#[test]
#[ignore]
fn over_with_three() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 3 over").is_ok());
    assert_eq!(vec![1, 2, 3, 2], f.stack());
}

#[test]
#[ignore]
fn over_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("1 over"));
    assert_eq!(Err(Error::StackUnderflow), f.eval("over"));
}

#[test]
#[ignore]
fn defining_a_new_word() {
    let mut f = Forth::new();
    assert!(f.eval(": CoUnT 1 2 3 ;").is_ok());
    assert!(f.eval("count COUNT").is_ok());
    assert_eq!(vec![1, 2, 3, 1, 2, 3], f.stack());
}

#[test]
#[ignore]
fn redefining_an_existing_word() {
    let mut f = Forth::new();
    assert!(f.eval(": foo dup ;").is_ok());
    assert!(f.eval(": foo dup dup ;").is_ok());
    assert!(f.eval("1 foo").is_ok());
    assert_eq!(vec![1, 1, 1], f.stack());
}

#[test]
#[ignore]
fn redefining_an_existing_built_in_word() {
    let mut f = Forth::new();
    assert!(f.eval(": swap dup ;").is_ok());
    assert!(f.eval("1 swap").is_ok());
    assert_eq!(vec![1, 1], f.stack());
}

#[test]
#[ignore]
fn defining_words_with_odd_characters() {
    let mut f = Forth::new();
    assert!(f.eval(": € 220371 ; €").is_ok());
    assert_eq!(vec![220371], f.stack());
}

#[test]
#[ignore]
fn defining_a_number() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::InvalidWord), f.eval(": 1 2 ;"));
}

#[test]
#[ignore]
fn malformed_word_definition() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::InvalidWord), f.eval(":"));
    assert_eq!(Err(Error::InvalidWord), f.eval(": foo"));
    assert_eq!(Err(Error::InvalidWord), f.eval(": foo 1"));
}

#[test]
#[ignore]
fn calling_non_existing_word() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::UnknownWord), f.eval("1 foo"));
}
<commit_msg>forth: test binary ops with one operand<commit_after>extern crate forth;

use forth::{Error, Forth, Value};

#[test]
fn no_input_no_stack() {
    assert_eq!(Vec::<Value>::new(), Forth::new().stack());
}

#[test]
#[ignore]
fn numbers_just_get_pushed_onto_the_stack() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 3 4 5").is_ok());
    assert_eq!(vec![1, 2, 3, 4, 5], f.stack());
}

#[test]
#[ignore]
fn non_word_characters_are_separators() {
    let mut f = Forth::new();
    assert!(f.eval("1\u{0000}2\u{0001}3\n4\r5 6\t7").is_ok());
    assert_eq!(vec![1, 2, 3, 4, 5, 6, 7], f.stack());
}

#[test]
#[ignore]
fn can_add_two_numbers() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 +").is_ok());
    assert_eq!(vec![3], f.stack());
}

#[test]
#[ignore]
fn addition_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("1 +"));
    assert_eq!(Err(Error::StackUnderflow), f.eval("+"));
}

#[test]
#[ignore]
fn can_subtract_two_numbers() {
    let mut f = Forth::new();
    assert!(f.eval("3 4 -").is_ok());
    assert_eq!(vec![-1], f.stack());
}

#[test]
#[ignore]
fn subtraction_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("1 -"));
    assert_eq!(Err(Error::StackUnderflow), f.eval("-"));
}

#[test]
#[ignore]
fn can_multiply_two_numbers() {
    let mut f = Forth::new();
    assert!(f.eval("2 4 *").is_ok());
    assert_eq!(vec![8], f.stack());
}

#[test]
#[ignore]
fn multiplication_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("1 *"));
    assert_eq!(Err(Error::StackUnderflow), f.eval("*"));
}

#[test]
#[ignore]
fn can_divide_two_numbers() {
    let mut f = Forth::new();
    assert!(f.eval("12 3 /").is_ok());
    assert_eq!(vec![4], f.stack());
}

#[test]
#[ignore]
fn performs_integer_division() {
    let mut f = Forth::new();
    assert!(f.eval("8 3 /").is_ok());
    assert_eq!(vec![2], f.stack());
}

#[test]
#[ignore]
fn division_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("1 /"));
    assert_eq!(Err(Error::StackUnderflow), f.eval("/"));
}

#[test]
#[ignore]
fn errors_if_dividing_by_zero() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::DivisionByZero), f.eval("4 0 /"));
}

#[test]
#[ignore]
fn addition_and_subtraction() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 + 4 -").is_ok());
    assert_eq!(vec![-1], f.stack());
}

#[test]
#[ignore]
fn multiplication_and_division() {
    let mut f = Forth::new();
    assert!(f.eval("2 4 * 3 /").is_ok());
    assert_eq!(vec![2], f.stack());
}

#[test]
#[ignore]
fn dup() {
    let mut f = Forth::new();
    assert!(f.eval("1 DUP").is_ok());
    assert_eq!(vec![1, 1], f.stack());
}

#[test]
#[ignore]
fn dup_case_insensitive() {
    let mut f = Forth::new();
    assert!(f.eval("1 Dup").is_ok());
    assert_eq!(vec![1, 1], f.stack());
}

#[test]
#[ignore]
fn dup_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("dup"));
}

#[test]
#[ignore]
fn drop() {
    let mut f = Forth::new();
    assert!(f.eval("1 drop").is_ok());
    assert_eq!(Vec::<Value>::new(), f.stack());
}

#[test]
#[ignore]
fn drop_with_two() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 drop").is_ok());
    assert_eq!(vec![1], f.stack());
}

#[test]
#[ignore]
fn drop_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("drop"));
}

#[test]
#[ignore]
fn swap() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 swap").is_ok());
    assert_eq!(vec![2, 1], f.stack());
}

#[test]
#[ignore]
fn swap_with_three() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 3 swap").is_ok());
    assert_eq!(vec![1, 3, 2], f.stack());
}

#[test]
#[ignore]
fn swap_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("1 swap"));
    assert_eq!(Err(Error::StackUnderflow), f.eval("swap"));
}

#[test]
#[ignore]
fn over() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 over").is_ok());
    assert_eq!(vec![1, 2, 1], f.stack());
}

#[test]
#[ignore]
fn over_with_three() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 3 over").is_ok());
    assert_eq!(vec![1, 2, 3, 2], f.stack());
}

#[test]
#[ignore]
fn over_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("1 over"));
    assert_eq!(Err(Error::StackUnderflow), f.eval("over"));
}

#[test]
#[ignore]
fn defining_a_new_word() {
    let mut f = Forth::new();
    assert!(f.eval(": CoUnT 1 2 3 ;").is_ok());
    assert!(f.eval("count COUNT").is_ok());
    assert_eq!(vec![1, 2, 3, 1, 2, 3], f.stack());
}

#[test]
#[ignore]
fn redefining_an_existing_word() {
    let mut f = Forth::new();
    assert!(f.eval(": foo dup ;").is_ok());
    assert!(f.eval(": foo dup dup ;").is_ok());
    assert!(f.eval("1 foo").is_ok());
    assert_eq!(vec![1, 1, 1], f.stack());
}

#[test]
#[ignore]
fn redefining_an_existing_built_in_word() {
    let mut f = Forth::new();
    assert!(f.eval(": swap dup ;").is_ok());
    assert!(f.eval("1 swap").is_ok());
    assert_eq!(vec![1, 1], f.stack());
}

#[test]
#[ignore]
fn defining_words_with_odd_characters() {
    let mut f = Forth::new();
    assert!(f.eval(": € 220371 ; €").is_ok());
    assert_eq!(vec![220371], f.stack());
}

#[test]
#[ignore]
fn defining_a_number() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::InvalidWord), f.eval(": 1 2 ;"));
}

#[test]
#[ignore]
fn malformed_word_definition() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::InvalidWord), f.eval(":"));
    assert_eq!(Err(Error::InvalidWord), f.eval(": foo"));
    assert_eq!(Err(Error::InvalidWord), f.eval(": foo 1"));
}

#[test]
#[ignore]
fn calling_non_existing_word() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::UnknownWord), f.eval("1 foo"));
}
<|endoftext|>


<commit_before>//! An example that determines the time stamp counter frequency (RDTSC, RDTSCP) .
extern crate raw_cpuid;

use std::time;

const MHZ_TO_HZ: u64 = 1000000;
const KHZ_TO_HZ: u64 = 1000;

#[rustversion::nightly]
use core::arch::x86_64::_rdtsc as rdtsc;

#[rustversion::stable]
unsafe fn rdtsc() -> u64 {
    0
}

fn main() {
    let cpuid = raw_cpuid::CpuId::new();
    let has_tsc = cpuid
        .get_feature_info()
        .map_or(false, |finfo| finfo.has_tsc());

    let has_invariant_tsc = cpuid
        .get_extended_function_info()
        .map_or(false, |efinfo| efinfo.has_invariant_tsc());

    let tsc_frequency_hz = cpuid.get_tsc_info().map(|tinfo| {
        if tinfo.nominal_frequency() != 0 {
            Some(tinfo.tsc_frequency())
        } else if tinfo.numerator() != 0 && tinfo.denominator() != 0 {
            // Skylake and Kabylake don't report the crystal clock, approximate with base frequency:
            cpuid
                .get_processor_frequency_info()
                .map(|pinfo| pinfo.processor_base_frequency() as u64 * MHZ_TO_HZ)
                .map(|cpu_base_freq_hz| {
                    let crystal_hz =
                        cpu_base_freq_hz * tinfo.denominator() as u64 / tinfo.numerator() as u64;
                    crystal_hz * tinfo.numerator() as u64 / tinfo.denominator() as u64
                })
        } else {
            None
        }
    });

    if has_tsc {
        // Try to figure out TSC frequency with CPUID
        println!(
            "TSC Frequency is: {} ({})",
            match tsc_frequency_hz {
                Some(x) => format!("{} Hz", x.unwrap_or(0)),
                None => String::from("unknown"),
            },
            if has_invariant_tsc {
                "invariant"
            } else {
                "TSC frequency varies with speed-stepping"
            }
        );

        // Check if we run in a VM and the hypervisor can give us the TSC frequency
        cpuid.get_hypervisor_info().map(|hv| {
            hv.tsc_frequency().map(|tsc_khz| {
                let virtual_tsc_frequency_hz = tsc_khz as u64 * KHZ_TO_HZ;
                println!(
                    "Hypervisor reports TSC Frequency at: {} Hz",
                    virtual_tsc_frequency_hz
                );
            })
        });

        // Determine TSC frequency by measuring it (loop for a second, record ticks)
        let one_second = time::Duration::from_secs(1);
        let now = time::Instant::now();
        let start = unsafe { rdtsc() };
        loop {
            if now.elapsed() >= one_second {
                break;
            }
        }
        let end = unsafe { rdtsc() };
        if end > 0 {
            println!(
                "Empirical measurement of TSC frequency was: {} Hz",
                (end - start)
            );
        } else {
            // Don't have rdtsc on stable!
        }
    } else {
        println!("System does not have a TSC.")
    }
}
<commit_msg>Refactor code example.<commit_after>//! An example that determines the time stamp counter frequency (RDTSC, RDTSCP) .
extern crate raw_cpuid;

use std::time;

const MHZ_TO_HZ: u64 = 1000000;
const KHZ_TO_HZ: u64 = 1000;

#[rustversion::nightly]
use core::arch::x86_64::_rdtsc as rdtsc;

#[rustversion::stable]
unsafe fn rdtsc() -> u64 {
    0
}

fn main() {
    let cpuid = raw_cpuid::CpuId::new();
    let has_tsc = cpuid
        .get_feature_info()
        .map_or(false, |finfo| finfo.has_tsc());

    let has_invariant_tsc = cpuid
        .get_extended_function_info()
        .map_or(false, |efinfo| efinfo.has_invariant_tsc());

    let tsc_frequency_hz = cpuid.get_tsc_info().map(|tinfo| {
        if tinfo.nominal_frequency() != 0 {
            Some(tinfo.tsc_frequency())
        } else if tinfo.numerator() != 0 && tinfo.denominator() != 0 {
            // Skylake and Kabylake don't report the crystal clock, approximate with base frequency:
            cpuid
                .get_processor_frequency_info()
                .map(|pinfo| pinfo.processor_base_frequency() as u64 * MHZ_TO_HZ)
                .map(|cpu_base_freq_hz| {
                    let crystal_hz =
                        cpu_base_freq_hz * tinfo.denominator() as u64 / tinfo.numerator() as u64;
                    crystal_hz * tinfo.numerator() as u64 / tinfo.denominator() as u64
                })
        } else {
            None
        }
    });

    if has_tsc {
        // Try to figure out TSC frequency with CPUID
        println!(
            "TSC Frequency is: {} ({})",
            match tsc_frequency_hz {
                Some(x) => format!("{} Hz", x.unwrap_or(0)),
                None => String::from("unknown"),
            },
            if has_invariant_tsc {
                "invariant"
            } else {
                "TSC frequency varies with speed-stepping"
            }
        );

        // Check if we run in a VM and the hypervisor can give us the TSC frequency
        cpuid.get_hypervisor_info().map(|hv| {
            hv.tsc_frequency().map(|tsc_khz| {
                let virtual_tsc_frequency_hz = tsc_khz as u64 * KHZ_TO_HZ;
                println!(
                    "Hypervisor reports TSC Frequency at: {} Hz",
                    virtual_tsc_frequency_hz
                );
            })
        });

        // Determine TSC frequency by measuring it (loop for a second, record ticks)
        let one_second = time::Duration::from_secs(1);
        let now = time::Instant::now();
        let start = unsafe { rdtsc() };
        if start > 0 {
            loop {
                if now.elapsed() >= one_second {
                    break;
                }
            }
            let end = unsafe { rdtsc() };
            println!(
                "Empirical measurement of TSC frequency was: {} Hz",
                (end - start)
            );
        } else {
            // Don't have rdtsc on stable!
        }
    } else {
        println!("System does not have a TSC.")
    }
}
<|endoftext|>