<commit_before>extern crate iron;
extern crate mount;
extern crate rand;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate staticfile;

use iron::method::Method;
use iron::prelude::*;
use iron::status::Status;
use mount::Mount;
use rand::Rng;
use staticfile::Static;
use std::collections::HashMap;
use std::collections::HashSet;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;
use std::time::Duration;
use std::time::Instant;

static CONFIG_FILE_NAME: &str = "find-reviewer.json";

#[derive(Debug, Deserialize, Eq, PartialEq)]
enum FindReviewerRequest {
    NeedReviewer { coder: String },
    HaveTimeForReview { reviewer: String },
    WillReview { review_id: usize },
    WontReview { review_id: usize },
}

#[derive(Debug, Eq, PartialEq, Serialize)]
enum FindReviewerResponse {
    Accepted {},
    NoReviewerNeeded {},
    AlreadyRegistered {},
    NeedsReviewer { coder: String, review_id: usize },
    ReviewNotFound {},
}

type SharedApplication = Arc<Mutex<Application<RandomIdGenerator>>>;

fn main() {
    let configuration = load_configuration();
    let application = SharedApplication::new(Mutex::new(Application::new(configuration)));

    let mut mount = Mount::new();
    mount
        .mount("/find-reviewer", {
            let application = application.clone();
            move |request: &mut Request| Ok(dispatch_request(request, &application))
        })
        .mount("/", Static::new(Path::new("www")));

    thread::spawn(move || loop {
        thread::sleep(Duration::from_secs(1));
        application.lock().unwrap().process_timeouts();
    });

    Iron::new(mount).http("localhost:3000").unwrap();
}

fn load_configuration() -> ApplicationConfiguration {
    let config = File::open(CONFIG_FILE_NAME)
        .map(|open_file| serde_json::from_reader(open_file).expect(&format!("Could not parse {}", CONFIG_FILE_NAME)))
        .unwrap_or_else(|err| {
            println!("Could not read {}: {}\nFile will be created", CONFIG_FILE_NAME, err);
            ApplicationConfiguration::default()
        });

    File::create(CONFIG_FILE_NAME)
        .map(|created_file| {
            serde_json::to_writer_pretty(created_file, &config).expect(&format!("Could not serialize {}", CONFIG_FILE_NAME))
        })
        .unwrap_or_else(|err| println!("Could not write {}: {}", CONFIG_FILE_NAME, err));

    config
}

fn dispatch_request(request: &mut Request, application: &SharedApplication) -> Response {
    if request.method != Method::Post {
        return Response::with((Status::BadRequest, "Must be a POST request"));
    }

    let parsed = match serde_json::from_reader(request.body.by_ref()) {
        Err(message) => return Response::with((Status::BadRequest, format!("JSON error: {}", message))),
        Ok(request) => request,
    };

    let response = {
        let mut application = application.lock().unwrap();
        match parsed {
            FindReviewerRequest::NeedReviewer { coder } => application.need_reviewer(coder),
            FindReviewerRequest::HaveTimeForReview { reviewer } => application.have_time_for_review(&reviewer),
            FindReviewerRequest::WillReview { review_id } => application.will_review(review_id),
            FindReviewerRequest::WontReview { review_id } => application.wont_review(review_id),
        }
    };

    Response::with((Status::Ok, serde_json::to_string_pretty(&response).unwrap()))
}

use std::collections::hash_map::DefaultHasher;
use std::hash::BuildHasherDefault;

struct Application<G> {
    configuration: ApplicationConfiguration,
    waiting_coders: HashSet<String, BuildHasherDefault<DefaultHasher>>,
    active_reviews: HashMap<usize, Review>,
    id_generator: G,
}

#[derive(Serialize, Deserialize)]
struct ApplicationConfiguration {
    timeout: Duration,
    wip_limit: usize,
}

impl Default for ApplicationConfiguration {
    fn default() -> Self {
        ApplicationConfiguration {
            timeout: Duration::from_secs(30),
            wip_limit: 5,
        }
    }
}

#[derive(Clone)]
struct Review {
    pub coder: String,
    pub enqueued_coder: Option<String>,
    pub started: Instant,
}

impl<G: IdGenerator> Application<G> {
    fn new(configuration: ApplicationConfiguration) -> Application<G> {
        Application {
            configuration,
            waiting_coders: HashSet::default(),
            active_reviews: HashMap::default(),
            id_generator: G::new(),
        }
    }

    fn need_reviewer(&mut self, incoming_coder: String) -> FindReviewerResponse {
        if self.is_already_registered(&incoming_coder) {
            FindReviewerResponse::AlreadyRegistered {}
        } else if self.waiting_coders.len() >= self.configuration.wip_limit {
            let random_waiting_coder = self.waiting_coders.iter().next().unwrap().clone();
            self.start_review(random_waiting_coder, Some(incoming_coder))
        } else {
            self.waiting_coders.insert(incoming_coder);
            FindReviewerResponse::Accepted {}
        }
    }

    fn is_already_registered(&self, coder: &String) -> bool {
        self.waiting_coders.contains(coder) ||
            self.active_reviews
                .values()
                .any(|review| &review.coder == coder || review.enqueued_coder.as_ref() == Some(coder))
    }

    fn have_time_for_review(&mut self, incoming_reviewer: &String) -> FindReviewerResponse {
        let random_coder_except_incoming_reviewer = self.waiting_coders
            .iter()
            .filter(|&coder| coder != incoming_reviewer)
            .next()
            .cloned();
        match random_coder_except_incoming_reviewer {
            Some(coder) => self.start_review(coder, None),
            None => FindReviewerResponse::NoReviewerNeeded {},
        }
    }

    fn start_review(&mut self, coder: String, enqueued_coder: Option<String>) -> FindReviewerResponse {
        let review = Review {
            coder: coder.clone(),
            enqueued_coder,
            started: Instant::now(),
        };
        self.remove_coder(&coder);
        let review_id = self.insert_review(review);

        FindReviewerResponse::NeedsReviewer { coder, review_id }
    }

    fn generate_id(&mut self) -> usize {
        loop {
            let id = self.id_generator.generate_id();
            if !self.active_reviews.contains_key(&id) {
                return id;
            }
        }
    }

    fn will_review(&mut self, review_id: usize) -> FindReviewerResponse {
        match self.active_reviews.remove(&review_id) {
            Some(review) => {
                review
                    .enqueued_coder
                    .map(|coder| self.waiting_coders.insert(coder));
                FindReviewerResponse::Accepted {}
            }
            None => FindReviewerResponse::ReviewNotFound {},
        }
    }

    fn wont_review(&mut self, review_id: usize) -> FindReviewerResponse {
        match self.active_reviews.remove(&review_id) {
            Some(review) => {
                self.waiting_coders.insert(review.coder);
                FindReviewerResponse::Accepted {}
            }
            None => FindReviewerResponse::ReviewNotFound {},
        }
    }

    fn remove_coder(&mut self, coder: &String) {
        self.waiting_coders.remove(coder);
    }

    fn insert_review(&mut self, review: Review) -> usize {
        let id = self.generate_id();
        self.active_reviews.insert(id, review);
        id
    }

    fn process_timeouts(&mut self) {
        let now = Instant::now();
        let timed_out: Vec<_> = self.active_reviews
            .iter()
            .filter(|&(_, review)| (now - review.started) > self.configuration.timeout)
            .map(|(&id, _)| id)
            .collect();
        for review_id in timed_out {
            self.wont_review(review_id);
        }
    }
}

trait IdGenerator {
    fn generate_id(&mut self) -> usize;

    fn new() -> Self;
}

struct RandomIdGenerator;

impl IdGenerator for RandomIdGenerator {
    fn generate_id(&mut self) -> usize {
        rand::thread_rng().gen_range(0, 16383)
    }

    fn new() -> Self {
        RandomIdGenerator
    }
}

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

    struct SequenceIdGenerator {
        id: usize,
    }

    impl IdGenerator for SequenceIdGenerator {
        fn generate_id(&mut self) -> usize {
            self.id += 1;
            self.id
        }

        fn new() -> Self {
            SequenceIdGenerator { id: 0 }
        }
    }

    #[test]
    fn add_coder_no_more_than_once() {
        let mut app = create_application();

        let resp = app.need_reviewer("coder1".to_owned());
        assert_eq!(resp, FindReviewerResponse::Accepted {});

        let resp = app.need_reviewer("coder1".to_owned());
        assert_eq!(resp, FindReviewerResponse::AlreadyRegistered {});

        let resp = app.need_reviewer("coder2".to_owned());
        assert_eq!(resp, FindReviewerResponse::Accepted {});

        let resp = app.need_reviewer("coder3".to_owned());
        assert_eq!(resp, FindReviewerResponse::Accepted {});

        let resp = app.need_reviewer("coder2".to_owned());
        assert_eq!(resp, FindReviewerResponse::AlreadyRegistered {});

        let resp = app.need_reviewer("coder3".to_owned());
        assert_eq!(resp, FindReviewerResponse::AlreadyRegistered {});
    }

    use rand::SeedableRng;

    #[test]
    fn respect_wip_limit() {
        rand::weak_rng().reseed([1, 2, 3, 4]);

        let mut app = create_application();

        let resp = app.need_reviewer("coder1".to_owned());
        assert_eq!(resp, FindReviewerResponse::Accepted {});

        let resp = app.need_reviewer("coder2".to_owned());
        assert_eq!(resp, FindReviewerResponse::Accepted {});

        let resp = app.need_reviewer("coder3".to_owned());
        assert_eq!(resp, FindReviewerResponse::Accepted {});

        let resp = app.need_reviewer("coder4".to_owned());
        assert_eq!(resp, FindReviewerResponse::Accepted {});

        let resp = app.need_reviewer("coder5".to_owned());
        assert_eq!(resp, FindReviewerResponse::Accepted {});

        let resp = app.need_reviewer("coder6".to_owned());
        assert_eq!(
            resp,
            FindReviewerResponse::NeedsReviewer {
                coder: "coder3".to_owned(),
                review_id: 1,
            }
        );
    }

    fn create_application() -> Application<SequenceIdGenerator> {
        Application::new(ApplicationConfiguration::default())
    }
}
<commit_msg>Changed test: respected_wip_limit amended to use the application's dispatch mechanism<commit_after>extern crate iron;
extern crate mount;
extern crate rand;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate staticfile;

use iron::method::Method;
use iron::prelude::*;
use iron::status::Status;
use mount::Mount;
use rand::Rng;
use staticfile::Static;
use std::collections::HashMap;
use std::collections::HashSet;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;
use std::time::Duration;
use std::time::Instant;

static CONFIG_FILE_NAME: &str = "find-reviewer.json";

#[derive(Debug, Deserialize, Eq, PartialEq)]
enum FindReviewerRequest {
    NeedReviewer { coder: String },
    HaveTimeForReview { reviewer: String },
    WillReview { review_id: usize },
    WontReview { review_id: usize },
}

#[derive(Debug, Eq, PartialEq, Serialize)]
enum FindReviewerResponse {
    Accepted {},
    NoReviewerNeeded {},
    AlreadyRegistered {},
    NeedsReviewer { coder: String, review_id: usize },
    ReviewNotFound {},
}

type SharedApplication = Arc<Mutex<Application<RandomIdGenerator>>>;

fn main() {
    let configuration = load_configuration();
    let application = SharedApplication::new(Mutex::new(Application::new(configuration)));

    let mut mount = Mount::new();
    mount
        .mount("/find-reviewer", {
            let application = application.clone();
            move |request: &mut Request| Ok(process_request(request, &application))
        })
        .mount("/", Static::new(Path::new("www")));

    thread::spawn(move || loop {
        thread::sleep(Duration::from_secs(1));
        application.lock().unwrap().process_timeouts();
    });

    Iron::new(mount).http("localhost:3000").unwrap();
}

fn load_configuration() -> ApplicationConfiguration {
    let config = File::open(CONFIG_FILE_NAME)
        .map(|open_file| serde_json::from_reader(open_file).expect(&format!("Could not parse {}", CONFIG_FILE_NAME)))
        .unwrap_or_else(|err| {
            println!("Could not read {}: {}\nFile will be created", CONFIG_FILE_NAME, err);
            ApplicationConfiguration::default()
        });

    File::create(CONFIG_FILE_NAME)
        .map(|created_file| {
            serde_json::to_writer_pretty(created_file, &config).expect(&format!("Could not serialize {}", CONFIG_FILE_NAME))
        })
        .unwrap_or_else(|err| println!("Could not write {}: {}", CONFIG_FILE_NAME, err));

    config
}

fn process_request(request: &mut Request, application: &SharedApplication) -> Response {
    if request.method != Method::Post {
        return Response::with((Status::BadRequest, "Must be a POST request"));
    }

    let parsed = match serde_json::from_reader(request.body.by_ref()) {
        Err(message) => return Response::with((Status::BadRequest, format!("JSON error: {}", message))),
        Ok(request) => request,
    };

    let response = dispatch_request(&mut application.lock().unwrap(), parsed);

    Response::with((Status::Ok, serde_json::to_string_pretty(&response).unwrap()))
}

fn dispatch_request<G: IdGenerator>(application: &mut Application<G>, request: FindReviewerRequest) -> FindReviewerResponse {
    match request {
        FindReviewerRequest::NeedReviewer { coder } => application.need_reviewer(coder),
        FindReviewerRequest::HaveTimeForReview { reviewer } => application.have_time_for_review(&reviewer),
        FindReviewerRequest::WillReview { review_id } => application.will_review(review_id),
        FindReviewerRequest::WontReview { review_id } => application.wont_review(review_id),
    }
}

use std::collections::hash_map::DefaultHasher;
use std::hash::BuildHasherDefault;

struct Application<G> {
    configuration: ApplicationConfiguration,
    waiting_coders: HashSet<String, BuildHasherDefault<DefaultHasher>>,
    active_reviews: HashMap<usize, Review>,
    id_generator: G,
}

#[derive(Serialize, Deserialize)]
struct ApplicationConfiguration {
    timeout: Duration,
    wip_limit: usize,
}

impl Default for ApplicationConfiguration {
    fn default() -> Self {
        ApplicationConfiguration {
            timeout: Duration::from_secs(30),
            wip_limit: 5,
        }
    }
}

#[derive(Clone)]
struct Review {
    pub coder: String,
    pub enqueued_coder: Option<String>,
    pub started: Instant,
}

impl<G: IdGenerator> Application<G> {
    fn new(configuration: ApplicationConfiguration) -> Application<G> {
        Application {
            configuration,
            waiting_coders: HashSet::default(),
            active_reviews: HashMap::default(),
            id_generator: G::new(),
        }
    }

    fn need_reviewer(&mut self, incoming_coder: String) -> FindReviewerResponse {
        if self.is_already_registered(&incoming_coder) {
            FindReviewerResponse::AlreadyRegistered {}
        } else if self.waiting_coders.len() >= self.configuration.wip_limit {
            let random_waiting_coder = self.waiting_coders.iter().next().unwrap().clone();
            self.start_review(random_waiting_coder, Some(incoming_coder))
        } else {
            self.waiting_coders.insert(incoming_coder);
            FindReviewerResponse::Accepted {}
        }
    }

    fn is_already_registered(&self, coder: &String) -> bool {
        self.waiting_coders.contains(coder) ||
            self.active_reviews
                .values()
                .any(|review| &review.coder == coder || review.enqueued_coder.as_ref() == Some(coder))
    }

    fn have_time_for_review(&mut self, incoming_reviewer: &String) -> FindReviewerResponse {
        let random_coder_except_incoming_reviewer = self.waiting_coders
            .iter()
            .filter(|&coder| coder != incoming_reviewer)
            .next()
            .cloned();
        match random_coder_except_incoming_reviewer {
            Some(coder) => self.start_review(coder, None),
            None => FindReviewerResponse::NoReviewerNeeded {},
        }
    }

    fn start_review(&mut self, coder: String, enqueued_coder: Option<String>) -> FindReviewerResponse {
        let review = Review {
            coder: coder.clone(),
            enqueued_coder,
            started: Instant::now(),
        };
        self.remove_coder(&coder);
        let review_id = self.insert_review(review);

        FindReviewerResponse::NeedsReviewer { coder, review_id }
    }

    fn generate_id(&mut self) -> usize {
        loop {
            let id = self.id_generator.generate_id();
            if !self.active_reviews.contains_key(&id) {
                return id;
            }
        }
    }

    fn will_review(&mut self, review_id: usize) -> FindReviewerResponse {
        match self.active_reviews.remove(&review_id) {
            Some(review) => {
                review
                    .enqueued_coder
                    .map(|coder| self.waiting_coders.insert(coder));
                FindReviewerResponse::Accepted {}
            }
            None => FindReviewerResponse::ReviewNotFound {},
        }
    }

    fn wont_review(&mut self, review_id: usize) -> FindReviewerResponse {
        match self.active_reviews.remove(&review_id) {
            Some(review) => {
                self.waiting_coders.insert(review.coder);
                FindReviewerResponse::Accepted {}
            }
            None => FindReviewerResponse::ReviewNotFound {},
        }
    }

    fn remove_coder(&mut self, coder: &String) {
        self.waiting_coders.remove(coder);
    }

    fn insert_review(&mut self, review: Review) -> usize {
        let id = self.generate_id();
        self.active_reviews.insert(id, review);
        id
    }

    fn process_timeouts(&mut self) {
        let now = Instant::now();
        let timed_out: Vec<_> = self.active_reviews
            .iter()
            .filter(|&(_, review)| (now - review.started) > self.configuration.timeout)
            .map(|(&id, _)| id)
            .collect();
        for review_id in timed_out {
            self.wont_review(review_id);
        }
    }
}

trait IdGenerator {
    fn generate_id(&mut self) -> usize;

    fn new() -> Self;
}

struct RandomIdGenerator;

impl IdGenerator for RandomIdGenerator {
    fn generate_id(&mut self) -> usize {
        rand::thread_rng().gen_range(0, 16383)
    }

    fn new() -> Self {
        RandomIdGenerator
    }
}

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

    struct SequenceIdGenerator {
        id: usize,
    }

    impl IdGenerator for SequenceIdGenerator {
        fn generate_id(&mut self) -> usize {
            self.id += 1;
            self.id
        }

        fn new() -> Self {
            SequenceIdGenerator { id: 0 }
        }
    }

    #[test]
    fn add_coder_no_more_than_once() {
        let mut app = create_application();

        let resp = app.need_reviewer("coder1".to_owned());
        assert_eq!(resp, FindReviewerResponse::Accepted {});

        let resp = app.need_reviewer("coder1".to_owned());
        assert_eq!(resp, FindReviewerResponse::AlreadyRegistered {});

        let resp = app.need_reviewer("coder2".to_owned());
        assert_eq!(resp, FindReviewerResponse::Accepted {});

        let resp = app.need_reviewer("coder3".to_owned());
        assert_eq!(resp, FindReviewerResponse::Accepted {});

        let resp = app.need_reviewer("coder2".to_owned());
        assert_eq!(resp, FindReviewerResponse::AlreadyRegistered {});

        let resp = app.need_reviewer("coder3".to_owned());
        assert_eq!(resp, FindReviewerResponse::AlreadyRegistered {});
    }

    #[test]
    fn respect_wip_limit() {
        let mut application = create_application();
        let coder_set = (0..5).map(|x| format!("coder{}", x) ).collect::<HashSet<_>>();
        let coder_set_cloned = coder_set.clone();
        for coder in coder_set {
            let review_request = FindReviewerRequest::NeedReviewer {
                coder,
            };
            assert_eq!(dispatch_request(&mut application, review_request), FindReviewerResponse::Accepted {});
        }
        let answer = dispatch_request(
            &mut application,
            FindReviewerRequest::NeedReviewer {
                coder: format!("anothercoder"),
            },
        );
        match answer{
            FindReviewerResponse::NeedsReviewer {coder: coder_name,review_id: _} =>
                assert!(coder_set_cloned.iter().any(|x| *x==coder_name)),
            _ => panic!(),
        }
    }

    fn create_application() -> Application<SequenceIdGenerator> {
        Application::new(ApplicationConfiguration::default())
    }
}
<|endoftext|>


<commit_before>use syntax::ast;
use syntax::codemap;
use syntax::ext::base;
use syntax::parse::token;
use syntax::parse::parser::Parser;

use html::HtmlState;
use html::SubTag;
use html::RawHtml;
use html::RawRust;

/// Trait that means something can be parsed with a configuration.
pub trait Parse<Cfg> {
    /// Parse Self from a Parser and a configuration object.
    fn parse(&mut Parser, Cfg) -> Self;
}

const TEMPLATE:      &'static str = "template";
const RUST:          &'static str = "rust";
const END:           &'static str = "end";

///
///
///
fn parse_start_template(state: &mut HtmlState, parser: &mut Parser) {

    match (
        parser.parse_ident(),
        parser.parse_fn_decl(true),
        parser.bump_and_get(),
        parser.bump_and_get()
    ) {
        (
            functioname,
            ref function,
            token::BINOP(token::PERCENT),
            token::GT
        ) => {
            state.name = Some(functioname);
            println!("found template beginning")
        },

        (one, two, three, four) => {
            parser.fatal(format!(
                "Expected `<% template xxx() %>`, found <% template {} {} {}{}",
                one,
                two,
                three,
                four
            ).as_slice());
        }
    };
}

///
///
///
fn parse_end_template(parser: &mut Parser) {

    match (
        parser.parse_ident().as_str(),
        parser.bump_and_get(),
        parser.bump_and_get()
    ) {
        (
            template,
            token::BINOP(token::PERCENT),
            token::GT
        ) if template == TEMPLATE => { println!("found end template")},

        (one, two, three) => {
            parser.fatal(format!(
                "Expected `<% end template %>`, found <% end {} {}{}",
                one,
                Parser::token_to_string(&two),
                Parser::token_to_string(&three),
            ).as_slice());
        }
    };

}

///
///
///
fn is_template_tag_start (
    parser: &Parser,
    last_token: Option<token::Token>
) -> bool {

    if parser.token != token::BINOP(token::PERCENT) {
        return false;
    }

    match last_token {
        None => return false,
        Some(value) => {
            return value == token::LT;
        }
    }

}

/// Extract as raw text the content between two spans
///
fn block_to_string(
    context: &base::ExtCtxt,
    start_span: &codemap::Span,
    end_span: &codemap::Span
) -> String {

    let tmp_span = codemap::Span {
        lo: start_span.lo,
        hi: end_span.lo,
        expn_id: end_span.expn_id
    };

    context.codemap().span_to_snippet(tmp_span).unwrap_or(String::new())

}

/// Parse the inside of a orphan rust tag
/// TODO: implement it, for the moment we simply "consume" the inside
fn parse_rust_tag (
    parser: &mut Parser,
    context: &base::ExtCtxt
) -> SubTag {

    while parser.token != token::EOF {

        if parser.token == token::BINOP(token::PERCENT) {
            if parser.look_ahead(1, |token| *token == token::GT) {

                //TODO: certainly a better way to do "consume % and >"
                parser.bump();
                parser.bump();
                return RawRust(String::new());
            }
        }
        parser.bump();
    }

    RawRust(String::new())
}

/// Parse the content inside a <% template xxx() %> tag
/// and return when we've finished to parse the <% end template %>
/// if we have parsed all tokens without seeing <% end template %>
/// we quit with error
fn parse_inner_template (
    parser: &mut Parser,
    context: &base::ExtCtxt
) -> Vec<SubTag> {

    let mut sub_tags = Vec::new();

    let mut last_token = None;
    // to know when we have a piece of HTML to display as it
    let mut start_html_block = parser.span.clone();
    let mut end_html_block = parser.span.clone();

    while parser.token != token::EOF {

        if !is_template_tag_start(parser, last_token) {
            // we update endspan everytime as we're not sure
            // when a span will be the last one
            end_html_block = parser.span.clone();
            last_token = Some(parser.token.clone());
            parser.bump();
            continue;
        }
        last_token = Some(parser.token.clone());
        parser.bump();

        // the beginning of a tag implies that the current raw html block
        // is finished
        let inner_string = block_to_string(
            context,
            &start_html_block,
            &end_html_block
        );
        sub_tags.push(RawHtml(inner_string));

        match parser.parse_ident().as_str() {
            TEMPLATE => parser.fatal("<% template %> can't be nested"),
            RUST => sub_tags.push(parse_rust_tag(parser, context)),
            END => {
                parse_end_template(parser);
                return sub_tags;

            },
            _ => parser.fatal("unknown tag"),
        }

        // we start a new raw html block
        start_html_block = parser.span.clone();
        end_html_block = parser.span.clone();
    }

    parser.fatal("template tag opened but not closed");
}

///
///
///
impl<'a, 'b> Parse<(
    codemap::Span,
    &'a mut base::ExtCtxt<'b>,
    Option<ast::Ident>
)> for HtmlState {

    ///
    ///
    ///
    fn parse(
        parser: &mut Parser,
        (_, context, name): (codemap::Span, &'a mut base::ExtCtxt, Option<ast::Ident>)
    ) -> HtmlState {

        let mut state = HtmlState::new(name);

        println!("parser");

        let mut last_token = None;

        while parser.token != token::EOF {

            if !is_template_tag_start(parser, last_token) {
                // we update endspan everytime as we're not sure
                // when a span will be the last one
                last_token = Some(parser.token.clone());
                parser.bump();
                continue;
            }

            last_token = Some(parser.token.clone());
            parser.bump();
            //TODO handle token::LE (see how they've done for brain_fuck macro
            match parser.parse_ident().as_str() {

                TEMPLATE => {
                    parse_start_template(&mut state, parser);
                    state.sub_tags = parse_inner_template(parser, context);
                },
                END => {
                    parser.fatal("<% end template %> found without opening tag");
                },
                otherwise => {
                    let span = parser.last_span;
                    parser.span_fatal(
                        span,
                        format!(
                            "Expected `template` or `end`, but found `{}`",
                            otherwise
                        ).as_slice()
                    );
                }
            }

            last_token = Some(parser.token.clone());
            parser.bump();
        }

        state
    }
}
<commit_msg>removed the used of last_token, instead we use lookahead<commit_after>use syntax::ast;
use syntax::codemap;
use syntax::ext::base;
use syntax::parse::token;
use syntax::parse::parser::Parser;

use html::HtmlState;
use html::SubTag;
use html::RawHtml;
use html::RawRust;

/// Trait that means something can be parsed with a configuration.
pub trait Parse<Cfg> {
    /// Parse Self from a Parser and a configuration object.
    fn parse(&mut Parser, Cfg) -> Self;
}

const TEMPLATE:      &'static str = "template";
const RUST:          &'static str = "rust";
const END:           &'static str = "end";

///
///
///
fn parse_start_template(state: &mut HtmlState, parser: &mut Parser) {

    match (
        parser.parse_ident(),
        parser.parse_fn_decl(true),
        parser.bump_and_get(),
        parser.bump_and_get()
    ) {
        (
            functioname,
            ref function,
            token::BINOP(token::PERCENT),
            token::GT
        ) => {
            state.name = Some(functioname);
            println!("found template beginning")
        },

        (one, two, three, four) => {
            parser.fatal(format!(
                "Expected `<% template xxx() %>`, found <% template {} {} {}{}",
                one,
                two,
                three,
                four
            ).as_slice());
        }
    };
}

///
///
///
fn parse_end_template(parser: &mut Parser) {

    match (
        parser.parse_ident().as_str(),
        parser.bump_and_get(),
        parser.bump_and_get()
    ) {
        (
            template,
            token::BINOP(token::PERCENT),
            token::GT
        ) if template == TEMPLATE => { println!("found end template")},

        (one, two, three) => {
            parser.fatal(format!(
                "Expected `<% end template %>`, found <% end {} {}{}",
                one,
                Parser::token_to_string(&two),
                Parser::token_to_string(&three),
            ).as_slice());
        }
    };

}

///
///
///
fn is_tag_start (
    parser: &mut Parser
) -> bool {

    if parser.token == token::LT {
        let next_is_percent = parser.look_ahead(
            1,
            |token| *token == token::BINOP(token::PERCENT)
        );

        if next_is_percent {
            //TODO: certainly a better way to do "consume < and %"
            parser.bump();
            parser.bump();
            return true;
        }
    }
    return false;
}

/// Extract as raw text the content between two spans
///
fn block_to_string(
    context: &base::ExtCtxt,
    start_span: &codemap::Span,
    end_span: &codemap::Span
) -> String {

    let tmp_span = codemap::Span {
        lo: start_span.lo,
        hi: end_span.lo,
        expn_id: end_span.expn_id
    };

    context.codemap().span_to_snippet(tmp_span).unwrap_or(String::new())

}

/// Parse the inside of a orphan rust tag
/// TODO: implement it, for the moment we simply "consume" the inside
fn parse_rust_tag (
    parser: &mut Parser,
    context: &base::ExtCtxt
) -> SubTag {

    while parser.token != token::EOF {

        if parser.token == token::BINOP(token::PERCENT) {
            if parser.look_ahead(1, |token| *token == token::GT) {

                //TODO: certainly a better way to do "consume % and >"
                parser.bump();
                parser.bump();
                return RawRust(String::new());
            }
        }
        parser.bump();
    }

    RawRust(String::new())
}

/// Parse the content inside a <% template xxx() %> tag
/// and return when we've finished to parse the <% end template %>
/// if we have parsed all tokens without seeing <% end template %>
/// we quit with error
fn parse_inner_template (
    parser: &mut Parser,
    context: &base::ExtCtxt
) -> Vec<SubTag> {

    let mut sub_tags = Vec::new();

    // to know when we have a piece of HTML to display as it
    let mut start_html_block = parser.span.clone();
    let mut end_html_block = parser.span.clone();

    while parser.token != token::EOF {
        if !is_tag_start(parser) {
            // we update endspan everytime as we're not sure
            // when a span will be the last one
            end_html_block = parser.span.clone();
            parser.bump();
            continue;
        }

        // the beginning of a tag implies that the current raw html block
        // is finished
        let inner_string = block_to_string(
            context,
            &start_html_block,
            &end_html_block
        );
        sub_tags.push(RawHtml(inner_string));

        match parser.parse_ident().as_str() {
            TEMPLATE => parser.fatal("<% template %> can't be nested"),
            RUST => sub_tags.push(parse_rust_tag(parser, context)),
            END => {
                parse_end_template(parser);
                return sub_tags;

            },
            _ => parser.fatal("unknown tag"),
        }

        // we start a new raw html block
        start_html_block = parser.span.clone();
        end_html_block = parser.span.clone();
    }

    parser.fatal("template tag opened but not closed");
}

///
///
///
impl<'a, 'b> Parse<(
    codemap::Span,
    &'a mut base::ExtCtxt<'b>,
    Option<ast::Ident>
)> for HtmlState {

    ///
    ///
    ///
    fn parse(
        parser: &mut Parser,
        (_, context, name): (codemap::Span, &'a mut base::ExtCtxt, Option<ast::Ident>)
    ) -> HtmlState {

        let mut state = HtmlState::new(name);

        while parser.token != token::EOF {

            if !is_tag_start(parser) {
                parser.bump();
                continue;
            }

            match parser.parse_ident().as_str() {

                TEMPLATE => {
                    parse_start_template(&mut state, parser);
                    state.sub_tags = parse_inner_template(parser, context);
                },
                END => {
                    parser.fatal("<% end template %> found without opening tag");
                },
                otherwise => {
                    let span = parser.last_span;
                    parser.span_fatal(
                        span,
                        format!(
                            "Expected `template` or `end`, but found `{}`",
                            otherwise
                        ).as_slice()
                    );
                }
            }

            parser.bump();
        }

        state
    }
}
<|endoftext|>


<commit_before>use std::ffi::CStr;
use std::fmt;
use std::marker::PhantomFn;
use std::str;
use libc::{c_char, c_void};
use malloc_buf::MallocBuffer;

use block::Block;
use runtime::{Class, Object, Sel};

#[cfg(target_pointer_width = "64")]
const CODE_INLINE_CAP: usize = 30;

#[cfg(target_pointer_width = "32")]
const CODE_INLINE_CAP: usize = 14;

enum Code {
    Slice(&'static str),
    Owned(String),
    Inline(u8, [u8; CODE_INLINE_CAP]),
    Malloc(MallocBuffer<u8>)
}

/// An Objective-C type encoding.
///
/// For more information, see Apple's documentation:
/// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
pub struct Encoding {
    code: Code,
}

impl Encoding {
    /// Returns self as a `str`.
    pub fn as_str(&self) -> &str {
        match self.code {
            Code::Slice(code) => code,
            Code::Owned(ref code) => code,
            Code::Inline(len, ref bytes) => unsafe {
                str::from_utf8_unchecked(&bytes[..len as usize])
            },
            Code::Malloc(ref buf) => unsafe {
                str::from_utf8_unchecked(&buf[..buf.len() - 1])
            },
        }
    }
}

impl Clone for Encoding {
    fn clone(&self) -> Encoding {
        if let Code::Slice(code) = self.code {
            from_static_str(code)
        } else {
            from_str(self.as_str())
        }
    }
}

impl PartialEq for Encoding {
    fn eq(&self, other: &Encoding) -> bool {
        self.as_str() == other.as_str()
    }
}

impl fmt::Debug for Encoding {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.as_str())
    }
}

pub fn from_static_str(code: &'static str) -> Encoding {
    Encoding { code: Code::Slice(code) }
}

pub fn from_str(code: &str) -> Encoding {
    if code.len() > CODE_INLINE_CAP {
        Encoding { code: Code::Owned(code.to_string()) }
    } else {
        let mut bytes = [0; CODE_INLINE_CAP];
        for (dst, byte) in bytes.iter_mut().zip(code.bytes()) {
            *dst = byte;
        }
        Encoding { code: Code::Inline(code.len() as u8, bytes) }
    }
}

pub unsafe fn from_malloc_str(ptr: *mut c_char) -> Encoding {
    let s = CStr::from_ptr(ptr as *const c_char);
    let bytes = s.to_bytes_with_nul();
    assert!(str::from_utf8(bytes).is_ok());
    let buf = MallocBuffer::new(ptr as *mut u8, bytes.len()).unwrap();
    Encoding { code: Code::Malloc(buf) }
}

/// Types that have an Objective-C type encoding.
///
/// Unsafe because Objective-C will make assumptions about the type (like its
/// size and alignment) from its encoding, so the implementer must verify that
/// the encoding is accurate.
pub unsafe trait Encode : PhantomFn<Self> {
    /// Returns the Objective-C type encoding for Self.
    fn encode() -> Encoding;
}

macro_rules! encode_impls {
    ($($t:ty : $s:expr,)*) => ($(
        unsafe impl Encode for $t {
            fn encode() -> Encoding { from_static_str($s) }
        }
    )*);
}

encode_impls!(
    i8: "c",
    i16: "s",
    i32: "i",
    i64: "q",
    u8: "C",
    u16: "S",
    u32: "I",
    u64: "Q",
    f32: "f",
    f64: "d",
    bool: "B",
    (): "v",
    *mut c_char: "*",
    *const c_char: "r*",
    *mut c_void: "^v",
    *const c_void: "r^v",
    Sel: ":",
);

unsafe impl Encode for isize {
    #[cfg(target_pointer_width = "32")]
    fn encode() -> Encoding { i32::encode() }

    #[cfg(target_pointer_width = "64")]
    fn encode() -> Encoding { i64::encode() }
}

unsafe impl Encode for usize {
    #[cfg(target_pointer_width = "32")]
    fn encode() -> Encoding { u32::encode() }

    #[cfg(target_pointer_width = "64")]
    fn encode() -> Encoding { u64::encode() }
}

macro_rules! encode_message_impl {
    ($code:expr, $name:ident) => (
        encode_message_impl!($code, $name,);
    );
    ($code:expr, $name:ident, $($t:ident),*) => (
        unsafe impl<'a $(, $t)*> $crate::Encode for &'a $name<$($t),*> {
            fn encode() -> Encoding { from_static_str($code) }
        }

        unsafe impl<'a $(, $t)*> $crate::Encode for &'a mut $name<$($t),*> {
            fn encode() -> Encoding { from_static_str($code) }
        }

        unsafe impl<'a $(, $t)*> $crate::Encode for Option<&'a $name<$($t),*>> {
            fn encode() -> Encoding { from_static_str($code) }
        }

        unsafe impl<'a $(, $t)*> $crate::Encode for Option<&'a mut $name<$($t),*>> {
            fn encode() -> Encoding { from_static_str($code) }
        }

        unsafe impl<$($t),*> $crate::Encode for *const $name<$($t),*> {
            fn encode() -> Encoding { from_static_str($code) }
        }

        unsafe impl<$($t),*> $crate::Encode for *mut $name<$($t),*> {
            fn encode() -> Encoding { from_static_str($code) }
        }
    );
}

encode_message_impl!("@", Object);

encode_message_impl!("#", Class);

encode_message_impl!("@?", Block, A, R);

#[cfg(test)]
mod tests {
    use runtime::{Class, Object, Sel};
    use super::Encode;

    #[test]
    fn test_encode() {
        assert!(u32::encode().as_str() == "I");
        assert!(<()>::encode().as_str() == "v");
        assert!(<&Object>::encode().as_str() == "@");
        assert!(<*mut Object>::encode().as_str() == "@");
        assert!(<&Class>::encode().as_str() == "#");
        assert!(Sel::encode().as_str() == ":");
    }

    #[test]
    fn test_inline_encoding() {
        let enc = super::from_str("C");
        assert!(enc.as_str() == "C");

        let enc2 = enc.clone();
        assert!(enc2 == enc);
        assert!(enc2.as_str() == "C");
    }

    #[test]
    fn test_owned_encoding() {
        let s = "{Test=CCCCCCCCCCCCCCCCCCCCCCCCC}";
        let enc = super::from_str(s);
        assert!(enc.as_str() == s);

        let enc2 = enc.clone();
        assert!(enc2 == enc);
        assert!(enc2.as_str() == s);
    }
}
<commit_msg>Added a from_str method for Encoding.<commit_after>use std::ffi::CStr;
use std::fmt;
use std::marker::PhantomFn;
use std::str;
use libc::{c_char, c_void};
use malloc_buf::MallocBuffer;

use block::Block;
use runtime::{Class, Object, Sel};

#[cfg(target_pointer_width = "64")]
const CODE_INLINE_CAP: usize = 30;

#[cfg(target_pointer_width = "32")]
const CODE_INLINE_CAP: usize = 14;

enum Code {
    Slice(&'static str),
    Owned(String),
    Inline(u8, [u8; CODE_INLINE_CAP]),
    Malloc(MallocBuffer<u8>)
}

/// An Objective-C type encoding.
///
/// For more information, see Apple's documentation:
/// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
pub struct Encoding {
    code: Code,
}

impl Encoding {
    /// Constructs an `Encoding` from its string representation.
    /// Unsafe because the caller must ensure the string is a valid encoding.
    pub unsafe fn from_str(code: &str) -> Encoding {
        from_str(code)
    }

    /// Returns self as a `str`.
    pub fn as_str(&self) -> &str {
        match self.code {
            Code::Slice(code) => code,
            Code::Owned(ref code) => code,
            Code::Inline(len, ref bytes) => unsafe {
                str::from_utf8_unchecked(&bytes[..len as usize])
            },
            Code::Malloc(ref buf) => unsafe {
                str::from_utf8_unchecked(&buf[..buf.len() - 1])
            },
        }
    }
}

impl Clone for Encoding {
    fn clone(&self) -> Encoding {
        if let Code::Slice(code) = self.code {
            from_static_str(code)
        } else {
            from_str(self.as_str())
        }
    }
}

impl PartialEq for Encoding {
    fn eq(&self, other: &Encoding) -> bool {
        self.as_str() == other.as_str()
    }
}

impl fmt::Debug for Encoding {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.as_str())
    }
}

pub fn from_static_str(code: &'static str) -> Encoding {
    Encoding { code: Code::Slice(code) }
}

pub fn from_str(code: &str) -> Encoding {
    if code.len() > CODE_INLINE_CAP {
        Encoding { code: Code::Owned(code.to_string()) }
    } else {
        let mut bytes = [0; CODE_INLINE_CAP];
        for (dst, byte) in bytes.iter_mut().zip(code.bytes()) {
            *dst = byte;
        }
        Encoding { code: Code::Inline(code.len() as u8, bytes) }
    }
}

pub unsafe fn from_malloc_str(ptr: *mut c_char) -> Encoding {
    let s = CStr::from_ptr(ptr as *const c_char);
    let bytes = s.to_bytes_with_nul();
    assert!(str::from_utf8(bytes).is_ok());
    let buf = MallocBuffer::new(ptr as *mut u8, bytes.len()).unwrap();
    Encoding { code: Code::Malloc(buf) }
}

/// Types that have an Objective-C type encoding.
///
/// Unsafe because Objective-C will make assumptions about the type (like its
/// size and alignment) from its encoding, so the implementer must verify that
/// the encoding is accurate.
pub unsafe trait Encode : PhantomFn<Self> {
    /// Returns the Objective-C type encoding for Self.
    fn encode() -> Encoding;
}

macro_rules! encode_impls {
    ($($t:ty : $s:expr,)*) => ($(
        unsafe impl Encode for $t {
            fn encode() -> Encoding { from_static_str($s) }
        }
    )*);
}

encode_impls!(
    i8: "c",
    i16: "s",
    i32: "i",
    i64: "q",
    u8: "C",
    u16: "S",
    u32: "I",
    u64: "Q",
    f32: "f",
    f64: "d",
    bool: "B",
    (): "v",
    *mut c_char: "*",
    *const c_char: "r*",
    *mut c_void: "^v",
    *const c_void: "r^v",
    Sel: ":",
);

unsafe impl Encode for isize {
    #[cfg(target_pointer_width = "32")]
    fn encode() -> Encoding { i32::encode() }

    #[cfg(target_pointer_width = "64")]
    fn encode() -> Encoding { i64::encode() }
}

unsafe impl Encode for usize {
    #[cfg(target_pointer_width = "32")]
    fn encode() -> Encoding { u32::encode() }

    #[cfg(target_pointer_width = "64")]
    fn encode() -> Encoding { u64::encode() }
}

macro_rules! encode_message_impl {
    ($code:expr, $name:ident) => (
        encode_message_impl!($code, $name,);
    );
    ($code:expr, $name:ident, $($t:ident),*) => (
        unsafe impl<'a $(, $t)*> $crate::Encode for &'a $name<$($t),*> {
            fn encode() -> Encoding { from_static_str($code) }
        }

        unsafe impl<'a $(, $t)*> $crate::Encode for &'a mut $name<$($t),*> {
            fn encode() -> Encoding { from_static_str($code) }
        }

        unsafe impl<'a $(, $t)*> $crate::Encode for Option<&'a $name<$($t),*>> {
            fn encode() -> Encoding { from_static_str($code) }
        }

        unsafe impl<'a $(, $t)*> $crate::Encode for Option<&'a mut $name<$($t),*>> {
            fn encode() -> Encoding { from_static_str($code) }
        }

        unsafe impl<$($t),*> $crate::Encode for *const $name<$($t),*> {
            fn encode() -> Encoding { from_static_str($code) }
        }

        unsafe impl<$($t),*> $crate::Encode for *mut $name<$($t),*> {
            fn encode() -> Encoding { from_static_str($code) }
        }
    );
}

encode_message_impl!("@", Object);

encode_message_impl!("#", Class);

encode_message_impl!("@?", Block, A, R);

#[cfg(test)]
mod tests {
    use runtime::{Class, Object, Sel};
    use super::Encode;

    #[test]
    fn test_encode() {
        assert!(u32::encode().as_str() == "I");
        assert!(<()>::encode().as_str() == "v");
        assert!(<&Object>::encode().as_str() == "@");
        assert!(<*mut Object>::encode().as_str() == "@");
        assert!(<&Class>::encode().as_str() == "#");
        assert!(Sel::encode().as_str() == ":");
    }

    #[test]
    fn test_inline_encoding() {
        let enc = super::from_str("C");
        assert!(enc.as_str() == "C");

        let enc2 = enc.clone();
        assert!(enc2 == enc);
        assert!(enc2.as_str() == "C");
    }

    #[test]
    fn test_owned_encoding() {
        let s = "{Test=CCCCCCCCCCCCCCCCCCCCCCCCC}";
        let enc = super::from_str(s);
        assert!(enc.as_str() == s);

        let enc2 = enc.clone();
        assert!(enc2 == enc);
        assert!(enc2.as_str() == s);
    }
}
<|endoftext|>


<commit_before>use std::io::{Write, Result};

use parser::{Ast, Block};

use super::Generator;
use super::ast::{Code};
use super::ast::Statement::{Var, Expr};
use super::ast::Expression::{Call, Name, Str, Attr, Function};

impl<'a, W:Write+'a> Generator<'a, W> {
    pub fn wrap_amd(&self, code: Code, ast: &Ast) -> Code {
        let statements = ast.blocks.iter().flat_map(|b| match b {
            &Block::ImportModule(ref name, ref source)
            => vec!(Var(name.clone(),
                Call(Box::new(Name(String::from("require"))),
                     vec![Str(source.clone())]))
                ).into_iter(),
            &Block::ImportVars(ref items, ref source)
            => {
                let mut res = vec![
                    Var(String::from("_imp"),
                        Call(Box::new(Name(String::from("require"))),
                             vec![Str(source.clone())]))
                ];
                res.extend(
                    items.iter().map(|&(ref name, ref alias)|
                        Var(alias.clone().unwrap_or(name.clone()),
                            Attr(Box::new(Name(String::from("_imp"))),
                                 name.clone()))));
                res.into_iter()
                }
            _ => vec!().into_iter(),
        })
        .chain(code.statements.into_iter()).collect();
        return Code {
            statements: vec![
                Expr(Call(
                    Box::new(Name(String::from("define"))),
                    vec![
                    Str(self.amd_name.to_string()),
                    Function(None, vec!(), statements)])),
            ],
        };
    }
}
<commit_msg>Declare dependencies in "define" instead using require()<commit_after>use std::io::{Write, Result};
use std::collections::HashMap;

use parser::{Ast, Block};

use super::Generator;
use super::ast::{Code, Param};
use super::ast::Statement::{Var, Expr};
use super::ast::Expression::{Call, Name, Str, Attr, Function, List};

fn string_to_ident(src: &str) -> String {
    let mut res = String::new();
    res.push_str("_mod_");
    for ch in src.chars() {
        match ch {
            'a'...'z'|'A'...'Z'|'0'...'9'|'_' => res.push(ch),
            _ => res.push('_'),
        }
    }
    return res;
}

impl<'a, W:Write+'a> Generator<'a, W> {
    pub fn wrap_amd(&self, code: Code, ast: &Ast) -> Code {
        let mut code_prefix = vec![];
        let mut dependencies = vec![
            Str(String::from("require")),
            Str(String::from("exports")),
            ];
        let mut arguments = vec![
            Param { name: String::from("require"), default_value: None },
            Param { name: String::from("exports"), default_value: None },
        ];
        let mut modules = HashMap::new();
        for block in ast.blocks.iter() {
            match block {
                &Block::ImportModule(ref name, ref source) => {
                    dependencies.push(Str(source.clone()));
                    arguments.push(Param {
                        name: name.clone(),
                        default_value: None,
                        });
                }
                &Block::ImportVars(ref items, ref source) => {
                    if !modules.contains_key(source) {
                        let varname = string_to_ident(source);
                        arguments.push(Param {
                            name: varname.clone(),
                            default_value: None,
                            });
                        modules.insert(source, varname);
                        dependencies.push(Str(source.clone()));
                    }
                    let varname = &modules[source];
                    for &(ref name, ref alias) in items.iter() {
                        code_prefix.push(
                            Var(alias.as_ref().unwrap_or(name).clone(),
                                Attr(Box::new(Name(varname.clone())),
                                     name.clone())));
                    }
                }
                _ => {}
            }
        }
        code_prefix.extend(code.statements.into_iter());
        return Code {
            statements: vec![
                Expr(Call(Box::new(Name(String::from("define"))), vec![
                    Str(self.amd_name.to_string()),
                    List(dependencies),
                    Function(None, arguments, code_prefix)
                ])),
            ],
        };
    }
}
<|endoftext|>


<commit_before>extern crate chrono;
extern crate clap;
extern crate hostname;
#[macro_use]
extern crate log;
extern crate log4rs;
extern crate target_info;
extern crate xdg;

mod command_line_arguments;
mod context;
mod datetime;
mod execution_timer;
mod program_info;
mod qork;
mod system_info;

use xdg::BaseDirectories;

use context::Context;
use execution_timer::ExecutionTimer;

fn main() {
    std::env::set_var("IN_QORK", "1");
    let context = Context::new();
    configure_logging(context.xdg());
    let _timer = ExecutionTimer::with_start_message("main.main");
    context.log_created_message();
    load_user_configuration_if_valid(&context);
}

fn configure_logging(xdg: &BaseDirectories) {
    let path = xdg.place_config_file("logging.toml");

    match path {
        Ok(p) => {
            if p.exists() {
                log4rs::init_file(&p, Default::default()).unwrap();
                info!("Logging initialized using file at {:?}", &p);
            }
            else {
            }
        },
        Err(_) => {
            // Do nothing, not sure there is anything we can do.
        }
    }
}

fn load_user_configuration_if_valid(context: &Context) {
    if context.program_info().parsed_args().load_config() {
        let _timer = ExecutionTimer::with_start_message("main.load_user_configuration_if_valid");
        let dir = context.xdg().get_config_home();
        if !dir.exists() {
            warn!("The config_directory does not exist, no config will be loaded, config_directory={:?}", dir);
            return
        }

        if !dir.is_dir() {
            warn!("The config_directory is a file, not a directory, no config will be loaded, config_directory={:?}", dir);
            return
        }

        load_user_configuration(context);

    } else {
        info!("Loading of user configuration is disabled.");
    }
}

fn load_user_configuration(context: &Context) {
    let _timer = ExecutionTimer::with_start_message("main.load_user_configuration");
}

<commit_msg>Delete unnecessary timer.<commit_after>extern crate chrono;
extern crate clap;
extern crate hostname;
#[macro_use]
extern crate log;
extern crate log4rs;
extern crate target_info;
extern crate xdg;

mod command_line_arguments;
mod context;
mod datetime;
mod execution_timer;
mod program_info;
mod qork;
mod system_info;

use xdg::BaseDirectories;

use context::Context;
use execution_timer::ExecutionTimer;

fn main() {
    std::env::set_var("IN_QORK", "1");
    let context = Context::new();
    configure_logging(context.xdg());
    let _timer = ExecutionTimer::with_start_message("main.main");
    context.log_created_message();
    load_user_configuration_if_valid(&context);
}

fn configure_logging(xdg: &BaseDirectories) {
    let path = xdg.place_config_file("logging.toml");

    match path {
        Ok(p) => {
            if p.exists() {
                log4rs::init_file(&p, Default::default()).unwrap();
                info!("Logging initialized using file at {:?}", &p);
            }
            else {
            }
        },
        Err(_) => {
            // Do nothing, not sure there is anything we can do.
        }
    }
}

fn load_user_configuration_if_valid(context: &Context) {
    if context.program_info().parsed_args().load_config() {
        let dir = context.xdg().get_config_home();
        if !dir.exists() {
            warn!("The config_directory does not exist, no config will be loaded, config_directory={:?}", dir);
            return
        }

        if !dir.is_dir() {
            warn!("The config_directory is a file, not a directory, no config will be loaded, config_directory={:?}", dir);
            return
        }

        load_user_configuration(context);
    } else {
        info!("Loading of user configuration is disabled.");
    }
}

fn load_user_configuration(context: &Context) {
    let _timer = ExecutionTimer::with_start_message("main.load_user_configuration");
}

<|endoftext|>


<commit_before>use std::fmt;
use std::iter;
use std::f64;

use adivon::bag::Bag;
use adivon::stack::Stack;
use adivon::priority_queue::IndexMinPQ;

/// Weighted directed edge
#[derive(Clone, Copy)]
pub struct DirectedEdge {
    v: usize,
    w: usize,
    weight: f64
}

impl DirectedEdge {
    pub fn new(v: usize, w: usize, weight: f64) -> DirectedEdge {
        assert!(!weight.is_nan(), "weight is NaN");
        DirectedEdge {
            v: v,
            w: w,
            weight: weight
        }
    }

    #[inline]
    pub fn from(&self) -> usize {
        self.v
    }

    #[inline]
    pub fn to(&self) -> usize {
        self.w
    }

    #[inline]
    pub fn weight(&self) -> f64 {
        self.weight
    }
}

impl fmt::Debug for DirectedEdge {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} -> {} {:5.2}", self.v, self.w, self.weight)
    }
}

#[test]
fn test_directed_edge() {
    let e = DirectedEdge::new(12, 24, 3.14);
    assert_eq!(format!("{:?}", e), "12 -> 24  3.14");
}

/// Edge-weighted digraph, implemented using adjacency lists
#[derive(Clone)]
pub struct EdgeWeightedDigraph {
    v: usize,
    e: usize,
    adj: Vec<Bag<DirectedEdge>>
}

impl EdgeWeightedDigraph {
    pub fn new(v: usize) -> EdgeWeightedDigraph {
        EdgeWeightedDigraph {
            v: v,
            e: 0,
            adj: iter::repeat(Bag::new()).take(v).collect()
        }
    }

    pub fn v(&self) -> usize {
        self.v
    }

    pub fn e(&self) -> usize {
        self.e
    }

    #[inline]
    fn validate_vertex(&self, v: usize) {
        assert!(v < self.v, "vertex must be between 0 and V");
    }

    pub fn add_edge(&mut self, e: DirectedEdge) {
        let v = e.from();
        let w = e.to();
        self.validate_vertex(v);
        self.validate_vertex(w);
        self.adj[v].add(e);
        self.e += 1;
    }

    pub fn adj(&self, v: usize) -> ::std::vec::IntoIter<DirectedEdge> {
        self.validate_vertex(v);
        self.adj[v].iter().map(|e| e.clone()).collect::<Vec<DirectedEdge>>().into_iter()
    }

    pub fn outdegree(&self, v: usize) -> usize {
        self.validate_vertex(v);
        self.adj[v].len()
    }

    pub fn edges(&self) -> ::std::vec::IntoIter<DirectedEdge> {
        self.adj.iter()
            .flat_map(|adj| {
                adj.iter().map(|e| e.clone()).collect::<Vec<DirectedEdge>>().into_iter()
            })
            .collect::<Vec<DirectedEdge>>()
            .into_iter()
    }

    pub fn to_dot(&self) -> String {
        let mut dot = String::new();

        dot.push_str("digraph G {\n");
        for i in 0 .. self.v {
            dot.push_str(&format!("  {};\n", i));
        }

        for e in self.edges() {
            let v = e.from();
            let w = e.to();
            dot.push_str(&format!("  {} -> {} [ label=\"{}\" ];\n",
                                  v, w, e.weight))
        }
        dot.push_str("}\n");
        dot
    }
}

// Single-source shortest paths API
pub struct DijkstraSP<'a> {
    graph: &'a EdgeWeightedDigraph,
    dist_to: Vec<f64>,
    edge_to: Vec<Option<DirectedEdge>>,
    pq: IndexMinPQ<f64>,
    s: usize
}

impl<'a> DijkstraSP<'a> {
    fn new<'b>(graph: &'b EdgeWeightedDigraph, s: usize) -> DijkstraSP<'b> {
        let n = graph.v();
        for e in graph.edges() {
            if e.weight() < 0.0 {
                panic!("edge has negative weight in DijkstraSP");
            }
        }
        let dist_to = iter::repeat(f64::INFINITY).take(n).collect();
        let edge_to = iter::repeat(None).take(n).collect();
        let pq = IndexMinPQ::with_capacity(n);
        let mut sp = DijkstraSP {
            graph: graph,
            s: s,
            dist_to: dist_to,
            edge_to: edge_to,
            pq: pq
        };
        // alogrithm
        sp.dist_to[s] = 0.0;

        sp.pq.insert(s, 0.0);
        while !sp.pq.is_empty() {
            let v = sp.pq.del_min().unwrap();
            for e in graph.adj(v) {
                sp.relax(e);
            }
        }

        sp
    }

    fn relax(&mut self, e: DirectedEdge) {
        let v = e.from();
        let w = e.to();
        if self.dist_to[w] > self.dist_to[v] + e.weight() {
            self.dist_to[w] = self.dist_to[v] + e.weight();
            self.edge_to[w] = Some(e);
            if self.pq.contains(w) {
                self.pq.decrease_key(w, self.dist_to[w]);
            } else {
                self.pq.insert(w, self.dist_to[w]);
            }
        }
    }

    // length of shortest path from s to v
    pub fn dist_to(&self, v: usize) -> f64 {
        self.dist_to[v]
    }

    pub fn has_path_to(&self, v: usize) -> bool {
        self.dist_to[v] < f64::INFINITY
    }

    // shortest path from s to v
    pub fn path_to(&self, v: usize) -> ::std::vec::IntoIter<DirectedEdge> {
        if !self.has_path_to(v) {
            vec!().into_iter()
        } else {
            let mut path = Stack::new();
            let mut e = self.edge_to[v];
            while e.is_some() {
                path.push(e.unwrap());
                e = self.edge_to[e.unwrap().from()]
            }
            path.into_iter().collect::<Vec<DirectedEdge>>().into_iter()
        }
    }
}

impl EdgeWeightedDigraph {
    pub fn dijkstra_sp<'a>(&'a self, s: usize) -> DijkstraSP<'a> {
        DijkstraSP::new(self, s)
    }
}

#[test]
fn test_dijkstra_shortest_path() {
    let mut g = EdgeWeightedDigraph::new(6);
    g.add_edge(DirectedEdge::new(0, 1, 7.0));
    g.add_edge(DirectedEdge::new(1, 2, 10.0));
    g.add_edge(DirectedEdge::new(0, 2, 9.0));
    g.add_edge(DirectedEdge::new(0, 5, 14.0));
    g.add_edge(DirectedEdge::new(1, 3, 15.0));
    g.add_edge(DirectedEdge::new(2, 5, 2.0));
    g.add_edge(DirectedEdge::new(2, 3, 11.0));
    g.add_edge(DirectedEdge::new(4, 5, 9.0));
    g.add_edge(DirectedEdge::new(3, 4, 6.0));
    g.add_edge(DirectedEdge::new(2, 2, 1.0));

    assert_eq!(20.0, g.dijkstra_sp(0).dist_to(3));
    assert_eq!(26.0, g.dijkstra_sp(0).path_to(4).map(|e| e.weight()).sum());
}
<commit_msg>shortest_paths: add validation check for DijkstraSP<commit_after>use std::fmt;
use std::iter;
use std::f64;

use adivon::bag::Bag;
use adivon::stack::Stack;
use adivon::priority_queue::IndexMinPQ;

/// Weighted directed edge
#[derive(Clone, Copy)]
pub struct DirectedEdge {
    v: usize,
    w: usize,
    weight: f64
}

impl DirectedEdge {
    pub fn new(v: usize, w: usize, weight: f64) -> DirectedEdge {
        assert!(!weight.is_nan(), "weight is NaN");
        DirectedEdge {
            v: v,
            w: w,
            weight: weight
        }
    }

    #[inline]
    pub fn from(&self) -> usize {
        self.v
    }

    #[inline]
    pub fn to(&self) -> usize {
        self.w
    }

    #[inline]
    pub fn weight(&self) -> f64 {
        self.weight
    }
}

impl fmt::Debug for DirectedEdge {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} -> {} {:5.2}", self.v, self.w, self.weight)
    }
}

#[test]
fn test_directed_edge() {
    let e = DirectedEdge::new(12, 24, 3.14);
    assert_eq!(format!("{:?}", e), "12 -> 24  3.14");
}

/// Edge-weighted digraph, implemented using adjacency lists
#[derive(Clone)]
pub struct EdgeWeightedDigraph {
    v: usize,
    e: usize,
    adj: Vec<Bag<DirectedEdge>>
}

impl EdgeWeightedDigraph {
    pub fn new(v: usize) -> EdgeWeightedDigraph {
        EdgeWeightedDigraph {
            v: v,
            e: 0,
            adj: iter::repeat(Bag::new()).take(v).collect()
        }
    }

    pub fn v(&self) -> usize {
        self.v
    }

    pub fn e(&self) -> usize {
        self.e
    }

    #[inline]
    fn validate_vertex(&self, v: usize) {
        assert!(v < self.v, "vertex must be between 0 and V");
    }

    pub fn add_edge(&mut self, e: DirectedEdge) {
        let v = e.from();
        let w = e.to();
        self.validate_vertex(v);
        self.validate_vertex(w);
        self.adj[v].add(e);
        self.e += 1;
    }

    pub fn adj(&self, v: usize) -> ::std::vec::IntoIter<DirectedEdge> {
        self.validate_vertex(v);
        self.adj[v].iter().map(|e| e.clone()).collect::<Vec<DirectedEdge>>().into_iter()
    }

    pub fn outdegree(&self, v: usize) -> usize {
        self.validate_vertex(v);
        self.adj[v].len()
    }

    pub fn edges(&self) -> ::std::vec::IntoIter<DirectedEdge> {
        self.adj.iter()
            .flat_map(|adj| {
                adj.iter().map(|e| e.clone()).collect::<Vec<DirectedEdge>>().into_iter()
            })
            .collect::<Vec<DirectedEdge>>()
            .into_iter()
    }

    pub fn to_dot(&self) -> String {
        let mut dot = String::new();

        dot.push_str("digraph G {\n");
        for i in 0 .. self.v {
            dot.push_str(&format!("  {};\n", i));
        }

        for e in self.edges() {
            let v = e.from();
            let w = e.to();
            dot.push_str(&format!("  {} -> {} [ label=\"{}\" ];\n",
                                  v, w, e.weight))
        }
        dot.push_str("}\n");
        dot
    }
}

// Single-source shortest paths API
pub struct DijkstraSP<'a> {
    graph: &'a EdgeWeightedDigraph,
    dist_to: Vec<f64>,
    edge_to: Vec<Option<DirectedEdge>>,
    pq: IndexMinPQ<f64>,
    s: usize
}

impl<'a> DijkstraSP<'a> {
    fn new<'b>(graph: &'b EdgeWeightedDigraph, s: usize) -> DijkstraSP<'b> {
        let n = graph.v();
        for e in graph.edges() {
            if e.weight() < 0.0 {
                panic!("edge has negative weight in DijkstraSP");
            }
        }
        let dist_to = iter::repeat(f64::INFINITY).take(n).collect();
        let edge_to = iter::repeat(None).take(n).collect();
        let pq = IndexMinPQ::with_capacity(n);
        let mut sp = DijkstraSP {
            graph: graph,
            s: s,
            dist_to: dist_to,
            edge_to: edge_to,
            pq: pq
        };
        // alogrithm
        sp.dist_to[s] = 0.0;

        sp.pq.insert(s, 0.0);
        while !sp.pq.is_empty() {
            let v = sp.pq.del_min().unwrap();
            for e in graph.adj(v) {
                sp.relax(e);
            }
        }

        sp
    }

    fn relax(&mut self, e: DirectedEdge) {
        let v = e.from();
        let w = e.to();
        if self.dist_to[w] > self.dist_to[v] + e.weight() {
            self.dist_to[w] = self.dist_to[v] + e.weight();
            self.edge_to[w] = Some(e);
            if self.pq.contains(w) {
                self.pq.decrease_key(w, self.dist_to[w]);
            } else {
                self.pq.insert(w, self.dist_to[w]);
            }
        }
    }

    // length of shortest path from s to v
    pub fn dist_to(&self, v: usize) -> f64 {
        self.dist_to[v]
    }

    pub fn has_path_to(&self, v: usize) -> bool {
        self.dist_to[v] < f64::INFINITY
    }

    // shortest path from s to v
    pub fn path_to(&self, v: usize) -> ::std::vec::IntoIter<DirectedEdge> {
        if !self.has_path_to(v) {
            vec!().into_iter()
        } else {
            let mut path = Stack::new();
            let mut e = self.edge_to[v];
            while e.is_some() {
                path.push(e.unwrap());
                e = self.edge_to[e.unwrap().from()]
            }
            path.into_iter().collect::<Vec<DirectedEdge>>().into_iter()
        }
    }

    // FIXME: should this be public?
    pub fn check(&self) -> bool {
        let s = self.s;
        for e in self.graph.edges() {
            if e.weight() < 0.0 {
                return false;
            }
        }

        if self.dist_to[s] != 0.0 || self.edge_to[s].is_some() {
            return false;
        }

        for v in 0 .. self.graph.v() {
            if v == s { continue }
            if self.edge_to[v].is_none() && self.dist_to[v] != f64::INFINITY {
                // dist_to[] edge_to[] inconsistent
                return false;
            }
        }

        for v in 0 .. self.graph.v() {
            for e in self.graph.adj(v) {
                let w  = e.to();
                if self.dist_to[v] + e.weight() < self.dist_to[w] {
                    // edge not relaxed
                    return false;
                }
            }
        }

        for w in 0 .. self.graph.v() {
            if self.edge_to[w].is_none() { continue }
            let e = self.edge_to[w].unwrap();
            let v = e.from();
            if w != e.to() {
                return false;
            }
            if self.dist_to[v] + e.weight() != self.dist_to[w] {
                // edge on shortest path not tight
                return false;
            }
        }

        true
    }
}

impl EdgeWeightedDigraph {
    pub fn dijkstra_sp<'a>(&'a self, s: usize) -> DijkstraSP<'a> {
        DijkstraSP::new(self, s)
    }
}

#[test]
fn test_dijkstra_shortest_path() {
    let mut g = EdgeWeightedDigraph::new(6);
    g.add_edge(DirectedEdge::new(0, 1, 7.0));
    g.add_edge(DirectedEdge::new(1, 2, 10.0));
    g.add_edge(DirectedEdge::new(0, 2, 9.0));
    g.add_edge(DirectedEdge::new(0, 5, 14.0));
    g.add_edge(DirectedEdge::new(1, 3, 15.0));
    g.add_edge(DirectedEdge::new(2, 5, 2.0));
    g.add_edge(DirectedEdge::new(2, 3, 11.0));
    g.add_edge(DirectedEdge::new(4, 5, 9.0));
    g.add_edge(DirectedEdge::new(3, 4, 6.0));
    g.add_edge(DirectedEdge::new(2, 2, 1.0));

    assert_eq!(20.0, g.dijkstra_sp(0).dist_to(3));
    assert_eq!(26.0, g.dijkstra_sp(0).path_to(4).map(|e| e.weight()).sum());

    assert!(g.dijkstra_sp(0).check());
}
<|endoftext|>


<commit_before>//! Store SBD messages on the filesystem.

use std::fs;
use std::path::Path;

use walkdir;

use {Result, Error};
use mo::Message;
use storage;

const SBD_EXTENSION: &'static str = "sbd";

/// A structure for managing storing and retriving SBD messages on a filesystem.
///
/// Messages are stored in a directory hierarchy under a single root directory.
/// Message storage and retrieval are managed by a `Storage` object, which is
/// configured for a single root directory.
#[derive(Debug)]
pub struct Storage<P: AsRef<Path>> {
    root: P,
}

impl<P: AsRef<Path>> Storage<P> {
    /// Opens a new storage for a given directory.
    ///
    /// # Errors
    ///
    /// If the directory does not exist, returns an `NotADirectory` error.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::storage::FilesystemStorage;
    /// let storage = FilesystemStorage::open("data").unwrap();
    /// assert!(FilesystemStorage::open("not/a/directory").is_err());
    /// ```
    pub fn open(root: P) -> Result<Storage<P>> {
        let metadata = try!(fs::metadata(root.as_ref()));
        if !metadata.is_dir() {
            Err(Error::NotADirectory(root.as_ref().as_os_str().to_os_string()))
        } else {
            Ok(Storage { root: root })
        }
    }

    /// Returns a `StorageIterator` over the messages in this storage.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::storage::FilesystemStorage;
    /// for message in FilesystemStorage::open("data").unwrap().iter() {
    ///     println!("{:?}", message);
    /// }
    /// ```
    pub fn iter(&self) -> StorageIterator {
        StorageIterator::new(&self)
    }
}

impl<P: AsRef<Path>> storage::Storage for Storage<P> {
    /// Stores a message on the filesystem.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use sbd::storage::{FilesystemStorage, Storage};
    /// use sbd::mo::Message;
    /// let message: Message = Default::default();
    /// let storage = FilesystemStorage::open("/var/iridium").unwrap();
    /// storage.store(&message);
    /// ```
    fn store(&mut self, message: &Message) -> Result<()> {
        let mut path_buf = self.root.as_ref().to_path_buf();
        path_buf.push(message.imei());
        path_buf.push(message.time_of_session().format("%Y").to_string());
        path_buf.push(message.time_of_session().format("%m").to_string());
        try!(fs::create_dir_all(&path_buf));
        path_buf.push(message.time_of_session()
            .format(&format!("%y%m%d_%H%M%S.{}", SBD_EXTENSION))
            .to_string());
        let mut file = try!(fs::File::create(&path_buf));
        try!(message.write_to(&mut file));
        Ok(())
    }
}

/// An iterator over the messages in a `Storage`.
///
/// For now, this iterator will just return all messages with an `sbd` extension under the root of
/// the storage tree. We could try to get smarter and mirror the pattern logic for saving, but for
/// now that's more work and complexity that we need.
///
/// # Errors
///
/// This iterator's `Item` is a `sbd::Result<Message>`, because a file with an `sbd` extension
/// might not convert to a message successfully.
#[allow(missing_debug_implementations)]
pub struct StorageIterator {
    iter: walkdir::Iter,
}

impl StorageIterator {
    fn new<P: AsRef<Path>>(storage: &Storage<P>) -> StorageIterator {
        StorageIterator { iter: walkdir::WalkDir::new(&storage.root).into_iter() }
    }
}

impl Iterator for StorageIterator {
    type Item = Result<Message>;
    fn next(&mut self) -> Option<Self::Item> {
        self.iter
            .by_ref()
            .skip_while(|r| {
                r.as_ref()
                    .map(|d| d.path().extension().map(|e| e != SBD_EXTENSION).unwrap_or(true))
                    .unwrap_or(true)
            })
            .next()
            .map(|r| r.map_err(|e| Error::from(e)).and_then(|d| Message::from_path(d.path())))
    }
}

#[cfg(test)]
mod tests {
    use std::path::PathBuf;

    extern crate tempdir;
    use self::tempdir::TempDir;

    use super::*;

    use mo::Message;
    use storage::Storage as StorageTrait;

    #[test]
    fn open() {
        Storage::open(TempDir::new("").unwrap().path()).unwrap();
    }

    #[test]
    fn no_directory() {
        assert!(Storage::open("not/a/real/directory").is_err());
    }

    #[test]
    fn file_is_error() {
        assert!(Storage::open("data/0-mo.sbd").is_err());
    }

    #[test]
    fn store() {
        let tempdir = TempDir::new("").unwrap();
        let mut storage = Storage::open(tempdir.path()).unwrap();
        let message = Message::from_path("data/0-mo.sbd").unwrap();
        storage.store(&message).unwrap();
        let mut message_path = PathBuf::from(tempdir.path());
        message_path.push("300234063904190");
        message_path.push("2015");
        message_path.push("07");
        message_path.push("150709_181508.sbd");
        Message::from_path(message_path).unwrap();
    }

    #[test]
    fn iter() {
        let tempdir = TempDir::new("").unwrap();
        let mut storage = Storage::open(tempdir.path()).unwrap();
        assert_eq!(0, storage.iter().collect::<Vec<_>>().len());
        let message = Message::from_path("data/0-mo.sbd").unwrap();
        storage.store(&message).unwrap();
        assert_eq!(1, storage.iter().collect::<Vec<_>>().len());
    }
}
<commit_msg>Typo in docs<commit_after>//! Store SBD messages on the filesystem.

use std::fs;
use std::path::Path;

use walkdir;

use {Result, Error};
use mo::Message;
use storage;

const SBD_EXTENSION: &'static str = "sbd";

/// A structure for managing storing and retriving SBD messages on a filesystem.
///
/// Messages are stored in a directory hierarchy under a single root directory.
/// Message storage and retrieval are managed by a `Storage` object, which is
/// configured for a single root directory.
#[derive(Debug)]
pub struct Storage<P: AsRef<Path>> {
    root: P,
}

impl<P: AsRef<Path>> Storage<P> {
    /// Opens a new storage for a given directory.
    ///
    /// # Errors
    ///
    /// If the directory does not exist, returns an `NotADirectory` error.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::storage::FilesystemStorage;
    /// let storage = FilesystemStorage::open("data").unwrap();
    /// assert!(FilesystemStorage::open("not/a/directory").is_err());
    /// ```
    pub fn open(root: P) -> Result<Storage<P>> {
        let metadata = try!(fs::metadata(root.as_ref()));
        if !metadata.is_dir() {
            Err(Error::NotADirectory(root.as_ref().as_os_str().to_os_string()))
        } else {
            Ok(Storage { root: root })
        }
    }

    /// Returns a `StorageIterator` over the messages in this storage.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::storage::FilesystemStorage;
    /// for message in FilesystemStorage::open("data").unwrap().iter() {
    ///     println!("{:?}", message);
    /// }
    /// ```
    pub fn iter(&self) -> StorageIterator {
        StorageIterator::new(&self)
    }
}

impl<P: AsRef<Path>> storage::Storage for Storage<P> {
    /// Stores a message on the filesystem.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// use sbd::storage::{FilesystemStorage, Storage};
    /// use sbd::mo::Message;
    /// let message: Message = Default::default();
    /// let storage = FilesystemStorage::open("/var/iridium").unwrap();
    /// storage.store(&message);
    /// ```
    fn store(&mut self, message: &Message) -> Result<()> {
        let mut path_buf = self.root.as_ref().to_path_buf();
        path_buf.push(message.imei());
        path_buf.push(message.time_of_session().format("%Y").to_string());
        path_buf.push(message.time_of_session().format("%m").to_string());
        try!(fs::create_dir_all(&path_buf));
        path_buf.push(message.time_of_session()
            .format(&format!("%y%m%d_%H%M%S.{}", SBD_EXTENSION))
            .to_string());
        let mut file = try!(fs::File::create(&path_buf));
        try!(message.write_to(&mut file));
        Ok(())
    }
}

/// An iterator over the messages in a `Storage`.
///
/// For now, this iterator will just return all messages with an `sbd` extension under the root of
/// the storage tree. We could try to get smarter and mirror the pattern logic for saving, but for
/// now that's more work and complexity than we need.
///
/// # Errors
///
/// This iterator's `Item` is a `sbd::Result<Message>`, because a file with an `sbd` extension
/// might not convert to a message successfully.
#[allow(missing_debug_implementations)]
pub struct StorageIterator {
    iter: walkdir::Iter,
}

impl StorageIterator {
    fn new<P: AsRef<Path>>(storage: &Storage<P>) -> StorageIterator {
        StorageIterator { iter: walkdir::WalkDir::new(&storage.root).into_iter() }
    }
}

impl Iterator for StorageIterator {
    type Item = Result<Message>;
    fn next(&mut self) -> Option<Self::Item> {
        self.iter
            .by_ref()
            .skip_while(|r| {
                r.as_ref()
                    .map(|d| d.path().extension().map(|e| e != SBD_EXTENSION).unwrap_or(true))
                    .unwrap_or(true)
            })
            .next()
            .map(|r| r.map_err(|e| Error::from(e)).and_then(|d| Message::from_path(d.path())))
    }
}

#[cfg(test)]
mod tests {
    use std::path::PathBuf;

    extern crate tempdir;
    use self::tempdir::TempDir;

    use super::*;

    use mo::Message;
    use storage::Storage as StorageTrait;

    #[test]
    fn open() {
        Storage::open(TempDir::new("").unwrap().path()).unwrap();
    }

    #[test]
    fn no_directory() {
        assert!(Storage::open("not/a/real/directory").is_err());
    }

    #[test]
    fn file_is_error() {
        assert!(Storage::open("data/0-mo.sbd").is_err());
    }

    #[test]
    fn store() {
        let tempdir = TempDir::new("").unwrap();
        let mut storage = Storage::open(tempdir.path()).unwrap();
        let message = Message::from_path("data/0-mo.sbd").unwrap();
        storage.store(&message).unwrap();
        let mut message_path = PathBuf::from(tempdir.path());
        message_path.push("300234063904190");
        message_path.push("2015");
        message_path.push("07");
        message_path.push("150709_181508.sbd");
        Message::from_path(message_path).unwrap();
    }

    #[test]
    fn iter() {
        let tempdir = TempDir::new("").unwrap();
        let mut storage = Storage::open(tempdir.path()).unwrap();
        assert_eq!(0, storage.iter().collect::<Vec<_>>().len());
        let message = Message::from_path("data/0-mo.sbd").unwrap();
        storage.store(&message).unwrap();
        assert_eq!(1, storage.iter().collect::<Vec<_>>().len());
    }
}
<|endoftext|>


<commit_before>use std::io::Write;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use super::{Result, MvccErrorKind};

const ENC_GROUP_SIZE: usize = 8;
const ENC_MARKER: u8 = b'\xff';
const ENC_PADDING: [u8; ENC_GROUP_SIZE] = [0; ENC_GROUP_SIZE];

// Refer: https://github.com/facebook/mysql-5.6/wiki/MyRocks-record-format#memcomparable-format
fn encode_bytes(key: &[u8]) -> Vec<u8> {
    let cap = key.len() / (ENC_GROUP_SIZE + 1) * (ENC_GROUP_SIZE + 1);
    let mut encoded = Vec::<u8>::with_capacity(cap);
    let len = key.len();
    let mut index = 0;
    while index <= len {
        let remain = len - index;
        let mut pad: usize = 0;
        if remain > ENC_GROUP_SIZE {
            encoded.write(&key[index..index + ENC_GROUP_SIZE]).unwrap();
        } else {
            pad = ENC_GROUP_SIZE - remain;
            encoded.write(&key[index..]).unwrap();
            encoded.write(&ENC_PADDING[..pad]).unwrap();
        }
        encoded.push(ENC_MARKER - (pad as u8));
        index += ENC_GROUP_SIZE;
    }
    encoded
}

fn decode_bytes(data: &[u8]) -> Result<(Vec<u8>, usize)> {
    let mut key = Vec::<u8>::with_capacity(data.len());
    let mut read: usize = 0;
    for chunk in data.chunks(ENC_GROUP_SIZE + 1) {
        if chunk.len() != ENC_GROUP_SIZE + 1 {
            return MvccErrorKind::KeyLength.as_result();
        }
        read += ENC_GROUP_SIZE + 1;

        let (marker, bytes) = chunk.split_last().unwrap();
        let pad_size = (ENC_MARKER - *marker) as usize;
        if pad_size == 0 {
            key.write(bytes).unwrap();
            continue;
        }
        if pad_size > ENC_GROUP_SIZE {
            return MvccErrorKind::KeyPadding.as_result();
        }
        let (bytes, padding) = bytes.split_at(ENC_GROUP_SIZE - pad_size);
        key.write(bytes).unwrap();
        if padding.iter().any(|x| *x != 0) {
            return MvccErrorKind::KeyPadding.as_result();
        }
        return Ok((key, read));
    }
    MvccErrorKind::KeyLength.as_result()
}

pub fn encode_key(key: &[u8], version: u64) -> Vec<u8> {
    let mut v = encode_bytes(key);
    v.write_u64::<BigEndian>(version).unwrap();
    v
}

pub fn decode_key(data: &[u8]) -> Result<(Vec<u8>, u64)> {
    let (key, read_size) = try!(decode_bytes(data));
    match data[read_size..].as_ref().read_u64::<BigEndian>() {
        Ok(ver) => Ok((key, ver)),
        Err(..) => MvccErrorKind::KeyVersion.as_result(),
    }
}

#[cfg(test)]
mod tests {
    use super::{encode_key, encode_bytes, decode_key, decode_bytes};
    use std::cmp::Ordering;

    #[test]
    fn test_enc_dec_bytes() {
        let pairs = vec![(vec![], vec![0, 0, 0, 0, 0, 0, 0, 0, 247]),
                         (vec![1, 2, 3], vec![1, 2, 3, 0, 0, 0, 0, 0, 250]),
                         (vec![0], vec![0, 0, 0, 0, 0, 0, 0, 0, 248]),
                         (vec![1, 2, 3], vec![1, 2, 3, 0, 0, 0, 0, 0, 250]),
                         (vec![1, 2, 3, 0], vec![1, 2, 3, 0, 0, 0, 0, 0, 251]),
                         (vec![1, 2, 3, 4, 5, 6, 7], vec![1, 2, 3, 4, 5, 6, 7, 0, 254]),

                         (vec![0, 0, 0, 0, 0, 0, 0, 0],
                          vec![0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 247]),

                         (vec![1, 2, 3, 4, 5, 6, 7, 8],
                          vec![1, 2, 3, 4, 5, 6, 7, 8, 255, 0, 0, 0, 0, 0, 0, 0, 0, 247]),

                         (vec![1, 2, 3, 4, 5, 6, 7, 8, 9],
                          vec![1, 2, 3, 4, 5, 6, 7, 8, 255, 9, 0, 0, 0, 0, 0, 0, 0, 248])];

        for (x, y) in pairs {
            assert_eq!(encode_bytes(&x), y);
            let (key, size) = decode_bytes(&y).unwrap();
            assert_eq!(key, x);
            assert_eq!(size, y.len());
        }
    }

    #[test]
    fn test_dec_bytes_fail() {
        let invalid_bytes = vec![vec![1, 2, 3, 4],
                                 vec![0, 0, 0, 0, 0, 0, 0, 247],
                                 vec![0, 0, 0, 0, 0, 0, 0, 0, 246],
                                 vec![0, 0, 0, 0, 0, 0, 0, 1, 247],
                                 vec![1, 2, 3, 4, 5, 6, 7, 8, 0],
                                 vec![1, 2, 3, 4, 5, 6, 7, 8, 255, 1],
                                 vec![1, 2, 3, 4, 5, 6, 7, 8, 255, 1, 2, 3, 4, 5, 6, 7, 8],
                                 vec![1, 2, 3, 4, 5, 6, 7, 8, 255, 1, 2, 3, 4, 5, 6, 7, 8, 255],
                                 vec![1, 2, 3, 4, 5, 6, 7, 8, 255, 1, 2, 3, 4, 5, 6, 7, 8, 0]];

        for x in invalid_bytes {
            assert!(decode_bytes(&x).is_err());
        }
    }

    #[test]
    fn test_encode_bytes_compare() {
        let pairs: Vec<(&'static [u8], &'static [u8], _)> = vec![(b"", b"\x00", Ordering::Less),
                 (b"\x00", b"\x00", Ordering::Equal),
                 (b"\xFF", b"\x00", Ordering::Greater),
                 (b"\xFF", b"\xFF\x00", Ordering::Less),
                 (b"a", b"b", Ordering::Less),
                 (b"a", b"\x00", Ordering::Greater),
                 (b"\x00", b"\x01", Ordering::Less),
                 (b"\x00\x01", b"\x00\x00", Ordering::Greater),
                 (b"\x00\x00\x00", b"\x00\x00", Ordering::Greater),
                 (b"\x00\x00\x00", b"\x00\x00", Ordering::Greater),

                 (b"\x00\x00\x00\x00\x00\x00\x00\x00",
                  b"\x00\x00\x00\x00\x00\x00\x00\x00\x00",
                  Ordering::Less),

                 (b"\x01\x02\x03\x00", b"\x01\x02\x03", Ordering::Greater),
                 (b"\x01\x03\x03\x04", b"\x01\x03\x03\x05", Ordering::Less),

                 (b"\x01\x02\x03\x04\x05\x06\x07",
                  b"\x01\x02\x03\x04\x05\x06\x07\x08",
                  Ordering::Less),

                 (b"\x01\x02\x03\x04\x05\x06\x07\x08\x09",
                  b"\x01\x02\x03\x04\x05\x06\x07\x08",
                  Ordering::Greater),

                 (b"\x01\x02\x03\x04\x05\x06\x07\x08\x00",
                  b"\x01\x02\x03\x04\x05\x06\x07\x08",
                  Ordering::Greater)];

        for (x, y, ord) in pairs {
            assert_eq!(encode_bytes(x).cmp(&encode_bytes(y)), ord);
        }
    }

    #[test]
    fn test_encode_key() {
        let pairs: Vec<(&'static [u8], u64)> = vec![
          (b"abc", 0),
          (b"\x00\x00", 100),
        ];

        for (x, y) in pairs {
            let data = encode_key(x, y);
            let (k, ver) = decode_key(&data).unwrap();
            assert_eq!((x, y), (&k as &[u8], ver));
        }
    }

    #[test]
    fn test_key_compare() {
        let a1 = encode_key(b"A", 1);
        let a2 = encode_key(b"A", 2);
        let b1 = encode_key(b"B", 1);
        assert!(a2 > a1);
        assert!(a2 < b1);
    }

    use test::Bencher;

    #[bench]
    fn bench_encode(b: &mut Bencher) {
        let key = [b'x'; 20];
        b.iter(|| encode_bytes(&key));
    }

    #[bench]
    fn bench_decode(b: &mut Bencher) {
        let key = [b'x'; 20];
        let encoded = encode_bytes(&key);
        b.iter(|| decode_bytes(&encoded));
    }
}
<commit_msg>*: address comment<commit_after>use std::io::Write;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use super::{Result, MvccErrorKind};

const ENC_GROUP_SIZE: usize = 8;
const ENC_MARKER: u8 = b'\xff';
const ENC_PADDING: [u8; ENC_GROUP_SIZE] = [0; ENC_GROUP_SIZE];

// Refer: https://github.com/facebook/mysql-5.6/wiki/MyRocks-record-format#memcomparable-format
fn encode_bytes(key: &[u8]) -> Vec<u8> {
    let cap = key.len() / (ENC_GROUP_SIZE + 1) * (ENC_GROUP_SIZE + 1);
    let mut encoded = Vec::<u8>::with_capacity(cap);
    let len = key.len();
    let mut index = 0;
    while index <= len {
        let remain = len - index;
        let mut pad: usize = 0;
        if remain > ENC_GROUP_SIZE {
            encoded.write(&key[index..index + ENC_GROUP_SIZE]).unwrap();
        } else {
            pad = ENC_GROUP_SIZE - remain;
            encoded.write(&key[index..]).unwrap();
            encoded.write(&ENC_PADDING[..pad]).unwrap();
        }
        encoded.push(ENC_MARKER - (pad as u8));
        index += ENC_GROUP_SIZE;
    }
    encoded
}

fn decode_bytes(data: &[u8]) -> Result<(Vec<u8>, usize)> {
    let mut key = Vec::<u8>::with_capacity(data.len());
    let mut read: usize = 0;
    for chunk in data.chunks(ENC_GROUP_SIZE + 1) {
        if chunk.len() != ENC_GROUP_SIZE + 1 {
            return MvccErrorKind::KeyLength.as_result();
        }
        read += ENC_GROUP_SIZE + 1;

        let (marker, bytes) = chunk.split_last().unwrap();
        let pad_size = (ENC_MARKER - *marker) as usize;
        if pad_size == 0 {
            key.write(bytes).unwrap();
            continue;
        }
        if pad_size > ENC_GROUP_SIZE {
            return MvccErrorKind::KeyPadding.as_result();
        }
        let (bytes, padding) = bytes.split_at(ENC_GROUP_SIZE - pad_size);
        key.write(bytes).unwrap();
        if padding.iter().any(|x| *x != 0) {
            return MvccErrorKind::KeyPadding.as_result();
        }
        return Ok((key, read));
    }
    MvccErrorKind::KeyLength.as_result()
}

pub fn encode_key(key: &[u8], version: u64) -> Vec<u8> {
    let mut v = encode_bytes(key);
    v.write_u64::<BigEndian>(version).unwrap();
    v
}

pub fn decode_key(data: &[u8]) -> Result<(Vec<u8>, u64)> {
    let (key, read_size) = try!(decode_bytes(data));
    match data[read_size..].as_ref().read_u64::<BigEndian>() {
        Ok(ver) => Ok((key, ver)),
        Err(..) => MvccErrorKind::KeyVersion.as_result(),
    }
}

#[cfg(test)]
mod tests {
    use super::{encode_key, encode_bytes, decode_key, decode_bytes};
    use std::cmp::Ordering;

    #[test]
    fn test_enc_dec_bytes() {
        let pairs = vec![(vec![], vec![0, 0, 0, 0, 0, 0, 0, 0, 247]),
                         (vec![1, 2, 3], vec![1, 2, 3, 0, 0, 0, 0, 0, 250]),
                         (vec![0], vec![0, 0, 0, 0, 0, 0, 0, 0, 248]),
                         (vec![1, 2, 3], vec![1, 2, 3, 0, 0, 0, 0, 0, 250]),
                         (vec![1, 2, 3, 0], vec![1, 2, 3, 0, 0, 0, 0, 0, 251]),
                         (vec![1, 2, 3, 4, 5, 6, 7], vec![1, 2, 3, 4, 5, 6, 7, 0, 254]),

                         (vec![0, 0, 0, 0, 0, 0, 0, 0],
                          vec![0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 247]),

                         (vec![1, 2, 3, 4, 5, 6, 7, 8],
                          vec![1, 2, 3, 4, 5, 6, 7, 8, 255, 0, 0, 0, 0, 0, 0, 0, 0, 247]),

                         (vec![1, 2, 3, 4, 5, 6, 7, 8, 9],
                          vec![1, 2, 3, 4, 5, 6, 7, 8, 255, 9, 0, 0, 0, 0, 0, 0, 0, 248])];

        for (x, y) in pairs {
            assert_eq!(encode_bytes(&x), y);
            let (key, size) = decode_bytes(&y).unwrap();
            assert_eq!(key, x);
            assert_eq!(size, y.len());
        }
    }

    #[test]
    fn test_dec_bytes_fail() {
        let invalid_bytes = vec![vec![1, 2, 3, 4],
                                 vec![0, 0, 0, 0, 0, 0, 0, 247],
                                 vec![0, 0, 0, 0, 0, 0, 0, 0, 246],
                                 vec![0, 0, 0, 0, 0, 0, 0, 1, 247],
                                 vec![1, 2, 3, 4, 5, 6, 7, 8, 0],
                                 vec![1, 2, 3, 4, 5, 6, 7, 8, 255, 1],
                                 vec![1, 2, 3, 4, 5, 6, 7, 8, 255, 1, 2, 3, 4, 5, 6, 7, 8],
                                 vec![1, 2, 3, 4, 5, 6, 7, 8, 255, 1, 2, 3, 4, 5, 6, 7, 8, 255],
                                 vec![1, 2, 3, 4, 5, 6, 7, 8, 255, 1, 2, 3, 4, 5, 6, 7, 8, 0]];

        for x in invalid_bytes {
            assert!(decode_bytes(&x).is_err());
        }
    }

    #[test]
    fn test_encode_bytes_compare() {
        let pairs: Vec<(&[u8], &[u8], _)> = vec![(b"", b"\x00", Ordering::Less),
                 (b"\x00", b"\x00", Ordering::Equal),
                 (b"\xFF", b"\x00", Ordering::Greater),
                 (b"\xFF", b"\xFF\x00", Ordering::Less),
                 (b"a", b"b", Ordering::Less),
                 (b"a", b"\x00", Ordering::Greater),
                 (b"\x00", b"\x01", Ordering::Less),
                 (b"\x00\x01", b"\x00\x00", Ordering::Greater),
                 (b"\x00\x00\x00", b"\x00\x00", Ordering::Greater),
                 (b"\x00\x00\x00", b"\x00\x00", Ordering::Greater),

                 (b"\x00\x00\x00\x00\x00\x00\x00\x00",
                  b"\x00\x00\x00\x00\x00\x00\x00\x00\x00",
                  Ordering::Less),

                 (b"\x01\x02\x03\x00", b"\x01\x02\x03", Ordering::Greater),
                 (b"\x01\x03\x03\x04", b"\x01\x03\x03\x05", Ordering::Less),

                 (b"\x01\x02\x03\x04\x05\x06\x07",
                  b"\x01\x02\x03\x04\x05\x06\x07\x08",
                  Ordering::Less),

                 (b"\x01\x02\x03\x04\x05\x06\x07\x08\x09",
                  b"\x01\x02\x03\x04\x05\x06\x07\x08",
                  Ordering::Greater),

                 (b"\x01\x02\x03\x04\x05\x06\x07\x08\x00",
                  b"\x01\x02\x03\x04\x05\x06\x07\x08",
                  Ordering::Greater)];

        for (x, y, ord) in pairs {
            assert_eq!(encode_bytes(x).cmp(&encode_bytes(y)), ord);
        }
    }

    #[test]
    fn test_encode_key() {
        let pairs: Vec<(&[u8], u64)> = vec![
          (b"abc", 0),
          (b"\x00\x00", 100),
        ];

        for (x, y) in pairs {
            let data = encode_key(x, y);
            let (k, ver) = decode_key(&data).unwrap();
            assert_eq!((x, y), (&k as &[u8], ver));
        }
    }

    #[test]
    fn test_key_compare() {
        let a1 = encode_key(b"A", 1);
        let a2 = encode_key(b"A", 2);
        let b1 = encode_key(b"B", 1);
        assert!(a2 > a1);
        assert!(a2 < b1);
    }

    use test::Bencher;

    #[bench]
    fn bench_encode(b: &mut Bencher) {
        let key = [b'x'; 20];
        b.iter(|| encode_bytes(&key));
    }

    #[bench]
    fn bench_decode(b: &mut Bencher) {
        let key = [b'x'; 20];
        let encoded = encode_bytes(&key);
        b.iter(|| decode_bytes(&encoded));
    }
}
<|endoftext|>