<commit_before>#![allow(dead_code, unused_imports, unused_variables)]

use std::any::Any;
use std::rc::Rc;
use std::sync::Arc;
use std::marker::PhantomData;
use std::collections::HashMap;

use serde_json;
use serde_json::error::Error as JsonError;
use serde::{Serialize, Deserialize};
use http::header::{self, HeaderValue};
use cookie::{CookieJar, Cookie, Key};
use futures::Future;
use futures::future::{FutureResult, ok as FutOk, err as FutErr};

use error::{Result, Error, ResponseError};
use httprequest::HttpRequest;
use httpresponse::HttpResponse;
use middleware::{Middleware, Started, Response};

/// The helper trait to obtain your session data from a request.
///
/// ```rust
/// use actix_web::*;
/// use actix_web::middleware::RequestSession;
///
/// fn index(mut req: HttpRequest) -> Result<&'static str> {
///     // access session data
///     if let Some(count) = req.session().get::<i32>("counter")? {
///         req.session().set("counter", count+1)?;
///     } else {
///         req.session().set("counter", 1)?;
///     }
///
///     Ok("Welcome!")
/// }
/// # fn main() {}
/// ```
pub trait RequestSession {
    fn session(&mut self) -> Session;
}

impl RequestSession for HttpRequest {

    fn session(&mut self) -> Session {
        if let Some(s_impl) = self.extensions().get_mut::<Arc<SessionImplBox>>() {
            if let Some(s) = Arc::get_mut(s_impl) {
                return Session(s.0.as_mut())
            }
        }
        //Session(&mut DUMMY)
        unreachable!()
    }
}

/// The high-level interface you use to modify session data.
///
/// Session object could be obtained with
/// [`RequestSession::session`](trait.RequestSession.html#tymethod.session)
/// method. `RequestSession` trait is implemented for `HttpRequest`.
///
/// ```rust
/// use actix_web::*;
/// use actix_web::middleware::RequestSession;
///
/// fn index(mut req: HttpRequest) -> Result<&'static str> {
///     // access session data
///     if let Some(count) = req.session().get::<i32>("counter")? {
///         req.session().set("counter", count+1)?;
///     } else {
///         req.session().set("counter", 1)?;
///     }
///
///     Ok("Welcome!")
/// }
/// # fn main() {}
/// ```
pub struct Session<'a>(&'a mut SessionImpl);

impl<'a> Session<'a> {

    /// Get a `value` from the session.
    pub fn get<T: Deserialize<'a>>(&'a self, key: &str) -> Result<Option<T>> {
        if let Some(s) = self.0.get(key) {
            Ok(Some(serde_json::from_str(s)?))
        } else {
            Ok(None)
        }
    }

    /// Set a `value` from the session.
    pub fn set<T: Serialize>(&'a mut self, key: &str, value: T) -> Result<()> {
        self.0.set(key, serde_json::to_string(&value)?);
        Ok(())
    }

    /// Remove value from the session.
    pub fn remove(&'a mut self, key: &str) {
        self.0.remove(key)
    }

    /// Clear the session.
    pub fn clear(&'a mut self) {
        self.0.clear()
    }
}

struct SessionImplBox(Box<SessionImpl>);

#[doc(hidden)]
unsafe impl Send for SessionImplBox {}
#[doc(hidden)]
unsafe impl Sync for SessionImplBox {}

/// Session storage middleware
///
/// ```rust
/// # extern crate actix;
/// # extern crate actix_web;
/// # use actix_web::middleware::{SessionStorage, CookieSessionBackend};
/// use actix_web::*;
///
/// fn main() {
///    let app = Application::new().middleware(
///        SessionStorage::new(                      // <- create session middleware
///            CookieSessionBackend::build(&[0; 32]) // <- create cookie session backend
///               .secure(false)
///               .finish())
///    );
/// }
/// ```
pub struct SessionStorage<T, S>(T, PhantomData<S>);

impl<S, T: SessionBackend<S>> SessionStorage<T, S> {
    /// Create session storage
    pub fn new(backend: T) -> SessionStorage<T, S> {
        SessionStorage(backend, PhantomData)
    }
}

impl<S: 'static, T: SessionBackend<S>> Middleware<S> for SessionStorage<T, S> {

    fn start(&self, req: &mut HttpRequest<S>) -> Started {
        let mut req = req.clone();

        let fut = self.0.from_request(&mut req)
            .then(move |res| {
                match res {
                    Ok(sess) => {
                        req.extensions().insert(Arc::new(SessionImplBox(Box::new(sess))));
                        FutOk(None)
                    },
                    Err(err) => FutErr(err)
                }
            });
        Started::Future(Box::new(fut))
    }

    fn response(&self, req: &mut HttpRequest<S>, resp: HttpResponse) -> Response {
        if let Some(s_box) = req.extensions().remove::<Arc<SessionImplBox>>() {
            s_box.0.write(resp)
        } else {
            Response::Done(resp)
        }
    }
}

/// A simple key-value storage interface that is internally used by `Session`.
#[doc(hidden)]
pub trait SessionImpl: 'static {

    fn get(&self, key: &str) -> Option<&str>;

    fn set(&mut self, key: &str, value: String);

    fn remove(&mut self, key: &str);

    fn clear(&mut self);

    /// Write session to storage backend.
    fn write(&self, resp: HttpResponse) -> Response;
}

/// Session's storage backend trait definition.
#[doc(hidden)]
pub trait SessionBackend<S>: Sized + 'static {
    type Session: SessionImpl;
    type ReadFuture: Future<Item=Self::Session, Error=Error>;

    /// Parse the session from request and load data from a storage backend.
    fn from_request(&self, request: &mut HttpRequest<S>) -> Self::ReadFuture;
}

/// Dummy session impl, does not do anything
struct DummySessionImpl;

static DUMMY: DummySessionImpl = DummySessionImpl;

impl SessionImpl for DummySessionImpl {

    fn get(&self, key: &str) -> Option<&str> {
        None
    }
    fn set(&mut self, key: &str, value: String) {}
    fn remove(&mut self, key: &str) {}
    fn clear(&mut self) {}
    fn write(&self, resp: HttpResponse) -> Response {
        Response::Done(resp)
    }
}

/// Session that uses signed cookies as session storage
pub struct CookieSession {
    changed: bool,
    state: HashMap<String, String>,
    inner: Rc<CookieSessionInner>,
}

/// Errors that can occure during handling cookie session
#[derive(Fail, Debug)]
pub enum CookieSessionError {
    /// Size of the serialized session is greater than 4000 bytes.
    #[fail(display="Size of the serialized session is greater than 4000 bytes.")]
    Overflow,
    /// Fail to serialize session.
    #[fail(display="Fail to serialize session")]
    Serialize(JsonError),
}

impl ResponseError for CookieSessionError {}

impl SessionImpl for CookieSession {

    fn get(&self, key: &str) -> Option<&str> {
        if let Some(s) = self.state.get(key) {
            Some(s)
        } else {
            None
        }
    }

    fn set(&mut self, key: &str, value: String) {
        self.changed = true;
        self.state.insert(key.to_owned(), value);
    }

    fn remove(&mut self, key: &str) {
        self.changed = true;
        self.state.remove(key);
    }

    fn clear(&mut self) {
        self.changed = true;
        self.state.clear()
    }

    fn write(&self, mut resp: HttpResponse) -> Response {
        if self.changed {
            let _ = self.inner.set_cookie(&mut resp, &self.state);
        }
        Response::Done(resp)
    }
}

struct CookieSessionInner {
    key: Key,
    name: String,
    path: String,
    domain: Option<String>,
    secure: bool,
}

impl CookieSessionInner {

    fn new(key: &[u8]) -> CookieSessionInner {
        CookieSessionInner {
            key: Key::from_master(key),
            name: "actix_session".to_owned(),
            path: "/".to_owned(),
            domain: None,
            secure: true }
    }

    fn set_cookie(&self, resp: &mut HttpResponse, state: &HashMap<String, String>) -> Result<()> {
        let value = serde_json::to_string(&state)
            .map_err(CookieSessionError::Serialize)?;
        if value.len() > 4064 {
            return Err(CookieSessionError::Overflow.into())
        }

        let mut cookie = Cookie::new(self.name.clone(), value);
        cookie.set_path(self.path.clone());
        cookie.set_secure(self.secure);
        cookie.set_http_only(true);

        if let Some(ref domain) = self.domain {
            cookie.set_domain(domain.clone());
        }

        let mut jar = CookieJar::new();
        jar.signed(&self.key).add(cookie);

        for cookie in jar.delta() {
            let val = HeaderValue::from_str(&cookie.to_string())?;
            resp.headers_mut().append(header::SET_COOKIE, val);
        }

        Ok(())
    }

    fn load<S>(&self, req: &mut HttpRequest<S>) -> HashMap<String, String> {
        if let Ok(cookies) = req.cookies() {
            for cookie in cookies {
                if cookie.name() == self.name {
                    let mut jar = CookieJar::new();
                    jar.add_original(cookie.clone());
                    if let Some(cookie) = jar.signed(&self.key).get(&self.name) {
                        if let Ok(val) = serde_json::from_str(cookie.value()) {
                            return val;
                        }
                    }
                }
            }
        }
        HashMap::new()
    }
}

/// Use signed cookies as session storage.
///
/// `CookieSessionBackend` creates sessions which are limited to storing
/// fewer than 4000 bytes of data (as the payload must fit into a single cookie).
/// Internal server error get generated if session contains more than 4000 bytes.
///
/// You need to pass a random value to the constructor of `CookieSessionBackend`.
/// This is private key for cookie session, When this value is changed, all session data is lost.
///
/// Note that whatever you write into your session is visible by the user (but not modifiable).
///
/// Constructor panics if key length is less than 32 bytes.
pub struct CookieSessionBackend(Rc<CookieSessionInner>);

impl CookieSessionBackend {

    /// Construct new `CookieSessionBackend` instance.
    ///
    /// Panics if key length is less than 32 bytes.
    pub fn new(key: &[u8]) -> CookieSessionBackend {
        CookieSessionBackend(
            Rc::new(CookieSessionInner::new(key)))
    }

    /// Creates a new `CookieSessionBackendBuilder` instance from the given key.
    ///
    /// Panics if key length is less than 32 bytes.
    ///
    /// # Example
    ///
    /// ```
    /// use actix_web::middleware::CookieSessionBackend;
    ///
    /// let backend = CookieSessionBackend::build(&[0; 32]).finish();
    /// ```
    pub fn build(key: &[u8]) -> CookieSessionBackendBuilder {
        CookieSessionBackendBuilder::new(key)
    }
}

impl<S> SessionBackend<S> for CookieSessionBackend {

    type Session = CookieSession;
    type ReadFuture = FutureResult<CookieSession, Error>;

    fn from_request(&self, req: &mut HttpRequest<S>) -> Self::ReadFuture {
        let state = self.0.load(req);
        FutOk(
            CookieSession {
                changed: false,
                state: state,
                inner: Rc::clone(&self.0),
            })
    }
}

/// Structure that follows the builder pattern for building `CookieSessionBackend` structs.
///
/// To construct a backend:
///
///   1. Call [`CookieSessionBackend::build`](struct.CookieSessionBackend.html#method.build) to start building.
///   2. Use any of the builder methods to set fields in the backend.
///   3. Call [finish](#method.finish) to retrieve the constructed backend.
///
/// # Example
///
/// ```rust
/// # extern crate actix_web;
///
/// use actix_web::middleware::CookieSessionBackend;
///
/// # fn main() {
/// let backend: CookieSessionBackend = CookieSessionBackend::build(&[0; 32])
///     .domain("www.rust-lang.org")
///     .path("/")
///     .secure(true)
///     .finish();
/// # }
/// ```
pub struct CookieSessionBackendBuilder(CookieSessionInner);

impl CookieSessionBackendBuilder {
    pub fn new(key: &[u8]) -> CookieSessionBackendBuilder {
        CookieSessionBackendBuilder(
            CookieSessionInner::new(key))
    }

    /// Sets the `path` field in the session cookie being built.
    pub fn path<S: Into<String>>(mut self, value: S) -> CookieSessionBackendBuilder {
        self.0.path = value.into();
        self
    }

    /// Sets the `domain` field in the session cookie being built.
    pub fn domain<S: Into<String>>(mut self, value: S) -> CookieSessionBackendBuilder {
        self.0.domain = Some(value.into());
        self
    }

    /// Sets the `secure` field in the session cookie being built.
    pub fn secure(mut self, value: bool) -> CookieSessionBackendBuilder {
        self.0.secure = value;
        self
    }

    /// Finishes building and returns the built `CookieSessionBackend`.
    pub fn finish(self) -> CookieSessionBackend {
        CookieSessionBackend(Rc::new(self.0))
    }
}
<commit_msg>set sessio name<commit_after>#![allow(dead_code, unused_imports, unused_variables)]

use std::any::Any;
use std::rc::Rc;
use std::sync::Arc;
use std::marker::PhantomData;
use std::collections::HashMap;

use serde_json;
use serde_json::error::Error as JsonError;
use serde::{Serialize, Deserialize};
use http::header::{self, HeaderValue};
use cookie::{CookieJar, Cookie, Key};
use futures::Future;
use futures::future::{FutureResult, ok as FutOk, err as FutErr};

use error::{Result, Error, ResponseError};
use httprequest::HttpRequest;
use httpresponse::HttpResponse;
use middleware::{Middleware, Started, Response};

/// The helper trait to obtain your session data from a request.
///
/// ```rust
/// use actix_web::*;
/// use actix_web::middleware::RequestSession;
///
/// fn index(mut req: HttpRequest) -> Result<&'static str> {
///     // access session data
///     if let Some(count) = req.session().get::<i32>("counter")? {
///         req.session().set("counter", count+1)?;
///     } else {
///         req.session().set("counter", 1)?;
///     }
///
///     Ok("Welcome!")
/// }
/// # fn main() {}
/// ```
pub trait RequestSession {
    fn session(&mut self) -> Session;
}

impl RequestSession for HttpRequest {

    fn session(&mut self) -> Session {
        if let Some(s_impl) = self.extensions().get_mut::<Arc<SessionImplBox>>() {
            if let Some(s) = Arc::get_mut(s_impl) {
                return Session(s.0.as_mut())
            }
        }
        //Session(&mut DUMMY)
        unreachable!()
    }
}

/// The high-level interface you use to modify session data.
///
/// Session object could be obtained with
/// [`RequestSession::session`](trait.RequestSession.html#tymethod.session)
/// method. `RequestSession` trait is implemented for `HttpRequest`.
///
/// ```rust
/// use actix_web::*;
/// use actix_web::middleware::RequestSession;
///
/// fn index(mut req: HttpRequest) -> Result<&'static str> {
///     // access session data
///     if let Some(count) = req.session().get::<i32>("counter")? {
///         req.session().set("counter", count+1)?;
///     } else {
///         req.session().set("counter", 1)?;
///     }
///
///     Ok("Welcome!")
/// }
/// # fn main() {}
/// ```
pub struct Session<'a>(&'a mut SessionImpl);

impl<'a> Session<'a> {

    /// Get a `value` from the session.
    pub fn get<T: Deserialize<'a>>(&'a self, key: &str) -> Result<Option<T>> {
        if let Some(s) = self.0.get(key) {
            Ok(Some(serde_json::from_str(s)?))
        } else {
            Ok(None)
        }
    }

    /// Set a `value` from the session.
    pub fn set<T: Serialize>(&'a mut self, key: &str, value: T) -> Result<()> {
        self.0.set(key, serde_json::to_string(&value)?);
        Ok(())
    }

    /// Remove value from the session.
    pub fn remove(&'a mut self, key: &str) {
        self.0.remove(key)
    }

    /// Clear the session.
    pub fn clear(&'a mut self) {
        self.0.clear()
    }
}

struct SessionImplBox(Box<SessionImpl>);

#[doc(hidden)]
unsafe impl Send for SessionImplBox {}
#[doc(hidden)]
unsafe impl Sync for SessionImplBox {}

/// Session storage middleware
///
/// ```rust
/// # extern crate actix;
/// # extern crate actix_web;
/// # use actix_web::middleware::{SessionStorage, CookieSessionBackend};
/// use actix_web::*;
///
/// fn main() {
///    let app = Application::new().middleware(
///        SessionStorage::new(                      // <- create session middleware
///            CookieSessionBackend::build(&[0; 32]) // <- create cookie session backend
///               .secure(false)
///               .finish())
///    );
/// }
/// ```
pub struct SessionStorage<T, S>(T, PhantomData<S>);

impl<S, T: SessionBackend<S>> SessionStorage<T, S> {
    /// Create session storage
    pub fn new(backend: T) -> SessionStorage<T, S> {
        SessionStorage(backend, PhantomData)
    }
}

impl<S: 'static, T: SessionBackend<S>> Middleware<S> for SessionStorage<T, S> {

    fn start(&self, req: &mut HttpRequest<S>) -> Started {
        let mut req = req.clone();

        let fut = self.0.from_request(&mut req)
            .then(move |res| {
                match res {
                    Ok(sess) => {
                        req.extensions().insert(Arc::new(SessionImplBox(Box::new(sess))));
                        FutOk(None)
                    },
                    Err(err) => FutErr(err)
                }
            });
        Started::Future(Box::new(fut))
    }

    fn response(&self, req: &mut HttpRequest<S>, resp: HttpResponse) -> Response {
        if let Some(s_box) = req.extensions().remove::<Arc<SessionImplBox>>() {
            s_box.0.write(resp)
        } else {
            Response::Done(resp)
        }
    }
}

/// A simple key-value storage interface that is internally used by `Session`.
#[doc(hidden)]
pub trait SessionImpl: 'static {

    fn get(&self, key: &str) -> Option<&str>;

    fn set(&mut self, key: &str, value: String);

    fn remove(&mut self, key: &str);

    fn clear(&mut self);

    /// Write session to storage backend.
    fn write(&self, resp: HttpResponse) -> Response;
}

/// Session's storage backend trait definition.
#[doc(hidden)]
pub trait SessionBackend<S>: Sized + 'static {
    type Session: SessionImpl;
    type ReadFuture: Future<Item=Self::Session, Error=Error>;

    /// Parse the session from request and load data from a storage backend.
    fn from_request(&self, request: &mut HttpRequest<S>) -> Self::ReadFuture;
}

/// Dummy session impl, does not do anything
struct DummySessionImpl;

static DUMMY: DummySessionImpl = DummySessionImpl;

impl SessionImpl for DummySessionImpl {

    fn get(&self, key: &str) -> Option<&str> {
        None
    }
    fn set(&mut self, key: &str, value: String) {}
    fn remove(&mut self, key: &str) {}
    fn clear(&mut self) {}
    fn write(&self, resp: HttpResponse) -> Response {
        Response::Done(resp)
    }
}

/// Session that uses signed cookies as session storage
pub struct CookieSession {
    changed: bool,
    state: HashMap<String, String>,
    inner: Rc<CookieSessionInner>,
}

/// Errors that can occure during handling cookie session
#[derive(Fail, Debug)]
pub enum CookieSessionError {
    /// Size of the serialized session is greater than 4000 bytes.
    #[fail(display="Size of the serialized session is greater than 4000 bytes.")]
    Overflow,
    /// Fail to serialize session.
    #[fail(display="Fail to serialize session")]
    Serialize(JsonError),
}

impl ResponseError for CookieSessionError {}

impl SessionImpl for CookieSession {

    fn get(&self, key: &str) -> Option<&str> {
        if let Some(s) = self.state.get(key) {
            Some(s)
        } else {
            None
        }
    }

    fn set(&mut self, key: &str, value: String) {
        self.changed = true;
        self.state.insert(key.to_owned(), value);
    }

    fn remove(&mut self, key: &str) {
        self.changed = true;
        self.state.remove(key);
    }

    fn clear(&mut self) {
        self.changed = true;
        self.state.clear()
    }

    fn write(&self, mut resp: HttpResponse) -> Response {
        if self.changed {
            let _ = self.inner.set_cookie(&mut resp, &self.state);
        }
        Response::Done(resp)
    }
}

struct CookieSessionInner {
    key: Key,
    name: String,
    path: String,
    domain: Option<String>,
    secure: bool,
}

impl CookieSessionInner {

    fn new(key: &[u8]) -> CookieSessionInner {
        CookieSessionInner {
            key: Key::from_master(key),
            name: "actix_session".to_owned(),
            path: "/".to_owned(),
            domain: None,
            secure: true }
    }

    fn set_cookie(&self, resp: &mut HttpResponse, state: &HashMap<String, String>) -> Result<()> {
        let value = serde_json::to_string(&state)
            .map_err(CookieSessionError::Serialize)?;
        if value.len() > 4064 {
            return Err(CookieSessionError::Overflow.into())
        }

        let mut cookie = Cookie::new(self.name.clone(), value);
        cookie.set_path(self.path.clone());
        cookie.set_secure(self.secure);
        cookie.set_http_only(true);

        if let Some(ref domain) = self.domain {
            cookie.set_domain(domain.clone());
        }

        let mut jar = CookieJar::new();
        jar.signed(&self.key).add(cookie);

        for cookie in jar.delta() {
            let val = HeaderValue::from_str(&cookie.to_string())?;
            resp.headers_mut().append(header::SET_COOKIE, val);
        }

        Ok(())
    }

    fn load<S>(&self, req: &mut HttpRequest<S>) -> HashMap<String, String> {
        if let Ok(cookies) = req.cookies() {
            for cookie in cookies {
                if cookie.name() == self.name {
                    let mut jar = CookieJar::new();
                    jar.add_original(cookie.clone());
                    if let Some(cookie) = jar.signed(&self.key).get(&self.name) {
                        if let Ok(val) = serde_json::from_str(cookie.value()) {
                            return val;
                        }
                    }
                }
            }
        }
        HashMap::new()
    }
}

/// Use signed cookies as session storage.
///
/// `CookieSessionBackend` creates sessions which are limited to storing
/// fewer than 4000 bytes of data (as the payload must fit into a single cookie).
/// Internal server error get generated if session contains more than 4000 bytes.
///
/// You need to pass a random value to the constructor of `CookieSessionBackend`.
/// This is private key for cookie session, When this value is changed, all session data is lost.
///
/// Note that whatever you write into your session is visible by the user (but not modifiable).
///
/// Constructor panics if key length is less than 32 bytes.
pub struct CookieSessionBackend(Rc<CookieSessionInner>);

impl CookieSessionBackend {

    /// Construct new `CookieSessionBackend` instance.
    ///
    /// Panics if key length is less than 32 bytes.
    pub fn new(key: &[u8]) -> CookieSessionBackend {
        CookieSessionBackend(
            Rc::new(CookieSessionInner::new(key)))
    }

    /// Creates a new `CookieSessionBackendBuilder` instance from the given key.
    ///
    /// Panics if key length is less than 32 bytes.
    ///
    /// # Example
    ///
    /// ```
    /// use actix_web::middleware::CookieSessionBackend;
    ///
    /// let backend = CookieSessionBackend::build(&[0; 32]).finish();
    /// ```
    pub fn build(key: &[u8]) -> CookieSessionBackendBuilder {
        CookieSessionBackendBuilder::new(key)
    }
}

impl<S> SessionBackend<S> for CookieSessionBackend {

    type Session = CookieSession;
    type ReadFuture = FutureResult<CookieSession, Error>;

    fn from_request(&self, req: &mut HttpRequest<S>) -> Self::ReadFuture {
        let state = self.0.load(req);
        FutOk(
            CookieSession {
                changed: false,
                state: state,
                inner: Rc::clone(&self.0),
            })
    }
}

/// Structure that follows the builder pattern for building `CookieSessionBackend` structs.
///
/// To construct a backend:
///
///   1. Call [`CookieSessionBackend::build`](struct.CookieSessionBackend.html#method.build) to start building.
///   2. Use any of the builder methods to set fields in the backend.
///   3. Call [finish](#method.finish) to retrieve the constructed backend.
///
/// # Example
///
/// ```rust
/// # extern crate actix_web;
///
/// use actix_web::middleware::CookieSessionBackend;
///
/// # fn main() {
/// let backend: CookieSessionBackend = CookieSessionBackend::build(&[0; 32])
///     .domain("www.rust-lang.org")
///     .name("actix_session")
///     .path("/")
///     .secure(true)
///     .finish();
/// # }
/// ```
pub struct CookieSessionBackendBuilder(CookieSessionInner);

impl CookieSessionBackendBuilder {
    pub fn new(key: &[u8]) -> CookieSessionBackendBuilder {
        CookieSessionBackendBuilder(
            CookieSessionInner::new(key))
    }

    /// Sets the `path` field in the session cookie being built.
    pub fn path<S: Into<String>>(mut self, value: S) -> CookieSessionBackendBuilder {
        self.0.path = value.into();
        self
    }

    /// Sets the `name` field in the session cookie being built.
    pub fn name<S: Into<String>>(mut self, value: S) -> CookieSessionBackendBuilder {
        self.0.name = value.into();
        self
    }

    /// Sets the `domain` field in the session cookie being built.
    pub fn domain<S: Into<String>>(mut self, value: S) -> CookieSessionBackendBuilder {
        self.0.domain = Some(value.into());
        self
    }

    /// Sets the `secure` field in the session cookie being built.
    pub fn secure(mut self, value: bool) -> CookieSessionBackendBuilder {
        self.0.secure = value;
        self
    }

    /// Finishes building and returns the built `CookieSessionBackend`.
    pub fn finish(self) -> CookieSessionBackend {
        CookieSessionBackend(Rc::new(self.0))
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use hyper::header::Headers;
use hyper::method::Method;
use msg::constellation_msg::{PipelineId, ReferrerPolicy};
use std::cell::{Cell, RefCell};
use std::default::Default;
use std::mem::swap;
use url::{Origin as UrlOrigin, Url};

/// An [initiator](https://fetch.spec.whatwg.org/#concept-request-initiator)
#[derive(Copy, Clone, PartialEq, HeapSizeOf)]
pub enum Initiator {
    None,
    Download,
    ImageSet,
    Manifest,
    XSLT
}

/// A request [type](https://fetch.spec.whatwg.org/#concept-request-type)
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, HeapSizeOf)]
pub enum Type {
    None, Audio, Font, Image,
    Script, Style, Track, Video
}

/// A request [destination](https://fetch.spec.whatwg.org/#concept-request-destination)
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, HeapSizeOf)]
pub enum Destination {
    None, Document, Embed, Font, Image, Manifest,
    Media, Object, Report, Script, ServiceWorker,
    SharedWorker, Style, Worker, XSLT
}

/// A request [origin](https://fetch.spec.whatwg.org/#concept-request-origin)
#[derive(Clone, PartialEq, Debug, HeapSizeOf)]
pub enum Origin {
    Client,
    Origin(UrlOrigin)
}

/// A [referer](https://fetch.spec.whatwg.org/#concept-request-referrer)
#[derive(Clone, PartialEq, HeapSizeOf)]
pub enum Referrer {
    NoReferrer,
    /// Default referrer if nothing is specified
    Client,
    ReferrerUrl(Url)
}

/// A [request mode](https://fetch.spec.whatwg.org/#concept-request-mode)
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, HeapSizeOf)]
pub enum RequestMode {
    Navigate,
    SameOrigin,
    NoCORS,
    CORSMode
}

/// Request [credentials mode](https://fetch.spec.whatwg.org/#concept-request-credentials-mode)
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, HeapSizeOf)]
pub enum CredentialsMode {
    Omit,
    CredentialsSameOrigin,
    Include
}

/// [Cache mode](https://fetch.spec.whatwg.org/#concept-request-cache-mode)
#[derive(Copy, Clone, PartialEq, HeapSizeOf)]
pub enum CacheMode {
    Default,
    NoStore,
    Reload,
    NoCache,
    ForceCache,
    OnlyIfCached
}

/// [Redirect mode](https://fetch.spec.whatwg.org/#concept-request-redirect-mode)
#[derive(Copy, Clone, PartialEq, HeapSizeOf)]
pub enum RedirectMode {
    Follow,
    Error,
    Manual
}

/// [Response tainting](https://fetch.spec.whatwg.org/#concept-request-response-tainting)
#[derive(Copy, Clone, PartialEq, HeapSizeOf)]
pub enum ResponseTainting {
    Basic,
    CORSTainting,
    Opaque
}

/// [Window](https://fetch.spec.whatwg.org/#concept-request-window)
#[derive(Copy, Clone, PartialEq, HeapSizeOf)]
pub enum Window {
    NoWindow,
    Client,
    // TODO: Environmental settings object
}

/// [CORS settings attribute](https://html.spec.whatwg.org/multipage/#attr-crossorigin-anonymous)
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize)]
pub enum CORSSettings {
    Anonymous,
    UseCredentials
}

#[derive(Serialize, Deserialize, Clone)]
pub struct RequestInit {
    #[serde(deserialize_with = "::hyper_serde::deserialize",
            serialize_with = "::hyper_serde::serialize")]
    pub method: Method,
    pub url: Url,
    #[serde(deserialize_with = "::hyper_serde::deserialize",
            serialize_with = "::hyper_serde::serialize")]
    pub headers: Headers,
    pub unsafe_request: bool,
    pub body: Option<Vec<u8>>,
    // TODO: client object
    pub type_: Type,
    pub destination: Destination,
    pub synchronous: bool,
    pub mode: RequestMode,
    pub use_cors_preflight: bool,
    pub credentials_mode: CredentialsMode,
    pub use_url_credentials: bool,
    // this should actually be set by fetch, but fetch
    // doesn't have info about the client right now
    pub origin: Url,
    // XXXManishearth these should be part of the client object
    pub referrer_url: Option<Url>,
    pub referrer_policy: Option<ReferrerPolicy>,
    pub pipeline_id: Option<PipelineId>,
}

impl Default for RequestInit {
    fn default() -> RequestInit {
        RequestInit {
            method: Method::Get,
            url: Url::parse("about:blank").unwrap(),
            headers: Headers::new(),
            unsafe_request: false,
            body: None,
            type_: Type::None,
            destination: Destination::None,
            synchronous: false,
            mode: RequestMode::NoCORS,
            use_cors_preflight: false,
            credentials_mode: CredentialsMode::Omit,
            use_url_credentials: false,
            origin: Url::parse("about:blank").unwrap(),
            referrer_url: None,
            referrer_policy: None,
            pipeline_id: None,
        }
    }
}

/// A [Request](https://fetch.spec.whatwg.org/#requests) as defined by the Fetch spec
#[derive(Clone, HeapSizeOf)]
pub struct Request {
    #[ignore_heap_size_of = "Defined in hyper"]
    pub method: RefCell<Method>,
    pub local_urls_only: bool,
    pub sandboxed_storage_area_urls: bool,
    #[ignore_heap_size_of = "Defined in hyper"]
    pub headers: RefCell<Headers>,
    pub unsafe_request: bool,
    pub body: RefCell<Option<Vec<u8>>>,
    // TODO: client object
    pub is_service_worker_global_scope: bool,
    pub window: Cell<Window>,
    // TODO: target browsing context
    pub keep_alive: Cell<bool>,
    pub skip_service_worker: Cell<bool>,
    pub initiator: Initiator,
    pub type_: Type,
    pub destination: Destination,
    // TODO: priority object
    pub origin: RefCell<Origin>,
    pub omit_origin_header: Cell<bool>,
    /// https://fetch.spec.whatwg.org/#concept-request-referrer
    pub referrer: RefCell<Referrer>,
    pub referrer_policy: Cell<Option<ReferrerPolicy>>,
    pub pipeline_id: Cell<Option<PipelineId>>,
    pub synchronous: bool,
    pub mode: RequestMode,
    pub use_cors_preflight: bool,
    pub credentials_mode: CredentialsMode,
    pub use_url_credentials: bool,
    pub cache_mode: Cell<CacheMode>,
    pub redirect_mode: Cell<RedirectMode>,
    pub integrity_metadata: RefCell<String>,
    // Use the last method on url_list to act as spec current url field, and
    // first method to act as spec url field
    pub url_list: RefCell<Vec<Url>>,
    pub redirect_count: Cell<u32>,
    pub response_tainting: Cell<ResponseTainting>,
    pub done: Cell<bool>,
}

impl Request {
    pub fn new(url: Url,
               origin: Option<Origin>,
               is_service_worker_global_scope: bool,
               pipeline_id: Option<PipelineId>) -> Request {
        Request {
            method: RefCell::new(Method::Get),
            local_urls_only: false,
            sandboxed_storage_area_urls: false,
            headers: RefCell::new(Headers::new()),
            unsafe_request: false,
            body: RefCell::new(None),
            is_service_worker_global_scope: is_service_worker_global_scope,
            window: Cell::new(Window::Client),
            keep_alive: Cell::new(false),
            skip_service_worker: Cell::new(false),
            initiator: Initiator::None,
            type_: Type::None,
            destination: Destination::None,
            origin: RefCell::new(origin.unwrap_or(Origin::Client)),
            omit_origin_header: Cell::new(false),
            referrer: RefCell::new(Referrer::Client),
            referrer_policy: Cell::new(None),
            pipeline_id: Cell::new(pipeline_id),
            synchronous: false,
            mode: RequestMode::NoCORS,
            use_cors_preflight: false,
            credentials_mode: CredentialsMode::Omit,
            use_url_credentials: false,
            cache_mode: Cell::new(CacheMode::Default),
            redirect_mode: Cell::new(RedirectMode::Follow),
            integrity_metadata: RefCell::new(String::new()),
            url_list: RefCell::new(vec![url]),
            redirect_count: Cell::new(0),
            response_tainting: Cell::new(ResponseTainting::Basic),
            done: Cell::new(false)
        }
    }

    pub fn from_init(init: RequestInit) -> Request {
        let mut req = Request::new(init.url,
                                   Some(Origin::Origin(init.origin.origin())),
                                   false, init.pipeline_id);
        *req.method.borrow_mut() = init.method;
        *req.headers.borrow_mut() = init.headers;
        req.unsafe_request = init.unsafe_request;
        *req.body.borrow_mut() = init.body;
        req.type_ = init.type_;
        req.destination = init.destination;
        req.synchronous = init.synchronous;
        req.mode = init.mode;
        req.use_cors_preflight = init.use_cors_preflight;
        req.credentials_mode = init.credentials_mode;
        req.use_url_credentials = init.use_url_credentials;
        *req.referrer.borrow_mut() = if let Some(url) = init.referrer_url {
            Referrer::ReferrerUrl(url)
        } else {
            Referrer::NoReferrer
        };
        req.referrer_policy.set(init.referrer_policy);
        req.pipeline_id.set(init.pipeline_id);
        req
    }

    pub fn url(&self) -> Url {
        self.url_list.borrow().first().unwrap().clone()
    }

    pub fn current_url(&self) -> Url {
        self.url_list.borrow().last().unwrap().clone()
    }

    pub fn current_url_string(&self) -> String {
        self.url_list.borrow().last().unwrap().to_string()
    }

    pub fn is_navigation_request(&self) -> bool {
        self.destination == Destination::Document
    }

    pub fn is_subresource_request(&self) -> bool {
        match self.destination {
            Destination::Font | Destination::Image | Destination::Manifest
                | Destination::Media | Destination::Script
                | Destination::Style | Destination::XSLT
                | Destination::None => true,
            _ => false
        }
    }
}

impl Referrer {
    pub fn to_url(&self) -> Option<&Url> {
        match *self {
            Referrer::NoReferrer | Referrer::Client => None,
            Referrer::ReferrerUrl(ref url) => Some(url)
        }
    }
    pub fn from_url(url: Option<Url>) -> Self {
        if let Some(url) = url {
            Referrer::ReferrerUrl(url)
        } else {
            Referrer::NoReferrer
        }
    }
    pub fn take(&mut self) -> Option<Url> {
        let mut new = Referrer::Client;
        swap(self, &mut new);
        match new {
            Referrer::NoReferrer | Referrer::Client => None,
            Referrer::ReferrerUrl(url) => Some(url)
        }
    }
}
<commit_msg>Auto merge of #13678 - frewsxcv:unused-trait-method, r=KiChjang<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use hyper::header::Headers;
use hyper::method::Method;
use msg::constellation_msg::{PipelineId, ReferrerPolicy};
use std::cell::{Cell, RefCell};
use std::default::Default;
use std::mem::swap;
use url::{Origin as UrlOrigin, Url};

/// An [initiator](https://fetch.spec.whatwg.org/#concept-request-initiator)
#[derive(Copy, Clone, PartialEq, HeapSizeOf)]
pub enum Initiator {
    None,
    Download,
    ImageSet,
    Manifest,
    XSLT
}

/// A request [type](https://fetch.spec.whatwg.org/#concept-request-type)
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, HeapSizeOf)]
pub enum Type {
    None, Audio, Font, Image,
    Script, Style, Track, Video
}

/// A request [destination](https://fetch.spec.whatwg.org/#concept-request-destination)
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, HeapSizeOf)]
pub enum Destination {
    None, Document, Embed, Font, Image, Manifest,
    Media, Object, Report, Script, ServiceWorker,
    SharedWorker, Style, Worker, XSLT
}

/// A request [origin](https://fetch.spec.whatwg.org/#concept-request-origin)
#[derive(Clone, PartialEq, Debug, HeapSizeOf)]
pub enum Origin {
    Client,
    Origin(UrlOrigin)
}

/// A [referer](https://fetch.spec.whatwg.org/#concept-request-referrer)
#[derive(Clone, PartialEq, HeapSizeOf)]
pub enum Referrer {
    NoReferrer,
    /// Default referrer if nothing is specified
    Client,
    ReferrerUrl(Url)
}

/// A [request mode](https://fetch.spec.whatwg.org/#concept-request-mode)
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, HeapSizeOf)]
pub enum RequestMode {
    Navigate,
    SameOrigin,
    NoCORS,
    CORSMode
}

/// Request [credentials mode](https://fetch.spec.whatwg.org/#concept-request-credentials-mode)
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, HeapSizeOf)]
pub enum CredentialsMode {
    Omit,
    CredentialsSameOrigin,
    Include
}

/// [Cache mode](https://fetch.spec.whatwg.org/#concept-request-cache-mode)
#[derive(Copy, Clone, PartialEq, HeapSizeOf)]
pub enum CacheMode {
    Default,
    NoStore,
    Reload,
    NoCache,
    ForceCache,
    OnlyIfCached
}

/// [Redirect mode](https://fetch.spec.whatwg.org/#concept-request-redirect-mode)
#[derive(Copy, Clone, PartialEq, HeapSizeOf)]
pub enum RedirectMode {
    Follow,
    Error,
    Manual
}

/// [Response tainting](https://fetch.spec.whatwg.org/#concept-request-response-tainting)
#[derive(Copy, Clone, PartialEq, HeapSizeOf)]
pub enum ResponseTainting {
    Basic,
    CORSTainting,
    Opaque
}

/// [Window](https://fetch.spec.whatwg.org/#concept-request-window)
#[derive(Copy, Clone, PartialEq, HeapSizeOf)]
pub enum Window {
    NoWindow,
    Client,
    // TODO: Environmental settings object
}

/// [CORS settings attribute](https://html.spec.whatwg.org/multipage/#attr-crossorigin-anonymous)
#[derive(Copy, Clone, PartialEq, Serialize, Deserialize)]
pub enum CORSSettings {
    Anonymous,
    UseCredentials
}

#[derive(Serialize, Deserialize, Clone)]
pub struct RequestInit {
    #[serde(deserialize_with = "::hyper_serde::deserialize",
            serialize_with = "::hyper_serde::serialize")]
    pub method: Method,
    pub url: Url,
    #[serde(deserialize_with = "::hyper_serde::deserialize",
            serialize_with = "::hyper_serde::serialize")]
    pub headers: Headers,
    pub unsafe_request: bool,
    pub body: Option<Vec<u8>>,
    // TODO: client object
    pub type_: Type,
    pub destination: Destination,
    pub synchronous: bool,
    pub mode: RequestMode,
    pub use_cors_preflight: bool,
    pub credentials_mode: CredentialsMode,
    pub use_url_credentials: bool,
    // this should actually be set by fetch, but fetch
    // doesn't have info about the client right now
    pub origin: Url,
    // XXXManishearth these should be part of the client object
    pub referrer_url: Option<Url>,
    pub referrer_policy: Option<ReferrerPolicy>,
    pub pipeline_id: Option<PipelineId>,
}

impl Default for RequestInit {
    fn default() -> RequestInit {
        RequestInit {
            method: Method::Get,
            url: Url::parse("about:blank").unwrap(),
            headers: Headers::new(),
            unsafe_request: false,
            body: None,
            type_: Type::None,
            destination: Destination::None,
            synchronous: false,
            mode: RequestMode::NoCORS,
            use_cors_preflight: false,
            credentials_mode: CredentialsMode::Omit,
            use_url_credentials: false,
            origin: Url::parse("about:blank").unwrap(),
            referrer_url: None,
            referrer_policy: None,
            pipeline_id: None,
        }
    }
}

/// A [Request](https://fetch.spec.whatwg.org/#requests) as defined by the Fetch spec
#[derive(Clone, HeapSizeOf)]
pub struct Request {
    #[ignore_heap_size_of = "Defined in hyper"]
    pub method: RefCell<Method>,
    pub local_urls_only: bool,
    pub sandboxed_storage_area_urls: bool,
    #[ignore_heap_size_of = "Defined in hyper"]
    pub headers: RefCell<Headers>,
    pub unsafe_request: bool,
    pub body: RefCell<Option<Vec<u8>>>,
    // TODO: client object
    pub is_service_worker_global_scope: bool,
    pub window: Cell<Window>,
    // TODO: target browsing context
    pub keep_alive: Cell<bool>,
    pub skip_service_worker: Cell<bool>,
    pub initiator: Initiator,
    pub type_: Type,
    pub destination: Destination,
    // TODO: priority object
    pub origin: RefCell<Origin>,
    pub omit_origin_header: Cell<bool>,
    /// https://fetch.spec.whatwg.org/#concept-request-referrer
    pub referrer: RefCell<Referrer>,
    pub referrer_policy: Cell<Option<ReferrerPolicy>>,
    pub pipeline_id: Cell<Option<PipelineId>>,
    pub synchronous: bool,
    pub mode: RequestMode,
    pub use_cors_preflight: bool,
    pub credentials_mode: CredentialsMode,
    pub use_url_credentials: bool,
    pub cache_mode: Cell<CacheMode>,
    pub redirect_mode: Cell<RedirectMode>,
    pub integrity_metadata: RefCell<String>,
    // Use the last method on url_list to act as spec current url field, and
    // first method to act as spec url field
    pub url_list: RefCell<Vec<Url>>,
    pub redirect_count: Cell<u32>,
    pub response_tainting: Cell<ResponseTainting>,
    pub done: Cell<bool>,
}

impl Request {
    pub fn new(url: Url,
               origin: Option<Origin>,
               is_service_worker_global_scope: bool,
               pipeline_id: Option<PipelineId>) -> Request {
        Request {
            method: RefCell::new(Method::Get),
            local_urls_only: false,
            sandboxed_storage_area_urls: false,
            headers: RefCell::new(Headers::new()),
            unsafe_request: false,
            body: RefCell::new(None),
            is_service_worker_global_scope: is_service_worker_global_scope,
            window: Cell::new(Window::Client),
            keep_alive: Cell::new(false),
            skip_service_worker: Cell::new(false),
            initiator: Initiator::None,
            type_: Type::None,
            destination: Destination::None,
            origin: RefCell::new(origin.unwrap_or(Origin::Client)),
            omit_origin_header: Cell::new(false),
            referrer: RefCell::new(Referrer::Client),
            referrer_policy: Cell::new(None),
            pipeline_id: Cell::new(pipeline_id),
            synchronous: false,
            mode: RequestMode::NoCORS,
            use_cors_preflight: false,
            credentials_mode: CredentialsMode::Omit,
            use_url_credentials: false,
            cache_mode: Cell::new(CacheMode::Default),
            redirect_mode: Cell::new(RedirectMode::Follow),
            integrity_metadata: RefCell::new(String::new()),
            url_list: RefCell::new(vec![url]),
            redirect_count: Cell::new(0),
            response_tainting: Cell::new(ResponseTainting::Basic),
            done: Cell::new(false)
        }
    }

    pub fn from_init(init: RequestInit) -> Request {
        let mut req = Request::new(init.url,
                                   Some(Origin::Origin(init.origin.origin())),
                                   false, init.pipeline_id);
        *req.method.borrow_mut() = init.method;
        *req.headers.borrow_mut() = init.headers;
        req.unsafe_request = init.unsafe_request;
        *req.body.borrow_mut() = init.body;
        req.type_ = init.type_;
        req.destination = init.destination;
        req.synchronous = init.synchronous;
        req.mode = init.mode;
        req.use_cors_preflight = init.use_cors_preflight;
        req.credentials_mode = init.credentials_mode;
        req.use_url_credentials = init.use_url_credentials;
        *req.referrer.borrow_mut() = if let Some(url) = init.referrer_url {
            Referrer::ReferrerUrl(url)
        } else {
            Referrer::NoReferrer
        };
        req.referrer_policy.set(init.referrer_policy);
        req.pipeline_id.set(init.pipeline_id);
        req
    }

    pub fn url(&self) -> Url {
        self.url_list.borrow().first().unwrap().clone()
    }

    pub fn current_url(&self) -> Url {
        self.url_list.borrow().last().unwrap().clone()
    }

    pub fn is_navigation_request(&self) -> bool {
        self.destination == Destination::Document
    }

    pub fn is_subresource_request(&self) -> bool {
        match self.destination {
            Destination::Font | Destination::Image | Destination::Manifest
                | Destination::Media | Destination::Script
                | Destination::Style | Destination::XSLT
                | Destination::None => true,
            _ => false
        }
    }
}

impl Referrer {
    pub fn to_url(&self) -> Option<&Url> {
        match *self {
            Referrer::NoReferrer | Referrer::Client => None,
            Referrer::ReferrerUrl(ref url) => Some(url)
        }
    }
    pub fn from_url(url: Option<Url>) -> Self {
        if let Some(url) = url {
            Referrer::ReferrerUrl(url)
        } else {
            Referrer::NoReferrer
        }
    }
    pub fn take(&mut self) -> Option<Url> {
        let mut new = Referrer::Client;
        swap(self, &mut new);
        match new {
            Referrer::NoReferrer | Referrer::Client => None,
            Referrer::ReferrerUrl(url) => Some(url)
        }
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::TNode;
use properties::{DeclaredValue, PropertyDeclaration};
use values::HasViewportPercentage;

/// A structure to collect information about the cascade.
///
/// This is useful to gather information about what an element is affected by,
/// and can be used in the future to track optimisations like when a
/// non-inherited property is explicitly inherited, in order to cut-off the
/// traversal.
pub struct CascadeInfo {
    pub saw_viewport_units: bool,
    #[cfg(debug_assertions)]
    finished: bool,
}

impl CascadeInfo {
    #[cfg(debug_assertions)]
    pub fn new() -> Self {
        CascadeInfo {
            saw_viewport_units: false,
            finished: false,
        }
    }

    #[cfg(not(debug_assertions))]
    pub fn new() -> Self {
        CascadeInfo {
            saw_viewport_units: false,
        }
    }

    /// Called when a property is cascaded.
    ///
    /// NOTE: We can add a vast amount of information here.
    #[inline]
    pub fn on_cascade_property<T>(&mut self,
                                  _property_declaration: &PropertyDeclaration,
                                  value: &DeclaredValue<T>)
        where T: HasViewportPercentage
    {
        // TODO: we can be smarter and keep a property bitfield to keep track of
        // the last applying rule.
        if value.has_viewport_percentage() {
            self.saw_viewport_units = true;
        }
    }

    #[cfg(debug_assertions)]
    fn mark_as_finished_if_appropriate(&mut self) {
        self.finished = true;
    }

    #[cfg(not(debug_assertions))]
    fn mark_as_finished_if_appropriate(&mut self) {}

    #[allow(unsafe_code)]
    pub fn finish<N: TNode>(mut self, node: &N) {
        self.mark_as_finished_if_appropriate();

        if self.saw_viewport_units {
            unsafe {
                node.set_dirty_on_viewport_size_changed();
            }
        }
    }
}

#[cfg(debug_assertions)]
impl Drop for CascadeInfo {
    fn drop(&mut self) {
        debug_assert!(self.finished,
                      "Didn't use the result of CascadeInfo, if you don't need it, consider passing None");
    }
}
<commit_msg>style: Document `cascade_info`.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! A structure to collect information about the cascade.

#![deny(missing_docs)]

use dom::TNode;
use properties::{DeclaredValue, PropertyDeclaration};
use values::HasViewportPercentage;

/// A structure to collect information about the cascade.
///
/// This is useful to gather information about what an element is affected by,
/// and can be used in the future to track optimisations like when a
/// non-inherited property is explicitly inherited, in order to cut-off the
/// traversal.
pub struct CascadeInfo {
    /// Whether we've seen viewport units so far.
    pub saw_viewport_units: bool,
    /// Whether the cascade has been marked as finished. This is a debug-only
    /// flag to ensure `finish` is called, given it's optional to not pass a
    /// `CascadeInfo`.
    #[cfg(debug_assertions)]
    finished: bool,
}

impl CascadeInfo {
    /// Construct a new `CascadeInfo`.
    #[cfg(debug_assertions)]
    pub fn new() -> Self {
        CascadeInfo {
            saw_viewport_units: false,
            finished: false,
        }
    }

    /// Construct a new `CascadeInfo`.
    #[cfg(not(debug_assertions))]
    pub fn new() -> Self {
        CascadeInfo {
            saw_viewport_units: false,
        }
    }

    /// Called when a property is cascaded.
    ///
    /// NOTE: We can add a vast amount of information here.
    #[inline]
    pub fn on_cascade_property<T>(&mut self,
                                  _property_declaration: &PropertyDeclaration,
                                  value: &DeclaredValue<T>)
        where T: HasViewportPercentage,
    {
        // TODO: we can be smarter and keep a property bitfield to keep track of
        // the last applying rule.
        if value.has_viewport_percentage() {
            self.saw_viewport_units = true;
        }
    }

    #[cfg(debug_assertions)]
    fn mark_as_finished_if_appropriate(&mut self) {
        self.finished = true;
    }

    #[cfg(not(debug_assertions))]
    fn mark_as_finished_if_appropriate(&mut self) {}

    /// Called when the cascade is finished, in order to use the information
    /// we've collected.
    ///
    /// Currently used for styling to mark a node as needing restyling when the
    /// viewport size changes.
    #[allow(unsafe_code)]
    pub fn finish<N: TNode>(mut self, node: &N) {
        self.mark_as_finished_if_appropriate();

        if self.saw_viewport_units {
            unsafe {
                node.set_dirty_on_viewport_size_changed();
            }
        }
    }
}

#[cfg(debug_assertions)]
impl Drop for CascadeInfo {
    fn drop(&mut self) {
        debug_assert!(self.finished,
                      "Didn't use the result of CascadeInfo, if you don't need \
                      it, consider passing None");
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2018 The predicates-rs Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/license/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Render `Case` as a tree.

use std::fmt;

use predicates_core::reflection;

/// Render `Self` as a displayable tree.
pub trait CaseTreeExt {
    /// Render `Self` as a displayable tree.
    fn tree(&self) -> CaseTree;
}

impl<'a> CaseTreeExt for reflection::Case<'a> {
    fn tree(&self) -> CaseTree {
        CaseTree(convert(self))
    }
}

type CaseTreeInner = treeline::Tree<Box<dyn fmt::Display>>;

fn convert(case: &reflection::Case<'_>) -> CaseTreeInner {
    let mut leaves: Vec<CaseTreeInner> = vec![];

    leaves.extend(case.predicate().iter().flat_map(|pred| {
        pred.parameters().map(|item| {
            let root: Box<dyn fmt::Display> = Box::new(item.to_string());
            treeline::Tree::new(root, vec![])
        })
    }));

    leaves.extend(case.products().map(|item| {
        let root: Box<dyn fmt::Display> = Box::new(item.to_string());
        treeline::Tree::new(root, vec![])
    }));

    leaves.extend(case.children().map(|item| convert(item)));

    let root = Box::new(case.predicate().map(|p| p.to_string()).unwrap_or_default());
    CaseTreeInner::new(root, leaves)
}

/// A `Case` rendered as a tree for display.
#[allow(missing_debug_implementations)]
pub struct CaseTree(CaseTreeInner);

impl fmt::Display for CaseTree {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.0.fmt(f)
    }
}
<commit_msg>feat: Refine `CaseTreeInner` bounds<commit_after>// Copyright (c) 2018 The predicates-rs Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/license/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Render `Case` as a tree.

use std::fmt;

use predicates_core::reflection;

/// Render `Self` as a displayable tree.
pub trait CaseTreeExt {
    /// Render `Self` as a displayable tree.
    fn tree(&self) -> CaseTree;
}

impl<'a> CaseTreeExt for reflection::Case<'a> {
    fn tree(&self) -> CaseTree {
        CaseTree(convert(self))
    }
}

type CaseTreeInner = treeline::Tree<Box<dyn fmt::Display + Send + Sync>>;

fn convert(case: &reflection::Case<'_>) -> CaseTreeInner {
    let mut leaves: Vec<CaseTreeInner> = vec![];

    leaves.extend(case.predicate().iter().flat_map(|pred| {
        pred.parameters().map(|item| {
            let root: Box<dyn fmt::Display + Send + Sync> = Box::new(item.to_string());
            treeline::Tree::new(root, vec![])
        })
    }));

    leaves.extend(case.products().map(|item| {
        let root: Box<dyn fmt::Display + Send + Sync> = Box::new(item.to_string());
        treeline::Tree::new(root, vec![])
    }));

    leaves.extend(case.children().map(|item| convert(item)));

    let root = Box::new(case.predicate().map(|p| p.to_string()).unwrap_or_default());
    CaseTreeInner::new(root, leaves)
}

/// A `Case` rendered as a tree for display.
#[allow(missing_debug_implementations)]
pub struct CaseTree(CaseTreeInner);

impl fmt::Display for CaseTree {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.0.fmt(f)
    }
}
<|endoftext|>


<commit_before>//! Searches, processes and uploads release files.
use std::collections::HashMap;
use std::fmt;
use std::io::BufWriter;
use std::path::PathBuf;
use std::str;
use std::sync::Arc;
use std::time::{Duration, Instant};

use console::style;
use failure::{bail, Error};
use parking_lot::RwLock;
use rayon::prelude::*;
use rayon::ThreadPoolBuilder;
use symbolic::common::ByteView;
use symbolic::debuginfo::sourcebundle::{SourceBundleWriter, SourceFileInfo, SourceFileType};
use url::Url;

use crate::api::{Api, ChunkUploadCapability, ChunkUploadOptions, FileContents, ProgressBarMode};
use crate::constants::DEFAULT_MAX_WAIT;
use crate::utils::chunks::{upload_chunks, Chunk, ASSEMBLE_POLL_INTERVAL};
use crate::utils::fs::{get_sha1_checksums, TempFile};
use crate::utils::progress::{ProgressBar, ProgressStyle};

/// Fallback concurrency for release file uploads.
static DEFAULT_CONCURRENCY: usize = 4;

pub struct UploadContext<'a> {
    pub org: &'a str,
    pub project: Option<&'a str>,
    pub release: &'a str,
    pub dist: Option<&'a str>,
    pub wait: bool,
}

#[derive(PartialEq, Debug, Copy, Clone)]
pub enum LogLevel {
    Warning,
    Error,
}

impl fmt::Display for LogLevel {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            LogLevel::Warning => write!(f, "warning"),
            LogLevel::Error => write!(f, "error"),
        }
    }
}

#[derive(Clone, Debug)]
pub struct ReleaseFile {
    pub url: String,
    pub path: PathBuf,
    pub contents: Vec<u8>,
    pub ty: SourceFileType,
    pub headers: Vec<(String, String)>,
    pub messages: Vec<(LogLevel, String)>,
}

impl ReleaseFile {
    pub fn log(&mut self, level: LogLevel, msg: String) {
        self.messages.push((level, msg));
    }

    pub fn warn(&mut self, msg: String) {
        self.log(LogLevel::Warning, msg);
    }

    pub fn error(&mut self, msg: String) {
        self.log(LogLevel::Error, msg);
    }
}

pub type ReleaseFiles = HashMap<String, ReleaseFile>;

pub struct ReleaseFileUpload<'a> {
    context: &'a UploadContext<'a>,
    files: ReleaseFiles,
}

impl<'a> ReleaseFileUpload<'a> {
    pub fn new(context: &'a UploadContext) -> Self {
        ReleaseFileUpload {
            context,
            files: HashMap::new(),
        }
    }

    pub fn files(&mut self, files: &ReleaseFiles) -> &mut Self {
        for (k, v) in files {
            self.files.insert(k.to_owned(), v.to_owned());
        }
        self
    }

    pub fn upload(&self) -> Result<(), Error> {
        let api = Api::current();

        let chunk_options = api.get_chunk_upload_options(self.context.org)?;
        if let Some(ref chunk_options) = chunk_options {
            if chunk_options.supports(ChunkUploadCapability::ReleaseFiles) {
                return upload_files_chunked(self.context, &self.files, chunk_options);
            }
        }

        // Do not permit uploads of more than 20k files if the server does not
        // support artifact bundles.  This is a termporary downside protection to
        // protect users from uploading more sources than we support.
        if self.files.len() > 20_000 {
            bail!(
                "Too many sources: {} exceeds maximum allowed files per release",
                &self.files.len()
            );
        }

        let concurrency = chunk_options.map_or(DEFAULT_CONCURRENCY, |o| usize::from(o.concurrency));
        upload_files_parallel(self.context, &self.files, concurrency)
    }
}

fn upload_files_parallel(
    context: &UploadContext,
    files: &ReleaseFiles,
    num_threads: usize,
) -> Result<(), Error> {
    let api = Api::current();

    // get a list of release files first so we know the file IDs of
    // files that already exist.
    let release_files: HashMap<_, _> = api
        .list_release_files(context.org, context.project, context.release)?
        .into_iter()
        .map(|artifact| ((artifact.dist, artifact.name), artifact.id))
        .collect();

    println!(
        "{} Uploading source maps for release {}",
        style(">").dim(),
        style(context.release).cyan()
    );

    let progress_style = ProgressStyle::default_bar().template(&format!(
        "{} Uploading {} source map{}...\
     \n{{wide_bar}}  {{bytes}}/{{total_bytes}} ({{eta}})",
        style(">").dim(),
        style(files.len().to_string()).yellow(),
        if files.len() == 1 { "" } else { "s" }
    ));

    let total_bytes = files.values().map(|file| file.contents.len() as u64).sum();
    let files = files.iter().collect::<Vec<_>>();

    let pb = Arc::new(ProgressBar::new(total_bytes));
    pb.set_style(progress_style);

    let pool = ThreadPoolBuilder::new().num_threads(num_threads).build()?;
    let bytes = Arc::new(RwLock::new(vec![0u64; files.len()]));

    pool.install(|| {
        files
            .into_par_iter()
            .enumerate()
            .map(|(index, (_, file))| -> Result<(), Error> {
                let api = Api::current();
                let mode = ProgressBarMode::Shared((
                    pb.clone(),
                    file.contents.len() as u64,
                    index,
                    bytes.clone(),
                ));

                if let Some(old_id) =
                    release_files.get(&(context.dist.map(|x| x.into()), file.url.clone()))
                {
                    api.delete_release_file(context.org, context.project, context.release, old_id)
                        .ok();
                }

                api.upload_release_file(
                    context.org,
                    context.project,
                    context.release,
                    &FileContents::FromBytes(&file.contents),
                    &file.url,
                    context.dist,
                    Some(file.headers.as_slice()),
                    mode,
                )?;

                Ok(())
            })
            .collect::<Result<(), _>>()
    })?;

    pb.finish_and_clear();

    Ok(())
}

fn upload_files_chunked(
    context: &UploadContext,
    files: &ReleaseFiles,
    options: &ChunkUploadOptions,
) -> Result<(), Error> {
    let archive = build_artifact_bundle(context, files)?;

    let progress_style =
        ProgressStyle::default_spinner().template("{spinner} Optimizing bundle for upload...");

    let progress = ProgressBar::new_spinner();
    progress.enable_steady_tick(100);
    progress.set_style(progress_style);

    let view = ByteView::open(archive.path())?;
    let (checksum, checksums) = get_sha1_checksums(&view, options.chunk_size)?;
    let chunks = view
        .chunks(options.chunk_size as usize)
        .zip(checksums.iter())
        .map(|(data, checksum)| Chunk((*checksum, data)))
        .collect::<Vec<_>>();

    progress.finish_with_duration("Optimizing");

    let progress_style = ProgressStyle::default_bar().template(&format!(
        "{} Uploading release files...\
       \n{{wide_bar}}  {{bytes}}/{{total_bytes}} ({{eta}})",
        style(">").dim(),
    ));

    upload_chunks(&chunks, options, progress_style)?;
    println!("{} Uploaded release files to Sentry", style(">").dim(),);

    let progress_style = ProgressStyle::default_spinner().template("{spinner} Processing files...");

    let progress = ProgressBar::new_spinner();
    progress.enable_steady_tick(100);
    progress.set_style(progress_style);

    let assemble_start = Instant::now();
    let max_wait = match options.max_wait {
        0 => DEFAULT_MAX_WAIT,
        secs => Duration::from_secs(secs),
    };

    let api = Api::current();
    let response = loop {
        let response =
            api.assemble_artifacts(context.org, context.release, checksum, &checksums)?;

        // Poll until there is a response, unless the user has specified to skip polling. In
        // that case, we return the potentially partial response from the server. This might
        // still contain a cached error.
        if !context.wait || response.state.is_finished() {
            break response;
        }

        if assemble_start.elapsed() > max_wait {
            break response;
        }

        std::thread::sleep(ASSEMBLE_POLL_INTERVAL);
    };

    if response.state.is_err() {
        let message = response.detail.as_deref().unwrap_or("unknown error");
        bail!("Failed to process uploaded files: {}", message);
    }

    progress.finish_with_duration("Processing");

    if response.state.is_pending() {
        if context.wait {
            bail!("Failed to process files in {}s", max_wait.as_secs());
        } else {
            println!(
                "{} File upload complete (processing pending on server)",
                style(">").dim()
            );
        }
    } else {
        println!("{} File processing complete", style(">").dim());
    }

    Ok(())
}

fn build_artifact_bundle(context: &UploadContext, files: &ReleaseFiles) -> Result<TempFile, Error> {
    let progress_style = ProgressStyle::default_bar().template(
        "{prefix:.dim} Bundling files for upload... {msg:.dim}\
       \n{wide_bar}  {pos}/{len}",
    );

    let progress = ProgressBar::new(files.len() as u64);
    progress.set_style(progress_style);
    progress.set_prefix(">");

    let archive = TempFile::create()?;
    let mut bundle = SourceBundleWriter::start(BufWriter::new(archive.open()?))?;

    bundle.set_attribute("org".to_owned(), context.org.to_owned());
    if let Some(project) = context.project {
        bundle.set_attribute("project".to_owned(), project.to_owned());
    }
    bundle.set_attribute("release".to_owned(), context.release.to_owned());
    if let Some(dist) = context.dist {
        bundle.set_attribute("dist".to_owned(), dist.to_owned());
    }

    for file in files.values() {
        progress.inc(1);
        progress.set_message(file.url.to_owned());

        let mut info = SourceFileInfo::new();
        info.set_ty(file.ty);
        info.set_url(file.url.clone());
        for (k, v) in &file.headers {
            info.add_header(k.clone(), v.clone());
        }

        let bundle_path = url_to_bundle_path(&file.url)?;
        bundle.add_file(bundle_path, file.contents.as_slice(), info)?;
    }

    bundle.finish()?;

    println!(
        "{} Bundled {} {} for upload",
        style(">").dim(),
        style(files.len()).yellow(),
        match files.len() {
            1 => "file",
            _ => "files",
        }
    );

    progress.finish_with_duration("Bundling");

    Ok(archive)
}

fn url_to_bundle_path(url: &str) -> Result<String, Error> {
    let base = Url::parse("http://~").unwrap();
    let url = if let Some(rest) = url.strip_prefix("~/") {
        base.join(rest)?
    } else {
        base.join(url)?
    };

    let mut path = url.path().to_string();
    if let Some(fragment) = url.fragment() {
        path = format!("{}#{}", path, fragment);
    }
    if path.starts_with('/') {
        path.remove(0);
    }

    Ok(match url.host_str() {
        Some("~") => format!("_/_/{}", path),
        Some(host) => format!("{}/{}/{}", url.scheme(), host, path),
        None => format!("{}/_/{}", url.scheme(), path),
    })
}

#[test]
fn test_url_to_bundle_path() {
    assert_eq!(url_to_bundle_path("~/bar").unwrap(), "_/_/bar");
    assert_eq!(url_to_bundle_path("~/foo/bar").unwrap(), "_/_/foo/bar");
    assert_eq!(
        url_to_bundle_path("~/dist/js/bundle.js.map").unwrap(),
        "_/_/dist/js/bundle.js.map"
    );
    assert_eq!(
        url_to_bundle_path("~/babel.config.js").unwrap(),
        "_/_/babel.config.js"
    );

    assert_eq!(url_to_bundle_path("~/#/bar").unwrap(), "_/_/#/bar");
    assert_eq!(url_to_bundle_path("~/foo/#/bar").unwrap(), "_/_/foo/#/bar");
    assert_eq!(
        url_to_bundle_path("~/dist/#js/bundle.js.map").unwrap(),
        "_/_/dist/#js/bundle.js.map"
    );
    assert_eq!(
        url_to_bundle_path("~/#foo/babel.config.js").unwrap(),
        "_/_/#foo/babel.config.js"
    );
}
<commit_msg>feat: Print upload context details (#1016)<commit_after>//! Searches, processes and uploads release files.
use std::collections::HashMap;
use std::fmt;
use std::io::BufWriter;
use std::path::PathBuf;
use std::str;
use std::sync::Arc;
use std::time::{Duration, Instant};

use console::style;
use failure::{bail, Error};
use parking_lot::RwLock;
use rayon::prelude::*;
use rayon::ThreadPoolBuilder;
use symbolic::common::ByteView;
use symbolic::debuginfo::sourcebundle::{SourceBundleWriter, SourceFileInfo, SourceFileType};
use url::Url;

use crate::api::{Api, ChunkUploadCapability, ChunkUploadOptions, FileContents, ProgressBarMode};
use crate::constants::DEFAULT_MAX_WAIT;
use crate::utils::chunks::{upload_chunks, Chunk, ASSEMBLE_POLL_INTERVAL};
use crate::utils::fs::{get_sha1_checksums, TempFile};
use crate::utils::progress::{ProgressBar, ProgressStyle};

/// Fallback concurrency for release file uploads.
static DEFAULT_CONCURRENCY: usize = 4;

pub struct UploadContext<'a> {
    pub org: &'a str,
    pub project: Option<&'a str>,
    pub release: &'a str,
    pub dist: Option<&'a str>,
    pub wait: bool,
}

#[derive(PartialEq, Debug, Copy, Clone)]
pub enum LogLevel {
    Warning,
    Error,
}

impl fmt::Display for LogLevel {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            LogLevel::Warning => write!(f, "warning"),
            LogLevel::Error => write!(f, "error"),
        }
    }
}

#[derive(Clone, Debug)]
pub struct ReleaseFile {
    pub url: String,
    pub path: PathBuf,
    pub contents: Vec<u8>,
    pub ty: SourceFileType,
    pub headers: Vec<(String, String)>,
    pub messages: Vec<(LogLevel, String)>,
}

impl ReleaseFile {
    pub fn log(&mut self, level: LogLevel, msg: String) {
        self.messages.push((level, msg));
    }

    pub fn warn(&mut self, msg: String) {
        self.log(LogLevel::Warning, msg);
    }

    pub fn error(&mut self, msg: String) {
        self.log(LogLevel::Error, msg);
    }
}

pub type ReleaseFiles = HashMap<String, ReleaseFile>;

pub struct ReleaseFileUpload<'a> {
    context: &'a UploadContext<'a>,
    files: ReleaseFiles,
}

impl<'a> ReleaseFileUpload<'a> {
    pub fn new(context: &'a UploadContext) -> Self {
        ReleaseFileUpload {
            context,
            files: HashMap::new(),
        }
    }

    pub fn files(&mut self, files: &ReleaseFiles) -> &mut Self {
        for (k, v) in files {
            self.files.insert(k.to_owned(), v.to_owned());
        }
        self
    }

    pub fn upload(&self) -> Result<(), Error> {
        let api = Api::current();

        let chunk_options = api.get_chunk_upload_options(self.context.org)?;
        if let Some(ref chunk_options) = chunk_options {
            if chunk_options.supports(ChunkUploadCapability::ReleaseFiles) {
                return upload_files_chunked(self.context, &self.files, chunk_options);
            }
        }

        // Do not permit uploads of more than 20k files if the server does not
        // support artifact bundles.  This is a termporary downside protection to
        // protect users from uploading more sources than we support.
        if self.files.len() > 20_000 {
            bail!(
                "Too many sources: {} exceeds maximum allowed files per release",
                &self.files.len()
            );
        }

        let concurrency = chunk_options.map_or(DEFAULT_CONCURRENCY, |o| usize::from(o.concurrency));
        upload_files_parallel(self.context, &self.files, concurrency)
    }
}

fn upload_files_parallel(
    context: &UploadContext,
    files: &ReleaseFiles,
    num_threads: usize,
) -> Result<(), Error> {
    let api = Api::current();

    // get a list of release files first so we know the file IDs of
    // files that already exist.
    let release_files: HashMap<_, _> = api
        .list_release_files(context.org, context.project, context.release)?
        .into_iter()
        .map(|artifact| ((artifact.dist, artifact.name), artifact.id))
        .collect();

    println!(
        "{} Uploading source maps for release {}",
        style(">").dim(),
        style(context.release).cyan()
    );

    let progress_style = ProgressStyle::default_bar().template(&format!(
        "{} Uploading {} source map{}...\
     \n{{wide_bar}}  {{bytes}}/{{total_bytes}} ({{eta}})",
        style(">").dim(),
        style(files.len().to_string()).yellow(),
        if files.len() == 1 { "" } else { "s" }
    ));

    let total_bytes = files.values().map(|file| file.contents.len() as u64).sum();
    let files = files.iter().collect::<Vec<_>>();

    let pb = Arc::new(ProgressBar::new(total_bytes));
    pb.set_style(progress_style);

    let pool = ThreadPoolBuilder::new().num_threads(num_threads).build()?;
    let bytes = Arc::new(RwLock::new(vec![0u64; files.len()]));

    pool.install(|| {
        files
            .into_par_iter()
            .enumerate()
            .map(|(index, (_, file))| -> Result<(), Error> {
                let api = Api::current();
                let mode = ProgressBarMode::Shared((
                    pb.clone(),
                    file.contents.len() as u64,
                    index,
                    bytes.clone(),
                ));

                if let Some(old_id) =
                    release_files.get(&(context.dist.map(|x| x.into()), file.url.clone()))
                {
                    api.delete_release_file(context.org, context.project, context.release, old_id)
                        .ok();
                }

                api.upload_release_file(
                    context.org,
                    context.project,
                    context.release,
                    &FileContents::FromBytes(&file.contents),
                    &file.url,
                    context.dist,
                    Some(file.headers.as_slice()),
                    mode,
                )?;

                Ok(())
            })
            .collect::<Result<(), _>>()
    })?;

    pb.finish_and_clear();

    print_upload_context_details(context);

    Ok(())
}

fn upload_files_chunked(
    context: &UploadContext,
    files: &ReleaseFiles,
    options: &ChunkUploadOptions,
) -> Result<(), Error> {
    let archive = build_artifact_bundle(context, files)?;

    let progress_style =
        ProgressStyle::default_spinner().template("{spinner} Optimizing bundle for upload...");

    let progress = ProgressBar::new_spinner();
    progress.enable_steady_tick(100);
    progress.set_style(progress_style);

    let view = ByteView::open(archive.path())?;
    let (checksum, checksums) = get_sha1_checksums(&view, options.chunk_size)?;
    let chunks = view
        .chunks(options.chunk_size as usize)
        .zip(checksums.iter())
        .map(|(data, checksum)| Chunk((*checksum, data)))
        .collect::<Vec<_>>();

    progress.finish_with_duration("Optimizing");

    let progress_style = ProgressStyle::default_bar().template(&format!(
        "{} Uploading release files...\
       \n{{wide_bar}}  {{bytes}}/{{total_bytes}} ({{eta}})",
        style(">").dim(),
    ));

    upload_chunks(&chunks, options, progress_style)?;
    println!("{} Uploaded release files to Sentry", style(">").dim(),);

    let progress_style = ProgressStyle::default_spinner().template("{spinner} Processing files...");

    let progress = ProgressBar::new_spinner();
    progress.enable_steady_tick(100);
    progress.set_style(progress_style);

    let assemble_start = Instant::now();
    let max_wait = match options.max_wait {
        0 => DEFAULT_MAX_WAIT,
        secs => Duration::from_secs(secs),
    };

    let api = Api::current();
    let response = loop {
        let response =
            api.assemble_artifacts(context.org, context.release, checksum, &checksums)?;

        // Poll until there is a response, unless the user has specified to skip polling. In
        // that case, we return the potentially partial response from the server. This might
        // still contain a cached error.
        if !context.wait || response.state.is_finished() {
            break response;
        }

        if assemble_start.elapsed() > max_wait {
            break response;
        }

        std::thread::sleep(ASSEMBLE_POLL_INTERVAL);
    };

    if response.state.is_err() {
        let message = response.detail.as_deref().unwrap_or("unknown error");
        bail!("Failed to process uploaded files: {}", message);
    }

    progress.finish_with_duration("Processing");

    if response.state.is_pending() {
        if context.wait {
            bail!("Failed to process files in {}s", max_wait.as_secs());
        } else {
            println!(
                "{} File upload complete (processing pending on server)",
                style(">").dim()
            );
        }
    } else {
        println!("{} File processing complete", style(">").dim());
    }

    print_upload_context_details(context);

    Ok(())
}

fn build_artifact_bundle(context: &UploadContext, files: &ReleaseFiles) -> Result<TempFile, Error> {
    let progress_style = ProgressStyle::default_bar().template(
        "{prefix:.dim} Bundling files for upload... {msg:.dim}\
       \n{wide_bar}  {pos}/{len}",
    );

    let progress = ProgressBar::new(files.len() as u64);
    progress.set_style(progress_style);
    progress.set_prefix(">");

    let archive = TempFile::create()?;
    let mut bundle = SourceBundleWriter::start(BufWriter::new(archive.open()?))?;

    bundle.set_attribute("org".to_owned(), context.org.to_owned());
    if let Some(project) = context.project {
        bundle.set_attribute("project".to_owned(), project.to_owned());
    }
    bundle.set_attribute("release".to_owned(), context.release.to_owned());
    if let Some(dist) = context.dist {
        bundle.set_attribute("dist".to_owned(), dist.to_owned());
    }

    for file in files.values() {
        progress.inc(1);
        progress.set_message(file.url.to_owned());

        let mut info = SourceFileInfo::new();
        info.set_ty(file.ty);
        info.set_url(file.url.clone());
        for (k, v) in &file.headers {
            info.add_header(k.clone(), v.clone());
        }

        let bundle_path = url_to_bundle_path(&file.url)?;
        bundle.add_file(bundle_path, file.contents.as_slice(), info)?;
    }

    bundle.finish()?;

    println!(
        "{} Bundled {} {} for upload",
        style(">").dim(),
        style(files.len()).yellow(),
        match files.len() {
            1 => "file",
            _ => "files",
        }
    );

    progress.finish_with_duration("Bundling");

    Ok(archive)
}

fn url_to_bundle_path(url: &str) -> Result<String, Error> {
    let base = Url::parse("http://~").unwrap();
    let url = if let Some(rest) = url.strip_prefix("~/") {
        base.join(rest)?
    } else {
        base.join(url)?
    };

    let mut path = url.path().to_string();
    if let Some(fragment) = url.fragment() {
        path = format!("{}#{}", path, fragment);
    }
    if path.starts_with('/') {
        path.remove(0);
    }

    Ok(match url.host_str() {
        Some("~") => format!("_/_/{}", path),
        Some(host) => format!("{}/{}/{}", url.scheme(), host, path),
        None => format!("{}/_/{}", url.scheme(), path),
    })
}

fn print_upload_context_details(context: &UploadContext) {
    println!(
        "{} {}",
        style("> Organization:").dim(),
        style(context.org).yellow()
    );
    println!(
        "{} {}",
        style("> Project:").dim(),
        style(context.project.unwrap_or("None")).yellow()
    );
    println!(
        "{} {}",
        style("> Release:").dim(),
        style(context.release).yellow()
    );
    println!(
        "{} {}",
        style("> Dist:").dim(),
        style(context.dist.unwrap_or("None")).yellow()
    );
}

#[test]
fn test_url_to_bundle_path() {
    assert_eq!(url_to_bundle_path("~/bar").unwrap(), "_/_/bar");
    assert_eq!(url_to_bundle_path("~/foo/bar").unwrap(), "_/_/foo/bar");
    assert_eq!(
        url_to_bundle_path("~/dist/js/bundle.js.map").unwrap(),
        "_/_/dist/js/bundle.js.map"
    );
    assert_eq!(
        url_to_bundle_path("~/babel.config.js").unwrap(),
        "_/_/babel.config.js"
    );

    assert_eq!(url_to_bundle_path("~/#/bar").unwrap(), "_/_/#/bar");
    assert_eq!(url_to_bundle_path("~/foo/#/bar").unwrap(), "_/_/foo/#/bar");
    assert_eq!(
        url_to_bundle_path("~/dist/#js/bundle.js.map").unwrap(),
        "_/_/dist/#js/bundle.js.map"
    );
    assert_eq!(
        url_to_bundle_path("~/#foo/babel.config.js").unwrap(),
        "_/_/#foo/babel.config.js"
    );
}
<|endoftext|>


<commit_before>extern crate golomb;

use std::os;
use std::io;
use std::collections;

fn main() {
    let args = os::args();
    assert!(args.len() >= 2);

    let (tx, rx) = channel();

    let mut out = golomb::Encoder::new(io::ChanWriter::new(tx));

    spawn(proc() {
        let mut w = io::MemWriter::new();
        for refs in rx.iter() {
            let mut bytes = refs.iter().map(|&q| q);
            for b in bytes {
                print!("{:08t} ", b);
                w.write_u8(b).ok().expect("buffer sizing error");
            }
        }
        println!("");

        let bytes_ref = w.get_ref();
        let v = collections::bitv::from_bytes(bytes_ref);
        let mut int_iter = golomb::Decoder::new(v.iter()).map(|u| -> int {
            if (u & 0x01) != 0 {
                ((u as int) - 1) / -2
            } else {
                (u as int) / 2
            }
        });
        for i in int_iter {
            print!("{:d} ", i);
        }
        println!("");
    });

    for arg in args.slice(1, args.len()).iter() {
        let x_opt: Option<int> = from_str(arg.as_slice().trim());
        let x = x_opt.unwrap();

        out.write(x, |u| {
            if u >= 0 {
                2 * u as uint
            } else {
                (-(2 * u) + 1) as uint
            }
        }).ok().expect("failed to write");
    }
    out.flush().ok().expect("failed to flush");
}
<commit_msg>Add more log info to convert example<commit_after>extern crate golomb;

use std::os;
use std::io;
use std::collections;

fn main() {
    let args = os::args();
    assert!(args.len() >= 2);

    let (tx, rx) = channel();

    let mut out = golomb::Encoder::new(io::ChanWriter::new(tx));

    spawn(proc() {
        let mut w = io::MemWriter::new();
        for refs in rx.iter() {
            let mut bytes = refs.iter().map(|&q| q);
            for b in bytes {
                print!("{:08t} ", b);
                w.write_u8(b).ok().expect("buffer sizing error");
            }
        }
        println!("");

        let bytes_ref = w.get_ref();
        println!("Compressed to {} bytes", bytes_ref.len());
        let v = collections::bitv::from_bytes(bytes_ref);
        let mut int_iter = golomb::Decoder::new(v.iter()).map(|u| -> int {
            if (u & 0x01) != 0 {
                ((u as int) - 1) / -2
            } else {
                (u as int) / 2
            }
        });
        for i in int_iter {
            print!("{:d} ", i);
        }
        println!("");
    });

    for arg in args.slice(1, args.len()).iter() {
        let x_opt: Option<int> = from_str(arg.as_slice().trim());
        let x = x_opt.unwrap();

        out.write(x, |u| {
            if u >= 0 {
                2 * u as uint
            } else {
                (-(2 * u) + 1) as uint
            }
        }).ok().expect("failed to write");
    }
    println!("Compressing {} bytes", (args.len()-1) * std::mem::size_of::<int>());
    out.flush().ok().expect("failed to flush");
}
<|endoftext|>