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

use std;
use std::collections::BTreeMap as Map;
use std::env;
use std::net::{IpAddr, SocketAddr};
use std::path::Path;
use std::{fs, 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 default_magic_size = config.magic_limit.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("qr")
                    .short("Q")
                    .long("qr-code")
                    .help("Show URL's QR code at startup"),
            )
            .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 use a custom config file\ndefault path: ~/.config/fht2p/fht2p.json"),
            )
            .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_cert_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")
                    .short("F")
                    .long("config-print")
                    .help("Print the content of 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("Enable follow links"),
            )
            .arg(
                Arg::with_name("upload")
                    .long("upload")
                    .short("u")
                    .help("Enable upload function"),
            )
            .arg(Arg::with_name("mkdir").short("m").long("mkdir").help("Enable mkdir function"))
            .arg(
                Arg::with_name("magic-limit")
                    .long("magic-limit")
                    .short("M")
                    .takes_value(true)
                    .default_value(&default_magic_size)
                    .help("The size 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")
                    .short("S")
                    .long("cache-secs")
                    .takes_value(true)
                    .default_value(&default_cache)
                    .help("Set the secs of cache(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")
                    .takes_value(true)
                    .help("Enable http proxy(Regular for allowed domains, empty string can allow all)")
                    .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 address")
                    .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 [Default: "."]"#),
            )
    };

    // clap's NOTE: The first argument will be parsed as the binary name unless AppSettings::NoBinaryName is used
    let args = env::args().collect::<Vec<_>>();
    // not contains other than -v* or --qr
    let args_is_empty = args.iter().skip(1).all(|arg| arg.starts_with("-v") || arg == "--qr");
    let matches = app.get_matches_from(args);
    let qr = matches.is_present("qr");

    // -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.show_qrcode(qr), 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.show_qrcode(qr), join_handle)
}

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

impl Default for Server {
    fn default() -> Server {
        let addr = SERVER_ADDR.get();
        Self::new(addr.ip(), addr.port())
    }
}
impl Server {
    fn new(ip: IpAddr, port: u16) -> Self {
        Server { ip, 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> {
        debug!("Config.load_from_file: {}", path);
        let str = fs::read_to_string(path).map_err(|e| format!("config file('{}') read failed: {}", 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 failed: {}", 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()?;
    let confp = home.as_path().join(".config/fht2p").join(CONFIG_STR_PATH);
    if confp.exists() {
        Some(confp.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>args.rs: fix args_is_empty<commit_after>use clap::{App, Arg};
use json5;
use regex::Regex;

use std;
use std::collections::BTreeMap as Map;
use std::env;
use std::net::{IpAddr, SocketAddr};
use std::path::Path;
use std::{fs, 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 default_magic_size = config.magic_limit.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("qr")
                    .short("Q")
                    .long("qr-code")
                    .help("Show URL's QR code at startup"),
            )
            .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 use a custom config file\ndefault path: ~/.config/fht2p/fht2p.json"),
            )
            .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_cert_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")
                    .short("F")
                    .long("config-print")
                    .help("Print the content of 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("Enable follow links"),
            )
            .arg(
                Arg::with_name("upload")
                    .long("upload")
                    .short("u")
                    .help("Enable upload function"),
            )
            .arg(Arg::with_name("mkdir").short("m").long("mkdir").help("Enable mkdir function"))
            .arg(
                Arg::with_name("magic-limit")
                    .long("magic-limit")
                    .short("M")
                    .takes_value(true)
                    .default_value(&default_magic_size)
                    .help("The size 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")
                    .short("S")
                    .long("cache-secs")
                    .takes_value(true)
                    .default_value(&default_cache)
                    .help("Set the secs of cache(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")
                    .takes_value(true)
                    .help("Enable http proxy(Regular for allowed domains, empty string can allow all)")
                    .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 address")
                    .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 [Default: "."]"#),
            )
    };

    // clap's NOTE: The first argument will be parsed as the binary name unless AppSettings::NoBinaryName is used
    let args = env::args().collect::<Vec<_>>();
    // not contains other than -v* or -Q/--qr-code
    let args_is_empty = args
        .iter()
        .skip(1)
        .all(|arg| arg.starts_with("-v") || ["-Q", "--qr-code"].contains(&arg.as_str()));
    let matches = app.get_matches_from(args);
    let qr = matches.is_present("qr");

    // -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.show_qrcode(qr), 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.show_qrcode(qr), join_handle)
}

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

impl Default for Server {
    fn default() -> Server {
        let addr = SERVER_ADDR.get();
        Self::new(addr.ip(), addr.port())
    }
}
impl Server {
    fn new(ip: IpAddr, port: u16) -> Self {
        Server { ip, 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> {
        debug!("Config.load_from_file: {}", path);
        let str = fs::read_to_string(path).map_err(|e| format!("config file('{}') read failed: {}", 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 failed: {}", 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()?;
    let confp = home.as_path().join(".config/fht2p").join(CONFIG_STR_PATH);
    if confp.exists() {
        Some(confp.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>use std::fmt;
use std::borrow::Cow;

use escape::Escaped;

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Attribute<'a> {
    pub name: Cow<'a, str>,
    pub value: Cow<'a, str>,
}

impl<'a> Attribute<'a> {
    pub fn new<N, V>(name: N, value: V) -> Attribute<'a>
        where N: Into<Cow<'a, str>>,
              V: Into<Cow<'a, str>>
    {
        Attribute {
            name: name.into(),
            value: value.into(),
        }
    }
}

impl<'a> fmt::Display for Attribute<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}=\"{}\"", self.name.as_ref(), Escaped(&self.value))
    }
}

#[derive(Clone, Debug)]
/// Prefer `attr_set!` macro over manual construction
pub struct AttributeSet<'a>(pub Cow<'a, [Attribute<'a>]>);

impl<'a> AttributeSet<'a> {
    fn search_name<S>(&self, name: S) -> Option<usize>
        where S: AsRef<str>
    {
        for (i, attr) in self.0.iter().enumerate() {
            if attr.name == name.as_ref() {
                return Some(i);
            }
        }
        None
    }

    pub fn get_attr<S>(&self, name: S) -> Option<&Attribute<'a>>
        where S: AsRef<str>
    {
        self.search_name(name).map(|index| &self.0.as_ref()[index])
    }

    pub fn get_attr_mut<S>(&mut self, name: S) -> Option<&mut Attribute<'a>>
        where S: AsRef<str>
    {
        self.search_name(name).map(move |index| &mut self.0.to_mut()[index])
    }

    pub fn set_attr<N, V>(&mut self, name: N, value: V)
        where N: Into<Cow<'a, str>>,
              V: Into<Cow<'a, str>>
    {
        let name = name.into();
        if let Some(index) = self.search_name(name.as_ref()) {
            self.0.to_mut()[index].value = value.into();
        } else {
            self.0.to_mut().push(Attribute::new(name, value));
        }
    }
}

impl<'a> fmt::Display for AttributeSet<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for attr in self.0.iter() {
            try!(write!(f, " {}", attr));
        }
        Ok(())
    }
}
<commit_msg>Simplify attr accessors<commit_after>use std::fmt;
use std::borrow::Cow;

use escape::Escaped;

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Attribute<'a> {
    pub name: Cow<'a, str>,
    pub value: Cow<'a, str>,
}

impl<'a> Attribute<'a> {
    pub fn new<N, V>(name: N, value: V) -> Attribute<'a>
        where N: Into<Cow<'a, str>>,
              V: Into<Cow<'a, str>>
    {
        Attribute {
            name: name.into(),
            value: value.into(),
        }
    }
}

impl<'a> fmt::Display for Attribute<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}=\"{}\"", self.name.as_ref(), Escaped(&self.value))
    }
}

#[derive(Clone, Debug)]
/// Prefer `attr_set!` macro over manual construction
pub struct AttributeSet<'a>(pub Cow<'a, [Attribute<'a>]>);

impl<'a> AttributeSet<'a> {
    pub fn get_attr<S>(&self, name: S) -> Option<&Attribute<'a>>
        where S: AsRef<str>
    {
        self.0.iter().find(|attr| attr.name == name.as_ref())
    }

    pub fn get_attr_mut<S>(&mut self, name: S) -> Option<&mut Attribute<'a>>
        where S: AsRef<str>
    {
        self.0.to_mut().iter_mut().find(|attr| attr.name == name.as_ref())
    }

    pub fn set_attr<N, V>(&mut self, name: N, value: V)
        where N: Into<Cow<'a, str>>,
              V: Into<Cow<'a, str>>
    {
        let (name, value) = (name.into(), value.into());
        let attrs = self.0.to_mut();

        if let Some(pos) = attrs.iter().position(|attr| attr.name == name) {
            attrs[pos].value = value;
        } else {
            attrs.push(Attribute::new(name, value));
        }
    }
}

impl<'a> fmt::Display for AttributeSet<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for attr in self.0.iter() {
            try!(write!(f, " {}", attr));
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>//! C API for mp4parse module.
//!
//! Parses ISO Base Media Format aka video/mp4 streams.
//!
//! # Examples
//!
//! ```rust
//! extern crate mp4parse;
//!
//! // Minimal valid mp4 containing no tracks.
//! let data = b"\0\0\0\x10ftypmp42\0\0\0\0";
//!
//! let context = mp4parse::mp4parse_new();
//! unsafe {
//!     let rv = mp4parse::mp4parse_read(context, data.as_ptr(), data.len());
//!     assert_eq!(rv, mp4parse::mp4parse_error::MP4PARSE_OK);
//!     mp4parse::mp4parse_free(context);
//! }
//! ```

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use std;
use std::io::Cursor;

// Symbols we need from our rust api.
use MediaContext;
use TrackType;
use read_mp4;
use Error;
use media_time_to_ms;
use track_time_to_ms;
use SampleEntry;

#[repr(C)]
#[derive(PartialEq,Debug)]
pub enum mp4parse_error {
    MP4PARSE_OK = 0,
    MP4PARSE_ERROR_BADARG = 1,
    MP4PARSE_ERROR_INVALID = 2,
    MP4PARSE_ERROR_UNSUPPORTED = 3,
    MP4PARSE_ERROR_EOF = 4,
    MP4PARSE_ERROR_ASSERT = 5,
    MP4PARSE_ERROR_IO = 6,
}

#[repr(C)]
pub enum mp4parse_track_type {
    MP4PARSE_TRACK_TYPE_VIDEO = 0,
    MP4PARSE_TRACK_TYPE_AUDIO = 1,
}

#[repr(C)]
pub struct mp4parse_track_info {
    pub track_type: mp4parse_track_type,
    pub track_id: u32,
    pub duration: u64,
    pub media_time: i64, // wants to be u64? understand how elst adjustment works
    // TODO(kinetik): include crypto guff
}

#[repr(C)]
pub struct mp4parse_track_audio_info {
    pub channels: u16,
    pub bit_depth: u16,
    pub sample_rate: u32,
    // TODO(kinetik):
    // int32_t profile;
    // int32_t extended_profile; // check types
    // extra_data
    // codec_specific_config
}

#[repr(C)]
pub struct mp4parse_track_video_info {
    pub display_width: u32,
    pub display_height: u32,
    pub image_width: u16,
    pub image_height: u16,
    // TODO(kinetik):
    // extra_data
    // codec_specific_config
}

#[repr(C)]
#[allow(non_camel_case_types)]
pub struct mp4parse_state(MediaContext);

// C API wrapper functions.

/// Allocate an opaque rust-side parser context.
#[no_mangle]
pub extern "C" fn mp4parse_new() -> *mut mp4parse_state {
    let context = Box::new(mp4parse_state(MediaContext::new()));
    Box::into_raw(context)
}

/// Free a rust-side parser context.
#[no_mangle]
pub unsafe extern "C" fn mp4parse_free(context: *mut mp4parse_state) {
    assert!(!context.is_null());
    let _ = Box::from_raw(context);
}

/// Feed a buffer through `read_mp4()` with the given rust-side
/// parser context, returning success or an error code.
///
/// This is safe to call with NULL arguments but will crash
/// if given invalid pointers, as is usual for C.
#[no_mangle]
pub unsafe extern "C" fn mp4parse_read(context: *mut mp4parse_state, buffer: *const u8, size: usize) -> mp4parse_error {
    // Validate arguments from C.
    if context.is_null() || buffer.is_null() || size < 8 {
        return mp4parse_error::MP4PARSE_ERROR_BADARG;
    }

    let mut context: &mut MediaContext = &mut (*context).0;

    // Wrap the buffer we've been give in a slice.
    let b = std::slice::from_raw_parts(buffer, size);
    let mut c = Cursor::new(b);

    let r = if cfg!(not(feature = "fuzz")) {
        // Parse in a subthread to catch any panics.
        let task = std::thread::spawn(move || read_mp4(&mut c, &mut context));
        // The task's JoinHandle will return an error result if the thread
        // panicked, and will wrap the closure's return'd result in an
        // Ok(..) otherwise, meaning we could see Ok(Err(Error::..))
        // here. So map thread failures back to an
        // mp4parse::Error::AssertCaught.
        task.join().unwrap_or(Err(Error::AssertCaught))
    } else {
        read_mp4(&mut c, &mut context)
    };
    match r {
        Ok(_) => mp4parse_error::MP4PARSE_OK,
        Err(Error::InvalidData) => mp4parse_error::MP4PARSE_ERROR_INVALID,
        Err(Error::Unsupported) => mp4parse_error::MP4PARSE_ERROR_UNSUPPORTED,
        Err(Error::UnexpectedEOF) => mp4parse_error::MP4PARSE_ERROR_EOF,
        Err(Error::AssertCaught) => mp4parse_error::MP4PARSE_ERROR_ASSERT,
        Err(Error::Io(_)) => mp4parse_error::MP4PARSE_ERROR_IO,
    }
}

/// Return the number of tracks parsed by previous `read_mp4()` calls.
#[no_mangle]
pub unsafe extern "C" fn mp4parse_get_track_count(context: *const mp4parse_state) -> u32 {
    // Validate argument from C.
    assert!(!context.is_null());
    let context: &MediaContext = &(*context).0;

    // Make sure the track count fits in a u32.
    assert!(context.tracks.len() < u32::max_value() as usize);
    context.tracks.len() as u32
}

#[no_mangle]
pub unsafe extern "C" fn mp4parse_get_track_info(context: *mut mp4parse_state, track: u32, info: *mut mp4parse_track_info) -> mp4parse_error {
    if context.is_null() || info.is_null() {
        return mp4parse_error::MP4PARSE_ERROR_BADARG;
    }

    let context: &mut MediaContext = &mut (*context).0;
    let track_index: usize = track as usize;
    let info: &mut mp4parse_track_info = &mut *info;

    if track_index >= context.tracks.len() {
        return mp4parse_error::MP4PARSE_ERROR_BADARG;
    }

    info.track_type = match context.tracks[track_index].track_type {
        TrackType::Video => mp4parse_track_type::MP4PARSE_TRACK_TYPE_VIDEO,
        TrackType::Audio => mp4parse_track_type::MP4PARSE_TRACK_TYPE_AUDIO,
        TrackType::Unknown => return mp4parse_error::MP4PARSE_ERROR_UNSUPPORTED,
    };

    // Maybe context & track should just have a single simple is_valid() instead?
    if context.timescale.is_none() ||
       context.tracks[track_index].timescale.is_none() ||
       context.tracks[track_index].duration.is_none() ||
       context.tracks[track_index].track_id.is_none() {
        return mp4parse_error::MP4PARSE_ERROR_INVALID;
    }

    let track = &context.tracks[track_index];
    let empty_duration = if track.empty_duration.is_some() {
        media_time_to_ms(track.empty_duration.unwrap(), context.timescale.unwrap())
    } else {
        0
    };
    info.media_time = if track.media_time.is_some() {
        track_time_to_ms(track.media_time.unwrap(), track.timescale.unwrap()) as i64 - empty_duration as i64
    } else {
        0
    };
    info.duration = track_time_to_ms(track.duration.unwrap(), track.timescale.unwrap());
    info.track_id = track.track_id.unwrap();

    mp4parse_error::MP4PARSE_OK
}

#[no_mangle]
pub unsafe extern "C" fn mp4parse_get_track_audio_info(context: *mut mp4parse_state, track: u32, info: *mut mp4parse_track_audio_info) -> mp4parse_error {
    if context.is_null() || info.is_null() {
        return mp4parse_error::MP4PARSE_ERROR_BADARG;
    }

    let context: &mut MediaContext = &mut (*context).0;

    if track as usize >= context.tracks.len() {
        return mp4parse_error::MP4PARSE_ERROR_BADARG;
    }

    let track = &context.tracks[track as usize];

    match track.track_type {
        TrackType::Audio => {}
        _ => return mp4parse_error::MP4PARSE_ERROR_INVALID,
    };

    let audio = match track.data {
        Some(ref data) => data,
        None => return mp4parse_error::MP4PARSE_ERROR_INVALID,
    };

    let audio = match *audio {
        SampleEntry::Audio(ref x) => x,
        _ => return mp4parse_error::MP4PARSE_ERROR_INVALID,
    };

    (*info).channels = audio.channelcount;
    (*info).bit_depth = audio.samplesize;
    (*info).sample_rate = audio.samplerate >> 16; // 16.16 fixed point

    mp4parse_error::MP4PARSE_OK
}

#[no_mangle]
pub unsafe extern "C" fn mp4parse_get_track_video_info(context: *mut mp4parse_state, track: u32, info: *mut mp4parse_track_video_info) -> mp4parse_error {
    if context.is_null() || info.is_null() {
        return mp4parse_error::MP4PARSE_ERROR_BADARG;
    }

    let context: &mut MediaContext = &mut (*context).0;

    if track as usize >= context.tracks.len() {
        return mp4parse_error::MP4PARSE_ERROR_BADARG;
    }

    let track = &context.tracks[track as usize];

    match track.track_type {
        TrackType::Video => {}
        _ => return mp4parse_error::MP4PARSE_ERROR_INVALID,
    };

    let video = match track.data {
        Some(ref data) => data,
        None => return mp4parse_error::MP4PARSE_ERROR_INVALID,
    };

    let video = match *video {
        SampleEntry::Video(ref x) => x,
        _ => return mp4parse_error::MP4PARSE_ERROR_INVALID,
    };

    if let Some(ref tkhd) = track.tkhd {
        (*info).display_width = tkhd.width >> 16; // 16.16 fixed point
        (*info).display_height = tkhd.height >> 16; // 16.16 fixed point
    } else {
        return mp4parse_error::MP4PARSE_ERROR_INVALID;
    }
    (*info).image_width = video.width;
    (*info).image_height = video.height;

    mp4parse_error::MP4PARSE_OK
}

#[test]
fn new_context() {
    let context = mp4parse_new();
    assert!(!context.is_null());
    unsafe {
        mp4parse_free(context);
    }
}

#[test]
#[should_panic(expected = "assertion failed")]
fn free_null_context() {
    unsafe {
        mp4parse_free(std::ptr::null_mut());
    }
}

#[test]
fn arg_validation() {
    let null_buffer = std::ptr::null();
    let null_context = std::ptr::null_mut();

    let context = mp4parse_new();
    assert!(!context.is_null());

    let buffer = vec![0u8; 8];

    unsafe {
        assert_eq!(mp4parse_error::MP4PARSE_ERROR_BADARG,
                   mp4parse_read(null_context, null_buffer, 0));
        assert_eq!(mp4parse_error::MP4PARSE_ERROR_BADARG,
                   mp4parse_read(context, null_buffer, 0));
    }

    for size in 0..buffer.len() {
        println!("testing buffer length {}", size);
        unsafe {
            assert_eq!(mp4parse_error::MP4PARSE_ERROR_BADARG,
                       mp4parse_read(context, buffer.as_ptr(), size));
        }
    }

    unsafe {
        mp4parse_free(context);
    }
}
<commit_msg>capi: Simplify media_time computation.<commit_after>//! C API for mp4parse module.
//!
//! Parses ISO Base Media Format aka video/mp4 streams.
//!
//! # Examples
//!
//! ```rust
//! extern crate mp4parse;
//!
//! // Minimal valid mp4 containing no tracks.
//! let data = b"\0\0\0\x10ftypmp42\0\0\0\0";
//!
//! let context = mp4parse::mp4parse_new();
//! unsafe {
//!     let rv = mp4parse::mp4parse_read(context, data.as_ptr(), data.len());
//!     assert_eq!(rv, mp4parse::mp4parse_error::MP4PARSE_OK);
//!     mp4parse::mp4parse_free(context);
//! }
//! ```

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

use std;
use std::io::Cursor;

// Symbols we need from our rust api.
use MediaContext;
use TrackType;
use read_mp4;
use Error;
use media_time_to_ms;
use track_time_to_ms;
use SampleEntry;

#[repr(C)]
#[derive(PartialEq,Debug)]
pub enum mp4parse_error {
    MP4PARSE_OK = 0,
    MP4PARSE_ERROR_BADARG = 1,
    MP4PARSE_ERROR_INVALID = 2,
    MP4PARSE_ERROR_UNSUPPORTED = 3,
    MP4PARSE_ERROR_EOF = 4,
    MP4PARSE_ERROR_ASSERT = 5,
    MP4PARSE_ERROR_IO = 6,
}

#[repr(C)]
pub enum mp4parse_track_type {
    MP4PARSE_TRACK_TYPE_VIDEO = 0,
    MP4PARSE_TRACK_TYPE_AUDIO = 1,
}

#[repr(C)]
pub struct mp4parse_track_info {
    pub track_type: mp4parse_track_type,
    pub track_id: u32,
    pub duration: u64,
    pub media_time: i64, // wants to be u64? understand how elst adjustment works
    // TODO(kinetik): include crypto guff
}

#[repr(C)]
pub struct mp4parse_track_audio_info {
    pub channels: u16,
    pub bit_depth: u16,
    pub sample_rate: u32,
    // TODO(kinetik):
    // int32_t profile;
    // int32_t extended_profile; // check types
    // extra_data
    // codec_specific_config
}

#[repr(C)]
pub struct mp4parse_track_video_info {
    pub display_width: u32,
    pub display_height: u32,
    pub image_width: u16,
    pub image_height: u16,
    // TODO(kinetik):
    // extra_data
    // codec_specific_config
}

#[repr(C)]
#[allow(non_camel_case_types)]
pub struct mp4parse_state(MediaContext);

// C API wrapper functions.

/// Allocate an opaque rust-side parser context.
#[no_mangle]
pub extern "C" fn mp4parse_new() -> *mut mp4parse_state {
    let context = Box::new(mp4parse_state(MediaContext::new()));
    Box::into_raw(context)
}

/// Free a rust-side parser context.
#[no_mangle]
pub unsafe extern "C" fn mp4parse_free(context: *mut mp4parse_state) {
    assert!(!context.is_null());
    let _ = Box::from_raw(context);
}

/// Feed a buffer through `read_mp4()` with the given rust-side
/// parser context, returning success or an error code.
///
/// This is safe to call with NULL arguments but will crash
/// if given invalid pointers, as is usual for C.
#[no_mangle]
pub unsafe extern "C" fn mp4parse_read(context: *mut mp4parse_state, buffer: *const u8, size: usize) -> mp4parse_error {
    // Validate arguments from C.
    if context.is_null() || buffer.is_null() || size < 8 {
        return mp4parse_error::MP4PARSE_ERROR_BADARG;
    }

    let mut context: &mut MediaContext = &mut (*context).0;

    // Wrap the buffer we've been give in a slice.
    let b = std::slice::from_raw_parts(buffer, size);
    let mut c = Cursor::new(b);

    let r = if cfg!(not(feature = "fuzz")) {
        // Parse in a subthread to catch any panics.
        let task = std::thread::spawn(move || read_mp4(&mut c, &mut context));
        // The task's JoinHandle will return an error result if the thread
        // panicked, and will wrap the closure's return'd result in an
        // Ok(..) otherwise, meaning we could see Ok(Err(Error::..))
        // here. So map thread failures back to an
        // mp4parse::Error::AssertCaught.
        task.join().unwrap_or(Err(Error::AssertCaught))
    } else {
        read_mp4(&mut c, &mut context)
    };
    match r {
        Ok(_) => mp4parse_error::MP4PARSE_OK,
        Err(Error::InvalidData) => mp4parse_error::MP4PARSE_ERROR_INVALID,
        Err(Error::Unsupported) => mp4parse_error::MP4PARSE_ERROR_UNSUPPORTED,
        Err(Error::UnexpectedEOF) => mp4parse_error::MP4PARSE_ERROR_EOF,
        Err(Error::AssertCaught) => mp4parse_error::MP4PARSE_ERROR_ASSERT,
        Err(Error::Io(_)) => mp4parse_error::MP4PARSE_ERROR_IO,
    }
}

/// Return the number of tracks parsed by previous `read_mp4()` calls.
#[no_mangle]
pub unsafe extern "C" fn mp4parse_get_track_count(context: *const mp4parse_state) -> u32 {
    // Validate argument from C.
    assert!(!context.is_null());
    let context: &MediaContext = &(*context).0;

    // Make sure the track count fits in a u32.
    assert!(context.tracks.len() < u32::max_value() as usize);
    context.tracks.len() as u32
}

#[no_mangle]
pub unsafe extern "C" fn mp4parse_get_track_info(context: *mut mp4parse_state, track: u32, info: *mut mp4parse_track_info) -> mp4parse_error {
    if context.is_null() || info.is_null() {
        return mp4parse_error::MP4PARSE_ERROR_BADARG;
    }

    let context: &mut MediaContext = &mut (*context).0;
    let track_index: usize = track as usize;
    let info: &mut mp4parse_track_info = &mut *info;

    if track_index >= context.tracks.len() {
        return mp4parse_error::MP4PARSE_ERROR_BADARG;
    }

    info.track_type = match context.tracks[track_index].track_type {
        TrackType::Video => mp4parse_track_type::MP4PARSE_TRACK_TYPE_VIDEO,
        TrackType::Audio => mp4parse_track_type::MP4PARSE_TRACK_TYPE_AUDIO,
        TrackType::Unknown => return mp4parse_error::MP4PARSE_ERROR_UNSUPPORTED,
    };

    // Maybe context & track should just have a single simple is_valid() instead?
    if context.timescale.is_none() ||
       context.tracks[track_index].timescale.is_none() ||
       context.tracks[track_index].duration.is_none() ||
       context.tracks[track_index].track_id.is_none() {
        return mp4parse_error::MP4PARSE_ERROR_INVALID;
    }

    let track = &context.tracks[track_index];
    info.media_time = track.media_time.map_or(0, |media_time| {
        track_time_to_ms(media_time, track.timescale.unwrap()) as i64
    }) - track.empty_duration.map_or(0, |empty_duration| {
        media_time_to_ms(empty_duration, context.timescale.unwrap()) as i64
    });
    info.duration = track_time_to_ms(track.duration.unwrap(), track.timescale.unwrap());
    info.track_id = track.track_id.unwrap();

    mp4parse_error::MP4PARSE_OK
}

#[no_mangle]
pub unsafe extern "C" fn mp4parse_get_track_audio_info(context: *mut mp4parse_state, track: u32, info: *mut mp4parse_track_audio_info) -> mp4parse_error {
    if context.is_null() || info.is_null() {
        return mp4parse_error::MP4PARSE_ERROR_BADARG;
    }

    let context: &mut MediaContext = &mut (*context).0;

    if track as usize >= context.tracks.len() {
        return mp4parse_error::MP4PARSE_ERROR_BADARG;
    }

    let track = &context.tracks[track as usize];

    match track.track_type {
        TrackType::Audio => {}
        _ => return mp4parse_error::MP4PARSE_ERROR_INVALID,
    };

    let audio = match track.data {
        Some(ref data) => data,
        None => return mp4parse_error::MP4PARSE_ERROR_INVALID,
    };

    let audio = match *audio {
        SampleEntry::Audio(ref x) => x,
        _ => return mp4parse_error::MP4PARSE_ERROR_INVALID,
    };

    (*info).channels = audio.channelcount;
    (*info).bit_depth = audio.samplesize;
    (*info).sample_rate = audio.samplerate >> 16; // 16.16 fixed point

    mp4parse_error::MP4PARSE_OK
}

#[no_mangle]
pub unsafe extern "C" fn mp4parse_get_track_video_info(context: *mut mp4parse_state, track: u32, info: *mut mp4parse_track_video_info) -> mp4parse_error {
    if context.is_null() || info.is_null() {
        return mp4parse_error::MP4PARSE_ERROR_BADARG;
    }

    let context: &mut MediaContext = &mut (*context).0;

    if track as usize >= context.tracks.len() {
        return mp4parse_error::MP4PARSE_ERROR_BADARG;
    }

    let track = &context.tracks[track as usize];

    match track.track_type {
        TrackType::Video => {}
        _ => return mp4parse_error::MP4PARSE_ERROR_INVALID,
    };

    let video = match track.data {
        Some(ref data) => data,
        None => return mp4parse_error::MP4PARSE_ERROR_INVALID,
    };

    let video = match *video {
        SampleEntry::Video(ref x) => x,
        _ => return mp4parse_error::MP4PARSE_ERROR_INVALID,
    };

    if let Some(ref tkhd) = track.tkhd {
        (*info).display_width = tkhd.width >> 16; // 16.16 fixed point
        (*info).display_height = tkhd.height >> 16; // 16.16 fixed point
    } else {
        return mp4parse_error::MP4PARSE_ERROR_INVALID;
    }
    (*info).image_width = video.width;
    (*info).image_height = video.height;

    mp4parse_error::MP4PARSE_OK
}

#[test]
fn new_context() {
    let context = mp4parse_new();
    assert!(!context.is_null());
    unsafe {
        mp4parse_free(context);
    }
}

#[test]
#[should_panic(expected = "assertion failed")]
fn free_null_context() {
    unsafe {
        mp4parse_free(std::ptr::null_mut());
    }
}

#[test]
fn arg_validation() {
    let null_buffer = std::ptr::null();
    let null_context = std::ptr::null_mut();

    let context = mp4parse_new();
    assert!(!context.is_null());

    let buffer = vec![0u8; 8];

    unsafe {
        assert_eq!(mp4parse_error::MP4PARSE_ERROR_BADARG,
                   mp4parse_read(null_context, null_buffer, 0));
        assert_eq!(mp4parse_error::MP4PARSE_ERROR_BADARG,
                   mp4parse_read(context, null_buffer, 0));
    }

    for size in 0..buffer.len() {
        println!("testing buffer length {}", size);
        unsafe {
            assert_eq!(mp4parse_error::MP4PARSE_ERROR_BADARG,
                       mp4parse_read(context, buffer.as_ptr(), size));
        }
    }

    unsafe {
        mp4parse_free(context);
    }
}
<|endoftext|>


<commit_before>use std::fmt;

pub struct Card {
    pub value: Value,
    pub suit: Suit
}

impl fmt::Display for Card {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}{}", self.value, self.suit)
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Value {
    None    =   0,
    Two     =   2,
    Three   =   3,
    Four    =   4,
    Five    =   5,
    Six     =   6,
    Seven   =   7,
    Eight   =   8,
    Nine    =   9,
    Ten     =   10,
    Jack    =   11,
    Queen   =   12,
    King    =   13,
    Ace     =   14
}

impl fmt::Display for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let value = match *self {
            Value::Two      =>  "2",
            Value::Three    =>  "3",
            Value::Four     =>  "4",
            Value::Five     =>  "5",
            Value::Six      =>  "6",
            Value::Seven    =>  "7",
            Value::Eight    =>  "8",
            Value::Nine     =>  "9",
            Value::Ten      =>  "T",
            Value::Jack     =>  "J",
            Value::Queen    =>  "Q",
            Value::King     =>  "K",
            Value::Ace      =>  "A",
            Value::None     =>  "-"
        };
        write!(f, "{}", value)
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Suit {
    None,
    Diamonds,
    Clubs,
    Hearts,
    Spades
}

impl fmt::Display for Suit {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let suit = match *self {
            Suit::Diamonds  =>  "D",
            Suit::Clubs     =>  "C",
            Suit::Hearts    =>  "H",
            Suit::Spades    =>  "S",
            Suit::None      =>  "-"
        };
        write!(f, "{}", suit)
    }
}
<commit_msg>Suit Display is now the unicode picture of the suit<commit_after>use std::fmt;

pub struct Card {
    pub value: Value,
    pub suit: Suit
}

impl fmt::Display for Card {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}{}", self.value, self.suit)
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Value {
    None    =   0,
    Two     =   2,
    Three   =   3,
    Four    =   4,
    Five    =   5,
    Six     =   6,
    Seven   =   7,
    Eight   =   8,
    Nine    =   9,
    Ten     =   10,
    Jack    =   11,
    Queen   =   12,
    King    =   13,
    Ace     =   14
}

impl fmt::Display for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let value = match *self {
            Value::Two      =>  "2",
            Value::Three    =>  "3",
            Value::Four     =>  "4",
            Value::Five     =>  "5",
            Value::Six      =>  "6",
            Value::Seven    =>  "7",
            Value::Eight    =>  "8",
            Value::Nine     =>  "9",
            Value::Ten      =>  "T",
            Value::Jack     =>  "J",
            Value::Queen    =>  "Q",
            Value::King     =>  "K",
            Value::Ace      =>  "A",
            Value::None     =>  "-"
        };
        write!(f, "{}", value)
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Suit {
    None,
    Diamonds,
    Clubs,
    Hearts,
    Spades
}

impl fmt::Display for Suit {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let suit = match *self {
            Suit::Diamonds  =>  "♢",
            Suit::Clubs     =>  "♣",
            Suit::Hearts    =>  "♡",
            Suit::Spades    =>  "♠",
            Suit::None      =>  "-"
        };
        write!(f, "{}", suit)
    }
}
<|endoftext|>


<commit_before>//! The `conf` module contains functions for loading and saving game
//! configurations.
//!
//! A `Conf` struct is used to specify hardware setup stuff used to create
//! the window and other context information.

use std::io;
use toml;

use GameResult;

/// A structure containing configuration data
/// for the game engine.
#[derive(Serialize, Deserialize, Debug, PartialEq, SmartDefault)]
pub struct Conf {
    /// The window title.
    #[default = r#""An easy, good game".to_owned()"#]
    pub window_title: String,
    /// A file path to the window's icon.
    /// It is rooted in the `resources` directory (see the `filesystem` module for details),
    /// and an empty string results in a blank/default icon.
    #[default = r#""".to_owned()"#]
    pub window_icon: String,
    /// The window's height
    #[default = "800"]
    pub window_height: u32,
    /// The window's width
    #[default = "600"]
    pub window_width: u32,
    /// Whether or not the graphics draw rate should be
    /// synchronized with the monitor's draw rate.
    #[default = "true"]
    pub vsync: bool,
    #[default = "true"]
    pub resizable: bool,
    /* To implement still.
     * window_borderless: bool,
     * window_resizable: bool,
     * window_fullscreen: bool,
     *
     * Modules to enable
     * modules_audio: bool,
     * modules_event: bool,
     * modules_graphics: bool,
     * modules_image: bool,
     * modules_joystic: bool,
     * modules_keyboard: bool,
     * modules_mouse: bool,
     * modules_sound: bool,
     * modules_system: bool,
     * modules_timer: bool,
     * modules_video: bool,
     * modules_window: bool,
     * modules_thread: bool, */
}

// impl Default for Conf {
//     /// Create a new Conf with some vague defaults.
//     ///
//     /// ```rust,ignore
//     /// Conf {
//     ///     window_title: "An easy, good game"
//     ///     window_icon: ""
//     ///     window_height: 600
//     ///     window_width: 800
//     ///     vsync: true
//     /// }
//     /// ```
//     fn default() -> Self {
//         Conf {
//             window_title: String::from("An easy, good game"),
//             window_icon: String::from(""),
//             window_height: 600,
//             window_width: 800,
//             vsync: true,
//             resizable: false,
//         }

//     }
// }

impl Conf {
    /// Same as Conf::default()
    pub fn new() -> Self {
        Self::default()
    }

    /// Load a TOML file from the given `Read` and attempts to parse
    /// a `Conf` from it.
    pub fn from_toml_file<R: io::Read>(file: &mut R) -> GameResult<Conf> {
        let mut s = String::new();
        file.read_to_string(&mut s)?;
        let decoded = toml::from_str(&s)?;
        Ok(decoded)
    }

    /// Saves the `Conf` to the given `Write` object,
    /// formatted as TOML.
    pub fn to_toml_file<W: io::Write>(&self, file: &mut W) -> GameResult<()> {
        let s = toml::to_vec(self)?;
        file.write_all(&s)?;
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use conf;

    /// Tries to encode and decode a `Conf` object
    /// and makes sure it gets the same result it had.
    #[test]
    fn encode_round_trip() {
        let c1 = conf::Conf::new();
        let mut writer = Vec::new();
        let _c = c1.to_toml_file(&mut writer).unwrap();
        //println!("{}", String::from_utf8_lossy(&writer));
        let mut reader = writer.as_slice();
        let c2 = conf::Conf::from_toml_file(&mut reader).unwrap();
        assert_eq!(c1, c2);
    }
}
<commit_msg>Fixed typos in default width and height, derp<commit_after>//! The `conf` module contains functions for loading and saving game
//! configurations.
//!
//! A `Conf` struct is used to specify hardware setup stuff used to create
//! the window and other context information.

use std::io;
use toml;

use GameResult;

/// A structure containing configuration data
/// for the game engine.
#[derive(Serialize, Deserialize, Debug, PartialEq, SmartDefault)]
pub struct Conf {
    /// The window title.
    #[default = r#""An easy, good game".to_owned()"#]
    pub window_title: String,
    /// A file path to the window's icon.
    /// It is rooted in the `resources` directory (see the `filesystem` module for details),
    /// and an empty string results in a blank/default icon.
    #[default = r#""".to_owned()"#]
    pub window_icon: String,
    /// The window's height
    #[default = "600"]
    pub window_height: u32,
    /// The window's width
    #[default = "800"]
    pub window_width: u32,
    /// Whether or not the graphics draw rate should be
    /// synchronized with the monitor's draw rate.
    #[default = "true"]
    pub vsync: bool,
    #[default = "true"]
    pub resizable: bool,
    /* To implement still.
     * window_borderless: bool,
     * window_resizable: bool,
     * window_fullscreen: bool,
     *
     * Modules to enable
     * modules_audio: bool,
     * modules_event: bool,
     * modules_graphics: bool,
     * modules_image: bool,
     * modules_joystic: bool,
     * modules_keyboard: bool,
     * modules_mouse: bool,
     * modules_sound: bool,
     * modules_system: bool,
     * modules_timer: bool,
     * modules_video: bool,
     * modules_window: bool,
     * modules_thread: bool, */
}

// impl Default for Conf {
//     /// Create a new Conf with some vague defaults.
//     ///
//     /// ```rust,ignore
//     /// Conf {
//     ///     window_title: "An easy, good game"
//     ///     window_icon: ""
//     ///     window_height: 600
//     ///     window_width: 800
//     ///     vsync: true
//     /// }
//     /// ```
//     fn default() -> Self {
//         Conf {
//             window_title: String::from("An easy, good game"),
//             window_icon: String::from(""),
//             window_height: 600,
//             window_width: 800,
//             vsync: true,
//             resizable: false,
//         }

//     }
// }

impl Conf {
    /// Same as Conf::default()
    pub fn new() -> Self {
        Self::default()
    }

    /// Load a TOML file from the given `Read` and attempts to parse
    /// a `Conf` from it.
    pub fn from_toml_file<R: io::Read>(file: &mut R) -> GameResult<Conf> {
        let mut s = String::new();
        file.read_to_string(&mut s)?;
        let decoded = toml::from_str(&s)?;
        Ok(decoded)
    }

    /// Saves the `Conf` to the given `Write` object,
    /// formatted as TOML.
    pub fn to_toml_file<W: io::Write>(&self, file: &mut W) -> GameResult<()> {
        let s = toml::to_vec(self)?;
        file.write_all(&s)?;
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use conf;

    /// Tries to encode and decode a `Conf` object
    /// and makes sure it gets the same result it had.
    #[test]
    fn encode_round_trip() {
        let c1 = conf::Conf::new();
        let mut writer = Vec::new();
        let _c = c1.to_toml_file(&mut writer).unwrap();
        //println!("{}", String::from_utf8_lossy(&writer));
        let mut reader = writer.as_slice();
        let c2 = conf::Conf::from_toml_file(&mut reader).unwrap();
        assert_eq!(c1, c2);
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

use serde;

pub use self::Data::*;

/// An internal Data format used to resolve L20n resources.
#[doc(hidden)]
#[derive(Debug, PartialEq, Clone)]
pub enum Data {
    Null,
    Bool(bool),
    Num(i64),
    Str(String),
    List(Vec<Data>),
    Map(HashMap<String, Data>),
}

impl Data {
    pub fn get(&self, key: &str) -> Option<&Data> {
        match *self {
            Data::Map(ref map) => map.get(key),
            _ => None
        }
    }
}

#[doc(hidden)]
pub struct Encoder {
    data: Vec<Data>
}

impl Encoder {
    #[doc(hidden)]
    pub fn new() -> Encoder {
        Encoder { data: vec![] }
    }

    #[doc(hidden)]
    pub fn data(mut self) -> Option<Data> {
        self.data.pop()
    }
}

/// Errors that occur encoding environment data into something the L20n
/// resources can use.
#[derive(Debug)]
pub enum EncodeError {
    /// Type is not usable in L20n.
    UnsupportedType,
    /// Maps in L20n require keys to be Strings.
    KeyIsNotString,
    /// A map element is missing.
    MissingElements,
}

pub type EncoderResult = Result<(), EncodeError>;

impl serde::Serializer for Encoder {
    type Error = EncodeError;

    fn visit_bool(&mut self, v: bool) -> EncoderResult { self.data.push(Bool(v)); Ok(()) }
    fn visit_i64(&mut self, v: i64) -> EncoderResult { self.data.push(Num(v)); Ok(()) }
    fn visit_u64(&mut self, v: u64) -> EncoderResult { self.visit_i64(v as i64) }
    fn visit_f64(&mut self, v: f64) -> EncoderResult { self.visit_i64(v as i64) }

    fn visit_str(&mut self, v: &str) -> EncoderResult {
        self.data.push(Str(v.to_string()));
        Ok(())
    }

    fn visit_unit(&mut self) -> EncoderResult { self.data.push(Null); Ok(()) }
    fn visit_none(&mut self) -> EncoderResult { self.visit_unit() }
    fn visit_some<V>(&mut self, v: V) -> EncoderResult where V: serde::Serialize { v.serialize(self) }
    fn visit_seq<V>(&mut self, mut v: V) -> EncoderResult where V: serde::ser::SeqVisitor {
        self.data.push(List(vec![]));
        v.visit(self).map(|_| ())
    }
    fn visit_seq_elt<V>(&mut self, v: V) -> EncoderResult where V: serde::Serialize {
        match self.data.pop() {
            Some(List(mut list)) => {
                let mut elt_encoder = Encoder::new();
                try!(v.serialize(&mut elt_encoder));
                list.push(elt_encoder.data.pop().unwrap());
                self.data.push(List(list));
                Ok(())
            },
            _ => Err(EncodeError::UnsupportedType)
        }
    }
    fn visit_map<V>(&mut self, mut v: V) -> EncoderResult where V: serde::ser::MapVisitor {
        self.data.push(Map(HashMap::new()));
        v.visit(self).map(|_| ())
    }
    fn visit_map_elt<K, V>(&mut self, k: K, v: V) -> EncoderResult where K: serde::Serialize, V: serde::Serialize {
        match self.data.pop() {
            Some(Map(mut map)) => {
                let mut map_encoder = Encoder::new();
                try!(k.serialize(&mut map_encoder));
                let k = match map_encoder.data.pop() {
                    Some(Data::Str(s)) => s,
                    _ => return Err(EncodeError::KeyIsNotString)
                };

                try!(v.serialize(&mut map_encoder));
                let v = map_encoder.data.pop().unwrap();
                map.insert(k, v);
                self.data.push(Map(map));
                Ok(())
            },
            _ => Err(EncodeError::UnsupportedType)
        }
    }
}

pub struct Decoder {
    data: Vec<Data>
}

impl Decoder {
    /// Creates a new Decoder.
    pub fn new(data: Data) -> Decoder {
        Decoder {
            data: vec![data]
        }
    }
}

impl serde::Deserializer for Decoder {
    type Error = serde::de::value::Error;

    fn visit<V>(&mut self, mut visitor: V) -> Result<V::Value, Self::Error>
    where V: serde::de::Visitor {
        println!("visit {:?}", self.data);
        match self.data.pop() {
            Some(Data::Null) => visitor.visit_unit(),
            Some(Data::Bool(b)) => visitor.visit_bool(b),
            Some(Data::Num(n)) => visitor.visit_i64(n),
            Some(Data::Str(s)) => visitor.visit_str(&s),
            Some(Data::List(list)) => {
                let len = list.len();
                visitor.visit_seq(serde::de::value::SeqDeserializer::new(list.into_iter(), len))
            }
            Some(Data::Map(map)) => {
                let len = map.len();
                visitor.visit_map(serde::de::value::MapDeserializer::new(map.into_iter(), len))
            },
            None => unreachable!()
        }
    }
}

impl serde::de::value::ValueDeserializer for Data {
    type Deserializer = Decoder;
    fn into_deserializer(self) -> Decoder {
        Decoder::new(self)
    }
}

<commit_msg>Remove annoying println!(), probably left behind for debugging<commit_after>use std::collections::HashMap;

use serde;

pub use self::Data::*;

/// An internal Data format used to resolve L20n resources.
#[doc(hidden)]
#[derive(Debug, PartialEq, Clone)]
pub enum Data {
    Null,
    Bool(bool),
    Num(i64),
    Str(String),
    List(Vec<Data>),
    Map(HashMap<String, Data>),
}

impl Data {
    pub fn get(&self, key: &str) -> Option<&Data> {
        match *self {
            Data::Map(ref map) => map.get(key),
            _ => None
        }
    }
}

#[doc(hidden)]
pub struct Encoder {
    data: Vec<Data>
}

impl Encoder {
    #[doc(hidden)]
    pub fn new() -> Encoder {
        Encoder { data: vec![] }
    }

    #[doc(hidden)]
    pub fn data(mut self) -> Option<Data> {
        self.data.pop()
    }
}

/// Errors that occur encoding environment data into something the L20n
/// resources can use.
#[derive(Debug)]
pub enum EncodeError {
    /// Type is not usable in L20n.
    UnsupportedType,
    /// Maps in L20n require keys to be Strings.
    KeyIsNotString,
    /// A map element is missing.
    MissingElements,
}

pub type EncoderResult = Result<(), EncodeError>;

impl serde::Serializer for Encoder {
    type Error = EncodeError;

    fn visit_bool(&mut self, v: bool) -> EncoderResult { self.data.push(Bool(v)); Ok(()) }
    fn visit_i64(&mut self, v: i64) -> EncoderResult { self.data.push(Num(v)); Ok(()) }
    fn visit_u64(&mut self, v: u64) -> EncoderResult { self.visit_i64(v as i64) }
    fn visit_f64(&mut self, v: f64) -> EncoderResult { self.visit_i64(v as i64) }

    fn visit_str(&mut self, v: &str) -> EncoderResult {
        self.data.push(Str(v.to_string()));
        Ok(())
    }

    fn visit_unit(&mut self) -> EncoderResult { self.data.push(Null); Ok(()) }
    fn visit_none(&mut self) -> EncoderResult { self.visit_unit() }
    fn visit_some<V>(&mut self, v: V) -> EncoderResult where V: serde::Serialize { v.serialize(self) }
    fn visit_seq<V>(&mut self, mut v: V) -> EncoderResult where V: serde::ser::SeqVisitor {
        self.data.push(List(vec![]));
        v.visit(self).map(|_| ())
    }
    fn visit_seq_elt<V>(&mut self, v: V) -> EncoderResult where V: serde::Serialize {
        match self.data.pop() {
            Some(List(mut list)) => {
                let mut elt_encoder = Encoder::new();
                try!(v.serialize(&mut elt_encoder));
                list.push(elt_encoder.data.pop().unwrap());
                self.data.push(List(list));
                Ok(())
            },
            _ => Err(EncodeError::UnsupportedType)
        }
    }
    fn visit_map<V>(&mut self, mut v: V) -> EncoderResult where V: serde::ser::MapVisitor {
        self.data.push(Map(HashMap::new()));
        v.visit(self).map(|_| ())
    }
    fn visit_map_elt<K, V>(&mut self, k: K, v: V) -> EncoderResult where K: serde::Serialize, V: serde::Serialize {
        match self.data.pop() {
            Some(Map(mut map)) => {
                let mut map_encoder = Encoder::new();
                try!(k.serialize(&mut map_encoder));
                let k = match map_encoder.data.pop() {
                    Some(Data::Str(s)) => s,
                    _ => return Err(EncodeError::KeyIsNotString)
                };

                try!(v.serialize(&mut map_encoder));
                let v = map_encoder.data.pop().unwrap();
                map.insert(k, v);
                self.data.push(Map(map));
                Ok(())
            },
            _ => Err(EncodeError::UnsupportedType)
        }
    }
}

pub struct Decoder {
    data: Vec<Data>
}

impl Decoder {
    /// Creates a new Decoder.
    pub fn new(data: Data) -> Decoder {
        Decoder {
            data: vec![data]
        }
    }
}

impl serde::Deserializer for Decoder {
    type Error = serde::de::value::Error;

    fn visit<V>(&mut self, mut visitor: V) -> Result<V::Value, Self::Error>
    where V: serde::de::Visitor {
        match self.data.pop() {
            Some(Data::Null) => visitor.visit_unit(),
            Some(Data::Bool(b)) => visitor.visit_bool(b),
            Some(Data::Num(n)) => visitor.visit_i64(n),
            Some(Data::Str(s)) => visitor.visit_str(&s),
            Some(Data::List(list)) => {
                let len = list.len();
                visitor.visit_seq(serde::de::value::SeqDeserializer::new(list.into_iter(), len))
            }
            Some(Data::Map(map)) => {
                let len = map.len();
                visitor.visit_map(serde::de::value::MapDeserializer::new(map.into_iter(), len))
            },
            None => unreachable!()
        }
    }
}

impl serde::de::value::ValueDeserializer for Data {
    type Deserializer = Decoder;
    fn into_deserializer(self) -> Decoder {
        Decoder::new(self)
    }
}

<|endoftext|>


<commit_before>use luminance::{Dim2, Flat, RGBA32F, Texture};
use serde_json::from_reader;
use std::collections::HashMap;
use std::fs::File;
use std::path::Path;

use resource::{Load, LoadError, Result, ResCache};

/// Time.
pub type Time = f32;

/// A clip is an object that implements a visual sequence.
pub struct Clip<'a, 'b> where 'a: 'b {
  act: Box<Fn(Time) -> &'a Texture<Flat, Dim2, RGBA32F> + 'b>
}

impl<'a, 'b> Clip<'a, 'b> {
  pub fn new<F>(act: F) -> Self where F: 'b + Fn(Time) -> &'a Texture<Flat, Dim2, RGBA32F> {
    Clip {
      act: Box::new(act)
    }
  }
}

/// A cut is an object that slices a `Clip` at an *input time* and *output time*. It is instantiated
/// in a `Track` at a given *instance time*.
#[derive(Clone)]
pub struct Cut<'a, 'b, 'c> where 'a: 'b, 'b: 'c {
  pub in_time: Time,
  pub out_time: Time,
  pub inst_time: Time,
  pub clip: &'c Clip<'a, 'b>
}

impl<'a, 'b, 'c> Cut<'a, 'b, 'c> where 'a: 'b, 'b: 'c {
  pub fn new(in_time: Time, out_time: Time, inst_time: Time, clip: &'c Clip<'a, 'b>) -> Self {
    assert!(in_time <= out_time);

    Cut {
      in_time: in_time,
      out_time: out_time,
      inst_time: inst_time,
      clip: clip
    }
  }

  /// Duration of the cut.
  pub fn dur(&self) -> Time {
    self.out_time - self.in_time
  }
}

/// A track gathers `Cut`s and its purpose is to be used inside a `Timeline`.
#[derive(Clone)]
pub struct Track<'a, 'b, 'c> where 'a: 'b, 'b: 'c {
  cuts: Vec<Cut<'a, 'b, 'c>>
}

impl<'a, 'b, 'c> Track<'a, 'b, 'c> where 'a: 'b, 'b: 'c {
  pub fn new() -> Self {
    Track {
      cuts: Vec::new()
    }
  }

  pub fn add_cut(&mut self, cut: Cut<'a, 'b, 'c>) {
    self.cuts.push(cut);
  }
}

impl<'a, 'b, 'c, 'd> From<&'d [Cut<'a, 'b, 'c>]> for Track<'a, 'b, 'c> {
  fn from(cuts: &'d [Cut<'a, 'b, 'c>]) -> Self {
    Track {
      cuts: cuts.to_vec()
    }
  }
}

/// A timeline gathers tracks used to build up the visual aspect of the demo.
#[derive(Clone)]
pub struct Timeline<'a, 'b, 'c> where 'a: 'b, 'b: 'c {
  tracks: Vec<Track<'a, 'b, 'c>>,
}

impl<'a, 'b, 'c> Timeline<'a, 'b, 'c> where 'a: 'b, 'b: 'c {
  pub fn new() -> Self {
    Timeline {
      tracks: Vec::new(),
    }
  }

  /// Turn a TimelineManifest into a Timeline by providing a mapping between clips’ names and real
  /// clips.
  pub fn from_manifest(manifest: &TimelineManifest, mapping: &HashMap<String, &'c Clip<'a, 'b>>) -> Self {
    let mut timeline = Self::new();

    for track_manifest in &manifest.tracks {
      let mut track = Track::new();

      for cut_manifest in &track_manifest.cuts {
        let in_time = cut_manifest.in_time;
        let out_time = cut_manifest.out_time;
        let inst_time = cut_manifest.inst_time;
        let clip = mapping.get(&cut_manifest.clip).cloned().unwrap();

        track.add_cut(Cut::new(in_time, out_time, inst_time, clip));
      }

      timeline.add_track(track);
    }

    timeline
  }

  pub fn add_track(&mut self, track: Track<'a, 'b, 'c>) {
    self.tracks.push(track);
  }

  // TODO: currently, we play the first track we find; add transition support
  pub fn play(&self, t: Time) -> Option<&'a Texture<Flat, Dim2, RGBA32F>> {
    for track in &self.tracks {
      for cut in &track.cuts {
        if cut.inst_time <= t && t <= cut.inst_time + cut.dur() {
          return Some((cut.clip.act)(t));
        }
      }
    }

    None
  }
}

impl<'a, 'b, 'c, 'd> From<&'d [Track<'a, 'b, 'c>]> for Timeline<'a, 'b, 'c> {
  fn from(tracks: &'d [Track<'a, 'b, 'c>]) -> Self {
    Timeline {
      tracks: tracks.to_vec(),
    }
  }
}

#[derive(Debug, Deserialize, PartialEq, Serialize)]
pub struct TimelineManifest {
  pub tracks: Vec<TrackManifest>
}

impl Load for TimelineManifest {
  type Args = ();

  const TY_STR: &'static str = "edit";

  fn load<P>(path: P, _: &mut ResCache, _: Self::Args) -> Result<Self> where P: AsRef<Path> {
    let path = path.as_ref();

    info!("loading timeline: {:?}", path);

    let file = File::open(path).map_err(|e| LoadError::FileNotFound(path.to_path_buf(), format!("{:?}", e)))?;
    from_reader(file).map_err(|e| LoadError::ParseFailed(format!("{:?}", e)))
  }
}

#[derive(Debug, Deserialize, PartialEq, Serialize)]
pub struct TrackManifest {
  pub cuts: Vec<CutManifest>
}

#[derive(Debug, Deserialize, PartialEq, Serialize)]
pub struct CutManifest {
  pub in_time: Time,
  pub out_time: Time,
  pub inst_time: Time,
  pub clip: String
}
<commit_msg>Clip warning.<commit_after>use luminance::{Dim2, Flat, RGBA32F, Texture};
use serde_json::from_reader;
use std::collections::HashMap;
use std::fs::File;
use std::path::Path;

use resource::{Load, LoadError, Result, ResCache};

/// Time.
pub type Time = f32;

/// A clip is an object that implements a visual sequence.
pub struct Clip<'a, 'b> where 'a: 'b {
  act: Box<Fn(Time) -> &'a Texture<Flat, Dim2, RGBA32F> + 'b>
}

impl<'a, 'b> Clip<'a, 'b> {
  pub fn new<F>(act: F) -> Self where F: 'b + Fn(Time) -> &'a Texture<Flat, Dim2, RGBA32F> {
    Clip {
      act: Box::new(act)
    }
  }
}

/// A cut is an object that slices a `Clip` at an *input time* and *output time*. It is instantiated
/// in a `Track` at a given *instance time*.
#[derive(Clone)]
pub struct Cut<'a, 'b, 'c> where 'a: 'b, 'b: 'c {
  pub in_time: Time,
  pub out_time: Time,
  pub inst_time: Time,
  pub clip: &'c Clip<'a, 'b>
}

impl<'a, 'b, 'c> Cut<'a, 'b, 'c> where 'a: 'b, 'b: 'c {
  pub fn new(in_time: Time, out_time: Time, inst_time: Time, clip: &'c Clip<'a, 'b>) -> Self {
    assert!(in_time <= out_time);

    Cut {
      in_time: in_time,
      out_time: out_time,
      inst_time: inst_time,
      clip: clip
    }
  }

  /// Duration of the cut.
  pub fn dur(&self) -> Time {
    self.out_time - self.in_time
  }
}

/// A track gathers `Cut`s and its purpose is to be used inside a `Timeline`.
#[derive(Clone)]
pub struct Track<'a, 'b, 'c> where 'a: 'b, 'b: 'c {
  cuts: Vec<Cut<'a, 'b, 'c>>
}

impl<'a, 'b, 'c> Track<'a, 'b, 'c> where 'a: 'b, 'b: 'c {
  pub fn new() -> Self {
    Track {
      cuts: Vec::new()
    }
  }

  pub fn add_cut(&mut self, cut: Cut<'a, 'b, 'c>) {
    self.cuts.push(cut);
  }
}

impl<'a, 'b, 'c, 'd> From<&'d [Cut<'a, 'b, 'c>]> for Track<'a, 'b, 'c> {
  fn from(cuts: &'d [Cut<'a, 'b, 'c>]) -> Self {
    Track {
      cuts: cuts.to_vec()
    }
  }
}

/// A timeline gathers tracks used to build up the visual aspect of the demo.
#[derive(Clone)]
pub struct Timeline<'a, 'b, 'c> where 'a: 'b, 'b: 'c {
  tracks: Vec<Track<'a, 'b, 'c>>,
}

impl<'a, 'b, 'c> Timeline<'a, 'b, 'c> where 'a: 'b, 'b: 'c {
  pub fn new() -> Self {
    Timeline {
      tracks: Vec::new(),
    }
  }

  /// Turn a TimelineManifest into a Timeline by providing a mapping between clips’ names and real
  /// clips.
  pub fn from_manifest(manifest: &TimelineManifest, mapping: &HashMap<String, &'c Clip<'a, 'b>>) -> Self {
    let mut timeline = Self::new();

    for track_manifest in &manifest.tracks {
      let mut track = Track::new();

      for cut_manifest in &track_manifest.cuts {
        let in_time = cut_manifest.in_time;
        let out_time = cut_manifest.out_time;
        let inst_time = cut_manifest.inst_time;

        if let Some(clip) = mapping.get(&cut_manifest.clip).cloned() {
          track.add_cut(Cut::new(in_time, out_time, inst_time, clip));
        } else {
          warn!("the clip {:?} doesn’t exist", cut_manifest.clip);
        }
      }

      timeline.add_track(track);
    }

    timeline
  }

  pub fn add_track(&mut self, track: Track<'a, 'b, 'c>) {
    self.tracks.push(track);
  }

  // TODO: currently, we play the first track we find; add transition support
  pub fn play(&self, t: Time) -> Option<&'a Texture<Flat, Dim2, RGBA32F>> {
    for track in &self.tracks {
      for cut in &track.cuts {
        if cut.inst_time <= t && t <= cut.inst_time + cut.dur() {
          return Some((cut.clip.act)(t));
        }
      }
    }

    None
  }
}

impl<'a, 'b, 'c, 'd> From<&'d [Track<'a, 'b, 'c>]> for Timeline<'a, 'b, 'c> {
  fn from(tracks: &'d [Track<'a, 'b, 'c>]) -> Self {
    Timeline {
      tracks: tracks.to_vec(),
    }
  }
}

#[derive(Debug, Deserialize, PartialEq, Serialize)]
pub struct TimelineManifest {
  pub tracks: Vec<TrackManifest>
}

impl Load for TimelineManifest {
  type Args = ();

  const TY_STR: &'static str = "edit";

  fn load<P>(path: P, _: &mut ResCache, _: Self::Args) -> Result<Self> where P: AsRef<Path> {
    let path = path.as_ref();

    info!("loading timeline: {:?}", path);

    let file = File::open(path).map_err(|e| LoadError::FileNotFound(path.to_path_buf(), format!("{:?}", e)))?;
    from_reader(file).map_err(|e| LoadError::ParseFailed(format!("{:?}", e)))
  }
}

#[derive(Debug, Deserialize, PartialEq, Serialize)]
pub struct TrackManifest {
  pub cuts: Vec<CutManifest>
}

#[derive(Debug, Deserialize, PartialEq, Serialize)]
pub struct CutManifest {
  pub in_time: Time,
  pub out_time: Time,
  pub inst_time: Time,
  pub clip: String
}
<|endoftext|>


<commit_before>use std::io::{self, Read};
use std::cmp;
use std::fmt;

use parse::{self, ParseError};
use value::{Value, Args, FromLisp, ToLisp};
use valtype::Type;
use env::Env;

pub type FuncResult = Result<Value, FuncError>;

pub enum FuncError {
    InvalidArguments {
        expected: Args,
        got: usize,
    },
    InvalidType {
        expected: Vec<Type>,
        got: Value,
    },
    UndeclaredSymbol(String),

    AttemptToCallNonFunction(Value),
    AttemptToEvalEmptyList,

    GivenEmptyList,

    IoError(io::Error),

    ParsingErr(ParseError),
}

pub struct Lisp {
    pub scopes: Vec<Env>,
}

impl Lisp {
    pub fn new() -> Lisp {
        Lisp {
            scopes: vec![Env::std_lib()],
        }
    }

    pub fn eval<T: FromLisp>(&mut self, code: &str) -> Result<T, FuncError> {
        let result = try!(self.eval_raw(code));

        return T::from_lisp(result);
    }

    pub fn eval_reader<R: Read>(&mut self, mut code: R) -> FuncResult {
        let mut read_string = String::new();
        match code.read_to_string(&mut read_string) {
            Ok(_) => (),
            Err(err) => return Err(FuncError::IoError(err)),
        }

        self.eval_raw(&read_string)
    }

    pub fn eval_raw(&mut self, code: &str) -> FuncResult {
        let tokens = match parse::parse_str(code) {
            Ok(tok) => tok,
            Err(err) => return Err(FuncError::ParsingErr(err)),
        };

        self.eval_token_vec(tokens)
    }

    pub fn eval_token_vec(&mut self, mut tokens: Vec<Value>) -> FuncResult {
        let ret_token = match tokens.pop() {
            Some(token) => token,
            None => unreachable!(),
        };

        for token in tokens {
            try!(self.eval_token(token));
        }

        self.eval_token(ret_token)
    }

    pub fn eval_token(&mut self, token: Value) -> FuncResult {
        match token {
            Value::Symbol(sym) => {
                for env in self.scopes.iter().rev() {
                    if let Some(val) = env.map.get(&sym) {
                        return Ok(val.clone());
                    }
                }

                Err(FuncError::UndeclaredSymbol(sym))
            },
            Value::List(mut tokens) => {
                if tokens.len() == 0 {
                  return Err(FuncError::AttemptToEvalEmptyList);
                }

                let val = tokens.remove(0);
                let func = try!(self.eval_token(val.clone()));
                self.sub_scope(); // each list has its own scope

                match func {
                    Value::HardFunc(hard_func) => {
                        let mut args = Vec::new();

                        for token in tokens {
                            args.push(try!(self.eval_token(token)));
                        }

                        match hard_func.args {
                            Args::Variant => (),
                            Args::Fixed(count) => {
                                if args.len() != count {
                                    invalid_args!(self, Args::Fixed(count), args);
                                }
                            },
                            Args::Multiple(possible_counts) => {
                                let mut arg_match = false;

                                // I wish I could label match statements and break out of them...
                                for count in &possible_counts {
                                    if args.len() == *count {
                                        arg_match = true;
                                        break;
                                    }
                                }

                                if !arg_match {
                                    invalid_args!(self, Args::Multiple(possible_counts), args);
                                }
                            },
                            Args::Atleast(count) => {
                                if args.len() < count {
                                    invalid_args!(self, Args::Atleast(count), args);
                                }
                            }
                        }

                        let result = (hard_func.func)(args, self);
                        self.exit_scope();

                        result
                    },
                    Value::Lambda { args, body } => {
                        if tokens.len() != args.len() {
                            invalid_args!(self, Args::Fixed(args.len()), tokens);
                        }

                        for sym in args.iter() {
                            let value = try!(self.eval_token(tokens.remove(0)));
                            self.cur_scope().set(sym, value);
                        }

                        let result = self.eval_token_vec(body);
                        self.exit_scope();

                        result
                    },
                    _ => return Err(FuncError::AttemptToCallNonFunction(val)),
                }
            },
            Value::Quote(val) => Ok(*val),
            _ => Ok(token),
        }
    }

    pub fn set_global<T: ToLisp>(&mut self, name: &str, value: T) {
        self.scopes[0].set(name, value);
    }

    pub fn sub_scope(&mut self) {
        self.scopes.push(Env::new());
    }

    pub fn exit_scope(&mut self) {
        self.scopes.pop();
    }

    pub fn cur_scope(&mut self) -> &mut Env {
        let index = self.scopes.len() - 1;
        &mut self.scopes[index]
    }

    pub fn parent_scope(&mut self) -> &mut Env {
        let index = cmp::max(self.scopes.len() as i32 - 2, 0) as usize;
        &mut self.scopes[index]
    }
}

impl fmt::Debug for Value {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Value::List(ref values) => parse::write_list(fmt, values, "(", " ", ")"),
            &Value::String(ref string) => write!(fmt, "{:?}", string),
            &Value::Symbol(ref string) => write!(fmt, "{}", string),
            &Value::Number(num) => write!(fmt, "{}", num),
            &Value::HardFunc(ref func) => write!(fmt, "HardFunc({:?})", func.args),
            &Value::Lambda { ref args, ref body }=> write!(fmt, "λ {:?} => {:?}", args, body),
            &Value::Nil => write!(fmt, "nil"),
            &Value::Bool(val) => write!(fmt, "{}", val),
            &Value::Quote(ref tok) => write!(fmt, "'{:?}", tok),
            &Value::Type(ref typ) => write!(fmt, "{:?}", typ),
            &Value::Foreign(ref val) => write!(fmt, "{:?}", val),
        }
    }
}
<commit_msg>Add ability to create Lisp with no stdlib<commit_after>use std::io::{self, Read};
use std::cmp;
use std::fmt;

use parse::{self, ParseError};
use value::{Value, Args, FromLisp, ToLisp};
use valtype::Type;
use env::Env;

pub type FuncResult = Result<Value, FuncError>;

pub enum FuncError {
    InvalidArguments {
        expected: Args,
        got: usize,
    },
    InvalidType {
        expected: Vec<Type>,
        got: Value,
    },
    UndeclaredSymbol(String),

    AttemptToCallNonFunction(Value),
    AttemptToEvalEmptyList,

    GivenEmptyList,

    IoError(io::Error),

    ParsingErr(ParseError),
}

pub struct Lisp {
    pub scopes: Vec<Env>,
}

impl Lisp {
    pub fn new() -> Lisp {
        Lisp {
            scopes: vec![Env::std_lib()],
        }
    }

    pub fn new_no_stdlib() -> Lisp {
        Lisp {
            scopes: Vec::new(),
        }
    }

    pub fn eval<T: FromLisp>(&mut self, code: &str) -> Result<T, FuncError> {
        let result = try!(self.eval_raw(code));

        return T::from_lisp(result);
    }

    pub fn eval_reader<R: Read>(&mut self, mut code: R) -> FuncResult {
        let mut read_string = String::new();
        match code.read_to_string(&mut read_string) {
            Ok(_) => (),
            Err(err) => return Err(FuncError::IoError(err)),
        }

        self.eval_raw(&read_string)
    }

    pub fn eval_raw(&mut self, code: &str) -> FuncResult {
        let tokens = match parse::parse_str(code) {
            Ok(tok) => tok,
            Err(err) => return Err(FuncError::ParsingErr(err)),
        };

        self.eval_token_vec(tokens)
    }

    pub fn eval_token_vec(&mut self, mut tokens: Vec<Value>) -> FuncResult {
        let ret_token = match tokens.pop() {
            Some(token) => token,
            None => unreachable!(),
        };

        for token in tokens {
            try!(self.eval_token(token));
        }

        self.eval_token(ret_token)
    }

    pub fn eval_token(&mut self, token: Value) -> FuncResult {
        match token {
            Value::Symbol(sym) => {
                for env in self.scopes.iter().rev() {
                    if let Some(val) = env.map.get(&sym) {
                        return Ok(val.clone());
                    }
                }

                Err(FuncError::UndeclaredSymbol(sym))
            },
            Value::List(mut tokens) => {
                if tokens.len() == 0 {
                  return Err(FuncError::AttemptToEvalEmptyList);
                }

                let val = tokens.remove(0);
                let func = try!(self.eval_token(val.clone()));
                self.sub_scope(); // each list has its own scope

                match func {
                    Value::HardFunc(hard_func) => {
                        let mut args = Vec::new();

                        for token in tokens {
                            args.push(try!(self.eval_token(token)));
                        }

                        match hard_func.args {
                            Args::Variant => (),
                            Args::Fixed(count) => {
                                if args.len() != count {
                                    invalid_args!(self, Args::Fixed(count), args);
                                }
                            },
                            Args::Multiple(possible_counts) => {
                                let mut arg_match = false;

                                // I wish I could label match statements and break out of them...
                                for count in &possible_counts {
                                    if args.len() == *count {
                                        arg_match = true;
                                        break;
                                    }
                                }

                                if !arg_match {
                                    invalid_args!(self, Args::Multiple(possible_counts), args);
                                }
                            },
                            Args::Atleast(count) => {
                                if args.len() < count {
                                    invalid_args!(self, Args::Atleast(count), args);
                                }
                            }
                        }

                        let result = (hard_func.func)(args, self);
                        self.exit_scope();

                        result
                    },
                    Value::Lambda { args, body } => {
                        if tokens.len() != args.len() {
                            invalid_args!(self, Args::Fixed(args.len()), tokens);
                        }

                        for sym in args.iter() {
                            let value = try!(self.eval_token(tokens.remove(0)));
                            self.cur_scope().set(sym, value);
                        }

                        let result = self.eval_token_vec(body);
                        self.exit_scope();

                        result
                    },
                    _ => return Err(FuncError::AttemptToCallNonFunction(val)),
                }
            },
            Value::Quote(val) => Ok(*val),
            _ => Ok(token),
        }
    }

    pub fn set_global<T: ToLisp>(&mut self, name: &str, value: T) {
        self.scopes[0].set(name, value);
    }

    pub fn sub_scope(&mut self) {
        self.scopes.push(Env::new());
    }

    pub fn exit_scope(&mut self) {
        self.scopes.pop();
    }

    pub fn cur_scope(&mut self) -> &mut Env {
        let index = self.scopes.len() - 1;
        &mut self.scopes[index]
    }

    pub fn parent_scope(&mut self) -> &mut Env {
        let index = cmp::max(self.scopes.len() as i32 - 2, 0) as usize;
        &mut self.scopes[index]
    }
}

impl fmt::Debug for Value {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Value::List(ref values) => parse::write_list(fmt, values, "(", " ", ")"),
            &Value::String(ref string) => write!(fmt, "{:?}", string),
            &Value::Symbol(ref string) => write!(fmt, "{}", string),
            &Value::Number(num) => write!(fmt, "{}", num),
            &Value::HardFunc(ref func) => write!(fmt, "HardFunc({:?})", func.args),
            &Value::Lambda { ref args, ref body }=> write!(fmt, "λ {:?} => {:?}", args, body),
            &Value::Nil => write!(fmt, "nil"),
            &Value::Bool(val) => write!(fmt, "{}", val),
            &Value::Quote(ref tok) => write!(fmt, "'{:?}", tok),
            &Value::Type(ref typ) => write!(fmt, "{:?}", typ),
            &Value::Foreign(ref val) => write!(fmt, "{:?}", val),
        }
    }
}
<|endoftext|>