<commit_before>use std::collections::HashMap;

use crate::controllers::frontend_prelude::*;

use crate::controllers::helpers::*;

use crate::controllers::helpers::pagination::{Paginated, PaginationOptions};
use crate::models::{
    CrateOwner, Email, Follow, NewEmail, OwnerKind, User, Version, VersionOwnerAction,
};
use crate::schema::{crate_owners, crates, emails, follows, users, versions};
use crate::views::{EncodableMe, EncodablePrivateUser, EncodableVersion, OwnedCrate};

/// Handles the `GET /me` route.
pub fn me(req: &mut dyn RequestExt) -> EndpointResult {
    let user_id = req.authenticate()?.forbid_api_token_auth()?.user_id();
    let conn = req.db_read_prefer_primary()?;

    let (user, verified, email, verification_sent): (User, Option<bool>, Option<String>, bool) =
        users::table
            .find(user_id)
            .left_join(emails::table)
            .select((
                users::all_columns,
                emails::verified.nullable(),
                emails::email.nullable(),
                emails::token_generated_at.nullable().is_not_null(),
            ))
            .first(&*conn)?;

    let owned_crates = CrateOwner::by_owner_kind(OwnerKind::User)
        .inner_join(crates::table)
        .filter(crate_owners::owner_id.eq(user_id))
        .select((crates::id, crates::name, crate_owners::email_notifications))
        .order(crates::name.asc())
        .load(&*conn)?
        .into_iter()
        .map(|(id, name, email_notifications)| OwnedCrate {
            id,
            name,
            email_notifications,
        })
        .collect();

    let verified = verified.unwrap_or(false);
    let verification_sent = verified || verification_sent;
    Ok(req.json(&EncodableMe {
        user: EncodablePrivateUser::from(user, email, verified, verification_sent),
        owned_crates,
    }))
}

/// Handles the `GET /me/updates` route.
pub fn updates(req: &mut dyn RequestExt) -> EndpointResult {
    use diesel::dsl::any;

    let authenticated_user = req.authenticate()?.forbid_api_token_auth()?;
    let user = authenticated_user.user();

    let followed_crates = Follow::belonging_to(&user).select(follows::crate_id);
    let query = versions::table
        .inner_join(crates::table)
        .left_outer_join(users::table)
        .filter(crates::id.eq(any(followed_crates)))
        .order(versions::created_at.desc())
        .select((
            versions::all_columns,
            crates::name,
            users::all_columns.nullable(),
        ))
        .pages_pagination(PaginationOptions::builder().gather(req)?);
    let conn = req.db_write()?;
    let data: Paginated<(Version, String, Option<User>)> = query.load(&*conn)?;
    let more = data.next_page_params().is_some();
    let versions = data.iter().map(|(v, _, _)| v).cloned().collect::<Vec<_>>();
    let data = data
        .into_iter()
        .zip(VersionOwnerAction::for_versions(&conn, &versions)?.into_iter())
        .map(|((v, cn, pb), voas)| (v, cn, pb, voas));

    let versions = data
        .into_iter()
        .map(|(version, crate_name, published_by, actions)| {
            EncodableVersion::from(version, &crate_name, published_by, actions)
        })
        .collect::<Vec<_>>();

    Ok(req.json(&json!({
        "versions": versions,
        "meta": { "more": more },
    })))
}

/// Handles the `PUT /users/:user_id` route.
pub fn update_user(req: &mut dyn RequestExt) -> EndpointResult {
    use self::emails::user_id;
    use diesel::insert_into;

    let authenticated_user = req.authenticate()?;

    let mut body = String::new();
    req.body().read_to_string(&mut body)?;

    let param_user_id = &req.params()["user_id"];
    let conn = req.db_write()?;
    let user = authenticated_user.user();

    // need to check if current user matches user to be updated
    if &user.id.to_string() != param_user_id {
        return Err(bad_request("current user does not match requested user"));
    }

    #[derive(Deserialize)]
    struct UserUpdate {
        user: User,
    }

    #[derive(Deserialize)]
    struct User {
        email: Option<String>,
    }

    let user_update: UserUpdate =
        serde_json::from_str(&body).map_err(|_| bad_request("invalid json request"))?;

    let user_email = match &user_update.user.email {
        Some(email) => email.trim(),
        None => return Err(bad_request("empty email rejected")),
    };

    if user_email.is_empty() {
        return Err(bad_request("empty email rejected"));
    }

    conn.transaction::<_, Box<dyn AppError>, _>(|| {
        let new_email = NewEmail {
            user_id: user.id,
            email: user_email,
        };

        let token: String = insert_into(emails::table)
            .values(&new_email)
            .on_conflict(user_id)
            .do_update()
            .set(&new_email)
            .returning(emails::token)
            .get_result(&*conn)
            .map_err(|_| server_error("Error in creating token"))?;

        // This swallows any errors that occur while attempting to send the email. Some users have
        // an invalid email set in their GitHub profile, and we should let them sign in even though
        // we're trying to silently use their invalid address during signup and can't send them an
        // email. They'll then have to provide a valid email address.
        let _ = req
            .app()
            .emails
            .send_user_confirm(user_email, &user.gh_login, &token);

        Ok(())
    })?;

    ok_true()
}

/// Handles the `PUT /confirm/:email_token` route
pub fn confirm_user_email(req: &mut dyn RequestExt) -> EndpointResult {
    use diesel::update;

    let conn = req.db_write()?;
    let req_token = &req.params()["email_token"];

    let updated_rows = update(emails::table.filter(emails::token.eq(req_token)))
        .set(emails::verified.eq(true))
        .execute(&*conn)?;

    if updated_rows == 0 {
        return Err(bad_request("Email belonging to token not found."));
    }

    ok_true()
}

/// Handles `PUT /user/:user_id/resend` route
pub fn regenerate_token_and_send(req: &mut dyn RequestExt) -> EndpointResult {
    use diesel::dsl::sql;
    use diesel::update;

    let param_user_id = req.params()["user_id"]
        .parse::<i32>()
        .map_err(|err| err.chain(bad_request("invalid user_id")))?;
    let authenticated_user = req.authenticate()?;
    let conn = req.db_write()?;
    let user = authenticated_user.user();

    // need to check if current user matches user to be updated
    if user.id != param_user_id {
        return Err(bad_request("current user does not match requested user"));
    }

    conn.transaction(|| {
        let email: Email = update(Email::belonging_to(&user))
            .set(emails::token.eq(sql("DEFAULT")))
            .get_result(&*conn)
            .map_err(|_| bad_request("Email could not be found"))?;

        req.app()
            .emails
            .send_user_confirm(&email.email, &user.gh_login, &email.token)
    })?;

    ok_true()
}

/// Handles `PUT /me/email_notifications` route
pub fn update_email_notifications(req: &mut dyn RequestExt) -> EndpointResult {
    use self::crate_owners::dsl::*;
    use diesel::pg::upsert::excluded;

    #[derive(Deserialize)]
    struct CrateEmailNotifications {
        id: i32,
        email_notifications: bool,
    }

    let mut body = String::new();
    req.body().read_to_string(&mut body)?;
    let updates: HashMap<i32, bool> = serde_json::from_str::<Vec<CrateEmailNotifications>>(&body)
        .map_err(|_| bad_request("invalid json request"))?
        .iter()
        .map(|c| (c.id, c.email_notifications))
        .collect();

    let user_id = req.authenticate()?.user_id();
    let conn = req.db_write()?;

    // Build inserts from existing crates belonging to the current user
    let to_insert = CrateOwner::by_owner_kind(OwnerKind::User)
        .filter(owner_id.eq(user_id))
        .select((crate_id, owner_id, owner_kind, email_notifications))
        .load(&*conn)?
        .into_iter()
        // Remove records whose `email_notifications` will not change from their current value
        .map(
            |(c_id, o_id, o_kind, e_notifications): (i32, i32, i32, bool)| {
                let current_e_notifications = *updates.get(&c_id).unwrap_or(&e_notifications);
                (
                    crate_id.eq(c_id),
                    owner_id.eq(o_id),
                    owner_kind.eq(o_kind),
                    email_notifications.eq(current_e_notifications),
                )
            },
        )
        .collect::<Vec<_>>();

    // Upsert crate owners; this should only actually exectute updates
    diesel::insert_into(crate_owners)
        .values(&to_insert)
        .on_conflict((crate_id, owner_id, owner_kind))
        .do_update()
        .set(email_notifications.eq(excluded(email_notifications)))
        .execute(&*conn)?;

    ok_true()
}
<commit_msg>Use `db_read_prefer_primary()` for `GET /me/updates` endpoint<commit_after>use std::collections::HashMap;

use crate::controllers::frontend_prelude::*;

use crate::controllers::helpers::*;

use crate::controllers::helpers::pagination::{Paginated, PaginationOptions};
use crate::models::{
    CrateOwner, Email, Follow, NewEmail, OwnerKind, User, Version, VersionOwnerAction,
};
use crate::schema::{crate_owners, crates, emails, follows, users, versions};
use crate::views::{EncodableMe, EncodablePrivateUser, EncodableVersion, OwnedCrate};

/// Handles the `GET /me` route.
pub fn me(req: &mut dyn RequestExt) -> EndpointResult {
    let user_id = req.authenticate()?.forbid_api_token_auth()?.user_id();
    let conn = req.db_read_prefer_primary()?;

    let (user, verified, email, verification_sent): (User, Option<bool>, Option<String>, bool) =
        users::table
            .find(user_id)
            .left_join(emails::table)
            .select((
                users::all_columns,
                emails::verified.nullable(),
                emails::email.nullable(),
                emails::token_generated_at.nullable().is_not_null(),
            ))
            .first(&*conn)?;

    let owned_crates = CrateOwner::by_owner_kind(OwnerKind::User)
        .inner_join(crates::table)
        .filter(crate_owners::owner_id.eq(user_id))
        .select((crates::id, crates::name, crate_owners::email_notifications))
        .order(crates::name.asc())
        .load(&*conn)?
        .into_iter()
        .map(|(id, name, email_notifications)| OwnedCrate {
            id,
            name,
            email_notifications,
        })
        .collect();

    let verified = verified.unwrap_or(false);
    let verification_sent = verified || verification_sent;
    Ok(req.json(&EncodableMe {
        user: EncodablePrivateUser::from(user, email, verified, verification_sent),
        owned_crates,
    }))
}

/// Handles the `GET /me/updates` route.
pub fn updates(req: &mut dyn RequestExt) -> EndpointResult {
    use diesel::dsl::any;

    let authenticated_user = req.authenticate()?.forbid_api_token_auth()?;
    let user = authenticated_user.user();

    let followed_crates = Follow::belonging_to(&user).select(follows::crate_id);
    let query = versions::table
        .inner_join(crates::table)
        .left_outer_join(users::table)
        .filter(crates::id.eq(any(followed_crates)))
        .order(versions::created_at.desc())
        .select((
            versions::all_columns,
            crates::name,
            users::all_columns.nullable(),
        ))
        .pages_pagination(PaginationOptions::builder().gather(req)?);
    let conn = req.db_read_prefer_primary()?;
    let data: Paginated<(Version, String, Option<User>)> = query.load(&*conn)?;
    let more = data.next_page_params().is_some();
    let versions = data.iter().map(|(v, _, _)| v).cloned().collect::<Vec<_>>();
    let data = data
        .into_iter()
        .zip(VersionOwnerAction::for_versions(&conn, &versions)?.into_iter())
        .map(|((v, cn, pb), voas)| (v, cn, pb, voas));

    let versions = data
        .into_iter()
        .map(|(version, crate_name, published_by, actions)| {
            EncodableVersion::from(version, &crate_name, published_by, actions)
        })
        .collect::<Vec<_>>();

    Ok(req.json(&json!({
        "versions": versions,
        "meta": { "more": more },
    })))
}

/// Handles the `PUT /users/:user_id` route.
pub fn update_user(req: &mut dyn RequestExt) -> EndpointResult {
    use self::emails::user_id;
    use diesel::insert_into;

    let authenticated_user = req.authenticate()?;

    let mut body = String::new();
    req.body().read_to_string(&mut body)?;

    let param_user_id = &req.params()["user_id"];
    let conn = req.db_write()?;
    let user = authenticated_user.user();

    // need to check if current user matches user to be updated
    if &user.id.to_string() != param_user_id {
        return Err(bad_request("current user does not match requested user"));
    }

    #[derive(Deserialize)]
    struct UserUpdate {
        user: User,
    }

    #[derive(Deserialize)]
    struct User {
        email: Option<String>,
    }

    let user_update: UserUpdate =
        serde_json::from_str(&body).map_err(|_| bad_request("invalid json request"))?;

    let user_email = match &user_update.user.email {
        Some(email) => email.trim(),
        None => return Err(bad_request("empty email rejected")),
    };

    if user_email.is_empty() {
        return Err(bad_request("empty email rejected"));
    }

    conn.transaction::<_, Box<dyn AppError>, _>(|| {
        let new_email = NewEmail {
            user_id: user.id,
            email: user_email,
        };

        let token: String = insert_into(emails::table)
            .values(&new_email)
            .on_conflict(user_id)
            .do_update()
            .set(&new_email)
            .returning(emails::token)
            .get_result(&*conn)
            .map_err(|_| server_error("Error in creating token"))?;

        // This swallows any errors that occur while attempting to send the email. Some users have
        // an invalid email set in their GitHub profile, and we should let them sign in even though
        // we're trying to silently use their invalid address during signup and can't send them an
        // email. They'll then have to provide a valid email address.
        let _ = req
            .app()
            .emails
            .send_user_confirm(user_email, &user.gh_login, &token);

        Ok(())
    })?;

    ok_true()
}

/// Handles the `PUT /confirm/:email_token` route
pub fn confirm_user_email(req: &mut dyn RequestExt) -> EndpointResult {
    use diesel::update;

    let conn = req.db_write()?;
    let req_token = &req.params()["email_token"];

    let updated_rows = update(emails::table.filter(emails::token.eq(req_token)))
        .set(emails::verified.eq(true))
        .execute(&*conn)?;

    if updated_rows == 0 {
        return Err(bad_request("Email belonging to token not found."));
    }

    ok_true()
}

/// Handles `PUT /user/:user_id/resend` route
pub fn regenerate_token_and_send(req: &mut dyn RequestExt) -> EndpointResult {
    use diesel::dsl::sql;
    use diesel::update;

    let param_user_id = req.params()["user_id"]
        .parse::<i32>()
        .map_err(|err| err.chain(bad_request("invalid user_id")))?;
    let authenticated_user = req.authenticate()?;
    let conn = req.db_write()?;
    let user = authenticated_user.user();

    // need to check if current user matches user to be updated
    if user.id != param_user_id {
        return Err(bad_request("current user does not match requested user"));
    }

    conn.transaction(|| {
        let email: Email = update(Email::belonging_to(&user))
            .set(emails::token.eq(sql("DEFAULT")))
            .get_result(&*conn)
            .map_err(|_| bad_request("Email could not be found"))?;

        req.app()
            .emails
            .send_user_confirm(&email.email, &user.gh_login, &email.token)
    })?;

    ok_true()
}

/// Handles `PUT /me/email_notifications` route
pub fn update_email_notifications(req: &mut dyn RequestExt) -> EndpointResult {
    use self::crate_owners::dsl::*;
    use diesel::pg::upsert::excluded;

    #[derive(Deserialize)]
    struct CrateEmailNotifications {
        id: i32,
        email_notifications: bool,
    }

    let mut body = String::new();
    req.body().read_to_string(&mut body)?;
    let updates: HashMap<i32, bool> = serde_json::from_str::<Vec<CrateEmailNotifications>>(&body)
        .map_err(|_| bad_request("invalid json request"))?
        .iter()
        .map(|c| (c.id, c.email_notifications))
        .collect();

    let user_id = req.authenticate()?.user_id();
    let conn = req.db_write()?;

    // Build inserts from existing crates belonging to the current user
    let to_insert = CrateOwner::by_owner_kind(OwnerKind::User)
        .filter(owner_id.eq(user_id))
        .select((crate_id, owner_id, owner_kind, email_notifications))
        .load(&*conn)?
        .into_iter()
        // Remove records whose `email_notifications` will not change from their current value
        .map(
            |(c_id, o_id, o_kind, e_notifications): (i32, i32, i32, bool)| {
                let current_e_notifications = *updates.get(&c_id).unwrap_or(&e_notifications);
                (
                    crate_id.eq(c_id),
                    owner_id.eq(o_id),
                    owner_kind.eq(o_kind),
                    email_notifications.eq(current_e_notifications),
                )
            },
        )
        .collect::<Vec<_>>();

    // Upsert crate owners; this should only actually exectute updates
    diesel::insert_into(crate_owners)
        .values(&to_insert)
        .on_conflict((crate_id, owner_id, owner_kind))
        .do_update()
        .set(email_notifications.eq(excluded(email_notifications)))
        .execute(&*conn)?;

    ok_true()
}
<|endoftext|>


<commit_before>#![crate_name = "seax_scheme"]
#![unstable(feature="scheme")]
#![crate_type = "lib"]
#![feature(convert)]
#![feature(box_syntax,box_patterns)]
#![feature(slice_patterns)]
#![feature(collections)]
#![feature(staged_api)]
#![staged_api]

//! Library for compiling Scheme programs to Seax SVM bytecode.
//!
//! This Scheme implementation is based on the Scheme programming
//! language described in the
//! [Revised6 Report on Scheme](http://www.r6rs.org/)
//! (R6RS). Any cases in which Seax Scheme differs from
//! R6RS Scheme are documented in the RustDoc for the
//! Seax Scheme library and in the Seax Scheme language manual.
//! Do note, however, that some inconsistencies between Seax Scheme
//! and R6RS may be the result of unimplemented features in
//! Seax Scheme.

#[macro_use]
extern crate seax_svm as svm;

#[macro_use]
extern crate log;

/// Contains the Scheme abstract syntax tree (AST).
///
/// The AST stores the semantic structure of a parsed Scheme
/// program, and is responsible for compiling those programs
/// to SVM bytecode instructions, performing semantic analysis
/// (as necessary), and (eventually) for optimizing programs.
#[unstable(feature = "ast")]
pub mod ast;

/// Contains the Scheme parser.
///
/// This parser is based on the
/// [Scheme grammar](final/html/r6rs/r6rs-Z-H-7.html) given in the
/// [Revised6 Report on Scheme](http://www.r6rs.org/)
/// (R6RS).
/// Any deviations from the R6RS standard, especially those with an impact
/// on the valid programs accepted by the parser, will be noted in the
/// parser's RustDoc.
#[unstable(feature="parser")]
pub mod parser;

mod forktab;

#[unstable(feature="forktable")]
pub use self::forktab::ForkTable;

use svm::slist::List;
use svm::cell::SVMCell;

use std::iter::FromIterator;

use self::ast::{ASTNode,ExprNode};

/// Compile a Scheme program into a list of SVM cells (a control stack)
///
/// # Arguments
///
///  + `program` - a string containing a Scheme program or line
///
/// # Return Value
///
///  + A `Result` containing either a `List` of `SVMCells` if the program
///    was compiled successfully, or a `String` with any error messages that
///    occured during compilation
///
/// TODO: Should this return a list of errors instead?
#[unstable(feature="compile")]
pub fn compile(program: &str) -> Result<List<SVMCell>, String> {
    parser::parse(program)
        .and_then(|tree: ExprNode     | {
            debug!("parsed:\n{:?}",tree);
            tree.compile(&ForkTable::new()) })
        .map(     |prog: Vec<SVMCell> | {
            debug!("compiled: {:?}",prog);
            let result = List::from_iter(prog);
            debug!("control stack: {:?}", result);
            result
             })
}<commit_msg>Update to build successfully on latest nightly.<commit_after>#![crate_name = "seax_scheme"]
#![unstable(feature="scheme")]
#![crate_type = "lib"]
#![feature(convert)]
#![feature(box_syntax,box_patterns)]
#![feature(vec_push_all)]
#![feature(slice_patterns)]
#![feature(collections)]
#![feature(staged_api)]
#![staged_api]

//! Library for compiling Scheme programs to Seax SVM bytecode.
//!
//! This Scheme implementation is based on the Scheme programming
//! language described in the
//! [Revised6 Report on Scheme](http://www.r6rs.org/)
//! (R6RS). Any cases in which Seax Scheme differs from
//! R6RS Scheme are documented in the RustDoc for the
//! Seax Scheme library and in the Seax Scheme language manual.
//! Do note, however, that some inconsistencies between Seax Scheme
//! and R6RS may be the result of unimplemented features in
//! Seax Scheme.

#[macro_use]
extern crate seax_svm as svm;

#[macro_use]
extern crate log;

/// Contains the Scheme abstract syntax tree (AST).
///
/// The AST stores the semantic structure of a parsed Scheme
/// program, and is responsible for compiling those programs
/// to SVM bytecode instructions, performing semantic analysis
/// (as necessary), and (eventually) for optimizing programs.
#[unstable(feature = "ast")]
pub mod ast;

/// Contains the Scheme parser.
///
/// This parser is based on the
/// [Scheme grammar](final/html/r6rs/r6rs-Z-H-7.html) given in the
/// [Revised6 Report on Scheme](http://www.r6rs.org/)
/// (R6RS).
/// Any deviations from the R6RS standard, especially those with an impact
/// on the valid programs accepted by the parser, will be noted in the
/// parser's RustDoc.
#[unstable(feature="parser")]
pub mod parser;

mod forktab;

#[unstable(feature="forktable")]
pub use self::forktab::ForkTable;

use svm::slist::List;
use svm::cell::SVMCell;

use std::iter::FromIterator;

use self::ast::{ASTNode,ExprNode};

/// Compile a Scheme program into a list of SVM cells (a control stack)
///
/// # Arguments
///
///  + `program` - a string containing a Scheme program or line
///
/// # Return Value
///
///  + A `Result` containing either a `List` of `SVMCells` if the program
///    was compiled successfully, or a `String` with any error messages that
///    occured during compilation
///
/// TODO: Should this return a list of errors instead?
#[unstable(feature="compile")]
pub fn compile(program: &str) -> Result<List<SVMCell>, String> {
    parser::parse(program)
        .and_then(|tree: ExprNode     | {
            debug!("parsed:\n{:?}",tree);
            tree.compile(&ForkTable::new()) })
        .map(     |prog: Vec<SVMCell> | {
            debug!("compiled: {:?}",prog);
            let result = List::from_iter(prog);
            debug!("control stack: {:?}", result);
            result
             })
}
<|endoftext|>


<commit_before>use std::cell::Cell;
use std::cell::RefCell;

use cairo::Rectangle;

use allowed_url::Fragment;
use aspect_ratio::*;
use attributes::Attribute;
use css::CssStyles;
use dpi::Dpi;
use drawing_ctx::{DrawingCtx, ViewParams};
use error::{AttributeResultExt, RenderingError};
use float_eq_cairo::ApproxEqCairo;
use length::*;
use node::*;
use parsers::{Parse, ParseValue};
use properties::{ComputedValues, Overflow};
use property_bag::{OwnedPropertyBag, PropertyBag};
use rect::RectangleExt;
use viewbox::*;
use viewport::{draw_in_viewport, ClipMode};

pub struct NodeGroup();

impl NodeGroup {
    pub fn new() -> NodeGroup {
        NodeGroup()
    }
}

impl NodeTrait for NodeGroup {
    fn set_atts(&self, _: &RsvgNode, _: &PropertyBag<'_>) -> NodeResult {
        Ok(())
    }

    fn draw(
        &self,
        node: &RsvgNode,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx,
        clipping: bool,
    ) -> Result<(), RenderingError> {
        let values = cascaded.get();

        draw_ctx.with_discrete_layer(node, values, clipping, &mut |dc| {
            node.draw_children(cascaded, dc, clipping)
        })
    }
}

pub struct NodeDefs();

impl NodeDefs {
    pub fn new() -> NodeDefs {
        NodeDefs()
    }
}

impl NodeTrait for NodeDefs {
    fn set_atts(&self, _: &RsvgNode, _: &PropertyBag<'_>) -> NodeResult {
        Ok(())
    }
}

pub struct NodeSwitch();

impl NodeSwitch {
    pub fn new() -> NodeSwitch {
        NodeSwitch()
    }
}

impl NodeTrait for NodeSwitch {
    fn set_atts(&self, _: &RsvgNode, _: &PropertyBag<'_>) -> NodeResult {
        Ok(())
    }

    fn draw(
        &self,
        node: &RsvgNode,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx,
        clipping: bool,
    ) -> Result<(), RenderingError> {
        let values = cascaded.get();

        draw_ctx.with_discrete_layer(node, values, clipping, &mut |dc| {
            if let Some(child) = node
                .children()
                .filter(|c| c.get_type() != NodeType::Chars)
                .find(|c| c.get_cond())
            {
                dc.draw_node_from_stack(&CascadedValues::new(cascaded, &child), &child, clipping)
            } else {
                Ok(())
            }
        })
    }
}

/// Intrinsic dimensions of an SVG document fragment
#[derive(Copy, Clone)]
pub struct IntrinsicDimensions {
    pub width: Option<LengthHorizontal>,
    pub height: Option<LengthVertical>,
    pub vbox: Option<ViewBox>,
}

pub struct NodeSvg {
    preserve_aspect_ratio: Cell<AspectRatio>,
    x: Cell<LengthHorizontal>,
    y: Cell<LengthVertical>,
    w: Cell<LengthHorizontal>,
    h: Cell<LengthVertical>,
    vbox: Cell<Option<ViewBox>>,
    pbag: RefCell<Option<OwnedPropertyBag>>,
}

impl NodeSvg {
    pub fn new() -> NodeSvg {
        NodeSvg {
            preserve_aspect_ratio: Cell::new(AspectRatio::default()),
            x: Cell::new(LengthHorizontal::parse_str("0").unwrap()),
            y: Cell::new(LengthVertical::parse_str("0").unwrap()),
            w: Cell::new(LengthHorizontal::parse_str("100%").unwrap()),
            h: Cell::new(LengthVertical::parse_str("100%").unwrap()),
            vbox: Cell::new(None),
            pbag: RefCell::new(None),
        }
    }

    pub fn set_delayed_style(&self, node: &RsvgNode, css_styles: &CssStyles) {
        if let Some(owned_pbag) = self.pbag.borrow().as_ref() {
            let pbag = PropertyBag::from_owned(owned_pbag);
            node.set_style(css_styles, &pbag);
        }
    }

    pub fn get_size(&self, dpi: Dpi) -> Option<(i32, i32)> {
        match (self.w.get(), self.h.get(), self.vbox.get()) {
            (w, h, Some(vbox)) => Some((
                w.hand_normalize(dpi.x(), vbox.width, 12.0).round() as i32,
                h.hand_normalize(dpi.y(), vbox.height, 12.0).round() as i32,
            )),
            (w, h, None) if w.unit() != LengthUnit::Percent && h.unit() != LengthUnit::Percent => {
                Some((
                    w.hand_normalize(dpi.x(), 0.0, 12.0).round() as i32,
                    h.hand_normalize(dpi.y(), 0.0, 12.0).round() as i32,
                ))
            }
            (_, _, _) => None,
        }
    }

    pub fn get_intrinsic_dimensions(&self) -> IntrinsicDimensions {
        // FIXME: width/height are Option<>, and we don't store that yet;
        // we resolve to 100% for default values at parsing time.
        IntrinsicDimensions {
            width: Some(self.w.get()),
            height: Some(self.h.get()),
            vbox: self.vbox.get(),
        }
    }

    fn get_viewport(&self, values: &ComputedValues, params: &ViewParams) -> Rectangle {
        let x = self.x.get();
        let y = self.y.get();
        let w = self.w.get();
        let h = self.h.get();

        Rectangle::new(
            x.normalize(values, &params),
            y.normalize(values, &params),
            w.normalize(values, &params),
            h.normalize(values, &params),
        )
    }
}

impl NodeTrait for NodeSvg {
    fn set_atts(&self, node: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
        // SVG element has overflow:hidden
        // https://www.w3.org/TR/SVG/styling.html#UAStyleSheet
        node.set_overflow_hidden();

        // x & y attributes have no effect on outermost svg
        // http://www.w3.org/TR/SVG/struct.html#SVGElement
        let is_inner_svg = node.get_parent().is_some();

        for (attr, value) in pbag.iter() {
            match attr {
                Attribute::PreserveAspectRatio => {
                    self.preserve_aspect_ratio.set(attr.parse(value)?)
                }

                Attribute::X => {
                    if is_inner_svg {
                        self.x.set(attr.parse(value)?);
                    }
                }

                Attribute::Y => {
                    if is_inner_svg {
                        self.y.set(attr.parse(value)?);
                    }
                }

                Attribute::Width => self
                    .w
                    .set(attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)?),

                Attribute::Height => self
                    .h
                    .set(attr.parse_and_validate(value, LengthVertical::check_nonnegative)?),

                Attribute::ViewBox => self.vbox.set(attr.parse(value).map(Some)?),

                _ => (),
            }
        }

        // The "style" sub-element is not loaded yet here, so we need
        // to store other attributes to be applied later.
        *self.pbag.borrow_mut() = Some(pbag.to_owned());

        Ok(())
    }

    fn draw(
        &self,
        node: &RsvgNode,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx,
        clipping: bool,
    ) -> Result<(), RenderingError> {
        let values = cascaded.get();

        let params = draw_ctx.get_view_params();

        let clip_mode = if !values.is_overflow() && node.get_parent().is_some() {
            Some(ClipMode::ClipToViewport)
        } else {
            None
        };

        draw_in_viewport(
            &self.get_viewport(values, &params),
            clip_mode,
            self.vbox.get(),
            self.preserve_aspect_ratio.get(),
            node,
            values,
            draw_ctx,
            clipping,
            &mut |dc| {
                // we don't push a layer because draw_in_viewport() already does it
                node.draw_children(cascaded, dc, clipping)
            },
        )
    }
}

pub struct NodeUse {
    link: RefCell<Option<Fragment>>,
    x: Cell<LengthHorizontal>,
    y: Cell<LengthVertical>,
    w: Cell<Option<LengthHorizontal>>,
    h: Cell<Option<LengthVertical>>,
}

impl NodeUse {
    pub fn new() -> NodeUse {
        NodeUse {
            link: RefCell::new(None),
            x: Cell::new(Default::default()),
            y: Cell::new(Default::default()),
            w: Cell::new(None),
            h: Cell::new(None),
        }
    }
}

impl NodeTrait for NodeUse {
    fn set_atts(&self, _: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
        for (attr, value) in pbag.iter() {
            match attr {
                Attribute::XlinkHref => {
                    *self.link.borrow_mut() =
                        Some(Fragment::parse(value).attribute(Attribute::XlinkHref)?)
                }

                Attribute::X => self.x.set(attr.parse(value)?),
                Attribute::Y => self.y.set(attr.parse(value)?),

                Attribute::Width => self.w.set(
                    attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)
                        .map(Some)?,
                ),
                Attribute::Height => self.h.set(
                    attr.parse_and_validate(value, LengthVertical::check_nonnegative)
                        .map(Some)?,
                ),

                _ => (),
            }
        }

        Ok(())
    }

    fn draw(
        &self,
        node: &RsvgNode,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx,
        clipping: bool,
    ) -> Result<(), RenderingError> {
        let values = cascaded.get();

        let link = self.link.borrow();

        if link.is_none() {
            return Ok(());
        }

        let link = link.as_ref().unwrap();

        let child = if let Some(acquired) = draw_ctx.get_acquired_node(link) {
            // Here we clone the acquired child, so that we can drop the AcquiredNode as
            // early as possible.  This is so that the child's drawing method will be able
            // to re-acquire the child for other purposes.
            acquired.get().clone()
        } else {
            rsvg_log!(
                "element {} references nonexistent \"{}\"",
                node.get_human_readable_name(),
                link,
            );
            return Ok(());
        };

        if Node::is_ancestor(child.clone(), node.clone()) {
            // or, if we're <use>'ing ourselves
            return Err(RenderingError::CircularReference);
        }

        draw_ctx.increase_num_elements_rendered_through_use(1);

        let params = draw_ctx.get_view_params();

        let nx = self.x.get().normalize(values, &params);
        let ny = self.y.get().normalize(values, &params);

        // If attributes ‘width’ and/or ‘height’ are not specified,
        // [...] use values of '100%' for these attributes.
        // From https://www.w3.org/TR/SVG/struct.html#UseElement in
        // "If the ‘use’ element references a ‘symbol’ element"

        let nw = self
            .w
            .get()
            .unwrap_or_else(|| LengthHorizontal::parse_str("100%").unwrap())
            .normalize(values, &params);
        let nh = self
            .h
            .get()
            .unwrap_or_else(|| LengthVertical::parse_str("100%").unwrap())
            .normalize(values, &params);

        // width or height set to 0 disables rendering of the element
        // https://www.w3.org/TR/SVG/struct.html#UseElementWidthAttribute
        if nw.approx_eq_cairo(&0.0) || nh.approx_eq_cairo(&0.0) {
            return Ok(());
        }

        let viewport = Rectangle::new(nx, ny, nw, nh);

        if child.get_type() != NodeType::Symbol {
            let cr = draw_ctx.get_cairo_context();
            cr.translate(viewport.x, viewport.y);

            draw_ctx.with_discrete_layer(node, values, clipping, &mut |dc| {
                dc.draw_node_from_stack(
                    &CascadedValues::new_from_values(&child, values),
                    &child,
                    clipping,
                )
            })
        } else {
            child.with_impl(|symbol: &NodeSymbol| {
                let clip_mode = if !values.is_overflow()
                    || (values.overflow == Overflow::Visible && child.is_overflow())
                {
                    Some(ClipMode::ClipToVbox)
                } else {
                    None
                };

                draw_in_viewport(
                    &viewport,
                    clip_mode,
                    symbol.vbox.get(),
                    symbol.preserve_aspect_ratio.get(),
                    node,
                    values,
                    draw_ctx,
                    clipping,
                    &mut |dc| {
                        // We don't push a layer because draw_in_viewport() already does it
                        child.draw_children(
                            &CascadedValues::new_from_values(&child, values),
                            dc,
                            clipping,
                        )
                    },
                )
            })
        }
    }
}

pub struct NodeSymbol {
    preserve_aspect_ratio: Cell<AspectRatio>,
    vbox: Cell<Option<ViewBox>>,
}

impl NodeSymbol {
    pub fn new() -> NodeSymbol {
        NodeSymbol {
            preserve_aspect_ratio: Cell::new(AspectRatio::default()),
            vbox: Cell::new(None),
        }
    }
}

impl NodeTrait for NodeSymbol {
    fn set_atts(&self, node: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
        // symbol element has overflow:hidden
        // https://www.w3.org/TR/SVG/styling.html#UAStyleSheet
        node.set_overflow_hidden();

        for (attr, value) in pbag.iter() {
            match attr {
                Attribute::PreserveAspectRatio => {
                    self.preserve_aspect_ratio.set(attr.parse(value)?)
                }

                Attribute::ViewBox => self.vbox.set(attr.parse(value).map(Some)?),

                _ => (),
            }
        }

        Ok(())
    }
}
<commit_msg>NodeSvg: store Option<> for the x/y/w/h viewport, per the spec<commit_after>use std::cell::Cell;
use std::cell::RefCell;

use cairo::Rectangle;

use allowed_url::Fragment;
use aspect_ratio::*;
use attributes::Attribute;
use css::CssStyles;
use dpi::Dpi;
use drawing_ctx::{DrawingCtx, ViewParams};
use error::{AttributeResultExt, RenderingError};
use float_eq_cairo::ApproxEqCairo;
use length::*;
use node::*;
use parsers::{Parse, ParseValue};
use properties::{ComputedValues, Overflow};
use property_bag::{OwnedPropertyBag, PropertyBag};
use rect::RectangleExt;
use viewbox::*;
use viewport::{draw_in_viewport, ClipMode};

pub struct NodeGroup();

impl NodeGroup {
    pub fn new() -> NodeGroup {
        NodeGroup()
    }
}

impl NodeTrait for NodeGroup {
    fn set_atts(&self, _: &RsvgNode, _: &PropertyBag<'_>) -> NodeResult {
        Ok(())
    }

    fn draw(
        &self,
        node: &RsvgNode,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx,
        clipping: bool,
    ) -> Result<(), RenderingError> {
        let values = cascaded.get();

        draw_ctx.with_discrete_layer(node, values, clipping, &mut |dc| {
            node.draw_children(cascaded, dc, clipping)
        })
    }
}

pub struct NodeDefs();

impl NodeDefs {
    pub fn new() -> NodeDefs {
        NodeDefs()
    }
}

impl NodeTrait for NodeDefs {
    fn set_atts(&self, _: &RsvgNode, _: &PropertyBag<'_>) -> NodeResult {
        Ok(())
    }
}

pub struct NodeSwitch();

impl NodeSwitch {
    pub fn new() -> NodeSwitch {
        NodeSwitch()
    }
}

impl NodeTrait for NodeSwitch {
    fn set_atts(&self, _: &RsvgNode, _: &PropertyBag<'_>) -> NodeResult {
        Ok(())
    }

    fn draw(
        &self,
        node: &RsvgNode,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx,
        clipping: bool,
    ) -> Result<(), RenderingError> {
        let values = cascaded.get();

        draw_ctx.with_discrete_layer(node, values, clipping, &mut |dc| {
            if let Some(child) = node
                .children()
                .filter(|c| c.get_type() != NodeType::Chars)
                .find(|c| c.get_cond())
            {
                dc.draw_node_from_stack(&CascadedValues::new(cascaded, &child), &child, clipping)
            } else {
                Ok(())
            }
        })
    }
}

/// Intrinsic dimensions of an SVG document fragment
#[derive(Copy, Clone)]
pub struct IntrinsicDimensions {
    pub width: Option<LengthHorizontal>,
    pub height: Option<LengthVertical>,
    pub vbox: Option<ViewBox>,
}

pub struct NodeSvg {
    preserve_aspect_ratio: Cell<AspectRatio>,
    x: Cell<Option<LengthHorizontal>>,
    y: Cell<Option<LengthVertical>>,
    w: Cell<Option<LengthHorizontal>>,
    h: Cell<Option<LengthVertical>>,
    vbox: Cell<Option<ViewBox>>,
    pbag: RefCell<Option<OwnedPropertyBag>>,
}

impl NodeSvg {
    pub fn new() -> NodeSvg {
        NodeSvg {
            preserve_aspect_ratio: Cell::new(AspectRatio::default()),
            x: Cell::new(None),
            y: Cell::new(None),
            w: Cell::new(None),
            h: Cell::new(None),
            vbox: Cell::new(None),
            pbag: RefCell::new(None),
        }
    }

    pub fn set_delayed_style(&self, node: &RsvgNode, css_styles: &CssStyles) {
        if let Some(owned_pbag) = self.pbag.borrow().as_ref() {
            let pbag = PropertyBag::from_owned(owned_pbag);
            node.set_style(css_styles, &pbag);
        }
    }

    pub fn get_size(&self, dpi: Dpi) -> Option<(i32, i32)> {
        let (_, _, w, h) = self.get_unnormalized_viewport();

        match (w, h, self.vbox.get()) {
            (w, h, Some(vbox)) => Some((
                w.hand_normalize(dpi.x(), vbox.width, 12.0).round() as i32,
                h.hand_normalize(dpi.y(), vbox.height, 12.0).round() as i32,
            )),
            (w, h, None) if w.unit() != LengthUnit::Percent && h.unit() != LengthUnit::Percent => {
                Some((
                    w.hand_normalize(dpi.x(), 0.0, 12.0).round() as i32,
                    h.hand_normalize(dpi.y(), 0.0, 12.0).round() as i32,
                ))
            }
            (_, _, _) => None,
        }
    }

    pub fn get_intrinsic_dimensions(&self) -> IntrinsicDimensions {
        IntrinsicDimensions {
            width: self.w.get(),
            height: self.h.get(),
            vbox: self.vbox.get(),
        }
    }

    // returns (x, y, w, h)
    fn get_unnormalized_viewport(
        &self,
    ) -> (
        LengthHorizontal,
        LengthVertical,
        LengthHorizontal,
        LengthVertical,
    ) {
        // these defaults are per the spec
        let x = self
            .x
            .get()
            .unwrap_or_else(|| LengthHorizontal::parse_str("0").unwrap());
        let y = self
            .y
            .get()
            .unwrap_or_else(|| LengthVertical::parse_str("0").unwrap());
        let w = self
            .w
            .get()
            .unwrap_or_else(|| LengthHorizontal::parse_str("100%").unwrap());
        let h = self
            .h
            .get()
            .unwrap_or_else(|| LengthVertical::parse_str("100%").unwrap());

        (x, y, w, h)
    }

    fn get_viewport(&self, values: &ComputedValues, params: &ViewParams) -> Rectangle {
        let (x, y, w, h) = self.get_unnormalized_viewport();

        Rectangle::new(
            x.normalize(values, &params),
            y.normalize(values, &params),
            w.normalize(values, &params),
            h.normalize(values, &params),
        )
    }
}

impl NodeTrait for NodeSvg {
    fn set_atts(&self, node: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
        // SVG element has overflow:hidden
        // https://www.w3.org/TR/SVG/styling.html#UAStyleSheet
        node.set_overflow_hidden();

        // x & y attributes have no effect on outermost svg
        // http://www.w3.org/TR/SVG/struct.html#SVGElement
        let is_inner_svg = node.get_parent().is_some();

        for (attr, value) in pbag.iter() {
            match attr {
                Attribute::PreserveAspectRatio => {
                    self.preserve_aspect_ratio.set(attr.parse(value)?)
                }

                Attribute::X => {
                    if is_inner_svg {
                        self.x.set(Some(attr.parse(value)?));
                    }
                }

                Attribute::Y => {
                    if is_inner_svg {
                        self.y.set(Some(attr.parse(value)?));
                    }
                }

                Attribute::Width => self.w.set(Some(
                    attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)?,
                )),

                Attribute::Height => self.h.set(Some(
                    attr.parse_and_validate(value, LengthVertical::check_nonnegative)?,
                )),

                Attribute::ViewBox => self.vbox.set(attr.parse(value).map(Some)?),

                _ => (),
            }
        }

        // The "style" sub-element is not loaded yet here, so we need
        // to store other attributes to be applied later.
        *self.pbag.borrow_mut() = Some(pbag.to_owned());

        Ok(())
    }

    fn draw(
        &self,
        node: &RsvgNode,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx,
        clipping: bool,
    ) -> Result<(), RenderingError> {
        let values = cascaded.get();

        let params = draw_ctx.get_view_params();

        let clip_mode = if !values.is_overflow() && node.get_parent().is_some() {
            Some(ClipMode::ClipToViewport)
        } else {
            None
        };

        draw_in_viewport(
            &self.get_viewport(values, &params),
            clip_mode,
            self.vbox.get(),
            self.preserve_aspect_ratio.get(),
            node,
            values,
            draw_ctx,
            clipping,
            &mut |dc| {
                // we don't push a layer because draw_in_viewport() already does it
                node.draw_children(cascaded, dc, clipping)
            },
        )
    }
}

pub struct NodeUse {
    link: RefCell<Option<Fragment>>,
    x: Cell<LengthHorizontal>,
    y: Cell<LengthVertical>,
    w: Cell<Option<LengthHorizontal>>,
    h: Cell<Option<LengthVertical>>,
}

impl NodeUse {
    pub fn new() -> NodeUse {
        NodeUse {
            link: RefCell::new(None),
            x: Cell::new(Default::default()),
            y: Cell::new(Default::default()),
            w: Cell::new(None),
            h: Cell::new(None),
        }
    }
}

impl NodeTrait for NodeUse {
    fn set_atts(&self, _: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
        for (attr, value) in pbag.iter() {
            match attr {
                Attribute::XlinkHref => {
                    *self.link.borrow_mut() =
                        Some(Fragment::parse(value).attribute(Attribute::XlinkHref)?)
                }

                Attribute::X => self.x.set(attr.parse(value)?),
                Attribute::Y => self.y.set(attr.parse(value)?),

                Attribute::Width => self.w.set(
                    attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)
                        .map(Some)?,
                ),
                Attribute::Height => self.h.set(
                    attr.parse_and_validate(value, LengthVertical::check_nonnegative)
                        .map(Some)?,
                ),

                _ => (),
            }
        }

        Ok(())
    }

    fn draw(
        &self,
        node: &RsvgNode,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx,
        clipping: bool,
    ) -> Result<(), RenderingError> {
        let values = cascaded.get();

        let link = self.link.borrow();

        if link.is_none() {
            return Ok(());
        }

        let link = link.as_ref().unwrap();

        let child = if let Some(acquired) = draw_ctx.get_acquired_node(link) {
            // Here we clone the acquired child, so that we can drop the AcquiredNode as
            // early as possible.  This is so that the child's drawing method will be able
            // to re-acquire the child for other purposes.
            acquired.get().clone()
        } else {
            rsvg_log!(
                "element {} references nonexistent \"{}\"",
                node.get_human_readable_name(),
                link,
            );
            return Ok(());
        };

        if Node::is_ancestor(child.clone(), node.clone()) {
            // or, if we're <use>'ing ourselves
            return Err(RenderingError::CircularReference);
        }

        draw_ctx.increase_num_elements_rendered_through_use(1);

        let params = draw_ctx.get_view_params();

        let nx = self.x.get().normalize(values, &params);
        let ny = self.y.get().normalize(values, &params);

        // If attributes ‘width’ and/or ‘height’ are not specified,
        // [...] use values of '100%' for these attributes.
        // From https://www.w3.org/TR/SVG/struct.html#UseElement in
        // "If the ‘use’ element references a ‘symbol’ element"

        let nw = self
            .w
            .get()
            .unwrap_or_else(|| LengthHorizontal::parse_str("100%").unwrap())
            .normalize(values, &params);
        let nh = self
            .h
            .get()
            .unwrap_or_else(|| LengthVertical::parse_str("100%").unwrap())
            .normalize(values, &params);

        // width or height set to 0 disables rendering of the element
        // https://www.w3.org/TR/SVG/struct.html#UseElementWidthAttribute
        if nw.approx_eq_cairo(&0.0) || nh.approx_eq_cairo(&0.0) {
            return Ok(());
        }

        let viewport = Rectangle::new(nx, ny, nw, nh);

        if child.get_type() != NodeType::Symbol {
            let cr = draw_ctx.get_cairo_context();
            cr.translate(viewport.x, viewport.y);

            draw_ctx.with_discrete_layer(node, values, clipping, &mut |dc| {
                dc.draw_node_from_stack(
                    &CascadedValues::new_from_values(&child, values),
                    &child,
                    clipping,
                )
            })
        } else {
            child.with_impl(|symbol: &NodeSymbol| {
                let clip_mode = if !values.is_overflow()
                    || (values.overflow == Overflow::Visible && child.is_overflow())
                {
                    Some(ClipMode::ClipToVbox)
                } else {
                    None
                };

                draw_in_viewport(
                    &viewport,
                    clip_mode,
                    symbol.vbox.get(),
                    symbol.preserve_aspect_ratio.get(),
                    node,
                    values,
                    draw_ctx,
                    clipping,
                    &mut |dc| {
                        // We don't push a layer because draw_in_viewport() already does it
                        child.draw_children(
                            &CascadedValues::new_from_values(&child, values),
                            dc,
                            clipping,
                        )
                    },
                )
            })
        }
    }
}

pub struct NodeSymbol {
    preserve_aspect_ratio: Cell<AspectRatio>,
    vbox: Cell<Option<ViewBox>>,
}

impl NodeSymbol {
    pub fn new() -> NodeSymbol {
        NodeSymbol {
            preserve_aspect_ratio: Cell::new(AspectRatio::default()),
            vbox: Cell::new(None),
        }
    }
}

impl NodeTrait for NodeSymbol {
    fn set_atts(&self, node: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
        // symbol element has overflow:hidden
        // https://www.w3.org/TR/SVG/styling.html#UAStyleSheet
        node.set_overflow_hidden();

        for (attr, value) in pbag.iter() {
            match attr {
                Attribute::PreserveAspectRatio => {
                    self.preserve_aspect_ratio.set(attr.parse(value)?)
                }

                Attribute::ViewBox => self.vbox.set(attr.parse(value).map(Some)?),

                _ => (),
            }
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>#![allow(non_snake_case, non_camel_case_types)]

pub use self::error::*;

use std::mem;

mod error;

pub fn SQLITE_STATIC() -> sqlite3_destructor_type {
    Some(unsafe { mem::transmute(0isize) })
}

pub fn SQLITE_TRANSIENT() -> sqlite3_destructor_type {
    Some(unsafe { mem::transmute(-1isize) })
}

/// Run-Time Limit Categories
#[repr(C)]
pub enum Limit {
    /// The maximum size of any string or BLOB or table row, in bytes.
    SQLITE_LIMIT_LENGTH = SQLITE_LIMIT_LENGTH as isize,
    /// The maximum length of an SQL statement, in bytes.
    SQLITE_LIMIT_SQL_LENGTH = SQLITE_LIMIT_SQL_LENGTH as isize,
    /// The maximum number of columns in a table definition or in the result set of a SELECT
    /// or the maximum number of columns in an index or in an ORDER BY or GROUP BY clause.
    SQLITE_LIMIT_COLUMN = SQLITE_LIMIT_COLUMN as isize,
    /// The maximum depth of the parse tree on any expression.
    SQLITE_LIMIT_EXPR_DEPTH = SQLITE_LIMIT_EXPR_DEPTH as isize,
    /// The maximum number of terms in a compound SELECT statement.
    SQLITE_LIMIT_COMPOUND_SELECT = SQLITE_LIMIT_COMPOUND_SELECT as isize,
    /// The maximum number of instructions in a virtual machine program used to implement an SQL statement.
    SQLITE_LIMIT_VDBE_OP = SQLITE_LIMIT_VDBE_OP as isize,
    /// The maximum number of arguments on a function.
    SQLITE_LIMIT_FUNCTION_ARG = SQLITE_LIMIT_FUNCTION_ARG as isize,
    /// The maximum number of attached databases.
    SQLITE_LIMIT_ATTACHED = SQLITE_LIMIT_ATTACHED as isize,
    /// The maximum length of the pattern argument to the LIKE or GLOB operators.
    SQLITE_LIMIT_LIKE_PATTERN_LENGTH = SQLITE_LIMIT_LIKE_PATTERN_LENGTH as isize,
    /// The maximum index number of any parameter in an SQL statement.
    SQLITE_LIMIT_VARIABLE_NUMBER = SQLITE_LIMIT_VARIABLE_NUMBER as isize,
    /// The maximum depth of recursion for triggers.
    SQLITE_LIMIT_TRIGGER_DEPTH = 10,
    /// The maximum number of auxiliary worker threads that a single prepared statement may start.
    SQLITE_LIMIT_WORKER_THREADS = 11,
}

include!(concat!(env!("OUT_DIR"), "/bindgen.rs"));

#[cfg(test)]
extern crate rand;

#[cfg(all(feature = "sqlcipher", test))]
mod tests {
    use std::env;
    use std::mem;
    use std::path::{Path, PathBuf};
    use std::fs;
    use std::ffi::CString;
    use std::ptr;
    use std::os::raw::c_int;
    use rand::{thread_rng, Rng};

    const FIRST_KEY: &'static str = "my first passphrase";
    const SECOND_KEY: &'static str = "my second passphrase";
    const DB_PATH: &'static str = "bindings_test_sqlcipher";
    const OPEN_FLAGS: c_int = super::SQLITE_OPEN_READWRITE|super::SQLITE_OPEN_URI|super::SQLITE_OPEN_NOMUTEX;

    fn new_db() -> PathBuf {
        let out_dir = env::var("OUT_DIR").unwrap();
        let stub: String = thread_rng().gen_ascii_chars().take(10).collect();
        let path = Path::new(out_dir.as_str());
        let mut pbuf = path.join(DB_PATH);
        pbuf.set_extension(stub);
        pbuf
    }

    fn create_test_db(path: &PathBuf, key: &str) -> *mut super::sqlite3 {
        fs::remove_file(path);
        let flags = super::SQLITE_OPEN_CREATE | OPEN_FLAGS;
        _open_test_db(path, flags, key)
    }

    fn open_test_db(path: &PathBuf, key: &str) -> *mut super::sqlite3 {
        _open_test_db(path, OPEN_FLAGS, key)
    }

    fn _open_test_db(path: &PathBuf, flags: c_int, key: &str) -> *mut super::sqlite3 {
        unsafe {
            let mut db: *mut super::sqlite3 = mem::uninitialized();
            let path_ = CString::new(path.to_str().unwrap()).unwrap();
            let r = super::sqlite3_open_v2(path_.as_ptr(), &mut db, flags, ptr::null());

            assert!(r == super::SQLITE_OK, "Can't open {0}: {1}", path.to_str().unwrap(), r);

            let database_name = CString::new("main").unwrap();
            let passphrase = CString::new(key).unwrap();
            let c_len = (key.len() + 1) as c_int;
            let r = super::sqlite3_key_v2(db, database_name.as_ptr(), passphrase.as_ptr() as *mut ::std::os::raw::c_void, c_len);

            return db;
        }
    }

    fn exec(db: *mut super::sqlite3, cmd: &str) -> Option<c_int> {
        let mut stmt: *mut super::sqlite3_stmt = unsafe { mem::uninitialized() };
        let query = CString::new(cmd);
        unsafe {
            let len_with_nul = (cmd.len() + 1) as c_int;
            let r = super::sqlite3_prepare_v2(db, query.unwrap().as_ptr(), len_with_nul, &mut stmt, ptr::null_mut());

            let r = super::sqlite3_step(stmt);
            super::sqlite3_finalize(stmt);

            match r {
                super::SQLITE_OK => None,
                super::SQLITE_DONE => None,
                super::SQLITE_ROW => None,
                super::SQLITE_ERROR => panic!("SQLite error encountered"),
                super::SQLITE_BUSY => panic!("SQLite claiming busy: finalize statement, maybe?"),
                _ => panic!("SQlite returned a weird error code: {0}: \"{1}\"", r, cmd)

            }
        }
    }

    #[test]
    fn test_sqlcipher_create_and_encrypt() {
        create_and_encrypt(FIRST_KEY);
    }

    fn create_and_encrypt(key: &str) -> PathBuf {
        unsafe {
            let db_path = new_db();
            let db = create_test_db(&db_path, key);

            let cmds = vec![
                "CREATE TABLE lemming ( first INTEGER PRIMARY KEY );",
                "INSERT INTO lemming VALUES (11);",
                "INSERT INTO lemming VALUES (13);",
                "INSERT INTO lemming VALUES (17);",
            ];
            for cmd in cmds {
                exec(db, cmd);
            }

            let r = super::sqlite3_close(db);
            assert_eq!(r, super::SQLITE_OK);
            return db_path;
        }
    }

    #[test]
    fn test_sqlcipher_open_and_decrypt() {
        let path = create_and_encrypt(FIRST_KEY);
        let db = open_and_decrypt(&path, FIRST_KEY);
        unsafe {
            let r = super::sqlite3_close(db);
            assert_eq!(r, super::SQLITE_OK)
        }
    }

    fn open_and_decrypt(path: &PathBuf, key: &str) -> *mut super::sqlite3 {
        unsafe {
            let db = open_test_db(path, key);

            let cmds = vec![
                "SELECT * FROM lemming LIMIT 1;",
            ];
            for cmd in cmds {
                exec(db, cmd);
            }
            return db;
        }
    }

    #[test]
    fn test_sqlcipher_open_and_rekey() {
        let path = create_and_encrypt(FIRST_KEY);
        let db = open_and_decrypt(&path, FIRST_KEY);
        unsafe {
            // Decrypt and then re-encrypt via the C API
            let database_name = CString::new("main").unwrap();
            let passphrase = CString::new(SECOND_KEY).unwrap();
            let c_len = (SECOND_KEY.len() + 1) as c_int;
            let r = super::sqlite3_rekey_v2(db, database_name.as_ptr(), passphrase.as_ptr() as *mut ::std::os::raw::c_void, c_len);
            exec(db, "SELECT * FROM lemming LIMIT 2;");

            let r = super::sqlite3_close(db);
            assert_eq!(r, super::SQLITE_OK);
        }
        // Verify the new key is intact
        let db = open_test_db(&path, SECOND_KEY);
        unsafe {
            exec(db, "SELECT * FROM lemming LIMIT 2;");

            let r = super::sqlite3_close(db);
            assert_eq!(r, super::SQLITE_OK);
        }
    }
}<commit_msg>More tidying<commit_after>#![allow(non_snake_case, non_camel_case_types)]

pub use self::error::*;

use std::mem;

mod error;

pub fn SQLITE_STATIC() -> sqlite3_destructor_type {
    Some(unsafe { mem::transmute(0isize) })
}

pub fn SQLITE_TRANSIENT() -> sqlite3_destructor_type {
    Some(unsafe { mem::transmute(-1isize) })
}

/// Run-Time Limit Categories
#[repr(C)]
pub enum Limit {
    /// The maximum size of any string or BLOB or table row, in bytes.
    SQLITE_LIMIT_LENGTH = SQLITE_LIMIT_LENGTH as isize,
    /// The maximum length of an SQL statement, in bytes.
    SQLITE_LIMIT_SQL_LENGTH = SQLITE_LIMIT_SQL_LENGTH as isize,
    /// The maximum number of columns in a table definition or in the result set of a SELECT
    /// or the maximum number of columns in an index or in an ORDER BY or GROUP BY clause.
    SQLITE_LIMIT_COLUMN = SQLITE_LIMIT_COLUMN as isize,
    /// The maximum depth of the parse tree on any expression.
    SQLITE_LIMIT_EXPR_DEPTH = SQLITE_LIMIT_EXPR_DEPTH as isize,
    /// The maximum number of terms in a compound SELECT statement.
    SQLITE_LIMIT_COMPOUND_SELECT = SQLITE_LIMIT_COMPOUND_SELECT as isize,
    /// The maximum number of instructions in a virtual machine program used to implement an SQL statement.
    SQLITE_LIMIT_VDBE_OP = SQLITE_LIMIT_VDBE_OP as isize,
    /// The maximum number of arguments on a function.
    SQLITE_LIMIT_FUNCTION_ARG = SQLITE_LIMIT_FUNCTION_ARG as isize,
    /// The maximum number of attached databases.
    SQLITE_LIMIT_ATTACHED = SQLITE_LIMIT_ATTACHED as isize,
    /// The maximum length of the pattern argument to the LIKE or GLOB operators.
    SQLITE_LIMIT_LIKE_PATTERN_LENGTH = SQLITE_LIMIT_LIKE_PATTERN_LENGTH as isize,
    /// The maximum index number of any parameter in an SQL statement.
    SQLITE_LIMIT_VARIABLE_NUMBER = SQLITE_LIMIT_VARIABLE_NUMBER as isize,
    /// The maximum depth of recursion for triggers.
    SQLITE_LIMIT_TRIGGER_DEPTH = 10,
    /// The maximum number of auxiliary worker threads that a single prepared statement may start.
    SQLITE_LIMIT_WORKER_THREADS = 11,
}

include!(concat!(env!("OUT_DIR"), "/bindgen.rs"));

#[cfg(test)]
extern crate rand;

#[cfg(all(feature = "sqlcipher", test))]
mod tests {
    use std::env;
    use std::mem;
    use std::path::{Path, PathBuf};
    use std::ffi::CString;
    use std::ptr;
    use std::os::raw::c_int;
    use rand::{thread_rng, Rng};

    const FIRST_KEY: &'static str = "my first passphrase";
    const SECOND_KEY: &'static str = "my second passphrase";
    const DB_PATH: &'static str = "bindings_test_sqlcipher";
    const OPEN_FLAGS: c_int = super::SQLITE_OPEN_READWRITE|super::SQLITE_OPEN_URI|super::SQLITE_OPEN_NOMUTEX;

    fn new_db() -> PathBuf {
        let out_dir = env::var("OUT_DIR").unwrap();
        let stub: String = thread_rng().gen_ascii_chars().take(10).collect();
        let path = Path::new(out_dir.as_str());
        let mut pbuf = path.join(DB_PATH);
        pbuf.set_extension(stub);
        pbuf
    }

    fn create_test_db(path: &PathBuf, key: &str) -> *mut super::sqlite3 {
        let flags = super::SQLITE_OPEN_CREATE | OPEN_FLAGS;
        _open_test_db(path, flags, key)
    }

    fn open_test_db(path: &PathBuf, key: &str) -> *mut super::sqlite3 {
        _open_test_db(path, OPEN_FLAGS, key)
    }

    fn _open_test_db(path: &PathBuf, flags: c_int, key: &str) -> *mut super::sqlite3 {
        unsafe {
            let mut db: *mut super::sqlite3 = mem::uninitialized();
            let path_ = CString::new(path.to_str().unwrap()).unwrap();
            let r = super::sqlite3_open_v2(path_.as_ptr(), &mut db, flags, ptr::null());

            assert!(r == super::SQLITE_OK, "Can't open {0}: {1}", path.to_str().unwrap(), r);

            let database_name = CString::new("main").unwrap();
            let passphrase = CString::new(key).unwrap();
            let c_len = (key.len() + 1) as c_int;
            let r = super::sqlite3_key_v2(db, database_name.as_ptr(), passphrase.as_ptr() as *mut ::std::os::raw::c_void, c_len);

            assert!(r == super::SQLITE_OK, "Can't set encryption key for {0}: {1}", path.to_str().unwrap(), r);

            return db;
        }
    }

    fn exec(db: *mut super::sqlite3, cmd: &str) -> Option<c_int> {
        let mut stmt: *mut super::sqlite3_stmt = unsafe { mem::uninitialized() };
        let query = CString::new(cmd);
        unsafe {
            let len_with_nul = (cmd.len() + 1) as c_int;
            let r = super::sqlite3_prepare_v2(db, query.unwrap().as_ptr(), len_with_nul, &mut stmt, ptr::null_mut());

            assert!(r == super::SQLITE_OK, "Failed to prepare statement {0}: {1}", cmd, r);

            let r = super::sqlite3_step(stmt);
            super::sqlite3_finalize(stmt);

            match r {
                super::SQLITE_OK => None,
                super::SQLITE_DONE => None,
                super::SQLITE_ROW => None,
                super::SQLITE_ERROR => panic!("SQLite error encountered"),
                super::SQLITE_BUSY => panic!("SQLite claiming busy: finalize statement, maybe?"),
                _ => panic!("SQlite returned a weird error code: {0}: \"{1}\"", r, cmd)

            }
        }
    }

    #[test]
    fn test_sqlcipher_create_and_encrypt() {
        create_and_encrypt(FIRST_KEY);
    }

    fn create_and_encrypt(key: &str) -> PathBuf {
        unsafe {
            let db_path = new_db();
            let db = create_test_db(&db_path, key);

            let cmds = vec![
                "CREATE TABLE lemming ( first INTEGER PRIMARY KEY );",
                "INSERT INTO lemming VALUES (11);",
                "INSERT INTO lemming VALUES (13);",
                "INSERT INTO lemming VALUES (17);",
            ];
            for cmd in cmds {
                exec(db, cmd);
            }

            let r = super::sqlite3_close(db);
            assert_eq!(r, super::SQLITE_OK);
            return db_path;
        }
    }

    #[test]
    fn test_sqlcipher_open_and_decrypt() {
        let path = create_and_encrypt(FIRST_KEY);
        let db = open_and_decrypt(&path, FIRST_KEY);
        unsafe {
            let r = super::sqlite3_close(db);
            assert_eq!(r, super::SQLITE_OK)
        }
    }

    fn open_and_decrypt(path: &PathBuf, key: &str) -> *mut super::sqlite3 {
        let db = open_test_db(path, key);

        let cmds = vec![
            "SELECT * FROM lemming LIMIT 1;",
        ];
        for cmd in cmds {
            exec(db, cmd);
        }
        return db;
    }

    #[test]
    fn test_sqlcipher_open_and_rekey() {
        let path = create_and_encrypt(FIRST_KEY);
        let db = open_and_decrypt(&path, FIRST_KEY);
        unsafe {
            // Decrypt and then re-encrypt via the C API
            let database_name = CString::new("main").unwrap();
            let passphrase = CString::new(SECOND_KEY).unwrap();
            let c_len = (SECOND_KEY.len() + 1) as c_int;
            let r = super::sqlite3_rekey_v2(db, database_name.as_ptr(), passphrase.as_ptr() as *mut ::std::os::raw::c_void, c_len);

            assert!(r == super::SQLITE_OK, "Can't set encryption key for {0}: {1}", path.to_str().unwrap(), r);

            exec(db, "SELECT * FROM lemming LIMIT 2;");

            let r = super::sqlite3_close(db);
            assert_eq!(r, super::SQLITE_OK);
        }
        // Verify the new key is intact
        let db = open_test_db(&path, SECOND_KEY);
        unsafe {
            exec(db, "SELECT * FROM lemming LIMIT 2;");

            let r = super::sqlite3_close(db);
            assert_eq!(r, super::SQLITE_OK);
        }
    }
}<|endoftext|>


<commit_before>//use std::env;
//use std::fs;
//use std::path::Path;
//use std::process::Command;

fn main() {
    println!("cargo:rustc-flags=-l dylib=crypto");
    println!("cargo:rustc-flags=-l dylib=ssl");
    println!("cargo:rustc-flags=-l dylib=stdc++");
    println!("cargo:rustc-flags=-l dylib=uv");
    println!("cargo:rustc-link-search={}", "/usr/lib/");
    println!("cargo:rustc-link-lib=static=cassandra_static");
}<commit_msg>fixed mac compiling issue.  added  /usr/local/lib64 to the search path, which is where the cql driver lands<commit_after>//use std::env;
//use std::fs;
//use std::path::Path;
//use std::process::Command;

fn main() {
    println!("cargo:rustc-flags=-l dylib=crypto");
    println!("cargo:rustc-flags=-l dylib=ssl");
    println!("cargo:rustc-flags=-l dylib=stdc++");
    println!("cargo:rustc-flags=-l dylib=uv");
    println!("cargo:rustc-link-search={}", "/usr/lib/");
    println!("cargo:rustc-link-search={}", "/usr/local/lib64");
    println!("cargo:rustc-link-lib=static=cassandra_static");
}
<|endoftext|>


<commit_before>#![allow(clippy::option_if_let_else)]

use std::env;
use std::fs;
use std::path::Path;
use std::process::{Command, ExitStatus, Stdio};
use std::str;

#[cfg(all(feature = "backtrace", not(feature = "std")))]
compile_error! {
    "`backtrace` feature without `std` feature is not supported"
}

// This code exercises the surface area that we expect of the std Backtrace
// type. If the current toolchain is able to compile it, we go ahead and use
// backtrace in anyhow.
const PROBE: &str = r#"
    #![feature(backtrace)]
    #![allow(dead_code)]

    use std::backtrace::{Backtrace, BacktraceStatus};
    use std::error::Error;
    use std::fmt::{self, Display};

    #[derive(Debug)]
    struct E;

    impl Display for E {
        fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result {
            unimplemented!()
        }
    }

    impl Error for E {
        fn backtrace(&self) -> Option<&Backtrace> {
            let backtrace = Backtrace::capture();
            match backtrace.status() {
                BacktraceStatus::Captured | BacktraceStatus::Disabled | _ => {}
            }
            unimplemented!()
        }
    }
"#;

fn main() {
    if cfg!(feature = "std") {
        match compile_probe() {
            Some(status) if status.success() => println!("cargo:rustc-cfg=backtrace"),
            _ => {}
        }
    }

    let rustc = match rustc_minor_version() {
        Some(rustc) => rustc,
        None => return,
    };

    if rustc < 51 {
        println!("cargo:rustc-cfg=anyhow_no_ptr_addr_of");
    }

    if rustc < 52 {
        println!("cargo:rustc-cfg=anyhow_no_fmt_arguments_as_str");
    }
}

fn compile_probe() -> Option<ExitStatus> {
    println!("cargo:rerun-if-env-changed=RUSTC_WRAPPER");

    let rustc = env::var_os("RUSTC")?;
    let out_dir = env::var_os("OUT_DIR")?;
    let probefile = Path::new(&out_dir).join("probe.rs");
    fs::write(&probefile, PROBE).ok()?;

    // Make sure to pick up Cargo rustc configuration.
    let mut cmd = if let Some(wrapper) = env::var_os("RUSTC_WRAPPER") {
        let mut cmd = Command::new(wrapper);
        // The wrapper's first argument is supposed to be the path to rustc.
        cmd.arg(rustc);
        cmd
    } else {
        Command::new(rustc)
    };

    cmd.stderr(Stdio::null())
        .arg("--edition=2018")
        .arg("--crate-name=anyhow_build")
        .arg("--crate-type=lib")
        .arg("--emit=metadata")
        .arg("--out-dir")
        .arg(out_dir)
        .arg(probefile);

    if let Some(target) = env::var_os("TARGET") {
        cmd.arg("--target").arg(target);
    }

    // If Cargo wants to set RUSTFLAGS, use that.
    if let Ok(rustflags) = env::var("CARGO_ENCODED_RUSTFLAGS") {
        if !rustflags.is_empty() {
            for arg in rustflags.split('\x1f') {
                cmd.arg(arg);
            }
        }
    }

    cmd.status().ok()
}

fn rustc_minor_version() -> Option<u32> {
    let rustc = env::var_os("RUSTC")?;
    let output = Command::new(rustc).arg("--version").output().ok()?;
    let version = str::from_utf8(&output.stdout).ok()?;
    let mut pieces = version.split('.');
    if pieces.next() != Some("rustc 1") {
        return None;
    }
    pieces.next()?.parse().ok()
}
<commit_msg>Add link to Cargo RUSTC_WRAPPER PR<commit_after>#![allow(clippy::option_if_let_else)]

use std::env;
use std::fs;
use std::path::Path;
use std::process::{Command, ExitStatus, Stdio};
use std::str;

#[cfg(all(feature = "backtrace", not(feature = "std")))]
compile_error! {
    "`backtrace` feature without `std` feature is not supported"
}

// This code exercises the surface area that we expect of the std Backtrace
// type. If the current toolchain is able to compile it, we go ahead and use
// backtrace in anyhow.
const PROBE: &str = r#"
    #![feature(backtrace)]
    #![allow(dead_code)]

    use std::backtrace::{Backtrace, BacktraceStatus};
    use std::error::Error;
    use std::fmt::{self, Display};

    #[derive(Debug)]
    struct E;

    impl Display for E {
        fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result {
            unimplemented!()
        }
    }

    impl Error for E {
        fn backtrace(&self) -> Option<&Backtrace> {
            let backtrace = Backtrace::capture();
            match backtrace.status() {
                BacktraceStatus::Captured | BacktraceStatus::Disabled | _ => {}
            }
            unimplemented!()
        }
    }
"#;

fn main() {
    if cfg!(feature = "std") {
        match compile_probe() {
            Some(status) if status.success() => println!("cargo:rustc-cfg=backtrace"),
            _ => {}
        }
    }

    let rustc = match rustc_minor_version() {
        Some(rustc) => rustc,
        None => return,
    };

    if rustc < 51 {
        println!("cargo:rustc-cfg=anyhow_no_ptr_addr_of");
    }

    if rustc < 52 {
        println!("cargo:rustc-cfg=anyhow_no_fmt_arguments_as_str");
    }
}

fn compile_probe() -> Option<ExitStatus> {
    // FIXME: obsoleted by https://github.com/rust-lang/cargo/pull/10973
    println!("cargo:rerun-if-env-changed=RUSTC_WRAPPER");

    let rustc = env::var_os("RUSTC")?;
    let out_dir = env::var_os("OUT_DIR")?;
    let probefile = Path::new(&out_dir).join("probe.rs");
    fs::write(&probefile, PROBE).ok()?;

    // Make sure to pick up Cargo rustc configuration.
    let mut cmd = if let Some(wrapper) = env::var_os("RUSTC_WRAPPER") {
        let mut cmd = Command::new(wrapper);
        // The wrapper's first argument is supposed to be the path to rustc.
        cmd.arg(rustc);
        cmd
    } else {
        Command::new(rustc)
    };

    cmd.stderr(Stdio::null())
        .arg("--edition=2018")
        .arg("--crate-name=anyhow_build")
        .arg("--crate-type=lib")
        .arg("--emit=metadata")
        .arg("--out-dir")
        .arg(out_dir)
        .arg(probefile);

    if let Some(target) = env::var_os("TARGET") {
        cmd.arg("--target").arg(target);
    }

    // If Cargo wants to set RUSTFLAGS, use that.
    if let Ok(rustflags) = env::var("CARGO_ENCODED_RUSTFLAGS") {
        if !rustflags.is_empty() {
            for arg in rustflags.split('\x1f') {
                cmd.arg(arg);
            }
        }
    }

    cmd.status().ok()
}

fn rustc_minor_version() -> Option<u32> {
    let rustc = env::var_os("RUSTC")?;
    let output = Command::new(rustc).arg("--version").output().ok()?;
    let version = str::from_utf8(&output.stdout).ok()?;
    let mut pieces = version.split('.');
    if pieces.next() != Some("rustc 1") {
        return None;
    }
    pieces.next()?.parse().ok()
}
<|endoftext|>


<commit_before>//! All keys in leveldb are compared by their binary value unless
//! defined otherwise.
//!
//! Comparators allow to override this comparison.
//! The ordering of keys introduced by the compartor influences iteration order.
//! Databases written with one Comparator cannot be opened with another.
use leveldb_sys::*;
use libc::{size_t, c_void, c_char};
use std::mem;
use std::slice;
use std::cmp::Ordering;
use database::key::Key;
use database::key::from_u8;
use std::marker::PhantomData;

/// A comparator has two important functions:
///
/// * the name function returns a fixed name to detect errors when
///   opening databases with a different name
/// * The comparison implementation
pub trait Comparator {
    /// The type that the comparator compares.
    type K: Key;

    /// Return the name of the Comparator
    fn name(&self) -> *const c_char;
    /// compare two keys. This must implement a total ordering.
    fn compare(&self, a: &Self::K, b: &Self::K) -> Ordering;
    /// whether the comparator is the `DefaultComparator`
    fn null() -> bool {
        false
    }
}

/// OrdComparator is a comparator comparing Keys that implement `Ord`
pub struct OrdComparator<K: Key + Ord> {
    name: String,
    marker: PhantomData<K>,
}

impl<K: Key + Ord> OrdComparator<K> {
    /// Create a new OrdComparator
    pub fn new(name: &str) -> OrdComparator<K> {
        OrdComparator {
            marker: PhantomData,
            name: name.to_string(),
        }
    }
}

/// DefaultComparator is the a stand in for "no comparator set"
#[derive(Copy,Clone)]
pub struct DefaultComparator;

unsafe trait InternalComparator : Comparator where Self: Sized {

    extern "C" fn name(state: *mut c_void) -> *const c_char {
        let x = unsafe { &*(state as *mut Self) };
        x.name()
    }

    extern "C" fn compare(state: *mut c_void,
                          a: *const i8,
                          a_len: size_t,
                          b: *const i8,
                          b_len: size_t)
                          -> i32 {
        unsafe {
            let a_slice = slice::from_raw_parts::<u8>(a as *const u8, a_len as usize);
            let b_slice = slice::from_raw_parts::<u8>(b as *const u8, b_len as usize);
            let x = &*(state as *mut Self);
            let a_key = from_u8::<<Self as Comparator>::K>(a_slice);
            let b_key = from_u8::<<Self as Comparator>::K>(b_slice);
            match x.compare(&a_key, &b_key) {
                Ordering::Less => -1,
                Ordering::Equal => 0,
                Ordering::Greater => 1,
            }
        }
    }

    extern "C" fn destructor(state: *mut c_void) {
        let _x: Box<Self> = unsafe { mem::transmute(state) };
         // let the Box fall out of scope and run the T's destructor
    }
}

unsafe impl<C: Comparator> InternalComparator for C {}

#[allow(missing_docs)]
pub fn create_comparator<K: Key, T: Comparator<K = K>>(x: Box<T>) -> *mut leveldb_comparator_t {
    unsafe {
        leveldb_comparator_create(mem::transmute(x),
                                  <T as InternalComparator>::destructor,
                                  <T as InternalComparator>::compare,
                                  <T as InternalComparator>::name)
    }
}

impl<K: Key + Ord> Comparator for OrdComparator<K> {
  type K = K;

    fn name(&self) -> *const c_char {
        let slice: &str = self.name.as_ref();
        slice.as_ptr() as *const c_char
    }

    fn compare(&self, a: &K, b: &K) -> Ordering {
        a.cmp(b)
    }
}

impl Comparator for DefaultComparator {
  type K = i32;

    fn name(&self) -> *const c_char {
        "default_comparator".as_ptr() as *const c_char
    }

    fn compare(&self, _a: &i32, _b: &i32) -> Ordering {
        Ordering::Equal
    }

    fn null() -> bool {
        true
    }
}
<commit_msg>Simplify signature of create_comparator<commit_after>//! All keys in leveldb are compared by their binary value unless
//! defined otherwise.
//!
//! Comparators allow to override this comparison.
//! The ordering of keys introduced by the compartor influences iteration order.
//! Databases written with one Comparator cannot be opened with another.
use leveldb_sys::*;
use libc::{size_t, c_void, c_char};
use std::mem;
use std::slice;
use std::cmp::Ordering;
use database::key::Key;
use database::key::from_u8;
use std::marker::PhantomData;

/// A comparator has two important functions:
///
/// * the name function returns a fixed name to detect errors when
///   opening databases with a different name
/// * The comparison implementation
pub trait Comparator {
    /// The type that the comparator compares.
    type K: Key;

    /// Return the name of the Comparator
    fn name(&self) -> *const c_char;
    /// compare two keys. This must implement a total ordering.
    fn compare(&self, a: &Self::K, b: &Self::K) -> Ordering;
    /// whether the comparator is the `DefaultComparator`
    fn null() -> bool {
        false
    }
}

/// OrdComparator is a comparator comparing Keys that implement `Ord`
pub struct OrdComparator<K: Key + Ord> {
    name: String,
    marker: PhantomData<K>,
}

impl<K: Key + Ord> OrdComparator<K> {
    /// Create a new OrdComparator
    pub fn new(name: &str) -> OrdComparator<K> {
        OrdComparator {
            marker: PhantomData,
            name: name.to_string(),
        }
    }
}

/// DefaultComparator is the a stand in for "no comparator set"
#[derive(Copy,Clone)]
pub struct DefaultComparator;

unsafe trait InternalComparator : Comparator where Self: Sized {

    extern "C" fn name(state: *mut c_void) -> *const c_char {
        let x = unsafe { &*(state as *mut Self) };
        x.name()
    }

    extern "C" fn compare(state: *mut c_void,
                          a: *const i8,
                          a_len: size_t,
                          b: *const i8,
                          b_len: size_t)
                          -> i32 {
        unsafe {
            let a_slice = slice::from_raw_parts::<u8>(a as *const u8, a_len as usize);
            let b_slice = slice::from_raw_parts::<u8>(b as *const u8, b_len as usize);
            let x = &*(state as *mut Self);
            let a_key = from_u8::<<Self as Comparator>::K>(a_slice);
            let b_key = from_u8::<<Self as Comparator>::K>(b_slice);
            match x.compare(&a_key, &b_key) {
                Ordering::Less => -1,
                Ordering::Equal => 0,
                Ordering::Greater => 1,
            }
        }
    }

    extern "C" fn destructor(state: *mut c_void) {
        let _x: Box<Self> = unsafe { mem::transmute(state) };
         // let the Box fall out of scope and run the T's destructor
    }
}

unsafe impl<C: Comparator> InternalComparator for C {}

#[allow(missing_docs)]
pub fn create_comparator<T: Comparator>(x: Box<T>) -> *mut leveldb_comparator_t {
    unsafe {
        leveldb_comparator_create(mem::transmute(x),
                                  <T as InternalComparator>::destructor,
                                  <T as InternalComparator>::compare,
                                  <T as InternalComparator>::name)
    }
}

impl<K: Key + Ord> Comparator for OrdComparator<K> {
  type K = K;

    fn name(&self) -> *const c_char {
        let slice: &str = self.name.as_ref();
        slice.as_ptr() as *const c_char
    }

    fn compare(&self, a: &K, b: &K) -> Ordering {
        a.cmp(b)
    }
}

impl Comparator for DefaultComparator {
  type K = i32;

    fn name(&self) -> *const c_char {
        "default_comparator".as_ptr() as *const c_char
    }

    fn compare(&self, _a: &i32, _b: &i32) -> Ordering {
        Ordering::Equal
    }

    fn null() -> bool {
        true
    }
}
<|endoftext|>


<commit_before>use std::os;
use std::io::Command;
use std::io::process::InheritFd;

fn main() {
    let dst = Path::new(os::getenv("OUT_DIR").unwrap());

    setup();
    configure();
    build();
    install(&dst);
    clean();
    println!("cargo:rustc-flags=-L {} -l termbox:static", dst.join("lib").display());
}

fn setup() {
    let mut cmd = Command::new("git");
    cmd.arg("clone");
    cmd.arg("https://github.com/nsf/termbox");
    cmd.arg(".termbox");
    let cargo_dir = Path::new(os::getenv("CARGO_MANIFEST_DIR").unwrap());
    cmd.cwd(&cargo_dir);

    run(&mut cmd);
}

fn clean() {
    let mut cmd = Command::new("rm");
    cmd.arg("-rf");
    cmd.arg(".termbox");
    let cargo_dir = Path::new(os::getenv("CARGO_MANIFEST_DIR").unwrap());
    cmd.cwd(&cargo_dir);
    run(&mut cmd);
}

fn configure() {
    let mut cmd = waf();
    cmd.arg("configure");
    cmd.arg("--prefix=/");

    let target = os::getenv("TARGET").unwrap();
    let mut cflags;
    if target.as_slice().contains("i686") {
        cflags = "-m32"
    } else if target.as_slice().contains("x86_64") {
        cflags = "-m64 -fPIC"
    } else {
        cflags = ""
    }
    println!("waf configure: setting CFLAGS to: `{}`", cflags);
    os::setenv("CFLAGS", cflags);

    run(&mut cmd);
    os::unsetenv("CFLAGS");
}

fn build() {
    let mut cmd = waf();
    cmd.arg("build");
    cmd.arg("--targets=termbox_static");
    run(&mut cmd);
}

fn install(dst: &Path) {
    let mut cmd = waf();
    cmd.arg("install");
    cmd.arg("--targets=termbox_static");
    cmd.arg(format!("--destdir={}", dst.display()));
    run(&mut cmd);
}

fn waf() -> Command {
    let cargo_dir = Path::new(os::getenv("CARGO_MANIFEST_DIR").unwrap());
    let termbox_dir = cargo_dir.join(".termbox");
    let mut cmd = Command::new("./waf");
    cmd.cwd(&termbox_dir);
    cmd
}

fn run(cmd: &mut Command) {
    println!("running: {:?}", cmd);
    assert!(cmd.stdout(InheritFd(1))
                .stderr(InheritFd(2))
                .status()
                .unwrap()
                .success());
}
<commit_msg>Keep up with rust nightly<commit_after>use std::os;
use std::old_io::Command;
use std::old_io::process::InheritFd;

fn main() {
    let dst = Path::new(os::getenv("OUT_DIR").unwrap());

    setup();
    configure();
    build();
    install(&dst);
    clean();
    println!("cargo:rustc-flags=-L {} -l termbox:static", dst.join("lib").display());
}

fn setup() {
    let mut cmd = Command::new("git");
    cmd.arg("clone");
    cmd.arg("https://github.com/nsf/termbox");
    cmd.arg(".termbox");
    let cargo_dir = Path::new(os::getenv("CARGO_MANIFEST_DIR").unwrap());
    cmd.cwd(&cargo_dir);

    run(&mut cmd);
}

fn clean() {
    let mut cmd = Command::new("rm");
    cmd.arg("-rf");
    cmd.arg(".termbox");
    let cargo_dir = Path::new(os::getenv("CARGO_MANIFEST_DIR").unwrap());
    cmd.cwd(&cargo_dir);
    run(&mut cmd);
}

fn configure() {
    let mut cmd = waf();
    cmd.arg("configure");
    cmd.arg("--prefix=/");

    let target = os::getenv("TARGET").unwrap();
    let mut cflags;
    if target.as_slice().contains("i686") {
        cflags = "-m32"
    } else if target.as_slice().contains("x86_64") {
        cflags = "-m64 -fPIC"
    } else {
        cflags = ""
    }
    println!("waf configure: setting CFLAGS to: `{}`", cflags);
    os::setenv("CFLAGS", cflags);

    run(&mut cmd);
    os::unsetenv("CFLAGS");
}

fn build() {
    let mut cmd = waf();
    cmd.arg("build");
    cmd.arg("--targets=termbox_static");
    run(&mut cmd);
}

fn install(dst: &Path) {
    let mut cmd = waf();
    cmd.arg("install");
    cmd.arg("--targets=termbox_static");
    cmd.arg(format!("--destdir={}", dst.display()));
    run(&mut cmd);
}

fn waf() -> Command {
    let cargo_dir = Path::new(os::getenv("CARGO_MANIFEST_DIR").unwrap());
    let termbox_dir = cargo_dir.join(".termbox");
    let mut cmd = Command::new("./waf");
    cmd.cwd(&termbox_dir);
    cmd
}

fn run(cmd: &mut Command) {
    println!("running: {:?}", cmd);
    assert!(cmd.stdout(InheritFd(1))
                .stderr(InheritFd(2))
                .status()
                .unwrap()
                .success());
}
<|endoftext|>


<commit_before>use std::env;
use std::process::Command;
use std::path::*;

fn is_dylib() -> bool {
    option_env!("DyLib_DOtherSide").is_some()
}

fn main() {
    if is_dylib() {
        Command::new("sh")
            .arg("build_lib.sh")
            .env("IS_DYLIB", "1")
            .output()
            .unwrap_or_else(|e| panic!("failed to execute process: {}", e));
    } else {
        Command::new("sh")
            .arg("build_lib.sh")
            .output()
            .unwrap_or_else(|e| panic!("failed to execute process: {}", e));
    }
    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let path = Path::new(&manifest_dir).join("DOtherSide").join("build").join("lib");

    println!("cargo:rustc-link-search=native={}", path.display());
    println!("cargo:rerun-if-changed={}", path.display());
    if is_dylib() {
        println!("cargo:rustc-link-lib=dylib=DOtherSide");
    } else {
        println!("cargo:rustc-link-lib=static=DOtherSide");
    }
    println!("cargo:rustc-link-lib=dylib=stdc++");

    let target = env::var("TARGET").expect("Environment variable TARGET not set");

    let osx_framework = if target.contains("darwin") { "=framework" }
                        else  { "" };
    // On Linux, libraries are named "Qt5Core", not "QtCore" as on OSX
    let linux_qt_lib_ver = if target.contains("linux") { "5" }
                           else  { "" };

    const QT_PLUGINS: [&'static str; 5] = ["Core", "Gui", "Qml", "Quick", "Widgets"];
    for plugin in &QT_PLUGINS {
        println!("cargo:rustc-link-lib{}=Qt{}{}", osx_framework, linux_qt_lib_ver, plugin);
    }
}
<commit_msg>Fix static link library name<commit_after>use std::env;
use std::process::Command;
use std::path::*;

fn is_dylib() -> bool {
    option_env!("DyLib_DOtherSide").is_some()
}

fn main() {
    if is_dylib() {
        Command::new("sh")
            .arg("build_lib.sh")
            .env("IS_DYLIB", "1")
            .output()
            .unwrap_or_else(|e| panic!("failed to execute process: {}", e));
    } else {
        Command::new("sh")
            .arg("build_lib.sh")
            .output()
            .unwrap_or_else(|e| panic!("failed to execute process: {}", e));
    }
    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let path = Path::new(&manifest_dir).join("DOtherSide").join("build").join("lib");

    println!("cargo:rustc-link-search=native={}", path.display());
    println!("cargo:rerun-if-changed={}", path.display());
    if is_dylib() {
        println!("cargo:rustc-link-lib=dylib=DOtherSide");
    } else {
        println!("cargo:rustc-link-lib=static=DOtherSideStatic");
    }
    println!("cargo:rustc-link-lib=dylib=stdc++");

    let target = env::var("TARGET").expect("Environment variable TARGET not set");

    let osx_framework = if target.contains("darwin") { "=framework" }
                        else  { "" };
    // On Linux, libraries are named "Qt5Core", not "QtCore" as on OSX
    let linux_qt_lib_ver = if target.contains("linux") { "5" }
                           else  { "" };

    const QT_PLUGINS: [&'static str; 5] = ["Core", "Gui", "Qml", "Quick", "Widgets"];
    for plugin in &QT_PLUGINS {
        println!("cargo:rustc-link-lib{}=Qt{}{}", osx_framework, linux_qt_lib_ver, plugin);
    }
}
<|endoftext|>


<commit_before>#[cfg(any(feature = "backend_egl", feature = "renderer_gl"))]
fn gl_generate() {
    use gl_generator::{Api, Fallbacks, Profile, Registry};
    use std::{env, fs::File, path::PathBuf};

    let dest = PathBuf::from(&env::var("OUT_DIR").unwrap());

    println!("cargo:rerun-if-changed=build.rs");

    if env::var_os("CARGO_FEATURE_BACKEND_EGL").is_some() {
        let mut file = File::create(&dest.join("egl_bindings.rs")).unwrap();
        Registry::new(
            Api::Egl,
            (1, 5),
            Profile::Core,
            Fallbacks::All,
            [
                "EGL_KHR_create_context",
                "EGL_EXT_create_context_robustness",
                "EGL_KHR_create_context_no_error",
                "EGL_KHR_platform_x11",
                "EGL_KHR_platform_android",
                "EGL_KHR_platform_wayland",
                "EGL_KHR_platform_gbm",
                "EGL_EXT_platform_base",
                "EGL_EXT_platform_x11",
                "EGL_MESA_platform_gbm",
                "EGL_EXT_platform_wayland",
                "EGL_EXT_platform_device",
                "EGL_KHR_image_base",
                "EGL_EXT_output_base",
                "EGL_EXT_output_drm",
                "EGL_EXT_device_drm",
                "EGL_EXT_device_enumeration",
                "EGL_EXT_device_query",
                "EGL_KHR_stream",
                "EGL_KHR_stream_producer_eglsurface",
                "EGL_EXT_stream_consumer_egloutput",
                "EGL_KHR_stream_fifo",
                "EGL_NV_output_drm_flip_event",
                "EGL_NV_stream_attrib",
            ],
        )
        .write_bindings(gl_generator::GlobalGenerator, &mut file)
        .unwrap();
    }

    if env::var_os("CARGO_FEATURE_RENDERER_GL").is_some() {
        let mut file = File::create(&dest.join("gl_bindings.rs")).unwrap();
        Registry::new(
            Api::Gles2,
            (3, 2),
            Profile::Compatibility,
            Fallbacks::None,
            ["GL_OES_EGL_image"],
        )
        .write_bindings(gl_generator::StructGenerator, &mut file)
        .unwrap();
    }
}

#[cfg(feature = "backend_session_logind")]
fn find_logind() {
    // We should allow only dynamic linkage due to libsystemd and libelogind LICENSE.

    #[cfg(feature = "backend_session_elogind")]
    {
        if pkg_config::Config::new()
            .statik(false)
            .probe("libelogind")
            .is_err()
        {
            println!("cargo:warning=Could not find `libelogind.so`.");
            println!("cargo:warning=If your system is systemd-based, you should only enable the `backend_session_logind` feature, not `backend_session_elogind`.");
            std::process::exit(1);
        }
    }

    #[cfg(not(feature = "backend_session_elogind"))]
    {
        if pkg_config::Config::new()
            .statik(false)
            .probe("libsystemd")
            .is_err()
        {
            println!("cargo:warning=Could not find `libsystemd.so`.");
            println!("cargo:warning=If your system uses elogind, please enable the `backend_session_elogind` feature.");
            println!("cargo:warning=Otherwise, you may need to disable the `backend_session_logind` feature as your system does not support it.");
            std::process::exit(1);
        }
    }
}

fn main() {
    #[cfg(any(feature = "backend_egl", feature = "renderer_gl"))]
    gl_generate();

    #[cfg(feature = "backend_session_logind")]
    find_logind();
}
<commit_msg>Update build.rs to generate code for the actual extensions we are using now<commit_after>#[cfg(any(feature = "backend_egl", feature = "renderer_gl"))]
fn gl_generate() {
    use gl_generator::{Api, Fallbacks, Profile, Registry};
    use std::{env, fs::File, path::PathBuf};

    let dest = PathBuf::from(&env::var("OUT_DIR").unwrap());

    if env::var_os("CARGO_FEATURE_BACKEND_EGL").is_some() {
        let mut file = File::create(&dest.join("egl_bindings.rs")).unwrap();
        Registry::new(
            Api::Egl,
            (1, 5),
            Profile::Core,
            Fallbacks::All,
            [
                "EGL_KHR_create_context",
                "EGL_EXT_create_context_robustness",
                "EGL_KHR_create_context_no_error",
                "EGL_KHR_no_config_context",
                "EGL_EXT_platform_base",
                "EGL_EXT_platform_x11",
                "EGL_EXT_platform_wayland",
                "EGL_MESA_platform_gbm",
                "EGL_WL_bind_wayland_display",
                "EGL_KHR_image_base",
                "EGL_EXT_image_dma_buf_import",
                "EGL_EXT_image_dma_buf_import_modifiers",
            ],
        )
        .write_bindings(gl_generator::GlobalGenerator, &mut file)
        .unwrap();
    }

    if env::var_os("CARGO_FEATURE_RENDERER_GL").is_some() {
        let mut file = File::create(&dest.join("gl_bindings.rs")).unwrap();
        Registry::new(
            Api::Gles2,
            (3, 2),
            Profile::Compatibility,
            Fallbacks::None,
            [
                "GL_OES_EGL_image",
                "GL_OES_EGL_image_external",
                "GL_EXT_texture_format_BGRA8888",
                "GL_EXT_unpack_subimage",
            ],
        )
        .write_bindings(gl_generator::StructGenerator, &mut file)
        .unwrap();
    }
}

#[cfg(feature = "backend_session_logind")]
fn find_logind() {
    // We should allow only dynamic linkage due to libsystemd and libelogind LICENSE.

    #[cfg(feature = "backend_session_elogind")]
    {
        if pkg_config::Config::new()
            .statik(false)
            .probe("libelogind")
            .is_err()
        {
            println!("cargo:warning=Could not find `libelogind.so`.");
            println!("cargo:warning=If your system is systemd-based, you should only enable the `backend_session_logind` feature, not `backend_session_elogind`.");
            std::process::exit(1);
        }
    }

    #[cfg(not(feature = "backend_session_elogind"))]
    {
        if pkg_config::Config::new()
            .statik(false)
            .probe("libsystemd")
            .is_err()
        {
            println!("cargo:warning=Could not find `libsystemd.so`.");
            println!("cargo:warning=If your system uses elogind, please enable the `backend_session_elogind` feature.");
            println!("cargo:warning=Otherwise, you may need to disable the `backend_session_logind` feature as your system does not support it.");
            std::process::exit(1);
        }
    }
}

fn main() {
    #[cfg(any(feature = "backend_egl", feature = "renderer_gl"))]
    gl_generate();

    #[cfg(feature = "backend_session_logind")]
    find_logind();
}
<|endoftext|>