<commit_before>use crate::agents::FetchUrlCached;
use crate::bridges::{complete_auth, BridgeData};
use crate::crypto::{self, SigningAlgorithm};
use crate::email_address::EmailAddress;
use crate::error::BrokerError;
use crate::utils::{http::ResponseExt, unix_timestamp};
use crate::web::{empty_response, json_response, Context, HandlerResult};
use crate::webfinger::{Link, Relation};
use crate::{metrics, validation};
use http::StatusCode;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use url::Url;

/// The origin of the Google identity provider.
pub const GOOGLE_IDP_ORIGIN: &str = "https://accounts.google.com";
/// The leeway allowed when verifying iat & exp claims, in seconds.
pub const LEEWAY: u64 = 30;

/// Data we store in the session.
#[derive(Clone, Serialize, Deserialize)]
pub struct OidcBridgeData {
    pub link: Link,
    pub origin: String,
    pub client_id: String,
    pub nonce: String,
    pub signing_alg: SigningAlgorithm,
}

/// OpenID Connect configuration document.
#[derive(Deserialize)]
struct ProviderConfig {
    authorization_endpoint: Url,
    jwks_uri: Url,
    #[serde(default = "default_response_modes_supported")]
    response_modes_supported: Vec<String>,
    #[serde(default = "default_id_token_signing_alg_values_supported")]
    id_token_signing_alg_values_supported: Vec<String>,
    // NOTE: This field is non-standard.
    #[serde(default)]
    accepts_id_token_signing_alg_query_param: bool,
}

fn default_response_modes_supported() -> Vec<String> {
    vec!["fragment".to_owned()]
}

fn default_id_token_signing_alg_values_supported() -> Vec<String> {
    vec!["RS256".to_owned()]
}

/// OpenID Connect key set document.
#[derive(Deserialize)]
struct ProviderKeys {
    #[serde(default)]
    keys: Vec<ProviderKey>,
}

#[derive(Deserialize)]
pub struct ProviderKey {
    #[serde(default)]
    pub alg: String,
    #[serde(default)]
    pub crv: String,
    #[serde(rename = "use")]
    #[serde(default)]
    pub use_: String,
    #[serde(default)]
    pub kid: String,
    #[serde(default)]
    pub n: String,
    #[serde(default)]
    pub e: String,
    #[serde(default)]
    pub x: String,
}

/// Provide authentication using OpenID Connect.
///
/// Redirect the user agent to the provider authorization endpoint, which we discover by reading
/// the providers configuration document. Included in the request is a nonce which we can later use
/// to definitively match the callback to this request.
///
/// This function handles both Portier providers, which works without registration, as well as
/// the Google provider, for which we have a preregistered `client_id`.
pub async fn auth(ctx: &mut Context, email_addr: &EmailAddress, link: &Link) -> HandlerResult {
    // Generate a nonce for the provider.
    let provider_nonce = crypto::nonce(&ctx.app.rng).await;

    // Determine the parameters to use, based on the webfinger link.
    let provider_origin = validation::parse_oidc_href(&link.href).ok_or_else(|| {
        BrokerError::Provider(format!("invalid href (validation failed): {}", link.href))
    })?;
    let mut bridge_data = match link.rel {
        Relation::Portier => {
            metrics::AUTH_OIDC_REQUESTS_PORTIER.inc();
            #[cfg(not(feature = "insecure"))]
            {
                if link.href.scheme() != "https" {
                    return Err(BrokerError::Provider(format!(
                        "invalid href (not HTTPS): {}",
                        link.href
                    )));
                }
            }
            OidcBridgeData {
                link: link.clone(),
                origin: provider_origin,
                client_id: ctx.app.public_url.clone(),
                nonce: provider_nonce,
                signing_alg: SigningAlgorithm::Rs256,
            }
        }
        // Delegate to the OpenID Connect bridge for Google, if configured.
        Relation::Google => {
            metrics::AUTH_OIDC_REQUESTS_GOOGLE.inc();
            let client_id = ctx
                .app
                .google_client_id
                .as_ref()
                .ok_or(BrokerError::ProviderCancelled)?;
            if provider_origin != GOOGLE_IDP_ORIGIN {
                return Err(BrokerError::Provider(format!(
                    "invalid href: Google provider only supports {}",
                    GOOGLE_IDP_ORIGIN
                )));
            }
            OidcBridgeData {
                link: link.clone(),
                origin: provider_origin,
                client_id: client_id.clone(),
                nonce: provider_nonce,
                signing_alg: SigningAlgorithm::Rs256,
            }
        }
    };

    // Retrieve the provider's configuration.
    let (
        ProviderConfig {
            authorization_endpoint: mut auth_url,
            response_modes_supported: response_modes,
            id_token_signing_alg_values_supported: signing_algs,
            accepts_id_token_signing_alg_query_param: accepts_signing_alg,
            ..
        },
        key_set,
    ) = fetch_config(ctx, &bridge_data).await?;

    {
        // Create the URL to redirect to.
        let mut query = auth_url.query_pairs_mut();
        query.extend_pairs(&[
            ("login_hint", email_addr.as_str()),
            ("scope", "openid email"),
            ("nonce", &bridge_data.nonce),
            ("state", &ctx.session_id),
            ("response_type", "id_token"),
            ("client_id", &bridge_data.client_id),
            ("redirect_uri", &format!("{}/callback", &ctx.app.public_url)),
        ]);

        // Prefer `form_post` response mode, otherwise use `fragment`.
        if response_modes.iter().any(|mode| mode == "form_post") {
            query.append_pair("response_mode", "form_post");
        } else if !response_modes.iter().any(|mode| mode == "fragment") {
            return Err(BrokerError::Provider(format!(
                "neither form_post nor fragment response modes supported by {}'s IdP ",
                email_addr.domain()
            )));
        }

        // NOTE: This query parameter is non-standard.
        // Prefer `Ed25519`, but there is no standard way to select it, so we introduce extra
        // fields, and take care we don't accidentally break the protocol. On top of this,
        // `alg=EdDSA` could also mean Ed448, so we inspect the key set to make sure there are only
        // Ed25519 keys among EdDSA keys.
        if accepts_signing_alg && signing_algs.iter().any(|s| s.as_str() == "EdDSA") {
            let mut found_ed25519 = false;
            let mut found_other_ed_dsa = false;
            for key in &key_set.keys {
                if key.use_ == "sig" && key.alg == "EdDSA" {
                    if key.crv == "Ed25519" {
                        found_ed25519 = true;
                    } else {
                        found_other_ed_dsa = true;
                        break;
                    }
                }
            }
            if found_ed25519 && !found_other_ed_dsa {
                bridge_data.signing_alg = SigningAlgorithm::EdDsa;
                query.append_pair("id_token_signing_alg", "EdDSA");
            }
        }

        query.finish();
    }

    // Save session data, committing the session to this provider.
    // If this fails, another auth mechanism has already claimed the session.
    if !ctx.save_session(BridgeData::Oidc(bridge_data)).await? {
        return Err(BrokerError::ProviderCancelled);
    }

    if ctx.want_json {
        Ok(json_response(&json!({
            "result": "redirect_to_provider",
            "url": auth_url.as_str(),
        })))
    } else {
        let mut res = empty_response(StatusCode::SEE_OTHER);
        res.header(hyper::header::LOCATION, auth_url.as_str());
        Ok(res)
    }
}

/// Request handler for OpenID Connect callbacks.
///
/// Match the returned email address and nonce against our session data, then extract the identity
/// token returned by the provider and verify it. Return an identity token for the relying party if
/// successful, or an error message otherwise.
pub async fn callback(ctx: &mut Context) -> HandlerResult {
    let mut params = ctx.form_params();
    let session_id = try_get_provider_param!(params, "state");
    let id_token = try_get_provider_param!(params, "id_token");

    #[allow(clippy::match_wildcard_for_single_variants)]
    let bridge_data = match ctx.load_session(&session_id).await? {
        BridgeData::Oidc(bridge_data) => bridge_data,
        _ => return Err(BrokerError::ProviderInput("invalid session".to_owned())),
    };

    // Retrieve the provider's configuration.
    let (_, key_set) = fetch_config(ctx, &bridge_data).await?;

    // Verify the signature.
    let jwt_payload = crypto::verify_jws(&id_token, &key_set.keys, bridge_data.signing_alg)
        .map_err(|err| {
            BrokerError::ProviderInput(format!(
                "could not verify the token received from {}: {}",
                bridge_data.origin, err
            ))
        })?;

    let data = ctx.session_data.as_ref().expect("session vanished");

    // Extract the token claims.
    let descr = format!("{}'s token payload", data.email_addr.domain());
    let iss = try_get_token_field!(jwt_payload, "iss", descr);
    let aud = try_get_token_field!(jwt_payload, "aud", descr);
    let email = try_get_token_field!(jwt_payload, "email", descr);
    let iat = try_get_token_field!(jwt_payload, "iat", Value::as_u64, descr);
    let exp = try_get_token_field!(jwt_payload, "exp", Value::as_u64, descr);
    let nonce = try_get_token_field!(jwt_payload, "nonce", descr);

    // Verify the token claims.
    check_token_field!(iss == bridge_data.origin, "iss", descr);
    check_token_field!(aud == bridge_data.client_id, "aud", descr);
    check_token_field!(nonce == bridge_data.nonce, "nonce", descr);

    let now = unix_timestamp();
    let exp = exp.checked_add(LEEWAY).unwrap_or(u64::min_value());
    let iat = iat.checked_sub(LEEWAY).unwrap_or(u64::max_value());
    check_token_field!(now < exp, "exp", descr);
    check_token_field!(iat <= now, "iat", descr);

    match bridge_data.link.rel {
        Relation::Portier => {
            // `email` should match the normalized email, as we sent it to the IdP.
            check_token_field!(email == data.email_addr.as_str(), "email", descr);
            // `email_original` should not be necessary for Broker -> IdP, but verify it any way.
            if let Some(email_original) = jwt_payload.get("email_original").and_then(Value::as_str)
            {
                check_token_field!(
                    email_original == data.email_addr.as_str(),
                    "email_original",
                    descr
                );
            }
        }
        Relation::Google => {
            // Check `email` after additional Google-specific normalization.
            let email_addr: EmailAddress = email.parse().map_err(|err| {
                BrokerError::ProviderInput(format!("failed to parse email in {}: {}", descr, err))
            })?;
            let google_email_addr = email_addr.normalize_google();
            let expected = data.email_addr.normalize_google();
            check_token_field!(google_email_addr == expected, "email", descr);
        }
    }

    // Everything is okay. Build a new identity token and send it to the relying party.
    metrics::AUTH_OIDC_COMPLETED.inc();
    complete_auth(ctx).await
}

// Retrieve and verify the provider's configuration.
async fn fetch_config(
    ctx: &mut Context,
    bridge_data: &OidcBridgeData,
) -> Result<(ProviderConfig, ProviderKeys), BrokerError> {
    let config_url = format!("{}/.well-known/openid-configuration", bridge_data.origin)
        .parse()
        .expect("could not build the OpenID Connect configuration URL");

    let provider_config = ctx
        .app
        .store
        .send(FetchUrlCached {
            url: config_url,
            metric: &*metrics::AUTH_OIDC_FETCH_CONFIG_DURATION,
        })
        .await
        .map_err(|e| {
            BrokerError::Provider(format!(
                "could not fetch {}'s configuration: {}",
                bridge_data.origin, e
            ))
        })?;
    let provider_config: ProviderConfig = serde_json::from_str(&provider_config).map_err(|e| {
        BrokerError::Provider(format!(
            "could not parse {}'s configuration: {}",
            bridge_data.origin, e
        ))
    })?;

    #[cfg(not(feature = "insecure"))]
    {
        if provider_config.authorization_endpoint.scheme() != "https" {
            return Err(BrokerError::Provider(format!(
                "{}'s authorization_endpoint is not HTTPS",
                bridge_data.origin
            )));
        }
        if provider_config.jwks_uri.scheme() != "https" {
            return Err(BrokerError::Provider(format!(
                "{}'s jwks_uri is not HTTPS",
                bridge_data.origin
            )));
        }
    }

    // Grab the keys from the provider.
    let key_set = ctx
        .app
        .store
        .send(FetchUrlCached {
            url: provider_config.jwks_uri.clone(),
            metric: &*metrics::AUTH_OIDC_FETCH_JWKS_DURATION,
        })
        .await
        .map_err(|e| {
            BrokerError::Provider(format!(
                "could not fetch {}'s keys: {}",
                bridge_data.origin, e
            ))
        })?;
    let key_set: ProviderKeys = serde_json::from_str(&key_set).map_err(|e| {
        BrokerError::Provider(format!(
            "could not parse{}'s keys: {}",
            bridge_data.origin, e
        ))
    })?;

    Ok((provider_config, key_set))
}
<commit_msg>Accept floating point for JWT timestamps<commit_after>use crate::agents::FetchUrlCached;
use crate::bridges::{complete_auth, BridgeData};
use crate::crypto::{self, SigningAlgorithm};
use crate::email_address::EmailAddress;
use crate::error::BrokerError;
use crate::utils::{http::ResponseExt, unix_timestamp};
use crate::web::{empty_response, json_response, Context, HandlerResult};
use crate::webfinger::{Link, Relation};
use crate::{metrics, validation};
use http::StatusCode;
use serde::{Deserialize, Serialize};
use serde_json::{json, Value};
use url::Url;

/// The origin of the Google identity provider.
pub const GOOGLE_IDP_ORIGIN: &str = "https://accounts.google.com";
/// The leeway allowed when verifying iat & exp claims, in seconds.
pub const LEEWAY: u64 = 30;

/// Data we store in the session.
#[derive(Clone, Serialize, Deserialize)]
pub struct OidcBridgeData {
    pub link: Link,
    pub origin: String,
    pub client_id: String,
    pub nonce: String,
    pub signing_alg: SigningAlgorithm,
}

/// OpenID Connect configuration document.
#[derive(Deserialize)]
struct ProviderConfig {
    authorization_endpoint: Url,
    jwks_uri: Url,
    #[serde(default = "default_response_modes_supported")]
    response_modes_supported: Vec<String>,
    #[serde(default = "default_id_token_signing_alg_values_supported")]
    id_token_signing_alg_values_supported: Vec<String>,
    // NOTE: This field is non-standard.
    #[serde(default)]
    accepts_id_token_signing_alg_query_param: bool,
}

fn default_response_modes_supported() -> Vec<String> {
    vec!["fragment".to_owned()]
}

fn default_id_token_signing_alg_values_supported() -> Vec<String> {
    vec!["RS256".to_owned()]
}

/// OpenID Connect key set document.
#[derive(Deserialize)]
struct ProviderKeys {
    #[serde(default)]
    keys: Vec<ProviderKey>,
}

#[derive(Deserialize)]
pub struct ProviderKey {
    #[serde(default)]
    pub alg: String,
    #[serde(default)]
    pub crv: String,
    #[serde(rename = "use")]
    #[serde(default)]
    pub use_: String,
    #[serde(default)]
    pub kid: String,
    #[serde(default)]
    pub n: String,
    #[serde(default)]
    pub e: String,
    #[serde(default)]
    pub x: String,
}

/// Provide authentication using OpenID Connect.
///
/// Redirect the user agent to the provider authorization endpoint, which we discover by reading
/// the providers configuration document. Included in the request is a nonce which we can later use
/// to definitively match the callback to this request.
///
/// This function handles both Portier providers, which works without registration, as well as
/// the Google provider, for which we have a preregistered `client_id`.
pub async fn auth(ctx: &mut Context, email_addr: &EmailAddress, link: &Link) -> HandlerResult {
    // Generate a nonce for the provider.
    let provider_nonce = crypto::nonce(&ctx.app.rng).await;

    // Determine the parameters to use, based on the webfinger link.
    let provider_origin = validation::parse_oidc_href(&link.href).ok_or_else(|| {
        BrokerError::Provider(format!("invalid href (validation failed): {}", link.href))
    })?;
    let mut bridge_data = match link.rel {
        Relation::Portier => {
            metrics::AUTH_OIDC_REQUESTS_PORTIER.inc();
            #[cfg(not(feature = "insecure"))]
            {
                if link.href.scheme() != "https" {
                    return Err(BrokerError::Provider(format!(
                        "invalid href (not HTTPS): {}",
                        link.href
                    )));
                }
            }
            OidcBridgeData {
                link: link.clone(),
                origin: provider_origin,
                client_id: ctx.app.public_url.clone(),
                nonce: provider_nonce,
                signing_alg: SigningAlgorithm::Rs256,
            }
        }
        // Delegate to the OpenID Connect bridge for Google, if configured.
        Relation::Google => {
            metrics::AUTH_OIDC_REQUESTS_GOOGLE.inc();
            let client_id = ctx
                .app
                .google_client_id
                .as_ref()
                .ok_or(BrokerError::ProviderCancelled)?;
            if provider_origin != GOOGLE_IDP_ORIGIN {
                return Err(BrokerError::Provider(format!(
                    "invalid href: Google provider only supports {}",
                    GOOGLE_IDP_ORIGIN
                )));
            }
            OidcBridgeData {
                link: link.clone(),
                origin: provider_origin,
                client_id: client_id.clone(),
                nonce: provider_nonce,
                signing_alg: SigningAlgorithm::Rs256,
            }
        }
    };

    // Retrieve the provider's configuration.
    let (
        ProviderConfig {
            authorization_endpoint: mut auth_url,
            response_modes_supported: response_modes,
            id_token_signing_alg_values_supported: signing_algs,
            accepts_id_token_signing_alg_query_param: accepts_signing_alg,
            ..
        },
        key_set,
    ) = fetch_config(ctx, &bridge_data).await?;

    {
        // Create the URL to redirect to.
        let mut query = auth_url.query_pairs_mut();
        query.extend_pairs(&[
            ("login_hint", email_addr.as_str()),
            ("scope", "openid email"),
            ("nonce", &bridge_data.nonce),
            ("state", &ctx.session_id),
            ("response_type", "id_token"),
            ("client_id", &bridge_data.client_id),
            ("redirect_uri", &format!("{}/callback", &ctx.app.public_url)),
        ]);

        // Prefer `form_post` response mode, otherwise use `fragment`.
        if response_modes.iter().any(|mode| mode == "form_post") {
            query.append_pair("response_mode", "form_post");
        } else if !response_modes.iter().any(|mode| mode == "fragment") {
            return Err(BrokerError::Provider(format!(
                "neither form_post nor fragment response modes supported by {}'s IdP ",
                email_addr.domain()
            )));
        }

        // NOTE: This query parameter is non-standard.
        // Prefer `Ed25519`, but there is no standard way to select it, so we introduce extra
        // fields, and take care we don't accidentally break the protocol. On top of this,
        // `alg=EdDSA` could also mean Ed448, so we inspect the key set to make sure there are only
        // Ed25519 keys among EdDSA keys.
        if accepts_signing_alg && signing_algs.iter().any(|s| s.as_str() == "EdDSA") {
            let mut found_ed25519 = false;
            let mut found_other_ed_dsa = false;
            for key in &key_set.keys {
                if key.use_ == "sig" && key.alg == "EdDSA" {
                    if key.crv == "Ed25519" {
                        found_ed25519 = true;
                    } else {
                        found_other_ed_dsa = true;
                        break;
                    }
                }
            }
            if found_ed25519 && !found_other_ed_dsa {
                bridge_data.signing_alg = SigningAlgorithm::EdDsa;
                query.append_pair("id_token_signing_alg", "EdDSA");
            }
        }

        query.finish();
    }

    // Save session data, committing the session to this provider.
    // If this fails, another auth mechanism has already claimed the session.
    if !ctx.save_session(BridgeData::Oidc(bridge_data)).await? {
        return Err(BrokerError::ProviderCancelled);
    }

    if ctx.want_json {
        Ok(json_response(&json!({
            "result": "redirect_to_provider",
            "url": auth_url.as_str(),
        })))
    } else {
        let mut res = empty_response(StatusCode::SEE_OTHER);
        res.header(hyper::header::LOCATION, auth_url.as_str());
        Ok(res)
    }
}

/// Request handler for OpenID Connect callbacks.
///
/// Match the returned email address and nonce against our session data, then extract the identity
/// token returned by the provider and verify it. Return an identity token for the relying party if
/// successful, or an error message otherwise.
pub async fn callback(ctx: &mut Context) -> HandlerResult {
    let mut params = ctx.form_params();
    let session_id = try_get_provider_param!(params, "state");
    let id_token = try_get_provider_param!(params, "id_token");

    #[allow(clippy::match_wildcard_for_single_variants)]
    let bridge_data = match ctx.load_session(&session_id).await? {
        BridgeData::Oidc(bridge_data) => bridge_data,
        _ => return Err(BrokerError::ProviderInput("invalid session".to_owned())),
    };

    // Retrieve the provider's configuration.
    let (_, key_set) = fetch_config(ctx, &bridge_data).await?;

    // Verify the signature.
    let jwt_payload = crypto::verify_jws(&id_token, &key_set.keys, bridge_data.signing_alg)
        .map_err(|err| {
            BrokerError::ProviderInput(format!(
                "could not verify the token received from {}: {}",
                bridge_data.origin, err
            ))
        })?;

    let data = ctx.session_data.as_ref().expect("session vanished");

    // Extract the token claims.
    let descr = format!("{}'s token payload", data.email_addr.domain());
    let iss = try_get_token_field!(jwt_payload, "iss", descr);
    let aud = try_get_token_field!(jwt_payload, "aud", descr);
    let email = try_get_token_field!(jwt_payload, "email", descr);
    let iat = try_get_token_field!(jwt_payload, "iat", value_as_unix_timestamp, descr);
    let exp = try_get_token_field!(jwt_payload, "exp", value_as_unix_timestamp, descr);
    let nonce = try_get_token_field!(jwt_payload, "nonce", descr);

    // Verify the token claims.
    check_token_field!(iss == bridge_data.origin, "iss", descr);
    check_token_field!(aud == bridge_data.client_id, "aud", descr);
    check_token_field!(nonce == bridge_data.nonce, "nonce", descr);

    let now = unix_timestamp();
    let exp = exp.checked_add(LEEWAY).unwrap_or(u64::min_value());
    let iat = iat.checked_sub(LEEWAY).unwrap_or(u64::max_value());
    check_token_field!(now < exp, "exp", descr);
    check_token_field!(iat <= now, "iat", descr);

    match bridge_data.link.rel {
        Relation::Portier => {
            // `email` should match the normalized email, as we sent it to the IdP.
            check_token_field!(email == data.email_addr.as_str(), "email", descr);
            // `email_original` should not be necessary for Broker -> IdP, but verify it any way.
            if let Some(email_original) = jwt_payload.get("email_original").and_then(Value::as_str)
            {
                check_token_field!(
                    email_original == data.email_addr.as_str(),
                    "email_original",
                    descr
                );
            }
        }
        Relation::Google => {
            // Check `email` after additional Google-specific normalization.
            let email_addr: EmailAddress = email.parse().map_err(|err| {
                BrokerError::ProviderInput(format!("failed to parse email in {}: {}", descr, err))
            })?;
            let google_email_addr = email_addr.normalize_google();
            let expected = data.email_addr.normalize_google();
            check_token_field!(google_email_addr == expected, "email", descr);
        }
    }

    // Everything is okay. Build a new identity token and send it to the relying party.
    metrics::AUTH_OIDC_COMPLETED.inc();
    complete_auth(ctx).await
}

// Retrieve and verify the provider's configuration.
async fn fetch_config(
    ctx: &mut Context,
    bridge_data: &OidcBridgeData,
) -> Result<(ProviderConfig, ProviderKeys), BrokerError> {
    let config_url = format!("{}/.well-known/openid-configuration", bridge_data.origin)
        .parse()
        .expect("could not build the OpenID Connect configuration URL");

    let provider_config = ctx
        .app
        .store
        .send(FetchUrlCached {
            url: config_url,
            metric: &*metrics::AUTH_OIDC_FETCH_CONFIG_DURATION,
        })
        .await
        .map_err(|e| {
            BrokerError::Provider(format!(
                "could not fetch {}'s configuration: {}",
                bridge_data.origin, e
            ))
        })?;
    let provider_config: ProviderConfig = serde_json::from_str(&provider_config).map_err(|e| {
        BrokerError::Provider(format!(
            "could not parse {}'s configuration: {}",
            bridge_data.origin, e
        ))
    })?;

    #[cfg(not(feature = "insecure"))]
    {
        if provider_config.authorization_endpoint.scheme() != "https" {
            return Err(BrokerError::Provider(format!(
                "{}'s authorization_endpoint is not HTTPS",
                bridge_data.origin
            )));
        }
        if provider_config.jwks_uri.scheme() != "https" {
            return Err(BrokerError::Provider(format!(
                "{}'s jwks_uri is not HTTPS",
                bridge_data.origin
            )));
        }
    }

    // Grab the keys from the provider.
    let key_set = ctx
        .app
        .store
        .send(FetchUrlCached {
            url: provider_config.jwks_uri.clone(),
            metric: &*metrics::AUTH_OIDC_FETCH_JWKS_DURATION,
        })
        .await
        .map_err(|e| {
            BrokerError::Provider(format!(
                "could not fetch {}'s keys: {}",
                bridge_data.origin, e
            ))
        })?;
    let key_set: ProviderKeys = serde_json::from_str(&key_set).map_err(|e| {
        BrokerError::Provider(format!(
            "could not parse{}'s keys: {}",
            bridge_data.origin, e
        ))
    })?;

    Ok((provider_config, key_set))
}

/// Try to convert a JSON Value to a u64 Unix timestamp.
///
/// Accepts a Number as either u64 or f64, and truncates the latter.
fn value_as_unix_timestamp(val: &Value) -> Option<u64> {
    val.as_u64().or_else(|| val.as_f64().map(|num| num as u64))
}
<|endoftext|>


<commit_before>// Copyright 2013 The OMath Developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// 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.

#[macro_escape];

use std::vec::{Items, MutItems};

pub trait Array
<
    T: Clone,
    Slice
>
{
    fn i<'a>(&'a self, i: uint) -> &'a T;
    fn mut_i<'a>(&'a mut self, i: uint) -> &'a mut T;
    fn as_slice<'a>(&'a self) -> &'a Slice;
    fn as_mut_slice<'a>(&'a mut self) -> &'a mut Slice;
    fn from_slice(slice: Slice) -> Self;
    fn build(builder: |i: uint| -> T) -> Self;
    fn iter<'a>(&'a self) -> Items<'a, T>;
    fn mut_iter<'a>(&'a mut self) -> MutItems<'a, T>;

    /// Swap two elements of the type in place.
    #[inline]
    fn swap(&mut self, a: uint, b: uint) {
        let tmp = self.i(a).clone();
        *self.mut_i(a) = self.i(b).clone();
        *self.mut_i(b) = tmp;
    }

    fn fold(&self, f: |&T, &T| -> T) -> T;
    fn each_mut(&mut self, f: |i: uint, x: &mut T|);
}

macro_rules! array(
    (impl<$S:ident> $Self:ty -> [$T:ty, ..$n:expr] $_n:ident) => (
        impl<$S: Clone> Array<$T, [$T,..$n]> for $Self {
            #[inline]
            fn i<'a>(&'a self, i: uint) -> &'a $T {
                &'a self.as_slice()[i]
            }

            #[inline]
            fn mut_i<'a>(&'a mut self, i: uint) -> &'a mut $T {
                &'a mut self.as_mut_slice()[i]
            }

            #[inline]
            fn as_slice<'a>(&'a self) -> &'a [$T,..$n] {
                unsafe { ::std::cast::transmute(self) }
            }

            #[inline]
            fn as_mut_slice<'a>(&'a mut self) -> &'a mut [$T,..$n] {
                unsafe { ::std::cast::transmute(self) }
            }

            #[inline]
            fn from_slice(slice: [$T,..$n]) -> $Self {
                unsafe { ::std::cast::transmute(slice) }
            }

            #[inline]
            fn build(builder: |i: uint| -> $T) -> $Self {
                Array::from_slice(gen_builder!($_n))
            }

            #[inline]
            fn iter<'a>(&'a self) -> ::std::vec::Items<'a, $T> {
                self.as_slice().iter()
            }

            #[inline]
            fn mut_iter<'a>(&'a mut self) -> ::std::vec::MutItems<'a, $T> {
                self.as_mut_slice().mut_iter()
            }

            #[inline]
            fn fold(&self, f: |&$T, &$T| -> $T) -> $T {
                gen_fold!($_n)
            }

            #[inline]
            fn each_mut(&mut self, f: |i: uint, x: &mut $T|) {
                gen_each_mut!($_n)
            }
        }
    )
)

#[inline]
pub fn build<T: Clone, Slice, A: Array<T, Slice>>(builder: |i: uint| -> T) -> A {
    Array::build(builder)
}

macro_rules! gen_builder(
    (_2) => ({ [builder(0), builder(1)] });
    (_3) => ({ [builder(0), builder(1), builder(2)] });
    (_4) => ({ [builder(0), builder(1), builder(2), builder(3)] });
)

macro_rules! gen_fold(
    (_2) => ({ f(self.i(0), self.i(1)) });
    (_3) => ({ f(&f(self.i(0), self.i(1)), self.i(2)) });
    (_4) => ({ f(&f(&f(self.i(0), self.i(1)), self.i(2)), self.i(3)) });
)

macro_rules! gen_each_mut(
    (_2) => ({ f(0, self.mut_i(0)); f(1, self.mut_i(1)); });
    (_3) => ({ f(0, self.mut_i(0)); f(1, self.mut_i(1)); f(2, self.mut_i(2)); });
    (_4) => ({ f(0, self.mut_i(0)); f(1, self.mut_i(1)); f(2, self.mut_i(2)); f(3, self.mut_i(3)); });
)

<commit_msg>Updated to latest Rust: std::vec -> std::slice<commit_after>// Copyright 2013 The OMath Developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// 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.

#[macro_escape];

use std::slice::{Items, MutItems};

pub trait Array
<
    T: Clone,
    Slice
>
{
    fn i<'a>(&'a self, i: uint) -> &'a T;
    fn mut_i<'a>(&'a mut self, i: uint) -> &'a mut T;
    fn as_slice<'a>(&'a self) -> &'a Slice;
    fn as_mut_slice<'a>(&'a mut self) -> &'a mut Slice;
    fn from_slice(slice: Slice) -> Self;
    fn build(builder: |i: uint| -> T) -> Self;
    fn iter<'a>(&'a self) -> Items<'a, T>;
    fn mut_iter<'a>(&'a mut self) -> MutItems<'a, T>;

    /// Swap two elements of the type in place.
    #[inline]
    fn swap(&mut self, a: uint, b: uint) {
        let tmp = self.i(a).clone();
        *self.mut_i(a) = self.i(b).clone();
        *self.mut_i(b) = tmp;
    }

    fn fold(&self, f: |&T, &T| -> T) -> T;
    fn each_mut(&mut self, f: |i: uint, x: &mut T|);
}

macro_rules! array(
    (impl<$S:ident> $Self:ty -> [$T:ty, ..$n:expr] $_n:ident) => (
        impl<$S: Clone> Array<$T, [$T,..$n]> for $Self {
            #[inline]
            fn i<'a>(&'a self, i: uint) -> &'a $T {
                &'a self.as_slice()[i]
            }

            #[inline]
            fn mut_i<'a>(&'a mut self, i: uint) -> &'a mut $T {
                &'a mut self.as_mut_slice()[i]
            }

            #[inline]
            fn as_slice<'a>(&'a self) -> &'a [$T,..$n] {
                unsafe { ::std::cast::transmute(self) }
            }

            #[inline]
            fn as_mut_slice<'a>(&'a mut self) -> &'a mut [$T,..$n] {
                unsafe { ::std::cast::transmute(self) }
            }

            #[inline]
            fn from_slice(slice: [$T,..$n]) -> $Self {
                unsafe { ::std::cast::transmute(slice) }
            }

            #[inline]
            fn build(builder: |i: uint| -> $T) -> $Self {
                Array::from_slice(gen_builder!($_n))
            }

            #[inline]
            fn iter<'a>(&'a self) -> ::std::slice::Items<'a, $T> {
                self.as_slice().iter()
            }

            #[inline]
            fn mut_iter<'a>(&'a mut self) -> ::std::slice::MutItems<'a, $T> {
                self.as_mut_slice().mut_iter()
            }

            #[inline]
            fn fold(&self, f: |&$T, &$T| -> $T) -> $T {
                gen_fold!($_n)
            }

            #[inline]
            fn each_mut(&mut self, f: |i: uint, x: &mut $T|) {
                gen_each_mut!($_n)
            }
        }
    )
)

#[inline]
pub fn build<T: Clone, Slice, A: Array<T, Slice>>(builder: |i: uint| -> T) -> A {
    Array::build(builder)
}

macro_rules! gen_builder(
    (_2) => ({ [builder(0), builder(1)] });
    (_3) => ({ [builder(0), builder(1), builder(2)] });
    (_4) => ({ [builder(0), builder(1), builder(2), builder(3)] });
)

macro_rules! gen_fold(
    (_2) => ({ f(self.i(0), self.i(1)) });
    (_3) => ({ f(&f(self.i(0), self.i(1)), self.i(2)) });
    (_4) => ({ f(&f(&f(self.i(0), self.i(1)), self.i(2)), self.i(3)) });
)

macro_rules! gen_each_mut(
    (_2) => ({ f(0, self.mut_i(0)); f(1, self.mut_i(1)); });
    (_3) => ({ f(0, self.mut_i(0)); f(1, self.mut_i(1)); f(2, self.mut_i(2)); });
    (_4) => ({ f(0, self.mut_i(0)); f(1, self.mut_i(1)); f(2, self.mut_i(2)); f(3, self.mut_i(3)); });
)

<|endoftext|>


<commit_before>extern crate libc;

use libc::{c_void};
use std::ffi::CStr;
use std::ffi::CString;
use std::{ptr, thread, time};

use super::super::couchbase::*;

#[derive(Debug)]
pub struct ClientOps {
    pub total: usize
}

#[derive(Debug)]
pub struct Client {
    pub opts: CreateSt,
    pub instance: Instance,
    pub uri: String,
    pub ops: ClientOps
}

impl Client {
    pub fn new(uri: &str) -> Client {
        let connstr = CString::new(uri).unwrap();

        let mut opts = CreateSt::default();
        opts.v3.connstr = connstr.as_ptr();

        let mut instance: Instance = ptr::null_mut();

        unsafe {
            let res = lcb_create(&mut instance as *mut Instance, &opts as *const CreateSt);
            if res != ErrorType::Success {
                println!("lcb_connect() failed - {:?}", res);
            }

            println!("Connecting to {}", uri);

            let res = lcb_connect(instance);
            if res != ErrorType::Success {
                println!("lcb_connect() failed - {:?}", res);
            }

            let res = lcb_wait(instance);
            if res != ErrorType::Success {
                println!("lcb_wait() failed - {:?}", res);
            }

            let res = lcb_get_bootstrap_status(instance);
            if res != ErrorType::Success {
                println!("lcb_get_bootstrap_status() failed - {:?}, \"{}\"",
                         res,
                         CStr::from_ptr(lcb_strerror(instance, res)).to_str().unwrap()
                );
            }

            lcb_install_callback3(instance, CallbackType::Get, Some(op_callback));
            lcb_install_callback3(instance, CallbackType::Store, Some(op_callback));

            let ops = ClientOps {
                total: 0
            };

            Client {
                opts: opts,
                instance: instance,
                uri: uri.to_string(),
                ops: ops
            }
        }
    }

    pub fn get<F>(&mut self, key: &str, callback: F) -> &mut Client
        where F: Fn(&ResponseGet)
    {
        let ckey = CString::new(key).unwrap();
        let mut gcmd = CmdGet::default();
        gcmd.key._type = KvBufferType::Copy;
        gcmd.key.contig.bytes = ckey.as_ptr() as *const libc::c_void;
        gcmd.key.contig.nbytes = key.len() as u64;

        self.ops.total += 1;

        unsafe {
            let boxed: Box<Fn(&ResponseGet)> = Box::new(|response: &ResponseGet| {
                callback(response);
            });

            let user_data = &boxed as *const _ as *mut c_void;

            let res = lcb_get3(self.instance, user_data, &gcmd as *const CmdGet);
            if res != ErrorType::Success {
                println!("lcb_get3() failed - {:?}", res);
            }

            let res = lcb_wait(self.instance);
            if res != ErrorType::Success {
                println!("lcb_wait() failed - {:?}", res);
            }
        }

        self
    }

    pub fn store<F>(&mut self, key: &str, value: &str, callback: F) -> &mut Client
        where F: Fn(&ResponseStore)
    {
        let ckey = CString::new(key).unwrap();
        let cvalue = CString::new(value).unwrap();
        let mut gcmd = CmdStore::default();
        gcmd.key._type = KvBufferType::Copy;
        gcmd.key.contig.bytes = ckey.as_ptr() as *const libc::c_void;
        gcmd.key.contig.nbytes = key.len() as u64;
        gcmd.value._type = KvBufferType::Copy;
        gcmd.value.contig.bytes = cvalue.as_ptr() as *const libc::c_void;
        gcmd.value.contig.nbytes = value.len() as u64;

        unsafe {
            let boxed: Box<Fn(&ResponseStore)> = Box::new(|res: &ResponseStore| {
                callback(res);
            });

            let user_data = &boxed as *const _ as *mut c_void;

            let res = lcb_store3(self.instance, user_data, &gcmd as *const CmdStore);
            if res != ErrorType::Success {
                println!("lcb_get3() failed - {:?}", res);
            }

            let res = lcb_wait(self.instance);
            if res != ErrorType::Success {
                println!("lcb_wait() failed - {:?}", res);
            }
        }

        self
    }

    pub fn ops_unfinished_count(&self) -> usize {
        return self.ops.total;
    }

    pub fn ops_finished(&mut self) -> bool {
        return self.ops_unfinished_count() == 0;
    }

    pub fn wait(&mut self) {
         let interval = time::Duration::from_millis(100);
         while self.ops_finished() == false {
             thread::sleep(interval);
         }
    }

    pub fn wait_max(&mut self, max_msec: usize) {
        let mut t = 0 as usize;
        let interval = time::Duration::from_millis(100);
        while self.ops_finished() == false {
            thread::sleep(interval);
            t += 100;

            if t > max_msec {
                println!("wait_max - still {} operations unfinished", self.ops_unfinished_count());
                break;
            }
        }
    }
}

impl Drop for Client {
    fn drop(&mut self) {
        unsafe {
            println!("Disconnecting from {}", self.uri);
            lcb_destroy(self.instance);
        }
    }
}

unsafe extern "C" fn op_callback(_instance: Instance, cbtype: CallbackType, resp: *const ResponseBase) {
    match cbtype {
        CallbackType::Get => {
            let gresp = resp as *const ResponseGet;
            let cookie = (*gresp).cookie;
            let callback = cookie as *const Box<Fn(&ResponseGet)>;
            (*callback)(&(*gresp));
        },
        CallbackType::Store => {
            let gresp = resp as *const ResponseStore;

            let cookie = (*gresp).cookie;
            let callback = cookie as *const Box<Fn(&ResponseStore)>;
            (*callback)(&(*gresp));
        },
        _ => panic!("! Unknown Callback...")
    };
}
<commit_msg>Update self.ops.total += 1;<commit_after>extern crate libc;

use libc::{c_void};
use std::ffi::CStr;
use std::ffi::CString;
use std::{ptr, thread, time};

use super::super::couchbase::*;

#[derive(Debug)]
pub struct ClientOps {
    pub total: usize
}

#[derive(Debug)]
pub struct Client {
    pub opts: CreateSt,
    pub instance: Instance,
    pub uri: String,
    pub ops: ClientOps
}

impl Client {
    pub fn new(uri: &str) -> Client {
        let connstr = CString::new(uri).unwrap();

        let mut opts = CreateSt::default();
        opts.v3.connstr = connstr.as_ptr();

        let mut instance: Instance = ptr::null_mut();

        unsafe {
            let res = lcb_create(&mut instance as *mut Instance, &opts as *const CreateSt);
            if res != ErrorType::Success {
                println!("lcb_connect() failed - {:?}", res);
            }

            println!("Connecting to {}", uri);

            let res = lcb_connect(instance);
            if res != ErrorType::Success {
                println!("lcb_connect() failed - {:?}", res);
            }

            let res = lcb_wait(instance);
            if res != ErrorType::Success {
                println!("lcb_wait() failed - {:?}", res);
            }

            let res = lcb_get_bootstrap_status(instance);
            if res != ErrorType::Success {
                println!("lcb_get_bootstrap_status() failed - {:?}, \"{}\"",
                         res,
                         CStr::from_ptr(lcb_strerror(instance, res)).to_str().unwrap()
                );
            }

            lcb_install_callback3(instance, CallbackType::Get, Some(op_callback));
            lcb_install_callback3(instance, CallbackType::Store, Some(op_callback));

            let ops = ClientOps {
                total: 0
            };

            Client {
                opts: opts,
                instance: instance,
                uri: uri.to_string(),
                ops: ops
            }
        }
    }

    pub fn get<F>(&mut self, key: &str, callback: F) -> &mut Client
        where F: Fn(&ResponseGet)
    {
        let ckey = CString::new(key).unwrap();
        let mut gcmd = CmdGet::default();
        gcmd.key._type = KvBufferType::Copy;
        gcmd.key.contig.bytes = ckey.as_ptr() as *const libc::c_void;
        gcmd.key.contig.nbytes = key.len() as u64;

        self.ops.total += 1;

        unsafe {
            let boxed: Box<Fn(&ResponseGet)> = Box::new(|response: &ResponseGet| {
                callback(response);
            });

            let user_data = &boxed as *const _ as *mut c_void;

            let res = lcb_get3(self.instance, user_data, &gcmd as *const CmdGet);
            if res != ErrorType::Success {
                println!("lcb_get3() failed - {:?}", res);
            }

            let res = lcb_wait(self.instance);
            if res != ErrorType::Success {
                println!("lcb_wait() failed - {:?}", res);
            }
        }

        self
    }

    pub fn store<F>(&mut self, key: &str, value: &str, callback: F) -> &mut Client
        where F: Fn(&ResponseStore)
    {
        let ckey = CString::new(key).unwrap();
        let cvalue = CString::new(value).unwrap();
        let mut gcmd = CmdStore::default();
        gcmd.key._type = KvBufferType::Copy;
        gcmd.key.contig.bytes = ckey.as_ptr() as *const libc::c_void;
        gcmd.key.contig.nbytes = key.len() as u64;
        gcmd.value._type = KvBufferType::Copy;
        gcmd.value.contig.bytes = cvalue.as_ptr() as *const libc::c_void;
        gcmd.value.contig.nbytes = value.len() as u64;

        self.ops.total += 1;

        unsafe {
            let boxed: Box<Fn(&ResponseStore)> = Box::new(|res: &ResponseStore| {
                callback(res);
            });

            let user_data = &boxed as *const _ as *mut c_void;

            let res = lcb_store3(self.instance, user_data, &gcmd as *const CmdStore);
            if res != ErrorType::Success {
                println!("lcb_get3() failed - {:?}", res);
            }

            let res = lcb_wait(self.instance);
            if res != ErrorType::Success {
                println!("lcb_wait() failed - {:?}", res);
            }
        }

        self
    }

    pub fn ops_unfinished_count(&self) -> usize {
        return self.ops.total;
    }

    pub fn ops_finished(&mut self) -> bool {
        return self.ops_unfinished_count() == 0;
    }

    pub fn wait(&mut self) {
         let interval = time::Duration::from_millis(100);
         while self.ops_finished() == false {
             thread::sleep(interval);
         }
    }

    pub fn wait_max(&mut self, max_msec: usize) {
        let mut t = 0 as usize;
        let interval = time::Duration::from_millis(100);
        while self.ops_finished() == false {
            thread::sleep(interval);
            t += 100;

            if t > max_msec {
                println!("wait_max - still {} operations unfinished", self.ops_unfinished_count());
                break;
            }
        }
    }
}

impl Drop for Client {
    fn drop(&mut self) {
        unsafe {
            println!("Disconnecting from {}", self.uri);
            lcb_destroy(self.instance);
        }
    }
}

unsafe extern "C" fn op_callback(_instance: Instance, cbtype: CallbackType, resp: *const ResponseBase) {
    match cbtype {
        CallbackType::Get => {
            let gresp = resp as *const ResponseGet;
            let cookie = (*gresp).cookie;
            let callback = cookie as *const Box<Fn(&ResponseGet)>;
            (*callback)(&(*gresp));
        },
        CallbackType::Store => {
            let gresp = resp as *const ResponseStore;

            let cookie = (*gresp).cookie;
            let callback = cookie as *const Box<Fn(&ResponseStore)>;
            (*callback)(&(*gresp));
        },
        _ => panic!("! Unknown Callback...")
    };
}
<|endoftext|>


<commit_before>
use {
    AddEllipse,
    AddPolygon,
    AddRectangle,
    AddTween,
    BackEnd,
    Borrowed,
    CanTransform,
    Clear,
    Color,
    EllipseColorContext,
    Field,
    HasTransform,
    Matrix2d,
    PolygonColorContext,
    RectangleColorContext,
    TweenColorContext,
    View,
    Value,
};
use vecmath::{
    identity,
};

/// A context with color information.
pub struct ColorContext<'a> {
    /// Base/original transformation.
    pub base: Field<'a, Matrix2d>,
    /// Current transformation.
    pub transform: Field<'a, Matrix2d>,
    /// Current color.
    pub color: Field<'a, Color>,
}

impl<'a> HasTransform<'a, Matrix2d> for ColorContext<'a> {
    #[inline(always)]
    fn get_transform(&'a self) -> &'a Matrix2d {
        self.transform.get()
    }
}

impl<'a> CanTransform<'a, ColorContext<'a>, Matrix2d> for ColorContext<'a> {
    #[inline(always)]
    fn transform(&'a self, value: Matrix2d) -> ColorContext<'a> {
        ColorContext {
            base: Borrowed(self.base.get()),
            transform: Value(value),
            color: Borrowed(self.color.get()),
        }
    }
}

impl<'a> AddRectangle<'a, RectangleColorContext<'a>> for ColorContext<'a> {
    #[inline(always)]
    fn rect(&'a self, x: f64, y: f64, w: f64, h: f64) -> RectangleColorContext<'a> {
        RectangleColorContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            color: Borrowed(self.color.get()),
            rect: Value([x, y, w, h]),
        }
    }
}

#[test]
fn test_rect() {
    use {Context, AddColor};

    let c = Context::new();
    let color = c.rgba(1.0, 0.0, 0.0, 1.0);
    let rect_color = color.rect(0.0, 0.0, 100.0, 100.0);
    assert_eq!(rect_color.rect.get()[2], 100.0);
}

impl<'a> AddEllipse<'a, EllipseColorContext<'a>> for ColorContext<'a> {
    #[inline(always)]
    fn ellipse(&'a self, x: f64, y: f64, w: f64, h: f64) -> EllipseColorContext<'a> {
        EllipseColorContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            color: Borrowed(self.color.get()),
            rect: Value([x, y, w, h]),
        }
    }
}

impl<'a> AddPolygon<'a, PolygonColorContext<'a>> for ColorContext<'a> {
    #[inline(always)]
    fn polygon(&'a self, polygon: &'a [f64]) -> PolygonColorContext<'a> {
        PolygonColorContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            color: Borrowed(self.color.get()),
            polygon: Value(polygon),
        }
    }
}

impl<'a> AddTween<'a, TweenColorContext<'a>> for ColorContext<'a> {
    #[inline(always)]
    fn lerp(&'a self, tween_factor: f64) -> TweenColorContext<'a> {
        TweenColorContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            color: Borrowed(self.color.get()),
            tween_factor: Value(tween_factor),
        }
    }
}

impl<'a> Clear for ColorContext<'a> {
    fn clear<B: BackEnd>(&self, back_end: &mut B) {
        if back_end.supports_clear_rgba() {
            let color = self.color.get();
            back_end.clear_rgba(color[0], color[1], color[2], color[3]);
        }
    }
}

impl<'a> View<'a> for ColorContext<'a> {
    #[inline(always)]
    fn view(&'a self) -> ColorContext<'a> {
        ColorContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.base.get()),
            color: Borrowed(self.color.get()),
        }
    }

    #[inline(always)]
    fn reset(&'a self) -> ColorContext<'a> {
        ColorContext {
            base: Borrowed(self.base.get()),
            transform: Value(identity()),
            color: Borrowed(self.color.get()),
        }
    }

    #[inline(always)]
    fn store_view(&'a self) -> ColorContext<'a> {
        ColorContext {
            base: Borrowed(self.transform.get()),
            transform: Borrowed(self.transform.get()),
            color: Borrowed(self.color.get()),
        }
    }
}

<commit_msg>Implemented `HasColor` and `CanColor` for `ColorContext`<commit_after>
use {
    AddEllipse,
    AddPolygon,
    AddRectangle,
    AddTween,
    BackEnd,
    Borrowed,
    CanColor,
    CanTransform,
    Clear,
    Color,
    EllipseColorContext,
    Field,
    HasColor,
    HasTransform,
    Matrix2d,
    PolygonColorContext,
    RectangleColorContext,
    TweenColorContext,
    View,
    Value,
};
use vecmath::{
    identity,
};

/// A context with color information.
pub struct ColorContext<'a> {
    /// Base/original transformation.
    pub base: Field<'a, Matrix2d>,
    /// Current transformation.
    pub transform: Field<'a, Matrix2d>,
    /// Current color.
    pub color: Field<'a, Color>,
}

impl<'a> HasTransform<'a, Matrix2d> for ColorContext<'a> {
    #[inline(always)]
    fn get_transform(&'a self) -> &'a Matrix2d {
        self.transform.get()
    }
}

impl<'a> CanTransform<'a, ColorContext<'a>, Matrix2d> for ColorContext<'a> {
    #[inline(always)]
    fn transform(&'a self, value: Matrix2d) -> ColorContext<'a> {
        ColorContext {
            base: Borrowed(self.base.get()),
            transform: Value(value),
            color: Borrowed(self.color.get()),
        }
    }
}

impl<'a> HasColor<'a, Color> for ColorContext<'a> {
    #[inline(always)]
    fn get_color(&'a self) -> &'a Color {
        self.color.get()
    }
}

impl<'a> CanColor<'a, ColorContext<'a>, Color> for ColorContext<'a> {
    #[inline(always)]
    fn color(&'a self, value: Color) -> ColorContext<'a> {
        ColorContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            color: Value(value),
        }
    }
}

impl<'a> AddRectangle<'a, RectangleColorContext<'a>> for ColorContext<'a> {
    #[inline(always)]
    fn rect(&'a self, x: f64, y: f64, w: f64, h: f64) -> RectangleColorContext<'a> {
        RectangleColorContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            color: Borrowed(self.color.get()),
            rect: Value([x, y, w, h]),
        }
    }
}

#[test]
fn test_rect() {
    use {Context, AddColor};

    let c = Context::new();
    let color = c.rgba(1.0, 0.0, 0.0, 1.0);
    let rect_color = color.rect(0.0, 0.0, 100.0, 100.0);
    assert_eq!(rect_color.rect.get()[2], 100.0);
}

impl<'a> AddEllipse<'a, EllipseColorContext<'a>> for ColorContext<'a> {
    #[inline(always)]
    fn ellipse(&'a self, x: f64, y: f64, w: f64, h: f64) -> EllipseColorContext<'a> {
        EllipseColorContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            color: Borrowed(self.color.get()),
            rect: Value([x, y, w, h]),
        }
    }
}

impl<'a> AddPolygon<'a, PolygonColorContext<'a>> for ColorContext<'a> {
    #[inline(always)]
    fn polygon(&'a self, polygon: &'a [f64]) -> PolygonColorContext<'a> {
        PolygonColorContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            color: Borrowed(self.color.get()),
            polygon: Value(polygon),
        }
    }
}

impl<'a> AddTween<'a, TweenColorContext<'a>> for ColorContext<'a> {
    #[inline(always)]
    fn lerp(&'a self, tween_factor: f64) -> TweenColorContext<'a> {
        TweenColorContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.transform.get()),
            color: Borrowed(self.color.get()),
            tween_factor: Value(tween_factor),
        }
    }
}

impl<'a> Clear for ColorContext<'a> {
    fn clear<B: BackEnd>(&self, back_end: &mut B) {
        if back_end.supports_clear_rgba() {
            let color = self.color.get();
            back_end.clear_rgba(color[0], color[1], color[2], color[3]);
        }
    }
}

impl<'a> View<'a> for ColorContext<'a> {
    #[inline(always)]
    fn view(&'a self) -> ColorContext<'a> {
        ColorContext {
            base: Borrowed(self.base.get()),
            transform: Borrowed(self.base.get()),
            color: Borrowed(self.color.get()),
        }
    }

    #[inline(always)]
    fn reset(&'a self) -> ColorContext<'a> {
        ColorContext {
            base: Borrowed(self.base.get()),
            transform: Value(identity()),
            color: Borrowed(self.color.get()),
        }
    }

    #[inline(always)]
    fn store_view(&'a self) -> ColorContext<'a> {
        ColorContext {
            base: Borrowed(self.transform.get()),
            transform: Borrowed(self.transform.get()),
            color: Borrowed(self.color.get()),
        }
    }
}

<|endoftext|>


<commit_before>use cocoa::base::{id, nil};
use cocoa::foundation::{NSString, NSUInteger};
use device::_make_device;
use std::borrow::Cow;
use std::error::Error;
use std::ffi::CStr;
use std::fmt::{self, Display, Formatter};
use sys::{MTLCommandQueue, MTLDevice};
use {Device};

pub struct CommandQueue(id);

impl CommandQueue {
    pub fn new(device: &Device) -> Result<Self, CommandQueueError> {
        let command_queue = unsafe { device.get_raw().newCommandQueue() };
        if command_queue != nil {
            Ok(CommandQueue(command_queue))
        } else {
            Err(CommandQueueError::CouldNotCreate)
        }
    }

    pub fn with_max_command_buffer_count(device: &Device,
                                         max_command_buffer_count: usize) -> Result<Self, CommandQueueError> {
        let command_queue = unsafe {
            device.get_raw().newCommandQueueWithMaxCommandBufferCount(max_command_buffer_count as NSUInteger)
        };
        if command_queue != nil {
            Ok(CommandQueue(command_queue))
        } else {
            Err(CommandQueueError::CouldNotCreate)
        }
    }

    pub fn insert_debug_capture_boundary(&self) {
        unsafe { self.0.insertDebugCaptureBoundary(); }
    }

    pub fn set_label(&self, label: &str) {
        unsafe { self.0.setLabel(NSString::alloc(nil).init_str(label)) }
    }

    pub fn get_label(&self) -> Cow<str> {
        unsafe { CStr::from_ptr(self.0.label().UTF8String()).to_string_lossy() }
    }

    pub fn get_device(&self) -> Device {
        let device = unsafe { self.0.device() };
        _make_device(device)
    }
}

/// Internal utility function to create a CommandQueue without exposing internals.
/// Not exported publicly from this crate.
#[doc(hidden)]
pub fn _make_command_queue(command_queue: id) -> CommandQueue {
    CommandQueue(command_queue)
}

#[derive(Clone, Debug)]
pub enum CommandQueueError {
    CouldNotCreate
}

impl Display for CommandQueueError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let descr = match *self {
            CommandQueueError::CouldNotCreate => "DeviceError::CouldNotCreate"
        };
        write!(f, "{}", descr)
    }
}

impl Error for CommandQueueError {
    fn description(&self) -> &str {
        match *self {
            CommandQueueError::CouldNotCreate => "Could not create command queue."
        }
    }
}
<commit_msg>Make a 2-liner function a 1-liner<commit_after>use cocoa::base::{id, nil};
use cocoa::foundation::{NSString, NSUInteger};
use device::_make_device;
use std::borrow::Cow;
use std::error::Error;
use std::ffi::CStr;
use std::fmt::{self, Display, Formatter};
use sys::{MTLCommandQueue, MTLDevice};
use {Device};

pub struct CommandQueue(id);

impl CommandQueue {
    pub fn new(device: &Device) -> Result<Self, CommandQueueError> {
        let command_queue = unsafe { device.get_raw().newCommandQueue() };
        if command_queue != nil {
            Ok(CommandQueue(command_queue))
        } else {
            Err(CommandQueueError::CouldNotCreate)
        }
    }

    pub fn with_max_command_buffer_count(device: &Device,
                                         max_command_buffer_count: usize) -> Result<Self, CommandQueueError> {
        let command_queue = unsafe {
            device.get_raw().newCommandQueueWithMaxCommandBufferCount(max_command_buffer_count as NSUInteger)
        };
        if command_queue != nil {
            Ok(CommandQueue(command_queue))
        } else {
            Err(CommandQueueError::CouldNotCreate)
        }
    }

    pub fn insert_debug_capture_boundary(&self) {
        unsafe { self.0.insertDebugCaptureBoundary(); }
    }

    pub fn set_label(&self, label: &str) {
        unsafe { self.0.setLabel(NSString::alloc(nil).init_str(label)) }
    }

    pub fn get_label(&self) -> Cow<str> {
        unsafe { CStr::from_ptr(self.0.label().UTF8String()).to_string_lossy() }
    }

    pub fn get_device(&self) -> Device {
        unsafe { _make_device(self.0.device()) };
    }
}

/// Internal utility function to create a CommandQueue without exposing internals.
/// Not exported publicly from this crate.
#[doc(hidden)]
pub fn _make_command_queue(command_queue: id) -> CommandQueue {
    CommandQueue(command_queue)
}

#[derive(Clone, Debug)]
pub enum CommandQueueError {
    CouldNotCreate
}

impl Display for CommandQueueError {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let descr = match *self {
            CommandQueueError::CouldNotCreate => "DeviceError::CouldNotCreate"
        };
        write!(f, "{}", descr)
    }
}

impl Error for CommandQueueError {
    fn description(&self) -> &str {
        match *self {
            CommandQueueError::CouldNotCreate => "Could not create command queue."
        }
    }
}
<|endoftext|>


<commit_before>//! Backend implementation for a glutin headless renderer.

use {Frame, IncompatibleOpenGl, SwapBuffersError};
use debug;
use context;
use backend::{self, Backend};
use std::rc::Rc;
use std::cell::RefCell;
use std::ops::Deref;
use std::os::raw::c_void;
use super::glutin;
use super::glutin::PossiblyCurrent as Pc;
use takeable_option::Takeable;

/// A headless glutin context.
pub struct Headless {
    context: Rc<context::Context>,
    glutin: Rc<RefCell<Takeable<glutin::Context<Pc>>>>,
}

/// An implementation of the `Backend` trait for a glutin headless context.
pub struct GlutinBackend(Rc<RefCell<Takeable<glutin::Context<Pc>>>>);

impl Deref for Headless {
    type Target = context::Context;
    fn deref(&self) -> &context::Context {
        &self.context
    }
}

/*
impl Deref for GlutinBackend {
    type Target = glutin::Context<Pc>;
    fn deref(&self) -> &glutin::Context<Pc> {
        &self.0
    }
}*/

unsafe impl Backend for GlutinBackend {
    #[inline]
    fn swap_buffers(&self) -> Result<(), SwapBuffersError> {
        Ok(())
    }

    #[inline]
    unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
        self.0.borrow().get_proc_address(symbol) as *const _
    }

    #[inline]
    fn get_framebuffer_dimensions(&self) -> (u32, u32) {
        (800, 600)      // FIXME: these are random
    }

    #[inline]
    fn is_current(&self) -> bool {
        self.0.borrow().is_current()
    }

    #[inline]
    unsafe fn make_current(&self) {
        let mut gl_window_takeable = self.0.borrow_mut();
        let gl_window = Takeable::take(&mut gl_window_takeable);
        let gl_window_new = gl_window.make_current().unwrap();
        Takeable::insert(&mut gl_window_takeable, gl_window_new);
    }
}

impl backend::Facade for Headless {
    #[inline]
    fn get_context(&self) -> &Rc<context::Context> {
        &self.context
    }
}

impl Headless {
    /// Create a new glium `Headless` context.
    ///
    /// Performs a compatibility check to make sure that all core elements of glium are supported
    /// by the implementation.
    pub fn new(context: glutin::Context<Pc>) -> Result<Self, IncompatibleOpenGl> {
        Self::with_debug(context, Default::default())
    }

    /// Create a new glium `Headless` context.
    ///
    /// This function does the same as `build_glium`, except that the resulting context
    /// will assume that the current OpenGL context will never change.
    pub unsafe fn unchecked(context: glutin::Context<Pc>) -> Result<Self, IncompatibleOpenGl> {
        Self::unchecked_with_debug(context, Default::default())
    }

    /// The same as the `new` constructor, but allows for specifying debug callback behaviour.
    pub fn with_debug(context: glutin::Context<Pc>, debug: debug::DebugCallbackBehavior)
        -> Result<Self, IncompatibleOpenGl>
    {
        Self::new_inner(context, debug, true)
    }

    /// The same as the `unchecked` constructor, but allows for specifying debug callback behaviour.
    pub unsafe fn unchecked_with_debug(
        context: glutin::Context<Pc>,
        debug: debug::DebugCallbackBehavior,
    ) -> Result<Self, IncompatibleOpenGl>
    {
        Self::new_inner(context, debug, false)
    }

    fn new_inner(
        context: glutin::Context<Pc>,
        debug: debug::DebugCallbackBehavior,
        checked: bool,
    ) -> Result<Self, IncompatibleOpenGl>
    {
        let glutin_context = Rc::new(RefCell::new(Takeable::new(context)));
        let glutin_backend = GlutinBackend(glutin_context.clone());
        let context = try!(unsafe { context::Context::new(glutin_backend, checked, debug) });
        Ok(Headless { context: context, glutin: glutin_context })
    }

    /// Start drawing on the backbuffer.
    ///
    /// This function returns a `Frame`, which can be used to draw on it. When the `Frame` is
    /// destroyed, the buffers are swapped.
    ///
    /// Note that destroying a `Frame` is immediate, even if vsync is enabled.
    ///
    /// If the framebuffer dimensions have changed since the last call to `draw`, the inner glutin
    /// context will be resized accordingly before returning the `Frame`.
    #[inline]
    pub fn draw(&self) -> Frame {
        Frame::new(self.context.clone(), self.get_framebuffer_dimensions())
    }
}
<commit_msg>Impl Deref again<commit_after>//! Backend implementation for a glutin headless renderer.

use {Frame, IncompatibleOpenGl, SwapBuffersError};
use debug;
use context;
use backend::{self, Backend};
use std::rc::Rc;
use std::cell::RefCell;
use std::ops::Deref;
use std::os::raw::c_void;
use super::glutin;
use super::glutin::PossiblyCurrent as Pc;
use takeable_option::Takeable;

/// A headless glutin context.
pub struct Headless {
    context: Rc<context::Context>,
    glutin: Rc<RefCell<Takeable<glutin::Context<Pc>>>>,
}

/// An implementation of the `Backend` trait for a glutin headless context.
pub struct GlutinBackend(Rc<RefCell<Takeable<glutin::Context<Pc>>>>);

impl Deref for Headless {
    type Target = context::Context;
    fn deref(&self) -> &context::Context {
        &self.context
    }
}

impl Deref for GlutinBackend {
    type Target = Rc<RefCell<Takeable<glutin::Context<Pc>>>>;
    fn deref(&self) -> &Rc<RefCell<Takeable<glutin::Context<Pc>>>> {
        &self.0
    }
}

unsafe impl Backend for GlutinBackend {
    #[inline]
    fn swap_buffers(&self) -> Result<(), SwapBuffersError> {
        Ok(())
    }

    #[inline]
    unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
        self.0.borrow().get_proc_address(symbol) as *const _
    }

    #[inline]
    fn get_framebuffer_dimensions(&self) -> (u32, u32) {
        (800, 600)      // FIXME: these are random
    }

    #[inline]
    fn is_current(&self) -> bool {
        self.0.borrow().is_current()
    }

    #[inline]
    unsafe fn make_current(&self) {
        let mut gl_window_takeable = self.0.borrow_mut();
        let gl_window = Takeable::take(&mut gl_window_takeable);
        let gl_window_new = gl_window.make_current().unwrap();
        Takeable::insert(&mut gl_window_takeable, gl_window_new);
    }
}

impl backend::Facade for Headless {
    #[inline]
    fn get_context(&self) -> &Rc<context::Context> {
        &self.context
    }
}

impl Headless {
    /// Create a new glium `Headless` context.
    ///
    /// Performs a compatibility check to make sure that all core elements of glium are supported
    /// by the implementation.
    pub fn new(context: glutin::Context<Pc>) -> Result<Self, IncompatibleOpenGl> {
        Self::with_debug(context, Default::default())
    }

    /// Create a new glium `Headless` context.
    ///
    /// This function does the same as `build_glium`, except that the resulting context
    /// will assume that the current OpenGL context will never change.
    pub unsafe fn unchecked(context: glutin::Context<Pc>) -> Result<Self, IncompatibleOpenGl> {
        Self::unchecked_with_debug(context, Default::default())
    }

    /// The same as the `new` constructor, but allows for specifying debug callback behaviour.
    pub fn with_debug(context: glutin::Context<Pc>, debug: debug::DebugCallbackBehavior)
        -> Result<Self, IncompatibleOpenGl>
    {
        Self::new_inner(context, debug, true)
    }

    /// The same as the `unchecked` constructor, but allows for specifying debug callback behaviour.
    pub unsafe fn unchecked_with_debug(
        context: glutin::Context<Pc>,
        debug: debug::DebugCallbackBehavior,
    ) -> Result<Self, IncompatibleOpenGl>
    {
        Self::new_inner(context, debug, false)
    }

    fn new_inner(
        context: glutin::Context<Pc>,
        debug: debug::DebugCallbackBehavior,
        checked: bool,
    ) -> Result<Self, IncompatibleOpenGl>
    {
        let glutin_context = Rc::new(RefCell::new(Takeable::new(context)));
        let glutin_backend = GlutinBackend(glutin_context.clone());
        let context = try!(unsafe { context::Context::new(glutin_backend, checked, debug) });
        Ok(Headless { context: context, glutin: glutin_context })
    }

    /// Start drawing on the backbuffer.
    ///
    /// This function returns a `Frame`, which can be used to draw on it. When the `Frame` is
    /// destroyed, the buffers are swapped.
    ///
    /// Note that destroying a `Frame` is immediate, even if vsync is enabled.
    ///
    /// If the framebuffer dimensions have changed since the last call to `draw`, the inner glutin
    /// context will be resized accordingly before returning the `Frame`.
    #[inline]
    pub fn draw(&self) -> Frame {
        Frame::new(self.context.clone(), self.get_framebuffer_dimensions())
    }
}
<|endoftext|>