<commit_before>#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

extern crate ytdl;
extern crate clap;
extern crate url;
#[macro_use]
extern crate log;
extern crate env_logger;
extern crate reqwest;
extern crate pbr;
extern crate serde_json;
extern crate openssl_probe;

use clap::{App, AppSettings, Arg};
use pbr::{ProgressBar, Units};
use reqwest::header::{ContentLength, Headers, Range};
use std::env;
use std::fs::OpenOptions;
use std::io::{Read, Write};
use std::str::FromStr;
use ytdl::format;
use ytdl::format_list::{Filter, FormatList};
use ytdl::video_info::{self, YTDL_PROXY_URL};

#[derive(Debug)]
struct Options {
    no_progress: bool,
    info_only: bool,
    silent: bool,
    debug: bool,
    append: bool,
    json: bool,
    download_url: bool,
    filter: Vec<String>,
    byte_range: String,
    output_file: String,
    start_offset: i32,
    proxy_url: String,
}

fn main() {
    openssl_probe::init_ssl_cert_env_vars();
    env_logger::init();

    let flags = vec![
        Arg::with_name("output")
            .short("o")
            .long("output")
            .value_name("FILE")
            .help("write output to a file")
            .takes_value(true),
        Arg::with_name("proxy-url")
            .short("p")
            .long("proxy-url")
            .value_name("PROXY_URL")
            .help("use proxy for the request")
            .takes_value(true),
        Arg::with_name("no-progress")
            .long("no-progress")
            .help("write output to a file"),
        Arg::with_name("range")
            .short("r")
            .long("range")
            .value_name("RANGE")
            .help(
                "download a specific range of bytes of the video, [start]-[end]",
            )
            .takes_value(true),
        Arg::with_name("url")
            .help("youtube video url, short url or video id")
            .required(true)
            .index(1),
        Arg::with_name("download-url")
            .short("-u")
            .long("download-url")
            .help("prints download url to stdout"),
        Arg::with_name("json")
            .short("j")
            .long("json")
            .help("print info json to stdout"),
        Arg::with_name("debug")
            .short("d")
            .long("debug")
            .help("output debug log"),
        Arg::with_name("filter")
            .short("f")
            .long("filter")
            .value_name("FILTER")
            .multiple(true)
            .help("filter available formats, syntax: val1 val2 val3")
            .takes_value(true),
        Arg::with_name("append")
            .short("-a")
            .long("--append")
            .help("append to output file instead of overwriting"),
        Arg::with_name("start-offset")
            .long("start-offset")
            .value_name("STARTOFFSET")
            .help("offset the start of the video")
            .takes_value(true),
        Arg::with_name("silent")
            .short("s")
            .long("silent")
            .help("only output error, also diables progressbar"),
        Arg::with_name("info")
            .short("i")
            .long("info")
            .help("only output info"),
    ];

    let matches = App::new("ytdl")
        .setting(AppSettings::ArgRequiredElseHelp)
        .version("0.1.4")
        .about("download youtube videos")
        .args(&flags)
        .get_matches();

    let mut options = Options {
        no_progress: matches.is_present("no-progress"),
        info_only: matches.is_present("info"),
        silent: matches.is_present("silent"),
        debug: matches.is_present("debug"),
        append: matches.is_present("append"),
        json: matches.is_present("json"),
        download_url: matches.is_present("download-url"),
        filter: matches
            .values_of("filter")
            .unwrap_or_default()
            .map(|x| x.to_string())
            .collect(),
        output_file: matches.value_of("output").unwrap_or_default().to_string(),
        byte_range: matches.value_of("range").unwrap_or_default().to_string(),
        start_offset: matches
            .value_of("start-offset")
            .unwrap_or("0")
            .parse::<i32>()
            .unwrap(),
        proxy_url: matches
            .value_of("proxy-url")
            .unwrap_or_default()
            .to_string(),
    };

    if !options.proxy_url.is_empty() {
        env::set_var(YTDL_PROXY_URL, &options.proxy_url);
    }

    let identifier = matches.value_of("url").unwrap_or_default();
    if options.filter.is_empty() {
        options.filter = vec![
            format!("{}:mp4", format::FORMAT_EXTENSION_KEY),
            format!("!{}:", format::FORMAT_VIDEO_ENCODING_KEY),
            format!("!{}:", format::FORMAT_AUDIO_ENCODING_KEY),
            format!("best"),
        ];
    }

    handler(identifier, &options);
}

fn handler(identifier: &str, options: &Options) {
    info!("fetching video info...");
    let info = match video_info::get_video_info(identifier) {
        Ok(i) => i,
        Err(e) => {
            println!("unable to fetch video info: {}", e.to_string());
            return;
        }
    };

    if options.info_only {
        println!("Author: {}", info.author);
        println!("Duration: {}s", info.duration);
        return;
    } else if options.json {
        println!("{}", serde_json::to_string(&info).unwrap_or_default());
        return;
    }

    let formats = filter_formats(&options.filter, &info.formats);
    if formats.is_empty() {
        println!("no formats available that match criteria");
        return;
    }

    let mut download_url = match video_info::get_download_url(&formats[0]) {
        Ok(u) => u,
        Err(e) => {
            println!("unable to get download url: {}", e.to_string());
            return;
        }
    };

    if options.start_offset != 0 {
        download_url
            .query_pairs_mut()
            .append_pair("begin", &format!("{}", &options.start_offset * 1000,));
    }

    if options.download_url {
        println!("{}", download_url.as_str());
    }

    let mut filename = if !options.output_file.is_empty() {
        format!("{}.{}", options.output_file, &formats[0].extension)
    } else {
        video_info::get_filename(&info, &formats[0])
    };
    filename = filename.replace("/", "-");

    let mut file = if options.append {
        OpenOptions::new()
            .write(true)
            .create(true)
            .append(true)
            .open(&filename)
            .expect("create output file fail")
    } else {
        OpenOptions::new()
            .write(true)
            .create(true)
            .open(&filename)
            .expect("create output file fail")
    };

    info!("download to {}", filename);
    let mut headers = Headers::new();
    if !options.byte_range.is_empty() {
        let r =
            Range::from_str(&format!("bytes={}", &options.byte_range)).expect("invalid range str");
        headers.set(r);
    }

    let client = video_info::get_client().expect("get request client fail");
    let mut resp = client
        .get(download_url.as_str())
        .expect("download fail")
        .headers(headers)
        .send()
        .expect("download fail");

    let file_size = resp.headers()
        .get::<ContentLength>()
        .map(|l| **l)
        .unwrap_or(0);

    let mut pb = ProgressBar::new(file_size);
    pb.format("╢▌▌░╟");
    pb.set_units(Units::Bytes);
    pb.show_percent = true;
    pb.show_speed = true;
    pb.show_time_left = true;
    pb.set_width(Some(100));
    let mut buf = [0; 128 * 1024];

    loop {
        match resp.read(&mut buf) {
            Ok(len) => {
                file.write_all(&buf[..len]).expect("write to file fail");
                if !options.silent && !options.no_progress {
                    pb.add(len as u64);
                }
                if len == 0 {
                    break;
                }
            }
            Err(e) => panic!("{}", e.to_string()),
        };
    }

    println!("");
}

fn filter_formats(filters: &Vec<String>, formats: &FormatList) -> FormatList {
    let mut formats = formats.clone();
    for fi in filters {
        let filter_str = fi.as_str();
        formats = match filter_str {
            "best" | "worst" => {
                formats
                    .extremes(format::FORMAT_RESOLUTION_KEY, filter_str == "best")
                    .extremes(format::FORMAT_AUDIO_BITRATE_KEY, filter_str == "best")
            }
            "best-video" | "worst-video" => {
                formats.extremes(
                    format::FORMAT_RESOLUTION_KEY,
                    filter_str.starts_with("best"),
                )
            }
            "best-audio" | "worst-audio" => {
                formats.extremes(
                    format::FORMAT_AUDIO_BITRATE_KEY,
                    filter_str.starts_with("best"),
                )
            }
            _ => {
                let split = filter_str.splitn(2, ":").collect::<Vec<&str>>();
                if split.len() != 2 {
                    panic!("invalid filter key");
                }

                let mut key = split[0].to_string();
                let exclude = key.starts_with("!");
                let start = if exclude { 1 } else { 0 };
                let key: String = key.drain(start..).collect::<String>();
                let value = split[1].trim();
                if value == "best" || value == "worst" {
                    let f = formats.extremes(&key, value == "best");
                    formats = formats.subtract(&f);
                    return formats;
                }
                let mut vals = value.split(",").collect::<Vec<&str>>();
                for i in 0..vals.len() {
                    vals[i] = vals[i].trim();
                }
                let f = formats.filter(&key, &vals);
                if exclude {
                    formats.subtract(&f)
                } else {
                    f
                }
            }
        };
    }
    formats
}
<commit_msg>env_logger init unwrap<commit_after>#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

extern crate ytdl;
extern crate clap;
extern crate url;
#[macro_use]
extern crate log;
extern crate env_logger;
extern crate reqwest;
extern crate pbr;
extern crate serde_json;
extern crate openssl_probe;

use clap::{App, AppSettings, Arg};
use pbr::{ProgressBar, Units};
use reqwest::header::{ContentLength, Headers, Range};
use std::env;
use std::fs::OpenOptions;
use std::io::{Read, Write};
use std::str::FromStr;
use ytdl::format;
use ytdl::format_list::{Filter, FormatList};
use ytdl::video_info::{self, YTDL_PROXY_URL};

#[derive(Debug)]
struct Options {
    no_progress: bool,
    info_only: bool,
    silent: bool,
    debug: bool,
    append: bool,
    json: bool,
    download_url: bool,
    filter: Vec<String>,
    byte_range: String,
    output_file: String,
    start_offset: i32,
    proxy_url: String,
}

fn main() {
    openssl_probe::init_ssl_cert_env_vars();
    env_logger::init().unwrap();

    let flags = vec![
        Arg::with_name("output")
            .short("o")
            .long("output")
            .value_name("FILE")
            .help("write output to a file")
            .takes_value(true),
        Arg::with_name("proxy-url")
            .short("p")
            .long("proxy-url")
            .value_name("PROXY_URL")
            .help("use proxy for the request")
            .takes_value(true),
        Arg::with_name("no-progress")
            .long("no-progress")
            .help("write output to a file"),
        Arg::with_name("range")
            .short("r")
            .long("range")
            .value_name("RANGE")
            .help(
                "download a specific range of bytes of the video, [start]-[end]",
            )
            .takes_value(true),
        Arg::with_name("url")
            .help("youtube video url, short url or video id")
            .required(true)
            .index(1),
        Arg::with_name("download-url")
            .short("-u")
            .long("download-url")
            .help("prints download url to stdout"),
        Arg::with_name("json")
            .short("j")
            .long("json")
            .help("print info json to stdout"),
        Arg::with_name("debug")
            .short("d")
            .long("debug")
            .help("output debug log"),
        Arg::with_name("filter")
            .short("f")
            .long("filter")
            .value_name("FILTER")
            .multiple(true)
            .help("filter available formats, syntax: val1 val2 val3")
            .takes_value(true),
        Arg::with_name("append")
            .short("-a")
            .long("--append")
            .help("append to output file instead of overwriting"),
        Arg::with_name("start-offset")
            .long("start-offset")
            .value_name("STARTOFFSET")
            .help("offset the start of the video")
            .takes_value(true),
        Arg::with_name("silent")
            .short("s")
            .long("silent")
            .help("only output error, also diables progressbar"),
        Arg::with_name("info")
            .short("i")
            .long("info")
            .help("only output info"),
    ];

    let matches = App::new("ytdl")
        .setting(AppSettings::ArgRequiredElseHelp)
        .version("0.1.4")
        .about("download youtube videos")
        .args(&flags)
        .get_matches();

    let mut options = Options {
        no_progress: matches.is_present("no-progress"),
        info_only: matches.is_present("info"),
        silent: matches.is_present("silent"),
        debug: matches.is_present("debug"),
        append: matches.is_present("append"),
        json: matches.is_present("json"),
        download_url: matches.is_present("download-url"),
        filter: matches
            .values_of("filter")
            .unwrap_or_default()
            .map(|x| x.to_string())
            .collect(),
        output_file: matches.value_of("output").unwrap_or_default().to_string(),
        byte_range: matches.value_of("range").unwrap_or_default().to_string(),
        start_offset: matches
            .value_of("start-offset")
            .unwrap_or("0")
            .parse::<i32>()
            .unwrap(),
        proxy_url: matches
            .value_of("proxy-url")
            .unwrap_or_default()
            .to_string(),
    };

    if !options.proxy_url.is_empty() {
        env::set_var(YTDL_PROXY_URL, &options.proxy_url);
    }

    let identifier = matches.value_of("url").unwrap_or_default();
    if options.filter.is_empty() {
        options.filter = vec![
            format!("{}:mp4", format::FORMAT_EXTENSION_KEY),
            format!("!{}:", format::FORMAT_VIDEO_ENCODING_KEY),
            format!("!{}:", format::FORMAT_AUDIO_ENCODING_KEY),
            format!("best"),
        ];
    }

    handler(identifier, &options);
}

fn handler(identifier: &str, options: &Options) {
    info!("fetching video info...");
    let info = match video_info::get_video_info(identifier) {
        Ok(i) => i,
        Err(e) => {
            println!("unable to fetch video info: {}", e.to_string());
            return;
        }
    };

    if options.info_only {
        println!("Author: {}", info.author);
        println!("Duration: {}s", info.duration);
        return;
    } else if options.json {
        println!("{}", serde_json::to_string(&info).unwrap_or_default());
        return;
    }

    let formats = filter_formats(&options.filter, &info.formats);
    if formats.is_empty() {
        println!("no formats available that match criteria");
        return;
    }

    let mut download_url = match video_info::get_download_url(&formats[0]) {
        Ok(u) => u,
        Err(e) => {
            println!("unable to get download url: {}", e.to_string());
            return;
        }
    };

    if options.start_offset != 0 {
        download_url
            .query_pairs_mut()
            .append_pair("begin", &format!("{}", &options.start_offset * 1000,));
    }

    if options.download_url {
        println!("{}", download_url.as_str());
    }

    let mut filename = if !options.output_file.is_empty() {
        format!("{}.{}", options.output_file, &formats[0].extension)
    } else {
        video_info::get_filename(&info, &formats[0])
    };
    filename = filename.replace("/", "-");

    let mut file = if options.append {
        OpenOptions::new()
            .write(true)
            .create(true)
            .append(true)
            .open(&filename)
            .expect("create output file fail")
    } else {
        OpenOptions::new()
            .write(true)
            .create(true)
            .open(&filename)
            .expect("create output file fail")
    };

    info!("download to {}", filename);
    let mut headers = Headers::new();
    if !options.byte_range.is_empty() {
        let r =
            Range::from_str(&format!("bytes={}", &options.byte_range)).expect("invalid range str");
        headers.set(r);
    }

    let client = video_info::get_client().expect("get request client fail");
    let mut resp = client
        .get(download_url.as_str())
        .expect("download fail")
        .headers(headers)
        .send()
        .expect("download fail");

    let file_size = resp.headers()
        .get::<ContentLength>()
        .map(|l| **l)
        .unwrap_or(0);

    let mut pb = ProgressBar::new(file_size);
    pb.format("╢▌▌░╟");
    pb.set_units(Units::Bytes);
    pb.show_percent = true;
    pb.show_speed = true;
    pb.show_time_left = true;
    pb.set_width(Some(100));
    let mut buf = [0; 128 * 1024];

    loop {
        match resp.read(&mut buf) {
            Ok(len) => {
                file.write_all(&buf[..len]).expect("write to file fail");
                if !options.silent && !options.no_progress {
                    pb.add(len as u64);
                }
                if len == 0 {
                    break;
                }
            }
            Err(e) => panic!("{}", e.to_string()),
        };
    }

    println!("");
}

fn filter_formats(filters: &Vec<String>, formats: &FormatList) -> FormatList {
    let mut formats = formats.clone();
    for fi in filters {
        let filter_str = fi.as_str();
        formats = match filter_str {
            "best" | "worst" => {
                formats
                    .extremes(format::FORMAT_RESOLUTION_KEY, filter_str == "best")
                    .extremes(format::FORMAT_AUDIO_BITRATE_KEY, filter_str == "best")
            }
            "best-video" | "worst-video" => {
                formats.extremes(
                    format::FORMAT_RESOLUTION_KEY,
                    filter_str.starts_with("best"),
                )
            }
            "best-audio" | "worst-audio" => {
                formats.extremes(
                    format::FORMAT_AUDIO_BITRATE_KEY,
                    filter_str.starts_with("best"),
                )
            }
            _ => {
                let split = filter_str.splitn(2, ":").collect::<Vec<&str>>();
                if split.len() != 2 {
                    panic!("invalid filter key");
                }

                let mut key = split[0].to_string();
                let exclude = key.starts_with("!");
                let start = if exclude { 1 } else { 0 };
                let key: String = key.drain(start..).collect::<String>();
                let value = split[1].trim();
                if value == "best" || value == "worst" {
                    let f = formats.extremes(&key, value == "best");
                    formats = formats.subtract(&f);
                    return formats;
                }
                let mut vals = value.split(",").collect::<Vec<&str>>();
                for i in 0..vals.len() {
                    vals[i] = vals[i].trim();
                }
                let f = formats.filter(&key, &vals);
                if exclude {
                    formats.subtract(&f)
                } else {
                    f
                }
            }
        };
    }
    formats
}
<|endoftext|>


<commit_before>//! Thread pool for blocking operations

use futures::sync::oneshot;
use futures::{Async, Future, Poll};
use parking_lot::Mutex;
use threadpool::ThreadPool;

/// Env variable for default cpu pool size
const ENV_CPU_POOL_VAR: &str = "ACTIX_CPU_POOL";

lazy_static::lazy_static! {
    pub(crate) static ref DEFAULT_POOL: Mutex<ThreadPool> = {
        let default = match std::env::var(ENV_CPU_POOL_VAR) {
            Ok(val) => {
                if let Ok(val) = val.parse() {
                    val
                } else {
                    log::error!("Can not parse ACTIX_CPU_POOL value");
                    num_cpus::get() * 5
                }
            }
            Err(_) => num_cpus::get() * 5,
        };
        Mutex::new(
            threadpool::Builder::new()
                .thread_name("actix-web".to_owned())
                .num_threads(default)
                .build(),
        )
    };
}

thread_local! {
    static POOL: ThreadPool = {
        DEFAULT_POOL.lock().clone()
    };
}

pub enum BlockingError<E> {
    Error(E),
    Canceled,
}

/// Execute blocking function on a thread pool, returns future that resolves
/// to result of the function execution.
pub fn run<F, I, E>(f: F) -> CpuFuture<I, E>
where
    F: FnOnce() -> Result<I, E> + Send + 'static,
    I: Send + 'static,
    E: Send + 'static,
{
    let (tx, rx) = oneshot::channel();
    POOL.with(|pool| {
        pool.execute(move || {
            let _ = tx.send(f());
        })
    });

    CpuFuture { rx }
}

pub struct CpuFuture<I, E> {
    rx: oneshot::Receiver<Result<I, E>>,
}

impl<I, E> Future for CpuFuture<I, E> {
    type Item = I;
    type Error = BlockingError<E>;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        let res =
            futures::try_ready!(self.rx.poll().map_err(|_| BlockingError::Canceled));
        match res {
            Ok(val) => Ok(Async::Ready(val)),
            Err(err) => Err(BlockingError::Error(err)),
        }
    }
}
<commit_msg>implement ResponseError trait for BlockingError<commit_after>//! Thread pool for blocking operations

use std::fmt;

use derive_more::Display;
use futures::sync::oneshot;
use futures::{Async, Future, Poll};
use parking_lot::Mutex;
use threadpool::ThreadPool;

use crate::ResponseError;

/// Env variable for default cpu pool size
const ENV_CPU_POOL_VAR: &str = "ACTIX_CPU_POOL";

lazy_static::lazy_static! {
    pub(crate) static ref DEFAULT_POOL: Mutex<ThreadPool> = {
        let default = match std::env::var(ENV_CPU_POOL_VAR) {
            Ok(val) => {
                if let Ok(val) = val.parse() {
                    val
                } else {
                    log::error!("Can not parse ACTIX_CPU_POOL value");
                    num_cpus::get() * 5
                }
            }
            Err(_) => num_cpus::get() * 5,
        };
        Mutex::new(
            threadpool::Builder::new()
                .thread_name("actix-web".to_owned())
                .num_threads(default)
                .build(),
        )
    };
}

thread_local! {
    static POOL: ThreadPool = {
        DEFAULT_POOL.lock().clone()
    };
}

#[derive(Debug, Display)]
pub enum BlockingError<E: fmt::Debug> {
    #[display(fmt = "{:?}", _0)]
    Error(E),
    #[display(fmt = "Thread pool is gone")]
    Canceled,
}

impl<E: fmt::Debug> ResponseError for BlockingError<E> {}

/// Execute blocking function on a thread pool, returns future that resolves
/// to result of the function execution.
pub fn run<F, I, E>(f: F) -> CpuFuture<I, E>
where
    F: FnOnce() -> Result<I, E> + Send + 'static,
    I: Send + 'static,
    E: Send + fmt::Debug + 'static,
{
    let (tx, rx) = oneshot::channel();
    POOL.with(|pool| {
        pool.execute(move || {
            let _ = tx.send(f());
        })
    });

    CpuFuture { rx }
}

pub struct CpuFuture<I, E> {
    rx: oneshot::Receiver<Result<I, E>>,
}

impl<I, E: fmt::Debug> Future for CpuFuture<I, E> {
    type Item = I;
    type Error = BlockingError<E>;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        let res =
            futures::try_ready!(self.rx.poll().map_err(|_| BlockingError::Canceled));
        match res {
            Ok(val) => Ok(Async::Ready(val)),
            Err(err) => Err(BlockingError::Error(err)),
        }
    }
}
<|endoftext|>


<commit_before>pub mod bookitem;
pub mod bookconfig;

pub mod bookconfig_test;

pub use self::bookitem::{BookItem, BookItems};
pub use self::bookconfig::BookConfig;

use std::path::{Path, PathBuf};
use std::fs::{self, File};
use std::error::Error;
use std::io;
use std::io::Write;
use std::io::ErrorKind;
use std::process::Command;

use {theme, parse, utils};
use renderer::{Renderer, HtmlHandlebars};

pub struct MDBook {
    root: PathBuf,
    dest: PathBuf,
    src: PathBuf,
    theme_path: PathBuf,

    pub title: String,
    pub author: String,
    pub description: String,

    pub content: Vec<BookItem>,
    renderer: Box<Renderer>,

    livereload: Option<String>,
}

impl MDBook {
    /// Create a new `MDBook` struct with root directory `root`
    ///
    /// Default directory paths:
    ///
    /// - source: `root/src`
    /// - output: `root/book`
    /// - theme: `root/theme`
    ///
    /// They can both be changed by using [`set_src()`](#method.set_src) and [`set_dest()`](#method.set_dest)

    pub fn new(root: &Path) -> MDBook {

        if !root.exists() || !root.is_dir() {
            warn!("{:?} No directory with that name", root);
        }

        MDBook {
            root: root.to_owned(),
            dest: root.join("book"),
            src: root.join("src"),
            theme_path: root.join("theme"),

            title: String::new(),
            author: String::new(),
            description: String::new(),

            content: vec![],
            renderer: Box::new(HtmlHandlebars::new()),

            livereload: None,
        }
    }

    /// Returns a flat depth-first iterator over the elements of the book, it returns an [BookItem enum](bookitem.html):
    /// `(section: String, bookitem: &BookItem)`
    ///
    /// ```no_run
    /// # extern crate mdbook;
    /// # use mdbook::MDBook;
    /// # use mdbook::BookItem;
    /// # use std::path::Path;
    /// # fn main() {
    /// # let mut book = MDBook::new(Path::new("mybook"));
    /// for item in book.iter() {
    ///     match item {
    ///         &BookItem::Chapter(ref section, ref chapter) => {},
    ///         &BookItem::Affix(ref chapter) => {},
    ///         &BookItem::Spacer => {},
    ///     }
    /// }
    ///
    /// // would print something like this:
    /// // 1. Chapter 1
    /// // 1.1 Sub Chapter
    /// // 1.2 Sub Chapter
    /// // 2. Chapter 2
    /// //
    /// // etc.
    /// # }
    /// ```

    pub fn iter(&self) -> BookItems {
        BookItems {
            items: &self.content[..],
            current_index: 0,
            stack: Vec::new(),
        }
    }

    /// `init()` creates some boilerplate files and directories to get you started with your book.
    ///
    /// ```text
    /// book-test/
    /// ├── book
    /// └── src
    ///     ├── chapter_1.md
    ///     └── SUMMARY.md
    /// ```
    ///
    /// It uses the paths given as source and output directories and adds a `SUMMARY.md` and a
    /// `chapter_1.md` to the source directory.

    pub fn init(&mut self) -> Result<(), Box<Error>> {

        debug!("[fn]: init");

        if !self.root.exists() {
            fs::create_dir_all(&self.root).unwrap();
            info!("{:?} created", &self.root);
        }

        {

            if !self.dest.exists() {
                debug!("[*]: {:?} does not exist, trying to create directory", self.dest);
                try!(fs::create_dir_all(&self.dest));
            }

            if !self.src.exists() {
                debug!("[*]: {:?} does not exist, trying to create directory", self.src);
                try!(fs::create_dir_all(&self.src));
            }

            let summary = self.src.join("SUMMARY.md");

            if !summary.exists() {

                // Summary does not exist, create it

                debug!("[*]: {:?} does not exist, trying to create SUMMARY.md", self.src.join("SUMMARY.md"));
                let mut f = try!(File::create(&self.src.join("SUMMARY.md")));

                debug!("[*]: Writing to SUMMARY.md");

                try!(writeln!(f, "# Summary"));
                try!(writeln!(f, ""));
                try!(writeln!(f, "- [Chapter 1](./chapter_1.md)"));
            }
        }

        // parse SUMMARY.md, and create the missing item related file
        try!(self.parse_summary());

        debug!("[*]: constructing paths for missing files");
        for item in self.iter() {
            debug!("[*]: item: {:?}", item);
            match *item {
                BookItem::Spacer => continue,
                BookItem::Chapter(_, ref ch) |
                BookItem::Affix(ref ch) => {
                    if ch.path != PathBuf::new() {
                        let path = self.src.join(&ch.path);

                        if !path.exists() {
                            debug!("[*]: {:?} does not exist, trying to create file", path);
                            try!(::std::fs::create_dir_all(path.parent().unwrap()));
                            let mut f = try!(File::create(path));

                            // debug!("[*]: Writing to {:?}", path);
                            try!(writeln!(f, "# {}", ch.name));
                        }
                    }
                },
            }
        }

        debug!("[*]: init done");
        Ok(())
    }

    pub fn create_gitignore(&self) {
        let gitignore = self.get_gitignore();

        if !gitignore.exists() {
            // Gitignore does not exist, create it

            // Because of `src/book/mdbook.rs#L37-L39`, `dest` will always start with `root`. If it
            // is not, `strip_prefix` will return an Error.
            if !self.get_dest().starts_with(&self.root) {
                return;
            }

            let relative = self.get_dest()
                               .strip_prefix(&self.root)
                               .expect("Destination is not relative to root.");
            let relative = relative.to_str()
                                   .expect("Path could not be yielded into a string slice.");

            debug!("[*]: {:?} does not exist, trying to create .gitignore", gitignore);

            let mut f = File::create(&gitignore).expect("Could not create file.");

            debug!("[*]: Writing to .gitignore");

            writeln!(f, "{}", relative).expect("Could not write to file.");
        }
    }

    /// The `build()` method is the one where everything happens. First it parses `SUMMARY.md` to
    /// construct the book's structure in the form of a `Vec<BookItem>` and then calls `render()`
    /// method of the current renderer.
    ///
    /// It is the renderer who generates all the output files.
    pub fn build(&mut self) -> Result<(), Box<Error>> {
        debug!("[fn]: build");

        try!(self.init());

        // Clean output directory
        try!(utils::fs::remove_dir_content(&self.dest));

        try!(self.renderer.render(&self));

        Ok(())
    }

    pub fn get_gitignore(&self) -> PathBuf {
        self.root.join(".gitignore")
    }

    pub fn copy_theme(&self) -> Result<(), Box<Error>> {
        debug!("[fn]: copy_theme");

        let theme_dir = self.src.join("theme");

        if !theme_dir.exists() {
            debug!("[*]: {:?} does not exist, trying to create directory", theme_dir);
            try!(fs::create_dir(&theme_dir));
        }

        // index.hbs
        let mut index = try!(File::create(&theme_dir.join("index.hbs")));
        try!(index.write_all(theme::INDEX));

        // book.css
        let mut css = try!(File::create(&theme_dir.join("book.css")));
        try!(css.write_all(theme::CSS));

        // favicon.png
        let mut favicon = try!(File::create(&theme_dir.join("favicon.png")));
        try!(favicon.write_all(theme::FAVICON));

        // book.js
        let mut js = try!(File::create(&theme_dir.join("book.js")));
        try!(js.write_all(theme::JS));

        // highlight.css
        let mut highlight_css = try!(File::create(&theme_dir.join("highlight.css")));
        try!(highlight_css.write_all(theme::HIGHLIGHT_CSS));

        // highlight.js
        let mut highlight_js = try!(File::create(&theme_dir.join("highlight.js")));
        try!(highlight_js.write_all(theme::HIGHLIGHT_JS));

        Ok(())
    }

    pub fn write_file<P: AsRef<Path>>(&self, filename: P, content: &[u8]) -> Result<(), Box<Error>> {
        let path = self.get_dest().join(filename);
        try!(utils::fs::create_file(&path).and_then(|mut file| {
            file.write_all(content)
        }).map_err(|e| {
            io::Error::new(io::ErrorKind::Other, format!("Could not create {}: {}", path.display(), e))
        }));
        Ok(())
    }

    /// Parses the `book.json` file (if it exists) to extract the configuration parameters.
    /// The `book.json` file should be in the root directory of the book.
    /// The root directory is the one specified when creating a new `MDBook`
    ///
    /// ```no_run
    /// # extern crate mdbook;
    /// # use mdbook::MDBook;
    /// # use std::path::Path;
    /// # fn main() {
    /// let mut book = MDBook::new(Path::new("root_dir"));
    /// # }
    /// ```
    ///
    /// In this example, `root_dir` will be the root directory of our book and is specified in function
    /// of the current working directory by using a relative path instead of an absolute path.

    pub fn read_config(mut self) -> Self {

        let config = BookConfig::new(&self.root)
                         .read_config(&self.root)
                         .to_owned();

        self.title = config.title;
        self.description = config.description;
        self.author = config.author;

        self.dest = config.dest;
        self.src = config.src;
        self.theme_path = config.theme_path;

        self
    }

    /// You can change the default renderer to another one by using this method. The only requirement
    /// is for your renderer to implement the [Renderer trait](../../renderer/renderer/trait.Renderer.html)
    ///
    /// ```no_run
    /// extern crate mdbook;
    /// use mdbook::MDBook;
    /// use mdbook::renderer::HtmlHandlebars;
    /// # use std::path::Path;
    ///
    /// fn main() {
    ///     let mut book = MDBook::new(Path::new("mybook"))
    ///                         .set_renderer(Box::new(HtmlHandlebars::new()));
    ///
    ///     // In this example we replace the default renderer by the default renderer...
    ///     // Don't forget to put your renderer in a Box
    /// }
    /// ```
    ///
    /// **note:** Don't forget to put your renderer in a `Box` before passing it to `set_renderer()`

    pub fn set_renderer(mut self, renderer: Box<Renderer>) -> Self {
        self.renderer = renderer;
        self
    }

    pub fn test(&mut self) -> Result<(), Box<Error>> {
        // read in the chapters
        try!(self.parse_summary());
        for item in self.iter() {

            if let BookItem::Chapter(_, ref ch) = *item {
                if ch.path != PathBuf::new() {

                    let path = self.get_src().join(&ch.path);

                    println!("[*]: Testing file: {:?}", path);

                    let output_result = Command::new("rustdoc")
                                            .arg(&path)
                                            .arg("--test")
                                            .output();
                    let output = try!(output_result);

                    if !output.status.success() {
                        return Err(Box::new(io::Error::new(ErrorKind::Other, format!(
                                        "{}\n{}",
                                        String::from_utf8_lossy(&output.stdout),
                                        String::from_utf8_lossy(&output.stderr)))) as Box<Error>);
                    }
                }
            }
        }
        Ok(())
    }

    pub fn get_root(&self) -> &Path {
        &self.root
    }

    pub fn set_dest(mut self, dest: &Path) -> Self {

        // Handle absolute and relative paths
        if dest.is_absolute() {
            self.dest = dest.to_owned();
        } else {
            let dest = self.root.join(dest).to_owned();
            self.dest = dest;
        }

        self
    }

    pub fn get_dest(&self) -> &Path {
        &self.dest
    }

    pub fn set_src(mut self, src: &Path) -> Self {

        // Handle absolute and relative paths
        if src.is_absolute() {
            self.src = src.to_owned();
        } else {
            let src = self.root.join(src).to_owned();
            self.src = src;
        }

        self
    }

    pub fn get_src(&self) -> &Path {
        &self.src
    }

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

    pub fn get_title(&self) -> &str {
        &self.title
    }

    pub fn set_author(mut self, author: &str) -> Self {
        self.author = author.to_owned();
        self
    }

    pub fn get_author(&self) -> &str {
        &self.author
    }

    pub fn set_description(mut self, description: &str) -> Self {
        self.description = description.to_owned();
        self
    }

    pub fn get_description(&self) -> &str {
        &self.description
    }

    pub fn set_livereload(&mut self, livereload: String) -> &mut Self {
        self.livereload = Some(livereload);
        self
    }

    pub fn unset_livereload(&mut self) -> &Self {
        self.livereload = None;
        self
    }

    pub fn get_livereload(&self) -> Option<&String> {
        self.livereload.as_ref()
    }

    pub fn set_theme_path(mut self, theme_path: &Path) -> Self {
        self.theme_path = if theme_path.is_absolute() {
            theme_path.to_owned()
        } else {
            self.root.join(theme_path).to_owned()
        };
        self
    }

    pub fn get_theme_path(&self) -> &Path {
        &self.theme_path
    }

    // Construct book
    fn parse_summary(&mut self) -> Result<(), Box<Error>> {
        // When append becomes stable, use self.content.append() ...
        self.content = try!(parse::construct_bookitems(&self.src.join("SUMMARY.md")));
        Ok(())
    }
}
<commit_msg>move misplaced example<commit_after>pub mod bookitem;
pub mod bookconfig;

pub mod bookconfig_test;

pub use self::bookitem::{BookItem, BookItems};
pub use self::bookconfig::BookConfig;

use std::path::{Path, PathBuf};
use std::fs::{self, File};
use std::error::Error;
use std::io;
use std::io::Write;
use std::io::ErrorKind;
use std::process::Command;

use {theme, parse, utils};
use renderer::{Renderer, HtmlHandlebars};

pub struct MDBook {
    root: PathBuf,
    dest: PathBuf,
    src: PathBuf,
    theme_path: PathBuf,

    pub title: String,
    pub author: String,
    pub description: String,

    pub content: Vec<BookItem>,
    renderer: Box<Renderer>,

    livereload: Option<String>,
}

impl MDBook {
    /// Create a new `MDBook` struct with root directory `root`
    ///
    /// # Examples
    ///
    /// ```no_run
    /// # extern crate mdbook;
    /// # use mdbook::MDBook;
    /// # use std::path::Path;
    /// # fn main() {
    /// let book = MDBook::new(Path::new("root_dir"));
    /// # }
    /// ```
    ///
    /// In this example, `root_dir` will be the root directory of our book and is specified in function
    /// of the current working directory by using a relative path instead of an absolute path.
    ///
    /// Default directory paths:
    ///
    /// - source: `root/src`
    /// - output: `root/book`
    /// - theme: `root/theme`
    ///
    /// They can both be changed by using [`set_src()`](#method.set_src) and [`set_dest()`](#method.set_dest)

    pub fn new(root: &Path) -> MDBook {

        if !root.exists() || !root.is_dir() {
            warn!("{:?} No directory with that name", root);
        }

        MDBook {
            root: root.to_owned(),
            dest: root.join("book"),
            src: root.join("src"),
            theme_path: root.join("theme"),

            title: String::new(),
            author: String::new(),
            description: String::new(),

            content: vec![],
            renderer: Box::new(HtmlHandlebars::new()),

            livereload: None,
        }
    }

    /// Returns a flat depth-first iterator over the elements of the book, it returns an [BookItem enum](bookitem.html):
    /// `(section: String, bookitem: &BookItem)`
    ///
    /// ```no_run
    /// # extern crate mdbook;
    /// # use mdbook::MDBook;
    /// # use mdbook::BookItem;
    /// # use std::path::Path;
    /// # fn main() {
    /// # let mut book = MDBook::new(Path::new("mybook"));
    /// for item in book.iter() {
    ///     match item {
    ///         &BookItem::Chapter(ref section, ref chapter) => {},
    ///         &BookItem::Affix(ref chapter) => {},
    ///         &BookItem::Spacer => {},
    ///     }
    /// }
    ///
    /// // would print something like this:
    /// // 1. Chapter 1
    /// // 1.1 Sub Chapter
    /// // 1.2 Sub Chapter
    /// // 2. Chapter 2
    /// //
    /// // etc.
    /// # }
    /// ```

    pub fn iter(&self) -> BookItems {
        BookItems {
            items: &self.content[..],
            current_index: 0,
            stack: Vec::new(),
        }
    }

    /// `init()` creates some boilerplate files and directories to get you started with your book.
    ///
    /// ```text
    /// book-test/
    /// ├── book
    /// └── src
    ///     ├── chapter_1.md
    ///     └── SUMMARY.md
    /// ```
    ///
    /// It uses the paths given as source and output directories and adds a `SUMMARY.md` and a
    /// `chapter_1.md` to the source directory.

    pub fn init(&mut self) -> Result<(), Box<Error>> {

        debug!("[fn]: init");

        if !self.root.exists() {
            fs::create_dir_all(&self.root).unwrap();
            info!("{:?} created", &self.root);
        }

        {

            if !self.dest.exists() {
                debug!("[*]: {:?} does not exist, trying to create directory", self.dest);
                try!(fs::create_dir_all(&self.dest));
            }

            if !self.src.exists() {
                debug!("[*]: {:?} does not exist, trying to create directory", self.src);
                try!(fs::create_dir_all(&self.src));
            }

            let summary = self.src.join("SUMMARY.md");

            if !summary.exists() {

                // Summary does not exist, create it

                debug!("[*]: {:?} does not exist, trying to create SUMMARY.md", self.src.join("SUMMARY.md"));
                let mut f = try!(File::create(&self.src.join("SUMMARY.md")));

                debug!("[*]: Writing to SUMMARY.md");

                try!(writeln!(f, "# Summary"));
                try!(writeln!(f, ""));
                try!(writeln!(f, "- [Chapter 1](./chapter_1.md)"));
            }
        }

        // parse SUMMARY.md, and create the missing item related file
        try!(self.parse_summary());

        debug!("[*]: constructing paths for missing files");
        for item in self.iter() {
            debug!("[*]: item: {:?}", item);
            match *item {
                BookItem::Spacer => continue,
                BookItem::Chapter(_, ref ch) |
                BookItem::Affix(ref ch) => {
                    if ch.path != PathBuf::new() {
                        let path = self.src.join(&ch.path);

                        if !path.exists() {
                            debug!("[*]: {:?} does not exist, trying to create file", path);
                            try!(::std::fs::create_dir_all(path.parent().unwrap()));
                            let mut f = try!(File::create(path));

                            // debug!("[*]: Writing to {:?}", path);
                            try!(writeln!(f, "# {}", ch.name));
                        }
                    }
                },
            }
        }

        debug!("[*]: init done");
        Ok(())
    }

    pub fn create_gitignore(&self) {
        let gitignore = self.get_gitignore();

        if !gitignore.exists() {
            // Gitignore does not exist, create it

            // Because of `src/book/mdbook.rs#L37-L39`, `dest` will always start with `root`. If it
            // is not, `strip_prefix` will return an Error.
            if !self.get_dest().starts_with(&self.root) {
                return;
            }

            let relative = self.get_dest()
                               .strip_prefix(&self.root)
                               .expect("Destination is not relative to root.");
            let relative = relative.to_str()
                                   .expect("Path could not be yielded into a string slice.");

            debug!("[*]: {:?} does not exist, trying to create .gitignore", gitignore);

            let mut f = File::create(&gitignore).expect("Could not create file.");

            debug!("[*]: Writing to .gitignore");

            writeln!(f, "{}", relative).expect("Could not write to file.");
        }
    }

    /// The `build()` method is the one where everything happens. First it parses `SUMMARY.md` to
    /// construct the book's structure in the form of a `Vec<BookItem>` and then calls `render()`
    /// method of the current renderer.
    ///
    /// It is the renderer who generates all the output files.
    pub fn build(&mut self) -> Result<(), Box<Error>> {
        debug!("[fn]: build");

        try!(self.init());

        // Clean output directory
        try!(utils::fs::remove_dir_content(&self.dest));

        try!(self.renderer.render(&self));

        Ok(())
    }

    pub fn get_gitignore(&self) -> PathBuf {
        self.root.join(".gitignore")
    }

    pub fn copy_theme(&self) -> Result<(), Box<Error>> {
        debug!("[fn]: copy_theme");

        let theme_dir = self.src.join("theme");

        if !theme_dir.exists() {
            debug!("[*]: {:?} does not exist, trying to create directory", theme_dir);
            try!(fs::create_dir(&theme_dir));
        }

        // index.hbs
        let mut index = try!(File::create(&theme_dir.join("index.hbs")));
        try!(index.write_all(theme::INDEX));

        // book.css
        let mut css = try!(File::create(&theme_dir.join("book.css")));
        try!(css.write_all(theme::CSS));

        // favicon.png
        let mut favicon = try!(File::create(&theme_dir.join("favicon.png")));
        try!(favicon.write_all(theme::FAVICON));

        // book.js
        let mut js = try!(File::create(&theme_dir.join("book.js")));
        try!(js.write_all(theme::JS));

        // highlight.css
        let mut highlight_css = try!(File::create(&theme_dir.join("highlight.css")));
        try!(highlight_css.write_all(theme::HIGHLIGHT_CSS));

        // highlight.js
        let mut highlight_js = try!(File::create(&theme_dir.join("highlight.js")));
        try!(highlight_js.write_all(theme::HIGHLIGHT_JS));

        Ok(())
    }

    pub fn write_file<P: AsRef<Path>>(&self, filename: P, content: &[u8]) -> Result<(), Box<Error>> {
        let path = self.get_dest().join(filename);
        try!(utils::fs::create_file(&path).and_then(|mut file| {
            file.write_all(content)
        }).map_err(|e| {
            io::Error::new(io::ErrorKind::Other, format!("Could not create {}: {}", path.display(), e))
        }));
        Ok(())
    }

    /// Parses the `book.json` file (if it exists) to extract the configuration parameters.
    /// The `book.json` file should be in the root directory of the book.
    /// The root directory is the one specified when creating a new `MDBook`

    pub fn read_config(mut self) -> Self {

        let config = BookConfig::new(&self.root)
                         .read_config(&self.root)
                         .to_owned();

        self.title = config.title;
        self.description = config.description;
        self.author = config.author;

        self.dest = config.dest;
        self.src = config.src;
        self.theme_path = config.theme_path;

        self
    }

    /// You can change the default renderer to another one by using this method. The only requirement
    /// is for your renderer to implement the [Renderer trait](../../renderer/renderer/trait.Renderer.html)
    ///
    /// ```no_run
    /// extern crate mdbook;
    /// use mdbook::MDBook;
    /// use mdbook::renderer::HtmlHandlebars;
    /// # use std::path::Path;
    ///
    /// fn main() {
    ///     let mut book = MDBook::new(Path::new("mybook"))
    ///                         .set_renderer(Box::new(HtmlHandlebars::new()));
    ///
    ///     // In this example we replace the default renderer by the default renderer...
    ///     // Don't forget to put your renderer in a Box
    /// }
    /// ```
    ///
    /// **note:** Don't forget to put your renderer in a `Box` before passing it to `set_renderer()`

    pub fn set_renderer(mut self, renderer: Box<Renderer>) -> Self {
        self.renderer = renderer;
        self
    }

    pub fn test(&mut self) -> Result<(), Box<Error>> {
        // read in the chapters
        try!(self.parse_summary());
        for item in self.iter() {

            if let BookItem::Chapter(_, ref ch) = *item {
                if ch.path != PathBuf::new() {

                    let path = self.get_src().join(&ch.path);

                    println!("[*]: Testing file: {:?}", path);

                    let output_result = Command::new("rustdoc")
                                            .arg(&path)
                                            .arg("--test")
                                            .output();
                    let output = try!(output_result);

                    if !output.status.success() {
                        return Err(Box::new(io::Error::new(ErrorKind::Other, format!(
                                        "{}\n{}",
                                        String::from_utf8_lossy(&output.stdout),
                                        String::from_utf8_lossy(&output.stderr)))) as Box<Error>);
                    }
                }
            }
        }
        Ok(())
    }

    pub fn get_root(&self) -> &Path {
        &self.root
    }

    pub fn set_dest(mut self, dest: &Path) -> Self {

        // Handle absolute and relative paths
        if dest.is_absolute() {
            self.dest = dest.to_owned();
        } else {
            let dest = self.root.join(dest).to_owned();
            self.dest = dest;
        }

        self
    }

    pub fn get_dest(&self) -> &Path {
        &self.dest
    }

    pub fn set_src(mut self, src: &Path) -> Self {

        // Handle absolute and relative paths
        if src.is_absolute() {
            self.src = src.to_owned();
        } else {
            let src = self.root.join(src).to_owned();
            self.src = src;
        }

        self
    }

    pub fn get_src(&self) -> &Path {
        &self.src
    }

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

    pub fn get_title(&self) -> &str {
        &self.title
    }

    pub fn set_author(mut self, author: &str) -> Self {
        self.author = author.to_owned();
        self
    }

    pub fn get_author(&self) -> &str {
        &self.author
    }

    pub fn set_description(mut self, description: &str) -> Self {
        self.description = description.to_owned();
        self
    }

    pub fn get_description(&self) -> &str {
        &self.description
    }

    pub fn set_livereload(&mut self, livereload: String) -> &mut Self {
        self.livereload = Some(livereload);
        self
    }

    pub fn unset_livereload(&mut self) -> &Self {
        self.livereload = None;
        self
    }

    pub fn get_livereload(&self) -> Option<&String> {
        self.livereload.as_ref()
    }

    pub fn set_theme_path(mut self, theme_path: &Path) -> Self {
        self.theme_path = if theme_path.is_absolute() {
            theme_path.to_owned()
        } else {
            self.root.join(theme_path).to_owned()
        };
        self
    }

    pub fn get_theme_path(&self) -> &Path {
        &self.theme_path
    }

    // Construct book
    fn parse_summary(&mut self) -> Result<(), Box<Error>> {
        // When append becomes stable, use self.content.append() ...
        self.content = try!(parse::construct_bookitems(&self.src.join("SUMMARY.md")));
        Ok(())
    }
}
<|endoftext|>


<commit_before>// This code is heavily inspired by hoedown.

//
// Performance observations:
//
// Note that buf.head() == Some(&ch) is much faster than buf[0] == ch.
//

static SP: u8 = ' ' as u8;
static NL: u8 = '\n' as u8;
static CR: u8 = '\r' as u8;
static TAB: u8 = '\t' as u8;
static STAR: u8 = '*' as u8;
static DASH: u8 = '-' as u8;
static UNDERSCORE: u8 = '_' as u8;

//
// Skip up to three leading spaces.
//
// Up to three leading spaces are allowed for many elements.
//
// We don't need to care about a TAB here as in this position it is equivalent
// to 4 spaces, which means that when we find a TAB here we would not parse the
// corresponding element.
//
fn skip_initial_three_spaces<'a>(buf: &'a[u8])-> &'a[u8] {
    let mut buf = buf;
    if buf.head() == Some(&SP) { buf = buf.tail();
    if buf.head() == Some(&SP) { buf = buf.tail();
    if buf.head() == Some(&SP) { buf = buf.tail(); } } }
    return buf;
}

//
// Return Some(`rem`) if the line is a horizontal rule, with `rem` being the
// buf after the hrule. Otherwise return None.
//
fn is_hrule<'a>(buf: &'a[u8]) -> Option<&'a[u8]> {
    let buf = skip_initial_three_spaces(buf);

    let item = match buf.head() {
        Some(&c) if c == STAR ||
                    c == DASH ||
                    c == UNDERSCORE => c,
        _                           => return None
    };

    // The count of '*', '-' or '_'
    let mut cnt: uint = 0;

    // Counts the consumed spaces (and the final NL)
    let mut spc: uint = 0;

    for &ch in buf.iter() {
        if      ch == item { cnt += 1; }
        else if ch == NL   { spc += 1; break; }
        else if ch == SP   { spc += 1; }
        else               { return None; }
    }

    if cnt >= 3 {
        Some(buf.slice_from(cnt + spc))
    } else {
        None
    }
}

#[test]
fn test_is_hrule() {
    // examples as given on the markdown homepage
    assert!(is_hrule(bytes!("* * *\n")).is_some());
    assert!(is_hrule(bytes!("***\n")).is_some());
    assert!(is_hrule(bytes!("*****\n")).is_some());
    assert!(is_hrule(bytes!("- - -\n")).is_some());
    assert!(is_hrule(bytes!("---------------------------------------\n")).is_some());

    // up to three spaces ignored
    assert!(is_hrule(bytes!(" ***\n")).is_some());
    assert!(is_hrule(bytes!("  ***\n")).is_some());
    assert!(is_hrule(bytes!("   ***\n")).is_some());

    // but not four, or a tab which is equivalent to four spaces
    assert!(is_hrule(bytes!("    ***\n")).is_none());
    assert!(is_hrule(bytes!("\t***\n")).is_none());

    // need at least three
    assert!(is_hrule(bytes!("*\n")).is_none());
    assert!(is_hrule(bytes!("**\n")).is_none());
    assert!(is_hrule(bytes!("* *\n")).is_none());
    assert!(is_hrule(bytes!("   * *\n")).is_none());

    // Also works without newline at the end
    assert!(is_hrule(bytes!("* * *")).is_some());

    // And underscores also supported
    assert!(is_hrule(bytes!("___")).is_some());
    assert!(is_hrule(bytes!("______________")).is_some());
    assert!(is_hrule(bytes!(" ______________")).is_some());

    // Test if the remaining buf actually works.
    let s = bytes!("   * * *\nremaining");
    let res = is_hrule(s);
    assert!(res.is_some());
    assert_eq!(res.unwrap(), bytes!("remaining"));
}

//
// Return Some(`rem`) if the line is an empty line, with `rem` being the buf
// after the empty line. Otherwise return None.
//
fn is_empty<'a>(buf: &'a[u8]) -> Option<&'a[u8]> {
    let mut cnt: uint = 0;

    for &ch in buf.iter() {
        if      ch == NL  { cnt += 1; break; }
        else if ch == CR  { cnt += 1; }
        else if ch == SP  { cnt += 1; }
        else if ch == TAB { cnt += 1; }
        else              { return None; }
    }

    if cnt > 0 {
        Some(buf.slice_from(cnt))
    } else {
        None
    }
}

#[test]
fn test_is_empty() {
    assert!(is_empty(bytes!("\n")).is_some());
    assert!(is_empty(bytes!("    \n")).is_some());
    assert!(is_empty(bytes!("  \t  \n")).is_some());
    assert!(is_empty(bytes!("  \t  \r\n")).is_some());
    assert!(is_empty(bytes!("  \t  \nabc")).is_some());
    assert!(is_empty(bytes!("  \t  ")).is_some());

    assert!(is_empty(bytes!("a")).is_none());
    assert!(is_empty(bytes!(" a")).is_none());
    assert!(is_empty(bytes!(" a\n")).is_none());
    assert!(is_empty(bytes!(" \ta\n")).is_none());

    // Test if the remaining buf actually works.
    let s = bytes!("   \t\r\nremaining");
    let res = is_empty(s);
    assert!(res.is_some());
    assert_eq!(res.unwrap(), bytes!("remaining"));
}
<commit_msg>Implement is_codefence<commit_after>// This code is heavily inspired by hoedown.

//
// Performance observations:
//
// Note that buf.head() == Some(&ch) is much faster than buf[0] == ch.
//

static SP: u8 = ' ' as u8;
static NL: u8 = '\n' as u8;
static CR: u8 = '\r' as u8;
static TAB: u8 = '\t' as u8;
static STAR: u8 = '*' as u8;
static DASH: u8 = '-' as u8;
static UNDERSCORE: u8 = '_' as u8;
static TILDE: u8 = '~' as u8;
static BACKTICK: u8 = '`' as u8;

//
// Skip up to three leading spaces.
//
// Up to three leading spaces are allowed for many elements.
//
// We don't need to care about a TAB here as in this position it is equivalent
// to 4 spaces, which means that when we find a TAB here we would not parse the
// corresponding element.
//
fn skip_initial_three_spaces<'a>(buf: &'a[u8])-> &'a[u8] {
    let mut buf = buf;
    if buf.head() == Some(&SP) { buf = buf.tail();
    if buf.head() == Some(&SP) { buf = buf.tail();
    if buf.head() == Some(&SP) { buf = buf.tail(); } } }
    return buf;
}

//
// Return Some(`rem`) if the line is a horizontal rule, with `rem` being the
// buf after the hrule. Otherwise return None.
//
fn is_hrule<'a>(buf: &'a[u8]) -> Option<&'a[u8]> {
    let buf = skip_initial_three_spaces(buf);

    let item = match buf.head() {
        Some(&c) if c == STAR ||
                    c == DASH ||
                    c == UNDERSCORE => c,
        _                           => return None
    };

    // The count of '*', '-' or '_'
    let mut cnt: uint = 0;

    // Counts the consumed spaces (and the final NL)
    let mut spc: uint = 0;

    for &ch in buf.iter() {
        if      ch == item { cnt += 1; }
        else if ch == NL   { spc += 1; break; }
        else if ch == SP   { spc += 1; }
        else               { return None; }
    }

    if cnt >= 3 {
        Some(buf.slice_from(cnt + spc))
    } else {
        None
    }
}

#[test]
fn test_is_hrule() {
    // examples as given on the markdown homepage
    assert!(is_hrule(bytes!("* * *\n")).is_some());
    assert!(is_hrule(bytes!("***\n")).is_some());
    assert!(is_hrule(bytes!("*****\n")).is_some());
    assert!(is_hrule(bytes!("- - -\n")).is_some());
    assert!(is_hrule(bytes!("---------------------------------------\n")).is_some());

    // up to three spaces ignored
    assert!(is_hrule(bytes!(" ***\n")).is_some());
    assert!(is_hrule(bytes!("  ***\n")).is_some());
    assert!(is_hrule(bytes!("   ***\n")).is_some());

    // but not four, or a tab which is equivalent to four spaces
    assert!(is_hrule(bytes!("    ***\n")).is_none());
    assert!(is_hrule(bytes!("\t***\n")).is_none());

    // need at least three
    assert!(is_hrule(bytes!("*\n")).is_none());
    assert!(is_hrule(bytes!("**\n")).is_none());
    assert!(is_hrule(bytes!("* *\n")).is_none());
    assert!(is_hrule(bytes!("   * *\n")).is_none());

    // Also works without newline at the end
    assert!(is_hrule(bytes!("* * *")).is_some());

    // And underscores also supported
    assert!(is_hrule(bytes!("___")).is_some());
    assert!(is_hrule(bytes!("______________")).is_some());
    assert!(is_hrule(bytes!(" ______________")).is_some());

    // Test if the remaining buf actually works.
    let s = bytes!("   * * *\nremaining");
    let res = is_hrule(s);
    assert!(res.is_some());
    assert_eq!(res.unwrap(), bytes!("remaining"));
}

//
// Return Some(`rem`) if the line is an empty line, with `rem` being the buf
// after the empty line. Otherwise return None.
//
fn is_empty<'a>(buf: &'a[u8]) -> Option<&'a[u8]> {
    let mut cnt: uint = 0;

    for &ch in buf.iter() {
        if      ch == NL  { cnt += 1; break; }
        else if ch == CR  { cnt += 1; }
        else if ch == SP  { cnt += 1; }
        else if ch == TAB { cnt += 1; }
        else              { return None; }
    }

    if cnt > 0 {
        Some(buf.slice_from(cnt))
    } else {
        None
    }
}

#[test]
fn test_is_empty() {
    assert!(is_empty(bytes!("\n")).is_some());
    assert!(is_empty(bytes!("    \n")).is_some());
    assert!(is_empty(bytes!("  \t  \n")).is_some());
    assert!(is_empty(bytes!("  \t  \r\n")).is_some());
    assert!(is_empty(bytes!("  \t  \nabc")).is_some());
    assert!(is_empty(bytes!("  \t  ")).is_some());

    assert!(is_empty(bytes!("a")).is_none());
    assert!(is_empty(bytes!(" a")).is_none());
    assert!(is_empty(bytes!(" a\n")).is_none());
    assert!(is_empty(bytes!(" \ta\n")).is_none());

    // Test if the remaining buf actually works.
    let s = bytes!("   \t\r\nremaining");
    let res = is_empty(s);
    assert!(res.is_some());
    assert_eq!(res.unwrap(), bytes!("remaining"));
}

fn is_codefence<'a>(buf: &'a[u8]) -> Option<(&'a[u8], uint, u8)> {
    let buf = skip_initial_three_spaces(buf);

    let item = match buf.head() {
        Some(&c) if c == TILDE ||
                    c == BACKTICK => c,
        _                         => return None
    };

    // The count of '~' or '`' characters
    let mut cnt: uint = 0;

    for &ch in buf.iter() {
        if ch == item { cnt += 1; }
        else          { break; }
    }

    if cnt >= 3 {
        Some((buf.slice_from(cnt), cnt, item))
    } else {
        None
    }
}

#[test]
fn test_is_codefence() {
    assert!(is_codefence(bytes!("```")).is_some());
    assert!(is_codefence(bytes!("~~~")).is_some());
    assert!(is_codefence(bytes!("`````````")).is_some());
    assert!(is_codefence(bytes!("~~~~")).is_some());
    assert!(is_codefence(bytes!("   ```")).is_some());
    assert!(is_codefence(bytes!("  ~~~")).is_some());

    assert!(is_codefence(bytes!("  ~~")).is_none());
    assert!(is_codefence(bytes!(" ``")).is_none());
    assert!(is_codefence(bytes!("    ```")).is_none());
    assert!(is_codefence(bytes!("\t```")).is_none());

    // Test if the remaining buf actually works.
    let s = bytes!("   ```remaining\n");
    let res = is_codefence(s);
    assert!(res.is_some());
    assert_eq!(res.unwrap(), (bytes!("remaining\n"), 3, BACKTICK));

    let s = bytes!("   ~~~~~~~~remaining\n");
    let res = is_codefence(s);
    assert!(res.is_some());
    assert_eq!(res.unwrap(), (bytes!("remaining\n"), 8, TILDE));
}

<|endoftext|>


<commit_before>use postgres::rows;
use postgres::types::FromSql;
use std::hash::{Hash, Hasher};

//This isn't a real iterator, because it does not have a definitive item type
//However, it works very much like one...
pub struct RowIter<'a> {
  row : &'a rows::Row<'a>,
  index : usize
}

impl <'a> RowIter<'a> {
  pub fn new(row : &'a rows::Row) -> Self {
    RowIter {
      row   : row,
      index : 0
    }
  }
  pub fn next<T>(&mut self) -> Option<T> where T : FromSql {
    let idx = self.index;
    self.index += 1;
    self.row.get(idx)
  }
}

pub trait HashTO {
  fn hash_to(&self, &mut Hasher);
}

impl <T : Hash> HashTO for T {
  fn hash_to(&self, _h : &mut Hasher) {
    //TODO: all data structures will be terrible until this is fixed
    //self.hash(h)
  }
}

pub mod types {
  use super::values;
  use super::values::Value;
  use super::RowIter;
  use std::any::Any;
  use std::fmt;
  use std::sync::Arc;
  use std::hash::{Hash, Hasher};
  use super::HashTO;

  pub trait Type : Sync + Send + HashTO + Any {
    // For a registered type, name() will provide the way to add it to a
    // predicate, and the thing to pattern match against when loading from the
    // db.
    // None will be returned if the type is anonymous, such as a tuple or list.
    fn name(&self) -> Option<&'static str>;
    // Takes in an iterator over the row, then attempts to read a value.
    // Since the underlying .get() will panic, I'm not including an additional
    // error reporting path (for now)
    fn extract(&self, &mut RowIter) -> Arc<Value>;
    // Generates the database representation of the fields required. For example,
    // for a bitvector this would be
    // vec!["bytea".to_string(), "int64".to_string()]
    // or similar, depending on how you chose to construct the size
    fn repr(&self) -> Vec<::std::string::String>;
    // Escape hatch
    fn inner(&self) -> &Any;
    // Check equality
    // This should be symmetric, but we have no way to enforce it within the
    // type system of rust.
    fn inner_eq(&self, &Type) -> bool;
  }

  impl Hash for Type {
    fn hash<H : Hasher>(&self, hasher : &mut H) {
      self.hash_to(hasher)
    }
  }

  impl fmt::Debug for Type {
    fn fmt(&self, f : &mut fmt::Formatter) -> fmt::Result {
      write!(f, "[Name: {:?}, Repr: {:?}]", self.name(), self.repr())
    }
  }

  impl Eq for Type {}
  impl PartialEq for Type {
    fn eq(&self, t : &Type) -> bool {
      self.inner_eq(t)
    }
  }

  #[derive(Debug,Clone,Hash)]
  pub struct Tuple {
    elements : Vec<Arc<Type>>
  }

  impl Tuple {
    pub fn new(elems : Vec<Arc<Type>>) -> Self {
      Tuple { elements : elems }
    }
  }

  impl Type for Tuple {
    fn name(&self) -> Option<&'static str> {
      None
    }
    fn extract(&self, rows : &mut RowIter) -> Arc<Value> {
      Arc::new(values::Tuple::new(self.elements.iter().map(|elem| {elem.extract(rows)}).collect()))
    }
    fn repr(&self) -> Vec<::std::string::String> {
      self.elements.iter().flat_map(|elem| {elem.repr()}).collect()
    }
    fn inner(&self) -> &Any {
      self as &Any
    }
    fn inner_eq(&self, other : &Type) -> bool {
      match other.inner().downcast_ref::<Self>() {
        Some(tup) => self.elements == tup.elements,
        // If the target is not a tuple, our types are not equal.
        None => false
      }
    }
  }

  #[derive(Debug,Clone,Hash)]
  pub struct List {
    elem : Arc<Type>
  }

  impl List {
    pub fn new(elem : Arc<Type>) -> Self {
      List { elem : elem }
    }
  }

  impl Type for List {
    fn name(&self) -> Option<&'static str> {
      None
    }
    fn extract(&self, _rows : &mut RowIter) -> Arc<Value> {
      panic!("List support disabled, will be re-enabled via arrays maybe")
    }
    fn repr(&self) -> Vec<::std::string::String> {
      panic!("List support disabled, will be re-enabled via arrays maybe")
    }
    fn inner(&self) -> &Any {
      self as &Any
    }
    fn inner_eq(&self, other : &Type) -> bool {
      match other.inner().downcast_ref::<Self>() {
        Some(ref tup) => self.elem == tup.elem.clone(),
        // If the target is not a tuple, our types are not equal.
        None => false
      }
    }
  }

  pub fn default_types() -> Vec<Arc<Type>> {
    vec![Arc::new(UInt64), Arc::new(String), Arc::new(Bytes)]
  }

  #[derive(Debug,Clone,Hash)]
  pub struct UInt64;

  impl Type for UInt64 {
    fn name(&self) -> Option<&'static str> {
      Some("uint64")
    }
    fn extract(&self, rows : &mut RowIter) -> Arc<Value> {
      let x : i64 = rows.next().unwrap();
      Arc::new(values::UInt64::new(x as u64))
    }
    fn repr(&self) -> Vec<::std::string::String> {
      vec!["int8".to_string()]
    }
    fn inner(&self) -> &Any {
      self as &Any
    }
    fn inner_eq(&self, other : &Type) -> bool {
      match other.inner().downcast_ref::<Self>() {
        Some(_) => true,
        _ => false
      }
    }
  }

  #[derive(Debug,Clone,Hash)]
  pub struct String;

  impl Type for String {
    fn name(&self) -> Option<&'static str> {
      Some("string")
    }
    fn extract(&self, rows : &mut RowIter) -> Arc<Value> {
      Arc::new(values::String::new(rows.next().unwrap()))
    }
    fn repr(&self) -> Vec<::std::string::String> {
      vec!["varchar".to_string()]
    }
    fn inner(&self) -> &Any {
      self as &Any
    }
    fn inner_eq(&self, other : &Type) -> bool {
      match other.inner().downcast_ref::<Self>() {
        Some(_) => true,
        _ => false
      }
    }
  }

  #[derive(Debug,Clone,Hash)]
  pub struct Bytes;

  impl Type for Bytes {
    fn name(&self) -> Option<&'static str> {
      Some("bytes")
    }
    fn extract(&self, rows : &mut RowIter) -> Arc<Value> {
      Arc::new(values::Bytes::new(rows.next().unwrap()))
    }
    fn repr(&self) -> Vec<::std::string::String> {
      vec!["bytea".to_string()]
    }
    fn inner(&self) -> &Any {
      self as &Any
    }
    fn inner_eq(&self, other : &Type) -> bool {
      match other.inner().downcast_ref::<Self>() {
        Some(_) => true,
        _ => false
      }
    }
  }

}

pub mod values {
  use postgres::types::ToSql;
  use std::any::Any;
  use super::types;
  use super::types::Type;
  use super::HashTO;
  use std::sync::Arc;
  use std::hash::{Hash, Hasher};
  use std::fmt;

  pub trait Value : Sync + Send + HashTO + fmt::Debug + Any {
    // Returns the type, needed if you want to do type checking, or tuple values
    fn type_(&self) -> Arc<Type>;
    // Get a rust dynamic type, to be used by someone who was typed in the holmes
    // language, and so knows what they're actually getting
    fn get(&self) -> &Any;
    // Converts the value into a list of ToSql trait objects to allow for
    // insertion into the database via a prepared query
    fn to_sql(&self) -> Vec<&ToSql>;
    // Escape hatch
    fn inner(&self) -> &Any;
    // Used to test equality, to implement PartialEq
    fn inner_eq(&self, other : &Value) -> bool;
  }

  impl Hash for Value {
    fn hash<H : Hasher>(&self, hasher : &mut H) {
      self.hash_to(hasher)
    }
  }

  pub trait ToValue {
    fn to_value(self) -> Arc<Value>;
  }

  impl Eq for Value {}
  impl PartialEq for Value {
    fn eq(&self, other : &Value) -> bool {
      self.inner_eq(other)
    }
  }

  #[derive(Debug,Clone,PartialEq,Hash)]
  pub struct List {
    elements : Vec<Arc<Value>>,
  }

  impl Value for List {
    fn type_(&self) -> Arc<Type> {
      match self.elements.first() {
        Some(e) => Arc::new(types::List::new(e.type_())),
        //TODO have some kind of poly type to default to? Equal to everything?
        None => Arc::new(types::List::new(Arc::new(types::UInt64)))
      }
    }
    fn get(&self) -> &Any {
      &self.elements as &Any
    }
    fn to_sql(&self) -> Vec<&ToSql> {
      panic!("List SQL disabled")
    }
    fn inner(&self) -> &Any {
      self as &Any
    }
    fn inner_eq(&self, other : &Value) -> bool {
      let other_typed : &List = match other.inner().downcast_ref::<Self>() {
        Some(x) => x,
        None => return false
      };
      self == other_typed
    }
  }

  impl List {
    pub fn new(elements : Vec<Arc<Value>>) -> Self {
      List { elements : elements }
    }
  }

  #[derive(Debug,Clone,PartialEq,Hash)]
  pub struct Tuple {
    elements : Vec<Arc<Value>>,
  }

  impl Value for Tuple {
    fn type_(&self) -> Arc<Type> {
      Arc::new(types::Tuple::new(self.elements.iter().map(|val| {val.type_()}).collect()))
    }
    fn get(&self) -> &Any {
      &self.elements as &Any
    }
    fn to_sql(&self) -> Vec<&ToSql> {
      self.elements.iter().flat_map(|val| val.to_sql()).collect()
    }
    fn inner(&self) -> &Any {
      self as &Any
    }
    fn inner_eq(&self, other : &Value) -> bool {
      let other_typed : &Tuple = match other.inner().downcast_ref::<Self>() {
        Some(x) => x,
        None => return false
      };
      self == other_typed
    }
  }

  impl Tuple {
    pub fn new(elements : Vec<Arc<Value>>) -> Self {
      Tuple { elements : elements }
    }
  }

  #[derive(Debug,PartialEq,Hash)]
  pub struct UInt64 {
    val : u64,
    sql : i64
  }

  impl ToValue for u64 {
    fn to_value(self) -> Arc<Value> {
      Arc::new(UInt64::new(self))
    }
  }
  impl ToValue for ::std::string::String {
    fn to_value(self) -> Arc<Value> {
      Arc::new(String::new(self))
    }
  }
  impl ToValue for Vec<u8> {
    fn to_value(self) -> Arc<Value> {
      Arc::new(Bytes::new(self))
    }
  }
  impl ToValue for &'static str {
    fn to_value(self) -> Arc<Value> {
      Arc::new(String::new(self.to_string()))
    }
  }

  impl Value for UInt64 {
    fn type_(&self) -> Arc<Type> {
      Arc::new(types::UInt64)
    }
    fn get(&self) -> &Any {
      &self.val as &Any
    }
    fn to_sql(&self) -> Vec<&ToSql> {
      vec![&self.sql]
    }
    fn inner(&self) -> &Any {
      self as &Any
    }
    fn inner_eq(&self, other : &Value) -> bool {
      let other_typed : &UInt64 = match other.inner().downcast_ref::<Self>() {
        Some(x) => x,
        None => return false
      };
      self == other_typed
    }
  }

  impl UInt64 {
    pub fn new(val : u64) -> Self {
      UInt64 { val : val, sql : val as i64 }
    }
  }

  #[derive(Debug,PartialEq,Hash)]
  pub struct String {
    val : ::std::string::String,
  }

  impl Value for String {
    fn type_(&self) -> Arc<Type> {
      Arc::new(types::String)
    }
    fn get(&self) -> &Any {
      &self.val as &Any
    }
    fn to_sql(&self) -> Vec<&ToSql> {
      vec![&self.val as &ToSql]
    }
    fn inner(&self) -> &Any {
      self as &Any
    }
    fn inner_eq(&self, other : &Value) -> bool {
      let other_typed : &String = match other.inner().downcast_ref::<Self>() {
        Some(x) => x,
        None => return false
      };
      self == other_typed
    }
  }

  impl String {
    pub fn new(val : ::std::string::String) -> Self {
      String { val : val }
    }
  }

  #[derive(Debug,PartialEq,Hash)]
  pub struct Bytes {
    val : Vec<u8>,
  }

  impl Value for Bytes {
    fn type_(&self) -> Arc<Type> {
      Arc::new(types::Bytes)
    }
    fn get(&self) -> &Any {
      &self.val as &Any
    }
    fn to_sql(&self) -> Vec<&ToSql> {
      vec![&self.val as &ToSql]
    }
    fn inner(&self) -> &Any {
      self as &Any
    }
    fn inner_eq(&self, other : &Value) -> bool {
      let other_typed : &Bytes = match other.inner().downcast_ref::<Self>() {
        Some(x) => x,
        None => return false
      };
      self == other_typed
    }
  }

  impl Bytes {
    pub fn new(val : Vec<u8>) -> Self {
      Bytes { val : val }
    }
  }

}
<commit_msg>Fixed hashing<commit_after>use postgres::rows;
use postgres::types::FromSql;
use std::hash::{Hash, Hasher};

//This isn't a real iterator, because it does not have a definitive item type
//However, it works very much like one...
pub struct RowIter<'a> {
  row : &'a rows::Row<'a>,
  index : usize
}

impl <'a> RowIter<'a> {
  pub fn new(row : &'a rows::Row) -> Self {
    RowIter {
      row   : row,
      index : 0
    }
  }
  pub fn next<T>(&mut self) -> Option<T> where T : FromSql {
    let idx = self.index;
    self.index += 1;
    self.row.get(idx)
  }
}

pub trait HashTO {
  fn hash_to(&self, &mut Hasher);
}

struct HashProxy<'a> {
  pub hasher : &'a mut Hasher
}

impl <'a> Hasher for HashProxy<'a> {
  fn finish(&self) -> u64 {
    self.hasher.finish()
  }
  fn write(&mut self, bytes : &[u8]) {
    self.hasher.write(bytes)
  }
}

impl <T : Hash + Sized> HashTO for T {
  fn hash_to(&self, h : &mut Hasher) {
    //TODO: all data structures will be terrible until this is fixed
    self.hash(&mut HashProxy { hasher : h })
  }
}

pub mod types {
  use super::values;
  use super::values::Value;
  use super::RowIter;
  use std::any::Any;
  use std::fmt;
  use std::sync::Arc;
  use std::hash::{Hash, Hasher};
  use super::HashTO;

  pub trait Type : Sync + Send + HashTO + Any {
    // For a registered type, name() will provide the way to add it to a
    // predicate, and the thing to pattern match against when loading from the
    // db.
    // None will be returned if the type is anonymous, such as a tuple or list.
    fn name(&self) -> Option<&'static str>;
    // Takes in an iterator over the row, then attempts to read a value.
    // Since the underlying .get() will panic, I'm not including an additional
    // error reporting path (for now)
    fn extract(&self, &mut RowIter) -> Arc<Value>;
    // Generates the database representation of the fields required. For example,
    // for a bitvector this would be
    // vec!["bytea".to_string(), "int64".to_string()]
    // or similar, depending on how you chose to construct the size
    fn repr(&self) -> Vec<::std::string::String>;
    // Escape hatch
    fn inner(&self) -> &Any;
    // Check equality
    // This should be symmetric, but we have no way to enforce it within the
    // type system of rust.
    fn inner_eq(&self, &Type) -> bool;
  }

  impl Hash for Type {
    fn hash<H : Hasher>(&self, hasher : &mut H) {
      self.hash_to(hasher)
    }
  }

  impl fmt::Debug for Type {
    fn fmt(&self, f : &mut fmt::Formatter) -> fmt::Result {
      write!(f, "[Name: {:?}, Repr: {:?}]", self.name(), self.repr())
    }
  }

  impl Eq for Type {}
  impl PartialEq for Type {
    fn eq(&self, t : &Type) -> bool {
      self.inner_eq(t)
    }
  }

  #[derive(Debug,Clone,Hash)]
  pub struct Tuple {
    elements : Vec<Arc<Type>>
  }

  impl Tuple {
    pub fn new(elems : Vec<Arc<Type>>) -> Self {
      Tuple { elements : elems }
    }
  }

  impl Type for Tuple {
    fn name(&self) -> Option<&'static str> {
      None
    }
    fn extract(&self, rows : &mut RowIter) -> Arc<Value> {
      Arc::new(values::Tuple::new(self.elements.iter().map(|elem| {elem.extract(rows)}).collect()))
    }
    fn repr(&self) -> Vec<::std::string::String> {
      self.elements.iter().flat_map(|elem| {elem.repr()}).collect()
    }
    fn inner(&self) -> &Any {
      self as &Any
    }
    fn inner_eq(&self, other : &Type) -> bool {
      match other.inner().downcast_ref::<Self>() {
        Some(tup) => self.elements == tup.elements,
        // If the target is not a tuple, our types are not equal.
        None => false
      }
    }
  }

  #[derive(Debug,Clone,Hash)]
  pub struct List {
    elem : Arc<Type>
  }

  impl List {
    pub fn new(elem : Arc<Type>) -> Self {
      List { elem : elem }
    }
  }

  impl Type for List {
    fn name(&self) -> Option<&'static str> {
      None
    }
    fn extract(&self, _rows : &mut RowIter) -> Arc<Value> {
      panic!("List support disabled, will be re-enabled via arrays maybe")
    }
    fn repr(&self) -> Vec<::std::string::String> {
      panic!("List support disabled, will be re-enabled via arrays maybe")
    }
    fn inner(&self) -> &Any {
      self as &Any
    }
    fn inner_eq(&self, other : &Type) -> bool {
      match other.inner().downcast_ref::<Self>() {
        Some(ref tup) => self.elem == tup.elem.clone(),
        // If the target is not a tuple, our types are not equal.
        None => false
      }
    }
  }

  pub fn default_types() -> Vec<Arc<Type>> {
    vec![Arc::new(UInt64), Arc::new(String), Arc::new(Bytes)]
  }

  #[derive(Debug,Clone,Hash)]
  pub struct UInt64;

  impl Type for UInt64 {
    fn name(&self) -> Option<&'static str> {
      Some("uint64")
    }
    fn extract(&self, rows : &mut RowIter) -> Arc<Value> {
      let x : i64 = rows.next().unwrap();
      Arc::new(values::UInt64::new(x as u64))
    }
    fn repr(&self) -> Vec<::std::string::String> {
      vec!["int8".to_string()]
    }
    fn inner(&self) -> &Any {
      self as &Any
    }
    fn inner_eq(&self, other : &Type) -> bool {
      match other.inner().downcast_ref::<Self>() {
        Some(_) => true,
        _ => false
      }
    }
  }

  #[derive(Debug,Clone,Hash)]
  pub struct String;

  impl Type for String {
    fn name(&self) -> Option<&'static str> {
      Some("string")
    }
    fn extract(&self, rows : &mut RowIter) -> Arc<Value> {
      Arc::new(values::String::new(rows.next().unwrap()))
    }
    fn repr(&self) -> Vec<::std::string::String> {
      vec!["varchar".to_string()]
    }
    fn inner(&self) -> &Any {
      self as &Any
    }
    fn inner_eq(&self, other : &Type) -> bool {
      match other.inner().downcast_ref::<Self>() {
        Some(_) => true,
        _ => false
      }
    }
  }

  #[derive(Debug,Clone,Hash)]
  pub struct Bytes;

  impl Type for Bytes {
    fn name(&self) -> Option<&'static str> {
      Some("bytes")
    }
    fn extract(&self, rows : &mut RowIter) -> Arc<Value> {
      Arc::new(values::Bytes::new(rows.next().unwrap()))
    }
    fn repr(&self) -> Vec<::std::string::String> {
      vec!["bytea".to_string()]
    }
    fn inner(&self) -> &Any {
      self as &Any
    }
    fn inner_eq(&self, other : &Type) -> bool {
      match other.inner().downcast_ref::<Self>() {
        Some(_) => true,
        _ => false
      }
    }
  }

}

pub mod values {
  use postgres::types::ToSql;
  use std::any::Any;
  use super::types;
  use super::types::Type;
  use super::HashTO;
  use std::sync::Arc;
  use std::hash::{Hash, Hasher};
  use std::fmt;

  pub trait Value : Sync + Send + HashTO + fmt::Debug + Any {
    // Returns the type, needed if you want to do type checking, or tuple values
    fn type_(&self) -> Arc<Type>;
    // Get a rust dynamic type, to be used by someone who was typed in the holmes
    // language, and so knows what they're actually getting
    fn get(&self) -> &Any;
    // Converts the value into a list of ToSql trait objects to allow for
    // insertion into the database via a prepared query
    fn to_sql(&self) -> Vec<&ToSql>;
    // Escape hatch
    fn inner(&self) -> &Any;
    // Used to test equality, to implement PartialEq
    fn inner_eq(&self, other : &Value) -> bool;
  }

  impl Hash for Value {
    fn hash<H : Hasher>(&self, hasher : &mut H) {
      self.hash_to(hasher)
    }
  }

  pub trait ToValue {
    fn to_value(self) -> Arc<Value>;
  }

  impl Eq for Value {}
  impl PartialEq for Value {
    fn eq(&self, other : &Value) -> bool {
      self.inner_eq(other)
    }
  }

  #[derive(Debug,Clone,PartialEq,Hash)]
  pub struct List {
    elements : Vec<Arc<Value>>,
  }

  impl Value for List {
    fn type_(&self) -> Arc<Type> {
      match self.elements.first() {
        Some(e) => Arc::new(types::List::new(e.type_())),
        //TODO have some kind of poly type to default to? Equal to everything?
        None => Arc::new(types::List::new(Arc::new(types::UInt64)))
      }
    }
    fn get(&self) -> &Any {
      &self.elements as &Any
    }
    fn to_sql(&self) -> Vec<&ToSql> {
      panic!("List SQL disabled")
    }
    fn inner(&self) -> &Any {
      self as &Any
    }
    fn inner_eq(&self, other : &Value) -> bool {
      let other_typed : &List = match other.inner().downcast_ref::<Self>() {
        Some(x) => x,
        None => return false
      };
      self == other_typed
    }
  }

  impl List {
    pub fn new(elements : Vec<Arc<Value>>) -> Self {
      List { elements : elements }
    }
  }

  #[derive(Debug,Clone,PartialEq,Hash)]
  pub struct Tuple {
    elements : Vec<Arc<Value>>,
  }

  impl Value for Tuple {
    fn type_(&self) -> Arc<Type> {
      Arc::new(types::Tuple::new(self.elements.iter().map(|val| {val.type_()}).collect()))
    }
    fn get(&self) -> &Any {
      &self.elements as &Any
    }
    fn to_sql(&self) -> Vec<&ToSql> {
      self.elements.iter().flat_map(|val| val.to_sql()).collect()
    }
    fn inner(&self) -> &Any {
      self as &Any
    }
    fn inner_eq(&self, other : &Value) -> bool {
      let other_typed : &Tuple = match other.inner().downcast_ref::<Self>() {
        Some(x) => x,
        None => return false
      };
      self == other_typed
    }
  }

  impl Tuple {
    pub fn new(elements : Vec<Arc<Value>>) -> Self {
      Tuple { elements : elements }
    }
  }

  #[derive(Debug,PartialEq,Hash)]
  pub struct UInt64 {
    val : u64,
    sql : i64
  }

  impl ToValue for u64 {
    fn to_value(self) -> Arc<Value> {
      Arc::new(UInt64::new(self))
    }
  }
  impl ToValue for ::std::string::String {
    fn to_value(self) -> Arc<Value> {
      Arc::new(String::new(self))
    }
  }
  impl ToValue for Vec<u8> {
    fn to_value(self) -> Arc<Value> {
      Arc::new(Bytes::new(self))
    }
  }
  impl ToValue for &'static str {
    fn to_value(self) -> Arc<Value> {
      Arc::new(String::new(self.to_string()))
    }
  }

  impl Value for UInt64 {
    fn type_(&self) -> Arc<Type> {
      Arc::new(types::UInt64)
    }
    fn get(&self) -> &Any {
      &self.val as &Any
    }
    fn to_sql(&self) -> Vec<&ToSql> {
      vec![&self.sql]
    }
    fn inner(&self) -> &Any {
      self as &Any
    }
    fn inner_eq(&self, other : &Value) -> bool {
      let other_typed : &UInt64 = match other.inner().downcast_ref::<Self>() {
        Some(x) => x,
        None => return false
      };
      self == other_typed
    }
  }

  impl UInt64 {
    pub fn new(val : u64) -> Self {
      UInt64 { val : val, sql : val as i64 }
    }
  }

  #[derive(Debug,PartialEq,Hash)]
  pub struct String {
    val : ::std::string::String,
  }

  impl Value for String {
    fn type_(&self) -> Arc<Type> {
      Arc::new(types::String)
    }
    fn get(&self) -> &Any {
      &self.val as &Any
    }
    fn to_sql(&self) -> Vec<&ToSql> {
      vec![&self.val as &ToSql]
    }
    fn inner(&self) -> &Any {
      self as &Any
    }
    fn inner_eq(&self, other : &Value) -> bool {
      let other_typed : &String = match other.inner().downcast_ref::<Self>() {
        Some(x) => x,
        None => return false
      };
      self == other_typed
    }
  }

  impl String {
    pub fn new(val : ::std::string::String) -> Self {
      String { val : val }
    }
  }

  #[derive(Debug,PartialEq,Hash)]
  pub struct Bytes {
    val : Vec<u8>,
  }

  impl Value for Bytes {
    fn type_(&self) -> Arc<Type> {
      Arc::new(types::Bytes)
    }
    fn get(&self) -> &Any {
      &self.val as &Any
    }
    fn to_sql(&self) -> Vec<&ToSql> {
      vec![&self.val as &ToSql]
    }
    fn inner(&self) -> &Any {
      self as &Any
    }
    fn inner_eq(&self, other : &Value) -> bool {
      let other_typed : &Bytes = match other.inner().downcast_ref::<Self>() {
        Some(x) => x,
        None => return false
      };
      self == other_typed
    }
  }

  impl Bytes {
    pub fn new(val : Vec<u8>) -> Self {
      Bytes { val : val }
    }
  }

}
<|endoftext|>


<commit_before>use std::collections::{
    HashMap,
    BinaryHeap,
};
use std::collections::hash_map::Hasher;
use std::collections::hash_map::Entry::{
    Occupied,
    Vacant,
};

use std::default::Default;
use std::hash::Hash;
use std::ops::{
    Add,
};

use super::MinScored;
use super::visit::{
    Visitable,
    VisitMap,
};

/// Dijkstra's shortest path algorithm.
pub fn dijkstra<'a, G: Visitable, K, F, Edges>(graph: &'a G,
                                               start: G::NodeId,
                                               goal: Option<G::NodeId>,
                                               mut edges: F) -> HashMap<G::NodeId, K> where
    G::NodeId: Clone + Eq + Hash<Hasher>,
    K: Default + Add<Output=K> + Copy + PartialOrd,
    F: FnMut(&'a G, G::NodeId) -> Edges,
    Edges: Iterator<Item=(G::NodeId, K)>,
    <G as Visitable>::Map: VisitMap<G::NodeId>,
{
    let mut visited = graph.visit_map();
    let mut scores = HashMap::new();
    let mut predecessor = HashMap::new();
    let mut visit_next = BinaryHeap::new();
    let zero_score: K = Default::default();
    scores.insert(start.clone(), zero_score);
    visit_next.push(MinScored(zero_score, start));
    while let Some(MinScored(node_score, node)) = visit_next.pop() {
        if visited.is_visited(&node) {
            continue
        }
        for (next, edge) in edges(graph, node.clone()) {
            if visited.is_visited(&next) {
                continue
            }
            let mut next_score = node_score + edge;
            match scores.entry(next.clone()) {
                Occupied(ent) => if next_score < *ent.get() {
                    *ent.into_mut() = next_score;
                    predecessor.insert(next.clone(), node.clone());
                } else {
                    next_score = *ent.get();
                },
                Vacant(ent) => {
                    ent.insert(next_score);
                    predecessor.insert(next.clone(), node.clone());
                }
            }
            visit_next.push(MinScored(next_score, next));
        }
        if goal.as_ref() == Some(&node) {
            break
        }
        visited.visit(node);
    }
    scores
}
<commit_msg>dijkstra: Check for reaching the goal earlier<commit_after>use std::collections::{
    HashMap,
    BinaryHeap,
};
use std::collections::hash_map::Hasher;
use std::collections::hash_map::Entry::{
    Occupied,
    Vacant,
};

use std::default::Default;
use std::hash::Hash;
use std::ops::{
    Add,
};

use super::MinScored;
use super::visit::{
    Visitable,
    VisitMap,
};

/// Dijkstra's shortest path algorithm.
pub fn dijkstra<'a, G: Visitable, K, F, Edges>(graph: &'a G,
                                               start: G::NodeId,
                                               goal: Option<G::NodeId>,
                                               mut edges: F) -> HashMap<G::NodeId, K> where
    G::NodeId: Clone + Eq + Hash<Hasher>,
    K: Default + Add<Output=K> + Copy + PartialOrd,
    F: FnMut(&'a G, G::NodeId) -> Edges,
    Edges: Iterator<Item=(G::NodeId, K)>,
    <G as Visitable>::Map: VisitMap<G::NodeId>,
{
    let mut visited = graph.visit_map();
    let mut scores = HashMap::new();
    let mut predecessor = HashMap::new();
    let mut visit_next = BinaryHeap::new();
    let zero_score: K = Default::default();
    scores.insert(start.clone(), zero_score);
    visit_next.push(MinScored(zero_score, start));
    while let Some(MinScored(node_score, node)) = visit_next.pop() {
        if visited.is_visited(&node) {
            continue
        }
        if goal.as_ref() == Some(&node) {
            break
        }
        for (next, edge) in edges(graph, node.clone()) {
            if visited.is_visited(&next) {
                continue
            }
            let mut next_score = node_score + edge;
            match scores.entry(next.clone()) {
                Occupied(ent) => if next_score < *ent.get() {
                    *ent.into_mut() = next_score;
                    predecessor.insert(next.clone(), node.clone());
                } else {
                    next_score = *ent.get();
                },
                Vacant(ent) => {
                    ent.insert(next_score);
                    predecessor.insert(next.clone(), node.clone());
                }
            }
            visit_next.push(MinScored(next_score, next));
        }
        visited.visit(node);
    }
    scores
}
<|endoftext|>