<commit_before>// partially (c) 2016 Google Inc. (Lewin Bormann, lewinb@google.com)
//
// See project root for licensing information.
//

use std::collections::hash_map::DefaultHasher;
use std::collections::HashMap;
use std::error::Error;
use std::fmt;
use std::fs;
use std::hash::{Hash, Hasher};
use std::io;
use std::io::{Read, Write};

use crate::types::Token;

/// Implements a specialized storage to set and retrieve `Token` instances.
/// The `scope_hash` represents the signature of the scopes for which the given token
/// should be stored or retrieved.
/// For completeness, the underlying, sorted scopes are provided as well. They might be
/// useful for presentation to the user.
pub trait TokenStorage {
    type Error: 'static + Error;

    /// If `token` is None, it is invalid or revoked and should be removed from storage.
    /// Otherwise, it should be saved.
    fn set(
        &mut self,
        scope_hash: u64,
        scopes: &Vec<&str>,
        token: Option<Token>,
    ) -> Result<(), Self::Error>;
    /// A `None` result indicates that there is no token for the given scope_hash.
    fn get(&self, scope_hash: u64, scopes: &Vec<&str>) -> Result<Option<Token>, Self::Error>;
}

/// Calculate a hash value describing the scopes, and return a sorted Vec of the scopes.
pub fn hash_scopes<'a, I, T>(scopes: I) -> (u64, Vec<String>)
where
    T: AsRef<str> + Ord + 'a,
    I: IntoIterator<Item = &'a T>,
{
    let mut sv: Vec<&str> = scopes
        .into_iter()
        .map(|s| s.as_ref())
        .collect::<Vec<&str>>();
    sv.sort();
    let mut sh = DefaultHasher::new();
    &sv.hash(&mut sh);
    let sv = sv.iter().map(|s| s.to_string()).collect();
    (sh.finish(), sv)
}

/// A storage that remembers nothing.
#[derive(Default)]
pub struct NullStorage;
#[derive(Debug)]
pub struct NullError;

impl Error for NullError {
    fn description(&self) -> &str {
        "NULL"
    }
}

impl fmt::Display for NullError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        "NULL-ERROR".fmt(f)
    }
}

impl TokenStorage for NullStorage {
    type Error = NullError;
    fn set(&mut self, _: u64, _: &Vec<&str>, _: Option<Token>) -> Result<(), NullError> {
        Ok(())
    }
    fn get(&self, _: u64, _: &Vec<&str>) -> Result<Option<Token>, NullError> {
        Ok(None)
    }
}

/// A storage that remembers values for one session only.
#[derive(Default)]
pub struct MemoryStorage {
    pub tokens: HashMap<u64, Token>,
}

impl TokenStorage for MemoryStorage {
    type Error = NullError;

    fn set(
        &mut self,
        scope_hash: u64,
        _: &Vec<&str>,
        token: Option<Token>,
    ) -> Result<(), NullError> {
        match token {
            Some(t) => self.tokens.insert(scope_hash, t),
            None => self.tokens.remove(&scope_hash),
        };
        Ok(())
    }

    fn get(&self, scope_hash: u64, _: &Vec<&str>) -> Result<Option<Token>, NullError> {
        match self.tokens.get(&scope_hash) {
            Some(t) => Ok(Some(t.clone())),
            None => Ok(None),
        }
    }
}

/// A single stored token.
#[derive(Serialize, Deserialize)]
struct JSONToken {
    pub hash: u64,
    pub token: Token,
}

/// List of tokens in a JSON object
#[derive(Serialize, Deserialize)]
struct JSONTokens {
    pub tokens: Vec<JSONToken>,
}

/// Serializes tokens to a JSON file on disk.
#[derive(Default)]
pub struct DiskTokenStorage {
    location: String,
    tokens: HashMap<u64, Token>,
}

impl DiskTokenStorage {
    pub fn new<S: AsRef<str>>(location: S) -> Result<DiskTokenStorage, io::Error> {
        let mut dts = DiskTokenStorage {
            location: location.as_ref().to_owned(),
            tokens: HashMap::new(),
        };

        // best-effort
        let read_result = dts.load_from_file();

        match read_result {
            Result::Ok(()) => Result::Ok(dts),
            Result::Err(e) => {
                match e.kind() {
                    io::ErrorKind::NotFound => Result::Ok(dts), // File not found; ignore and create new one
                    _ => Result::Err(e),                        // e.g. PermissionDenied
                }
            }
        }
    }

    fn load_from_file(&mut self) -> Result<(), io::Error> {
        let mut f = fs::OpenOptions::new().read(true).open(&self.location)?;
        let mut contents = String::new();

        match f.read_to_string(&mut contents) {
            Result::Err(e) => return Result::Err(e),
            Result::Ok(_sz) => (),
        }

        let tokens: JSONTokens;

        match serde_json::from_str(&contents) {
            Result::Err(e) => return Result::Err(io::Error::new(io::ErrorKind::InvalidData, e)),
            Result::Ok(t) => tokens = t,
        }

        for t in tokens.tokens {
            self.tokens.insert(t.hash, t.token);
        }
        return Result::Ok(());
    }

    pub fn dump_to_file(&mut self) -> Result<(), io::Error> {
        let mut jsontokens = JSONTokens { tokens: Vec::new() };

        for (hash, token) in self.tokens.iter() {
            jsontokens.tokens.push(JSONToken {
                hash: *hash,
                token: token.clone(),
            });
        }

        let serialized;;

        match serde_json::to_string(&jsontokens) {
            Result::Err(e) => return Result::Err(io::Error::new(io::ErrorKind::InvalidData, e)),
            Result::Ok(s) => serialized = s,
        }

        let mut f = fs::OpenOptions::new()
            .create(true)
            .write(true)
            .truncate(true)
            .open(&self.location)?;
        f.write(serialized.as_ref()).map(|_| ())
    }
}

impl TokenStorage for DiskTokenStorage {
    type Error = io::Error;
    fn set(
        &mut self,
        scope_hash: u64,
        _: &Vec<&str>,
        token: Option<Token>,
    ) -> Result<(), Self::Error> {
        match token {
            None => {
                self.tokens.remove(&scope_hash);
                ()
            }
            Some(t) => {
                self.tokens.insert(scope_hash, t.clone());
                ()
            }
        }
        self.dump_to_file()
    }
    fn get(&self, scope_hash: u64, _: &Vec<&str>) -> Result<Option<Token>, Self::Error> {
        Result::Ok(self.tokens.get(&scope_hash).map(|tok| tok.clone()))
    }
}
<commit_msg>imp(Storage): Implement Default for MemoryStorage and make errors sendable<commit_after>// partially (c) 2016 Google Inc. (Lewin Bormann, lewinb@google.com)
//
// See project root for licensing information.
//

use std::collections::hash_map::DefaultHasher;
use std::collections::HashMap;
use std::error::Error;
use std::fmt;
use std::fs;
use std::hash::{Hash, Hasher};
use std::io;
use std::io::{Read, Write};

use crate::types::Token;

/// Implements a specialized storage to set and retrieve `Token` instances.
/// The `scope_hash` represents the signature of the scopes for which the given token
/// should be stored or retrieved.
/// For completeness, the underlying, sorted scopes are provided as well. They might be
/// useful for presentation to the user.
pub trait TokenStorage {
    type Error: 'static + Error + Send;

    /// If `token` is None, it is invalid or revoked and should be removed from storage.
    /// Otherwise, it should be saved.
    fn set(
        &mut self,
        scope_hash: u64,
        scopes: &Vec<&str>,
        token: Option<Token>,
    ) -> Result<(), Self::Error>;
    /// A `None` result indicates that there is no token for the given scope_hash.
    fn get(&self, scope_hash: u64, scopes: &Vec<&str>) -> Result<Option<Token>, Self::Error>;
}

/// Calculate a hash value describing the scopes, and return a sorted Vec of the scopes.
pub fn hash_scopes<'a, I, T>(scopes: I) -> (u64, Vec<String>)
where
    T: AsRef<str> + Ord + 'a,
    I: IntoIterator<Item = &'a T>,
{
    let mut sv: Vec<&str> = scopes
        .into_iter()
        .map(|s| s.as_ref())
        .collect::<Vec<&str>>();
    sv.sort();
    let mut sh = DefaultHasher::new();
    &sv.hash(&mut sh);
    let sv = sv.iter().map(|s| s.to_string()).collect();
    (sh.finish(), sv)
}

/// A storage that remembers nothing.
#[derive(Default)]
pub struct NullStorage;
#[derive(Debug)]
pub struct NullError;

impl Error for NullError {
    fn description(&self) -> &str {
        "NULL"
    }
}

impl fmt::Display for NullError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        "NULL-ERROR".fmt(f)
    }
}

impl TokenStorage for NullStorage {
    type Error = NullError;
    fn set(&mut self, _: u64, _: &Vec<&str>, _: Option<Token>) -> Result<(), NullError> {
        Ok(())
    }
    fn get(&self, _: u64, _: &Vec<&str>) -> Result<Option<Token>, NullError> {
        Ok(None)
    }
}

/// A storage that remembers values for one session only.
#[derive(Default)]
pub struct MemoryStorage {
    pub tokens: HashMap<u64, Token>,
}

impl MemoryStorage {
    pub fn new() -> MemoryStorage {
        Default::default()
    }
}

impl TokenStorage for MemoryStorage {
    type Error = NullError;

    fn set(
        &mut self,
        scope_hash: u64,
        _: &Vec<&str>,
        token: Option<Token>,
    ) -> Result<(), NullError> {
        match token {
            Some(t) => self.tokens.insert(scope_hash, t),
            None => self.tokens.remove(&scope_hash),
        };
        Ok(())
    }

    fn get(&self, scope_hash: u64, _: &Vec<&str>) -> Result<Option<Token>, NullError> {
        match self.tokens.get(&scope_hash) {
            Some(t) => Ok(Some(t.clone())),
            None => Ok(None),
        }
    }
}

/// A single stored token.
#[derive(Serialize, Deserialize)]
struct JSONToken {
    pub hash: u64,
    pub token: Token,
}

/// List of tokens in a JSON object
#[derive(Serialize, Deserialize)]
struct JSONTokens {
    pub tokens: Vec<JSONToken>,
}

/// Serializes tokens to a JSON file on disk.
#[derive(Default)]
pub struct DiskTokenStorage {
    location: String,
    tokens: HashMap<u64, Token>,
}

impl DiskTokenStorage {
    pub fn new<S: AsRef<str>>(location: S) -> Result<DiskTokenStorage, io::Error> {
        let mut dts = DiskTokenStorage {
            location: location.as_ref().to_owned(),
            tokens: HashMap::new(),
        };

        // best-effort
        let read_result = dts.load_from_file();

        match read_result {
            Result::Ok(()) => Result::Ok(dts),
            Result::Err(e) => {
                match e.kind() {
                    io::ErrorKind::NotFound => Result::Ok(dts), // File not found; ignore and create new one
                    _ => Result::Err(e),                        // e.g. PermissionDenied
                }
            }
        }
    }

    fn load_from_file(&mut self) -> Result<(), io::Error> {
        let mut f = fs::OpenOptions::new().read(true).open(&self.location)?;
        let mut contents = String::new();

        match f.read_to_string(&mut contents) {
            Result::Err(e) => return Result::Err(e),
            Result::Ok(_sz) => (),
        }

        let tokens: JSONTokens;

        match serde_json::from_str(&contents) {
            Result::Err(e) => return Result::Err(io::Error::new(io::ErrorKind::InvalidData, e)),
            Result::Ok(t) => tokens = t,
        }

        for t in tokens.tokens {
            self.tokens.insert(t.hash, t.token);
        }
        return Result::Ok(());
    }

    pub fn dump_to_file(&mut self) -> Result<(), io::Error> {
        let mut jsontokens = JSONTokens { tokens: Vec::new() };

        for (hash, token) in self.tokens.iter() {
            jsontokens.tokens.push(JSONToken {
                hash: *hash,
                token: token.clone(),
            });
        }

        let serialized;;

        match serde_json::to_string(&jsontokens) {
            Result::Err(e) => return Result::Err(io::Error::new(io::ErrorKind::InvalidData, e)),
            Result::Ok(s) => serialized = s,
        }

        let mut f = fs::OpenOptions::new()
            .create(true)
            .write(true)
            .truncate(true)
            .open(&self.location)?;
        f.write(serialized.as_ref()).map(|_| ())
    }
}

impl TokenStorage for DiskTokenStorage {
    type Error = io::Error;
    fn set(
        &mut self,
        scope_hash: u64,
        _: &Vec<&str>,
        token: Option<Token>,
    ) -> Result<(), Self::Error> {
        match token {
            None => {
                self.tokens.remove(&scope_hash);
                ()
            }
            Some(t) => {
                self.tokens.insert(scope_hash, t.clone());
                ()
            }
        }
        self.dump_to_file()
    }
    fn get(&self, scope_hash: u64, _: &Vec<&str>) -> Result<Option<Token>, Self::Error> {
        Result::Ok(self.tokens.get(&scope_hash).map(|tok| tok.clone()))
    }
}
<|endoftext|>


<commit_before>// partially (c) 2016 Google Inc. (Lewin Bormann, lewinb@google.com)
//
// See project root for licensing information.
//
use crate::types::Token;

use std::collections::BTreeMap;
use std::io;
use std::path::{Path, PathBuf};
use std::sync::Mutex;

use serde::{Deserialize, Serialize};

// The storage layer allows retrieving tokens for scopes that have been
// previously granted tokens. One wrinkle is that a token granted for a set
// of scopes X is also valid for any subset of X's scopes. So when retrieving a
// token for a set of scopes provided by the caller it's beneficial to compare
// that set to all previously stored tokens to see if it is a subset of any
// existing set. To do this efficiently we store a bloom filter along with each
// token that represents the set of scopes the token is associated with. The
// bloom filter allows for efficiently skipping any entries that are
// definitively not a superset.
// The current implementation uses a 64bit bloom filter with 4 hash functions.

/// ScopeFilter represents a filter for a set of scopes. It can definitively
/// prove that a given list of scopes is not a subset of another.
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
struct ScopeFilter {
    bitmask: u64,
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
enum FilterResponse {
    Maybe,
    No,
}

impl ScopeFilter {
    fn new<T>(scopes: &[T]) -> Self
    where
        T: AsRef<str>,
    {
        let mut bitmask = 0u64;
        for scope in scopes {
            let scope_hash = seahash::hash(scope.as_ref().as_bytes());
            // Use the first 4 6-bit chunks of the seahash as the 4 hash values
            // in the bloom filter.
            for i in 0..4 {
                // h is a hash derived value in the range 0..64
                let h = (scope_hash >> (6 * i)) & 0b11_1111;
                bitmask |= 1 << h;
            }
        }
        ScopeFilter { bitmask }
    }

    /// Determine if this ScopeFilter could be a subset of the provided filter.
    fn is_subset_of(self, filter: ScopeFilter) -> FilterResponse {
        if self.bitmask & filter.bitmask == self.bitmask {
            FilterResponse::Maybe
        } else {
            FilterResponse::No
        }
    }
}

#[derive(Debug)]
pub struct ScopesAndFilter<'a, T> {
    filter: ScopeFilter,
    scopes: &'a [T],
}

// Implement Clone manually. Auto derive fails to work correctly because we want
// Clone to be implemented regardless of whether T is Clone or not.
impl<'a, T> Clone for ScopesAndFilter<'a, T> {
    fn clone(&self) -> Self {
        ScopesAndFilter {
            filter: self.filter,
            scopes: self.scopes,
        }
    }
}
impl<'a, T> Copy for ScopesAndFilter<'a, T> {}

impl<'a, T> From<&'a [T]> for ScopesAndFilter<'a, T>
where
    T: AsRef<str>,
{
    fn from(scopes: &'a [T]) -> Self {
        let filter = ScopeFilter::new(scopes);
        ScopesAndFilter { filter, scopes }
    }
}

impl<'a, T> ScopesAndFilter<'a, T>
where
    T: AsRef<str>,
{
    // implement an inherent from method even though From is implemented. This
    // is because passing an array ref like &[&str; 1] (&["foo"]) will be auto
    // deref'd to a slice on function boundaries, but it will not implement the
    // From trait. This inherent method just serves to auto deref from array
    // refs to slices and proxy to the From impl.
    pub fn from(scopes: &'a [T]) -> Self {
        <Self as From<&'a [T]>>::from(scopes)
    }
}

pub(crate) enum Storage {
    Memory { tokens: Mutex<JSONTokens> },
    Disk(DiskStorage),
}

impl Storage {
    pub(crate) async fn set<T>(&self, scopes: ScopesAndFilter<'_, T>, token: Token)
    where
        T: AsRef<str>,
    {
        match self {
            Storage::Memory { tokens } => tokens.lock().unwrap().set(scopes, token),
            Storage::Disk(disk_storage) => disk_storage.set(scopes, token).await,
        }
    }

    pub(crate) fn get<T>(&self, scopes: ScopesAndFilter<T>) -> Option<Token>
    where
        T: AsRef<str>,
    {
        match self {
            Storage::Memory { tokens } => tokens.lock().unwrap().get(scopes),
            Storage::Disk(disk_storage) => disk_storage.get(scopes),
        }
    }
}

/// A single stored token.
#[derive(Debug, Clone, Serialize, Deserialize)]
struct JSONToken {
    pub scopes: Vec<String>,
    pub token: Token,
}

/// List of tokens in a JSON object
#[derive(Debug, Clone)]
pub(crate) struct JSONTokens {
    token_map: BTreeMap<ScopeFilter, Vec<JSONToken>>,
}

impl JSONTokens {
    pub(crate) fn new() -> Self {
        JSONTokens {
            token_map: BTreeMap::new(),
        }
    }

    pub(crate) async fn load_from_file(filename: &Path) -> Result<Self, io::Error> {
        let contents = tokio::fs::read(filename).await?;
        let token_vec: Vec<JSONToken> = serde_json::from_slice(&contents)
            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
        let mut token_map: BTreeMap<ScopeFilter, Vec<JSONToken>> = BTreeMap::new();
        for token in token_vec {
            let filter = ScopesAndFilter::from(&token.scopes).filter;
            token_map.entry(filter).or_default().push(token);
        }
        Ok(JSONTokens { token_map })
    }

    fn get<T>(&self, ScopesAndFilter { filter, scopes }: ScopesAndFilter<T>) -> Option<Token>
    where
        T: AsRef<str>,
    {
        let requested_scopes_are_subset_of = |other_scopes: &[String]| {
            scopes
                .iter()
                .all(|s| other_scopes.iter().any(|t| t.as_str() == s.as_ref()))
        };
        // Check for exact match of bloom filter first. In the common case an
        // application will provide the same set of scopes repeatedly. If a
        // token exists for the exact scope list requested a lookup of the
        // ScopeFilter will return a list that would contain it.
        if let Some(tokens) = self.token_map.get(&filter) {
            for t in tokens {
                if requested_scopes_are_subset_of(t.scopes.as_slice()) {
                    return Some(t.token.clone());
                }
            }
        }

        // No exact match for the scopes provided. Search for any tokens that
        // exist for a superset of the scopes requested.
        for t in self
            .token_map
            .iter()
            .filter(|(k, _v)| filter.is_subset_of(**k) == FilterResponse::Maybe)
            .flat_map(|(_, v)| v.iter())
        {
            if requested_scopes_are_subset_of(&t.scopes) {
                return Some(t.token.clone());
            }
        }
        None
    }

    fn set<T>(&mut self, ScopesAndFilter { filter, scopes }: ScopesAndFilter<T>, token: Token)
    where
        T: AsRef<str>,
    {
        self.token_map.entry(filter).or_default().push(JSONToken {
            scopes: scopes.iter().map(|x| x.as_ref().to_string()).collect(),
            token,
        });
    }

    fn all_tokens(&self) -> Vec<JSONToken> {
        self.token_map
            .values()
            .flat_map(|v| v.iter())
            .cloned()
            .collect()
    }
}

pub(crate) struct DiskStorage {
    tokens: Mutex<JSONTokens>,
    write_tx: tokio::sync::mpsc::Sender<Vec<JSONToken>>,
}

impl DiskStorage {
    pub(crate) async fn new(path: PathBuf) -> Result<Self, io::Error> {
        let tokens = JSONTokens::load_from_file(&path).await?;
        // Writing to disk will happen in a separate task. This means in the
        // common case returning a token to the user will not be required to
        // wait for disk i/o. We communicate with a dedicated writer task via a
        // buffered channel. This ensures that the writes happen in the order
        // received, and if writes fall too far behind we will block GetToken
        // requests until disk i/o completes.
        let (write_tx, mut write_rx) = tokio::sync::mpsc::channel::<Vec<JSONToken>>(2);
        tokio::spawn(async move {
            while let Some(tokens) = write_rx.recv().await {
                match serde_json::to_string(&tokens) {
                    Err(e) => log::error!("Failed to serialize tokens: {}", e),
                    Ok(ser) => {
                        if let Err(e) = tokio::fs::write(path.clone(), &ser).await {
                            log::error!("Failed to write tokens to disk: {}", e);
                        }
                    }
                }
            }
        });
        Ok(DiskStorage {
            tokens: Mutex::new(tokens),
            write_tx,
        })
    }

    async fn set<T>(&self, scopes: ScopesAndFilter<'_, T>, token: Token)
    where
        T: AsRef<str>,
    {
        let cloned_tokens = {
            let mut tokens = self.tokens.lock().unwrap();
            tokens.set(scopes, token);
            tokens.all_tokens()
        };
        self.write_tx
            .clone()
            .send(cloned_tokens)
            .await
            .expect("disk storage task not running");
    }

    pub(crate) fn get<T>(&self, scopes: ScopesAndFilter<T>) -> Option<Token>
    where
        T: AsRef<str>,
    {
        self.tokens.lock().unwrap().get(scopes)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_scope_filter() {
        let foo = ScopeFilter::new(&["foo"]);
        let bar = ScopeFilter::new(&["bar"]);
        let foobar = ScopeFilter::new(&["foo", "bar"]);

        // foo and bar are both subsets of foobar. This condition should hold no
        // matter what changes are made to the bloom filter implementation.
        assert!(foo.is_subset_of(foobar) == FilterResponse::Maybe);
        assert!(bar.is_subset_of(foobar) == FilterResponse::Maybe);

        // These conditions hold under the current bloom filter implementation
        // because "foo" and "bar" don't collide, but if the bloom filter
        // implementations change it could be valid for them to return Maybe.
        assert!(foo.is_subset_of(bar) == FilterResponse::No);
        assert!(bar.is_subset_of(foo) == FilterResponse::No);
        assert!(foobar.is_subset_of(foo) == FilterResponse::No);
        assert!(foobar.is_subset_of(bar) == FilterResponse::No);
    }
}
<commit_msg>Fix a bug in refactoring the storage layer.<commit_after>// partially (c) 2016 Google Inc. (Lewin Bormann, lewinb@google.com)
//
// See project root for licensing information.
//
use crate::types::Token;

use std::collections::BTreeMap;
use std::io;
use std::path::{Path, PathBuf};
use std::sync::Mutex;

use serde::{Deserialize, Serialize};

// The storage layer allows retrieving tokens for scopes that have been
// previously granted tokens. One wrinkle is that a token granted for a set
// of scopes X is also valid for any subset of X's scopes. So when retrieving a
// token for a set of scopes provided by the caller it's beneficial to compare
// that set to all previously stored tokens to see if it is a subset of any
// existing set. To do this efficiently we store a bloom filter along with each
// token that represents the set of scopes the token is associated with. The
// bloom filter allows for efficiently skipping any entries that are
// definitively not a superset.
// The current implementation uses a 64bit bloom filter with 4 hash functions.

/// ScopeFilter represents a filter for a set of scopes. It can definitively
/// prove that a given list of scopes is not a subset of another.
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
struct ScopeFilter {
    bitmask: u64,
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
enum FilterResponse {
    Maybe,
    No,
}

impl ScopeFilter {
    fn new<T>(scopes: &[T]) -> Self
    where
        T: AsRef<str>,
    {
        let mut bitmask = 0u64;
        for scope in scopes {
            let scope_hash = seahash::hash(scope.as_ref().as_bytes());
            // Use the first 4 6-bit chunks of the seahash as the 4 hash values
            // in the bloom filter.
            for i in 0..4 {
                // h is a hash derived value in the range 0..64
                let h = (scope_hash >> (6 * i)) & 0b11_1111;
                bitmask |= 1 << h;
            }
        }
        ScopeFilter { bitmask }
    }

    /// Determine if this ScopeFilter could be a subset of the provided filter.
    fn is_subset_of(self, filter: ScopeFilter) -> FilterResponse {
        if self.bitmask & filter.bitmask == self.bitmask {
            FilterResponse::Maybe
        } else {
            FilterResponse::No
        }
    }
}

#[derive(Debug)]
pub struct ScopesAndFilter<'a, T> {
    filter: ScopeFilter,
    scopes: &'a [T],
}

// Implement Clone manually. Auto derive fails to work correctly because we want
// Clone to be implemented regardless of whether T is Clone or not.
impl<'a, T> Clone for ScopesAndFilter<'a, T> {
    fn clone(&self) -> Self {
        ScopesAndFilter {
            filter: self.filter,
            scopes: self.scopes,
        }
    }
}
impl<'a, T> Copy for ScopesAndFilter<'a, T> {}

impl<'a, T> From<&'a [T]> for ScopesAndFilter<'a, T>
where
    T: AsRef<str>,
{
    fn from(scopes: &'a [T]) -> Self {
        let filter = ScopeFilter::new(scopes);
        ScopesAndFilter { filter, scopes }
    }
}

impl<'a, T> ScopesAndFilter<'a, T>
where
    T: AsRef<str>,
{
    // implement an inherent from method even though From is implemented. This
    // is because passing an array ref like &[&str; 1] (&["foo"]) will be auto
    // deref'd to a slice on function boundaries, but it will not implement the
    // From trait. This inherent method just serves to auto deref from array
    // refs to slices and proxy to the From impl.
    pub fn from(scopes: &'a [T]) -> Self {
        <Self as From<&'a [T]>>::from(scopes)
    }
}

pub(crate) enum Storage {
    Memory { tokens: Mutex<JSONTokens> },
    Disk(DiskStorage),
}

impl Storage {
    pub(crate) async fn set<T>(&self, scopes: ScopesAndFilter<'_, T>, token: Token)
    where
        T: AsRef<str>,
    {
        match self {
            Storage::Memory { tokens } => tokens.lock().unwrap().set(scopes, token),
            Storage::Disk(disk_storage) => disk_storage.set(scopes, token).await,
        }
    }

    pub(crate) fn get<T>(&self, scopes: ScopesAndFilter<T>) -> Option<Token>
    where
        T: AsRef<str>,
    {
        match self {
            Storage::Memory { tokens } => tokens.lock().unwrap().get(scopes),
            Storage::Disk(disk_storage) => disk_storage.get(scopes),
        }
    }
}

/// A single stored token.
#[derive(Debug, Clone, Serialize, Deserialize)]
struct JSONToken {
    pub scopes: Vec<String>,
    pub token: Token,
}

/// List of tokens in a JSON object
#[derive(Debug, Clone)]
pub(crate) struct JSONTokens {
    token_map: BTreeMap<ScopeFilter, Vec<JSONToken>>,
}

impl JSONTokens {
    pub(crate) fn new() -> Self {
        JSONTokens {
            token_map: BTreeMap::new(),
        }
    }

    pub(crate) async fn load_from_file(filename: &Path) -> Result<Self, io::Error> {
        let contents = tokio::fs::read(filename).await?;
        let token_vec: Vec<JSONToken> = serde_json::from_slice(&contents)
            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
        let mut token_map: BTreeMap<ScopeFilter, Vec<JSONToken>> = BTreeMap::new();
        for token in token_vec {
            let filter = ScopesAndFilter::from(&token.scopes).filter;
            token_map.entry(filter).or_default().push(token);
        }
        Ok(JSONTokens { token_map })
    }

    fn get<T>(&self, ScopesAndFilter { filter, scopes }: ScopesAndFilter<T>) -> Option<Token>
    where
        T: AsRef<str>,
    {
        let requested_scopes_are_subset_of = |other_scopes: &[String]| {
            scopes
                .iter()
                .all(|s| other_scopes.iter().any(|t| t.as_str() == s.as_ref()))
        };
        // Check for exact match of bloom filter first. In the common case an
        // application will provide the same set of scopes repeatedly. If a
        // token exists for the exact scope list requested a lookup of the
        // ScopeFilter will return a list that would contain it.
        if let Some(tokens) = self.token_map.get(&filter) {
            for t in tokens {
                if requested_scopes_are_subset_of(t.scopes.as_slice()) {
                    return Some(t.token.clone());
                }
            }
        }

        // No exact match for the scopes provided. Search for any tokens that
        // exist for a superset of the scopes requested.
        for t in self
            .token_map
            .iter()
            .filter(|(k, _v)| filter.is_subset_of(**k) == FilterResponse::Maybe)
            .flat_map(|(_, v)| v.iter())
        {
            if requested_scopes_are_subset_of(&t.scopes) {
                return Some(t.token.clone());
            }
        }
        None
    }

    fn set<T>(&mut self, ScopesAndFilter { filter, scopes }: ScopesAndFilter<T>, token: Token)
    where
        T: AsRef<str>,
    {
        self.token_map.entry(filter).or_default().push(JSONToken {
            scopes: scopes.iter().map(|x| x.as_ref().to_string()).collect(),
            token,
        });
    }

    fn all_tokens(&self) -> Vec<JSONToken> {
        self.token_map
            .values()
            .flat_map(|v| v.iter())
            .cloned()
            .collect()
    }
}

pub(crate) struct DiskStorage {
    tokens: Mutex<JSONTokens>,
    write_tx: tokio::sync::mpsc::Sender<Vec<JSONToken>>,
}

impl DiskStorage {
    pub(crate) async fn new(path: PathBuf) -> Result<Self, io::Error> {
        let tokens = match JSONTokens::load_from_file(&path).await {
            Ok(tokens) => tokens,
            Err(e) if e.kind() == io::ErrorKind::NotFound => JSONTokens::new(),
            Err(e) => return Err(e),
        };

        // Writing to disk will happen in a separate task. This means in the
        // common case returning a token to the user will not be required to
        // wait for disk i/o. We communicate with a dedicated writer task via a
        // buffered channel. This ensures that the writes happen in the order
        // received, and if writes fall too far behind we will block GetToken
        // requests until disk i/o completes.
        let (write_tx, mut write_rx) = tokio::sync::mpsc::channel::<Vec<JSONToken>>(2);
        tokio::spawn(async move {
            while let Some(tokens) = write_rx.recv().await {
                match serde_json::to_string(&tokens) {
                    Err(e) => log::error!("Failed to serialize tokens: {}", e),
                    Ok(ser) => {
                        if let Err(e) = tokio::fs::write(path.clone(), &ser).await {
                            log::error!("Failed to write tokens to disk: {}", e);
                        }
                    }
                }
            }
        });
        Ok(DiskStorage {
            tokens: Mutex::new(tokens),
            write_tx,
        })
    }

    async fn set<T>(&self, scopes: ScopesAndFilter<'_, T>, token: Token)
    where
        T: AsRef<str>,
    {
        let cloned_tokens = {
            let mut tokens = self.tokens.lock().unwrap();
            tokens.set(scopes, token);
            tokens.all_tokens()
        };
        self.write_tx
            .clone()
            .send(cloned_tokens)
            .await
            .expect("disk storage task not running");
    }

    pub(crate) fn get<T>(&self, scopes: ScopesAndFilter<T>) -> Option<Token>
    where
        T: AsRef<str>,
    {
        self.tokens.lock().unwrap().get(scopes)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_scope_filter() {
        let foo = ScopeFilter::new(&["foo"]);
        let bar = ScopeFilter::new(&["bar"]);
        let foobar = ScopeFilter::new(&["foo", "bar"]);

        // foo and bar are both subsets of foobar. This condition should hold no
        // matter what changes are made to the bloom filter implementation.
        assert!(foo.is_subset_of(foobar) == FilterResponse::Maybe);
        assert!(bar.is_subset_of(foobar) == FilterResponse::Maybe);

        // These conditions hold under the current bloom filter implementation
        // because "foo" and "bar" don't collide, but if the bloom filter
        // implementations change it could be valid for them to return Maybe.
        assert!(foo.is_subset_of(bar) == FilterResponse::No);
        assert!(bar.is_subset_of(foo) == FilterResponse::No);
        assert!(foobar.is_subset_of(foo) == FilterResponse::No);
        assert!(foobar.is_subset_of(bar) == FilterResponse::No);
    }
}
<|endoftext|>


<commit_before>//! This LintPass catches both string addition and string addition + assignment
//!
//! Note that since we have two lints where one subsumes the other, we try to
//! disable the subsumed lint unless it has a higher level

use rustc::lint::*;
use rustc::middle::ty::TypeVariants::TyStruct;
use syntax::ast::*;
use syntax::codemap::{Span, Spanned};
use eq_op::is_exp_equal;
use types::match_ty_unwrap;
use utils::{match_def_path, span_lint, walk_ptrs_ty, get_parent_expr};

declare_lint! {
    pub STRING_ADD_ASSIGN,
    Warn,
    "Warn on `x = x + ..` where x is a `String`"
}

declare_lint! {
	pub STRING_ADD,
	Allow,
	"Warn on `x + ..` where x is a `String`"
}

#[derive(Copy, Clone)]
pub struct StringAdd;

impl LintPass for StringAdd {
    fn get_lints(&self) -> LintArray {
        lint_array!(STRING_ADD)
    }

    fn check_expr(&mut self, cx: &Context, e: &Expr) {
		if let &ExprBinary(Spanned{ node: BiAdd, .. }, ref left, _) = &e.node {
			if is_string(cx, left) {
				if let Allow = cx.current_level(STRING_ADD_ASSIGN) {
					// the string_add_assign is allow, so no duplicates
				} else {
					let parent = get_parent_expr(cx, e);
					if let Some(ref p) = parent {
						if let &ExprAssign(ref target, _) = &p.node {
							// avoid duplicate matches
							if is_exp_equal(target, left) { return; }
						}
					}
				}
				//TODO check for duplicates
				 span_lint(cx, STRING_ADD, e.span,
						"you add something to a string. \
						Consider using `String::push_str()` instead.")
			}
		}
	}
}

#[derive(Copy, Clone)]
pub struct StringAddAssign;

impl LintPass for StringAddAssign {
    fn get_lints(&self) -> LintArray {
        lint_array!(STRING_ADD_ASSIGN)
    }

    fn check_expr(&mut self, cx: &Context, e: &Expr) {
        if let &ExprAssign(ref target, ref  src) = &e.node {
            if is_string(cx, target) && is_add(src, target) {
                span_lint(cx, STRING_ADD_ASSIGN, e.span,
                    "you assign the result of adding something to this string. \
                    Consider using `String::push_str()` instead.")
            }
        }
    }
}

fn is_string(cx: &Context, e: &Expr) -> bool {
	let ty = walk_ptrs_ty(cx.tcx.expr_ty(e));
    if let TyStruct(did, _) = ty.sty {
        match_def_path(cx, did.did, &["collections", "string", "String"])
    } else { false }
}

fn is_add(src: &Expr, target: &Expr) -> bool {
    match &src.node {
        &ExprBinary(Spanned{ node: BiAdd, .. }, ref left, _) =>
            is_exp_equal(target, left),
        &ExprBlock(ref block) => block.stmts.is_empty() &&
            block.expr.as_ref().map_or(false, |expr| is_add(&*expr, target)),
        &ExprParen(ref expr) => is_add(&*expr, target),
        _ => false
    }
}
<commit_msg>fixed formatting<commit_after>//! This LintPass catches both string addition and string addition + assignment
//!
//! Note that since we have two lints where one subsumes the other, we try to
//! disable the subsumed lint unless it has a higher level

use rustc::lint::*;
use rustc::middle::ty::TypeVariants::TyStruct;
use syntax::ast::*;
use syntax::codemap::{Span, Spanned};
use eq_op::is_exp_equal;
use types::match_ty_unwrap;
use utils::{match_def_path, span_lint, walk_ptrs_ty, get_parent_expr};

declare_lint! {
    pub STRING_ADD_ASSIGN,
    Warn,
    "Warn on `x = x + ..` where x is a `String`"
}

declare_lint! {
    pub STRING_ADD,
    Allow,
    "Warn on `x + ..` where x is a `String`"
}

#[derive(Copy, Clone)]
pub struct StringAdd;

impl LintPass for StringAdd {
    fn get_lints(&self) -> LintArray {
        lint_array!(STRING_ADD)
    }

    fn check_expr(&mut self, cx: &Context, e: &Expr) {
        if let &ExprBinary(Spanned{ node: BiAdd, .. }, ref left, _) = &e.node {
            if is_string(cx, left) {
                if let Allow = cx.current_level(STRING_ADD_ASSIGN) {
                    // the string_add_assign is allow, so no duplicates
                } else {
                    let parent = get_parent_expr(cx, e);
                    if let Some(ref p) = parent {
                        if let &ExprAssign(ref target, _) = &p.node {
                            // avoid duplicate matches
                            if is_exp_equal(target, left) { return; }
                        }
                    }
                }
                //TODO check for duplicates
                 span_lint(cx, STRING_ADD, e.span,
                        "you add something to a string. \
                        Consider using `String::push_str()` instead.")
            }
        }
    }
}

#[derive(Copy, Clone)]
pub struct StringAddAssign;

impl LintPass for StringAddAssign {
    fn get_lints(&self) -> LintArray {
        lint_array!(STRING_ADD_ASSIGN)
    }

    fn check_expr(&mut self, cx: &Context, e: &Expr) {
        if let &ExprAssign(ref target, ref  src) = &e.node {
            if is_string(cx, target) && is_add(src, target) {
                span_lint(cx, STRING_ADD_ASSIGN, e.span,
                    "you assign the result of adding something to this string. \
                    Consider using `String::push_str()` instead.")
            }
        }
    }
}

fn is_string(cx: &Context, e: &Expr) -> bool {
    let ty = walk_ptrs_ty(cx.tcx.expr_ty(e));
    if let TyStruct(did, _) = ty.sty {
        match_def_path(cx, did.did, &["collections", "string", "String"])
    } else { false }
}

fn is_add(src: &Expr, target: &Expr) -> bool {
    match &src.node {
        &ExprBinary(Spanned{ node: BiAdd, .. }, ref left, _) =>
            is_exp_equal(target, left),
        &ExprBlock(ref block) => block.stmts.is_empty() &&
            block.expr.as_ref().map_or(false, |expr| is_add(&*expr, target)),
        &ExprParen(ref expr) => is_add(&*expr, target),
        _ => false
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 Kyle Mayes
//
// 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.

//! Provides helper functionality.

use std::{io, env};
use std::process::{Command};
use std::path::{Path, PathBuf};

use glob;

use libc::{c_int};

use super::{CXVersion};

//================================================
// Macros
//================================================

// try_opt! ______________________________________

macro_rules! try_opt {
    ($option:expr) => ({
        match $option {
            Some(some) => some,
            None => return None,
        }
    });
}

//================================================
// Structs
//================================================

/// A `clang` executable.
#[derive(Clone, Debug)]
pub struct Clang {
    /// The path to this `clang` executable.
    pub path: PathBuf,
    /// The version of this `clang` executable if it could be parsed.
    pub version: Option<CXVersion>,
    /// The directories searched by this `clang` executable for C headers if they could be parsed.
    pub c_search_paths: Option<Vec<PathBuf>>,
    /// The directories searched by this `clang` executable for C++ headers if they could be parsed.
    pub cpp_search_paths: Option<Vec<PathBuf>>,
}

impl Clang {
    //- Constructors -----------------------------

    fn new(path: PathBuf, args: &[String]) -> Clang {
        let version = parse_version(&path);
        let c_search_paths = parse_search_paths(&path, "c", args);
        let cpp_search_paths = parse_search_paths(&path, "c++", args);
        Clang {
            path: path,
            version: version,
            c_search_paths: c_search_paths,
            cpp_search_paths: cpp_search_paths,
        }
    }

    /// Returns a `clang` executable if one can be found.
    ///
    /// If the `CLANG_PATH` environment variable is set, that is the instance of `clang` used.
    /// Otherwise, a series of directories are searched. First, If a path is supplied, that is the
    /// first directory searched. Then, the directory returned by `llvm-config --bindir` is
    /// searched. On OS X systems, `xcodebuild -find clang` will next be queried. Last, the
    /// directories in the system's `PATH` are searched.
    pub fn find(path: Option<&Path>, args: &[String]) -> Option<Clang> {
        if let Ok(path) = env::var("CLANG_PATH") {
            return Some(Clang::new(path.into(), args));
        }

        let mut paths = vec![];
        if let Some(path) = path {
            paths.push(path.into());
        }
        if let Ok(path) = run_llvm_config(&["--bindir"]) {
            paths.push(path.into());
        }
        if cfg!(target_os="macos") {
            if let Ok((path, _)) = run("xcodebuild", &["-find", "clang"]) {
                paths.push(path.into());
            }
        }
        paths.extend(env::split_paths(&env::var("PATH").unwrap()));

        let default = format!("clang{}", env::consts::EXE_SUFFIX);
        let versioned = format!("clang-[0-9]*{}", env::consts::EXE_SUFFIX);
        let patterns = &[&default[..], &versioned[..]];
        for path in paths {
            if let Some(path) = find(&path, patterns) {
                return Some(Clang::new(path, args));
            }
        }
        None
    }
}

//================================================
// Functions
//================================================

/// Returns the first match to the supplied glob patterns in the supplied directory if there are any
/// matches.
fn find(directory: &Path, patterns: &[&str]) -> Option<PathBuf> {
    for pattern in patterns {
        let pattern = directory.join(pattern).to_string_lossy().into_owned();
        if let Some(path) = try_opt!(glob::glob(&pattern).ok()).filter_map(|p| p.ok()).next() {
            if path.is_file() && is_executable(&path).unwrap_or(false) {
                return Some(path);
            }
        }
    }
    None
}

#[cfg(unix)]
fn is_executable(path: &Path) -> io::Result<bool> {
    use libc;
    use std::ffi::CString;
    use std::os::unix::ffi::OsStrExt;

    let path = CString::new(path.as_os_str().as_bytes())?;
    unsafe { Ok(libc::access(path.as_ptr(), libc::X_OK) == 0) }
}

#[cfg(not(unix))]
fn is_executable(path: &Path) -> io::Result<bool> {
    Ok(true)
}

/// Attempts to run an executable, returning the `stdout` and `stderr` output if successful.
fn run(executable: &str, arguments: &[&str]) -> Result<(String, String), String> {
    Command::new(executable).args(arguments).output().map(|o| {
        let stdout = String::from_utf8_lossy(&o.stdout).into_owned();
        let stderr = String::from_utf8_lossy(&o.stderr).into_owned();
        (stdout, stderr)
    }).map_err(|_| format!("could not run executable: `{}`", executable))
}

/// Runs `clang`, returning the `stdout` and `stderr` output.
fn run_clang(path: &Path, arguments: &[&str]) -> (String, String) {
    run(&path.to_string_lossy().into_owned(), arguments).unwrap()
}

/// Runs `llvm-config`, returning the `stdout` output if successful.
fn run_llvm_config(arguments: &[&str]) -> Result<String, String> {
    let config = env::var("LLVM_CONFIG_PATH").unwrap_or_else(|_| "llvm-config".to_string());
    run(&config, arguments).map(|(o, _)| o)
}

/// Parses a version number if possible, ignoring trailing non-digit characters.
fn parse_version_number(number: &str) -> Option<c_int> {
    number.chars().take_while(|c| c.is_digit(10)).collect::<String>().parse().ok()
}

/// Parses the version from the output of a `clang` executable if possible.
fn parse_version(path: &Path) -> Option<CXVersion> {
    let output = run_clang(path, &["--version"]).0;
    let start = try_opt!(output.find("version ")) + 8;
    let mut numbers = try_opt!(output[start..].split_whitespace().nth(0)).split('.');
    let major = try_opt!(numbers.next().and_then(parse_version_number));
    let minor = try_opt!(numbers.next().and_then(parse_version_number));
    let subminor = numbers.next().and_then(parse_version_number).unwrap_or(0);
    Some(CXVersion { Major: major, Minor: minor, Subminor: subminor })
}

/// Parses the search paths from the output of a `clang` executable if possible.
fn parse_search_paths(path: &Path, language: &str, args: &[String]) -> Option<Vec<PathBuf>> {
    let mut clang_args = vec!["-E", "-x", language, "-", "-v"];
    clang_args.extend(args.iter().map(|s| &**s));
    let output = run_clang(path, &clang_args).1;
    let start = try_opt!(output.find("#include <...> search starts here:")) + 34;
    let end = try_opt!(output.find("End of search list."));
    let paths = output[start..end].replace("(framework directory)", "");
    Some(paths.lines().filter(|l| !l.is_empty()).map(|l| Path::new(l.trim()).into()).collect())
}
<commit_msg>Fix warning on Windows<commit_after>// Copyright 2016 Kyle Mayes
//
// 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.

//! Provides helper functionality.

use std::{io, env};
use std::process::{Command};
use std::path::{Path, PathBuf};

use glob;

use libc::{c_int};

use super::{CXVersion};

//================================================
// Macros
//================================================

// try_opt! ______________________________________

macro_rules! try_opt {
    ($option:expr) => ({
        match $option {
            Some(some) => some,
            None => return None,
        }
    });
}

//================================================
// Structs
//================================================

/// A `clang` executable.
#[derive(Clone, Debug)]
pub struct Clang {
    /// The path to this `clang` executable.
    pub path: PathBuf,
    /// The version of this `clang` executable if it could be parsed.
    pub version: Option<CXVersion>,
    /// The directories searched by this `clang` executable for C headers if they could be parsed.
    pub c_search_paths: Option<Vec<PathBuf>>,
    /// The directories searched by this `clang` executable for C++ headers if they could be parsed.
    pub cpp_search_paths: Option<Vec<PathBuf>>,
}

impl Clang {
    //- Constructors -----------------------------

    fn new(path: PathBuf, args: &[String]) -> Clang {
        let version = parse_version(&path);
        let c_search_paths = parse_search_paths(&path, "c", args);
        let cpp_search_paths = parse_search_paths(&path, "c++", args);
        Clang {
            path: path,
            version: version,
            c_search_paths: c_search_paths,
            cpp_search_paths: cpp_search_paths,
        }
    }

    /// Returns a `clang` executable if one can be found.
    ///
    /// If the `CLANG_PATH` environment variable is set, that is the instance of `clang` used.
    /// Otherwise, a series of directories are searched. First, If a path is supplied, that is the
    /// first directory searched. Then, the directory returned by `llvm-config --bindir` is
    /// searched. On OS X systems, `xcodebuild -find clang` will next be queried. Last, the
    /// directories in the system's `PATH` are searched.
    pub fn find(path: Option<&Path>, args: &[String]) -> Option<Clang> {
        if let Ok(path) = env::var("CLANG_PATH") {
            return Some(Clang::new(path.into(), args));
        }

        let mut paths = vec![];
        if let Some(path) = path {
            paths.push(path.into());
        }
        if let Ok(path) = run_llvm_config(&["--bindir"]) {
            paths.push(path.into());
        }
        if cfg!(target_os="macos") {
            if let Ok((path, _)) = run("xcodebuild", &["-find", "clang"]) {
                paths.push(path.into());
            }
        }
        paths.extend(env::split_paths(&env::var("PATH").unwrap()));

        let default = format!("clang{}", env::consts::EXE_SUFFIX);
        let versioned = format!("clang-[0-9]*{}", env::consts::EXE_SUFFIX);
        let patterns = &[&default[..], &versioned[..]];
        for path in paths {
            if let Some(path) = find(&path, patterns) {
                return Some(Clang::new(path, args));
            }
        }
        None
    }
}

//================================================
// Functions
//================================================

/// Returns the first match to the supplied glob patterns in the supplied directory if there are any
/// matches.
fn find(directory: &Path, patterns: &[&str]) -> Option<PathBuf> {
    for pattern in patterns {
        let pattern = directory.join(pattern).to_string_lossy().into_owned();
        if let Some(path) = try_opt!(glob::glob(&pattern).ok()).filter_map(|p| p.ok()).next() {
            if path.is_file() && is_executable(&path).unwrap_or(false) {
                return Some(path);
            }
        }
    }
    None
}

#[cfg(unix)]
fn is_executable(path: &Path) -> io::Result<bool> {
    use libc;
    use std::ffi::CString;
    use std::os::unix::ffi::OsStrExt;

    let path = CString::new(path.as_os_str().as_bytes())?;
    unsafe { Ok(libc::access(path.as_ptr(), libc::X_OK) == 0) }
}

#[cfg(not(unix))]
fn is_executable(_: &Path) -> io::Result<bool> {
    Ok(true)
}

/// Attempts to run an executable, returning the `stdout` and `stderr` output if successful.
fn run(executable: &str, arguments: &[&str]) -> Result<(String, String), String> {
    Command::new(executable).args(arguments).output().map(|o| {
        let stdout = String::from_utf8_lossy(&o.stdout).into_owned();
        let stderr = String::from_utf8_lossy(&o.stderr).into_owned();
        (stdout, stderr)
    }).map_err(|_| format!("could not run executable: `{}`", executable))
}

/// Runs `clang`, returning the `stdout` and `stderr` output.
fn run_clang(path: &Path, arguments: &[&str]) -> (String, String) {
    run(&path.to_string_lossy().into_owned(), arguments).unwrap()
}

/// Runs `llvm-config`, returning the `stdout` output if successful.
fn run_llvm_config(arguments: &[&str]) -> Result<String, String> {
    let config = env::var("LLVM_CONFIG_PATH").unwrap_or_else(|_| "llvm-config".to_string());
    run(&config, arguments).map(|(o, _)| o)
}

/// Parses a version number if possible, ignoring trailing non-digit characters.
fn parse_version_number(number: &str) -> Option<c_int> {
    number.chars().take_while(|c| c.is_digit(10)).collect::<String>().parse().ok()
}

/// Parses the version from the output of a `clang` executable if possible.
fn parse_version(path: &Path) -> Option<CXVersion> {
    let output = run_clang(path, &["--version"]).0;
    let start = try_opt!(output.find("version ")) + 8;
    let mut numbers = try_opt!(output[start..].split_whitespace().nth(0)).split('.');
    let major = try_opt!(numbers.next().and_then(parse_version_number));
    let minor = try_opt!(numbers.next().and_then(parse_version_number));
    let subminor = numbers.next().and_then(parse_version_number).unwrap_or(0);
    Some(CXVersion { Major: major, Minor: minor, Subminor: subminor })
}

/// Parses the search paths from the output of a `clang` executable if possible.
fn parse_search_paths(path: &Path, language: &str, args: &[String]) -> Option<Vec<PathBuf>> {
    let mut clang_args = vec!["-E", "-x", language, "-", "-v"];
    clang_args.extend(args.iter().map(|s| &**s));
    let output = run_clang(path, &clang_args).1;
    let start = try_opt!(output.find("#include <...> search starts here:")) + 34;
    let end = try_opt!(output.find("End of search list."));
    let paths = output[start..end].replace("(framework directory)", "");
    Some(paths.lines().filter(|l| !l.is_empty()).map(|l| Path::new(l.trim()).into()).collect())
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::process::Command;

use rustc_version::VersionMeta;
use tempdir::TempDir;
use toml::{Table, Value};

use CompilationMode;
use cargo::{Root, Rustflags};
use errors::*;
use extensions::CommandExt;
use rustc::{Src, Sysroot};
use util;
use xargo::Home;
use {cargo, xargo};

#[cfg(feature = "dev")]
fn profile() -> &'static str {
    "debug"
}

#[cfg(not(feature = "dev"))]
fn profile() -> &'static str {
    "release"
}

fn build(cmode: &CompilationMode,
         blueprint: Blueprint,
         ctoml: &cargo::Toml,
         home: &Home,
         rustflags: &Rustflags,
         hash: u64,
         verbose: bool)
         -> Result<()> {
    const TOML: &'static str = r#"
[package]
authors = ["The Rust Project Developers"]
name = "sysroot"
version = "0.0.0"
"#;

    let rustlib = home.lock_rw(cmode.triple())?;
    rustlib.remove_siblings()
        .chain_err(|| format!("couldn't clear {}", rustlib.path().display()))?;
    let dst = rustlib.parent().join("lib");
    util::mkdir(&dst)?;
    for (_, stage) in blueprint.stages {
        let td = TempDir::new("xargo")
            .chain_err(|| "couldn't create a temporary directory")?;
        let td = td.path();

        let mut stoml = TOML.to_owned();

        let mut map = Table::new();
        map.insert("dependencies".to_owned(), Value::Table(stage.toml));
        stoml.push_str(&Value::Table(map).to_string());

        if let Some(profile) = ctoml.profile() {
            stoml.push_str(&profile.to_string())
        }

        util::write(&td.join("Cargo.toml"), &stoml)?;
        util::mkdir(&td.join("src"))?;
        util::write(&td.join("src/lib.rs"), "")?;

        let cargo = || {
            let mut cmd = Command::new("cargo");
            cmd.env("RUSTFLAGS", rustflags.for_xargo(home));
            cmd.env_remove("CARGO_TARGET_DIR");
            cmd.arg("build");

            match () {
                #[cfg(feature = "dev")]
                () => {}
                #[cfg(not(feature = "dev"))]
                () => {
                    cmd.arg("--release");
                }
            }
            cmd.arg("--manifest-path");
            cmd.arg(td.join("Cargo.toml"));
            cmd.args(&["--target", cmode.triple()]);

            if verbose {
                cmd.arg("-v");
            }

            cmd
        };

        for krate in stage.crates {
            cargo().arg("-p").arg(krate).run(verbose)?;
        }

        // Copy artifacts to Xargo sysroot
        util::cp_r(&td.join("target")
                       .join(cmode.triple())
                       .join(profile())
                       .join("deps"),
                   &dst)?;

    }

    // Create hash file
    util::write(&rustlib.parent().join(".hash"), &hash.to_string())?;

    Ok(())
}

fn old_hash(cmode: &CompilationMode, home: &Home) -> Result<Option<u64>> {
    // FIXME this should be `lock_ro`
    let lock = home.lock_rw(cmode.triple())?;
    let hfile = lock.parent().join(".hash");

    if hfile.exists() {
        Ok(util::read(&hfile)?.parse().ok())
    } else {
        Ok(None)
    }
}

/// Computes the hash of the would-be target sysroot
///
/// This information is used to compute the hash
///
/// - Dependencies in `Xargo.toml` for a specific target
/// - RUSTFLAGS / build.rustflags / target.*.rustflags
/// - The target specification file, is any
/// - `[profile.release]` in `Cargo.toml`
/// - `rustc` commit hash
fn hash(cmode: &CompilationMode,
        blueprint: &Blueprint,
        rustflags: &Rustflags,
        ctoml: &cargo::Toml,
        meta: &VersionMeta)
        -> Result<u64> {
    let mut hasher = DefaultHasher::new();

    blueprint.hash(&mut hasher);

    rustflags.hash(&mut hasher);

    cmode.hash(&mut hasher)?;

    if let Some(profile) = ctoml.profile() {
        profile.hash(&mut hasher);
    }

    if let Some(ref hash) = meta.commit_hash {
        hash.hash(&mut hasher);
    }

    Ok(hasher.finish())
}

pub fn update(cmode: &CompilationMode,
              home: &Home,
              root: &Root,
              rustflags: &Rustflags,
              meta: &VersionMeta,
              src: &Src,
              sysroot: &Sysroot,
              verbose: bool)
              -> Result<()> {
    let ctoml = cargo::toml(root)?;
    let xtoml = xargo::toml(root)?;

    let blueprint = Blueprint::from(xtoml.as_ref(), cmode.triple(), &src)?;

    let hash = hash(cmode, &blueprint, rustflags, &ctoml, meta)?;

    if old_hash(cmode, home)? != Some(hash) {
        build(cmode, blueprint, &ctoml, home, rustflags, hash, verbose)?;
    }

    // copy host artifacts into the sysroot, if necessary
    if cmode.is_native() {
        return Ok(());
    }

    let lock = home.lock_rw(&meta.host)?;
    let hfile = lock.parent().join(".hash");

    let hash = meta.commit_hash.as_ref().map(|s| &**s).unwrap_or("");
    if hfile.exists() {
        if util::read(&hfile)? == hash {
            return Ok(());
        }
    }

    lock.remove_siblings()
        .chain_err(|| format!("couldn't clear {}", lock.path().display()))?;
    let dst = lock.parent().join("lib");
    util::mkdir(&dst)?;
    util::cp_r(&sysroot.path()
                   .join("lib/rustlib")
                   .join(&meta.host)
                   .join("lib"),
               &dst)?;

    util::write(&hfile, hash)?;

    Ok(())
}

/// Per stage dependencies
#[derive(Debug)]
pub struct Stage {
    crates: Vec<String>,
    toml: Table,
}

/// A sysroot that will be built in "stages"
#[derive(Debug)]
pub struct Blueprint {
    stages: BTreeMap<i64, Stage>,
}

impl Blueprint {
    fn new() -> Self {
        Blueprint { stages: BTreeMap::new() }
    }

    fn from(toml: Option<&xargo::Toml>,
            target: &str,
            src: &Src)
            -> Result<Self> {
        let deps = match (toml.and_then(|t| t.dependencies()),
                          toml.and_then(|t| t.target_dependencies(target))) {
            (Some(value), Some(tvalue)) => {
                let mut deps = value.as_table()
                    .cloned()
                    .ok_or_else(|| {
                        format!("Xargo.toml: `dependencies` must be a table")
                    })?;

                let more_deps = tvalue.as_table()
                    .ok_or_else(|| {
                        format!("Xargo.toml: `target.{}.dependencies` must be \
                                 a table",
                                target)
                    })?;
                for (k, v) in more_deps {
                    if deps.insert(k.to_owned(), v.clone()).is_some() {
                        Err(format!("found duplicate dependency name {}, \
                                     but all dependencies must have a \
                                     unique name",
                                    k))?
                    }
                }

                deps
            }
            (Some(value), None) |
            (None, Some(value)) => {
                if let Some(table) = value.as_table() {
                    table.clone()
                } else {
                    Err(format!("Xargo.toml: target.{}.dependencies must be \
                                 a table",
                                target))?
                }
            }
            (None, None) => {
                // If no dependencies were listed, we assume `core` as the
                // only dependency
                let mut t = BTreeMap::new();
                t.insert("core".to_owned(), Value::Table(BTreeMap::new()));
                t
            }
        };

        let mut blueprint = Blueprint::new();
        for (k, v) in deps {
            if let Value::Table(mut map) = v {
                let stage = if let Some(value) = map.remove("stage") {
                    value.as_integer()
                        .ok_or_else(|| {
                            format!("dependencies.{}.stage must be an integer",
                                    k)
                        })?
                } else {
                    0
                };

                let path =
                    src.path().join(format!("lib{}", k)).display().to_string();

                map.insert("path".to_owned(), Value::String(path));

                blueprint.push(stage, k, map);
            } else {
                Err(format!("Xargo.toml: target.{}.dependencies.{} must be \
                             a table",
                            target,
                            k))?
            }
        }

        Ok(blueprint)
    }

    fn push(&mut self, stage: i64, krate: String, toml: Table) {
        let stage = self.stages.entry(stage).or_insert_with(|| {
            Stage {
                crates: vec![],
                toml: Table::new(),
            }
        });

        stage.toml.insert(krate.clone(), Value::Table(toml));
        stage.crates.push(krate);
    }

    fn hash<H>(&self, hasher: &mut H)
        where H: Hasher
    {
        for stage in self.stages.values() {
            for (k, v) in stage.toml.iter() {
                k.hash(hasher);
                v.to_string().hash(hasher);
            }
        }
    }
}
<commit_msg>allow inserting foreign crates into the sysroot<commit_after>use std::collections::BTreeMap;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::process::Command;

use rustc_version::VersionMeta;
use tempdir::TempDir;
use toml::{Table, Value};

use CompilationMode;
use cargo::{Root, Rustflags};
use errors::*;
use extensions::CommandExt;
use rustc::{Src, Sysroot};
use util;
use xargo::Home;
use {cargo, xargo};

#[cfg(feature = "dev")]
fn profile() -> &'static str {
    "debug"
}

#[cfg(not(feature = "dev"))]
fn profile() -> &'static str {
    "release"
}

fn build(cmode: &CompilationMode,
         blueprint: Blueprint,
         ctoml: &cargo::Toml,
         home: &Home,
         rustflags: &Rustflags,
         hash: u64,
         verbose: bool)
         -> Result<()> {
    const TOML: &'static str = r#"
[package]
authors = ["The Rust Project Developers"]
name = "sysroot"
version = "0.0.0"
"#;

    let rustlib = home.lock_rw(cmode.triple())?;
    rustlib.remove_siblings()
        .chain_err(|| format!("couldn't clear {}", rustlib.path().display()))?;
    let dst = rustlib.parent().join("lib");
    util::mkdir(&dst)?;
    for (_, stage) in blueprint.stages {
        let td = TempDir::new("xargo")
            .chain_err(|| "couldn't create a temporary directory")?;
        let td = td.path();

        let mut stoml = TOML.to_owned();

        let mut map = Table::new();
        map.insert("dependencies".to_owned(), Value::Table(stage.toml));
        stoml.push_str(&Value::Table(map).to_string());

        if let Some(profile) = ctoml.profile() {
            stoml.push_str(&profile.to_string())
        }

        util::write(&td.join("Cargo.toml"), &stoml)?;
        util::mkdir(&td.join("src"))?;
        util::write(&td.join("src/lib.rs"), "")?;

        let cargo = || {
            let mut cmd = Command::new("cargo");
            cmd.env("RUSTFLAGS", rustflags.for_xargo(home));
            cmd.env_remove("CARGO_TARGET_DIR");
            cmd.arg("build");

            match () {
                #[cfg(feature = "dev")]
                () => {}
                #[cfg(not(feature = "dev"))]
                () => {
                    cmd.arg("--release");
                }
            }
            cmd.arg("--manifest-path");
            cmd.arg(td.join("Cargo.toml"));
            cmd.args(&["--target", cmode.triple()]);

            if verbose {
                cmd.arg("-v");
            }

            cmd
        };

        for krate in stage.crates {
            cargo().arg("-p").arg(krate).run(verbose)?;
        }

        // Copy artifacts to Xargo sysroot
        util::cp_r(&td.join("target")
                       .join(cmode.triple())
                       .join(profile())
                       .join("deps"),
                   &dst)?;

    }

    // Create hash file
    util::write(&rustlib.parent().join(".hash"), &hash.to_string())?;

    Ok(())
}

fn old_hash(cmode: &CompilationMode, home: &Home) -> Result<Option<u64>> {
    // FIXME this should be `lock_ro`
    let lock = home.lock_rw(cmode.triple())?;
    let hfile = lock.parent().join(".hash");

    if hfile.exists() {
        Ok(util::read(&hfile)?.parse().ok())
    } else {
        Ok(None)
    }
}

/// Computes the hash of the would-be target sysroot
///
/// This information is used to compute the hash
///
/// - Dependencies in `Xargo.toml` for a specific target
/// - RUSTFLAGS / build.rustflags / target.*.rustflags
/// - The target specification file, is any
/// - `[profile.release]` in `Cargo.toml`
/// - `rustc` commit hash
fn hash(cmode: &CompilationMode,
        blueprint: &Blueprint,
        rustflags: &Rustflags,
        ctoml: &cargo::Toml,
        meta: &VersionMeta)
        -> Result<u64> {
    let mut hasher = DefaultHasher::new();

    blueprint.hash(&mut hasher);

    rustflags.hash(&mut hasher);

    cmode.hash(&mut hasher)?;

    if let Some(profile) = ctoml.profile() {
        profile.hash(&mut hasher);
    }

    if let Some(ref hash) = meta.commit_hash {
        hash.hash(&mut hasher);
    }

    Ok(hasher.finish())
}

pub fn update(cmode: &CompilationMode,
              home: &Home,
              root: &Root,
              rustflags: &Rustflags,
              meta: &VersionMeta,
              src: &Src,
              sysroot: &Sysroot,
              verbose: bool)
              -> Result<()> {
    let ctoml = cargo::toml(root)?;
    let xtoml = xargo::toml(root)?;

    let blueprint = Blueprint::from(xtoml.as_ref(), cmode.triple(), &src)?;

    let hash = hash(cmode, &blueprint, rustflags, &ctoml, meta)?;

    if old_hash(cmode, home)? != Some(hash) {
        build(cmode, blueprint, &ctoml, home, rustflags, hash, verbose)?;
    }

    // copy host artifacts into the sysroot, if necessary
    if cmode.is_native() {
        return Ok(());
    }

    let lock = home.lock_rw(&meta.host)?;
    let hfile = lock.parent().join(".hash");

    let hash = meta.commit_hash.as_ref().map(|s| &**s).unwrap_or("");
    if hfile.exists() {
        if util::read(&hfile)? == hash {
            return Ok(());
        }
    }

    lock.remove_siblings()
        .chain_err(|| format!("couldn't clear {}", lock.path().display()))?;
    let dst = lock.parent().join("lib");
    util::mkdir(&dst)?;
    util::cp_r(&sysroot.path()
                   .join("lib/rustlib")
                   .join(&meta.host)
                   .join("lib"),
               &dst)?;

    util::write(&hfile, hash)?;

    Ok(())
}

/// Per stage dependencies
#[derive(Debug)]
pub struct Stage {
    crates: Vec<String>,
    toml: Table,
}

/// A sysroot that will be built in "stages"
#[derive(Debug)]
pub struct Blueprint {
    stages: BTreeMap<i64, Stage>,
}

impl Blueprint {
    fn new() -> Self {
        Blueprint { stages: BTreeMap::new() }
    }

    fn from(toml: Option<&xargo::Toml>,
            target: &str,
            src: &Src)
            -> Result<Self> {
        let deps = match (toml.and_then(|t| t.dependencies()),
                          toml.and_then(|t| t.target_dependencies(target))) {
            (Some(value), Some(tvalue)) => {
                let mut deps = value.as_table()
                    .cloned()
                    .ok_or_else(|| {
                        format!("Xargo.toml: `dependencies` must be a table")
                    })?;

                let more_deps = tvalue.as_table()
                    .ok_or_else(|| {
                        format!("Xargo.toml: `target.{}.dependencies` must be \
                                 a table",
                                target)
                    })?;
                for (k, v) in more_deps {
                    if deps.insert(k.to_owned(), v.clone()).is_some() {
                        Err(format!("found duplicate dependency name {}, \
                                     but all dependencies must have a \
                                     unique name",
                                    k))?
                    }
                }

                deps
            }
            (Some(value), None) |
            (None, Some(value)) => {
                if let Some(table) = value.as_table() {
                    table.clone()
                } else {
                    Err(format!("Xargo.toml: target.{}.dependencies must be \
                                 a table",
                                target))?
                }
            }
            (None, None) => {
                // If no dependencies were listed, we assume `core` as the
                // only dependency
                let mut t = BTreeMap::new();
                t.insert("core".to_owned(), Value::Table(BTreeMap::new()));
                t
            }
        };

        let mut blueprint = Blueprint::new();
        for (k, v) in deps {
            if let Value::Table(mut map) = v {
                let stage = if let Some(value) = map.remove("stage") {
                    value.as_integer()
                        .ok_or_else(|| {
                            format!("dependencies.{}.stage must be an integer",
                                    k)
                        })?
                } else {
                    0
                };

                if !map.contains_key("path") && !map.contains_key("git") {
                    let path = src.path()
                        .join(format!("lib{}", k))
                        .display()
                        .to_string();

                    map.insert("path".to_owned(), Value::String(path));
                }

                blueprint.push(stage, k, map);
            } else {
                Err(format!("Xargo.toml: target.{}.dependencies.{} must be \
                             a table",
                            target,
                            k))?
            }
        }

        Ok(blueprint)
    }

    fn push(&mut self, stage: i64, krate: String, toml: Table) {
        let stage = self.stages.entry(stage).or_insert_with(|| {
            Stage {
                crates: vec![],
                toml: Table::new(),
            }
        });

        stage.toml.insert(krate.clone(), Value::Table(toml));
        stage.crates.push(krate);
    }

    fn hash<H>(&self, hasher: &mut H)
        where H: Hasher
    {
        for stage in self.stages.values() {
            for (k, v) in stage.toml.iter() {
                k.hash(hasher);
                v.to_string().hash(hasher);
            }
        }
    }
}
<|endoftext|>


<commit_before>use libc::*;
use std::mem;
use std::collections::VecDeque;
// I tried
// It's a bit hard to write this without a Windows pc on hand...
// But this should work on Windows Vista, 7, and 8
#[link(name = "Xinput9_1_0")]
extern "stdcall" {
	fn XInputGetCapabilities(index: u32, flags: u32, capabilities: *mut Capabilities) -> i32;
	fn XInputGetState(index: u32, state: *mut State) -> i32;
}

#[repr(C)]
struct State {
	packet: i32,
	gamepad: Gamepad
}

#[repr(C)]
struct Capabilities {
	_type: u8,
	sub_type: u8,
	flags: u32,
	gamepad: Gamepad,
	vibration: Vibration
}

bitflags!(
	flags Buttons: u32 {
		const DPAD_UP = 0x0001,
		const DPAD_DOWN = 0x0002,
		const DPAD_LEFT = 0x0004,
		const DPAD_RIGHT = 0x0008,
		const START = 0x0010,
		const BACK = 0x0020,
		const LEFT_THUMB = 0x0040,
		const RIGHT_THUMB = 0x0080,
		const LEFT_SHOULDER = 0x0100,
		const RIGHT_SHOULDER = 0x0200,
		const A = 0x1000,
		const B = 0x2000,
		const X = 0x4000,
		const Y = 0x8000,
	}
);
#[repr(C)]
#[derive(Copy)]
struct Gamepad {
	buttons: Buttons,
	left_trigger: u8,
	right_trigger: u8,
	thumb_lx: i16,
	thumb_ly: i16,
	thumb_rx: i16,
	thumb_ry: i16
}
#[repr(C)]
struct Vibration {
	left_motor_speed: i32,
	right_motor_speed: i32
}

/// Scan for joysticks
pub fn scan() -> Vec<NativeJoystick> {
	(0..4).filter_map(|i| Joystick::new(i).ok()).collect()
}

pub struct NativeJoystick {
	index: u8,
	last: Gamepad,
	last_packet: i32,
	events: VecDeque<::Event>
}
impl ::Joystick for NativeJoystick {
	type WithState = NativeJoystick;
	fn new(index: u8) -> Result<NativeJoystick, &'static str> {
		unsafe {
			let mut caps: Capabilities = mem::uninitialized();
			match XInputGetCapabilities(index as u32, 0, &mut caps) {
				0 =>
					Ok(NativeJoystick {
						index: index,
						last: caps.gamepad,
						last_packet: 0,
						events: VecDeque::with_capacity(10)
					}),
				0x48F => Err("Device not connected"),
				_ => Err("Unknown Error")
			}
		}
	}
	fn poll(&mut self) -> Option<::Event> {
		use ::StatefulJoystick;
		self.update();
		self.events.pop_back()
	}
	fn is_connected(&self) -> bool {
		true
	}
	fn get_id(&self) -> String {
		String::from_str("XInput Device")
	}
	fn get_index(&self) -> u8 {
		self.index
	}
	fn get_num_buttons(&self) -> u8 {
		4
	}
	fn get_num_axes(&self) -> u8 {
		4
	}
	fn with_state(self) -> NativeJoystick {
		self
	}
}
macro_rules! event{
	(button $this:expr, $now:expr, $last:expr, $btn:expr, $id:expr) => (
		{
			let now_btn = $now.buttons.contains($btn);
			let last_btn = $last.buttons.contains($btn);
			if now_btn && !last_btn {
				$this.events.push_back(::Event::ButtonPressed($id))
			} else if !now_btn && last_btn {
				$this.events.push_back(::Event::ButtonReleased($id))
			}
		}
	);
	(axis $this:expr, $now:expr, $last:expr, $field:ident, $id:expr) => (
		if $now.$field != $last.$field {
			$this.events.push_back(::Event::JoystickMoved($id, $now.$field))
		}
	);
	(axes $this:expr, $now:expr, $last:expr, $($field:ident => $id:expr),+) => ({
		$(event!(axis $this, $now, $last, $field, $id);)+
	});
	(buttons $this:expr, $now:expr, $last:expr, $($btn:ident => $id:expr),+) => ({
		$(event!(button $this, $now, $last, $btn, $id);)+
	});
}
impl ::StatefulJoystick for NativeJoystick {
	fn get_axis(&self, index: u8) -> Option<i16> {
		match index {
			0 => Some(self.last.thumb_lx),
			1 => Some(self.last.thumb_ly),
			2 => Some(self.last.thumb_rx),
			3 => Some(self.last.thumb_ry),
			_ => None
		}
	}
	fn get_button(&self, index: u8) -> Option<bool> {
		let bits = match index {
		0 => Some(Y),
			1 => Some(A),
			2 => Some(B),
			3 => Some(X),
			4 => Some(LEFT_SHOULDER),
			5 => Some(RIGHT_SHOULDER),
			8 => Some(BACK),
			9 => Some(START),
			10 => Some(LEFT_TRIGGER),
			11 => Some(RIGHT_TRIGGER),
			_ => None
		};
		bits.map(|v| self.last.buttons.contains(v))
	}
	fn update(&mut self) {
		let state = unsafe {
			let mut state: State = mem::uninitialized();
			XInputGetState(self.index as u32, &mut state);
			state
		};
		let now = state.gamepad;
		if state.packet != self.last_packet {
			let last = self.last;
			event!{axes self, now, last,
				thumb_lx => 0,
				thumb_ly => 1,
				thumb_rx => 2,
				thumb_ry => 3
			}
			event!{buttons self, now, last,
			Y => 0,
				A => 1,
				B => 2,
				X => 3,
				LEFT_SHOULDER => 4,
				RIGHT_SHOULDER => 5,
				BACK => 8,
				START => 9,
				LEFT_TRIGGER => 10,
				RIGHT_TRIGGER => 11
			}
		}
		self.last_packet = state.packet;
		self.last = now;
	}
}
<commit_msg>Fix windows support<commit_after>use libc::*;
use std::mem;
use std::collections::VecDeque;
// I tried
// It's a bit hard to write this without a Windows pc on hand...
// But this should work on Windows Vista, 7, and 8
#[link(name = "Xinput9_1_0")]
extern "stdcall" {
	fn XInputGetCapabilities(index: u32, flags: u32, capabilities: *mut Capabilities) -> i32;
	fn XInputGetState(index: u32, state: *mut State) -> i32;
}

#[repr(C)]
struct State {
	packet: i32,
	gamepad: Gamepad
}

#[repr(C)]
struct Capabilities {
	_type: u8,
	sub_type: u8,
	flags: u32,
	gamepad: Gamepad,
	vibration: Vibration
}

bitflags!(
	flags Buttons: u32 {
		const DPAD_UP = 0x0001,
		const DPAD_DOWN = 0x0002,
		const DPAD_LEFT = 0x0004,
		const DPAD_RIGHT = 0x0008,
		const START = 0x0010,
		const BACK = 0x0020,
		const LEFT_THUMB = 0x0040,
		const RIGHT_THUMB = 0x0080,
		const LEFT_SHOULDER = 0x0100,
		const RIGHT_SHOULDER = 0x0200,
		const A = 0x1000,
		const B = 0x2000,
		const X = 0x4000,
		const Y = 0x8000,
	}
);
#[repr(C)]
#[derive(Copy)]
struct Gamepad {
	buttons: Buttons,
	left_trigger: u8,
	right_trigger: u8,
	thumb_lx: i16,
	thumb_ly: i16,
	thumb_rx: i16,
	thumb_ry: i16
}
#[repr(C)]
struct Vibration {
	left_motor_speed: i32,
	right_motor_speed: i32
}

/// Scan for joysticks
pub fn scan() -> Vec<NativeJoystick> {
	(0..4).filter_map(|i| ::Joystick::new(i).ok()).collect()
}

pub struct NativeJoystick {
	index: u8,
	last: Gamepad,
	last_packet: i32,
	events: VecDeque<::Event>
}
impl ::Joystick for NativeJoystick {
	type WithState = NativeJoystick;
	fn new(index: u8) -> Result<NativeJoystick, &'static str> {
		unsafe {
			let mut caps: Capabilities = mem::uninitialized();
			match XInputGetCapabilities(index as u32, 0, &mut caps) {
				0 =>
					Ok(NativeJoystick {
						index: index,
						last: caps.gamepad,
						last_packet: 0,
						events: VecDeque::with_capacity(10)
					}),
				0x48F => Err("Device not connected"),
				_ => Err("Unknown Error")
			}
		}
	}
	fn poll(&mut self) -> Option<::Event> {
		use ::StatefulJoystick;
		self.update();
		self.events.pop_back()
	}
	fn is_connected(&self) -> bool {
		true
	}
	fn get_id(&self) -> String {
		"XInput Device".to_string()
	}
	fn get_index(&self) -> u8 {
		self.index
	}
	fn get_num_buttons(&self) -> u8 {
		4
	}
	fn get_num_axes(&self) -> u8 {
		4
	}
	fn with_state(self) -> NativeJoystick {
		self
	}
}
macro_rules! event{
	(button $this:expr, $now:expr, $last:expr, $btn:expr, $id:expr) => (
		{
			let now_btn = $now.buttons.contains($btn);
			let last_btn = $last.buttons.contains($btn);
			if now_btn && !last_btn {
				$this.events.push_back(::Event::ButtonPressed($id))
			} else if !now_btn && last_btn {
				$this.events.push_back(::Event::ButtonReleased($id))
			}
		}
	);
	(axis $this:expr, $now:expr, $last:expr, $field:ident, $id:expr) => (
		if $now.$field != $last.$field {
			$this.events.push_back(::Event::JoystickMoved($id, $now.$field))
		}
	);
	(axes $this:expr, $now:expr, $last:expr, $($field:ident => $id:expr),+) => ({
		$(event!(axis $this, $now, $last, $field, $id);)+
	});
	(buttons $this:expr, $now:expr, $last:expr, $($btn:ident => $id:expr),+) => ({
		$(event!(button $this, $now, $last, $btn, $id);)+
	});
}
impl ::StatefulJoystick for NativeJoystick {
	fn get_axis(&self, index: ::Axis) -> Option<i16> {
		match index {
			::Axis::LeftX => Some(self.last.thumb_lx),
			::Axis::LeftY => Some(self.last.thumb_ly),
			::Axis::RightX => Some(self.last.thumb_rx),
			::Axis::RightY => Some(self.last.thumb_ry),
			_ => None
		}
	}
	fn get_button(&self, index: ::Button) -> Option<bool> {
		let bits = match index {
			::Button::A => Some(A),
			::Button::B => Some(B),
			::Button::X => Some(X),
			::Button::Y => Some(Y),
			::Button::LeftShoulder => Some(LEFT_SHOULDER),
			::Button::RightShoulder => Some(RIGHT_SHOULDER),
			::Button::Back => Some(BACK),
			::Button::Guide => Some(START),
			::Button::LeftStick => Some(LEFT_THUMB),
			::Button::RightStick => Some(RIGHT_THUMB),
			_ => None
		};
		bits.map(|v| self.last.buttons.contains(v))
	}
	fn update(&mut self) {
		let state = unsafe {
			let mut state: State = mem::uninitialized();
			XInputGetState(self.index as u32, &mut state);
			state
		};
		let now = state.gamepad;
		if state.packet != self.last_packet {
			let last = self.last;
			event!{axes self, now, last,
				thumb_lx => ::Axis::LeftX,
				thumb_ly => ::Axis::LeftY,
				thumb_rx => ::Axis::RightX,
				thumb_ry => ::Axis::RightY
			}
			event!{buttons self, now, last,
				A => ::Button::A,
				B => ::Button::B,
				X => ::Button::X,
				Y => ::Button::Y,
				LEFT_SHOULDER => ::Button::LeftShoulder,
				RIGHT_SHOULDER => ::Button::RightShoulder,
				BACK => ::Button::Back,
				START => ::Button::Guide,
				LEFT_THUMB => ::Button::LeftStick,
				RIGHT_THUMB => ::Button::RightStick
			}
		}
		self.last_packet = state.packet;
		self.last = now;
	}
}
<|endoftext|>


<commit_before>use rand::Rng;
use field::Pos;

#[derive(Clone, PartialEq, Debug)]
pub struct Zobrist {
  hashes: Vec<u64>
}

impl Zobrist {
  pub fn new<T: Rng>(length: Pos, rng: &mut T) -> Zobrist {
    let mut zobrist = Zobrist {
      hashes: Vec::with_capacity(length)
    };
    for _ in 0 .. length {
      zobrist.hashes.push(rng.gen());
    }
    zobrist
  }

  pub fn get_hash(&self, pos: Pos) -> u64 {
    self.hashes[pos]
  }
}
<commit_msg>Use itertools.<commit_after>use itertools;
use rand::Rng;
use field::Pos;

#[derive(Clone, PartialEq, Debug)]
pub struct Zobrist {
  hashes: Vec<u64>
}

impl Zobrist {
  pub fn new<T: Rng>(length: Pos, rng: &mut T) -> Zobrist {
    Zobrist {
      hashes: itertools::repeat_call(|| rng.gen()).take(length).collect()
    }
  }

  pub fn get_hash(&self, pos: Pos) -> u64 {
    self.hashes[pos]
  }
}
<|endoftext|>