<commit_before>use clap::{App, Arg};
use json5;
use regex::Regex;

use std;
use std::collections::BTreeMap as Map;
use std::env;
use std::fs::File;
use std::io::Read;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::path::Path;
use std::{process, str};

use crate::{
    config::{Auth, Cert, Config, ProxyRoute, Route},
    consts::*,
    logger::{logger_init, JoinHandle},
};

/// Get `Config` by `parse` `args`
pub fn parse() -> (Config, JoinHandle) {
    let mut config = Config::default();
    let mut server = Server::default();
    let mut routes: Vec<String> = vec!["./".to_owned()];

    let default_addr = server.ip.to_string();
    let default_port = server.port.to_string();
    let app = {
        App::new(NAME)
            .version(VERSION)
            .author(env!("CARGO_PKG_AUTHORS"))
            .about(env!("CARGO_PKG_DESCRIPTION"))
            .about(DESC)
            .arg(
                Arg::with_name("verbose")
                    .short("v")
                    .multiple(true)
                    .help("Increases logging verbosity each use for up to 3 times(warn0_info1_debug2_trace3+)"),
            )
            .arg(
                Arg::with_name("config")
                    .long("config")
                    .short("c")
                    .takes_value(true)
                    .help("Set the path to a custom config file"),
            )
            .arg(
                Arg::with_name("auth")
                    .long("auth")
                    .short("a")
                    .takes_value(true)
                    .help("Set the username:password for authorization")
                    .validator(|s| {
                        s.parse::<Auth>()
                            .map(|_| ())
                            .map_err(|e| format!("invalid value for auth: {}", e))
                    }),
            )
            .arg(
                Arg::with_name("cert")
                    .long("cert")
                    .short("C")
                    .takes_value(true)
                    .help("Set the cert for https,  public_key_file:private_key_file")
                    .validator(|s| {
                        s.parse::<Cert>()
                            .map(|_| ())
                            .map_err(|e| format!("invalid value for cert: {}", e))
                    }),
            )
            .arg(
                Arg::with_name("config-print")
                    .long("config-print")
                    .help("Print the default config file"),
            )
            .arg(
                Arg::with_name("redirect-html")
                    .long("redirect-html")
                    .short("r")
                    .help("Redirect dir to `index.html` or `index.htm` if it exists"),
            )
            .arg(
                Arg::with_name("show-hider")
                    .long("show-hider")
                    .short("s")
                    .help("show entries starts with '.'"),
            )
            .arg(
                Arg::with_name("keepalive")
                    .long("keepalive")
                    .short("k")
                    .help("Close HTTP keep alive"),
            )
            .arg(
                Arg::with_name("follow-links")
                    .long("follow-links")
                    .short("f")
                    .help("Whether follow links(default not)"),
            )
            .arg(
                Arg::with_name("upload")
                    .long("upload")
                    .short("u")
                    .help("Whether enable upload(default not)"),
            )
            .arg(
                Arg::with_name("mkdir")
                    .short("m")
                    .long("mkdir")
                    .help("Whether enable mkdir(default not)"),
            )
            .arg(
                Arg::with_name("magic-limit")
                    .long("magic-limit")
                    .short("M")
                    .takes_value(true)
                    .help("The limit for detect file ContenType(use 0 to close)")
                    .validator(|s| {
                        s.parse::<u64>()
                            .map(|_| ())
                            .map_err(|e| format!("invalid value for magic-limit: {}", e))
                    }),
            )
            .arg(
                Arg::with_name("cache-secs")
                    .long("cache-secs")
                    .takes_value(true)
                    .help("Set cache secs(use 0 to close)")
                    .validator(|s| {
                        s.parse::<u32>()
                            .map(|_| ())
                            .map_err(|e| format!("invalid value for cache-secs: {}", e))
                    }),
            )
            .arg(
                Arg::with_name("proxy")
                    .long("proxy")
                    .short("P")
                    // .default_value("")
                    .takes_value(true)
                    .help("Enable http tunnel proxy(CONNECT)")
                    .validator(|s| {
                        Regex::new(&s)
                            .map(|_| ())
                            .map_err(|e| format!("invalid value for proxy: {}", e))
                    }),
            )
            .arg(
                Arg::with_name("ip")
                    .long("ip")
                    .short("i")
                    .default_value(&default_addr)
                    .takes_value(true)
                    .help("Set listenning ip")
                    .validator(|s| {
                        s.parse::<IpAddr>()
                            .map(|_| ())
                            .map_err(|e| format!("invalid value for ip: {}", e))
                    }),
            )
            .arg(
                Arg::with_name("port")
                    .long("port")
                    .short("p")
                    .default_value(&default_port)
                    .takes_value(true)
                    .help("Set listenning port")
                    .validator(|s| {
                        s.parse::<u16>()
                            .map(|_| ())
                            .map_err(|e| format!("invalid value for port: {}", e))
                    }),
            )
            .arg(
                Arg::with_name("PATH")
                    .index(1)
                    .multiple(true)
                    .help(r#"Set the paths to share"#),
            )
    };

    // clap's NOTE: The first argument will be parsed as the binary name unless AppSettings::NoBinaryName is used
    let args = env::args_os().collect::<Vec<_>>();
    let args_is_empty = args.len() <= 1;
    let matches = app.get_matches_from(args);

    // -P/--config-print
    if matches.is_present("config-print") {
        config_print();
    }

    let join_handle = logger_init(matches.occurrences_of("verbose"));

    //-c/--config选项，如果有就载入该文件。
    if let Some(s) = matches.value_of("config") {
        let config = Config::load_from_file(&s)
            .map_err(|e| {
                error!("{:?}", e);
                process::exit(1);
            })
            .unwrap();
        return (config, join_handle);
    }

    // 命令行有没有参数？有就解析参数，没有就寻找配置文件，再没有就使用默认配置。
    let conf = if args_is_empty {
        match get_config_path() {
            Some(s) => Config::load_from_file(&s)
                .map_err(|e| {
                    error!("{:?}", e);
                    process::exit(1);
                })
                .unwrap(),
            None => Config::load_from_STR(),
        }
    } else {
        let redirect_html = matches.is_present("redirect-html");
        let follow_links = matches.is_present("follow-links");
        let show_hider = matches.is_present("show-hider");
        let upload = matches.is_present("upload");
        let mkdir = matches.is_present("mkdir");
        let authorized = matches.is_present("auth");

        matches.value_of("ip").map(|p| server.ip = p.parse().unwrap());
        matches.value_of("port").map(|p| server.port = p.parse().unwrap());
        config.addr = SocketAddr::new(server.ip, server.port);
        config.auth = matches.value_of("auth").map(|cp| cp.parse::<Auth>().unwrap());
        config.cert = matches.value_of("cert").map(|cp| cp.parse::<Cert>().unwrap());
        config.proxy = matches.value_of("proxy").map(|s| (&ProxyRoute::new(true, s)).into());
        config.keep_alive = !matches.is_present("keepalive");
        matches.values_of_lossy("PATH").map(|args| routes = args);

        config.routes = args_paths_to_route(
            &routes[..],
            redirect_html,
            follow_links,
            show_hider,
            upload,
            mkdir,
            authorized,
        )
        .map_err(|e| {
            error!("{:?}", e);
            process::exit(1);
        })
        .unwrap();
        config
    };

    (conf, join_handle)
}

#[derive(Debug, Clone)]
pub struct Server {
    pub ip: IpAddr,
    pub port: u16,
}

impl Default for Server {
    fn default() -> Server {
        Self::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 8000)
    }
}
impl Server {
    fn new(ip: IpAddr, port: u16) -> Self {
        Server { ip: ip, port: port }
    }
    fn get_sa() -> SocketAddr {
        Self::default().into()
    }
}

impl Into<SocketAddr> for Server {
    fn into(self) -> SocketAddr {
        SocketAddr::new(self.ip, self.port)
    }
}

// 关键是结构体的字段名，和 json 的[name]对应
#[serde(rename_all = "camelCase")]
#[derive(Debug, Deserialize, Serialize)]
pub struct Fht2p {
    setting: Setting,
    proxy: Option<ProxyRoute>,
    routes: Map<String, Route>,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Setting {
    #[serde(default = "Server::get_sa")]
    addr: SocketAddr,
    keep_alive: bool,
    magic_limit: u64,
    cache_secs: u32,
    auth: Option<Auth>,
    cert: Option<Cert>,
}

impl Config {
    fn load_from_file(path: &str) -> Result<Self, String> {
        let mut str = String::new();
        let mut file = File::open(path).map_err(|e| format!("config file('{}') open fails: {}", path, e))?;
        file.read_to_string(&mut str)
            .map_err(|e| format!("config file('{}') read fails: {}", path, e))?;
        Self::load_from_str(path, &str)
    }
    fn load_from_str(file_name: &str, json: &str) -> Result<Config, String> {
        let mut config = Self::default();
        config.routes.clear();

        let json: Fht2p = json5::from_str(json).map_err(|e| format!("config file('{}') parse fails: {}", file_name, e))?;
        config.keep_alive = json.setting.keep_alive;
        config.magic_limit = json.setting.magic_limit;
        config.cache_secs = json.setting.cache_secs;
        config.addr = json.setting.addr;
        config.cert = json.setting.cert.clone();
        config.auth = json.setting.auth.clone();
        config.proxy = json.proxy.map(|ref pc| pc.into());

        for (url, route) in &json.routes {
            if !Path::new(&route.path).exists() {
                warn!("'{}''s routes({:?}: {:?}) is not exists", file_name, url, route.path);
            }
            config.routes.insert(
                url.clone(),
                Route::new(
                    url.as_str(),
                    route.path.as_str(),
                    route.redirect_html,
                    route.follow_links,
                    route.show_hider,
                    route.upload,
                    route.mkdir,
                    route.authorized,
                ),
            );
        }
        if config.routes.is_empty() {
            return Err(format!("'{}''s routes is empty", file_name));
        }
        Ok(config)
    }
    #[allow(non_snake_case)]
    fn load_from_STR() -> Self {
        Config::load_from_str("CONFIG-STR", CONFIG_STR).unwrap()
    }
}

// 打印默认配置文件。
fn config_print() {
    println!("{}", CONFIG_STR);
    std::process::exit(0);
}

// 家目录 ～/.config/fht2p/fht2p.json
fn get_config_path() -> Option<String> {
    // using the home_dir function from https://crates.io/crates/dirs instead.
    #[allow(deprecated)]
    let home = std::env::home_dir()?;
    if home.as_path().join(".config/fht2p").join(CONFIG_STR_PATH).exists() {
        Some(
            home.as_path()
                .join(".config/fht2p")
                .join(CONFIG_STR_PATH)
                .to_string_lossy()
                .into_owned(),
        )
    } else {
        None
    }
}

// 参数转换为Route url, path
fn args_paths_to_route(
    map: &[String],
    redirect_html: bool,
    follow_links: bool,
    show_hider: bool,
    upload: bool,
    mkdir: bool,
    authorized: bool,
) -> Result<Map<String, Route>, String> {
    let mut routes = Map::new();
    for (idx, path) in map.iter().enumerate() {
        if !Path::new(&path).exists() {
            warn!("{:?} is not exists", &path);
        }
        if idx == 0 {
            let route = Route::new(
                "/".to_owned(),
                path.to_string(),
                redirect_html,
                follow_links,
                show_hider,
                upload,
                mkdir,
                authorized,
            );
            routes.insert("/".to_owned(), route);
        } else {
            let route_url = route_name(path)?;
            let route = Route::new(
                route_url.clone(),
                path.to_string(),
                redirect_html,
                follow_links,
                show_hider,
                upload,
                mkdir,
                authorized,
            );
            if routes.insert(route_url, route).is_some() {
                return Err(format!("{} already defined", route_name(path).unwrap()));
            }
        }
    }
    fn route_name(msg: &str) -> Result<String, String> {
        let path = Path::new(msg);
        path.file_name()
            .map(|s| "/".to_owned() + s.to_str().unwrap())
            .map(|mut s| {
                if Path::new(msg).is_dir() {
                    s.push('/');
                }
                s
            })
            .ok_or_else(|| format!("Path '{}' dost not have name", msg))
    }
    Ok(routes)
}
<commit_msg>update the message of help<commit_after>use clap::{App, Arg};
use json5;
use regex::Regex;

use std;
use std::collections::BTreeMap as Map;
use std::env;
use std::fs::File;
use std::io::Read;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::path::Path;
use std::{process, str};

use crate::{
    config::{Auth, Cert, Config, ProxyRoute, Route},
    consts::*,
    logger::{logger_init, JoinHandle},
};

/// Get `Config` by `parse` `args`
pub fn parse() -> (Config, JoinHandle) {
    let mut config = Config::default();
    let mut server = Server::default();
    let mut routes: Vec<String> = vec!["./".to_owned()];

    let default_addr = server.ip.to_string();
    let default_port = server.port.to_string();
    let default_cache = config.cache_secs.to_string();
    let app = {
        App::new(NAME)
            .version(VERSION)
            .author(env!("CARGO_PKG_AUTHORS"))
            .about(env!("CARGO_PKG_DESCRIPTION"))
            .about(DESC)
            .arg(
                Arg::with_name("verbose")
                    .short("v")
                    .multiple(true)
                    .help("Increases logging verbosity each use for up to 3 times(warn0_info1_debug2_trace3+)"),
            )
            .arg(
                Arg::with_name("config")
                    .long("config")
                    .short("c")
                    .takes_value(true)
                    .help("Set the path to a custom config file"),
            )
            .arg(
                Arg::with_name("auth")
                    .long("auth")
                    .short("a")
                    .takes_value(true)
                    .help("Set the username:password for authorization")
                    .validator(|s| {
                        s.parse::<Auth>()
                            .map(|_| ())
                            .map_err(|e| format!("invalid value for auth: {}", e))
                    }),
            )
            .arg(
                Arg::with_name("cert")
                    .long("cert")
                    .short("C")
                    .takes_value(true)
                    .help("Set the cert for https,  public_key_file:private_key_file")
                    .validator(|s| {
                        s.parse::<Cert>()
                            .map(|_| ())
                            .map_err(|e| format!("invalid value for cert: {}", e))
                    }),
            )
            .arg(
                Arg::with_name("config-print")
                    .long("config-print")
                    .help("Print the default config file"),
            )
            .arg(
                Arg::with_name("redirect-html")
                    .long("redirect-html")
                    .short("r")
                    .help("Redirect dir to `index.html` or `index.htm` if it exists"),
            )
            .arg(
                Arg::with_name("show-hider")
                    .long("show-hider")
                    .short("s")
                    .help("show entries starts with '.'"),
            )
            .arg(
                Arg::with_name("keepalive")
                    .long("keepalive")
                    .short("k")
                    .help("Close HTTP keep alive"),
            )
            .arg(
                Arg::with_name("follow-links")
                    .long("follow-links")
                    .short("f")
                    .help("Whether follow links(default not)"),
            )
            .arg(
                Arg::with_name("upload")
                    .long("upload")
                    .short("u")
                    .help("Whether enable upload(default not)"),
            )
            .arg(
                Arg::with_name("mkdir")
                    .short("m")
                    .long("mkdir")
                    .help("Whether enable mkdir(default not)"),
            )
            .arg(
                Arg::with_name("magic-limit")
                    .long("magic-limit")
                    .short("M")
                    .takes_value(true)
                    .help("The limit for detect file ContenType(use 0 to close)")
                    .validator(|s| {
                        s.parse::<u64>()
                            .map(|_| ())
                            .map_err(|e| format!("invalid value for magic-limit: {}", e))
                    }),
            )
            .arg(
                Arg::with_name("cache-secs")
                    .long("cache-secs")
                    .takes_value(true)
                    .default_value(&default_cache)
                    .help("Set cache secs(use 0 to close)")
                    .validator(|s| {
                        s.parse::<u32>()
                            .map(|_| ())
                            .map_err(|e| format!("invalid value for cache-secs: {}", e))
                    }),
            )
            .arg(
                Arg::with_name("proxy")
                    .long("proxy")
                    .short("P")
                    // .default_value("")
                    .takes_value(true)
                    .help("Enable http proxy function")
                    .validator(|s| {
                        Regex::new(&s)
                            .map(|_| ())
                            .map_err(|e| format!("invalid value for proxy: {}", e))
                    }),
            )
            .arg(
                Arg::with_name("ip")
                    .long("ip")
                    .short("i")
                    .default_value(&default_addr)
                    .takes_value(true)
                    .help("Set listenning ip")
                    .validator(|s| {
                        s.parse::<IpAddr>()
                            .map(|_| ())
                            .map_err(|e| format!("invalid value for ip: {}", e))
                    }),
            )
            .arg(
                Arg::with_name("port")
                    .long("port")
                    .short("p")
                    .default_value(&default_port)
                    .takes_value(true)
                    .help("Set listenning port")
                    .validator(|s| {
                        s.parse::<u16>()
                            .map(|_| ())
                            .map_err(|e| format!("invalid value for port: {}", e))
                    }),
            )
            .arg(
                Arg::with_name("PATH")
                    .index(1)
                    .multiple(true)
                    .help(r#"Set the paths to share"#),
            )
    };

    // clap's NOTE: The first argument will be parsed as the binary name unless AppSettings::NoBinaryName is used
    let args = env::args_os().collect::<Vec<_>>();
    let args_is_empty = args.len() <= 1;
    let matches = app.get_matches_from(args);

    // -P/--config-print
    if matches.is_present("config-print") {
        config_print();
    }

    let join_handle = logger_init(matches.occurrences_of("verbose"));

    //-c/--config选项，如果有就载入该文件。
    if let Some(s) = matches.value_of("config") {
        let config = Config::load_from_file(&s)
            .map_err(|e| {
                error!("{:?}", e);
                process::exit(1);
            })
            .unwrap();
        return (config, join_handle);
    }

    // 命令行有没有参数？有就解析参数，没有就寻找配置文件，再没有就使用默认配置。
    let conf = if args_is_empty {
        match get_config_path() {
            Some(s) => Config::load_from_file(&s)
                .map_err(|e| {
                    error!("{:?}", e);
                    process::exit(1);
                })
                .unwrap(),
            None => Config::load_from_STR(),
        }
    } else {
        let redirect_html = matches.is_present("redirect-html");
        let follow_links = matches.is_present("follow-links");
        let show_hider = matches.is_present("show-hider");
        let upload = matches.is_present("upload");
        let mkdir = matches.is_present("mkdir");
        let authorized = matches.is_present("auth");

        matches.value_of("ip").map(|p| server.ip = p.parse().unwrap());
        matches.value_of("port").map(|p| server.port = p.parse().unwrap());
        matches
            .value_of("cache-secs")
            .map(|cs| config.cache_secs = cs.parse::<u32>().unwrap());

        config.addr = SocketAddr::new(server.ip, server.port);
        config.auth = matches.value_of("auth").map(|cp| cp.parse::<Auth>().unwrap());
        config.cert = matches.value_of("cert").map(|cp| cp.parse::<Cert>().unwrap());
        config.proxy = matches.value_of("proxy").map(|s| (&ProxyRoute::new(true, s)).into());
        config.keep_alive = !matches.is_present("keepalive");
        matches.values_of_lossy("PATH").map(|args| routes = args);

        config.routes = args_paths_to_route(
            &routes[..],
            redirect_html,
            follow_links,
            show_hider,
            upload,
            mkdir,
            authorized,
        )
        .map_err(|e| {
            error!("{:?}", e);
            process::exit(1);
        })
        .unwrap();
        config
    };

    (conf, join_handle)
}

#[derive(Debug, Clone)]
pub struct Server {
    pub ip: IpAddr,
    pub port: u16,
}

impl Default for Server {
    fn default() -> Server {
        Self::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 8000)
    }
}
impl Server {
    fn new(ip: IpAddr, port: u16) -> Self {
        Server { ip: ip, port: port }
    }
    fn get_sa() -> SocketAddr {
        Self::default().into()
    }
}

impl Into<SocketAddr> for Server {
    fn into(self) -> SocketAddr {
        SocketAddr::new(self.ip, self.port)
    }
}

// 关键是结构体的字段名，和 json 的[name]对应
#[serde(rename_all = "camelCase")]
#[derive(Debug, Deserialize, Serialize)]
pub struct Fht2p {
    setting: Setting,
    proxy: Option<ProxyRoute>,
    routes: Map<String, Route>,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Setting {
    #[serde(default = "Server::get_sa")]
    addr: SocketAddr,
    keep_alive: bool,
    magic_limit: u64,
    cache_secs: u32,
    auth: Option<Auth>,
    cert: Option<Cert>,
}

impl Config {
    fn load_from_file(path: &str) -> Result<Self, String> {
        let mut str = String::new();
        let mut file = File::open(path).map_err(|e| format!("config file('{}') open fails: {}", path, e))?;
        file.read_to_string(&mut str)
            .map_err(|e| format!("config file('{}') read fails: {}", path, e))?;
        Self::load_from_str(path, &str)
    }
    fn load_from_str(file_name: &str, json: &str) -> Result<Config, String> {
        let mut config = Self::default();
        config.routes.clear();

        let json: Fht2p = json5::from_str(json).map_err(|e| format!("config file('{}') parse fails: {}", file_name, e))?;
        config.keep_alive = json.setting.keep_alive;
        config.magic_limit = json.setting.magic_limit;
        config.cache_secs = json.setting.cache_secs;
        config.addr = json.setting.addr;
        config.cert = json.setting.cert.clone();
        config.auth = json.setting.auth.clone();
        config.proxy = json.proxy.map(|ref pc| pc.into());

        for (url, route) in &json.routes {
            if !Path::new(&route.path).exists() {
                warn!("'{}''s routes({:?}: {:?}) is not exists", file_name, url, route.path);
            }
            config.routes.insert(
                url.clone(),
                Route::new(
                    url.as_str(),
                    route.path.as_str(),
                    route.redirect_html,
                    route.follow_links,
                    route.show_hider,
                    route.upload,
                    route.mkdir,
                    route.authorized,
                ),
            );
        }
        if config.routes.is_empty() {
            return Err(format!("'{}''s routes is empty", file_name));
        }
        Ok(config)
    }
    #[allow(non_snake_case)]
    fn load_from_STR() -> Self {
        Config::load_from_str("CONFIG-STR", CONFIG_STR).unwrap()
    }
}

// 打印默认配置文件。
fn config_print() {
    println!("{}", CONFIG_STR);
    std::process::exit(0);
}

// 家目录 ～/.config/fht2p/fht2p.json
fn get_config_path() -> Option<String> {
    // using the home_dir function from https://crates.io/crates/dirs instead.
    #[allow(deprecated)]
    let home = std::env::home_dir()?;
    if home.as_path().join(".config/fht2p").join(CONFIG_STR_PATH).exists() {
        Some(
            home.as_path()
                .join(".config/fht2p")
                .join(CONFIG_STR_PATH)
                .to_string_lossy()
                .into_owned(),
        )
    } else {
        None
    }
}

// 参数转换为Route url, path
fn args_paths_to_route(
    map: &[String],
    redirect_html: bool,
    follow_links: bool,
    show_hider: bool,
    upload: bool,
    mkdir: bool,
    authorized: bool,
) -> Result<Map<String, Route>, String> {
    let mut routes = Map::new();
    for (idx, path) in map.iter().enumerate() {
        if !Path::new(&path).exists() {
            warn!("{:?} is not exists", &path);
        }
        if idx == 0 {
            let route = Route::new(
                "/".to_owned(),
                path.to_string(),
                redirect_html,
                follow_links,
                show_hider,
                upload,
                mkdir,
                authorized,
            );
            routes.insert("/".to_owned(), route);
        } else {
            let route_url = route_name(path)?;
            let route = Route::new(
                route_url.clone(),
                path.to_string(),
                redirect_html,
                follow_links,
                show_hider,
                upload,
                mkdir,
                authorized,
            );
            if routes.insert(route_url, route).is_some() {
                return Err(format!("{} already defined", route_name(path).unwrap()));
            }
        }
    }
    fn route_name(msg: &str) -> Result<String, String> {
        let path = Path::new(msg);
        path.file_name()
            .map(|s| "/".to_owned() + s.to_str().unwrap())
            .map(|mut s| {
                if Path::new(msg).is_dir() {
                    s.push('/');
                }
                s
            })
            .ok_or_else(|| format!("Path '{}' dost not have name", msg))
    }
    Ok(routes)
}
<|endoftext|>


<commit_before>/* Copyright 2016 Jordan Miner
 *
 * Licensed under the MIT license <LICENSE or
 * http://opensource.org/licenses/MIT>. This file may not be copied,
 * modified, or distributed except according to those terms.
 */

use super::control_prelude::*;
use super::containers::{
    Container,
    NonDialogContainer,
};

#[derive(Clone)]
pub struct TabInfo<'a, 'b> {
    title: &'a str,
    control: &'b Control,
}

impl<'a, 'b> TabInfo<'a, 'b> {
    pub fn new(control: &'b Control) -> Self {
        TabInfo {
            title: "",
            control: control,
        }
    }

    pub fn title(&mut self, title: &'a str) -> &mut Self {
        self.title = title;
        self
    }
}

/// # Examples
///
/// Using the `append_tabs` helper:
///
/// ```
/// # use clear_coat::*;
/// let tabs = Tabs::new();
/// tabs.append_tabs(&[
///     TabInfo::new(&Text::new()).title("First"),
///     TabInfo::new(&Text::new()).title("Second"),
/// ]);
/// ```
///
/// or the more verbose way:
///
/// ```
/// # use clear_coat::*;
/// let tabs = Tabs::new();
/// tabs.append(&Text::new());
/// tabs.set_tab_title(0, "First");
/// tabs.append(&Text::new());
/// tabs.set_tab_title(1, "Second");
/// ```
#[derive(Clone)]
pub struct Tabs(HandleRc);

impl Tabs {
    pub fn new() -> Self {
        unsafe {
            ::iup_open();
            let ih = IupTabsv(ptr::null_mut());
            Tabs(HandleRc::new(ih))
        }
    }

    pub fn tab_title(&self, pos: usize) -> String {
        get_str_attribute(self.handle(), &format!("TABTITLE{}\0", pos))
    }

    pub fn set_tab_title(&self, pos: usize, title: &str) -> &Self {
        set_str_attribute(self.handle(), &format!("TABTITLE{}\0", pos), title);
        self
    }

    pub fn append_tabs(&self, info: &[&TabInfo]) -> &Self {
        let mut index = self.child_count();
        for ti in info {
            self.append(ti.control).expect("failed to append tab");
            self.set_tab_title(index, ti.title);
            index += 1;
        }
        self
    }
}

impl_control_traits!(Tabs);

impl Container for Tabs {}
impl NonDialogContainer for Tabs {}

impl ActiveAttribute for Tabs {}
impl MinMaxSizeAttribute for Tabs {}
impl TipAttribute for Tabs {}
impl VisibleAttribute for Tabs {}

impl MenuCommonCallbacks for Tabs {}
impl GetKillFocusCallbacks for Tabs {}
impl EnterLeaveWindowCallbacks for Tabs {}

<commit_msg>Add comments that tab indexes are zero-based<commit_after>/* Copyright 2016 Jordan Miner
 *
 * Licensed under the MIT license <LICENSE or
 * http://opensource.org/licenses/MIT>. This file may not be copied,
 * modified, or distributed except according to those terms.
 */

use super::control_prelude::*;
use super::containers::{
    Container,
    NonDialogContainer,
};

#[derive(Clone)]
pub struct TabInfo<'a, 'b> {
    title: &'a str,
    control: &'b Control,
}

impl<'a, 'b> TabInfo<'a, 'b> {
    pub fn new(control: &'b Control) -> Self {
        TabInfo {
            title: "",
            control: control,
        }
    }

    pub fn title(&mut self, title: &'a str) -> &mut Self {
        self.title = title;
        self
    }
}

/// # Examples
///
/// Using the `append_tabs` helper:
///
/// ```
/// # use clear_coat::*;
/// let tabs = Tabs::new();
/// tabs.append_tabs(&[
///     TabInfo::new(&Text::new()).title("First"),
///     TabInfo::new(&Text::new()).title("Second"),
/// ]);
/// ```
///
/// or the more verbose way:
///
/// ```
/// # use clear_coat::*;
/// let tabs = Tabs::new();
/// tabs.append(&Text::new());
/// tabs.set_tab_title(0, "First");
/// tabs.append(&Text::new());
/// tabs.set_tab_title(1, "Second");
/// ```
#[derive(Clone)]
pub struct Tabs(HandleRc);

impl Tabs {
    pub fn new() -> Self {
        unsafe {
            ::iup_open();
            let ih = IupTabsv(ptr::null_mut());
            Tabs(HandleRc::new(ih))
        }
    }

    /// A `pos` of 0 is the first tab.
    pub fn tab_title(&self, pos: usize) -> String {
        get_str_attribute(self.handle(), &format!("TABTITLE{}\0", pos))
    }

    /// A `pos` of 0 is the first tab.
    pub fn set_tab_title(&self, pos: usize, title: &str) -> &Self {
        set_str_attribute(self.handle(), &format!("TABTITLE{}\0", pos), title);
        self
    }

    pub fn append_tabs(&self, info: &[&TabInfo]) -> &Self {
        let mut index = self.child_count();
        for ti in info {
            self.append(ti.control).expect("failed to append tab");
            self.set_tab_title(index, ti.title);
            index += 1;
        }
        self
    }
}

impl_control_traits!(Tabs);

impl Container for Tabs {}
impl NonDialogContainer for Tabs {}

impl ActiveAttribute for Tabs {}
impl MinMaxSizeAttribute for Tabs {}
impl TipAttribute for Tabs {}
impl VisibleAttribute for Tabs {}

impl MenuCommonCallbacks for Tabs {}
impl GetKillFocusCallbacks for Tabs {}
impl EnterLeaveWindowCallbacks for Tabs {}

<|endoftext|>


<commit_before>macro_rules! decode_op(
	($op:ident $handler:ident $data:ident) => (
		match $op {
			0x20 => $handler.add($data),
			0x30 => $handler.addc($data),
			0x28 => $handler.and($data),
			0x38 => $handler.andc($data),
			0x1C => $handler.beq($data),
			0x1D => $handler.bne($data),
			0x24 => $handler.cmpeq($data),
			0x34 => $handler.cmpeqc($data),
			0x26 => $handler.cmple($data),
			0x36 => $handler.cmplec($data),
			0x25 => $handler.cmplt($data),
			0x35 => $handler.cmpltc($data),
			0x23 => $handler.div($data),
			0x33 => $handler.divc($data),
			0x1B => $handler.jmp($data),
			0x18 => $handler.ld($data),
			0x1F => $handler.ldr($data),
			0x22 => $handler.mul($data),
			0x32 => $handler.mulc($data),
			0x29 => $handler.or($data),
			0x39 => $handler.orc($data),
			0x2C => $handler.shl($data),
			0x3C => $handler.shlc($data),
			0x2D => $handler.shr($data),
			0x3D => $handler.shrc($data),
			0x2E => $handler.sra($data),
			0x3E => $handler.srac($data),
			0x21 => $handler.sub($data),
			0x31 => $handler.subc($data),
			0x19 => $handler.st($data),
			0x2A => $handler.xor($data),
			0x3A => $handler.xorc($data),
			0x2B => $handler.xnor($data),
			0x3B => $handler.xnorc($data),
			_ => fail!("Unrecognized opcode.")
		}
)
)

fn each(v: &mut [u32], op: &fn(v: &mut u32)) {
	let mut n = 0;
	while n < v.len() {
		op(&mut v[n]);
		n += 1;
	}
}

struct Mem {
	data:	[u8, ..4096]
}

impl Mem {
	fn read(&self, addr: u32) -> u8 {
		self.data[addr]
	}

	fn read_word(&self, addr: u32) -> u32 {
		let b1 = self.read(addr) as u32;
		let b2 = self.read(addr + 1) as u32;
		let b3 = self.read(addr + 2) as u32;
		let b4 = self.read(addr + 3) as u32;

		(b1 << 24) | (b2 << 16) | (b3 << 8) | b4
	}

	fn write(&mut self, addr: u32, val: u8) {
		self.data[addr] = val;
	}
}

struct Beta {
	pc:			u32,
	register:	[u32, ..31],
	mem:		Mem
}

impl Beta {
	fn reset(&mut self) {
		self.pc = 0;
		do each(self.register) |r| { *r = 0; }

		//println(fmt!("reset. pc at %d", self.pc as int));
	}

	fn tick(&mut self) {
		let instruction: u32 = self.mem.read_word(self.pc);

		// decode invariant part of instruction format
		let op = instruction >> 26;
		let data = instruction & 0x3FFFFFFF;

		println(fmt!("op: %d", op as int));

		decode_op!(op self data)

		self.pc += 4;
		//println(fmt!("tick. pc at %d", self.pc as int));
	}

	fn read_reg(&self, reg: uint) -> u32 {
		match reg {
			0..30	=> self.register[reg],
			31		=> 0,
			_		=> fail!(fmt!("tried to read from nonexistant register %d", reg as int))
		}
	}

	fn write_reg(&mut self, reg: uint, val: u32) {
		match reg {
			0..30	=> self.register[reg] = val,
			31		=> {},
			_		=> fail!(fmt!("tried to write to nonexistant register %d", reg as int))
		};
	}

	fn args(data: u32) -> (u32, u32, u32) {
		(data >> 21, data >> 16, data >> 11)
	}

	fn args_literal(data: u32) -> (u32, u32, u32) {
		(data >> 21, data >> 16, data & 0xFFFF)
	}

	fn exec_op(&mut self, data: u32, exp: &fn(a: u32, b: u32) -> u32) {
		let (r_c, r_a, r_b) = Beta::args(data);
		let a = self.read_reg(r_a as uint);
		let b = self.read_reg(r_b as uint);
		self.write_reg(r_c as uint, exp(a, b));
		println(fmt!("fn \"%?\"", exp));
	}

	fn exec_op_lit(&mut self, data: u32, exp: &fn(a: u32, b: u32) -> u32) {
		let (r_c, r_a, lit) = Beta::args_literal(data);
		let a = self.read_reg(r_a as uint);
		self.write_reg(r_c as uint, exp(a, lit));
		println(fmt!("fn \"%?\"", exp));
	}

	/* INSTRUCTION SET */

	fn add(&mut self, data: u32) {
		self.exec_op(data, |a, b| a + b);
	}

	fn addc(&mut self, data: u32) {
		self.exec_op_lit(data, |a, b| a + b);
	}

	fn and(&mut self, data: u32) {
		self.exec_op(data, |a, b| a & b);
	}

	fn andc(&mut self, data: u32) {
		self.exec_op_lit(data, |a, b| a & b);
	}

	fn beq(&mut self, data: u32) {
		let (r_c, r_a, lit) = Beta::args_literal(data);
		let a = self.read_reg(r_a as uint);

		self.write_reg(r_c as uint, self.pc + 4);
		let displacement = (lit as i16 as u32)*4;
		let target = self.pc + 4 + displacement;

		if(a == 0) { self.pc = target; }
	}

	fn bne(&mut self, data: u32) {
		let (r_c, r_a, lit) = Beta::args_literal(data);
		let a = self.read_reg(r_a as uint);

		self.write_reg(r_c as uint, self.pc + 4);
		let displacement = (lit as i16 as u32)*4;
		let target = self.pc + 4 + displacement;

		if(a != 0) { self.pc = target; }
	}

	fn cmpeq(&mut self, data: u32) {
		self.exec_op(data, |a, b| if(a == b) {1} else {0});
	}

	fn cmpeqc(&mut self, data: u32) {
		self.exec_op_lit(data, |a, b| if(a == b) {1} else {0});
	}

	fn cmple(&mut self, data: u32) {
		self.exec_op(data, |a, b| if(a <= b) {1} else {0});
	}

	fn cmplec(&mut self, data: u32) {
		self.exec_op_lit(data, |a, b| if(a <= b) {1} else {0});
	}

	fn cmplt(&mut self, data: u32) {
		self.exec_op(data, |a, b| if(a < b) {1} else {0});
	}

	fn cmpltc(&mut self, data: u32) {
		self.exec_op_lit(data, |a, b| if(a < b) {1} else {0});
	}

	fn div(&mut self, data: u32) {
		self.exec_op(data, |a, b| a / b);
	}

	fn divc(&mut self, data: u32) {
		self.exec_op_lit(data, |a, b| a / b);
	}

	fn jmp(&mut self, data: u32) {
		let (r_c, r_a, _lit) = Beta::args_literal(data);

		self.write_reg(r_c as uint, self.pc + 4);
		self.pc = self.read_reg(r_a as uint) & 0xFFFFFFFC;
	}

	fn ld(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn ldr(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn mul(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn mulc(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn or(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn orc(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn shl(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn shlc(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn shr(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn shrc(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn sra(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn srac(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn sub(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn subc(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn st(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn xor(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn xorc(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn xnor(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn xnorc(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}
}

fn main() {
	let mut b = ~Beta {
		pc: 10u32,
		register: [3u32, ..31],
		mem: Mem {
			data: [0u8, ..4*1024]
		}
	};

	b.reset();
	b.tick();
	println("success");
}
<commit_msg>Implemented ld and ldr.<commit_after>macro_rules! decode_op(
	($op:ident $handler:ident $data:ident) => (
		match $op {
			0x20 => $handler.add($data),
			0x30 => $handler.addc($data),
			0x28 => $handler.and($data),
			0x38 => $handler.andc($data),
			0x1C => $handler.beq($data),
			0x1D => $handler.bne($data),
			0x24 => $handler.cmpeq($data),
			0x34 => $handler.cmpeqc($data),
			0x26 => $handler.cmple($data),
			0x36 => $handler.cmplec($data),
			0x25 => $handler.cmplt($data),
			0x35 => $handler.cmpltc($data),
			0x23 => $handler.div($data),
			0x33 => $handler.divc($data),
			0x1B => $handler.jmp($data),
			0x18 => $handler.ld($data),
			0x1F => $handler.ldr($data),
			0x22 => $handler.mul($data),
			0x32 => $handler.mulc($data),
			0x29 => $handler.or($data),
			0x39 => $handler.orc($data),
			0x2C => $handler.shl($data),
			0x3C => $handler.shlc($data),
			0x2D => $handler.shr($data),
			0x3D => $handler.shrc($data),
			0x2E => $handler.sra($data),
			0x3E => $handler.srac($data),
			0x21 => $handler.sub($data),
			0x31 => $handler.subc($data),
			0x19 => $handler.st($data),
			0x2A => $handler.xor($data),
			0x3A => $handler.xorc($data),
			0x2B => $handler.xnor($data),
			0x3B => $handler.xnorc($data),
			_ => fail!("Unrecognized opcode.")
		}
)
)

fn each(v: &mut [u32], op: &fn(v: &mut u32)) {
	let mut n = 0;
	while n < v.len() {
		op(&mut v[n]);
		n += 1;
	}
}

struct Mem {
	data:	[u8, ..4096]
}

impl Mem {
	fn read(&self, addr: u32) -> u8 {
		self.data[addr]
	}

	fn read_word(&self, addr: u32) -> u32 {
		let b1 = self.read(addr) as u32;
		let b2 = self.read(addr + 1) as u32;
		let b3 = self.read(addr + 2) as u32;
		let b4 = self.read(addr + 3) as u32;

		(b1 << 24) | (b2 << 16) | (b3 << 8) | b4
	}

	fn write(&mut self, addr: u32, val: u8) {
		self.data[addr] = val;
	}
}

struct Beta {
	pc:			u32,
	register:	[u32, ..31],
	mem:		Mem
}

impl Beta {
	fn reset(&mut self) {
		self.pc = 0;
		do each(self.register) |r| { *r = 0; }

		//println(fmt!("reset. pc at %d", self.pc as int));
	}

	fn tick(&mut self) {
		let instruction: u32 = self.mem.read_word(self.pc);

		// decode invariant part of instruction format
		let op = instruction >> 26;
		let data = instruction & 0x3FFFFFFF;

		println(fmt!("op: %d", op as int));

		decode_op!(op self data)

		self.pc += 4;
		//println(fmt!("tick. pc at %d", self.pc as int));
	}

	fn read_reg(&self, reg: uint) -> u32 {
		match reg {
			0..30	=> self.register[reg],
			31		=> 0,
			_		=> fail!(fmt!("tried to read from nonexistant register %d", reg as int))
		}
	}

	fn write_reg(&mut self, reg: uint, val: u32) {
		match reg {
			0..30	=> self.register[reg] = val,
			31		=> {},
			_		=> fail!(fmt!("tried to write to nonexistant register %d", reg as int))
		};
	}

	fn args(data: u32) -> (u32, u32, u32) {
		(data >> 21, data >> 16, data >> 11)
	}

	fn args_literal(data: u32) -> (u32, u32, u32) {
		(data >> 21, data >> 16, data & 0xFFFF)
	}

	fn exec_op(&mut self, data: u32, exp: &fn(a: u32, b: u32) -> u32) {
		let (r_c, r_a, r_b) = Beta::args(data);
		let a = self.read_reg(r_a as uint);
		let b = self.read_reg(r_b as uint);
		self.write_reg(r_c as uint, exp(a, b));
		println(fmt!("fn \"%?\"", exp));
	}

	fn exec_op_lit(&mut self, data: u32, exp: &fn(a: u32, b: u32) -> u32) {
		let (r_c, r_a, lit) = Beta::args_literal(data);
		let a = self.read_reg(r_a as uint);
		self.write_reg(r_c as uint, exp(a, lit));
		println(fmt!("fn \"%?\"", exp));
	}

	/* INSTRUCTION SET */

	fn add(&mut self, data: u32) {
		self.exec_op(data, |a, b| a + b);
	}

	fn addc(&mut self, data: u32) {
		self.exec_op_lit(data, |a, b| a + b);
	}

	fn and(&mut self, data: u32) {
		self.exec_op(data, |a, b| a & b);
	}

	fn andc(&mut self, data: u32) {
		self.exec_op_lit(data, |a, b| a & b);
	}

	fn beq(&mut self, data: u32) {
		let (r_c, r_a, lit) = Beta::args_literal(data);
		let a = self.read_reg(r_a as uint);

		self.write_reg(r_c as uint, self.pc + 4);
		let displacement = (lit as i16 as u32)*4;
		let target = self.pc + 4 + displacement;

		if(a == 0) { self.pc = target; }
	}

	fn bne(&mut self, data: u32) {
		let (r_c, r_a, lit) = Beta::args_literal(data);
		let a = self.read_reg(r_a as uint);

		self.write_reg(r_c as uint, self.pc + 4);
		let displacement = (lit as i16 as u32)*4;
		let target = self.pc + 4 + displacement;

		if(a != 0) { self.pc = target; }
	}

	fn cmpeq(&mut self, data: u32) {
		self.exec_op(data, |a, b| if(a == b) {1} else {0});
	}

	fn cmpeqc(&mut self, data: u32) {
		self.exec_op_lit(data, |a, b| if(a == b) {1} else {0});
	}

	fn cmple(&mut self, data: u32) {
		self.exec_op(data, |a, b| if(a <= b) {1} else {0});
	}

	fn cmplec(&mut self, data: u32) {
		self.exec_op_lit(data, |a, b| if(a <= b) {1} else {0});
	}

	fn cmplt(&mut self, data: u32) {
		self.exec_op(data, |a, b| if(a < b) {1} else {0});
	}

	fn cmpltc(&mut self, data: u32) {
		self.exec_op_lit(data, |a, b| if(a < b) {1} else {0});
	}

	fn div(&mut self, data: u32) {
		self.exec_op(data, |a, b| a / b);
	}

	fn divc(&mut self, data: u32) {
		self.exec_op_lit(data, |a, b| a / b);
	}

	fn jmp(&mut self, data: u32) {
		let (r_c, r_a, _lit) = Beta::args_literal(data);

		self.write_reg(r_c as uint, self.pc + 4);
		self.pc = self.read_reg(r_a as uint) & 0xFFFFFFFC;
	}

	fn ld(&mut self, data: u32) {
		let (r_c, r_a, lit) = Beta::args_literal(data);
		let a = self.read_reg(r_a as uint);
		let value = self.mem.read_word(a + lit);
		self.write_reg(r_c as uint, value);
	}

	fn ldr(&mut self, data: u32) {
		let (r_c, r_a, lit) = Beta::args_literal(data);
		let a = self.read_reg(r_a as uint);

		let ea = (self.pc & 0x7FFFFFFF) + 4 + (lit as i16 as u32)*4;
		let memval = self.mem.read_word(ea);

		self.write_reg(r_c as uint, memval);

		if(a != 0b11111) { warn!("\"The Ra field is ignored and should be 11111.\" It is not."); }
	}

	fn mul(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn mulc(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn or(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn orc(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn shl(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn shlc(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn shr(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn shrc(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn sra(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn srac(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn sub(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn subc(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn st(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn xor(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn xorc(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn xnor(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}

	fn xnorc(&mut self, data: u32) {
		println(fmt!("data: %d", data as int));
	}
}

fn main() {
	let mut b = ~Beta {
		pc: 10u32,
		register: [3u32, ..31],
		mem: Mem {
			data: [0u8, ..4*1024]
		}
	};

	b.reset();
	b.tick();
	println("success");
}
<|endoftext|>


<commit_before>//! Static blog generation.

use std::fs::{self, File};
use std::ops::Deref;
use std::path::Path;
use std::io;
use std::io::prelude::*;

use ammonia::Ammonia;
use chrono::{self, Datelike, DateTime, TimeZone, UTC};
use hoedown::{self, Markdown};
use hoedown::renderer::html;
use hoedown::Render;
use yaml::YamlLoader;
use yaml::ScanError;

/// The length of a blog post preview.
const PREVIEW_LENGTH: usize = 200;

/// An owned string containing HTML.
#[derive(Debug, Clone)]
pub struct Html(String);

impl Deref for Html {
    type Target = String;

    fn deref(&self) -> &String {
        &self.0
    }
}

/// A struct representing a blog post.
#[derive(Debug, Clone)]
pub struct Post {
    /// The title of the post.
    pub title: String,

    /// The time that the post was written.
    pub date: DateTime<UTC>,

    /// The post rendered as HTML.
    pub html: Html,
}

impl Post {
    /// The human-readable format that the date should appear as when rendering the post.
    pub const DATE_FORMAT: &'static str = "%B %e, %Y";

    /// Returns the URL to retrieve a blog post.
    pub fn url(&self) -> String {
        let escaped_title = self.title.to_lowercase().replace(" ", "-");
        format!("/blog/{}/{}/{}/{}",
                self.date.year(),
                self.date.month(),
                self.date.day(),
                escaped_title)
    }

    /// Creates a summarized version of the blog post.
    pub fn to_summary(&self) -> Summary {
        let mut ammonia = Ammonia::default();

        ammonia.url_relative = true;

        // Add a link at the end of the preview, then sanitize the HTML and close any unclosed
        // tags.
        let preview_link = format!(r#"... Continue→"#, self.url());
        let preview = &self.html
                           .chars()
                           .take(PREVIEW_LENGTH)
                           .chain(preview_link.chars())
                           .collect::<String>();
        Summary {
            title: self.title.to_owned(),
            date: self.date.format(Self::DATE_FORMAT).to_string(),
            preview: ammonia.clean(preview).to_owned(),
            url: self.url(),
        }
    }
}

/// A brief summary of a blog post.
#[derive(Serialize, Debug)]
pub struct Summary {
    /// The title of the post.
    pub title: String,

    /// The date the post was written.
    pub date: String,

    /// A short preview of the post.
    pub preview: String,

    /// A URL to reach the full post.
    pub url: String,
}

quick_error! {
    /// Encapsulates errors that might occur while parsing a blog post.
    #[derive(Debug)]
    pub enum PostParseError {
        /// There was a problem reading the file.
        Io(err: io::Error) {
            from()
            description("io error")
            display("I/O error: {}", err)
            cause(err)
        }

        /// There was a syntax error in the metadata.
        MetadataSyntax(err: String) {
            from()
            from(err: ScanError) -> (err.to_string())
            from(err: chrono::ParseError) -> (err.to_string())
            description("yaml parsing error")
            display("Error parsing metadata: {}", err)
        }
    }
}

fn parse_post(post: &str) -> Result<Post, PostParseError> {
    use hoedown::{AUTOLINK, FENCED_CODE, TABLES};
    use self::PostParseError::*;

    // Read up to the first double newline to determine the end of metadata.
    //
    // TODO: This could probably be more robust. Investigate separating the metadata and
    // markdown as separate YAML documents.
    let contents = post.splitn(2, "\n\n").collect::<Vec<_>>();
    let metadata = contents[0];
    let metadata = try!(YamlLoader::load_from_str(metadata))[0].to_owned();

    let content = contents[1];
    let markdown = Markdown::new(content)
                       .extensions(AUTOLINK)
                       .extensions(FENCED_CODE)
                       .extensions(TABLES);

    let mut html = hoedown::Html::new(html::Flags::empty(), 0);
    let html_string = html.render(&markdown).to_str().unwrap().to_owned();

    let title = try!(metadata["title"]
                         .as_str()
                         .ok_or(PostParseError::MetadataSyntax("could not find key 'title'"
                                                                   .to_owned())))
                    .to_owned();

    let date_string = try!(metadata["date"]
                               .as_str()
                               .ok_or(PostParseError::MetadataSyntax("could not find 'key'"
                                                                         .to_owned())));
    let date = try!(UTC.datetime_from_str(date_string, "%l:%M%P %m/%d/%y"));

    Ok(Post {
        title: title,
        date: date,
        html: Html(html_string),
    })
}

/// Retrieves blog post content and metadata by parsing all markdown files in a given directory.
pub fn parse_posts(directory: &Path) -> Result<Vec<Post>, PostParseError> {
    try!(fs::read_dir(directory))
        .into_iter()
        .map(|entry| {
            let mut file = try!(File::open(try!(entry).path()));

            let mut contents = String::new();
            try!(file.read_to_string(&mut contents));

            parse_post(&contents)
        })
        .collect()
}
<commit_msg>fix incorrect usage of hoedown API<commit_after>//! Static blog generation.

use std::fs::{self, File};
use std::ops::Deref;
use std::path::Path;
use std::io;
use std::io::prelude::*;

use ammonia::Ammonia;
use chrono::{self, Datelike, DateTime, TimeZone, UTC};
use hoedown::{self, Markdown};
use hoedown::renderer::html;
use hoedown::Render;
use yaml::YamlLoader;
use yaml::ScanError;

/// The length of a blog post preview.
const PREVIEW_LENGTH: usize = 200;

/// An owned string containing HTML.
#[derive(Debug, Clone)]
pub struct Html(String);

impl Deref for Html {
    type Target = String;

    fn deref(&self) -> &String {
        &self.0
    }
}

/// A struct representing a blog post.
#[derive(Debug, Clone)]
pub struct Post {
    /// The title of the post.
    pub title: String,

    /// The time that the post was written.
    pub date: DateTime<UTC>,

    /// The post rendered as HTML.
    pub html: Html,
}

impl Post {
    /// The human-readable format that the date should appear as when rendering the post.
    pub const DATE_FORMAT: &'static str = "%B %e, %Y";

    /// Returns the URL to retrieve a blog post.
    pub fn url(&self) -> String {
        let escaped_title = self.title.to_lowercase().replace(" ", "-");
        format!("/blog/{}/{}/{}/{}",
                self.date.year(),
                self.date.month(),
                self.date.day(),
                escaped_title)
    }

    /// Creates a summarized version of the blog post.
    pub fn to_summary(&self) -> Summary {
        let mut ammonia = Ammonia::default();

        ammonia.url_relative = true;

        // Add a link at the end of the preview, then sanitize the HTML and close any unclosed
        // tags.
        let preview_link = format!(r#"... Continue→"#, self.url());
        let preview = &self.html
                           .chars()
                           .take(PREVIEW_LENGTH)
                           .chain(preview_link.chars())
                           .collect::<String>();
        Summary {
            title: self.title.to_owned(),
            date: self.date.format(Self::DATE_FORMAT).to_string(),
            preview: ammonia.clean(preview).to_owned(),
            url: self.url(),
        }
    }
}

/// A brief summary of a blog post.
#[derive(Serialize, Debug)]
pub struct Summary {
    /// The title of the post.
    pub title: String,

    /// The date the post was written.
    pub date: String,

    /// A short preview of the post.
    pub preview: String,

    /// A URL to reach the full post.
    pub url: String,
}

quick_error! {
    /// Encapsulates errors that might occur while parsing a blog post.
    #[derive(Debug)]
    pub enum PostParseError {
        /// There was a problem reading the file.
        Io(err: io::Error) {
            from()
            description("io error")
            display("I/O error: {}", err)
            cause(err)
        }

        /// There was a syntax error in the metadata.
        MetadataSyntax(err: String) {
            from()
            from(err: ScanError) -> (err.to_string())
            from(err: chrono::ParseError) -> (err.to_string())
            description("yaml parsing error")
            display("Error parsing metadata: {}", err)
        }
    }
}

fn parse_post(post: &str) -> Result<Post, PostParseError> {
    use hoedown::{AUTOLINK, FENCED_CODE, TABLES};
    use self::PostParseError::*;

    // Read up to the first double newline to determine the end of metadata.
    //
    // TODO: This could probably be more robust. Investigate separating the metadata and
    // markdown as separate YAML documents.
    let contents = post.splitn(2, "\n\n").collect::<Vec<_>>();
    let metadata = contents[0];
    let metadata = try!(YamlLoader::load_from_str(metadata))[0].to_owned();

    let content = contents[1];
    let markdown = Markdown::new(content).extensions(AUTOLINK | FENCED_CODE | TABLES);

    let mut html = hoedown::Html::new(html::Flags::empty(), 0);
    let html_string = html.render(&markdown).to_str().unwrap().to_owned();

    let title = try!(metadata["title"]
                         .as_str()
                         .ok_or(PostParseError::MetadataSyntax("could not find key 'title'"
                                                                   .to_owned())))
                    .to_owned();

    let date_string = try!(metadata["date"]
                               .as_str()
                               .ok_or(PostParseError::MetadataSyntax("could not find 'key'"
                                                                         .to_owned())));
    let date = try!(UTC.datetime_from_str(date_string, "%l:%M%P %m/%d/%y"));

    Ok(Post {
        title: title,
        date: date,
        html: Html(html_string),
    })
}

/// Retrieves blog post content and metadata by parsing all markdown files in a given directory.
pub fn parse_posts(directory: &Path) -> Result<Vec<Post>, PostParseError> {
    try!(fs::read_dir(directory))
        .into_iter()
        .map(|entry| {
            let mut file = try!(File::open(try!(entry).path()));

            let mut contents = String::new();
            try!(file.read_to_string(&mut contents));

            parse_post(&contents)
        })
        .collect()
}
<|endoftext|>


<commit_before>use rustc_serialize::json;
use super::mpfr::Mpfr;
use gmp::mpz::Mpz;

#[test]
fn test_set() {
    let mut a: Mpfr = From::<i64>::from(1000);
    let b: Mpfr = From::<i64>::from(5000);
    assert!(a != b);
    a.set(&b);
    assert!(a == b);
}

#[test]
fn test_eq() {
    let x: Mpfr = From::<f64>::from(1.234567);
    let y: Mpfr = From::<f64>::from(1.234567);
    let z: Mpfr = From::<f64>::from(1.234568);

    assert!(x == y);
    assert!(x != z);
    assert!(y != z);
}

#[test]
fn test_ord() {
    let x: Mpfr = From::<i64>::from(-1048576);
    let y: Mpfr = From::<i64>::from(2);
    let z: Mpfr = From::<i64>::from(1048576);

    assert!(x < y && x < z && y < z);
    assert!(x <= x && x <= y && x <= z && y <= z);
    assert!(z > y && z > x && y > x);
    assert!(z >= z && z >= y && z >= x && y >= x);
}

#[test]
#[should_panic]
fn test_div_zero() {
    let x: Mpfr = From::<i64>::from(1);
    let y = Mpfr::zero(1);
    x / y;
}

#[test]
fn test_clone() {
    let a: Mpfr = From::<i64>::from(100);
    let b = a.clone();
    assert!(b == a);
}

#[test]
fn test_add() {
    let a: Mpfr = From::<i64>::from(15);
    let b: Mpfr = From::<i64>::from(20);
    let result: Mpfr = From::<i64>::from(35);

    assert!(&a + b == result);
    assert!(&a + 20 == result);
    assert!(20 + &a == result);
    assert!(&a + 20.0 == result);
    assert!(20.0 + &a == result);
}

#[test]
fn test_sub() {
    let a: Mpfr = From::<i64>::from(15);
    let b: Mpfr = From::<i64>::from(20);
    let result: Mpfr = From::<i64>::from(-5);

    assert!(&a - &b == result);
    assert!(&a - 20 == result);
    assert!(15 - &b == result);
    assert!(&a - 20.0 == result);
    assert!(15.0 - &b == result);
}

#[test]
fn test_mul() {
    let a: Mpfr = From::<i64>::from(15);
    let b: Mpfr = From::<i64>::from(20);
    let result: Mpfr = From::<i64>::from(300);

    assert!(&a * b == result);
    assert!(&a * 20 == result);
    assert!(20 * &a == result);
    assert!(&a * 20.0 == result);
    assert!(20.0 * &a == result);
}

#[test]
fn test_div() {
    let a: Mpfr = From::<i64>::from(15);
    let b: Mpfr = From::<i64>::from(20);
    let result: Mpfr = From::<f64>::from(0.75);

    assert!(&a / &b == result);
    assert!(&a / 20 == result);
    assert!(15 / &b == result);
    assert!(&a / 20.0 == result);
    assert!(15.0 / &b == result);
}

#[test]
fn test_rounding() {
    let a: Mpfr = From::<f64>::from(2.4999);
    let b: Mpfr = From::<f64>::from(2.5);
    let two: Mpfr = From::<i64>::from(2);
    let three: Mpfr = From::<i64>::from(3);

    assert!(a.floor() == two);
    assert!(a.round() == two);
    assert!(a.ceil() == three);

    assert!(b.floor() == two);
    assert!(b.round() == three);
    assert!(b.ceil() == three);
}

#[test]
fn test_pow_root() {
    let a: Mpfr = From::<f64>::from(2.654);
    let two: Mpfr = From::<i64>::from(2);
    let three: Mpfr = From::<i64>::from(3);
    let asq = &a * &a;
    let acb = &a * &a * &a;

    assert!(a.pow(&two) == asq);
    assert!(a.pow(&three) == acb);
    assert!(asq.sqrt() == a);
    assert!(acb.cbrt() == a);
    assert!(asq.root(2) == a);
    assert!(acb.root(3) == a);
}

#[test]
fn test_exp_log() {
    let a: Mpfr = From::<i64>::from(1);
    let b: Mpfr = From::<f64>::from(2.718281828459045);

    assert!(a.exp() == b);
    assert!(b.log() == a);
}

#[test]
fn test_new_from_str() {
    let a: Mpfr = From::<i64>::from(1);
    let b: Mpfr = Mpfr::new_from_str("1", 10).unwrap();

    assert!(a == b);

    let a: Mpfr = From::<f64>::from(1.234);
    let b: Mpfr = Mpfr::new_from_str("1.234", 10).unwrap();

    assert!(a == b);
}

#[test]
fn test_new2_from_str() {
    let epsilon = 1e-100f64;

    {
        let a: Mpfr = From::<i64>::from(1);
        let b: Mpfr = Mpfr::new2_from_str(200, "1", 10).unwrap();
        let c = a.clone();
        let d = b.clone();

        assert!(a - b < From::from(epsilon) || c - d > From::from(-epsilon));
    }

    {
        let a: Mpfr = From::<f64>::from(1.234);
        let b: Mpfr = Mpfr::new2_from_str(200, "1.234", 10).unwrap();
        let c = a.clone();
        let d = b.clone();

        assert!(a - b < From::from(epsilon) || c - d > From::from(-epsilon));
    }
}

#[test]
fn test_mpfr_macro() {
    let a: Mpfr = Mpfr::new_from_str("0.123456789012345678901234567890", 10).unwrap();
    assert_eq!(mpfr!(0.123456789012345678901234567890), a);
    let b: Mpfr = Mpfr::new_from_str("-5.", 10).unwrap();
    assert_eq!(mpfr!(-5.), b);
}

#[test]
fn test_debug() {
    let a: Mpfr = Mpfr::new2_from_str(128, "1.23456789123456789123456789123456789e5", 10).unwrap();

    assert_eq!(format!("{:?}", a), "1.23456789123456789123456789123456789e+05");
}

#[test]
fn test_display() {
    let a: Mpfr = Mpfr::new2_from_str(128, "1.23456789123456789123456789123456789e5", 10).unwrap();

    assert_eq!(format!("{}", a), "1.23456789123456789123456789123456789e+05");
}

#[test]
fn test_to_string() {
    let a: Mpfr = Mpfr::new2_from_str(128, "1.23456789123456789123456789123456789e5", 10).unwrap();

    assert_eq!(a.to_string(), "1.23456789123456789123456789123456789e+05");
}

#[test]
fn test_to_string_new2_from_str() {
    let a: Mpfr = Mpfr::new2_from_str(128, "1.23456789123456789123456789123456789e5", 10).unwrap();

    assert!(a == Mpfr::new2_from_str(128, a.to_string(), 10).unwrap());
}

#[test]
fn test_into() {
    let a: Mpfr = mpfr!(0.1234);
    let b: i64 = (&a).into();
    assert_eq!(b, 0);
    let c: f64 = (&a).into();
    assert_eq!(c, 0.1234);
    let d: u64 = (&a).into();
    assert_eq!(d, 0);

    let zero: Mpz = From::from(0i64);
    assert_eq!(Into::<Mpz>::into(&a), zero);
}

#[test]
fn test_abs() {
    let a: Mpfr = From::<i64>::from(1);
    let b: Mpfr = From::<i64>::from(-1);

    assert!(a.abs() == b.abs());
}

#[test]
fn test_rustc_serialize() {
    #[derive(RustcDecodable, RustcEncodable, PartialEq)]
    struct Test {
        price: Mpfr,
    }
    let a: Test = Test { price: From::<f64>::from(0.75) };
    assert_eq!(json::encode(&a).unwrap(), "{\"price\":\"7.5e-01\"}");
    let b: Test = json::decode("{\"price\": \"0.75\"}").unwrap();
    assert!(a == b);
}
<commit_msg>Additional tests<commit_after>use rustc_serialize::json;
use super::mpfr::Mpfr;
use gmp::mpz::Mpz;

#[test]
fn test_set() {
    let mut a: Mpfr = From::<i64>::from(1000);
    let b: Mpfr = From::<i64>::from(5000);
    assert!(a != b);
    a.set(&b);
    assert!(a == b);
}

#[test]
fn test_eq() {
    let x: Mpfr = From::<f64>::from(1.234567);
    let y: Mpfr = From::<f64>::from(1.234567);
    let z: Mpfr = From::<f64>::from(1.234568);

    assert!(x == y);
    assert!(x != z);
    assert!(y != z);
}

#[test]
fn test_ord() {
    let x: Mpfr = From::<i64>::from(-1048576);
    let y: Mpfr = From::<i64>::from(2);
    let z: Mpfr = From::<i64>::from(1048576);

    assert!(x < y && x < z && y < z);
    assert!(x <= x && x <= y && x <= z && y <= z);
    assert!(z > y && z > x && y > x);
    assert!(z >= z && z >= y && z >= x && y >= x);
}

#[test]
#[should_panic]
fn test_div_zero() {
    let x: Mpfr = From::<i64>::from(1);
    let y = Mpfr::zero(1);
    x / y;
}

#[test]
fn test_clone() {
    let a: Mpfr = From::<i64>::from(100);
    let b = a.clone();
    assert!(b == a);
}

#[test]
fn test_add() {
    let a: Mpfr = From::<i64>::from(15);
    let b: Mpfr = From::<i64>::from(20);
    let result: Mpfr = From::<i64>::from(35);

    assert!(&a + &b == result);
    assert!(&a + b.clone() == result);
    assert!(a.clone() + &b == result);
    assert!(a.clone() + b.clone() == result);
    assert!(&a + 20 == result);
    assert!(20 + &a == result);
    assert!(&a + 20.0 == result);
    assert!(20.0 + &a == result);
    assert!(a.clone() + 20 == result);
    assert!(20 + a.clone() == result);
    assert!(a.clone() + 20.0 == result);
    assert!(20.0 + a.clone() == result);
}

#[test]
fn test_add_prec() {
    let high_prec = 128;
    let a: Mpfr = Mpfr::new2_from_str(high_prec, "15", 10).unwrap();
    let b: Mpfr = From::<i64>::from(20);

    assert!((&a + &b).get_prec() == high_prec);
    assert!((&b + &a).get_prec() == high_prec);
    assert!((a.clone() + &b).get_prec() == high_prec);
    assert!((b.clone() + &a).get_prec() == high_prec);
    assert!((a.clone() + b.clone()).get_prec() == high_prec);
    assert!((b.clone() + a.clone()).get_prec() == high_prec);
    assert!((&a + 20).get_prec() == high_prec);
    assert!((20 + &a).get_prec() == high_prec);
    assert!((&a + 20.0).get_prec() == high_prec);
    assert!((20.0 + &a).get_prec() == high_prec);
}

#[test]
fn test_sub() {
    let a: Mpfr = From::<i64>::from(15);
    let b: Mpfr = From::<i64>::from(20);
    let result: Mpfr = From::<i64>::from(-5);

    assert!(&a - &b == result);
    assert!(&a - b.clone() == result);
    assert!(a.clone() - &b == result);
    assert!(a.clone() - b.clone() == result);
    assert!(&a - 20 == result);
    assert!(15 - &b == result);
    assert!(&a - 20.0 == result);
    assert!(15.0 - &b == result);
    assert!(a.clone() - 20 == result);
    assert!(15 - b.clone() == result);
    assert!(a.clone() - 20.0 == result);
    assert!(15.0 - b.clone() == result);
}

#[test]
fn test_sub_prec() {
    let high_prec = 128;
    let a: Mpfr = Mpfr::new2_from_str(high_prec, "15", 10).unwrap();
    let b: Mpfr = From::<i64>::from(20);

    assert!((&a - &b).get_prec() == high_prec);
    assert!((&b - &a).get_prec() == high_prec);
    assert!((a.clone() - &b).get_prec() == high_prec);
    assert!((b.clone() - &a).get_prec() == high_prec);
    assert!((a.clone() - b.clone()).get_prec() == high_prec);
    assert!((b.clone() - a.clone()).get_prec() == high_prec);
    assert!((&a - 20).get_prec() == high_prec);
    assert!((20 - &a).get_prec() == high_prec);
    assert!((&a - 20.0).get_prec() == high_prec);
    assert!((20.0 - &a).get_prec() == high_prec);
}

#[test]
fn test_mul() {
    let a: Mpfr = From::<i64>::from(15);
    let b: Mpfr = From::<i64>::from(20);
    let result: Mpfr = From::<i64>::from(300);

    assert!(&a * &b == result);
    assert!(&a * b.clone() == result);
    assert!(a.clone() * &b == result);
    assert!(a.clone() * b.clone() == result);
    assert!(&a * 20 == result);
    assert!(20 * &a == result);
    assert!(&a * 20.0 == result);
    assert!(20.0 * &a == result);
    assert!(a.clone() * 20 == result);
    assert!(20 * a.clone() == result);
    assert!(a.clone() * 20.0 == result);
    assert!(20.0 * a.clone() == result);
}

#[test]
fn test_mul_prec() {
    let high_prec = 128;
    let a: Mpfr = Mpfr::new2_from_str(high_prec, "15", 10).unwrap();
    let b: Mpfr = From::<i64>::from(20);

    assert!((&a * &b).get_prec() == high_prec);
    assert!((&b * &a).get_prec() == high_prec);
    assert!((a.clone() * &b).get_prec() == high_prec);
    assert!((b.clone() * &a).get_prec() == high_prec);
    assert!((a.clone() * b.clone()).get_prec() == high_prec);
    assert!((b.clone() * a.clone()).get_prec() == high_prec);
    assert!((&a * 20).get_prec() == high_prec);
    assert!((20 * &a).get_prec() == high_prec);
    assert!((&a * 20.0).get_prec() == high_prec);
    assert!((20.0 * &a).get_prec() == high_prec);
}

#[test]
fn test_div() {
    let a: Mpfr = From::<i64>::from(15);
    let b: Mpfr = From::<i64>::from(20);
    let result: Mpfr = From::<f64>::from(0.75);

    assert!(&a / &b == result);
    assert!(&a / b.clone() == result);
    assert!(a.clone() / &b == result);
    assert!(a.clone() / b.clone() == result);
    assert!(&a / 20 == result);
    assert!(15 / &b == result);
    assert!(&a / 20.0 == result);
    assert!(15.0 / &b == result);
    assert!(a.clone() / 20 == result);
    assert!(15 / b.clone() == result);
    assert!(a.clone() / 20.0 == result);
    assert!(15.0 / b.clone() == result);
}

#[test]
fn test_div_prec() {
    let high_prec = 128;
    let a: Mpfr = Mpfr::new2_from_str(high_prec, "15", 10).unwrap();
    let b: Mpfr = From::<i64>::from(20);

    assert!((&a / &b).get_prec() == high_prec);
    assert!((&b / &a).get_prec() == high_prec);
    assert!((a.clone() / &b).get_prec() == high_prec);
    assert!((b.clone() / &a).get_prec() == high_prec);
    assert!((a.clone() / b.clone()).get_prec() == high_prec);
    assert!((b.clone() / a.clone()).get_prec() == high_prec);
    assert!((&a / 20).get_prec() == high_prec);
    assert!((20 / &a).get_prec() == high_prec);
    assert!((&a / 20.0).get_prec() == high_prec);
    assert!((20.0 / &a).get_prec() == high_prec);
}

#[test]
fn test_rounding() {
    let a: Mpfr = From::<f64>::from(2.4999);
    let b: Mpfr = From::<f64>::from(2.5);
    let two: Mpfr = From::<i64>::from(2);
    let three: Mpfr = From::<i64>::from(3);

    assert!(a.floor() == two);
    assert!(a.round() == two);
    assert!(a.ceil() == three);

    assert!(b.floor() == two);
    assert!(b.round() == three);
    assert!(b.ceil() == three);
}

#[test]
fn test_pow_root() {
    let a: Mpfr = From::<f64>::from(2.654);
    let two: Mpfr = From::<i64>::from(2);
    let three: Mpfr = From::<i64>::from(3);
    let asq = &a * &a;
    let acb = &a * &a * &a;

    assert!(a.pow(&two) == asq);
    assert!(a.pow(&three) == acb);
    assert!(asq.sqrt() == a);
    assert!(acb.cbrt() == a);
    assert!(asq.root(2) == a);
    assert!(acb.root(3) == a);
}

#[test]
fn test_exp_log() {
    let a: Mpfr = From::<i64>::from(1);
    let b: Mpfr = From::<f64>::from(2.718281828459045);

    assert!(a.exp() == b);
    assert!(b.log() == a);
}

#[test]
fn test_new_from_str() {
    let a: Mpfr = From::<i64>::from(1);
    let b: Mpfr = Mpfr::new_from_str("1", 10).unwrap();

    assert!(a == b);

    let a: Mpfr = From::<f64>::from(1.234);
    let b: Mpfr = Mpfr::new_from_str("1.234", 10).unwrap();

    assert!(a == b);
}

#[test]
fn test_new2_from_str() {
    let epsilon = 1e-100f64;

    {
        let a: Mpfr = From::<i64>::from(1);
        let b: Mpfr = Mpfr::new2_from_str(200, "1", 10).unwrap();
        let c = a.clone();
        let d = b.clone();

        assert!(a - b < From::from(epsilon) || c - d > From::from(-epsilon));
    }

    {
        let a: Mpfr = From::<f64>::from(1.234);
        let b: Mpfr = Mpfr::new2_from_str(200, "1.234", 10).unwrap();
        let c = a.clone();
        let d = b.clone();

        assert!(a - b < From::from(epsilon) || c - d > From::from(-epsilon));
    }
}

#[test]
fn test_mpfr_default_prec() {
    let default_prec = 53;

    let a: Mpfr = Mpfr::new_from_str("1.234", 10).unwrap();
    let b: Mpfr = From::from(1.234f64);
    let c: Mpfr = From::from(-1i64);
    let d: Mpfr = From::from(1u64);

    assert!(a.get_prec() == default_prec);
    assert!(b.get_prec() == default_prec);
    assert!(c.get_prec() == default_prec);
    assert!(d.get_prec() == default_prec);
}

#[test]
fn test_mpfr_get_prec() {
    let prec = 128;

    let a: Mpfr = Mpfr::new2_from_str(prec, "1.234", 10).unwrap();
    let b: Mpfr = Mpfr::new2(prec);

    assert!(a.get_prec() == prec);
    assert!(b.get_prec() == prec);
}

#[test]
fn test_mpfr_set_prec() {
    let new_prec = 128;
    let mut a = Mpfr::new_from_str("1.234", 10).unwrap();

    assert!(a.get_prec() != new_prec);

    a.set_prec(new_prec);

    assert!(a == Mpfr::nan());
    assert!(a.get_prec() == new_prec);
}

#[test]
fn test_mpfr_macro() {
    let a: Mpfr = Mpfr::new_from_str("0.123456789012345678901234567890", 10).unwrap();
    assert_eq!(mpfr!(0.123456789012345678901234567890), a);
    let b: Mpfr = Mpfr::new_from_str("-5.", 10).unwrap();
    assert_eq!(mpfr!(-5.), b);
}

#[test]
fn test_debug() {
    let a: Mpfr = Mpfr::new2_from_str(128, "1.23456789123456789123456789123456789e5", 10).unwrap();

    assert_eq!(format!("{:?}", a), "1.23456789123456789123456789123456789e+05");
}

#[test]
fn test_display() {
    let a: Mpfr = Mpfr::new2_from_str(128, "1.23456789123456789123456789123456789e5", 10).unwrap();

    assert_eq!(format!("{}", a), "1.23456789123456789123456789123456789e+05");
}

#[test]
fn test_to_string() {
    let a: Mpfr = Mpfr::new2_from_str(128, "1.23456789123456789123456789123456789e5", 10).unwrap();

    assert_eq!(a.to_string(), "1.23456789123456789123456789123456789e+05");
}

#[test]
fn test_to_string_new2_from_str() {
    let a: Mpfr = Mpfr::new2_from_str(128, "1.23456789123456789123456789123456789e5", 10).unwrap();

    assert!(a == Mpfr::new2_from_str(128, a.to_string(), 10).unwrap());
}

#[test]
fn test_into() {
    let a: Mpfr = mpfr!(0.1234);
    let b: i64 = (&a).into();
    assert_eq!(b, 0);
    let c: f64 = (&a).into();
    assert_eq!(c, 0.1234);
    let d: u64 = (&a).into();
    assert_eq!(d, 0);

    let zero: Mpz = From::from(0i64);
    assert_eq!(Into::<Mpz>::into(&a), zero);
}

#[test]
fn test_abs() {
    let a: Mpfr = From::<i64>::from(1);
    let b: Mpfr = From::<i64>::from(-1);

    assert!(a.abs() == b.abs());
}

#[test]
fn test_rustc_serialize() {
    #[derive(RustcDecodable, RustcEncodable, PartialEq)]
    struct Test {
        price: Mpfr,
    }
    let a: Test = Test { price: From::<f64>::from(0.75) };
    assert_eq!(json::encode(&a).unwrap(), "{\"price\":\"7.5e-01\"}");
    let b: Test = json::decode("{\"price\": \"0.75\"}").unwrap();
    assert!(a == b);
}
<|endoftext|>


<commit_before>use crate::async_runtime;
use crate::config::{Config, EnvConfig};
use crate::error::{Error, ErrorKind, Result};
use crate::git::Git;
use crate::github_api::Client;
use getopts::Options;
use std::env;
use std::ffi::OsStr;
use std::fs;
use std::path::PathBuf;

fn fix_ssh_url(mut url: String) -> String {
    if url.starts_with("git@") {
        // Examples:
        //  git@service.com:user/repo.git -> ssh://git@service.com:user/repo.git
        url.insert_str(0, "ssh://");
    }
    if url.starts_with("ssh://") {
        // Note: Convert SSH protocol URL port number. In Git protocol port number can be omitted
        // but url::Url::parse does not allow it. So if port number is not provided but colon is
        // put, we append default SSH port number 22.
        //
        // Examples:
        //  ssh://git@service.com:user/repo.git -> ssh://git@service.com:22/user/repo.git
        //  ssh://git@ssh.dev.azure.com:v3/team/repo/repo -> ssh://git@ssh.dev.azure.com:22/v3/team/repo/repo
        let scheme_len = "ssh://".len();
        if let Some(i) = &url[scheme_len..].find(':') {
            // Check if port number is omitted
            let after_colon = url[scheme_len + i + 1..].as_bytes();
            if after_colon.is_empty() || !after_colon[0].is_ascii_digit() {
                url.insert_str(scheme_len + i + 1, "22/"); // Insert port number after colon
            }
        }
    }
    url
}

#[cfg_attr(feature = "cargo-clippy", allow(clippy::large_enum_variant))]
#[derive(Debug)]
pub enum Parsed {
    Help(String),
    Version(&'static str),
    OpenPage(Config),
}

fn normalize_repo_format(mut slug: String, env: &EnvConfig) -> Result<String> {
    if slug.is_empty() {
        return Error::err(ErrorKind::BrokenRepoFormat { input: slug });
    }

    if slug.starts_with("git@")
        || slug.starts_with("https://")
        || slug.starts_with("http://")
        || slug.starts_with("ssh://")
    {
        if !slug.ends_with(".git") {
            slug.push_str(".git");
        }
        return Ok(slug);
    }

    match slug.chars().filter(|c| *c == '/').count() {
        1 => Ok(format!("https://github.com/{}.git", slug)),
        2 => Ok(format!("https://{}.git", slug)),
        0 => {
            let client = Client::build("api.github.com", &env.github_token, &env.https_proxy)?;
            async_runtime::blocking(client.most_popular_repo_by_name(&slug))
                .map(|repo| repo.clone_url)
        }
        _ => Error::err(ErrorKind::BrokenRepoFormat { input: slug }),
    }
}

fn get_cwd(specified: Option<String>) -> Result<PathBuf> {
    if let Some(dir) = specified {
        let p = fs::canonicalize(&dir)?;
        if !p.exists() {
            return Error::err(ErrorKind::SpecifiedDirNotExist { dir });
        }
        Ok(p)
    } else {
        Ok(env::current_dir()?.canonicalize()?)
    }
}

const USAGE: &str = "\
Usage: git brws [Options] {Args}

  Open a repository, file, commit, diff or pull request, issue or project's
  website in your web browser from command line.
  GitHub, Bitbucket, GitLab, GitHub Enterprise, Azure DevOps are supported as
  hosting service.
  git-brws looks some environment variables for configuration. Please see
  https://github.com/rhysd/git-brws#readme for more details.

Examples:
  - Current repository:

    $ git brws

  - GitHub repository:

    $ git brws -r rhysd/git-brws

  - Most popular GitHub repository by name:

    $ git brws -r git-brws

  - File:

    $ git brws some/file.txt

  - Commit:

    $ git brws HEAD~3

  - Tag:

    $ git brws 0.10.0

  - Diff between commits:

    $ git brws HEAD~3..HEAD

  - Diff between topic and topic's merge base commit:

    $ git brws master...topic

  - Line 123 of file:

    $ git brws some/file.txt#L123

  - Range from line 123 to line 126 of file:

    $ git brws some/file.txt#L123-L126

  - Pull request page (for GitHub and GitHub Enterprise):

    $ git brws --pr

  - Website of repository at current directory

    $ git brws --website

  - Website of other repository

    $ git brws --website --repo react

  - Issue page:

    $ git brws '#8'";

impl Parsed {
    pub fn parse_iter<I>(argv: I) -> Result<Parsed>
    where
        I: IntoIterator,
        I::Item: AsRef<OsStr>,
    {
        let mut opts = Options::new();

        opts.optopt("r", "repo", "Shorthand format (repo, user/repo, host/user/repo) or Git URL you want to see. When only repo name is specified, most popular repository will be searched from GitHub", "REPO");
        opts.optopt(
            "b",
            "branch",
            "Branch name to browse. Default value is current branch",
            "BRANCH",
        );
        opts.optopt(
            "d",
            "dir",
            "Directory path to the repository. Default value is current working directory",
            "PATH",
        );
        opts.optopt("R", "remote", "Remote name (e.g. origin). Default value is a remote the current branch is tracking. If current branch tracks no branch, it falls back to 'origin'", "REMOTE");
        opts.optflag(
            "u",
            "url",
            "Output URL to stdout instead of opening in browser",
        );
        opts.optflag(
            "p",
            "pr",
            "Open pull request page instead of repository page. If not existing, open 'Create Pull Request' page",
        );
        opts.optflag(
            "w",
            "website",
            "Open website page instead of repository page (homepage URL for GitHub, GitLab pages, Bitbucket Cloud)",
        );
        opts.optflag(
            "B",
            "blame",
            "Open blame page instead of repository page. File path to blame must be passed also.",
        );
        opts.optflag("h", "help", "Print this help");
        opts.optflag("v", "version", "Show version");

        let matches = opts.parse(argv.into_iter().skip(1))?;

        if matches.opt_present("h") {
            return Ok(Parsed::Help(opts.usage(USAGE)));
        }

        if matches.opt_present("v") {
            return Ok(Parsed::Version(
                option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"),
            ));
        }

        let env = EnvConfig::from_iter(env::vars())?.with_global_env();
        let cwd = get_cwd(matches.opt_str("d"))?;
        let branch = matches.opt_str("b");
        let (repo_url, remote) = match (matches.opt_str("r"), matches.opt_str("R")) {
            (Some(repo), remote) => {
                if !matches.free.is_empty() {
                    return Error::err(ErrorKind::ArgsNotAllowed {
                        flag: "--repo {repo}",
                        args: matches.free,
                    });
                }
                (normalize_repo_format(repo, &env)?, remote)
            }
            (None, remote) => {
                let git = Git::new(&cwd, &env.git_command);
                let (url, remote) = if let Some(remote) = remote {
                    (git.remote_url(&remote)?, remote)
                } else {
                    git.tracking_remote_url(&branch)?
                };
                (url, Some(remote))
            }
        };

        let repo_url = fix_ssh_url(repo_url);

        Ok(Parsed::OpenPage(Config {
            repo_url,
            branch,
            cwd,
            stdout: matches.opt_present("u"),
            pull_request: matches.opt_present("p"),
            website: matches.opt_present("w"),
            blame: matches.opt_present("B"),
            args: matches.free,
            remote,
            env,
        }))
    }
}
<commit_msg>handle SCP-like syntax correctly (fix #24)<commit_after>use crate::async_runtime;
use crate::config::{Config, EnvConfig};
use crate::error::{Error, ErrorKind, Result};
use crate::git::Git;
use crate::github_api::Client;
use getopts::Options;
use std::env;
use std::ffi::OsStr;
use std::fs;
use std::path::PathBuf;

fn handle_scp_like_syntax(mut url: String) -> String {
    // ref: https://git-scm.com/book/en/v2/Git-on-the-Server-The-Protocols

    if url.contains("://") {
        // When url is a URL like https://server/project.git
        return url;
    }

    // When the target is an scp-like syntax: [user@]server:project.git
    // Handle ':' in the syntax. Note that GitHub user name may start with number (#24)
    if let Some(i) = url.find(':') {
        let after_colon = i + 1;
        if let Some(i) = url[after_colon..].find(':') {
            // When port number exists
            //  git@service.com:123:user/repo.git -> git@service.com:123/user/repo.git
            let i = after_colon + i;
            url.replace_range(i..i + 1, "/");
        } else {
            // When a port number is omitted, default SSH port is 22
            //  git@service.com:user/repo.git -> git@service.com:22/user/repo.git
            url.insert_str(after_colon, "22/"); // Insert port number after colon
        }
    }

    // Examples:
    //  git@service.com:22/user/repo.git -> ssh://git@service.com:22/user/repo.git
    url.insert_str(0, "ssh://");
    url
}

#[cfg_attr(feature = "cargo-clippy", allow(clippy::large_enum_variant))]
#[derive(Debug)]
pub enum Parsed {
    Help(String),
    Version(&'static str),
    OpenPage(Config),
}

fn is_scp_like_syntax_with_user(s: &str) -> bool {
    // SCP-like syntax like user@project:project
    // user@ cannot be omitted in SCP-like syntax because `s` can be a search query
    //
    // Note that checking it starts with "git@" does not work because SSH protocol for
    // visualstudio.com is "mycompany@vs-ssh.visualstudio.com:v3/project.git"
    if s.contains(char::is_whitespace) {
        return false; // Containing whitespace means it's a search query
    }
    for (i, c) in s.char_indices() {
        match c {
            '@' if i == 0 => return false,          // Seems query like "@rhysd"
            '@' => return s[i + 1..].contains(':'), // SCP-like syntax must also contain ':'
            ':' => return false,                    // '@' must be put before ':'
            _ => continue,
        }
    }
    false
}

fn normalize_repo_format(mut slug: String, env: &EnvConfig) -> Result<String> {
    if slug.is_empty() {
        return Error::err(ErrorKind::BrokenRepoFormat { input: slug });
    }

    // - URL like https://server/project
    // - SCP-like syntax user@project:project
    //
    // Note: user@ cannot be omitted in SCP-like syntax because `slug` can be search word
    if slug.starts_with("https://")
        || slug.starts_with("ssh://")
        || slug.starts_with("http://")
        || slug.starts_with("file://")
        || is_scp_like_syntax_with_user(&slug)
    {
        if !slug.ends_with(".git") {
            slug.push_str(".git");
        }
        return Ok(slug);
    }

    match slug.chars().filter(|c| *c == '/').count() {
        1 => Ok(format!("https://github.com/{}.git", slug)),
        2 => Ok(format!("https://{}.git", slug)),
        0 => {
            let client = Client::build("api.github.com", &env.github_token, &env.https_proxy)?;
            async_runtime::blocking(client.most_popular_repo_by_name(&slug))
                .map(|repo| repo.clone_url)
        }
        _ => Error::err(ErrorKind::BrokenRepoFormat { input: slug }),
    }
}

fn get_cwd(specified: Option<String>) -> Result<PathBuf> {
    if let Some(dir) = specified {
        let p = fs::canonicalize(&dir)?;
        if !p.exists() {
            return Error::err(ErrorKind::SpecifiedDirNotExist { dir });
        }
        Ok(p)
    } else {
        Ok(env::current_dir()?.canonicalize()?)
    }
}

const USAGE: &str = "\
Usage: git brws [Options] {Args}

  Open a repository, file, commit, diff or pull request, issue or project's
  website in your web browser from command line.
  GitHub, Bitbucket, GitLab, GitHub Enterprise, Azure DevOps are supported as
  hosting service.
  git-brws looks some environment variables for configuration. Please see
  https://github.com/rhysd/git-brws#readme for more details.

Examples:
  - Current repository:

    $ git brws

  - GitHub repository:

    $ git brws -r rhysd/git-brws

  - Most popular GitHub repository by name:

    $ git brws -r git-brws

  - File:

    $ git brws some/file.txt

  - Commit:

    $ git brws HEAD~3

  - Tag:

    $ git brws 0.10.0

  - Diff between commits:

    $ git brws HEAD~3..HEAD

  - Diff between topic and topic's merge base commit:

    $ git brws master...topic

  - Line 123 of file:

    $ git brws some/file.txt#L123

  - Range from line 123 to line 126 of file:

    $ git brws some/file.txt#L123-L126

  - Pull request page (for GitHub and GitHub Enterprise):

    $ git brws --pr

  - Website of repository at current directory

    $ git brws --website

  - Website of other repository

    $ git brws --website --repo react

  - Issue page:

    $ git brws '#8'";

impl Parsed {
    pub fn parse_iter<I>(argv: I) -> Result<Parsed>
    where
        I: IntoIterator,
        I::Item: AsRef<OsStr>,
    {
        let mut opts = Options::new();

        opts.optopt("r", "repo", "Shorthand format (repo, user/repo, host/user/repo) or Git URL you want to see. When only repo name is specified, most popular repository will be searched from GitHub", "REPO");
        opts.optopt(
            "b",
            "branch",
            "Branch name to browse. Default value is current branch",
            "BRANCH",
        );
        opts.optopt(
            "d",
            "dir",
            "Directory path to the repository. Default value is current working directory",
            "PATH",
        );
        opts.optopt("R", "remote", "Remote name (e.g. origin). Default value is a remote the current branch is tracking. If current branch tracks no branch, it falls back to 'origin'", "REMOTE");
        opts.optflag(
            "u",
            "url",
            "Output URL to stdout instead of opening in browser",
        );
        opts.optflag(
            "p",
            "pr",
            "Open pull request page instead of repository page. If not existing, open 'Create Pull Request' page",
        );
        opts.optflag(
            "w",
            "website",
            "Open website page instead of repository page (homepage URL for GitHub, GitLab pages, Bitbucket Cloud)",
        );
        opts.optflag(
            "B",
            "blame",
            "Open blame page instead of repository page. File path to blame must be passed also.",
        );
        opts.optflag("h", "help", "Print this help");
        opts.optflag("v", "version", "Show version");

        let matches = opts.parse(argv.into_iter().skip(1))?;

        if matches.opt_present("h") {
            return Ok(Parsed::Help(opts.usage(USAGE)));
        }

        if matches.opt_present("v") {
            return Ok(Parsed::Version(
                option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"),
            ));
        }

        let env = EnvConfig::from_iter(env::vars())?.with_global_env();
        let cwd = get_cwd(matches.opt_str("d"))?;
        let branch = matches.opt_str("b");
        let (repo_url, remote) = match (matches.opt_str("r"), matches.opt_str("R")) {
            (Some(repo), remote) => {
                if !matches.free.is_empty() {
                    return Error::err(ErrorKind::ArgsNotAllowed {
                        flag: "--repo {repo}",
                        args: matches.free,
                    });
                }
                (normalize_repo_format(repo, &env)?, remote)
            }
            (None, remote) => {
                let git = Git::new(&cwd, &env.git_command);
                let (url, remote) = if let Some(remote) = remote {
                    (git.remote_url(&remote)?, remote)
                } else {
                    git.tracking_remote_url(&branch)?
                };
                (url, Some(remote))
            }
        };

        let repo_url = handle_scp_like_syntax(repo_url);

        Ok(Parsed::OpenPage(Config {
            repo_url,
            branch,
            cwd,
            stdout: matches.opt_present("u"),
            pull_request: matches.opt_present("p"),
            website: matches.opt_present("w"),
            blame: matches.opt_present("B"),
            args: matches.free,
            remote,
            env,
        }))
    }
}
<|endoftext|>


<commit_before>//! Temporal quantification

use peripheral;
use peripheral::gptim::CntR;

/// The frequency, in Hz, of the clock that backs up this module.
///
/// The inverse of this value is the length of a single tick in seconds.
pub const FREQUENCY: u32 = ::APB1_CLOCK;

pub struct Instant {
    cnt: CntR,
}

impl Instant {
    /// Returns the instant corresponding to "now".
    pub fn now() -> Self {
        Instant { cnt: peripheral::tim2().cnt.read() }
    }

    /// Returns the number of "ticks" that have passed since this instant was
    /// created.
    ///
    /// # Caveats
    ///
    /// This function doesn't handle overflows. There's no way to know if more
    /// than `1 << 32` ticks have passed; the number of ticks will just wrap
    /// around.
    pub fn elapsed(&self) -> u32 {
        let cnt = peripheral::tim2().cnt.read();

        let before = (u32::from(self.cnt.cnth()) << 16) +
                     u32::from(self.cnt.cntl());
        let now = (u32::from(cnt.cnth()) << 16) + u32::from(cnt.cntl());

        now.wrapping_sub(before)
    }
}

/// Initializes the necessary stuff to be able to use the `time` module
///
/// # Safety
///
/// - Must be called once
/// - Must be called in an interrupt-free environment
pub unsafe fn init() {
    let rcc = peripheral::rcc_mut();
    let tim2 = peripheral::tim2_mut();

    rcc.apb1enr.modify(|_, w| w.tim2en(true));
    tim2.cr1.write(|w| w.opm(false).cen(true));
}
<commit_msg>derive Copy for Instant<commit_after>//! Temporal quantification

use peripheral;
use peripheral::gptim::CntR;

/// The frequency, in Hz, of the clock that backs up this module.
///
/// The inverse of this value is the length of a single tick in seconds.
pub const FREQUENCY: u32 = ::APB1_CLOCK;

#[derive(Clone, Copy)]
pub struct Instant {
    cnt: CntR,
}

impl Instant {
    /// Returns the instant corresponding to "now".
    pub fn now() -> Self {
        Instant { cnt: peripheral::tim2().cnt.read() }
    }

    /// Returns the number of "ticks" that have passed since this instant was
    /// created.
    ///
    /// # Caveats
    ///
    /// This function doesn't handle overflows. There's no way to know if more
    /// than `1 << 32` ticks have passed; the number of ticks will just wrap
    /// around.
    pub fn elapsed(&self) -> u32 {
        let cnt = peripheral::tim2().cnt.read();

        let before = (u32::from(self.cnt.cnth()) << 16) +
                     u32::from(self.cnt.cntl());
        let now = (u32::from(cnt.cnth()) << 16) + u32::from(cnt.cntl());

        now.wrapping_sub(before)
    }
}

/// Initializes the necessary stuff to be able to use the `time` module
///
/// # Safety
///
/// - Must be called once
/// - Must be called in an interrupt-free environment
pub unsafe fn init() {
    let rcc = peripheral::rcc_mut();
    let tim2 = peripheral::tim2_mut();

    rcc.apb1enr.modify(|_, w| w.tim2en(true));
    tim2.cr1.write(|w| w.opm(false).cen(true));
}
<|endoftext|>