<commit_before>// lib.rs
// Volatile
//
// Created by Daniel Seitz on 1/7/17

#![no_std]
#![feature(core_intrinsics)]

//! Volatile memory operations.
//!
//! This library contains wrappers around raw pointers to perform volatile memory operations. This
//! is mainly useful for memory mapped I/O. Writing to the peripheral memory addresses will
//! normally be optimized out by the compiler. The `Volatile` type wraps a memory address to
//! perform volatile operations in order to force the compiler to keep these memory accesses and
//! stores.

mod tests;

use core::ops::*;
use core::intrinsics::{volatile_load, volatile_store};
use core::mem::size_of;

/// A volatile pointer.
///
/// This type acts as a pointer that only uses volatile operations. Pointer arithmetic can be
/// performed on it, and it can be dereferenced to its raw type in order to perform volatile memory
/// operations. This is especially useful for I/O operations where writing and reading from memory
/// mapped I/O registers would normally be optimized out by the compiler.
///
/// # Examples
///
/// ```rust,no_run
/// use volatile::Volatile;
///
/// let value: i32 = 0;
/// unsafe {
///   let mut ptr = Volatile::new(&value);
///   let mask = 0x0F0F;
///   *ptr |= mask;
/// }
/// assert_eq!(value, 0x0F0F);
/// ```
#[derive(Copy, Clone)]
pub struct Volatile<T: Copy>(RawVolatile<T>);

#[doc(hidden)]
#[derive(Copy, Clone)]
pub struct RawVolatile<T: Copy>(*const T);

impl<T: Copy> Volatile<T> {
  /// Creates a new `Volatile` pointer.
  ///
  /// This is unsafe because the address could be potentially anywhere, and forcing a write to a
  /// memory address could cause undefined behavior if the wrong address is chosen.
  pub unsafe fn new(ptr: *const T) -> Self {
    Volatile(RawVolatile::new(ptr))
  }

  /// Returns the inner pointer.
  pub fn as_ptr(self) -> *const T {
    (self.0).0
  }

  /// Returns the inner pointer mutably.
  pub fn as_mut(self) -> *mut T {
    (self.0).0 as *mut T
  }

  pub unsafe fn offset(self, count: isize) -> Self {
    let base = self.as_ptr() as isize;
    // TODO: See https://github.com/rust-lang/rust/issues/39056
    //  Change this back to a regular multiply once this gets fixed so we have overflow checking
    let offset = count.wrapping_mul(size_of::<T>() as isize);
    let addr = (base + offset as isize) as *const T;
    Volatile::new(addr)
  }
}

impl<T: Copy> Deref for Volatile<T> {
  type Target = RawVolatile<T>;

  fn deref(&self) -> &Self::Target {
    &self.0
  }
}

impl<T: Copy> DerefMut for Volatile<T> {
  fn deref_mut(&mut self) -> &mut Self::Target {
    &mut self.0
  }
}

/*** Raw Volatile Implementation ***/

impl<T: Copy> RawVolatile<T> {
  fn new(ptr: *const T) -> Self {
    RawVolatile(ptr)
  }

  /// Stores a value into the address pointed at.
  pub unsafe fn store(&mut self, rhs: T) {
    volatile_store(self.0 as *mut T, rhs);
  }

  /// Loads a value from the address pointed at.
  pub unsafe fn load(&self) -> T {
    volatile_load(self.0 as *const T)
  }
}

impl<T: Add<Output=T> + Copy> Add<T> for RawVolatile<T> {
  type Output = T;

  fn add(self, rhs: T) -> Self::Output {
    unsafe {
      volatile_load(self.0) + rhs
    }
  }
}

impl<T: Add<Output=T> + Copy> AddAssign<T> for RawVolatile<T> {
  fn add_assign(&mut self, rhs: T) {
    unsafe {
      volatile_store(self.0 as *mut T, volatile_load(self.0) + rhs);
    }
  }
}

impl<T: Sub<Output=T> + Copy> Sub<T> for RawVolatile<T> {
  type Output = T;

  fn sub(self, rhs: T) -> Self::Output {
    unsafe {
      volatile_load(self.0) - rhs
    }
  }
}

impl<T: Sub<Output=T> + Copy> SubAssign<T> for RawVolatile<T> {
  fn sub_assign(&mut self, rhs: T) {
    unsafe {
      volatile_store(self.0 as *mut T, volatile_load(self.0) - rhs);
    }
  }
}

impl<T: Mul<Output=T> + Copy> Mul<T> for RawVolatile<T> {
  type Output = T;

  fn mul(self, rhs: T) -> Self::Output {
    unsafe {
      volatile_load(self.0) * rhs
    }
  }
}

impl<T: Mul<Output=T> + Copy> MulAssign<T> for RawVolatile<T> {
  fn mul_assign(&mut self, rhs: T) {
    unsafe {
      volatile_store(self.0 as *mut T, volatile_load(self.0) * rhs);
    }
  }
}

impl<T: Div<Output=T> + Copy> Div<T> for RawVolatile<T> {
  type Output = T;

  fn div(self, rhs: T) -> Self::Output {
    unsafe {
      volatile_load(self.0) / rhs
    }
  }
}

impl<T: Div<Output=T> + Copy> DivAssign<T> for RawVolatile<T> {
  fn div_assign(&mut self, rhs: T) {
    unsafe {
      volatile_store(self.0 as *mut T, volatile_load(self.0) / rhs);
    }
  }
}

impl<T: Rem<Output=T> + Copy> Rem<T> for RawVolatile<T> {
  type Output = T;

  fn rem(self, rhs: T) -> Self::Output {
    unsafe {
      volatile_load(self.0) % rhs
    }
  }
}

impl<T: Rem<Output=T> + Copy> RemAssign<T> for RawVolatile<T> {
  fn rem_assign(&mut self, rhs: T) {
    unsafe {
      volatile_store(self.0 as *mut T, volatile_load(self.0) % rhs);
    }
  }
}

/*** Bitwise Operators ***/

impl<T: BitAnd<Output=T> + Copy> BitAnd<T> for RawVolatile<T> {
  type Output = T;

  fn bitand(self, rhs: T) -> Self::Output {
    unsafe {
      volatile_load(self.0) & rhs
    }
  }
}

impl<T: BitAnd<Output=T> + Copy> BitAndAssign<T> for RawVolatile<T> {
  fn bitand_assign(&mut self, rhs: T) {
    unsafe {
      volatile_store(self.0 as *mut T, volatile_load(self.0) & rhs);
    }
  }
}

impl<T: BitOr<Output=T> + Copy> BitOr<T> for RawVolatile<T> {
  type Output = T;

  fn bitor(self, rhs: T) -> Self::Output {
    unsafe {
      volatile_load(self.0) | rhs
    }
  }
}

impl<T: BitOr<Output=T> + Copy> BitOrAssign<T> for RawVolatile<T> {
  fn bitor_assign(&mut self, rhs: T) {
    unsafe {
      volatile_store(self.0 as *mut T, volatile_load(self.0) | rhs);
    }
  }
}

impl<T: BitXor<Output=T> + Copy> BitXor<T> for RawVolatile<T> {
  type Output = T;

  fn bitxor(self, rhs: T) -> Self::Output {
    unsafe {
      volatile_load(self.0) ^ rhs
    }
  }
}

impl<T: BitXor<Output=T> + Copy> BitXorAssign<T> for RawVolatile<T> {
  fn bitxor_assign(&mut self, rhs: T) {
    unsafe {
      volatile_store(self.0 as *mut T, volatile_load(self.0) ^ rhs);
    }
  }
}

impl<T: Shl<T, Output=T> + Copy> Shl<T> for RawVolatile<T> {
  type Output = T;

  fn shl(self, rhs: T) -> Self::Output {
    unsafe {
      volatile_load(self.0) << rhs
    }
  }
}

impl<T: Shl<T, Output=T> + Copy> ShlAssign<T> for RawVolatile<T> {
  fn shl_assign(&mut self, rhs: T) {
    unsafe {
      volatile_store(self.0 as *mut T, volatile_load(self.0) << rhs);
    }
  }
}

impl<T: Shr<T, Output=T> + Copy> Shr<T> for RawVolatile<T> {
  type Output = T;

  fn shr(self, rhs: T) -> Self::Output {
    unsafe {
      volatile_load(self.0) >> rhs
    }
  }
}

impl<T: Shr<T, Output=T> + Copy> ShrAssign<T> for RawVolatile<T> {
  fn shr_assign(&mut self, rhs: T) {
    unsafe {
      volatile_store(self.0 as *mut T, volatile_load(self.0) >> rhs);
    }
  }
}

/*** Negation ***/

impl<T: Neg<Output=T> + Copy> Neg for RawVolatile<T> {
  type Output = T;

  fn neg(self) -> Self::Output {
    unsafe {
      -volatile_load(self.0)
    }
  }
}

impl<T: Not<Output=T> + Copy> Not for RawVolatile<T> {
  type Output = T;

  fn not(self) -> Self::Output {
    unsafe {
      !volatile_load(self.0)
    }
  }
}

<commit_msg>remove unused factory method<commit_after>// lib.rs
// Volatile
//
// Created by Daniel Seitz on 1/7/17

#![no_std]
#![feature(core_intrinsics)]

//! Volatile memory operations.
//!
//! This library contains wrappers around raw pointers to perform volatile memory operations. This
//! is mainly useful for memory mapped I/O. Writing to the peripheral memory addresses will
//! normally be optimized out by the compiler. The `Volatile` type wraps a memory address to
//! perform volatile operations in order to force the compiler to keep these memory accesses and
//! stores.

mod tests;

use core::ops::*;
use core::intrinsics::{volatile_load, volatile_store};
use core::mem::size_of;

/// A volatile pointer.
///
/// This type acts as a pointer that only uses volatile operations. Pointer arithmetic can be
/// performed on it, and it can be dereferenced to its raw type in order to perform volatile memory
/// operations. This is especially useful for I/O operations where writing and reading from memory
/// mapped I/O registers would normally be optimized out by the compiler.
///
/// # Examples
///
/// ```rust,no_run
/// use volatile::Volatile;
///
/// let value: i32 = 0;
/// unsafe {
///   let mut ptr = Volatile::new(&value);
///   let mask = 0x0F0F;
///   *ptr |= mask;
/// }
/// assert_eq!(value, 0x0F0F);
/// ```
#[derive(Copy, Clone)]
pub struct Volatile<T: Copy>(RawVolatile<T>);

#[doc(hidden)]
#[derive(Copy, Clone)]
pub struct RawVolatile<T: Copy>(*const T);

impl<T: Copy> Volatile<T> {
  /// Creates a new `Volatile` pointer.
  ///
  /// This is unsafe because the address could be potentially anywhere, and forcing a write to a
  /// memory address could cause undefined behavior if the wrong address is chosen.
  pub unsafe fn new(ptr: *const T) -> Self {
    Volatile(RawVolatile(ptr))
  }

  /// Returns the inner pointer.
  pub fn as_ptr(self) -> *const T {
    (self.0).0
  }

  /// Returns the inner pointer mutably.
  pub fn as_mut(self) -> *mut T {
    (self.0).0 as *mut T
  }

  pub unsafe fn offset(self, count: isize) -> Self {
    let base = self.as_ptr() as isize;
    // TODO: See https://github.com/rust-lang/rust/issues/39056
    //  Change this back to a regular multiply once this gets fixed so we have overflow checking
    let offset = count.wrapping_mul(size_of::<T>() as isize);
    let addr = (base + offset as isize) as *const T;
    Volatile::new(addr)
  }
}

impl<T: Copy> Deref for Volatile<T> {
  type Target = RawVolatile<T>;

  fn deref(&self) -> &Self::Target {
    &self.0
  }
}

impl<T: Copy> DerefMut for Volatile<T> {
  fn deref_mut(&mut self) -> &mut Self::Target {
    &mut self.0
  }
}

/*** Raw Volatile Implementation ***/

impl<T: Copy> RawVolatile<T> {
  /// Stores a value into the address pointed at.
  pub unsafe fn store(&mut self, rhs: T) {
    volatile_store(self.0 as *mut T, rhs);
  }

  /// Loads a value from the address pointed at.
  pub unsafe fn load(&self) -> T {
    volatile_load(self.0 as *const T)
  }
}

impl<T: Add<Output=T> + Copy> Add<T> for RawVolatile<T> {
  type Output = T;

  fn add(self, rhs: T) -> Self::Output {
    unsafe {
      volatile_load(self.0) + rhs
    }
  }
}

impl<T: Add<Output=T> + Copy> AddAssign<T> for RawVolatile<T> {
  fn add_assign(&mut self, rhs: T) {
    unsafe {
      volatile_store(self.0 as *mut T, volatile_load(self.0) + rhs);
    }
  }
}

impl<T: Sub<Output=T> + Copy> Sub<T> for RawVolatile<T> {
  type Output = T;

  fn sub(self, rhs: T) -> Self::Output {
    unsafe {
      volatile_load(self.0) - rhs
    }
  }
}

impl<T: Sub<Output=T> + Copy> SubAssign<T> for RawVolatile<T> {
  fn sub_assign(&mut self, rhs: T) {
    unsafe {
      volatile_store(self.0 as *mut T, volatile_load(self.0) - rhs);
    }
  }
}

impl<T: Mul<Output=T> + Copy> Mul<T> for RawVolatile<T> {
  type Output = T;

  fn mul(self, rhs: T) -> Self::Output {
    unsafe {
      volatile_load(self.0) * rhs
    }
  }
}

impl<T: Mul<Output=T> + Copy> MulAssign<T> for RawVolatile<T> {
  fn mul_assign(&mut self, rhs: T) {
    unsafe {
      volatile_store(self.0 as *mut T, volatile_load(self.0) * rhs);
    }
  }
}

impl<T: Div<Output=T> + Copy> Div<T> for RawVolatile<T> {
  type Output = T;

  fn div(self, rhs: T) -> Self::Output {
    unsafe {
      volatile_load(self.0) / rhs
    }
  }
}

impl<T: Div<Output=T> + Copy> DivAssign<T> for RawVolatile<T> {
  fn div_assign(&mut self, rhs: T) {
    unsafe {
      volatile_store(self.0 as *mut T, volatile_load(self.0) / rhs);
    }
  }
}

impl<T: Rem<Output=T> + Copy> Rem<T> for RawVolatile<T> {
  type Output = T;

  fn rem(self, rhs: T) -> Self::Output {
    unsafe {
      volatile_load(self.0) % rhs
    }
  }
}

impl<T: Rem<Output=T> + Copy> RemAssign<T> for RawVolatile<T> {
  fn rem_assign(&mut self, rhs: T) {
    unsafe {
      volatile_store(self.0 as *mut T, volatile_load(self.0) % rhs);
    }
  }
}

/*** Bitwise Operators ***/

impl<T: BitAnd<Output=T> + Copy> BitAnd<T> for RawVolatile<T> {
  type Output = T;

  fn bitand(self, rhs: T) -> Self::Output {
    unsafe {
      volatile_load(self.0) & rhs
    }
  }
}

impl<T: BitAnd<Output=T> + Copy> BitAndAssign<T> for RawVolatile<T> {
  fn bitand_assign(&mut self, rhs: T) {
    unsafe {
      volatile_store(self.0 as *mut T, volatile_load(self.0) & rhs);
    }
  }
}

impl<T: BitOr<Output=T> + Copy> BitOr<T> for RawVolatile<T> {
  type Output = T;

  fn bitor(self, rhs: T) -> Self::Output {
    unsafe {
      volatile_load(self.0) | rhs
    }
  }
}

impl<T: BitOr<Output=T> + Copy> BitOrAssign<T> for RawVolatile<T> {
  fn bitor_assign(&mut self, rhs: T) {
    unsafe {
      volatile_store(self.0 as *mut T, volatile_load(self.0) | rhs);
    }
  }
}

impl<T: BitXor<Output=T> + Copy> BitXor<T> for RawVolatile<T> {
  type Output = T;

  fn bitxor(self, rhs: T) -> Self::Output {
    unsafe {
      volatile_load(self.0) ^ rhs
    }
  }
}

impl<T: BitXor<Output=T> + Copy> BitXorAssign<T> for RawVolatile<T> {
  fn bitxor_assign(&mut self, rhs: T) {
    unsafe {
      volatile_store(self.0 as *mut T, volatile_load(self.0) ^ rhs);
    }
  }
}

impl<T: Shl<T, Output=T> + Copy> Shl<T> for RawVolatile<T> {
  type Output = T;

  fn shl(self, rhs: T) -> Self::Output {
    unsafe {
      volatile_load(self.0) << rhs
    }
  }
}

impl<T: Shl<T, Output=T> + Copy> ShlAssign<T> for RawVolatile<T> {
  fn shl_assign(&mut self, rhs: T) {
    unsafe {
      volatile_store(self.0 as *mut T, volatile_load(self.0) << rhs);
    }
  }
}

impl<T: Shr<T, Output=T> + Copy> Shr<T> for RawVolatile<T> {
  type Output = T;

  fn shr(self, rhs: T) -> Self::Output {
    unsafe {
      volatile_load(self.0) >> rhs
    }
  }
}

impl<T: Shr<T, Output=T> + Copy> ShrAssign<T> for RawVolatile<T> {
  fn shr_assign(&mut self, rhs: T) {
    unsafe {
      volatile_store(self.0 as *mut T, volatile_load(self.0) >> rhs);
    }
  }
}

/*** Negation ***/

impl<T: Neg<Output=T> + Copy> Neg for RawVolatile<T> {
  type Output = T;

  fn neg(self) -> Self::Output {
    unsafe {
      -volatile_load(self.0)
    }
  }
}

impl<T: Not<Output=T> + Copy> Not for RawVolatile<T> {
  type Output = T;

  fn not(self) -> Self::Output {
    unsafe {
      !volatile_load(self.0)
    }
  }
}

<|endoftext|>


<commit_before>#[macro_use]
extern crate rocket;

mod database;
mod domain;

use std::path::Path;

use aws_sdk_s3::types::ByteStream;
use aws_sdk_s3::Client;
use chrono::{DateTime, Datelike, SecondsFormat, Utc};
use dotenv::dotenv;
use rocket::data::ToByteUnit;
use rocket::http::ContentType;
use rocket::outcome::{try_outcome, Outcome};
use rocket::request::FromRequest;
use rocket::request::{self, FromParam};
use rocket::serde::json::Json;
use rocket::serde::Serialize;
use rocket::{Data, Request};
use rocket_db_pools::{Connection, Database};
use tokio::fs::File;

use database::Db;
use domain::entities::{ApiKeyId, DropId, User, UserId};
use domain::ApiKeyValue;

impl<'a> FromParam<'a> for DropId {
    type Error = String;

    fn from_param(param: &'a str) -> Result<Self, Self::Error> {
        DropId::try_from(param)
            .map_err(|err| format!("failed to parse '{}' as a drop ID: {}", param, err))
    }
}

impl<'a> FromParam<'a> for UserId {
    type Error = String;

    fn from_param(param: &'a str) -> Result<Self, Self::Error> {
        UserId::try_from(param)
            .map_err(|err| format!("failed to parse '{}' as a user ID: {}", param, err))
    }
}

pub struct ApiKeyBearer {
    user: User,
}

struct ApiKeyLookup {
    pub id: String,
    pub username: String,
    pub full_name: Option<String>,
}

#[rocket::async_trait]
impl<'r> FromRequest<'r> for ApiKeyBearer {
    type Error = String;

    async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
        let mut db = try_outcome!(req
            .guard::<Connection<Db>>()
            .await
            .map_failure(|f| (f.0, "failed to acquire database connection".to_string())));

        match req.headers().get_one("Authorization") {
            Some(authorization) => {
                let token = authorization.replace("Bearer ", "");

                let api_key = sqlx::query_as!(
                    ApiKeyLookup,
                    "
                    select user.id, user.username, user.full_name from api_key
                    inner join user on user.id = api_key.user_id
                    where api_key.value = $1
                    ",
                    token
                )
                .fetch_one(&mut *db)
                .await
                .unwrap();

                Outcome::Success(ApiKeyBearer {
                    user: User {
                        id: UserId::try_from(api_key.id).unwrap(),
                        username: api_key.username,
                        full_name: api_key.full_name,
                    },
                })
            }
            _ => Outcome::Forward(()),
        }
    }
}

#[get("/")]
async fn index() -> &'static str {
    "滴"
}

#[post("/drops", data = "<drop>")]
async fn upload_drop(_bearer: ApiKeyBearer, drop: Data<'_>) -> std::io::Result<String> {
    let id = DropId::new();

    let temp_filepath = Path::new("temp").join(id.to_string());

    drop.open(2.mebibytes()).into_file(&temp_filepath).await?;

    let s3_config = aws_config::load_from_env().await;
    let client = Client::new(&s3_config);

    let body = ByteStream::from_path(&temp_filepath).await?;

    client
        .put_object()
        .bucket(env!("AWS_S3_BUCKET"))
        .key(format!(
            "drops/{}/{}",
            id.created_at().date().year(),
            id.unprefixed()
        ))
        .body(body)
        .send()
        .await
        .expect("failed to upload to S3");

    tokio::fs::remove_file(temp_filepath).await.ok();

    Ok(id.to_string())
}

#[get("/drops/<id>")]
async fn get_drop(id: DropId) -> (ContentType, Vec<u8>) {
    let s3_config = aws_config::load_from_env().await;
    let client = Client::new(&s3_config);

    let object = client
        .get_object()
        .bucket(env!("AWS_S3_BUCKET"))
        .key(format!(
            "drops/{}/{}",
            id.created_at().date().year(),
            id.unprefixed()
        ))
        .send()
        .await
        .expect("failed to retrieve object from S3");

    let bytes = object
        .body
        .collect()
        .await
        .map(|data| data.into_bytes())
        .unwrap()
        .to_vec();

    let mime_type = tree_magic_mini::from_u8(&bytes);

    let content_type = ContentType::parse_flexible(mime_type).unwrap_or(ContentType::Binary);

    (content_type, bytes)
}

#[derive(Debug, Serialize)]
pub struct DropMetadata {
    pub created_at: DateTime<Utc>,
    pub size_in_bytes: u64,
}

#[get("/drops/<id>/meta")]
async fn get_drop_metadata(id: DropId) -> Json<DropMetadata> {
    let upload_dir = "upload";
    let filepath = Path::new(upload_dir).join(id.unprefixed());

    let file = File::open(&filepath).await.unwrap();
    let file_metadata = File::metadata(&file).await.unwrap();

    Json(DropMetadata {
        created_at: id.created_at(),
        size_in_bytes: file_metadata.len(),
    })
}

#[derive(Debug, Serialize)]
pub struct UserJson {
    pub id: String,
    pub username: String,
    pub full_name: Option<String>,
}

#[post("/users")]
async fn create_user(
    _bearer: ApiKeyBearer,
    mut db: Connection<Db>,
) -> database::Result<Json<UserJson>> {
    let id = UserId::new();

    let username = "maxdeviant";
    let full_name = "Marshall Bowers";

    let unprefixed_id = id.unprefixed();
    let now = Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true);

    sqlx::query!(
        "
        insert into user (id, created_at, updated_at, username, full_name)
        values (?, ?, ?, ?, ?)
        ",
        unprefixed_id,
        now,
        now,
        username,
        full_name
    )
    .execute(&mut *db)
    .await?;

    Ok(Json(UserJson {
        id: id.to_string(),
        username: username.to_string(),
        full_name: Some(full_name.to_string()),
    }))
}

#[derive(Debug, Serialize)]
pub struct ApiKeyJson {
    pub id: String,
    pub value: String,
}

#[post("/keys")]
async fn generate_api_key(
    bearer: ApiKeyBearer,
    mut db: Connection<Db>,
) -> database::Result<Json<ApiKeyJson>> {
    let id = ApiKeyId::new();
    let value = ApiKeyValue::new();

    let unprefixed_id = id.unprefixed();
    let unprefixed_user_id = bearer.user.id.unprefixed();
    let key_value = value.to_string();
    let now = Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true);

    sqlx::query!(
        "
        insert into api_key (id, created_at, updated_at, user_id, value)
        values (?, ?, ?, ?, ?)
        ",
        unprefixed_id,
        now,
        now,
        unprefixed_user_id,
        key_value
    )
    .execute(&mut *db)
    .await?;

    Ok(Json(ApiKeyJson {
        id: id.to_string(),
        value: value.to_string(),
    }))
}

async fn ensure_root_user_exists() {
    use sqlx::{Connection, SqliteConnection};

    let root_username = env!("ROOT_USERNAME");
    let root_api_key = env!("ROOT_API_KEY");

    let database_url = env!("DATABASE_URL");
    let mut connection = SqliteConnection::connect(database_url).await.unwrap();

    let user_id = UserId::new();
    let unprefixed_user_id = user_id.unprefixed();
    let now = Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true);

    #[derive(Debug)]
    struct RootUser {
        id: String,
    }

    let root_user = sqlx::query_as!(
        RootUser,
        "
        insert into user (id, created_at, updated_at, username)
        values (?, ?, ?, ?)
        -- This is a no-op update just so we can get the `id` returned.
        on conflict (username) do update set updated_at = updated_at
        returning id
        ",
        unprefixed_user_id,
        now,
        now,
        root_username
    )
    .fetch_one(&mut connection)
    .await
    .expect("failed to insert root user");

    println!("Root user: {:?}", root_user);

    let key_id = ApiKeyId::new();
    let unprefixed_key_id = key_id.unprefixed();
    let now = Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true);

    sqlx::query!(
        "
        insert into api_key (id, created_at, updated_at, user_id, value)
        values (?, ?, ?, ?, ?)
        on conflict (value) do nothing
        ",
        unprefixed_key_id,
        now,
        now,
        root_user.id,
        root_api_key
    )
    .execute(&mut connection)
    .await
    .unwrap();
}

#[rocket::main]
async fn main() -> Result<(), rocket::Error> {
    dotenv().ok();

    ensure_root_user_exists().await;

    let rocket = rocket::build()
        .mount(
            "/",
            routes![index, upload_drop, get_drop, get_drop_metadata,],
        )
        .mount("/x/", routes![create_user, generate_api_key])
        .attach(Db::init())
        .ignite()
        .await?;

    let _ = rocket.launch().await?;

    Ok(())
}
<commit_msg>Update metadata endpoint to use S3 as well<commit_after>#[macro_use]
extern crate rocket;

mod database;
mod domain;

use std::path::Path;

use aws_sdk_s3::types::ByteStream;
use aws_sdk_s3::Client;
use chrono::{DateTime, Datelike, SecondsFormat, Utc};
use dotenv::dotenv;
use rocket::data::ToByteUnit;
use rocket::http::ContentType;
use rocket::outcome::{try_outcome, Outcome};
use rocket::request::FromRequest;
use rocket::request::{self, FromParam};
use rocket::serde::json::Json;
use rocket::serde::Serialize;
use rocket::{Data, Request};
use rocket_db_pools::{Connection, Database};

use database::Db;
use domain::entities::{ApiKeyId, DropId, User, UserId};
use domain::ApiKeyValue;

impl<'a> FromParam<'a> for DropId {
    type Error = String;

    fn from_param(param: &'a str) -> Result<Self, Self::Error> {
        DropId::try_from(param)
            .map_err(|err| format!("failed to parse '{}' as a drop ID: {}", param, err))
    }
}

impl<'a> FromParam<'a> for UserId {
    type Error = String;

    fn from_param(param: &'a str) -> Result<Self, Self::Error> {
        UserId::try_from(param)
            .map_err(|err| format!("failed to parse '{}' as a user ID: {}", param, err))
    }
}

pub struct ApiKeyBearer {
    user: User,
}

struct ApiKeyLookup {
    pub id: String,
    pub username: String,
    pub full_name: Option<String>,
}

#[rocket::async_trait]
impl<'r> FromRequest<'r> for ApiKeyBearer {
    type Error = String;

    async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
        let mut db = try_outcome!(req
            .guard::<Connection<Db>>()
            .await
            .map_failure(|f| (f.0, "failed to acquire database connection".to_string())));

        match req.headers().get_one("Authorization") {
            Some(authorization) => {
                let token = authorization.replace("Bearer ", "");

                let api_key = sqlx::query_as!(
                    ApiKeyLookup,
                    "
                    select user.id, user.username, user.full_name from api_key
                    inner join user on user.id = api_key.user_id
                    where api_key.value = $1
                    ",
                    token
                )
                .fetch_one(&mut *db)
                .await
                .unwrap();

                Outcome::Success(ApiKeyBearer {
                    user: User {
                        id: UserId::try_from(api_key.id).unwrap(),
                        username: api_key.username,
                        full_name: api_key.full_name,
                    },
                })
            }
            _ => Outcome::Forward(()),
        }
    }
}

#[get("/")]
async fn index() -> &'static str {
    "滴"
}

#[post("/drops", data = "<drop>")]
async fn upload_drop(_bearer: ApiKeyBearer, drop: Data<'_>) -> std::io::Result<String> {
    let id = DropId::new();

    let temp_filepath = Path::new("temp").join(id.to_string());

    drop.open(2.mebibytes()).into_file(&temp_filepath).await?;

    let s3_config = aws_config::load_from_env().await;
    let client = Client::new(&s3_config);

    let body = ByteStream::from_path(&temp_filepath).await?;

    client
        .put_object()
        .bucket(env!("AWS_S3_BUCKET"))
        .key(format!(
            "drops/{}/{}",
            id.created_at().date().year(),
            id.unprefixed()
        ))
        .body(body)
        .send()
        .await
        .expect("failed to upload to S3");

    tokio::fs::remove_file(temp_filepath).await.ok();

    Ok(id.to_string())
}

#[get("/drops/<id>")]
async fn get_drop(id: DropId) -> (ContentType, Vec<u8>) {
    let s3_config = aws_config::load_from_env().await;
    let client = Client::new(&s3_config);

    let object = client
        .get_object()
        .bucket(env!("AWS_S3_BUCKET"))
        .key(format!(
            "drops/{}/{}",
            id.created_at().date().year(),
            id.unprefixed()
        ))
        .send()
        .await
        .expect("failed to retrieve object from S3");

    let bytes = object
        .body
        .collect()
        .await
        .map(|data| data.into_bytes())
        .unwrap()
        .to_vec();

    let mime_type = tree_magic_mini::from_u8(&bytes);

    let content_type = ContentType::parse_flexible(mime_type).unwrap_or(ContentType::Binary);

    (content_type, bytes)
}

#[derive(Debug, Serialize)]
pub struct DropMetadata {
    pub created_at: DateTime<Utc>,
    pub size_in_bytes: i64,
}

#[get("/drops/<id>/meta")]
async fn get_drop_metadata(id: DropId) -> Json<DropMetadata> {
    let s3_config = aws_config::load_from_env().await;
    let client = Client::new(&s3_config);

    let object = client
        .get_object()
        .bucket(env!("AWS_S3_BUCKET"))
        .key(format!(
            "drops/{}/{}",
            id.created_at().date().year(),
            id.unprefixed()
        ))
        .send()
        .await
        .expect("failed to retrieve object from S3");

    Json(DropMetadata {
        created_at: id.created_at(),
        size_in_bytes: object.content_length,
    })
}

#[derive(Debug, Serialize)]
pub struct UserJson {
    pub id: String,
    pub username: String,
    pub full_name: Option<String>,
}

#[post("/users")]
async fn create_user(
    _bearer: ApiKeyBearer,
    mut db: Connection<Db>,
) -> database::Result<Json<UserJson>> {
    let id = UserId::new();

    let username = "maxdeviant";
    let full_name = "Marshall Bowers";

    let unprefixed_id = id.unprefixed();
    let now = Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true);

    sqlx::query!(
        "
        insert into user (id, created_at, updated_at, username, full_name)
        values (?, ?, ?, ?, ?)
        ",
        unprefixed_id,
        now,
        now,
        username,
        full_name
    )
    .execute(&mut *db)
    .await?;

    Ok(Json(UserJson {
        id: id.to_string(),
        username: username.to_string(),
        full_name: Some(full_name.to_string()),
    }))
}

#[derive(Debug, Serialize)]
pub struct ApiKeyJson {
    pub id: String,
    pub value: String,
}

#[post("/keys")]
async fn generate_api_key(
    bearer: ApiKeyBearer,
    mut db: Connection<Db>,
) -> database::Result<Json<ApiKeyJson>> {
    let id = ApiKeyId::new();
    let value = ApiKeyValue::new();

    let unprefixed_id = id.unprefixed();
    let unprefixed_user_id = bearer.user.id.unprefixed();
    let key_value = value.to_string();
    let now = Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true);

    sqlx::query!(
        "
        insert into api_key (id, created_at, updated_at, user_id, value)
        values (?, ?, ?, ?, ?)
        ",
        unprefixed_id,
        now,
        now,
        unprefixed_user_id,
        key_value
    )
    .execute(&mut *db)
    .await?;

    Ok(Json(ApiKeyJson {
        id: id.to_string(),
        value: value.to_string(),
    }))
}

async fn ensure_root_user_exists() {
    use sqlx::{Connection, SqliteConnection};

    let root_username = env!("ROOT_USERNAME");
    let root_api_key = env!("ROOT_API_KEY");

    let database_url = env!("DATABASE_URL");
    let mut connection = SqliteConnection::connect(database_url).await.unwrap();

    let user_id = UserId::new();
    let unprefixed_user_id = user_id.unprefixed();
    let now = Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true);

    #[derive(Debug)]
    struct RootUser {
        id: String,
    }

    let root_user = sqlx::query_as!(
        RootUser,
        "
        insert into user (id, created_at, updated_at, username)
        values (?, ?, ?, ?)
        -- This is a no-op update just so we can get the `id` returned.
        on conflict (username) do update set updated_at = updated_at
        returning id
        ",
        unprefixed_user_id,
        now,
        now,
        root_username
    )
    .fetch_one(&mut connection)
    .await
    .expect("failed to insert root user");

    println!("Root user: {:?}", root_user);

    let key_id = ApiKeyId::new();
    let unprefixed_key_id = key_id.unprefixed();
    let now = Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true);

    sqlx::query!(
        "
        insert into api_key (id, created_at, updated_at, user_id, value)
        values (?, ?, ?, ?, ?)
        on conflict (value) do nothing
        ",
        unprefixed_key_id,
        now,
        now,
        root_user.id,
        root_api_key
    )
    .execute(&mut connection)
    .await
    .unwrap();
}

#[rocket::main]
async fn main() -> Result<(), rocket::Error> {
    dotenv().ok();

    ensure_root_user_exists().await;

    let rocket = rocket::build()
        .mount(
            "/",
            routes![index, upload_drop, get_drop, get_drop_metadata,],
        )
        .mount("/x/", routes![create_user, generate_api_key])
        .attach(Db::init())
        .ignite()
        .await?;

    let _ = rocket.launch().await?;

    Ok(())
}
<|endoftext|>


<commit_before>// Copyright © 2017-2018 Mozilla Foundation
//
// This program is made available under an ISC-style license.  See the
// accompanying file LICENSE for details.

//! Stream Functions
//!
//! # Example
//! ```no_run
//! extern crate cubeb;
//! use std::thread;
//! use std::time::Duration;
//!
//! type Frame = cubeb::MonoFrame<f32>;
//!
//! fn main() {
//!     let ctx = cubeb::init("Cubeb tone example").unwrap();
//!
//!     let params = cubeb::StreamParamsBuilder::new()
//!         .format(cubeb::SampleFormat::Float32LE)
//!         .rate(44_100)
//!         .channels(1)
//!         .layout(cubeb::ChannelLayout::MONO)
//!         .prefs(cubeb::StreamPrefs::NONE)
//!         .take();
//!
//!     let phase_inc = 440.0 / 44_100.0;
//!     let mut phase = 0.0;
//!     let volume = 0.25;
//!
//!     let mut builder = cubeb::StreamBuilder::<Frame>::new();
//!     builder
//!         .name("Cubeb Square Wave")
//!         .default_output(&params)
//!         .latency(0x1000)
//!         .data_callback(move |_, output| {
//!             // Generate a square wave
//!             for x in output.iter_mut() {
//!                 x.m = if phase < 0.5 { volume } else { -volume };
//!                 phase = (phase + phase_inc) % 1.0;
//!             }
//!
//!             output.len() as isize
//!         })
//!         .state_callback(|state| {
//!             println!("stream {:?}", state);
//!         });
//!     let stream = builder.init(&ctx).expect("Failed to create stream.");
//!
//!     // Start playback
//!     stream.start().unwrap();
//!
//!     // Play for 1/2 second
//!     thread::sleep(Duration::from_millis(500));
//!
//!     // Shutdown
//!     stream.stop().unwrap();
//! }
//! ```

use {ContextRef, DeviceId, Error, Result, State, StreamParamsRef};
use cubeb_core;
use ffi;
use std::{ops, panic, ptr};
use std::ffi::CString;
use std::marker::PhantomData;
use std::os::raw::{c_long, c_void};
use std::slice::{from_raw_parts, from_raw_parts_mut};

pub type DataCallback<F> = FnMut(&[F], &mut [F]) -> isize + Send + Sync + 'static;
pub type StateCallback = FnMut(State) + Send + Sync + 'static;
pub type DeviceChangedCallback = FnMut() + Send + Sync + 'static;

pub struct StreamCallbacks<F> {
    pub(crate) data: Box<DataCallback<F>>,
    pub(crate) state: Box<StateCallback>,
    pub(crate) device_changed: Option<Box<DeviceChangedCallback>>,
}

pub struct Stream<F>(cubeb_core::Stream, PhantomData<*const F>);

impl<F> Stream<F> {
    fn new(s: cubeb_core::Stream) -> Stream<F> {
        Stream(s, PhantomData)
    }
}

impl<F> Drop for Stream<F> {
    fn drop(&mut self) {
        let _ = unsafe { Box::from_raw(self.user_ptr() as *mut StreamCallbacks<F>) };
    }
}

impl<F> ops::Deref for Stream<F> {
    type Target = cubeb_core::Stream;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

pub struct StreamBuilder<'a, F> {
    name: Option<CString>,
    input: Option<(DeviceId, &'a StreamParamsRef)>,
    output: Option<(DeviceId, &'a StreamParamsRef)>,
    latency: Option<u32>,
    data_cb: Option<Box<DataCallback<F>>>,
    state_cb: Option<Box<StateCallback>>,
    device_changed_cb: Option<Box<DeviceChangedCallback>>,
}

impl<'a, F> StreamBuilder<'a, F> {
    pub fn new() -> StreamBuilder<'a, F> {
        StreamBuilder {
            name: None,
            input: None,
            output: None,
            latency: None,
            data_cb: None,
            state_cb: None,
            device_changed_cb: None,
        }
    }

    pub fn data_callback<D>(&mut self, cb: D) -> &mut Self
    where
        D: FnMut(&[F], &mut [F]) -> isize + Send + Sync + 'static,
    {
        self.data_cb = Some(Box::new(cb) as Box<DataCallback<F>>);
        self
    }
    pub fn state_callback<S>(&mut self, cb: S) -> &mut Self
    where
        S: FnMut(State) + Send + Sync + 'static,
    {
        self.state_cb = Some(Box::new(cb) as Box<StateCallback>);
        self
    }

    pub fn name<T: Into<Vec<u8>>>(&mut self, name: T) -> &mut Self {
        self.name = Some(CString::new(name).unwrap());
        self
    }

    pub fn default_input(&mut self, params: &'a StreamParamsRef) -> &mut Self {
        self.input = Some((ptr::null(), params));
        self
    }

    pub fn input(&mut self, device: DeviceId, params: &'a StreamParamsRef) -> &mut Self {
        self.input = Some((device, params));
        self
    }

    pub fn default_output(&mut self, params: &'a StreamParamsRef) -> &mut Self {
        self.output = Some((ptr::null(), params));
        self
    }

    pub fn output(&mut self, device: DeviceId, params: &'a StreamParamsRef) -> &mut Self {
        self.output = Some((device, params));
        self
    }

    pub fn latency(&mut self, latency: u32) -> &mut Self {
        self.latency = Some(latency);
        self
    }

    pub fn device_changed_cb<CB>(&mut self, cb: CB) -> &mut Self
    where
        CB: FnMut() + Send + Sync + 'static,
    {
        self.device_changed_cb = Some(Box::new(cb) as Box<DeviceChangedCallback>);
        self
    }

    pub fn init(self, ctx: &ContextRef) -> Result<Stream<F>> {
        if self.data_cb.is_none() || self.state_cb.is_none() {
            return Err(Error::error());
        }

        let has_device_changed = self.device_changed_cb.is_some();
        let cbs = Box::into_raw(Box::new(StreamCallbacks {
            data: self.data_cb.unwrap(),
            state: self.state_cb.unwrap(),
            device_changed: self.device_changed_cb,
        }));

        let stream_name = self.name.as_ref().and_then(|n| Some(n.as_c_str()));
        let (input_device, input_stream_params) =
            self.input.map_or((ptr::null(), None), |x| (x.0, Some(x.1)));
        let (output_device, output_stream_params) = self.output
            .map_or((ptr::null(), None), |x| (x.0, Some(x.1)));
        let latency = self.latency.unwrap_or(1);
        let data_callback: ffi::cubeb_data_callback = Some(data_cb_c::<F>);
        let state_callback: ffi::cubeb_state_callback = Some(state_cb_c::<F>);

        let stream = unsafe {
            try!(ctx.stream_init(
                stream_name,
                input_device,
                input_stream_params,
                output_device,
                output_stream_params,
                latency,
                data_callback,
                state_callback,
                cbs as *mut _
            ))
        };
        if has_device_changed {
            let device_changed_callback: ffi::cubeb_device_changed_callback =
                Some(device_changed_cb_c::<F>);
            try!(stream.register_device_changed_callback(device_changed_callback));
        }
        Ok(Stream::new(stream))
    }
}

// C callable callbacks
unsafe extern "C" fn data_cb_c<F>(
    _: *mut ffi::cubeb_stream,
    user_ptr: *mut c_void,
    input_buffer: *const c_void,
    output_buffer: *mut c_void,
    nframes: c_long,
) -> c_long {
    let ok = panic::catch_unwind(|| {
        let cbs = &mut *(user_ptr as *mut StreamCallbacks<F>);
        let input: &[F] = if input_buffer.is_null() {
            &[]
        } else {
            from_raw_parts(input_buffer as *const _, nframes as usize)
        };
        let output: &mut [F] = if output_buffer.is_null() {
            &mut []
        } else {
            from_raw_parts_mut(output_buffer as *mut _, nframes as usize)
        };
        (cbs.data)(input, output) as c_long
    });
    ok.unwrap_or(0)
}

unsafe extern "C" fn state_cb_c<F>(
    _: *mut ffi::cubeb_stream,
    user_ptr: *mut c_void,
    state: ffi::cubeb_state,
) {
    let ok = panic::catch_unwind(|| {
        let state = State::from(state);
        let cbs = &mut *(user_ptr as *mut StreamCallbacks<F>);
        (cbs.state)(state);
    });
    ok.expect("State callback panicked");
}

unsafe extern "C" fn device_changed_cb_c<F>(user_ptr: *mut c_void) {
    let ok = panic::catch_unwind(|| {
        let cbs = &mut *(user_ptr as *mut StreamCallbacks<F>);
        if let Some(ref mut device_changed) = cbs.device_changed {
            device_changed();
        }
    });
    ok.expect("Device changed callback panicked");
}
<commit_msg>cubeb-api: Drop inner Stream before deallocating StreamCallbacks.<commit_after>// Copyright © 2017-2018 Mozilla Foundation
//
// This program is made available under an ISC-style license.  See the
// accompanying file LICENSE for details.

//! Stream Functions
//!
//! # Example
//! ```no_run
//! extern crate cubeb;
//! use std::thread;
//! use std::time::Duration;
//!
//! type Frame = cubeb::MonoFrame<f32>;
//!
//! fn main() {
//!     let ctx = cubeb::init("Cubeb tone example").unwrap();
//!
//!     let params = cubeb::StreamParamsBuilder::new()
//!         .format(cubeb::SampleFormat::Float32LE)
//!         .rate(44_100)
//!         .channels(1)
//!         .layout(cubeb::ChannelLayout::MONO)
//!         .prefs(cubeb::StreamPrefs::NONE)
//!         .take();
//!
//!     let phase_inc = 440.0 / 44_100.0;
//!     let mut phase = 0.0;
//!     let volume = 0.25;
//!
//!     let mut builder = cubeb::StreamBuilder::<Frame>::new();
//!     builder
//!         .name("Cubeb Square Wave")
//!         .default_output(&params)
//!         .latency(0x1000)
//!         .data_callback(move |_, output| {
//!             // Generate a square wave
//!             for x in output.iter_mut() {
//!                 x.m = if phase < 0.5 { volume } else { -volume };
//!                 phase = (phase + phase_inc) % 1.0;
//!             }
//!
//!             output.len() as isize
//!         })
//!         .state_callback(|state| {
//!             println!("stream {:?}", state);
//!         });
//!     let stream = builder.init(&ctx).expect("Failed to create stream.");
//!
//!     // Start playback
//!     stream.start().unwrap();
//!
//!     // Play for 1/2 second
//!     thread::sleep(Duration::from_millis(500));
//!
//!     // Shutdown
//!     stream.stop().unwrap();
//! }
//! ```

use {ContextRef, DeviceId, Error, Result, State, StreamParamsRef};
use cubeb_core;
use ffi;
use std::{ops, panic, ptr};
use std::ffi::CString;
use std::marker::PhantomData;
use std::mem::ManuallyDrop;
use std::os::raw::{c_long, c_void};
use std::slice::{from_raw_parts, from_raw_parts_mut};

pub type DataCallback<F> = FnMut(&[F], &mut [F]) -> isize + Send + Sync + 'static;
pub type StateCallback = FnMut(State) + Send + Sync + 'static;
pub type DeviceChangedCallback = FnMut() + Send + Sync + 'static;

pub struct StreamCallbacks<F> {
    pub(crate) data: Box<DataCallback<F>>,
    pub(crate) state: Box<StateCallback>,
    pub(crate) device_changed: Option<Box<DeviceChangedCallback>>,
}

pub struct Stream<F>(ManuallyDrop<cubeb_core::Stream>,
                     PhantomData<*const F>);

impl<F> Stream<F> {
    fn new(s: cubeb_core::Stream) -> Stream<F> {
        Stream(ManuallyDrop::new(s), PhantomData)
    }
}

impl<F> Drop for Stream<F> {
    fn drop(&mut self) {
        let user_ptr = self.user_ptr();
        unsafe { ManuallyDrop::drop(&mut self.0) };
        let _ = unsafe { Box::from_raw(user_ptr as *mut StreamCallbacks<F>) };
    }
}

impl<F> ops::Deref for Stream<F> {
    type Target = cubeb_core::Stream;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

pub struct StreamBuilder<'a, F> {
    name: Option<CString>,
    input: Option<(DeviceId, &'a StreamParamsRef)>,
    output: Option<(DeviceId, &'a StreamParamsRef)>,
    latency: Option<u32>,
    data_cb: Option<Box<DataCallback<F>>>,
    state_cb: Option<Box<StateCallback>>,
    device_changed_cb: Option<Box<DeviceChangedCallback>>,
}

impl<'a, F> StreamBuilder<'a, F> {
    pub fn new() -> StreamBuilder<'a, F> {
        StreamBuilder {
            name: None,
            input: None,
            output: None,
            latency: None,
            data_cb: None,
            state_cb: None,
            device_changed_cb: None,
        }
    }

    pub fn data_callback<D>(&mut self, cb: D) -> &mut Self
    where
        D: FnMut(&[F], &mut [F]) -> isize + Send + Sync + 'static,
    {
        self.data_cb = Some(Box::new(cb) as Box<DataCallback<F>>);
        self
    }
    pub fn state_callback<S>(&mut self, cb: S) -> &mut Self
    where
        S: FnMut(State) + Send + Sync + 'static,
    {
        self.state_cb = Some(Box::new(cb) as Box<StateCallback>);
        self
    }

    pub fn name<T: Into<Vec<u8>>>(&mut self, name: T) -> &mut Self {
        self.name = Some(CString::new(name).unwrap());
        self
    }

    pub fn default_input(&mut self, params: &'a StreamParamsRef) -> &mut Self {
        self.input = Some((ptr::null(), params));
        self
    }

    pub fn input(&mut self, device: DeviceId, params: &'a StreamParamsRef) -> &mut Self {
        self.input = Some((device, params));
        self
    }

    pub fn default_output(&mut self, params: &'a StreamParamsRef) -> &mut Self {
        self.output = Some((ptr::null(), params));
        self
    }

    pub fn output(&mut self, device: DeviceId, params: &'a StreamParamsRef) -> &mut Self {
        self.output = Some((device, params));
        self
    }

    pub fn latency(&mut self, latency: u32) -> &mut Self {
        self.latency = Some(latency);
        self
    }

    pub fn device_changed_cb<CB>(&mut self, cb: CB) -> &mut Self
    where
        CB: FnMut() + Send + Sync + 'static,
    {
        self.device_changed_cb = Some(Box::new(cb) as Box<DeviceChangedCallback>);
        self
    }

    pub fn init(self, ctx: &ContextRef) -> Result<Stream<F>> {
        if self.data_cb.is_none() || self.state_cb.is_none() {
            return Err(Error::error());
        }

        let has_device_changed = self.device_changed_cb.is_some();
        let cbs = Box::into_raw(Box::new(StreamCallbacks {
            data: self.data_cb.unwrap(),
            state: self.state_cb.unwrap(),
            device_changed: self.device_changed_cb,
        }));

        let stream_name = self.name.as_ref().and_then(|n| Some(n.as_c_str()));
        let (input_device, input_stream_params) =
            self.input.map_or((ptr::null(), None), |x| (x.0, Some(x.1)));
        let (output_device, output_stream_params) = self.output
            .map_or((ptr::null(), None), |x| (x.0, Some(x.1)));
        let latency = self.latency.unwrap_or(1);
        let data_callback: ffi::cubeb_data_callback = Some(data_cb_c::<F>);
        let state_callback: ffi::cubeb_state_callback = Some(state_cb_c::<F>);

        let stream = unsafe {
            try!(ctx.stream_init(
                stream_name,
                input_device,
                input_stream_params,
                output_device,
                output_stream_params,
                latency,
                data_callback,
                state_callback,
                cbs as *mut _
            ))
        };
        if has_device_changed {
            let device_changed_callback: ffi::cubeb_device_changed_callback =
                Some(device_changed_cb_c::<F>);
            try!(stream.register_device_changed_callback(device_changed_callback));
        }
        Ok(Stream::new(stream))
    }
}

// C callable callbacks
unsafe extern "C" fn data_cb_c<F>(
    _: *mut ffi::cubeb_stream,
    user_ptr: *mut c_void,
    input_buffer: *const c_void,
    output_buffer: *mut c_void,
    nframes: c_long,
) -> c_long {
    let ok = panic::catch_unwind(|| {
        let cbs = &mut *(user_ptr as *mut StreamCallbacks<F>);
        let input: &[F] = if input_buffer.is_null() {
            &[]
        } else {
            from_raw_parts(input_buffer as *const _, nframes as usize)
        };
        let output: &mut [F] = if output_buffer.is_null() {
            &mut []
        } else {
            from_raw_parts_mut(output_buffer as *mut _, nframes as usize)
        };
        (cbs.data)(input, output) as c_long
    });
    ok.unwrap_or(0)
}

unsafe extern "C" fn state_cb_c<F>(
    _: *mut ffi::cubeb_stream,
    user_ptr: *mut c_void,
    state: ffi::cubeb_state,
) {
    let ok = panic::catch_unwind(|| {
        let state = State::from(state);
        let cbs = &mut *(user_ptr as *mut StreamCallbacks<F>);
        (cbs.state)(state);
    });
    ok.expect("State callback panicked");
}

unsafe extern "C" fn device_changed_cb_c<F>(user_ptr: *mut c_void) {
    let ok = panic::catch_unwind(|| {
        let cbs = &mut *(user_ptr as *mut StreamCallbacks<F>);
        if let Some(ref mut device_changed) = cbs.device_changed {
            device_changed();
        }
    });
    ok.expect("Device changed callback panicked");
}
<|endoftext|>


<commit_before>use crate::support::{database, project};

#[test]
fn migration_revert_runs_the_last_migration_down() {
    let p = project("migration_revert_runs_the_last_migration_down")
        .folder("migrations")
        .build();
    let db = database(&p.database_url());

    p.create_migration(
        "12345_create_users_table",
        "CREATE TABLE users ( id INTEGER )",
        "DROP TABLE users",
    );

    // Make sure the project is setup
    p.command("setup").run();

    assert!(db.table_exists("users"));

    let result = p.command("migration").arg("revert").run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains("Rolling back migration 12345"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(!db.table_exists("users"));
}

#[test]
fn migration_revert_respects_migration_dir_var() {
    let p = project("migration_revert_var").folder("foo").build();
    let db = database(&p.database_url());

    p.create_migration_in_directory(
        "foo",
        "12345_create_users_table",
        "CREATE TABLE users ( id INTEGER )",
        "DROP TABLE users",
    );

    // Make sure the project is setup.
    p.command("setup").arg("--migration-dir=foo").run();

    assert!(db.table_exists("users"));

    let result = p
        .command("migration")
        .arg("revert")
        .arg("--migration-dir=foo")
        .run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains("Rolling back migration 12345"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(!db.table_exists("users"));
}

#[test]
fn migration_revert_respects_migration_dir_env() {
    let p = project("migration_revert_env").folder("bar").build();
    let db = database(&p.database_url());

    p.create_migration_in_directory(
        "bar",
        "12345_create_users_table",
        "CREATE TABLE users ( id INTEGER )",
        "DROP TABLE users",
    );

    // Make sure the project is setup.
    p.command("setup").arg("--migration-dir=bar").run();

    assert!(db.table_exists("users"));

    let result = p
        .command("migration")
        .arg("revert")
        .env("MIGRATION_DIRECTORY", "bar")
        .run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains("Rolling back migration 12345"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(!db.table_exists("users"));
}

#[test]
fn migration_revert_respects_migration_dir_from_diesel_toml() {
    let p = project("migration_revert_respects_migration_dir_from_diesel_toml")
        .folder("custom_migrations")
        .file(
            "diesel.toml",
            r#"
            [migrations_directory]
            dir = "custom_migrations"
            "#,
        )
        .build();

    let db = database(&p.database_url());

    p.create_migration_in_directory(
        "custom_migrations",
        "12345_create_users_table",
        "CREATE TABLE users ( id INTEGER )",
        "DROP TABLE users",
    );

    // Make sure the project is setup.
    p.command("setup").run();

    assert!(db.table_exists("users"));

    let result = p.command("migration").arg("revert").run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains("Rolling back migration 12345"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(!db.table_exists("users"));
}

#[test]
fn migration_revert_runs_the_last_two_migration_down() {
    let p = project("migration_revert_runs_the_last_two_migration_down")
        .folder("migrations")
        .build();
    let db = database(&p.database_url());

    p.create_migration(
        "2017-08-31-210424_create_customers",
        "CREATE TABLE customers ( id INTEGER PRIMARY KEY )",
        "DROP TABLE customers",
    );

    p.create_migration(
        "2017-09-03-210424_create_contracts",
        "CREATE TABLE contracts ( id INTEGER PRIMARY KEY )",
        "DROP TABLE contracts",
    );

    p.create_migration(
        "2017-09-12-210424_create_bills",
        "CREATE TABLE bills ( id INTEGER PRIMARY KEY )",
        "DROP TABLE bills",
    );

    // Make sure the project is setup
    p.command("setup").run();

    assert!(db.table_exists("customers"));
    assert!(db.table_exists("contracts"));
    assert!(db.table_exists("bills"));

    // Reverts the last two migration files. The `contracts` and `bills` tables should be dropped.
    // The `customers` table shouldn't be reverted.
    let result = p
        .command("migration")
        .arg("revert")
        .arg("-n")
        .arg("2")
        .run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains("Rolling back migration 2017-09-12-210424_create_bills\nRolling back migration 2017-09-03-210424_create_contracts"),
        "Unexpected stdout {}",
        result.stdout()
    );

    assert!(db.table_exists("customers"));
    assert!(!db.table_exists("contracts"));
    assert!(!db.table_exists("bills"));
}

#[test]
fn migration_revert_all_runs_the_migrations_down() {
    let p = project("migration_revert_all_runs_the_migrations_down")
        .folder("migrations")
        .build();
    let db = database(&p.database_url());

    p.create_migration(
        "2017-08-31-210424_create_customers",
        "CREATE TABLE customers ( id INTEGER PRIMARY KEY )",
        "DROP TABLE customers",
    );

    p.create_migration(
        "2017-09-03-210424_create_contracts",
        "CREATE TABLE contracts ( id INTEGER PRIMARY KEY )",
        "DROP TABLE contracts",
    );

    p.create_migration(
        "2017-09-12-210424_create_bills",
        "CREATE TABLE bills ( id INTEGER PRIMARY KEY )",
        "DROP TABLE bills",
    );

    // Make sure the project is setup
    p.command("setup").run();

    assert!(db.table_exists("customers"));
    assert!(db.table_exists("contracts"));
    assert!(db.table_exists("bills"));

    let result = p.command("migration").arg("revert").arg("--all").run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);

    assert!(
        result.stdout()
            == "Rolling back migration 2017-09-12-210424_create_bills\n\
                Rolling back migration 2017-09-03-210424_create_contracts\n\
                Rolling back migration 2017-08-31-210424_create_customers\n",
        "Unexpected stdout : {}",
        result.stdout()
    );

    assert!(!db.table_exists("customers"));
    assert!(!db.table_exists("contracts"));
    assert!(!db.table_exists("bills"));
}

#[test]
fn migration_revert_with_zero_should_not_revert_any_migration() {
    let p = project("migration_revert_with_zero_should_not_revert_any_migration")
        .folder("migrations")
        .build();
    let db = database(&p.database_url());

    p.create_migration(
        "2017-08-31-210424_create_customers",
        "CREATE TABLE customers ( id INTEGER PRIMARY KEY )",
        "DROP TABLE customers",
    );

    // Make sure the project is setup
    p.command("setup").run();

    assert!(db.table_exists("customers"));

    // Should not revert any migration.
    let result = p
        .command("migration")
        .arg("revert")
        .arg("-n")
        .arg("0")
        .run();

    assert!(!result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(result.stdout() == "");
}

#[test]
fn migration_revert_n_with_a_string_should_throw_an_error() {
    let p = project("migration_revert_with_an_invalid_input_should_throw_an_error")
        .folder("migrations")
        .build();

    // Make sure the project is setup
    p.command("setup").run();

    // Should not revert any migration.
    let result = p
        .command("migration")
        .arg("revert")
        .arg("-n")
        .arg("infinite")
        .run();

    assert!(!result.is_success(), "Result was unsuccessful {:?}", result);

    assert!(
        result.stderr() == "error: Invalid value for '--number <REVERT_NUMBER>': infinite isn't a positive integer.\n",
        "Unexpected stderr : {}",
        result.stderr()
    );
}

#[test]
fn migration_revert_with_more_than_max_should_revert_all() {
    let p = project("migration_revert_with_more_than_max_should_revert_all")
        .folder("migrations")
        .build();
    let db = database(&p.database_url());

    p.create_migration(
        "2017-08-31-210424_create_customers",
        "CREATE TABLE customers ( id INTEGER PRIMARY KEY )",
        "DROP TABLE customers",
    );

    p.create_migration(
        "2017-09-03-210424_create_contracts",
        "CREATE TABLE contracts ( id INTEGER PRIMARY KEY )",
        "DROP TABLE contracts",
    );

    p.create_migration(
        "2017-09-12-210424_create_bills",
        "CREATE TABLE bills ( id INTEGER PRIMARY KEY )",
        "DROP TABLE bills",
    );

    // Make sure the project is setup
    p.command("setup").run();

    assert!(db.table_exists("customers"));
    assert!(db.table_exists("contracts"));
    assert!(db.table_exists("bills"));

    let result = p
        .command("migration")
        .arg("revert")
        .arg("-n")
        .arg("1000")
        .run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);

    assert!(
        result.stdout()
            == "Rolling back migration 2017-09-12-210424_create_bills\n\
                Rolling back migration 2017-09-03-210424_create_contracts\n\
                Rolling back migration 2017-08-31-210424_create_customers\n",
        "Unexpected stdout : {}",
        result.stdout()
    );

    assert!(!db.table_exists("customers"));
    assert!(!db.table_exists("contracts"));
    assert!(!db.table_exists("bills"));
}
<commit_msg>Use a shorter identifier name for mysql<commit_after>use crate::support::{database, project};

#[test]
fn migration_revert_runs_the_last_migration_down() {
    let p = project("migration_revert_runs_the_last_migration_down")
        .folder("migrations")
        .build();
    let db = database(&p.database_url());

    p.create_migration(
        "12345_create_users_table",
        "CREATE TABLE users ( id INTEGER )",
        "DROP TABLE users",
    );

    // Make sure the project is setup
    p.command("setup").run();

    assert!(db.table_exists("users"));

    let result = p.command("migration").arg("revert").run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains("Rolling back migration 12345"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(!db.table_exists("users"));
}

#[test]
fn migration_revert_respects_migration_dir_var() {
    let p = project("migration_revert_var").folder("foo").build();
    let db = database(&p.database_url());

    p.create_migration_in_directory(
        "foo",
        "12345_create_users_table",
        "CREATE TABLE users ( id INTEGER )",
        "DROP TABLE users",
    );

    // Make sure the project is setup.
    p.command("setup").arg("--migration-dir=foo").run();

    assert!(db.table_exists("users"));

    let result = p
        .command("migration")
        .arg("revert")
        .arg("--migration-dir=foo")
        .run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains("Rolling back migration 12345"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(!db.table_exists("users"));
}

#[test]
fn migration_revert_respects_migration_dir_env() {
    let p = project("migration_revert_env").folder("bar").build();
    let db = database(&p.database_url());

    p.create_migration_in_directory(
        "bar",
        "12345_create_users_table",
        "CREATE TABLE users ( id INTEGER )",
        "DROP TABLE users",
    );

    // Make sure the project is setup.
    p.command("setup").arg("--migration-dir=bar").run();

    assert!(db.table_exists("users"));

    let result = p
        .command("migration")
        .arg("revert")
        .env("MIGRATION_DIRECTORY", "bar")
        .run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains("Rolling back migration 12345"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(!db.table_exists("users"));
}

#[test]
fn migration_revert_respects_migration_dir_from_diesel_toml() {
    let p = project("migration_revert_respects_migration_dir_from_diesel_toml")
        .folder("custom_migrations")
        .file(
            "diesel.toml",
            r#"
            [migrations_directory]
            dir = "custom_migrations"
            "#,
        )
        .build();

    let db = database(&p.database_url());

    p.create_migration_in_directory(
        "custom_migrations",
        "12345_create_users_table",
        "CREATE TABLE users ( id INTEGER )",
        "DROP TABLE users",
    );

    // Make sure the project is setup.
    p.command("setup").run();

    assert!(db.table_exists("users"));

    let result = p.command("migration").arg("revert").run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains("Rolling back migration 12345"),
        "Unexpected stdout {}",
        result.stdout()
    );
    assert!(!db.table_exists("users"));
}

#[test]
fn migration_revert_runs_the_last_two_migration_down() {
    let p = project("migration_revert_runs_the_last_two_migration_down")
        .folder("migrations")
        .build();
    let db = database(&p.database_url());

    p.create_migration(
        "2017-08-31-210424_create_customers",
        "CREATE TABLE customers ( id INTEGER PRIMARY KEY )",
        "DROP TABLE customers",
    );

    p.create_migration(
        "2017-09-03-210424_create_contracts",
        "CREATE TABLE contracts ( id INTEGER PRIMARY KEY )",
        "DROP TABLE contracts",
    );

    p.create_migration(
        "2017-09-12-210424_create_bills",
        "CREATE TABLE bills ( id INTEGER PRIMARY KEY )",
        "DROP TABLE bills",
    );

    // Make sure the project is setup
    p.command("setup").run();

    assert!(db.table_exists("customers"));
    assert!(db.table_exists("contracts"));
    assert!(db.table_exists("bills"));

    // Reverts the last two migration files. The `contracts` and `bills` tables should be dropped.
    // The `customers` table shouldn't be reverted.
    let result = p
        .command("migration")
        .arg("revert")
        .arg("-n")
        .arg("2")
        .run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(
        result.stdout().contains("Rolling back migration 2017-09-12-210424_create_bills\nRolling back migration 2017-09-03-210424_create_contracts"),
        "Unexpected stdout {}",
        result.stdout()
    );

    assert!(db.table_exists("customers"));
    assert!(!db.table_exists("contracts"));
    assert!(!db.table_exists("bills"));
}

#[test]
fn migration_revert_all_runs_the_migrations_down() {
    let p = project("migration_revert_all_runs_the_migrations_down")
        .folder("migrations")
        .build();
    let db = database(&p.database_url());

    p.create_migration(
        "2017-08-31-210424_create_customers",
        "CREATE TABLE customers ( id INTEGER PRIMARY KEY )",
        "DROP TABLE customers",
    );

    p.create_migration(
        "2017-09-03-210424_create_contracts",
        "CREATE TABLE contracts ( id INTEGER PRIMARY KEY )",
        "DROP TABLE contracts",
    );

    p.create_migration(
        "2017-09-12-210424_create_bills",
        "CREATE TABLE bills ( id INTEGER PRIMARY KEY )",
        "DROP TABLE bills",
    );

    // Make sure the project is setup
    p.command("setup").run();

    assert!(db.table_exists("customers"));
    assert!(db.table_exists("contracts"));
    assert!(db.table_exists("bills"));

    let result = p.command("migration").arg("revert").arg("--all").run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);

    assert!(
        result.stdout()
            == "Rolling back migration 2017-09-12-210424_create_bills\n\
                Rolling back migration 2017-09-03-210424_create_contracts\n\
                Rolling back migration 2017-08-31-210424_create_customers\n",
        "Unexpected stdout : {}",
        result.stdout()
    );

    assert!(!db.table_exists("customers"));
    assert!(!db.table_exists("contracts"));
    assert!(!db.table_exists("bills"));
}

#[test]
fn migration_revert_with_zero_should_not_revert_any_migration() {
    let p = project("migration_revert_with_zero_should_not_revert")
        .folder("migrations")
        .build();
    let db = database(&p.database_url());

    p.create_migration(
        "2017-08-31-210424_create_customers",
        "CREATE TABLE customers ( id INTEGER PRIMARY KEY )",
        "DROP TABLE customers",
    );

    // Make sure the project is setup
    p.command("setup").run();

    assert!(db.table_exists("customers"));

    // Should not revert any migration.
    let result = p
        .command("migration")
        .arg("revert")
        .arg("-n")
        .arg("0")
        .run();

    assert!(!result.is_success(), "Result was unsuccessful {:?}", result);
    assert!(result.stdout() == "");
}

#[test]
fn migration_revert_n_with_a_string_should_throw_an_error() {
    let p = project("migration_revert_with_an_invalid_input_should_throw_an_error")
        .folder("migrations")
        .build();

    // Make sure the project is setup
    p.command("setup").run();

    // Should not revert any migration.
    let result = p
        .command("migration")
        .arg("revert")
        .arg("-n")
        .arg("infinite")
        .run();

    assert!(!result.is_success(), "Result was unsuccessful {:?}", result);

    assert!(
        result.stderr() == "error: Invalid value for '--number <REVERT_NUMBER>': infinite isn't a positive integer.\n",
        "Unexpected stderr : {}",
        result.stderr()
    );
}

#[test]
fn migration_revert_with_more_than_max_should_revert_all() {
    let p = project("migration_revert_with_more_than_max_should_revert_all")
        .folder("migrations")
        .build();
    let db = database(&p.database_url());

    p.create_migration(
        "2017-08-31-210424_create_customers",
        "CREATE TABLE customers ( id INTEGER PRIMARY KEY )",
        "DROP TABLE customers",
    );

    p.create_migration(
        "2017-09-03-210424_create_contracts",
        "CREATE TABLE contracts ( id INTEGER PRIMARY KEY )",
        "DROP TABLE contracts",
    );

    p.create_migration(
        "2017-09-12-210424_create_bills",
        "CREATE TABLE bills ( id INTEGER PRIMARY KEY )",
        "DROP TABLE bills",
    );

    // Make sure the project is setup
    p.command("setup").run();

    assert!(db.table_exists("customers"));
    assert!(db.table_exists("contracts"));
    assert!(db.table_exists("bills"));

    let result = p
        .command("migration")
        .arg("revert")
        .arg("-n")
        .arg("1000")
        .run();

    assert!(result.is_success(), "Result was unsuccessful {:?}", result);

    assert!(
        result.stdout()
            == "Rolling back migration 2017-09-12-210424_create_bills\n\
                Rolling back migration 2017-09-03-210424_create_contracts\n\
                Rolling back migration 2017-08-31-210424_create_customers\n",
        "Unexpected stdout : {}",
        result.stdout()
    );

    assert!(!db.table_exists("customers"));
    assert!(!db.table_exists("contracts"));
    assert!(!db.table_exists("bills"));
}
<|endoftext|>


<commit_before>use std::cell::{Cell, RefCell};

use cairo::{self, ImageSurface, MatrixTrait, PatternTrait};

use allowed_url::{Fragment, Href};
use aspect_ratio::AspectRatio;
use attributes::Attribute;
use drawing_ctx::DrawingCtx;
use error::{NodeError, RenderingError};
use node::{CascadedValues, NodeResult, NodeTrait, RsvgNode};
use parsers::{ParseError, ParseValue};
use property_bag::PropertyBag;
use rect::IRect;
use surface_utils::shared_surface::{SharedImageSurface, SurfaceType};

use super::bounds::BoundsBuilder;
use super::context::{FilterContext, FilterOutput, FilterResult};
use super::{Filter, FilterError, Primitive};

/// The `feImage` filter primitive.
pub struct Image {
    base: Primitive,
    aspect: Cell<AspectRatio>,
    href: RefCell<Option<Href>>,
}

impl Image {
    /// Constructs a new `Image` with empty properties.
    #[inline]
    pub fn new() -> Image {
        Image {
            base: Primitive::new::<Self>(),
            aspect: Cell::new(AspectRatio::default()),
            href: RefCell::new(None),
        }
    }

    fn set_href(&self, attr: Attribute, href_str: &str) -> NodeResult {
        let href = Href::parse(href_str)
            .map_err(|_| NodeError::parse_error(attr, ParseError::new("could not parse href")))?;

        *self.href.borrow_mut() = Some(href);

        Ok(())
    }

    /// Renders the filter if the source is an existing node.
    fn render_node(
        &self,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
        bounds: IRect,
        fragment: &Fragment,
    ) -> Result<ImageSurface, FilterError> {
        let acquired_drawable = draw_ctx
            .get_acquired_node(fragment)
            .ok_or(FilterError::InvalidInput)?;
        let drawable = acquired_drawable.get();

        let surface = ImageSurface::create(
            cairo::Format::ARgb32,
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
        )?;

        draw_ctx.get_cairo_context().set_matrix(ctx.paffine());

        let node_being_filtered_values = ctx.get_computed_values_from_node_being_filtered();

        let cascaded = CascadedValues::new_from_values(&drawable, node_being_filtered_values);

        draw_ctx
            .draw_node_on_surface(
                &drawable,
                &cascaded,
                &surface,
                f64::from(ctx.source_graphic().width()),
                f64::from(ctx.source_graphic().height()),
            )
            .map_err(|e| {
                if let RenderingError::Cairo(status) = e {
                    FilterError::CairoError(status)
                } else {
                    // FIXME: this is just a dummy value; we should probably have a way to indicate
                    // an error in the underlying drawing process.
                    FilterError::CairoError(cairo::Status::InvalidStatus)
                }
            })?;

        // Clip the output to bounds.
        let output_surface = ImageSurface::create(
            cairo::Format::ARgb32,
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
        )?;

        let cr = cairo::Context::new(&output_surface);
        cr.rectangle(
            f64::from(bounds.x0),
            f64::from(bounds.y0),
            f64::from(bounds.x1 - bounds.x0),
            f64::from(bounds.y1 - bounds.y0),
        );
        cr.clip();
        cr.set_source_surface(&surface, 0f64, 0f64);
        cr.paint();

        Ok(output_surface)
    }

    /// Renders the filter if the source is an external image.
    fn render_external_image(
        &self,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
        bounds_builder: BoundsBuilder<'_>,
        href: &Href,
    ) -> Result<ImageSurface, FilterError> {
        let surface = if let Href::PlainUrl(ref url) = *href {
            // FIXME: translate the error better here
            draw_ctx
                .lookup_image(&url)
                .map_err(|_| FilterError::InvalidInput)?
        } else {
            unreachable!();
        };

        let output_surface = ImageSurface::create(
            cairo::Format::ARgb32,
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
        )?;

        // TODO: this goes through a f64->i32->f64 conversion.
        let render_bounds = bounds_builder.into_irect_without_clipping(draw_ctx);
        let aspect = self.aspect.get();
        let (x, y, w, h) = aspect.compute(
            f64::from(surface.width()),
            f64::from(surface.height()),
            f64::from(render_bounds.x0),
            f64::from(render_bounds.y0),
            f64::from(render_bounds.x1 - render_bounds.x0),
            f64::from(render_bounds.y1 - render_bounds.y0),
        );

        if w != 0f64 && h != 0f64 {
            let ptn = surface.to_cairo_pattern();
            let mut matrix = cairo::Matrix::new(
                w / f64::from(surface.width()),
                0f64,
                0f64,
                h / f64::from(surface.height()),
                x,
                y,
            );
            matrix.invert();
            ptn.set_matrix(matrix);

            let bounds = bounds_builder.into_irect(draw_ctx);
            let cr = cairo::Context::new(&output_surface);
            cr.rectangle(
                f64::from(bounds.x0),
                f64::from(bounds.y0),
                f64::from(bounds.x1 - bounds.x0),
                f64::from(bounds.y1 - bounds.y0),
            );
            cr.clip();
            cr.set_source(&ptn);
            cr.paint();
        }

        Ok(output_surface)
    }
}

impl NodeTrait for Image {
    fn set_atts(&self, node: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
        self.base.set_atts(node, pbag)?;

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

                // "path" is used by some older Adobe Illustrator versions
                Attribute::XlinkHref | Attribute::Path => self.set_href(attr, value)?,

                _ => (),
            }
        }

        Ok(())
    }
}

impl Filter for Image {
    fn render(
        &self,
        _node: &RsvgNode,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterResult, FilterError> {
        let bounds_builder = self.base.get_bounds(ctx);
        let bounds = bounds_builder.into_irect(draw_ctx);

        let href_borrow = self.href.borrow();
        let href_opt = href_borrow.as_ref();

        if let Some(href) = href_opt {
            let output_surface = match *href {
                Href::PlainUrl(_) => {
                    self.render_external_image(ctx, draw_ctx, bounds_builder, href)?
                }
                Href::WithFragment(ref frag) => self.render_node(ctx, draw_ctx, bounds, frag)?,
            };

            Ok(FilterResult {
                name: self.base.result.borrow().clone(),
                output: FilterOutput {
                    surface: SharedImageSurface::new(output_surface, SurfaceType::SRgb)?,
                    bounds,
                },
            })
        } else {
            Err(FilterError::InvalidInput)
        }
    }

    #[inline]
    fn is_affected_by_color_interpolation_filters(&self) -> bool {
        false
    }
}
<commit_msg>image filter: use approx_eq_cairo to check w and h<commit_after>use std::cell::{Cell, RefCell};

use cairo::{self, ImageSurface, MatrixTrait, PatternTrait};

use allowed_url::{Fragment, Href};
use aspect_ratio::AspectRatio;
use attributes::Attribute;
use drawing_ctx::DrawingCtx;
use error::{NodeError, RenderingError};
use float_eq_cairo::ApproxEqCairo;
use node::{CascadedValues, NodeResult, NodeTrait, RsvgNode};
use parsers::{ParseError, ParseValue};
use property_bag::PropertyBag;
use rect::IRect;
use surface_utils::shared_surface::{SharedImageSurface, SurfaceType};

use super::bounds::BoundsBuilder;
use super::context::{FilterContext, FilterOutput, FilterResult};
use super::{Filter, FilterError, Primitive};

/// The `feImage` filter primitive.
pub struct Image {
    base: Primitive,
    aspect: Cell<AspectRatio>,
    href: RefCell<Option<Href>>,
}

impl Image {
    /// Constructs a new `Image` with empty properties.
    #[inline]
    pub fn new() -> Image {
        Image {
            base: Primitive::new::<Self>(),
            aspect: Cell::new(AspectRatio::default()),
            href: RefCell::new(None),
        }
    }

    fn set_href(&self, attr: Attribute, href_str: &str) -> NodeResult {
        let href = Href::parse(href_str)
            .map_err(|_| NodeError::parse_error(attr, ParseError::new("could not parse href")))?;

        *self.href.borrow_mut() = Some(href);

        Ok(())
    }

    /// Renders the filter if the source is an existing node.
    fn render_node(
        &self,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
        bounds: IRect,
        fragment: &Fragment,
    ) -> Result<ImageSurface, FilterError> {
        let acquired_drawable = draw_ctx
            .get_acquired_node(fragment)
            .ok_or(FilterError::InvalidInput)?;
        let drawable = acquired_drawable.get();

        let surface = ImageSurface::create(
            cairo::Format::ARgb32,
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
        )?;

        draw_ctx.get_cairo_context().set_matrix(ctx.paffine());

        let node_being_filtered_values = ctx.get_computed_values_from_node_being_filtered();

        let cascaded = CascadedValues::new_from_values(&drawable, node_being_filtered_values);

        draw_ctx
            .draw_node_on_surface(
                &drawable,
                &cascaded,
                &surface,
                f64::from(ctx.source_graphic().width()),
                f64::from(ctx.source_graphic().height()),
            )
            .map_err(|e| {
                if let RenderingError::Cairo(status) = e {
                    FilterError::CairoError(status)
                } else {
                    // FIXME: this is just a dummy value; we should probably have a way to indicate
                    // an error in the underlying drawing process.
                    FilterError::CairoError(cairo::Status::InvalidStatus)
                }
            })?;

        // Clip the output to bounds.
        let output_surface = ImageSurface::create(
            cairo::Format::ARgb32,
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
        )?;

        let cr = cairo::Context::new(&output_surface);
        cr.rectangle(
            f64::from(bounds.x0),
            f64::from(bounds.y0),
            f64::from(bounds.x1 - bounds.x0),
            f64::from(bounds.y1 - bounds.y0),
        );
        cr.clip();
        cr.set_source_surface(&surface, 0f64, 0f64);
        cr.paint();

        Ok(output_surface)
    }

    /// Renders the filter if the source is an external image.
    fn render_external_image(
        &self,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
        bounds_builder: BoundsBuilder<'_>,
        href: &Href,
    ) -> Result<ImageSurface, FilterError> {
        let surface = if let Href::PlainUrl(ref url) = *href {
            // FIXME: translate the error better here
            draw_ctx
                .lookup_image(&url)
                .map_err(|_| FilterError::InvalidInput)?
        } else {
            unreachable!();
        };

        let output_surface = ImageSurface::create(
            cairo::Format::ARgb32,
            ctx.source_graphic().width(),
            ctx.source_graphic().height(),
        )?;

        // TODO: this goes through a f64->i32->f64 conversion.
        let render_bounds = bounds_builder.into_irect_without_clipping(draw_ctx);
        let aspect = self.aspect.get();
        let (x, y, w, h) = aspect.compute(
            f64::from(surface.width()),
            f64::from(surface.height()),
            f64::from(render_bounds.x0),
            f64::from(render_bounds.y0),
            f64::from(render_bounds.x1 - render_bounds.x0),
            f64::from(render_bounds.y1 - render_bounds.y0),
        );

        if w.approx_eq_cairo(&0.0) || h.approx_eq_cairo(&0.0) {
            return Ok(output_surface);
        }

        let ptn = surface.to_cairo_pattern();
        let mut matrix = cairo::Matrix::new(
            w / f64::from(surface.width()),
            0f64,
            0f64,
            h / f64::from(surface.height()),
            x,
            y,
        );
        matrix.invert();
        ptn.set_matrix(matrix);

        let bounds = bounds_builder.into_irect(draw_ctx);
        let cr = cairo::Context::new(&output_surface);
        cr.rectangle(
            f64::from(bounds.x0),
            f64::from(bounds.y0),
            f64::from(bounds.x1 - bounds.x0),
            f64::from(bounds.y1 - bounds.y0),
        );
        cr.clip();
        cr.set_source(&ptn);
        cr.paint();

        Ok(output_surface)
    }
}

impl NodeTrait for Image {
    fn set_atts(&self, node: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
        self.base.set_atts(node, pbag)?;

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

                // "path" is used by some older Adobe Illustrator versions
                Attribute::XlinkHref | Attribute::Path => self.set_href(attr, value)?,

                _ => (),
            }
        }

        Ok(())
    }
}

impl Filter for Image {
    fn render(
        &self,
        _node: &RsvgNode,
        ctx: &FilterContext,
        draw_ctx: &mut DrawingCtx,
    ) -> Result<FilterResult, FilterError> {
        let bounds_builder = self.base.get_bounds(ctx);
        let bounds = bounds_builder.into_irect(draw_ctx);

        let href_borrow = self.href.borrow();
        let href_opt = href_borrow.as_ref();

        if let Some(href) = href_opt {
            let output_surface = match *href {
                Href::PlainUrl(_) => {
                    self.render_external_image(ctx, draw_ctx, bounds_builder, href)?
                }
                Href::WithFragment(ref frag) => self.render_node(ctx, draw_ctx, bounds, frag)?,
            };

            Ok(FilterResult {
                name: self.base.result.borrow().clone(),
                output: FilterOutput {
                    surface: SharedImageSurface::new(output_surface, SurfaceType::SRgb)?,
                    bounds,
                },
            })
        } else {
            Err(FilterError::InvalidInput)
        }
    }

    #[inline]
    fn is_affected_by_color_interpolation_filters(&self) -> bool {
        false
    }
}
<|endoftext|>