<commit_before>// This file is part of rss.
//
// Copyright © 2015-2017 The rust-syndication Developers
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the MIT License and/or Apache 2.0 License.

#![warn(missing_docs)]
#![doc(html_root_url = "https://docs.rs/rss/")]

//! Library for serializing the RSS web content syndication format.
//!
//! # Reading
//!
//! ## From a `BufRead`
//!
//! A channel can be read from any object that implements the `BufRead` trait.
//!
//! ```rust
//! use std::fs::File;
//! use std::io::BufReader;
//! use rss::Channel;
//!
//! let file = File::open("tests/data/rss2sample.xml").unwrap();
//! let reader = BufReader::new(file);
//! let channel = Channel::read_from(reader).unwrap();
//! ```
//! ## From a URL
//!
//! A channel can also be read from a URL.
//!
//! To enable this functionality you must enable the `from_url` feature in your Cargo.toml.
//!
//! ```toml
//! [dependencies]
//! rss = { version = "*", features = ["from_url"] }
//! ```
//!
//! ```ignore
//! use rss::Channel;
//!
//! let channel = Channel::from_url("https://feedpress.me/usererror.xml").unwrap();
//! ```
//!
//! # Writing
//!
//! A channel can be written to any object that implements the `Write` trait or converted to an
//! XML string using the `ToString` trait.
//!
//! **Note**: Writing a channel does not perform any escaping of XML entities.
//!
//! ## Example
//!
//! ```rust
//! use std::fs::File;
//! use std::io::{BufReader, sink};
//! use rss::Channel;
//!
//! let file = File::open("tests/data/rss2sample.xml").unwrap();
//! let reader = BufReader::new(file);
//! let channel = Channel::read_from(reader).unwrap();
//!
//! // write to the channel to a writer
//! channel.write_to(sink()).unwrap();
//!
//! // convert the channel to a string
//! let string = channel.to_string();
//! ```
//!
//! # Creation
//!
//! A channel can be created using the Builder functions.
//!
//! ## Example
//!
//! ```
//! use rss::{ChannelBuilder, ImageBuilder};
//!
//! let image = ImageBuilder::default()
//!     .url("http://jupiterbroadcasting.com/images/LAS-300-Badge.jpg")
//!     .title("LAS 300 Logo")
//!     .link("http://www.jupiterbroadcasting.com")
//!     .finalize();
//!
//! let channel = ChannelBuilder::default()
//!     .title("The Linux Action Show! OGG")
//!     .link("http://www.jupiterbroadcasting.com")
//!     .description("Ogg Vorbis audio versions of The Linux Action Show!")
//!     .image(image)
//!     .finalize();
//! ```
//!
//! # Validation
//!
//! Validation can be performed using either a `Channel` or a builder.
//!
//! The the following checks are performed during validation:
//!
//! * Ensures that integer properties can be parsed from their string representaiton into
//! integers
//! * Ensures that the integer properties are within their valid range according to the RSS 2.0
//! specification
//! * Ensures that URL properties can be parsed
//! * Ensures that string properties where only certain values are allowed fall within those
//! valid values
//!
//! ## Example
//!
//! ```
//! use rss::Channel;
//!
//! let input = include_str!("tests/data/rss2sample.xml");
//! let channel = input.parse::<Channel>().unwrap();
//! channel.validate().unwrap();
//! ```
//!
//! ## Example
//!
//! ```
//! use rss::ImageBuilder;
//!
//! let builder = ImageBuilder::default()
//!     .url("http://jupiterbroadcasting.com/images/LAS-300-Badge.jpg")
//!     .title("LAS 300 Logo")
//!     .link("http://www.jupiterbroadcasting.com")
//!     .validate()
//!     .unwrap();
//! ```
extern crate quick_xml;
extern crate chrono;
extern crate url;
extern crate mime;

#[cfg(feature = "from_url")]
extern crate reqwest;

mod fromxml;
mod toxml;

mod channel;
pub use channel::Channel;
pub use channel::ChannelBuilder;

mod item;
pub use item::Item;
pub use item::ItemBuilder;

mod category;
pub use category::Category;
pub use category::CategoryBuilder;

mod guid;
pub use guid::Guid;
pub use guid::GuidBuilder;

mod enclosure;
pub use enclosure::Enclosure;
pub use enclosure::EnclosureBuilder;

mod source;
pub use source::Source;
pub use source::SourceBuilder;

mod cloud;
pub use cloud::Cloud;
pub use cloud::CloudBuilder;

mod image;
pub use image::Image;
pub use image::ImageBuilder;

mod textinput;
pub use textinput::TextInput;
pub use textinput::TextInputBuilder;

/// Types and functions for namespaced extensions.
pub mod extension;
pub use extension::Extension;

mod error;
pub use error::Error;
<commit_msg>representaiton -> representation<commit_after>// This file is part of rss.
//
// Copyright © 2015-2017 The rust-syndication Developers
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the MIT License and/or Apache 2.0 License.

#![warn(missing_docs)]
#![doc(html_root_url = "https://docs.rs/rss/")]

//! Library for serializing the RSS web content syndication format.
//!
//! # Reading
//!
//! ## From a `BufRead`
//!
//! A channel can be read from any object that implements the `BufRead` trait.
//!
//! ```rust
//! use std::fs::File;
//! use std::io::BufReader;
//! use rss::Channel;
//!
//! let file = File::open("tests/data/rss2sample.xml").unwrap();
//! let reader = BufReader::new(file);
//! let channel = Channel::read_from(reader).unwrap();
//! ```
//! ## From a URL
//!
//! A channel can also be read from a URL.
//!
//! To enable this functionality you must enable the `from_url` feature in your Cargo.toml.
//!
//! ```toml
//! [dependencies]
//! rss = { version = "*", features = ["from_url"] }
//! ```
//!
//! ```ignore
//! use rss::Channel;
//!
//! let channel = Channel::from_url("https://feedpress.me/usererror.xml").unwrap();
//! ```
//!
//! # Writing
//!
//! A channel can be written to any object that implements the `Write` trait or converted to an
//! XML string using the `ToString` trait.
//!
//! **Note**: Writing a channel does not perform any escaping of XML entities.
//!
//! ## Example
//!
//! ```rust
//! use std::fs::File;
//! use std::io::{BufReader, sink};
//! use rss::Channel;
//!
//! let file = File::open("tests/data/rss2sample.xml").unwrap();
//! let reader = BufReader::new(file);
//! let channel = Channel::read_from(reader).unwrap();
//!
//! // write to the channel to a writer
//! channel.write_to(sink()).unwrap();
//!
//! // convert the channel to a string
//! let string = channel.to_string();
//! ```
//!
//! # Creation
//!
//! A channel can be created using the Builder functions.
//!
//! ## Example
//!
//! ```
//! use rss::{ChannelBuilder, ImageBuilder};
//!
//! let image = ImageBuilder::default()
//!     .url("http://jupiterbroadcasting.com/images/LAS-300-Badge.jpg")
//!     .title("LAS 300 Logo")
//!     .link("http://www.jupiterbroadcasting.com")
//!     .finalize();
//!
//! let channel = ChannelBuilder::default()
//!     .title("The Linux Action Show! OGG")
//!     .link("http://www.jupiterbroadcasting.com")
//!     .description("Ogg Vorbis audio versions of The Linux Action Show!")
//!     .image(image)
//!     .finalize();
//! ```
//!
//! # Validation
//!
//! Validation can be performed using either a `Channel` or a builder.
//!
//! The the following checks are performed during validation:
//!
//! * Ensures that integer properties can be parsed from their string representation into
//! integers
//! * Ensures that the integer properties are within their valid range according to the RSS 2.0
//! specification
//! * Ensures that URL properties can be parsed
//! * Ensures that string properties where only certain values are allowed fall within those
//! valid values
//!
//! ## Example
//!
//! ```
//! use rss::Channel;
//!
//! let input = include_str!("tests/data/rss2sample.xml");
//! let channel = input.parse::<Channel>().unwrap();
//! channel.validate().unwrap();
//! ```
//!
//! ## Example
//!
//! ```
//! use rss::ImageBuilder;
//!
//! let builder = ImageBuilder::default()
//!     .url("http://jupiterbroadcasting.com/images/LAS-300-Badge.jpg")
//!     .title("LAS 300 Logo")
//!     .link("http://www.jupiterbroadcasting.com")
//!     .validate()
//!     .unwrap();
//! ```
extern crate quick_xml;
extern crate chrono;
extern crate url;
extern crate mime;

#[cfg(feature = "from_url")]
extern crate reqwest;

mod fromxml;
mod toxml;

mod channel;
pub use channel::Channel;
pub use channel::ChannelBuilder;

mod item;
pub use item::Item;
pub use item::ItemBuilder;

mod category;
pub use category::Category;
pub use category::CategoryBuilder;

mod guid;
pub use guid::Guid;
pub use guid::GuidBuilder;

mod enclosure;
pub use enclosure::Enclosure;
pub use enclosure::EnclosureBuilder;

mod source;
pub use source::Source;
pub use source::SourceBuilder;

mod cloud;
pub use cloud::Cloud;
pub use cloud::CloudBuilder;

mod image;
pub use image::Image;
pub use image::ImageBuilder;

mod textinput;
pub use textinput::TextInput;
pub use textinput::TextInputBuilder;

/// Types and functions for namespaced extensions.
pub mod extension;
pub use extension::Extension;

mod error;
pub use error::Error;
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;
extern crate getopts;
extern crate unix_daemonize;
extern crate byteorder;
extern crate udt;
extern crate time;
extern crate sodiumoxide;
extern crate rustc_serialize;
extern crate colored;

pub mod connection;

use unix_daemonize::{daemonize_redirect, ChdirMode};
use std::process::Command;
use std::net::{SocketAddr, SocketAddrV4, IpAddr};
use std::str;
use std::env;
use std::thread;
use std::str::FromStr;
use std::fs::{OpenOptions, File};
use std::path::{Path, PathBuf};
use std::time::{Instant, Duration};
use std::io::{Cursor, Error, Seek, SeekFrom, stderr, Read, Write};
use std::sync::mpsc;
use log::{LogRecord, LogLevel, LogMetadata};
use colored::*;
use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian};
use sodiumoxide::crypto::secretbox;
use sodiumoxide::crypto::secretbox::xsalsa20poly1305::Key;
use rustc_serialize::hex::{FromHex, ToHex};

// TODO config
const INITIAL_ACCEPT_TIMEOUT_SECONDS: u64 = 60;

macro_rules! overprint {
    ($fmt: expr) => {
        print!(concat!("\x1b[2K\r", $fmt));
        std::io::stdout().flush().unwrap();
    };
    ($fmt:expr, $($arg:tt)*) => {
        print!(concat!("\x1b[2K\r", $fmt) , $($arg)*);
        std::io::stdout().flush().unwrap();
    };
}

#[macro_export]
macro_rules! die {
    ($fmt: expr) => {
        error!($fmt);
        panic!($fmt)
    };
    ($fmt:expr, $($arg:tt)*) => {
        error!($fmt, $($arg)*);
        panic!($fmt, $($arg)*)
    };
}

pub struct Server<'a> {
    pub ip: String,
    filename: &'a str,
    conn: connection::Server,
}

#[allow(dead_code)]
enum ShoopErrKind {
    Severed,
    Fatal,
}

struct ShoopErr {
    kind: ShoopErrKind,
    msg: Option<String>,
    finished: u64,
}

pub struct ShoopLogger;

impl log::Log for ShoopLogger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= LogLevel::Info
    }

    fn log(&self, record: &LogRecord) {
        if self.enabled(record.metadata()) {
            let prefix_symbol = match record.level() {
                LogLevel::Error => "E".red().bold(),
                LogLevel::Warn => "W".yellow().bold(),
                LogLevel::Info => "I".normal(),
                LogLevel::Debug => "D".dimmed(),
                LogLevel::Trace => "T".dimmed(),
            };

            println!("[{}] {}", prefix_symbol, record.args());
        }
    }
}

impl ShoopLogger {
    pub fn init() -> Result<(), log::SetLoggerError> {
        log::set_logger(|max_log_level| {
            max_log_level.set(log::LogLevelFilter::Info);
            Box::new(ShoopLogger)
        })
    }
}

impl ShoopErr {
    pub fn new(kind: ShoopErrKind, msg: &str, finished: u64) -> ShoopErr {
        ShoopErr {
            kind: kind,
            msg: Some(String::from(msg)),
            finished: finished,
        }
    }

    #[allow(dead_code)]
    pub fn from(err: Error, finished: u64) -> ShoopErr {
        ShoopErr {
            kind: ShoopErrKind::Severed,
            msg: Some(format!("{:?}", err)),
            finished: finished,
        }
    }
}

impl<'a> Server<'a> {
    pub fn new(port_range: connection::PortRange, filename: &str) -> Server {
        let sshconnstr = match env::var("SSH_CONNECTION") {
            Ok(s) => s.trim().to_owned(),
            Err(_) => {
                die!("SSH_CONNECTION env variable unset and required. Quitting.");
            }
        };
        let sshconn: Vec<&str> = sshconnstr.split(' ').collect();
        let ip = sshconn[2].to_owned();
        let key = secretbox::gen_key();
        let Key(keybytes) = key;
        let port = connection::Server::get_open_port(&port_range).unwrap();
        println!("shoop 0 {} {} {}", ip, port, keybytes.to_hex());

        let stdout = Some(Path::new(&env::var("HOME").unwrap()).join(".shoop.log"));
        let stderr = Some(Path::new(&env::var("HOME").unwrap()).join(".shoop.log"));
        daemonize_redirect(stdout, stderr, ChdirMode::ChdirRoot).unwrap();

        let conn = connection::Server::new(IpAddr::from_str(&ip).unwrap(), port, key);
        Server {
            ip: ip,
            conn: conn,
            filename: filename,
        }
    }

    pub fn start(&self) {
        self.conn.listen().unwrap();

        let mut connection_count: usize = 0;
        info!("listening...");
        loop {
            info!("waiting for connection...");
            let (tx, rx) = mpsc::channel();
            if connection_count == 0 {
                thread::spawn(move || {
                    thread::sleep(Duration::from_secs(INITIAL_ACCEPT_TIMEOUT_SECONDS));
                    if let Err(_) = rx.try_recv() {
                        error!("timed out waiting for initial connection. exiting.");
                        std::process::exit(1);
                    }
                });
            }
            let client = match self.conn.accept() {
                Ok(client) => client,
                Err(e) => {
                    die!("error on sock accept() {:?}", e);
                }
            };
            connection_count += 1;
            tx.send(()).unwrap();
            info!("accepted connection!");
            if let Ok(starthdr) = client.recv() {
                let version = starthdr[0];
                let mut rdr = Cursor::new(starthdr);
                rdr.set_position(1);
                let offset = rdr.read_u64::<LittleEndian>().unwrap();
                if version == 0x00 {
                    match self.send_file(&client, offset) {
                        Ok(_) => {
                            info!("done sending file");
                            let _ = client.close();
                            break;
                        }
                        Err(ShoopErr { kind: ShoopErrKind::Severed, msg, finished }) => {
                            info!("connection severed, msg: {:?}, finished: {}", msg, finished);
                            let _ = client.close();
                            continue;
                        }
                        Err(ShoopErr { kind: ShoopErrKind::Fatal, msg, finished }) => {
                            die!("connection fatal, msg: {:?}, finished: {}", msg, finished);
                        }
                    }
                } else {
                    die!("unrecognized version");
                }
            } else {
                die!("failed to receive version byte from client");
            }
        }
        info!("exiting listen loop.");
    }

    fn send_file(&self,
                 client: &connection::ServerConnection,
                 offset: u64)
                 -> Result<(), ShoopErr> {
        let mut f = File::open(self.filename).unwrap();
        f.seek(SeekFrom::Start(offset)).unwrap();
        let metadata = f.metadata().unwrap();

        let remaining = metadata.len() - offset;
        info!("total {} bytes", remaining);

        let mut wtr = vec![];
        wtr.write_u64::<LittleEndian>(remaining).unwrap();
        match client.send(&wtr[..]) {
            Ok(()) => info!("wrote filesize header."),
            Err(e) => {
                return Err(ShoopErr::new(ShoopErrKind::Severed, &format!("{:?}", e), remaining))
            }
        }
        let mut payload = vec![0; 1300];
        f.seek(SeekFrom::Start(offset)).unwrap();
        info!("sending file...");
        loop {
            match f.read(&mut payload) {
                Ok(0) => {
                    break;
                }
                Ok(read) => {
                    if let Err(e) = client.send(&payload[0..read]) {
                        return Err(ShoopErr::new(ShoopErrKind::Severed,
                                                 &format!("{:?}", e),
                                                 remaining));
                    }
                }
                Err(e) => {
                    client.close().expect("Error closing stream");
                    die!("failed to read from file: {:?}", e);
                }
            }
        }

        client.close().expect("Error closing stream.");
        Ok(())
    }
}

pub fn download(remote_ssh_host: &str,
                port_range: connection::PortRange,
                remote_path: &str,
                local_path: PathBuf) {
    let cmd = format!("shoop -s '{}' -p {}", remote_path, port_range);
    // println!("addr: {}, path: {}, cmd: {}", addr, path, cmd);

    overprint!(" - establishing SSH session...");
    assert!(command_exists("ssh"), "`ssh` is required!");
    let output = Command::new("ssh")
        .arg(remote_ssh_host.to_owned())
        .arg(cmd)
        .output()
        .unwrap_or_else(|e| {
            die!("failed to execute process: {}", e);
        });
    let infostring = String::from_utf8_lossy(&output.stdout).to_owned().trim().to_owned();
    let info: Vec<&str> = infostring.split(' ').collect();
    if info.len() != 5 {
        die!("Unexpected response from server. Are you suuuuure shoop is setup on the server?");
    }

    let (magic, version, ip, port, keyhex) = (info[0], info[1], info[2], info[3], info[4]);
    overprint!(" - opening UDT connection...");
    if magic != "shoop" || version != "0" {
        die!("Unexpected response from server. Are you suuuuure shoop is setup on the server?");
    }

    let mut keybytes = [0u8; 32];
    keybytes.copy_from_slice(&keyhex.from_hex().unwrap()[..]);
    let key = Key(keybytes);
    let addr: SocketAddr = SocketAddr::V4(SocketAddrV4::from_str(&format!("{}:{}", ip, port)[..])
        .unwrap());
    let conn = connection::Client::new(addr, key);

    let mut offset = 0u64;
    let mut filesize = None;
    let start_ts = Instant::now();
    loop {
        match conn.connect() {
            Ok(()) => {
                overprint!(" - connection opened, shakin' hands, makin' frands");
            }
            Err(e) => {
                die!("errrrrrrr connecting to {}:{} - {:?}", ip, port, e);
            }
        }
        let mut wtr = vec![];
        wtr.push(0);
        wtr.write_u64::<LittleEndian>(offset).unwrap();
        if let Err(_) = conn.send(&wtr[..]) {
            conn.close().unwrap();
            continue;
        }

        if let Ok(msg) = conn.recv() {
            if msg.is_empty() {
                die!("failed to get filesize from server, probable timeout.");
            }
            let mut rdr = Cursor::new(msg);
            filesize = filesize.or_else(|| Some(rdr.read_u64::<LittleEndian>().unwrap()));
            overprint!("downloading {} ({:.1}MB)\n",
                       local_path.to_string_lossy(),
                       (filesize.unwrap() as f64) / (1024f64 * 1024f64));
            match recv_file(&conn, filesize.unwrap(), &local_path, offset) {
                Ok(_) => {
                    break;
                }
                Err(ShoopErr { kind: ShoopErrKind::Severed, finished, .. }) => {
                    println!("{}", " * [[SEVERED]]".yellow().bold());
                    offset = finished;
                }
                Err(ShoopErr { kind: ShoopErrKind::Fatal, msg, .. }) => {
                    die!("{:?}", msg);
                }
            }
        }
        let _ = conn.close();
    }

    let elapsed = start_ts.elapsed().as_secs();
    let fmt_time = if elapsed < 60 {
        format!("{}s", elapsed)
    } else if elapsed < 60 * 60 {
        format!("{}m{}s", elapsed / 60, elapsed % 60)
    } else {
        format!("{}h{}m{}s", elapsed / (60 * 60), elapsed / 60, elapsed % 60)
    };
    println!("shooped it all up in {}", fmt_time.green().bold());
}

fn command_exists(command: &str) -> bool {
    match Command::new("which").arg(command).output() {
        Ok(output) => output.status.success(),
        Err(_) => false,
    }
}

fn recv_file(conn: &connection::Client,
             filesize: u64,
             filename: &PathBuf,
             offset: u64)
             -> Result<(), ShoopErr> {
    let mut f = OpenOptions::new().write(true).create(true).truncate(false).open(filename).unwrap();
    f.seek(SeekFrom::Start(offset)).unwrap();
    let start = Instant::now();
    let mut ts = Instant::now();
    let mut total = offset;
    let mut speed_ts = Instant::now();
    let mut speed_total = total;
    let mut speed = 0u64;
    loop {
        let buf = try!(conn.recv()
            .map_err(|e| ShoopErr::new(ShoopErrKind::Severed, &format!("{:?}", e), total)));
        if buf.len() < 1 {
            return Err(ShoopErr::new(ShoopErrKind::Severed, "empty msg", total));
        }

        f.write_all(&buf[..]).unwrap();
        total += buf.len() as u64;
        let speed_elapsed = speed_ts.elapsed();
        if speed_elapsed > Duration::new(1, 0) {
            speed = ((total - speed_total) as f64 /
                     ((speed_elapsed.as_secs() as f64) +
                      (speed_elapsed.subsec_nanos() as f64) /
                      1_000_000_000f64)) as u64;
            speed_ts = Instant::now();
            speed_total = total;
        }
        let speedfmt = if speed < 1024 {
            format!("{} b/s", speed)
        } else if speed < 1024 * 1024 {
            format!("{} kb/s", speed / 1024)
        } else {
            format!("{:.1} MB/s", ((speed / 1024) as f64) / 1024f64)
        };

        if ts.elapsed() > Duration::new(0, 100_000_000) {
            overprint!("   {:.1}M / {:.1}M ({:.1}%) [ {} ]",
                       (total as f64) / (1024f64 * 1024f64),
                       (filesize as f64) / (1024f64 * 1024f64),
                       (total as f64) / (filesize as f64) * 100f64,
                       speedfmt);
            ts = Instant::now();
        }
        if total >= filesize {
            overprint!("   {0:.1}M / {0:.1}M (100%) [ avg {1:.1} MB/s ]\n",
                       (filesize as f64) / (1024f64 * 1024f64),
                       ((total - offset) / start.elapsed().as_secs() / 1024) as f64 / 1024f64);
            break;
        }
    }
    let _ = conn.close();
    Ok(())
}
<commit_msg>remove any remnants forcing ipv4<commit_after>#[macro_use]
extern crate log;
extern crate getopts;
extern crate unix_daemonize;
extern crate byteorder;
extern crate udt;
extern crate time;
extern crate sodiumoxide;
extern crate rustc_serialize;
extern crate colored;

pub mod connection;

use unix_daemonize::{daemonize_redirect, ChdirMode};
use std::process::Command;
use std::net::{SocketAddr, IpAddr};
use std::str;
use std::env;
use std::thread;
use std::str::FromStr;
use std::fs::{OpenOptions, File};
use std::path::{Path, PathBuf};
use std::time::{Instant, Duration};
use std::io::{Cursor, Error, Seek, SeekFrom, stderr, Read, Write};
use std::sync::mpsc;
use log::{LogRecord, LogLevel, LogMetadata};
use colored::*;
use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian};
use sodiumoxide::crypto::secretbox;
use sodiumoxide::crypto::secretbox::xsalsa20poly1305::Key;
use rustc_serialize::hex::{FromHex, ToHex};

// TODO config
const INITIAL_ACCEPT_TIMEOUT_SECONDS: u64 = 60;

macro_rules! overprint {
    ($fmt: expr) => {
        print!(concat!("\x1b[2K\r", $fmt));
        std::io::stdout().flush().unwrap();
    };
    ($fmt:expr, $($arg:tt)*) => {
        print!(concat!("\x1b[2K\r", $fmt) , $($arg)*);
        std::io::stdout().flush().unwrap();
    };
}

#[macro_export]
macro_rules! die {
    ($fmt: expr) => {
        error!($fmt);
        panic!($fmt)
    };
    ($fmt:expr, $($arg:tt)*) => {
        error!($fmt, $($arg)*);
        panic!($fmt, $($arg)*)
    };
}

pub struct Server<'a> {
    pub ip: String,
    filename: &'a str,
    conn: connection::Server,
}

#[allow(dead_code)]
enum ShoopErrKind {
    Severed,
    Fatal,
}

struct ShoopErr {
    kind: ShoopErrKind,
    msg: Option<String>,
    finished: u64,
}

pub struct ShoopLogger;

impl log::Log for ShoopLogger {
    fn enabled(&self, metadata: &LogMetadata) -> bool {
        metadata.level() <= LogLevel::Info
    }

    fn log(&self, record: &LogRecord) {
        if self.enabled(record.metadata()) {
            let prefix_symbol = match record.level() {
                LogLevel::Error => "E".red().bold(),
                LogLevel::Warn => "W".yellow().bold(),
                LogLevel::Info => "I".normal(),
                LogLevel::Debug => "D".dimmed(),
                LogLevel::Trace => "T".dimmed(),
            };

            println!("[{}] {}", prefix_symbol, record.args());
        }
    }
}

impl ShoopLogger {
    pub fn init() -> Result<(), log::SetLoggerError> {
        log::set_logger(|max_log_level| {
            max_log_level.set(log::LogLevelFilter::Info);
            Box::new(ShoopLogger)
        })
    }
}

impl ShoopErr {
    pub fn new(kind: ShoopErrKind, msg: &str, finished: u64) -> ShoopErr {
        ShoopErr {
            kind: kind,
            msg: Some(String::from(msg)),
            finished: finished,
        }
    }

    #[allow(dead_code)]
    pub fn from(err: Error, finished: u64) -> ShoopErr {
        ShoopErr {
            kind: ShoopErrKind::Severed,
            msg: Some(format!("{:?}", err)),
            finished: finished,
        }
    }
}

impl<'a> Server<'a> {
    pub fn new(port_range: connection::PortRange, filename: &str) -> Server {
        let sshconnstr = match env::var("SSH_CONNECTION") {
            Ok(s) => s.trim().to_owned(),
            Err(_) => {
                die!("SSH_CONNECTION env variable unset and required. Quitting.");
            }
        };
        let sshconn: Vec<&str> = sshconnstr.split(' ').collect();
        let ip = sshconn[2].to_owned();
        let key = secretbox::gen_key();
        let Key(keybytes) = key;
        let port = connection::Server::get_open_port(&port_range).unwrap();
        println!("shoop 0 {} {} {}", ip, port, keybytes.to_hex());

        let stdout = Some(Path::new(&env::var("HOME").unwrap()).join(".shoop.log"));
        let stderr = Some(Path::new(&env::var("HOME").unwrap()).join(".shoop.log"));
        daemonize_redirect(stdout, stderr, ChdirMode::ChdirRoot).unwrap();

        let conn = connection::Server::new(IpAddr::from_str(&ip).unwrap(), port, key);
        Server {
            ip: ip,
            conn: conn,
            filename: filename,
        }
    }

    pub fn start(&self) {
        self.conn.listen().unwrap();

        let mut connection_count: usize = 0;
        info!("listening...");
        loop {
            info!("waiting for connection...");
            let (tx, rx) = mpsc::channel();
            if connection_count == 0 {
                thread::spawn(move || {
                    thread::sleep(Duration::from_secs(INITIAL_ACCEPT_TIMEOUT_SECONDS));
                    if let Err(_) = rx.try_recv() {
                        error!("timed out waiting for initial connection. exiting.");
                        std::process::exit(1);
                    }
                });
            }
            let client = match self.conn.accept() {
                Ok(client) => client,
                Err(e) => {
                    die!("error on sock accept() {:?}", e);
                }
            };
            connection_count += 1;
            tx.send(()).unwrap();
            info!("accepted connection!");
            if let Ok(starthdr) = client.recv() {
                let version = starthdr[0];
                let mut rdr = Cursor::new(starthdr);
                rdr.set_position(1);
                let offset = rdr.read_u64::<LittleEndian>().unwrap();
                if version == 0x00 {
                    match self.send_file(&client, offset) {
                        Ok(_) => {
                            info!("done sending file");
                            let _ = client.close();
                            break;
                        }
                        Err(ShoopErr { kind: ShoopErrKind::Severed, msg, finished }) => {
                            info!("connection severed, msg: {:?}, finished: {}", msg, finished);
                            let _ = client.close();
                            continue;
                        }
                        Err(ShoopErr { kind: ShoopErrKind::Fatal, msg, finished }) => {
                            die!("connection fatal, msg: {:?}, finished: {}", msg, finished);
                        }
                    }
                } else {
                    die!("unrecognized version");
                }
            } else {
                die!("failed to receive version byte from client");
            }
        }
        info!("exiting listen loop.");
    }

    fn send_file(&self,
                 client: &connection::ServerConnection,
                 offset: u64)
                 -> Result<(), ShoopErr> {
        let mut f = File::open(self.filename).unwrap();
        f.seek(SeekFrom::Start(offset)).unwrap();
        let metadata = f.metadata().unwrap();

        let remaining = metadata.len() - offset;
        info!("total {} bytes", remaining);

        let mut wtr = vec![];
        wtr.write_u64::<LittleEndian>(remaining).unwrap();
        match client.send(&wtr[..]) {
            Ok(()) => info!("wrote filesize header."),
            Err(e) => {
                return Err(ShoopErr::new(ShoopErrKind::Severed, &format!("{:?}", e), remaining))
            }
        }
        let mut payload = vec![0; 1300];
        f.seek(SeekFrom::Start(offset)).unwrap();
        info!("sending file...");
        loop {
            match f.read(&mut payload) {
                Ok(0) => {
                    break;
                }
                Ok(read) => {
                    if let Err(e) = client.send(&payload[0..read]) {
                        return Err(ShoopErr::new(ShoopErrKind::Severed,
                                                 &format!("{:?}", e),
                                                 remaining));
                    }
                }
                Err(e) => {
                    client.close().expect("Error closing stream");
                    die!("failed to read from file: {:?}", e);
                }
            }
        }

        client.close().expect("Error closing stream.");
        Ok(())
    }
}

pub fn download(remote_ssh_host: &str,
                port_range: connection::PortRange,
                remote_path: &str,
                local_path: PathBuf) {
    let cmd = format!("shoop -s '{}' -p {}", remote_path, port_range);
    // println!("addr: {}, path: {}, cmd: {}", addr, path, cmd);

    overprint!(" - establishing SSH session...");
    assert!(command_exists("ssh"), "`ssh` is required!");
    let output = Command::new("ssh")
        .arg(remote_ssh_host.to_owned())
        .arg(cmd)
        .output()
        .unwrap_or_else(|e| {
            die!("failed to execute process: {}", e);
        });
    let infostring = String::from_utf8_lossy(&output.stdout).to_owned().trim().to_owned();
    let info: Vec<&str> = infostring.split(' ').collect();
    if info.len() != 5 {
        die!("Unexpected response from server. Are you suuuuure shoop is setup on the server?");
    }

    let (magic, version, ip, port, keyhex) = (info[0], info[1], info[2], info[3], info[4]);
    overprint!(" - opening UDT connection...");
    if magic != "shoop" || version != "0" {
        die!("Unexpected response from server. Are you suuuuure shoop is setup on the server?");
    }

    let mut keybytes = [0u8; 32];
    keybytes.copy_from_slice(&keyhex.from_hex().unwrap()[..]);
    let key = Key(keybytes);
    let addr: SocketAddr = SocketAddr::from_str(&format!("{}:{}", ip, port)[..])
        .unwrap();
    let conn = connection::Client::new(addr, key);

    let mut offset = 0u64;
    let mut filesize = None;
    let start_ts = Instant::now();
    loop {
        match conn.connect() {
            Ok(()) => {
                overprint!(" - connection opened, shakin' hands, makin' frands");
            }
            Err(e) => {
                die!("errrrrrrr connecting to {}:{} - {:?}", ip, port, e);
            }
        }
        let mut wtr = vec![];
        wtr.push(0);
        wtr.write_u64::<LittleEndian>(offset).unwrap();
        if let Err(_) = conn.send(&wtr[..]) {
            conn.close().unwrap();
            continue;
        }

        if let Ok(msg) = conn.recv() {
            if msg.is_empty() {
                die!("failed to get filesize from server, probable timeout.");
            }
            let mut rdr = Cursor::new(msg);
            filesize = filesize.or_else(|| Some(rdr.read_u64::<LittleEndian>().unwrap()));
            overprint!("downloading {} ({:.1}MB)\n",
                       local_path.to_string_lossy(),
                       (filesize.unwrap() as f64) / (1024f64 * 1024f64));
            match recv_file(&conn, filesize.unwrap(), &local_path, offset) {
                Ok(_) => {
                    break;
                }
                Err(ShoopErr { kind: ShoopErrKind::Severed, finished, .. }) => {
                    println!("{}", " * [[SEVERED]]".yellow().bold());
                    offset = finished;
                }
                Err(ShoopErr { kind: ShoopErrKind::Fatal, msg, .. }) => {
                    die!("{:?}", msg);
                }
            }
        }
        let _ = conn.close();
    }

    let elapsed = start_ts.elapsed().as_secs();
    let fmt_time = if elapsed < 60 {
        format!("{}s", elapsed)
    } else if elapsed < 60 * 60 {
        format!("{}m{}s", elapsed / 60, elapsed % 60)
    } else {
        format!("{}h{}m{}s", elapsed / (60 * 60), elapsed / 60, elapsed % 60)
    };
    println!("shooped it all up in {}", fmt_time.green().bold());
}

fn command_exists(command: &str) -> bool {
    match Command::new("which").arg(command).output() {
        Ok(output) => output.status.success(),
        Err(_) => false,
    }
}

fn recv_file(conn: &connection::Client,
             filesize: u64,
             filename: &PathBuf,
             offset: u64)
             -> Result<(), ShoopErr> {
    let mut f = OpenOptions::new().write(true).create(true).truncate(false).open(filename).unwrap();
    f.seek(SeekFrom::Start(offset)).unwrap();
    let start = Instant::now();
    let mut ts = Instant::now();
    let mut total = offset;
    let mut speed_ts = Instant::now();
    let mut speed_total = total;
    let mut speed = 0u64;
    loop {
        let buf = try!(conn.recv()
            .map_err(|e| ShoopErr::new(ShoopErrKind::Severed, &format!("{:?}", e), total)));
        if buf.len() < 1 {
            return Err(ShoopErr::new(ShoopErrKind::Severed, "empty msg", total));
        }

        f.write_all(&buf[..]).unwrap();
        total += buf.len() as u64;
        let speed_elapsed = speed_ts.elapsed();
        if speed_elapsed > Duration::new(1, 0) {
            speed = ((total - speed_total) as f64 /
                     ((speed_elapsed.as_secs() as f64) +
                      (speed_elapsed.subsec_nanos() as f64) /
                      1_000_000_000f64)) as u64;
            speed_ts = Instant::now();
            speed_total = total;
        }
        let speedfmt = if speed < 1024 {
            format!("{} b/s", speed)
        } else if speed < 1024 * 1024 {
            format!("{} kb/s", speed / 1024)
        } else {
            format!("{:.1} MB/s", ((speed / 1024) as f64) / 1024f64)
        };

        if ts.elapsed() > Duration::new(0, 100_000_000) {
            overprint!("   {:.1}M / {:.1}M ({:.1}%) [ {} ]",
                       (total as f64) / (1024f64 * 1024f64),
                       (filesize as f64) / (1024f64 * 1024f64),
                       (total as f64) / (filesize as f64) * 100f64,
                       speedfmt);
            ts = Instant::now();
        }
        if total >= filesize {
            overprint!("   {0:.1}M / {0:.1}M (100%) [ avg {1:.1} MB/s ]\n",
                       (filesize as f64) / (1024f64 * 1024f64),
                       ((total - offset) / start.elapsed().as_secs() / 1024) as f64 / 1024f64);
            break;
        }
    }
    let _ = conn.close();
    Ok(())
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

extern crate byteorder;

use std::io;
use std::io::prelude::*;
use std::net::TcpStream;
use byteorder::{ByteOrder, LittleEndian};

#[derive(Debug)]
pub struct NtrStream {
    tcp_stream: TcpStream,
    current_seq: u32,
    is_heartbeat_sendable: bool,
}
impl NtrStream {
    pub fn connect(address: &str) -> io::Result<Self> {
        TcpStream::connect(&(address.to_owned() + ":8000") as &str)
            .map(|x| NtrStream {
                tcp_stream: x,
                current_seq: 1000,
                is_heartbeat_sendable: true,
            })
    }

    pub fn disconnect(self) {}

    pub fn send_packet(&mut self, packet_type: u32, cmd: u32, args: &Vec<u32>, data_len: u32) -> io::Result<usize> {
        let mut buf = [0u8; 84];

        LittleEndian::write_u32(&mut buf[0..4], 0x12345678);
        LittleEndian::write_u32(&mut buf[4..8], self.current_seq);
        LittleEndian::write_u32(&mut buf[8..12], packet_type);
        LittleEndian::write_u32(&mut buf[12..16], cmd);
        for i in 0..16 {
            LittleEndian::write_u32(&mut buf[(4 * i + 16)..(4 * i + 20)], args[i]);
        }
        LittleEndian::write_u32(&mut buf[80..84], data_len);

        self.current_seq += 1000;
        self.tcp_stream.write(&buf)
    }

    pub fn send_read_mem_packet(&mut self, addr: u32, size: u32, pid: u32) -> io::Result<usize> {
        self.send_empty_packet(9, pid, addr, size)
    }

    pub fn send_write_mem_packet(&mut self, addr: u32, pid: u32, buf: &Vec<u8>) -> io::Result<usize> {
        let args = &mut vec![0u32; 16];
        args[0] = pid;
        args[1] = addr;
        args[2] = buf.len() as u32;
        self.send_packet(1, 10, args, args[2])
            .and(self.tcp_stream.write(buf))
    }

    pub fn send_heartbeat_packet(&mut self) -> io::Result<usize> {
        self.send_packet(0, 0, &vec![0u32; 16], 0)
    }

    pub fn send_hello_packet(&mut self) -> io::Result<usize> {
        self.send_packet(0, 3, &vec![0u32; 16], 0)
    }

    pub fn send_reload_packet(&mut self) -> io::Result<usize> {
        self.send_packet(0, 4, &vec![0u32; 16], 0)
    }

    pub fn send_empty_packet(&mut self, cmd: u32, arg0: u32, arg1: u32, arg2: u32) -> io::Result<usize> {
        let args = &mut vec![0u32, 16];
        args[0] = arg0;
        args[1] = arg1;
        args[2] = arg2;
        self.send_packet(0, cmd, args, 0)
    }

    pub fn tcp_stream(&self) -> &TcpStream {
        &self.tcp_stream
    }
}<commit_msg>restructure; implement listening thread<commit_after>#![allow(dead_code)]
#![allow(unused_variables)]

#![feature(read_exact)]

extern crate byteorder;

use std::io;
use std::io::prelude::*;
use std::net::TcpStream;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::thread;
use byteorder::{ByteOrder, LittleEndian};

#[derive(Debug)]
pub struct Ntr {
    tcp_stream: TcpStream,
    current_seq: AtomicUsize,
    is_heartbeat_sendable: AtomicBool,
}
impl Ntr {
    pub fn connect(address: &str) -> io::Result<Arc<Self>> {
        let tcp_stream = try!(TcpStream::connect(&(address.to_owned() + ":8000") as &str));

        let ntr = Arc::new(Ntr {
            tcp_stream: tcp_stream,
            current_seq: AtomicUsize::new(1000),
            is_heartbeat_sendable: AtomicBool::new(true),
        });

        // spawn receiver thread
        {
            let ntr = ntr.clone();
            thread::spawn(move || {
                let mut ntr_stream = NtrStream::new(&ntr);
                let mut buf = [0u8; 84];
                loop {
                    if let Err(_) = ntr_stream.tcp_stream().read_exact(&mut buf) {
                        break;
                    }
                    let seq = LittleEndian::read_u32(&buf[4..8]);
                    let cmd = LittleEndian::read_u32(&buf[12..16]);
                    let data_len = LittleEndian::read_u32(&buf[80..84]) as usize;

                    if data_len != 0 {
                        let mut data_buf = Vec::with_capacity(data_len);
                        ntr_stream.tcp_stream().read_exact(&mut data_buf[0..data_len]).unwrap();
                        if cmd == 0 {
                            ntr_stream.set_heartbeat_sendable(true);
                        } else if cmd == 9 {
                            // TODO; handle read mem
                        }
                    }
                }
            });
        }

        // spawn heartbeat thread
        {
            let ntr = ntr.clone();
            thread::spawn(move || {
                let ntr_stream = NtrStream::new(&ntr);
                // TODO
            });
        }

        Ok(ntr)
    }

    pub fn disconnect(self) {}
}

#[derive(Debug)]
struct NtrStream<'a> {
    tcp_stream: TcpStream,
    current_seq: &'a AtomicUsize,
    is_heartbeat_sendable: &'a AtomicBool,
}
impl<'a> NtrStream<'a> {
    fn new(ntr: &'a Ntr) -> Self {
        NtrStream {
            tcp_stream: ntr.tcp_stream.try_clone().unwrap(),
            current_seq: &ntr.current_seq,
            is_heartbeat_sendable: &ntr.is_heartbeat_sendable,
        }
    }

    fn send_packet(&mut self, packet_type: u32, cmd: u32, args: &Vec<u32>, data_len: u32) -> io::Result<usize> {
        let mut buf = [0u8; 84];

        LittleEndian::write_u32(&mut buf[0..4], 0x12345678);
        LittleEndian::write_u32(&mut buf[4..8], self.current_seq.fetch_add(1000, Ordering::SeqCst) as u32);
        LittleEndian::write_u32(&mut buf[8..12], packet_type);
        LittleEndian::write_u32(&mut buf[12..16], cmd);
        for i in 0..16 {
            LittleEndian::write_u32(&mut buf[(4 * i + 16)..(4 * i + 20)], args[i]);
        }
        LittleEndian::write_u32(&mut buf[80..84], data_len);

        self.tcp_stream.write(&buf)
    }

    fn send_read_mem_packet(&mut self, addr: u32, size: u32, pid: u32) -> io::Result<usize> {
        self.send_empty_packet(9, pid, addr, size)
    }

    fn send_write_mem_packet(&mut self, addr: u32, pid: u32, buf: &Vec<u8>) -> io::Result<usize> {
        let args = &mut vec![0u32; 16];
        args[0] = pid;
        args[1] = addr;
        args[2] = buf.len() as u32;
        self.send_packet(1, 10, args, args[2])
            .and(self.tcp_stream.write(buf))
    }

    fn send_heartbeat_packet(&mut self) -> io::Result<usize> {
        self.send_packet(0, 0, &vec![0u32; 16], 0)
    }

    fn send_hello_packet(&mut self) -> io::Result<usize> {
        self.send_packet(0, 3, &vec![0u32; 16], 0)
    }

    fn send_reload_packet(&mut self) -> io::Result<usize> {
        self.send_packet(0, 4, &vec![0u32; 16], 0)
    }

    fn send_empty_packet(&mut self, cmd: u32, arg0: u32, arg1: u32, arg2: u32) -> io::Result<usize> {
        let args = &mut vec![0u32, 16];
        args[0] = arg0;
        args[1] = arg1;
        args[2] = arg2;
        self.send_packet(0, cmd, args, 0)
    }

    fn tcp_stream(&mut self) -> &TcpStream {
        &self.tcp_stream
    }

    fn set_heartbeat_sendable(&self, val: bool) {
        self.is_heartbeat_sendable.store(val, Ordering::SeqCst)
    }
}<|endoftext|>


<commit_before>#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum Param {
    Short(char),
    Long(String),
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
enum Value {
    Flag(bool),
    Opt(Option<String>),
}

pub trait IntoParam {
    fn into_param(self) -> Param;
}

impl IntoParam for &'static str {
    fn into_param(self) -> Param {
        Param::Long(self.to_owned())
    }
}

impl IntoParam for char {
    fn into_param(self) -> Param {
        Param::Short(self)
    }
}

use std::collections::HashMap;

/// Our homebrewed Arg Parser
#[derive(Clone, Debug, Default)]
pub struct ArgParser {
    params: HashMap<Param, Value>,
    pub args: Vec<String>,
}

impl ArgParser {
    /// Create a new ArgParser object
    ///
    /// The capacity specifies the initial capacity of the number parameters.
    /// Always good to set it at the number of flags and opts total.
    pub fn new(capacity: usize) -> Self {
        ArgParser {
            params: HashMap::with_capacity(capacity),
            args: Vec::new(),
        }
    }

    /// Builder method for adding both short and long flags
    ///
    /// Flags are just parameters that have no assigned values. They are used
    /// for when certain features or options have been enabled for the application
    ///
    /// For example
    /// > ls -l --human-readable
    ///   ^  ^  ^
    ///   |  |  |
    ///   |  |  `-- A long flag to enable human readable numbers.
    ///   |  `-- A short flag to enable the long format.
    ///   `-- The command to list files.
    pub fn add_flag(mut self, short: &str, long: &str) -> Self {
        if let Some(short) = short.chars().next() {
            self.params.insert(Param::Short(short), Value::Flag(false));
        }
        if !long.is_empty() {
            self.params.insert(Param::Long(long.to_owned()), Value::Flag(false));
        }
        self
    }

    /// Builder method for adding both short and long opts
    ///
    /// Opts are parameters that hold assigned values. They are used
    /// for when certain features or options have been enabled for the application
    ///
    /// For example
    /// > ls -T 4 --color=always
    ///   ^  ^    ^
    ///   |  |    |
    ///   |  |    `-- A long opt to enable the use of color with value `always`.
    ///   |  `-- A short opt to set tab size to the value `4`.
    ///   `-- The command to list files.
    pub fn add_opt(mut self, short: &str, long: &str) -> Self {
        if let Some(short) = short.chars().next() {
            self.params.insert(Param::Short(short), Value::Opt(None));
        }
        if !long.is_empty() {
            self.params.insert(Param::Long(long.to_owned()), Value::Opt(None));
        }
        self
    }

    /// Start parsing user inputted args for which flags and opts are used at
    /// runtime. The rest of the args that are not associated to opts get added
    /// to `ArgParser.args`.
    pub fn initialize<A: Iterator<Item=String>>(&mut self, args: A) {
        let mut args = args.skip(1);
        while let Some(mut arg) = args.next() {
            if arg.starts_with("--") {
                // Remove both dashes
                arg.remove(0);
                arg.remove(0);
                if let Some(i) = arg.find('=') {
                    let (lhs, rhs) = arg.split_at(i);
                    if let Some(&mut Value::Opt(Some(ref mut value))) = self.params.get_mut(&Param::Long(lhs.to_owned())) {
                        *value = rhs.to_owned();
                    }
                }
                else {
                    if let Some(&mut Value::Flag(ref mut switch)) = self.params.get_mut(&Param::Long(arg)) {
                        *switch = true;
                    }
                }
            }
            else if arg.starts_with("-") {
                for ch in arg[1..].chars() {
                    match self.params.get_mut(&Param::Short(ch)) {
                        Some(&mut Value::Flag(ref mut switch)) => *switch = true,
                        Some(&mut Value::Opt(ref mut value)) => *value = args.next(),
                        None => (),
                    }
                }
            }
            else {
                self.args.push(arg);
            }
        }
    }

    /// Check if a Flag or Opt has been found after initialization.
    pub fn flagged<P: IntoParam>(&self, name: P) -> bool {
        match self.params.get(&name.into_param()) {
            Some(&Value::Flag(switch)) => switch,
            Some(&Value::Opt(Some(_))) => true,
            _ => false,
        }
    }

    /// Modify the state of a flag. Use `true` if the flag is to be enabled. Use `false` to
    /// disable its use.
    pub fn set_flag<F: IntoParam>(&mut self, flag: F, state: bool) {
        if let Some(&mut Value::Flag(ref mut switch)) = self.params.get_mut(&flag.into_param()) {
            *switch = state;
        }
    }

    /// Modify the state value of an opt. Use `Some(String)` to set if the opt is to be enabled and
    /// has been assigned a value from `String`. Use `None` to disable the opt's use.
    pub fn set_opt<O: IntoParam>(&mut self, opt: O, state: Option<String>) {
        if let Some(&mut Value::Opt(ref mut value)) = self.params.get_mut(&opt.into_param()) {
            *value = state;
        }
    }

    /// Get the state of an Opt. If it has been enabled, it will return a `Some(String)` value
    /// otherwise it will return None.
    pub fn get_opt<O: IntoParam>(&self, opt: O) -> Option<String> {
        if let Some(&Value::Opt(ref value)) = self.params.get(&opt.into_param()) {
            return value.clone();
        }
        None
    }
}

pub fn to_human_readable_string(size: u64) -> String {
    if size < 1024 {
        return format!("{}", size);
    }

    static UNITS: [&'static str; 7] = ["", "K", "M", "G", "T", "P", "E"];

    let sizef = size as f64;
    let digit_groups = (sizef.log10() / 1024f64.log10()) as i32;
    format!("{:.1}{}",
            sizef / 1024f64.powf(digit_groups as f64),
            UNITS[digit_groups as usize])
}
<commit_msg>Borrow Param as &str when its a long, for hashmap keys<commit_after>#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Param {
    Short(char),
    Long(String),
}

use std::borrow::Borrow;

impl Borrow<str> for Param {
    fn borrow(&self) -> &str {
        if let Param::Long(ref string) = *self {
            &*string
        } else {
            ""
        }
    }
}

use std::hash::{Hash,Hasher};

impl Hash for Param {
    fn hash<H: Hasher>(&self, state: &mut H){
        match *self {
            Param::Short(ref c) => c.hash(state),
            Param::Long(ref s) => s.hash(state)
        }
    }
}

#[derive(Clone, Debug, Eq, PartialEq, Hash)]
enum Value {
    Flag(bool),
    Opt(Option<String>),
}

pub trait IntoParam {
    fn into_param(self) -> Param;
}

impl IntoParam for &'static str {
    fn into_param(self) -> Param {
        Param::Long(self.to_owned())
    }
}

impl IntoParam for char {
    fn into_param(self) -> Param {
        Param::Short(self)
    }
}

use std::collections::HashMap;

/// Our homebrewed Arg Parser
#[derive(Clone, Debug, Default)]
pub struct ArgParser {
    params: HashMap<Param, Value>,
    pub args: Vec<String>,
}

impl ArgParser {
    /// Create a new ArgParser object
    ///
    /// The capacity specifies the initial capacity of the number parameters.
    /// Always good to set it at the number of flags and opts total.
    pub fn new(capacity: usize) -> Self {
        ArgParser {
            params: HashMap::with_capacity(capacity),
            args: Vec::new(),
        }
    }

    /// Builder method for adding both short and long flags
    ///
    /// Flags are just parameters that have no assigned values. They are used
    /// for when certain features or options have been enabled for the application
    ///
    /// For example
    /// > ls -l --human-readable
    ///   ^  ^  ^
    ///   |  |  |
    ///   |  |  `-- A long flag to enable human readable numbers.
    ///   |  `-- A short flag to enable the long format.
    ///   `-- The command to list files.
    pub fn add_flag(mut self, short: &str, long: &str) -> Self {
        if let Some(short) = short.chars().next() {
            self.params.insert(Param::Short(short), Value::Flag(false));
        }
        if !long.is_empty() {
            self.params.insert(Param::Long(long.to_owned()), Value::Flag(false));
        }
        self
    }

    /// Builder method for adding both short and long opts
    ///
    /// Opts are parameters that hold assigned values. They are used
    /// for when certain features or options have been enabled for the application
    ///
    /// For example
    /// > ls -T 4 --color=always
    ///   ^  ^    ^
    ///   |  |    |
    ///   |  |    `-- A long opt to enable the use of color with value `always`.
    ///   |  `-- A short opt to set tab size to the value `4`.
    ///   `-- The command to list files.
    pub fn add_opt(mut self, short: &str, long: &str) -> Self {
        if let Some(short) = short.chars().next() {
            self.params.insert(Param::Short(short), Value::Opt(None));
        }
        if !long.is_empty() {
            self.params.insert(Param::Long(long.to_owned()), Value::Opt(None));
        }
        self
    }

    /// Start parsing user inputted args for which flags and opts are used at
    /// runtime. The rest of the args that are not associated to opts get added
    /// to `ArgParser.args`.
    pub fn initialize<A: Iterator<Item=String>>(&mut self, args: A) {
        let mut args = args.skip(1);
        while let Some(mut arg) = args.next() {
            if arg.starts_with("--") {
                // Remove both dashes
                let arg = &arg[2..];
                if let Some(i) = arg.find('=') {
                    let (lhs, rhs) = arg.split_at(i);
                    if let Some(&mut Value::Opt(Some(ref mut value))) = self.params.get_mut(lhs) {
                        *value = rhs.to_owned();
                    }
                }
                else {
                    if let Some(&mut Value::Flag(ref mut switch)) = self.params.get_mut(arg) {
                        *switch = true;
                    }
                }
            }
            else if arg.starts_with("-") {
                for ch in arg[1..].chars() {
                    match self.params.get_mut(&Param::Short(ch)) {
                        Some(&mut Value::Flag(ref mut switch)) => *switch = true,
                        Some(&mut Value::Opt(ref mut value)) => *value = args.next(),
                        None => (),
                    }
                }
            }
            else {
                self.args.push(arg);
            }
        }
    }

    /// Check if a Flag or Opt has been found after initialization.
    pub fn flagged<P: IntoParam>(&self, name: P) -> bool {
        match self.params.get(&name.into_param()) {
            Some(&Value::Flag(switch)) => switch,
            Some(&Value::Opt(Some(_))) => true,
            _ => false,
        }
    }

    /// Modify the state of a flag. Use `true` if the flag is to be enabled. Use `false` to
    /// disable its use.
    pub fn set_flag<F: IntoParam>(&mut self, flag: F, state: bool) {
        if let Some(&mut Value::Flag(ref mut switch)) = self.params.get_mut(&flag.into_param()) {
            *switch = state;
        }
    }

    /// Modify the state value of an opt. Use `Some(String)` to set if the opt is to be enabled and
    /// has been assigned a value from `String`. Use `None` to disable the opt's use.
    pub fn set_opt<O: IntoParam>(&mut self, opt: O, state: Option<String>) {
        if let Some(&mut Value::Opt(ref mut value)) = self.params.get_mut(&opt.into_param()) {
            *value = state;
        }
    }

    /// Get the state of an Opt. If it has been enabled, it will return a `Some(String)` value
    /// otherwise it will return None.
    pub fn get_opt<O: IntoParam>(&self, opt: O) -> Option<String> {
        if let Some(&Value::Opt(ref value)) = self.params.get(&opt.into_param()) {
            return value.clone();
        }
        None
    }
}

pub fn to_human_readable_string(size: u64) -> String {
    if size < 1024 {
        return format!("{}", size);
    }

    static UNITS: [&'static str; 7] = ["", "K", "M", "G", "T", "P", "E"];

    let sizef = size as f64;
    let digit_groups = (sizef.log10() / 1024f64.log10()) as i32;
    format!("{:.1}{}",
            sizef / 1024f64.powf(digit_groups as f64),
            UNITS[digit_groups as usize])
}
<|endoftext|>


<commit_before>extern crate num_traits;

#[macro_use]
mod macros;
pub mod traitdefs;

mod nbez;
pub use nbez::*;

use traitdefs::Float;
use std::fmt::{Debug, Formatter};
use std::marker::PhantomData;

#[inline]
fn lerp<F: Float>(a: F, b: F, factor: F) -> F {
    let fact1 = F::from_f32(1.0).unwrap() - factor;
    a * fact1 + b * factor
}

// There are macros in place to make it easier to create new bezier structs, as they can be created
// with a very consistent pattern. However, those macros are also written in a very consistent pattern
// which unfortunately is significantly harder, if not impossible, to create with a traditional
// macro. So, the macro invocations are generated with the build script and then inserted here.
include!(concat!(env!("OUT_DIR"), "/macro_invocs.rs"));

impl<F: Float> Vector2d<F> {
    pub fn perp(self) -> Vector2d<F> {
        Vector2d {
            x: -self.y,
            y: self.x
        }
    }
}

/// Iterator over bezier curve chains
pub struct BezIter<'a, F, B>
        where F: Float,
              B: BezCurve<F> + OrderStatic {
    points: *const B::Point,
    len: usize,
    lifetime: PhantomData<&'a ()>
}

impl<'a, F, B> Iterator for BezIter<'a, F, B>
        where F: Float,
              B: BezCurve<F> + OrderStatic {
    type Item = B;
    fn next(&mut self) -> Option<B> {
        use std::slice;

        let order = B::order_static();

        if self.len <= order {
            None
        } else {unsafe{
            let slice = slice::from_raw_parts(self.points, order + 1);
            self.points = self.points.offset(order as isize);
            self.len -= order;
            B::from_slice(slice)
        }}
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let order = B::order_static();
        let size = (self.len - (self.len - 1) % order) / order;
        (size, Some(size))
    }
}

impl<'a, F, B> DoubleEndedIterator for BezIter<'a, F, B>
        where F: Float,
              B: BezCurve<F> + OrderStatic {
    fn next_back(&mut self) -> Option<B> {
        use std::slice;

        let order = B::order_static();

        // If there are any control points in the iterator that can't be used to create a full curve,
        // ignore them.
        let end = self.len - (self.len - 1) % order;
        if end <= order {
            None
        } else {unsafe{
            let slice = slice::from_raw_parts(self.points.offset((end-order-1) as isize), order + 1);
            self.len -= order;
            B::from_slice(slice)
        }}
    }
}

impl<'a, F, B> ExactSizeIterator for BezIter<'a, F, B>
        where F: Float,
              B: BezCurve<F> + OrderStatic {}

/// Bezier curve trait
pub trait BezCurve<F: Float>
        where Self: Sized {
    type Point;
    type Vector;
    type Elevated: BezCurve<F, Point = Self::Point, Vector = Self::Vector>;

    /// Attempt to create a curve from a slice. Fails if the slice's length does not match the
    /// curve's order + 1.
    fn from_slice(&[Self::Point]) -> Option<Self>;

    /// Perform interpolation on the curve for a value of `t` from `0.0` to `1.0` inclusive. Returns `None`
    /// if `t` is outside of that range.
    fn interp(&self, t: F) -> Option<Self::Point> {
        check_t_bounds!(t);
        Some(self.interp_unbounded(t))
    }
    /// Perform interpolation on the curve with no range bounds
    fn interp_unbounded(&self, t: F) -> Self::Point;

    /// Get the slope for the given `t`, bounded on `0.0` to `1.0` inclusive. Returns `None` if
    /// `t` is not within bounds.
    fn slope(&self, t: F) -> Option<Self::Vector> {
        check_t_bounds!(t);
        Some(self.slope_unbounded(t))
    }
    /// Get the slope for the given `t` with no range bounds
    fn slope_unbounded(&self, t: F) -> Self::Vector;

    /// Elevate the curve order, getting a curve that is one order higher but gives the same results
    /// upon interpolation
    fn elevate(&self) -> Self::Elevated;

    /// Split the curve at the given `t`, bounded on `0.0` to `1.0` inclusive. Returns `None` if `t` is
    /// not within bounds.
    fn split(&self, t: F) -> Option<(Self, Self)>;

    /// Gets the order of the curve
    fn order(&self) -> usize;
}

/// Gets the statically-known order of the curve
pub trait OrderStatic {
    fn order_static() -> usize;
}

/// A chain on bezier curves, with the last point of each curve being the first point of the next.
#[derive(Clone, Copy)]
pub struct BezChain<F, B, C>
        where F: Float,
              B: BezCurve<F> + OrderStatic,
              C: AsRef<[B::Point]> {
    points: C,
    phantom: PhantomData<(F, B)>
}

impl<F, B, C> BezChain<F, B, C>
        where F: Float,
              B: BezCurve<F> + OrderStatic,
              C: AsRef<[B::Point]> {
    /// Create a new BezChain by wrapping around a container.
    #[inline]
    pub fn from_container(container: C) -> BezChain<F, B, C> {
        BezChain {
            points: container,
            phantom: PhantomData
        }
    }

    /// Get the bezier curve that is `index` curves away from the start. Returns `None` if not enough
    /// points exist for the given curve index.
    pub fn get(&self, index: usize) -> Option<B> {
        let len = self.points.as_ref().len();
        let order = B::order_static();
        let curve_index = index * order;
        let curve_end_index = index * order + order + 1;

        if curve_end_index > len - (len - 1) % order {
            None
        } else {
            Some(B::from_slice(&self.points.as_ref()[curve_index..curve_end_index]).unwrap())
        }
    }

    /// Get an iterator over all curves in the chain.
    #[inline]
    pub fn iter(&self) -> BezIter<F, B> {
        BezIter {
            points: self.points.as_ref().as_ptr(),
            len: self.points.as_ref().len(),
            lifetime: PhantomData
        }
    }

    /// Get the order of the chain's curves. Identical to order_static().
    #[inline]
    pub fn order(&self) -> usize {
        B::order_static()
    }

    /// Unwrap the chain, returning the underlying container.
    #[inline]
    pub fn unwrap(self) -> C {
        self.points
    }
}

impl<F, B, C> OrderStatic for BezChain<F, B, C>
        where F: Float,
              B: BezCurve<F> + OrderStatic,
              C: AsRef<[B::Point]> {
    #[inline]
    fn order_static() -> usize {
        B::order_static()
    }
}

impl<F, B, C> AsRef<C> for BezChain<F, B, C>
        where F: Float,
              B: BezCurve<F> + OrderStatic,
              C: AsRef<[B::Point]> {
    fn as_ref(&self) -> &C {
        &self.points
    }
}

impl<F, B, C> AsMut<C> for BezChain<F, B, C>
        where F: Float,
              B: BezCurve<F> + OrderStatic,
              C: AsRef<[B::Point]> {
    fn as_mut(&mut self) -> &mut C {
        &mut self.points
    }
}

impl<F, B, C> Debug for BezChain<F, B, C>
        where F: Float,
              B: BezCurve<F> + OrderStatic,
              C: AsRef<[B::Point]> + Debug {
    fn fmt(&self, f: &mut Formatter) -> Result<(), ::std::fmt::Error> {
        f.debug_tuple("BezChain")
            .field(&self.points)
            .finish()
    }
}

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

    fn test_poly_eq<B1, B2>(nbez_poly: &B1, bez_poly: &B2)
            where B1: BezCurve<f64, Point = f64>,
                  B2: BezCurve<f64, Point = f64> {
        let mut t = 0.0;
        while t <= 1.0 {
            // Assert that values are equal within a tolerance.
            assert!((nbez_poly.interp(t).unwrap() - bez_poly.interp(t).unwrap()).abs() <= 0.000000001);
            t += 1.0/30.0;
        }
    }

    fn test_poly_slope_eq<B1, B2>(nbez_poly: &B1, bez_poly: &B2)
            where B1: BezCurve<f64, Vector = f64>,
                  B2: BezCurve<f64, Vector = f64> {
        let mut t = 0.0;
        while t <= 1.0 {
            // Ditto.
            assert!((nbez_poly.slope(t).unwrap() - bez_poly.slope(t).unwrap()).abs() <= 0.000000001);
            t += 1.0/30.0;
        }
    }

    #[test]
    fn bez_poly_equiviliance() {
        let mut nbez_poly = NBezPoly::from_container(Vec::with_capacity(7));
        nbez_poly.as_mut().push(0.0);
        nbez_poly.as_mut().push(1.0);

        let bezpoly1o = BezPoly1o::new(0.0, 1.0);
        test_poly_eq(&nbez_poly, &bezpoly1o);
        test_poly_slope_eq(&nbez_poly, &bezpoly1o);

        nbez_poly.as_mut().push(-1.0);
        let bezpoly2o = BezPoly2o::new(0.0, 1.0, -1.0);
        test_poly_eq(&nbez_poly, &bezpoly2o);
        test_poly_slope_eq(&nbez_poly, &bezpoly2o);

        nbez_poly.as_mut().push(2.0);
        let bezpoly3o = BezPoly3o::new(0.0, 1.0, -1.0, 2.0);
        test_poly_eq(&nbez_poly, &bezpoly3o);
        test_poly_slope_eq(&nbez_poly, &bezpoly3o);

        nbez_poly.as_mut().push(-2.0);
        let bezpoly4o = BezPoly4o::new(0.0, 1.0, -1.0, 2.0, -2.0);
        test_poly_eq(&nbez_poly, &bezpoly4o);
        test_poly_slope_eq(&nbez_poly, &bezpoly4o);

        nbez_poly.as_mut().push(3.0);
        let bezpoly5o = BezPoly5o::new(0.0, 1.0, -1.0, 2.0, -2.0, 3.0);
        test_poly_eq(&nbez_poly, &bezpoly5o);
        test_poly_slope_eq(&nbez_poly, &bezpoly5o);

        nbez_poly.as_mut().push(-3.0);
        let bezpoly6o = BezPoly6o::new(0.0, 1.0, -1.0, 2.0, -2.0, 3.0, -3.0);
        test_poly_eq(&nbez_poly, &bezpoly6o);
        test_poly_slope_eq(&nbez_poly, &bezpoly6o);
    }

    fn test_bez_elevation<B>(curve: &B)
            where B: BezCurve<f64, Point = f64, Vector = f64> {

        let elevated = curve.elevate();
        test_poly_eq(curve, &elevated);
        test_poly_slope_eq(curve, &elevated);
    }

    #[test]
    fn bez_elevation() {
        test_bez_elevation(&BezPoly1o::new(0.0, 1.0));
        test_bez_elevation(&BezPoly2o::new(0.0, 1.0, -1.0));
        test_bez_elevation(&BezPoly3o::new(0.0, 1.0, -1.0, 2.0));
        test_bez_elevation(&BezPoly4o::new(0.0, 1.0, -1.0, 2.0, -2.0));
        test_bez_elevation(&BezPoly5o::new(0.0, 1.0, -1.0, 2.0, -2.0, 3.0));
        test_bez_elevation(&BezPoly6o::new(0.0, 1.0, -1.0, 2.0, -2.0, 3.0, -3.0));
    }

    #[test]
    fn nbez_elevation() {
        let mut nbez_poly = NBezPoly::from_container(Vec::with_capacity(7));
        nbez_poly.as_mut().push(0.0);
        nbez_poly.as_mut().push(1.0);

        test_bez_elevation(&nbez_poly);

        nbez_poly.as_mut().push(-1.0);
        test_bez_elevation(&nbez_poly);

        nbez_poly.as_mut().push(2.0);
        test_bez_elevation(&nbez_poly);

        nbez_poly.as_mut().push(-2.0);
        test_bez_elevation(&nbez_poly);

        nbez_poly.as_mut().push(3.0);
        test_bez_elevation(&nbez_poly);

        nbez_poly.as_mut().push(-3.0);
        test_bez_elevation(&nbez_poly);
    }
}<commit_msg>Added bezier splitting test<commit_after>extern crate num_traits;

#[macro_use]
mod macros;
pub mod traitdefs;

mod nbez;
pub use nbez::*;

use traitdefs::Float;
use std::fmt::{Debug, Formatter};
use std::marker::PhantomData;

#[inline]
fn lerp<F: Float>(a: F, b: F, factor: F) -> F {
    let fact1 = F::from_f32(1.0).unwrap() - factor;
    a * fact1 + b * factor
}

// There are macros in place to make it easier to create new bezier structs, as they can be created
// with a very consistent pattern. However, those macros are also written in a very consistent pattern
// which unfortunately is significantly harder, if not impossible, to create with a traditional
// macro. So, the macro invocations are generated with the build script and then inserted here.
include!(concat!(env!("OUT_DIR"), "/macro_invocs.rs"));

impl<F: Float> Vector2d<F> {
    pub fn perp(self) -> Vector2d<F> {
        Vector2d {
            x: -self.y,
            y: self.x
        }
    }
}

/// Iterator over bezier curve chains
pub struct BezIter<'a, F, B>
        where F: Float,
              B: BezCurve<F> + OrderStatic {
    points: *const B::Point,
    len: usize,
    lifetime: PhantomData<&'a ()>
}

impl<'a, F, B> Iterator for BezIter<'a, F, B>
        where F: Float,
              B: BezCurve<F> + OrderStatic {
    type Item = B;
    fn next(&mut self) -> Option<B> {
        use std::slice;

        let order = B::order_static();

        if self.len <= order {
            None
        } else {unsafe{
            let slice = slice::from_raw_parts(self.points, order + 1);
            self.points = self.points.offset(order as isize);
            self.len -= order;
            B::from_slice(slice)
        }}
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let order = B::order_static();
        let size = (self.len - (self.len - 1) % order) / order;
        (size, Some(size))
    }
}

impl<'a, F, B> DoubleEndedIterator for BezIter<'a, F, B>
        where F: Float,
              B: BezCurve<F> + OrderStatic {
    fn next_back(&mut self) -> Option<B> {
        use std::slice;

        let order = B::order_static();

        // If there are any control points in the iterator that can't be used to create a full curve,
        // ignore them.
        let end = self.len - (self.len - 1) % order;
        if end <= order {
            None
        } else {unsafe{
            let slice = slice::from_raw_parts(self.points.offset((end-order-1) as isize), order + 1);
            self.len -= order;
            B::from_slice(slice)
        }}
    }
}

impl<'a, F, B> ExactSizeIterator for BezIter<'a, F, B>
        where F: Float,
              B: BezCurve<F> + OrderStatic {}

/// Bezier curve trait
pub trait BezCurve<F: Float>
        where Self: Sized {
    type Point;
    type Vector;
    type Elevated: BezCurve<F, Point = Self::Point, Vector = Self::Vector>;

    /// Attempt to create a curve from a slice. Fails if the slice's length does not match the
    /// curve's order + 1.
    fn from_slice(&[Self::Point]) -> Option<Self>;

    /// Perform interpolation on the curve for a value of `t` from `0.0` to `1.0` inclusive. Returns `None`
    /// if `t` is outside of that range.
    fn interp(&self, t: F) -> Option<Self::Point> {
        check_t_bounds!(t);
        Some(self.interp_unbounded(t))
    }
    /// Perform interpolation on the curve with no range bounds
    fn interp_unbounded(&self, t: F) -> Self::Point;

    /// Get the slope for the given `t`, bounded on `0.0` to `1.0` inclusive. Returns `None` if
    /// `t` is not within bounds.
    fn slope(&self, t: F) -> Option<Self::Vector> {
        check_t_bounds!(t);
        Some(self.slope_unbounded(t))
    }
    /// Get the slope for the given `t` with no range bounds
    fn slope_unbounded(&self, t: F) -> Self::Vector;

    /// Elevate the curve order, getting a curve that is one order higher but gives the same results
    /// upon interpolation
    fn elevate(&self) -> Self::Elevated;

    /// Split the curve at the given `t`, bounded on `0.0` to `1.0` inclusive. Returns `None` if `t` is
    /// not within bounds.
    fn split(&self, t: F) -> Option<(Self, Self)>;

    /// Gets the order of the curve
    fn order(&self) -> usize;
}

/// Gets the statically-known order of the curve
pub trait OrderStatic {
    fn order_static() -> usize;
}

/// A chain on bezier curves, with the last point of each curve being the first point of the next.
#[derive(Clone, Copy)]
pub struct BezChain<F, B, C>
        where F: Float,
              B: BezCurve<F> + OrderStatic,
              C: AsRef<[B::Point]> {
    points: C,
    phantom: PhantomData<(F, B)>
}

impl<F, B, C> BezChain<F, B, C>
        where F: Float,
              B: BezCurve<F> + OrderStatic,
              C: AsRef<[B::Point]> {
    /// Create a new BezChain by wrapping around a container.
    #[inline]
    pub fn from_container(container: C) -> BezChain<F, B, C> {
        BezChain {
            points: container,
            phantom: PhantomData
        }
    }

    /// Get the bezier curve that is `index` curves away from the start. Returns `None` if not enough
    /// points exist for the given curve index.
    pub fn get(&self, index: usize) -> Option<B> {
        let len = self.points.as_ref().len();
        let order = B::order_static();
        let curve_index = index * order;
        let curve_end_index = index * order + order + 1;

        if curve_end_index > len - (len - 1) % order {
            None
        } else {
            Some(B::from_slice(&self.points.as_ref()[curve_index..curve_end_index]).unwrap())
        }
    }

    /// Get an iterator over all curves in the chain.
    #[inline]
    pub fn iter(&self) -> BezIter<F, B> {
        BezIter {
            points: self.points.as_ref().as_ptr(),
            len: self.points.as_ref().len(),
            lifetime: PhantomData
        }
    }

    /// Get the order of the chain's curves. Identical to order_static().
    #[inline]
    pub fn order(&self) -> usize {
        B::order_static()
    }

    /// Unwrap the chain, returning the underlying container.
    #[inline]
    pub fn unwrap(self) -> C {
        self.points
    }
}

impl<F, B, C> OrderStatic for BezChain<F, B, C>
        where F: Float,
              B: BezCurve<F> + OrderStatic,
              C: AsRef<[B::Point]> {
    #[inline]
    fn order_static() -> usize {
        B::order_static()
    }
}

impl<F, B, C> AsRef<C> for BezChain<F, B, C>
        where F: Float,
              B: BezCurve<F> + OrderStatic,
              C: AsRef<[B::Point]> {
    fn as_ref(&self) -> &C {
        &self.points
    }
}

impl<F, B, C> AsMut<C> for BezChain<F, B, C>
        where F: Float,
              B: BezCurve<F> + OrderStatic,
              C: AsRef<[B::Point]> {
    fn as_mut(&mut self) -> &mut C {
        &mut self.points
    }
}

impl<F, B, C> Debug for BezChain<F, B, C>
        where F: Float,
              B: BezCurve<F> + OrderStatic,
              C: AsRef<[B::Point]> + Debug {
    fn fmt(&self, f: &mut Formatter) -> Result<(), ::std::fmt::Error> {
        f.debug_tuple("BezChain")
            .field(&self.points)
            .finish()
    }
}

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

    fn test_poly_eq<B1, B2>(nbez_poly: &B1, bez_poly: &B2)
            where B1: BezCurve<f64, Point = f64>,
                  B2: BezCurve<f64, Point = f64> {
        let mut t = 0.0;
        while t <= 1.0 {
            // Assert that values are equal within a tolerance.
            assert!((nbez_poly.interp(t).unwrap() - bez_poly.interp(t).unwrap()).abs() <= 0.000000001);
            t += 1.0/30.0;
        }
    }

    fn test_poly_slope_eq<B1, B2>(nbez_poly: &B1, bez_poly: &B2)
            where B1: BezCurve<f64, Vector = f64>,
                  B2: BezCurve<f64, Vector = f64> {
        let mut t = 0.0;
        while t <= 1.0 {
            // Ditto.
            assert!((nbez_poly.slope(t).unwrap() - bez_poly.slope(t).unwrap()).abs() <= 0.000000001);
            t += 1.0/30.0;
        }
    }

    #[test]
    fn bez_poly_equiviliance() {
        let mut nbez_poly = NBezPoly::from_container(Vec::with_capacity(7));
        nbez_poly.as_mut().push(0.0);
        nbez_poly.as_mut().push(1.0);

        let bezpoly1o = BezPoly1o::new(0.0, 1.0);
        test_poly_eq(&nbez_poly, &bezpoly1o);
        test_poly_slope_eq(&nbez_poly, &bezpoly1o);

        nbez_poly.as_mut().push(-1.0);
        let bezpoly2o = BezPoly2o::new(0.0, 1.0, -1.0);
        test_poly_eq(&nbez_poly, &bezpoly2o);
        test_poly_slope_eq(&nbez_poly, &bezpoly2o);

        nbez_poly.as_mut().push(2.0);
        let bezpoly3o = BezPoly3o::new(0.0, 1.0, -1.0, 2.0);
        test_poly_eq(&nbez_poly, &bezpoly3o);
        test_poly_slope_eq(&nbez_poly, &bezpoly3o);

        nbez_poly.as_mut().push(-2.0);
        let bezpoly4o = BezPoly4o::new(0.0, 1.0, -1.0, 2.0, -2.0);
        test_poly_eq(&nbez_poly, &bezpoly4o);
        test_poly_slope_eq(&nbez_poly, &bezpoly4o);

        nbez_poly.as_mut().push(3.0);
        let bezpoly5o = BezPoly5o::new(0.0, 1.0, -1.0, 2.0, -2.0, 3.0);
        test_poly_eq(&nbez_poly, &bezpoly5o);
        test_poly_slope_eq(&nbez_poly, &bezpoly5o);

        nbez_poly.as_mut().push(-3.0);
        let bezpoly6o = BezPoly6o::new(0.0, 1.0, -1.0, 2.0, -2.0, 3.0, -3.0);
        test_poly_eq(&nbez_poly, &bezpoly6o);
        test_poly_slope_eq(&nbez_poly, &bezpoly6o);
    }

    fn test_bez_elevation<B>(curve: &B)
            where B: BezCurve<f64, Point = f64, Vector = f64> {

        let elevated = curve.elevate();
        test_poly_eq(curve, &elevated);
        test_poly_slope_eq(curve, &elevated);
    }

    #[test]
    fn bez_elevation() {
        test_bez_elevation(&BezPoly1o::new(0.0, 1.0));
        test_bez_elevation(&BezPoly2o::new(0.0, 1.0, -1.0));
        test_bez_elevation(&BezPoly3o::new(0.0, 1.0, -1.0, 2.0));
        test_bez_elevation(&BezPoly4o::new(0.0, 1.0, -1.0, 2.0, -2.0));
        test_bez_elevation(&BezPoly5o::new(0.0, 1.0, -1.0, 2.0, -2.0, 3.0));
        test_bez_elevation(&BezPoly6o::new(0.0, 1.0, -1.0, 2.0, -2.0, 3.0, -3.0));
    }

    #[test]
    fn nbez_elevation() {
        let mut nbez_poly = NBezPoly::from_container(Vec::with_capacity(7));
        nbez_poly.as_mut().push(0.0);
        nbez_poly.as_mut().push(1.0);

        test_bez_elevation(&nbez_poly);

        nbez_poly.as_mut().push(-1.0);
        test_bez_elevation(&nbez_poly);

        nbez_poly.as_mut().push(2.0);
        test_bez_elevation(&nbez_poly);

        nbez_poly.as_mut().push(-2.0);
        test_bez_elevation(&nbez_poly);

        nbez_poly.as_mut().push(3.0);
        test_bez_elevation(&nbez_poly);

        nbez_poly.as_mut().push(-3.0);
        test_bez_elevation(&nbez_poly);
    }

    fn test_bez_split<B>(curve: &B)
            where B: BezCurve<f64, Point = f64, Vector = f64> {

        // Sanity check bool to make sure the assertions are actually running
        let mut has_run = false;

        let mut s = 10.0/30.0;
        while s < 1.0 {
            let (left, right) = curve.split(s).unwrap();

            let mut t = 0.0;
            while t < 1.0 {
                if t < s {
                    println!("l {} {}", curve.interp(t).unwrap(), left.interp(t/s).unwrap());
                    assert!((curve.interp(t).unwrap() - left.interp(t/s).unwrap()).abs() <= 0.000000001);
                    has_run = true;

                } else {
                    println!("r {} {}", curve.interp(t).unwrap(), right.interp((t-s)/(1.0 - s)).unwrap());
                    assert!((curve.interp(t).unwrap() - right.interp((t-s)/(1.0 - s)).unwrap()).abs() <= 0.000000001);
                    has_run = true;
                }

                t += 1.0/30.0;
            }

            s += 1.0/30.0;
        }

        assert!(has_run);
        println!("");
    }

    #[test]
    fn bez_split() {
        let bezpoly1o = BezPoly1o::new(0.0, 1.0);
        test_bez_split(&bezpoly1o);

        let bezpoly2o = BezPoly2o::new(0.0, 1.0, -1.0);
        test_bez_split(&bezpoly2o);

        let bezpoly3o = BezPoly3o::new(0.0, 1.0, -1.0, 2.0);
        test_bez_split(&bezpoly3o);

        let bezpoly4o = BezPoly4o::new(0.0, 1.0, -1.0, 2.0, -2.0);
        test_bez_split(&bezpoly4o);

        let bezpoly5o = BezPoly5o::new(0.0, 1.0, -1.0, 2.0, -2.0, 3.0);
        test_bez_split(&bezpoly5o);

        let bezpoly6o = BezPoly6o::new(0.0, 1.0, -1.0, 2.0, -2.0, 3.0, -3.0);
        test_bez_split(&bezpoly6o);
    }
}<|endoftext|>