<commit_before>use std::fmt::{Debug, Formatter};
use std::fmt;
use std::ops::Deref;
use std::process::exit;

use clap::ArgMatches;

use runtime::Runtime;
use module::Module;

use storage::parser::FileHeaderParser;
use storage::parser::Parser;
use storage::json::parser::JsonHeaderParser;
use module::helpers::cli::get_file_filter_by_cli;
use module::helpers::cli::CliFileFilter;

mod header;

use self::header::get_url_from_header;
use self::header::get_tags_from_header;

pub struct BM<'a> {
    rt: &'a Runtime<'a>,
}

impl<'a> BM<'a> {

    pub fn new(rt: &'a Runtime<'a>) -> BM<'a> {
        BM {
            rt: rt,
        }
    }

    /**
     * Subcommand: add
     */
    fn command_add(&self, matches: &ArgMatches) -> bool {
        use std::process::exit;
        use self::header::build_header;

        let parser = Parser::new(JsonHeaderParser::new(None));

        let url    = matches.value_of("url").map(String::from).unwrap(); // clap ensures this is present

        if !self.validate_url(&url, &parser) {
            error!("URL validation failed, exiting.");
            exit(1);
        } else {
            debug!("Verification succeeded");
        }

        let tags   = matches.value_of("tags").and_then(|s| {
            Some(s.split(",").map(String::from).collect())
        }).unwrap_or(vec![]);

        debug!("Building header with");
        debug!("    url  = '{:?}'", url);
        debug!("    tags = '{:?}'", tags);
        let header = build_header(url, tags);

        let fileid = self.rt.store().new_file_with_header(self, header);
        self.rt.store().load(self, &parser, &fileid).and_then(|file| {
            info!("Created file in memory: {}", fileid);
            Some(self.rt.store().persist(&parser, file))
        }).unwrap_or(false)
    }

    fn validate_url<HP>(&self, url: &String, parser: &Parser<HP>) -> bool
        where HP: FileHeaderParser
    {
        use util::is_url;

        if !is_url(url) {
            error!("Url '{}' is not a valid URL. Will not store.", url);
            return false;
        }

        let is_in_store = self.rt
            .store()
            .load_for_module(self, parser)
            .iter()
            .any(|file| {
                let f = file.deref().borrow();
                get_url_from_header(f.header()).map(|url_in_store| {
                    &url_in_store == url
                }).unwrap_or(false)
            });

        if is_in_store {
            error!("URL '{}' seems to be in the store already", url);
            return false;
        }

        return true;
    }

    /**
     * Subcommand: list
     */
    fn command_list(&self, matches: &ArgMatches) -> bool {
        use ui::file::{FilePrinter, TablePrinter};
        use std::ops::Deref;

        let parser = Parser::new(JsonHeaderParser::new(None));
        let filter : Box<CliFileFilter> = get_file_filter_by_cli(&parser, matches, "id", "match", "tags", Some("URL"));

        let files  = self.rt
            .store()
            .load_for_module(self, &parser)
            .into_iter()
            .filter(|file| filter.filter_file(file));
        let printer = TablePrinter::new(self.rt.is_verbose(), self.rt.is_debugging());

        printer.print_files_custom(files,
            &|file| {
                let fl = file.deref().borrow();
                let hdr = fl.header();
                let url = get_url_from_header(hdr).unwrap_or(String::from("Parser error"));
                let tags = get_tags_from_header(hdr);

                debug!("Custom printer field: url  = '{:?}'", url);
                debug!("Custom printer field: tags = '{:?}'", tags);

                vec![url, tags.join(", ")]
            }
        );
        true
    }

    /**
     * Subcommand: open
     */
    fn command_open(&self, matches: &ArgMatches) -> bool {
        use open;

        let parser = Parser::new(JsonHeaderParser::new(None));
        let filter : Box<CliFileFilter> = get_file_filter_by_cli(&parser, matches, "id", "match", "tags", None);
        let result = self.rt
            .store()
            .load_for_module(self, &parser)
            .iter()
            .filter(|file| filter.filter_file(file))
            .map(|file| {
                debug!("File loaded, can open now: {:?}", file);
                let f = file.deref().borrow();
                get_url_from_header(f.header()).map(|url| {
                    if open::that(&url[..]).is_ok() {
                        info!("open({})", url);
                        true
                    } else {
                        info!("could not open({})", url);
                        false
                    }
                })
                .unwrap_or(false)
            })
            .fold((0, 0), |acc, succeeded| {
                let (worked, failed) = acc;
                if succeeded {
                    (worked + 1, failed)
                } else {
                    (worked, failed + 1)
                }
            });

        let (succ, fail) = result;
        info!("open() succeeded for {} files", succ);
        info!("open() failed    for {} files", fail);
        return fail == 0;
    }

    /**
     * Subcommand: remove
     */
    fn command_remove(&self, matches: &ArgMatches) -> bool {
        let parser = Parser::new(JsonHeaderParser::new(None));
        let filter : Box<CliFileFilter> = get_file_filter_by_cli(&parser, matches, "id", "match", "tags", None);
        let result = self.rt
            .store()
            .load_for_module(self, &parser)
            .iter()
            .filter(|file| filter.filter_file(file))
            .map(|file| {
                debug!("File loaded, can remove now: {:?}", file);
                let f = file.deref().borrow();
                self.rt.store().remove(f.id().clone())
            })
            .all(|x| x);

        if result {
            info!("Removing succeeded");
        } else {
            info!("Removing failed");
        }

        return result;
    }

    /**
     * Subcommand: add_tags
     */
    fn command_add_tags(&self, matches: &ArgMatches) -> bool {
        self.alter_tags_in_files(matches, |old_tags, cli_tags| {
            let mut new_tags = old_tags.clone();
            new_tags.append(&mut cli_tags.clone());
            new_tags
        })
    }

    /**
     * Subcommand: rm_tags
     */
    fn command_rm_tags(&self, matches: &ArgMatches) -> bool {
        self.alter_tags_in_files(matches, |old_tags, cli_tags| {
            old_tags.clone()
                .into_iter()
                .filter(|tag| !cli_tags.contains(tag))
                .collect()
        })
    }

    /**
     * Subcommand: set_tags
     */
    fn command_set_tags(&self, matches: &ArgMatches) -> bool {
        self.alter_tags_in_files(matches, |old_tags, cli_tags| {
            cli_tags.clone()
        })
    }

    /**
     * Helper function to alter the tags in a file
     */
    fn alter_tags_in_files<F>(&self, matches: &ArgMatches, generate_new_tags: F) -> bool
        where F: Fn(Vec<String>, &Vec<String>) -> Vec<String>
    {
        use self::header::rebuild_header_with_tags;

        let cli_tags = matches.value_of("tags")
                          .map(|ts| {
                            ts.split(",")
                              .map(String::from)
                              .collect::<Vec<String>>()
                          })
                          .unwrap_or(vec![]);

        let parser = Parser::new(JsonHeaderParser::new(None));
        let filter : Box<CliFileFilter> = get_file_filter_by_cli(&parser,
                                                                 matches,
                                                                 "with_id",
                                                                 "with_match",
                                                                 "with_tags",
                                                                 None);
        self.rt
            .store()
            .load_for_module(self, &parser)
            .into_iter()
            .filter(|file| filter.filter_file(file))
            .map(|file| {
                debug!("Remove tags from file: {:?}", file);

                let hdr = {
                    let f = file.deref().borrow();
                    f.header().clone()
                };

                debug!("Tags:...");
                let old_tags = get_tags_from_header(&hdr);
                debug!("    old_tags = {:?}", &old_tags);
                debug!("    cli_tags = {:?}", &cli_tags);

                let new_tags = generate_new_tags(old_tags, &cli_tags);
                debug!("    new_tags = {:?}", &new_tags);

                let new_header = rebuild_header_with_tags(&hdr, new_tags)
                    .unwrap_or_else(|| {
                        error!("Could not rebuild header for file");
                        exit(1);
                    });
                {
                    let mut f_mut = file.deref().borrow_mut();
                    f_mut.set_header(new_header);
                }

                self.rt.store().persist(&parser, file);
                true
            })
            .all(|x| x)
    }

}

/**
 * Trait implementation for BM module
 */
impl<'a> Module<'a> for BM<'a> {

    fn exec(&self, matches: &ArgMatches) -> bool {
        match matches.subcommand_name() {
            Some("add") => {
                self.command_add(matches.subcommand_matches("add").unwrap())
            },

            Some("list") => {
                self.command_list(matches.subcommand_matches("list").unwrap())
            },

            Some("open") => {
                self.command_open(matches.subcommand_matches("open").unwrap())
            },

            Some("remove") => {
                self.command_remove(matches.subcommand_matches("remove").unwrap())
            },

            Some("add_tags") => {
                self.command_add_tags(matches.subcommand_matches("add_tags").unwrap())
            },

            Some("rm_tags") => {
                self.command_rm_tags(matches.subcommand_matches("rm_tags").unwrap())
            },

            Some("set_tags") => {
                self.command_set_tags(matches.subcommand_matches("set_tags").unwrap())
            },

            Some(_) | None => {
                info!("No command given, doing nothing");
                false
            },
        }
    }

    fn name(&self) -> &'static str {
        "bookmark"
    }
}

impl<'a> Debug for BM<'a> {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        try!(write!(fmt, "BM"));
        Ok(())
    }

}

<commit_msg>Auto merge of #45 - matthiasbeyer:bm-command-output, r=matthiasbeyer<commit_after>use std::fmt::{Debug, Formatter};
use std::fmt;
use std::ops::Deref;
use std::process::exit;

use clap::ArgMatches;

use runtime::Runtime;
use module::Module;

use storage::parser::FileHeaderParser;
use storage::parser::Parser;
use storage::json::parser::JsonHeaderParser;
use module::helpers::cli::get_file_filter_by_cli;
use module::helpers::cli::CliFileFilter;

mod header;

use self::header::get_url_from_header;
use self::header::get_tags_from_header;

pub struct BM<'a> {
    rt: &'a Runtime<'a>,
}

impl<'a> BM<'a> {

    pub fn new(rt: &'a Runtime<'a>) -> BM<'a> {
        BM {
            rt: rt,
        }
    }

    /**
     * Subcommand: add
     */
    fn command_add(&self, matches: &ArgMatches) -> bool {
        use std::process::exit;
        use self::header::build_header;

        let parser = Parser::new(JsonHeaderParser::new(None));

        let url    = matches.value_of("url").map(String::from).unwrap(); // clap ensures this is present

        if !self.validate_url(&url, &parser) {
            error!("URL validation failed, exiting.");
            exit(1);
        } else {
            debug!("Verification succeeded");
        }

        let tags   = matches.value_of("tags").and_then(|s| {
            Some(s.split(",").map(String::from).collect())
        }).unwrap_or(vec![]);

        debug!("Building header with");
        debug!("    url  = '{:?}'", url);
        debug!("    tags = '{:?}'", tags);
        let header = build_header(url, tags);

        let fileid = self.rt
                         .store()
                         .new_file_with_header(self, header);

        let result = self.rt
            .store()
            .load(self, &parser, &fileid)
            .map(|file| {
                info!("Created file in memory: {}", fileid);
                self.rt
                    .store()
                    .persist(&parser, file)
            })
            .unwrap_or(false);

        if result {
            info!("Adding worked");
        } else {
            info!("Adding failed");
        }

        result
    }

    fn validate_url<HP>(&self, url: &String, parser: &Parser<HP>) -> bool
        where HP: FileHeaderParser
    {
        use util::is_url;

        if !is_url(url) {
            error!("Url '{}' is not a valid URL. Will not store.", url);
            return false;
        }

        let is_in_store = self.rt
            .store()
            .load_for_module(self, parser)
            .iter()
            .any(|file| {
                let f = file.deref().borrow();
                get_url_from_header(f.header()).map(|url_in_store| {
                    &url_in_store == url
                }).unwrap_or(false)
            });

        if is_in_store {
            error!("URL '{}' seems to be in the store already", url);
            return false;
        }

        return true;
    }

    /**
     * Subcommand: list
     */
    fn command_list(&self, matches: &ArgMatches) -> bool {
        use ui::file::{FilePrinter, TablePrinter};
        use std::ops::Deref;

        let parser = Parser::new(JsonHeaderParser::new(None));
        let filter : Box<CliFileFilter> = get_file_filter_by_cli(&parser, matches, "id", "match", "tags", Some("URL"));

        let files  = self.rt
            .store()
            .load_for_module(self, &parser)
            .into_iter()
            .filter(|file| filter.filter_file(file));
        let printer = TablePrinter::new(self.rt.is_verbose(), self.rt.is_debugging());

        printer.print_files_custom(files,
            &|file| {
                let fl = file.deref().borrow();
                let hdr = fl.header();
                let url = get_url_from_header(hdr).unwrap_or(String::from("Parser error"));
                let tags = get_tags_from_header(hdr);

                debug!("Custom printer field: url  = '{:?}'", url);
                debug!("Custom printer field: tags = '{:?}'", tags);

                vec![url, tags.join(", ")]
            }
        );
        true
    }

    /**
     * Subcommand: open
     */
    fn command_open(&self, matches: &ArgMatches) -> bool {
        use open;

        let parser = Parser::new(JsonHeaderParser::new(None));
        let filter : Box<CliFileFilter> = get_file_filter_by_cli(&parser, matches, "id", "match", "tags", None);
        let result = self.rt
            .store()
            .load_for_module(self, &parser)
            .iter()
            .filter(|file| filter.filter_file(file))
            .map(|file| {
                debug!("File loaded, can open now: {:?}", file);
                let f = file.deref().borrow();
                get_url_from_header(f.header()).map(|url| {
                    if open::that(&url[..]).is_ok() {
                        info!("open({})", url);
                        true
                    } else {
                        info!("could not open({})", url);
                        false
                    }
                })
                .unwrap_or(false)
            })
            .fold((0, 0), |acc, succeeded| {
                let (worked, failed) = acc;
                if succeeded {
                    (worked + 1, failed)
                } else {
                    (worked, failed + 1)
                }
            });

        let (succ, fail) = result;
        info!("open() succeeded for {} files", succ);
        info!("open() failed    for {} files", fail);
        return fail == 0;
    }

    /**
     * Subcommand: remove
     */
    fn command_remove(&self, matches: &ArgMatches) -> bool {
        let parser = Parser::new(JsonHeaderParser::new(None));
        let filter : Box<CliFileFilter> = get_file_filter_by_cli(&parser, matches, "id", "match", "tags", None);
        let result = self.rt
            .store()
            .load_for_module(self, &parser)
            .iter()
            .filter(|file| filter.filter_file(file))
            .map(|file| {
                debug!("File loaded, can remove now: {:?}", file);
                let f = file.deref().borrow();
                self.rt.store().remove(f.id().clone())
            })
            .fold((0, 0), |acc, succeeded| {
                let (worked, failed) = acc;
                if succeeded {
                    (worked + 1, failed)
                } else {
                    (worked, failed + 1)
                }
            });

        let (worked, failed) = result;

        info!("Removing succeeded for {} files", worked);
        info!("Removing failed for {} files", failed);

        return failed == 0;
    }

    /**
     * Subcommand: add_tags
     */
    fn command_add_tags(&self, matches: &ArgMatches) -> bool {
        self.alter_tags_in_files(matches, |old_tags, cli_tags| {
            let mut new_tags = old_tags.clone();
            new_tags.append(&mut cli_tags.clone());
            new_tags
        })
    }

    /**
     * Subcommand: rm_tags
     */
    fn command_rm_tags(&self, matches: &ArgMatches) -> bool {
        self.alter_tags_in_files(matches, |old_tags, cli_tags| {
            old_tags.clone()
                .into_iter()
                .filter(|tag| !cli_tags.contains(tag))
                .collect()
        })
    }

    /**
     * Subcommand: set_tags
     */
    fn command_set_tags(&self, matches: &ArgMatches) -> bool {
        self.alter_tags_in_files(matches, |old_tags, cli_tags| {
            cli_tags.clone()
        })
    }

    /**
     * Helper function to alter the tags in a file
     */
    fn alter_tags_in_files<F>(&self, matches: &ArgMatches, generate_new_tags: F) -> bool
        where F: Fn(Vec<String>, &Vec<String>) -> Vec<String>
    {
        use self::header::rebuild_header_with_tags;

        let cli_tags = matches.value_of("tags")
                          .map(|ts| {
                            ts.split(",")
                              .map(String::from)
                              .collect::<Vec<String>>()
                          })
                          .unwrap_or(vec![]);

        let parser = Parser::new(JsonHeaderParser::new(None));
        let filter : Box<CliFileFilter> = get_file_filter_by_cli(&parser,
                                                                 matches,
                                                                 "with_id",
                                                                 "with_match",
                                                                 "with_tags",
                                                                 None);
        self.rt
            .store()
            .load_for_module(self, &parser)
            .into_iter()
            .filter(|file| filter.filter_file(file))
            .map(|file| {
                debug!("Remove tags from file: {:?}", file);

                let hdr = {
                    let f = file.deref().borrow();
                    f.header().clone()
                };

                debug!("Tags:...");
                let old_tags = get_tags_from_header(&hdr);
                debug!("    old_tags = {:?}", &old_tags);
                debug!("    cli_tags = {:?}", &cli_tags);

                let new_tags = generate_new_tags(old_tags, &cli_tags);
                debug!("    new_tags = {:?}", &new_tags);

                let new_header = rebuild_header_with_tags(&hdr, new_tags)
                    .unwrap_or_else(|| {
                        error!("Could not rebuild header for file");
                        exit(1);
                    });
                {
                    let mut f_mut = file.deref().borrow_mut();
                    f_mut.set_header(new_header);
                }

                self.rt.store().persist(&parser, file);
                true
            })
            .all(|x| x)
    }

}

/**
 * Trait implementation for BM module
 */
impl<'a> Module<'a> for BM<'a> {

    fn exec(&self, matches: &ArgMatches) -> bool {
        match matches.subcommand_name() {
            Some("add") => {
                self.command_add(matches.subcommand_matches("add").unwrap())
            },

            Some("list") => {
                self.command_list(matches.subcommand_matches("list").unwrap())
            },

            Some("open") => {
                self.command_open(matches.subcommand_matches("open").unwrap())
            },

            Some("remove") => {
                self.command_remove(matches.subcommand_matches("remove").unwrap())
            },

            Some("add_tags") => {
                self.command_add_tags(matches.subcommand_matches("add_tags").unwrap())
            },

            Some("rm_tags") => {
                self.command_rm_tags(matches.subcommand_matches("rm_tags").unwrap())
            },

            Some("set_tags") => {
                self.command_set_tags(matches.subcommand_matches("set_tags").unwrap())
            },

            Some(_) | None => {
                info!("No command given, doing nothing");
                false
            },
        }
    }

    fn name(&self) -> &'static str {
        "bookmark"
    }
}

impl<'a> Debug for BM<'a> {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        try!(write!(fmt, "BM"));
        Ok(())
    }

}

<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Eliza Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015-2016 Eliza Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Macros to make our custom address types require a lot less repetitive code.
#[macro_export]
macro_rules! Addr {
    (($size:ty) $(pub)* enum $name:ident $($tail:tt)*) => {
        Addr! { @impl $name, $size }
    };
    (($size:ty) $(pub)* struct $name:ident $($tail:tt)*) => {
        Addr! { @impl $name, $size }
    };
    (@impl $ty:ident, $size:ty) => {

        impl ::core::fmt::Debug for $ty {
            fn fmt(&self, f: &mut ::core::fmt::Formatter)
                  -> ::core::fmt::Result {
                write!(f, "{}({:#x})", stringify!($ty), self.0)
            }
        }

        impl ::core::convert::Into<$size> for $ty {
            #[inline] fn into(self) -> $size { self.0 }
        }

        impl ::core::convert::From<$size> for $ty {
            #[inline] fn from(n: $size) -> Self { $ty(n) }
        }

        impl<T> ::core::convert::From<*mut T> for $ty {
            #[inline] fn from(ptr: *mut T) -> Self { $ty(ptr as $size) }
        }

        impl<T> ::core::convert::From<*const T> for $ty {
            #[inline] fn from(ptr: *const T) -> Self { $ty(ptr as $size) }
        }

        impl ::core::ops::Deref for $ty {
            type Target = $size;
            #[inline] fn deref(&self) -> &Self::Target { &self.0 }
        }

        impl $ty {
            #[inline(always)]
            pub const fn as_mut_ptr<T>(&self) -> *mut T { self.0 as *mut _ }

            #[inline(always)]
            pub const fn as_ptr<T>(&self) -> *const T { self.0 as *const _ }

            /// Returns true if this address is aligned on a page boundary.
            #[inline]
            pub fn is_page_aligned(&self) -> bool {
                use $crate::arch::PAGE_SIZE;
                **self % PAGE_SIZE as <Self as Addr>::Repr == 0 as <Self as Addr>::Repr
            }
        }

        impl_ops! {
            Add, add, + for $ty, $size
            Sub, sub, - for $ty, $size
            Div, div, / for $ty, $size
            Mul, mul, * for $ty, $size
            Shl, shl, >> for $ty, $size
            Shr, shr, << for $ty, $size
            Rem, rem, % for $ty, $size
            BitAnd, bitand, & for $ty, $size
            BitOr, bitor, | for $ty, $size
            BitXor, bitxor, ^ for $ty, $size

        }

        impl_assign_ops! {
            AddAssign, add_assign, += for $ty, $size
            SubAssign, sub_assign, -= for $ty, $size
            DivAssign, div_assign, /= for $ty, $size
            MulAssign, mul_assign, *= for $ty, $size
            ShlAssign, shl_assign, >>= for $ty, $size
            ShrAssign, shr_assign, <<= for $ty, $size
            RemAssign, rem_assign, %= for $ty, $size
            BitAndAssign, bitand_assign, &= for $ty, $size
            BitOrAssign, bitor_assign, |= for $ty, $size
            BitXorAssign, bitxor_assign, ^= for $ty, $size
        }

        impl_fmt! {
            Binary for $ty
            Display for $ty
            Octal for $ty
            LowerHex for $ty
            UpperHex for $ty
        }

        impl Addr for $ty {
            type Repr = $size;
        }

    }
}

macro_rules! impl_page_ops {
    ($($name:ident, $fun:ident, $op:tt for $ty:ident, $size:ty)*) => {$(
        impl ::core::ops::$name<$ty> for $ty {
            type Output = Self;

            #[inline] fn $fun(self, rhs: $ty) -> Self {
                expr!( $ty { number: expr!(self.number $op rhs.number) })
            }
        }
        impl ::core::ops::$name<$size> for $ty {
            type Output = $ty;

            #[inline] fn $fun(self, rhs: $size) -> Self {
                expr!($ty{ number: expr!(self.number $op rhs) })
            }
        }

        forward_ref_binop! {
            $name, $fun for $ty, $ty
        }
        forward_ref_binop! {
            $name, $fun for $ty, $size
        }
    )*}
}

macro_rules! impl_page_assign_ops {
    ($($name:ident, $fun:ident, $op:tt for $ty:ident, $size:ty)*) => {$(
        impl ::core::ops::$name<$ty> for $ty {

            #[inline] fn $fun(&mut self, rhs: $ty) {
                expr!(self.number $op rhs.number);
            }
        }
        impl ::core::ops::$name<$size> for $ty {

            #[inline] fn $fun(&mut self, rhs: $size) {
                expr!(self.number $op rhs);
            }
        }
    )*}
}

macro_rules! Page {
    (($addr:ty) $(pub)* struct $name:ident $($tail:tt)*) => {
        Page! { @impl $name, $addr }
    };
    (@impl $ty:ident, $addr:ty) => {

        impl Page for $ty {
            type Address = $addr;

            /// Create a new `Page` containing the given address.
            //  TODO: rewrite this as `up`/`down` using the page shift, instead.
            fn containing(addr: $addr) -> Self {
                use ::PAGE_SHIFT;
                assert!( *addr < 0x0000_8000_0000_0000 ||
                         *addr >= 0xffff_8000_0000_0000
                       , "invalid address: 0x{:x}", addr );
                $ty { number: *addr >> PAGE_SHIFT as <Self::Address as Addr>::Repr }
            }

            /// Return the start virtual address of this page
            #[inline]
            fn base(&self) -> $addr {
                Self::Address::from(
                    self.number << PAGE_SHIFT as <Self::Address as Addr>::Repr )
            }

            #[inline] fn number(&self) -> usize {
                self.number as usize
            }
        }

        // impl ::core::num::One for $ty {
        //     #[inline] fn one() -> Self {
        //         $ty { number:
        //             <<$addr as Addr>::Repr as ::core::num::One>::one()
        //         }
        //     }
        // }

        impl ::core::iter::Step for $ty {
            #[inline]
            fn step(&self, by: &Self) -> Option<Self> {
                self.number
                    .step(&by.number)
                    .map(|s| $ty { number: s})
            }

            #[inline]
            #[allow(trivial_numeric_casts)]
            fn steps_between(start: &$ty, end: &$ty, by: &$ty)
                            -> Option<usize> {
                use ::core::iter::Step;
                <<$addr as Addr>::Repr as Step>::steps_between( &start.number
                                                              , &end.number
                                                              , &by.number
                                                              )
            }

            #[inline]
            fn steps_between_by_one(start: &$ty, end: &$ty) -> Option<usize> {
                use ::core::iter::Step;
                <<$addr as Addr>::Repr as Step>::steps_between( &start.number
                                                              , &end.number
                                                              , &1
                                                              )
            }

            #[inline] fn sub_one(&self) -> Self { self - 1 }

            #[inline] fn add_one(&self) -> Self { self + 1 }

            #[inline] fn replace_one(&mut self) -> Self { unimplemented!() }

            #[inline] fn replace_zero(&mut self) -> Self { unimplemented!() }

            #[inline] fn is_negative(&self) -> bool { unimplemented!(); }

        }

        impl_page_ops! {
            Add, add, + for $ty, <<$ty as Page>::Address as Addr>::Repr
            Sub, sub, - for $ty, <<$ty as Page>::Address as Addr>::Repr
            Div, div, / for $ty, <<$ty as Page>::Address as Addr>::Repr
            Mul, mul, * for $ty, <<$ty as Page>::Address as Addr>::Repr
            Shl, shl, >> for $ty,<<$ty as Page>::Address as Addr>::Repr
            Shr, shr, << for $ty, <<$ty as Page>::Address as Addr>::Repr
            Rem, rem, % for $ty, <<$ty as Page>::Address as Addr>::Repr
            BitAnd, bitand, & for $ty, <<$ty as Page>::Address as Addr>::Repr
            BitOr, bitor, | for $ty, <<$ty as Page>::Address as Addr>::Repr
            BitXor, bitxor, ^ for $ty, <<$ty as Page>::Address as Addr>::Repr

        }

        impl_page_assign_ops! {
            AddAssign, add_assign, += for $ty, <<$ty as Page>::Address as Addr>::Repr
            SubAssign, sub_assign, -= for $ty, <<$ty as Page>::Address as Addr>::Repr
            DivAssign, div_assign, /= for $ty, <<$ty as Page>::Address as Addr>::Repr
            MulAssign, mul_assign, *= for $ty, <<$ty as Page>::Address as Addr>::Repr
            ShlAssign, shl_assign, >>= for $ty, <<$ty as Page>::Address as Addr>::Repr
            ShrAssign, shr_assign, <<= for $ty, <<$ty as Page>::Address as Addr>::Repr
            RemAssign, rem_assign, %= for $ty, <<$ty as Page>::Address as Addr>::Repr
            BitAndAssign, bitand_assign, &= for $ty, <<$ty as Page>::Address as Addr>::Repr
            BitOrAssign, bitor_assign, |= for $ty, <<$ty as Page>::Address as Addr>::Repr
            BitXorAssign, bitxor_assign, ^= for $ty, <<$ty as Page>::Address as Addr>::Repr
        }
    }
}
<commit_msg>[memory] implement fmt::Pointer for Addr types<commit_after>//
//  SOS: the Stupid Operating System
//  by Eliza Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015-2016 Eliza Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Macros to make our custom address types require a lot less repetitive code.
#[macro_export]
macro_rules! Addr {
    (($size:ty) $(pub)* enum $name:ident $($tail:tt)*) => {
        Addr! { @impl $name, $size }
    };
    (($size:ty) $(pub)* struct $name:ident $($tail:tt)*) => {
        Addr! { @impl $name, $size }
    };
    (@impl $ty:ident, $size:ty) => {

        impl ::core::fmt::Debug for $ty {
            fn fmt(&self, f: &mut ::core::fmt::Formatter)
                  -> ::core::fmt::Result {
                write!(f, "{}({:#x})", stringify!($ty), self.0)
            }
        }

        impl ::core::fmt::Pointer for $ty {
            fn fmt(&self, f: &mut ::core::fmt::Formatter)
                  -> ::core::fmt::Result {
                write!(f, "{:#x}", self.0)
            }
        }

        impl ::core::convert::Into<$size> for $ty {
            #[inline] fn into(self) -> $size { self.0 }
        }

        impl ::core::convert::From<$size> for $ty {
            #[inline] fn from(n: $size) -> Self { $ty(n) }
        }

        impl<T> ::core::convert::From<*mut T> for $ty {
            #[inline] fn from(ptr: *mut T) -> Self { $ty(ptr as $size) }
        }

        impl<T> ::core::convert::From<*const T> for $ty {
            #[inline] fn from(ptr: *const T) -> Self { $ty(ptr as $size) }
        }

        impl ::core::ops::Deref for $ty {
            type Target = $size;
            #[inline] fn deref(&self) -> &Self::Target { &self.0 }
        }

        impl $ty {
            #[inline(always)]
            pub const fn as_mut_ptr<T>(&self) -> *mut T { self.0 as *mut _ }

            #[inline(always)]
            pub const fn as_ptr<T>(&self) -> *const T { self.0 as *const _ }

            /// Returns true if this address is aligned on a page boundary.
            #[inline]
            pub fn is_page_aligned(&self) -> bool {
                use $crate::arch::PAGE_SIZE;
                **self % PAGE_SIZE as <Self as Addr>::Repr == 0 as <Self as Addr>::Repr
            }
        }

        impl_ops! {
            Add, add, + for $ty, $size
            Sub, sub, - for $ty, $size
            Div, div, / for $ty, $size
            Mul, mul, * for $ty, $size
            Shl, shl, >> for $ty, $size
            Shr, shr, << for $ty, $size
            Rem, rem, % for $ty, $size
            BitAnd, bitand, & for $ty, $size
            BitOr, bitor, | for $ty, $size
            BitXor, bitxor, ^ for $ty, $size

        }

        impl_assign_ops! {
            AddAssign, add_assign, += for $ty, $size
            SubAssign, sub_assign, -= for $ty, $size
            DivAssign, div_assign, /= for $ty, $size
            MulAssign, mul_assign, *= for $ty, $size
            ShlAssign, shl_assign, >>= for $ty, $size
            ShrAssign, shr_assign, <<= for $ty, $size
            RemAssign, rem_assign, %= for $ty, $size
            BitAndAssign, bitand_assign, &= for $ty, $size
            BitOrAssign, bitor_assign, |= for $ty, $size
            BitXorAssign, bitxor_assign, ^= for $ty, $size
        }

        impl_fmt! {
            Binary for $ty
            Display for $ty
            Octal for $ty
            LowerHex for $ty
            UpperHex for $ty
        }

        impl Addr for $ty {
            type Repr = $size;
        }

    }
}

macro_rules! impl_page_ops {
    ($($name:ident, $fun:ident, $op:tt for $ty:ident, $size:ty)*) => {$(
        impl ::core::ops::$name<$ty> for $ty {
            type Output = Self;

            #[inline] fn $fun(self, rhs: $ty) -> Self {
                expr!( $ty { number: expr!(self.number $op rhs.number) })
            }
        }
        impl ::core::ops::$name<$size> for $ty {
            type Output = $ty;

            #[inline] fn $fun(self, rhs: $size) -> Self {
                expr!($ty{ number: expr!(self.number $op rhs) })
            }
        }

        forward_ref_binop! {
            $name, $fun for $ty, $ty
        }
        forward_ref_binop! {
            $name, $fun for $ty, $size
        }
    )*}
}

macro_rules! impl_page_assign_ops {
    ($($name:ident, $fun:ident, $op:tt for $ty:ident, $size:ty)*) => {$(
        impl ::core::ops::$name<$ty> for $ty {

            #[inline] fn $fun(&mut self, rhs: $ty) {
                expr!(self.number $op rhs.number);
            }
        }
        impl ::core::ops::$name<$size> for $ty {

            #[inline] fn $fun(&mut self, rhs: $size) {
                expr!(self.number $op rhs);
            }
        }
    )*}
}

macro_rules! Page {
    (($addr:ty) $(pub)* struct $name:ident $($tail:tt)*) => {
        Page! { @impl $name, $addr }
    };
    (@impl $ty:ident, $addr:ty) => {

        impl Page for $ty {
            type Address = $addr;

            /// Create a new `Page` containing the given address.
            //  TODO: rewrite this as `up`/`down` using the page shift, instead.
            fn containing(addr: $addr) -> Self {
                use ::PAGE_SHIFT;
                assert!( *addr < 0x0000_8000_0000_0000 ||
                         *addr >= 0xffff_8000_0000_0000
                       , "invalid address: 0x{:x}", addr );
                $ty { number: *addr >> PAGE_SHIFT as <Self::Address as Addr>::Repr }
            }

            /// Return the start virtual address of this page
            #[inline]
            fn base(&self) -> $addr {
                Self::Address::from(
                    self.number << PAGE_SHIFT as <Self::Address as Addr>::Repr )
            }

            #[inline] fn number(&self) -> usize {
                self.number as usize
            }
        }

        // impl ::core::num::One for $ty {
        //     #[inline] fn one() -> Self {
        //         $ty { number:
        //             <<$addr as Addr>::Repr as ::core::num::One>::one()
        //         }
        //     }
        // }

        impl ::core::iter::Step for $ty {
            #[inline]
            fn step(&self, by: &Self) -> Option<Self> {
                self.number
                    .step(&by.number)
                    .map(|s| $ty { number: s})
            }

            #[inline]
            #[allow(trivial_numeric_casts)]
            fn steps_between(start: &$ty, end: &$ty, by: &$ty)
                            -> Option<usize> {
                use ::core::iter::Step;
                <<$addr as Addr>::Repr as Step>::steps_between( &start.number
                                                              , &end.number
                                                              , &by.number
                                                              )
            }

            #[inline]
            fn steps_between_by_one(start: &$ty, end: &$ty) -> Option<usize> {
                use ::core::iter::Step;
                <<$addr as Addr>::Repr as Step>::steps_between( &start.number
                                                              , &end.number
                                                              , &1
                                                              )
            }

            #[inline] fn sub_one(&self) -> Self { self - 1 }

            #[inline] fn add_one(&self) -> Self { self + 1 }

            #[inline] fn replace_one(&mut self) -> Self { unimplemented!() }

            #[inline] fn replace_zero(&mut self) -> Self { unimplemented!() }

            #[inline] fn is_negative(&self) -> bool { unimplemented!(); }

        }

        impl_page_ops! {
            Add, add, + for $ty, <<$ty as Page>::Address as Addr>::Repr
            Sub, sub, - for $ty, <<$ty as Page>::Address as Addr>::Repr
            Div, div, / for $ty, <<$ty as Page>::Address as Addr>::Repr
            Mul, mul, * for $ty, <<$ty as Page>::Address as Addr>::Repr
            Shl, shl, >> for $ty,<<$ty as Page>::Address as Addr>::Repr
            Shr, shr, << for $ty, <<$ty as Page>::Address as Addr>::Repr
            Rem, rem, % for $ty, <<$ty as Page>::Address as Addr>::Repr
            BitAnd, bitand, & for $ty, <<$ty as Page>::Address as Addr>::Repr
            BitOr, bitor, | for $ty, <<$ty as Page>::Address as Addr>::Repr
            BitXor, bitxor, ^ for $ty, <<$ty as Page>::Address as Addr>::Repr

        }

        impl_page_assign_ops! {
            AddAssign, add_assign, += for $ty, <<$ty as Page>::Address as Addr>::Repr
            SubAssign, sub_assign, -= for $ty, <<$ty as Page>::Address as Addr>::Repr
            DivAssign, div_assign, /= for $ty, <<$ty as Page>::Address as Addr>::Repr
            MulAssign, mul_assign, *= for $ty, <<$ty as Page>::Address as Addr>::Repr
            ShlAssign, shl_assign, >>= for $ty, <<$ty as Page>::Address as Addr>::Repr
            ShrAssign, shr_assign, <<= for $ty, <<$ty as Page>::Address as Addr>::Repr
            RemAssign, rem_assign, %= for $ty, <<$ty as Page>::Address as Addr>::Repr
            BitAndAssign, bitand_assign, &= for $ty, <<$ty as Page>::Address as Addr>::Repr
            BitOrAssign, bitor_assign, |= for $ty, <<$ty as Page>::Address as Addr>::Repr
            BitXorAssign, bitxor_assign, ^= for $ty, <<$ty as Page>::Address as Addr>::Repr
        }
    }
}
<|endoftext|>


<commit_before>#![allow(clippy::large_enum_variant)]

use futures::{try_ready, Async, Future, Poll, Stream};
use futures_cpupool::{CpuFuture, CpuPool};
use lazy_static::lazy_static;
use state_machine_future::{transition, RentToOwn, StateMachineFuture};
use std::io;
use std::net::{SocketAddr, ToSocketAddrs};
#[cfg(unix)]
use std::path::Path;
use std::time::Instant;
use std::vec;
use tokio_tcp::TcpStream;
use tokio_timer::Delay;
#[cfg(unix)]
use tokio_uds::UnixStream;

use crate::proto::{Client, Connection, HandshakeFuture, SimpleQueryStream};
use crate::{Config, Error, Host, Socket, TargetSessionAttrs, TlsMode};

lazy_static! {
    static ref DNS_POOL: CpuPool = futures_cpupool::Builder::new()
        .name_prefix("postgres-dns-")
        .pool_size(2)
        .create();
}

#[derive(StateMachineFuture)]
pub enum ConnectOnce<T>
where
    T: TlsMode<Socket>,
{
    #[state_machine_future(start)]
    #[cfg_attr(unix, state_machine_future(transitions(ConnectingUnix, ResolvingDns)))]
    #[cfg_attr(not(unix), state_machine_future(transitions(ConnectingTcp)))]
    Start {
        idx: usize,
        tls_mode: T,
        config: Config,
    },
    #[cfg(unix)]
    #[state_machine_future(transitions(Handshaking))]
    ConnectingUnix {
        future: tokio_uds::ConnectFuture,
        timeout: Option<Delay>,
        tls_mode: T,
        config: Config,
    },
    #[state_machine_future(transitions(ConnectingTcp))]
    ResolvingDns {
        future: CpuFuture<vec::IntoIter<SocketAddr>, io::Error>,
        timeout: Option<Delay>,
        tls_mode: T,
        config: Config,
    },
    #[state_machine_future(transitions(Handshaking))]
    ConnectingTcp {
        future: tokio_tcp::ConnectFuture,
        addrs: vec::IntoIter<SocketAddr>,
        timeout: Option<Delay>,
        tls_mode: T,
        config: Config,
    },
    #[state_machine_future(transitions(CheckingSessionAttrs, Finished))]
    Handshaking {
        future: HandshakeFuture<Socket, T>,
        target_session_attrs: TargetSessionAttrs,
    },
    #[state_machine_future(transitions(Finished))]
    CheckingSessionAttrs {
        stream: SimpleQueryStream,
        client: Client,
        connection: Connection<T::Stream>,
    },
    #[state_machine_future(ready)]
    Finished((Client, Connection<T::Stream>)),
    #[state_machine_future(error)]
    Failed(Error),
}

impl<T> PollConnectOnce<T> for ConnectOnce<T>
where
    T: TlsMode<Socket>,
{
    fn poll_start<'a>(state: &'a mut RentToOwn<'a, Start<T>>) -> Poll<AfterStart<T>, Error> {
        let state = state.take();

        let port = *state
            .config
            .0
            .port
            .get(state.idx)
            .or_else(|| state.config.0.port.get(0))
            .unwrap_or(&5432);

        let timeout = state
            .config
            .0
            .connect_timeout
            .map(|d| Delay::new(Instant::now() + d));

        match &state.config.0.host[state.idx] {
            Host::Tcp(host) => {
                let host = host.clone();
                transition!(ResolvingDns {
                    future: DNS_POOL.spawn_fn(move || (&*host, port).to_socket_addrs()),
                    timeout,
                    tls_mode: state.tls_mode,
                    config: state.config,
                })
            }
            #[cfg(unix)]
            Host::Unix(host) => {
                let path = Path::new(host).join(format!(".s.PGSQL.{}", port));
                transition!(ConnectingUnix {
                    future: UnixStream::connect(path),
                    timeout,
                    tls_mode: state.tls_mode,
                    config: state.config,
                })
            }
        }
    }

    #[cfg(unix)]
    fn poll_connecting_unix<'a>(
        state: &'a mut RentToOwn<'a, ConnectingUnix<T>>,
    ) -> Poll<AfterConnectingUnix<T>, Error> {
        if let Some(timeout) = &mut state.timeout {
            match timeout.poll() {
                Ok(Async::Ready(())) => {
                    return Err(Error::connect(io::Error::from(io::ErrorKind::TimedOut)))
                }
                Ok(Async::NotReady) => {}
                Err(e) => return Err(Error::connect(io::Error::new(io::ErrorKind::Other, e))),
            }
        }

        let stream = try_ready!(state.future.poll().map_err(Error::connect));
        let stream = Socket::new_unix(stream);
        let state = state.take();

        transition!(Handshaking {
            target_session_attrs: state.config.0.target_session_attrs,
            future: HandshakeFuture::new(stream, state.tls_mode, state.config),
        })
    }

    fn poll_resolving_dns<'a>(
        state: &'a mut RentToOwn<'a, ResolvingDns<T>>,
    ) -> Poll<AfterResolvingDns<T>, Error> {
        if let Some(timeout) = &mut state.timeout {
            match timeout.poll() {
                Ok(Async::Ready(())) => {
                    return Err(Error::connect(io::Error::from(io::ErrorKind::TimedOut)))
                }
                Ok(Async::NotReady) => {}
                Err(e) => return Err(Error::connect(io::Error::new(io::ErrorKind::Other, e))),
            }
        }

        let mut addrs = try_ready!(state.future.poll().map_err(Error::connect));
        let state = state.take();

        let addr = match addrs.next() {
            Some(addr) => addr,
            None => {
                return Err(Error::connect(io::Error::new(
                    io::ErrorKind::InvalidData,
                    "resolved 0 addresses",
                )));
            }
        };

        transition!(ConnectingTcp {
            future: TcpStream::connect(&addr),
            addrs,
            timeout: state.timeout,
            tls_mode: state.tls_mode,
            config: state.config,
        })
    }

    fn poll_connecting_tcp<'a>(
        state: &'a mut RentToOwn<'a, ConnectingTcp<T>>,
    ) -> Poll<AfterConnectingTcp<T>, Error> {
        if let Some(timeout) = &mut state.timeout {
            match timeout.poll() {
                Ok(Async::Ready(())) => {
                    return Err(Error::connect(io::Error::from(io::ErrorKind::TimedOut)))
                }
                Ok(Async::NotReady) => {}
                Err(e) => return Err(Error::connect(io::Error::new(io::ErrorKind::Other, e))),
            }
        }

        let stream = loop {
            match state.future.poll() {
                Ok(Async::Ready(stream)) => break stream,
                Ok(Async::NotReady) => return Ok(Async::NotReady),
                Err(e) => {
                    let addr = match state.addrs.next() {
                        Some(addr) => addr,
                        None => return Err(Error::connect(e)),
                    };
                    state.future = TcpStream::connect(&addr);
                }
            }
        };
        let state = state.take();

        stream.set_nodelay(true).map_err(Error::connect)?;
        if state.config.0.keepalives {
            stream
                .set_keepalive(Some(state.config.0.keepalives_idle))
                .map_err(Error::connect)?;
        }

        let stream = Socket::new_tcp(stream);

        transition!(Handshaking {
            target_session_attrs: state.config.0.target_session_attrs,
            future: HandshakeFuture::new(stream, state.tls_mode, state.config),
        })
    }

    fn poll_handshaking<'a>(
        state: &'a mut RentToOwn<'a, Handshaking<T>>,
    ) -> Poll<AfterHandshaking<T>, Error> {
        let (client, connection) = try_ready!(state.future.poll());

        if let TargetSessionAttrs::ReadWrite = state.target_session_attrs {
            transition!(CheckingSessionAttrs {
                stream: client.batch_execute("SHOW transaction_read_only"),
                client,
                connection,
            })
        } else {
            transition!(Finished((client, connection)))
        }
    }

    fn poll_checking_session_attrs<'a>(
        state: &'a mut RentToOwn<'a, CheckingSessionAttrs<T>>,
    ) -> Poll<AfterCheckingSessionAttrs<T>, Error> {
        if let Async::Ready(()) = state.connection.poll()? {
            return Err(Error::closed());
        }

        match try_ready!(state.stream.poll()) {
            Some(row) => {
                if row.get(0) == Some("on") {
                    Err(Error::connect(io::Error::new(
                        io::ErrorKind::PermissionDenied,
                        "database does not allow writes",
                    )))
                } else {
                    let state = state.take();
                    transition!(Finished((state.client, state.connection)))
                }
            }
            None => Err(Error::closed()),
        }
    }
}

impl<T> ConnectOnceFuture<T>
where
    T: TlsMode<Socket>,
{
    pub fn new(idx: usize, tls_mode: T, config: Config) -> ConnectOnceFuture<T> {
        ConnectOnce::start(idx, tls_mode, config)
    }
}
<commit_msg>cleanup<commit_after>#![allow(clippy::large_enum_variant)]

use futures::{try_ready, Async, Future, Poll, Stream};
use futures_cpupool::{CpuFuture, CpuPool};
use lazy_static::lazy_static;
use state_machine_future::{transition, RentToOwn, StateMachineFuture};
use std::io;
use std::net::{SocketAddr, ToSocketAddrs};
use std::time::Instant;
use std::vec;
use tokio_tcp::TcpStream;
use tokio_timer::Delay;
#[cfg(unix)]
use tokio_uds::UnixStream;

use crate::proto::{Client, Connection, HandshakeFuture, SimpleQueryStream};
use crate::{Config, Error, Host, Socket, TargetSessionAttrs, TlsMode};

lazy_static! {
    static ref DNS_POOL: CpuPool = futures_cpupool::Builder::new()
        .name_prefix("postgres-dns-")
        .pool_size(2)
        .create();
}

#[derive(StateMachineFuture)]
pub enum ConnectOnce<T>
where
    T: TlsMode<Socket>,
{
    #[state_machine_future(start)]
    #[cfg_attr(unix, state_machine_future(transitions(ConnectingUnix, ResolvingDns)))]
    #[cfg_attr(not(unix), state_machine_future(transitions(ConnectingTcp)))]
    Start {
        idx: usize,
        tls_mode: T,
        config: Config,
    },
    #[cfg(unix)]
    #[state_machine_future(transitions(Handshaking))]
    ConnectingUnix {
        future: tokio_uds::ConnectFuture,
        timeout: Option<Delay>,
        tls_mode: T,
        config: Config,
    },
    #[state_machine_future(transitions(ConnectingTcp))]
    ResolvingDns {
        future: CpuFuture<vec::IntoIter<SocketAddr>, io::Error>,
        timeout: Option<Delay>,
        tls_mode: T,
        config: Config,
    },
    #[state_machine_future(transitions(Handshaking))]
    ConnectingTcp {
        future: tokio_tcp::ConnectFuture,
        addrs: vec::IntoIter<SocketAddr>,
        timeout: Option<Delay>,
        tls_mode: T,
        config: Config,
    },
    #[state_machine_future(transitions(CheckingSessionAttrs, Finished))]
    Handshaking {
        future: HandshakeFuture<Socket, T>,
        target_session_attrs: TargetSessionAttrs,
    },
    #[state_machine_future(transitions(Finished))]
    CheckingSessionAttrs {
        stream: SimpleQueryStream,
        client: Client,
        connection: Connection<T::Stream>,
    },
    #[state_machine_future(ready)]
    Finished((Client, Connection<T::Stream>)),
    #[state_machine_future(error)]
    Failed(Error),
}

impl<T> PollConnectOnce<T> for ConnectOnce<T>
where
    T: TlsMode<Socket>,
{
    fn poll_start<'a>(state: &'a mut RentToOwn<'a, Start<T>>) -> Poll<AfterStart<T>, Error> {
        let state = state.take();

        let port = *state
            .config
            .0
            .port
            .get(state.idx)
            .or_else(|| state.config.0.port.get(0))
            .unwrap_or(&5432);

        let timeout = state
            .config
            .0
            .connect_timeout
            .map(|d| Delay::new(Instant::now() + d));

        match &state.config.0.host[state.idx] {
            Host::Tcp(host) => {
                let host = host.clone();
                transition!(ResolvingDns {
                    future: DNS_POOL.spawn_fn(move || (&*host, port).to_socket_addrs()),
                    timeout,
                    tls_mode: state.tls_mode,
                    config: state.config,
                })
            }
            #[cfg(unix)]
            Host::Unix(host) => {
                let path = host.join(format!(".s.PGSQL.{}", port));
                transition!(ConnectingUnix {
                    future: UnixStream::connect(path),
                    timeout,
                    tls_mode: state.tls_mode,
                    config: state.config,
                })
            }
        }
    }

    #[cfg(unix)]
    fn poll_connecting_unix<'a>(
        state: &'a mut RentToOwn<'a, ConnectingUnix<T>>,
    ) -> Poll<AfterConnectingUnix<T>, Error> {
        if let Some(timeout) = &mut state.timeout {
            match timeout.poll() {
                Ok(Async::Ready(())) => {
                    return Err(Error::connect(io::Error::from(io::ErrorKind::TimedOut)))
                }
                Ok(Async::NotReady) => {}
                Err(e) => return Err(Error::connect(io::Error::new(io::ErrorKind::Other, e))),
            }
        }

        let stream = try_ready!(state.future.poll().map_err(Error::connect));
        let stream = Socket::new_unix(stream);
        let state = state.take();

        transition!(Handshaking {
            target_session_attrs: state.config.0.target_session_attrs,
            future: HandshakeFuture::new(stream, state.tls_mode, state.config),
        })
    }

    fn poll_resolving_dns<'a>(
        state: &'a mut RentToOwn<'a, ResolvingDns<T>>,
    ) -> Poll<AfterResolvingDns<T>, Error> {
        if let Some(timeout) = &mut state.timeout {
            match timeout.poll() {
                Ok(Async::Ready(())) => {
                    return Err(Error::connect(io::Error::from(io::ErrorKind::TimedOut)))
                }
                Ok(Async::NotReady) => {}
                Err(e) => return Err(Error::connect(io::Error::new(io::ErrorKind::Other, e))),
            }
        }

        let mut addrs = try_ready!(state.future.poll().map_err(Error::connect));
        let state = state.take();

        let addr = match addrs.next() {
            Some(addr) => addr,
            None => {
                return Err(Error::connect(io::Error::new(
                    io::ErrorKind::InvalidData,
                    "resolved 0 addresses",
                )));
            }
        };

        transition!(ConnectingTcp {
            future: TcpStream::connect(&addr),
            addrs,
            timeout: state.timeout,
            tls_mode: state.tls_mode,
            config: state.config,
        })
    }

    fn poll_connecting_tcp<'a>(
        state: &'a mut RentToOwn<'a, ConnectingTcp<T>>,
    ) -> Poll<AfterConnectingTcp<T>, Error> {
        if let Some(timeout) = &mut state.timeout {
            match timeout.poll() {
                Ok(Async::Ready(())) => {
                    return Err(Error::connect(io::Error::from(io::ErrorKind::TimedOut)))
                }
                Ok(Async::NotReady) => {}
                Err(e) => return Err(Error::connect(io::Error::new(io::ErrorKind::Other, e))),
            }
        }

        let stream = loop {
            match state.future.poll() {
                Ok(Async::Ready(stream)) => break stream,
                Ok(Async::NotReady) => return Ok(Async::NotReady),
                Err(e) => {
                    let addr = match state.addrs.next() {
                        Some(addr) => addr,
                        None => return Err(Error::connect(e)),
                    };
                    state.future = TcpStream::connect(&addr);
                }
            }
        };
        let state = state.take();

        stream.set_nodelay(true).map_err(Error::connect)?;
        if state.config.0.keepalives {
            stream
                .set_keepalive(Some(state.config.0.keepalives_idle))
                .map_err(Error::connect)?;
        }

        let stream = Socket::new_tcp(stream);

        transition!(Handshaking {
            target_session_attrs: state.config.0.target_session_attrs,
            future: HandshakeFuture::new(stream, state.tls_mode, state.config),
        })
    }

    fn poll_handshaking<'a>(
        state: &'a mut RentToOwn<'a, Handshaking<T>>,
    ) -> Poll<AfterHandshaking<T>, Error> {
        let (client, connection) = try_ready!(state.future.poll());

        if let TargetSessionAttrs::ReadWrite = state.target_session_attrs {
            transition!(CheckingSessionAttrs {
                stream: client.batch_execute("SHOW transaction_read_only"),
                client,
                connection,
            })
        } else {
            transition!(Finished((client, connection)))
        }
    }

    fn poll_checking_session_attrs<'a>(
        state: &'a mut RentToOwn<'a, CheckingSessionAttrs<T>>,
    ) -> Poll<AfterCheckingSessionAttrs<T>, Error> {
        if let Async::Ready(()) = state.connection.poll()? {
            return Err(Error::closed());
        }

        match try_ready!(state.stream.poll()) {
            Some(row) => {
                if row.get(0) == Some("on") {
                    Err(Error::connect(io::Error::new(
                        io::ErrorKind::PermissionDenied,
                        "database does not allow writes",
                    )))
                } else {
                    let state = state.take();
                    transition!(Finished((state.client, state.connection)))
                }
            }
            None => Err(Error::closed()),
        }
    }
}

impl<T> ConnectOnceFuture<T>
where
    T: TlsMode<Socket>,
{
    pub fn new(idx: usize, tls_mode: T, config: Config) -> ConnectOnceFuture<T> {
        ConnectOnce::start(idx, tls_mode, config)
    }
}
<|endoftext|>


<commit_before>extern crate sdl;

use std::rand::Rng;

use sdl::video::{SurfaceFlag, VideoFlag};
use sdl::event::{Event, Key};

#[main]
pub fn main() {
    sdl::init(&[sdl::InitFlag::Video]);
    sdl::wm::set_caption("rust-sdl demo - video", "rust-sdl");

    let mut rng = std::rand::thread_rng();
    let screen = match sdl::video::set_video_mode(800, 600, 32,
                                                  &[SurfaceFlag::HWSurface],
                                                  &[VideoFlag::DoubleBuf]) {
        Ok(screen) => screen,
        Err(err) => panic!("failed to set video mode: {}", err)
    };

    // Note: You'll want to put this and the flip call inside the main loop
    // but we don't as to not startle epileptics
    for i in 0us..10 {
        for j in 0us..10 {
            screen.fill_rect(Some(sdl::Rect {
                x: (i as i16) * 800 / 10,
                y: (j as i16) * 600 / 10,
                w: 800 / 10,
                h: 600 / 10
            }), rng.gen::<sdl::video::Color>());
        }
    }

    screen.flip();

    'main : loop {
        'event : loop {
            match sdl::event::poll_event() {
                Event::Quit => break 'main,
                Event::None => break 'event,
                Event::Key(k, _, _, _)
                    if k == Key::Escape
                        => break 'main,
                _ => {}
            }
        }
    }

    sdl::quit();
}
<commit_msg>sdl-demo: fix 'nonstandard #[main]' error<commit_after>extern crate sdl;

use std::rand::Rng;

use sdl::video::{SurfaceFlag, VideoFlag};
use sdl::event::{Event, Key};

fn main() {
    sdl::init(&[sdl::InitFlag::Video]);
    sdl::wm::set_caption("rust-sdl demo - video", "rust-sdl");

    let mut rng = std::rand::thread_rng();
    let screen = match sdl::video::set_video_mode(800, 600, 32,
                                                  &[SurfaceFlag::HWSurface],
                                                  &[VideoFlag::DoubleBuf]) {
        Ok(screen) => screen,
        Err(err) => panic!("failed to set video mode: {}", err)
    };

    // Note: You'll want to put this and the flip call inside the main loop
    // but we don't as to not startle epileptics
    for i in 0us..10 {
        for j in 0us..10 {
            screen.fill_rect(Some(sdl::Rect {
                x: (i as i16) * 800 / 10,
                y: (j as i16) * 600 / 10,
                w: 800 / 10,
                h: 600 / 10
            }), rng.gen::<sdl::video::Color>());
        }
    }

    screen.flip();

    'main : loop {
        'event : loop {
            match sdl::event::poll_event() {
                Event::Quit => break 'main,
                Event::None => break 'event,
                Event::Key(k, _, _, _)
                    if k == Key::Escape
                        => break 'main,
                _ => {}
            }
        }
    }

    sdl::quit();
}
<|endoftext|>


<commit_before>/*
 * Rustyard - Simon Whitehead, 2016
 */

use lexer;
use rpn_calculator as calc;
use token;

/// The ShuntingYard struct transforms an expression
/// to a 64-bit floating point value
pub struct ShuntingYard<'a> {
    lexer: lexer::Lexer<'a>,
    output_queue: Vec<token::Token>,
    stack: Vec<token::Token>,
    pub errors: Vec<String>
}

impl<'a> ShuntingYard<'a> {
    pub fn new(raw_input: &str) -> ShuntingYard {
        let mut yard = ShuntingYard {
            lexer: lexer::Lexer::new(raw_input),
            output_queue: vec![],
            stack: vec![],
            errors: vec![]
        };

        // If there were Lexer errors, add them now.
        let lexer_errors = yard.lexer.errors.clone();
        yard.errors.extend(lexer_errors);

        // Transform the Lexer input via the Shunting Yard algorithm
        yard.transform();
        yard
    }

    /// calculate returns a 64-bit floating value after
    /// parsing the Reverse Polish Notation represented
    /// by the output_queue.
    pub fn calculate(&self) -> Option<f64> {
        calc::calculate(&self.output_queue)
    }

    // Transforms the input from the Lexer in to the output_queue
    // and stack based on the Shunting Yard algorithm
    fn transform(&mut self) {
        // Iterate over each token and move it based on the algorithm
        for t in self.lexer.ast.to_vec() {
            // If the token is a number, then add it to the output queue
            match t {
                token::Token::WholeNumber(_) => self.output_queue.push(t),
                token::Token::DecimalNumber(_) => self.output_queue.push(t),
                token::Token::Operator(o1, o1_associativity, o1_precedence) => {
                    loop {
                        match self.stack.last() {
                            Some(&token::Token::Operator(_, _, o2_precedence)) => {
                                if (o1_associativity == token::LEFT_ASSOCIATIVE &&
                                   o1_precedence <= o2_precedence) ||
                                   (o1_associativity == token::RIGHT_ASSOCIATIVE &&
                                   o1_precedence < o2_precedence) {
                                    self.output_queue.push(self.stack.pop().unwrap());
                                } else {
                                    self.stack.push(token::Token::Operator(o1, o1_associativity, o1_precedence));
                                    break
                                }
                            },
                            _ => { self.stack.push(token::Token::Operator(o1, o1_associativity, o1_precedence)); break; }
                        }
                    }
                },
                token::Token::LeftParenthesis => self.stack.push(token::Token::LeftParenthesis),
                token::Token::RightParenthesis => {
                    let mut found_left_paren = false;
                    loop {
                        match self.stack.last() {
                            Some(&token::Token::LeftParenthesis) => {
                                found_left_paren = true;
                                self.stack.pop().unwrap();
                            },
                            None => {
                                if !found_left_paren {
                                    self.errors.push("Unbalanced parenthesis".to_string());
                                }
                                break;
                            },
                            _ => self.output_queue.push(self.stack.pop().unwrap()),
                        }
                    }
                },
                _ => ()
            }
        }

        // Are there any operators left on the stack?
        while self.stack.len() > 0 {
            // Pop them off and push them to the output_queue
            let op = self.stack.pop();
            match op {
                Some(token::Token::Operator(o, oa, op)) => {
                    self.output_queue.push(token::Token::Operator(o, oa, op));
                },
                Some(token::Token::LeftParenthesis) => {
                    self.errors.push("Unbalanced parenthesis".to_string());
                    break;
                },
                Some(token::Token::RightParenthesis) => {
                    self.errors.push("Unbalanced parenthesis".to_string());
                    break;
                },
                _ => ()
            }
        }
    }

    /// to_string returns the string representation of the Shunting Yard
    /// algorithm in Reverse Polish Notation.
    pub fn to_string(&self) -> String {
        let mut result = String::new(); // String to output the result
        let output_queue = self.output_queue.to_vec(); // Make a copy of the output queue

        // Iterate over the output queue and print each one to the result
        for t in output_queue {
            match t {
                token::Token::Operator(c, _, _) => result.push(c),
                token::Token::DecimalNumber(n) => result.push_str(&n.to_string()[..]),
                token::Token::LeftParenthesis => result.push_str("("),
                token::Token::RightParenthesis => result.push_str(")"),
                _ => ()
            };

            result.push_str(" "); // Space separated
        }

        result
    }

    /// to_string_ast returns the string representation of the
    /// Lexer tokens.
    pub fn to_string_ast(&self) -> String {
        let mut result = String::new(); // String to output the result to
        let ast = self.lexer.ast.to_vec();    // Copy the AST into its own vector so we can consume it

        // Loop over each item in the AST and print a String representation of it
        for t in ast {
            match t {
                token::Token::Operator(c, _, _) => result.push(c),
                token::Token::DecimalNumber(n) => result.push_str(&n.to_string()[..]),
                token::Token::LeftParenthesis => result.push_str("("),
                token::Token::RightParenthesis => result.push_str(")"),
                _ => ()
            };
            result.push_str(" "); // Space separated
        }

        // Return the result
        result
    }
}
<commit_msg>Implemented the std::string::ToString trait on the ShuntingYard and moved 'to_string' there<commit_after>/*
 * Rustyard - Simon Whitehead, 2016
 */

use std;

use lexer;
use rpn_calculator as calc;
use token;

/// The ShuntingYard struct transforms an expression
/// to a 64-bit floating point value
pub struct ShuntingYard<'a> {
    lexer: lexer::Lexer<'a>,
    output_queue: Vec<token::Token>,
    stack: Vec<token::Token>,
    pub errors: Vec<String>
}

impl<'a> ShuntingYard<'a> {
    pub fn new(raw_input: &str) -> ShuntingYard {
        let mut yard = ShuntingYard {
            lexer: lexer::Lexer::new(raw_input),
            output_queue: vec![],
            stack: vec![],
            errors: vec![]
        };

        // If there were Lexer errors, add them now.
        let lexer_errors = yard.lexer.errors.clone();
        yard.errors.extend(lexer_errors);

        // Transform the Lexer input via the Shunting Yard algorithm
        yard.transform();
        yard
    }

    /// calculate returns a 64-bit floating value after
    /// parsing the Reverse Polish Notation represented
    /// by the output_queue.
    pub fn calculate(&self) -> Option<f64> {
        calc::calculate(&self.output_queue)
    }

    // Transforms the input from the Lexer in to the output_queue
    // and stack based on the Shunting Yard algorithm
    fn transform(&mut self) {
        // Iterate over each token and move it based on the algorithm
        for t in self.lexer.ast.to_vec() {
            // If the token is a number, then add it to the output queue
            match t {
                token::Token::WholeNumber(_) => self.output_queue.push(t),
                token::Token::DecimalNumber(_) => self.output_queue.push(t),
                token::Token::Operator(o1, o1_associativity, o1_precedence) => {
                    loop {
                        match self.stack.last() {
                            Some(&token::Token::Operator(_, _, o2_precedence)) => {
                                if (o1_associativity == token::LEFT_ASSOCIATIVE &&
                                   o1_precedence <= o2_precedence) ||
                                   (o1_associativity == token::RIGHT_ASSOCIATIVE &&
                                   o1_precedence < o2_precedence) {
                                    self.output_queue.push(self.stack.pop().unwrap());
                                } else {
                                    self.stack.push(token::Token::Operator(o1, o1_associativity, o1_precedence));
                                    break
                                }
                            },
                            _ => { self.stack.push(token::Token::Operator(o1, o1_associativity, o1_precedence)); break; }
                        }
                    }
                },
                token::Token::LeftParenthesis => self.stack.push(token::Token::LeftParenthesis),
                token::Token::RightParenthesis => {
                    let mut found_left_paren = false;
                    loop {
                        match self.stack.last() {
                            Some(&token::Token::LeftParenthesis) => {
                                found_left_paren = true;
                                self.stack.pop().unwrap();
                            },
                            None => {
                                if !found_left_paren {
                                    self.errors.push("Unbalanced parenthesis".to_string());
                                }
                                break;
                            },
                            _ => self.output_queue.push(self.stack.pop().unwrap()),
                        }
                    }
                },
                _ => ()
            }
        }

        // Are there any operators left on the stack?
        while self.stack.len() > 0 {
            // Pop them off and push them to the output_queue
            let op = self.stack.pop();
            match op {
                Some(token::Token::Operator(o, oa, op)) => {
                    self.output_queue.push(token::Token::Operator(o, oa, op));
                },
                Some(token::Token::LeftParenthesis) => {
                    self.errors.push("Unbalanced parenthesis".to_string());
                    break;
                },
                Some(token::Token::RightParenthesis) => {
                    self.errors.push("Unbalanced parenthesis".to_string());
                    break;
                },
                _ => ()
            }
        }
    }

    /// to_string_ast returns the string representation of the
    /// Lexer tokens.
    pub fn to_string_ast(&self) -> String {
        let mut result = String::new(); // String to output the result to
        let ast = self.lexer.ast.to_vec();    // Copy the AST into its own vector so we can consume it

        // Loop over each item in the AST and print a String representation of it
        for t in ast {
            match t {
                token::Token::Operator(c, _, _) => result.push(c),
                token::Token::DecimalNumber(n) => result.push_str(&n.to_string()[..]),
                token::Token::LeftParenthesis => result.push_str("("),
                token::Token::RightParenthesis => result.push_str(")"),
                _ => ()
            };
            result.push_str(" "); // Space separated
        }

        // Return the result
        result
    }
}

impl<'a> std::string::ToString for ShuntingYard<'a> {
    /// to_string returns the string representation of the Shunting Yard
    /// algorithm in Reverse Polish Notation.
    fn to_string(&self) -> String {
        let mut result = String::new(); // String to output the result
        let output_queue = self.output_queue.to_vec(); // Make a copy of the output queue

        // Iterate over the output queue and print each one to the result
        for t in output_queue {
            match t {
                token::Token::Operator(c, _, _) => result.push(c),
                token::Token::DecimalNumber(n) => result.push_str(&n.to_string()[..]),
                token::Token::LeftParenthesis => result.push_str("("),
                token::Token::RightParenthesis => result.push_str(")"),
                _ => ()
            };

            result.push_str(" "); // Space separated
        }

        result
    }

}
<|endoftext|>


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

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

use piston_window::math;

use client;
use canvas;
use animation::{Animation, MoveAnimation, RotateAnimation, AnimationStatus};
use state::{TurtleState, DrawingState, Path};
use query::{Query, DrawingCommand, Response};
use radians::{self, Radians};
use {Point, Distance, Event};

use self::DrawingCommand::*;

pub struct TurtleWindow {
    renderer: process::Child,
    thread_handle: Option<thread::JoinHandle<()>>,
    /// Channel for receiving responses from the rendering process
    response_channel: mpsc::Receiver<Response>,
}

impl TurtleWindow {
    pub fn new() -> TurtleWindow {
        canvas::start();

        let (response_tx, response_rx) = mpsc::channel();

        let current_exe = env::current_exe()
            .expect("Could not read path of the currently running executable")
            .into_os_string();
        let mut renderer_process = process::Command::new(current_exe)
            .env("RUN_TURTLE_CANVAS", "true")
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .spawn()
            .expect("renderer process failed to start");

        let renderer_stdout = renderer_process.stdout.take()
            .expect("renderer process was not opened with stdout");
        let handle = thread::spawn(move || {
            client::run(renderer_stdout, response_tx);
        });

        Self {
            renderer: renderer_process,
            thread_handle: Some(handle),
            response_channel: response_rx,
        }
    }

    /// Provides read-only access to the turtle state
    pub fn turtle(&self) -> TurtleState {
        unimplemented!();
    }

    /// Update the turtle state
    pub fn update_turtle(&mut self, turtle: TurtleState) {
        unimplemented!();
    }

    /// Provides read-only access to the drawing
    pub fn drawing(&self) -> DrawingState {
        unimplemented!();
    }

    /// Update the drawing state
    pub fn update_drawing(&mut self, drawing: DrawingState) {
        unimplemented!();
    }

    /// Provides read-only access to the temporary path
    fn temporary_path(&self) -> Option<Path> {
        unimplemented!();
    }

    fn set_temporary_path(&mut self, path: Option<Path>) {
        unimplemented!();
    }

    /// See [`Turtle::poll_event()`](struct.Turtle.html#method.poll_event).
    pub fn poll_event(&mut self) -> Option<Event> {
        //TODO: query for an event, then read the response
        unimplemented!();
        //match self.response_channel.try_recv() {
        //    Ok(event) => Some(event),
        //    Err(TryRecvError::Empty) => None, // Do nothing
        //    // The window has been closed
        //    Err(TryRecvError::Disconnected) => self.exit_process(), // Quit
        //}
    }

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

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

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

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

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

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

        self.play_animation(animation);
    }

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

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

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

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

        self.play_animation(animation);
    }

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

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

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

        self.play_animation(animation);
    }

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

                    break;
                },
            }
        }
    }

    // During tests, we disable the renderer. That means that if we let this code run, it will
    // quit the application during the tests and make it look like everything passes.
    // We disable this code so that none of that happens.
    #[cfg(any(feature = "test", test))]
    fn send_drawing_command(&mut self, _: DrawingCommand) {}

    #[cfg(not(any(feature = "test", test)))]
    #[inline]
    fn send_drawing_command(&mut self, command: DrawingCommand) {
        if let Some(ref mut stdin) = self.renderer.stdin {
            client::send_query(stdin, &Query::Drawing(command));
        }
        else {
            unreachable!("bug: renderer process was not opened with stdin");
        }
    }

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

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

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

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

use piston_window::math;

use client;
use canvas;
use animation::{Animation, MoveAnimation, RotateAnimation, AnimationStatus};
use state::{TurtleState, DrawingState, Path};
use query::{Query, DrawingCommand, Response};
use radians::{self, Radians};
use {Point, Distance, Event};

use self::DrawingCommand::*;

#[cfg(any(feature = "test", test))]
fn renderer_client(_: mpsc::Sender<Response>) -> (process::Child, thread::JoinHandle<()>) {
    let command = if cfg!(windows) {
        "dir"
    } else {
        "ls"
    };
    let mut test_proc = process::Command::new(command)
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .spawn()
        .expect("test process failed to start");
    test_proc.kill().expect("test process could not be killed!");
    let handle = thread::spawn(move || {});
    (test_proc, handle)
}

#[cfg(not(any(feature = "test", test)))]
fn renderer_client(response_tx: mpsc::Sender<Response>) -> (process::Child, thread::JoinHandle<()>) {
    let current_exe = env::current_exe()
        .expect("Could not read path of the currently running executable")
        .into_os_string();
    let mut renderer_process = process::Command::new(current_exe)
        .env("RUN_TURTLE_CANVAS", "true")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .spawn()
        .expect("renderer process failed to start");

    let renderer_stdout = renderer_process.stdout.take()
        .expect("renderer process was not opened with stdout");
    let handle = thread::spawn(move || {
        client::run(renderer_stdout, response_tx);
    });

    (renderer_process, handle)
}

pub struct TurtleWindow {
    renderer: process::Child,
    thread_handle: Option<thread::JoinHandle<()>>,
    /// Channel for receiving responses from the rendering process
    response_channel: mpsc::Receiver<Response>,
}

impl TurtleWindow {
    pub fn new() -> TurtleWindow {
        canvas::start();

        let (response_tx, response_rx) = mpsc::channel();
        let (renderer_process, handle) = renderer_client(response_tx);

        Self {
            renderer: renderer_process,
            thread_handle: Some(handle),
            response_channel: response_rx,
        }
    }

    /// Provides read-only access to the turtle state
    pub fn turtle(&self) -> TurtleState {
        unimplemented!();
    }

    /// Update the turtle state
    pub fn update_turtle(&mut self, turtle: TurtleState) {
        unimplemented!();
    }

    /// Provides read-only access to the drawing
    pub fn drawing(&self) -> DrawingState {
        unimplemented!();
    }

    /// Update the drawing state
    pub fn update_drawing(&mut self, drawing: DrawingState) {
        unimplemented!();
    }

    /// Provides read-only access to the temporary path
    fn temporary_path(&self) -> Option<Path> {
        unimplemented!();
    }

    fn set_temporary_path(&mut self, path: Option<Path>) {
        unimplemented!();
    }

    /// See [`Turtle::poll_event()`](struct.Turtle.html#method.poll_event).
    pub fn poll_event(&mut self) -> Option<Event> {
        //TODO: query for an event, then read the response
        unimplemented!();
        //match self.response_channel.try_recv() {
        //    Ok(event) => Some(event),
        //    Err(TryRecvError::Empty) => None, // Do nothing
        //    // The window has been closed
        //    Err(TryRecvError::Disconnected) => self.exit_process(), // Quit
        //}
    }

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

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

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

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

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

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

        self.play_animation(animation);
    }

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

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

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

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

        self.play_animation(animation);
    }

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

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

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

        self.play_animation(animation);
    }

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

                    break;
                },
            }
        }
    }

    // During tests, we disable the renderer. That means that if we let this code run, it will
    // quit the application during the tests and make it look like everything passes.
    // We disable this code so that none of that happens.
    #[cfg(any(feature = "test", test))]
    fn send_drawing_command(&mut self, _: DrawingCommand) {}

    #[cfg(not(any(feature = "test", test)))]
    #[inline]
    fn send_drawing_command(&mut self, command: DrawingCommand) {
        if let Some(ref mut stdin) = self.renderer.stdin {
            client::send_query(stdin, &Query::Drawing(command));
        }
        else {
            unreachable!("bug: renderer process was not opened with stdin");
        }
    }

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

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

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