<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()?;

        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)
    }

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

        // 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)]
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,
        );
    }
}
<commit_msg>downloads_counter: add persist_*_with_conn to aid testing<commit_after>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)]
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,
        );
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(arbitrary_self_types)]

use std::rc::Rc;

trait Foo {
    fn foo(self: Rc<Self>) -> usize;
}

trait Bar {
    fn foo(self: Rc<Self>) -> usize where Self: Sized;
    fn bar(self: Box<Self>) -> usize;
}

impl Foo for usize {
    fn foo(self: Rc<Self>) -> usize {
        *self
    }
}

impl Bar for usize {
    fn foo(self: Rc<Self>) -> usize {
        *self
    }

    fn bar(self: Box<Self>) -> usize {
        *self
    }
}

fn make_foo() {
    let x = Box::new(5usize) as Box<Foo>;
    //~^ ERROR E0038
    //~| NOTE method `foo` has a non-standard `self` type
    //~| NOTE the trait `Foo` cannot be made into an object
    //~| ERROR E0038
    //~| NOTE method `foo` has a non-standard `self` type
    //~| NOTE the trait `Foo` cannot be made into an object
    //~| NOTE required because of the requirements on the impl of
    //~|      `std::ops::CoerceUnsized<std::boxed::Box<Foo>>` for `std::boxed::Box<usize>`
}

fn make_bar() {
    let x = Box::new(5usize) as Box<Bar>;
    x.bar();
}

fn main() {}
<commit_msg>fix error message in arbitrary-self-types-not-object-safe test put the error message on one line so the test suite does not think it is two errors use a substring of the error message so it fits in 100 chars for tidy<commit_after>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(arbitrary_self_types)]

use std::rc::Rc;

trait Foo {
    fn foo(self: Rc<Self>) -> usize;
}

trait Bar {
    fn foo(self: Rc<Self>) -> usize where Self: Sized;
    fn bar(self: Box<Self>) -> usize;
}

impl Foo for usize {
    fn foo(self: Rc<Self>) -> usize {
        *self
    }
}

impl Bar for usize {
    fn foo(self: Rc<Self>) -> usize {
        *self
    }

    fn bar(self: Box<Self>) -> usize {
        *self
    }
}

fn make_foo() {
    let x = Box::new(5usize) as Box<Foo>;
    //~^ ERROR E0038
    //~| NOTE method `foo` has a non-standard `self` type
    //~| NOTE the trait `Foo` cannot be made into an object
    //~| ERROR E0038
    //~| NOTE method `foo` has a non-standard `self` type
    //~| NOTE the trait `Foo` cannot be made into an object
    //~| NOTE requirements on the impl of `std::ops::CoerceUnsized<std::boxed::Box<Foo>>`
}

fn make_bar() {
    let x = Box::new(5usize) as Box<Bar>;
    x.bar();
}

fn main() {}
<|endoftext|>


<commit_before>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A wrapper around any Reader to treat it as an RNG.

use old_io::Reader;
use rand::Rng;
use result::Result::{Ok, Err};
use slice::SliceExt;

/// An RNG that reads random bytes straight from a `Reader`. This will
/// work best with an infinite reader, but this is not required.
///
/// # Panics
///
/// It will panic if it there is insufficient data to fulfill a request.
///
/// # Example
///
/// ```rust
/// use std::rand::{reader, Rng};
/// use std::old_io::MemReader;
///
/// let mut rng = reader::ReaderRng::new(MemReader::new(vec!(1,2,3,4,5,6,7,8)));
/// println!("{:x}", rng.gen::<uint>());
/// ```
pub struct ReaderRng<R> {
    reader: R
}

impl<R: Reader> ReaderRng<R> {
    /// Create a new `ReaderRng` from a `Reader`.
    pub fn new(r: R) -> ReaderRng<R> {
        ReaderRng {
            reader: r
        }
    }
}

impl<R: Reader> Rng for ReaderRng<R> {
    fn next_u32(&mut self) -> u32 {
        // This is designed for speed: reading a LE integer on a LE
        // platform just involves blitting the bytes into the memory
        // of the u32, similarly for BE on BE; avoiding byteswapping.
        if cfg!(target_endian="little") {
            self.reader.read_le_u32().unwrap()
        } else {
            self.reader.read_be_u32().unwrap()
        }
    }
    fn next_u64(&mut self) -> u64 {
        // see above for explanation.
        if cfg!(target_endian="little") {
            self.reader.read_le_u64().unwrap()
        } else {
            self.reader.read_be_u64().unwrap()
        }
    }
    fn fill_bytes(&mut self, v: &mut [u8]) {
        if v.len() == 0 { return }
        match self.reader.read_at_least(v.len(), v) {
            Ok(_) => {}
            Err(e) => panic!("ReaderRng.fill_bytes error: {:?}", e)
        }
    }
}

#[cfg(test)]
mod test {
    use prelude::v1::*;

    use super::ReaderRng;
    use old_io::MemReader;
    use num::Int;
    use rand::Rng;

    #[test]
    fn test_reader_rng_u64() {
        // transmute from the target to avoid endianness concerns.
        let v = vec![0u8, 0, 0, 0, 0, 0, 0, 1,
                     0  , 0, 0, 0, 0, 0, 0, 2,
                     0,   0, 0, 0, 0, 0, 0, 3];
        let mut rng = ReaderRng::new(MemReader::new(v));

        assert_eq!(rng.next_u64(), 1_u64.to_be());
        assert_eq!(rng.next_u64(), 2_u64.to_be());
        assert_eq!(rng.next_u64(), 3_u64.to_be());
    }
    #[test]
    fn test_reader_rng_u32() {
        let v = vec![0u8, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3];
        let mut rng = ReaderRng::new(MemReader::new(v));

        assert_eq!(rng.next_u32(), 1_u32.to_be());
        assert_eq!(rng.next_u32(), 2_u32.to_be());
        assert_eq!(rng.next_u32(), 3_u32.to_be());
    }
    #[test]
    fn test_reader_rng_fill_bytes() {
        let v = [1u8, 2, 3, 4, 5, 6, 7, 8];
        let mut w = [0u8; 8];

        let mut rng = ReaderRng::new(MemReader::new(v.as_slice().to_vec()));
        rng.fill_bytes(&mut w);

        assert!(v == w);
    }

    #[test]
    #[should_fail]
    fn test_reader_rng_insufficient_bytes() {
        let mut rng = ReaderRng::new(MemReader::new(vec!()));
        let mut v = [0u8; 3];
        rng.fill_bytes(&mut v);
    }
}
<commit_msg>Auto merge of #21680 - japaric:slice, r=alexcrichton<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A wrapper around any Reader to treat it as an RNG.

use old_io::Reader;
use rand::Rng;
use result::Result::{Ok, Err};
use slice::SliceExt;

/// An RNG that reads random bytes straight from a `Reader`. This will
/// work best with an infinite reader, but this is not required.
///
/// # Panics
///
/// It will panic if it there is insufficient data to fulfill a request.
///
/// # Example
///
/// ```rust
/// use std::rand::{reader, Rng};
/// use std::old_io::MemReader;
///
/// let mut rng = reader::ReaderRng::new(MemReader::new(vec!(1,2,3,4,5,6,7,8)));
/// println!("{:x}", rng.gen::<uint>());
/// ```
pub struct ReaderRng<R> {
    reader: R
}

impl<R: Reader> ReaderRng<R> {
    /// Create a new `ReaderRng` from a `Reader`.
    pub fn new(r: R) -> ReaderRng<R> {
        ReaderRng {
            reader: r
        }
    }
}

impl<R: Reader> Rng for ReaderRng<R> {
    fn next_u32(&mut self) -> u32 {
        // This is designed for speed: reading a LE integer on a LE
        // platform just involves blitting the bytes into the memory
        // of the u32, similarly for BE on BE; avoiding byteswapping.
        if cfg!(target_endian="little") {
            self.reader.read_le_u32().unwrap()
        } else {
            self.reader.read_be_u32().unwrap()
        }
    }
    fn next_u64(&mut self) -> u64 {
        // see above for explanation.
        if cfg!(target_endian="little") {
            self.reader.read_le_u64().unwrap()
        } else {
            self.reader.read_be_u64().unwrap()
        }
    }
    fn fill_bytes(&mut self, v: &mut [u8]) {
        if v.len() == 0 { return }
        match self.reader.read_at_least(v.len(), v) {
            Ok(_) => {}
            Err(e) => panic!("ReaderRng.fill_bytes error: {:?}", e)
        }
    }
}

#[cfg(test)]
mod test {
    use prelude::v1::*;

    use super::ReaderRng;
    use old_io::MemReader;
    use num::Int;
    use rand::Rng;

    #[test]
    fn test_reader_rng_u64() {
        // transmute from the target to avoid endianness concerns.
        let v = vec![0u8, 0, 0, 0, 0, 0, 0, 1,
                     0  , 0, 0, 0, 0, 0, 0, 2,
                     0,   0, 0, 0, 0, 0, 0, 3];
        let mut rng = ReaderRng::new(MemReader::new(v));

        assert_eq!(rng.next_u64(), 1_u64.to_be());
        assert_eq!(rng.next_u64(), 2_u64.to_be());
        assert_eq!(rng.next_u64(), 3_u64.to_be());
    }
    #[test]
    fn test_reader_rng_u32() {
        let v = vec![0u8, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3];
        let mut rng = ReaderRng::new(MemReader::new(v));

        assert_eq!(rng.next_u32(), 1_u32.to_be());
        assert_eq!(rng.next_u32(), 2_u32.to_be());
        assert_eq!(rng.next_u32(), 3_u32.to_be());
    }
    #[test]
    fn test_reader_rng_fill_bytes() {
        let v = [1u8, 2, 3, 4, 5, 6, 7, 8];
        let mut w = [0u8; 8];

        let mut rng = ReaderRng::new(MemReader::new(v.to_vec()));
        rng.fill_bytes(&mut w);

        assert!(v == w);
    }

    #[test]
    #[should_fail]
    fn test_reader_rng_insufficient_bytes() {
        let mut rng = ReaderRng::new(MemReader::new(vec!()));
        let mut v = [0u8; 3];
        rng.fill_bytes(&mut v);
    }
}
<|endoftext|>


<commit_before>use base64;
use std::borrow::Cow;
use std::io::Write;
use xml_rs::name::Name;
use xml_rs::namespace::Namespace;
use xml_rs::writer::{EmitterConfig, Error as XmlWriterError, EventWriter, XmlEvent};

use events::{Event, Writer};
use Error;

static XML_PROLOGUE: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
"#;

impl From<XmlWriterError> for Error {
    fn from(err: XmlWriterError) -> Error {
        match err {
            XmlWriterError::Io(err) => Error::Io(err),
            _ => Error::InvalidData,
        }
    }
}

#[derive(PartialEq)]
enum Element {
    Dictionary,
    Array,
}

pub struct XmlWriter<W: Write> {
    xml_writer: EventWriter<W>,
    stack: Vec<Element>,
    expecting_key: bool,
    written_prologue: bool,
    // Not very nice
    empty_namespace: Namespace,
}

impl<W: Write> XmlWriter<W> {
    pub fn new(writer: W) -> XmlWriter<W> {
        let config = EmitterConfig::new()
            .line_separator("\n")
            .indent_string("\t")
            .perform_indent(true)
            .write_document_declaration(false)
            .normalize_empty_elements(true)
            .cdata_to_characters(true)
            .keep_element_names_stack(false)
            .autopad_comments(true);

        XmlWriter {
            xml_writer: EventWriter::new_with_config(writer, config),
            stack: Vec::new(),
            expecting_key: false,
            written_prologue: false,
            empty_namespace: Namespace::empty(),
        }
    }

    fn write_element_and_value(&mut self, name: &str, value: &str) -> Result<(), Error> {
        self.start_element(name)?;
        self.write_value(value)?;
        self.end_element(name)?;
        Ok(())
    }

    fn start_element(&mut self, name: &str) -> Result<(), Error> {
        self.xml_writer.write(XmlEvent::StartElement {
            name: Name::local(name),
            attributes: Cow::Borrowed(&[]),
            namespace: Cow::Borrowed(&self.empty_namespace),
        })?;
        Ok(())
    }

    fn end_element(&mut self, name: &str) -> Result<(), Error> {
        self.xml_writer.write(XmlEvent::EndElement {
            name: Some(Name::local(name)),
        })?;
        Ok(())
    }

    fn write_value(&mut self, value: &str) -> Result<(), Error> {
        self.xml_writer.write(XmlEvent::Characters(value))?;
        Ok(())
    }

    pub fn write(&mut self, event: &Event) -> Result<(), Error> {
        <Self as Writer>::write(self, event)
    }
}

impl<W: Write> Writer for XmlWriter<W> {
    fn write(&mut self, event: &Event) -> Result<(), Error> {
        if !self.written_prologue {
            self.xml_writer
                .inner_mut()
                .write_all(XML_PROLOGUE.as_bytes())?;

            self.written_prologue = true;
        }

        if self.expecting_key {
            match *event {
                Event::EndDictionary => match self.stack.pop() {
                    Some(Element::Dictionary) => {
                        self.end_element("dict")?;
                        self.expecting_key = self.stack.last() == Some(&Element::Dictionary);
                    }
                    _ => return Err(Error::InvalidData),
                },
                Event::StringValue(ref value) => {
                    self.write_element_and_value("key", &*value)?;
                    self.expecting_key = false;
                }
                _ => return Err(Error::InvalidData),
            }
        } else {
            match *event {
                Event::StartArray(_) => {
                    self.start_element("array")?;
                    self.stack.push(Element::Array);
                }
                Event::EndArray => match self.stack.pop() {
                    Some(Element::Array) => self.end_element("array")?,
                    _ => return Err(Error::InvalidData),
                },

                Event::StartDictionary(_) => {
                    self.start_element("dict")?;
                    self.stack.push(Element::Dictionary);
                }
                Event::EndDictionary => return Err(Error::InvalidData),

                Event::BooleanValue(true) => {
                    self.start_element("true")?;
                    self.end_element("true")?;
                }
                Event::BooleanValue(false) => {
                    self.start_element("false")?;
                    self.end_element("false")?;
                }
                Event::DataValue(ref value) => {
                    let base64_data = base64::encode_config(&value, base64::MIME);
                    self.write_element_and_value("data", &base64_data)?;
                }
                Event::DateValue(ref value) => {
                    self.write_element_and_value("date", &value.to_rfc3339())?
                }
                Event::IntegerValue(ref value) => {
                    self.write_element_and_value("integer", &value.to_string())?
                }
                Event::RealValue(ref value) => {
                    self.write_element_and_value("real", &value.to_string())?
                }
                Event::StringValue(ref value) => self.write_element_and_value("string", &*value)?,
            };

            self.expecting_key = self.stack.last() == Some(&Element::Dictionary);
        }

        // If there are no more open tags then write the </plist> element
        if self.stack.len() == 0 {
            // We didn't tell the xml_writer about the <plist> tag so we'll skip telling it
            // about the </plist> tag as well.
            self.xml_writer.inner_mut().write_all(b"\n</plist>")?;
        }

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use humantime::parse_rfc3339_weak;
    use std::io::Cursor;

    use super::*;
    use events::Event::*;

    #[test]
    fn streaming_parser() {
        let plist = &[
            StartDictionary(None),
            StringValue("Author".to_owned()),
            StringValue("William Shakespeare".to_owned()),
            StringValue("Lines".to_owned()),
            StartArray(None),
            StringValue("It is a tale told by an idiot,".to_owned()),
            StringValue("Full of sound and fury, signifying nothing.".to_owned()),
            EndArray,
            StringValue("Death".to_owned()),
            IntegerValue(1564),
            StringValue("Height".to_owned()),
            RealValue(1.60),
            StringValue("Data".to_owned()),
            DataValue(vec![0, 0, 0, 190, 0, 0, 0, 3, 0, 0, 0, 30, 0, 0, 0]),
            StringValue("Birthdate".to_owned()),
            DateValue(parse_rfc3339_weak("1981-05-16 11:32:06").unwrap().into()),
            StringValue("Comment".to_owned()),
            StringValue("2 < 3".to_owned()), // make sure characters are escaped
            EndDictionary,
        ];

        let mut cursor = Cursor::new(Vec::new());

        {
            let mut plist_w = XmlWriter::new(&mut cursor);

            for item in plist {
                plist_w.write(item).unwrap();
            }
        }

        let comparison = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
<plist version=\"1.0\">
<dict>
\t<key>Author</key>
\t<string>William Shakespeare</string>
\t<key>Lines</key>
\t<array>
\t\t<string>It is a tale told by an idiot,</string>
\t\t<string>Full of sound and fury, signifying nothing.</string>
\t</array>
\t<key>Death</key>
\t<integer>1564</integer>
\t<key>Height</key>
\t<real>1.6</real>
\t<key>Data</key>
\t<data>AAAAvgAAAAMAAAAeAAAA</data>
\t<key>Birthdate</key>
\t<date>1981-05-16T11:32:06Z</date>
\t<key>Comment</key>
\t<string>2 < 3</string>
</dict>
</plist>";

        let s = String::from_utf8(cursor.into_inner()).unwrap();

        assert_eq!(s, comparison);
    }
}
<commit_msg>Add XmlWriter.into_inner()<commit_after>use base64;
use std::borrow::Cow;
use std::io::Write;
use xml_rs::name::Name;
use xml_rs::namespace::Namespace;
use xml_rs::writer::{EmitterConfig, Error as XmlWriterError, EventWriter, XmlEvent};

use events::{Event, Writer};
use Error;

static XML_PROLOGUE: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
"#;

impl From<XmlWriterError> for Error {
    fn from(err: XmlWriterError) -> Error {
        match err {
            XmlWriterError::Io(err) => Error::Io(err),
            _ => Error::InvalidData,
        }
    }
}

#[derive(PartialEq)]
enum Element {
    Dictionary,
    Array,
}

pub struct XmlWriter<W: Write> {
    xml_writer: EventWriter<W>,
    stack: Vec<Element>,
    expecting_key: bool,
    written_prologue: bool,
    // Not very nice
    empty_namespace: Namespace,
}

impl<W: Write> XmlWriter<W> {
    pub fn new(writer: W) -> XmlWriter<W> {
        let config = EmitterConfig::new()
            .line_separator("\n")
            .indent_string("\t")
            .perform_indent(true)
            .write_document_declaration(false)
            .normalize_empty_elements(true)
            .cdata_to_characters(true)
            .keep_element_names_stack(false)
            .autopad_comments(true);

        XmlWriter {
            xml_writer: EventWriter::new_with_config(writer, config),
            stack: Vec::new(),
            expecting_key: false,
            written_prologue: false,
            empty_namespace: Namespace::empty(),
        }
    }

    fn write_element_and_value(&mut self, name: &str, value: &str) -> Result<(), Error> {
        self.start_element(name)?;
        self.write_value(value)?;
        self.end_element(name)?;
        Ok(())
    }

    fn start_element(&mut self, name: &str) -> Result<(), Error> {
        self.xml_writer.write(XmlEvent::StartElement {
            name: Name::local(name),
            attributes: Cow::Borrowed(&[]),
            namespace: Cow::Borrowed(&self.empty_namespace),
        })?;
        Ok(())
    }

    fn end_element(&mut self, name: &str) -> Result<(), Error> {
        self.xml_writer.write(XmlEvent::EndElement {
            name: Some(Name::local(name)),
        })?;
        Ok(())
    }

    fn write_value(&mut self, value: &str) -> Result<(), Error> {
        self.xml_writer.write(XmlEvent::Characters(value))?;
        Ok(())
    }

    pub fn write(&mut self, event: &Event) -> Result<(), Error> {
        <Self as Writer>::write(self, event)
    }

    pub fn into_inner(self) -> W {
        self.xml_writer.into_inner()
    }
}

impl<W: Write> Writer for XmlWriter<W> {
    fn write(&mut self, event: &Event) -> Result<(), Error> {
        if !self.written_prologue {
            self.xml_writer
                .inner_mut()
                .write_all(XML_PROLOGUE.as_bytes())?;

            self.written_prologue = true;
        }

        if self.expecting_key {
            match *event {
                Event::EndDictionary => match self.stack.pop() {
                    Some(Element::Dictionary) => {
                        self.end_element("dict")?;
                        self.expecting_key = self.stack.last() == Some(&Element::Dictionary);
                    }
                    _ => return Err(Error::InvalidData),
                },
                Event::StringValue(ref value) => {
                    self.write_element_and_value("key", &*value)?;
                    self.expecting_key = false;
                }
                _ => return Err(Error::InvalidData),
            }
        } else {
            match *event {
                Event::StartArray(_) => {
                    self.start_element("array")?;
                    self.stack.push(Element::Array);
                }
                Event::EndArray => match self.stack.pop() {
                    Some(Element::Array) => self.end_element("array")?,
                    _ => return Err(Error::InvalidData),
                },

                Event::StartDictionary(_) => {
                    self.start_element("dict")?;
                    self.stack.push(Element::Dictionary);
                }
                Event::EndDictionary => return Err(Error::InvalidData),

                Event::BooleanValue(true) => {
                    self.start_element("true")?;
                    self.end_element("true")?;
                }
                Event::BooleanValue(false) => {
                    self.start_element("false")?;
                    self.end_element("false")?;
                }
                Event::DataValue(ref value) => {
                    let base64_data = base64::encode_config(&value, base64::MIME);
                    self.write_element_and_value("data", &base64_data)?;
                }
                Event::DateValue(ref value) => {
                    self.write_element_and_value("date", &value.to_rfc3339())?
                }
                Event::IntegerValue(ref value) => {
                    self.write_element_and_value("integer", &value.to_string())?
                }
                Event::RealValue(ref value) => {
                    self.write_element_and_value("real", &value.to_string())?
                }
                Event::StringValue(ref value) => self.write_element_and_value("string", &*value)?,
            };

            self.expecting_key = self.stack.last() == Some(&Element::Dictionary);
        }

        // If there are no more open tags then write the </plist> element
        if self.stack.len() == 0 {
            // We didn't tell the xml_writer about the <plist> tag so we'll skip telling it
            // about the </plist> tag as well.
            self.xml_writer.inner_mut().write_all(b"\n</plist>")?;
        }

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use humantime::parse_rfc3339_weak;
    use std::io::Cursor;

    use super::*;
    use events::Event::*;

    #[test]
    fn streaming_parser() {
        let plist = &[
            StartDictionary(None),
            StringValue("Author".to_owned()),
            StringValue("William Shakespeare".to_owned()),
            StringValue("Lines".to_owned()),
            StartArray(None),
            StringValue("It is a tale told by an idiot,".to_owned()),
            StringValue("Full of sound and fury, signifying nothing.".to_owned()),
            EndArray,
            StringValue("Death".to_owned()),
            IntegerValue(1564),
            StringValue("Height".to_owned()),
            RealValue(1.60),
            StringValue("Data".to_owned()),
            DataValue(vec![0, 0, 0, 190, 0, 0, 0, 3, 0, 0, 0, 30, 0, 0, 0]),
            StringValue("Birthdate".to_owned()),
            DateValue(parse_rfc3339_weak("1981-05-16 11:32:06").unwrap().into()),
            StringValue("Comment".to_owned()),
            StringValue("2 < 3".to_owned()), // make sure characters are escaped
            EndDictionary,
        ];

        let mut cursor = Cursor::new(Vec::new());

        {
            let mut plist_w = XmlWriter::new(&mut cursor);

            for item in plist {
                plist_w.write(item).unwrap();
            }
        }

        let comparison = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
<plist version=\"1.0\">
<dict>
\t<key>Author</key>
\t<string>William Shakespeare</string>
\t<key>Lines</key>
\t<array>
\t\t<string>It is a tale told by an idiot,</string>
\t\t<string>Full of sound and fury, signifying nothing.</string>
\t</array>
\t<key>Death</key>
\t<integer>1564</integer>
\t<key>Height</key>
\t<real>1.6</real>
\t<key>Data</key>
\t<data>AAAAvgAAAAMAAAAeAAAA</data>
\t<key>Birthdate</key>
\t<date>1981-05-16T11:32:06Z</date>
\t<key>Comment</key>
\t<string>2 < 3</string>
</dict>
</plist>";

        let s = String::from_utf8(cursor.into_inner()).unwrap();

        assert_eq!(s, comparison);
    }
}
<|endoftext|>


<commit_before>extern mod std;

#[test]
fn test_tokenize() {
    assert tokenize( ~"(1 2 3)" ) == ~[~"(", ~"1", ~"2", ~"3", ~")"];
}

#[test]
fn test_tokenize_empty() {
    assert tokenize( ~"" ) == ~[];
}

#[test]
fn test_tokenize_no_spaces() {
    assert tokenize( ~"one" ) == ~[~"one"];
}

#[test]
fn test_pad_parentheses() {
    assert pad_parentheses( ~"(1 2 3)" ) == ~" ( 1 2 3 ) "
}

#[test]
fn test_pad_parentheses_empty() {
    assert pad_parentheses( ~"" ) == ~""
}

#[test]
fn test_pad_parentheses_one() {
    assert pad_parentheses( ~"(" ) == ~" ( ";
    assert pad_parentheses( ~")" ) == ~" ) ";
}

fn pad_parentheses( input:&str ) -> ~str {
    str::replace(str::replace(input, ")", " ) "), "(", " ( ")
}

fn tokenize( input:&str ) -> ~[~str] {
    str::words(pad_parentheses(input))
}

#[test]
fn test_that_atom_can_read_a_symbol() {
    match atom(~"hello") {
        Symbol(~"hello") => (),
            _ => fail
    }
}

#[test]
fn test_that_atom_can_read_an_int() {
    match atom(~"10") {
        Int(10) => (),
            _ => fail
    }
}

#[test]
fn test_that_atom_can_read_a_float() {
    match atom(~"10.1") {
        Float(10.1) => (),
            _ => fail
    }
}

fn atom( input:~str ) -> Expression {
    match int::from_str(input) {
        Some(number) => Int(number),
        None => match float::from_str(input) {
            Some(number) => Float(number),
            None => Symbol(input)
        }
    }
}

#[test]
fn test_that_parse_can_read_an_atom() {
    let atom = parse( ~[~"12"] );
    match atom {
        Int(12) => (),
            _ => fail
    }
}

#[test]
fn test_that_parse_can_read_a_list() {
    let list = parse( ~[ ~"(", ~"1", ~")" ] );
    match list {
        List([Int(1)]) => (),
            _ => fail ~"not a list"
    }
}

#[test]
fn test_that_parse_can_read_a_nested_list() {
    let list = parse( ~[ ~"(", ~"1", ~"(", ~"2", ~")", ~"3", ~")" ] );
    match list {
        List([Int(1), List([Int(2)]), Int(3)]) => (),
        _ => fail
    }
}

fn parse( tokens:~[~str] ) -> Expression {
    fn subexpression( tokens:~[~str] ) -> (Expression, ~[~str]) {
        let token = tokens.head();
        match token {
            ~"(" => {
                let mut accumulator:~[Expression] = ~[];
                let mut remainder = tokens.tail();
                while remainder.len() > 0 && remainder.head() != ~")" {
                    let (expr, new_remainder) = subexpression( remainder );
                    accumulator += [expr];
                    remainder = new_remainder
                }
                (List(accumulator), remainder.tail())
            }
            ~")" => fail,
                _ => (atom(token), tokens.tail())
        }
    }
    let (expression, remainder) = subexpression( tokens );
    expression
}

enum Expression {
    Int(int),
    Float(float),
    Symbol(~str),
    List(~[Expression])
}

fn print_expression( expression:Expression ) {
    fn print_expr( expression:Expression ) {
        match expression {
            Int(number) => { io::print( fmt!("%d", number) ) }
            Float(number) => { io::print( fmt!("%f", number) ) }
            Symbol(string) => { io::print( string ) }
            List(expressions) => {
                io::print("(");
                for expressions.init().each |&expression| {
                    print_expr( expression );
                    io::print(", ");
                }
                print_expr( expressions.last() );
                io::print(")");
            }
        }
    }
    print_expr(expression);
    io::println(~"");
}

fn main() {
    print_expression( parse( tokenize( "(1 2 3 (1 2 3))" ) ) );
    let blah:Expression = List(~[Int(1), List(~[Float(1.0), Symbol(~"xyz")])]);
    print_expression(blah);
    io::println( "(begin 1 2)" )
}
<commit_msg>Remove comma from printed expressions.<commit_after>extern mod std;

#[test]
fn test_tokenize() {
    assert tokenize( ~"(1 2 3)" ) == ~[~"(", ~"1", ~"2", ~"3", ~")"];
}

#[test]
fn test_tokenize_empty() {
    assert tokenize( ~"" ) == ~[];
}

#[test]
fn test_tokenize_no_spaces() {
    assert tokenize( ~"one" ) == ~[~"one"];
}

#[test]
fn test_pad_parentheses() {
    assert pad_parentheses( ~"(1 2 3)" ) == ~" ( 1 2 3 ) "
}

#[test]
fn test_pad_parentheses_empty() {
    assert pad_parentheses( ~"" ) == ~""
}

#[test]
fn test_pad_parentheses_one() {
    assert pad_parentheses( ~"(" ) == ~" ( ";
    assert pad_parentheses( ~")" ) == ~" ) ";
}

fn pad_parentheses( input:&str ) -> ~str {
    str::replace(str::replace(input, ")", " ) "), "(", " ( ")
}

fn tokenize( input:&str ) -> ~[~str] {
    str::words(pad_parentheses(input))
}

#[test]
fn test_that_atom_can_read_a_symbol() {
    match atom(~"hello") {
        Symbol(~"hello") => (),
            _ => fail
    }
}

#[test]
fn test_that_atom_can_read_an_int() {
    match atom(~"10") {
        Int(10) => (),
            _ => fail
    }
}

#[test]
fn test_that_atom_can_read_a_float() {
    match atom(~"10.1") {
        Float(10.1) => (),
            _ => fail
    }
}

fn atom( input:~str ) -> Expression {
    match int::from_str(input) {
        Some(number) => Int(number),
        None => match float::from_str(input) {
            Some(number) => Float(number),
            None => Symbol(input)
        }
    }
}

#[test]
fn test_that_parse_can_read_an_atom() {
    let atom = parse( ~[~"12"] );
    match atom {
        Int(12) => (),
            _ => fail
    }
}

#[test]
fn test_that_parse_can_read_a_list() {
    let list = parse( ~[ ~"(", ~"1", ~")" ] );
    match list {
        List([Int(1)]) => (),
            _ => fail ~"not a list"
    }
}

#[test]
fn test_that_parse_can_read_a_nested_list() {
    let list = parse( ~[ ~"(", ~"1", ~"(", ~"2", ~")", ~"3", ~")" ] );
    match list {
        List([Int(1), List([Int(2)]), Int(3)]) => (),
        _ => fail
    }
}

fn parse( tokens:~[~str] ) -> Expression {
    fn subexpression( tokens:~[~str] ) -> (Expression, ~[~str]) {
        let token = tokens.head();
        match token {
            ~"(" => {
                let mut accumulator:~[Expression] = ~[];
                let mut remainder = tokens.tail();
                while remainder.len() > 0 && remainder.head() != ~")" {
                    let (expr, new_remainder) = subexpression( remainder );
                    accumulator += [expr];
                    remainder = new_remainder
                }
                (List(accumulator), remainder.tail())
            }
            ~")" => fail,
                _ => (atom(token), tokens.tail())
        }
    }
    let (expression, remainder) = subexpression( tokens );
    expression
}

enum Expression {
    Int(int),
    Float(float),
    Symbol(~str),
    List(~[Expression])
}

fn print_expression( expression:Expression ) {
    fn print_expr( expression:Expression ) {
        match expression {
            Int(number) => { io::print( fmt!("%d", number) ) }
            Float(number) => { io::print( fmt!("%f", number) ) }
            Symbol(string) => { io::print( string ) }
            List(expressions) => {
                io::print("(");
                for expressions.init().each |&expression| {
                    print_expr( expression );
                    io::print(" ");
                }
                print_expr( expressions.last() );
                io::print(")");
            }
        }
    }
    print_expr(expression);
    io::println(~"");
}

fn main() {
    print_expression( parse( tokenize( "(1 2 3 (1 2 3))" ) ) );
    let blah:Expression = List(~[Int(1), List(~[Float(1.0), Symbol(~"xyz")])]);
    print_expression(blah);
    io::println( "(begin 1 2)" )
}
<|endoftext|>


<commit_before>#[cfg(feature = "use_glib")]
use glib::translate::*;
#[cfg(feature = "use_glib")]
use glib_ffi;
#[cfg(feature = "use_glib")]
use gobject_ffi;
#[cfg(feature = "use_glib")]
use std::ptr;
#[cfg(feature = "use_glib")]
use std::mem;
use std::cmp::PartialEq;
use ffi;

use ffi::enums::{
    Antialias,
    SubpixelOrder,
    HintStyle,
    HintMetrics,
};

#[cfg(feature = "use_glib")]
glib_wrapper! {
    pub struct FontOptions(Boxed<ffi::cairo_font_options_t>);

    match fn {
        copy => |ptr| {
            let ptr = ffi::cairo_font_options_copy(ptr);
            let status = ffi::cairo_font_options_status(ptr);
            status.ensure_valid();
            ptr
        },
        free => |ptr| ffi::cairo_font_options_destroy(ptr),
        get_type => || ffi::gobject::cairo_gobject_font_options_get_type(),
    }
}

#[cfg(not(feature = "use_glib"))]
pub struct FontOptions(*mut ffi::cairo_font_options_t);

impl FontOptions {
    pub fn new() -> FontOptions {
        let font_options: FontOptions = unsafe {
            FontOptions::from_raw_full(ffi::cairo_font_options_create())
        };
        font_options.ensure_status();
        font_options
    }

    #[cfg(feature = "use_glib")]
    #[doc(hidden)]
    pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_font_options_t) -> FontOptions {
        from_glib_full(ptr)
    }

    #[cfg(not(feature = "use_glib"))]
    #[doc(hidden)]
    pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_font_options_t) -> FontOptions {
        assert!(!ptr.is_null());
        FontOptions(ptr)
    }

    #[cfg(feature = "use_glib")]
    #[doc(hidden)]
    pub fn to_raw_none(&self) -> *mut ffi::cairo_font_options_t {
        mut_override(self.to_glib_none().0)
    }

    #[cfg(not(feature = "use_glib"))]
    #[doc(hidden)]
    pub fn to_raw_none(&self) -> *mut ffi::cairo_font_options_t {
        self.0
    }

    pub fn ensure_status(&self) {
        let status = unsafe {
            ffi::cairo_font_options_status(self.to_raw_none())
        };
        status.ensure_valid()
    }

    pub fn merge(&mut self, other: &FontOptions) {
        unsafe {
            ffi::cairo_font_options_merge(self.to_raw_none(), other.to_raw_none())
        }
    }

    pub fn hash(&self) -> u64 {
        unsafe {
            u64::from(ffi::cairo_font_options_hash(self.to_raw_none()))
        }
    }

    pub fn set_antialias(&mut self, antialias: Antialias) {
        unsafe {
            ffi::cairo_font_options_set_antialias(self.to_raw_none(), antialias)
        }
    }

    pub fn get_antialias(&self) -> Antialias {
        unsafe {
            ffi::cairo_font_options_get_antialias(self.to_raw_none())
        }
    }

    pub fn set_subpixel_order(&mut self, order: SubpixelOrder) {
        unsafe {
            ffi::cairo_font_options_set_subpixel_order(self.to_raw_none(), order)
        }
    }

    pub fn get_subpixel_order(&self) -> SubpixelOrder {
        unsafe {
            ffi::cairo_font_options_get_subpixel_order(self.to_raw_none())
        }
    }

    pub fn set_hint_style(&mut self, hint_style: HintStyle) {
        unsafe {
            ffi::cairo_font_options_set_hint_style(self.to_raw_none(), hint_style)
        }
    }

    pub fn get_hint_style(&self) -> HintStyle {
        unsafe {
            ffi::cairo_font_options_get_hint_style(self.to_raw_none())
        }
    }

    pub fn set_hint_metrics(&mut self, hint_metrics: HintMetrics) {
        unsafe {
            ffi::cairo_font_options_set_hint_metrics(self.to_raw_none(), hint_metrics)
        }
    }

    pub fn get_hint_metrics(&self) -> HintMetrics {
        unsafe {
            ffi::cairo_font_options_get_hint_metrics(self.to_raw_none())
        }
    }
}

impl PartialEq for FontOptions {
    fn eq(&self, other: &FontOptions) -> bool {
        unsafe {
            ffi::cairo_font_options_equal(self.to_raw_none(), other.to_raw_none()).as_bool()
        }
    }
}

impl Default for FontOptions {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(not(feature = "use_glib"))]
impl Drop for FontOptions {
    fn drop(&mut self) {
        unsafe {
            ffi::cairo_font_options_destroy(self.to_raw_none());
        }
    }
}

#[cfg(not(feature = "use_glib"))]
impl Clone for FontOptions {
    fn clone(&self) -> FontOptions {
        unsafe {
            FontOptions::from_raw_full(ffi::cairo_font_options_copy(self.to_raw_none()))
        }
    }
}
<commit_msg>Implement Hash and Eq for FontOptions<commit_after>#[cfg(feature = "use_glib")]
use glib::translate::*;
#[cfg(feature = "use_glib")]
use glib_ffi;
#[cfg(feature = "use_glib")]
use gobject_ffi;
#[cfg(feature = "use_glib")]
use std::ptr;
#[cfg(feature = "use_glib")]
use std::mem;
use std::hash;
use std::cmp::PartialEq;
use ffi;

use ffi::enums::{
    Antialias,
    SubpixelOrder,
    HintStyle,
    HintMetrics,
};

#[cfg(feature = "use_glib")]
glib_wrapper! {
    pub struct FontOptions(Boxed<ffi::cairo_font_options_t>);

    match fn {
        copy => |ptr| {
            let ptr = ffi::cairo_font_options_copy(ptr);
            let status = ffi::cairo_font_options_status(ptr);
            status.ensure_valid();
            ptr
        },
        free => |ptr| ffi::cairo_font_options_destroy(ptr),
        get_type => || ffi::gobject::cairo_gobject_font_options_get_type(),
    }
}

#[cfg(not(feature = "use_glib"))]
pub struct FontOptions(*mut ffi::cairo_font_options_t);

impl FontOptions {
    pub fn new() -> FontOptions {
        let font_options: FontOptions = unsafe {
            FontOptions::from_raw_full(ffi::cairo_font_options_create())
        };
        font_options.ensure_status();
        font_options
    }

    #[cfg(feature = "use_glib")]
    #[doc(hidden)]
    pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_font_options_t) -> FontOptions {
        from_glib_full(ptr)
    }

    #[cfg(not(feature = "use_glib"))]
    #[doc(hidden)]
    pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_font_options_t) -> FontOptions {
        assert!(!ptr.is_null());
        FontOptions(ptr)
    }

    #[cfg(feature = "use_glib")]
    #[doc(hidden)]
    pub fn to_raw_none(&self) -> *mut ffi::cairo_font_options_t {
        mut_override(self.to_glib_none().0)
    }

    #[cfg(not(feature = "use_glib"))]
    #[doc(hidden)]
    pub fn to_raw_none(&self) -> *mut ffi::cairo_font_options_t {
        self.0
    }

    pub fn ensure_status(&self) {
        let status = unsafe {
            ffi::cairo_font_options_status(self.to_raw_none())
        };
        status.ensure_valid()
    }

    pub fn merge(&mut self, other: &FontOptions) {
        unsafe {
            ffi::cairo_font_options_merge(self.to_raw_none(), other.to_raw_none())
        }
    }

    pub fn set_antialias(&mut self, antialias: Antialias) {
        unsafe {
            ffi::cairo_font_options_set_antialias(self.to_raw_none(), antialias)
        }
    }

    pub fn get_antialias(&self) -> Antialias {
        unsafe {
            ffi::cairo_font_options_get_antialias(self.to_raw_none())
        }
    }

    pub fn set_subpixel_order(&mut self, order: SubpixelOrder) {
        unsafe {
            ffi::cairo_font_options_set_subpixel_order(self.to_raw_none(), order)
        }
    }

    pub fn get_subpixel_order(&self) -> SubpixelOrder {
        unsafe {
            ffi::cairo_font_options_get_subpixel_order(self.to_raw_none())
        }
    }

    pub fn set_hint_style(&mut self, hint_style: HintStyle) {
        unsafe {
            ffi::cairo_font_options_set_hint_style(self.to_raw_none(), hint_style)
        }
    }

    pub fn get_hint_style(&self) -> HintStyle {
        unsafe {
            ffi::cairo_font_options_get_hint_style(self.to_raw_none())
        }
    }

    pub fn set_hint_metrics(&mut self, hint_metrics: HintMetrics) {
        unsafe {
            ffi::cairo_font_options_set_hint_metrics(self.to_raw_none(), hint_metrics)
        }
    }

    pub fn get_hint_metrics(&self) -> HintMetrics {
        unsafe {
            ffi::cairo_font_options_get_hint_metrics(self.to_raw_none())
        }
    }
}

impl PartialEq for FontOptions {
    fn eq(&self, other: &FontOptions) -> bool {
        unsafe {
            ffi::cairo_font_options_equal(self.to_raw_none(), other.to_raw_none()).as_bool()
        }
    }
}

impl Eq for FontOptions { }

impl hash::Hash for FontOptions {
    fn hash<H>(&self, state: &mut H) where H: hash::Hasher {
        unsafe {
            hash::Hash::hash(&ffi::cairo_font_options_hash(self.to_raw_none()), state)
        }
    }
}

impl Default for FontOptions {
    fn default() -> Self {
        Self::new()
    }
}

#[cfg(not(feature = "use_glib"))]
impl Drop for FontOptions {
    fn drop(&mut self) {
        unsafe {
            ffi::cairo_font_options_destroy(self.to_raw_none());
        }
    }
}

#[cfg(not(feature = "use_glib"))]
impl Clone for FontOptions {
    fn clone(&self) -> FontOptions {
        unsafe {
            FontOptions::from_raw_full(ffi::cairo_font_options_copy(self.to_raw_none()))
        }
    }
}
<|endoftext|>


<commit_before>use arraymap::ArrayMap;
use itertools::Itertools;
use mli::*;
use mli_ndarray::Ndeep;
use ndarray::{s, Array, Array1, Array2, ArrayBase, ArrayView1, Data, OwnedRepr};
use std::marker::PhantomData;

type D1 = ndarray::Ix1;
type D2 = ndarray::Ix2;

pub struct DefConvInput {
    pub offsets: Array2<f32>,
    pub features: Array2<f32>,
}

impl DefConvInput {
    /// Extracts the corner features and relative coordinate for a bilinear interpolation.
    fn extract_corners(&self, coordinate: [f32; 2]) -> ([f32; 4], [f32; 2]) {
        // Get the integer versions of four corners.
        let c00 = coordinate.map(|f| f.floor() as isize);
        let c11 = coordinate.map(|f| f.ceil() as isize);
        let c01 = [c00[0], c11[1]];
        let c10 = [c11[0], c00[1]];
        let coords = [c00, c01, c10, c11];

        // Get the relative coordinate.
        let rc = [coordinate[0] - c00[0] as f32, coordinate[1] - c00[1] as f32];

        // Create a closure to detect out-of-bounds.
        let in_bounds = |[y, x]: [isize; 2]| {
            if y < 0
                || x < 0
                || y >= self.features.shape()[0] as isize
                || x >= self.features.shape()[1] as isize
            {
                None
            } else {
                Some([y, x].map(|&n| n as usize))
            }
        };

        // Create a closure to extract the feature with zero-padding.
        let extract = |c| in_bounds(c).map(|c| self.features[c]).unwrap_or(0.0);

        // Extract the corner features.
        (coords.map(|&c| extract(c)), rc)
    }

    /// Looks up a location in the features using bilinear interpolation and zero-padding.
    fn bilinear(&self, coordinate: [f32; 2]) -> f32 {
        // Extract the corner features and the relative coordinate.
        let (f, rc) = self.extract_corners(coordinate);

        // Perform the x interpolation.
        let fx = [
            (1.0 - rc[0]) * f[0] + rc[0] * f[1],
            (1.0 - rc[0]) * f[2] + rc[0] * f[3],
        ];

        // Perform the y interpolation.
        (1.0 - rc[1]) * fx[0] + rc[1] * fx[1]
    }

    /// Looks up a gradient from a location in the features using bilinear interpolation and zero-padding.
    fn bilinear_gradient(&self, coordinate: [f32; 2]) -> [f32; 2] {
        // Extract the corner features and the relative coordinate.
        let (f, rc) = self.extract_corners(coordinate);

        // Perform the y interpolation to get the x values.
        let fx = [
            (1.0 - rc[0]) * f[0] + rc[0] * f[1],
            (1.0 - rc[0]) * f[2] + rc[0] * f[3],
        ];

        // Perform the x interpolation to get the y values.
        let fy = [
            (1.0 - rc[1]) * f[0] + rc[1] * f[2],
            (1.0 - rc[1]) * f[1] + rc[1] * f[3],
        ];

        // The gradient is the difference between the interpolation values in each dimension.
        [fy[1] - fy[0], fx[1] - fx[0]]
    }
}

#[derive(Clone, Debug)]
pub struct DefConv2 {
    weights: Array1<f32>,
    output_shape: [usize; 2],
}

impl DefConv2 {
    pub fn new(weights: Array1<f32>, output_shape: [usize; 2]) -> Self {
        Self {
            weights,
            output_shape,
        }
    }
}

impl Forward for DefConv2 {
    type Input = DefConvInput;
    type Internal = ();
    type Output = Array2<f32>;

    fn forward(&self, input: &Self::Input) -> ((), Self::Output) {
        // Get shapes.
        let outshape = self.output_shape;
        let inshape = input.features.shape();

        // Compute the coordinate multiplier.
        let multipliers = [
            inshape[0] as f32 / outshape[0] as f32,
            inshape[1] as f32 / outshape[1] as f32,
        ];

        // Compute bilinear interpolation for (y, x) pairs.
        (
            (),
            Array::from_shape_vec(
                outshape,
                (0..outshape[0])
                    .cartesian_product(0..outshape[1])
                    .map(|(y, x)| {
                        input.bilinear([
                            (y as f32 + 0.5) * multipliers[0],
                            (x as f32 + 0.5) * multipliers[1],
                        ])
                    })
                    .collect(),
            )
            .expect("mli-defconv: unexpected shape vec"),
        )
    }
}
<commit_msg>add implementation of input feature backprop for mli-defconv<commit_after>use arraymap::ArrayMap;
use itertools::Itertools;
use mli::*;
use mli_ndarray::Ndeep;
use ndarray::{s, Array, Array1, Array2, ArrayBase, ArrayView1, Data, OwnedRepr};
use std::marker::PhantomData;

type D1 = ndarray::Ix1;
type D2 = ndarray::Ix2;

fn corners(coordinate: [f32; 2]) -> [[isize; 2]; 4] {
    let c00 = coordinate.map(|f| f.floor() as isize);
    let c11 = coordinate.map(|f| f.ceil() as isize);
    let c01 = [c00[0], c11[1]];
    let c10 = [c11[0], c00[1]];
    [c00, c01, c10, c11]
}

pub struct DefConvData {
    pub offsets: Array2<f32>,
    pub features: Array2<f32>,
}

impl DefConvData {
    fn validate_corners(&self, coordinate: [f32; 2]) -> ([Option<[usize; 2]>; 4], [f32; 2]) {
        // Get the integer versions of four corners.
        let coords = corners(coordinate);

        // Get the relative coordinate.
        let rc = [
            coordinate[0] - coords[0][0] as f32,
            coordinate[1] - coords[0][1] as f32,
        ];

        // Create a closure to detect out-of-bounds.
        let in_bounds = |[y, x]: [isize; 2]| {
            if y < 0
                || x < 0
                || y >= self.features.shape()[0] as isize
                || x >= self.features.shape()[1] as isize
            {
                None
            } else {
                Some([y, x].map(|&n| n as usize))
            }
        };

        // Create a closure to extract the feature with zero-padding.
        let extract = |c| in_bounds(c);

        (coords.map(|&c| extract(c)), rc)
    }

    /// Extracts the corner features and relative coordinate for a bilinear interpolation.
    fn extract_corners(&self, coordinate: [f32; 2]) -> ([f32; 4], [f32; 2]) {
        let (validated_corners, rc) = self.validate_corners(coordinate);
        (
            validated_corners.map(|c| c.as_ref().map(|&c| self.features[c]).unwrap_or(0.0)),
            rc,
        )
    }

    /// Looks up a location in the features using bilinear interpolation and zero-padding.
    fn bilinear(&self, coordinate: [f32; 2]) -> f32 {
        // Extract the corner features and the relative coordinate.
        let (f, rc) = self.extract_corners(coordinate);

        // Perform the y interpolation.
        let fx = [
            (1.0 - rc[0]) * f[0] + rc[0] * f[2],
            (1.0 - rc[0]) * f[1] + rc[0] * f[3],
        ];

        // Perform the x interpolation.
        (1.0 - rc[1]) * fx[0] + rc[1] * fx[1]
    }

    /// Looks up a position gradient from a location in the features using bilinear interpolation and zero-padding.
    fn bilinear_position_gradient(&self, coordinate: [f32; 2]) -> [f32; 2] {
        // Extract the corner features and the relative coordinate.
        let (f, rc) = self.extract_corners(coordinate);

        // Perform the y interpolation to get the x values.
        let fx = [
            (1.0 - rc[0]) * f[0] + rc[0] * f[2],
            (1.0 - rc[0]) * f[1] + rc[0] * f[3],
        ];

        // Perform the x interpolation to get the y values.
        let fy = [
            (1.0 - rc[1]) * f[0] + rc[1] * f[1],
            (1.0 - rc[1]) * f[2] + rc[1] * f[3],
        ];

        // The gradient is the difference between the interpolation values in each dimension.
        [fy[1] - fy[0], fx[1] - fx[0]]
    }
}

#[derive(Clone, Debug)]
pub struct DefConv2 {
    weights: Array1<f32>,
    output_shape: [usize; 2],
}

impl DefConv2 {
    pub fn new(weights: Array1<f32>, output_shape: [usize; 2]) -> Self {
        Self {
            weights,
            output_shape,
        }
    }
}

impl Forward for DefConv2 {
    type Input = DefConvData;
    type Internal = ();
    type Output = Array2<f32>;

    fn forward(&self, input: &Self::Input) -> ((), Self::Output) {
        assert_eq!(self.weights.len(), input.offsets.shape()[0]);
        // Get shapes.
        let outshape = self.output_shape;
        let inshape = input.features.raw_dim();

        // Compute the coordinate multiplier.
        let multipliers = [
            inshape[0] as f32 / outshape[0] as f32,
            inshape[1] as f32 / outshape[1] as f32,
        ];

        // Compute bilinear interpolation for (y, x) pairs.
        (
            (),
            Array::from_shape_vec(
                outshape,
                (0..outshape[0])
                    .cartesian_product(0..outshape[1])
                    .map(|(y, x)| {
                        self.weights
                            .iter()
                            .zip(input.offsets.outer_iter())
                            .map(|(weight, offset)| {
                                input.bilinear([
                                    (y as f32 + 0.5 + offset[0]) * multipliers[0],
                                    (x as f32 + 0.5 + offset[1]) * multipliers[1],
                                ]) * weight
                            })
                            .sum()
                    })
                    .collect(),
            )
            .expect("mli-defconv: unexpected shape vec"),
        )
    }
}

impl Backward for DefConv2 {
    type OutputDelta = Array2<f32>;
    type InputDelta = DefConvData;
    type TrainDelta = Ndeep<OwnedRepr<f32>, D1>;

    fn backward(
        &self,
        input: &Self::Input,
        _: &Self::Internal,
        output_delta: &Self::OutputDelta,
    ) -> (Self::InputDelta, Self::TrainDelta) {
        // Get shapes.
        let outshape = self.output_shape;
        let inshape = input.features.raw_dim();

        // Compute the coordinate multiplier.
        let multipliers = [
            inshape[0] as f32 / outshape[0] as f32,
            inshape[1] as f32 / outshape[1] as f32,
        ];

        // We cannot determine in advance what locations are affected
        let mut feature_deltas: Array2<f32> = Array2::zeros(inshape);

        for (y, x) in (0..outshape[0]).cartesian_product(0..outshape[1]) {
            for (weight, offset) in self.weights.iter().zip(input.offsets.outer_iter()) {
                let sample_coordinate = [
                    (y as f32 + 0.5 + offset[0]) * multipliers[0],
                    (x as f32 + 0.5 + offset[1]) * multipliers[1],
                ];
                let (coords, rc) = input.validate_corners(sample_coordinate);
                for (coord, coeff) in coords.iter().zip(
                    [
                        (1.0 - rc[0]) * (1.0 - rc[1]),
                        (1.0 - rc[0]) * rc[1],
                        rc[0] * (1.0 - rc[1]),
                        rc[0] * rc[1],
                    ]
                    .iter(),
                ) {
                    if let Some(coord) = coord {
                        feature_deltas[*coord] += coeff * weight * output_delta[[y, x]];
                    }
                }
            }
        }

        // Compute bilinear interpolation for (y, x) pairs.
        (
            DefConvData {
                features: feature_deltas,
                offsets: unimplemented!(),
            },
            unimplemented!(),
        )
    }
}
<|endoftext|>


<commit_before>use structs::{CliError, Config, UserConfig, Member, Team, PublicKey};
use std;
use std::fs::File;
use std::io::prelude::*;
use std::collections::HashMap;

use glob::glob;
use reqwest;
use reqwest::header::Authorization;
use serde_json;

pub struct GithubClient {
    client: reqwest::Client,
    conf: Config,
}

impl GithubClient {
    pub fn new(config: &Config) -> Result<GithubClient, CliError> {
        if std::env::var("SSL_CERT_FILE").is_err() {
            std::env::set_var("SSL_CERT_FILE", &config.cert_path);
        }
        let client = reqwest::Client::new()?;
        Ok(GithubClient { client: client, conf: config.clone() })
    }

    fn get_cache_path(url: &str) -> std::path::PathBuf {
        let mut path = std::env::temp_dir();
        path.push("ghteam-auth-cache");
        path.push(url);
        path
    }

    fn load_content_from_cache(&self, url: &str) -> Result<(std::fs::Metadata, String), CliError> {
        let path = Self::get_cache_path(url);
        let metadata = std::fs::metadata(path.to_str().unwrap())?;
        let mut f = File::open(path.to_str().unwrap())?;
        let mut content = String::new();
        f.read_to_string(&mut content)?;
        Ok((metadata, content))
    }

    fn store_content_to_cache(&self, url: &str, content: &str) -> Result<(), CliError> {
        let path = Self::get_cache_path(url);
        std::fs::create_dir_all(path.parent().unwrap())?;
        let mut f = File::create(path.to_str().unwrap())?;
        f.write(content.as_bytes())?;
        Ok(())
    }

    fn get_content_from_url(&self, url: &str) -> Result<String, CliError> {
        let token = String::from("token ") + &self.conf.token;
        let res = self.client.get(url).header(Authorization(token)).send();
        let mut content = String::new();
        res?.read_to_string(&mut content)?;
        self.store_content_to_cache(url, &content)?;
        Ok(content)
    }

    fn get_content(&self, url: &str) -> Result<String, CliError> {
        match self.load_content_from_cache(url) {
            Ok((metadata, cache_content)) => {
                match std::time::SystemTime::now().duration_since(metadata.modified()?) {
                    Ok(caching_duration) => {
                        if caching_duration.as_secs() > self.conf.cache_duration {
                            match self.get_content_from_url(url) {
                                Ok(content_from_url) => Ok(content_from_url),
                                Err(_) => Ok(cache_content),
                            }
                        } else {
                            Ok(cache_content)
                        }
                    }
                    Err(_) => Ok(cache_content),
                }
            }
            Err(_) => self.get_content_from_url(url),
        }
    }

    #[allow(dead_code)]
    pub fn print_user_public_key(&self, user: &str) -> Result<(), CliError> {
        let keys = self.get_user_public_key(user)?;
        println!("{}", keys);
        Ok(())
    }

    fn get_user_public_key(&self, user: &str) -> Result<String, CliError> {
        let url = format!("{}/users/{}/keys", self.conf.endpoint, user);
        let content = self.get_content(&url)?;
        let keys = serde_json::from_str::<Vec<PublicKey>>(&content)?;
        Ok(keys.iter().map(|k| k.key.clone()).collect::<Vec<String>>().join("\n"))
    }

    #[allow(dead_code)]
    pub fn check_pam(&self, user: &str) -> Result<bool, CliError> {
        let (_, members) = self.get_team_members()?;
        Ok(members.contains_key(user))
    }

    pub fn get_team_members(&self) -> Result<(Team, HashMap<String, Member>), CliError> {
        let teams: HashMap<String, Team> = self.get_teams()?;
        if let Some(team) = teams.get(&self.conf.team.clone()) {
            Ok((team.clone(), self.get_members(team.id)?))
        } else {
            Err(CliError::from(std::io::Error::new(std::io::ErrorKind::NotFound, "Team not found")))
        }
    }

    fn get_teams(&self) -> Result<HashMap<String, Team>, CliError> {
        let url = format!("{}/orgs/{}/teams", self.conf.endpoint, self.conf.org);
        let content = self.get_content(&url)?;
        let teams = serde_json::from_str::<Vec<Team>>(&content)?;
        let mut team_map = HashMap::new();
        for team in teams {
            team_map.insert(team.name.clone(), team);
        }
        Ok(team_map)
    }

    fn get_members(&self, mid: u64) -> Result<HashMap<String, Member>, CliError> {
        let url = format!("{}/teams/{}/members", self.conf.endpoint, mid);
        let content = self.get_content(&url)?;
        let members = serde_json::from_str::<Vec<Member>>(&content)?;
        let mut member_map = HashMap::new();
        for member in members {
            member_map.insert(member.login.clone(), member);
        }
        Ok(member_map)
    }

    fn get_user_conf_path(&self, user: &str) -> Result<UserConfig, CliError> {
        let home = self.conf.home.replace("{}", user);
        UserConfig::new(&(home + &self.conf.user_conf_path))
    }

    #[allow(dead_code)]
    pub fn clear_all_caches(&self) -> Result<(), CliError> {
        let mut path = std::env::temp_dir();
        path.push("ghteam-auth-cache");
        path.push("**");
        path.push("*");
        for entry in glob(&path.to_str().unwrap()).unwrap() {
            match entry {
                Ok(path) => {
                    if path.is_file() {
                        std::fs::remove_file(path)?
                    }
                }
                Err(e) => println!("{:?}", e),
            }
        }
        Ok(())
    }
}
<commit_msg>Rename function<commit_after>use structs::{CliError, Config, UserConfig, Member, Team, PublicKey};
use std;
use std::fs::File;
use std::io::prelude::*;
use std::collections::HashMap;

use glob::glob;
use reqwest;
use reqwest::header::Authorization;
use serde_json;

pub struct GithubClient {
    client: reqwest::Client,
    conf: Config,
}

impl GithubClient {
    pub fn new(config: &Config) -> Result<GithubClient, CliError> {
        if std::env::var("SSL_CERT_FILE").is_err() {
            std::env::set_var("SSL_CERT_FILE", &config.cert_path);
        }
        let client = reqwest::Client::new()?;
        Ok(GithubClient { client: client, conf: config.clone() })
    }

    fn get_cache_path(url: &str) -> std::path::PathBuf {
        let mut path = std::env::temp_dir();
        path.push("ghteam-auth-cache");
        path.push(url);
        path
    }

    fn load_content_from_cache(&self, url: &str) -> Result<(std::fs::Metadata, String), CliError> {
        let path = Self::get_cache_path(url);
        let metadata = std::fs::metadata(path.to_str().unwrap())?;
        let mut f = File::open(path.to_str().unwrap())?;
        let mut content = String::new();
        f.read_to_string(&mut content)?;
        Ok((metadata, content))
    }

    fn store_content_to_cache(&self, url: &str, content: &str) -> Result<(), CliError> {
        let path = Self::get_cache_path(url);
        std::fs::create_dir_all(path.parent().unwrap())?;
        let mut f = File::create(path.to_str().unwrap())?;
        f.write(content.as_bytes())?;
        Ok(())
    }

    fn get_content_from_url(&self, url: &str) -> Result<String, CliError> {
        let token = String::from("token ") + &self.conf.token;
        let res = self.client.get(url).header(Authorization(token)).send();
        let mut content = String::new();
        res?.read_to_string(&mut content)?;
        self.store_content_to_cache(url, &content)?;
        Ok(content)
    }

    fn get_content(&self, url: &str) -> Result<String, CliError> {
        match self.load_content_from_cache(url) {
            Ok((metadata, cache_content)) => {
                match std::time::SystemTime::now().duration_since(metadata.modified()?) {
                    Ok(caching_duration) => {
                        if caching_duration.as_secs() > self.conf.cache_duration {
                            match self.get_content_from_url(url) {
                                Ok(content_from_url) => Ok(content_from_url),
                                Err(_) => Ok(cache_content),
                            }
                        } else {
                            Ok(cache_content)
                        }
                    }
                    Err(_) => Ok(cache_content),
                }
            }
            Err(_) => self.get_content_from_url(url),
        }
    }

    #[allow(dead_code)]
    pub fn print_user_public_key(&self, user: &str) -> Result<(), CliError> {
        let keys = self.get_user_public_key(user)?;
        println!("{}", keys);
        Ok(())
    }

    fn get_user_public_key(&self, user: &str) -> Result<String, CliError> {
        let url = format!("{}/users/{}/keys", self.conf.endpoint, user);
        let content = self.get_content(&url)?;
        let keys = serde_json::from_str::<Vec<PublicKey>>(&content)?;
        Ok(keys.iter().map(|k| k.key.clone()).collect::<Vec<String>>().join("\n"))
    }

    #[allow(dead_code)]
    pub fn check_pam(&self, user: &str) -> Result<bool, CliError> {
        let (_, members) = self.get_team_members()?;
        Ok(members.contains_key(user))
    }

    pub fn get_team_members(&self) -> Result<(Team, HashMap<String, Member>), CliError> {
        let teams: HashMap<String, Team> = self.get_teams()?;
        if let Some(team) = teams.get(&self.conf.team.clone()) {
            Ok((team.clone(), self.get_members(team.id)?))
        } else {
            Err(CliError::from(std::io::Error::new(std::io::ErrorKind::NotFound, "Team not found")))
        }
    }

    fn get_teams(&self) -> Result<HashMap<String, Team>, CliError> {
        let url = format!("{}/orgs/{}/teams", self.conf.endpoint, self.conf.org);
        let content = self.get_content(&url)?;
        let teams = serde_json::from_str::<Vec<Team>>(&content)?;
        let mut team_map = HashMap::new();
        for team in teams {
            team_map.insert(team.name.clone(), team);
        }
        Ok(team_map)
    }

    fn get_members(&self, mid: u64) -> Result<HashMap<String, Member>, CliError> {
        let url = format!("{}/teams/{}/members", self.conf.endpoint, mid);
        let content = self.get_content(&url)?;
        let members = serde_json::from_str::<Vec<Member>>(&content)?;
        let mut member_map = HashMap::new();
        for member in members {
            member_map.insert(member.login.clone(), member);
        }
        Ok(member_map)
    }

    fn get_user_conf(&self, user: &str) -> Result<UserConfig, CliError> {
        let home = self.conf.home.replace("{}", user);
        UserConfig::new(&(home + &self.conf.user_conf_path))
    }

    #[allow(dead_code)]
    pub fn clear_all_caches(&self) -> Result<(), CliError> {
        let mut path = std::env::temp_dir();
        path.push("ghteam-auth-cache");
        path.push("**");
        path.push("*");
        for entry in glob(&path.to_str().unwrap()).unwrap() {
            match entry {
                Ok(path) => {
                    if path.is_file() {
                        std::fs::remove_file(path)?
                    }
                }
                Err(e) => println!("{:?}", e),
            }
        }
        Ok(())
    }
}
<|endoftext|>