<commit_before>use regex_syntax;
use rustc::hir::*;
use rustc::lint::*;
use rustc::middle::const_val::ConstVal;
use rustc_const_eval::ConstContext;
use rustc::ty::subst::Substs;
use std::collections::HashSet;
use std::error::Error;
use syntax::ast::{LitKind, NodeId};
use syntax::codemap::{BytePos, Span};
use syntax::symbol::InternedString;
use utils::{is_expn_of, match_def_path, match_type, paths, span_help_and_lint, span_lint, opt_def_id};

/// **What it does:** Checks [regex](https://crates.io/crates/regex) creation
/// (with `Regex::new`,`RegexBuilder::new` or `RegexSet::new`) for correct
/// regex syntax.
///
/// **Why is this bad?** This will lead to a runtime panic.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// Regex::new("|")
/// ```
declare_lint! {
    pub INVALID_REGEX,
    Deny,
    "invalid regular expressions"
}

/// **What it does:** Checks for trivial [regex] creation (with `Regex::new`,
/// `RegexBuilder::new` or `RegexSet::new`).
///
/// [regex]: https://crates.io/crates/regex
///
/// **Why is this bad?** Matching the regex can likely be replaced by `==` or
/// `str::starts_with`, `str::ends_with` or `std::contains` or other `str`
/// methods.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// Regex::new("^foobar")
/// ```
declare_lint! {
    pub TRIVIAL_REGEX,
    Warn,
    "trivial regular expressions"
}

/// **What it does:** Checks for usage of `regex!(_)` which (as of now) is
/// usually slower than `Regex::new(_)` unless called in a loop (which is a bad
/// idea anyway).
///
/// **Why is this bad?** Performance, at least for now. The macro version is
/// likely to catch up long-term, but for now the dynamic version is faster.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// regex!("foo|bar")
/// ```
declare_lint! {
    pub REGEX_MACRO,
    Warn,
    "use of `regex!(_)` instead of `Regex::new(_)`"
}

#[derive(Clone, Default)]
pub struct Pass {
    spans: HashSet<Span>,
    last: Option<NodeId>,
}

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(INVALID_REGEX, REGEX_MACRO, TRIVIAL_REGEX)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
    fn check_crate(&mut self, _: &LateContext<'a, 'tcx>, _: &'tcx Crate) {
        self.spans.clear();
    }

    fn check_block(&mut self, cx: &LateContext<'a, 'tcx>, block: &'tcx Block) {
        if_let_chain!{[
            self.last.is_none(),
            let Some(ref expr) = block.expr,
            match_type(cx, cx.tables.expr_ty(expr), &paths::REGEX),
            let Some(span) = is_expn_of(expr.span, "regex"),
        ], {
            if !self.spans.contains(&span) {
                span_lint(cx,
                          REGEX_MACRO,
                          span,
                          "`regex!(_)` found. \
                          Please use `Regex::new(_)`, which is faster for now.");
                self.spans.insert(span);
            }
            self.last = Some(block.id);
        }}
    }

    fn check_block_post(&mut self, _: &LateContext<'a, 'tcx>, block: &'tcx Block) {
        if self.last.map_or(false, |id| block.id == id) {
            self.last = None;
        }
    }

    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
        if_let_chain!{[
            let ExprCall(ref fun, ref args) = expr.node,
            let ExprPath(ref qpath) = fun.node,
            args.len() == 1,
            let Some(def_id) = opt_def_id(cx.tables.qpath_def(qpath, fun.hir_id)),
        ], {
            if match_def_path(cx.tcx, def_id, &paths::REGEX_NEW) ||
               match_def_path(cx.tcx, def_id, &paths::REGEX_BUILDER_NEW) {
                check_regex(cx, &args[0], true);
            } else if match_def_path(cx.tcx, def_id, &paths::REGEX_BYTES_NEW) ||
               match_def_path(cx.tcx, def_id, &paths::REGEX_BYTES_BUILDER_NEW) {
                check_regex(cx, &args[0], false);
            } else if match_def_path(cx.tcx, def_id, &paths::REGEX_SET_NEW) {
                check_set(cx, &args[0], true);
            } else if match_def_path(cx.tcx, def_id, &paths::REGEX_BYTES_SET_NEW) {
                check_set(cx, &args[0], false);
            }
        }}
    }
}

#[allow(cast_possible_truncation)]
fn str_span(base: Span, s: &str, c: usize) -> Span {
    let mut si = s.char_indices().skip(c);

    match (si.next(), si.next()) {
        (Some((l, _)), Some((h, _))) => {
            Span::new(base.lo() + BytePos(l as u32), base.lo() + BytePos(h as u32), base.ctxt())
        },
        _ => base,
    }
}

fn const_str(cx: &LateContext, e: &Expr) -> Option<InternedString> {
    let parent_item = cx.tcx.hir.get_parent(e.id);
    let parent_def_id = cx.tcx.hir.local_def_id(parent_item);
    let substs = Substs::identity_for_item(cx.tcx, parent_def_id);
    match ConstContext::new(cx.tcx, cx.param_env.and(substs), cx.tables).eval(e) {
        Ok(ConstVal::Str(r)) => Some(r),
        _ => None,
    }
}

fn is_trivial_regex(s: &regex_syntax::Expr) -> Option<&'static str> {
    use regex_syntax::Expr;

    match *s {
        Expr::Empty | Expr::StartText | Expr::EndText => Some("the regex is unlikely to be useful as it is"),
        Expr::Literal { .. } => Some("consider using `str::contains`"),
        Expr::Concat(ref exprs) => match exprs.len() {
            2 => match (&exprs[0], &exprs[1]) {
                (&Expr::StartText, &Expr::EndText) => Some("consider using `str::is_empty`"),
                (&Expr::StartText, &Expr::Literal { .. }) => Some("consider using `str::starts_with`"),
                (&Expr::Literal { .. }, &Expr::EndText) => Some("consider using `str::ends_with`"),
                _ => None,
            },
            3 => if let (&Expr::StartText, &Expr::Literal { .. }, &Expr::EndText) = (&exprs[0], &exprs[1], &exprs[2]) {
                Some("consider using `==` on `str`s")
            } else {
                None
            },
            _ => None,
        },
        _ => None,
    }
}

fn check_set(cx: &LateContext, expr: &Expr, utf8: bool) {
    if_let_chain! {[
        let ExprAddrOf(_, ref expr) = expr.node,
        let ExprArray(ref exprs) = expr.node,
    ], {
        for expr in exprs {
            check_regex(cx, expr, utf8);
        }
    }}
}

fn check_regex(cx: &LateContext, expr: &Expr, utf8: bool) {
    let builder = regex_syntax::ExprBuilder::new().unicode(utf8);

    if let ExprLit(ref lit) = expr.node {
        if let LitKind::Str(ref r, _) = lit.node {
            let r = &r.as_str();
            match builder.parse(r) {
                Ok(r) => if let Some(repl) = is_trivial_regex(&r) {
                    span_help_and_lint(
                        cx,
                        TRIVIAL_REGEX,
                        expr.span,
                        "trivial regex",
                        &format!("consider using {}", repl),
                    );
                },
                Err(e) => {
                    span_lint(
                        cx,
                        INVALID_REGEX,
                        str_span(expr.span, r, e.position()),
                        &format!("regex syntax error: {}", e.description()),
                    );
                },
            }
        }
    } else if let Some(r) = const_str(cx, expr) {
        match builder.parse(&r) {
            Ok(r) => if let Some(repl) = is_trivial_regex(&r) {
                span_help_and_lint(
                    cx,
                    TRIVIAL_REGEX,
                    expr.span,
                    "trivial regex",
                    &format!("consider using {}", repl),
                );
            },
            Err(e) => {
                span_lint(
                    cx,
                    INVALID_REGEX,
                    expr.span,
                    &format!("regex syntax error on position {}: {}", e.position(), e.description()),
                );
            },
        }
    }
}
<commit_msg>Fix link in trivial_regex<commit_after>use regex_syntax;
use rustc::hir::*;
use rustc::lint::*;
use rustc::middle::const_val::ConstVal;
use rustc_const_eval::ConstContext;
use rustc::ty::subst::Substs;
use std::collections::HashSet;
use std::error::Error;
use syntax::ast::{LitKind, NodeId};
use syntax::codemap::{BytePos, Span};
use syntax::symbol::InternedString;
use utils::{is_expn_of, match_def_path, match_type, paths, span_help_and_lint, span_lint, opt_def_id};

/// **What it does:** Checks [regex](https://crates.io/crates/regex) creation
/// (with `Regex::new`,`RegexBuilder::new` or `RegexSet::new`) for correct
/// regex syntax.
///
/// **Why is this bad?** This will lead to a runtime panic.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// Regex::new("|")
/// ```
declare_lint! {
    pub INVALID_REGEX,
    Deny,
    "invalid regular expressions"
}

/// **What it does:** Checks for trivial [regex](https://crates.io/crates/regex)
/// creation (with `Regex::new`, `RegexBuilder::new` or `RegexSet::new`).
///
/// **Why is this bad?** Matching the regex can likely be replaced by `==` or
/// `str::starts_with`, `str::ends_with` or `std::contains` or other `str`
/// methods.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// Regex::new("^foobar")
/// ```
declare_lint! {
    pub TRIVIAL_REGEX,
    Warn,
    "trivial regular expressions"
}

/// **What it does:** Checks for usage of `regex!(_)` which (as of now) is
/// usually slower than `Regex::new(_)` unless called in a loop (which is a bad
/// idea anyway).
///
/// **Why is this bad?** Performance, at least for now. The macro version is
/// likely to catch up long-term, but for now the dynamic version is faster.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// regex!("foo|bar")
/// ```
declare_lint! {
    pub REGEX_MACRO,
    Warn,
    "use of `regex!(_)` instead of `Regex::new(_)`"
}

#[derive(Clone, Default)]
pub struct Pass {
    spans: HashSet<Span>,
    last: Option<NodeId>,
}

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(INVALID_REGEX, REGEX_MACRO, TRIVIAL_REGEX)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
    fn check_crate(&mut self, _: &LateContext<'a, 'tcx>, _: &'tcx Crate) {
        self.spans.clear();
    }

    fn check_block(&mut self, cx: &LateContext<'a, 'tcx>, block: &'tcx Block) {
        if_let_chain!{[
            self.last.is_none(),
            let Some(ref expr) = block.expr,
            match_type(cx, cx.tables.expr_ty(expr), &paths::REGEX),
            let Some(span) = is_expn_of(expr.span, "regex"),
        ], {
            if !self.spans.contains(&span) {
                span_lint(cx,
                          REGEX_MACRO,
                          span,
                          "`regex!(_)` found. \
                          Please use `Regex::new(_)`, which is faster for now.");
                self.spans.insert(span);
            }
            self.last = Some(block.id);
        }}
    }

    fn check_block_post(&mut self, _: &LateContext<'a, 'tcx>, block: &'tcx Block) {
        if self.last.map_or(false, |id| block.id == id) {
            self.last = None;
        }
    }

    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
        if_let_chain!{[
            let ExprCall(ref fun, ref args) = expr.node,
            let ExprPath(ref qpath) = fun.node,
            args.len() == 1,
            let Some(def_id) = opt_def_id(cx.tables.qpath_def(qpath, fun.hir_id)),
        ], {
            if match_def_path(cx.tcx, def_id, &paths::REGEX_NEW) ||
               match_def_path(cx.tcx, def_id, &paths::REGEX_BUILDER_NEW) {
                check_regex(cx, &args[0], true);
            } else if match_def_path(cx.tcx, def_id, &paths::REGEX_BYTES_NEW) ||
               match_def_path(cx.tcx, def_id, &paths::REGEX_BYTES_BUILDER_NEW) {
                check_regex(cx, &args[0], false);
            } else if match_def_path(cx.tcx, def_id, &paths::REGEX_SET_NEW) {
                check_set(cx, &args[0], true);
            } else if match_def_path(cx.tcx, def_id, &paths::REGEX_BYTES_SET_NEW) {
                check_set(cx, &args[0], false);
            }
        }}
    }
}

#[allow(cast_possible_truncation)]
fn str_span(base: Span, s: &str, c: usize) -> Span {
    let mut si = s.char_indices().skip(c);

    match (si.next(), si.next()) {
        (Some((l, _)), Some((h, _))) => {
            Span::new(base.lo() + BytePos(l as u32), base.lo() + BytePos(h as u32), base.ctxt())
        },
        _ => base,
    }
}

fn const_str(cx: &LateContext, e: &Expr) -> Option<InternedString> {
    let parent_item = cx.tcx.hir.get_parent(e.id);
    let parent_def_id = cx.tcx.hir.local_def_id(parent_item);
    let substs = Substs::identity_for_item(cx.tcx, parent_def_id);
    match ConstContext::new(cx.tcx, cx.param_env.and(substs), cx.tables).eval(e) {
        Ok(ConstVal::Str(r)) => Some(r),
        _ => None,
    }
}

fn is_trivial_regex(s: &regex_syntax::Expr) -> Option<&'static str> {
    use regex_syntax::Expr;

    match *s {
        Expr::Empty | Expr::StartText | Expr::EndText => Some("the regex is unlikely to be useful as it is"),
        Expr::Literal { .. } => Some("consider using `str::contains`"),
        Expr::Concat(ref exprs) => match exprs.len() {
            2 => match (&exprs[0], &exprs[1]) {
                (&Expr::StartText, &Expr::EndText) => Some("consider using `str::is_empty`"),
                (&Expr::StartText, &Expr::Literal { .. }) => Some("consider using `str::starts_with`"),
                (&Expr::Literal { .. }, &Expr::EndText) => Some("consider using `str::ends_with`"),
                _ => None,
            },
            3 => if let (&Expr::StartText, &Expr::Literal { .. }, &Expr::EndText) = (&exprs[0], &exprs[1], &exprs[2]) {
                Some("consider using `==` on `str`s")
            } else {
                None
            },
            _ => None,
        },
        _ => None,
    }
}

fn check_set(cx: &LateContext, expr: &Expr, utf8: bool) {
    if_let_chain! {[
        let ExprAddrOf(_, ref expr) = expr.node,
        let ExprArray(ref exprs) = expr.node,
    ], {
        for expr in exprs {
            check_regex(cx, expr, utf8);
        }
    }}
}

fn check_regex(cx: &LateContext, expr: &Expr, utf8: bool) {
    let builder = regex_syntax::ExprBuilder::new().unicode(utf8);

    if let ExprLit(ref lit) = expr.node {
        if let LitKind::Str(ref r, _) = lit.node {
            let r = &r.as_str();
            match builder.parse(r) {
                Ok(r) => if let Some(repl) = is_trivial_regex(&r) {
                    span_help_and_lint(
                        cx,
                        TRIVIAL_REGEX,
                        expr.span,
                        "trivial regex",
                        &format!("consider using {}", repl),
                    );
                },
                Err(e) => {
                    span_lint(
                        cx,
                        INVALID_REGEX,
                        str_span(expr.span, r, e.position()),
                        &format!("regex syntax error: {}", e.description()),
                    );
                },
            }
        }
    } else if let Some(r) = const_str(cx, expr) {
        match builder.parse(&r) {
            Ok(r) => if let Some(repl) = is_trivial_regex(&r) {
                span_help_and_lint(
                    cx,
                    TRIVIAL_REGEX,
                    expr.span,
                    "trivial regex",
                    &format!("consider using {}", repl),
                );
            },
            Err(e) => {
                span_lint(
                    cx,
                    INVALID_REGEX,
                    expr.span,
                    &format!("regex syntax error on position {}: {}", e.position(), e.description()),
                );
            },
        }
    }
}
<|endoftext|>


<commit_before>use base64::{decode, encode};
use std::collections::HashMap;
use std::str;

use conduit::RequestExt;
use conduit_middleware::{AfterResult, BeforeResult};
use cookie::{Cookie, Key, SameSite};

use super::RequestCookies;

const MAX_AGE_DAYS: i64 = 90;

pub struct SessionMiddleware {
    cookie_name: String,
    key: Key,
    secure: bool,
}

pub struct Session {
    data: HashMap<String, String>,
    dirty: bool,
}

impl SessionMiddleware {
    pub fn new(cookie: &str, key: Key, secure: bool) -> SessionMiddleware {
        SessionMiddleware {
            cookie_name: cookie.to_string(),
            key,
            secure,
        }
    }

    pub fn decode(&self, cookie: Cookie<'_>) -> HashMap<String, String> {
        let mut ret = HashMap::new();
        let bytes = decode(cookie.value().as_bytes()).unwrap_or_default();
        let mut parts = bytes.split(|&a| a == 0xff);
        while let (Some(key), Some(value)) = (parts.next(), parts.next()) {
            if key.is_empty() {
                break;
            }
            if let (Ok(key), Ok(value)) = (str::from_utf8(key), str::from_utf8(value)) {
                ret.insert(key.to_string(), value.to_string());
            }
        }
        ret
    }

    pub fn encode(&self, h: &HashMap<String, String>) -> String {
        let mut ret = Vec::new();
        for (i, (k, v)) in h.iter().enumerate() {
            if i != 0 {
                ret.push(0xff)
            }
            ret.extend(k.bytes());
            ret.push(0xff);
            ret.extend(v.bytes());
        }
        while ret.len() * 8 % 6 != 0 {
            ret.push(0xff);
        }
        encode(&ret[..])
    }
}

impl conduit_middleware::Middleware for SessionMiddleware {
    fn before(&self, req: &mut dyn RequestExt) -> BeforeResult {
        let session = {
            let jar = req.cookies_mut().signed(&self.key);
            jar.get(&self.cookie_name)
                .map(|cookie| self.decode(cookie))
                .unwrap_or_else(HashMap::new)
        };
        req.mut_extensions().insert(Session {
            data: session,
            dirty: false,
        });
        Ok(())
    }

    fn after(&self, req: &mut dyn RequestExt, res: AfterResult) -> AfterResult {
        let session = req.extensions().find::<Session>();
        let session = session.expect("session must be present after request");
        if session.dirty {
            let encoded = self.encode(&session.data);
            let cookie = Cookie::build(self.cookie_name.to_string(), encoded)
                .http_only(true)
                .secure(self.secure)
                .same_site(SameSite::Strict)
                .max_age(time::Duration::days(MAX_AGE_DAYS))
                .path("/")
                .finish();
            req.cookies_mut().signed(&self.key).add(cookie);
        }
        res
    }
}

pub trait RequestSession {
    fn session(&self) -> &HashMap<String, String>;
    fn session_mut(&mut self) -> &mut HashMap<String, String>;
}

impl<T: RequestExt + ?Sized> RequestSession for T {
    fn session(&self) -> &HashMap<String, String> {
        &self
            .extensions()
            .find::<Session>()
            .expect("missing cookie session")
            .data
    }

    fn session_mut(&mut self) -> &mut HashMap<String, String> {
        let session = self
            .mut_extensions()
            .find_mut::<Session>()
            .expect("missing cookie session");
        session.dirty = true;
        &mut session.data
    }
}

#[cfg(test)]
mod test {
    use std::collections::HashMap;

    use conduit::{header, Body, Handler, HttpResult, Method, RequestExt, Response};
    use conduit_middleware::MiddlewareBuilder;
    use conduit_test::MockRequest;
    use cookie::{Cookie, Key};

    use {Middleware, RequestSession, SessionMiddleware};

    fn test_key() -> Key {
        let master_key: Vec<u8> = (0..32).collect();
        Key::derive_from(&master_key)
    }

    #[test]
    fn simple() {
        let mut req = MockRequest::new(Method::POST, "/articles");
        let key = test_key();

        // Set the session cookie
        let mut app = MiddlewareBuilder::new(set_session);
        app.add(Middleware::new());
        app.add(SessionMiddleware::new("lol", key, false));
        let response = app.call(&mut req).unwrap();

        let v = response
            .headers()
            .get(header::SET_COOKIE)
            .unwrap()
            .to_str()
            .unwrap();
        assert!(v.starts_with("lol"));

        // Use the session cookie
        req.header(header::COOKIE, v);
        let key = test_key();
        let mut app = MiddlewareBuilder::new(use_session);
        app.add(Middleware::new());
        app.add(SessionMiddleware::new("lol", key, false));
        assert!(app.call(&mut req).is_ok());

        fn set_session(req: &mut dyn RequestExt) -> HttpResult {
            assert!(req
                .session_mut()
                .insert("foo".to_string(), "bar".to_string())
                .is_none());
            Response::builder().body(Body::empty())
        }
        fn use_session(req: &mut dyn RequestExt) -> HttpResult {
            assert_eq!(*req.session().get("foo").unwrap(), "bar");
            Response::builder().body(Body::empty())
        }
    }

    #[test]
    fn no_equals() {
        let key = test_key();
        let m = SessionMiddleware::new("test", key, false);
        let e = {
            let mut map = HashMap::new();
            map.insert("a".to_string(), "bc".to_string());
            m.encode(&map)
        };
        assert!(!e.ends_with('='));
        let m = m.decode(Cookie::new("foo", e));
        assert_eq!(*m.get("a").unwrap(), "bc");
    }

    #[test]
    fn dirty_tracking() {
        let mut req = MockRequest::new(Method::GET, "/");

        let mut app = MiddlewareBuilder::new(read_session);
        app.add(Middleware::new());
        app.add(SessionMiddleware::new("dirty", test_key(), false));
        let response = app.call(&mut req).unwrap();

        assert!(response.headers().get(header::SET_COOKIE).is_none());

        let mut app = MiddlewareBuilder::new(modify_session);
        app.add(Middleware::new());
        app.add(SessionMiddleware::new("dirty", test_key(), false));
        let response = app.call(&mut req).unwrap();

        assert!(response.headers().get(header::SET_COOKIE).is_some());

        fn read_session(req: &mut dyn RequestExt) -> HttpResult {
            req.session();
            Response::builder().body(Body::empty())
        }
        fn modify_session(req: &mut dyn RequestExt) -> HttpResult {
            req.session_mut();
            Response::builder().body(Body::empty())
        }
    }
}
<commit_msg>session: Remove unused `&self` from `encode/decode()` functions<commit_after>use base64::{decode, encode};
use std::collections::HashMap;
use std::str;

use conduit::RequestExt;
use conduit_middleware::{AfterResult, BeforeResult};
use cookie::{Cookie, Key, SameSite};

use super::RequestCookies;

const MAX_AGE_DAYS: i64 = 90;

pub struct SessionMiddleware {
    cookie_name: String,
    key: Key,
    secure: bool,
}

pub struct Session {
    data: HashMap<String, String>,
    dirty: bool,
}

impl SessionMiddleware {
    pub fn new(cookie: &str, key: Key, secure: bool) -> SessionMiddleware {
        SessionMiddleware {
            cookie_name: cookie.to_string(),
            key,
            secure,
        }
    }

    pub fn decode(cookie: Cookie<'_>) -> HashMap<String, String> {
        let mut ret = HashMap::new();
        let bytes = decode(cookie.value().as_bytes()).unwrap_or_default();
        let mut parts = bytes.split(|&a| a == 0xff);
        while let (Some(key), Some(value)) = (parts.next(), parts.next()) {
            if key.is_empty() {
                break;
            }
            if let (Ok(key), Ok(value)) = (str::from_utf8(key), str::from_utf8(value)) {
                ret.insert(key.to_string(), value.to_string());
            }
        }
        ret
    }

    pub fn encode(h: &HashMap<String, String>) -> String {
        let mut ret = Vec::new();
        for (i, (k, v)) in h.iter().enumerate() {
            if i != 0 {
                ret.push(0xff)
            }
            ret.extend(k.bytes());
            ret.push(0xff);
            ret.extend(v.bytes());
        }
        while ret.len() * 8 % 6 != 0 {
            ret.push(0xff);
        }
        encode(&ret[..])
    }
}

impl conduit_middleware::Middleware for SessionMiddleware {
    fn before(&self, req: &mut dyn RequestExt) -> BeforeResult {
        let session = {
            let jar = req.cookies_mut().signed(&self.key);
            jar.get(&self.cookie_name)
                .map(|cookie| Self::decode(cookie))
                .unwrap_or_else(HashMap::new)
        };
        req.mut_extensions().insert(Session {
            data: session,
            dirty: false,
        });
        Ok(())
    }

    fn after(&self, req: &mut dyn RequestExt, res: AfterResult) -> AfterResult {
        let session = req.extensions().find::<Session>();
        let session = session.expect("session must be present after request");
        if session.dirty {
            let encoded = Self::encode(&session.data);
            let cookie = Cookie::build(self.cookie_name.to_string(), encoded)
                .http_only(true)
                .secure(self.secure)
                .same_site(SameSite::Strict)
                .max_age(time::Duration::days(MAX_AGE_DAYS))
                .path("/")
                .finish();
            req.cookies_mut().signed(&self.key).add(cookie);
        }
        res
    }
}

pub trait RequestSession {
    fn session(&self) -> &HashMap<String, String>;
    fn session_mut(&mut self) -> &mut HashMap<String, String>;
}

impl<T: RequestExt + ?Sized> RequestSession for T {
    fn session(&self) -> &HashMap<String, String> {
        &self
            .extensions()
            .find::<Session>()
            .expect("missing cookie session")
            .data
    }

    fn session_mut(&mut self) -> &mut HashMap<String, String> {
        let session = self
            .mut_extensions()
            .find_mut::<Session>()
            .expect("missing cookie session");
        session.dirty = true;
        &mut session.data
    }
}

#[cfg(test)]
mod test {
    use std::collections::HashMap;

    use conduit::{header, Body, Handler, HttpResult, Method, RequestExt, Response};
    use conduit_middleware::MiddlewareBuilder;
    use conduit_test::MockRequest;
    use cookie::{Cookie, Key};

    use {Middleware, RequestSession, SessionMiddleware};

    fn test_key() -> Key {
        let master_key: Vec<u8> = (0..32).collect();
        Key::derive_from(&master_key)
    }

    #[test]
    fn simple() {
        let mut req = MockRequest::new(Method::POST, "/articles");
        let key = test_key();

        // Set the session cookie
        let mut app = MiddlewareBuilder::new(set_session);
        app.add(Middleware::new());
        app.add(SessionMiddleware::new("lol", key, false));
        let response = app.call(&mut req).unwrap();

        let v = response
            .headers()
            .get(header::SET_COOKIE)
            .unwrap()
            .to_str()
            .unwrap();
        assert!(v.starts_with("lol"));

        // Use the session cookie
        req.header(header::COOKIE, v);
        let key = test_key();
        let mut app = MiddlewareBuilder::new(use_session);
        app.add(Middleware::new());
        app.add(SessionMiddleware::new("lol", key, false));
        assert!(app.call(&mut req).is_ok());

        fn set_session(req: &mut dyn RequestExt) -> HttpResult {
            assert!(req
                .session_mut()
                .insert("foo".to_string(), "bar".to_string())
                .is_none());
            Response::builder().body(Body::empty())
        }
        fn use_session(req: &mut dyn RequestExt) -> HttpResult {
            assert_eq!(*req.session().get("foo").unwrap(), "bar");
            Response::builder().body(Body::empty())
        }
    }

    #[test]
    fn no_equals() {
        let e = {
            let mut map = HashMap::new();
            map.insert("a".to_string(), "bc".to_string());
            SessionMiddleware::encode(&map)
        };
        assert!(!e.ends_with('='));

        let m = SessionMiddleware::decode(Cookie::new("foo", e));
        assert_eq!(*m.get("a").unwrap(), "bc");
    }

    #[test]
    fn dirty_tracking() {
        let mut req = MockRequest::new(Method::GET, "/");

        let mut app = MiddlewareBuilder::new(read_session);
        app.add(Middleware::new());
        app.add(SessionMiddleware::new("dirty", test_key(), false));
        let response = app.call(&mut req).unwrap();

        assert!(response.headers().get(header::SET_COOKIE).is_none());

        let mut app = MiddlewareBuilder::new(modify_session);
        app.add(Middleware::new());
        app.add(SessionMiddleware::new("dirty", test_key(), false));
        let response = app.call(&mut req).unwrap();

        assert!(response.headers().get(header::SET_COOKIE).is_some());

        fn read_session(req: &mut dyn RequestExt) -> HttpResult {
            req.session();
            Response::builder().body(Body::empty())
        }
        fn modify_session(req: &mut dyn RequestExt) -> HttpResult {
            req.session_mut();
            Response::builder().body(Body::empty())
        }
    }
}
<|endoftext|>


<commit_before>use std::thread::Thread;
use std::collections::hash_map::HashMap;
use std::time::Duration;
use std::io::BufferedReader;
use std::io::BufferedWriter;
use std::sync::mpsc::{Sender, Receiver, channel};
use std::io::Timer;
use std::io::IoResult;
use std::io::net::tcp::TcpStream;
use connection::Connection;
use subscription::AckMode;
use subscription::AckMode::{Auto, Client, ClientIndividual};
use subscription::AckOrNack;
use subscription::AckOrNack::{Ack, Nack};
use subscription::Subscription;
use frame::Frame;
use frame::Transmission;
use frame::Transmission::{HeartBeat, CompleteFrame};
use header;
use header::Header;
use header::ReceiptId;
use header::StompHeaderSet;
use transaction::Transaction;

//TODO: Replace the HashMap<String, ReceiptStatus> with a Set when
// Sets finally get `equiv` functionality
enum ReceiptStatus {
  Outstanding
}

pub struct Session {
  pub connection : Connection,
  sender: Sender<Frame>,
  receiver: Receiver<Frame>,
  next_transaction_id: u32,
  next_subscription_id: u32,
  next_receipt_id: u32,
  subscriptions: HashMap<String, Subscription>,
  outstanding_receipts: HashMap<String, ReceiptStatus>,
  receipt_callback: fn(&Frame) -> (),
  error_callback: fn(&Frame) -> ()
}

pub static GRACE_PERIOD_MULTIPLIER : f64 = 2.0f64;

impl Session {
  pub fn new(connection: Connection, tx_heartbeat_ms: u32, rx_heartbeat_ms: u32) -> Session {
    let reading_stream = connection.tcp_stream.clone();
    let writing_stream = reading_stream.clone();
    let (sender_tx, sender_rx) : (Sender<Frame>, Receiver<Frame>) = channel();
    let (receiver_tx, receiver_rx) : (Sender<Frame>, Receiver<Frame>) = channel();

    let modified_rx_heartbeat_ms : u32 = ((rx_heartbeat_ms as f64) * GRACE_PERIOD_MULTIPLIER) as u32;
    let _ = Thread::spawn(move || {
      match modified_rx_heartbeat_ms {
        0 => Session::receive_loop(receiver_tx, reading_stream),
        _ => Session::receive_loop_with_heartbeat(receiver_tx, reading_stream, Duration::milliseconds(modified_rx_heartbeat_ms as i64))
      }
    });
    let _ = Thread::spawn(move || {
      match tx_heartbeat_ms {
        0 => Session::send_loop(sender_rx, writing_stream),
        _ => Session::send_loop_with_heartbeat(sender_rx, writing_stream, Duration::milliseconds(tx_heartbeat_ms as i64))
      }
    });

    Session {
      connection: connection,
      sender : sender_tx,
      receiver : receiver_rx,
      next_transaction_id: 0,
      next_subscription_id: 0,
      next_receipt_id: 0,
      subscriptions: HashMap::with_capacity(1),
      outstanding_receipts: HashMap::new(),
      receipt_callback: Session::default_receipt_callback,
      error_callback: Session::default_error_callback
    }
  }

 fn send_loop(frames_to_send: Receiver<Frame>, tcp_stream: TcpStream){
    let mut writer : BufferedWriter<TcpStream> = BufferedWriter::new(tcp_stream);
    loop {
      let frame_to_send = frames_to_send.recv().ok().expect("Could not receive the next frame: communication was lost with the receiving thread.");
      frame_to_send.write(&mut writer).ok().expect("Couldn't send message!");
    }
  }

  fn send_loop_with_heartbeat(frames_to_send: Receiver<Frame>, tcp_stream: TcpStream, heartbeat: Duration){
    let mut writer : BufferedWriter<TcpStream> = BufferedWriter::new(tcp_stream);
    let mut timer = Timer::new().unwrap();
    loop {
      let timeout = timer.oneshot(heartbeat);
      select! {
        _ = timeout.recv() => {
          debug!("Sending heartbeat...");
          writer.write_char('\n').ok().expect("Failed to send heartbeat.");
          let _ = writer.flush();
        },
        frame_to_send = frames_to_send.recv() => {
          frame_to_send.unwrap().write(&mut writer).ok().expect("Couldn't send message!");
        }
      }
    }
  }

   fn receive_loop(frame_recipient: Sender<Frame>, tcp_stream: TcpStream){
    let (trans_tx, trans_rx) : (Sender<Transmission>, Receiver<Transmission>) = channel();
    let _ = Thread::spawn(move || {
      Session::read_loop(trans_tx, tcp_stream);
    });
    loop {
      match trans_rx.recv() {
        Ok(HeartBeat) => debug!("Received heartbeat"),
        Ok(CompleteFrame(frame)) => frame_recipient.send(frame).unwrap(),
        Err(_) => panic!("Could not read Transmission from remote host: the reading thread has died.")
      }
    }
  }

  fn receive_loop_with_heartbeat(frame_recipient: Sender<Frame>, tcp_stream: TcpStream, heartbeat: Duration){
    let (trans_tx, trans_rx) : (Sender<Transmission>, Receiver<Transmission>) = channel();
    let _ = Thread::spawn(move || {
      Session::read_loop(trans_tx, tcp_stream);
    });

    let mut timer = Timer::new().unwrap();
    loop {
      let timeout = timer.oneshot(heartbeat);
      select! {
        _ = timeout.recv() => error!("Did not receive expected heartbeat!"),
        transmission = trans_rx.recv() => {
          match transmission {
            Ok(HeartBeat) => debug!("Received heartbeat"),
            Ok(CompleteFrame(frame)) => frame_recipient.send(frame).unwrap(),
            Err(_) => panic!("Could not read Transmission from remote host: the readin thread has died.")
          }
        }
      }
    }
  }

  fn read_loop(transmission_listener: Sender<Transmission>, tcp_stream: TcpStream){
    let mut reader : BufferedReader<TcpStream> = BufferedReader::new(tcp_stream);
    loop {
      match Frame::read(&mut reader){
         Ok(transmission) => transmission_listener.send(transmission).unwrap(),
         Err(error) => panic!("Couldn't read from server!: {}", error)
      }
    }
  }

  fn default_error_callback(frame : &Frame) {
    error!("ERROR received:\n{}", frame);
  }

  fn default_receipt_callback(frame : &Frame) {
    info!("RECEIPT received:\n{}", frame);
  }

  pub fn on_error(&mut self, callback: fn(&Frame)) {
    self.error_callback = callback;
  }

  fn handle_receipt(&mut self, frame: Frame) {
    match frame.headers.get_receipt_id() {
      Some(ReceiptId(ref receipt_id)) => {
        match self.outstanding_receipts.remove(*receipt_id) {
          Some(ReceiptStatus::Outstanding) => {
            debug!("Removed ReceiptId '{}' from pending receipts.", *receipt_id)
          },
          None => {
            panic!("Received unexpected RECEIPT '{}'", *receipt_id)
          }
        }
      },
      None => panic!("Received RECEIPT frame without a receipt-id")
    };
    (self.receipt_callback)(&frame);
  }

  pub fn on_receipt(&mut self, callback: fn(&Frame)) {
     self.receipt_callback = callback;
  }

  pub fn outstanding_receipts(&self) -> Vec<&str> {
    self.outstanding_receipts.keys().map(|key| key.as_slice()).collect()
  }

  fn generate_transaction_id(&mut self) -> u32 {
    let id = self.next_transaction_id;
    self.next_transaction_id += 1;
    id
  }

  fn generate_subscription_id(&mut self) -> u32 {
    let id = self.next_subscription_id;
    self.next_subscription_id += 1;
    id
  }

  fn generate_receipt_id(&mut self) -> u32 {
    let id = self.next_receipt_id;
    self.next_receipt_id += 1;
    id
  }

  pub fn send_text(&mut self, topic: &str, body: &str) -> IoResult<()> {
    Ok(try!(self.send_bytes(topic, "text/plain", body.as_bytes())))
  }

  pub fn send_bytes(&mut self, topic: &str, mime_type: &str, body: &[u8]) -> IoResult<()> {
    let send_frame = Frame::send(topic, mime_type, body);
    Ok(self.send(send_frame))
  }

  pub fn send_text_with_receipt(&mut self, topic: &str, body: &str) -> IoResult<()> {
    Ok(try!(self.send_bytes_with_receipt(topic, "text/plain", body.as_bytes())))
  }

  pub fn send_bytes_with_receipt(&mut self, topic: &str, mime_type: &str, body: &[u8]) -> IoResult<()> {
    let mut send_frame = Frame::send(topic, mime_type, body);
    let receipt_id = format!("message/{}", self.generate_receipt_id().to_string());
    send_frame.headers.push(
      Header::encode_key_value("receipt", receipt_id.as_slice())
    );
    //try!(send_frame.write(&mut self.connection.sender));
    self.send(send_frame);
    self.outstanding_receipts.insert(receipt_id, ReceiptStatus::Outstanding);
    Ok(())
  }

  pub fn subscribe(&mut self, topic: &str, ack_mode: AckMode, callback: fn(&Frame)->AckOrNack)-> IoResult<String> {
    let next_id = self.generate_subscription_id();
    let sub = Subscription::new(next_id, topic, ack_mode, callback);
    let subscribe_frame = Frame::subscribe(sub.id.as_slice(), sub.topic.as_slice(), ack_mode);
    debug!("Sending frame:\n{}", subscribe_frame);
    self.send(subscribe_frame);
    //try!(subscribe_frame.write(&mut self.connection.sender));
    debug!("Registering callback for subscription id: {}", sub.id);
    let id_to_return = sub.id.to_string();
    self.subscriptions.insert(sub.id.to_string(), sub);
    Ok(id_to_return)
  }

  pub fn unsubscribe(&mut self, sub_id: &str) -> IoResult<()> {
     let _ = self.subscriptions.remove(sub_id);
     let unsubscribe_frame = Frame::unsubscribe(sub_id.as_slice());
     Ok(self.send(unsubscribe_frame))
  }

  pub fn disconnect(&mut self) -> IoResult<()> {
    let disconnect_frame = Frame::disconnect();
    Ok(self.send(disconnect_frame))
  }

  pub fn begin_transaction<'a>(&'a mut self) -> IoResult<Transaction<'a>> {
    let mut tx = Transaction::new(self.generate_transaction_id(), self);
    let _ = try!(tx.begin());
    Ok(tx)
  }

  pub fn send(&self, frame: Frame) {
    let _ = self.sender.send(frame);
  }

  pub fn receive(&self) -> Frame {
    self.receiver.recv().unwrap()
  }

  pub fn dispatch(&mut self, frame: Frame) {
    // Check for ERROR frame
    match frame.command.as_slice() {
       "ERROR" => return (self.error_callback)(&frame),
       "RECEIPT" => return self.handle_receipt(frame),
        _ => {} // No operation
    };

    let ack_mode : AckMode;
    let callback : fn(&Frame) -> AckOrNack;
    { // This extra scope is required to free up `frame` and `subscription`
      // following a borrow.
      let header::Subscription(sub_id) =
        frame.headers
        .get_subscription()
        .expect("Frame did not contain a subscription header.");

      let (a, c) =
         self.subscriptions
         .get(sub_id)
         .map(|sub| (sub.ack_mode, sub.callback))
         .expect("Received a message for an unknown subscription.");
      ack_mode = a;
      callback = c;
    }

    debug!("Executing.");
    let callback_result : AckOrNack = (callback)(&frame);
    match ack_mode {
      Auto => {
        debug!("Auto ack, no frame sent.");
      }
      Client | ClientIndividual => {
        let header::Ack(ack_id) =
          frame.headers
          .get_ack()
          .expect("Message did not have an 'ack' header.");
        match callback_result {
          Ack =>  self.acknowledge_frame(ack_id),
          Nack => self.negatively_acknowledge_frame(ack_id)
        }.unwrap_or_else(|error|panic!(format!("Could not acknowledge frame: {}", error)));
      } // Client | ...
    }
  }

  fn acknowledge_frame(&mut self, ack_id: &str) -> IoResult<()> {
    let ack_frame = Frame::ack(ack_id);
    Ok(self.send(ack_frame))
  }

  fn negatively_acknowledge_frame(&mut self, ack_id: &str) -> IoResult<()>{
    let nack_frame = Frame::nack(ack_id);
    Ok(self.send(nack_frame))
  }

  pub fn listen(&mut self) {
    loop {
      let frame = self.receive();
      debug!("Received '{}' frame, dispatching.", frame.command);
      self.dispatch(frame)
    }
  }
}
<commit_msg>Changed OutstandingReceipt from HashMap to HashSet<commit_after>use std::thread::Thread;
use std::collections::hash_set::HashSet;
use std::collections::hash_map::HashMap;
use std::time::Duration;
use std::io::BufferedReader;
use std::io::BufferedWriter;
use std::sync::mpsc::{Sender, Receiver, channel};
use std::io::Timer;
use std::io::IoResult;
use std::io::net::tcp::TcpStream;
use connection::Connection;
use subscription::AckMode;
use subscription::AckMode::{Auto, Client, ClientIndividual};
use subscription::AckOrNack;
use subscription::AckOrNack::{Ack, Nack};
use subscription::Subscription;
use frame::Frame;
use frame::Transmission;
use frame::Transmission::{HeartBeat, CompleteFrame};
use header;
use header::Header;
use header::ReceiptId;
use header::StompHeaderSet;
use transaction::Transaction;

pub struct Session {
  pub connection : Connection,
  sender: Sender<Frame>,
  receiver: Receiver<Frame>,
  next_transaction_id: u32,
  next_subscription_id: u32,
  next_receipt_id: u32,
  subscriptions: HashMap<String, Subscription>,
  outstanding_receipts: HashSet<String>,
  receipt_callback: fn(&Frame) -> (),
  error_callback: fn(&Frame) -> ()
}

pub static GRACE_PERIOD_MULTIPLIER : f64 = 2.0f64;

impl Session {
  pub fn new(connection: Connection, tx_heartbeat_ms: u32, rx_heartbeat_ms: u32) -> Session {
    let reading_stream = connection.tcp_stream.clone();
    let writing_stream = reading_stream.clone();
    let (sender_tx, sender_rx) : (Sender<Frame>, Receiver<Frame>) = channel();
    let (receiver_tx, receiver_rx) : (Sender<Frame>, Receiver<Frame>) = channel();

    let modified_rx_heartbeat_ms : u32 = ((rx_heartbeat_ms as f64) * GRACE_PERIOD_MULTIPLIER) as u32;
    let _ = Thread::spawn(move || {
      match modified_rx_heartbeat_ms {
        0 => Session::receive_loop(receiver_tx, reading_stream),
        _ => Session::receive_loop_with_heartbeat(receiver_tx, reading_stream, Duration::milliseconds(modified_rx_heartbeat_ms as i64))
      }
    });
    let _ = Thread::spawn(move || {
      match tx_heartbeat_ms {
        0 => Session::send_loop(sender_rx, writing_stream),
        _ => Session::send_loop_with_heartbeat(sender_rx, writing_stream, Duration::milliseconds(tx_heartbeat_ms as i64))
      }
    });

    Session {
      connection: connection,
      sender : sender_tx,
      receiver : receiver_rx,
      next_transaction_id: 0,
      next_subscription_id: 0,
      next_receipt_id: 0,
      subscriptions: HashMap::with_capacity(1),
      outstanding_receipts: HashSet::new(),
      receipt_callback: Session::default_receipt_callback,
      error_callback: Session::default_error_callback
    }
  }

 fn send_loop(frames_to_send: Receiver<Frame>, tcp_stream: TcpStream){
    let mut writer : BufferedWriter<TcpStream> = BufferedWriter::new(tcp_stream);
    loop {
      let frame_to_send = frames_to_send.recv().ok().expect("Could not receive the next frame: communication was lost with the receiving thread.");
      frame_to_send.write(&mut writer).ok().expect("Couldn't send message!");
    }
  }

  fn send_loop_with_heartbeat(frames_to_send: Receiver<Frame>, tcp_stream: TcpStream, heartbeat: Duration){
    let mut writer : BufferedWriter<TcpStream> = BufferedWriter::new(tcp_stream);
    let mut timer = Timer::new().unwrap();
    loop {
      let timeout = timer.oneshot(heartbeat);
      select! {
        _ = timeout.recv() => {
          debug!("Sending heartbeat...");
          writer.write_char('\n').ok().expect("Failed to send heartbeat.");
          let _ = writer.flush();
        },
        frame_to_send = frames_to_send.recv() => {
          frame_to_send.unwrap().write(&mut writer).ok().expect("Couldn't send message!");
        }
      }
    }
  }

   fn receive_loop(frame_recipient: Sender<Frame>, tcp_stream: TcpStream){
    let (trans_tx, trans_rx) : (Sender<Transmission>, Receiver<Transmission>) = channel();
    let _ = Thread::spawn(move || {
      Session::read_loop(trans_tx, tcp_stream);
    });
    loop {
      match trans_rx.recv() {
        Ok(HeartBeat) => debug!("Received heartbeat"),
        Ok(CompleteFrame(frame)) => frame_recipient.send(frame).unwrap(),
        Err(_) => panic!("Could not read Transmission from remote host: the reading thread has died.")
      }
    }
  }

  fn receive_loop_with_heartbeat(frame_recipient: Sender<Frame>, tcp_stream: TcpStream, heartbeat: Duration){
    let (trans_tx, trans_rx) : (Sender<Transmission>, Receiver<Transmission>) = channel();
    let _ = Thread::spawn(move || {
      Session::read_loop(trans_tx, tcp_stream);
    });

    let mut timer = Timer::new().unwrap();
    loop {
      let timeout = timer.oneshot(heartbeat);
      select! {
        _ = timeout.recv() => error!("Did not receive expected heartbeat!"),
        transmission = trans_rx.recv() => {
          match transmission {
            Ok(HeartBeat) => debug!("Received heartbeat"),
            Ok(CompleteFrame(frame)) => frame_recipient.send(frame).unwrap(),
            Err(_) => panic!("Could not read Transmission from remote host: the readin thread has died.")
          }
        }
      }
    }
  }

  fn read_loop(transmission_listener: Sender<Transmission>, tcp_stream: TcpStream){
    let mut reader : BufferedReader<TcpStream> = BufferedReader::new(tcp_stream);
    loop {
      match Frame::read(&mut reader){
         Ok(transmission) => transmission_listener.send(transmission).unwrap(),
         Err(error) => panic!("Couldn't read from server!: {}", error)
      }
    }
  }

  fn default_error_callback(frame : &Frame) {
    error!("ERROR received:\n{}", frame);
  }

  fn default_receipt_callback(frame : &Frame) {
    info!("RECEIPT received:\n{}", frame);
  }

  pub fn on_error(&mut self, callback: fn(&Frame)) {
    self.error_callback = callback;
  }

  fn handle_receipt(&mut self, frame: Frame) {
    match frame.headers.get_receipt_id() {
      Some(ReceiptId(ref receipt_id)) => {
        if self.outstanding_receipts.remove(*receipt_id) {
            debug!("Removed ReceiptId '{}' from pending receipts.", *receipt_id)
        } else {
            panic!("Received unexpected RECEIPT '{}'", *receipt_id)
        }
      },
      None => panic!("Received RECEIPT frame without a receipt-id")
    };
    (self.receipt_callback)(&frame);
  }

  pub fn on_receipt(&mut self, callback: fn(&Frame)) {
     self.receipt_callback = callback;
  }

  pub fn outstanding_receipts(&self) -> Vec<&str> {
    self.outstanding_receipts.iter().map(|key| key.as_slice()).collect()
  }

  fn generate_transaction_id(&mut self) -> u32 {
    let id = self.next_transaction_id;
    self.next_transaction_id += 1;
    id
  }

  fn generate_subscription_id(&mut self) -> u32 {
    let id = self.next_subscription_id;
    self.next_subscription_id += 1;
    id
  }

  fn generate_receipt_id(&mut self) -> u32 {
    let id = self.next_receipt_id;
    self.next_receipt_id += 1;
    id
  }

  pub fn send_text(&mut self, topic: &str, body: &str) -> IoResult<()> {
    Ok(try!(self.send_bytes(topic, "text/plain", body.as_bytes())))
  }

  pub fn send_bytes(&mut self, topic: &str, mime_type: &str, body: &[u8]) -> IoResult<()> {
    let send_frame = Frame::send(topic, mime_type, body);
    Ok(self.send(send_frame))
  }

  pub fn send_text_with_receipt(&mut self, topic: &str, body: &str) -> IoResult<()> {
    Ok(try!(self.send_bytes_with_receipt(topic, "text/plain", body.as_bytes())))
  }

  pub fn send_bytes_with_receipt(&mut self, topic: &str, mime_type: &str, body: &[u8]) -> IoResult<()> {
    let mut send_frame = Frame::send(topic, mime_type, body);
    let receipt_id = format!("message/{}", self.generate_receipt_id().to_string());
    send_frame.headers.push(
      Header::encode_key_value("receipt", receipt_id.as_slice())
    );
    //try!(send_frame.write(&mut self.connection.sender));
    self.send(send_frame);
    self.outstanding_receipts.insert(receipt_id);
    Ok(())
  }

  pub fn subscribe(&mut self, topic: &str, ack_mode: AckMode, callback: fn(&Frame)->AckOrNack)-> IoResult<String> {
    let next_id = self.generate_subscription_id();
    let sub = Subscription::new(next_id, topic, ack_mode, callback);
    let subscribe_frame = Frame::subscribe(sub.id.as_slice(), sub.topic.as_slice(), ack_mode);
    debug!("Sending frame:\n{}", subscribe_frame);
    self.send(subscribe_frame);
    //try!(subscribe_frame.write(&mut self.connection.sender));
    debug!("Registering callback for subscription id: {}", sub.id);
    let id_to_return = sub.id.to_string();
    self.subscriptions.insert(sub.id.to_string(), sub);
    Ok(id_to_return)
  }

  pub fn unsubscribe(&mut self, sub_id: &str) -> IoResult<()> {
     let _ = self.subscriptions.remove(sub_id);
     let unsubscribe_frame = Frame::unsubscribe(sub_id.as_slice());
     Ok(self.send(unsubscribe_frame))
  }

  pub fn disconnect(&mut self) -> IoResult<()> {
    let disconnect_frame = Frame::disconnect();
    Ok(self.send(disconnect_frame))
  }

  pub fn begin_transaction<'a>(&'a mut self) -> IoResult<Transaction<'a>> {
    let mut tx = Transaction::new(self.generate_transaction_id(), self);
    let _ = try!(tx.begin());
    Ok(tx)
  }

  pub fn send(&self, frame: Frame) {
    let _ = self.sender.send(frame);
  }

  pub fn receive(&self) -> Frame {
    self.receiver.recv().unwrap()
  }

  pub fn dispatch(&mut self, frame: Frame) {
    // Check for ERROR frame
    match frame.command.as_slice() {
       "ERROR" => return (self.error_callback)(&frame),
       "RECEIPT" => return self.handle_receipt(frame),
        _ => {} // No operation
    };

    let ack_mode : AckMode;
    let callback : fn(&Frame) -> AckOrNack;
    { // This extra scope is required to free up `frame` and `subscription`
      // following a borrow.
      let header::Subscription(sub_id) =
        frame.headers
        .get_subscription()
        .expect("Frame did not contain a subscription header.");

      let (a, c) =
         self.subscriptions
         .get(sub_id)
         .map(|sub| (sub.ack_mode, sub.callback))
         .expect("Received a message for an unknown subscription.");
      ack_mode = a;
      callback = c;
    }

    debug!("Executing.");
    let callback_result : AckOrNack = (callback)(&frame);
    match ack_mode {
      Auto => {
        debug!("Auto ack, no frame sent.");
      }
      Client | ClientIndividual => {
        let header::Ack(ack_id) =
          frame.headers
          .get_ack()
          .expect("Message did not have an 'ack' header.");
        match callback_result {
          Ack =>  self.acknowledge_frame(ack_id),
          Nack => self.negatively_acknowledge_frame(ack_id)
        }.unwrap_or_else(|error|panic!(format!("Could not acknowledge frame: {}", error)));
      } // Client | ...
    }
  }

  fn acknowledge_frame(&mut self, ack_id: &str) -> IoResult<()> {
    let ack_frame = Frame::ack(ack_id);
    Ok(self.send(ack_frame))
  }

  fn negatively_acknowledge_frame(&mut self, ack_id: &str) -> IoResult<()>{
    let nack_frame = Frame::nack(ack_id);
    Ok(self.send(nack_frame))
  }

  pub fn listen(&mut self) {
    loop {
      let frame = self.receive();
      debug!("Received '{}' frame, dispatching.", frame.command);
      self.dispatch(frame)
    }
  }
}
<|endoftext|>


<commit_before>//!
//! A session runs a filesystem implementation while it is being mounted
//! to a specific mount point. A session begins by mounting the filesystem
//! and ends by unmounting it. While the filesystem is mounted, the session
//! loop receives, dispatches and replies to kernel requests for filesystem
//! operations under its mount point.
//!

use std::task::TaskBuilder;
use libc::{EAGAIN, EINTR, ENODEV, ENOENT};
use channel;
use channel::Channel;
use Filesystem;
use request::{request, dispatch};

/// The max size of write requests from the kernel. The absolute minimum is 4k,
/// FUSE recommends at least 128k, max 16M. The FUSE default is 16M on OS X
/// and 128k on other systems.
pub const MAX_WRITE_SIZE: uint = 16*1024*1024;

/// Size of the buffer for reading a request from the kernel. Since the kernel may send
/// up to MAX_WRITE_SIZE bytes in a write request, we use that value plus some extra space.
const BUFFER_SIZE: uint = MAX_WRITE_SIZE + 4096;

/// The session data structure
pub struct Session<FS> {
    /// Filesystem operation implementations
    pub filesystem: FS,
    /// Path of the mounted filesystem
    pub mountpoint: Path,
    /// Communication channel to the kernel driver
    ch: Channel,
    /// FUSE protocol major version
    pub proto_major: uint,
    /// FUSE protocol minor version
    pub proto_minor: uint,
    /// True if the filesystem is initialized (init operation done)
    pub initialized: bool,
    /// True if the filesystem was destroyed (destroy operation done)
    pub destroyed: bool,
}

impl<FS: Filesystem+Send> Session<FS> {
    /// Create a new session by mounting the given filesystem to the given mountpoint
    pub fn new (filesystem: FS, mountpoint: &Path, options: &[&[u8]]) -> Session<FS> {
        info!("Mounting {}", mountpoint.display());
        let ch = match Channel::new(mountpoint, options) {
            Ok(ch) => ch,
            Err(err) => fail!("Unable to mount filesystem. Error {:i}", err),
        };
        Session {
            filesystem: filesystem,
            mountpoint: mountpoint.clone(),
            ch: ch,
            proto_major: 0,
            proto_minor: 0,
            initialized: false,
            destroyed: false,
        }
    }

    /// Run the session loop that receives kernel requests and dispatches them to method
    /// calls into the filesystem. This read-dispatch-loop is non-concurrent to prevent
    /// having multiple buffers (which take up much memory), but the filesystem methods
    /// may run concurrent by spawning tasks.
    /// Make sure to run this on a new single threaded scheduler since native I/O in the
    /// session loop can block.
    pub fn run (&mut self) {
        // Buffer for receiving requests from the kernel. Only one is allocated and
        // it is reused immediately after dispatching to conserve memory and allocations.
        let mut buffer = Vec::from_elem(BUFFER_SIZE, 0u8);
        loop {
            // Read the next request from the given channel to kernel driver
            // The kernel driver makes sure that we get exactly one request per read
            match self.ch.receive(buffer.as_mut_slice()) {
                Err(ENOENT) => continue,                // Operation interrupted. Accordingly to FUSE, this is safe to retry
                Err(EINTR) => continue,                 // Interrupted system call, retry
                Err(EAGAIN) => continue,                // Explicitly try again
                Err(ENODEV) => break,                   // Filesystem was unmounted, quit the loop
                Err(err) => fail!("Lost connection to FUSE device. Error {:i}", err),
                Ok(len) => match request(self.ch.sender(), buffer.slice_to(len)) {
                    None => break,                      // Illegal request, quit the loop
                    Some(req) => dispatch(&req, self),
                },
            }
        }
    }

    /// Run the session loop in a background task
    pub fn spawn (self) -> BackgroundSession {
        BackgroundSession::new(self)
    }
}

#[unsafe_destructor]
impl<FS: Filesystem+Send> Drop for Session<FS> {
    fn drop (&mut self) {
        info!("Unmounted {}", self.mountpoint.display());
        // The actual unmounting takes place because self.ch is dropped here
    }
}

/// The background session data structure
pub struct BackgroundSession {
    /// Path of the mounted filesystem
    pub mountpoint: Path,
}

impl BackgroundSession {
    /// Create a new background session for the given session by running its
    /// session loop in a background task. If the returned handle is dropped,
    /// the filesystem is unmounted and the given session ends.
    pub fn new<FS: Filesystem+Send> (se: Session<FS>) -> BackgroundSession {
        let mountpoint = se.mountpoint.clone();
        // The background task is started using a a new native thread
        // since native I/O in the session loop can block
        let task = TaskBuilder::new().named(format!("FUSE {}", mountpoint.display()));
        task.spawn(proc() {
            let mut se = se;
            se.run();
        });
        BackgroundSession { mountpoint: mountpoint }
    }
}

impl Drop for BackgroundSession {
    fn drop (&mut self) {
        info!("Unmounting {}", self.mountpoint.display());
        // Unmounting the filesystem will eventually end the session loop,
        // drop the session and hence end the background task.
        channel::unmount(&self.mountpoint);
    }
}
<commit_msg>fail! -> panic!<commit_after>//!
//! A session runs a filesystem implementation while it is being mounted
//! to a specific mount point. A session begins by mounting the filesystem
//! and ends by unmounting it. While the filesystem is mounted, the session
//! loop receives, dispatches and replies to kernel requests for filesystem
//! operations under its mount point.
//!

use std::task::TaskBuilder;
use libc::{EAGAIN, EINTR, ENODEV, ENOENT};
use channel;
use channel::Channel;
use Filesystem;
use request::{request, dispatch};

/// The max size of write requests from the kernel. The absolute minimum is 4k,
/// FUSE recommends at least 128k, max 16M. The FUSE default is 16M on OS X
/// and 128k on other systems.
pub const MAX_WRITE_SIZE: uint = 16*1024*1024;

/// Size of the buffer for reading a request from the kernel. Since the kernel may send
/// up to MAX_WRITE_SIZE bytes in a write request, we use that value plus some extra space.
const BUFFER_SIZE: uint = MAX_WRITE_SIZE + 4096;

/// The session data structure
pub struct Session<FS> {
    /// Filesystem operation implementations
    pub filesystem: FS,
    /// Path of the mounted filesystem
    pub mountpoint: Path,
    /// Communication channel to the kernel driver
    ch: Channel,
    /// FUSE protocol major version
    pub proto_major: uint,
    /// FUSE protocol minor version
    pub proto_minor: uint,
    /// True if the filesystem is initialized (init operation done)
    pub initialized: bool,
    /// True if the filesystem was destroyed (destroy operation done)
    pub destroyed: bool,
}

impl<FS: Filesystem+Send> Session<FS> {
    /// Create a new session by mounting the given filesystem to the given mountpoint
    pub fn new (filesystem: FS, mountpoint: &Path, options: &[&[u8]]) -> Session<FS> {
        info!("Mounting {}", mountpoint.display());
        let ch = match Channel::new(mountpoint, options) {
            Ok(ch) => ch,
            Err(err) => panic!("Unable to mount filesystem. Error {:i}", err),
        };
        Session {
            filesystem: filesystem,
            mountpoint: mountpoint.clone(),
            ch: ch,
            proto_major: 0,
            proto_minor: 0,
            initialized: false,
            destroyed: false,
        }
    }

    /// Run the session loop that receives kernel requests and dispatches them to method
    /// calls into the filesystem. This read-dispatch-loop is non-concurrent to prevent
    /// having multiple buffers (which take up much memory), but the filesystem methods
    /// may run concurrent by spawning tasks.
    /// Make sure to run this on a new single threaded scheduler since native I/O in the
    /// session loop can block.
    pub fn run (&mut self) {
        // Buffer for receiving requests from the kernel. Only one is allocated and
        // it is reused immediately after dispatching to conserve memory and allocations.
        let mut buffer = Vec::from_elem(BUFFER_SIZE, 0u8);
        loop {
            // Read the next request from the given channel to kernel driver
            // The kernel driver makes sure that we get exactly one request per read
            match self.ch.receive(buffer.as_mut_slice()) {
                Err(ENOENT) => continue,                // Operation interrupted. Accordingly to FUSE, this is safe to retry
                Err(EINTR) => continue,                 // Interrupted system call, retry
                Err(EAGAIN) => continue,                // Explicitly try again
                Err(ENODEV) => break,                   // Filesystem was unmounted, quit the loop
                Err(err) => panic!("Lost connection to FUSE device. Error {:i}", err),
                Ok(len) => match request(self.ch.sender(), buffer.slice_to(len)) {
                    None => break,                      // Illegal request, quit the loop
                    Some(req) => dispatch(&req, self),
                },
            }
        }
    }

    /// Run the session loop in a background task
    pub fn spawn (self) -> BackgroundSession {
        BackgroundSession::new(self)
    }
}

#[unsafe_destructor]
impl<FS: Filesystem+Send> Drop for Session<FS> {
    fn drop (&mut self) {
        info!("Unmounted {}", self.mountpoint.display());
        // The actual unmounting takes place because self.ch is dropped here
    }
}

/// The background session data structure
pub struct BackgroundSession {
    /// Path of the mounted filesystem
    pub mountpoint: Path,
}

impl BackgroundSession {
    /// Create a new background session for the given session by running its
    /// session loop in a background task. If the returned handle is dropped,
    /// the filesystem is unmounted and the given session ends.
    pub fn new<FS: Filesystem+Send> (se: Session<FS>) -> BackgroundSession {
        let mountpoint = se.mountpoint.clone();
        // The background task is started using a a new native thread
        // since native I/O in the session loop can block
        let task = TaskBuilder::new().named(format!("FUSE {}", mountpoint.display()));
        task.spawn(proc() {
            let mut se = se;
            se.run();
        });
        BackgroundSession { mountpoint: mountpoint }
    }
}

impl Drop for BackgroundSession {
    fn drop (&mut self) {
        info!("Unmounting {}", self.mountpoint.display());
        // Unmounting the filesystem will eventually end the session loop,
        // drop the session and hence end the background task.
        channel::unmount(&self.mountpoint);
    }
}
<|endoftext|>


<commit_before>use std::mem;
use std::marker::PhantomData;
use gl;
use gl::types::*;
use context::Context;
use image_data::Image2d;
use types::GLError;

pub struct Texture<T: TextureType> {
    gl_id: GLuint,
    phantom: PhantomData<*mut T>
}
pub type Texture2d = Texture<Tx2d>;
pub type TextureCubeMap = Texture<TxCubeMap>;

impl<T: TextureType> Texture<T> {
    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl<T: TextureType> Drop for Texture<T> {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteTextures(1, &self.gl_id as *const GLuint);
        }
    }
}

impl Context {
    pub fn gen_texture<T: TextureType>(&self) -> Texture<T> {
        unsafe {
            let mut id : GLuint =  0;

            gl::GenTextures(1, &mut id as *mut GLuint);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`n` is negative",
                _ => "Unknown error"
            }

            Texture {
                gl_id: id,
                phantom: PhantomData
            }
        }
    }
}

pub trait ImageTargetType {
    fn gl_enum(&self) -> GLenum;
}

pub trait TextureType {
    type ImageTargetType: ImageTargetType;

    fn target() -> TextureBindingTarget;
}

pub struct Tx2d;

gl_enum! {
    pub gl_enum Tx2dImageTarget {
        Texture2d as TEXTURE_2D_TARGET = gl::TEXTURE_2D
    }
}

impl ImageTargetType for Tx2dImageTarget {
    fn gl_enum(&self) -> GLenum {
        self.gl_enum()
    }
}

impl TextureType for Tx2d {
    type ImageTargetType = Tx2dImageTarget;

    fn target() -> TextureBindingTarget {
        TextureBindingTarget::Texture2d
    }
}

pub struct TxCubeMap;

gl_enum! {
    pub gl_enum TxCubeMapImageTarget {
        CubeMapPositiveX as TEXTURE_CUBE_MAP_POSITIVE_X =
            gl::TEXTURE_CUBE_MAP_POSITIVE_X,
        CubeMapNegativeX as TEXTURE_CUBE_MAP_NEGATIVE_X =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_X,
        CubeMapPositiveY as TEXTURE_CUBE_MAP_POSITIVE_Y =
            gl::TEXTURE_CUBE_MAP_POSITIVE_Y,
        CubeMapNegativeY as TEXTURE_CUBE_MAP_NEGATIVE_Y =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_Y,
        CubeMapPositiveZ as TEXTURE_CUBE_MAP_POSITIVE_Z =
            gl::TEXTURE_CUBE_MAP_POSITIVE_Z,
        CubeMapNegativeZ as TEXTURE_CUBE_MAP_NEGATIVE_Z =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_Z
    }
}

impl ImageTargetType for TxCubeMapImageTarget {
    fn gl_enum(&self) -> GLenum {
        self.gl_enum()
    }
}

impl TextureType for TxCubeMap {
    type ImageTargetType = TxCubeMapImageTarget;

    fn target() -> TextureBindingTarget {
        TextureBindingTarget::TextureCubeMap
    }
}

gl_enum! {
    pub gl_enum TextureBindingTarget {
        Texture2d as TEXTURE_2D = gl::TEXTURE_2D,
        TextureCubeMap as TEXTURE_CUBE_MAP = gl::TEXTURE_CUBE_MAP
    }
}

// TODO: Use type refinements someday...
#[derive(Debug, Clone, Copy)]
pub enum TextureFilter {
    Nearest,
    Linear
}

#[derive(Debug, Clone, Copy)]
pub enum TextureMipmapFilter {
    Filter(TextureFilter),
    MipmapFilter { criterion: TextureFilter, mipmap: TextureFilter }
}

pub const NEAREST : TextureFilter = TextureFilter::Nearest;
pub const LINEAR : TextureFilter = TextureFilter::Linear;
pub const NEAREST_MIPMAP_NEAREST : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Nearest,
        mipmap: TextureFilter::Nearest
    };
pub const LINEAR_MIPMAP_NEAREST : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Linear,
        mipmap: TextureFilter::Nearest
    };
pub const NEAREST_MIPMAP_LINEAR : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Nearest,
        mipmap: TextureFilter::Linear
    };
pub const LINEAR_MIPMAP_LINEAR : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Linear,
        mipmap: TextureFilter::Linear
    };

#[allow(dead_code)]
impl TextureFilter {
    fn from_gl(gl_enum: GLenum) -> Result<Self, ()> {
        match gl_enum {
            gl::NEAREST => { Ok(self::NEAREST) },
            gl::LINEAR => { Ok(self::LINEAR) },
            _ => { Err(()) }
        }
    }

    fn gl_enum(&self) -> GLenum {
        match *self {
            self::NEAREST => gl::NEAREST,
            self::LINEAR => gl::LINEAR
        }
    }
}

#[allow(dead_code)]
impl TextureMipmapFilter {
    fn from_gl(gl_enum: GLenum) -> Result<Self, ()> {
        match gl_enum {
            gl::NEAREST => { Ok(TextureMipmapFilter::Filter(self::NEAREST)) },
            gl::LINEAR => { Ok(TextureMipmapFilter::Filter(self::LINEAR)) },
            gl::NEAREST_MIPMAP_NEAREST => { Ok(self::NEAREST_MIPMAP_NEAREST) },
            gl::LINEAR_MIPMAP_NEAREST => { Ok(self::LINEAR_MIPMAP_NEAREST) },
            gl::NEAREST_MIPMAP_LINEAR => { Ok(self::NEAREST_MIPMAP_LINEAR) },
            gl::LINEAR_MIPMAP_LINEAR => { Ok(self::LINEAR_MIPMAP_LINEAR) },
            _ => { Err(()) }
        }
    }

    fn gl_enum(&self) -> GLenum {
        match *self {
            TextureMipmapFilter::Filter(self::LINEAR) => { gl::LINEAR },
            TextureMipmapFilter::Filter(self::NEAREST) => { gl::NEAREST },
            self::NEAREST_MIPMAP_NEAREST => { gl::NEAREST_MIPMAP_NEAREST },
            self::LINEAR_MIPMAP_NEAREST => { gl::LINEAR_MIPMAP_NEAREST },
            self::NEAREST_MIPMAP_LINEAR => { gl::NEAREST_MIPMAP_LINEAR },
            self::LINEAR_MIPMAP_LINEAR => { gl::LINEAR_MIPMAP_LINEAR }
        }
    }
}

pub trait TextureBinding {
    type TextureType: TextureType;

    fn target() -> TextureBindingTarget {
        Self::TextureType::target()
    }

    fn image_2d<I>(&mut self,
                   level: i32,
                   target: <Self::TextureType as TextureType>::ImageTargetType,
                   img: &I)
        where I: Image2d
    {
        unsafe {
            let ptr = mem::transmute(img.textel_bytes().as_ptr());
            gl::TexImage2D(target.gl_enum(),
                           level as GLint,
                           img.format().textel_format.gl_enum() as GLint,
                           img.width() as i32,
                           img.height() as i32,
                           0,
                           img.format().textel_format.gl_enum(),
                           img.format().textel_type.gl_enum(),
                           ptr);
            dbg_gl_error! {
                GLError::InvalidEnum => "`target`, `format`, or `type` is not an accepted value",
                GLError::InvalidValue => "`target`, `level`, `internalformat`, `width`, `height`, or `border` is an invalid value",
                GLError::InvalidOperation => "`format` conflicts with either `internalformat` or `type`",
                _ => "Unknown error"
            }
        }
    }
}

pub struct Texture2dBinding<'a> {
    phantom: PhantomData<&'a mut Texture2d>
}

impl<'a> TextureBinding for Texture2dBinding<'a> {
    type TextureType = Tx2d;
}

pub struct TextureCubeMapBinding<'a> {
    phantom: PhantomData<&'a mut TextureCubeMap>
}

impl<'a> TextureBinding for TextureCubeMapBinding<'a> {
    type TextureType = TxCubeMap;
}

unsafe fn _bind_texture<T: TextureType>(texture: &mut Texture<T>) {
    gl::BindTexture(T::target().gl_enum(), texture.gl_id());
    dbg_gl_error! {
        GLError::InvalidEnum => "`target` is not one of the allowed values",
        GLError::InvalidOperation => "`texture` was created with a target that doesn't match `target`",
        _ => "Unknown error"
    }
}

pub struct Texture2dBinder;
impl Texture2dBinder {
    pub fn bind<'a>(&'a mut self, texture: &mut Texture2d)
        -> Texture2dBinding<'a>
    {
        unsafe {
            _bind_texture(texture);
        }
        Texture2dBinding { phantom: PhantomData }
    }
}

pub struct TextureCubeMapBinder;
impl TextureCubeMapBinder {
    pub fn bind<'a>(&'a mut self, texture: &mut TextureCubeMap)
        -> TextureCubeMapBinding<'a>
    {
        unsafe {
            _bind_texture(texture);
        }
        TextureCubeMapBinding { phantom: PhantomData }
    }
}
<commit_msg>Impl `From` to convert from `TextureFilter` to `TextureMipmapFilter`<commit_after>use std::mem;
use std::marker::PhantomData;
use gl;
use gl::types::*;
use context::Context;
use image_data::Image2d;
use types::GLError;

pub struct Texture<T: TextureType> {
    gl_id: GLuint,
    phantom: PhantomData<*mut T>
}
pub type Texture2d = Texture<Tx2d>;
pub type TextureCubeMap = Texture<TxCubeMap>;

impl<T: TextureType> Texture<T> {
    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl<T: TextureType> Drop for Texture<T> {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteTextures(1, &self.gl_id as *const GLuint);
        }
    }
}

impl Context {
    pub fn gen_texture<T: TextureType>(&self) -> Texture<T> {
        unsafe {
            let mut id : GLuint =  0;

            gl::GenTextures(1, &mut id as *mut GLuint);
            dbg_gl_sanity_check! {
                GLError::InvalidValue => "`n` is negative",
                _ => "Unknown error"
            }

            Texture {
                gl_id: id,
                phantom: PhantomData
            }
        }
    }
}

pub trait ImageTargetType {
    fn gl_enum(&self) -> GLenum;
}

pub trait TextureType {
    type ImageTargetType: ImageTargetType;

    fn target() -> TextureBindingTarget;
}

pub struct Tx2d;

gl_enum! {
    pub gl_enum Tx2dImageTarget {
        Texture2d as TEXTURE_2D_TARGET = gl::TEXTURE_2D
    }
}

impl ImageTargetType for Tx2dImageTarget {
    fn gl_enum(&self) -> GLenum {
        self.gl_enum()
    }
}

impl TextureType for Tx2d {
    type ImageTargetType = Tx2dImageTarget;

    fn target() -> TextureBindingTarget {
        TextureBindingTarget::Texture2d
    }
}

pub struct TxCubeMap;

gl_enum! {
    pub gl_enum TxCubeMapImageTarget {
        CubeMapPositiveX as TEXTURE_CUBE_MAP_POSITIVE_X =
            gl::TEXTURE_CUBE_MAP_POSITIVE_X,
        CubeMapNegativeX as TEXTURE_CUBE_MAP_NEGATIVE_X =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_X,
        CubeMapPositiveY as TEXTURE_CUBE_MAP_POSITIVE_Y =
            gl::TEXTURE_CUBE_MAP_POSITIVE_Y,
        CubeMapNegativeY as TEXTURE_CUBE_MAP_NEGATIVE_Y =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_Y,
        CubeMapPositiveZ as TEXTURE_CUBE_MAP_POSITIVE_Z =
            gl::TEXTURE_CUBE_MAP_POSITIVE_Z,
        CubeMapNegativeZ as TEXTURE_CUBE_MAP_NEGATIVE_Z =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_Z
    }
}

impl ImageTargetType for TxCubeMapImageTarget {
    fn gl_enum(&self) -> GLenum {
        self.gl_enum()
    }
}

impl TextureType for TxCubeMap {
    type ImageTargetType = TxCubeMapImageTarget;

    fn target() -> TextureBindingTarget {
        TextureBindingTarget::TextureCubeMap
    }
}

gl_enum! {
    pub gl_enum TextureBindingTarget {
        Texture2d as TEXTURE_2D = gl::TEXTURE_2D,
        TextureCubeMap as TEXTURE_CUBE_MAP = gl::TEXTURE_CUBE_MAP
    }
}

// TODO: Use type refinements someday...
#[derive(Debug, Clone, Copy)]
pub enum TextureFilter {
    Nearest,
    Linear
}

#[derive(Debug, Clone, Copy)]
pub enum TextureMipmapFilter {
    Filter(TextureFilter),
    MipmapFilter { criterion: TextureFilter, mipmap: TextureFilter }
}

pub const NEAREST : TextureFilter = TextureFilter::Nearest;
pub const LINEAR : TextureFilter = TextureFilter::Linear;
pub const NEAREST_MIPMAP_NEAREST : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Nearest,
        mipmap: TextureFilter::Nearest
    };
pub const LINEAR_MIPMAP_NEAREST : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Linear,
        mipmap: TextureFilter::Nearest
    };
pub const NEAREST_MIPMAP_LINEAR : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Nearest,
        mipmap: TextureFilter::Linear
    };
pub const LINEAR_MIPMAP_LINEAR : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Linear,
        mipmap: TextureFilter::Linear
    };

#[allow(dead_code)]
impl TextureFilter {
    fn from_gl(gl_enum: GLenum) -> Result<Self, ()> {
        match gl_enum {
            gl::NEAREST => { Ok(self::NEAREST) },
            gl::LINEAR => { Ok(self::LINEAR) },
            _ => { Err(()) }
        }
    }

    fn gl_enum(&self) -> GLenum {
        match *self {
            self::NEAREST => gl::NEAREST,
            self::LINEAR => gl::LINEAR
        }
    }
}

#[allow(dead_code)]
impl TextureMipmapFilter {
    fn from_gl(gl_enum: GLenum) -> Result<Self, ()> {
        match gl_enum {
            gl::NEAREST => { Ok(TextureMipmapFilter::Filter(self::NEAREST)) },
            gl::LINEAR => { Ok(TextureMipmapFilter::Filter(self::LINEAR)) },
            gl::NEAREST_MIPMAP_NEAREST => { Ok(self::NEAREST_MIPMAP_NEAREST) },
            gl::LINEAR_MIPMAP_NEAREST => { Ok(self::LINEAR_MIPMAP_NEAREST) },
            gl::NEAREST_MIPMAP_LINEAR => { Ok(self::NEAREST_MIPMAP_LINEAR) },
            gl::LINEAR_MIPMAP_LINEAR => { Ok(self::LINEAR_MIPMAP_LINEAR) },
            _ => { Err(()) }
        }
    }

    fn gl_enum(&self) -> GLenum {
        match *self {
            TextureMipmapFilter::Filter(self::LINEAR) => { gl::LINEAR },
            TextureMipmapFilter::Filter(self::NEAREST) => { gl::NEAREST },
            self::NEAREST_MIPMAP_NEAREST => { gl::NEAREST_MIPMAP_NEAREST },
            self::LINEAR_MIPMAP_NEAREST => { gl::LINEAR_MIPMAP_NEAREST },
            self::NEAREST_MIPMAP_LINEAR => { gl::NEAREST_MIPMAP_LINEAR },
            self::LINEAR_MIPMAP_LINEAR => { gl::LINEAR_MIPMAP_LINEAR }
        }
    }
}

impl From<TextureFilter> for TextureMipmapFilter {
    fn from(filter: TextureFilter) -> TextureMipmapFilter {
        TextureMipmapFilter::Filter(filter)
    }
}

pub trait TextureBinding {
    type TextureType: TextureType;

    fn target() -> TextureBindingTarget {
        Self::TextureType::target()
    }

    fn image_2d<I>(&mut self,
                   level: i32,
                   target: <Self::TextureType as TextureType>::ImageTargetType,
                   img: &I)
        where I: Image2d
    {
        unsafe {
            let ptr = mem::transmute(img.textel_bytes().as_ptr());
            gl::TexImage2D(target.gl_enum(),
                           level as GLint,
                           img.format().textel_format.gl_enum() as GLint,
                           img.width() as i32,
                           img.height() as i32,
                           0,
                           img.format().textel_format.gl_enum(),
                           img.format().textel_type.gl_enum(),
                           ptr);
            dbg_gl_error! {
                GLError::InvalidEnum => "`target`, `format`, or `type` is not an accepted value",
                GLError::InvalidValue => "`target`, `level`, `internalformat`, `width`, `height`, or `border` is an invalid value",
                GLError::InvalidOperation => "`format` conflicts with either `internalformat` or `type`",
                _ => "Unknown error"
            }
        }
    }
}

pub struct Texture2dBinding<'a> {
    phantom: PhantomData<&'a mut Texture2d>
}

impl<'a> TextureBinding for Texture2dBinding<'a> {
    type TextureType = Tx2d;
}

pub struct TextureCubeMapBinding<'a> {
    phantom: PhantomData<&'a mut TextureCubeMap>
}

impl<'a> TextureBinding for TextureCubeMapBinding<'a> {
    type TextureType = TxCubeMap;
}

unsafe fn _bind_texture<T: TextureType>(texture: &mut Texture<T>) {
    gl::BindTexture(T::target().gl_enum(), texture.gl_id());
    dbg_gl_error! {
        GLError::InvalidEnum => "`target` is not one of the allowed values",
        GLError::InvalidOperation => "`texture` was created with a target that doesn't match `target`",
        _ => "Unknown error"
    }
}

pub struct Texture2dBinder;
impl Texture2dBinder {
    pub fn bind<'a>(&'a mut self, texture: &mut Texture2d)
        -> Texture2dBinding<'a>
    {
        unsafe {
            _bind_texture(texture);
        }
        Texture2dBinding { phantom: PhantomData }
    }
}

pub struct TextureCubeMapBinder;
impl TextureCubeMapBinder {
    pub fn bind<'a>(&'a mut self, texture: &mut TextureCubeMap)
        -> TextureCubeMapBinding<'a>
    {
        unsafe {
            _bind_texture(texture);
        }
        TextureCubeMapBinding { phantom: PhantomData }
    }
}
<|endoftext|>


<commit_before>use std::net::IpAddr;
use convert::TryFrom;
use metainfo::SHA1Hash20b;
use bencode::{BDict, BString, BInt, BList, DecodeError, DecodeErrorKind};
use std::str::FromStr;

pub struct TrackerReq {
    pub info_hash: SHA1Hash20b,
    pub peer_id: SHA1Hash20b,
    pub port: u32,
    pub uploaded: u64,
    pub left: u64,
    pub compact: bool,
    pub no_peer_id: bool,
    pub event: TrackerEvent,
    pub ip: Option<IpAddr>,
    pub numwant: Option<u32>,
    pub key: Option<String>,
    pub trackerid: Option<String>,
}

fn missing_field(fld: &str) -> DecodeError {
    DecodeError {
        position: None,
        kind: DecodeErrorKind::MissingField(fld.to_string()),
    }
}

impl TryFrom<BDict> for TrackerReq {
    type Err = DecodeError;
    fn try_from(dict: BDict) -> Result<Self, Self::Err> {
        // let info_hash: BString = try!(dict.get_copy("info hash").ok_or(missing_field("info_hash")));
        // let peer_id: BString = try!(dict.get_copy("peer id").ok_or(missing_field("peer id")));
        // let port: BInt = try!(dict.get_copy("port").ok_or(missing_field("port")));
        // let uploaded: BInt = try!(dict.get_copy("uploaded").ok_or(missing_field("uploaded")));
        // let left: BInt = try!(dict.get_copy("uploaded").ok_or(missing_field("uploaded")));
        unimplemented!();
    }
}

impl TryFrom<BDict> for TrackerResp {
    type Err = DecodeError;
    fn try_from(dict: BDict) -> Result<Self, Self::Err> {
        let failure_reason: Option<String> = dict.get_copy("failure reason");
        let warning_message: Option<String> = dict.get_copy("warning message");
        let interval: BInt = try!(dict.get_copy("interval").ok_or(missing_field("interval")));
        let min_interval: Option<BInt> = dict.get_copy("min interval");//.ok(); //.ok_or(missing_field("min interval"));
        let tracker_id: String = try!(dict.get_copy("tracker id")
            .ok_or(missing_field("tracker id")));
        let complete: BInt = try!(dict.get_copy("complete").ok_or(missing_field("complete")));

        let peers_blist: Vec<BDict> = try!(dict.get_copy("peers").ok_or(missing_field("peers")));
        let mut peers_list = Vec::new();
        for peer in peers_blist {
            let peer_id: String = try!(peer.get_copy("peer id").ok_or(missing_field("peer id")));
            let peer_ip: String = try!(peer.get_copy("ip").ok_or(missing_field("ip")));
            let peer_port: BInt = try!(peer.get_copy("port").ok_or(missing_field("port")));
            let ip = try!(IpAddr::from_str(&peer_ip).map_err(|_| missing_field("ip")));
            peers_list.push(Peer {
                peer_id: peer_id,
                ip: ip,
                port: peer_port.to_i64() as u32,
            });
        }
        Ok(TrackerResp {
            failure_reason: failure_reason,
            warning_message: warning_message,
            interval: interval.to_i64() as u32,
            min_interval: min_interval.map(|i| i.to_i64() as u32),
            tracker_id: tracker_id,
            complete: complete.to_i64() as u32,
            peers: peers_list,
        })
    }
}

pub enum TrackerEvent {
    Started,
    Stopped,
    Completed,
}

pub struct TrackerResp {
    pub failure_reason: Option<String>,
    pub warning_message: Option<String>,
    pub interval: u32,
    pub min_interval: Option<u32>,
    pub tracker_id: String,
    pub complete: u32,
    pub peers: Vec<Peer>,
}

pub struct Peer {
    pub peer_id: String,
    pub ip: IpAddr,
    pub port: u32,
}
<commit_msg>Add comments<commit_after>use std::net::IpAddr;
use convert::TryFrom;
use metainfo::SHA1Hash20b;
use bencode::{BDict, BString, BInt, BList, DecodeError, DecodeErrorKind};
use std::str::FromStr;

pub struct TrackerReq {
    pub info_hash: SHA1Hash20b,
    pub peer_id: SHA1Hash20b,
    pub port: u32,
    pub uploaded: u64,
    pub left: u64,
    pub compact: bool,
    pub no_peer_id: bool,
    pub event: TrackerEvent,
    pub ip: Option<IpAddr>,
    pub numwant: Option<u32>,
    pub key: Option<String>,
    pub trackerid: Option<String>,
}

fn missing_field(fld: &str) -> DecodeError {
    DecodeError {
        position: None,
        kind: DecodeErrorKind::MissingField(fld.to_string()),
    }
}

impl TryFrom<BDict> for TrackerReq {
    type Err = DecodeError;
    fn try_from(dict: BDict) -> Result<Self, Self::Err> {
        // let info_hash: BString = try!(dict.get_copy("info hash").ok_or(missing_field("info_hash")));
        // let peer_id: BString = try!(dict.get_copy("peer id").ok_or(missing_field("peer id")));
        // let port: BInt = try!(dict.get_copy("port").ok_or(missing_field("port")));
        // let uploaded: BInt = try!(dict.get_copy("uploaded").ok_or(missing_field("uploaded")));
        // let left: BInt = try!(dict.get_copy("uploaded").ok_or(missing_field("uploaded")));
        unimplemented!();
    }
}

impl TryFrom<BDict> for TrackerResp {
    type Err = DecodeError;
    fn try_from(dict: BDict) -> Result<Self, Self::Err> {
        //required fields
        let interval: BInt = try!(dict.get_copy("interval").ok_or(missing_field("interval")));
        let tracker_id: String = try!(dict.get_copy("tracker id")
            .ok_or(missing_field("tracker id")));
        let complete: BInt = try!(dict.get_copy("complete").ok_or(missing_field("complete")));

        //optional fields
        let failure_reason: Option<String> = dict.get_copy("failure reason");
        let warning_message: Option<String> = dict.get_copy("warning message");
        let min_interval: Option<BInt> = dict.get_copy("min interval");

        //parse the peer list
        let peers_blist: Vec<BDict> = try!(dict.get_copy("peers").ok_or(missing_field("peers")));
        let mut peers_list = Vec::new();
        for peer in peers_blist {
            let peer_id: String = try!(peer.get_copy("peer id").ok_or(missing_field("peer id")));
            let peer_ip: String = try!(peer.get_copy("ip").ok_or(missing_field("ip")));
            let peer_port: BInt = try!(peer.get_copy("port").ok_or(missing_field("port")));
            let ip = try!(IpAddr::from_str(&peer_ip).map_err(|_| missing_field("ip")));

            peers_list.push(Peer {
                peer_id: peer_id,
                ip: ip,
                port: peer_port.to_i64() as u32,
            });
        }

        //piece it together
        Ok(TrackerResp {
            failure_reason: failure_reason,
            warning_message: warning_message,
            interval: interval.to_i64() as u32,
            min_interval: min_interval.map(|i| i.to_i64() as u32),
            tracker_id: tracker_id,
            complete: complete.to_i64() as u32,
            peers: peers_list,
        })
    }
}

pub enum TrackerEvent {
    Started,
    Stopped,
    Completed,
}

pub struct TrackerResp {
    pub failure_reason: Option<String>,
    pub warning_message: Option<String>,
    pub interval: u32,
    pub min_interval: Option<u32>,
    pub tracker_id: String,
    pub complete: u32,
    pub peers: Vec<Peer>,
}

pub struct Peer {
    pub peer_id: String,
    pub ip: IpAddr,
    pub port: u32,
}
<|endoftext|>