<commit_before>//! Cookie session.
//!
//! [**CookieSession**](struct.CookieSession.html)
//! uses cookies as session storage. `CookieSession` creates sessions
//! which are limited to storing fewer than 4000 bytes of data, as the payload
//! must fit into a single cookie. An internal server error is generated if a
//! session contains more than 4000 bytes.
//!
//! A cookie may have a security policy of *signed* or *private*. Each has
//! a respective `CookieSession` constructor.
//!
//! A *signed* cookie may be viewed but not modified by the client. A *private*
//! cookie may neither be viewed nor modified by the client.
//!
//! The constructors take a key as an argument. This is the private key
//! for cookie session - when this value is changed, all session data is lost.

use std::collections::HashMap;
use std::rc::Rc;

use actix_service::{Service, Transform};
use actix_web::cookie::{Cookie, CookieJar, Key, SameSite};
use actix_web::dev::{ServiceRequest, ServiceResponse};
use actix_web::http::{header::SET_COOKIE, HeaderValue};
use actix_web::{Error, HttpMessage, ResponseError};
use derive_more::{Display, From};
use futures::future::{ok, Future, FutureResult};
use futures::Poll;
use serde_json::error::Error as JsonError;

use crate::{Session, SessionStatus};

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

impl ResponseError for CookieSessionError {}

enum CookieSecurity {
    Signed,
    Private,
}

struct CookieSessionInner {
    key: Key,
    security: CookieSecurity,
    name: String,
    path: String,
    domain: Option<String>,
    secure: bool,
    http_only: bool,
    max_age: Option<time::Duration>,
    same_site: Option<SameSite>,
}

impl CookieSessionInner {
    fn new(key: &[u8], security: CookieSecurity) -> CookieSessionInner {
        CookieSessionInner {
            security,
            key: Key::from_master(key),
            name: "actix-session".to_owned(),
            path: "/".to_owned(),
            domain: None,
            secure: true,
            http_only: true,
            max_age: None,
            same_site: None,
        }
    }

    fn set_cookie<B>(
        &self,
        res: &mut ServiceResponse<B>,
        state: impl Iterator<Item = (String, String)>,
    ) -> Result<(), Error> {
        let state: HashMap<String, String> = state.collect();
        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(self.http_only);

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

        if let Some(max_age) = self.max_age {
            cookie.set_max_age(max_age);
        }

        if let Some(same_site) = self.same_site {
            cookie.set_same_site(same_site);
        }

        let mut jar = CookieJar::new();

        match self.security {
            CookieSecurity::Signed => jar.signed(&self.key).add(cookie),
            CookieSecurity::Private => jar.private(&self.key).add(cookie),
        }

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

        Ok(())
    }

    fn load(&self, req: &ServiceRequest) -> HashMap<String, String> {
        if let Ok(cookies) = req.cookies() {
            for cookie in cookies.iter() {
                if cookie.name() == self.name {
                    let mut jar = CookieJar::new();
                    jar.add_original(cookie.clone());

                    let cookie_opt = match self.security {
                        CookieSecurity::Signed => jar.signed(&self.key).get(&self.name),
                        CookieSecurity::Private => {
                            jar.private(&self.key).get(&self.name)
                        }
                    };
                    if let Some(cookie) = cookie_opt {
                        if let Ok(val) = serde_json::from_str(cookie.value()) {
                            return val;
                        }
                    }
                }
            }
        }
        HashMap::new()
    }
}

/// Use cookies for session storage.
///
/// `CookieSession` creates sessions which are limited to storing
/// fewer than 4000 bytes of data (as the payload must fit into a single
/// cookie). An Internal Server Error is generated if the session contains more
/// than 4000 bytes.
///
/// A cookie may have a security policy of *signed* or *private*. Each has a
/// respective `CookieSessionBackend` constructor.
///
/// A *signed* cookie is stored on the client as plaintext alongside
/// a signature such that the cookie may be viewed but not modified by the
/// client.
///
/// A *private* cookie is stored on the client as encrypted text
/// such that it may neither be viewed nor modified by the client.
///
/// The constructors take a key as an argument.
/// This is the private key for cookie session - when this value is changed,
/// all session data is lost. The constructors will panic if the key is less
/// than 32 bytes in length.
///
/// The backend relies on `cookie` crate to create and read cookies.
/// By default all cookies are percent encoded, but certain symbols may
/// cause troubles when reading cookie, if they are not properly percent encoded.
///
/// # Example
///
/// ```rust
/// use actix_session::CookieSession;
/// use actix_web::{web, App, HttpResponse, HttpServer};
///
/// fn main() {
///     let app = App::new().wrap(
///         CookieSession::signed(&[0; 32])
///             .domain("www.rust-lang.org")
///             .name("actix_session")
///             .path("/")
///             .secure(true))
///         .service(web::resource("/").to(|| HttpResponse::Ok()));
/// }
/// ```
pub struct CookieSession(Rc<CookieSessionInner>);

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

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

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

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

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

    /// Sets the `secure` field in the session cookie being built.
    ///
    /// If the `secure` field is set, a cookie will only be transmitted when the
    /// connection is secure - i.e. `https`
    pub fn secure(mut self, value: bool) -> CookieSession {
        Rc::get_mut(&mut self.0).unwrap().secure = value;
        self
    }

    /// Sets the `http_only` field in the session cookie being built.
    pub fn http_only(mut self, value: bool) -> CookieSession {
        Rc::get_mut(&mut self.0).unwrap().http_only = value;
        self
    }

    /// Sets the `same_site` field in the session cookie being built.
    pub fn same_site(mut self, value: SameSite) -> CookieSession {
        Rc::get_mut(&mut self.0).unwrap().same_site = Some(value);
        self
    }

    /// Sets the `max-age` field in the session cookie being built.
    pub fn max_age(self, seconds: i64) -> CookieSession {
        self.max_age_time(time::Duration::seconds(seconds))
    }

    /// Sets the `max-age` field in the session cookie being built.
    pub fn max_age_time(mut self, value: time::Duration) -> CookieSession {
        Rc::get_mut(&mut self.0).unwrap().max_age = Some(value);
        self
    }
}

impl<S, B: 'static> Transform<S> for CookieSession
where
    S: Service<Request = ServiceRequest, Response = ServiceResponse<B>>,
    S::Future: 'static,
    S::Error: 'static,
{
    type Request = ServiceRequest;
    type Response = ServiceResponse<B>;
    type Error = S::Error;
    type InitError = ();
    type Transform = CookieSessionMiddleware<S>;
    type Future = FutureResult<Self::Transform, Self::InitError>;

    fn new_transform(&self, service: S) -> Self::Future {
        ok(CookieSessionMiddleware {
            service,
            inner: self.0.clone(),
        })
    }
}

/// Cookie session middleware
pub struct CookieSessionMiddleware<S> {
    service: S,
    inner: Rc<CookieSessionInner>,
}

impl<S, B: 'static> Service for CookieSessionMiddleware<S>
where
    S: Service<Request = ServiceRequest, Response = ServiceResponse<B>>,
    S::Future: 'static,
    S::Error: 'static,
{
    type Request = ServiceRequest;
    type Response = ServiceResponse<B>;
    type Error = S::Error;
    type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;

    fn poll_ready(&mut self) -> Poll<(), Self::Error> {
        self.service.poll_ready()
    }

    fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
        let inner = self.inner.clone();
        let state = self.inner.load(&req);
        Session::set_session(state.into_iter(), &mut req);

        Box::new(self.service.call(req).map(move |mut res| {
            match Session::get_changes(&mut res) {
                (SessionStatus::Changed, Some(state)) =>
                    res.checked_expr(|res| inner.set_cookie(res, state)),
                _ => res
            }
        }))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use actix_web::{test, web, App};
    use bytes::Bytes;

    #[test]
    fn cookie_session() {
        let mut app = test::init_service(
            App::new()
                .wrap(CookieSession::signed(&[0; 32]).secure(false))
                .service(web::resource("/").to(|ses: Session| {
                    let _ = ses.set("counter", 100);
                    "test"
                })),
        );

        let request = test::TestRequest::get().to_request();
        let response = test::block_on(app.call(request)).unwrap();
        assert!(response
            .response()
            .cookies()
            .find(|c| c.name() == "actix-session")
            .is_some());
    }

    #[test]
    fn private_cookie() {
        let mut app = test::init_service(
            App::new()
                .wrap(CookieSession::private(&[0; 32]).secure(false))
                .service(web::resource("/").to(|ses: Session| {
                    let _ = ses.set("counter", 100);
                    "test"
                })),
        );

        let request = test::TestRequest::get().to_request();
        let response = test::block_on(app.call(request)).unwrap();
        assert!(response
            .response()
            .cookies()
            .find(|c| c.name() == "actix-session")
            .is_some());
    }

    #[test]
    fn cookie_session_extractor() {
        let mut app = test::init_service(
            App::new()
                .wrap(CookieSession::signed(&[0; 32]).secure(false))
                .service(web::resource("/").to(|ses: Session| {
                    let _ = ses.set("counter", 100);
                    "test"
                })),
        );

        let request = test::TestRequest::get().to_request();
        let response = test::block_on(app.call(request)).unwrap();
        assert!(response
            .response()
            .cookies()
            .find(|c| c.name() == "actix-session")
            .is_some());
    }

    #[test]
    fn basics() {
        let mut app = test::init_service(
            App::new()
                .wrap(
                    CookieSession::signed(&[0; 32])
                        .path("/test/")
                        .name("actix-test")
                        .domain("localhost")
                        .http_only(true)
                        .same_site(SameSite::Lax)
                        .max_age(100),
                )
                .service(web::resource("/").to(|ses: Session| {
                    let _ = ses.set("counter", 100);
                    "test"
                }))
                .service(web::resource("/test/").to(|ses: Session| {
                    let val: usize = ses.get("counter").unwrap().unwrap();
                    format!("counter: {}", val)
                })),
        );

        let request = test::TestRequest::get().to_request();
        let response = test::block_on(app.call(request)).unwrap();
        let cookie = response
            .response()
            .cookies()
            .find(|c| c.name() == "actix-test")
            .unwrap()
            .clone();
        assert_eq!(cookie.path().unwrap(), "/test/");

        let request = test::TestRequest::with_uri("/test/")
            .cookie(cookie)
            .to_request();
        let body = test::read_response(&mut app, request);
        assert_eq!(body, Bytes::from_static(b"counter: 100"));
    }
}
<commit_msg>updated session cookie to support login, logout, changes<commit_after>//! Cookie session.
//!
//! [**CookieSession**](struct.CookieSession.html)
//! uses cookies as session storage. `CookieSession` creates sessions
//! which are limited to storing fewer than 4000 bytes of data, as the payload
//! must fit into a single cookie. An internal server error is generated if a
//! session contains more than 4000 bytes.
//!
//! A cookie may have a security policy of *signed* or *private*. Each has
//! a respective `CookieSession` constructor.
//!
//! A *signed* cookie may be viewed but not modified by the client. A *private*
//! cookie may neither be viewed nor modified by the client.
//!
//! The constructors take a key as an argument. This is the private key
//! for cookie session - when this value is changed, all session data is lost.

use std::collections::HashMap;
use std::rc::Rc;

use actix_service::{Service, Transform};
use actix_web::cookie::{Cookie, CookieJar, Key, SameSite};
use actix_web::dev::{ServiceRequest, ServiceResponse};
use actix_web::http::{header::SET_COOKIE, HeaderValue};
use actix_web::{Error, HttpMessage, ResponseError};
use derive_more::{Display, From};
use futures::future::{ok, Future, FutureResult};
use futures::Poll;
use serde_json::error::Error as JsonError;

use crate::{Session, SessionStatus};

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

impl ResponseError for CookieSessionError {}

enum CookieSecurity {
    Signed,
    Private,
}

struct CookieSessionInner {
    key: Key,
    security: CookieSecurity,
    name: String,
    path: String,
    domain: Option<String>,
    secure: bool,
    http_only: bool,
    max_age: Option<time::Duration>,
    same_site: Option<SameSite>,
}

impl CookieSessionInner {
    fn new(key: &[u8], security: CookieSecurity) -> CookieSessionInner {
        CookieSessionInner {
            security,
            key: Key::from_master(key),
            name: "actix-session".to_owned(),
            path: "/".to_owned(),
            domain: None,
            secure: true,
            http_only: true,
            max_age: None,
            same_site: None,
        }
    }

    fn set_cookie<B>(
        &self,
        res: &mut ServiceResponse<B>,
        state: impl Iterator<Item = (String, String)>,
    ) -> Result<(), Error> {
        let state: HashMap<String, String> = state.collect();
        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(self.http_only);

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

        if let Some(max_age) = self.max_age {
            cookie.set_max_age(max_age);
        }

        if let Some(same_site) = self.same_site {
            cookie.set_same_site(same_site);
        }

        let mut jar = CookieJar::new();

        match self.security {
            CookieSecurity::Signed => jar.signed(&self.key).add(cookie),
            CookieSecurity::Private => jar.private(&self.key).add(cookie),
        }

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

        Ok(())
    }

    /// invalidates session cookie
    fn remove_cookie<B>(&self, res: &mut ServiceResponse<B>)
                    -> Result<(), Error> {
        let mut cookie = Cookie::named(self.name.clone());
        cookie.set_value("");
        cookie.set_max_age(time::Duration::seconds(0));
        cookie.set_expires(time::now() - time::Duration::days(365));

        let val = HeaderValue::from_str(&cookie.to_string())?;
        res.headers_mut().append(SET_COOKIE, val);

        Ok(())
    }

    fn load(&self, req: &ServiceRequest) -> (bool, HashMap<String, String>) {
        if let Ok(cookies) = req.cookies() {
            for cookie in cookies.iter() {
                if cookie.name() == self.name {
                    let mut jar = CookieJar::new();
                    jar.add_original(cookie.clone());

                    let cookie_opt = match self.security {
                        CookieSecurity::Signed => jar.signed(&self.key).get(&self.name),
                        CookieSecurity::Private => {
                            jar.private(&self.key).get(&self.name)
                        }
                    };
                    if let Some(cookie) = cookie_opt {
                        if let Ok(val) = serde_json::from_str(cookie.value()) {
                            return (false, val);
                        }
                    }
                }
            }
        }
        (true, HashMap::new())
    }
}

/// Use cookies for session storage.
///
/// `CookieSession` creates sessions which are limited to storing
/// fewer than 4000 bytes of data (as the payload must fit into a single
/// cookie). An Internal Server Error is generated if the session contains more
/// than 4000 bytes.
///
/// A cookie may have a security policy of *signed* or *private*. Each has a
/// respective `CookieSessionBackend` constructor.
///
/// A *signed* cookie is stored on the client as plaintext alongside
/// a signature such that the cookie may be viewed but not modified by the
/// client.
///
/// A *private* cookie is stored on the client as encrypted text
/// such that it may neither be viewed nor modified by the client.
///
/// The constructors take a key as an argument.
/// This is the private key for cookie session - when this value is changed,
/// all session data is lost. The constructors will panic if the key is less
/// than 32 bytes in length.
///
/// The backend relies on `cookie` crate to create and read cookies.
/// By default all cookies are percent encoded, but certain symbols may
/// cause troubles when reading cookie, if they are not properly percent encoded.
///
/// # Example
///
/// ```rust
/// use actix_session::CookieSession;
/// use actix_web::{web, App, HttpResponse, HttpServer};
///
/// fn main() {
///     let app = App::new().wrap(
///         CookieSession::signed(&[0; 32])
///             .domain("www.rust-lang.org")
///             .name("actix_session")
///             .path("/")
///             .secure(true))
///         .service(web::resource("/").to(|| HttpResponse::Ok()));
/// }
/// ```
pub struct CookieSession(Rc<CookieSessionInner>);

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

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

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

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

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

    /// Sets the `secure` field in the session cookie being built.
    ///
    /// If the `secure` field is set, a cookie will only be transmitted when the
    /// connection is secure - i.e. `https`
    pub fn secure(mut self, value: bool) -> CookieSession {
        Rc::get_mut(&mut self.0).unwrap().secure = value;
        self
    }

    /// Sets the `http_only` field in the session cookie being built.
    pub fn http_only(mut self, value: bool) -> CookieSession {
        Rc::get_mut(&mut self.0).unwrap().http_only = value;
        self
    }

    /// Sets the `same_site` field in the session cookie being built.
    pub fn same_site(mut self, value: SameSite) -> CookieSession {
        Rc::get_mut(&mut self.0).unwrap().same_site = Some(value);
        self
    }

    /// Sets the `max-age` field in the session cookie being built.
    pub fn max_age(self, seconds: i64) -> CookieSession {
        self.max_age_time(time::Duration::seconds(seconds))
    }

    /// Sets the `max-age` field in the session cookie being built.
    pub fn max_age_time(mut self, value: time::Duration) -> CookieSession {
        Rc::get_mut(&mut self.0).unwrap().max_age = Some(value);
        self
    }
}

impl<S, B: 'static> Transform<S> for CookieSession
where
    S: Service<Request = ServiceRequest, Response = ServiceResponse<B>>,
    S::Future: 'static,
    S::Error: 'static,
{
    type Request = ServiceRequest;
    type Response = ServiceResponse<B>;
    type Error = S::Error;
    type InitError = ();
    type Transform = CookieSessionMiddleware<S>;
    type Future = FutureResult<Self::Transform, Self::InitError>;

    fn new_transform(&self, service: S) -> Self::Future {
        ok(CookieSessionMiddleware {
            service,
            inner: self.0.clone(),
        })
    }
}

/// Cookie session middleware
pub struct CookieSessionMiddleware<S> {
    service: S,
    inner: Rc<CookieSessionInner>,
}

impl<S, B: 'static> Service for CookieSessionMiddleware<S>
where
    S: Service<Request = ServiceRequest, Response = ServiceResponse<B>>,
    S::Future: 'static,
    S::Error: 'static,
{
    type Request = ServiceRequest;
    type Response = ServiceResponse<B>;
    type Error = S::Error;
    type Future = Box<Future<Item = Self::Response, Error = Self::Error>>;

    fn poll_ready(&mut self) -> Poll<(), Self::Error> {
        self.service.poll_ready()
    }

    /// On first request, a new session cookie is returned in response, regardless
    /// of whether any session state is set.  With subsequent requests, if the
    /// session state changes, then set-cookie is returned in response.  As
    /// a user logs out, call session.purge() to set SessionStatus accordingly
    /// and this will trigger removal of the session cookie in the response.
    fn call(&mut self, mut req: ServiceRequest) -> Self::Future {
        let inner = self.inner.clone();
        let (is_new, state) = self.inner.load(&req);
        Session::set_session(state.into_iter(), &mut req);

        Box::new(self.service.call(req).map(move |mut res| {
            match Session::get_changes(&mut res) {
                (SessionStatus::Changed, Some(state))
                 | (SessionStatus::Renewed, Some(state)) =>
                    res.checked_expr(|res| inner.set_cookie(res, state)),
                (SessionStatus::Unchanged, _) =>
                    // set a new session cookie upon first request (new client)
                    if is_new {
                        let state: HashMap<String, String> = HashMap::new();
                        res.checked_expr(|res| inner.set_cookie(res, state.into_iter()))
                    } else {
                        res
                    },
                (SessionStatus::Purged, _) => {
				    inner.remove_cookie(&mut res);
					res
				},
                _ => res
            }
        }))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use actix_web::{test, web, App};
    use bytes::Bytes;

    #[test]
    fn cookie_session() {
        let mut app = test::init_service(
            App::new()
                .wrap(CookieSession::signed(&[0; 32]).secure(false))
                .service(web::resource("/").to(|ses: Session| {
                    let _ = ses.set("counter", 100);
                    "test"
                })),
        );

        let request = test::TestRequest::get().to_request();
        let response = test::block_on(app.call(request)).unwrap();
        assert!(response
            .response()
            .cookies()
            .find(|c| c.name() == "actix-session")
            .is_some());
    }

    #[test]
    fn private_cookie() {
        let mut app = test::init_service(
            App::new()
                .wrap(CookieSession::private(&[0; 32]).secure(false))
                .service(web::resource("/").to(|ses: Session| {
                    let _ = ses.set("counter", 100);
                    "test"
                })),
        );

        let request = test::TestRequest::get().to_request();
        let response = test::block_on(app.call(request)).unwrap();
        assert!(response
            .response()
            .cookies()
            .find(|c| c.name() == "actix-session")
            .is_some());
    }

    #[test]
    fn cookie_session_extractor() {
        let mut app = test::init_service(
            App::new()
                .wrap(CookieSession::signed(&[0; 32]).secure(false))
                .service(web::resource("/").to(|ses: Session| {
                    let _ = ses.set("counter", 100);
                    "test"
                })),
        );

        let request = test::TestRequest::get().to_request();
        let response = test::block_on(app.call(request)).unwrap();
        assert!(response
            .response()
            .cookies()
            .find(|c| c.name() == "actix-session")
            .is_some());
    }

    #[test]
    fn basics() {
        let mut app = test::init_service(
            App::new()
                .wrap(
                    CookieSession::signed(&[0; 32])
                        .path("/test/")
                        .name("actix-test")
                        .domain("localhost")
                        .http_only(true)
                        .same_site(SameSite::Lax)
                        .max_age(100),
                )
                .service(web::resource("/").to(|ses: Session| {
                    let _ = ses.set("counter", 100);
                    "test"
                }))
                .service(web::resource("/test/").to(|ses: Session| {
                    let val: usize = ses.get("counter").unwrap().unwrap();
                    format!("counter: {}", val)
                })),
        );

        let request = test::TestRequest::get().to_request();
        let response = test::block_on(app.call(request)).unwrap();
        let cookie = response
            .response()
            .cookies()
            .find(|c| c.name() == "actix-test")
            .unwrap()
            .clone();
        assert_eq!(cookie.path().unwrap(), "/test/");

        let request = test::TestRequest::with_uri("/test/")
            .cookie(cookie)
            .to_request();
        let body = test::read_response(&mut app, request);
        assert_eq!(body, Bytes::from_static(b"counter: 100"));
    }
}
<|endoftext|>


<commit_before>//! The `ast` module contains a lot of useful functionality
//! to create and walk through the ast (abstract syntaxtree)

pub use frontend::parser;
pub use frontend::lexer::Token;
pub use backend::zcode::zfile;

//==============================
// ast

pub struct AST {
    passages: Vec<ASTNode>
}

/// add zcode based on tokens
fn stuff(node: &ASTNode, mut out: &mut zfile::Zfile) {
     match node {
        &ASTNode::Passage(ref t) => {
            debug!("{:?}", t.category);
            match &t.category{
                /*&Token::TokText(ref s) => {

                    //out.op_print(s);
                },
                &Token::TokFormatBold => {
                    out.op_set_text_style(true, false, false, false);
                },
                 &Token::TokFormatItalic => {
                    out.op_set_text_style(false, false, false, true);
                },*/
                _ => {
                    debug!("no match 1");
                }
            };
            for child in &t.childs {
                stuff(child, out);
            }
            out.op_set_text_style(false, false, false, false);
        },
        &ASTNode::Default(ref t) => {
            match &t.category{
                &Token::TokText(ref s) => {
                    out.op_print(s);
                },
                &Token::TokFormatBold => {
                    out.op_set_text_style(true, false, false, false);
                },
                 &Token::TokFormatItalic => {
                    out.op_set_text_style(false, false, false, true);
                },
                _ => {
                    debug!("no match 2");
                }
            };

            for child in &t.childs {
                stuff(child, out);
            }
            out.op_set_text_style(false, false, false, false);
        }
    };
}

impl AST {
    /// convert ast to zcode
    pub fn to_zcode(&self,  out: &mut zfile::Zfile){
        for child in &self.passages {
            stuff(child, out);
        }
    }

    pub fn new() -> AST {
        AST {
            passages: Vec::new()
        }
    }

    /// prints the tree
    pub fn print(&self) {
        debug!("Abstract Syntax Tree: ");
        for child in &self.passages {
            child.print(0);
        }
        debug!("");
    }

    /// adds a passage to the path in the ast
    pub fn add_passage(&mut self, token: Token) {
        let node = ASTNode::Passage(NodePassage { category: token, childs: Vec::new() });
        self.passages.push(node);
    }

    /// adds a child to the path in the ast
    pub fn add_child(&mut self, path: &Vec<usize>, token: Token) {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            self.passages[*index].add_child(new_path, token)
        } else {
            self.passages.push(ASTNode::Default(NodeDefault { category: token, childs: Vec::new() }));
        }
    }

    /// counts the childs of the path in the asts
    pub fn cound_childs(&self, path: Vec<usize>) -> usize {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            self.passages[*index].cound_childs(new_path)
        } else {
            self.passages.len()
        }
    }
}

// ================================
// node types
enum ASTNode {
    Default (NodeDefault),
    Passage (NodePassage)
}

struct NodePassage {
    category: Token,
    pub childs: Vec<ASTNode>,
    /*tags: Vec<ASTNode>*/
}

struct NodeDefault {
    category: Token,
    childs: Vec<ASTNode>
}

impl ASTNode {
    /// adds an child to the path in the ast
    pub fn add_child(&mut self, path: Vec<usize>, token: Token) {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            match self {
                &mut ASTNode::Default(ref mut node) => node.childs[*index].add_child(new_path, token),
                &mut ASTNode::Passage(ref mut node) => node.childs[*index].add_child(new_path, token),
            }
        } else {
            match self {
                &mut ASTNode::Default(ref mut node) => node.childs.push(ASTNode::Default(NodeDefault { category: token, childs: Vec::new() } )),
                &mut ASTNode::Passage(ref mut node) => node.childs.push(ASTNode::Default(NodeDefault { category: token, childs: Vec::new() } )),
            }
        }
    }

    /// counts the childs of the current path in the ast
    pub fn cound_childs(&self, path: Vec<usize>) -> usize {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            match self {
                &ASTNode::Default(ref node) => node.childs[*index].cound_childs(new_path),
                &ASTNode::Passage(ref node) => node.childs[*index].cound_childs(new_path),
            }
        } else {
            match self {
                &ASTNode::Default(ref node) => node.childs.len(),
                &ASTNode::Passage(ref node) => node.childs.len(),
            }
        }
    }

    /// prints an node of an ast
    pub fn print(&self, indent: usize) {
        let mut spaces = "".to_string();
        for _ in 0..indent {
            spaces.push_str(" ");
        }

        match self {
            &ASTNode::Passage(ref t) => {
                debug!("{}|- : {:?}", spaces, t.category);
                for child in &t.childs {
                    child.print(indent+2);
                }
            },
            &ASTNode::Default(ref t) => {
                debug!("{}|- : {:?}", spaces, t.category);
                for child in &t.childs {
                    child.print(indent+2);
                }
            }
        }
    }
}

//==============================
//

#[test]
fn it_works() {

}

<commit_msg>-small changes<commit_after>//! The `ast` module contains a lot of useful functionality
//! to create and walk through the ast (abstract syntaxtree)

use frontend::parser;
use frontend::lexer::Token;
use backend::zcode::zfile;

//==============================
// ast

pub struct AST {
    passages: Vec<ASTNode>
}

/// add zcode based on tokens
fn stuff(node: &ASTNode, mut out: &mut zfile::Zfile) {
     match node {
        &ASTNode::Passage(ref t) => {
            debug!("{:?}", t.category);
            match &t.category{
                /*&Token::TokText(ref s) => {

                    //out.op_print(s);
                },
                &Token::TokFormatBold => {
                    out.op_set_text_style(true, false, false, false);
                },
                 &Token::TokFormatItalic => {
                    out.op_set_text_style(false, false, false, true);
                },*/
                _ => {
                    debug!("no match 1");
                }
            };
            for child in &t.childs {
                stuff(child, out);
            }
            out.op_set_text_style(false, false, false, false);
        },
        &ASTNode::Default(ref t) => {
            match &t.category{
                &Token::TokText(ref s) => {
                    out.op_print(s);
                },
                &Token::TokFormatBold => {
                    out.op_set_text_style(true, false, false, false);
                },
                 &Token::TokFormatItalic => {
                    out.op_set_text_style(false, false, false, true);
                },
                _ => {
                    debug!("no match 2");
                }
            };

            for child in &t.childs {
                stuff(child, out);
            }
            out.op_set_text_style(false, false, false, false);
        }
    };
}

impl AST {
    /// convert ast to zcode
    pub fn to_zcode(&self,  out: &mut zfile::Zfile){
        for child in &self.passages {
            stuff(child, out);
        }
    }

    pub fn new() -> AST {
        AST {
            passages: Vec::new()
        }
    }

    /// prints the tree
    pub fn print(&self) {
        debug!("Abstract Syntax Tree: ");
        for child in &self.passages {
            child.print(0);
        }
        debug!("");
    }

    /// adds a passage to the path in the ast
    pub fn add_passage(&mut self, token: Token) {
        let node = ASTNode::Passage(NodePassage { category: token, childs: Vec::new() });
        self.passages.push(node);
    }

    /// adds a child to the path in the ast
    pub fn add_child(&mut self, path: &Vec<usize>, token: Token) {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            self.passages[*index].add_child(new_path, token)
        } else {
            self.passages.push(ASTNode::Default(NodeDefault { category: token, childs: Vec::new() }));
        }
    }

    /// counts the childs of the path in the asts
    pub fn cound_childs(&self, path: Vec<usize>) -> usize {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            self.passages[*index].cound_childs(new_path)
        } else {
            self.passages.len()
        }
    }
}

// ================================
// node types
enum ASTNode {
    Default (NodeDefault),
    Passage (NodePassage)
}

struct NodePassage {
    category: Token,
    pub childs: Vec<ASTNode>,
    /*tags: Vec<ASTNode>*/
}

struct NodeDefault {
    category: Token,
    childs: Vec<ASTNode>
}

impl ASTNode {
    /// adds an child to the path in the ast
    pub fn add_child(&mut self, path: Vec<usize>, token: Token) {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            match self {
                &mut ASTNode::Default(ref mut node) => node.childs[*index].add_child(new_path, token),
                &mut ASTNode::Passage(ref mut node) => node.childs[*index].add_child(new_path, token),
            }
        } else {
            match self {
                &mut ASTNode::Default(ref mut node) => node.childs.push(ASTNode::Default(NodeDefault { category: token, childs: Vec::new() } )),
                &mut ASTNode::Passage(ref mut node) => node.childs.push(ASTNode::Default(NodeDefault { category: token, childs: Vec::new() } )),
            }
        }
    }

    /// counts the childs of the current path in the ast
    pub fn cound_childs(&self, path: Vec<usize>) -> usize {
        if let Some(index) = path.first() {
            let mut new_path: Vec<usize> = path.to_vec();
            new_path.remove(0);

            match self {
                &ASTNode::Default(ref node) => node.childs[*index].cound_childs(new_path),
                &ASTNode::Passage(ref node) => node.childs[*index].cound_childs(new_path),
            }
        } else {
            match self {
                &ASTNode::Default(ref node) => node.childs.len(),
                &ASTNode::Passage(ref node) => node.childs.len(),
            }
        }
    }

    /// prints an node of an ast
    pub fn print(&self, indent: usize) {
        let mut spaces = "".to_string();
        for _ in 0..indent {
            spaces.push_str(" ");
        }

        match self {
            &ASTNode::Passage(ref t) => {
                debug!("{}|- : {:?}", spaces, t.category);
                for child in &t.childs {
                    child.print(indent+2);
                }
            },
            &ASTNode::Default(ref t) => {
                debug!("{}|- : {:?}", spaces, t.category);
                for child in &t.childs {
                    child.print(indent+2);
                }
            }
        }
    }
}

//==============================
//

#[test]
fn it_works() {

}

<|endoftext|>


<commit_before>use std::path;

use sdl2::{self, Sdl};
use sdl2::rect::Rect;
use sdl2::render::{Renderer, Texture};
use sdl2::image::{LoadTexture, INIT_PNG};
use sdl2::pixels::Color;

use game::*;
use game::frontends::sdl::{Tileset, ExtraTileType};

use coord::Coord;

const CELL_WIDTH: usize = 7;
const CELL_HEIGHT: usize = 7;

struct SdlCellInfo {
    fg: Option<Rect>,
    bg: Option<Rect>,
    moon: bool,
    visible: bool,
}

pub struct SdlKnowledgeRenderer {
    buffer: TileBuffer,
    sdl: Sdl,
    sdl_renderer: Renderer<'static>,
    tile_texture: Texture,
    width: usize,
    height: usize,
    tileset: Tileset,
}

impl SdlKnowledgeRenderer {
    pub fn new(sdl: Sdl, width: usize, height: usize, tile_path: path::PathBuf, tileset: Tileset) -> Self {

        let width_px = (width * tileset.tile_width()) as u32;
        let height_px = (height * tileset.tile_height()) as u32;
        let window = sdl.video()
            .expect("Failed to connect to video subsystem")
            .window("Howl", width_px, height_px)
            .build()
            .expect("Failed to create window");

        let mut renderer = window.renderer()
            .build()
            .expect("Failed to initialise renderer");

        renderer.set_draw_color(Color::RGB(0, 0, 0));

        sdl2::image::init(INIT_PNG).expect("Failed to initialise image subsystem");

        let tile_texture = renderer.load_texture(&tile_path).expect("Failed to load tiles from file");

        SdlKnowledgeRenderer {
            buffer: TileBuffer::new(width, height),
            sdl: sdl,
            sdl_renderer: renderer,
            tile_texture: tile_texture,
            width: width,
            height: height,
            tileset: tileset,
        }
    }

    fn tile_width(&self) -> usize {
        self.tileset.tile_width()
    }

    fn tile_height(&self) -> usize {
        self.tileset.tile_height()
    }

    fn screen_rect(&self, coord: Coord) -> Rect {
        let width = self.tile_width() as i32;
        let height = self.tile_height() as i32;

        Rect::new(coord.x as i32 * width, coord.y as i32 * height, width as u32, height as u32)
    }

    fn simple_tile(tile: frontends::sdl::ComplexTile, is_front: bool) -> frontends::sdl::SimpleTile {
        match tile {
            frontends::sdl::ComplexTile::Simple(s) => s,
            frontends::sdl::ComplexTile::Wall { front, back } => {
                if is_front {
                    front
                } else {
                    back
                }
            }
        }
    }

    fn to_sdl_info(&self, cell: &CellDrawInfo) -> SdlCellInfo {
        let mut info = SdlCellInfo {
            moon: cell.moon,
            visible: cell.visible,
            fg: None,
            bg: None,
        };

        if let Some(bg_type) = cell.background {
            let complex_tile = self.tileset.resolve(bg_type);
            let tile = Self::simple_tile(*complex_tile, cell.front);
            info.bg = tile.background();
            info.fg = tile.foreground();
        }

        if let Some(fg_type) = cell.foreground {
            let complex_tile = self.tileset.resolve(fg_type);
            let tile = Self::simple_tile(*complex_tile, cell.front);
            if let Some(fg) = tile.foreground() {
                info.fg = Some(fg);
            }
        }

        info
    }

    fn clear_internal(&mut self) {
        self.sdl_renderer.clear();
    }

    fn draw_internal(&mut self) {

        let blank = *self.tileset.resolve_extra(ExtraTileType::Blank);
        let moon = *self.tileset.resolve_extra(ExtraTileType::Moon);

        for (coord, cell) in izip!(self.buffer.coord_iter(), self.buffer.iter()) {
            let rect = self.screen_rect(coord);
            let info = self.to_sdl_info(cell);

            if !info.visible {
                self.sdl_renderer.copy(&self.tile_texture, Some(blank), Some(rect)).expect("Rendering failed");
                continue;
            }
            if let Some(bg_rect) = info.bg {
                self.sdl_renderer.copy(&self.tile_texture, Some(bg_rect), Some(rect)).expect("Rendering failed");
            }
            if let Some(fg_rect) = info.fg {
                self.sdl_renderer.copy(&self.tile_texture, Some(fg_rect), Some(rect)).expect("Rendering failed");
            }
            if info.moon {
                self.sdl_renderer.copy(&self.tile_texture, Some(moon), Some(rect)).expect("Rendering failed");
            }
        }
    }

    fn draw_overlay_internal(&mut self, overlay: &RenderOverlay) {
        let aim_line_bg = *self.tileset.resolve_extra(ExtraTileType::AimLine);
        if let Some(ref aim_line) = overlay.aim_line {
            for coord in aim_line.iter() {
                if let Some(cell) = self.buffer.get(coord) {
                    let rect = self.screen_rect(coord);
                    let info = self.to_sdl_info(cell);

                    self.sdl_renderer.copy(&self.tile_texture, Some(aim_line_bg), Some(rect)).expect("Rendering failed");
                    if let Some(fg_rect) = info.fg {
                        self.sdl_renderer.copy(&self.tile_texture, Some(fg_rect), Some(rect)).expect("Rendering failed");
                    }
                }
            }
        }
    }
}

impl KnowledgeRenderer for SdlKnowledgeRenderer {
    fn width(&self) -> usize {
        self.width
    }

    fn height(&self) -> usize {
        self.height
    }

    fn world_offset(&self) -> Coord {
        Coord::new(0, 0)
    }

    fn update(&mut self, knowledge: &DrawableKnowledgeLevel, turn_id: u64, _position: Coord) {
        // TODO handle scrolling
        self.buffer.update(knowledge, turn_id, None);
    }

    fn draw(&mut self) {
        self.clear_internal();
        self.draw_internal();
        self.sdl_renderer.present();
    }

    fn draw_with_overlay(&mut self, overlay: &RenderOverlay) {
        // TODO render overlay
        self.clear_internal();
        self.draw_internal();
        self.draw_overlay_internal(overlay);
        self.sdl_renderer.present();
    }
}
<commit_msg>Removed outdated comment<commit_after>use std::path;

use sdl2::{self, Sdl};
use sdl2::rect::Rect;
use sdl2::render::{Renderer, Texture};
use sdl2::image::{LoadTexture, INIT_PNG};
use sdl2::pixels::Color;

use game::*;
use game::frontends::sdl::{Tileset, ExtraTileType};

use coord::Coord;

const CELL_WIDTH: usize = 7;
const CELL_HEIGHT: usize = 7;

struct SdlCellInfo {
    fg: Option<Rect>,
    bg: Option<Rect>,
    moon: bool,
    visible: bool,
}

pub struct SdlKnowledgeRenderer {
    buffer: TileBuffer,
    sdl: Sdl,
    sdl_renderer: Renderer<'static>,
    tile_texture: Texture,
    width: usize,
    height: usize,
    tileset: Tileset,
}

impl SdlKnowledgeRenderer {
    pub fn new(sdl: Sdl, width: usize, height: usize, tile_path: path::PathBuf, tileset: Tileset) -> Self {

        let width_px = (width * tileset.tile_width()) as u32;
        let height_px = (height * tileset.tile_height()) as u32;
        let window = sdl.video()
            .expect("Failed to connect to video subsystem")
            .window("Howl", width_px, height_px)
            .build()
            .expect("Failed to create window");

        let mut renderer = window.renderer()
            .build()
            .expect("Failed to initialise renderer");

        renderer.set_draw_color(Color::RGB(0, 0, 0));

        sdl2::image::init(INIT_PNG).expect("Failed to initialise image subsystem");

        let tile_texture = renderer.load_texture(&tile_path).expect("Failed to load tiles from file");

        SdlKnowledgeRenderer {
            buffer: TileBuffer::new(width, height),
            sdl: sdl,
            sdl_renderer: renderer,
            tile_texture: tile_texture,
            width: width,
            height: height,
            tileset: tileset,
        }
    }

    fn tile_width(&self) -> usize {
        self.tileset.tile_width()
    }

    fn tile_height(&self) -> usize {
        self.tileset.tile_height()
    }

    fn screen_rect(&self, coord: Coord) -> Rect {
        let width = self.tile_width() as i32;
        let height = self.tile_height() as i32;

        Rect::new(coord.x as i32 * width, coord.y as i32 * height, width as u32, height as u32)
    }

    fn simple_tile(tile: frontends::sdl::ComplexTile, is_front: bool) -> frontends::sdl::SimpleTile {
        match tile {
            frontends::sdl::ComplexTile::Simple(s) => s,
            frontends::sdl::ComplexTile::Wall { front, back } => {
                if is_front {
                    front
                } else {
                    back
                }
            }
        }
    }

    fn to_sdl_info(&self, cell: &CellDrawInfo) -> SdlCellInfo {
        let mut info = SdlCellInfo {
            moon: cell.moon,
            visible: cell.visible,
            fg: None,
            bg: None,
        };

        if let Some(bg_type) = cell.background {
            let complex_tile = self.tileset.resolve(bg_type);
            let tile = Self::simple_tile(*complex_tile, cell.front);
            info.bg = tile.background();
            info.fg = tile.foreground();
        }

        if let Some(fg_type) = cell.foreground {
            let complex_tile = self.tileset.resolve(fg_type);
            let tile = Self::simple_tile(*complex_tile, cell.front);
            if let Some(fg) = tile.foreground() {
                info.fg = Some(fg);
            }
        }

        info
    }

    fn clear_internal(&mut self) {
        self.sdl_renderer.clear();
    }

    fn draw_internal(&mut self) {

        let blank = *self.tileset.resolve_extra(ExtraTileType::Blank);
        let moon = *self.tileset.resolve_extra(ExtraTileType::Moon);

        for (coord, cell) in izip!(self.buffer.coord_iter(), self.buffer.iter()) {
            let rect = self.screen_rect(coord);
            let info = self.to_sdl_info(cell);

            if !info.visible {
                self.sdl_renderer.copy(&self.tile_texture, Some(blank), Some(rect)).expect("Rendering failed");
                continue;
            }
            if let Some(bg_rect) = info.bg {
                self.sdl_renderer.copy(&self.tile_texture, Some(bg_rect), Some(rect)).expect("Rendering failed");
            }
            if let Some(fg_rect) = info.fg {
                self.sdl_renderer.copy(&self.tile_texture, Some(fg_rect), Some(rect)).expect("Rendering failed");
            }
            if info.moon {
                self.sdl_renderer.copy(&self.tile_texture, Some(moon), Some(rect)).expect("Rendering failed");
            }
        }
    }

    fn draw_overlay_internal(&mut self, overlay: &RenderOverlay) {
        let aim_line_bg = *self.tileset.resolve_extra(ExtraTileType::AimLine);
        if let Some(ref aim_line) = overlay.aim_line {
            for coord in aim_line.iter() {
                if let Some(cell) = self.buffer.get(coord) {
                    let rect = self.screen_rect(coord);
                    let info = self.to_sdl_info(cell);

                    self.sdl_renderer.copy(&self.tile_texture, Some(aim_line_bg), Some(rect)).expect("Rendering failed");
                    if let Some(fg_rect) = info.fg {
                        self.sdl_renderer.copy(&self.tile_texture, Some(fg_rect), Some(rect)).expect("Rendering failed");
                    }
                }
            }
        }
    }
}

impl KnowledgeRenderer for SdlKnowledgeRenderer {
    fn width(&self) -> usize {
        self.width
    }

    fn height(&self) -> usize {
        self.height
    }

    fn world_offset(&self) -> Coord {
        Coord::new(0, 0)
    }

    fn update(&mut self, knowledge: &DrawableKnowledgeLevel, turn_id: u64, _position: Coord) {
        // TODO handle scrolling
        self.buffer.update(knowledge, turn_id, None);
    }

    fn draw(&mut self) {
        self.clear_internal();
        self.draw_internal();
        self.sdl_renderer.present();
    }

    fn draw_with_overlay(&mut self, overlay: &RenderOverlay) {
        self.clear_internal();
        self.draw_internal();
        self.draw_overlay_internal(overlay);
        self.sdl_renderer.present();
    }
}
<|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/. */

//! Common handling for the specified value CSS url() values.

use cssparser::Parser;
use gecko_bindings::bindings;
use gecko_bindings::structs::root::nsStyleImageRequest;
use gecko_bindings::structs::root::mozilla::CORSMode;
use gecko_bindings::structs::root::mozilla::css::URLValue;
use gecko_bindings::sugar::ownership::{HasArcFFI, FFIArcHelpers};
use gecko_bindings::sugar::refptr::RefPtr;
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use nsstring::nsCString;
use parser::{Parse, ParserContext};
use servo_arc::Arc;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ParseError, ToCss};
use stylesheets::UrlExtraData;
use values::computed::{Context, ToComputedValue};

/// A CSS url() value for gecko.
#[css(function = "url")]
#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss)]
pub struct CssUrl(pub Arc<CssUrlData>);

/// Data shared between CssUrls.
#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss)]
pub struct CssUrlData {
    /// The URL in unresolved string form.
    serialization: String,

    /// The URL extra data.
    #[css(skip)]
    pub extra_data: UrlExtraData,
}

impl CssUrl {
    /// Parse a URL from a string value that is a valid CSS token for a URL.
    pub fn parse_from_string(url: String, context: &ParserContext) -> Self {
        CssUrl(Arc::new(CssUrlData {
            serialization: url,
            extra_data: context.url_data.clone(),
        }))
    }

    /// Returns true if the URL is definitely invalid. We don't eagerly resolve
    /// URLs in gecko, so we just return false here.
    /// use its |resolved| status.
    pub fn is_invalid(&self) -> bool {
        false
    }

    /// Returns true if this URL looks like a fragment.
    /// See https://drafts.csswg.org/css-values/#local-urls
    pub fn is_fragment(&self) -> bool {
        self.as_str().chars().next().map_or(false, |c| c == '#')
    }

    /// Return the unresolved url as string, or the empty string if it's
    /// invalid.
    #[inline]
    pub fn as_str(&self) -> &str {
        self.0.as_str()
    }
}

impl CssUrlData {
    /// Return the unresolved url as string, or the empty string if it's
    /// invalid.
    pub fn as_str(&self) -> &str {
        &*self.serialization
    }
}

impl Parse for CssUrl {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        let url = input.expect_url()?;
        Ok(Self::parse_from_string(url.as_ref().to_owned(), context))
    }
}

impl Eq for CssUrl {}

impl MallocSizeOf for CssUrl {
    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
        // XXX: measure `serialization` once bug 1397971 lands

        // We ignore `extra_data`, because RefPtr is tricky, and there aren't
        // many of them in practise (sharing is common).

        0
    }
}

/// A specified non-image `url()` value.
#[derive(Clone, Debug, SpecifiedValueInfo, ToCss)]
pub struct SpecifiedUrl {
    /// The specified url value.
    pub url: CssUrl,
    /// Gecko's URLValue so that we can reuse it while rematching a
    /// property with this specified value.
    #[css(skip)]
    pub url_value: RefPtr<URLValue>,
}

impl SpecifiedUrl {
    /// Parse a URL from a string value.
    pub fn parse_from_string(url: String, context: &ParserContext) -> Self {
        Self::from_css_url(CssUrl::parse_from_string(url, context))
    }

    fn from_css_url_with_cors(url: CssUrl, cors: CORSMode) -> Self {
        let url_value = unsafe {
            let ptr = bindings::Gecko_URLValue_Create(
                url.0.clone().into_strong(),
                url.0.extra_data.0.get(),
                cors,
            );
            // We do not expect Gecko_URLValue_Create returns null.
            debug_assert!(!ptr.is_null());
            RefPtr::from_addrefed(ptr)
        };
        Self { url, url_value }
    }

    fn from_css_url(url: CssUrl) -> Self {
        use gecko_bindings::structs::root::mozilla::CORSMode_CORS_NONE;
        Self::from_css_url_with_cors(url, CORSMode_CORS_NONE)
    }

    fn from_css_url_with_cors_anonymous(url: CssUrl) -> Self {
        use gecko_bindings::structs::root::mozilla::CORSMode_CORS_ANONYMOUS;
        Self::from_css_url_with_cors(url, CORSMode_CORS_ANONYMOUS)
    }
}

impl Parse for SpecifiedUrl {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        CssUrl::parse(context, input).map(Self::from_css_url)
    }
}

impl PartialEq for SpecifiedUrl {
    fn eq(&self, other: &Self) -> bool {
        self.url.eq(&other.url)
    }
}

impl Eq for SpecifiedUrl {}

impl MallocSizeOf for SpecifiedUrl {
    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
        let mut n = self.url.size_of(ops);
        // Although this is a RefPtr, this is the primary reference because
        // SpecifiedUrl is responsible for creating the url_value. So we
        // measure unconditionally here.
        n += unsafe { bindings::Gecko_URLValue_SizeOfIncludingThis(self.url_value.get()) };
        n
    }
}

impl ToComputedValue for SpecifiedUrl {
    type ComputedValue = ComputedUrl;

    #[inline]
    fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
        ComputedUrl(self.clone())
    }

    #[inline]
    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        computed.0.clone()
    }
}

/// A specified image `url()` value.
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
pub struct SpecifiedImageUrl(pub SpecifiedUrl);

impl SpecifiedImageUrl {
    /// Parse a URL from a string value that is a valid CSS token for a URL.
    pub fn parse_from_string(url: String, context: &ParserContext) -> Self {
        SpecifiedImageUrl(SpecifiedUrl::parse_from_string(url, context))
    }

    /// Provides an alternate method for parsing that associates the URL
    /// with anonymous CORS headers.
    pub fn parse_with_cors_anonymous<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        CssUrl::parse(context, input)
            .map(SpecifiedUrl::from_css_url_with_cors_anonymous)
            .map(SpecifiedImageUrl)
    }
}

impl Parse for SpecifiedImageUrl {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        SpecifiedUrl::parse(context, input).map(SpecifiedImageUrl)
    }
}

impl ToComputedValue for SpecifiedImageUrl {
    type ComputedValue = ComputedImageUrl;

    #[inline]
    fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
        ComputedImageUrl(self.clone())
    }

    #[inline]
    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        computed.0.clone()
    }
}

fn serialize_computed_url<W>(
    url_value: &URLValue,
    dest: &mut CssWriter<W>,
    get_url: unsafe extern "C" fn(*const URLValue, *mut nsCString) -> (),
) -> fmt::Result
where
    W: Write,
{
    dest.write_str("url(")?;
    unsafe {
        let mut string = nsCString::new();
        get_url(url_value, &mut string);
        string.as_str_unchecked().to_css(dest)?;
    }
    dest.write_char(')')
}

/// The computed value of a CSS non-image `url()`.
///
/// The only difference between specified and computed URLs is the
/// serialization.
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
pub struct ComputedUrl(pub SpecifiedUrl);

impl ToCss for ComputedUrl {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        serialize_computed_url(
            &self.0.url_value,
            dest,
            bindings::Gecko_GetComputedURLSpec,
        )
    }
}

impl ComputedUrl {
    /// Convert from RefPtr<URLValue> to ComputedUrl.
    pub unsafe fn from_url_value(url_value: RefPtr<URLValue>) -> Self {
        let css_url = &*url_value.mCssUrl.mRawPtr;
        let url = CssUrl(CssUrlData::as_arc(&css_url).clone_arc());
        ComputedUrl(SpecifiedUrl { url, url_value })
    }

    /// Get a raw pointer to the URLValue held by this ComputedUrl, for FFI.
    pub fn url_value_ptr(&self) -> *mut URLValue {
        self.0.url_value.get()
    }
}

/// The computed value of a CSS image `url()`.
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
pub struct ComputedImageUrl(pub SpecifiedImageUrl);

impl ToCss for ComputedImageUrl {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        serialize_computed_url(
            &(self.0).0.url_value,
            dest,
            bindings::Gecko_GetComputedImageURLSpec,
        )
    }
}

impl ComputedImageUrl {
    /// Convert from nsStyleImageReques to ComputedImageUrl.
    pub unsafe fn from_image_request(image_request: &nsStyleImageRequest) -> Self {
        let url_value = image_request.mImageValue.to_safe();
        let css_url = &*url_value.mCssUrl.mRawPtr;
        let url = CssUrl(CssUrlData::as_arc(&css_url).clone_arc());
        ComputedImageUrl(SpecifiedImageUrl(SpecifiedUrl { url, url_value }))
    }

    /// Get a raw pointer to the URLValue held by this ComputedImageUrl, for FFI.
    pub fn url_value_ptr(&self) -> *mut URLValue {
        (self.0).0.url_value.get()
    }
}
<commit_msg>style: Have css::URLValue get URLExtraData from its CssUrlData.<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/. */

//! Common handling for the specified value CSS url() values.

use cssparser::Parser;
use gecko_bindings::bindings;
use gecko_bindings::structs::root::nsStyleImageRequest;
use gecko_bindings::structs::root::mozilla::CORSMode;
use gecko_bindings::structs::root::mozilla::css::URLValue;
use gecko_bindings::sugar::ownership::{HasArcFFI, FFIArcHelpers};
use gecko_bindings::sugar::refptr::RefPtr;
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use nsstring::nsCString;
use parser::{Parse, ParserContext};
use servo_arc::Arc;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ParseError, ToCss};
use stylesheets::UrlExtraData;
use values::computed::{Context, ToComputedValue};

/// A CSS url() value for gecko.
#[css(function = "url")]
#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss)]
pub struct CssUrl(pub Arc<CssUrlData>);

/// Data shared between CssUrls.
#[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss)]
pub struct CssUrlData {
    /// The URL in unresolved string form.
    serialization: String,

    /// The URL extra data.
    #[css(skip)]
    pub extra_data: UrlExtraData,
}

impl CssUrl {
    /// Parse a URL from a string value that is a valid CSS token for a URL.
    pub fn parse_from_string(url: String, context: &ParserContext) -> Self {
        CssUrl(Arc::new(CssUrlData {
            serialization: url,
            extra_data: context.url_data.clone(),
        }))
    }

    /// Returns true if the URL is definitely invalid. We don't eagerly resolve
    /// URLs in gecko, so we just return false here.
    /// use its |resolved| status.
    pub fn is_invalid(&self) -> bool {
        false
    }

    /// Returns true if this URL looks like a fragment.
    /// See https://drafts.csswg.org/css-values/#local-urls
    pub fn is_fragment(&self) -> bool {
        self.as_str().chars().next().map_or(false, |c| c == '#')
    }

    /// Return the unresolved url as string, or the empty string if it's
    /// invalid.
    #[inline]
    pub fn as_str(&self) -> &str {
        self.0.as_str()
    }
}

impl CssUrlData {
    /// Return the unresolved url as string, or the empty string if it's
    /// invalid.
    pub fn as_str(&self) -> &str {
        &*self.serialization
    }
}

impl Parse for CssUrl {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        let url = input.expect_url()?;
        Ok(Self::parse_from_string(url.as_ref().to_owned(), context))
    }
}

impl Eq for CssUrl {}

impl MallocSizeOf for CssUrl {
    fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
        // XXX: measure `serialization` once bug 1397971 lands

        // We ignore `extra_data`, because RefPtr is tricky, and there aren't
        // many of them in practise (sharing is common).

        0
    }
}

/// A specified non-image `url()` value.
#[derive(Clone, Debug, SpecifiedValueInfo, ToCss)]
pub struct SpecifiedUrl {
    /// The specified url value.
    pub url: CssUrl,
    /// Gecko's URLValue so that we can reuse it while rematching a
    /// property with this specified value.
    #[css(skip)]
    pub url_value: RefPtr<URLValue>,
}

impl SpecifiedUrl {
    /// Parse a URL from a string value.
    pub fn parse_from_string(url: String, context: &ParserContext) -> Self {
        Self::from_css_url(CssUrl::parse_from_string(url, context))
    }

    fn from_css_url_with_cors(url: CssUrl, cors: CORSMode) -> Self {
        let url_value = unsafe {
            let ptr = bindings::Gecko_URLValue_Create(
                url.0.clone().into_strong(),
                cors,
            );
            // We do not expect Gecko_URLValue_Create returns null.
            debug_assert!(!ptr.is_null());
            RefPtr::from_addrefed(ptr)
        };
        Self { url, url_value }
    }

    fn from_css_url(url: CssUrl) -> Self {
        use gecko_bindings::structs::root::mozilla::CORSMode_CORS_NONE;
        Self::from_css_url_with_cors(url, CORSMode_CORS_NONE)
    }

    fn from_css_url_with_cors_anonymous(url: CssUrl) -> Self {
        use gecko_bindings::structs::root::mozilla::CORSMode_CORS_ANONYMOUS;
        Self::from_css_url_with_cors(url, CORSMode_CORS_ANONYMOUS)
    }
}

impl Parse for SpecifiedUrl {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        CssUrl::parse(context, input).map(Self::from_css_url)
    }
}

impl PartialEq for SpecifiedUrl {
    fn eq(&self, other: &Self) -> bool {
        self.url.eq(&other.url)
    }
}

impl Eq for SpecifiedUrl {}

impl MallocSizeOf for SpecifiedUrl {
    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
        let mut n = self.url.size_of(ops);
        // Although this is a RefPtr, this is the primary reference because
        // SpecifiedUrl is responsible for creating the url_value. So we
        // measure unconditionally here.
        n += unsafe { bindings::Gecko_URLValue_SizeOfIncludingThis(self.url_value.get()) };
        n
    }
}

impl ToComputedValue for SpecifiedUrl {
    type ComputedValue = ComputedUrl;

    #[inline]
    fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
        ComputedUrl(self.clone())
    }

    #[inline]
    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        computed.0.clone()
    }
}

/// A specified image `url()` value.
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss)]
pub struct SpecifiedImageUrl(pub SpecifiedUrl);

impl SpecifiedImageUrl {
    /// Parse a URL from a string value that is a valid CSS token for a URL.
    pub fn parse_from_string(url: String, context: &ParserContext) -> Self {
        SpecifiedImageUrl(SpecifiedUrl::parse_from_string(url, context))
    }

    /// Provides an alternate method for parsing that associates the URL
    /// with anonymous CORS headers.
    pub fn parse_with_cors_anonymous<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        CssUrl::parse(context, input)
            .map(SpecifiedUrl::from_css_url_with_cors_anonymous)
            .map(SpecifiedImageUrl)
    }
}

impl Parse for SpecifiedImageUrl {
    fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        SpecifiedUrl::parse(context, input).map(SpecifiedImageUrl)
    }
}

impl ToComputedValue for SpecifiedImageUrl {
    type ComputedValue = ComputedImageUrl;

    #[inline]
    fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
        ComputedImageUrl(self.clone())
    }

    #[inline]
    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        computed.0.clone()
    }
}

fn serialize_computed_url<W>(
    url_value: &URLValue,
    dest: &mut CssWriter<W>,
    get_url: unsafe extern "C" fn(*const URLValue, *mut nsCString) -> (),
) -> fmt::Result
where
    W: Write,
{
    dest.write_str("url(")?;
    unsafe {
        let mut string = nsCString::new();
        get_url(url_value, &mut string);
        string.as_str_unchecked().to_css(dest)?;
    }
    dest.write_char(')')
}

/// The computed value of a CSS non-image `url()`.
///
/// The only difference between specified and computed URLs is the
/// serialization.
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
pub struct ComputedUrl(pub SpecifiedUrl);

impl ToCss for ComputedUrl {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        serialize_computed_url(
            &self.0.url_value,
            dest,
            bindings::Gecko_GetComputedURLSpec,
        )
    }
}

impl ComputedUrl {
    /// Convert from RefPtr<URLValue> to ComputedUrl.
    pub unsafe fn from_url_value(url_value: RefPtr<URLValue>) -> Self {
        let css_url = &*url_value.mCssUrl.mRawPtr;
        let url = CssUrl(CssUrlData::as_arc(&css_url).clone_arc());
        ComputedUrl(SpecifiedUrl { url, url_value })
    }

    /// Get a raw pointer to the URLValue held by this ComputedUrl, for FFI.
    pub fn url_value_ptr(&self) -> *mut URLValue {
        self.0.url_value.get()
    }
}

/// The computed value of a CSS image `url()`.
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
pub struct ComputedImageUrl(pub SpecifiedImageUrl);

impl ToCss for ComputedImageUrl {
    fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
    where
        W: Write,
    {
        serialize_computed_url(
            &(self.0).0.url_value,
            dest,
            bindings::Gecko_GetComputedImageURLSpec,
        )
    }
}

impl ComputedImageUrl {
    /// Convert from nsStyleImageReques to ComputedImageUrl.
    pub unsafe fn from_image_request(image_request: &nsStyleImageRequest) -> Self {
        let url_value = image_request.mImageValue.to_safe();
        let css_url = &*url_value.mCssUrl.mRawPtr;
        let url = CssUrl(CssUrlData::as_arc(&css_url).clone_arc());
        ComputedImageUrl(SpecifiedImageUrl(SpecifiedUrl { url, url_value }))
    }

    /// Get a raw pointer to the URLValue held by this ComputedImageUrl, for FFI.
    pub fn url_value_ptr(&self) -> *mut URLValue {
        (self.0).0.url_value.get()
    }
}
<|endoftext|>


<commit_before>//! Context management

use collections::{BTreeMap, Vec};
use core::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard};

use arch::context::Context as ArchContext;
use syscall::{Error, Result};

/// File operations
pub mod file;

/// Limit on number of contexts
pub const CONTEXT_MAX_CONTEXTS: usize = 65536;

/// Maximum context files
pub const CONTEXT_MAX_FILES: usize = 65536;

/// Context list type
pub struct ContextList {
    map: BTreeMap<usize, RwLock<Context>>,
    next_id: usize
}

impl ContextList {
    pub fn new() -> Self {
        ContextList {
            map: BTreeMap::new(),
            next_id: 1
        }
    }

    pub fn get(&self, id: usize) -> Option<&RwLock<Context>> {
        self.map.get(&id)
    }

    pub fn current(&self) -> Option<&RwLock<Context>> {
        self.map.get(&CONTEXT_ID.load(Ordering::SeqCst))
    }

    pub fn new_context(&mut self) -> Result<&RwLock<Context>> {
        if self.next_id >= CONTEXT_MAX_CONTEXTS {
            self.next_id = 1;
        }

        while self.map.contains_key(&self.next_id) {
            self.next_id += 1;
        }

        if self.next_id >= CONTEXT_MAX_CONTEXTS {
            return Err(Error::TryAgain);
        }

        let id = self.next_id;
        self.next_id += 1;
        assert!(self.map.insert(id, RwLock::new(Context::new(id))).is_none());
        Ok(self.map.get(&id).expect("failed to insert new context"))
    }

    pub fn spawn(&mut self, func: extern fn()) -> Result<&RwLock<Context>> {
        let context_lock = self.new_context()?;
        {
            let mut context = context_lock.write();
            print!("{}", format!("{}: {:X}\n", context.id, func as usize));
        }
        Ok(context_lock)
    }
}

/// Contexts list
static CONTEXTS: Once<RwLock<ContextList>> = Once::new();

#[thread_local]
static CONTEXT_ID: AtomicUsize = ATOMIC_USIZE_INIT;

pub fn init() {
    let mut contexts = contexts_mut();
    let context_lock = contexts.new_context().expect("could not initialize first context");
    let context = context_lock.read();
    CONTEXT_ID.store(context.id, Ordering::SeqCst);
}

/// Initialize contexts, called if needed
fn init_contexts() -> RwLock<ContextList> {
    RwLock::new(ContextList::new())
}

/// Get the global schemes list, const
pub fn contexts() -> RwLockReadGuard<'static, ContextList> {
    CONTEXTS.call_once(init_contexts).read()
}

/// Get the global schemes list, mutable
pub fn contexts_mut() -> RwLockWriteGuard<'static, ContextList> {
    CONTEXTS.call_once(init_contexts).write()
}

/// Switch to the next context
/// Do not call this while holding locks!
pub unsafe fn context_switch() {

    current.arch.switch_to(&mut next.arch);
}

/// A context, which identifies either a process or a thread
#[derive(Debug)]
pub struct Context {
    /// The ID of this context
    pub id: usize,
    /// The architecture specific context
    pub arch: ArchContext,
    /// The open files in the scheme
    pub files: Vec<Option<file::File>>
}

impl Context {
    /// Create a new context
    pub fn new(id: usize) -> Context {
        Context {
            id: id,
            arch: ArchContext::new(),
            files: Vec::new()
        }
    }

    /// Add a file to the lowest available slot.
    /// Return the file descriptor number or None if no slot was found
    pub fn add_file(&mut self, file: file::File) -> Option<usize> {
        for (i, mut file_option) in self.files.iter_mut().enumerate() {
            if file_option.is_none() {
                *file_option = Some(file);
                return Some(i);
            }
        }
        let len = self.files.len();
        if len < CONTEXT_MAX_FILES {
            self.files.push(Some(file));
            Some(len)
        } else {
            None
        }
    }
}
<commit_msg>Fix the build. Sorry<commit_after>//! Context management

use collections::{BTreeMap, Vec};
use core::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
use spin::{Once, RwLock, RwLockReadGuard, RwLockWriteGuard};

use arch::context::Context as ArchContext;
use syscall::{Error, Result};

/// File operations
pub mod file;

/// Limit on number of contexts
pub const CONTEXT_MAX_CONTEXTS: usize = 65536;

/// Maximum context files
pub const CONTEXT_MAX_FILES: usize = 65536;

/// Context list type
pub struct ContextList {
    map: BTreeMap<usize, RwLock<Context>>,
    next_id: usize
}

impl ContextList {
    pub fn new() -> Self {
        ContextList {
            map: BTreeMap::new(),
            next_id: 1
        }
    }

    pub fn get(&self, id: usize) -> Option<&RwLock<Context>> {
        self.map.get(&id)
    }

    pub fn current(&self) -> Option<&RwLock<Context>> {
        self.map.get(&CONTEXT_ID.load(Ordering::SeqCst))
    }

    pub fn new_context(&mut self) -> Result<&RwLock<Context>> {
        if self.next_id >= CONTEXT_MAX_CONTEXTS {
            self.next_id = 1;
        }

        while self.map.contains_key(&self.next_id) {
            self.next_id += 1;
        }

        if self.next_id >= CONTEXT_MAX_CONTEXTS {
            return Err(Error::TryAgain);
        }

        let id = self.next_id;
        self.next_id += 1;
        assert!(self.map.insert(id, RwLock::new(Context::new(id))).is_none());
        Ok(self.map.get(&id).expect("failed to insert new context"))
    }

    pub fn spawn(&mut self, func: extern fn()) -> Result<&RwLock<Context>> {
        let context_lock = self.new_context()?;
        {
            let mut context = context_lock.write();
            print!("{}", format!("{}: {:X}\n", context.id, func as usize));
        }
        Ok(context_lock)
    }
}

/// Contexts list
static CONTEXTS: Once<RwLock<ContextList>> = Once::new();

#[thread_local]
static CONTEXT_ID: AtomicUsize = ATOMIC_USIZE_INIT;

pub fn init() {
    let mut contexts = contexts_mut();
    let context_lock = contexts.new_context().expect("could not initialize first context");
    let context = context_lock.read();
    CONTEXT_ID.store(context.id, Ordering::SeqCst);
}

/// Initialize contexts, called if needed
fn init_contexts() -> RwLock<ContextList> {
    RwLock::new(ContextList::new())
}

/// Get the global schemes list, const
pub fn contexts() -> RwLockReadGuard<'static, ContextList> {
    CONTEXTS.call_once(init_contexts).read()
}

/// Get the global schemes list, mutable
pub fn contexts_mut() -> RwLockWriteGuard<'static, ContextList> {
    CONTEXTS.call_once(init_contexts).write()
}

/// Switch to the next context
/// Do not call this while holding locks!
pub unsafe fn context_switch() {
//    current.arch.switch_to(&mut next.arch);
}

/// A context, which identifies either a process or a thread
#[derive(Debug)]
pub struct Context {
    /// The ID of this context
    pub id: usize,
    /// The architecture specific context
    pub arch: ArchContext,
    /// The open files in the scheme
    pub files: Vec<Option<file::File>>
}

impl Context {
    /// Create a new context
    pub fn new(id: usize) -> Context {
        Context {
            id: id,
            arch: ArchContext::new(),
            files: Vec::new()
        }
    }

    /// Add a file to the lowest available slot.
    /// Return the file descriptor number or None if no slot was found
    pub fn add_file(&mut self, file: file::File) -> Option<usize> {
        for (i, mut file_option) in self.files.iter_mut().enumerate() {
            if file_option.is_none() {
                *file_option = Some(file);
                return Some(i);
            }
        }
        let len = self.files.len();
        if len < CONTEXT_MAX_FILES {
            self.files.push(Some(file));
            Some(len)
        } else {
            None
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Test that structs with higher-ranked where clauses don't generate
// "outlives" requirements. Issue #22246.

#![allow(dead_code)]

///////////////////////////////////////////////////////////////////////////

pub trait TheTrait<'b> {
    type TheAssocType;

    fn dummy(&'b self) { }
}

pub struct TheType<'b> {
    m: [fn(&'b()); 0]
}

impl<'a,'b> TheTrait<'a> for TheType<'b> {
    type TheAssocType = &'b ();
}

///////////////////////////////////////////////////////////////////////////

pub struct WithHrAssoc<T>
    where for<'a> T : TheTrait<'a>
{
    m: [T; 0]
}

fn with_assoc<'a,'b>() {
    // We get no error here because the where clause has a higher-ranked assoc type,
    // which could not be projected from.

    let _: &'a WithHrAssoc<TheType<'b>> = loop { };
}

///////////////////////////////////////////////////////////////////////////

pub trait TheSubTrait : for<'a> TheTrait<'a> {
}

impl<'b> TheSubTrait for TheType<'b> { }

pub struct WithHrAssocSub<T>
    where T : TheSubTrait
{
    m: [T; 0]
}

fn with_assoc_sub<'a,'b>() {
    // Same here, because although the where clause is not HR, it
    // extends a trait in a HR way.

    let _: &'a WithHrAssocSub<TheType<'b>> = loop { };
}

#[rustc_error]
fn main() { //~ ERROR compilation successful
}
<commit_msg>Add rustc_attrs feature to test.<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Test that structs with higher-ranked where clauses don't generate
// "outlives" requirements. Issue #22246.

#![allow(dead_code)]
#![feature(rustc_attrs)]

///////////////////////////////////////////////////////////////////////////

pub trait TheTrait<'b> {
    type TheAssocType;

    fn dummy(&'b self) { }
}

pub struct TheType<'b> {
    m: [fn(&'b()); 0]
}

impl<'a,'b> TheTrait<'a> for TheType<'b> {
    type TheAssocType = &'b ();
}

///////////////////////////////////////////////////////////////////////////

pub struct WithHrAssoc<T>
    where for<'a> T : TheTrait<'a>
{
    m: [T; 0]
}

fn with_assoc<'a,'b>() {
    // We get no error here because the where clause has a higher-ranked assoc type,
    // which could not be projected from.

    let _: &'a WithHrAssoc<TheType<'b>> = loop { };
}

///////////////////////////////////////////////////////////////////////////

pub trait TheSubTrait : for<'a> TheTrait<'a> {
}

impl<'b> TheSubTrait for TheType<'b> { }

pub struct WithHrAssocSub<T>
    where T : TheSubTrait
{
    m: [T; 0]
}

fn with_assoc_sub<'a,'b>() {
    // Same here, because although the where clause is not HR, it
    // extends a trait in a HR way.

    let _: &'a WithHrAssocSub<TheType<'b>> = loop { };
}

#[rustc_error]
fn main() { //~ ERROR compilation successful
}
<|endoftext|>