<commit_before>//! This module provides a token source (`GetToken`) that obtains tokens for service accounts.
//! Service accounts are usually used by software (i.e., non-human actors) to get access to
//! resources. Currently, this module only works with RS256 JWTs, and uses the Google request URL
//! by default (this can be amended by a patch).
//!
//! Resources:
//! - [Using OAuth 2.0 for Server to Server
//! Applications](https://developers.google.com/identity/protocols/OAuth2ServiceAccount)
//! - [JSON Web Tokens](https://jwt.io/)
//!
//! Copyright (c) 2016 Google Inc (lewinb@google.com).
//!

use std::borrow::BorrowMut;
use std::default::Default;
use std::error;
use std::io::{Read, Write};
use std::result;

use authenticator::GetToken;
use types::{StringError, Token};
use storage::{hash_scopes, MemoryStorage, TokenStorage};

use hyper::header;
use url::form_urlencoded;

use base64;
use chrono;
use hyper;
use openssl;
use serde_json;

const GRANT_TYPE: &'static str = "urn:ietf:params:oauth:grant-type:jwt-bearer";
const TOKEN_REQUEST_URL: &'static str = "https://www.googleapis.com/oauth2/v4/token";
const GOOGLE_RS256_HEAD: &'static str = "{\"alg\":\"RS256\",\"typ\":\"JWT\"}";

// Encodes s as Base64
fn encode_base64<T: AsRef<[u8]>>(s: T) -> String {
    base64::encode(s.as_ref())
}

// Calculates the SHA256 hash.
fn hash_sha256(data: &[u8]) -> Vec<u8> {
    let mut hasher = openssl::crypto::hash::Hasher::new(openssl::crypto::hash::Type::SHA256);
    let _ = hasher.write(data);
    hasher.finish()
}

// Signs the hash with key.
fn sign_rsa(key: &openssl::crypto::rsa::RSA, hash: &[u8]) -> String {
    let signature = key.sign(openssl::crypto::hash::Type::SHA256, hash).unwrap();
    let b64_signature = encode_base64(signature);

    b64_signature
}

// Reads an RSA key from pem_pkcs8 (the format of the 'private_key' field in the service account
// key).
fn decode_rsa_key(pem_pkcs8: &str) -> Result<openssl::crypto::rsa::RSA, Box<error::Error>> {
    let private_key = pem_pkcs8.to_string().replace("\\n", "\n");
    let privkey = openssl::crypto::rsa::RSA::private_key_from_pem(&mut private_key.as_bytes());

    match privkey {
        Err(e) => Err(Box::new(e)),
        Ok(key) => Ok(key),
    }
}

/// JSON schema of secret service account key. You can obtain the key from
/// the Cloud Console at https://console.cloud.google.com/.
///
/// You can use `helpers::service_account_key_from_file()` as a quick way to read a JSON client
/// secret into a ServiceAccountKey.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ServiceAccountKey {
    #[serde(rename="type")]
    pub key_type: Option<String>,
    pub project_id: Option<String>,
    pub private_key_id: Option<String>,
    pub private_key: Option<String>,
    pub client_email: Option<String>,
    pub client_id: Option<String>,
    pub auth_uri: Option<String>,
    pub token_uri: Option<String>,
    pub auth_provier_x509_cert_url: Option<String>,
    pub client_x509_cert_url: Option<String>,
}

#[derive(Serialize, Debug)]
struct Claims {
    iss: String,
    aud: String,
    exp: i64,
    iat: i64,
    sub: Option<String>,
    scope: String,
}

struct JWT {
    header: String,
    claims: Claims,
}

impl JWT {
    fn new(claims: Claims) -> JWT {
        JWT {
            header: GOOGLE_RS256_HEAD.to_string(),
            claims: claims,
        }
    }
    // Encodes the first two parts (header and claims) to base64 and assembles them into a form
    // ready to be signed.
    fn encode_claims(&self) -> String {
        let mut head = encode_base64(&self.header);
        let claims = encode_base64(serde_json::to_string(&self.claims).unwrap());

        head.push_str(".");
        head.push_str(&claims);
        head
    }

    fn sign(&self, private_key: &str) -> Result<String, Box<error::Error>> {
        let mut jwt_head = self.encode_claims();

        let key = try!(decode_rsa_key(private_key));
        let hash = hash_sha256(&jwt_head.as_bytes());
        let signature = sign_rsa(&key, &hash);

        jwt_head.push_str(".");
        jwt_head.push_str(&signature);

        Ok(jwt_head)
    }
}

fn init_claims_from_key<'a, I, T>(key: &ServiceAccountKey, scopes: I) -> Claims
    where T: AsRef<str> + 'a,
          I: IntoIterator<Item = &'a T>
{
    let iat = chrono::UTC::now().timestamp();
    let expiry = iat + 3600 - 5; // Max validity is 1h.

    let mut scopes_string = scopes.into_iter().fold(String::new(), |mut acc, sc| {
        acc.push_str(sc.as_ref());
        acc.push_str(" ");
        acc
    });
    scopes_string.pop();

    Claims {
        iss: key.client_email.clone().unwrap(),
        aud: TOKEN_REQUEST_URL.to_string(),
        exp: expiry,
        iat: iat,
        sub: None,
        scope: scopes_string,
    }
}

/// See "Additional claims" at https://developers.google.com/identity/protocols/OAuth2ServiceAccount
#[allow(dead_code)]
fn set_sub_claim(mut claims: Claims, sub: String) -> Claims {
    claims.sub = Some(sub);
    claims
}

/// A token source (`GetToken`) yielding OAuth tokens for services that use ServiceAccount authorization.
/// This token source caches token and automatically renews expired ones.
pub struct ServiceAccountAccess<C> {
    client: C,
    key: ServiceAccountKey,
    cache: MemoryStorage,
}

/// This is the schema of the server's response.
#[derive(Deserialize, Debug)]
struct TokenResponse {
    access_token: Option<String>,
    token_type: Option<String>,
    expires_in: Option<i64>,
}

impl TokenResponse {
    fn to_oauth_token(self) -> Token {
        let expires_ts = chrono::UTC::now().timestamp() + self.expires_in.unwrap_or(0);

        Token {
            access_token: self.access_token.unwrap(),
            token_type: self.token_type.unwrap(),
            refresh_token: String::new(),
            expires_in: self.expires_in,
            expires_in_timestamp: Some(expires_ts),
        }
    }
}

impl<'a, C> ServiceAccountAccess<C>
    where C: BorrowMut<hyper::Client>
{
    /// Returns a new `ServiceAccountAccess` token source.
    #[allow(dead_code)]
    pub fn new(key: ServiceAccountKey, client: C) -> ServiceAccountAccess<C> {
        ServiceAccountAccess {
            client: client,
            key: key,
            cache: MemoryStorage::default(),
        }
    }

    fn request_token(&mut self, scopes: &Vec<&str>) -> result::Result<Token, Box<error::Error>> {
        let signed = try!(JWT::new(init_claims_from_key(&self.key, scopes))
            .sign(self.key.private_key.as_ref().unwrap()));

        let body = form_urlencoded::serialize(vec![("grant_type".to_string(),
                                                    GRANT_TYPE.to_string()),
                                                   ("assertion".to_string(), signed)]);

        let mut response = String::new();
        let mut result = try!(self.client
            .borrow_mut()
            .post(TOKEN_REQUEST_URL)
            .body(&body)
            .header(header::ContentType("application/x-www-form-urlencoded".parse().unwrap()))
            .send());

        try!(result.read_to_string(&mut response));

        let token: Result<TokenResponse, serde_json::error::Error> =
            serde_json::from_str(&response);

        match token {
            Err(e) => return Err(Box::new(e)),
            Ok(token) => {
                if token.access_token.is_none() || token.token_type.is_none() ||
                   token.expires_in.is_none() {
                    Err(Box::new(StringError::new("Token response lacks fields".to_string(),
                                                  Some(&format!("{:?}", token)))))
                } else {
                    Ok(token.to_oauth_token())
                }
            }
        }
    }
}

impl<C: BorrowMut<hyper::Client>> GetToken for ServiceAccountAccess<C> {
    fn token<'b, I, T>(&mut self, scopes: I) -> result::Result<Token, Box<error::Error>>
        where T: AsRef<str> + Ord + 'b,
              I: IntoIterator<Item = &'b T>
    {
        let (hash, scps) = hash_scopes(scopes);

        if let Some(token) = try!(self.cache.get(hash, &scps)) {
            if !token.expired() {
                return Ok(token);
            }
        }

        let token = try!(self.request_token(&scps));
        let _ = self.cache.set(hash, &scps, Some(token.clone()));

        Ok(token)
    }

    fn api_key(&mut self) -> Option<String> {
        None
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use helper::service_account_key_from_file;
    use hyper;
    use authenticator::GetToken;

    // This is a valid but deactivated key.
    const TEST_PRIVATE_KEY_PATH: &'static str = "examples/Sanguine-69411a0c0eea.json";

    // This only works if you have the right key file; if needed, download your own and test it
    // with that.
    // #[test]
    #[allow(dead_code)]
    fn test_service_account_e2e() {
        let key = service_account_key_from_file(&TEST_PRIVATE_KEY_PATH.to_string()).unwrap();
        let mut acc = ServiceAccountAccess::new(key, hyper::client::Client::new());
        println!("{:?}",
                 acc.token(vec![&"https://www.googleapis.com/auth/pubsub"]).unwrap());
    }

    #[test]
    fn test_jwt_initialize_claims() {
        let key = service_account_key_from_file(&TEST_PRIVATE_KEY_PATH.to_string()).unwrap();
        let scopes = vec!["scope1", "scope2", "scope3"];
        let claims = super::init_claims_from_key(&key, &scopes);

        assert_eq!(claims.iss,
                   "oauth2-public-test@sanguine-rhythm-105020.iam.gserviceaccount.com".to_string());
        assert_eq!(claims.scope, "scope1 scope2 scope3".to_string());
        assert_eq!(claims.aud,
                   "https://www.googleapis.com/oauth2/v4/token".to_string());
        assert!(claims.exp > 1000000000);
        assert!(claims.iat < claims.exp);
        assert_eq!(claims.exp - claims.iat, 3595);
    }

    #[test]
    fn test_jwt_sign() {
        let key = service_account_key_from_file(&TEST_PRIVATE_KEY_PATH.to_string()).unwrap();
        let scopes = vec!["scope1", "scope2", "scope3"];
        let claims = super::init_claims_from_key(&key, &scopes);
        let jwt = super::JWT::new(claims);
        let signature = jwt.sign(key.private_key.as_ref().unwrap());

        assert!(signature.is_ok());

        let signature = signature.unwrap();
        assert_eq!(signature.split(".").nth(0).unwrap(),
                   "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9");
    }
}
<commit_msg>fix(service_account): Use correct base64 encoding<commit_after>//! This module provides a token source (`GetToken`) that obtains tokens for service accounts.
//! Service accounts are usually used by software (i.e., non-human actors) to get access to
//! resources. Currently, this module only works with RS256 JWTs, and uses the Google request URL
//! by default (this can be amended by a patch).
//!
//! Resources:
//! - [Using OAuth 2.0 for Server to Server
//! Applications](https://developers.google.com/identity/protocols/OAuth2ServiceAccount)
//! - [JSON Web Tokens](https://jwt.io/)
//!
//! Copyright (c) 2016 Google Inc (lewinb@google.com).
//!

use std::borrow::BorrowMut;
use std::default::Default;
use std::error;
use std::io::{Read, Write};
use std::result;

use authenticator::GetToken;
use types::{StringError, Token};
use storage::{hash_scopes, MemoryStorage, TokenStorage};

use hyper::header;
use url::form_urlencoded;

use base64;
use chrono;
use hyper;
use openssl;
use serde_json;

const GRANT_TYPE: &'static str = "urn:ietf:params:oauth:grant-type:jwt-bearer";
const TOKEN_REQUEST_URL: &'static str = "https://www.googleapis.com/oauth2/v4/token";
const GOOGLE_RS256_HEAD: &'static str = "{\"alg\":\"RS256\",\"typ\":\"JWT\"}";

// Encodes s as Base64
fn encode_base64<T: AsRef<[u8]>>(s: T) -> String {
    base64::encode_mode(s.as_ref(), base64::Base64Mode::UrlSafe)
}

// Calculates the SHA256 hash.
fn hash_sha256(data: &[u8]) -> Vec<u8> {
    let mut hasher = openssl::crypto::hash::Hasher::new(openssl::crypto::hash::Type::SHA256);
    let _ = hasher.write(data);
    hasher.finish()
}

// Signs the hash with key.
fn sign_rsa(key: &openssl::crypto::rsa::RSA, hash: &[u8]) -> String {
    let signature = key.sign(openssl::crypto::hash::Type::SHA256, hash).unwrap();
    let b64_signature = encode_base64(signature);

    b64_signature
}

// Reads an RSA key from pem_pkcs8 (the format of the 'private_key' field in the service account
// key).
fn decode_rsa_key(pem_pkcs8: &str) -> Result<openssl::crypto::rsa::RSA, Box<error::Error>> {
    let private_key = pem_pkcs8.to_string().replace("\\n", "\n");
    let privkey = openssl::crypto::rsa::RSA::private_key_from_pem(&mut private_key.as_bytes());

    match privkey {
        Err(e) => Err(Box::new(e)),
        Ok(key) => Ok(key),
    }
}

/// JSON schema of secret service account key. You can obtain the key from
/// the Cloud Console at https://console.cloud.google.com/.
///
/// You can use `helpers::service_account_key_from_file()` as a quick way to read a JSON client
/// secret into a ServiceAccountKey.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ServiceAccountKey {
    #[serde(rename="type")]
    pub key_type: Option<String>,
    pub project_id: Option<String>,
    pub private_key_id: Option<String>,
    pub private_key: Option<String>,
    pub client_email: Option<String>,
    pub client_id: Option<String>,
    pub auth_uri: Option<String>,
    pub token_uri: Option<String>,
    pub auth_provier_x509_cert_url: Option<String>,
    pub client_x509_cert_url: Option<String>,
}

#[derive(Serialize, Debug)]
struct Claims {
    iss: String,
    aud: String,
    exp: i64,
    iat: i64,
    sub: Option<String>,
    scope: String,
}

struct JWT {
    header: String,
    claims: Claims,
}

impl JWT {
    fn new(claims: Claims) -> JWT {
        JWT {
            header: GOOGLE_RS256_HEAD.to_string(),
            claims: claims,
        }
    }
    // Encodes the first two parts (header and claims) to base64 and assembles them into a form
    // ready to be signed.
    fn encode_claims(&self) -> String {
        let mut head = encode_base64(&self.header);
        let claims = encode_base64(serde_json::to_string(&self.claims).unwrap());

        head.push_str(".");
        head.push_str(&claims);
        head
    }

    fn sign(&self, private_key: &str) -> Result<String, Box<error::Error>> {
        let mut jwt_head = self.encode_claims();

        let key = try!(decode_rsa_key(private_key));
        let hash = hash_sha256(&jwt_head.as_bytes());
        let signature = sign_rsa(&key, &hash);

        jwt_head.push_str(".");
        jwt_head.push_str(&signature);

        Ok(jwt_head)
    }
}

fn init_claims_from_key<'a, I, T>(key: &ServiceAccountKey, scopes: I) -> Claims
    where T: AsRef<str> + 'a,
          I: IntoIterator<Item = &'a T>
{
    let iat = chrono::UTC::now().timestamp();
    let expiry = iat + 3600 - 5; // Max validity is 1h.

    let mut scopes_string = scopes.into_iter().fold(String::new(), |mut acc, sc| {
        acc.push_str(sc.as_ref());
        acc.push_str(" ");
        acc
    });
    scopes_string.pop();

    Claims {
        iss: key.client_email.clone().unwrap(),
        aud: TOKEN_REQUEST_URL.to_string(),
        exp: expiry,
        iat: iat,
        sub: None,
        scope: scopes_string,
    }
}

/// See "Additional claims" at https://developers.google.com/identity/protocols/OAuth2ServiceAccount
#[allow(dead_code)]
fn set_sub_claim(mut claims: Claims, sub: String) -> Claims {
    claims.sub = Some(sub);
    claims
}

/// A token source (`GetToken`) yielding OAuth tokens for services that use ServiceAccount authorization.
/// This token source caches token and automatically renews expired ones.
pub struct ServiceAccountAccess<C> {
    client: C,
    key: ServiceAccountKey,
    cache: MemoryStorage,
}

/// This is the schema of the server's response.
#[derive(Deserialize, Debug)]
struct TokenResponse {
    access_token: Option<String>,
    token_type: Option<String>,
    expires_in: Option<i64>,
}

impl TokenResponse {
    fn to_oauth_token(self) -> Token {
        let expires_ts = chrono::UTC::now().timestamp() + self.expires_in.unwrap_or(0);

        Token {
            access_token: self.access_token.unwrap(),
            token_type: self.token_type.unwrap(),
            refresh_token: String::new(),
            expires_in: self.expires_in,
            expires_in_timestamp: Some(expires_ts),
        }
    }
}

impl<'a, C> ServiceAccountAccess<C>
    where C: BorrowMut<hyper::Client>
{
    /// Returns a new `ServiceAccountAccess` token source.
    #[allow(dead_code)]
    pub fn new(key: ServiceAccountKey, client: C) -> ServiceAccountAccess<C> {
        ServiceAccountAccess {
            client: client,
            key: key,
            cache: MemoryStorage::default(),
        }
    }

    fn request_token(&mut self, scopes: &Vec<&str>) -> result::Result<Token, Box<error::Error>> {
        let signed = try!(JWT::new(init_claims_from_key(&self.key, scopes))
            .sign(self.key.private_key.as_ref().unwrap()));

        let body = form_urlencoded::serialize(vec![("grant_type".to_string(),
                                                    GRANT_TYPE.to_string()),
                                                   ("assertion".to_string(), signed)]);

        let mut response = String::new();
        let mut result = try!(self.client
            .borrow_mut()
            .post(TOKEN_REQUEST_URL)
            .body(&body)
            .header(header::ContentType("application/x-www-form-urlencoded".parse().unwrap()))
            .send());

        try!(result.read_to_string(&mut response));

        let token: Result<TokenResponse, serde_json::error::Error> =
            serde_json::from_str(&response);

        match token {
            Err(e) => return Err(Box::new(e)),
            Ok(token) => {
                if token.access_token.is_none() || token.token_type.is_none() ||
                   token.expires_in.is_none() {
                    Err(Box::new(StringError::new("Token response lacks fields".to_string(),
                                                  Some(&format!("{:?}", token)))))
                } else {
                    Ok(token.to_oauth_token())
                }
            }
        }
    }
}

impl<C: BorrowMut<hyper::Client>> GetToken for ServiceAccountAccess<C> {
    fn token<'b, I, T>(&mut self, scopes: I) -> result::Result<Token, Box<error::Error>>
        where T: AsRef<str> + Ord + 'b,
              I: IntoIterator<Item = &'b T>
    {
        let (hash, scps) = hash_scopes(scopes);

        if let Some(token) = try!(self.cache.get(hash, &scps)) {
            if !token.expired() {
                return Ok(token);
            }
        }

        let token = try!(self.request_token(&scps));
        let _ = self.cache.set(hash, &scps, Some(token.clone()));

        Ok(token)
    }

    fn api_key(&mut self) -> Option<String> {
        None
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use helper::service_account_key_from_file;
    use hyper;
    use authenticator::GetToken;

    // This is a valid but deactivated key.
    const TEST_PRIVATE_KEY_PATH: &'static str = "examples/Sanguine-69411a0c0eea.json";

    // This only works if you have the right key file; if needed, download your own and test it
    // with that.
    // #[test]
    #[allow(dead_code)]
    fn test_service_account_e2e() {
        let key = service_account_key_from_file(&TEST_PRIVATE_KEY_PATH.to_string()).unwrap();
        let mut acc = ServiceAccountAccess::new(key, hyper::client::Client::new());
        println!("{:?}",
                 acc.token(vec![&"https://www.googleapis.com/auth/pubsub"]).unwrap());
    }

    #[test]
    fn test_jwt_initialize_claims() {
        let key = service_account_key_from_file(&TEST_PRIVATE_KEY_PATH.to_string()).unwrap();
        let scopes = vec!["scope1", "scope2", "scope3"];
        let claims = super::init_claims_from_key(&key, &scopes);

        assert_eq!(claims.iss,
                   "oauth2-public-test@sanguine-rhythm-105020.iam.gserviceaccount.com".to_string());
        assert_eq!(claims.scope, "scope1 scope2 scope3".to_string());
        assert_eq!(claims.aud,
                   "https://www.googleapis.com/oauth2/v4/token".to_string());
        assert!(claims.exp > 1000000000);
        assert!(claims.iat < claims.exp);
        assert_eq!(claims.exp - claims.iat, 3595);
    }

    #[test]
    fn test_jwt_sign() {
        let key = service_account_key_from_file(&TEST_PRIVATE_KEY_PATH.to_string()).unwrap();
        let scopes = vec!["scope1", "scope2", "scope3"];
        let claims = super::init_claims_from_key(&key, &scopes);
        let jwt = super::JWT::new(claims);
        let signature = jwt.sign(key.private_key.as_ref().unwrap());

        assert!(signature.is_ok());

        let signature = signature.unwrap();
        assert_eq!(signature.split(".").nth(0).unwrap(),
                   "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9");
    }
}
<|endoftext|>


<commit_before>use ::cssparser::{Parser, ParserInput, Token};
use ::glib::translate::*;
use ::libc;

use std::f64::consts::*;

use drawing_ctx;
use drawing_ctx::RsvgDrawingCtx;
use parsers::ParseError;
use error::*;

/* Keep this in sync with ../../rsvg-private.h:LengthUnit */
#[repr(C)]
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum LengthUnit {
    Default,
    Percent,
    FontEm,
    FontEx,
    Inch,
    RelativeLarger,
    RelativeSmaller
}

/* Keep this in sync with ../../rsvg-private.h:LengthDir */
#[repr(C)]
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum LengthDir {
    Horizontal,
    Vertical,
    Both
}

/* This is *not* an opaque struct; it is actually visible to the C code.  It is so
 * that the remaining C code can create RsvgLength values as part of existing
 * structures or objects, without allocations on the heap.
 */
/* Keep this in sync with ../../rsvg-private.h:RsvgLength */
#[repr(C)]
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct RsvgLength {
    pub length: f64,
    pub unit: LengthUnit,
    dir: LengthDir
}

impl Default for RsvgLength {
    fn default () -> RsvgLength {
        RsvgLength {
            length: 0.0,
            unit:   LengthUnit::Default,
            dir:    LengthDir::Both
        }
    }
}

const POINTS_PER_INCH: f64 = 72.0;
const CM_PER_INCH:     f64 = 2.54;
const MM_PER_INCH:     f64 = 25.4;
const PICA_PER_INCH:   f64 = 6.0;

fn compute_named_size (name: &str) -> f64 {
    let power: f64;

    match name {
        "xx-small" => { power = -3.0; },
        "x-small"  => { power = -2.0; },
        "small"    => { power = -1.0; },
        "medium"   => { power = 0.0; },
        "large"    => { power = 1.0; },
        "x-large"  => { power = 2.0; },
        "xx-large" => { power = 3.0; },
        _          => { unreachable! (); }
    }

    12.0 * 1.2f64.powf (power) / POINTS_PER_INCH
}

#[no_mangle]
pub extern fn rsvg_length_parse (string: *const libc::c_char, dir: LengthDir) -> RsvgLength {
    let my_string = unsafe { &String::from_glib_none (string) };

    // FIXME: this ignores errors; propagate them upstream
    RsvgLength::parse (my_string, dir).unwrap_or (RsvgLength::default ())
}

/* https://www.w3.org/TR/SVG/types.html#DataTypeLength
 * https://www.w3.org/TR/2008/REC-CSS2-20080411/syndata.html#length-units
 *
 * Lengths have units.  When they need to be need resolved to
 * units in the user's coordinate system, some unit types
 * need to know if they are horizontal/vertical/both.  For example,
 * a some_object.width="50%" is 50% with respect to the current
 * viewport's width.  In this case, the @dir argument is used
 * inside RsvgLength::normalize(), when it needs to know to what the
 * length refers.
 */

fn make_err () -> AttributeError {
    AttributeError::Parse (ParseError::new ("expected length: number(\"em\" | \"ex\" | \"px\" | \"in\" | \"cm\" | \"mm\" | \"pt\" | \"pc\" | \"%\")?"))
}

impl RsvgLength {
    pub fn parse (string: &str, dir: LengthDir) -> Result <RsvgLength, AttributeError> {
        let mut input = ParserInput::new (string);
        let mut parser = Parser::new (&mut input);

        let length = {
            let token = parser.next ()
                .map_err (|_| AttributeError::Parse (ParseError::new ("expected number and optional symbol, or number and percentage")))?;

            match *token {
                Token::Number { value, .. } => RsvgLength { length: value as f64,
                                                            unit:   LengthUnit::Default,
                                                            dir:    dir },

                Token::Percentage { unit_value, .. } => RsvgLength { length: unit_value as f64,
                                                                     unit:   LengthUnit::Percent,
                                                                     dir:    dir },

                Token::Dimension { value, ref unit, .. } => {
                    let value = value as f64;

                    match unit.as_ref () {
                        "em" => RsvgLength { length: value,
                                             unit:   LengthUnit::FontEm,
                                             dir:    dir },

                        "ex" => RsvgLength { length: value,
                                             unit:   LengthUnit::FontEx,
                                             dir:    dir },

                        "pt" => RsvgLength { length: value / POINTS_PER_INCH,
                                             unit:   LengthUnit::Inch,
                                             dir:    dir },

                        "in" => RsvgLength { length: value,
                                             unit:   LengthUnit::Inch,
                                             dir:    dir },

                        "cm" => RsvgLength { length: value / CM_PER_INCH,
                                             unit:   LengthUnit::Inch,
                                             dir:    dir },

                        "mm" => RsvgLength { length: value / MM_PER_INCH,
                                             unit:   LengthUnit::Inch,
                                             dir:    dir },

                        "pc" => RsvgLength { length: value / PICA_PER_INCH,
                                             unit:   LengthUnit::Inch,
                                             dir:    dir },

                        "px" => RsvgLength { length: value,
                                             unit:   LengthUnit::Default,
                                             dir:    dir },

                        _ => return Err (make_err ())
                    }
                },

                // FIXME: why are the following in Length?  They should be in FontSize
                Token::Ident (ref cow) => match cow.as_ref () {
                    "larger" => RsvgLength { length: 0.0,
                                             unit:   LengthUnit::RelativeLarger,
                                             dir:    dir },

                    "smaller" => RsvgLength { length: 0.0,
                                              unit:  LengthUnit::RelativeSmaller,
                                              dir:   dir },

                    "xx-small" |
                    "x-small" |
                    "small" |
                    "medium" |
                    "large" |
                    "x-large" |
                    "xx-large" => RsvgLength { length: compute_named_size (&*string),
                                               unit:   LengthUnit::Inch,
                                               dir:    dir },

                    _ => return Err (make_err ())
                },

                _ => return Err (make_err ())
            }
        };

        parser.expect_exhausted ().map_err (|_| make_err ())?;

        Ok (length)
    }

    pub fn normalize (&self, draw_ctx: *const RsvgDrawingCtx) -> f64 {
        match self.unit {
            LengthUnit::Default => {
                self.length
            },

            LengthUnit::Percent => {
                let (width, height) = drawing_ctx::get_view_box_size (draw_ctx);

                match self.dir {
                    LengthDir::Horizontal => { self.length * width },
                    LengthDir::Vertical   => { self.length * height },
                    LengthDir::Both       => { self.length * viewport_percentage (width, height) }
                }
            },

            LengthUnit::FontEm => {
                self.length * drawing_ctx::get_normalized_font_size (draw_ctx)
            },

            LengthUnit::FontEx => {
                self.length * drawing_ctx::get_normalized_font_size (draw_ctx) / 2.0
            },

            LengthUnit::Inch => {
                let (dpi_x, dpi_y) = drawing_ctx::get_dpi (draw_ctx);

                match self.dir {
                    LengthDir::Horizontal => { self.length * dpi_x },
                    LengthDir::Vertical   => { self.length * dpi_y },
                    LengthDir::Both       => { self.length * viewport_percentage (dpi_x, dpi_y) }
                }
            },

            // FIXME: these are pending: https://www.w3.org/TR/2008/REC-CSS2-20080411/fonts.html#propdef-font-size
            LengthUnit::RelativeLarger |
            LengthUnit::RelativeSmaller => { 0.0 }
        }
    }

    pub fn hand_normalize (&self,
                           pixels_per_inch: f64,
                           width_or_height: f64,
                           font_size: f64) -> f64 {
        match self.unit {
            LengthUnit::Default => { self.length },

            LengthUnit::Percent => { self.length * width_or_height },

            LengthUnit::FontEm => { self.length * font_size },

            LengthUnit::FontEx => { self.length * font_size / 2.0 },

            LengthUnit::Inch => { self.length * pixels_per_inch },

            _ => { 0.0 }
        }
    }
}

fn viewport_percentage (x: f64, y: f64) -> f64 {
    /* https://www.w3.org/TR/SVG/coords.html#Units
     *
     * "For any other length value expressed as a percentage of the viewport, the
     * percentage is calculated as the specified percentage of
     * sqrt((actual-width)**2 + (actual-height)**2))/sqrt(2)."
     */
    return (x * x + y * y).sqrt () / SQRT_2;
}

#[no_mangle]
pub extern fn rsvg_length_normalize (raw_length: *const RsvgLength, draw_ctx: *const RsvgDrawingCtx) -> f64 {
    assert! (!raw_length.is_null ());

    let length: &RsvgLength = unsafe { &*raw_length };

    length.normalize (draw_ctx)
}

#[no_mangle]
pub extern fn rsvg_length_hand_normalize (raw_length: *const RsvgLength,
                                          pixels_per_inch: f64,
                                          width_or_height: f64,
                                          font_size: f64) -> f64 {
    assert! (!raw_length.is_null ());

    let length: &RsvgLength = unsafe { &*raw_length };

    length.hand_normalize (pixels_per_inch, width_or_height, font_size)
}

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

    #[test]
    fn parses_default () {
        assert_eq! (RsvgLength::parse ("42", LengthDir::Horizontal),
                    Ok (RsvgLength {
                        length: 42.0,
                        unit:   LengthUnit::Default,
                        dir:    LengthDir::Horizontal}));

        assert_eq! (RsvgLength::parse ("-42px", LengthDir::Horizontal),
                    Ok (RsvgLength {
                        length: -42.0,
                        unit:   LengthUnit::Default,
                        dir:    LengthDir::Horizontal}));
    }

    #[test]
    fn parses_percent () {
        assert_eq! (RsvgLength::parse ("50.0%", LengthDir::Horizontal),
                    Ok (RsvgLength {
                        length: 0.5,
                        unit:   LengthUnit::Percent,
                        dir:    LengthDir::Horizontal}));
    }

    #[test]
    fn parses_font_em () {
        assert_eq! (RsvgLength::parse ("22.5em", LengthDir::Vertical),
                    Ok (RsvgLength {
                        length: 22.5,
                        unit:   LengthUnit::FontEm,
                        dir:    LengthDir::Vertical }));
    }

    #[test]
    fn parses_font_ex () {
        assert_eq! (RsvgLength::parse ("22.5ex", LengthDir::Vertical),
                    Ok (RsvgLength {
                        length: 22.5,
                        unit:   LengthUnit::FontEx,
                        dir:    LengthDir::Vertical }));
    }

    #[test]
    fn parses_physical_units () {
        assert_eq! (RsvgLength::parse ("72pt", LengthDir::Both),
                    Ok (RsvgLength {
                        length: 1.0,
                        unit:   LengthUnit::Inch,
                        dir:    LengthDir::Both }));

        assert_eq! (RsvgLength::parse ("-22.5in", LengthDir::Both),
                    Ok (RsvgLength {
                        length: -22.5,
                        unit:   LengthUnit::Inch,
                        dir:    LengthDir::Both }));

        assert_eq! (RsvgLength::parse ("-254cm", LengthDir::Both),
                    Ok (RsvgLength {
                        length: -100.0,
                        unit:   LengthUnit::Inch,
                        dir:    LengthDir::Both }));

        assert_eq! (RsvgLength::parse ("254mm", LengthDir::Both),
                    Ok (RsvgLength {
                        length: 10.0,
                        unit:   LengthUnit::Inch,
                        dir:    LengthDir::Both }));

        assert_eq! (RsvgLength::parse ("60pc", LengthDir::Both),
                    Ok (RsvgLength {
                        length: 10.0,
                        unit:   LengthUnit::Inch,
                        dir:    LengthDir::Both }));
    }

    #[test]
    fn parses_relative_larger () {
        assert_eq! (RsvgLength::parse ("larger", LengthDir::Vertical),
                    Ok (RsvgLength {
                        length: 0.0,
                        unit:   LengthUnit::RelativeLarger,
                        dir:    LengthDir::Vertical }));
    }

    #[test]
    fn parses_relative_smaller () {
        assert_eq! (RsvgLength::parse ("smaller", LengthDir::Vertical),
                    Ok (RsvgLength {
                        length: 0.0,
                        unit:   LengthUnit::RelativeSmaller,
                        dir:    LengthDir::Vertical }));
    }

    #[test]
    fn parses_named_sizes () {
        let names = vec![ "xx-small",
                           "x-small",
                           "small",
                           "medium",
                           "large",
                           "x-large",
                           "xx-large" ];

        let mut previous_value: Option<f64> = None;

        // Just ensure that the values are progressively larger; don't
        // enforce a particular sequence.

        for name in names {
            let length = RsvgLength::parse (name, LengthDir::Both).unwrap ();

            assert_eq! (length.unit, LengthUnit::Inch);
            assert_eq! (length.dir, LengthDir::Both);

            if let Some (v) = previous_value {
                assert! (length.length > v);
            } else {
                previous_value = Some (length.length);
            }

        }
    }

    #[test]
    fn empty_length_yields_error () {
        assert! (is_parse_error (&RsvgLength::parse ("", LengthDir::Both)));
    }

    #[test]
    fn invalid_unit_yields_error () {
        assert! (is_parse_error (&RsvgLength::parse ("8furlong", LengthDir::Both)));
    }

    #[test]
    fn invalid_font_size_yields_error () {
        // FIXME: this is intended to test the (absence of) the "larger" et al values.
        // Since they really be in FontSize, not RsvgLength, we should remember
        // to move this test to that type later.
        assert! (is_parse_error (&RsvgLength::parse ("furlong", LengthDir::Both)));
    }
}
<commit_msg>RsvgLength::check_nonnegative(): New method<commit_after>use ::cssparser::{Parser, ParserInput, Token};
use ::glib::translate::*;
use ::libc;

use std::f64::consts::*;

use drawing_ctx;
use drawing_ctx::RsvgDrawingCtx;
use parsers::ParseError;
use error::*;

/* Keep this in sync with ../../rsvg-private.h:LengthUnit */
#[repr(C)]
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum LengthUnit {
    Default,
    Percent,
    FontEm,
    FontEx,
    Inch,
    RelativeLarger,
    RelativeSmaller
}

/* Keep this in sync with ../../rsvg-private.h:LengthDir */
#[repr(C)]
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum LengthDir {
    Horizontal,
    Vertical,
    Both
}

/* This is *not* an opaque struct; it is actually visible to the C code.  It is so
 * that the remaining C code can create RsvgLength values as part of existing
 * structures or objects, without allocations on the heap.
 */
/* Keep this in sync with ../../rsvg-private.h:RsvgLength */
#[repr(C)]
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct RsvgLength {
    pub length: f64,
    pub unit: LengthUnit,
    dir: LengthDir
}

impl Default for RsvgLength {
    fn default () -> RsvgLength {
        RsvgLength {
            length: 0.0,
            unit:   LengthUnit::Default,
            dir:    LengthDir::Both
        }
    }
}

const POINTS_PER_INCH: f64 = 72.0;
const CM_PER_INCH:     f64 = 2.54;
const MM_PER_INCH:     f64 = 25.4;
const PICA_PER_INCH:   f64 = 6.0;

fn compute_named_size (name: &str) -> f64 {
    let power: f64;

    match name {
        "xx-small" => { power = -3.0; },
        "x-small"  => { power = -2.0; },
        "small"    => { power = -1.0; },
        "medium"   => { power = 0.0; },
        "large"    => { power = 1.0; },
        "x-large"  => { power = 2.0; },
        "xx-large" => { power = 3.0; },
        _          => { unreachable! (); }
    }

    12.0 * 1.2f64.powf (power) / POINTS_PER_INCH
}

#[no_mangle]
pub extern fn rsvg_length_parse (string: *const libc::c_char, dir: LengthDir) -> RsvgLength {
    let my_string = unsafe { &String::from_glib_none (string) };

    // FIXME: this ignores errors; propagate them upstream
    RsvgLength::parse (my_string, dir).unwrap_or (RsvgLength::default ())
}

/* https://www.w3.org/TR/SVG/types.html#DataTypeLength
 * https://www.w3.org/TR/2008/REC-CSS2-20080411/syndata.html#length-units
 *
 * Lengths have units.  When they need to be need resolved to
 * units in the user's coordinate system, some unit types
 * need to know if they are horizontal/vertical/both.  For example,
 * a some_object.width="50%" is 50% with respect to the current
 * viewport's width.  In this case, the @dir argument is used
 * inside RsvgLength::normalize(), when it needs to know to what the
 * length refers.
 */

fn make_err () -> AttributeError {
    AttributeError::Parse (ParseError::new ("expected length: number(\"em\" | \"ex\" | \"px\" | \"in\" | \"cm\" | \"mm\" | \"pt\" | \"pc\" | \"%\")?"))
}

impl RsvgLength {
    pub fn parse (string: &str, dir: LengthDir) -> Result <RsvgLength, AttributeError> {
        let mut input = ParserInput::new (string);
        let mut parser = Parser::new (&mut input);

        let length = {
            let token = parser.next ()
                .map_err (|_| AttributeError::Parse (ParseError::new ("expected number and optional symbol, or number and percentage")))?;

            match *token {
                Token::Number { value, .. } => RsvgLength { length: value as f64,
                                                            unit:   LengthUnit::Default,
                                                            dir:    dir },

                Token::Percentage { unit_value, .. } => RsvgLength { length: unit_value as f64,
                                                                     unit:   LengthUnit::Percent,
                                                                     dir:    dir },

                Token::Dimension { value, ref unit, .. } => {
                    let value = value as f64;

                    match unit.as_ref () {
                        "em" => RsvgLength { length: value,
                                             unit:   LengthUnit::FontEm,
                                             dir:    dir },

                        "ex" => RsvgLength { length: value,
                                             unit:   LengthUnit::FontEx,
                                             dir:    dir },

                        "pt" => RsvgLength { length: value / POINTS_PER_INCH,
                                             unit:   LengthUnit::Inch,
                                             dir:    dir },

                        "in" => RsvgLength { length: value,
                                             unit:   LengthUnit::Inch,
                                             dir:    dir },

                        "cm" => RsvgLength { length: value / CM_PER_INCH,
                                             unit:   LengthUnit::Inch,
                                             dir:    dir },

                        "mm" => RsvgLength { length: value / MM_PER_INCH,
                                             unit:   LengthUnit::Inch,
                                             dir:    dir },

                        "pc" => RsvgLength { length: value / PICA_PER_INCH,
                                             unit:   LengthUnit::Inch,
                                             dir:    dir },

                        "px" => RsvgLength { length: value,
                                             unit:   LengthUnit::Default,
                                             dir:    dir },

                        _ => return Err (make_err ())
                    }
                },

                // FIXME: why are the following in Length?  They should be in FontSize
                Token::Ident (ref cow) => match cow.as_ref () {
                    "larger" => RsvgLength { length: 0.0,
                                             unit:   LengthUnit::RelativeLarger,
                                             dir:    dir },

                    "smaller" => RsvgLength { length: 0.0,
                                              unit:  LengthUnit::RelativeSmaller,
                                              dir:   dir },

                    "xx-small" |
                    "x-small" |
                    "small" |
                    "medium" |
                    "large" |
                    "x-large" |
                    "xx-large" => RsvgLength { length: compute_named_size (&*string),
                                               unit:   LengthUnit::Inch,
                                               dir:    dir },

                    _ => return Err (make_err ())
                },

                _ => return Err (make_err ())
            }
        };

        parser.expect_exhausted ().map_err (|_| make_err ())?;

        Ok (length)
    }

    pub fn check_nonnegative (&self) -> Result <RsvgLength, AttributeError> {
        if self.length >= 0.0 {
            Ok (*self)
        } else {
            Err (AttributeError::Value ("value must be non-negative".to_string ()))
        }
    }

    pub fn normalize (&self, draw_ctx: *const RsvgDrawingCtx) -> f64 {
        match self.unit {
            LengthUnit::Default => {
                self.length
            },

            LengthUnit::Percent => {
                let (width, height) = drawing_ctx::get_view_box_size (draw_ctx);

                match self.dir {
                    LengthDir::Horizontal => { self.length * width },
                    LengthDir::Vertical   => { self.length * height },
                    LengthDir::Both       => { self.length * viewport_percentage (width, height) }
                }
            },

            LengthUnit::FontEm => {
                self.length * drawing_ctx::get_normalized_font_size (draw_ctx)
            },

            LengthUnit::FontEx => {
                self.length * drawing_ctx::get_normalized_font_size (draw_ctx) / 2.0
            },

            LengthUnit::Inch => {
                let (dpi_x, dpi_y) = drawing_ctx::get_dpi (draw_ctx);

                match self.dir {
                    LengthDir::Horizontal => { self.length * dpi_x },
                    LengthDir::Vertical   => { self.length * dpi_y },
                    LengthDir::Both       => { self.length * viewport_percentage (dpi_x, dpi_y) }
                }
            },

            // FIXME: these are pending: https://www.w3.org/TR/2008/REC-CSS2-20080411/fonts.html#propdef-font-size
            LengthUnit::RelativeLarger |
            LengthUnit::RelativeSmaller => { 0.0 }
        }
    }

    pub fn hand_normalize (&self,
                           pixels_per_inch: f64,
                           width_or_height: f64,
                           font_size: f64) -> f64 {
        match self.unit {
            LengthUnit::Default => { self.length },

            LengthUnit::Percent => { self.length * width_or_height },

            LengthUnit::FontEm => { self.length * font_size },

            LengthUnit::FontEx => { self.length * font_size / 2.0 },

            LengthUnit::Inch => { self.length * pixels_per_inch },

            _ => { 0.0 }
        }
    }
}

fn viewport_percentage (x: f64, y: f64) -> f64 {
    /* https://www.w3.org/TR/SVG/coords.html#Units
     *
     * "For any other length value expressed as a percentage of the viewport, the
     * percentage is calculated as the specified percentage of
     * sqrt((actual-width)**2 + (actual-height)**2))/sqrt(2)."
     */
    return (x * x + y * y).sqrt () / SQRT_2;
}

#[no_mangle]
pub extern fn rsvg_length_normalize (raw_length: *const RsvgLength, draw_ctx: *const RsvgDrawingCtx) -> f64 {
    assert! (!raw_length.is_null ());

    let length: &RsvgLength = unsafe { &*raw_length };

    length.normalize (draw_ctx)
}

#[no_mangle]
pub extern fn rsvg_length_hand_normalize (raw_length: *const RsvgLength,
                                          pixels_per_inch: f64,
                                          width_or_height: f64,
                                          font_size: f64) -> f64 {
    assert! (!raw_length.is_null ());

    let length: &RsvgLength = unsafe { &*raw_length };

    length.hand_normalize (pixels_per_inch, width_or_height, font_size)
}

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

    #[test]
    fn parses_default () {
        assert_eq! (RsvgLength::parse ("42", LengthDir::Horizontal),
                    Ok (RsvgLength {
                        length: 42.0,
                        unit:   LengthUnit::Default,
                        dir:    LengthDir::Horizontal}));

        assert_eq! (RsvgLength::parse ("-42px", LengthDir::Horizontal),
                    Ok (RsvgLength {
                        length: -42.0,
                        unit:   LengthUnit::Default,
                        dir:    LengthDir::Horizontal}));
    }

    #[test]
    fn parses_percent () {
        assert_eq! (RsvgLength::parse ("50.0%", LengthDir::Horizontal),
                    Ok (RsvgLength {
                        length: 0.5,
                        unit:   LengthUnit::Percent,
                        dir:    LengthDir::Horizontal}));
    }

    #[test]
    fn parses_font_em () {
        assert_eq! (RsvgLength::parse ("22.5em", LengthDir::Vertical),
                    Ok (RsvgLength {
                        length: 22.5,
                        unit:   LengthUnit::FontEm,
                        dir:    LengthDir::Vertical }));
    }

    #[test]
    fn parses_font_ex () {
        assert_eq! (RsvgLength::parse ("22.5ex", LengthDir::Vertical),
                    Ok (RsvgLength {
                        length: 22.5,
                        unit:   LengthUnit::FontEx,
                        dir:    LengthDir::Vertical }));
    }

    #[test]
    fn parses_physical_units () {
        assert_eq! (RsvgLength::parse ("72pt", LengthDir::Both),
                    Ok (RsvgLength {
                        length: 1.0,
                        unit:   LengthUnit::Inch,
                        dir:    LengthDir::Both }));

        assert_eq! (RsvgLength::parse ("-22.5in", LengthDir::Both),
                    Ok (RsvgLength {
                        length: -22.5,
                        unit:   LengthUnit::Inch,
                        dir:    LengthDir::Both }));

        assert_eq! (RsvgLength::parse ("-254cm", LengthDir::Both),
                    Ok (RsvgLength {
                        length: -100.0,
                        unit:   LengthUnit::Inch,
                        dir:    LengthDir::Both }));

        assert_eq! (RsvgLength::parse ("254mm", LengthDir::Both),
                    Ok (RsvgLength {
                        length: 10.0,
                        unit:   LengthUnit::Inch,
                        dir:    LengthDir::Both }));

        assert_eq! (RsvgLength::parse ("60pc", LengthDir::Both),
                    Ok (RsvgLength {
                        length: 10.0,
                        unit:   LengthUnit::Inch,
                        dir:    LengthDir::Both }));
    }

    #[test]
    fn parses_relative_larger () {
        assert_eq! (RsvgLength::parse ("larger", LengthDir::Vertical),
                    Ok (RsvgLength {
                        length: 0.0,
                        unit:   LengthUnit::RelativeLarger,
                        dir:    LengthDir::Vertical }));
    }

    #[test]
    fn parses_relative_smaller () {
        assert_eq! (RsvgLength::parse ("smaller", LengthDir::Vertical),
                    Ok (RsvgLength {
                        length: 0.0,
                        unit:   LengthUnit::RelativeSmaller,
                        dir:    LengthDir::Vertical }));
    }

    #[test]
    fn parses_named_sizes () {
        let names = vec![ "xx-small",
                           "x-small",
                           "small",
                           "medium",
                           "large",
                           "x-large",
                           "xx-large" ];

        let mut previous_value: Option<f64> = None;

        // Just ensure that the values are progressively larger; don't
        // enforce a particular sequence.

        for name in names {
            let length = RsvgLength::parse (name, LengthDir::Both).unwrap ();

            assert_eq! (length.unit, LengthUnit::Inch);
            assert_eq! (length.dir, LengthDir::Both);

            if let Some (v) = previous_value {
                assert! (length.length > v);
            } else {
                previous_value = Some (length.length);
            }

        }
    }

    #[test]
    fn empty_length_yields_error () {
        assert! (is_parse_error (&RsvgLength::parse ("", LengthDir::Both)));
    }

    #[test]
    fn invalid_unit_yields_error () {
        assert! (is_parse_error (&RsvgLength::parse ("8furlong", LengthDir::Both)));
    }

    #[test]
    fn invalid_font_size_yields_error () {
        // FIXME: this is intended to test the (absence of) the "larger" et al values.
        // Since they really be in FontSize, not RsvgLength, we should remember
        // to move this test to that type later.
        assert! (is_parse_error (&RsvgLength::parse ("furlong", LengthDir::Both)));
    }

    #[test]
    fn check_nonnegative_works () {
        assert! (RsvgLength::parse ("0", LengthDir::Both).and_then (|l| l.check_nonnegative ()).is_ok ());
        assert! (RsvgLength::parse ("-10", LengthDir::Both).and_then (|l| l.check_nonnegative ()).is_err ());
    }
}
<|endoftext|>


<commit_before>use crate::grammar::ast::{
    BinaryExpression, BinaryOp, BooleanLit, CharLit, Expression, Identifier, NumLit, Parens,
    StringLit, Underscore,
};
use crate::grammar::model::Fragment;

use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::character::complete::char;
use nom::combinator::map;
use nom::multi::fold_many0;
use nom::sequence::delimited;
use nom::sequence::pair;
use nom::IResult;

impl<'s> Expression<'s> {
    pub fn frag(&self) -> Fragment<'s> {
        match self {
            Expression::NumLit(inner) => inner.frag,
            Expression::CharLit(inner) => inner.frag,
            Expression::StringLit(inner) => inner.frag,
            Expression::BooleanLit(inner) => inner.frag,
            Expression::Identifier(inner) => inner.frag,
            Expression::Underscore(inner) => inner.frag,
            Expression::Parens(inner) => inner.frag,
            Expression::BinaryExpression(inner) => inner.frag,
        }
    }

    fn new_bin_expr(
        frag: Fragment<'s>,
        left: Expression<'s>,
        op: BinaryOp,
        right: Expression<'s>,
    ) -> Expression<'s> {
        Expression::BinaryExpression(BinaryExpression {
            frag,
            left: Box::new(left),
            op,
            right: Box::new(right),
        })
    }

    fn parse_parens(input: Fragment<'s>) -> IResult<Fragment<'s>, Expression> {
        map(
            delimited(char('('), Expression::parse, char(')')),
            |inner| {
                Expression::Parens(Parens {
                    frag: input,
                    inner: Box::new(inner),
                })
            },
        )(input)
    }

    fn parse_atom(input: Fragment<'s>) -> IResult<Fragment<'s>, Expression<'s>> {
        macro_rules! generalize_parser {
            ($parse_fcn:expr, $wrap_fcn:expr) => {
                |input| $parse_fcn(input).map(|(input, inner)| (input, $wrap_fcn(inner)))
            };
        }

        alt((
            generalize_parser!(BooleanLit::parse, Expression::BooleanLit),
            generalize_parser!(CharLit::parse, Expression::CharLit),
            generalize_parser!(Identifier::parse, Expression::Identifier),
            generalize_parser!(NumLit::parse, Expression::NumLit),
            generalize_parser!(StringLit::parse, Expression::StringLit),
            generalize_parser!(Underscore::parse, Expression::Underscore),
        ))(input)
    }

    fn parse_factor(input: Fragment<'s>) -> IResult<Fragment<'s>, Expression> {
        alt((Self::parse_parens, Self::parse_atom))(input)
    }

    fn parse_mul_prec(input: Fragment<'s>) -> IResult<Fragment<'s>, Self> {
        let (input, left) = Self::parse_factor(input)?;

        fold_many0(
            pair(
                alt((
                    map(char('*'), |_| BinaryOp::Mul),
                    map(char('/'), |_| BinaryOp::Div),
                    map(char('%'), |_| BinaryOp::Mod),
                )),
                Self::parse_factor,
            ),
            left,
            |left, (op, right)| Self::new_bin_expr(left.frag(), left, op, right),
        )(input)
    }

    fn parse_add_prec(input: Fragment<'s>) -> IResult<Fragment<'s>, Self> {
        let (input, left) = Self::parse_mul_prec(input)?;

        fold_many0(
            pair(
                alt((
                    map(char('+'), |_| BinaryOp::Add),
                    map(char('-'), |_| BinaryOp::Sub),
                )),
                Self::parse_mul_prec,
            ),
            left,
            |left, (op, right)| Self::new_bin_expr(left.frag(), left, op, right),
        )(input)
    }

    fn parse_and(input: Fragment<'s>) -> IResult<Fragment<'s>, Self> {
        let (input, left) = Self::parse_add_prec(input)?;

        fold_many0(
            pair(map(char('&'), |_| BinaryOp::And), Self::parse_add_prec),
            left,
            |left, (op, right)| Self::new_bin_expr(left.frag(), left, op, right),
        )(input)
    }

    fn parse_xor(input: Fragment<'s>) -> IResult<Fragment<'s>, Self> {
        let (input, left) = Self::parse_and(input)?;

        fold_many0(
            pair(map(char('^'), |_| BinaryOp::Xor), Self::parse_and),
            left,
            |left, (op, right)| Self::new_bin_expr(left.frag, left, op, right),
        )(input)
    }

    fn parse_or(input: Fragment<'s>) -> IResult<Fragment<'s>, Self> {
        let (input, left) = Self::parse_xor(input)?;

        fold_many0(
            pair(map(char('|'), |_| BinaryOp::Or), Self::parse_xor),
            left,
            |left, (op, right)| Self::new_bin_expr(left.frag, left, op, right),
        )(input)
    }

    fn parse_cmp(input: Fragment<'s>) -> IResult<Fragment<'s>, Self> {
        let (input, left) = Self::parse_or(input)?;

        fold_many0(
            pair(
                alt((
                    map(tag("<="), |_| BinaryOp::Le),
                    map(tag(">="), |_| BinaryOp::Ge),
                    map(tag("=="), |_| BinaryOp::Eq),
                    map(tag("!="), |_| BinaryOp::NotEq),
                    map(tag("<"), |_| BinaryOp::Lt),
                    map(tag(">"), |_| BinaryOp::Gt),
                )),
                Self::parse_or,
            ),
            left,
            |left, (op, right)| Self::new_bin_expr(left.frag, left, op, right),
        )(input)
    }

    fn parse_and_and(input: Fragment<'s>) -> IResult<Fragment<'s>, Self> {
        let (input, left) = Self::parse_cmp(input)?;

        fold_many0(
            pair(map(tag("&&"), |_| BinaryOp::AndAnd), Self::parse_cmp),
            left,
            |left, (op, right)| Self::new_bin_expr(left.frag, left, op, right),
        )(input)
    }

    fn parse_or_or(input: Fragment<'s>) -> IResult<Fragment<'s>, Self> {
        let (input, left) = Self::parse_and_and(input)?;

        fold_many0(
            pair(map(tag("||"), |_| BinaryOp::OrOr), Self::parse_and_and),
            left,
            |left, (op, right)| Self::new_bin_expr(left.frag, left, op, right),
        )(input)
    }

    fn parse_dot_dot(input: Fragment<'s>) -> IResult<Fragment<'s>, Self> {
        map(
            tuple((
                Self::parse_or_or,
                map(tag(".."), |_| BinaryOp::DotDot),
                Self::parse_or_or,
            )),
            |(left, op, right)| Self::new_bin_expr(input, left, op, right),
        )(input)
    }

    fn parse_eq(input: Fragment<'s>) -> IResult<Fragment<'s>, Self> {
        map(
            tuple((
                Self::parse_dot_dot,
                map(char('='), |_| BinaryOp::Eq),
                Self::parse_dot_dot,
            )),
            |(left, op, right)| Self::new_bin_expr(input, left, op, right),
        )(input)
    }

    /// Parse a binary expression
    pub fn parse(input: Fragment<'s>) -> IResult<Fragment<'s>, Self> {
        context("expected binary expression", Self::parse_eq)(input)
    }
}
<commit_msg>Fully switch to fold_many0<commit_after>use crate::grammar::ast::{
    BinaryExpression, BinaryOp, BooleanLit, CharLit, Expression, Identifier, NumLit, Parens,
    StringLit, Underscore,
};
use crate::grammar::model::Fragment;

use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::character::complete::char;
use nom::combinator::map;
use nom::combinator::opt;
use nom::error::context;
use nom::multi::fold_many0;
use nom::sequence::delimited;
use nom::sequence::pair;
use nom::IResult;

impl<'s> Expression<'s> {
    /// Get the expression's fragment
    pub fn frag(&self) -> Fragment<'s> {
        match self {
            Expression::NumLit(inner) => inner.frag,
            Expression::CharLit(inner) => inner.frag,
            Expression::StringLit(inner) => inner.frag,
            Expression::BooleanLit(inner) => inner.frag,
            Expression::Identifier(inner) => inner.frag,
            Expression::Underscore(inner) => inner.frag,
            Expression::Parens(inner) => inner.frag,
            Expression::BinaryExpression(inner) => inner.frag,
        }
    }

    fn new_bin_expr(
        frag: Fragment<'s>,
        left: Expression<'s>,
        op: BinaryOp,
        right: Expression<'s>,
    ) -> Expression<'s> {
        Expression::BinaryExpression(BinaryExpression {
            frag,
            left: Box::new(left),
            op,
            right: Box::new(right),
        })
    }

    fn parse_parens(input: Fragment<'s>) -> IResult<Fragment<'s>, Expression> {
        map(
            delimited(char('('), Expression::parse, char(')')),
            |inner| {
                Expression::Parens(Parens {
                    frag: input,
                    inner: Box::new(inner),
                })
            },
        )(input)
    }

    fn parse_atom(input: Fragment<'s>) -> IResult<Fragment<'s>, Expression<'s>> {
        macro_rules! generalize_parser {
            ($parse_fcn:expr, $wrap_fcn:expr) => {
                |input| $parse_fcn(input).map(|(input, inner)| (input, $wrap_fcn(inner)))
            };
        }

        alt((
            generalize_parser!(BooleanLit::parse, Expression::BooleanLit),
            generalize_parser!(CharLit::parse, Expression::CharLit),
            generalize_parser!(Identifier::parse, Expression::Identifier),
            generalize_parser!(NumLit::parse, Expression::NumLit),
            generalize_parser!(StringLit::parse, Expression::StringLit),
            generalize_parser!(Underscore::parse, Expression::Underscore),
        ))(input)
    }

    fn parse_factor(input: Fragment<'s>) -> IResult<Fragment<'s>, Expression> {
        alt((Self::parse_parens, Self::parse_atom))(input)
    }

    fn parse_mul_prec(input: Fragment<'s>) -> IResult<Fragment<'s>, Self> {
        let (input, left) = Self::parse_factor(input)?;

        fold_many0(
            pair(
                alt((
                    map(char('*'), |_| BinaryOp::Mul),
                    map(char('/'), |_| BinaryOp::Div),
                    map(char('%'), |_| BinaryOp::Mod),
                )),
                Self::parse_factor,
            ),
            left,
            |left, (op, right)| Self::new_bin_expr(left.frag(), left, op, right),
        )(input)
    }

    fn parse_add_prec(input: Fragment<'s>) -> IResult<Fragment<'s>, Self> {
        let (input, left) = Self::parse_mul_prec(input)?;

        fold_many0(
            pair(
                alt((
                    map(char('+'), |_| BinaryOp::Add),
                    map(char('-'), |_| BinaryOp::Sub),
                )),
                Self::parse_mul_prec,
            ),
            left,
            |left, (op, right)| Self::new_bin_expr(left.frag(), left, op, right),
        )(input)
    }

    fn parse_and(input: Fragment<'s>) -> IResult<Fragment<'s>, Self> {
        let (input, left) = Self::parse_add_prec(input)?;

        fold_many0(
            pair(map(char('&'), |_| BinaryOp::And), Self::parse_add_prec),
            left,
            |left, (op, right)| Self::new_bin_expr(left.frag(), left, op, right),
        )(input)
    }

    fn parse_xor(input: Fragment<'s>) -> IResult<Fragment<'s>, Self> {
        let (input, left) = Self::parse_and(input)?;

        fold_many0(
            pair(map(char('^'), |_| BinaryOp::Xor), Self::parse_and),
            left,
            |left, (op, right)| Self::new_bin_expr(left.frag(), left, op, right),
        )(input)
    }

    fn parse_or(input: Fragment<'s>) -> IResult<Fragment<'s>, Self> {
        let (input, left) = Self::parse_xor(input)?;

        fold_many0(
            pair(map(char('|'), |_| BinaryOp::Or), Self::parse_xor),
            left,
            |left, (op, right)| Self::new_bin_expr(left.frag(), left, op, right),
        )(input)
    }

    fn parse_cmp(input: Fragment<'s>) -> IResult<Fragment<'s>, Self> {
        let (input, left) = Self::parse_or(input)?;

        fold_many0(
            pair(
                alt((
                    map(tag("<="), |_| BinaryOp::Le),
                    map(tag(">="), |_| BinaryOp::Ge),
                    map(tag("=="), |_| BinaryOp::Eq),
                    map(tag("!="), |_| BinaryOp::NotEq),
                    map(tag("<"), |_| BinaryOp::Lt),
                    map(tag(">"), |_| BinaryOp::Gt),
                )),
                Self::parse_or,
            ),
            left,
            |left, (op, right)| Self::new_bin_expr(left.frag(), left, op, right),
        )(input)
    }

    fn parse_and_and(input: Fragment<'s>) -> IResult<Fragment<'s>, Self> {
        let (input, left) = Self::parse_cmp(input)?;

        fold_many0(
            pair(map(tag("&&"), |_| BinaryOp::AndAnd), Self::parse_cmp),
            left,
            |left, (op, right)| Self::new_bin_expr(left.frag(), left, op, right),
        )(input)
    }

    fn parse_or_or(input: Fragment<'s>) -> IResult<Fragment<'s>, Self> {
        let (input, left) = Self::parse_and_and(input)?;

        fold_many0(
            pair(map(tag("||"), |_| BinaryOp::OrOr), Self::parse_and_and),
            left,
            |left, (op, right)| Self::new_bin_expr(left.frag(), left, op, right),
        )(input)
    }

    fn parse_dot_dot(input: Fragment<'s>) -> IResult<Fragment<'s>, Self> {
        map(
            pair(
                Self::parse_or_or,
                opt(pair(
                    map(tag(".."), |_| BinaryOp::DotDot),
                    Self::parse_or_or,
                )),
            ),
            |(left, opt)| match opt {
                Some((op, right)) => Self::new_bin_expr(input, left, op, right),
                None => left,
            },
        )(input)
    }

    fn parse_eq(input: Fragment<'s>) -> IResult<Fragment<'s>, Self> {
        map(
            pair(
                Self::parse_dot_dot,
                opt(pair(map(char('='), |_| BinaryOp::Eq), Self::parse_dot_dot)),
            ),
            |(left, opt)| match opt {
                Some((op, right)) => Self::new_bin_expr(input, left, op, right),
                None => left,
            },
        )(input)
    }

    /// Parse a binary expression
    pub fn parse(input: Fragment<'s>) -> IResult<Fragment<'s>, Self> {
        context("expected expression", Self::parse_eq)(input)
    }
}
<|endoftext|>


<commit_before>//! Render Queue

#![crate_name = "render"]

extern crate cgmath;
extern crate ecs;
extern crate gfx;

use std::{iter, slice};

// normalized depth
pub type Depth = u16;

pub type Scalar = f32;

pub type Transform = cgmath::Decomposed<
    Scalar,
    cgmath::Quaternion<Scalar>,
    cgmath::Point3<Scalar>
>;

struct Camera {
    transform: Transform,
    frustum: cgmath::Frustum<Scalar>,
}

pub struct Object<L, T> {
    batch: gfx::batch::RefBatch<L, T>,
    data: T,
    bounding_sphere: cgmath::Sphere<Scalar>,
    depth: Depth,
}

pub fn order_opaque<L, T>(a: &Object<L, T>, b: &Object<L, T>) -> Ordering {
    (&a.batch, a.depth).cmp(&(&b.batch, b.depth))
}

type Index = uint;

pub struct Queue<L, T> {
    pub objects: Vec<Object<L, T>>,
    indices: Vec<Index>,
    context: gfx::batch::Context,
}

struct ObjectIter<'a, L: 'a, T: 'a> {
    objects: &'a [Object<L, T>],
    id_iter: slice::Items<'a, Index>,
}

impl<'a, L, T> Iterator<&'a Object<L, T>> for ObjectIter<'a, L, T> {
    fn next(&mut self) -> Option<&'a Object<L, T>> {
        self.id_iter.next().map(|&i| &self.objects[i])
    }
}

impl<L, T: gfx::shade::ShaderParam<L>> Queue<L, T> {
    fn is_updated(&self) -> bool {
        self.objects.len() == self.indices.len()
    }

    /// Synchronize indices to have the same length as objects
    pub fn update(&mut self) {
        let ni = self.indices.len();
        if self.objects.len() > ni {
            self.indices.grow_fn(self.objects.len() - ni, |i| (ni + i) as Index);
        }else
        if self.objects.len() < ni {
            self.indices.retain(|&i| (i as uint) < ni);
        }
        debug_assert!(self.is_updated());
    }

    /// Sort objects with the given order
    pub fn sort(&mut self, order: |&Object<L, T>, &Object<L, T>| -> Ordering) {
        debug_assert!(self.is_updated());
        let objects = self.objects.as_slice();
        self.indices.sort_by(|&ia, &ib|
            (order)(&objects[ia], &objects[ib])
        );
    }

    /// Iterate objects in the sorted order
    pub fn objects<'a>(&'a self) -> ObjectIter<'a, L, T>  {
        debug_assert!(self.is_updated());
        ObjectIter {
            objects: self.objects.as_slice(),
            id_iter: self.indices.iter(),
        }
    }

    // render everything to the given frame
    pub fn render<C: gfx::CommandBuffer>(&self, renderer: &mut gfx::Renderer<C>,
                  frame: &gfx::Frame) {
        debug_assert!(self.is_updated());
        for &i in self.indices.iter() {
            let ob = &self.objects[i];
            renderer.draw((&ob.batch, &ob.data, &self.context), frame);
        }
    }
}

pub struct View<L, T> {
    pub frame: gfx::Frame,
    pub camera: Camera,
    pub queue: Queue<L, T>,
}

impl<L, T> View<L, T> {
    fn add(&mut self, batch: gfx::batch::RefBatch<L, T>, bound: cgmath::Sphere<Scalar>) {
        let _depth = 0u;
        //self.queue.objects.push(Object {})
    }
}
<commit_msg>Implemented queue population<commit_after>//! Render Queue

#![crate_name = "render"]

extern crate cgmath;
extern crate ecs;
extern crate gfx;

use std::{iter, num, slice};
use cgmath::Transform;

pub type Depth = u32;

pub type Scalar = f32;

pub type Space = cgmath::Decomposed<
    Scalar,
    cgmath::Vector3<Scalar>,
    cgmath::Quaternion<Scalar>
>;

struct Camera {
    space: Space,
    frustum: cgmath::Frustum<Scalar>,
}

pub struct Object<L, T> {
    batch: gfx::batch::RefBatch<L, T>,
    parameters: T,
    depth: Depth,
}

pub fn order_opaque<L, T>(a: &Object<L, T>, b: &Object<L, T>) -> Ordering {
    (&a.batch, a.depth).cmp(&(&b.batch, b.depth))
}

type Index = uint;

pub struct Queue<L, T> {
    pub objects: Vec<Object<L, T>>,
    indices: Vec<Index>,
}

struct ObjectIter<'a, L: 'a, T: 'a> {
    objects: &'a [Object<L, T>],
    id_iter: slice::Items<'a, Index>,
}

impl<'a, L, T> Iterator<&'a Object<L, T>> for ObjectIter<'a, L, T> {
    fn next(&mut self) -> Option<&'a Object<L, T>> {
        self.id_iter.next().map(|&i| &self.objects[i])
    }
}

impl<L, T: gfx::shade::ShaderParam<L>> Queue<L, T> {
    fn is_updated(&self) -> bool {
        self.objects.len() == self.indices.len()
    }

    /// Synchronize indices to have the same length as objects
    pub fn update(&mut self) {
        let ni = self.indices.len();
        if self.objects.len() > ni {
            self.indices.grow_fn(self.objects.len() - ni, |i| (ni + i) as Index);
        }else
        if self.objects.len() < ni {
            self.indices.retain(|&i| (i as uint) < ni);
        }
        debug_assert!(self.is_updated());
    }

    /// Sort objects with the given order
    pub fn sort(&mut self, order: |&Object<L, T>, &Object<L, T>| -> Ordering) {
        self.update();
        let objects = self.objects.as_slice();
        self.indices.sort_by(|&ia, &ib|
            (order)(&objects[ia], &objects[ib])
        );
    }

    /// Iterate objects in the sorted order
    pub fn objects<'a>(&'a self) -> ObjectIter<'a, L, T>  {
        debug_assert!(self.is_updated());
        ObjectIter {
            objects: self.objects.as_slice(),
            id_iter: self.indices.iter(),
        }
    }
}

pub struct View<L, T> {
    pub frame: gfx::Frame,
    pub camera: Camera,
    queue: Queue<L, T>,
}

impl<L, T: gfx::shade::ShaderParam<L>> View<L, T> {
    pub fn clear(&mut self) {
        self.queue.objects.clear()
    }

    pub fn add(&mut self, batch: gfx::batch::RefBatch<L, T>, data: T,
           world: &Space, _bound: cgmath::Sphere<Scalar>) {
        let view = self.camera.space.concat(world);
        let frustum = &self.camera.frustum;
        let depth_max: Depth = num::Bounded::max_value();
        let depth = (depth_max as f32 * (view.disp.z - frustum.near.d) /
            (frustum.far.d - frustum.near.d)) as Depth;
        //TODO: cull based on `bound`
        self.queue.objects.push(Object {
            batch: batch,
            parameters: data,
            depth: depth,
        });
    }

    pub fn render<'a, C: gfx::CommandBuffer>(&'a mut self, renderer: &mut gfx::Renderer<C>,
                  context: &'a gfx::batch::Context) {
        self.queue.sort(order_opaque);
        for ob in self.queue.objects() {
            renderer.draw((&ob.batch, &ob.parameters, context), &self.frame);
        }
    }
}
<|endoftext|>


<commit_before>//   Copyright 2014 Colin Sherratt
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

use std::sync::Arc;
#[cfg(target_os="linux")]
use libc::c_void;

use glfw::{WindowEvent, Glfw, Context, RenderContext};
use glfw::WindowMode::{Windowed, FullScreen};
use glfw;
use nice_glfw;
use gl;

use collect::TrieMap;

use ovr;

use input;

pub type WindowId = uint;

struct WindowHandle {
    window: glfw::Window,
    forced_event: Option<WindowEvent>,
    receiver: Receiver<(f64, WindowEvent)>,
    title: String
}

pub struct IOManager {
    glfw: Glfw,
    ovr: Option<ovr::Ovr>,
    windows: TrieMap<WindowHandle>,
    window_id: uint
}

impl IOManager {
    pub fn new(glfw: glfw::Glfw) -> IOManager {
        IOManager {
            glfw: glfw,
            ovr: None,
            windows: TrieMap::new(),
            window_id: 0
        }
    }

    fn add_window(&mut self, window: glfw::Window, recv: Receiver<(f64, WindowEvent)>) -> InputHandle {
        let id = self.window_id;
        self.window_id += 1;

        let (w, h) = window.get_framebuffer_size();

        self.windows.insert(id, {
            WindowHandle {
                window: window,
                forced_event: Some(glfw::WindowEvent::FramebufferSize(w, h)),
                receiver: recv,
                title: "snowmew".to_string()
            }
        });

        InputHandle{ handle: id }
    }

    fn create_window_context(&self, width: u32, height: u32, name: &str, mode: glfw::WindowMode)
            -> Option<(glfw::Window, Receiver<(f64,WindowEvent)>)> {

        nice_glfw::WindowBuilder::new(&self.glfw)
            .try_modern_context_hints()
            .size(width, height)
            .title(name)
            .mode(mode)
            .create()
    }

    pub fn window(&mut self, size: (u32, u32)) -> Option<Window> {
        let (width, height) = size;
        let win_opt = self.create_window_context(width, height, "Snowmew", Windowed);
        let (mut window, events) = match win_opt {
            Some((window, events)) => (window, events),
            None => return None
        };

        window.make_current();
        gl::load_with(|name| self.glfw.get_proc_address_raw(name));
        self.glfw.set_swap_interval(1);
        glfw::make_context_current(None);

        window.set_all_polling(true);
        window.show();
        let version = window.get_context_version();
        let rc = window.render_context();
        let handle = self.add_window(window, events);

        Some(Window {
            handle: handle,
            render: rc,
            version: (version.major, version.minor),
            hmd: None,
            os_spec: WindowOSSpec::new(&self.glfw)
        })
    }

    pub fn primary(&mut self, size: (u32, u32)) -> Option<Window> {
        let screen = {
            self.glfw.with_primary_monitor(|display| {
                let display = display.unwrap();
                let (width, height) = size;
                self.create_window_context(width, height, "Snowmew FullScreen", FullScreen(display))
            })
        };

        match screen {
            None => None,
            Some((mut window, events)) => {
                window.make_current();
                gl::load_with(|name| self.glfw.get_proc_address_raw(name));
                self.glfw.set_swap_interval(1);
                glfw::make_context_current(None);

                window.set_all_polling(true);
                window.show();
                let version = window.get_context_version();
                let rc = window.render_context();
                let handle = self.add_window(window, events);

                Some(Window {
                    handle: handle,
                    render: rc,
                    version: (version.major, version.minor),
                    hmd: None,
                    os_spec: WindowOSSpec::new(&self.glfw)
                })
            }
        }

    }

    pub fn get_primary_resolution(&self) -> (u32, u32) {
        self.glfw.with_primary_monitor(|display| {
            let display = display.expect("Could not get primnay display");
            let vm = display.get_video_mode().expect("Could not get video mode");
            (vm.width, vm.height)
        })
    }

    pub fn get_primary_position(&self) -> (i32, i32) {
        self.glfw.with_primary_monitor(|display| {
            let display = display.expect("Could not get primnay display");
            display.get_pos()
        })
    }

    #[cfg(target_os="linux")]
    fn create_hmd_window(&self, hmd: &ovr::HmdDescription) -> Option<(glfw::Window, Receiver<(f64,WindowEvent)>)> {
        self.glfw.with_connected_monitors(|monitors| {
            for m in monitors.iter() {
                let (x, y) = m.get_pos();
                if x == hmd.window_position.x &&
                   y == hmd.window_position.y {
                    let (width, height) = (hmd.resolution.x, hmd.resolution.y);
                    let win_opt = self.create_window_context(width as u32, height as u32, "Snowmew FullScreen", FullScreen(m));
                    let (window, events) = match win_opt {
                        Some((window, events)) => (window, events),
                        None => return None
                    };

                    return Some((window, events));
                }
            }

            // fallback if we could not guess at the screen
            let (width, height) = (hmd.resolution.x, hmd.resolution.y);
            let win_opt = self.glfw.create_window(width as u32, height as u32, "Snowmew", Windowed);
            let (window, events) = match win_opt {
                Some((window, events)) => (window, events),
                None => return None
            };

            // move viewport
            let (dx, dy) = (hmd.window_position.x, hmd.window_position.y);
            window.set_pos(dx as i32, dy as i32);

            Some((window, events))

        })
    }

    #[cfg(target_os="macos")]
    fn create_hmd_window(&self, hmd: &ovr::HmdDescription) -> Option<(glfw::Window, Receiver<(f64,WindowEvent)>)> {
        self.glfw.with_connected_monitors(|monitors| {
            for m in monitors.iter() {
                if !m.get_name().as_slice().contains("Rift") {
                    continue;
                }

                let (width, height) = (hmd.resolution.x, hmd.resolution.y);
                let win_opt = self.create_window_context(width as u32, height as u32, "Snowmew FullScreen", FullScreen(m));
                let (window, events) = match win_opt {
                    Some((window, events)) => (window, events),
                    None => return None
                };

                return Some((window, events));
            }
            None
        })
    }

    pub fn hmd(&mut self) -> Option<Window> {
        if !self.setup_ovr() {
            return None;
        }

        let (window, events, rc, hmd) = {
            let hmd = match self.ovr.as_ref().unwrap().first_hmd() {
                Some(hmd) => hmd,
                None => return None
            };
            let hmdinfo = hmd.get_description();

            let (mut window, events) = match self.create_hmd_window(&hmdinfo) {
                Some((window, events)) => (window, events),
                None => return None
            };

            window.make_current();
            gl::load_with(|name| self.glfw.get_proc_address_raw(name));
            self.glfw.set_swap_interval(1);
            glfw::make_context_current(None);

            window.set_all_polling(true);
            window.show();

            let rc = window.render_context();
            (window, events, rc, hmd)
        };

        let version = window.get_context_version();
        let handle = self.add_window(window, events);

        Some(Window {
            handle: handle,
            render: rc,
            version: (version.major, version.minor),
            hmd: Some(Arc::new(hmd)),
            os_spec: WindowOSSpec::new(&self.glfw)
        })
    }

    pub fn wait(&mut self) {
        self.glfw.wait_events();
    }

    pub fn poll(&mut self) {
        self.glfw.poll_events();
    }

    pub fn next_event(&mut self, handle: &InputHandle) -> input::EventGroup {
        let evt = self.windows.get_mut(&handle.handle)
        .map(|rx| {
            // this is a hack to inject the correct size into the event buffer
            match rx.forced_event.take() {
                Some(evt) => return input::Event::from_glfw(evt),
                None => ()
            };
            for (_, evt) in glfw::flush_messages(&rx.receiver) {
                let evt = input::Event::from_glfw(evt);
                if evt != input::EventGroup::Nop {
                    return evt;
                }
            }
            input::EventGroup::Nop
        });

        match evt {
            Some(e) => e,
            _ => input::EventGroup::Nop
        }
    }

    pub fn should_close(&mut self, handle: &InputHandle) -> bool {
        let should_close = self.windows.get_mut(&handle.handle)
            .map(|win| win.window.should_close());

        if let Some(x) = should_close {
            x
        } else {
            true
        }
    }

    pub fn set_title(&mut self, handle: &InputHandle, title: String) {
        self.windows.get_mut(&handle.handle)
            .map(|win| {
                if title != win.title {
                    win.window.set_title(title.as_slice());
                    win.title = title.clone();
                }
            });
    }

    fn setup_ovr(&mut self) -> bool {
        if self.ovr.is_some() &&
           self.ovr.as_ref().unwrap().detect() > 0 {
            return true;
        }

        if self.ovr.is_none() {
            self.ovr = ovr::Ovr::init();
        }

        self.ovr.is_some() && self.ovr.as_ref().unwrap().detect() > 0
    }

    pub fn set_window_position(&mut self, window: &Window, pos: (i32, i32)) {
        let (w, h) = pos;
        match self.windows.get_mut(&window.handle.handle) {
            Some(win) => win.window.set_pos(w, h),
            None => ()
        }
    }

    pub fn get_framebuffer_size(&mut self, window: &Window) -> (i32, i32) {
        match self.windows.get_mut(&window.handle.handle) {
            Some(win) => win.window.get_framebuffer_size(),
            None => (0, 0)
        }
    }

    pub fn get_proc_address(&self, name: &str) -> *const ::libc::c_void {
        self.glfw.get_proc_address_raw(name)
    }
}

#[deriving(Clone, Copy)]
pub struct InputHandle {
    handle: uint,
}

#[cfg(target_os="macos")]
struct WindowOSSpec;

#[cfg(target_os="macos")]
impl WindowOSSpec {
    fn new(_: &Glfw) -> WindowOSSpec {WindowOSSpec}
}

#[cfg(target_os="linux")]
struct WindowOSSpec {
    display: *mut c_void
}

#[cfg(target_os="linux")]
impl WindowOSSpec {
    fn new(glfw: &Glfw) -> WindowOSSpec {
        WindowOSSpec {
            display: glfw.get_x11_display()
        }
    }
}

pub struct Window {
    handle: InputHandle,
    render: RenderContext,
    version: (uint, uint),
    hmd: Option<Arc<ovr::Hmd>>,
    os_spec: WindowOSSpec
}

impl Window {
    pub fn swap_buffers(&self) {
        self.render.swap_buffers()
    }

    pub fn make_context_current(&self) {
        self.render.make_current()
    }

    pub fn get_context_version(&self) -> (uint, uint) {
        self.version
    }

    pub fn handle(&self) -> InputHandle {
        self.handle.clone()
    }

    pub fn is_hmd(&self) -> bool {
        self.hmd.is_some()
    }

    pub fn get_hmd<'a>(&'a self) -> Arc<ovr::Hmd> {
        self.hmd.as_ref().expect("no hmd device found!").clone()
    }

    /// Wrapper for `glfwGetGLXContext`
    #[cfg(target_os="linux")]
    pub fn get_x11_display(&self) -> *mut c_void {
        self.os_spec.display
    }
}

#[deriving(Clone, RustcEncodable, RustcDecodable)]
pub struct IoState {
    pub render_size: (uint, uint),
    pub size: (uint, uint),
    pub position: (int, int),
    pub show_mouse: bool,
    pub mouse_over: bool,
    pub window_title: String
}

impl IoState {
    pub fn new() -> IoState {
        IoState {
            render_size: (1, 1),
            size: (1, 1),
            position: (0, 0),
            show_mouse: true,
            mouse_over: false,
            window_title: "snowmew".to_string()
        }
    }

    pub fn window_action(&mut self, win: input::WindowEvent) {
        match win {
            input::WindowEvent::Size(x, y) => {
                self.size = (x as uint, y as uint);
            }
            input::WindowEvent::Position(x, y) => {
                self.position = (x, y);
            }
            input::WindowEvent::MouseOver(mouse) => {
                self.mouse_over = mouse;
            }
        }
    }
}
<commit_msg>Make WindowOSSpec sync<commit_after>//   Copyright 2014 Colin Sherratt
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

use std::sync::Arc;
use std::kinds::Sync;
#[cfg(target_os="linux")]
use libc::c_void;

use glfw::{WindowEvent, Glfw, Context, RenderContext};
use glfw::WindowMode::{Windowed, FullScreen};
use glfw;
use nice_glfw;
use gl;

use collect::TrieMap;

use ovr;

use input;

pub type WindowId = uint;

struct WindowHandle {
    window: glfw::Window,
    forced_event: Option<WindowEvent>,
    receiver: Receiver<(f64, WindowEvent)>,
    title: String
}

pub struct IOManager {
    glfw: Glfw,
    ovr: Option<ovr::Ovr>,
    windows: TrieMap<WindowHandle>,
    window_id: uint
}

impl IOManager {
    pub fn new(glfw: glfw::Glfw) -> IOManager {
        IOManager {
            glfw: glfw,
            ovr: None,
            windows: TrieMap::new(),
            window_id: 0
        }
    }

    fn add_window(&mut self, window: glfw::Window, recv: Receiver<(f64, WindowEvent)>) -> InputHandle {
        let id = self.window_id;
        self.window_id += 1;

        let (w, h) = window.get_framebuffer_size();

        self.windows.insert(id, {
            WindowHandle {
                window: window,
                forced_event: Some(glfw::WindowEvent::FramebufferSize(w, h)),
                receiver: recv,
                title: "snowmew".to_string()
            }
        });

        InputHandle{ handle: id }
    }

    fn create_window_context(&self, width: u32, height: u32, name: &str, mode: glfw::WindowMode)
            -> Option<(glfw::Window, Receiver<(f64,WindowEvent)>)> {

        nice_glfw::WindowBuilder::new(&self.glfw)
            .try_modern_context_hints()
            .size(width, height)
            .title(name)
            .mode(mode)
            .create()
    }

    pub fn window(&mut self, size: (u32, u32)) -> Option<Window> {
        let (width, height) = size;
        let win_opt = self.create_window_context(width, height, "Snowmew", Windowed);
        let (mut window, events) = match win_opt {
            Some((window, events)) => (window, events),
            None => return None
        };

        window.make_current();
        gl::load_with(|name| self.glfw.get_proc_address_raw(name));
        self.glfw.set_swap_interval(1);
        glfw::make_context_current(None);

        window.set_all_polling(true);
        window.show();
        let version = window.get_context_version();
        let rc = window.render_context();
        let handle = self.add_window(window, events);

        Some(Window {
            handle: handle,
            render: rc,
            version: (version.major, version.minor),
            hmd: None,
            os_spec: WindowOSSpec::new(&self.glfw)
        })
    }

    pub fn primary(&mut self, size: (u32, u32)) -> Option<Window> {
        let screen = {
            self.glfw.with_primary_monitor(|display| {
                let display = display.unwrap();
                let (width, height) = size;
                self.create_window_context(width, height, "Snowmew FullScreen", FullScreen(display))
            })
        };

        match screen {
            None => None,
            Some((mut window, events)) => {
                window.make_current();
                gl::load_with(|name| self.glfw.get_proc_address_raw(name));
                self.glfw.set_swap_interval(1);
                glfw::make_context_current(None);

                window.set_all_polling(true);
                window.show();
                let version = window.get_context_version();
                let rc = window.render_context();
                let handle = self.add_window(window, events);

                Some(Window {
                    handle: handle,
                    render: rc,
                    version: (version.major, version.minor),
                    hmd: None,
                    os_spec: WindowOSSpec::new(&self.glfw)
                })
            }
        }

    }

    pub fn get_primary_resolution(&self) -> (u32, u32) {
        self.glfw.with_primary_monitor(|display| {
            let display = display.expect("Could not get primnay display");
            let vm = display.get_video_mode().expect("Could not get video mode");
            (vm.width, vm.height)
        })
    }

    pub fn get_primary_position(&self) -> (i32, i32) {
        self.glfw.with_primary_monitor(|display| {
            let display = display.expect("Could not get primnay display");
            display.get_pos()
        })
    }

    #[cfg(target_os="linux")]
    fn create_hmd_window(&self, hmd: &ovr::HmdDescription) -> Option<(glfw::Window, Receiver<(f64,WindowEvent)>)> {
        self.glfw.with_connected_monitors(|monitors| {
            for m in monitors.iter() {
                let (x, y) = m.get_pos();
                if x == hmd.window_position.x &&
                   y == hmd.window_position.y {
                    let (width, height) = (hmd.resolution.x, hmd.resolution.y);
                    let win_opt = self.create_window_context(width as u32, height as u32, "Snowmew FullScreen", FullScreen(m));
                    let (window, events) = match win_opt {
                        Some((window, events)) => (window, events),
                        None => return None
                    };

                    return Some((window, events));
                }
            }

            // fallback if we could not guess at the screen
            let (width, height) = (hmd.resolution.x, hmd.resolution.y);
            let win_opt = self.glfw.create_window(width as u32, height as u32, "Snowmew", Windowed);
            let (window, events) = match win_opt {
                Some((window, events)) => (window, events),
                None => return None
            };

            // move viewport
            let (dx, dy) = (hmd.window_position.x, hmd.window_position.y);
            window.set_pos(dx as i32, dy as i32);

            Some((window, events))

        })
    }

    #[cfg(target_os="macos")]
    fn create_hmd_window(&self, hmd: &ovr::HmdDescription) -> Option<(glfw::Window, Receiver<(f64,WindowEvent)>)> {
        self.glfw.with_connected_monitors(|monitors| {
            for m in monitors.iter() {
                if !m.get_name().as_slice().contains("Rift") {
                    continue;
                }

                let (width, height) = (hmd.resolution.x, hmd.resolution.y);
                let win_opt = self.create_window_context(width as u32, height as u32, "Snowmew FullScreen", FullScreen(m));
                let (window, events) = match win_opt {
                    Some((window, events)) => (window, events),
                    None => return None
                };

                return Some((window, events));
            }
            None
        })
    }

    pub fn hmd(&mut self) -> Option<Window> {
        if !self.setup_ovr() {
            return None;
        }

        let (window, events, rc, hmd) = {
            let hmd = match self.ovr.as_ref().unwrap().first_hmd() {
                Some(hmd) => hmd,
                None => return None
            };
            let hmdinfo = hmd.get_description();

            let (mut window, events) = match self.create_hmd_window(&hmdinfo) {
                Some((window, events)) => (window, events),
                None => return None
            };

            window.make_current();
            gl::load_with(|name| self.glfw.get_proc_address_raw(name));
            self.glfw.set_swap_interval(1);
            glfw::make_context_current(None);

            window.set_all_polling(true);
            window.show();

            let rc = window.render_context();
            (window, events, rc, hmd)
        };

        let version = window.get_context_version();
        let handle = self.add_window(window, events);

        Some(Window {
            handle: handle,
            render: rc,
            version: (version.major, version.minor),
            hmd: Some(Arc::new(hmd)),
            os_spec: WindowOSSpec::new(&self.glfw)
        })
    }

    pub fn wait(&mut self) {
        self.glfw.wait_events();
    }

    pub fn poll(&mut self) {
        self.glfw.poll_events();
    }

    pub fn next_event(&mut self, handle: &InputHandle) -> input::EventGroup {
        let evt = self.windows.get_mut(&handle.handle)
        .map(|rx| {
            // this is a hack to inject the correct size into the event buffer
            match rx.forced_event.take() {
                Some(evt) => return input::Event::from_glfw(evt),
                None => ()
            };
            for (_, evt) in glfw::flush_messages(&rx.receiver) {
                let evt = input::Event::from_glfw(evt);
                if evt != input::EventGroup::Nop {
                    return evt;
                }
            }
            input::EventGroup::Nop
        });

        match evt {
            Some(e) => e,
            _ => input::EventGroup::Nop
        }
    }

    pub fn should_close(&mut self, handle: &InputHandle) -> bool {
        let should_close = self.windows.get_mut(&handle.handle)
            .map(|win| win.window.should_close());

        if let Some(x) = should_close {
            x
        } else {
            true
        }
    }

    pub fn set_title(&mut self, handle: &InputHandle, title: String) {
        self.windows.get_mut(&handle.handle)
            .map(|win| {
                if title != win.title {
                    win.window.set_title(title.as_slice());
                    win.title = title.clone();
                }
            });
    }

    fn setup_ovr(&mut self) -> bool {
        if self.ovr.is_some() &&
           self.ovr.as_ref().unwrap().detect() > 0 {
            return true;
        }

        if self.ovr.is_none() {
            self.ovr = ovr::Ovr::init();
        }

        self.ovr.is_some() && self.ovr.as_ref().unwrap().detect() > 0
    }

    pub fn set_window_position(&mut self, window: &Window, pos: (i32, i32)) {
        let (w, h) = pos;
        match self.windows.get_mut(&window.handle.handle) {
            Some(win) => win.window.set_pos(w, h),
            None => ()
        }
    }

    pub fn get_framebuffer_size(&mut self, window: &Window) -> (i32, i32) {
        match self.windows.get_mut(&window.handle.handle) {
            Some(win) => win.window.get_framebuffer_size(),
            None => (0, 0)
        }
    }

    pub fn get_proc_address(&self, name: &str) -> *const ::libc::c_void {
        self.glfw.get_proc_address_raw(name)
    }
}

#[deriving(Clone, Copy)]
pub struct InputHandle {
    handle: uint,
}

#[cfg(target_os="macos")]
struct WindowOSSpec;

#[cfg(target_os="macos")]
impl WindowOSSpec {
    fn new(_: &Glfw) -> WindowOSSpec {WindowOSSpec}
}

#[cfg(target_os="linux")]
struct WindowOSSpec {
    display: *mut c_void
}

#[cfg(target_os="linux")]
impl WindowOSSpec {
    fn new(glfw: &Glfw) -> WindowOSSpec {
        WindowOSSpec {
            display: glfw.get_x11_display()
        }
    }
}

unsafe impl Send for WindowOSSpec {}

pub struct Window {
    handle: InputHandle,
    render: RenderContext,
    version: (uint, uint),
    hmd: Option<Arc<ovr::Hmd>>,
    os_spec: WindowOSSpec
}

impl Window {
    pub fn swap_buffers(&self) {
        self.render.swap_buffers()
    }

    pub fn make_context_current(&self) {
        self.render.make_current()
    }

    pub fn get_context_version(&self) -> (uint, uint) {
        self.version
    }

    pub fn handle(&self) -> InputHandle {
        self.handle.clone()
    }

    pub fn is_hmd(&self) -> bool {
        self.hmd.is_some()
    }

    pub fn get_hmd<'a>(&'a self) -> Arc<ovr::Hmd> {
        self.hmd.as_ref().expect("no hmd device found!").clone()
    }

    /// Wrapper for `glfwGetGLXContext`
    #[cfg(target_os="linux")]
    pub fn get_x11_display(&self) -> *mut c_void {
        self.os_spec.display
    }
}

#[deriving(Clone, RustcEncodable, RustcDecodable)]
pub struct IoState {
    pub render_size: (uint, uint),
    pub size: (uint, uint),
    pub position: (int, int),
    pub show_mouse: bool,
    pub mouse_over: bool,
    pub window_title: String
}

impl IoState {
    pub fn new() -> IoState {
        IoState {
            render_size: (1, 1),
            size: (1, 1),
            position: (0, 0),
            show_mouse: true,
            mouse_over: false,
            window_title: "snowmew".to_string()
        }
    }

    pub fn window_action(&mut self, win: input::WindowEvent) {
        match win {
            input::WindowEvent::Size(x, y) => {
                self.size = (x as uint, y as uint);
            }
            input::WindowEvent::Position(x, y) => {
                self.position = (x, y);
            }
            input::WindowEvent::MouseOver(mouse) => {
                self.mouse_over = mouse;
            }
        }
    }
}
<|endoftext|>