<commit_before>#![feature(question_mark)]

#[macro_use]
extern crate log;
extern crate pulldown_cmark;
extern crate serde_json;
extern crate tera;
extern crate walkdir;

mod post;
mod theme;
mod utils;

use std::collections::BTreeMap;
use std::error::Error;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::rc::Rc;

use serde_json::Map;
use tera::{Tera, Context};
use walkdir::{DirEntry, WalkDir, WalkDirIterator};

use post::Post;
use theme::Theme;
use utils::{create_error, create_file};

pub struct Mdblog {
    root: PathBuf,
    theme: Theme,
    posts: Vec<Rc<Post>>,
    tags: BTreeMap<String, Vec<Rc<Post>>>,
    renderer: Option<Tera>,
}

impl Mdblog {
    pub fn new<P: AsRef<Path>>(root: P) -> Mdblog {
        Mdblog {
            root: root.as_ref().to_owned(),
            theme: Theme::new(&root),
            posts: Vec::new(),
            tags: BTreeMap::new(),
            renderer: None,
        }
    }

    pub fn init(&self) -> ::std::io::Result<()> {
        if self.root.exists() {
            return create_error(format!("{root} directory already existed.", root=self.root.display()));
        }

        let mut hello_post = create_file(&self.root.join("posts").join("hello.md"))?;
        hello_post.write_all(b"date: 1970-01-01\n")?;
        hello_post.write_all(b"tags: hello, world\n")?;
        hello_post.write_all(b"\n")?;
        hello_post.write_all(b"# hello\n\nhello world!\n")?;

        let mut config = create_file(&self.root.join("config.toml"))?;
        config.write_all(b"[blog]\ntheme = simple\n")?;

        let mut theme = Theme::new(&self.root);
        theme.load("simple")?;
        theme.init_dir()?;
        Ok(())
    }

    pub fn build(&mut self, theme: &str) -> ::std::io::Result<()> {
        self.load_theme(&theme)?;
        self.load_posts()?;
        self.export()?;
        Ok(())
    }

    pub fn server(&self, port: u16) {
        println!("server blog at localhost:{}", port);
    }

    pub fn load_theme(&mut self, theme: &str) -> ::std::io::Result<()> {
        self.theme.load(theme)?;
        let template_dir = self.root.join("_themes").join(&theme).join("templates");
        debug!("template dir: {}", template_dir.display());
        self.renderer = Some(Tera::new(&format!("{}/*", template_dir.display())));
        Ok(())
    }

    pub fn load_posts(&mut self) -> ::std::io::Result<()> {
        let posts_dir = self.root.join("posts");
        let walker = WalkDir::new(&posts_dir).into_iter();
        for entry in walker.filter_entry(|e| !is_hidden(e)) {
            let entry = entry.expect("get walker entry error");
            if !is_markdown_file(&entry) {
                continue;
            }
            let mut post = Post::new(&self.root,
                                     &entry.path()
                                           .strip_prefix(&self.root)
                                           .expect("create post path error")
                                           .to_owned());
            post.load()?;
            let post = Rc::new(post);
            for tag in post.tags() {
                let mut ps = self.tags.entry(tag.to_string()).or_insert(Vec::new());
                ps.push(post.clone());
            }
            self.posts.push(post.clone());
        }
        self.posts.sort_by(|p1, p2| p2.datetime().cmp(&p1.datetime()));
        for (_, tag_posts) in self.tags.iter_mut() {
            tag_posts.sort_by(|p1, p2| p2.datetime().cmp(&p1.datetime()));
        }
        debug!("Tags: {:?}", self.tags.keys().collect::<Vec<&String>>());
        Ok(())
    }

    pub fn export(&self) -> ::std::io::Result<()> {
        self.export_static()?;
        self.export_posts()?;
        self.export_index()?;
        self.export_tags()?;
        Ok(())
    }

    pub fn export_static(&self) -> ::std::io::Result<()> {
        self.theme.export_static()
    }

    pub fn export_posts(&self) -> ::std::io::Result<()> {
        for post in &self.posts {
            let dest = post.dest();
            let mut f = create_file(&dest)?;
            match self.render_post(post) {
                Ok(s) => {
                    f.write(s.as_bytes())?;
                    debug!("created html: {:?}", dest.display());
                },
                Err(e) => {
                    return Err(e);
                }
            }
        }
        Ok(())
    }

    pub fn export_index(&self) -> ::std::io::Result<()> {
        let dest = self.root.join("_builds/index.html");
        let mut f = create_file(&dest)?;
        match self.render_index() {
            Ok(s) => {
                f.write(s.as_bytes())?;
                debug!("created html: {:?}", dest.display());
            },
            Err(e) => {
                return Err(e);
            }
        }
    Ok(())
    }

    pub fn export_tags(&self) -> ::std::io::Result<()> {
        for tag in self.tags.keys() {
            let dest = self.root.join(format!("_builds/blog/tags/{}.html", tag));
            let mut f = create_file(&dest)?;
            match self.render_tag(tag) {
                Ok(s) => {
                    f.write(s.as_bytes())?;
                    debug!("created html: {:?}", dest.display());
                },
                Err(e) => {
                    return Err(e);
                }
            }
        }
        Ok(())
    }

    fn tag_url(&self, name: &str) -> String {
        format!("/blog/tags/{}.html", &name)
    }

    fn tag_map<T>(&self, name:&str, posts: &Vec<T>) -> Map<&str, String> {
        let mut map = Map::new();
        map.insert("name", name.to_string());
        let tag_len = format!("{:?}", &posts.len());
        map.insert("num", tag_len);
        map.insert("url", self.tag_url(&name));
        map
    }

    pub fn base_context(&self, title: &str) -> Context {
        let mut context = Context::new();
        context.add("title", &title);
        let mut all_tags = Vec::new();
        for (tag_key, tag_posts) in &self.tags {
            all_tags.push(self.tag_map(&tag_key, &tag_posts));
        }
        context.add("all_tags", &all_tags);

        context
    }

    pub fn render_post(&self, post: &Post) -> ::std::io::Result<String> {
        debug!("rendering post: {}", post.path.display());
        let tera = self.renderer.as_ref().expect("get renderer error");
        let mut context = self.base_context(&post.title());
        context.add("content", &post.content());
        context.add("datetime", &post.datetime().to_string());
        let mut post_tags = Vec::new();
        for tag_key in post.tags() {
            let tag_posts = self.tags.get(tag_key).expect(&format!("post tag({}) does not add to blog tags", tag_key));
            post_tags.push(self.tag_map(&tag_key, &tag_posts));
        }
        context.add("post_tags", &post_tags);

        match tera.render("post.tpl", context) {
            Ok(s) => {
                return Ok(s);
            },
            Err(e) => {
                return create_error(format!("post({path}) render error: {descrition}",
                                            path=post.path.display(),
                                            descrition=e.description()));
            }
        }
    }

    pub fn render_index(&self) -> ::std::io::Result<String> {
        debug!("rendering index");
        let tera = self.renderer.as_ref().expect("get renderer error");
        let mut context = self.base_context("Fu");
        context.add("posts", &self.posts_maps(&self.posts));
        match tera.render("index.tpl", context) {
            Ok(s) => {
                return Ok(s);
            },
            Err(e) => {
                return create_error(format!("index render error: {}", descrition=e.description()));
            }
        }
    }

    fn posts_maps<'a>(&self, posts: &'a Vec<Rc<Post>>) -> Vec<Map<&'a str, String>> {
        let mut maps = Vec::new();
        for post in posts {
            maps.push(post.map());
        }

        maps
    }

    pub fn render_tag(&self, tag:&str) -> ::std::io::Result<String> {
        debug!("rendering tag: {}", tag);
        let tera = self.renderer.as_ref().expect("get renderer error");
        let mut context = self.base_context(&tag);
        let posts = self.tags.get(tag).expect(&format!("get tag({}) error", &tag));
        context.add("posts", &self.posts_maps(&posts));
        match tera.render("tag.tpl", context) {
            Ok(s) => {
                return Ok(s);
            },
            Err(e) => {
                return create_error(format!("tag({tag}) render error: {descrition}",
                                            tag=tag,
                                            descrition=e.description()));
            }
        }
    }
}

fn is_hidden(entry: &DirEntry) -> bool {
    entry.file_name()
        .to_str()
        .map(|s| s.starts_with("."))
        .unwrap_or(false)
}

fn is_markdown_file(entry: &DirEntry) -> bool {
    if !entry.path().is_file() {
        return false;
    }
    let fname = entry.file_name().to_str();
    match fname {
        None => {
            return false;
        },
        Some(s) => {
            if s.starts_with(|c| (c == '.') | (c == '~')) {
                return false;
            } else if s.ends_with(".md") {
                return true;
            } else {
                return false;
            }
        }
    }
}
<commit_msg>add media dir<commit_after>#![feature(question_mark)]

#[macro_use]
extern crate log;
extern crate pulldown_cmark;
extern crate serde_json;
extern crate tera;
extern crate walkdir;

mod post;
mod theme;
mod utils;

use std::collections::BTreeMap;
use std::error::Error;
use std::fs;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::rc::Rc;

use serde_json::Map;
use tera::{Tera, Context};
use walkdir::{DirEntry, WalkDir, WalkDirIterator};

use post::Post;
use theme::Theme;
use utils::{create_error, create_file};

pub struct Mdblog {
    root: PathBuf,
    theme: Theme,
    posts: Vec<Rc<Post>>,
    tags: BTreeMap<String, Vec<Rc<Post>>>,
    renderer: Option<Tera>,
}

impl Mdblog {
    pub fn new<P: AsRef<Path>>(root: P) -> Mdblog {
        Mdblog {
            root: root.as_ref().to_owned(),
            theme: Theme::new(&root),
            posts: Vec::new(),
            tags: BTreeMap::new(),
            renderer: None,
        }
    }

    pub fn init(&self) -> ::std::io::Result<()> {
        if self.root.exists() {
            return create_error(format!("{root} directory already existed.", root=self.root.display()));
        }

        let mut hello_post = create_file(&self.root.join("posts").join("hello.md"))?;
        hello_post.write_all(b"date: 1970-01-01\n")?;
        hello_post.write_all(b"tags: hello, world\n")?;
        hello_post.write_all(b"\n")?;
        hello_post.write_all(b"# hello\n\nhello world!\n")?;

        let mut config = create_file(&self.root.join("config.toml"))?;
        config.write_all(b"[blog]\ntheme = simple\n")?;

        let mut theme = Theme::new(&self.root);
        theme.load("simple")?;
        theme.init_dir()?;

        fs::create_dir_all(self.root.join("media"))?;

        Ok(())
    }

    pub fn build(&mut self, theme: &str) -> ::std::io::Result<()> {
        self.load_theme(&theme)?;
        self.load_posts()?;
        self.export()?;
        Ok(())
    }

    pub fn server(&self, port: u16) {
        println!("server blog at localhost:{}", port);
    }

    pub fn load_theme(&mut self, theme: &str) -> ::std::io::Result<()> {
        self.theme.load(theme)?;
        let template_dir = self.root.join("_themes").join(&theme).join("templates");
        debug!("template dir: {}", template_dir.display());
        self.renderer = Some(Tera::new(&format!("{}/*", template_dir.display())));
        Ok(())
    }

    pub fn load_posts(&mut self) -> ::std::io::Result<()> {
        let posts_dir = self.root.join("posts");
        let walker = WalkDir::new(&posts_dir).into_iter();
        for entry in walker.filter_entry(|e| !is_hidden(e)) {
            let entry = entry.expect("get walker entry error");
            if !is_markdown_file(&entry) {
                continue;
            }
            let mut post = Post::new(&self.root,
                                     &entry.path()
                                           .strip_prefix(&self.root)
                                           .expect("create post path error")
                                           .to_owned());
            post.load()?;
            let post = Rc::new(post);
            for tag in post.tags() {
                let mut ps = self.tags.entry(tag.to_string()).or_insert(Vec::new());
                ps.push(post.clone());
            }
            self.posts.push(post.clone());
        }
        self.posts.sort_by(|p1, p2| p2.datetime().cmp(&p1.datetime()));
        for (_, tag_posts) in self.tags.iter_mut() {
            tag_posts.sort_by(|p1, p2| p2.datetime().cmp(&p1.datetime()));
        }
        debug!("Tags: {:?}", self.tags.keys().collect::<Vec<&String>>());
        Ok(())
    }

    pub fn export(&self) -> ::std::io::Result<()> {
        self.export_media()?;
        self.export_static()?;
        self.export_posts()?;
        self.export_index()?;
        self.export_tags()?;
        Ok(())
    }

    pub fn media_dest<P: AsRef<Path>>(&self, media: P) -> PathBuf {
        let relpath = media.as_ref().strip_prefix(&self.root.join("media")).expect("create post path error").to_owned();
        self.root.join("_builds/media").join(relpath)
    }

    pub fn export_media(&self) -> ::std::io::Result<()> {
        let walker = WalkDir::new(&self.root.join("media")).into_iter();
        for entry in walker.filter_entry(|e| !is_hidden(e)) {
            let entry = entry.expect("get walker entry error");
            let src_path = entry.path();
            if src_path.is_dir() {
                fs::create_dir_all(self.media_dest(src_path))?;
                continue;
            }
            fs::copy(src_path, self.media_dest(src_path))?;
        }
        Ok(())
    }

    pub fn export_static(&self) -> ::std::io::Result<()> {
        self.theme.export_static()
    }

    pub fn export_posts(&self) -> ::std::io::Result<()> {
        for post in &self.posts {
            let dest = post.dest();
            let mut f = create_file(&dest)?;
            match self.render_post(post) {
                Ok(s) => {
                    f.write(s.as_bytes())?;
                    debug!("created html: {:?}", dest.display());
                },
                Err(e) => {
                    return Err(e);
                }
            }
        }
        Ok(())
    }

    pub fn export_index(&self) -> ::std::io::Result<()> {
        let dest = self.root.join("_builds/index.html");
        let mut f = create_file(&dest)?;
        match self.render_index() {
            Ok(s) => {
                f.write(s.as_bytes())?;
                debug!("created html: {:?}", dest.display());
            },
            Err(e) => {
                return Err(e);
            }
        }
    Ok(())
    }

    pub fn export_tags(&self) -> ::std::io::Result<()> {
        for tag in self.tags.keys() {
            let dest = self.root.join(format!("_builds/blog/tags/{}.html", tag));
            let mut f = create_file(&dest)?;
            match self.render_tag(tag) {
                Ok(s) => {
                    f.write(s.as_bytes())?;
                    debug!("created html: {:?}", dest.display());
                },
                Err(e) => {
                    return Err(e);
                }
            }
        }
        Ok(())
    }

    fn tag_url(&self, name: &str) -> String {
        format!("/blog/tags/{}.html", &name)
    }

    fn tag_map<T>(&self, name:&str, posts: &Vec<T>) -> Map<&str, String> {
        let mut map = Map::new();
        map.insert("name", name.to_string());
        let tag_len = format!("{:?}", &posts.len());
        map.insert("num", tag_len);
        map.insert("url", self.tag_url(&name));
        map
    }

    pub fn base_context(&self, title: &str) -> Context {
        let mut context = Context::new();
        context.add("title", &title);
        let mut all_tags = Vec::new();
        for (tag_key, tag_posts) in &self.tags {
            all_tags.push(self.tag_map(&tag_key, &tag_posts));
        }
        context.add("all_tags", &all_tags);

        context
    }

    pub fn render_post(&self, post: &Post) -> ::std::io::Result<String> {
        debug!("rendering post: {}", post.path.display());
        let tera = self.renderer.as_ref().expect("get renderer error");
        let mut context = self.base_context(&post.title());
        context.add("content", &post.content());
        context.add("datetime", &post.datetime().to_string());
        let mut post_tags = Vec::new();
        for tag_key in post.tags() {
            let tag_posts = self.tags.get(tag_key).expect(&format!("post tag({}) does not add to blog tags", tag_key));
            post_tags.push(self.tag_map(&tag_key, &tag_posts));
        }
        context.add("post_tags", &post_tags);

        match tera.render("post.tpl", context) {
            Ok(s) => {
                return Ok(s);
            },
            Err(e) => {
                return create_error(format!("post({path}) render error: {descrition}",
                                            path=post.path.display(),
                                            descrition=e.description()));
            }
        }
    }

    pub fn render_index(&self) -> ::std::io::Result<String> {
        debug!("rendering index");
        let tera = self.renderer.as_ref().expect("get renderer error");
        let mut context = self.base_context("Fu");
        context.add("posts", &self.posts_maps(&self.posts));
        match tera.render("index.tpl", context) {
            Ok(s) => {
                return Ok(s);
            },
            Err(e) => {
                return create_error(format!("index render error: {}", descrition=e.description()));
            }
        }
    }

    fn posts_maps<'a>(&self, posts: &'a Vec<Rc<Post>>) -> Vec<Map<&'a str, String>> {
        let mut maps = Vec::new();
        for post in posts {
            maps.push(post.map());
        }

        maps
    }

    pub fn render_tag(&self, tag:&str) -> ::std::io::Result<String> {
        debug!("rendering tag: {}", tag);
        let tera = self.renderer.as_ref().expect("get renderer error");
        let mut context = self.base_context(&tag);
        let posts = self.tags.get(tag).expect(&format!("get tag({}) error", &tag));
        context.add("posts", &self.posts_maps(&posts));
        match tera.render("tag.tpl", context) {
            Ok(s) => {
                return Ok(s);
            },
            Err(e) => {
                return create_error(format!("tag({tag}) render error: {descrition}",
                                            tag=tag,
                                            descrition=e.description()));
            }
        }
    }
}

fn is_hidden(entry: &DirEntry) -> bool {
    entry.file_name()
        .to_str()
        .map(|s| s.starts_with("."))
        .unwrap_or(false)
}

fn is_markdown_file(entry: &DirEntry) -> bool {
    if !entry.path().is_file() {
        return false;
    }
    let fname = entry.file_name().to_str();
    match fname {
        None => {
            return false;
        },
        Some(s) => {
            if s.starts_with(|c| (c == '.') | (c == '~')) {
                return false;
            } else if s.ends_with(".md") {
                return true;
            } else {
                return false;
            }
        }
    }
}
<|endoftext|>


<commit_before>#[test]
fn it_works() {
}
<commit_msg>Begin implementing<commit_after>
pub trait BaseTime {
  pub const Self MIN;
  pub const Self MAX;
  pub const Self NEVER;
}
impl BaseTime for i64 {

}

pub trait Field {
  type Data = Self;
  const unique_identifier: u64;
}

struct ExtendedTime <Base: BaseTime> {
  base: Base,
  iteration: u64,
  id: SiphashID,
}

pub trait Basics {
  type Time: BaseTime;
  type Constants;
}

pub trait Accessor {
  fn get <Type: Field> (&mut self, id: SiphashID)->& Option <Field::Data>;
}

pub trait Mutator <B: Basics>: Accessor {
  fn get_mutable <Type: Field> (&mut self, id: SiphashID)->& mut Option <Field::Data>;
  fn set <Type: Field> (&mut self, id: SiphashID, Option <Field::Data> data);
  fn random_bits (&mut self, bits:u32)->u64;
  fn random_id (&mut self)->SiphashID;
  fn now (& self)->B::Time;
  fn constants (& self)->& B::Constants;
}
pub trait PredictorAccessor <B: Basics>: Accessor {

}

pub type Event =Box <Fn (&mut Mutator)>;
enum Prediction <Time: BaseTime> {
  Nothing,
  Immediately (Event),
  At (Time, Event),
}

pub trait TimeSteward <B: Basics> {
  pub type A: Accessor;
  pub type M: Mutator;
  pub type P: PredictorAccessor;
  pub type Predictor = Box <Fn(&mut P)->Prediction>;
  fn new (constants: B::Constants, predictors: Vec<Predictor>)->Self;
}

pub trait FlatTimeSteward <B: Basics>:TimeSteward <B> {

}

 #[test]
fn it_works() {
}
<|endoftext|>


<commit_before>// Copyright 2017 Daniel P. Clark & other digits Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
//
// #![deny(missing_docs,trivial_casts,trivial_numeric_casts,
//         missing_debug_implementations, missing_copy_implementations,
//         unsafe_code,unstable_features,unused_import_braces,unused_qualifications)
// ]
extern crate base_custom;
use base_custom::BaseCustom;

#[derive(Debug,Clone)]
pub struct Digits<'a> {
  mapping: &'a BaseCustom<char>,
  digit: u64,
  left: Option<Box<Digits<'a>>>,
}

impl<'a> Digits<'a> {
  pub fn new<S>(mapping: &'a BaseCustom<char>, number: S) -> Digits<'a>
  where S: Into<String> {
    let number = number.into();
    if number.is_empty() { return Digits { mapping: mapping, digit: 0, left: None }; };
    let (last, rest) = Digits::last_rest(number);
    let continuation = {
      if rest.is_empty() {
        None
      } else {
        Some(Box::new(Digits::new(&mapping, rest)))
      }
    };
    Digits {
      mapping: mapping,
      digit: mapping.decimal(last.to_string()),
      left: continuation,
    }
  }

  pub fn to_s(&self) -> String {
    let num = self.mapping.gen(self.digit);
    match &self.left {
      &None => format!("{}", num),
      &Some(ref bx) => format!("{}{}", bx.to_s(), num),
    }
  }

  pub fn to_string(&self) -> String {
    self.to_s()
  }

  fn last_rest(s: String) -> (char, String) {
    let mut n = s.chars().rev();
    let last = n.next().unwrap();
    let rest = n.rev().collect::<String>();
    (last, rest)
  }

  fn set_left(&mut self, d: Digits<'a>) {
    if d.is_end() {
      self.left = None;
    } else {
      self.left = Some(Box::new(d));
    }
  }

  pub fn zero(&self) -> Self {
    Digits { mapping: self.mapping, digit: 0, left: None }
  }

  fn is_end(&self) -> bool {
    self.digit == 0 && match self.left { None => true, _ => false }
  }

  pub fn add(&mut self, other: Self) -> Self {
    if other.is_end() { return self.clone(); };
    let (last, rest) = Digits::last_rest(other.to_s());

    // sums current single digit
    let added = self.mapping.gen(self.mapping.decimal(last.to_string()) + self.digit);
    let (l, r) = Digits::last_rest(added);
    self.digit = self.mapping.decimal(l.to_string());

    // sums for left
    let mut intermediate = Digits::new(self.mapping, r);
    intermediate.add(Digits::new(self.mapping, rest));

    let current_left = match self.left.clone() {
      None => { Box::new(self.zero()) },
      Some(bx) => { bx },
    }.as_ref().clone();

    self.set_left( intermediate.add(current_left).clone() );
    self.clone()
  }
}

pub trait Into<String> {
  fn into(self) -> String;
}

impl<'a> From<Digits<'a>> for String {
  fn from(d: Digits) -> String {
    d.to_s()
  }
}

impl<'a> Into<String> for Digits<'a> {
  fn into(self) -> String {
    self.to_s()
  }
}

impl Into<String> for String {
  fn into(self) -> String {
    self.clone()
  }
}

<commit_msg>Some documentation<commit_after>// Copyright 2017 Daniel P. Clark & other digits Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
//
#![deny(missing_docs,trivial_casts,trivial_numeric_casts,
        missing_debug_implementations, missing_copy_implementations,
        unsafe_code,unstable_features,unused_import_braces,unused_qualifications)
]
//! # digits
//!
//! The digits crate is a linked list implementation of a score card flipper.  But
//! in this case it's with any characters you want and you can enumerate through
//! possibilities beyond the numeric limits intrinsic in basic numerc types like `u64`.
//!
//! Primary use case would be brute forcing character sequences.
extern crate base_custom;
use base_custom::BaseCustom;

/// # struct Digits
///
/// This struct acts similar to a full number with a custom numeric character base.
/// But the underlying implementation is a linked list where all the methods recurse
/// as far as need to to implement the operations.
#[derive(Debug,Clone)]
pub struct Digits<'a> {
  mapping: &'a BaseCustom<char>,
  digit: u64,
  left: Option<Box<Digits<'a>>>,
}

impl<'a> Digits<'a> {
  /// `new` creates a new Digits instance with the provided character set and value.
  ///
  /// The first parameter must be a BaseCustom object which defines and maps all values.
  /// The second parameter is a string value with all valid characters from the BaseCustom set.
  ///
  /// You can view the numbers current value at any time with `to_s()` or `to_string()`.
  pub fn new<S>(mapping: &'a BaseCustom<char>, number: S) -> Digits<'a>
  where S: Into<String> {
    let number = number.into();
    if number.is_empty() { return Digits { mapping: mapping, digit: 0, left: None }; };
    let (last, rest) = Digits::last_rest(number);
    let continuation = {
      if rest.is_empty() {
        None
      } else {
        Some(Box::new(Digits::new(&mapping, rest)))
      }
    };
    Digits {
      mapping: mapping,
      digit: mapping.decimal(last.to_string()),
      left: continuation,
    }
  }

  /// Gives the full value of all digits within the linked list as a String.
  pub fn to_s(&self) -> String {
    let num = self.mapping.gen(self.digit);
    match &self.left {
      &None => format!("{}", num),
      &Some(ref bx) => format!("{}{}", bx.to_s(), num),
    }
  }

  /// Gives the full value of all digits within the linked list as a String.
  pub fn to_string(&self) -> String {
    self.to_s()
  }

  fn last_rest(s: String) -> (char, String) {
    let mut n = s.chars().rev();
    let last = n.next().unwrap();
    let rest = n.rev().collect::<String>();
    (last, rest)
  }

  fn set_left(&mut self, d: Digits<'a>) {
    if d.is_end() {
      self.left = None;
    } else {
      self.left = Some(Box::new(d));
    }
  }

  /// `zero` returns a Digits instance with value of zero and the current character mapping.
  pub fn zero(&self) -> Self {
    Digits { mapping: self.mapping, digit: 0, left: None }
  }

  fn is_end(&self) -> bool {
    self.digit == 0 && match self.left { None => true, _ => false }
  }

  /// Add two Digits instances together.  The one the `add` method is called on
  /// must be mutable and modifies itself.  The other is consumed.
  ///
  /// Returns a clone of the updated `Self` as well.
  pub fn add(&mut self, other: Self) -> Self {
    if other.is_end() { return self.clone(); };
    let (last, rest) = Digits::last_rest(other.to_s());

    // sums current single digit
    let added = self.mapping.gen(self.mapping.decimal(last.to_string()) + self.digit);
    let (l, r) = Digits::last_rest(added);
    self.digit = self.mapping.decimal(l.to_string());

    // sums for left
    let mut intermediate = Digits::new(self.mapping, r);
    intermediate.add(Digits::new(self.mapping, rest));

    let current_left = match self.left.clone() {
      None => { Box::new(self.zero()) },
      Some(bx) => { bx },
    }.as_ref().clone();

    self.set_left( intermediate.add(current_left).clone() );
    self.clone()
  }
}

/// NODOC
pub trait Into<String> {
  /// NODOC
  fn into(self) -> String;
}

impl<'a> From<Digits<'a>> for String {
  fn from(d: Digits) -> String {
    d.to_s()
  }
}

impl<'a> Into<String> for Digits<'a> {
  fn into(self) -> String {
    self.to_s()
  }
}

impl Into<String> for String {
  fn into(self) -> String {
    self.clone()
  }
}

<|endoftext|>


<commit_before>extern crate hyper;
extern crate url;
extern crate serde;
extern crate serde_json;

#[macro_use]
extern crate serde_derive;

use std::io::Read;
use std::fmt;

#[derive(Serialize, Deserialize, Debug, Clone)]
//#[serde(deny_unknown_fields)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub struct JournalEntry {
    #[serde(rename = "__CURSOR")]
    pub cursor: String,
    #[serde(rename = "__REALTIME_TIMESTAMP")]
    pub realtime_timestamp: String,
    #[serde(rename = "__MONOTONIC_TIMESTAMP")]
    pub monotonic_timestamp: String,
    #[serde(rename = "_BOOT_ID")]
    pub boot_id: String,
    #[serde(rename = "_MACHINE_ID")]
    pub machine_id: String,
    #[serde(rename = "_HOSTNAME")]
    pub hostname: String,
    pub priority: Option<String>,
    #[serde(rename = "_UID")]
    pub uid: Option<String>,
    #[serde(rename = "_GID")]
    pub gid: Option<String>,
    #[serde(rename = "_SYSTEMD_SLICE")]
    pub systemd_slice: Option<String>,
    #[serde(rename = "_CAP_EFFECTIVE")]
    pub cap_effective: Option<String>,
    #[serde(rename = "_TRANSPORT")]
    pub transport: String,
    #[serde(rename = "_COMM")]
    pub command: Option<String>,
    #[serde(rename = "_EXE")]
    pub executable: Option<String>,
    #[serde(rename = "_SYSTEMD_CGROUP")]
    pub systemd_cgroup: Option<String>,
    #[serde(rename = "_SYSTEMD_UNIT")]
    pub systemd_unit: Option<String>,
    #[serde(rename = "_SYSTEMD_INVOCATION_ID")]
    pub systemd_invocation_id: Option<String>,
    #[serde(rename = "_SYSTEMD_OWNER_UID")]
    pub systemd_owner_uid: Option<String>,
    #[serde(rename = "_SYSTEMD_USER_SLICE")]
    pub systemd_user_slice: Option<String>,
    #[serde(rename = "_SYSTEMD_USER_UNIT")]
    pub systemd_user_unit: Option<String>,
    #[serde(rename = "_SYSTEMD_SESSION")]
    pub systemd_session: Option<String>,
    pub syslog_facility: Option<String>,
    pub syslog_identifier: Option<String>,
    #[serde(rename = "_CMDLINE")]
    pub command_line: Option<String>,
    #[serde(rename = "_AUDIT_LOGINUID")]
    pub audit_login_uid: Option<String>,
    pub message: String,
    pub code_file: Option<String>,
    pub code_line: Option<String>,
    pub code_function: Option<String>,
    pub unit: Option<String>,
    pub user_unit: Option<String>,
    pub userspace_usec: Option<String>,
    pub kernel_usec: Option<String>,
    pub message_id: Option<String>,
    pub result: Option<String>,
    pub user_id: Option<String>,
    pub seat_id: Option<String>,
    pub leader: Option<String>,
    pub journal_name: Option<String>,
    pub journal_path: Option<String>,
    pub current_use: Option<String>,
    pub current_use_pretty: Option<String>,
    pub max_use: Option<String>,
    pub max_use_pretty: Option<String>,
    pub disk_keep_free: Option<String>,
    pub disk_keep_free_pretty: Option<String>,
    pub disk_available: Option<String>,
    pub disk_available_pretty: Option<String>,
    pub available: Option<String>,
    pub available_pretty: Option<String>,
    pub limit: Option<String>,
    pub limit_pretty: Option<String>,
    pub session_id: Option<String>,
    pub target: Option<String>,
    pub syslog_pid: Option<String>,
    #[serde(rename = "_PID")]
    pub pid: Option<String>,
    #[serde(rename = "_AUDIT_SESSION")]
    pub audit_session: Option<String>,
    #[serde(rename = "_AUDIT_TYPE")]
    pub audit_type: Option<String>,
    #[serde(rename = "_AUDIT_FIELD_NAME")]
    pub audit_field_name: Option<String>,
    #[serde(rename = "_AUDIT_FIELD_APPARMOR")]
    pub audit_field_apparmor: Option<String>,
    #[serde(rename = "_AUDIT_FIELD_OPERATION")]
    pub audit_field_operation: Option<String>,
    #[serde(rename = "_AUDIT_FIELD_PROFILE")]
    pub audit_field_profile: Option<String>,
    #[serde(rename = "_AUDIT_ID")]
    pub audit_id: Option<String>,
    #[serde(rename = "_KERNEL_SUBSYSTEM")]
    pub kernel_subsystem: Option<String>,
    #[serde(rename = "_KERNEL_DEVICE")]
    pub kernel_device: Option<String>,
    #[serde(rename = "_UDEV_SYSNAME")]
    pub udev_sysname: Option<String>,
    #[serde(rename = "_UDEV_DEVNODE")]
    pub udev_devnode: Option<String>,
    #[serde(rename = "_UDEV_DEVLINK")]
    pub udev_devlink: Option<String>,
    #[serde(rename = "_SOURCE_REALTIME_TIMESTAMP")]
    pub source_realtime_timestamp: Option<String>,
    #[serde(rename = "_SOURCE_MONOTONIC_TIMESTAMP")]
    pub source_monotonic_timestamp: Option<String>,
}

pub struct JournalGateway {
    baseurl: url::Url,
    client: hyper::Client,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PaginationParams {
    pub cursor: Option<String>,
    pub skip: Option<i32>,
    pub length: Option<u32>,
}

impl fmt::Display for PaginationParams {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut str_data = String::new();
        if let Some(ref cursor_str) = self.cursor {
            str_data = str_data + cursor_str;
        }
        if let Some(ref skip_int) = self.skip {
            str_data = format!("{}:{}", str_data, skip_int);
        }
        if let Some(ref length_int) = self.length {
            str_data = format!("{}:{}", str_data, length_int);
        }
        write!(f, "{}", str_data)
    }
}

impl JournalGateway {
    pub fn new(baseurl_str: &str) -> JournalGateway {
        let baseurl = url::Url::parse(baseurl_str).expect("Error during parsing baseurl");
        JournalGateway {
            baseurl: baseurl.to_owned(),
            client: hyper::Client::new(),
        }
    }

    pub fn get_all_entries(&self) -> Vec<JournalEntry> {
        self.get_entries(None, None)
    }

    pub fn get_first_entry(&self, filters: Option<&Vec<(String, String)>>) -> Option<JournalEntry> {
        let pagi = PaginationParams {
            cursor: None,
            skip: None,
            length: Some(1),
        };
        let list = self.get_entries(filters, Some(pagi));
        match list.is_empty() {
            true => None,
            false => Some(list[0].clone()),
        }
    }

    pub fn get_last_entry(&self, filters: Option<&Vec<(String, String)>>) -> Option<JournalEntry> {
        let pagi = PaginationParams {
            cursor: None,
            skip: Some(-1),
            length: Some(2),
        };
        let list = self.get_entries(filters, Some(pagi));
        match list.len() {
            0 => None,
            1 => Some(list[0].clone()),
            2 => Some(list[1].clone()),
            len => panic!("Requested 2 elements, but got {}", len),
        }
    }

    pub fn get_entries(&self,
                       filters: Option<&Vec<(String, String)>>,
                       pagination: Option<PaginationParams>)
                       -> Vec<JournalEntry> {

        let mut url = self.baseurl.join("entries").expect("url join failed");
        if let Some(filters_unwrapped) = filters {
            url.query_pairs_mut().extend_pairs(filters_unwrapped);
        }

        let mut request = self.client
            .request(hyper::method::Method::Get, url.as_str())
            .header(hyper::header::Accept::json());

        if let Some(pagi) = pagination {
            request =
                request.header(hyper::header::Range::Unregistered("entries".to_string(),
                                                                  pagi.to_string()));
        }

        let mut response = request.send().expect("request failure");
        let mut body = String::new();
        response
            .read_to_string(&mut body)
            .expect("body read failed");

        let mut res: Vec<JournalEntry> = vec![];
        for line in body.split("\n") {
            if line.len() > 0 {
                let deser_res = serde_json::from_str(&line);
                match deser_res {
                    Ok(entry) => res.push(entry),
                    Err(e) => println!("Error {}, skipping line: {}", e, line),
                }
            }
        }

        res
    }
}

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

    #[test]
    fn get_entries() {

        // TODO make this into a self contained test

        let journal_gw = JournalGateway::new("http://192.168.33.19:19531");
        //let res = journal_gw.get_all_entries();

        let filter = vec![("SYSLOG_IDENTIFIER".to_string(), "wash-manager".to_string())];

        let res: Vec<JournalEntry> = journal_gw.get_entries(Some(&filter), None);
        println!("Received {} entries", res.len());
        for entry in res {
            println!("{}: {}",
                     entry.syslog_identifier.unwrap_or("N/A".to_string()),
                     entry.message);
        }
        println!("First entry: {:?}\n",
                 journal_gw.get_first_entry(Some(&filter)));
        println!("Last entry: {:?}\n",
                 journal_gw.get_last_entry(Some(&filter)));
    }

    // TODO create another test with a filter, e.g.
    // let res = journal_gw.get_entries(Some(vec![("_COMM".to_string(), "cat".to_string())]));
}
<commit_msg>better error message for deserialization errors<commit_after>extern crate hyper;
extern crate url;
extern crate serde;
extern crate serde_json;

#[macro_use]
extern crate serde_derive;

use std::io::Read;
use std::fmt;

#[derive(Serialize, Deserialize, Debug, Clone)]
//#[serde(deny_unknown_fields)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub struct JournalEntry {
    #[serde(rename = "__CURSOR")]
    pub cursor: String,
    #[serde(rename = "__REALTIME_TIMESTAMP")]
    pub realtime_timestamp: String,
    #[serde(rename = "__MONOTONIC_TIMESTAMP")]
    pub monotonic_timestamp: String,
    #[serde(rename = "_BOOT_ID")]
    pub boot_id: String,
    #[serde(rename = "_MACHINE_ID")]
    pub machine_id: String,
    #[serde(rename = "_HOSTNAME")]
    pub hostname: String,
    pub priority: Option<String>,
    #[serde(rename = "_UID")]
    pub uid: Option<String>,
    #[serde(rename = "_GID")]
    pub gid: Option<String>,
    #[serde(rename = "_SYSTEMD_SLICE")]
    pub systemd_slice: Option<String>,
    #[serde(rename = "_CAP_EFFECTIVE")]
    pub cap_effective: Option<String>,
    #[serde(rename = "_TRANSPORT")]
    pub transport: String,
    #[serde(rename = "_COMM")]
    pub command: Option<String>,
    #[serde(rename = "_EXE")]
    pub executable: Option<String>,
    #[serde(rename = "_SYSTEMD_CGROUP")]
    pub systemd_cgroup: Option<String>,
    #[serde(rename = "_SYSTEMD_UNIT")]
    pub systemd_unit: Option<String>,
    #[serde(rename = "_SYSTEMD_INVOCATION_ID")]
    pub systemd_invocation_id: Option<String>,
    #[serde(rename = "_SYSTEMD_OWNER_UID")]
    pub systemd_owner_uid: Option<String>,
    #[serde(rename = "_SYSTEMD_USER_SLICE")]
    pub systemd_user_slice: Option<String>,
    #[serde(rename = "_SYSTEMD_USER_UNIT")]
    pub systemd_user_unit: Option<String>,
    #[serde(rename = "_SYSTEMD_SESSION")]
    pub systemd_session: Option<String>,
    pub syslog_facility: Option<String>,
    pub syslog_identifier: Option<String>,
    #[serde(rename = "_CMDLINE")]
    pub command_line: Option<String>,
    #[serde(rename = "_AUDIT_LOGINUID")]
    pub audit_login_uid: Option<String>,
    pub message: String,
    pub code_file: Option<String>,
    pub code_line: Option<String>,
    pub code_function: Option<String>,
    pub unit: Option<String>,
    pub user_unit: Option<String>,
    pub userspace_usec: Option<String>,
    pub kernel_usec: Option<String>,
    pub message_id: Option<String>,
    pub result: Option<String>,
    pub user_id: Option<String>,
    pub seat_id: Option<String>,
    pub leader: Option<String>,
    pub journal_name: Option<String>,
    pub journal_path: Option<String>,
    pub current_use: Option<String>,
    pub current_use_pretty: Option<String>,
    pub max_use: Option<String>,
    pub max_use_pretty: Option<String>,
    pub disk_keep_free: Option<String>,
    pub disk_keep_free_pretty: Option<String>,
    pub disk_available: Option<String>,
    pub disk_available_pretty: Option<String>,
    pub available: Option<String>,
    pub available_pretty: Option<String>,
    pub limit: Option<String>,
    pub limit_pretty: Option<String>,
    pub session_id: Option<String>,
    pub target: Option<String>,
    pub syslog_pid: Option<String>,
    #[serde(rename = "_PID")]
    pub pid: Option<String>,
    #[serde(rename = "_AUDIT_SESSION")]
    pub audit_session: Option<String>,
    #[serde(rename = "_AUDIT_TYPE")]
    pub audit_type: Option<String>,
    #[serde(rename = "_AUDIT_FIELD_NAME")]
    pub audit_field_name: Option<String>,
    #[serde(rename = "_AUDIT_FIELD_APPARMOR")]
    pub audit_field_apparmor: Option<String>,
    #[serde(rename = "_AUDIT_FIELD_OPERATION")]
    pub audit_field_operation: Option<String>,
    #[serde(rename = "_AUDIT_FIELD_PROFILE")]
    pub audit_field_profile: Option<String>,
    #[serde(rename = "_AUDIT_ID")]
    pub audit_id: Option<String>,
    #[serde(rename = "_KERNEL_SUBSYSTEM")]
    pub kernel_subsystem: Option<String>,
    #[serde(rename = "_KERNEL_DEVICE")]
    pub kernel_device: Option<String>,
    #[serde(rename = "_UDEV_SYSNAME")]
    pub udev_sysname: Option<String>,
    #[serde(rename = "_UDEV_DEVNODE")]
    pub udev_devnode: Option<String>,
    #[serde(rename = "_UDEV_DEVLINK")]
    pub udev_devlink: Option<String>,
    #[serde(rename = "_SOURCE_REALTIME_TIMESTAMP")]
    pub source_realtime_timestamp: Option<String>,
    #[serde(rename = "_SOURCE_MONOTONIC_TIMESTAMP")]
    pub source_monotonic_timestamp: Option<String>,
}

pub struct JournalGateway {
    baseurl: url::Url,
    client: hyper::Client,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PaginationParams {
    pub cursor: Option<String>,
    pub skip: Option<i32>,
    pub length: Option<u32>,
}

impl fmt::Display for PaginationParams {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut str_data = String::new();
        if let Some(ref cursor_str) = self.cursor {
            str_data = str_data + cursor_str;
        }
        if let Some(ref skip_int) = self.skip {
            str_data = format!("{}:{}", str_data, skip_int);
        }
        if let Some(ref length_int) = self.length {
            str_data = format!("{}:{}", str_data, length_int);
        }
        write!(f, "{}", str_data)
    }
}

impl JournalGateway {
    pub fn new(baseurl_str: &str) -> JournalGateway {
        let baseurl = url::Url::parse(baseurl_str).expect("Error during parsing baseurl");
        JournalGateway {
            baseurl: baseurl.to_owned(),
            client: hyper::Client::new(),
        }
    }

    pub fn get_all_entries(&self) -> Vec<JournalEntry> {
        self.get_entries(None, None)
    }

    pub fn get_first_entry(&self, filters: Option<&Vec<(String, String)>>) -> Option<JournalEntry> {
        let pagi = PaginationParams {
            cursor: None,
            skip: None,
            length: Some(1),
        };
        let list = self.get_entries(filters, Some(pagi));
        match list.is_empty() {
            true => None,
            false => Some(list[0].clone()),
        }
    }

    pub fn get_last_entry(&self, filters: Option<&Vec<(String, String)>>) -> Option<JournalEntry> {
        let pagi = PaginationParams {
            cursor: None,
            skip: Some(-1),
            length: Some(2),
        };
        let list = self.get_entries(filters, Some(pagi));
        match list.len() {
            0 => None,
            1 => Some(list[0].clone()),
            2 => Some(list[1].clone()),
            len => panic!("Requested 2 elements, but got {}", len),
        }
    }

    pub fn get_entries(&self,
                       filters: Option<&Vec<(String, String)>>,
                       pagination: Option<PaginationParams>)
                       -> Vec<JournalEntry> {

        let mut url = self.baseurl.join("entries").expect("url join failed");
        if let Some(filters_unwrapped) = filters {
            url.query_pairs_mut().extend_pairs(filters_unwrapped);
        }

        let mut request = self.client
            .request(hyper::method::Method::Get, url.as_str())
            .header(hyper::header::Accept::json());

        if let Some(pagi) = pagination {
            request =
                request.header(hyper::header::Range::Unregistered("entries".to_string(),
                                                                  pagi.to_string()));
        }

        let mut response = request.send().expect("request failure");
        let mut body = String::new();
        response
            .read_to_string(&mut body)
            .expect("body read failed");

        let mut res: Vec<JournalEntry> = vec![];
        for line in body.split("\n") {
            if line.len() > 0 {
                let deser_res = serde_json::from_str(&line);
                match deser_res {
                    Ok(entry) => res.push(entry),
                    Err(e) => println!("Deserialization error in line: {}, reason: {}", line, e),
                }
            }
        }

        res
    }
}

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

    #[test]
    fn get_entries() {

        // TODO make this into a self contained test

        let journal_gw = JournalGateway::new("http://192.168.33.19:19531");
        //let res = journal_gw.get_all_entries();

        let filter = vec![("SYSLOG_IDENTIFIER".to_string(), "wash-manager".to_string())];

        let res: Vec<JournalEntry> = journal_gw.get_entries(Some(&filter), None);
        println!("Received {} entries", res.len());
        for entry in res {
            println!("{}: {}",
                     entry.syslog_identifier.unwrap_or("N/A".to_string()),
                     entry.message);
        }
        println!("First entry: {:?}\n",
                 journal_gw.get_first_entry(Some(&filter)));
        println!("Last entry: {:?}\n",
                 journal_gw.get_last_entry(Some(&filter)));
    }

    // TODO create another test with a filter, e.g.
    // let res = journal_gw.get_entries(Some(vec![("_COMM".to_string(), "cat".to_string())]));
}
<|endoftext|>


<commit_before>
#![crate_name="mclient"]

#![feature(globs)]
use std::io::*;
use std::io::net::ip::SocketAddr;
use std::from_str::FromStr;
use std::time::Duration;
use std::default::Default;

pub trait Mclient {
    fn get(&mut self,key:&str) -> Result<String,Error> ;
}

pub struct Client {
    addr : SocketAddr,
    conn : Option<TcpStream>
}

#[deriving(PartialEq, Eq, Clone, Show)]
pub enum ErrorKind {
    InvalidHost,
    InvalidResponse,
    InterIoErr(IoError)
}

#[deriving(PartialEq, Eq, Clone, Show)]
struct Val {
    key : String,
    flag : u16,
    len : u64,
    v : Vec<u8>
}

#[deriving(PartialEq, Eq, Clone, Show)]
enum Mcresponse {
    Stored,
    NotStored,
    Deleted,
    NotFound,
    End,
    Value(Val)
}

enum Mcrequest {
    Get(&str),
    Set()
}

#[deriving(PartialEq, Eq, Clone, Show)]
pub struct Error {
    desc : &'static str,
    detail: Option<String>,
    kind : ErrorKind
}

pub fn new(host:&str,port:u16) -> Result<Client,Error> {
    let ipo : Option<IpAddr> = FromStr::from_str(host);
    match ipo {
        Some(ip) => {
            Ok(Client{addr:SocketAddr{ip:ip,port:port},conn:None})
        },
        None => {
            Err(Error{
                desc : "invalid host",
                kind : InvalidHost,
                detail : None
            })
        }
    }
}

impl Mclient for Client {
    fn get(&mut self,key:&str) -> Result<String,Error> {
        assert!(!key.contains(" "));
        assert!(!key.contains_char('\t'));
        assert!(!key.contains_char('\n'));
        let cmd = String::from_str("get ") + key.into_string() + "\r\n".into_string();
        if self.conn.is_none() {
            match TcpStream::connect_timeout(self.addr,Duration::seconds(1)) {
                Ok(c) => {
                    self.conn = Some(c);
                },
                Err(e) => {
                    return Err(Error{
                        desc : "fail to conn",
                        detail : None,
                        kind : InterIoErr(e)
                    })
                }
            }
        }

        let mut tc = self.conn.unwrap();
        tc.write_str(cmd.as_slice());
        let mut ret = [0u8,..1024];
        match tc.read_at_least(5,ret) {
            Ok(nread) => {
                let back = ret.slice_to(nread).clone();
                let first5 = back.slice_to(5);
                match first5 {

                    "END\r\n".as_bytes() {
                        Ok("".into_string())
                    },

                    _ => {
                        Err(Error{
                            desc : "invalid Response",
                            detail : None,
                            kind : InvalidResponse
                        })
                    }
                }
            },
            Err(err) => {
                Err(Error{
                    desc : "fail to read",
                    detail : None,
                    kind : InterIoErr(err)
                })
            }
        }
    }
}

#[test]
fn test_get() {
    let mut c = new("127.0.0.1",11211);
    assert!(c.is_ok());
    match c {
        Ok(mut mc) => { let ret = mc.get("foo");println!("ret {}",ret); }
        Err(e) => { fail!("not ok"); }
    }
}

<commit_msg>rename req & res<commit_after>
#![crate_name="mclient"]

#![feature(globs)]
use std::io::*;
use std::io::net::ip::SocketAddr;
use std::from_str::FromStr;
use std::time::Duration;
use std::default::Default;

const SUFFIX = String::from_str("\r\n");

pub trait Mclient {
    fn get(&mut self,key:&str) -> Result<String,Error> ;
    fn set(&mut self,key:&str,val:&str) -> Result<String,Error> ;
}

pub struct Client {
    addr : SocketAddr,
    conn : Option<TcpStream>
}

#[deriving(PartialEq, Eq, Clone, Show)]
pub enum ErrorKind {
    CmdErr,
    ClientErr(String),
    ServerErr(String),
    InterIoErr(IoError)
}

#[deriving(PartialEq, Eq, Clone, Show)]
struct Val {
    key : String,
    flag : u16,
    len : u64,
    v : Vec<u8>
}

#[deriving(PartialEq, Eq, Clone, Show)]
enum Response {
    Stored,
    NotStored,
    Deleted,
    NotFound,
    End,
    Value(Val)
}

#[deriving(PartialEq, Eq, Clone, Show)]
enum Request {
    Get(String)
    Set(String,String)
    Del(String)
}

#[deriving(PartialEq, Eq, Clone, Show)]
pub struct Error {
    desc : &'static str,
    detail: Option<String>,
    kind : ErrorKind
}

pub fn new(host:&str,port:u16) -> Result<Client,Error> {
    let ipo : Option<IpAddr> = FromStr::from_str(host);
    match ipo {
        Some(ip) => {
            Ok(Client{addr:SocketAddr{ip:ip,port:port},conn:None})
        },
        None => {
            Err(Error{
                desc : "invalid host",
                kind : InvalidHost,
                detail : None
            })
        }
    }
}

impl Mclient for Client {
    fn get(&mut self,key:&str) -> Result<String,Error> {
        assert!(!key.contains(" "));
        assert!(!key.contains_char('\t'));
        assert!(!key.contains_char('\n'));
        let cmd = String::from_str("get ") + key.into_string() + "\r\n".into_string();
        if self.conn.is_none() {
            match TcpStream::connect_timeout(self.addr,Duration::seconds(1)) {
                Ok(c) => {
                    self.conn = Some(c);
                },
                Err(e) => {
                    return Err(Error{
                        desc : "fail to conn",
                        detail : None,
                        kind : InterIoErr(e)
                    })
                }
            }
        }

        let mut tc = self.conn.unwrap();
        tc.write_str(cmd.as_slice());
        let mut ret = [0u8,..1024];
        match tc.read_at_least(5,ret) {
            Ok(nread) => {
                let back = ret.slice_to(nread).clone();
                let first5 = back.slice_to(5);
                match first5 {

                    "END\r\n".as_bytes() {
                        Ok("".into_string())
                    },

                    _ => {
                        Err(Error{
                            desc : "invalid Response",
                            detail : None,
                            kind : InvalidResponse
                        })
                    }
                }
            },
            Err(err) => {
                Err(Error{
                    desc : "fail to read",
                    detail : None,
                    kind : InterIoErr(err)
                })
            }
        }
    }
}

#[test]
fn test_get() {
    let mut c = new("127.0.0.1",11211);
    assert!(c.is_ok());
    match c {
        Ok(mut mc) => { let ret = mc.get("foo");println!("ret {}",ret); }
        Err(e) => { fail!("not ok"); }
    }
}

<|endoftext|>


<commit_before>#![feature(io,test)]

//! jed is creates Json iterators over instances of io.Read

extern crate rustc_serialize;
extern crate test;

use std::iter::Iterator;
use std::io::Chars;
use rustc_serialize::json::{ Json, Builder };
use std::io::Read;

/// An iterator over the Json elements of an io::Read stream
pub struct Iter<R> {
  inner: R
}

impl<R: Read> Iter<R> {
  pub fn new(inner: R) -> Iter<R> {
    Iter { inner: inner }
  }
}

impl<R: Read> Iterator for Iter<R> {
  type Item = Json;

  fn next(&mut self) -> Option<Json> {

    fn chomp<R: Read>(mut chars: Chars<R>, buf: &mut String) -> Option<Json> {
      match chars.next() {
        Some(Ok(c)) => {
          buf.push(c);
          match c {
            '}' | ']' => {
              let cbuf = buf.clone();
              match Builder::new(cbuf.chars()).build() {
                Ok(j) => Some(j),
                _ => chomp(chars, buf)
              }
            }, _ =>
              chomp(chars, buf)
          }
        }, _ => None
      }
    }
    let ref mut inner = self.inner;
    chomp(inner.chars(), &mut String::new())
  }
}

#[cfg(test)]
mod tests {
  use super::Iter;
  use test::Bencher;
  use std::io::{ empty, BufReader };

  #[test]
  fn test_empty_iter() {
    assert_eq!(Iter::new(empty()).count(), 0);
  }

  #[test]
  fn test_ary_iter() {
    let reader = BufReader::new("[][]".as_bytes());
    assert_eq!(Iter::new(reader).count(), 2)
  }

  #[test]
  fn test_obj_iter() {
    let reader = BufReader::new("{}{}".as_bytes());
    assert_eq!(Iter::new(reader).count(), 2)
  }

  #[bench]
  fn bench_iter(b: &mut Bencher) {
    b.iter(|| Iter::new(BufReader::new("{'foo':'bar'}{'foo':'baz'}".as_bytes())).count())
  }
}
<commit_msg>test for readers sourced from non-json inputs<commit_after>#![feature(io,test)]

//! jed is creates Json iterators over instances of io.Read

extern crate rustc_serialize;
extern crate test;

use std::iter::Iterator;
use std::io::Chars;
use rustc_serialize::json::{ Json, Builder };
use std::io::Read;

/// An iterator over the Json elements of an io::Read stream
pub struct Iter<R> {
  inner: R
}

impl<R: Read> Iter<R> {
  pub fn new(inner: R) -> Iter<R> {
    Iter { inner: inner }
  }
}

impl<R: Read> Iterator for Iter<R> {
  type Item = Json;

  fn next(&mut self) -> Option<Json> {

    fn chomp<R: Read>(mut chars: Chars<R>, buf: &mut String) -> Option<Json> {
      match chars.next() {
        Some(Ok(c)) => {
          buf.push(c);
          match c {
            '}' | ']' => {
              let cbuf = buf.clone();
              match Builder::new(cbuf.chars()).build() {
                Ok(j) => Some(j),
                _ => chomp(chars, buf)
              }
            }, _ =>
              chomp(chars, buf)
          }
        }, _ => None
      }
    }
    let ref mut inner = self.inner;
    chomp(inner.chars(), &mut String::new())
  }
}

#[cfg(test)]
mod tests {
  use super::Iter;
  use test::Bencher;
  use std::io::{ empty, BufReader };

  #[test]
  fn test_not_json_iter() {
    let reader = BufReader::new("bogus".as_bytes());
    assert_eq!(Iter::new(reader).count(), 0);
  }

  #[test]
  fn test_empty_iter() {
    assert_eq!(Iter::new(empty()).count(), 0);
  }

  #[test]
  fn test_ary_iter() {
    let reader = BufReader::new("[][]".as_bytes());
    assert_eq!(Iter::new(reader).count(), 2)
  }

  #[test]
  fn test_obj_iter() {
    let reader = BufReader::new("{}{}".as_bytes());
    assert_eq!(Iter::new(reader).count(), 2)
  }

  #[bench]
  fn bench_iter(b: &mut Bencher) {
    b.iter(|| Iter::new(BufReader::new("{'foo':'bar'}{'foo':'baz'}".as_bytes())).count())
  }
}
<|endoftext|>


<commit_before>extern crate capsize;
extern crate termsize;

use capsize::Capacity;
use std::io::Write;
use std::iter;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::default::Default;

static FORMAT: &'static str = "[=>_]";

/// Unit of display for progress counter
pub enum Units {
    /// No units
    None,
    /// formats units in humanized byte sizes
    Bytes
}

impl Default for Units {
    fn default() -> Units {
        Units::None
    }
}

/// A bar of progress information
#[derive(Default)]
pub struct Bar {
    total: usize,
    /// show progress percent, defaults to true
    pub show_percent: bool,
    /// show progress counter. Defaults to true
    pub show_counter: bool,
    /// show main progress bar UI. Defaults to true
    pub show_bar: bool,
    /// an arbitrary string label to prefix display with
    pub prefix: String,
    current: AtomicUsize,
    units: Units,
    bar_start: String,
    bar_current: String,
    bar_current_n: String,
    bar_empty: String,
    bar_end: String
}

impl Bar {
    /// creates a new bar with a target total size of `total`
    pub fn new(total: usize) -> Bar {
        let mut bar = Bar {
            total: total,
            show_percent: true,
            show_counter: true,
            show_bar: true,
            ..Default::default()
        };
        bar.format(FORMAT);
        bar
    }

    /// sets a custom format for display
    /// Example: bar.format("[=>_]")
    pub fn format(&mut self, spec: &str) {
        if spec.len() == 5 {
            let parts = spec.split("").collect::<Vec<&str>>();
            self.bar_start = parts[1].to_owned();
            self.bar_current = parts[2].to_owned();
            self.bar_current_n = parts[3].to_owned();
            self.bar_empty = parts[4].to_owned();
            self.bar_end = parts[5].to_owned();
        }
    }

    /// increment bar count by one
    pub fn incr(&self) -> usize {
        self.add(1)
    }

    /// add 1 to the bar count
    pub fn add(&self, delta: usize) -> usize {
        let prev = self.current.fetch_add(delta, Ordering::Relaxed);
        self.update();
        prev
    }

    /// sets the bar count to specified value
    pub fn set(&self, value: usize) {
        self.current.store(value, Ordering::Relaxed);
        self.update()
    }

    pub fn update(&self) {
        let current = self.current.load(Ordering::Relaxed);
        if current <= self.total {
            self.write(current)
        }
    }

    pub fn finish_print(&self, msg: &str) {
        println!("");
        println!("{}", msg)
    }

    fn width(&self) -> usize {
        termsize::get().map(|s|s.cols as usize).unwrap_or(80)
    }

    fn write(&self, current: usize) {
        fn repeat(what: &str, n: usize) -> String {
            iter::repeat(what).take(n).collect::<String>()
        }

        fn unit(value: usize, units: &Units) -> String {
            match *units {
                Units::None => value.to_string(),
                Units::Bytes => (value as i64).capacity()
            }
        }

        let width = self.width();

        let mut prefix = self.prefix.clone();
        let mut mid = String::new();
        let mut suffix = String::new();

        // counter
        if self.show_counter {
            prefix = prefix + &format!(
                "{} / {} ", unit(current, &self.units), unit(self.total, &self.units)
            );
        }

        // percent complete
        if self.show_percent {
            let percent = current as f64 / (self.total as f64 / 100_f64);
            suffix = suffix + &format!(
                " {:.*} %", 2, percent
            );
        }

        if self.show_bar {
            let size = width - (prefix.len() + suffix.len() + 3);
            if size > 0 {
                let cur_count = (
                    (current as f64 / self.total as f64) * size as f64
                ).ceil() as usize;
                let empt_count = size - cur_count;
                mid = self.bar_start.clone();
                if empt_count > 0 {
                    mid = mid + &repeat(self.bar_current.as_ref(), (cur_count - 1) as usize) +  &self.bar_current_n;
                } else {
                    mid = mid + &repeat(self.bar_current.as_ref(), cur_count as usize);
                }
                mid = mid + &repeat(self.bar_empty.as_ref(), (empt_count) as usize) + &self.bar_end
            }
        }
        let mut display = prefix + &mid + &suffix;
        if display.len() < width {
            let remaining = width - display.len();
            display = display + &repeat(" ", remaining);
        }

        let _ = write!(&mut std::io::stderr(), "\r{}", display);
    }
}

#[test]
fn it_works() {
}
<commit_msg>io progress<commit_after>extern crate capsize;
extern crate termsize;

use capsize::Capacity;
use std::io::{self, Read, Write};
use std::iter;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::default::Default;

static FORMAT: &'static str = "[=>_]";

/// Unit of display for progress counter
pub enum Units {
    /// No units
    None,
    /// formats units in humanized byte sizes
    Bytes
}

impl Default for Units {
    fn default() -> Units {
        Units::None
    }
}

/// IO progress. Implementations for Read and Write are provided
pub struct IO<T> {
    inner: T,
    bar: Bar
}

impl <R: Read> IO<R> {
    pub fn new(read: R, bar: Bar) -> IO<R> {
        IO {
            inner: read,
            bar: bar
        }
    }
}

impl <R: Read> Read for IO<R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let n = try!(self.inner.read(buf));
        self.bar.add(n);
        Ok(n)
    }
}

impl <W: Write> IO<W> {
    pub fn new(write: W, bar: Bar) -> IO<W> {
        IO {
            inner: write,
            bar: bar
        }
    }
}

impl <W: Write> Write for IO<W> {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let n = buf.len();
        self.bar.add(n);
        Ok(n)
    }
    fn flush(&mut self) -> io::Result<()> {
        Ok(())
    }
}

/// A bar of progress information
#[derive(Default)]
pub struct Bar {
    total: usize,
    /// show progress percent, defaults to true
    pub show_percent: bool,
    /// show progress counter. Defaults to true
    pub show_counter: bool,
    /// show main progress bar UI. Defaults to true
    pub show_bar: bool,
    /// an arbitrary string label to prefix display with
    pub prefix: String,
    current: AtomicUsize,
    units: Units,
    bar_start: String,
    bar_current: String,
    bar_current_n: String,
    bar_empty: String,
    bar_end: String
}

impl Bar {
    /// creates a new bar with a target total size of `total`
    pub fn new(total: usize) -> Bar {
        let mut bar = Bar {
            total: total,
            show_percent: true,
            show_counter: true,
            show_bar: true,
            ..Default::default()
        };
        bar.format(FORMAT);
        bar
    }

    /// sets a custom format for display
    /// Example: bar.format("[=>_]")
    pub fn format(&mut self, spec: &str) {
        if spec.len() == 5 {
            let parts = spec.split("").collect::<Vec<&str>>();
            self.bar_start = parts[1].to_owned();
            self.bar_current = parts[2].to_owned();
            self.bar_current_n = parts[3].to_owned();
            self.bar_empty = parts[4].to_owned();
            self.bar_end = parts[5].to_owned();
        }
    }

    /// increment bar count by one
    pub fn incr(&self) -> usize {
        self.add(1)
    }

    /// add 1 to the bar count
    pub fn add(&self, delta: usize) -> usize {
        let prev = self.current.fetch_add(delta, Ordering::Relaxed);
        self.update();
        prev
    }

    /// sets the bar count to specified value
    pub fn set(&self, value: usize) {
        self.current.store(value, Ordering::Relaxed);
        self.update()
    }

    pub fn update(&self) {
        let current = self.current.load(Ordering::Relaxed);
        if current <= self.total {
            self.write(current)
        }
    }

    pub fn finish_print(&self, msg: &str) {
        println!("");
        println!("{}", msg)
    }

    fn width(&self) -> usize {
        termsize::get().map(|s|s.cols as usize).unwrap_or(80)
    }

    fn write(&self, current: usize) {
        fn repeat(what: &str, n: usize) -> String {
            iter::repeat(what).take(n).collect::<String>()
        }

        fn unit(value: usize, units: &Units) -> String {
            match *units {
                Units::None => value.to_string(),
                Units::Bytes => (value as i64).capacity()
            }
        }

        let width = self.width();

        let mut prefix = self.prefix.clone();
        let mut mid = String::new();
        let mut suffix = String::new();

        // counter
        if self.show_counter {
            prefix = prefix + &format!(
                "{} / {} ", unit(current, &self.units), unit(self.total, &self.units)
            );
        }

        // percent complete
        if self.show_percent {
            let percent = current as f64 / (self.total as f64 / 100_f64);
            suffix = suffix + &format!(
                " {:.*} %", 2, percent
            );
        }

        if self.show_bar {
            let size = width - (prefix.len() + suffix.len() + 3);
            if size > 0 {
                let cur_count = (
                    (current as f64 / self.total as f64) * size as f64
                ).ceil() as usize;
                let empt_count = size - cur_count;
                mid = self.bar_start.clone();
                if empt_count > 0 {
                    mid = mid + &repeat(self.bar_current.as_ref(), (cur_count - 1) as usize) +  &self.bar_current_n;
                } else {
                    mid = mid + &repeat(self.bar_current.as_ref(), cur_count as usize);
                }
                mid = mid + &repeat(self.bar_empty.as_ref(), (empt_count) as usize) + &self.bar_end
            }
        }
        let mut display = prefix + &mid + &suffix;
        if display.len() < width {
            let remaining = width - display.len();
            display = display + &repeat(" ", remaining);
        }

        let _ = write!(&mut std::io::stderr(), "\r{}", display);
    }
}

#[test]
fn it_works() {
}
<|endoftext|>


<commit_before>// Forsaken docs justly quibble the vexed programmer's waning zeal
//! Text hyphenation in a variety of languages.
//!
//!
//! ## Usage
//!
//! A typical import comprises the `Hyphenation` trait, the `Standard`
//! hyphenator, and the `Language` enum. This exposes the crate's core
//! functionality, and the set of available languages.
//!
//!  ```rust
//! extern crate hyphenation;
//
//! use hyphenation::{Hyphenation, Standard, Language};
//! ```
//!
//! To begin with, we must initiate the `Corpus` for our working language.
//!
//! ```rust
//! let english_us = hyphenation::load(Language::English_US).unwrap();
//! ```
//!
//! Our English `Corpus` can now be used by *hyphenators*: iterators which
//! segment text in accordance with hyphenation practices, as described
//! by the corpus.
//!
//! The simplest (and, presently, only) hyphenator is `Standard`:
//!
//! ```rust
//! let h: Standard = "hyphenation".hyphenate(&english_us);
//! ```
//!
//! The `Standard` hyphenator does not allocate new strings, returning
//! slices instead.
//!
//! ```rust
//! let v: Vec<&str> = h.collect();
//! assert_eq!(v, vec!["hy", "phen", "ation"]);
//! ```
//!
//! While hyphenation is performed on a per-word basis, convenience calls
//! for `Hyphenation` to work with full text by default.
//!
//! ```rust
//! let h2: Standard = "Word hyphenation by computer.".hyphenate(&english_us);
//! let v2: Vec<&str> = h2.collect();
//! assert_eq!(v2, vec!["Word hy", "phen", "ation by com", "puter."]);
//! ```
//!
//! Moreover, hyphenators expose some simple methods to render hyphenated
//! text: `punctuate()` and `punctuate_with(string)`, which mark hyphenation
//! opportunities respectively with soft hyphens (Unicode `U+00AD SOFT HYPHEN`)
//! and any given `string`.
//!
//! ```rust
//! let h3 = "anfractuous".hyphenate(&english_us);
//! let s3: String = h2.clone().punctuate().collect();
//! assert_eq!(s3, "an\u{ad}frac\u{ad}tu\u{ad}ous".to_owned());
//!
//! let s4: String = h2.punctuate_with("-").collect()
//! assert_eq!(s4, "an-frac-tu-ous".to_owned());
//! ```
//!
//! If we would rather manipulate our text in other ways, we may employ
//! `opportunities()`, which returns the byte indices of hyphenation opportunities
//! within the string. (Internally, `opportunities()` is the fundamental method
//! required by `Hyphenation`; other functionality is implemented on top of it.)
//!
//! ```rust
//! let indices = "hyphenation".opportunities(&english_us);
//! assert_eq!(indices, vec![…]);
//! ```

extern crate fnv;
#[macro_use]
extern crate lazy_static;
extern crate serde_json;
extern crate unicode_segmentation;

mod klpair;
mod utilia;
pub mod exception;
pub mod hyphenator;
pub mod language;
pub mod load;
pub mod pattern;

pub use hyphenator::{Hyphenation, Standard};
pub use language::{Language, Corpus};
pub use load::{set_pattern_folder, language as load};
<commit_msg>Rustdoc, you torment me so.<commit_after>// Forsaken docs justly quibble the vexed programmer's waning zeal
//! Text hyphenation in a variety of languages.
//!
//!
//! ## Usage
//!
//! A typical import comprises the `Hyphenation` trait, the `Standard`
//! hyphenator, and the `Language` enum. This exposes the crate's core
//! functionality, and the set of available languages.
//!
//! ```ignore
//! extern crate hyphenation;
//
//! use hyphenation::{Hyphenation, Standard, Language};
//! ```
//!
//! To begin with, we must initiate the `Corpus` for our working language.
//!
//! ```ignore
//! let english_us = hyphenation::load(Language::English_US).unwrap();
//! ```
//!
//! Our English `Corpus` can now be used by *hyphenators*: iterators which
//! segment text in accordance with hyphenation practices, as described
//! by the corpus.
//!
//! The simplest (and, presently, only) hyphenator is `Standard`:
//!
//! ```ignore
//! let h: Standard = "hyphenation".hyphenate(&english_us);
//! ```
//!
//! The `Standard` hyphenator does not allocate new strings, returning
//! slices instead.
//!
//! ```ignore
//! let v: Vec<&str> = h.collect();
//! assert_eq!(v, vec!["hy", "phen", "ation"]);
//! ```
//!
//! While hyphenation is performed on a per-word basis, convenience calls
//! for `Hyphenation` to work with full text by default.
//!
//! ```ignore
//! let h2: Standard = "Word hyphenation by computer.".hyphenate(&english_us);
//! let v2: Vec<&str> = h2.collect();
//! assert_eq!(v2, vec!["Word hy", "phen", "ation by com", "puter."]);
//! ```
//!
//! Moreover, hyphenators expose some simple methods to render hyphenated
//! text: `punctuate()` and `punctuate_with(string)`, which mark hyphenation
//! opportunities respectively with soft hyphens (Unicode `U+00AD SOFT HYPHEN`)
//! and any given `string`.
//!
//! ```ignore
//! let h3 = "anfractuous".hyphenate(&english_us);
//! let s3: String = h2.clone().punctuate().collect();
//! assert_eq!(s3, "an\u{ad}frac\u{ad}tu\u{ad}ous".to_owned());
//!
//! let s4: String = h2.punctuate_with("-").collect()
//! assert_eq!(s4, "an-frac-tu-ous".to_owned());
//! ```
//!
//! If we would rather manipulate our text in other ways, we may employ
//! `opportunities()`, which returns the byte indices of hyphenation opportunities
//! within the string. (Internally, `opportunities()` is the fundamental method
//! required by `Hyphenation`; other functionality is implemented on top of it.)
//!
//! ```ignore
//! let indices = "hyphenation".opportunities(&english_us);
//! assert_eq!(indices, vec![2, 6]);
//! ```

extern crate fnv;
#[macro_use]
extern crate lazy_static;
extern crate serde_json;
extern crate unicode_segmentation;

mod klpair;
mod utilia;
pub mod exception;
pub mod hyphenator;
pub mod language;
pub mod load;
pub mod pattern;

pub use hyphenator::{Hyphenation, Standard};
pub use language::{Language, Corpus};
pub use load::{set_pattern_folder, language as load};
<|endoftext|>


<commit_before>//! Unified diff parsing/metadata extraction library for Rust
#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]
#![cfg_attr(feature="clippy", deny(clippy))]
#![cfg_attr(feature="clippy", warn(cyclomatic_complexity))]

extern crate regex;
#[macro_use]
extern crate lazy_static;

use std::fmt;

use regex::Regex;

lazy_static! {
    static ref RE_SOURCE_FILENAME: Regex = Regex::new(r"^--- (?P<filename>[^\t\n]+)(?:\t(?P<timestamp>[^\n]+))?").unwrap();
    static ref RE_TARGET_FILENAME: Regex = Regex::new(r"^\+\+\+ (?P<filename>[^\t\n]+)(?:\t(?P<timestamp>[^\n]+))?").unwrap();
    static ref RE_HUNK_HEADER: Regex = Regex::new(r"^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))?\ @@[ ]?(.*)").unwrap();
    static ref RE_HUNK_BODY_LINE: Regex = Regex::new(r"^(?P<line_type>[- \n\+\\])(?P<value>.*)").unwrap();
}

const LINE_TYPE_ADDED: &'static str = "+";
const LINE_TYPE_REMOVED: &'static str = "-";
const LINE_TYPE_CONTEXT: &'static str = " ";
const LINE_TYPE_EMPTY: &'static str = "\n";

/// A diff line
#[derive(Debug, Clone)]
pub struct Line {
    pub source_line_no: Option<usize>,
    pub target_line_no: Option<usize>,
    pub diff_line_no: usize,
    pub line_type: String,
    pub value: String,
}

impl Line {
    pub fn is_added(&self) -> bool {
        LINE_TYPE_ADDED == &self.line_type
    }

    pub fn is_removed(&self) -> bool {
        LINE_TYPE_REMOVED == &self.line_type
    }

    pub fn is_context(&self) -> bool {
        LINE_TYPE_CONTEXT == &self.line_type
    }
}

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

/// Each of the modified blocks of a file
#[derive(Debug, Clone)]
pub struct Hunk {
    pub added: usize,
    pub removed: usize,
    pub source_start: usize,
    pub source_length: usize,
    pub target_start: usize,
    pub target_length: usize,
    pub section_header: String,
    lines: Vec<Line>,
    source: Vec<String>,
    target: Vec<String>,
}

impl Hunk {
    pub fn is_valid(&self) -> bool {
        self.source.len() == self.source_length && self.target.len() == self.target_length
    }

    pub fn source_lines(&self) -> Vec<Line> {
        self.lines.iter().cloned().filter(|l| l.is_context() || l.is_removed()).collect()
    }

    pub fn target_lines(&self) -> Vec<Line> {
        self.lines.iter().cloned().filter(|l| l.is_context() || l.is_added()).collect()
    }

    pub fn append(&mut self, line: Line) {
        if line.is_added() {
            self.added = self.added + 1;
            self.target.push(format!("{}{}", line.line_type, line.value));
        } else if line.is_removed() {
            self.removed = self.removed + 1;
            self.source.push(format!("{}{}", line.line_type, line.value));
        } else if line.is_context() {
            self.source.push(format!("{}{}", line.line_type, line.value));
            self.target.push(format!("{}{}", line.line_type, line.value));
        }
        self.lines.push(line);
    }
}

impl fmt::Display for Hunk {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let header = format!("@@ -{},{} +{},{} @@ {}\n",
                             self.source_start,
                             self.source_length,
                             self.target_start,
                             self.target_length,
                             self.section_header);
        let content = self.lines.iter().map(|l| l.to_string()).collect::<Vec<String>>().join("\n");
        write!(f, "{}{}", header, content)
    }
}

impl IntoIterator for Hunk {
    type Item = Line;
    type IntoIter = ::std::vec::IntoIter<Line>;

    fn into_iter(self) -> Self::IntoIter {
        self.lines.into_iter()
    }
}

/// Patch updated file, contains a list of Hunks
#[derive(Debug, Clone)]
pub struct PatchedFile {
    pub source_file: String,
    pub source_timestamp: Option<String>,
    pub target_file: String,
    pub target_timestamp: Option<String>,
    hunks: Vec<Hunk>,
}

impl PatchedFile {
    pub fn path(&self) -> String {
        if self.source_file.starts_with("a/") && self.target_file.starts_with("b/") {
            return self.source_file[2..].to_owned();
        }
        if self.source_file.starts_with("a/") && "/dev/null" == &self.target_file {
            return self.source_file[2..].to_owned();
        }
        if self.target_file.starts_with("b/") && "/dev/null" == &self.source_file {
            return self.target_file[2..].to_owned();
        }
        self.source_file.clone()
    }

    pub fn added(&self) -> usize {
        self.hunks.iter().map(|h| h.added).fold(0, |acc, x| acc + x)
    }

    pub fn removed(&self) -> usize {
        self.hunks.iter().map(|h| h.removed).fold(0, |acc, x| acc + x)
    }

    pub fn is_added_file(&self) -> bool {
        self.hunks.len() == 1 && self.hunks[0].source_start == 0 && self.hunks[0].source_length == 0
    }

    pub fn is_removed_file(&self) -> bool {
        self.hunks.len() == 1 && self.hunks[0].target_start == 0 && self.hunks[0].target_length == 0
    }

    pub fn is_modified_file(&self) -> bool {
        !self.is_added_file() && !self.is_removed_file()
    }

    fn parse_hunk(&mut self, header: &str, diff: &[(usize, &str)]) {
        let header_info = RE_HUNK_HEADER.captures(header).unwrap();
        let source_start = header_info.at(1).unwrap().parse::<usize>().unwrap();
        let source_length = header_info.at(2).unwrap().parse::<usize>().unwrap();
        let target_start = header_info.at(3).unwrap().parse::<usize>().unwrap();
        let target_length = header_info.at(4).unwrap().parse::<usize>().unwrap();
        let section_header = header_info.at(5).unwrap();
        let mut hunk = Hunk {
            added: 0usize,
            removed: 0usize,
            source: vec![],
            target: vec![],
            lines: vec![],
            source_start: source_start,
            source_length: source_length,
            target_start: target_start,
            target_length: target_length,
            section_header: section_header.to_owned(),
        };
        let mut source_line_no = source_start;
        let mut target_line_no = target_start;
        let expected_source_end = source_start + source_length;
        let expected_target_end = target_start + target_length;
        for &(diff_line_no, line) in diff {
            if let Some(valid_line) = RE_HUNK_BODY_LINE.captures(line) {
                let mut line_type = valid_line.name("line_type").unwrap();
                if line_type == LINE_TYPE_EMPTY {
                    line_type = LINE_TYPE_CONTEXT;
                }
                let value = valid_line.name("value").unwrap();
                let mut original_line = Line {
                    source_line_no: None,
                    target_line_no: None,
                    diff_line_no: diff_line_no,
                    line_type: line_type.to_owned(),
                    value: value.to_owned(),
                };
                match line_type {
                    LINE_TYPE_ADDED => {
                        original_line.target_line_no = Some(target_line_no);
                        target_line_no = target_line_no + 1;
                    }
                    LINE_TYPE_REMOVED => {
                        original_line.source_line_no = Some(source_line_no);
                        source_line_no = source_line_no + 1;
                    }
                    LINE_TYPE_CONTEXT => {
                        original_line.target_line_no = Some(target_line_no);
                        target_line_no = target_line_no + 1;
                        original_line.source_line_no = Some(source_line_no);
                        source_line_no = source_line_no + 1;
                    }
                    _ => {}
                }
                hunk.append(original_line);
                if source_line_no == expected_source_end && target_line_no == expected_target_end {
                    break;
                }
            } else {
                // TODO: Error hunk diff line expected
            }
        }
        self.hunks.push(hunk);
    }
}

impl fmt::Display for PatchedFile {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let source = format!("--- {}\n", self.source_file);
        let target = format!("+++ {}\n", self.target_file);
        let hunks = self.hunks.iter().map(|h| h.to_string()).collect::<Vec<String>>().join("\n");
        write!(f, "{}{}{}", source, target, hunks)
    }
}

impl IntoIterator for PatchedFile {
    type Item = Hunk;
    type IntoIter = ::std::vec::IntoIter<Hunk>;

    fn into_iter(self) -> Self::IntoIter {
        self.hunks.into_iter()
    }
}

/// Unfied patchset
#[derive(Debug, Clone)]
pub struct PatchSet {
    files: Vec<PatchedFile>,
}

impl PatchSet {
    pub fn added_files(&self) -> Vec<PatchedFile> {
        self.files.iter().cloned().filter(|f| f.is_added_file()).collect()
    }

    pub fn removed_files(&self) -> Vec<PatchedFile> {
        self.files.iter().cloned().filter(|f| f.is_removed_file()).collect()
    }

    pub fn modified_files(&self) -> Vec<PatchedFile> {
        self.files.iter().cloned().filter(|f| f.is_modified_file()).collect()
    }

    pub fn new() -> PatchSet {
        PatchSet { files: vec![] }
    }

    pub fn parse<T: AsRef<str>>(&mut self, input: T) {
        let mut current_file: Option<PatchedFile> = None;
        let diff: Vec<(usize, &str)> = input.as_ref().split("\n").skip(1).enumerate().collect();
        let mut source_file: Option<String> = None;
        let mut source_timestamp: Option<String> = None;

        for &(_, line) in &diff {
            // check for source file header
            if let Some(captures) = RE_SOURCE_FILENAME.captures(line) {
                source_file = Some(captures.name("filename").unwrap_or("").to_owned());
                source_timestamp = Some(captures.name("timestamp").unwrap_or("").to_owned());
                current_file = None;
                continue;
            }
            // check for target file header
            if let Some(captures) = RE_TARGET_FILENAME.captures(line) {
                if current_file.is_none() {
                    // TODO: Error target without source
                }
                let target_file = Some(captures.name("filename").unwrap_or("").to_owned());
                let target_timestamp = Some(captures.name("timestamp").unwrap_or("").to_owned());

                // add current file to PatchSet
                current_file = Some(PatchedFile {
                    source_file: source_file.clone().unwrap(),
                    target_file: target_file.clone().unwrap(),
                    source_timestamp: source_timestamp.clone(),
                    target_timestamp: target_timestamp.clone(),
                    hunks: vec![],
                });
                self.files.push(current_file.clone().unwrap());
                continue;
            }
            // check for hunk header
            if RE_HUNK_HEADER.is_match(line) {
                if let Some(ref mut patched_file) = current_file {
                    patched_file.parse_hunk(line, &diff);
                } else {
                    // TODO: Error unexpected hunk found
                }
            }
        }
    }
}

impl fmt::Display for PatchSet {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let diff = self.files.iter().map(|f| f.to_string()).collect::<Vec<String>>().join("\n");
        write!(f, "{}", diff)
    }
}

impl IntoIterator for PatchSet {
    type Item = PatchedFile;
    type IntoIter = ::std::vec::IntoIter<PatchedFile>;

        fn into_iter(self) -> Self::IntoIter {
            self.files.into_iter()
        }
}
<commit_msg>Fix some clippy errors<commit_after>//! Unified diff parsing/metadata extraction library for Rust
#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]
#![cfg_attr(feature="clippy", deny(clippy))]
#![cfg_attr(feature="clippy", warn(cyclomatic_complexity))]

extern crate regex;
#[macro_use]
extern crate lazy_static;

use std::fmt;

use regex::Regex;

lazy_static! {
    static ref RE_SOURCE_FILENAME: Regex = Regex::new(r"^--- (?P<filename>[^\t\n]+)(?:\t(?P<timestamp>[^\n]+))?").unwrap();
    static ref RE_TARGET_FILENAME: Regex = Regex::new(r"^\+\+\+ (?P<filename>[^\t\n]+)(?:\t(?P<timestamp>[^\n]+))?").unwrap();
    static ref RE_HUNK_HEADER: Regex = Regex::new(r"^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@[ ]?(.*)").unwrap();
    static ref RE_HUNK_BODY_LINE: Regex = Regex::new(r"^(?P<line_type>[- \n\+\\])(?P<value>.*)").unwrap();
}

const LINE_TYPE_ADDED: &'static str = "+";
const LINE_TYPE_REMOVED: &'static str = "-";
const LINE_TYPE_CONTEXT: &'static str = " ";
const LINE_TYPE_EMPTY: &'static str = "\n";

/// A diff line
#[derive(Debug, Clone)]
pub struct Line {
    pub source_line_no: Option<usize>,
    pub target_line_no: Option<usize>,
    pub diff_line_no: usize,
    pub line_type: String,
    pub value: String,
}

impl Line {
    pub fn is_added(&self) -> bool {
        LINE_TYPE_ADDED == &self.line_type
    }

    pub fn is_removed(&self) -> bool {
        LINE_TYPE_REMOVED == &self.line_type
    }

    pub fn is_context(&self) -> bool {
        LINE_TYPE_CONTEXT == &self.line_type
    }
}

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

/// Each of the modified blocks of a file
#[derive(Debug, Clone)]
pub struct Hunk {
    pub added: usize,
    pub removed: usize,
    pub source_start: usize,
    pub source_length: usize,
    pub target_start: usize,
    pub target_length: usize,
    pub section_header: String,
    lines: Vec<Line>,
    source: Vec<String>,
    target: Vec<String>,
}

impl Hunk {
    pub fn is_valid(&self) -> bool {
        self.source.len() == self.source_length && self.target.len() == self.target_length
    }

    pub fn source_lines(&self) -> Vec<Line> {
        self.lines.iter().cloned().filter(|l| l.is_context() || l.is_removed()).collect()
    }

    pub fn target_lines(&self) -> Vec<Line> {
        self.lines.iter().cloned().filter(|l| l.is_context() || l.is_added()).collect()
    }

    pub fn append(&mut self, line: Line) {
        if line.is_added() {
            self.added = self.added + 1;
            self.target.push(format!("{}{}", line.line_type, line.value));
        } else if line.is_removed() {
            self.removed = self.removed + 1;
            self.source.push(format!("{}{}", line.line_type, line.value));
        } else if line.is_context() {
            self.source.push(format!("{}{}", line.line_type, line.value));
            self.target.push(format!("{}{}", line.line_type, line.value));
        }
        self.lines.push(line);
    }
}

impl fmt::Display for Hunk {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let header = format!("@@ -{},{} +{},{} @@ {}\n",
                             self.source_start,
                             self.source_length,
                             self.target_start,
                             self.target_length,
                             self.section_header);
        let content = self.lines.iter().map(|l| l.to_string()).collect::<Vec<String>>().join("\n");
        write!(f, "{}{}", header, content)
    }
}

impl IntoIterator for Hunk {
    type Item = Line;
    type IntoIter = ::std::vec::IntoIter<Line>;

    fn into_iter(self) -> Self::IntoIter {
        self.lines.into_iter()
    }
}

/// Patch updated file, contains a list of Hunks
#[derive(Debug, Clone)]
pub struct PatchedFile {
    pub source_file: String,
    pub source_timestamp: Option<String>,
    pub target_file: String,
    pub target_timestamp: Option<String>,
    hunks: Vec<Hunk>,
}

impl PatchedFile {
    pub fn path(&self) -> String {
        if self.source_file.starts_with("a/") && self.target_file.starts_with("b/") {
            return self.source_file[2..].to_owned();
        }
        if self.source_file.starts_with("a/") && "/dev/null" == &self.target_file {
            return self.source_file[2..].to_owned();
        }
        if self.target_file.starts_with("b/") && "/dev/null" == &self.source_file {
            return self.target_file[2..].to_owned();
        }
        self.source_file.clone()
    }

    pub fn added(&self) -> usize {
        self.hunks.iter().map(|h| h.added).fold(0, |acc, x| acc + x)
    }

    pub fn removed(&self) -> usize {
        self.hunks.iter().map(|h| h.removed).fold(0, |acc, x| acc + x)
    }

    pub fn is_added_file(&self) -> bool {
        self.hunks.len() == 1 && self.hunks[0].source_start == 0 && self.hunks[0].source_length == 0
    }

    pub fn is_removed_file(&self) -> bool {
        self.hunks.len() == 1 && self.hunks[0].target_start == 0 && self.hunks[0].target_length == 0
    }

    pub fn is_modified_file(&self) -> bool {
        !self.is_added_file() && !self.is_removed_file()
    }

    fn parse_hunk(&mut self, header: &str, diff: &[(usize, &str)]) {
        let header_info = RE_HUNK_HEADER.captures(header).unwrap();
        let source_start = header_info.at(1).unwrap().parse::<usize>().unwrap();
        let source_length = header_info.at(2).unwrap().parse::<usize>().unwrap();
        let target_start = header_info.at(3).unwrap().parse::<usize>().unwrap();
        let target_length = header_info.at(4).unwrap().parse::<usize>().unwrap();
        let section_header = header_info.at(5).unwrap();
        let mut hunk = Hunk {
            added: 0usize,
            removed: 0usize,
            source: vec![],
            target: vec![],
            lines: vec![],
            source_start: source_start,
            source_length: source_length,
            target_start: target_start,
            target_length: target_length,
            section_header: section_header.to_owned(),
        };
        let mut source_line_no = source_start;
        let mut target_line_no = target_start;
        let expected_source_end = source_start + source_length;
        let expected_target_end = target_start + target_length;
        for &(diff_line_no, line) in diff {
            if let Some(valid_line) = RE_HUNK_BODY_LINE.captures(line) {
                let mut line_type = valid_line.name("line_type").unwrap();
                if line_type == LINE_TYPE_EMPTY {
                    line_type = LINE_TYPE_CONTEXT;
                }
                let value = valid_line.name("value").unwrap();
                let mut original_line = Line {
                    source_line_no: None,
                    target_line_no: None,
                    diff_line_no: diff_line_no,
                    line_type: line_type.to_owned(),
                    value: value.to_owned(),
                };
                match line_type {
                    LINE_TYPE_ADDED => {
                        original_line.target_line_no = Some(target_line_no);
                        target_line_no = target_line_no + 1;
                    }
                    LINE_TYPE_REMOVED => {
                        original_line.source_line_no = Some(source_line_no);
                        source_line_no = source_line_no + 1;
                    }
                    LINE_TYPE_CONTEXT => {
                        original_line.target_line_no = Some(target_line_no);
                        target_line_no = target_line_no + 1;
                        original_line.source_line_no = Some(source_line_no);
                        source_line_no = source_line_no + 1;
                    }
                    _ => {}
                }
                hunk.append(original_line);
                if source_line_no == expected_source_end && target_line_no == expected_target_end {
                    break;
                }
            } else {
                // TODO: Error hunk diff line expected
            }
        }
        self.hunks.push(hunk);
    }
}

impl fmt::Display for PatchedFile {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let source = format!("--- {}\n", self.source_file);
        let target = format!("+++ {}\n", self.target_file);
        let hunks = self.hunks.iter().map(|h| h.to_string()).collect::<Vec<String>>().join("\n");
        write!(f, "{}{}{}", source, target, hunks)
    }
}

impl IntoIterator for PatchedFile {
    type Item = Hunk;
    type IntoIter = ::std::vec::IntoIter<Hunk>;

    fn into_iter(self) -> Self::IntoIter {
        self.hunks.into_iter()
    }
}

/// Unfied patchset
#[derive(Debug, Clone)]
pub struct PatchSet {
    files: Vec<PatchedFile>,
}

impl PatchSet {
    pub fn added_files(&self) -> Vec<PatchedFile> {
        self.files.iter().cloned().filter(|f| f.is_added_file()).collect()
    }

    pub fn removed_files(&self) -> Vec<PatchedFile> {
        self.files.iter().cloned().filter(|f| f.is_removed_file()).collect()
    }

    pub fn modified_files(&self) -> Vec<PatchedFile> {
        self.files.iter().cloned().filter(|f| f.is_modified_file()).collect()
    }

    pub fn new() -> PatchSet {
        PatchSet { files: vec![] }
    }

    pub fn parse<T: AsRef<str>>(&mut self, input: T) {
        let mut current_file: Option<PatchedFile> = None;
        let diff: Vec<(usize, &str)> = input.as_ref().split('\n').skip(1).enumerate().collect();
        let mut source_file: Option<String> = None;
        let mut source_timestamp: Option<String> = None;

        for &(_, line) in &diff {
            // check for source file header
            if let Some(captures) = RE_SOURCE_FILENAME.captures(line) {
                source_file = Some(captures.name("filename").unwrap_or("").to_owned());
                source_timestamp = Some(captures.name("timestamp").unwrap_or("").to_owned());
                current_file = None;
                continue;
            }
            // check for target file header
            if let Some(captures) = RE_TARGET_FILENAME.captures(line) {
                if current_file.is_none() {
                    // TODO: Error target without source
                }
                let target_file = Some(captures.name("filename").unwrap_or("").to_owned());
                let target_timestamp = Some(captures.name("timestamp").unwrap_or("").to_owned());

                // add current file to PatchSet
                current_file = Some(PatchedFile {
                    source_file: source_file.clone().unwrap(),
                    target_file: target_file.clone().unwrap(),
                    source_timestamp: source_timestamp.clone(),
                    target_timestamp: target_timestamp.clone(),
                    hunks: vec![],
                });
                self.files.push(current_file.clone().unwrap());
                continue;
            }
            // check for hunk header
            if RE_HUNK_HEADER.is_match(line) {
                if let Some(ref mut patched_file) = current_file {
                    patched_file.parse_hunk(line, &diff);
                } else {
                    // TODO: Error unexpected hunk found
                }
            }
        }
    }
}

impl fmt::Display for PatchSet {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let diff = self.files.iter().map(|f| f.to_string()).collect::<Vec<String>>().join("\n");
        write!(f, "{}", diff)
    }
}

impl IntoIterator for PatchSet {
    type Item = PatchedFile;
    type IntoIter = ::std::vec::IntoIter<PatchedFile>;

        fn into_iter(self) -> Self::IntoIter {
            self.files.into_iter()
        }
}
<|endoftext|>