<commit_before>use crate::App;
use anyhow::Error;
use dashmap::{DashMap, SharedValue};
use diesel::{pg::upsert::excluded, prelude::*};
use std::collections::HashMap;
use std::sync::atomic::{AtomicI64, AtomicUsize, Ordering};

/// crates.io receives a lot of download requests, and we can't execute a write query to the
/// database during each connection for performance reasons. To reduce the write load, this struct
/// collects the pending updates from the current process and writes in batch.
///
/// To avoid locking the whole data structure behind a RwLock, which could potentially delay
/// requests, this uses the dashmap crate. A DashMap has the same public API as an HashMap, but
/// stores the items into `num_cpus()*4` individually locked shards. This approach reduces the
/// likelyhood of a request encountering a locked shard.
///
/// Persisting the download counts in the database also takes advantage of the inner sharding of
/// DashMaps: to avoid locking all the download requests at the same time each iteration only
/// persists a single shard at the time.
///
/// The disadvantage of this approach is that download counts are stored in memory until they're
/// persisted, so it's possible to lose some of them if the process exits ungracefully. While
/// that's far from ideal, the advantage of batching database updates far outweights potentially
/// losing some download counts.
#[derive(Debug)]
pub struct DownloadsCounter {
    /// Inner storage for the download counts.
    inner: DashMap<i32, AtomicUsize>,
    /// Index of the next shard that should be persisted by `persist_next_shard`.
    shard_idx: AtomicUsize,
    /// Number of downloads that are not yet persisted on the database. This is just used as a
    /// metric included in log lines, and it's not guaranteed to be accurate.
    pending_count: AtomicI64,
}

impl DownloadsCounter {
    pub(crate) fn new() -> Self {
        Self {
            inner: DashMap::new(),
            shard_idx: AtomicUsize::new(0),
            pending_count: AtomicI64::new(0),
        }
    }

    pub(crate) fn increment(&self, version_id: i32) {
        self.pending_count.fetch_add(1, Ordering::SeqCst);

        if let Some(counter) = self.inner.get(&version_id) {
            // The version is already recorded in the DashMap, so we don't need to lock the whole
            // shard in write mode. The shard is instead locked in read mode, which allows an
            // unbounded number of readers as long as there are no write locks.
            counter.value().fetch_add(1, Ordering::SeqCst);
        } else {
            // The version is not in the DashMap, so we need to lock the whole shard in write mode
            // and insert the version into it. This has worse performance than the above case.
            self.inner
                .entry(version_id)
                .and_modify(|counter| {
                    // Handle the version being inserted by another thread while we were waiting
                    // for the write lock on the shard.
                    counter.fetch_add(1, Ordering::SeqCst);
                })
                .or_insert_with(|| AtomicUsize::new(1));
        }
    }

    pub fn persist_all_shards(&self, app: &App) -> Result<PersistStats, Error> {
        let conn = app.primary_database.get()?;
        self.persist_all_shards_with_conn(&conn)
    }

    pub fn persist_next_shard(&self, app: &App) -> Result<PersistStats, Error> {
        let conn = app.primary_database.get()?;
        self.persist_next_shard_with_conn(&conn)
    }

    fn persist_all_shards_with_conn(&self, conn: &PgConnection) -> Result<PersistStats, Error> {
        let mut stats = PersistStats::default();
        for shard in self.inner.shards() {
            let shard = std::mem::take(&mut *shard.write());
            stats = stats.merge(self.persist_shard(&conn, shard)?);
        }

        Ok(stats)
    }

    fn persist_next_shard_with_conn(&self, conn: &PgConnection) -> Result<PersistStats, Error> {
        // Replace the next shard in the ring with an empty HashMap (clearing it), and return the
        // previous contents for processing. The fetch_add method wraps around on overflow, so it's
        // fine to keep incrementing it without resetting.
        let shards = self.inner.shards();
        let idx = self.shard_idx.fetch_add(1, Ordering::SeqCst) % shards.len();
        let shard = std::mem::take(&mut *shards[idx].write());

        let mut stats = self.persist_shard(&conn, shard)?;
        stats.shard = Some(idx);
        Ok(stats)
    }

    fn persist_shard(
        &self,
        conn: &PgConnection,
        shard: HashMap<i32, SharedValue<AtomicUsize>>,
    ) -> Result<PersistStats, Error> {
        use crate::schema::version_downloads::dsl::*;

        let mut counted_downloads = 0;
        let mut counted_versions = 0;
        let mut to_insert = Vec::new();
        for (key, atomic) in shard.iter() {
            let count = atomic.get().load(Ordering::SeqCst);
            counted_downloads += count;
            counted_versions += 1;

            to_insert.push((*key, count));
        }

        if !to_insert.is_empty() {
            // The rows we're about to insert need to be sorted to avoid deadlocks when multiple
            // instances of crates.io are running at the same time.
            //
            // In PostgreSQL a transaction modifying a row locks that row until the transaction is
            // committed. Multiple transactions inserting rows into a table could end up
            // deadlocking each other though: PostgreSQL will detect that deadlock, abort one of
            // the transactions and allow the other one to continue. We don't want that to happen,
            // as we'd lose the downloads from the aborted transaction.
            //
            // Ensuring the rows are inserted in a consistent order (in our case by sorting them by
            // the version ID) will prevent deadlocks from occuring. For more information:
            //
            //     https://www.postgresql.org/docs/11/explicit-locking.html#LOCKING-DEADLOCKS
            //
            to_insert.sort_by_key(|(key, _)| *key);

            let to_insert = to_insert
                .into_iter()
                .map(|(key, count)| (version_id.eq(key), downloads.eq(count as i32)))
                .collect::<Vec<_>>();

            diesel::insert_into(version_downloads)
                .values(&to_insert)
                .on_conflict((version_id, date))
                .do_update()
                .set(downloads.eq(downloads + excluded(downloads)))
                .execute(conn)?;
        }

        let old_pending = self
            .pending_count
            .fetch_sub(counted_downloads as i64, Ordering::SeqCst);

        Ok(PersistStats {
            shard: None,
            counted_downloads,
            counted_versions,
            pending_downloads: old_pending - counted_downloads as i64,
        })
    }

    pub fn shards_count(&self) -> usize {
        self.inner.shards().len()
    }
}

#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
pub struct PersistStats {
    shard: Option<usize>,
    counted_downloads: usize,
    counted_versions: usize,
    pending_downloads: i64,
}

impl PersistStats {
    fn merge(self, other: PersistStats) -> Self {
        Self {
            shard: if self.shard == other.shard {
                other.shard
            } else {
                None
            },
            counted_downloads: self.counted_downloads + other.counted_downloads,
            counted_versions: self.counted_versions + other.counted_versions,
            pending_downloads: other.pending_downloads,
        }
    }

    pub fn log(&self) {
        println!(
            "downloads_counter shard={} counted_versions={} counted_downloads={} pending_downloads={}",
            self.shard.map(|s| s.to_string()).unwrap_or_else(|| "all".into()),
            self.counted_versions,
            self.counted_downloads,
            self.pending_downloads,
        );
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::models::{Crate, NewCrate, NewUser, NewVersion, User};
    use diesel::PgConnection;
    use semver::Version;

    #[test]
    fn test_increment_and_persist_all() {
        let counter = DownloadsCounter::new();
        let conn = crate::db::test_conn();
        let mut state = State::new(&conn);

        let v1 = state.new_version(&conn);
        let v2 = state.new_version(&conn);
        let v3 = state.new_version(&conn);

        // Add 15 downloads between v1 and v2, and no downloads for v3.
        for _ in 0..10 {
            counter.increment(v1);
        }
        for _ in 0..5 {
            counter.increment(v2);
        }
        assert_eq!(15, counter.pending_count.load(Ordering::SeqCst));

        // Persist everything to the database
        let stats = counter
            .persist_all_shards_with_conn(&conn)
            .expect("failed to persist all shards");

        // Ensure the stats are accurate
        assert_eq!(
            stats,
            PersistStats {
                shard: None,
                counted_downloads: 15,
                counted_versions: 2,
                pending_downloads: 0,
            }
        );

        // Ensure the download counts in the database are what we expect.
        state.assert_downloads_count(&conn, v1, 10);
        state.assert_downloads_count(&conn, v2, 5);
        state.assert_downloads_count(&conn, v3, 0);
    }

    #[test]
    fn test_increment_and_persist_shard() {
        let counter = DownloadsCounter::new();
        let conn = crate::db::test_conn();
        let mut state = State::new(&conn);

        let v1 = state.new_version(&conn);
        let v1_shard = counter.inner.determine_map(&v1);

        // For this test to work we need the two versions to be stored in different shards.
        let mut v2 = state.new_version(&conn);
        while counter.inner.determine_map(&v2) == v1_shard {
            v2 = state.new_version(&conn);
        }
        let v2_shard = counter.inner.determine_map(&v2);

        // Add 15 downloads between v1 and v2.
        for _ in 0..10 {
            counter.increment(v1);
        }
        for _ in 0..5 {
            counter.increment(v2);
        }
        assert_eq!(15, counter.pending_count.load(Ordering::SeqCst));

        // Persist one shard at the time and ensure the stats returned for each shard are expected.
        let mut pending = 15;
        for shard in 0..counter.shards_count() {
            let stats = counter
                .persist_next_shard_with_conn(&conn)
                .expect("failed to persist shard");

            if shard == v1_shard {
                pending -= 10;
                assert_eq!(
                    stats,
                    PersistStats {
                        shard: Some(shard),
                        counted_downloads: 10,
                        counted_versions: 1,
                        pending_downloads: pending,
                    }
                );
                state.assert_downloads_count(&conn, v1, 10);
            } else if shard == v2_shard {
                pending -= 5;
                assert_eq!(
                    stats,
                    PersistStats {
                        shard: Some(shard),
                        counted_downloads: 5,
                        counted_versions: 1,
                        pending_downloads: pending,
                    }
                );
                state.assert_downloads_count(&conn, v2, 5);
            } else {
                assert_eq!(
                    stats,
                    PersistStats {
                        shard: Some(shard),
                        counted_downloads: 0,
                        counted_versions: 0,
                        pending_downloads: pending,
                    }
                );
            };
        }
        assert_eq!(pending, 0);

        // Finally ensure that the download counts in the database are what we expect.
        state.assert_downloads_count(&conn, v1, 10);
        state.assert_downloads_count(&conn, v2, 5);
    }

    struct State {
        user: User,
        krate: Crate,
        next_version: u32,
    }

    impl State {
        fn new(conn: &PgConnection) -> Self {
            let user = NewUser {
                gh_id: 0,
                gh_login: "ghost",
                ..NewUser::default()
            }
            .create_or_update(None, conn)
            .expect("failed to create user");

            let krate = NewCrate {
                name: "foo",
                ..NewCrate::default()
            }
            .create_or_update(conn, user.id, None)
            .expect("failed to create crate");

            Self {
                user,
                krate,
                next_version: 1,
            }
        }

        fn new_version(&mut self, conn: &PgConnection) -> i32 {
            let version = NewVersion::new(
                self.krate.id,
                &Version::parse(&format!("{}.0.0", self.next_version)).unwrap(),
                &HashMap::new(),
                None,
                None,
                0,
                self.user.id,
            )
            .expect("failed to create version")
            .save(conn, &[], "ghost@example.com")
            .expect("failed to save version");

            self.next_version += 1;
            version.id
        }

        fn assert_downloads_count(&self, conn: &PgConnection, version: i32, expected: i64) {
            use crate::schema::version_downloads::dsl::*;
            use diesel::dsl::*;

            let actual: Option<i64> = version_downloads
                .select(sum(downloads))
                .filter(version_id.eq(version))
                .first(conn)
                .unwrap();
            assert_eq!(actual.unwrap_or(0), expected);
        }
    }
}
<commit_msg>downloads_counter: handle versions being deleted before persisting<commit_after>use crate::App;
use anyhow::Error;
use dashmap::{DashMap, SharedValue};
use diesel::{pg::upsert::excluded, prelude::*};
use std::collections::{HashMap, HashSet};
use std::sync::atomic::{AtomicI64, AtomicUsize, Ordering};

/// crates.io receives a lot of download requests, and we can't execute a write query to the
/// database during each connection for performance reasons. To reduce the write load, this struct
/// collects the pending updates from the current process and writes in batch.
///
/// To avoid locking the whole data structure behind a RwLock, which could potentially delay
/// requests, this uses the dashmap crate. A DashMap has the same public API as an HashMap, but
/// stores the items into `num_cpus()*4` individually locked shards. This approach reduces the
/// likelyhood of a request encountering a locked shard.
///
/// Persisting the download counts in the database also takes advantage of the inner sharding of
/// DashMaps: to avoid locking all the download requests at the same time each iteration only
/// persists a single shard at the time.
///
/// The disadvantage of this approach is that download counts are stored in memory until they're
/// persisted, so it's possible to lose some of them if the process exits ungracefully. While
/// that's far from ideal, the advantage of batching database updates far outweights potentially
/// losing some download counts.
#[derive(Debug)]
pub struct DownloadsCounter {
    /// Inner storage for the download counts.
    inner: DashMap<i32, AtomicUsize>,
    /// Index of the next shard that should be persisted by `persist_next_shard`.
    shard_idx: AtomicUsize,
    /// Number of downloads that are not yet persisted on the database. This is just used as a
    /// metric included in log lines, and it's not guaranteed to be accurate.
    pending_count: AtomicI64,
}

impl DownloadsCounter {
    pub(crate) fn new() -> Self {
        Self {
            inner: DashMap::new(),
            shard_idx: AtomicUsize::new(0),
            pending_count: AtomicI64::new(0),
        }
    }

    pub(crate) fn increment(&self, version_id: i32) {
        self.pending_count.fetch_add(1, Ordering::SeqCst);

        if let Some(counter) = self.inner.get(&version_id) {
            // The version is already recorded in the DashMap, so we don't need to lock the whole
            // shard in write mode. The shard is instead locked in read mode, which allows an
            // unbounded number of readers as long as there are no write locks.
            counter.value().fetch_add(1, Ordering::SeqCst);
        } else {
            // The version is not in the DashMap, so we need to lock the whole shard in write mode
            // and insert the version into it. This has worse performance than the above case.
            self.inner
                .entry(version_id)
                .and_modify(|counter| {
                    // Handle the version being inserted by another thread while we were waiting
                    // for the write lock on the shard.
                    counter.fetch_add(1, Ordering::SeqCst);
                })
                .or_insert_with(|| AtomicUsize::new(1));
        }
    }

    pub fn persist_all_shards(&self, app: &App) -> Result<PersistStats, Error> {
        let conn = app.primary_database.get()?;
        self.persist_all_shards_with_conn(&conn)
    }

    pub fn persist_next_shard(&self, app: &App) -> Result<PersistStats, Error> {
        let conn = app.primary_database.get()?;
        self.persist_next_shard_with_conn(&conn)
    }

    fn persist_all_shards_with_conn(&self, conn: &PgConnection) -> Result<PersistStats, Error> {
        let mut stats = PersistStats::default();
        for shard in self.inner.shards() {
            let shard = std::mem::take(&mut *shard.write());
            stats = stats.merge(self.persist_shard(&conn, shard)?);
        }

        Ok(stats)
    }

    fn persist_next_shard_with_conn(&self, conn: &PgConnection) -> Result<PersistStats, Error> {
        // Replace the next shard in the ring with an empty HashMap (clearing it), and return the
        // previous contents for processing. The fetch_add method wraps around on overflow, so it's
        // fine to keep incrementing it without resetting.
        let shards = self.inner.shards();
        let idx = self.shard_idx.fetch_add(1, Ordering::SeqCst) % shards.len();
        let shard = std::mem::take(&mut *shards[idx].write());

        let mut stats = self.persist_shard(&conn, shard)?;
        stats.shard = Some(idx);
        Ok(stats)
    }

    fn persist_shard(
        &self,
        conn: &PgConnection,
        shard: HashMap<i32, SharedValue<AtomicUsize>>,
    ) -> Result<PersistStats, Error> {
        use crate::schema::{version_downloads, versions};

        let mut discarded_downloads = 0;
        let mut counted_downloads = 0;
        let mut counted_versions = 0;

        let mut to_insert = shard
            .iter()
            .map(|(id, atomic)| (*id, atomic.get().load(Ordering::SeqCst)))
            .collect::<Vec<_>>();

        if !to_insert.is_empty() {
            // The rows we're about to insert need to be sorted to avoid deadlocks when multiple
            // instances of crates.io are running at the same time.
            //
            // In PostgreSQL a transaction modifying a row locks that row until the transaction is
            // committed. Multiple transactions inserting rows into a table could end up
            // deadlocking each other though: PostgreSQL will detect that deadlock, abort one of
            // the transactions and allow the other one to continue. We don't want that to happen,
            // as we'd lose the downloads from the aborted transaction.
            //
            // Ensuring the rows are inserted in a consistent order (in our case by sorting them by
            // the version ID) will prevent deadlocks from occuring. For more information:
            //
            //     https://www.postgresql.org/docs/11/explicit-locking.html#LOCKING-DEADLOCKS
            //
            to_insert.sort_by_key(|(key, _)| *key);

            // Our database schema enforces that every row in the `version_downloads` table points
            // to a valid version in the `versions` table with a foreign key. This doesn't cause
            // problems most of the times, as the rest of the application checks whether the
            // version exists before calling the `increment` method.
            //
            // On rare occasions crates are deleted from crates.io though, and that would break the
            // invariant if the crate is deleted after the `increment` method is called but before
            // the downloads are persisted in the database.
            //
            // That happening would cause the whole `INSERT` to fail, also losing the downloads in
            // the shard we were about to persist. To avoid that from happening this snippet does a
            // `SELECT` query on the version table before persisting to check whether every version
            // still exists in the database. Missing versions are removed from the following query.
            let version_ids = to_insert.iter().map(|(id, _)| *id).collect::<Vec<_>>();
            let existing_version_ids: HashSet<i32> = versions::table
                .select(versions::id)
                // `FOR SHARE` prevents updates or deletions on the selected rows in the `versions`
                // table until this transaction commits. That prevents a version from being deleted
                // between this query and the next one.
                //
                // `FOR SHARE` is used instead of `FOR UPDATE` to allow rows to be locked by
                // multiple `SELECT` transactions, to allow for concurrent downloads persisting.
                .for_share()
                .filter(versions::id.eq_any(version_ids))
                .load(conn)?
                .into_iter()
                .collect();

            let mut values = Vec::new();
            for (id, count) in &to_insert {
                if !existing_version_ids.contains(id) {
                    discarded_downloads += *count;
                    continue;
                }
                counted_versions += 1;
                counted_downloads += *count;
                values.push((
                    version_downloads::version_id.eq(*id),
                    version_downloads::downloads.eq(*count as i32),
                ));
            }

            diesel::insert_into(version_downloads::table)
                .values(&values)
                .on_conflict((version_downloads::version_id, version_downloads::date))
                .do_update()
                .set(
                    version_downloads::downloads
                        .eq(version_downloads::downloads + excluded(version_downloads::downloads)),
                )
                .execute(conn)?;
        }

        let old_pending = self.pending_count.fetch_sub(
            (counted_downloads + discarded_downloads) as i64,
            Ordering::SeqCst,
        );

        Ok(PersistStats {
            shard: None,
            counted_downloads,
            counted_versions,
            pending_downloads: old_pending - counted_downloads as i64,
        })
    }

    pub fn shards_count(&self) -> usize {
        self.inner.shards().len()
    }
}

#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
pub struct PersistStats {
    shard: Option<usize>,
    counted_downloads: usize,
    counted_versions: usize,
    pending_downloads: i64,
}

impl PersistStats {
    fn merge(self, other: PersistStats) -> Self {
        Self {
            shard: if self.shard == other.shard {
                other.shard
            } else {
                None
            },
            counted_downloads: self.counted_downloads + other.counted_downloads,
            counted_versions: self.counted_versions + other.counted_versions,
            pending_downloads: other.pending_downloads,
        }
    }

    pub fn log(&self) {
        println!(
            "downloads_counter shard={} counted_versions={} counted_downloads={} pending_downloads={}",
            self.shard.map(|s| s.to_string()).unwrap_or_else(|| "all".into()),
            self.counted_versions,
            self.counted_downloads,
            self.pending_downloads,
        );
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::models::{Crate, NewCrate, NewUser, NewVersion, User};
    use diesel::PgConnection;
    use semver::Version;

    #[test]
    fn test_increment_and_persist_all() {
        let counter = DownloadsCounter::new();
        let conn = crate::db::test_conn();
        let mut state = State::new(&conn);

        let v1 = state.new_version(&conn);
        let v2 = state.new_version(&conn);
        let v3 = state.new_version(&conn);

        // Add 15 downloads between v1 and v2, and no downloads for v3.
        for _ in 0..10 {
            counter.increment(v1);
        }
        for _ in 0..5 {
            counter.increment(v2);
        }
        assert_eq!(15, counter.pending_count.load(Ordering::SeqCst));

        // Persist everything to the database
        let stats = counter
            .persist_all_shards_with_conn(&conn)
            .expect("failed to persist all shards");

        // Ensure the stats are accurate
        assert_eq!(
            stats,
            PersistStats {
                shard: None,
                counted_downloads: 15,
                counted_versions: 2,
                pending_downloads: 0,
            }
        );

        // Ensure the download counts in the database are what we expect.
        state.assert_downloads_count(&conn, v1, 10);
        state.assert_downloads_count(&conn, v2, 5);
        state.assert_downloads_count(&conn, v3, 0);
    }

    #[test]
    fn test_increment_and_persist_shard() {
        let counter = DownloadsCounter::new();
        let conn = crate::db::test_conn();
        let mut state = State::new(&conn);

        let v1 = state.new_version(&conn);
        let v1_shard = counter.inner.determine_map(&v1);

        // For this test to work we need the two versions to be stored in different shards.
        let mut v2 = state.new_version(&conn);
        while counter.inner.determine_map(&v2) == v1_shard {
            v2 = state.new_version(&conn);
        }
        let v2_shard = counter.inner.determine_map(&v2);

        // Add 15 downloads between v1 and v2.
        for _ in 0..10 {
            counter.increment(v1);
        }
        for _ in 0..5 {
            counter.increment(v2);
        }
        assert_eq!(15, counter.pending_count.load(Ordering::SeqCst));

        // Persist one shard at the time and ensure the stats returned for each shard are expected.
        let mut pending = 15;
        for shard in 0..counter.shards_count() {
            let stats = counter
                .persist_next_shard_with_conn(&conn)
                .expect("failed to persist shard");

            if shard == v1_shard {
                pending -= 10;
                assert_eq!(
                    stats,
                    PersistStats {
                        shard: Some(shard),
                        counted_downloads: 10,
                        counted_versions: 1,
                        pending_downloads: pending,
                    }
                );
                state.assert_downloads_count(&conn, v1, 10);
            } else if shard == v2_shard {
                pending -= 5;
                assert_eq!(
                    stats,
                    PersistStats {
                        shard: Some(shard),
                        counted_downloads: 5,
                        counted_versions: 1,
                        pending_downloads: pending,
                    }
                );
                state.assert_downloads_count(&conn, v2, 5);
            } else {
                assert_eq!(
                    stats,
                    PersistStats {
                        shard: Some(shard),
                        counted_downloads: 0,
                        counted_versions: 0,
                        pending_downloads: pending,
                    }
                );
            };
        }
        assert_eq!(pending, 0);

        // Finally ensure that the download counts in the database are what we expect.
        state.assert_downloads_count(&conn, v1, 10);
        state.assert_downloads_count(&conn, v2, 5);
    }

    #[test]
    fn test_increment_missing_version() {
        let counter = DownloadsCounter::new();
        let conn = crate::db::test_conn();
        let mut state = State::new(&conn);

        let v1 = state.new_version(&conn);
        let v2 = v1 + 1; // Should not exist in the database!

        // No error should happen when calling the increment method on a missing version.
        counter.increment(v1);
        counter.increment(v2);

        // No error should happen when persisting. The missing versions should be ignored.
        let stats = counter
            .persist_all_shards_with_conn(&conn)
            .expect("failed to persist download counts");

        // The download should not be counted for version 2.
        assert_eq!(
            stats,
            PersistStats {
                shard: None,
                counted_downloads: 1,
                counted_versions: 1,
                pending_downloads: 0,
            }
        );
        state.assert_downloads_count(&conn, v1, 1);
        state.assert_downloads_count(&conn, v2, 0);
    }

    struct State {
        user: User,
        krate: Crate,
        next_version: u32,
    }

    impl State {
        fn new(conn: &PgConnection) -> Self {
            let user = NewUser {
                gh_id: 0,
                gh_login: "ghost",
                ..NewUser::default()
            }
            .create_or_update(None, conn)
            .expect("failed to create user");

            let krate = NewCrate {
                name: "foo",
                ..NewCrate::default()
            }
            .create_or_update(conn, user.id, None)
            .expect("failed to create crate");

            Self {
                user,
                krate,
                next_version: 1,
            }
        }

        fn new_version(&mut self, conn: &PgConnection) -> i32 {
            let version = NewVersion::new(
                self.krate.id,
                &Version::parse(&format!("{}.0.0", self.next_version)).unwrap(),
                &HashMap::new(),
                None,
                None,
                0,
                self.user.id,
            )
            .expect("failed to create version")
            .save(conn, &[], "ghost@example.com")
            .expect("failed to save version");

            self.next_version += 1;
            version.id
        }

        fn assert_downloads_count(&self, conn: &PgConnection, version: i32, expected: i64) {
            use crate::schema::version_downloads::dsl::*;
            use diesel::dsl::*;

            let actual: Option<i64> = version_downloads
                .select(sum(downloads))
                .filter(version_id.eq(version))
                .first(conn)
                .unwrap();
            assert_eq!(actual.unwrap_or(0), expected);
        }
    }
}
<|endoftext|>


<commit_before>use std::path::{ Path, PathBuf };
use std::fs;

static TYPE_STR: &'static str = "unknown";

pub struct UnknownFactory;

impl ::file_type::FileTypeFactory for UnknownFactory {
    fn try_create(&self, path: &Path) -> Option<Box<::file_type::FileType>> {
        Some(Box::new(Unknown {
            path: PathBuf::from(path),
            type_str: TYPE_STR
        }))
    }

    fn initialize(&self, _: &mut ::AppContext) -> Result<(), &'static str> {
        Ok(())
    }
}

pub struct Unknown {
    path: PathBuf,
    type_str: &'static str
}

impl ::file_type::FileType for Unknown {
    fn get_url(&self, context: &::AppContext) -> String {
        let file_name = self.path.file_name().expect("Problem parsing relative url");
        let relative = self.path.relative_from(&context.root_notes).expect("Problem parsing relative url");
        let parent_relative = if relative.parent().unwrap() == Path::new("") {
            String::from_str("")
        } else {
            format!("{}/", relative.parent().unwrap().to_str().unwrap())
        };
        format!("{}{}{:?}", context.base_url, parent_relative, file_name)
    }

    fn convert(&self, context: &::AppContext) {
        let relative = self.path.relative_from(&context.root_notes).expect("Problem parsing relative url");
        let destination = context.root_dest.join(&relative);
        fs::copy(&self.path, &destination).ok().expect("Problem copying unknown file");
    }

    fn get_type_str(&self) -> &'static str {
        self.type_str
    }
}
<commit_msg>fixed bug in unknown file type<commit_after>use std::path::{ Path, PathBuf };
use std::fs;

static TYPE_STR: &'static str = "unknown";

pub struct UnknownFactory;

impl ::file_type::FileTypeFactory for UnknownFactory {
    fn try_create(&self, path: &Path) -> Option<Box<::file_type::FileType>> {
        Some(Box::new(Unknown {
            path: PathBuf::from(path),
            type_str: TYPE_STR
        }))
    }

    fn initialize(&self, _: &mut ::AppContext) -> Result<(), &'static str> {
        Ok(())
    }
}

pub struct Unknown {
    path: PathBuf,
    type_str: &'static str
}

impl ::file_type::FileType for Unknown {
    fn get_url(&self, context: &::AppContext) -> String {
        let file_name = self.path.file_name().expect("Problem parsing relative url");
        let relative = self.path.relative_from(&context.root_notes).expect("Problem parsing relative url");
        let parent_relative = if relative.parent().unwrap() == Path::new("") {
            String::from_str("")
        } else {
            format!("{}/", relative.parent().unwrap().to_str().unwrap())
        };
        format!("{}{}{}", context.base_url, parent_relative, file_name.to_str().unwrap())
    }

    fn convert(&self, context: &::AppContext) {
        let relative = self.path.relative_from(&context.root_notes).expect("Problem parsing relative url");
        let destination = context.root_dest.join(&relative);
        fs::copy(&self.path, &destination).ok().expect("Problem copying unknown file");
    }

    fn get_type_str(&self) -> &'static str {
        self.type_str
    }
}
<|endoftext|>


<commit_before>use math::{Vector3, Matrix4, Point3};
use geometry::{Shape, Intersectable, Transformable};
use ray::Ray;
use material::Material;
use intersection::Intersection;
use math::EPSILON;

#[derive(Debug)]
pub struct Triangle {
    pub v0: Point3, // A
    pub v1: Point3, // B
    pub v2: Point3, // C
    pub ab: Vector3, // B - A
    pub ac: Vector3, // C - A
    pub normal: Vector3,
    material: Material,
}

impl Triangle {
    pub fn new(v0: Point3, v1: Point3, v2: Point3, material: Material) -> Triangle {
        let ab = v1 - v0;
        let ac = v2 - v0;

        Triangle {
            v0: v0,
            v1: v1,
            v2: v2,
            ab: ab,
            ac: ac,
            normal: ab.cross(&ac).normalize(),
            material: material,
        }
    }
}

impl Shape for Triangle {
    fn material(&self) -> &Material {
        &self.material
    }
}

impl Intersectable for Triangle {
    fn intersect(&self, ray: Ray) -> Option<Intersection> {
        let pvec = ray.direction.cross(&self.ac);
        let det = self.ab.dot(&pvec);

        if det.abs() < EPSILON {
            return None;
        }

        let inv_det = 1.0 / det;

        let tvec = ray.origin - self.v0;
        let u = tvec.dot(&pvec) * inv_det;

        if u < 0.0 || u > 1.0 {
            return None;
        }

        let qvec = tvec.cross(&self.ab);
        let v = ray.direction.dot(&qvec) * inv_det;

        if v < 0.0 || u + v > 1.0 {
            return None;
        }

        let t = self.ac.dot(&qvec) * inv_det;
        if t > EPSILON {

            let intersection_point = (ray.origin + ray.direction * t).as_point();
            let intersection =
                Intersection::new(t, self, intersection_point, ray, self.normal, false);

            return Some(intersection);
        }

        None
    }
}

impl Transformable for Triangle {
    fn transform(&mut self, matrix: Matrix4, normal_matrix: Matrix4) {
        self.v0 = self.v0 * matrix;
        self.v1 = self.v1 * matrix;
        self.v2 = self.v2 * matrix;
        self.normal = (self.normal * normal_matrix).normalize();
    }
}
<commit_msg>Fix triangle transforms<commit_after>use math::{Vector3, Matrix4, Point3};
use geometry::{Shape, Intersectable, Transformable};
use ray::Ray;
use material::Material;
use intersection::Intersection;
use math::EPSILON;

#[derive(Debug)]
pub struct Triangle {
    pub v0: Point3, // A
    pub v1: Point3, // B
    pub v2: Point3, // C
    pub ab: Vector3, // B - A
    pub ac: Vector3, // C - A
    pub normal: Vector3,
    material: Material,
}

impl Triangle {
    pub fn new(v0: Point3, v1: Point3, v2: Point3, material: Material) -> Triangle {
        let ab = v1 - v0;
        let ac = v2 - v0;

        Triangle {
            v0: v0,
            v1: v1,
            v2: v2,
            ab: ab,
            ac: ac,
            normal: ab.cross(&ac).normalize(),
            material: material,
        }
    }
}

impl Shape for Triangle {
    fn material(&self) -> &Material {
        &self.material
    }
}

impl Intersectable for Triangle {
    fn intersect(&self, ray: Ray) -> Option<Intersection> {
        let pvec = ray.direction.cross(&self.ac);
        let det = self.ab.dot(&pvec);

        if det.abs() < EPSILON {
            return None;
        }

        let inv_det = 1.0 / det;

        let tvec = ray.origin - self.v0;
        let u = tvec.dot(&pvec) * inv_det;

        if u < 0.0 || u > 1.0 {
            return None;
        }

        let qvec = tvec.cross(&self.ab);
        let v = ray.direction.dot(&qvec) * inv_det;

        if v < 0.0 || u + v > 1.0 {
            return None;
        }

        let t = self.ac.dot(&qvec) * inv_det;
        if t > EPSILON {

            let intersection_point = (ray.origin + ray.direction * t).as_point();
            let intersection =
                Intersection::new(t, self, intersection_point, ray, self.normal, false);

            return Some(intersection);
        }

        None
    }
}

impl Transformable for Triangle {
    fn transform(&mut self, matrix: Matrix4, normal_matrix: Matrix4) {
        self.v0 = self.v0 * matrix;
        self.v1 = self.v1 * matrix;
        self.v2 = self.v2 * matrix;
        self.ab = self.v1 - self.v0;
        self.ac = self.v2 - self.v0;
        self.normal = (self.normal * normal_matrix).normalize();
    }
}
<|endoftext|>


<commit_before><commit_msg>correlator: use new eventhandler<commit_after><|endoftext|>


<commit_before><commit_msg>Added draw_card test<commit_after><|endoftext|>


<commit_before><commit_msg>Open line loop<commit_after><|endoftext|>


<commit_before>use super::number::Number;
use super::distance::Distance;

#[derive(Debug)]
pub struct Point<T> {
   pub x: T,
   pub y: T
}

impl<T> Point<T> where T: Number {
   pub fn new(x: T, y: T) -> Self {
      Point {
         x: x,
         y: y
      }
   }
}

impl<T> Distance<Point<T>, T> for Point<T> where T: Number {
   fn distance(&self, other: &Self) -> T {
      let x = other.x - self.x;
      let y = other.y - self.y;
      return (x * x + y * y).sqrt()
   }
}

#[cfg(test)]
mod tests {
   use test::{Bencher, black_box};

   use super::super::distance::Distance;

   use super::*;

   #[bench]
   fn bench_distance_f64(b: &mut Bencher) {
      let p1 = Point::new(5_f64, 7_f64);
      let p2 = Point::new(3_f64, 2_f64);
      b.iter(|| {
         for _ in 0..1000 {
            black_box(
               p1.distance(&p2)
            );
         }
      });
   }

   #[bench]
   fn bench_distance_f32(b: &mut Bencher) {
      let p1 = Point::new(5_f32, 7_f32);
      let p2 = Point::new(3_f32, 2_f32);
      b.iter(|| {
         for _ in 0..1000 {
            black_box(
               p1.distance(&p2)
            );
         }
      });
   }
}
<commit_msg>Second attempt at fixing the Point::distance benchmarks<commit_after>use super::number::Number;
use super::distance::Distance;

#[derive(Debug)]
pub struct Point<T> {
   pub x: T,
   pub y: T
}

impl<T> Point<T> where T: Number {
   pub fn new(x: T, y: T) -> Self {
      Point {
         x: x,
         y: y
      }
   }
}

impl<T> Distance<Point<T>, T> for Point<T> where T: Number {
   fn distance(&self, other: &Self) -> T {
      let x = other.x - self.x;
      let y = other.y - self.y;
      return (x * x + y * y).sqrt()
   }
}

#[cfg(test)]
mod tests {
   use test::{Bencher, black_box};

   use super::super::distance::Distance;

   use super::*;

   #[bench]
   fn bench_distance_f64(b: &mut Bencher) {
      let p1 = Point::new(5_f64, 7_f64);
      let p2 = Point::new(3_f64, 2_f64);
      b.iter(|| {
         for _ in 0..1000 {
            black_box(
               p1.distance(
                  black_box(&p2)
               )
            );
         }
      });
   }

   #[bench]
   fn bench_distance_f32(b: &mut Bencher) {
      let p1 = Point::new(5_f32, 7_f32);
      let p2 = Point::new(3_f32, 2_f32);
      b.iter(|| {
         for _ in 0..1000 {
            black_box(
               p1.distance(
                  black_box(&p2)
               )
            );
         }
      });
   }
}
<|endoftext|>


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

use std::num::cast;

use angle::{Angle, tan, cot};
use frustum::Frustum;
use matrix::{Matrix4, ToMatrix4};
use num::{BaseFloat, zero, one};
use plane::Plane;

/// Create a perspective projection matrix.
///
/// This is the equivalent to the [gluPerspective]
/// (http://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml) function.
pub fn perspective<S: BaseFloat + 'static, A: Angle<S>>(fovy: A, aspect: S, near: S, far: S) -> Matrix4<S> {
    PerspectiveFov {
        fovy:   fovy,
        aspect: aspect,
        near:   near,
        far:    far,
    }.to_matrix4()
}

/// Create a perspective matrix from a view frustrum.
///
/// This is the equivalent of the now deprecated [glFrustrum]
/// (http://www.opengl.org/sdk/docs/man2/xhtml/glFrustum.xml) function.
pub fn frustum<S: BaseFloat + 'static>(left: S, right: S, bottom: S, top: S, near: S, far: S) -> Matrix4<S> {
    Perspective {
        left:   left,
        right:  right,
        bottom: bottom,
        top:    top,
        near:   near,
        far:    far,
    }.to_matrix4()
}

/// Create an orthographic projection matrix.
///
/// This is the equivalent of the now deprecated [glOrtho]
/// (http://www.opengl.org/sdk/docs/man2/xhtml/glOrtho.xml) function.
pub fn ortho<S: BaseFloat + 'static>(left: S, right: S, bottom: S, top: S, near: S, far: S) -> Matrix4<S> {
    Ortho {
        left:   left,
        right:  right,
        bottom: bottom,
        top:    top,
        near:   near,
        far:    far,
    }.to_matrix4()
}

pub trait Projection<S>: ToMatrix4<S> {
    fn to_frustum(&self) -> Frustum<S>;
}

/// A perspective projection based on a vertical field-of-view angle.
#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable)]
pub struct PerspectiveFov<S, A> {
    pub fovy:   A,
    pub aspect: S,
    pub near:   S,
    pub far:    S,
}

impl<S: BaseFloat, A: Angle<S>> PerspectiveFov<S, A> {
    pub fn to_perspective(&self) -> Perspective<S> {
        let angle = self.fovy.div_s(cast(2i8).unwrap());
        let ymax = self.near * tan(angle.to_rad());
        let xmax = ymax * self.aspect;

        Perspective {
            left:   -xmax,
            right:   xmax,
            bottom: -ymax,
            top:     ymax,
            near:    self.near.clone(),
            far:     self.far.clone(),
        }
    }
}

impl<S: BaseFloat + 'static, A: Angle<S>> Projection<S> for PerspectiveFov<S, A> {
    fn to_frustum(&self) -> Frustum<S> {
        // TODO: Could this be faster?
        Frustum::from_matrix4(self.to_matrix4()).unwrap()
    }
}

impl<S: BaseFloat, A: Angle<S>> ToMatrix4<S> for PerspectiveFov<S, A> {
    fn to_matrix4(&self) -> Matrix4<S> {
        let half_turn: A = Angle::turn_div_2();

        assert!(self.fovy   > zero(),    "The vertical field of view cannot be below zero, found: {:?}", self.fovy);
        assert!(self.fovy   < half_turn, "The vertical field of view cannot be greater than a half turn, found: {:?}", self.fovy);
        assert!(self.aspect > zero(),    "The aspect ratio cannot be below zero, found: {:?}", self.aspect);
        assert!(self.near   > zero(),    "The near plane distance cannot be below zero, found: {:?}", self.near);
        assert!(self.far    > zero(),    "The far plane distance cannot be below zero, found: {:?}", self.far);
        assert!(self.far    > self.near, "The far plane cannot be closer than the near plane, found: far: {:?}, near: {:?}", self.far, self.near);

        let f = cot(self.fovy.div_s(cast(2i8).unwrap()).to_rad());
        let two: S = cast(2i8).unwrap();

        let c0r0 = f / self.aspect;
        let c0r1 = zero();
        let c0r2 = zero();
        let c0r3 = zero();

        let c1r0 = zero();
        let c1r1 = f;
        let c1r2 = zero();
        let c1r3 = zero();

        let c2r0 = zero();
        let c2r1 = zero();
        let c2r2 = (self.far + self.near) / (self.near - self.far);
        let c2r3 = -one::<S>();

        let c3r0 = zero();
        let c3r1 = zero();
        let c3r2 = (two * self.far * self.near) / (self.near - self.far);
        let c3r3 = zero();

        Matrix4::new(c0r0, c0r1, c0r2, c0r3,
                     c1r0, c1r1, c1r2, c1r3,
                     c2r0, c2r1, c2r2, c2r3,
                     c3r0, c3r1, c3r2, c3r3)
    }
}

/// A perspective projection with arbitrary left/right/bottom/top distances
#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable)]
pub struct Perspective<S> {
    pub left:   S,
    pub right:  S,
    pub bottom: S,
    pub top:    S,
    pub near:   S,
    pub far:    S,
}

impl<S: BaseFloat + 'static> Projection<S> for Perspective<S> {
    fn to_frustum(&self) -> Frustum<S> {
        // TODO: Could this be faster?
        Frustum::from_matrix4(self.to_matrix4()).unwrap()
    }
}

impl<S: BaseFloat + 'static> ToMatrix4<S> for Perspective<S> {
    fn to_matrix4(&self) -> Matrix4<S> {
        assert!(self.left   <= self.right, "`left` cannot be greater than `right`, found: left: {:?} right: {:?}", self.left, self.right);
        assert!(self.bottom <= self.top,   "`bottom` cannot be greater than `top`, found: bottom: {:?} top: {:?}", self.bottom, self.top);
        assert!(self.near   <= self.far,   "`near` cannot be greater than `far`, found: near: {:?} far: {:?}", self.near, self.far);

        let two: S = cast(2i8).unwrap();

        let c0r0 = (two * self.near) / (self.right - self.left);
        let c0r1 = zero();
        let c0r2 = zero();
        let c0r3 = zero();

        let c1r0 = zero();
        let c1r1 = (two * self.near) / (self.top - self.bottom);
        let c1r2 = zero();
        let c1r3 = zero();

        let c2r0 = (self.right + self.left) / (self.right - self.left);
        let c2r1 = (self.top + self.bottom) / (self.top - self.bottom);
        let c2r2 = -(self.far + self.near) / (self.far - self.near);
        let c2r3 = -one::<S>();

        let c3r0 = zero();
        let c3r1 = zero();
        let c3r2 = -(two * self.far * self.near) / (self.far - self.near);
        let c3r3 = zero();

        Matrix4::new(c0r0, c0r1, c0r2, c0r3,
                     c1r0, c1r1, c1r2, c1r3,
                     c2r0, c2r1, c2r2, c2r3,
                     c3r0, c3r1, c3r2, c3r3)
    }
}

/// An orthographic projection with arbitrary left/right/bottom/top distances
#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable)]
pub struct Ortho<S> {
    pub left:   S,  right:  S,
    pub bottom: S,  top:    S,
    pub near:   S,  far:    S,
}

impl<S: BaseFloat> Projection<S> for Ortho<S> {
    fn to_frustum(&self) -> Frustum<S> {
        Frustum {
            left:   Plane::from_abcd( one::<S>(), zero::<S>(), zero::<S>(), self.left.clone()),
            right:  Plane::from_abcd(-one::<S>(), zero::<S>(), zero::<S>(), self.right.clone()),
            bottom: Plane::from_abcd(zero::<S>(),  one::<S>(), zero::<S>(), self.bottom.clone()),
            top:    Plane::from_abcd(zero::<S>(), -one::<S>(), zero::<S>(), self.top.clone()),
            near:   Plane::from_abcd(zero::<S>(), zero::<S>(), -one::<S>(), self.near.clone()),
            far:    Plane::from_abcd(zero::<S>(), zero::<S>(),  one::<S>(), self.far.clone()),
        }
    }
}

impl<S: BaseFloat> ToMatrix4<S> for Ortho<S> {
    fn to_matrix4(&self) -> Matrix4<S> {
        let two: S = cast(2i8).unwrap();

        let c0r0 = two / (self.right - self.left);
        let c0r1 = zero();
        let c0r2 = zero();
        let c0r3 = zero();

        let c1r0 = zero();
        let c1r1 = two / (self.top - self.bottom);
        let c1r2 = zero();
        let c1r3 = zero();

        let c2r0 = zero();
        let c2r1 = zero();
        let c2r2 = -two / (self.far - self.near);
        let c2r3 = zero();

        let c3r0 = -(self.right + self.left) / (self.right - self.left);
        let c3r1 = -(self.top + self.bottom) / (self.top - self.bottom);
        let c3r2 = -(self.far + self.near) / (self.far - self.near);
        let c3r3 = one::<S>();

        Matrix4::new(c0r0, c0r1, c0r2, c0r3,
                     c1r0, c1r1, c1r2, c1r3,
                     c2r0, c2r1, c2r2, c2r3,
                     c3r0, c3r1, c3r2, c3r3)
    }
}
<commit_msg>Exposed Ortho fields as public (last time it was Perspective)<commit_after>// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::num::cast;

use angle::{Angle, tan, cot};
use frustum::Frustum;
use matrix::{Matrix4, ToMatrix4};
use num::{BaseFloat, zero, one};
use plane::Plane;

/// Create a perspective projection matrix.
///
/// This is the equivalent to the [gluPerspective]
/// (http://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml) function.
pub fn perspective<S: BaseFloat + 'static, A: Angle<S>>(fovy: A, aspect: S, near: S, far: S) -> Matrix4<S> {
    PerspectiveFov {
        fovy:   fovy,
        aspect: aspect,
        near:   near,
        far:    far,
    }.to_matrix4()
}

/// Create a perspective matrix from a view frustrum.
///
/// This is the equivalent of the now deprecated [glFrustrum]
/// (http://www.opengl.org/sdk/docs/man2/xhtml/glFrustum.xml) function.
pub fn frustum<S: BaseFloat + 'static>(left: S, right: S, bottom: S, top: S, near: S, far: S) -> Matrix4<S> {
    Perspective {
        left:   left,
        right:  right,
        bottom: bottom,
        top:    top,
        near:   near,
        far:    far,
    }.to_matrix4()
}

/// Create an orthographic projection matrix.
///
/// This is the equivalent of the now deprecated [glOrtho]
/// (http://www.opengl.org/sdk/docs/man2/xhtml/glOrtho.xml) function.
pub fn ortho<S: BaseFloat + 'static>(left: S, right: S, bottom: S, top: S, near: S, far: S) -> Matrix4<S> {
    Ortho {
        left:   left,
        right:  right,
        bottom: bottom,
        top:    top,
        near:   near,
        far:    far,
    }.to_matrix4()
}

pub trait Projection<S>: ToMatrix4<S> {
    fn to_frustum(&self) -> Frustum<S>;
}

/// A perspective projection based on a vertical field-of-view angle.
#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable)]
pub struct PerspectiveFov<S, A> {
    pub fovy:   A,
    pub aspect: S,
    pub near:   S,
    pub far:    S,
}

impl<S: BaseFloat, A: Angle<S>> PerspectiveFov<S, A> {
    pub fn to_perspective(&self) -> Perspective<S> {
        let angle = self.fovy.div_s(cast(2i8).unwrap());
        let ymax = self.near * tan(angle.to_rad());
        let xmax = ymax * self.aspect;

        Perspective {
            left:   -xmax,
            right:   xmax,
            bottom: -ymax,
            top:     ymax,
            near:    self.near.clone(),
            far:     self.far.clone(),
        }
    }
}

impl<S: BaseFloat + 'static, A: Angle<S>> Projection<S> for PerspectiveFov<S, A> {
    fn to_frustum(&self) -> Frustum<S> {
        // TODO: Could this be faster?
        Frustum::from_matrix4(self.to_matrix4()).unwrap()
    }
}

impl<S: BaseFloat, A: Angle<S>> ToMatrix4<S> for PerspectiveFov<S, A> {
    fn to_matrix4(&self) -> Matrix4<S> {
        let half_turn: A = Angle::turn_div_2();

        assert!(self.fovy   > zero(),    "The vertical field of view cannot be below zero, found: {:?}", self.fovy);
        assert!(self.fovy   < half_turn, "The vertical field of view cannot be greater than a half turn, found: {:?}", self.fovy);
        assert!(self.aspect > zero(),    "The aspect ratio cannot be below zero, found: {:?}", self.aspect);
        assert!(self.near   > zero(),    "The near plane distance cannot be below zero, found: {:?}", self.near);
        assert!(self.far    > zero(),    "The far plane distance cannot be below zero, found: {:?}", self.far);
        assert!(self.far    > self.near, "The far plane cannot be closer than the near plane, found: far: {:?}, near: {:?}", self.far, self.near);

        let f = cot(self.fovy.div_s(cast(2i8).unwrap()).to_rad());
        let two: S = cast(2i8).unwrap();

        let c0r0 = f / self.aspect;
        let c0r1 = zero();
        let c0r2 = zero();
        let c0r3 = zero();

        let c1r0 = zero();
        let c1r1 = f;
        let c1r2 = zero();
        let c1r3 = zero();

        let c2r0 = zero();
        let c2r1 = zero();
        let c2r2 = (self.far + self.near) / (self.near - self.far);
        let c2r3 = -one::<S>();

        let c3r0 = zero();
        let c3r1 = zero();
        let c3r2 = (two * self.far * self.near) / (self.near - self.far);
        let c3r3 = zero();

        Matrix4::new(c0r0, c0r1, c0r2, c0r3,
                     c1r0, c1r1, c1r2, c1r3,
                     c2r0, c2r1, c2r2, c2r3,
                     c3r0, c3r1, c3r2, c3r3)
    }
}

/// A perspective projection with arbitrary left/right/bottom/top distances
#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable)]
pub struct Perspective<S> {
    pub left:   S,
    pub right:  S,
    pub bottom: S,
    pub top:    S,
    pub near:   S,
    pub far:    S,
}

impl<S: BaseFloat + 'static> Projection<S> for Perspective<S> {
    fn to_frustum(&self) -> Frustum<S> {
        // TODO: Could this be faster?
        Frustum::from_matrix4(self.to_matrix4()).unwrap()
    }
}

impl<S: BaseFloat + 'static> ToMatrix4<S> for Perspective<S> {
    fn to_matrix4(&self) -> Matrix4<S> {
        assert!(self.left   <= self.right, "`left` cannot be greater than `right`, found: left: {:?} right: {:?}", self.left, self.right);
        assert!(self.bottom <= self.top,   "`bottom` cannot be greater than `top`, found: bottom: {:?} top: {:?}", self.bottom, self.top);
        assert!(self.near   <= self.far,   "`near` cannot be greater than `far`, found: near: {:?} far: {:?}", self.near, self.far);

        let two: S = cast(2i8).unwrap();

        let c0r0 = (two * self.near) / (self.right - self.left);
        let c0r1 = zero();
        let c0r2 = zero();
        let c0r3 = zero();

        let c1r0 = zero();
        let c1r1 = (two * self.near) / (self.top - self.bottom);
        let c1r2 = zero();
        let c1r3 = zero();

        let c2r0 = (self.right + self.left) / (self.right - self.left);
        let c2r1 = (self.top + self.bottom) / (self.top - self.bottom);
        let c2r2 = -(self.far + self.near) / (self.far - self.near);
        let c2r3 = -one::<S>();

        let c3r0 = zero();
        let c3r1 = zero();
        let c3r2 = -(two * self.far * self.near) / (self.far - self.near);
        let c3r3 = zero();

        Matrix4::new(c0r0, c0r1, c0r2, c0r3,
                     c1r0, c1r1, c1r2, c1r3,
                     c2r0, c2r1, c2r2, c2r3,
                     c3r0, c3r1, c3r2, c3r3)
    }
}

/// An orthographic projection with arbitrary left/right/bottom/top distances
#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable)]
pub struct Ortho<S> {
    pub left:   S,
    pub right:  S,
    pub bottom: S,
    pub top:    S,
    pub near:   S,
    pub far:    S,
}

impl<S: BaseFloat> Projection<S> for Ortho<S> {
    fn to_frustum(&self) -> Frustum<S> {
        Frustum {
            left:   Plane::from_abcd( one::<S>(), zero::<S>(), zero::<S>(), self.left.clone()),
            right:  Plane::from_abcd(-one::<S>(), zero::<S>(), zero::<S>(), self.right.clone()),
            bottom: Plane::from_abcd(zero::<S>(),  one::<S>(), zero::<S>(), self.bottom.clone()),
            top:    Plane::from_abcd(zero::<S>(), -one::<S>(), zero::<S>(), self.top.clone()),
            near:   Plane::from_abcd(zero::<S>(), zero::<S>(), -one::<S>(), self.near.clone()),
            far:    Plane::from_abcd(zero::<S>(), zero::<S>(),  one::<S>(), self.far.clone()),
        }
    }
}

impl<S: BaseFloat> ToMatrix4<S> for Ortho<S> {
    fn to_matrix4(&self) -> Matrix4<S> {
        let two: S = cast(2i8).unwrap();

        let c0r0 = two / (self.right - self.left);
        let c0r1 = zero();
        let c0r2 = zero();
        let c0r3 = zero();

        let c1r0 = zero();
        let c1r1 = two / (self.top - self.bottom);
        let c1r2 = zero();
        let c1r3 = zero();

        let c2r0 = zero();
        let c2r1 = zero();
        let c2r2 = -two / (self.far - self.near);
        let c2r3 = zero();

        let c3r0 = -(self.right + self.left) / (self.right - self.left);
        let c3r1 = -(self.top + self.bottom) / (self.top - self.bottom);
        let c3r2 = -(self.far + self.near) / (self.far - self.near);
        let c3r3 = one::<S>();

        Matrix4::new(c0r0, c0r1, c0r2, c0r3,
                     c1r0, c1r1, c1r2, c1r3,
                     c2r0, c2r1, c2r2, c2r3,
                     c3r0, c3r1, c3r2, c3r3)
    }
}
<|endoftext|>


<commit_before>//! This module defines various methods and structs for interacting with uplaods on backblaze.
//!
//! The primary struct in this module is [UploadAuthorization][1], which has various methods for
//! uploading files to backblaze b2. This struct is usually obtained from a [B2Authorization][2]
//! using the method [get_upload_url][3].
//!
//!  [1]: struct.UploadAuthorization.html
//!  [2]: ../authorize/struct.B2Authorization.html
//!  [3]: ../authorize/struct.B2Authorization.html#method.get_upload_url

use std::io::{Write, Read, copy};

use hyper::{self, Client, Url};
use hyper::client::Body;
use hyper::client::request::Request;
use hyper::header::{Headers,ContentLength,ContentType};
use hyper::mime::Mime;
use hyper::method::Method;
use hyper::net::{Streaming, NetworkConnector, NetworkStream};

use serde::Deserialize;
use serde_json;

use B2Error;
use B2AuthHeader;
use raw::authorize::B2Authorization;
use raw::files::MoreFileInfo;
/// Contains the information needed to authorize an upload to b2. This struct is usually obtained
/// from a [B2Authorization][1] using the method [get_upload_url][2].
///
/// The b2 website specifies that you may not upload to the same url in parallel.
///
///  [1]: ../authorize/struct.B2Authorization.html
///  [2]: ../authorize/struct.B2Authorization.html#method.get_upload_url
#[derive(Deserialize,Serialize,Clone,Debug)]
#[serde(rename_all = "camelCase")]
pub struct UploadAuthorization {
    pub bucket_id: String,
    pub upload_url: String,
    pub authorization_token: String
}
impl UploadAuthorization {
    /// Returns a hyper header that authorizes an upload request.
    pub fn auth_header(&self) -> B2AuthHeader {
        B2AuthHeader(self.authorization_token.clone())
    }
}

/// Methods related to the [upload module][1].
///
///  [1]: ../upload/index.html
impl<'a> B2Authorization<'a> {
    /// Performs a [b2_get_upload_url][1] api call and returns the upload url wrapped in an
    /// [`UploadAuthorization`].
    ///
    /// # Errors
    /// This function returns a [`B2Error`] in case something goes wrong. Besides the standard
    /// errors, this function can fail with [`is_bucket_not_found`].
    ///
    ///  [1]: https://www.backblaze.com/b2/docs/b2_get_upload_url.html
    ///  [`B2Error`]: ../authorize/enum.B2Error.html
    ///  [`is_bucket_not_found`]: ../../enum.B2Error.html#method.is_bucket_not_found
    ///  [`UploadAuthorization`]: struct.UploadAuthorization.html
    pub fn get_upload_url(&self, bucket_id: &str, client: &Client)
        -> Result<UploadAuthorization,B2Error>
    {
        let url_string: String = format!("{}/b2api/v1/b2_get_upload_url", self.api_url);
        let url: &str = &url_string;

        #[derive(Serialize)]
        #[serde(rename_all = "camelCase")]
        struct Request<'a> {
            bucket_id: &'a str
        }
        let request = Request {
            bucket_id: bucket_id
        };
        let body: String = serde_json::to_string(&request)?;

        let resp = client.post(url)
            .body(Body::BufBody(body.as_bytes(), body.len()))
            .header(self.auth_header())
            .send()?;
        if resp.status != hyper::status::StatusCode::Ok {
            Err(B2Error::from_response(resp))
        } else {
            Ok(serde_json::from_reader(resp)?)
        }
    }
}
impl UploadAuthorization {
    /// Equivalent to calling [create_upload_file_request][1], writing everything in the Read to
    /// the Writer and calling finish.
    ///
    ///  [1]: struct.UploadAuthorization.html#method.create_upload_file_request
    pub fn upload_file<InfoType, R: Read, C, S>(&self, file: &mut R, file_name: String, content_type: Option<Mime>,
                                 content_length: u64, content_sha1: String, connector: &C)
        -> Result<MoreFileInfo<InfoType>, B2Error>
        where for<'de> InfoType: Deserialize<'de>, R: Sized, C: NetworkConnector<Stream=S>,
              S: Into<Box<NetworkStream + Send>>
    {
        let mut ufr = self.create_upload_file_request(
            file_name, content_type, content_length, content_sha1, connector)?;
        copy(file, &mut ufr)?;
        ufr.finish()
    }
    /// Starts a request to upload a file to backblaze b2. This function returns an
    /// [UploadFileRequest][1], which implements [Write][2]. When writing to this object, the
    /// data is sent to backblaze b2. This method of uploading can be used to
    /// implement things such as rate limiting of the request.
    ///
    /// After the file has been sent, you need to call the [finish method][3] on the
    /// [UploadFileRequest][1], in order to close the connection.
    ///
    /// The [upload_file method][4] can be used to upload any Reader easily. The backblaze api
    /// supports not specifying the sha1 checksum. This is not recommended, but if you wish to do
    /// this, simply pass the string `do_not_verify` as the sha1 checksum.
    ///
    /// The function [create_upload_file_request_sha1_at_end][5] might be of interest. This
    /// function behaves identically to this function, except the sha1 is passed when calling
    /// finish instead of before initiating the request.
    ///
    /// Read the [backblaze api documentation][6] for more information.
    ///
    /// # Errors
    /// This function returns a [`B2Error`] in case something goes wrong. Besides the standard
    /// errors, this function can fail with [`is_invalid_file_name`] and [`is_cap_exceeded`].
    ///
    ///  [1]: struct.UploadFileRequest.html
    ///  [2]: https://doc.rust-lang.org/stable/std/io/trait.Write.html
    ///  [3]: struct.UploadFileRequest.html#method.finish
    ///  [4]: struct.UploadAuthorization.html#method.upload_file
    ///  [5]: struct.UploadFileRequest.html#method.create_upload_file_request_sha1_at_end
    ///  [6]: https://www.backblaze.com/b2/docs/uploading.html
    ///  [`B2Error`]: ../authorize/enum.B2Error.html
    ///  [`is_invalid_file_name`]: ../../enum.B2Error.html#method.is_invalid_file_name
    ///  [`is_cap_exceeded`]: ../../enum.B2Error.html#method.is_cap_exceeded
    pub fn create_upload_file_request<C,S>(&self, file_name: String,
                                           content_type: Option<Mime>,
                                           content_length: u64, content_sha1: String,
                                           connector: &C)
        -> Result<UploadFileRequest, B2Error>
        where C: NetworkConnector<Stream=S>, S: Into<Box<NetworkStream + Send>>
    {
        let url: Url = Url::parse(&self.upload_url)?;
        let mut request = Request::with_connector(Method::Post, url, connector)?;
        {
            let headers: &mut Headers = request.headers_mut();
            headers.set(self.auth_header());
            headers.set(XBzFileName(file_name));
            headers.set(XBzContentSha1(content_sha1));
            headers.set(ContentLength(content_length));
            headers.set(ContentType(match content_type {
                Some(v) => v,
                None => "b2/x-auto".parse().unwrap()
            }));
        }
        Ok(UploadFileRequest { request: request.start()? })
    }
    /// Starts a request to upload a file to backblaze b2. This function returns an
    /// [UploadFileRequestSha1End][1], which implements [Write][2]. When writing to this object,
    /// the data is sent to backblaze b2. This method of uploading can be used to implement things
    /// such as rate limiting of the request.
    ///
    /// The value of the `content_length` parameter must be exactly the amount of bytes you are
    /// going to write, not including the 40 byte sha1 appended by the [finish method][3].
    ///
    /// After the file has been sent, you need to call the [finish method][3] on the
    /// [UploadFileRequestSha1End][1], in order to close the connection.
    ///
    /// Read the [backblaze api documentation][4] for more information.
    ///
    /// # Errors
    /// This function returns a [`B2Error`] in case something goes wrong. Besides the standard
    /// errors, this function can fail with [`is_invalid_file_name`] and [`is_cap_exceeded`].
    ///
    ///  [1]: struct.UploadFileRequestSha1End.html
    ///  [2]: https://doc.rust-lang.org/stable/std/io/trait.Write.html
    ///  [3]: struct.UploadFileRequestSha1End.html#method.finish
    ///  [4]: https://www.backblaze.com/b2/docs/uploading.html
    ///  [`B2Error`]: ../authorize/enum.B2Error.html
    ///  [`is_invalid_file_name`]: ../../enum.B2Error.html#method.is_invalid_file_name
    ///  [`is_cap_exceeded`]: ../../enum.B2Error.html#method.is_cap_exceeded
    pub fn create_upload_file_request_sha1_at_end<C,S>(&self, file_name: String,
                                                       content_type: Option<Mime>,
                                                       content_length: u64,
                                                       connector: &C)
        -> Result<UploadFileRequestSha1End, B2Error>
        where C: NetworkConnector<Stream=S>, S: Into<Box<NetworkStream + Send>>
    {
        let url: Url = Url::parse(&self.upload_url)?;
        let mut request = Request::with_connector(Method::Post, url, connector)?;
        {
            let headers: &mut Headers = request.headers_mut();
            headers.set(self.auth_header());
            headers.set(XBzFileName(file_name));
            headers.set(XBzContentSha1("hex_digits_at_end".to_owned()));
            headers.set(ContentLength(content_length + 40));
            headers.set(ContentType(match content_type {
                Some(v) => v,
                None => "b2/x-auto".parse().unwrap()
            }));
        }
        Ok(UploadFileRequestSha1End { request: request.start()? })
    }
}
header! { (XBzFileName, "X-Bz-File-Name") => [String] }
header! { (XBzContentSha1, "X-Bz-Content-Sha1") => [String] }

/// Contains an ongoing upload to the backblaze b2 api. This struct is created by the
/// [`create_upload_file_request`] method.
///
///  [`create_upload_file_request`]: struct.UploadAuthorization.html#method.create_upload_file_request
pub struct UploadFileRequest {
    request: Request<Streaming>
}
impl Write for UploadFileRequest {
    fn write(&mut self, msg: &[u8]) -> ::std::io::Result<usize> {
        self.request.write(msg)
    }
    fn flush(&mut self) -> ::std::io::Result<()> {
        self.request.flush()
    }
    fn write_all(&mut self, buf: &[u8]) -> Result<(), ::std::io::Error> {
        self.request.write_all(buf)
    }
    fn write_fmt(&mut self, fmt: ::core::fmt::Arguments) -> Result<(), ::std::io::Error> {
        self.request.write_fmt(fmt)
    }
}
impl UploadFileRequest {
    /// Finishes the upload of the file and returns information about the uploaded file.
    ///
    /// # Errors
    /// This function returns a [`B2Error`] in case something goes wrong. Besides the standard
    /// errors, this function can fail with [`is_cap_exceeded`], [`is_invalid_sha1`].
    ///
    ///  [`B2Error`]: ../authorize/enum.B2Error.html
    ///  [`is_cap_exceeded`]: ../../enum.B2Error.html#method.is_cap_exceeded
    ///  [`is_invalid_sha1`]: ../../enum.B2Error.html#method.is_invalid_sha1
    pub fn finish<InfoType>(self) -> Result<MoreFileInfo<InfoType>, B2Error>
        where for<'de> InfoType: Deserialize<'de>
    {
        let resp = self.request.send()?;
        if resp.status != hyper::status::StatusCode::Ok {
            Err(B2Error::from_response(resp))
        } else {
            Ok(serde_json::from_reader(resp)?)
        }
    }
}
/// Contains an ongoing upload to the backblaze b2 api. This struct is created by the
/// [create_upload_file_request_sha1_at_end][1] method.
///
///  [1]: struct.UploadAuthorization.html#method.create_upload_file_request_sha1_at_end
pub struct UploadFileRequestSha1End {
    request: Request<Streaming>
}
impl Write for UploadFileRequestSha1End {
    fn write(&mut self, msg: &[u8]) -> ::std::io::Result<usize> {
        self.request.write(msg)
    }
    fn flush(&mut self) -> ::std::io::Result<()> {
        self.request.flush()
    }
    fn write_all(&mut self, buf: &[u8]) -> Result<(), ::std::io::Error> {
        self.request.write_all(buf)
    }
    fn write_fmt(&mut self, fmt: ::core::fmt::Arguments) -> Result<(), ::std::io::Error> {
        self.request.write_fmt(fmt)
    }
}
impl UploadFileRequestSha1End {
    /// Finishes the upload of the file and returns information about the uploaded file. The `sha1`
    /// argument must be the ascii encoding of the sha1 of the file.
    ///
    /// The sha1 should be 40 bytes long, but this is not checked at runtime.
    ///
    /// # Errors
    /// This function returns a [`B2Error`] in case something goes wrong. Besides the standard
    /// errors, this function can fail with [`is_cap_exceeded`], [`is_invalid_sha1`].
    ///
    ///  [`B2Error`]: ../authorize/enum.B2Error.html
    ///  [`is_cap_exceeded`]: ../../enum.B2Error.html#method.is_cap_exceeded
    ///  [`is_invalid_sha1`]: ../../enum.B2Error.html#method.is_invalid_sha1
    pub fn finish<InfoType>(mut self, sha1: &str) -> Result<MoreFileInfo<InfoType>, B2Error>
        where for<'de> InfoType: Deserialize<'de>
    {
        self.request.write_all(sha1.as_bytes())?;
        let resp = self.request.send()?;
        if resp.status != hyper::status::StatusCode::Ok {
            Err(B2Error::from_response(resp))
        } else {
            Ok(serde_json::from_reader(resp)?)
        }
    }
}

<commit_msg>add note about non-40 byte sha1<commit_after>//! This module defines various methods and structs for interacting with uplaods on backblaze.
//!
//! The primary struct in this module is [UploadAuthorization][1], which has various methods for
//! uploading files to backblaze b2. This struct is usually obtained from a [B2Authorization][2]
//! using the method [get_upload_url][3].
//!
//!  [1]: struct.UploadAuthorization.html
//!  [2]: ../authorize/struct.B2Authorization.html
//!  [3]: ../authorize/struct.B2Authorization.html#method.get_upload_url

use std::io::{Write, Read, copy};

use hyper::{self, Client, Url};
use hyper::client::Body;
use hyper::client::request::Request;
use hyper::header::{Headers,ContentLength,ContentType};
use hyper::mime::Mime;
use hyper::method::Method;
use hyper::net::{Streaming, NetworkConnector, NetworkStream};

use serde::Deserialize;
use serde_json;

use B2Error;
use B2AuthHeader;
use raw::authorize::B2Authorization;
use raw::files::MoreFileInfo;
/// Contains the information needed to authorize an upload to b2. This struct is usually obtained
/// from a [B2Authorization][1] using the method [get_upload_url][2].
///
/// The b2 website specifies that you may not upload to the same url in parallel.
///
///  [1]: ../authorize/struct.B2Authorization.html
///  [2]: ../authorize/struct.B2Authorization.html#method.get_upload_url
#[derive(Deserialize,Serialize,Clone,Debug)]
#[serde(rename_all = "camelCase")]
pub struct UploadAuthorization {
    pub bucket_id: String,
    pub upload_url: String,
    pub authorization_token: String
}
impl UploadAuthorization {
    /// Returns a hyper header that authorizes an upload request.
    pub fn auth_header(&self) -> B2AuthHeader {
        B2AuthHeader(self.authorization_token.clone())
    }
}

/// Methods related to the [upload module][1].
///
///  [1]: ../upload/index.html
impl<'a> B2Authorization<'a> {
    /// Performs a [b2_get_upload_url][1] api call and returns the upload url wrapped in an
    /// [`UploadAuthorization`].
    ///
    /// # Errors
    /// This function returns a [`B2Error`] in case something goes wrong. Besides the standard
    /// errors, this function can fail with [`is_bucket_not_found`].
    ///
    ///  [1]: https://www.backblaze.com/b2/docs/b2_get_upload_url.html
    ///  [`B2Error`]: ../authorize/enum.B2Error.html
    ///  [`is_bucket_not_found`]: ../../enum.B2Error.html#method.is_bucket_not_found
    ///  [`UploadAuthorization`]: struct.UploadAuthorization.html
    pub fn get_upload_url(&self, bucket_id: &str, client: &Client)
        -> Result<UploadAuthorization,B2Error>
    {
        let url_string: String = format!("{}/b2api/v1/b2_get_upload_url", self.api_url);
        let url: &str = &url_string;

        #[derive(Serialize)]
        #[serde(rename_all = "camelCase")]
        struct Request<'a> {
            bucket_id: &'a str
        }
        let request = Request {
            bucket_id: bucket_id
        };
        let body: String = serde_json::to_string(&request)?;

        let resp = client.post(url)
            .body(Body::BufBody(body.as_bytes(), body.len()))
            .header(self.auth_header())
            .send()?;
        if resp.status != hyper::status::StatusCode::Ok {
            Err(B2Error::from_response(resp))
        } else {
            Ok(serde_json::from_reader(resp)?)
        }
    }
}
impl UploadAuthorization {
    /// Equivalent to calling [create_upload_file_request][1], writing everything in the Read to
    /// the Writer and calling finish.
    ///
    ///  [1]: struct.UploadAuthorization.html#method.create_upload_file_request
    pub fn upload_file<InfoType, R: Read, C, S>(&self, file: &mut R, file_name: String, content_type: Option<Mime>,
                                 content_length: u64, content_sha1: String, connector: &C)
        -> Result<MoreFileInfo<InfoType>, B2Error>
        where for<'de> InfoType: Deserialize<'de>, R: Sized, C: NetworkConnector<Stream=S>,
              S: Into<Box<NetworkStream + Send>>
    {
        let mut ufr = self.create_upload_file_request(
            file_name, content_type, content_length, content_sha1, connector)?;
        copy(file, &mut ufr)?;
        ufr.finish()
    }
    /// Starts a request to upload a file to backblaze b2. This function returns an
    /// [UploadFileRequest][1], which implements [Write][2]. When writing to this object, the
    /// data is sent to backblaze b2. This method of uploading can be used to
    /// implement things such as rate limiting of the request.
    ///
    /// After the file has been sent, you need to call the [finish method][3] on the
    /// [UploadFileRequest][1], in order to close the connection.
    ///
    /// The [upload_file method][4] can be used to upload any Reader easily. The backblaze api
    /// supports not specifying the sha1 checksum. This is not recommended, but if you wish to do
    /// this, simply pass the string `do_not_verify` as the sha1 checksum.
    ///
    /// The function [create_upload_file_request_sha1_at_end][5] might be of interest. This
    /// function behaves identically to this function, except the sha1 is passed when calling
    /// finish instead of before initiating the request.
    ///
    /// Read the [backblaze api documentation][6] for more information.
    ///
    /// # Errors
    /// This function returns a [`B2Error`] in case something goes wrong. Besides the standard
    /// errors, this function can fail with [`is_invalid_file_name`] and [`is_cap_exceeded`].
    ///
    ///  [1]: struct.UploadFileRequest.html
    ///  [2]: https://doc.rust-lang.org/stable/std/io/trait.Write.html
    ///  [3]: struct.UploadFileRequest.html#method.finish
    ///  [4]: struct.UploadAuthorization.html#method.upload_file
    ///  [5]: struct.UploadFileRequest.html#method.create_upload_file_request_sha1_at_end
    ///  [6]: https://www.backblaze.com/b2/docs/uploading.html
    ///  [`B2Error`]: ../authorize/enum.B2Error.html
    ///  [`is_invalid_file_name`]: ../../enum.B2Error.html#method.is_invalid_file_name
    ///  [`is_cap_exceeded`]: ../../enum.B2Error.html#method.is_cap_exceeded
    pub fn create_upload_file_request<C,S>(&self, file_name: String,
                                           content_type: Option<Mime>,
                                           content_length: u64, content_sha1: String,
                                           connector: &C)
        -> Result<UploadFileRequest, B2Error>
        where C: NetworkConnector<Stream=S>, S: Into<Box<NetworkStream + Send>>
    {
        let url: Url = Url::parse(&self.upload_url)?;
        let mut request = Request::with_connector(Method::Post, url, connector)?;
        {
            let headers: &mut Headers = request.headers_mut();
            headers.set(self.auth_header());
            headers.set(XBzFileName(file_name));
            headers.set(XBzContentSha1(content_sha1));
            headers.set(ContentLength(content_length));
            headers.set(ContentType(match content_type {
                Some(v) => v,
                None => "b2/x-auto".parse().unwrap()
            }));
        }
        Ok(UploadFileRequest { request: request.start()? })
    }
    /// Starts a request to upload a file to backblaze b2. This function returns an
    /// [UploadFileRequestSha1End][1], which implements [Write][2]. When writing to this object,
    /// the data is sent to backblaze b2. This method of uploading can be used to implement things
    /// such as rate limiting of the request.
    ///
    /// The value of the `content_length` parameter must be exactly the amount of bytes you are
    /// going to write, not including the 40 byte sha1 appended by the [finish method][3].
    ///
    /// After the file has been sent, you need to call the [finish method][3] on the
    /// [UploadFileRequestSha1End][1], in order to close the connection.
    ///
    /// Read the [backblaze api documentation][4] for more information.
    ///
    /// # Errors
    /// This function returns a [`B2Error`] in case something goes wrong. Besides the standard
    /// errors, this function can fail with [`is_invalid_file_name`] and [`is_cap_exceeded`].
    ///
    ///  [1]: struct.UploadFileRequestSha1End.html
    ///  [2]: https://doc.rust-lang.org/stable/std/io/trait.Write.html
    ///  [3]: struct.UploadFileRequestSha1End.html#method.finish
    ///  [4]: https://www.backblaze.com/b2/docs/uploading.html
    ///  [`B2Error`]: ../authorize/enum.B2Error.html
    ///  [`is_invalid_file_name`]: ../../enum.B2Error.html#method.is_invalid_file_name
    ///  [`is_cap_exceeded`]: ../../enum.B2Error.html#method.is_cap_exceeded
    pub fn create_upload_file_request_sha1_at_end<C,S>(&self, file_name: String,
                                                       content_type: Option<Mime>,
                                                       content_length: u64,
                                                       connector: &C)
        -> Result<UploadFileRequestSha1End, B2Error>
        where C: NetworkConnector<Stream=S>, S: Into<Box<NetworkStream + Send>>
    {
        let url: Url = Url::parse(&self.upload_url)?;
        let mut request = Request::with_connector(Method::Post, url, connector)?;
        {
            let headers: &mut Headers = request.headers_mut();
            headers.set(self.auth_header());
            headers.set(XBzFileName(file_name));
            headers.set(XBzContentSha1("hex_digits_at_end".to_owned()));
            headers.set(ContentLength(content_length + 40));
            headers.set(ContentType(match content_type {
                Some(v) => v,
                None => "b2/x-auto".parse().unwrap()
            }));
        }
        Ok(UploadFileRequestSha1End { request: request.start()? })
    }
}
header! { (XBzFileName, "X-Bz-File-Name") => [String] }
header! { (XBzContentSha1, "X-Bz-Content-Sha1") => [String] }

/// Contains an ongoing upload to the backblaze b2 api. This struct is created by the
/// [`create_upload_file_request`] method.
///
///  [`create_upload_file_request`]: struct.UploadAuthorization.html#method.create_upload_file_request
pub struct UploadFileRequest {
    request: Request<Streaming>
}
impl Write for UploadFileRequest {
    fn write(&mut self, msg: &[u8]) -> ::std::io::Result<usize> {
        self.request.write(msg)
    }
    fn flush(&mut self) -> ::std::io::Result<()> {
        self.request.flush()
    }
    fn write_all(&mut self, buf: &[u8]) -> Result<(), ::std::io::Error> {
        self.request.write_all(buf)
    }
    fn write_fmt(&mut self, fmt: ::core::fmt::Arguments) -> Result<(), ::std::io::Error> {
        self.request.write_fmt(fmt)
    }
}
impl UploadFileRequest {
    /// Finishes the upload of the file and returns information about the uploaded file.
    ///
    /// # Errors
    /// This function returns a [`B2Error`] in case something goes wrong. Besides the standard
    /// errors, this function can fail with [`is_cap_exceeded`], [`is_invalid_sha1`].
    ///
    ///  [`B2Error`]: ../authorize/enum.B2Error.html
    ///  [`is_cap_exceeded`]: ../../enum.B2Error.html#method.is_cap_exceeded
    ///  [`is_invalid_sha1`]: ../../enum.B2Error.html#method.is_invalid_sha1
    pub fn finish<InfoType>(self) -> Result<MoreFileInfo<InfoType>, B2Error>
        where for<'de> InfoType: Deserialize<'de>
    {
        let resp = self.request.send()?;
        if resp.status != hyper::status::StatusCode::Ok {
            Err(B2Error::from_response(resp))
        } else {
            Ok(serde_json::from_reader(resp)?)
        }
    }
}
/// Contains an ongoing upload to the backblaze b2 api. This struct is created by the
/// [create_upload_file_request_sha1_at_end][1] method.
///
///  [1]: struct.UploadAuthorization.html#method.create_upload_file_request_sha1_at_end
pub struct UploadFileRequestSha1End {
    request: Request<Streaming>
}
impl Write for UploadFileRequestSha1End {
    fn write(&mut self, msg: &[u8]) -> ::std::io::Result<usize> {
        self.request.write(msg)
    }
    fn flush(&mut self) -> ::std::io::Result<()> {
        self.request.flush()
    }
    fn write_all(&mut self, buf: &[u8]) -> Result<(), ::std::io::Error> {
        self.request.write_all(buf)
    }
    fn write_fmt(&mut self, fmt: ::core::fmt::Arguments) -> Result<(), ::std::io::Error> {
        self.request.write_fmt(fmt)
    }
}
impl UploadFileRequestSha1End {
    /// Finishes the upload of the file and returns information about the uploaded file. The `sha1`
    /// argument must be the ascii encoding of the sha1 of the file.
    ///
    /// The sha1 should be 40 bytes long, but this is not checked at runtime. If it is not 40 bytes
    /// the b2 server will complain with some kind of bad request error.
    ///
    /// # Errors
    /// This function returns a [`B2Error`] in case something goes wrong. Besides the standard
    /// errors, this function can fail with [`is_cap_exceeded`], [`is_invalid_sha1`].
    ///
    ///  [`B2Error`]: ../authorize/enum.B2Error.html
    ///  [`is_cap_exceeded`]: ../../enum.B2Error.html#method.is_cap_exceeded
    ///  [`is_invalid_sha1`]: ../../enum.B2Error.html#method.is_invalid_sha1
    pub fn finish<InfoType>(mut self, sha1: &str) -> Result<MoreFileInfo<InfoType>, B2Error>
        where for<'de> InfoType: Deserialize<'de>
    {
        self.request.write_all(sha1.as_bytes())?;
        let resp = self.request.send()?;
        if resp.status != hyper::status::StatusCode::Ok {
            Err(B2Error::from_response(resp))
        } else {
            Ok(serde_json::from_reader(resp)?)
        }
    }
}

<|endoftext|>


<commit_before>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the
// terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not
// distributed with this file, You can
// obtain one at
// http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible
// With Secondary Licenses", as defined by
// the Mozilla Public License, v. 2.0.

//! ReadBuffer crate.

use super::message::Message;

#[derive(Clone)]
pub struct ReadBuffer {
    /// Current message
    c_msg: Message,
    /// Current bytes remaining for next read
    c_remaining: u16,
    /// Current buffer
    c_buffer: Vec<u8>,
    /// Queue of messages created during last read
    queue: Vec<Message>
}

impl ReadBuffer {

    /// Creates a new ReadBuffer
    pub fn new() -> ReadBuffer {
        ReadBuffer {
            c_msg: Message::new(),
            c_remaining: 2u16,
            c_buffer: Vec::<u8>::with_capacity(2),
            queue: Vec::<Message>::new()
        }
    }

    /// Returns the number of remaining u8 needed to fill the current buffer
    pub fn remaining(&self) -> u16 {
        self.c_remaining.clone()
    }

    /// Pushes elem onto buffer
    pub fn push(&mut self, elem: u8) {
        self.c_buffer.push(elem);
        self.c_remaining -= 1;
    }

    /// Sets the buffer's capacity to size
    pub fn set_capacity(&mut self, size: u16) {
        self.c_remaining = size;
        self.c_buffer = Vec::<u8>::with_capacity(size as usize);
    }

    /// Calculates the payload len from the current buffer
    pub fn calc_payload_len(&mut self) {
        let mut len = 0u16;
        len = len | self.c_buffer[0] as u16;
        len = (len << 8) | self.c_buffer[1] as u16;
        self.c_msg.len = len;
    }

    /// Returns the total length of the expected payload
    pub fn payload_len(&self) -> u16 {
        self.c_msg.len.clone()
    }

    /// Pushes the current message into the buffer's message queue and resets
    /// to default state
    pub fn reset(&mut self) {
        self.queue.push(self.c_msg.clone());
        self.c_msg = Message::new();
        self.c_remaining = 2u16;
        self.c_buffer = Vec::<u8>::with_capacity(2);
    }

    /// Returns the length of internal message queue
    pub fn queue_len(&self) -> usize {
        self.queue.len()
    }

    /// Returns a mutable reference to the internal message queue
    pub fn queue_as_mut(&mut self) -> &mut Vec<Message> {
        &mut self.queue
    }

    /// Drains the queue returning a Vec<Vec<u8>> representing each payload
    pub fn drain_queue(&mut self) -> Vec<Vec<u8>> {
        let mut buffer = Vec::<Vec<u8>>::with_capacity(self.queue.len());
        let mut pos = self.queue.len() - 1;
        while let Some(msg) = self.queue.pop() {
            buffer.insert(pos, msg.payload);
            pos -= 1;
        }
        buffer
    }
}
<commit_msg>Fixed arithmatic overflow<commit_after>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the
// terms of the Mozilla Public License, v.
// 2.0. If a copy of the MPL was not
// distributed with this file, You can
// obtain one at
// http://mozilla.org/MPL/2.0/.
//
// This Source Code Form is "Incompatible
// With Secondary Licenses", as defined by
// the Mozilla Public License, v. 2.0.

//! ReadBuffer crate.

use super::message::Message;

#[derive(Clone)]
pub struct ReadBuffer {
    /// Current message
    c_msg: Message,
    /// Current bytes remaining for next read
    c_remaining: u16,
    /// Current buffer
    c_buffer: Vec<u8>,
    /// Queue of messages created during last read
    queue: Vec<Message>
}

impl ReadBuffer {

    /// Creates a new ReadBuffer
    pub fn new() -> ReadBuffer {
        ReadBuffer {
            c_msg: Message::new(),
            c_remaining: 2u16,
            c_buffer: Vec::<u8>::with_capacity(2),
            queue: Vec::<Message>::new()
        }
    }

    /// Returns the number of remaining u8 needed to fill the current buffer
    pub fn remaining(&self) -> u16 {
        self.c_remaining.clone()
    }

    /// Pushes elem onto buffer
    pub fn push(&mut self, elem: u8) {
        self.c_buffer.push(elem);
        self.c_remaining -= 1;
    }

    /// Sets the buffer's capacity to size
    pub fn set_capacity(&mut self, size: u16) {
        self.c_remaining = size;
        self.c_buffer = Vec::<u8>::with_capacity(size as usize);
    }

    /// Calculates the payload len from the current buffer
    pub fn calc_payload_len(&mut self) {
        let mut len = 0u16;
        len = len | self.c_buffer[0] as u16;
        len = (len << 8) | self.c_buffer[1] as u16;
        self.c_msg.len = len;
    }

    /// Returns the total length of the expected payload
    pub fn payload_len(&self) -> u16 {
        self.c_msg.len.clone()
    }

    /// Pushes the current message into the buffer's message queue and resets
    /// to default state
    pub fn reset(&mut self) {
        self.queue.push(self.c_msg.clone());
        self.c_msg = Message::new();
        self.c_remaining = 2u16;
        self.c_buffer = Vec::<u8>::with_capacity(2);
    }

    /// Returns the length of internal message queue
    pub fn queue_len(&self) -> usize {
        self.queue.len()
    }

    /// Returns a mutable reference to the internal message queue
    pub fn queue_as_mut(&mut self) -> &mut Vec<Message> {
        &mut self.queue
    }

    /// Drains the queue returning a Vec<Vec<u8>> representing each payload
    pub fn drain_queue(&mut self) -> Vec<Vec<u8>> {
        let mut buffer = Vec::<Vec<u8>>::with_capacity(self.queue.len());
        unsafe {
            buffer.set_len(self.queue.len());
        }
        let mut pos = self.queue.len() - 1;
        while let Some(msg) = self.queue.pop() {
            buffer.insert(pos, msg.payload);
            pos -= 1;
        }
        buffer
    }
}
<|endoftext|>