<commit_before>use std::boxed::Box;
use std::ops::Deref;

use rustc_serialize::{Encodable, Encoder};
use rustc_serialize::{Decodable, Decoder};

/// A struct for encoding nested reference types.
///
/// Encoding large objects by reference is really handy.  For example,
/// `encode(&large_hashmap, ...)` encodes the large structure without having to
/// own the hashmap.  However, it is impossible to serialize a reference if that
/// reference is inside of a struct.
///
/// ```ignore rust
/// // Not possible, rustc can not decode the reference.
/// #[derive(RustcEncoding, RustcDecoding)]
/// struct Message<'a>  {
///   big_map: &'a HashMap<u32, u32>,
///   message_type: String,
/// }
/// ```
///
/// This is because on the decoding side, you can't create the Message struct
/// because it needs to have a reference to a HashMap, which is impossible because
/// during deserialization, all members need to be owned by the deserialized
/// object.
///
/// This is where RefBox comes in.  During serialization, it serializs a reference,
/// but during deserialization, it puts that sub-object into a box!
///
/// ```ignore rust
/// // This works!
/// #[derive(RustcEncoding, RustcDecoding)]
/// struct Message<'a> {
///     big_map: RefBox<'a, HashMap<u32, u32>>,
///     message_type: String
/// }
/// ```
///
/// Now we can write
///
/// ```ignore rust
/// let my_map = HashMap::new();
/// let my_msg = Message {
///     big_map: RefBox::new(&my_map),
///     message_type: "foo".to_string()
/// };
///
/// let encoded = encode(&my_msg, ...).unwrap();
/// let decoded: Message<'static> = decode(&encoded[]).unwrap();
/// ```
///
/// Notice that we managed to encode and decode a struct with a nested reference
/// and that the decoded message has the lifetime `'static` which shows us
/// that the message owns everything inside it completely.
///
/// Please don't stick RefBox inside deep data structures.  It is much better
/// suited in the outermost layer of whatever it is that you are encoding.
pub struct RefBox<'a, T: 'a> {
    inner:  RefBoxInner<'a, T>
}

#[derive(Debug, Hash)]
enum RefBoxInner<'a, T: 'a> {
    Ref(&'a T),
    Box(Box<T>)
}

impl <'a, T> RefBox<'a, T> {
    /// Creates a new RefBox that looks at a value.
    pub fn new(v: &'a T) -> RefBox<'a, T> {
        RefBox {
            inner: RefBoxInner::Ref(v)
        }
    }
}

impl <T> RefBox<'static, T>  {
    /// Takes the value out of this refbox.
    ///
    /// Fails if this refbox was not created out of a deserialization.
    ///
    /// Unless you are doing some really weird things with static references,
    /// this function will never fail.
    pub fn take(self) -> Box<T> {
        match self.inner {
            RefBoxInner::Box(b) => b,
            _ => unreachable!()
        }
    }

    /// Tries to take the value out of this refbox.
    pub fn try_take(self) -> Result<Box<T>, RefBox<'static, T>> {
        match self.inner {
            RefBoxInner::Box(b) => Ok(b),
            o => Err(RefBox{ inner: o})
        }
    }
}
impl <'a, T: Encodable> Encodable for RefBox<'a, T> {
    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        self.inner.encode(s)
    }
}
impl <T: Decodable> Decodable for RefBox<'static, T> {
    fn decode<D: Decoder>(d: &mut D) -> Result<RefBox<'static, T>, D::Error> {
        let inner = try!(Decodable::decode(d));
        Ok(RefBox{inner: inner})
    }
}

impl <'a, T: Encodable> Encodable for RefBoxInner<'a, T> {
    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        s.emit_enum("RefBox", |s| {
            s.emit_enum_variant("Box", 1, 1, |s| {
                s.emit_enum_variant_arg(0, |s| {
                    match self {
                        &RefBoxInner::Ref(ref r) => r.encode(s),
                        &RefBoxInner::Box(ref b) => b.encode(s)
                    }
                })
            })
        })
    }
}

impl <T: Decodable> Decodable for RefBoxInner<'static, T> {
    fn decode<D: Decoder>(d: &mut D) -> Result<RefBoxInner<'static, T>, D::Error> {
        d.read_enum("RefBox", |d| {
            d.read_enum_variant(&["Ref", "Box"], |d, i| {
                assert!(i == 1);
                d.read_enum_variant_arg(0, |d| {
                    let decoded = try!(Decodable::decode(d));
                    Ok(RefBoxInner::Box(Box::new(decoded)))
                })
            })
        })
    }
}

impl <'a, T> Deref for RefBox<'a, T> {
    type Target = T;

    fn deref(&self) -> &T {
        match &self.inner {
            &RefBoxInner::Ref(ref t) => t,
            &RefBoxInner::Box(ref b) => b.deref()
        }
    }
}

<commit_msg>clean up wording of docs<commit_after>use std::boxed::Box;
use std::ops::Deref;

use rustc_serialize::{Encodable, Encoder};
use rustc_serialize::{Decodable, Decoder};

/// A struct for encoding nested reference types.
///
/// Encoding large objects by reference is really handy.  For example,
/// `encode(&large_hashmap, ...)` encodes the large structure without having to
/// own the hashmap.  However, it is impossible to serialize a reference if that
/// reference is inside of a struct.
///
/// ```ignore rust
/// // Not possible, rustc can not decode the reference.
/// #[derive(RustcEncoding, RustcDecoding)]
/// struct Message<'a>  {
///   big_map: &'a HashMap<u32, u32>,
///   message_type: String,
/// }
/// ```
///
/// This is because on the decoding side, you can't create the Message struct
/// because it needs to have a reference to a HashMap, which is impossible because
/// during deserialization, all members need to be owned by the deserialized
/// object.
///
/// This is where RefBox comes in.  During serialization, it serializs a reference,
/// but during deserialization, it puts that sub-object into a box!
///
/// ```ignore rust
/// // This works!
/// #[derive(RustcEncoding, RustcDecoding)]
/// struct Message<'a> {
///     big_map: RefBox<'a, HashMap<u32, u32>>,
///     message_type: String
/// }
/// ```
///
/// Now we can write
///
/// ```ignore rust
/// let my_map = HashMap::new();
/// let my_msg = Message {
///     big_map: RefBox::new(&my_map),
///     message_type: "foo".to_string()
/// };
///
/// let encoded = encode(&my_msg, ...).unwrap();
/// let decoded: Message<'static> = decode(&encoded[]).unwrap();
/// ```
///
/// Notice that we managed to encode and decode a struct with a nested reference
/// and that the decoded message has the lifetime `'static` which shows us
/// that the message owns everything inside it completely.
///
/// Please don't stick RefBox inside deep data structures.  It is much better
/// suited in the outermost layer of whatever it is that you are encoding.
pub struct RefBox<'a, T: 'a> {
    inner:  RefBoxInner<'a, T>
}

#[derive(Debug, Hash)]
enum RefBoxInner<'a, T: 'a> {
    Ref(&'a T),
    Box(Box<T>)
}

impl <'a, T> RefBox<'a, T> {
    /// Creates a new RefBox that looks at a borrowed value.
    pub fn new(v: &'a T) -> RefBox<'a, T> {
        RefBox {
            inner: RefBoxInner::Ref(v)
        }
    }
}

impl <T> RefBox<'static, T>  {
    /// Takes the value out of this refbox.
    ///
    /// Fails if this refbox was not created out of a deserialization.
    ///
    /// Unless you are doing some really weird things with static references,
    /// this function will never fail.
    pub fn take(self) -> Box<T> {
        match self.inner {
            RefBoxInner::Box(b) => b,
            _ => unreachable!()
        }
    }

    /// Tries to take the value out of this refbox.
    pub fn try_take(self) -> Result<Box<T>, RefBox<'static, T>> {
        match self.inner {
            RefBoxInner::Box(b) => Ok(b),
            o => Err(RefBox{ inner: o})
        }
    }
}
impl <'a, T: Encodable> Encodable for RefBox<'a, T> {
    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        self.inner.encode(s)
    }
}
impl <T: Decodable> Decodable for RefBox<'static, T> {
    fn decode<D: Decoder>(d: &mut D) -> Result<RefBox<'static, T>, D::Error> {
        let inner = try!(Decodable::decode(d));
        Ok(RefBox{inner: inner})
    }
}

impl <'a, T: Encodable> Encodable for RefBoxInner<'a, T> {
    fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
        s.emit_enum("RefBox", |s| {
            s.emit_enum_variant("Box", 1, 1, |s| {
                s.emit_enum_variant_arg(0, |s| {
                    match self {
                        &RefBoxInner::Ref(ref r) => r.encode(s),
                        &RefBoxInner::Box(ref b) => b.encode(s)
                    }
                })
            })
        })
    }
}

impl <T: Decodable> Decodable for RefBoxInner<'static, T> {
    fn decode<D: Decoder>(d: &mut D) -> Result<RefBoxInner<'static, T>, D::Error> {
        d.read_enum("RefBox", |d| {
            d.read_enum_variant(&["Ref", "Box"], |d, i| {
                assert!(i == 1);
                d.read_enum_variant_arg(0, |d| {
                    let decoded = try!(Decodable::decode(d));
                    Ok(RefBoxInner::Box(Box::new(decoded)))
                })
            })
        })
    }
}

impl <'a, T> Deref for RefBox<'a, T> {
    type Target = T;

    fn deref(&self) -> &T {
        match &self.inner {
            &RefBoxInner::Ref(ref t) => t,
            &RefBoxInner::Box(ref b) => b.deref()
        }
    }
}

<|endoftext|>


<commit_before>use crate::config::Config;
use crate::errors::*;
use crate::filter::{
    contains_markdown_file, contains_markdown_subdir, is_directory, is_hidden, is_image,
    is_markdown,
};
use crate::template;
use image;
use liquid;
use pulldown_cmark::{html, Options, Parser};
use rayon::prelude::*;
use std::fmt::{self, Debug};
use std::fs;
use std::fs::DirBuilder;
use std::fs::File;
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::vec::Vec;
use walkdir::{DirEntry, WalkDir};

#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum MenuCmd {
    OpenLevel,
    CloseLevel,
    CloseOpenLevel,
    None,
}

impl fmt::Display for MenuCmd {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        Debug::fmt(self, f)
    }
}

pub fn build(path: &Path, conf: &Config) -> Result<()> {
    let path_comps = path.components().collect::<Vec<_>>();
    let output_dir = PathBuf::from(conf.output_dir.as_ref().unwrap());
    let nav_items = prepare_site_structure(path, output_dir.as_path());
    // let last_gen_timestmp = set_and_determine_last_generation(output_dir.as_path());
    let entries = WalkDir::new(path)
        .min_depth(1)
        .into_iter()
        .filter_entry(|e| is_hidden(e))
        .filter(|e| e.is_ok() && is_markdown(e.as_ref().unwrap()))
        .collect::<Vec<_>>();
    entries.par_iter().for_each(|e| {
        let src = e.as_ref().unwrap();
        let mut target_dir = output_dir.clone();
        if src.path().parent().is_some() {
            for comp in src
                .path()
                .parent()
                .unwrap()
                .components()
                .skip(path_comps.len())
            {
                target_dir.push(comp.as_os_str());
            }
        }
        build_page(nav_items.clone(), src, target_dir.as_path(), conf);
    });
    copy_dirs(path, output_dir.as_path(), conf);
    Ok(())
}

fn build_page(
    nav_items: Vec<liquid::model::value::Value>,
    entry: &DirEntry,
    target_dir: &Path,
    conf: &Config,
) {
    let page_content = convert_markdown_to_html(entry.path());
    let html = if entry.file_name() == "gallery.md" {
        let images = prepare_gallery(entry, target_dir, conf);
        apply_gallery_template(&page_content, nav_items, images, entry.depth(), conf)
    } else {
        apply_page_template(&page_content, nav_items, entry.depth(), conf)
    };
    write_html_file(&html, target_dir, entry);
    copy_images(entry.path().parent().unwrap(), target_dir);
}

fn copy_dirs(path: &Path, target_path: &Path, conf: &Config) {
    if conf.copy_dirs.is_some() {
        let path_comps = path.components().collect::<Vec<_>>();
        for copy_dir in conf.copy_dirs.as_ref().unwrap() {
            let walker = WalkDir::new(path.join(copy_dir).as_path())
                .min_depth(1)
                .into_iter();
            for entry in walker.filter(|e| e.is_ok() && !is_directory(e.as_ref().unwrap())) {
                let entry = entry.unwrap();
                let mut target_file = PathBuf::from(target_path);
                for comp in entry.path().components().skip(path_comps.len()) {
                    target_file.push(comp.as_os_str());
                }
                match DirBuilder::new()
                    .recursive(true)
                    .create(target_file.parent().expect("Missing parent folder!"))
                {
                    Ok(_) => {}
                    Err(e) => println!("{}", e),
                }
                if !target_file.exists() {
                    fs::copy(entry.path(), target_file)
                        .expect(format!("error during copy of {:?}", copy_dir).as_ref());
                }
            }
        }
    }
}

fn copy_images(source: &Path, target: &Path) {
    let walker = WalkDir::new(source)
        .min_depth(1)
        .max_depth(1)
        .follow_links(true)
        .into_iter();
    for entry in walker.filter(|e| e.is_ok() && is_image(e.as_ref().unwrap())) {
        let mut target_file = PathBuf::from(target);
        let entry = entry.unwrap();
        target_file.push(entry.path().file_name().unwrap());
        if !target_file.exists() {
            fs::copy(entry.path(), target_file.as_path())
                .expect(format!("Error during copy of {:?}", entry.path().display()).as_ref());
        }
    }
}

fn prepare_site_structure(path: &Path, target_path: &Path) -> Vec<liquid::model::value::Value> {
    let mut nav_entries = Vec::<liquid::model::value::Value>::new();
    let walker = WalkDir::new(path)
        .min_depth(1)
        .sort_by(|a, b| a.file_name().cmp(b.file_name()))
        .into_iter();
    let mut prev_depth = 1;
    let path_comps = path.components().collect::<Vec<_>>();
    for entry in
        walker.filter_entry(|e| is_hidden(e) && is_directory(e) && contains_markdown_file(e))
    {
        let entry = entry.expect("Reading directory entry failed");
        let name = String::from(
            entry
                .file_name()
                .to_str()
                .expect("Failed to read navigation entries"),
        );
        let mut url = PathBuf::new();
        for comp in entry.path().components().skip(path_comps.len()) {
            url.push(comp.as_os_str());
        }
        let target_dir = target_path.join(url.as_path());
        match DirBuilder::new().recursive(true).create(target_dir) {
            Ok(_) => {}
            Err(e) => println!("{}", e),
        }
        url.push("index.html");
        let (menu_cmd, level_depth) =
            match (contains_markdown_subdir(&entry), prev_depth > entry.depth()) {
                (true, true) => (MenuCmd::CloseOpenLevel, prev_depth - entry.depth()),
                (true, false) => (MenuCmd::OpenLevel, 0),
                (false, true) => (MenuCmd::CloseLevel, prev_depth - entry.depth()),
                _ => (MenuCmd::None, 0),
            };
        let nav_entry = liquid::object!({
            "name": name,
            "url" : url.as_os_str().to_str().unwrap().to_owned(),
            "menu_cmd" : menu_cmd.to_string().to_owned(),
            "level_depth" : level_depth as i32,
        });
        nav_entries.push(liquid::model::value::Value::Object(nav_entry));
        prev_depth = entry.depth();
    }
    nav_entries
}

fn prepare_gallery(
    source_entry: &DirEntry,
    target_path: &Path,
    conf: &Config,
) -> Vec<liquid::model::value::Value> {
    let gallery_settings = conf.gallery.as_ref().unwrap();
    let mut images = Vec::<liquid::model::value::Value>::new();
    let img_dir = gallery_settings.img_dir.as_ref().unwrap();
    let target_dir = target_path.join(img_dir.as_str());
    match DirBuilder::new().recursive(true).create(&target_dir) {
        Ok(_) => {}
        Err(e) => println!("{}", e),
    }
    let entries = WalkDir::new(
        source_entry
            .path()
            .parent()
            .unwrap()
            .join(img_dir.as_str())
            .as_path(),
    )
    .min_depth(1)
    .follow_links(true)
    .into_iter()
    .filter(|e| e.is_ok() && !is_directory(e.as_ref().unwrap()))
    .collect::<Vec<_>>();
    for entry in entries {
        let entry = entry.unwrap();
        let mut img = image::open(entry.path()).expect(
            format!(
                "Resize of '{}' failed: The gallery folder should only contain \
                 images!",
                entry.path().display()
            )
            .as_ref(),
        );

        let mut image_path = PathBuf::from(&target_dir);
        let mut rel_image_path = PathBuf::from(img_dir.as_str());
        image_path.push(entry.file_name());
        image_path.set_extension("png");
        rel_image_path.push(entry.file_name());
        rel_image_path.set_extension("png");
        if !image_path.exists() {
            let _ = &mut File::create(&image_path).unwrap();
            img = img.resize(
                gallery_settings.img_width,
                gallery_settings.img_height,
                image::imageops::FilterType::Nearest,
            );
            img.save_with_format(&image_path, image::ImageFormat::Png)
                .expect(format!("Saving image '{}' failed", image_path.display()).as_ref());
        }

        let mut thumb_path = PathBuf::from(&target_dir);
        let mut rel_thumb_path = PathBuf::from(img_dir.as_str());
        let mut thumb_file_name = String::from(
            entry
                .path()
                .file_stem()
                .map(|s| s.to_str().unwrap())
                .unwrap(),
        );
        thumb_file_name.push_str("_thumb.png");
        thumb_path.push(thumb_file_name.clone());
        rel_thumb_path.push(thumb_file_name);
        if !thumb_path.exists() {
            let _ = &mut File::create(&thumb_path).unwrap();
            img = img.resize(
                gallery_settings.thumb_width,
                gallery_settings.thumb_height,
                image::imageops::FilterType::Nearest,
            );
            img.save_with_format(&thumb_path, image::ImageFormat::Png)
                .expect(format!("Saving thumb image '{}' failed", thumb_path.display()).as_ref());
        }

        let image_entry = liquid::object!({
            "name"  : rel_image_path.to_str().unwrap().to_owned(),
            "thumb" : rel_thumb_path.to_str().unwrap().to_owned(),
        });
        images.push(liquid::model::value::Value::Object(image_entry));
    }
    images
}

fn apply_gallery_template(
    content: &str,
    nav_items: Vec<liquid::model::value::Value>,
    images: Vec<liquid::model::value::Value>,
    depth: usize,
    conf: &Config,
) -> String {
    let template = liquid::ParserBuilder::with_stdlib()
        .build()
        .unwrap()
        .parse(template::load_gallery_template(conf).as_str())
        .expect("Gallery template could not be parsed!");
    let mut root_dir = String::new();
    for _ in 1..depth {
        root_dir.push_str("../");
    }
    let mut context = liquid::model::Object::new();
    context.insert(
        "root_dir".into(),
        liquid::model::value::Value::scalar(root_dir),
    );
    context.insert(
        "title".into(),
        liquid::model::value::Value::scalar(if conf.title.is_some() {
            conf.title.as_ref().unwrap().clone()
        } else {
            "None".to_string()
        }),
    );
    context.insert(
        "nav_items".into(),
        liquid::model::value::Value::Array(nav_items),
    );
    context.insert(
        "content".into(),
        liquid::model::value::Value::scalar(content.to_owned()),
    );
    context.insert("images".into(), liquid::model::value::Value::Array(images));
    match template.render(&context) {
        Ok(output) => output,
        Err(error) => panic!("Could not render Page template: {}", error),
    }
}

fn apply_page_template(
    content: &str,
    nav_items: Vec<liquid::model::value::Value>,
    depth: usize,
    conf: &Config,
) -> String {
    let template = liquid::ParserBuilder::with_stdlib()
        .build()
        .unwrap()
        .parse(template::load_page_template(conf).as_str())
        .expect("Page template could not be parsed!");
    let mut root_dir = String::new();
    for _ in 1..depth {
        root_dir.push_str("../");
    }
    let context = liquid::object!({
       "root_dir" : root_dir,
       "title" : if conf.title.is_some() {
            conf.title.as_ref().unwrap().clone()
        } else {
            "None".to_string()
        },
        "nav_items" : liquid::model::value::Value::Array(nav_items),
        "content" : content.to_owned(),
    });
    match template.render(&context) {
        Ok(output) => output,
        Err(error) => panic!("Could not render Page template: {}", error),
    }
}

fn convert_markdown_to_html(entry: &Path) -> String {
    let mut markdown = String::new();
    let mut html_output = String::new();
    match File::open(entry).and_then(|mut f| f.read_to_string(&mut markdown)) {
        Err(error) => panic!("failed to open {}: {}", entry.display(), error),
        Ok(_) => html::push_html(
            &mut html_output,
            Parser::new_ext(markdown.as_str(), Options::empty()),
        ),
    }
    html_output
}

fn write_html_file(html: &str, target_dir: &Path, entry: &DirEntry) {
    let html_file = match entry.file_name().to_str() {
        Some(fname) => {
            let mut tmp_path = PathBuf::from(fname);
            tmp_path.set_file_name("index.html");
            tmp_path
        }
        None => panic!("Could not write html file {}", entry.path().display()),
    };
    let file_path = target_dir.join(html_file.as_path());
    let result = File::create(file_path.as_path()).and_then(|mut f| f.write_all(html.as_bytes()));
    if result.is_err() {
        panic!(
            "Could not write html file {}: {:?}",
            file_path.as_path().display(),
            result.err()
        );
    }
    println!("written file {}", file_path.display());
}
<commit_msg>fix bug in render logic<commit_after>use crate::config::Config;
use crate::errors::*;
use crate::filter::{
    contains_markdown_file, contains_markdown_subdir, is_directory, is_hidden, is_image,
    is_markdown,
};
use crate::template;
use image;
use liquid;
use pulldown_cmark::{html, Options, Parser};
use rayon::prelude::*;
use std::fmt::{self, Debug};
use std::fs;
use std::fs::DirBuilder;
use std::fs::File;
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::vec::Vec;
use walkdir::{DirEntry, WalkDir};

#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum MenuCmd {
    OpenLevel,
    CloseLevel,
    CloseOpenLevel,
    None,
}

impl fmt::Display for MenuCmd {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        Debug::fmt(self, f)
    }
}

pub fn build(path: &Path, conf: &Config) -> Result<()> {
    let path_comps = path.components().collect::<Vec<_>>();
    let output_dir = PathBuf::from(conf.output_dir.as_ref().unwrap());
    let nav_items = prepare_site_structure(path, output_dir.as_path());
    // let last_gen_timestmp = set_and_determine_last_generation(output_dir.as_path());
    let entries = WalkDir::new(path)
        .min_depth(1)
        .into_iter()
        .filter_entry(|e| is_hidden(e))
        .filter(|e| e.is_ok() && is_markdown(e.as_ref().unwrap()))
        .collect::<Vec<_>>();
    entries.par_iter().for_each(|e| {
        let src = e.as_ref().unwrap();
        let mut target_dir = output_dir.clone();
        if src.path().parent().is_some() {
            for comp in src
                .path()
                .parent()
                .unwrap()
                .components()
                .skip(path_comps.len())
            {
                target_dir.push(comp.as_os_str());
            }
        }
        build_page(nav_items.clone(), src, target_dir.as_path(), conf);
    });
    copy_dirs(path, output_dir.as_path(), conf);
    Ok(())
}

fn build_page(
    nav_items: Vec<liquid::model::value::Value>,
    entry: &DirEntry,
    target_dir: &Path,
    conf: &Config,
) {
    let page_content = convert_markdown_to_html(entry.path());
    let html = if entry.file_name() == "gallery.md" {
        let images = prepare_gallery(entry, target_dir, conf);
        apply_gallery_template(&page_content, nav_items, images, entry.depth(), conf)
    } else {
        apply_page_template(&page_content, nav_items, entry.depth(), conf)
    };
    write_html_file(&html, target_dir, entry);
    copy_images(entry.path().parent().unwrap(), target_dir);
}

fn copy_dirs(path: &Path, target_path: &Path, conf: &Config) {
    if conf.copy_dirs.is_some() {
        let path_comps = path.components().collect::<Vec<_>>();
        for copy_dir in conf.copy_dirs.as_ref().unwrap() {
            let walker = WalkDir::new(path.join(copy_dir).as_path())
                .min_depth(1)
                .into_iter();
            for entry in walker.filter(|e| e.is_ok() && !is_directory(e.as_ref().unwrap())) {
                let entry = entry.unwrap();
                let mut target_file = PathBuf::from(target_path);
                for comp in entry.path().components().skip(path_comps.len()) {
                    target_file.push(comp.as_os_str());
                }
                match DirBuilder::new()
                    .recursive(true)
                    .create(target_file.parent().expect("Missing parent folder!"))
                {
                    Ok(_) => {}
                    Err(e) => println!("{}", e),
                }
                if !target_file.exists() {
                    fs::copy(entry.path(), target_file)
                        .expect(format!("error during copy of {:?}", copy_dir).as_ref());
                }
            }
        }
    }
}

fn copy_images(source: &Path, target: &Path) {
    let walker = WalkDir::new(source)
        .min_depth(1)
        .max_depth(1)
        .follow_links(true)
        .into_iter();
    for entry in walker.filter(|e| e.is_ok() && is_image(e.as_ref().unwrap())) {
        let mut target_file = PathBuf::from(target);
        let entry = entry.unwrap();
        target_file.push(entry.path().file_name().unwrap());
        if !target_file.exists() {
            fs::copy(entry.path(), target_file.as_path())
                .expect(format!("Error during copy of {:?}", entry.path().display()).as_ref());
        }
    }
}

fn prepare_site_structure(path: &Path, target_path: &Path) -> Vec<liquid::model::value::Value> {
    let mut nav_entries = Vec::<liquid::model::value::Value>::new();
    let walker = WalkDir::new(path)
        .min_depth(1)
        .sort_by(|a, b| a.file_name().cmp(b.file_name()))
        .into_iter();
    let mut prev_depth = 1;
    let path_comps = path.components().collect::<Vec<_>>();
    for entry in
        walker.filter_entry(|e| is_hidden(e) && is_directory(e) && contains_markdown_file(e))
    {
        let entry = entry.expect("Reading directory entry failed");
        let name = String::from(
            entry
                .file_name()
                .to_str()
                .expect("Failed to read navigation entries"),
        );
        let mut url = PathBuf::new();
        for comp in entry.path().components().skip(path_comps.len()) {
            url.push(comp.as_os_str());
        }
        let target_dir = target_path.join(url.as_path());
        match DirBuilder::new().recursive(true).create(target_dir) {
            Ok(_) => {}
            Err(e) => println!("{}", e),
        }
        url.push("index.html");
        let (menu_cmd, level_depth) =
            match (contains_markdown_subdir(&entry), prev_depth > entry.depth()) {
                (true, true) => (MenuCmd::CloseOpenLevel, prev_depth - entry.depth()),
                (true, false) => (MenuCmd::OpenLevel, 0),
                (false, true) => (MenuCmd::CloseLevel, prev_depth - entry.depth()),
                _ => (MenuCmd::None, 0),
            };
        let nav_entry = liquid::object!({
            "name": name,
            "url" : url.as_os_str().to_str().unwrap().to_owned(),
            "menu_cmd" : menu_cmd.to_string().to_owned(),
            "level_depth" : level_depth as i32,
        });
        nav_entries.push(liquid::model::value::Value::Object(nav_entry));
        prev_depth = entry.depth();
    }
    nav_entries
}

fn prepare_gallery(
    source_entry: &DirEntry,
    target_path: &Path,
    conf: &Config,
) -> Vec<liquid::model::value::Value> {
    let gallery_settings = conf.gallery.as_ref().unwrap();
    let mut images = Vec::<liquid::model::value::Value>::new();
    let img_dir = gallery_settings.img_dir.as_ref().unwrap();
    let target_dir = target_path.join(img_dir.as_str());
    match DirBuilder::new().recursive(true).create(&target_dir) {
        Ok(_) => {}
        Err(e) => println!("{}", e),
    }
    let entries = WalkDir::new(
        source_entry
            .path()
            .parent()
            .unwrap()
            .join(img_dir.as_str())
            .as_path(),
    )
    .min_depth(1)
    .follow_links(true)
    .into_iter()
    .filter(|e| e.is_ok() && !is_directory(e.as_ref().unwrap()))
    .collect::<Vec<_>>();
    for entry in entries {
        let entry = entry.unwrap();
        let mut img = image::open(entry.path()).expect(
            format!(
                "Resize of '{}' failed: The gallery folder should only contain \
                 images!",
                entry.path().display()
            )
            .as_ref(),
        );

        let mut image_path = PathBuf::from(&target_dir);
        let mut rel_image_path = PathBuf::from(img_dir.as_str());
        image_path.push(entry.file_name());
        image_path.set_extension("png");
        rel_image_path.push(entry.file_name());
        rel_image_path.set_extension("png");
        if !image_path.exists() {
            let _ = &mut File::create(&image_path).unwrap();
            img = img.resize(
                gallery_settings.img_width,
                gallery_settings.img_height,
                image::imageops::FilterType::Nearest,
            );
            img.save_with_format(&image_path, image::ImageFormat::Png)
                .expect(format!("Saving image '{}' failed", image_path.display()).as_ref());
        }

        let mut thumb_path = PathBuf::from(&target_dir);
        let mut rel_thumb_path = PathBuf::from(img_dir.as_str());
        let mut thumb_file_name = String::from(
            entry
                .path()
                .file_stem()
                .map(|s| s.to_str().unwrap())
                .unwrap(),
        );
        thumb_file_name.push_str("_thumb.png");
        thumb_path.push(thumb_file_name.clone());
        rel_thumb_path.push(thumb_file_name);
        if !thumb_path.exists() {
            let _ = &mut File::create(&thumb_path).unwrap();
            img = img.resize(
                gallery_settings.thumb_width,
                gallery_settings.thumb_height,
                image::imageops::FilterType::Nearest,
            );
            img.save_with_format(&thumb_path, image::ImageFormat::Png)
                .expect(format!("Saving thumb image '{}' failed", thumb_path.display()).as_ref());
        }

        let image_entry = liquid::object!({
            "name"  : rel_image_path.to_str().unwrap().to_owned(),
            "thumb" : rel_thumb_path.to_str().unwrap().to_owned(),
        });
        images.push(liquid::model::value::Value::Object(image_entry));
    }
    images
}

fn apply_gallery_template(
    content: &str,
    nav_items: Vec<liquid::model::value::Value>,
    images: Vec<liquid::model::value::Value>,
    depth: usize,
    conf: &Config,
) -> String {
    let template = liquid::ParserBuilder::with_stdlib()
        .build()
        .unwrap()
        .parse(template::load_gallery_template(conf).as_str())
        .expect("Gallery template could not be parsed!");
    let mut root_dir = String::new();
    for _ in 1..depth {
        root_dir.push_str("../");
    }
    let mut context = liquid::model::Object::new();
    context.insert(
        "root_dir".into(),
        liquid::model::value::Value::scalar(root_dir),
    );
    context.insert(
        "title".into(),
        liquid::model::value::Value::scalar(if conf.title.is_some() {
            conf.title.as_ref().unwrap().clone()
        } else {
            "None".to_string()
        }),
    );
    context.insert(
        "nav_items".into(),
        liquid::model::value::Value::Array(nav_items),
    );
    context.insert(
        "content".into(),
        liquid::model::value::Value::scalar(content.to_owned()),
    );
    context.insert("images".into(), liquid::model::value::Value::Array(images));
    match template.render(&context) {
        Ok(output) => output,
        Err(error) => panic!("Could not render Page template: {}", error),
    }
}

fn apply_page_template(
    content: &str,
    nav_items: Vec<liquid::model::value::Value>,
    depth: usize,
    conf: &Config,
) -> String {
    let template = liquid::ParserBuilder::with_stdlib()
        .build()
        .unwrap()
        .parse(template::load_page_template(conf).as_str())
        .expect("Page template could not be parsed!");
    let mut root_dir = String::from("./");
    for _ in 1..depth {
        root_dir.push_str("../");
    }
    let context = liquid::object!({
       "root_dir" : root_dir,
       "title" : if conf.title.is_some() {
            conf.title.as_ref().unwrap().clone()
        } else {
            "None".to_string()
        },
        "nav_items" : liquid::model::value::Value::Array(nav_items),
        "content" : content.to_owned(),
    });
    match template.render(&context) {
        Ok(output) => output,
        Err(error) => panic!("Could not render Page template: {}", error),
    }
}

fn convert_markdown_to_html(entry: &Path) -> String {
    let mut markdown = String::new();
    let mut html_output = String::new();
    match File::open(entry).and_then(|mut f| f.read_to_string(&mut markdown)) {
        Err(error) => panic!("failed to open {}: {}", entry.display(), error),
        Ok(_) => html::push_html(
            &mut html_output,
            Parser::new_ext(markdown.as_str(), Options::empty()),
        ),
    }
    html_output
}

fn write_html_file(html: &str, target_dir: &Path, entry: &DirEntry) {
    let html_file = match entry.file_name().to_str() {
        Some(fname) => {
            let mut tmp_path = PathBuf::from(fname);
            tmp_path.set_file_name("index.html");
            tmp_path
        }
        None => panic!("Could not write html file {}", entry.path().display()),
    };
    let file_path = target_dir.join(html_file.as_path());
    let result = File::create(file_path.as_path()).and_then(|mut f| f.write_all(html.as_bytes()));
    if result.is_err() {
        panic!(
            "Could not write html file {}: {:?}",
            file_path.as_path().display(),
            result.err()
        );
    }
    println!("written file {}", file_path.display());
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::collections::hashmap::{Occupied, Vacant};
use http::method::Method;
use http::method;
use iron::{Request, Response, Handler, IronResult, Error, IronError};
use iron::status;
use typemap::Assoc;
use recognizer::Router as Recognizer;
use recognizer::{Match, Params};

/// `Router` provides an interface for creating complex routes as middleware
/// for the Iron framework.
pub struct Router {
    // The routers, specialized by method.
    routers: HashMap<Method, Recognizer<Box<Handler + Send + Sync>>>,
    error: Option<Box<Handler + Send + Sync>>
}

#[deriving(Show)]
/// The error thrown by router if there is no matching route.
pub struct NoRoute;

impl Error for NoRoute {
    fn name(&self) -> &'static str { "No Route" }
}

impl Router {
    /// `new` constructs a new, blank `Router`.
    pub fn new() -> Router { Router { routers: HashMap::new(), error: None } }
    /// Add a new route to a `Router`, matching both a method and glob pattern.
    ///
    /// `route` supports glob patterns: `*` for a single wildcard segment and
    /// `:param` for matching storing that segment of the request url in the `Params`
    /// object, which is stored in the request `extensions`.
    ///
    /// For instance, to route `Get` requests on any route matching
    /// `/users/:userid/:friend` and store `userid` and `friend` in
    /// the exposed Params object:
    ///
    /// ```ignore
    /// router.route(http::method::Get, "/users/:userid/:friendid", controller);
    /// ```
    ///
    /// The controller provided to route can be any `Middleware`, which allows
    /// extreme flexibility when handling routes. For instance, you could provide
    /// a `Chain`, a `Middleware`, which contains an authorization middleware and
    /// a controller function, so that you can confirm that the request is
    /// authorized for this route before handling it.
    pub fn route<'a, H: Handler, S: Str>(&mut self, method: Method, glob: S, handler: H) -> &mut Router {
        match self.routers.entry(method) {
            Vacant(entry)   => entry.set(Recognizer::new()),
            Occupied(entry) => entry.into_mut()
        }.add(glob.as_slice(), box handler as Box<Handler + Send + Sync>);
        self
    }

    /// Like route, but specialized to the `Get` method.
    pub fn get<'a, H: Handler, S: Str>(&mut self, glob: S, handler: H) -> &mut Router {
        self.route(method::Get, glob, handler)
    }

    /// Like route, but specialized to the `Post` method.
    pub fn post<'a, H: Handler, S: Str>(&mut self, glob: S, handler: H) -> &mut Router {
        self.route(method::Post, glob, handler)
    }

    /// Like route, but specialized to the `Put` method.
    pub fn put<'a, H: Handler, S: Str>(&mut self, glob: S, handler: H) -> &mut Router {
        self.route(method::Put, glob, handler)
    }

    /// Like route, but specialized to the `Delete` method.
    pub fn delete<'a, H: Handler, S: Str>(&mut self, glob: S, handler: H) -> &mut Router {
        self.route(method::Delete, glob, handler)
    }

    /// Like route, but specialized to the `Head` method.
    pub fn head<'a, H: Handler, S: Str>(&mut self, glob: S, handler: H) -> &mut Router {
        self.route(method::Head, glob, handler)
    }

    /// Like route, but specialized to the `Patch` method.
    pub fn patch<'a, H: Handler, S: Str>(&mut self, glob: S, handler: H) -> &mut Router {
        self.route(method::Patch, glob, handler)
    }

    /// Like route, but specialized to the `Options` method.
    pub fn options<'a, H: Handler, S: Str>(&mut self, glob: S, handler: H) -> &mut Router {
        self.route(method::Options, glob, handler)
    }

    /// Add a Handler to be used for this Router's `catch` method.
    pub fn error<H: Handler>(&mut self, handler: H) -> &mut Router {
        self.error = Some(box handler as Box<Handler + Send + Sync>);
        self
    }

    fn recognize<'a>(&'a self, method: &Method, path: &str)
                     -> Option<Match<&'a Box<Handler + Send + Sync>>> {
        self.routers.find(method).and_then(|router| router.recognize(path).ok())
    }
}

impl Assoc<Params> for Router {}

impl Handler for Router {
    fn call(&self, req: &mut Request) -> IronResult<Response> {
        let matched = match self.recognize(&req.method, req.url.path.connect("/").as_slice()) {
            Some(matched) => matched,
            // No match.
            None => return Err(NoRoute.abstract())
        };

        req.extensions.insert::<Router, Params>(matched.params);
        matched.handler.call(req)
    }

    fn catch(&self, req: &mut Request, err: IronError) -> (Response, IronResult<()>) {
        match self.error {
            Some(ref error_handler) => error_handler.catch(req, err),
            // Error that is not caught by anything!
            None => (Response::status(status::InternalServerError), Err(err))
        }
    }
}
<commit_msg>abstract renamed to erase in iron<commit_after>use std::collections::HashMap;
use std::collections::hashmap::{Occupied, Vacant};
use http::method::Method;
use http::method;
use iron::{Request, Response, Handler, IronResult, Error, IronError};
use iron::status;
use typemap::Assoc;
use recognizer::Router as Recognizer;
use recognizer::{Match, Params};

/// `Router` provides an interface for creating complex routes as middleware
/// for the Iron framework.
pub struct Router {
    // The routers, specialized by method.
    routers: HashMap<Method, Recognizer<Box<Handler + Send + Sync>>>,
    error: Option<Box<Handler + Send + Sync>>
}

#[deriving(Show)]
/// The error thrown by router if there is no matching route.
pub struct NoRoute;

impl Error for NoRoute {
    fn name(&self) -> &'static str { "No Route" }
}

impl Router {
    /// `new` constructs a new, blank `Router`.
    pub fn new() -> Router { Router { routers: HashMap::new(), error: None } }
    /// Add a new route to a `Router`, matching both a method and glob pattern.
    ///
    /// `route` supports glob patterns: `*` for a single wildcard segment and
    /// `:param` for matching storing that segment of the request url in the `Params`
    /// object, which is stored in the request `extensions`.
    ///
    /// For instance, to route `Get` requests on any route matching
    /// `/users/:userid/:friend` and store `userid` and `friend` in
    /// the exposed Params object:
    ///
    /// ```ignore
    /// router.route(http::method::Get, "/users/:userid/:friendid", controller);
    /// ```
    ///
    /// The controller provided to route can be any `Middleware`, which allows
    /// extreme flexibility when handling routes. For instance, you could provide
    /// a `Chain`, a `Middleware`, which contains an authorization middleware and
    /// a controller function, so that you can confirm that the request is
    /// authorized for this route before handling it.
    pub fn route<'a, H: Handler, S: Str>(&mut self, method: Method, glob: S, handler: H) -> &mut Router {
        match self.routers.entry(method) {
            Vacant(entry)   => entry.set(Recognizer::new()),
            Occupied(entry) => entry.into_mut()
        }.add(glob.as_slice(), box handler as Box<Handler + Send + Sync>);
        self
    }

    /// Like route, but specialized to the `Get` method.
    pub fn get<'a, H: Handler, S: Str>(&mut self, glob: S, handler: H) -> &mut Router {
        self.route(method::Get, glob, handler)
    }

    /// Like route, but specialized to the `Post` method.
    pub fn post<'a, H: Handler, S: Str>(&mut self, glob: S, handler: H) -> &mut Router {
        self.route(method::Post, glob, handler)
    }

    /// Like route, but specialized to the `Put` method.
    pub fn put<'a, H: Handler, S: Str>(&mut self, glob: S, handler: H) -> &mut Router {
        self.route(method::Put, glob, handler)
    }

    /// Like route, but specialized to the `Delete` method.
    pub fn delete<'a, H: Handler, S: Str>(&mut self, glob: S, handler: H) -> &mut Router {
        self.route(method::Delete, glob, handler)
    }

    /// Like route, but specialized to the `Head` method.
    pub fn head<'a, H: Handler, S: Str>(&mut self, glob: S, handler: H) -> &mut Router {
        self.route(method::Head, glob, handler)
    }

    /// Like route, but specialized to the `Patch` method.
    pub fn patch<'a, H: Handler, S: Str>(&mut self, glob: S, handler: H) -> &mut Router {
        self.route(method::Patch, glob, handler)
    }

    /// Like route, but specialized to the `Options` method.
    pub fn options<'a, H: Handler, S: Str>(&mut self, glob: S, handler: H) -> &mut Router {
        self.route(method::Options, glob, handler)
    }

    /// Add a Handler to be used for this Router's `catch` method.
    pub fn error<H: Handler>(&mut self, handler: H) -> &mut Router {
        self.error = Some(box handler as Box<Handler + Send + Sync>);
        self
    }

    fn recognize<'a>(&'a self, method: &Method, path: &str)
                     -> Option<Match<&'a Box<Handler + Send + Sync>>> {
        self.routers.find(method).and_then(|router| router.recognize(path).ok())
    }
}

impl Assoc<Params> for Router {}

impl Handler for Router {
    fn call(&self, req: &mut Request) -> IronResult<Response> {
        let matched = match self.recognize(&req.method, req.url.path.connect("/").as_slice()) {
            Some(matched) => matched,
            // No match.
            None => return Err(NoRoute.erase())
        };

        req.extensions.insert::<Router, Params>(matched.params);
        matched.handler.call(req)
    }

    fn catch(&self, req: &mut Request, err: IronError) -> (Response, IronResult<()>) {
        match self.error {
            Some(ref error_handler) => error_handler.catch(req, err),
            // Error that is not caught by anything!
            None => (Response::status(status::InternalServerError), Err(err))
        }
    }
}
<|endoftext|>


<commit_before>//! The Runner is where all the examples are actually executed.
//!
//! A Runner is instanciated by using [`context::describe`](../context/fn.describe.html) and
//! [`context::rdescribe`](../context/fn.rdescribe.html). You should not try to instanciate
//! a Runner directly.
//!
//! The main methods are `Runner::run` and `Runner::result`.

use context::*;
use events;
use events::Event;
use example_result;
use example_result::ExampleResult;

pub type RunnerResult = Result<TestReport, TestReport>;

/// Handlers is a separate struct which only holds the registered handlers.
/// This is useful to Runner so that its recursive call doesn't have to keep a refernce to `self`
#[derive(Default)]
struct Handlers<'a> {
    handlers: Vec<&'a mut events::EventHandler>,
}

impl<'a> Handlers<'a> {
    fn broadcast(&mut self, event: &events::Event) {
        for h in &mut self.handlers {
            h.trigger(event)
        }
    }
}

pub struct Runner<'a> {
    describe: Context<'a>,
    report: Option<RunnerResult>,
    handlers: Handlers<'a>,
}

impl<'a> Runner<'a> {
    pub fn new(context: Context<'a>) -> Runner<'a> {
        Runner {
            describe: context,
            handlers: Handlers::default(),
            report: None,
        }
    }
}

#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
pub struct TestReport {
    pub total_tests: u32,
    pub success_count: u32,
    pub error_count: u32,
}

impl<'a> Runner<'a> {

    #[cfg_attr(feature="clippy", allow(redundant_closure))]
    fn run_test(test_name: &str,
                test_function: &mut Box<TestFunction>,
                handlers: &mut Handlers)
                -> ExampleResult {

        use std::panic::{catch_unwind, AssertUnwindSafe};
        use example_result;

        handlers.broadcast(&Event::StartTest(String::from(test_name)));
        let res = catch_unwind(AssertUnwindSafe(|| test_function()));

        let res = match res {
            Ok(res) => res,
            // if test panicked, it means that it failed
            Err(_) => example_result::FAILED_RES
        };
        handlers.broadcast(&Event::EndTest(res));
        res
    }

    fn run_and_recurse(report: &mut TestReport,
                       child_ctx: &mut Context,
                       handlers: &mut Handlers)
                       -> ExampleResult {
        let mut result = example_result::SUCCESS_RES;
        let before_functions = &mut child_ctx.before_each;
        let after_functions = &mut child_ctx.after_each;

        for test_function in &mut child_ctx.tests {
            let test_res = {
                for before_function in before_functions.iter_mut() {
                    before_function()
                }
                let res = match *test_function {
                    Testable::Test(ref name, ref mut test_function) => {
                        Runner::run_test(name, test_function, handlers)
                    }
                    Testable::Describe(ref name, ref mut desc) => {
                        handlers.broadcast(&Event::StartDescribe(name.clone()));
                        let res = Runner::run_and_recurse(report, desc, handlers);
                        handlers.broadcast(&Event::EndDescribe);
                        res
                    }
                };
                for after_function in after_functions.iter_mut() {
                    after_function()
                }
                res
            };

            //  FIXME: bug here ? why not test_res ?
            result = if result.is_ok() {
                report.success_count += 1;
                test_res
            } else {
                report.error_count += 1;
                result
            };

            report.total_tests += 1;
        }

        result
    }

    pub fn run(&mut self) -> Result<(), ()> {
        self.handlers.broadcast(&Event::StartRunner);

        let mut report = TestReport::default();
        let result = Runner::run_and_recurse(&mut report, &mut self.describe, &mut self.handlers);
        let result = result.res().and(Ok(report)).or_else(|_| Err(report));

        self.report = Some(result);
        self.handlers.broadcast(&Event::FinishedRunner(result));
        Ok(())
    }

    pub fn result(&self) -> RunnerResult {
        self.report.unwrap_or_else(|| Ok(TestReport::default()))
    }

    pub fn add_event_handler<H: events::EventHandler>(&mut self, handler: &'a mut H) {
        self.handlers.handlers.push(handler)
    }
}

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

    mod run {
        pub use super::*;

        #[test]
        fn it_create_a_runner_that_can_be_runned() {
            let mut runner = describe("A root", |ctx| {
                ctx.it("is expected to run", || {
                    assert_eq!(true, true);
                    Ok(()) as Result<(),()>
                })
            });
            assert!(runner.run().is_ok());
        }

        #[test]
        fn effectively_run_tests() {
            let ran = &mut false;

            {
                let mut runner = describe("A root", |ctx| {
                    ctx.it("is expected to run", || {
                        *ran = true;
                        Ok(()) as Result<(),()>
                    })
                });
                runner.run().unwrap()
            }

            assert_eq!(true, *ran)
        }

        #[test]
        fn effectively_run_two_tests() {
            use std::sync::atomic::{AtomicUsize, Ordering};
            let ran_counter = &mut AtomicUsize::new(0);

            {
                let mut runner = describe("A root", |ctx| {
                    ctx.it("first run", || {
                        ran_counter.fetch_add(1, Ordering::Relaxed);
                        Ok(()) as Result<(),()>
                    });
                    ctx.it("second run", || {
                        ran_counter.fetch_add(1, Ordering::Relaxed);
                        Ok(()) as Result<(),()>
                    });
                });
                runner.run().unwrap();
            }

            assert_eq!(2, ran_counter.load(Ordering::Relaxed))
        }

        #[test]
        fn effectively_run_two_tests_in_nested_describe() {
            use std::sync::atomic::{AtomicUsize, Ordering};
            let ran_counter = &mut AtomicUsize::new(0);

            {
                let mut runner = describe("A root", |ctx| {
                    ctx.describe("first describe", |ctx| {
                        ctx.it("first run", || {
                            ran_counter.fetch_add(1, Ordering::Relaxed);
                            Ok(()) as Result<(),()>
                        });
                    });
                    ctx.describe("second describe", |ctx| {
                        ctx.it("second run", || {
                            ran_counter.fetch_add(1, Ordering::Relaxed);
                            Ok(()) as Result<(),()>
                        });
                    });
                    ctx.describe("third describe", |ctx| {
                        ctx.describe("fourth describe", |ctx| {
                            ctx.it("third run", || {
                                ran_counter.fetch_add(1, Ordering::Relaxed);
                                Ok(()) as Result<(),()>
                            });
                        })
                    })
                });
                runner.run().unwrap();
            }

            assert_eq!(3, ran_counter.load(Ordering::Relaxed))
        }

        mod events {
            pub use super::*;
            pub use events::*;
            pub use events::Event::*;

            #[derive(Default)]
            struct StubEventHandler {
                pub events: Vec<Event>,
            }

            impl EventHandler for StubEventHandler {
                fn trigger(&mut self, event: &Event) {
                    self.events.push(event.clone())
                }
            }

            #[test]
            fn start_runner_event_is_sent() {
                let mut handler = StubEventHandler::default();
                {
                    let mut runner = describe("empty but should run anyway", |_| {});
                    runner.add_event_handler(&mut handler);

                    runner.run().unwrap();
                }

                assert_eq!(Some(&StartRunner), handler.events.get(0))
            }

            #[test]
            fn no_event_when_no_run() {
                let mut handler = StubEventHandler::default();

                {
                    let mut runner = describe("empty but should run anyway", |_| {});
                    runner.add_event_handler(&mut handler);
                }

                assert_eq!(None, handler.events.get(0))
            }

            #[test]
            fn finished_runner_event_is_last_event_sent() {
                let mut handler = StubEventHandler::default();
                {
                    let mut runner = describe("empty but should run anyway", |_| {});
                    runner.add_event_handler(&mut handler);

                    runner.run().unwrap();
                }

                if let Some(&FinishedRunner(_)) = handler.events.last() {
                    assert!(true);
                } else {
                    assert!(false, "FinishedRunner event not sent at last")
                }
            }

            #[test]
            fn finished_runner_event_has_correct_test_report() {
                let mut handler = StubEventHandler::default();
                {
                    let mut runner = describe("one good test",
                                              |ctx| ctx.it("is a good test", || Ok(()) as Result<(),()>));
                    runner.add_event_handler(&mut handler);

                    runner.run().unwrap();
                }

                if let Some(&FinishedRunner(Ok(report))) = handler.events.last() {
                    let expected_report = TestReport {
                        total_tests: 1,
                        success_count: 1,
                        error_count: 0,
                    };
                    assert_eq!(expected_report, report)
                } else {
                    assert!(false, "FinishedRunner send bad TestReport")
                }
            }

            #[test]
            fn start_test_is_broadcasted() {
                let mut handler = StubEventHandler::default();

                {
                    let mut runner = describe("root", |ctx| {
                        ctx.it("should run with an event", || Ok(()) as Result<(),()>);
                    });
                    runner.add_event_handler(&mut handler);
                    runner.run().unwrap();
                }

                assert_eq!(Some(&StartTest(String::from("should run with an event"))),
                           handler.events.get(1))
            }

            #[test]
            fn end_test_is_broadcasted() {
                let mut handler = StubEventHandler::default();

                {
                    let mut runner = describe("root", |ctx| {
                        ctx.it("should run with an event", || Ok(()) as Result<(),()>);
                    });
                    runner.add_event_handler(&mut handler);
                    runner.run().unwrap();
                }

                assert_eq!(Some(&EndTest(SUCCESS_RES)), handler.events.get(2));
            }

            #[test]
            fn start_describe_is_broadcasted() {
                let mut handler = StubEventHandler::default();

                {
                    let mut runner = describe("root, no hook", |ctx| {
                        ctx.describe("this has a hook", |_| {});
                    });
                    runner.add_event_handler(&mut handler);
                    runner.run().unwrap();
                }

                assert_eq!(Some(&StartDescribe(String::from("this has a hook"))),
                           handler.events.get(1));
            }

            #[test]
            fn end_describe_is_broadcasted() {
                let mut handler = StubEventHandler::default();

                {
                    let mut runner = describe("root, no hook", |ctx| {
                        ctx.describe("this has a hook", |_| {});
                    });
                    runner.add_event_handler(&mut handler);
                    runner.run().unwrap();
                }

                assert_eq!(Some(&EndDescribe), handler.events.get(2));
            }
        }
    }

    mod results {
        pub use super::*;

        #[test]
        fn tests_can_fail_with_an_error_result() {
            let mut runner = describe("A root", |ctx| ctx.it("should fail", || Err(()) as Result<(),()>));
            runner.run().unwrap();

            assert!(runner.result().is_err());
        }

        #[test]
        fn should_be_ok_if_tests_are_ok() {
            let mut runner = describe("A root", |ctx| ctx.it("should be ok", || Ok(()) as Result<(),()>));
            runner.run().unwrap();

            assert!(runner.result().is_ok());
        }

        #[test]
        fn is_ok_if_no_tests_have_been_runned() {
            let mut runner = describe("A root", |_ctx| {});
            runner.run().unwrap();

            assert!(runner.result().is_ok());
        }

        #[test]
        fn is_err_if_one_test_is_err() {
            let mut runner = describe("A root", |ctx| {
                ctx.it("an err", || Err(()) as Result<(),()>);
                ctx.it("an ok", || Ok(()) as Result<(),()>);
            });
            runner.run().unwrap();

            assert!(runner.result().is_err());
        }

        #[test]
        fn is_ok_if_all_tests_are_ok() {
            let mut runner = describe("A root", |ctx| {
                ctx.it("ok 1", || Ok(()) as Result<(),()>);
                ctx.it("ok 2", || Ok(()) as Result<(),()>);
                ctx.it("ok 3", || Ok(()) as Result<(),()>);
                ctx.it("ok 4", || Ok(()) as Result<(),()>);
            });
            runner.run().unwrap();

            assert!(runner.result().is_ok());
        }

        #[test]
        fn tests_can_contains_asserts_that_panic() {
            use std::sync::atomic::{AtomicUsize, Ordering};
            let counter = &mut AtomicUsize::new(0);

            let mut runner = describe("A root", |ctx| {
                ctx.it("assert_eq fail", || {
                    assert_eq!(true, false);
                    Ok(()) as Result<(),()>
                });
                ctx.it("this should also be runned", || {
                    counter.fetch_add(1, Ordering::Relaxed);
                    Ok(()) as Result<(),()>
                })
            });
            runner.run().unwrap();

            assert!(runner.result().is_err());
            assert_eq!(1, counter.load(Ordering::Relaxed));
        }

        #[test]
        fn can_count_the_tests() {
            let mut runner = describe("a root", |ctx| {
                ctx.it("first", || Ok(()) as Result<(),()>);
                ctx.it("second", || Ok(()) as Result<(),()>);
                ctx.it("third", || Ok(()) as Result<(),()>);
            });
            runner.run().unwrap();
            let result = runner.result();

            assert!(result.is_ok());
            if let Ok(report) = result {
                assert_eq!(3, report.total_tests);
            }
        }

        #[test]
        fn can_count_succes() {
            let mut runner = describe("a root", |ctx| {
                ctx.it("first", || Ok(()) as Result<(),()>);
                ctx.it("second", || Ok(()) as Result<(),()>);
                ctx.it("third", || Ok(()) as Result<(),()>);
            });
            runner.run().unwrap();
            let result = runner.result();

            assert!(result.is_ok());
            if let Ok(report) = result {
                assert_eq!(3, report.success_count);
            }
        }

        #[test]
        fn can_count_errors() {
            let mut runner = describe("a root", |ctx| {
                ctx.it("first", || Err(()) as Result<(),()>);
                ctx.it("second", || Err(()) as Result<(),()>);
                ctx.it("third", || Ok(()) as Result<(),()>);
            });
            runner.run().unwrap();
            let result = runner.result();

            assert!(result.is_err());
            if let Err(report) = result {
                assert_eq!(2, report.error_count);
            }
        }
    }
}
<commit_msg> fixed a bug in the count<commit_after>//! The Runner is where all the examples are actually executed.
//!
//! A Runner is instanciated by using [`context::describe`](../context/fn.describe.html) and
//! [`context::rdescribe`](../context/fn.rdescribe.html). You should not try to instanciate
//! a Runner directly.
//!
//! The main methods are `Runner::run` and `Runner::result`.

use context::*;
use events;
use events::Event;
use example_result;
use example_result::ExampleResult;

pub type RunnerResult = Result<TestReport, TestReport>;

/// Handlers is a separate struct which only holds the registered handlers.
/// This is useful to Runner so that its recursive call doesn't have to keep a refernce to `self`
#[derive(Default)]
struct Handlers<'a> {
    handlers: Vec<&'a mut events::EventHandler>,
}

impl<'a> Handlers<'a> {
    fn broadcast(&mut self, event: &events::Event) {
        for h in &mut self.handlers {
            h.trigger(event)
        }
    }
}

pub struct Runner<'a> {
    describe: Context<'a>,
    report: Option<RunnerResult>,
    handlers: Handlers<'a>,
}

impl<'a> Runner<'a> {
    pub fn new(context: Context<'a>) -> Runner<'a> {
        Runner {
            describe: context,
            handlers: Handlers::default(),
            report: None,
        }
    }
}

#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
pub struct TestReport {
    pub total_tests: u32,
    pub success_count: u32,
    pub error_count: u32,
}

impl<'a> Runner<'a> {

    #[cfg_attr(feature="clippy", allow(redundant_closure))]
    fn run_test(test_name: &str,
                test_function: &mut Box<TestFunction>,
                handlers: &mut Handlers)
                -> ExampleResult {

        use std::panic::{catch_unwind, AssertUnwindSafe};
        use example_result;

        handlers.broadcast(&Event::StartTest(String::from(test_name)));
        let res = catch_unwind(AssertUnwindSafe(|| test_function()));

        let res = match res {
            Ok(res) => res,
            // if test panicked, it means that it failed
            Err(_) => example_result::FAILED_RES
        };
        handlers.broadcast(&Event::EndTest(res));
        res
    }

    fn run_and_recurse(report: &mut TestReport,
                       child_ctx: &mut Context,
                       handlers: &mut Handlers)
                       -> ExampleResult {
        let mut result = example_result::SUCCESS_RES;
        let before_functions = &mut child_ctx.before_each;
        let after_functions = &mut child_ctx.after_each;

        for test_function in &mut child_ctx.tests {
            let test_res = {
                for before_function in before_functions.iter_mut() {
                    before_function()
                }
                let res = match *test_function {
                    Testable::Test(ref name, ref mut test_function) => {
                        Runner::run_test(name, test_function, handlers)
                    }
                    Testable::Describe(ref name, ref mut desc) => {
                        handlers.broadcast(&Event::StartDescribe(name.clone()));
                        let res = Runner::run_and_recurse(report, desc, handlers);
                        handlers.broadcast(&Event::EndDescribe);
                        res
                    }
                };
                for after_function in after_functions.iter_mut() {
                    after_function()
                }
                res
            };

            report.total_tests += 1;

            if test_res.is_ok() {
                report.success_count += 1;
            } else {
                report.error_count += 1
            }

            result = test_res.or(result);
        }

        result
    }

    pub fn run(&mut self) -> Result<(), ()> {
        self.handlers.broadcast(&Event::StartRunner);

        let mut report = TestReport::default();
        let result = Runner::run_and_recurse(&mut report, &mut self.describe, &mut self.handlers);
        let result = result.res().and(Ok(report)).or_else(|_| Err(report));

        self.report = Some(result);
        self.handlers.broadcast(&Event::FinishedRunner(result));
        Ok(())
    }

    pub fn result(&self) -> RunnerResult {
        self.report.unwrap_or_else(|| Ok(TestReport::default()))
    }

    pub fn add_event_handler<H: events::EventHandler>(&mut self, handler: &'a mut H) {
        self.handlers.handlers.push(handler)
    }
}

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

    mod run {
        pub use super::*;

        #[test]
        fn it_create_a_runner_that_can_be_runned() {
            let mut runner = describe("A root", |ctx| {
                ctx.it("is expected to run", || {
                    assert_eq!(true, true);
                    Ok(()) as Result<(),()>
                })
            });
            assert!(runner.run().is_ok());
        }

        #[test]
        fn effectively_run_tests() {
            let ran = &mut false;

            {
                let mut runner = describe("A root", |ctx| {
                    ctx.it("is expected to run", || {
                        *ran = true;
                        Ok(()) as Result<(),()>
                    })
                });
                runner.run().unwrap()
            }

            assert_eq!(true, *ran)
        }

        #[test]
        fn effectively_run_two_tests() {
            use std::sync::atomic::{AtomicUsize, Ordering};
            let ran_counter = &mut AtomicUsize::new(0);

            {
                let mut runner = describe("A root", |ctx| {
                    ctx.it("first run", || {
                        ran_counter.fetch_add(1, Ordering::Relaxed);
                        Ok(()) as Result<(),()>
                    });
                    ctx.it("second run", || {
                        ran_counter.fetch_add(1, Ordering::Relaxed);
                        Ok(()) as Result<(),()>
                    });
                });
                runner.run().unwrap();
            }

            assert_eq!(2, ran_counter.load(Ordering::Relaxed))
        }

        #[test]
        fn effectively_run_two_tests_in_nested_describe() {
            use std::sync::atomic::{AtomicUsize, Ordering};
            let ran_counter = &mut AtomicUsize::new(0);

            {
                let mut runner = describe("A root", |ctx| {
                    ctx.describe("first describe", |ctx| {
                        ctx.it("first run", || {
                            ran_counter.fetch_add(1, Ordering::Relaxed);
                            Ok(()) as Result<(),()>
                        });
                    });
                    ctx.describe("second describe", |ctx| {
                        ctx.it("second run", || {
                            ran_counter.fetch_add(1, Ordering::Relaxed);
                            Ok(()) as Result<(),()>
                        });
                    });
                    ctx.describe("third describe", |ctx| {
                        ctx.describe("fourth describe", |ctx| {
                            ctx.it("third run", || {
                                ran_counter.fetch_add(1, Ordering::Relaxed);
                                Ok(()) as Result<(),()>
                            });
                        })
                    })
                });
                runner.run().unwrap();
            }

            assert_eq!(3, ran_counter.load(Ordering::Relaxed))
        }

        mod events {
            pub use super::*;
            pub use events::*;
            pub use events::Event::*;

            #[derive(Default)]
            struct StubEventHandler {
                pub events: Vec<Event>,
            }

            impl EventHandler for StubEventHandler {
                fn trigger(&mut self, event: &Event) {
                    self.events.push(event.clone())
                }
            }

            #[test]
            fn start_runner_event_is_sent() {
                let mut handler = StubEventHandler::default();
                {
                    let mut runner = describe("empty but should run anyway", |_| {});
                    runner.add_event_handler(&mut handler);

                    runner.run().unwrap();
                }

                assert_eq!(Some(&StartRunner), handler.events.get(0))
            }

            #[test]
            fn no_event_when_no_run() {
                let mut handler = StubEventHandler::default();

                {
                    let mut runner = describe("empty but should run anyway", |_| {});
                    runner.add_event_handler(&mut handler);
                }

                assert_eq!(None, handler.events.get(0))
            }

            #[test]
            fn finished_runner_event_is_last_event_sent() {
                let mut handler = StubEventHandler::default();
                {
                    let mut runner = describe("empty but should run anyway", |_| {});
                    runner.add_event_handler(&mut handler);

                    runner.run().unwrap();
                }

                if let Some(&FinishedRunner(_)) = handler.events.last() {
                    assert!(true);
                } else {
                    assert!(false, "FinishedRunner event not sent at last")
                }
            }

            #[test]
            fn finished_runner_event_has_correct_test_report() {
                let mut handler = StubEventHandler::default();
                {
                    let mut runner = describe("one good test",
                                              |ctx| ctx.it("is a good test", || Ok(()) as Result<(),()>));
                    runner.add_event_handler(&mut handler);

                    runner.run().unwrap();
                }

                if let Some(&FinishedRunner(Ok(report))) = handler.events.last() {
                    let expected_report = TestReport {
                        total_tests: 1,
                        success_count: 1,
                        error_count: 0,
                    };
                    assert_eq!(expected_report, report)
                } else {
                    assert!(false, "FinishedRunner send bad TestReport")
                }
            }

            #[test]
            fn start_test_is_broadcasted() {
                let mut handler = StubEventHandler::default();

                {
                    let mut runner = describe("root", |ctx| {
                        ctx.it("should run with an event", || Ok(()) as Result<(),()>);
                    });
                    runner.add_event_handler(&mut handler);
                    runner.run().unwrap();
                }

                assert_eq!(Some(&StartTest(String::from("should run with an event"))),
                           handler.events.get(1))
            }

            #[test]
            fn end_test_is_broadcasted() {
                let mut handler = StubEventHandler::default();

                {
                    let mut runner = describe("root", |ctx| {
                        ctx.it("should run with an event", || Ok(()) as Result<(),()>);
                    });
                    runner.add_event_handler(&mut handler);
                    runner.run().unwrap();
                }

                assert_eq!(Some(&EndTest(SUCCESS_RES)), handler.events.get(2));
            }

            #[test]
            fn start_describe_is_broadcasted() {
                let mut handler = StubEventHandler::default();

                {
                    let mut runner = describe("root, no hook", |ctx| {
                        ctx.describe("this has a hook", |_| {});
                    });
                    runner.add_event_handler(&mut handler);
                    runner.run().unwrap();
                }

                assert_eq!(Some(&StartDescribe(String::from("this has a hook"))),
                           handler.events.get(1));
            }

            #[test]
            fn end_describe_is_broadcasted() {
                let mut handler = StubEventHandler::default();

                {
                    let mut runner = describe("root, no hook", |ctx| {
                        ctx.describe("this has a hook", |_| {});
                    });
                    runner.add_event_handler(&mut handler);
                    runner.run().unwrap();
                }

                assert_eq!(Some(&EndDescribe), handler.events.get(2));
            }
        }
    }

    mod results {
        pub use super::*;

        #[test]
        fn tests_can_fail_with_an_error_result() {
            let mut runner = describe("A root", |ctx| ctx.it("should fail", || Err(()) as Result<(),()>));
            runner.run().unwrap();

            assert!(runner.result().is_err());
        }

        #[test]
        fn should_be_ok_if_tests_are_ok() {
            let mut runner = describe("A root", |ctx| ctx.it("should be ok", || Ok(()) as Result<(),()>));
            runner.run().unwrap();

            assert!(runner.result().is_ok());
        }

        #[test]
        fn is_ok_if_no_tests_have_been_runned() {
            let mut runner = describe("A root", |_ctx| {});
            runner.run().unwrap();

            assert!(runner.result().is_ok());
        }

        #[test]
        fn is_err_if_one_test_is_err() {
            let mut runner = describe("A root", |ctx| {
                ctx.it("an err", || Err(()) as Result<(),()>);
                ctx.it("an ok", || Ok(()) as Result<(),()>);
            });
            runner.run().unwrap();

            assert!(runner.result().is_err());
        }

        #[test]
        fn is_ok_if_all_tests_are_ok() {
            let mut runner = describe("A root", |ctx| {
                ctx.it("ok 1", || Ok(()) as Result<(),()>);
                ctx.it("ok 2", || Ok(()) as Result<(),()>);
                ctx.it("ok 3", || Ok(()) as Result<(),()>);
                ctx.it("ok 4", || Ok(()) as Result<(),()>);
            });
            runner.run().unwrap();

            assert!(runner.result().is_ok());
        }

        #[test]
        fn correctly_count_errors() {
            let mut runner = describe("a root", |ctx| {
                ctx.it("first is ok", || ());
                ctx.it("second is not", || false);
            });
            runner.run().unwrap();
            if let Err(res) = runner.result() {
                assert_eq!((1, 1),
                           (res.success_count, res.error_count));
            } else {
                assert!(false, "unreachable");
            }

        }

        #[test]
        fn tests_can_contains_asserts_that_panic() {
            use std::sync::atomic::{AtomicUsize, Ordering};
            let counter = &mut AtomicUsize::new(0);

            let mut runner = describe("A root", |ctx| {
                ctx.it("assert_eq fail", || {
                    assert_eq!(true, false);
                    Ok(()) as Result<(),()>
                });
                ctx.it("this should also be runned", || {
                    counter.fetch_add(1, Ordering::Relaxed);
                    Ok(()) as Result<(),()>
                })
            });
            runner.run().unwrap();

            assert!(runner.result().is_err());
            assert_eq!(1, counter.load(Ordering::Relaxed));
        }

        #[test]
        fn can_count_the_tests() {
            let mut runner = describe("a root", |ctx| {
                ctx.it("first", || Ok(()) as Result<(),()>);
                ctx.it("second", || Ok(()) as Result<(),()>);
                ctx.it("third", || Ok(()) as Result<(),()>);
            });
            runner.run().unwrap();
            let result = runner.result();

            assert!(result.is_ok());
            if let Ok(report) = result {
                assert_eq!(3, report.total_tests);
            }
        }

        #[test]
        fn can_count_succes() {
            let mut runner = describe("a root", |ctx| {
                ctx.it("first", || Ok(()) as Result<(),()>);
                ctx.it("second", || Ok(()) as Result<(),()>);
                ctx.it("third", || Ok(()) as Result<(),()>);
            });
            runner.run().unwrap();
            let result = runner.result();

            assert!(result.is_ok());
            if let Ok(report) = result {
                assert_eq!(3, report.success_count);
            }
        }

        #[test]
        fn can_count_errors() {
            let mut runner = describe("a root", |ctx| {
                ctx.it("first", || Err(()) as Result<(),()>);
                ctx.it("second", || Err(()) as Result<(),()>);
                ctx.it("third", || Ok(()) as Result<(),()>);
            });
            runner.run().unwrap();
            let result = runner.result();

            assert!(result.is_err());
            if let Err(report) = result {
                assert_eq!(2, report.error_count);
            }
        }
    }
}
<|endoftext|>


<commit_before>use sha1::Sha1;

use cmd::cmd;
use types::{ToRedisArgs, FromRedisValue, RedisResult, ErrorKind};
use connection::ConnectionLike;

/// Represents a lua script.
pub struct Script {
    code: String,
    hash: String,
}

/// The script object represents a lua script that can be executed on the
/// redis server.  The object itself takes care of automatic uploading and
/// execution.  The script object itself can be shared and is immutable.
///
/// Example:
///
/// ```rust,no_run
/// # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
/// # let con = client.get_connection().unwrap();
/// let script = redis::Script::new(r"
///     return tonumber(ARGV[1]) + tonumber(ARGV[2]);
/// ");
/// let result = script.arg(1).arg(2).invoke(&con);
/// assert_eq!(result, Ok(3));
/// ```
impl Script {

    /// Creates a new script object.
    pub fn new(code: &str) -> Script {
        let mut hash = Sha1::new();
        hash.update(code.as_bytes());
        Script {
            code: code.to_string(),
            hash: hash.digest().to_string(),
        }
    }

    /// Returns the script's SHA1 hash in hexadecimal format.
    pub fn get_hash(&self) -> &str {
        &self.hash
    }

    /// Creates a script invocation object with a key filled in.
    #[inline]
    pub fn key<T: ToRedisArgs>(&self, key: T) -> ScriptInvocation {
        ScriptInvocation {
            script: self,
            args: vec![],
            keys: key.to_redis_args(),
        }
    }

    /// Creates a script invocation object with an argument filled in.
    #[inline]
    pub fn arg<T: ToRedisArgs>(&self, arg: T) -> ScriptInvocation {
        ScriptInvocation {
            script: self,
            args: arg.to_redis_args(),
            keys: vec![],
        }
    }

    /// Returns an empty script invocation object.  This is primarily useful
    /// for programmatically adding arguments and keys because the type will
    /// not change.  Normally you can use `arg` and `key` directly.
    #[inline]
    pub fn prepare_invoke(&self) -> ScriptInvocation {
        ScriptInvocation { script: self, args: vec![], keys: vec![] }
    }

    /// Invokes the script directly without arguments.
    #[inline]
    pub fn invoke<T: FromRedisValue>(&self, con: &ConnectionLike) -> RedisResult<T> {
        ScriptInvocation {
            script: self,
            args: vec![],
            keys: vec![],
        }.invoke(con)
    }
}

/// Represents a prepared script call.
pub struct ScriptInvocation<'a> {
    script: &'a Script,
    args: Vec<Vec<u8>>,
    keys: Vec<Vec<u8>>,
}

/// This type collects keys and other arguments for the script so that it
/// can be then invoked.  While the `Script` type itself holds the script,
/// the `ScriptInvocation` holds the arguments that should be invoked until
/// it's sent to the server.
impl<'a> ScriptInvocation<'a> {

    /// Adds a regular argument to the invocation.  This ends up as `ARGV[i]`
    /// in the script.
    #[inline]
    pub fn arg<T: ToRedisArgs>(&'a mut self, arg: T) -> &'a mut ScriptInvocation {
        self.args.extend(arg.to_redis_args().into_iter());
        self
    }

    /// Adds a key argument to the invocation.  This ends up as `KEYS[i]`
    /// in the script.
    #[inline]
    pub fn key<T: ToRedisArgs>(&'a mut self, key: T) -> &'a mut ScriptInvocation {
        self.keys.extend(key.to_redis_args().into_iter());
        self
    }

    /// Invokes the script and returns the result.
    #[inline]
    pub fn invoke<T: FromRedisValue>(&self, con: &ConnectionLike) -> RedisResult<T> {
        loop {
            match cmd("EVALSHA")
                .arg(self.script.hash.as_bytes())
                .arg(self.keys.len())
                .arg(&*self.keys)
                .arg(&*self.args).query(con) {
                Ok(val) => { return Ok(val); }
                Err(err) => {
                    if err.kind() == ErrorKind::NoScriptError {
                        let _ : () = try!(cmd("SCRIPT")
                            .arg("LOAD")
                            .arg(self.script.code.as_bytes())
                            .query(con));
                    } else {
                        fail!(err);
                    }
                }
            }
        }
    }
}
<commit_msg>Relax ScriptInvocation lifetimes<commit_after>use sha1::Sha1;

use cmd::cmd;
use types::{ToRedisArgs, FromRedisValue, RedisResult, ErrorKind};
use connection::ConnectionLike;

/// Represents a lua script.
pub struct Script {
    code: String,
    hash: String,
}

/// The script object represents a lua script that can be executed on the
/// redis server.  The object itself takes care of automatic uploading and
/// execution.  The script object itself can be shared and is immutable.
///
/// Example:
///
/// ```rust,no_run
/// # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
/// # let con = client.get_connection().unwrap();
/// let script = redis::Script::new(r"
///     return tonumber(ARGV[1]) + tonumber(ARGV[2]);
/// ");
/// let result = script.arg(1).arg(2).invoke(&con);
/// assert_eq!(result, Ok(3));
/// ```
impl Script {

    /// Creates a new script object.
    pub fn new(code: &str) -> Script {
        let mut hash = Sha1::new();
        hash.update(code.as_bytes());
        Script {
            code: code.to_string(),
            hash: hash.digest().to_string(),
        }
    }

    /// Returns the script's SHA1 hash in hexadecimal format.
    pub fn get_hash(&self) -> &str {
        &self.hash
    }

    /// Creates a script invocation object with a key filled in.
    #[inline]
    pub fn key<T: ToRedisArgs>(&self, key: T) -> ScriptInvocation {
        ScriptInvocation {
            script: self,
            args: vec![],
            keys: key.to_redis_args(),
        }
    }

    /// Creates a script invocation object with an argument filled in.
    #[inline]
    pub fn arg<T: ToRedisArgs>(&self, arg: T) -> ScriptInvocation {
        ScriptInvocation {
            script: self,
            args: arg.to_redis_args(),
            keys: vec![],
        }
    }

    /// Returns an empty script invocation object.  This is primarily useful
    /// for programmatically adding arguments and keys because the type will
    /// not change.  Normally you can use `arg` and `key` directly.
    #[inline]
    pub fn prepare_invoke(&self) -> ScriptInvocation {
        ScriptInvocation { script: self, args: vec![], keys: vec![] }
    }

    /// Invokes the script directly without arguments.
    #[inline]
    pub fn invoke<T: FromRedisValue>(&self, con: &ConnectionLike) -> RedisResult<T> {
        ScriptInvocation {
            script: self,
            args: vec![],
            keys: vec![],
        }.invoke(con)
    }
}

/// Represents a prepared script call.
pub struct ScriptInvocation<'a> {
    script: &'a Script,
    args: Vec<Vec<u8>>,
    keys: Vec<Vec<u8>>,
}

/// This type collects keys and other arguments for the script so that it
/// can be then invoked.  While the `Script` type itself holds the script,
/// the `ScriptInvocation` holds the arguments that should be invoked until
/// it's sent to the server.
impl<'a> ScriptInvocation<'a> {

    /// Adds a regular argument to the invocation.  This ends up as `ARGV[i]`
    /// in the script.
    #[inline]
    pub fn arg<'b, T: ToRedisArgs>(&'b mut self, arg: T) -> &'b mut ScriptInvocation<'a>
        where 'a: 'b
    {
        self.args.extend(arg.to_redis_args().into_iter());
        self
    }

    /// Adds a key argument to the invocation.  This ends up as `KEYS[i]`
    /// in the script.
    #[inline]
    pub fn key<'b, T: ToRedisArgs>(&'b mut self, key: T) -> &'b mut ScriptInvocation<'a>
        where 'a: 'b
    {
        self.keys.extend(key.to_redis_args().into_iter());
        self
    }

    /// Invokes the script and returns the result.
    #[inline]
    pub fn invoke<T: FromRedisValue>(&self, con: &ConnectionLike) -> RedisResult<T> {
        loop {
            match cmd("EVALSHA")
                .arg(self.script.hash.as_bytes())
                .arg(self.keys.len())
                .arg(&*self.keys)
                .arg(&*self.args).query(con) {
                Ok(val) => { return Ok(val); }
                Err(err) => {
                    if err.kind() == ErrorKind::NoScriptError {
                        let _ : () = try!(cmd("SCRIPT")
                            .arg("LOAD")
                            .arg(self.script.code.as_bytes())
                            .query(con));
                    } else {
                        fail!(err);
                    }
                }
            }
        }
    }
}
<|endoftext|>