<commit_before>/*
 * Copyright 2017 Ben Ashford
 *
 * 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 client for Redis using Tokio and Futures.
//!
//! Three interfaces are provided: one low-level, that makes no assumptions about how Redis is used; a high-level client,
//! suitable for the vast majority of use-cases; a PUBSUB client specifically for Redis's PUBSUB functionality.
//!
//! ## Low-level
//!
//! [`client::connect`](client/fn.connect.html) returns a pair of `Sink` and `Stream` (see [futures](https://github.com/alexcrichton/futures-rs)) which
//! both transport [`resp::RespValue`](resp/enum.RespValue.html)s between client and Redis, these work independently of one another
//! to allow pipelining.  It is the responsibility of the caller to match responses to requests.  It is also the
//! responsibility of the client to convert application data into instances of [`resp::RespValue`](resp/enum.RespValue.html) and
//! back (there are conversion traits available for common examples).
//!
//! This is a very low-level API compared to most Redis clients, but is done so intentionally, for two reasons: 1) it is
//! the common demoniator between a functional Redis client (i.e. is able to support all types of requests, including those
//! that block and have streaming responses), and 2) it results in clean `Sink`s and `Stream`s which will be composable
//! with other Tokio-based libraries.
//!
//! For most practical purposes this low-level interface will not be used, the only exception possibly being the
//! [`MONITOR`](https://redis.io/commands/monitor) command.
//!
//! ## High-level
//!
//! [`client::paired_connect`](client/fn.paired_connect.html) is used for most Redis commands (those for which one command
//! returns one response, it's not suitable for PUBSUB, `MONITOR` or other similar commands).  It allows a Redis command to
//! be sent and a Future returned for each command.
//!
//! Commands will be sent in the order that [`send`](client/struct.PairedConnection.html#method.send) is called, regardless
//! of how the future is realised.  This is to allow us to take advantage of Redis's features by implicitly pipelining
//! commands where appropriate.  One side-effect of this is that for many commands, e.g. `SET` we don't need to realise the
//! future at all, it can be assumed to be fire-and-forget; but, the final future of the final command does need to be
//! realised (at least) to ensure that the correct behaviour is observed.
//!
//! ## PUBSUB
//!
//! PUBSUB in Redis works differently.  A connection will subscribe to one or more topics, then receive all messages that
//! are published to that topic.  As such the single-request/single-response model of
//! [`paired_connect`](client/fn.paired_connect.html) will not work.  A specific
//! [`client::pubsub_connect`](client/fn.pubsub_connect.html) is provided for this purpose.
//!
//! It returns a future which resolves to a [`PubsubConnection`](client/struct.PubsubConnection.html), this provides a
//! [`subscribe`](client/struct.PubsubConnection.html#method.subscribe) function that takes a topic as a parameter and
//! returns a future which, once the subscription is confirmed, resolves to a stream that contains all messages published
//! to that topic.

#![feature(trace_macros)]

extern crate bytes;
extern crate futures;
extern crate tokio_core;
extern crate tokio_io;

#[macro_use]
pub mod resp;

#[macro_use]
pub mod client;

pub mod error;
<commit_msg>Allow builds on stable as well as nightly<commit_after>/*
 * Copyright 2017 Ben Ashford
 *
 * 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 client for Redis using Tokio and Futures.
//!
//! Three interfaces are provided: one low-level, that makes no assumptions about how Redis is used; a high-level client,
//! suitable for the vast majority of use-cases; a PUBSUB client specifically for Redis's PUBSUB functionality.
//!
//! ## Low-level
//!
//! [`client::connect`](client/fn.connect.html) returns a pair of `Sink` and `Stream` (see [futures](https://github.com/alexcrichton/futures-rs)) which
//! both transport [`resp::RespValue`](resp/enum.RespValue.html)s between client and Redis, these work independently of one another
//! to allow pipelining.  It is the responsibility of the caller to match responses to requests.  It is also the
//! responsibility of the client to convert application data into instances of [`resp::RespValue`](resp/enum.RespValue.html) and
//! back (there are conversion traits available for common examples).
//!
//! This is a very low-level API compared to most Redis clients, but is done so intentionally, for two reasons: 1) it is
//! the common demoniator between a functional Redis client (i.e. is able to support all types of requests, including those
//! that block and have streaming responses), and 2) it results in clean `Sink`s and `Stream`s which will be composable
//! with other Tokio-based libraries.
//!
//! For most practical purposes this low-level interface will not be used, the only exception possibly being the
//! [`MONITOR`](https://redis.io/commands/monitor) command.
//!
//! ## High-level
//!
//! [`client::paired_connect`](client/fn.paired_connect.html) is used for most Redis commands (those for which one command
//! returns one response, it's not suitable for PUBSUB, `MONITOR` or other similar commands).  It allows a Redis command to
//! be sent and a Future returned for each command.
//!
//! Commands will be sent in the order that [`send`](client/struct.PairedConnection.html#method.send) is called, regardless
//! of how the future is realised.  This is to allow us to take advantage of Redis's features by implicitly pipelining
//! commands where appropriate.  One side-effect of this is that for many commands, e.g. `SET` we don't need to realise the
//! future at all, it can be assumed to be fire-and-forget; but, the final future of the final command does need to be
//! realised (at least) to ensure that the correct behaviour is observed.
//!
//! ## PUBSUB
//!
//! PUBSUB in Redis works differently.  A connection will subscribe to one or more topics, then receive all messages that
//! are published to that topic.  As such the single-request/single-response model of
//! [`paired_connect`](client/fn.paired_connect.html) will not work.  A specific
//! [`client::pubsub_connect`](client/fn.pubsub_connect.html) is provided for this purpose.
//!
//! It returns a future which resolves to a [`PubsubConnection`](client/struct.PubsubConnection.html), this provides a
//! [`subscribe`](client/struct.PubsubConnection.html#method.subscribe) function that takes a topic as a parameter and
//! returns a future which, once the subscription is confirmed, resolves to a stream that contains all messages published
//! to that topic.

extern crate bytes;
extern crate futures;
extern crate tokio_core;
extern crate tokio_io;

#[macro_use]
pub mod resp;

#[macro_use]
pub mod client;

pub mod error;
<|endoftext|>


<commit_before>//! Composable primitives for asynchronous computations
//!
//! The async module contains utilities for managing asynchronous computations.
//! These utilities are primarily based around `Future` and `Stream` types as
//! well as functions that allow composing computations on these types.
//!
//! ## Future
//!
//! A `Future` is a proxy representing the result of a computation which may
//! not be complete.  The computation may be running concurrently in another
//! thread or may be triggered upon completion of an asynchronous callback. One
//! way to think of a `Future` is as a `Result` where the value is
//! asynchronously computed.
//!
//! For example:
//!
//! ```
//! use eventual::*;
//!
//! // Run a computation in another thread
//! let future1 = Future::spawn(|| {
//!     // Represents an expensive computation, but for now just return a
//!     // number
//!     42
//! });
//!
//! // Run another computation
//! let future2 = Future::spawn(|| {
//!     // Another expensive computation
//!     18
//! });
//!
//! let res = join((
//!         future1.map(|v| v * 2),
//!         future2.map(|v| v + 5)))
//!     .and_then(|(v1, v2)| v1 - v2)
//!     .await().unwrap();
//!
//! assert_eq!(61, res);
//!
//! ```
//!
//! ## Stream
//!
//! A `Stream` is like a `Future`, except that instead of representing a single
//! value, it represents a sequence of values.
//!

#![crate_name = "eventual"]

extern crate syncbox;
extern crate time;

#[macro_use]
extern crate log;

pub use self::future::{Future, Complete};
pub use self::join::{join, Join};
pub use self::receipt::Receipt;
pub use self::select::{select, Select};
pub use self::sequence::sequence;
pub use self::stream::{Stream, StreamIter, Sender, BusySender};
pub use self::timer::Timer;

use std::fmt;

// ## TODO
//
// * Switch generics to where clauses
//   - rust-lang/rust#20300 (T::Foo resolution)
//
// * Allow Async::or & Async::or_else to change the error type
//
// * Improve performance / reduce allocations

mod core;
mod future;
mod join;
mod process;
mod receipt;
mod select;
mod sequence;
mod stream;
mod timer;

/// A value representing an asynchronous computation
pub trait Async : Send + 'static + Sized {
    type Value: Send + 'static;
    type Error: Send + 'static;
    type Cancel: Cancel<Self>;

    /// Returns true if `expect` will succeed.
    fn is_ready(&self) -> bool;

    /// Returns true if the async value is ready and has failed
    fn is_err(&self) -> bool;

    /// Get the underlying value if present
    fn poll(self) -> Result<AsyncResult<Self::Value, Self::Error>, Self>;

    /// Get the underlying value if present, panic otherwise
    fn expect(self) -> AsyncResult<Self::Value, Self::Error> {
        if let Ok(v) = self.poll() {
            return v;
        }

        panic!("the async value is not ready");
    }

    /// Invokes the given function when the Async instance is ready to be
    /// consumed.
    fn ready<F>(self, f: F) -> Self::Cancel where F: FnOnce(Self) + Send + 'static;

    /// Invoke the callback with the resolved `Async` result.
    fn receive<F>(self, f: F)
            where F: FnOnce(AsyncResult<Self::Value, Self::Error>) + Send + 'static {
        self.ready(move |async| {
            match async.poll() {
                Ok(res) => f(res),
                Err(_) => panic!("ready callback invoked but is not actually ready"),
            }
        });
    }

    /// Blocks the thread until the async value is complete and returns the
    /// result.
    fn await(self) -> AsyncResult<Self::Value, Self::Error> {
        use std::sync::mpsc::channel;

        let (tx, rx) = channel();

        self.receive(move |res| tx.send(res).ok().expect("receiver thread died"));
        rx.recv().ok().expect("async disappeared without a trace")
    }

    /// Trigger the computation without waiting for the result
    fn fire(self) {
        self.receive(drop)
    }

    /*
     *
     * ===== Computation Builders =====
     *
     */

    /// This method returns a future whose completion value depends on the
    /// completion value of the original future.
    ///
    /// If the original future completes with an error, the future returned by
    /// this method completes with that error.
    ///
    /// If the original future completes successfully, the future returned by
    /// this method completes with the completion value of `next`.
    fn and<U: Async<Error=Self::Error>>(self, next: U) -> Future<U::Value, Self::Error> {
        self.and_then(move |_| next)
    }

    /// This method returns a future whose completion value depends on the
    /// completion value of the original future.
    ///
    /// If the original future completes with an error, the future returned by
    /// this method completes with that error.
    ///
    /// If the original future completes successfully, the callback to this
    /// method is called with the value, and the callback returns a new future.
    /// The future returned by this method then completes with the completion
    /// value of that returned future.
    ///
    /// ```
    /// use eventual::*;
    ///
    /// let f = Future::of(1337);
    ///
    /// f.and_then(|v| {
    ///     assert_eq!(v, 1337);
    ///     Ok(1007)
    /// }).and_then(|v| {
    ///     assert_eq!(v, 1007)
    /// }).await();
    ///
    /// let e = Future::<(), &'static str>::error("failed");
    ///
    /// e.and_then(|v| {
    ///     panic!("unreachable");
    ///     Ok(())
    /// }).await();
    /// ```
    fn and_then<F, U: Async<Error=Self::Error>>(self, f: F) -> Future<U::Value, Self::Error>
            where F: FnOnce(Self::Value) -> U + Send + 'static,
                  U::Value: Send + 'static {
        let (complete, ret) = Future::pair();

        complete.receive(move |c| {
            if let Ok(complete) = c {
                self.receive(move |res| {
                    match res {
                        Ok(v) => {
                            f(v).receive(move |res| {
                                match res {
                                    Ok(u) => complete.complete(u),
                                    Err(AsyncError::Failed(e)) => complete.fail(e),
                                    _ => {}
                                }
                            });
                        }
                        Err(AsyncError::Failed(e)) => complete.fail(e),
                        _ => {}
                    }
                });
            }
        });

        ret
    }

    /// This method returns a future whose completion value depends on the
    /// completion value of the original future.
    ///
    /// If the original future completes successfully, the future returned by
    /// this method will complete with that value.
    ///
    /// If the original future completes with an error, the future returned by
    /// this method will complete with the completion value of the `alt` future
    /// passed in. That can be either a success or error.
    fn or<A>(self, alt: A) -> Future<Self::Value, A::Error>
            where A: Async<Value=Self::Value> {
        self.or_else(move |_| alt)
    }

    /// This method returns a future whose completion value depends on the
    /// completion value of the original future.
    ///
    /// If the original future completes successfully, the future returned by
    /// this method will complete with that value.
    ///
    /// If the original future completes with an error, this method will invoke
    /// the callback passed to the method, which should return a future. The
    /// future returned by this method will complete with the completion value
    /// of that future. That can be either a success or error.
    fn or_else<F, A>(self, f: F) -> Future<Self::Value, A::Error>
            where F: FnOnce(Self::Error) -> A + Send + 'static,
                  A: Async<Value=Self::Value> {

        let (complete, ret) = Future::pair();

        complete.receive(move |c| {
            if let Ok(complete) = c {
                self.receive(move |res| {
                    match res {
                        Ok(v) => complete.complete(v),
                        Err(AsyncError::Failed(e)) => {
                            f(e).receive(move |res| {
                                match res {
                                    Ok(v) => complete.complete(v),
                                    Err(AsyncError::Failed(e)) => complete.fail(e),
                                    _ => {}
                                }
                            });
                        }
                        Err(AsyncError::Aborted) => drop(complete),
                    }
                });
            }
        });

        ret
    }
}

pub trait Pair {
    type Tx;

    fn pair() -> (Self::Tx, Self);
}

pub trait Cancel<A: Send + 'static> : Send + 'static {
    fn cancel(self) -> Option<A>;
}

/*
 *
 * ===== Async implementations =====
 *
 */

impl<T: Send + 'static, E: Send + 'static> Async for Result<T, E> {
    type Value = T;
    type Error = E;
    type Cancel = Option<Result<T, E>>;

    fn is_ready(&self) -> bool {
        true
    }

    fn is_err(&self) -> bool {
        self.is_err()
    }

    fn poll(self) -> Result<AsyncResult<T, E>, Result<T, E>> {
        Ok(self.await())
    }

    fn ready<F: FnOnce(Result<T, E>) + Send + 'static>(self, f: F) -> Option<Result<T, E>> {
        f(self);
        None
    }

    fn await(self) -> AsyncResult<T, E> {
        self.map_err(|e| AsyncError::Failed(e))
    }
}

impl<A: Send + 'static> Cancel<A> for Option<A> {
    fn cancel(self) -> Option<A> {
        self
    }
}

/*
 *
 * ===== Async implementations =====
 *
 */

macro_rules! async_impl_body {
    ($ty:ty) => (
        fn is_ready(&self) -> bool {
            true
        }

        fn is_err(&self) -> bool {
            false
        }

        fn poll(self) -> Result<AsyncResult<$ty, ()>, $ty> {
            Ok(Ok(self))
        }

        fn ready<F: FnOnce($ty) + Send + 'static>(self, f: F) -> Option<$ty> {
            f(self);
            None
        }

        fn await(self) -> AsyncResult<$ty, ()> {
            Ok(self)
        }
    );
}

macro_rules! async_impl {
    ($ty:ty) => (
        impl Async for $ty {
            type Value  = $ty;
            type Error  = ();
            type Cancel = Option<$ty>;

            async_impl_body!($ty);
        }
    );

    ($ty:ty, $($rest:tt),*) => (
        async_impl!($ty);
        async_impl!($($rest),*);
    );
}

// For now, implement on as many concrete types as possible. One day, Rust will
// support specialization (hopefully) and this won't be necessary.
async_impl!(
    (), bool, String,
    u8, u16, u32, u64, usize,
    i8, i16, i32, i64, isize);

impl<E: Send + 'static> Async for Vec<E> {
    type Value = Vec<E>;
    type Error = ();
    type Cancel = Option<Vec<E>>;

    async_impl_body!(Vec<E>);
}

/*
 *
 * ===== AsyncResult =====
 *
 */

pub type AsyncResult<T, E> = Result<T, AsyncError<E>>;

#[derive(Eq, PartialEq)]
pub enum AsyncError<E: Send + 'static> {
    Failed(E),
    Aborted,
}

impl<E: Send + 'static> AsyncError<E> {
    pub fn failed(err: E) -> AsyncError<E> {
        AsyncError::Failed(err)
    }

    pub fn aborted() -> AsyncError<E> {
        AsyncError::Aborted
    }

    pub fn is_aborted(&self) -> bool {
        match *self {
            AsyncError::Aborted => true,
            _ => false,
        }
    }

    pub fn is_failed(&self) -> bool {
        match *self {
            AsyncError::Failed(..) => true,
            _ => false,
        }
    }

    pub fn unwrap(self) -> E {
        match self {
            AsyncError::Failed(err) => err,
            AsyncError::Aborted => panic!("unwrapping a cancellation error"),
        }
    }

    pub fn take(self) -> Option<E> {
        match self {
            AsyncError::Failed(err) => Some(err),
            _ => None,
        }
    }
}

impl<E: Send + 'static + fmt::Debug> fmt::Debug for AsyncError<E> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            AsyncError::Failed(ref e) => write!(fmt, "AsyncError::Failed({:?})", e),
            AsyncError::Aborted => write!(fmt, "AsyncError::Aborted"),
        }
    }
}

/*
 *
 * ===== BoxedReceive =====
 *
 */

// Needed to allow virtual dispatch to Receive
trait BoxedReceive<T> : Send + 'static {
    fn receive_boxed(self: Box<Self>, val: T);
}

impl<F: FnOnce(T) + Send + 'static, T> BoxedReceive<T> for F {
    fn receive_boxed(self: Box<F>, val: T) {
        (*self)(val)
    }
}
<commit_msg>Use Ok() with and_then() in main docs example (fixes #10)<commit_after>//! Composable primitives for asynchronous computations
//!
//! The async module contains utilities for managing asynchronous computations.
//! These utilities are primarily based around `Future` and `Stream` types as
//! well as functions that allow composing computations on these types.
//!
//! ## Future
//!
//! A `Future` is a proxy representing the result of a computation which may
//! not be complete.  The computation may be running concurrently in another
//! thread or may be triggered upon completion of an asynchronous callback. One
//! way to think of a `Future` is as a `Result` where the value is
//! asynchronously computed.
//!
//! For example:
//!
//! ```
//! use eventual::*;
//!
//! // Run a computation in another thread
//! let future1 = Future::spawn(|| {
//!     // Represents an expensive computation, but for now just return a
//!     // number
//!     42
//! });
//!
//! // Run another computation
//! let future2 = Future::spawn(|| {
//!     // Another expensive computation
//!     18
//! });
//!
//! let res = join((
//!         future1.map(|v| v * 2),
//!         future2.map(|v| v + 5)))
//!     .and_then(|(v1, v2)| Ok(v1 - v2))
//!     .await().unwrap();
//!
//! assert_eq!(61, res);
//!
//! ```
//!
//! ## Stream
//!
//! A `Stream` is like a `Future`, except that instead of representing a single
//! value, it represents a sequence of values.
//!

#![crate_name = "eventual"]

extern crate syncbox;
extern crate time;

#[macro_use]
extern crate log;

pub use self::future::{Future, Complete};
pub use self::join::{join, Join};
pub use self::receipt::Receipt;
pub use self::select::{select, Select};
pub use self::sequence::sequence;
pub use self::stream::{Stream, StreamIter, Sender, BusySender};
pub use self::timer::Timer;

use std::fmt;

// ## TODO
//
// * Switch generics to where clauses
//   - rust-lang/rust#20300 (T::Foo resolution)
//
// * Allow Async::or & Async::or_else to change the error type
//
// * Improve performance / reduce allocations

mod core;
mod future;
mod join;
mod process;
mod receipt;
mod select;
mod sequence;
mod stream;
mod timer;

/// A value representing an asynchronous computation
pub trait Async : Send + 'static + Sized {
    type Value: Send + 'static;
    type Error: Send + 'static;
    type Cancel: Cancel<Self>;

    /// Returns true if `expect` will succeed.
    fn is_ready(&self) -> bool;

    /// Returns true if the async value is ready and has failed
    fn is_err(&self) -> bool;

    /// Get the underlying value if present
    fn poll(self) -> Result<AsyncResult<Self::Value, Self::Error>, Self>;

    /// Get the underlying value if present, panic otherwise
    fn expect(self) -> AsyncResult<Self::Value, Self::Error> {
        if let Ok(v) = self.poll() {
            return v;
        }

        panic!("the async value is not ready");
    }

    /// Invokes the given function when the Async instance is ready to be
    /// consumed.
    fn ready<F>(self, f: F) -> Self::Cancel where F: FnOnce(Self) + Send + 'static;

    /// Invoke the callback with the resolved `Async` result.
    fn receive<F>(self, f: F)
            where F: FnOnce(AsyncResult<Self::Value, Self::Error>) + Send + 'static {
        self.ready(move |async| {
            match async.poll() {
                Ok(res) => f(res),
                Err(_) => panic!("ready callback invoked but is not actually ready"),
            }
        });
    }

    /// Blocks the thread until the async value is complete and returns the
    /// result.
    fn await(self) -> AsyncResult<Self::Value, Self::Error> {
        use std::sync::mpsc::channel;

        let (tx, rx) = channel();

        self.receive(move |res| tx.send(res).ok().expect("receiver thread died"));
        rx.recv().ok().expect("async disappeared without a trace")
    }

    /// Trigger the computation without waiting for the result
    fn fire(self) {
        self.receive(drop)
    }

    /*
     *
     * ===== Computation Builders =====
     *
     */

    /// This method returns a future whose completion value depends on the
    /// completion value of the original future.
    ///
    /// If the original future completes with an error, the future returned by
    /// this method completes with that error.
    ///
    /// If the original future completes successfully, the future returned by
    /// this method completes with the completion value of `next`.
    fn and<U: Async<Error=Self::Error>>(self, next: U) -> Future<U::Value, Self::Error> {
        self.and_then(move |_| next)
    }

    /// This method returns a future whose completion value depends on the
    /// completion value of the original future.
    ///
    /// If the original future completes with an error, the future returned by
    /// this method completes with that error.
    ///
    /// If the original future completes successfully, the callback to this
    /// method is called with the value, and the callback returns a new future.
    /// The future returned by this method then completes with the completion
    /// value of that returned future.
    ///
    /// ```
    /// use eventual::*;
    ///
    /// let f = Future::of(1337);
    ///
    /// f.and_then(|v| {
    ///     assert_eq!(v, 1337);
    ///     Ok(1007)
    /// }).and_then(|v| {
    ///     assert_eq!(v, 1007)
    /// }).await();
    ///
    /// let e = Future::<(), &'static str>::error("failed");
    ///
    /// e.and_then(|v| {
    ///     panic!("unreachable");
    ///     Ok(())
    /// }).await();
    /// ```
    fn and_then<F, U: Async<Error=Self::Error>>(self, f: F) -> Future<U::Value, Self::Error>
            where F: FnOnce(Self::Value) -> U + Send + 'static,
                  U::Value: Send + 'static {
        let (complete, ret) = Future::pair();

        complete.receive(move |c| {
            if let Ok(complete) = c {
                self.receive(move |res| {
                    match res {
                        Ok(v) => {
                            f(v).receive(move |res| {
                                match res {
                                    Ok(u) => complete.complete(u),
                                    Err(AsyncError::Failed(e)) => complete.fail(e),
                                    _ => {}
                                }
                            });
                        }
                        Err(AsyncError::Failed(e)) => complete.fail(e),
                        _ => {}
                    }
                });
            }
        });

        ret
    }

    /// This method returns a future whose completion value depends on the
    /// completion value of the original future.
    ///
    /// If the original future completes successfully, the future returned by
    /// this method will complete with that value.
    ///
    /// If the original future completes with an error, the future returned by
    /// this method will complete with the completion value of the `alt` future
    /// passed in. That can be either a success or error.
    fn or<A>(self, alt: A) -> Future<Self::Value, A::Error>
            where A: Async<Value=Self::Value> {
        self.or_else(move |_| alt)
    }

    /// This method returns a future whose completion value depends on the
    /// completion value of the original future.
    ///
    /// If the original future completes successfully, the future returned by
    /// this method will complete with that value.
    ///
    /// If the original future completes with an error, this method will invoke
    /// the callback passed to the method, which should return a future. The
    /// future returned by this method will complete with the completion value
    /// of that future. That can be either a success or error.
    fn or_else<F, A>(self, f: F) -> Future<Self::Value, A::Error>
            where F: FnOnce(Self::Error) -> A + Send + 'static,
                  A: Async<Value=Self::Value> {

        let (complete, ret) = Future::pair();

        complete.receive(move |c| {
            if let Ok(complete) = c {
                self.receive(move |res| {
                    match res {
                        Ok(v) => complete.complete(v),
                        Err(AsyncError::Failed(e)) => {
                            f(e).receive(move |res| {
                                match res {
                                    Ok(v) => complete.complete(v),
                                    Err(AsyncError::Failed(e)) => complete.fail(e),
                                    _ => {}
                                }
                            });
                        }
                        Err(AsyncError::Aborted) => drop(complete),
                    }
                });
            }
        });

        ret
    }
}

pub trait Pair {
    type Tx;

    fn pair() -> (Self::Tx, Self);
}

pub trait Cancel<A: Send + 'static> : Send + 'static {
    fn cancel(self) -> Option<A>;
}

/*
 *
 * ===== Async implementations =====
 *
 */

impl<T: Send + 'static, E: Send + 'static> Async for Result<T, E> {
    type Value = T;
    type Error = E;
    type Cancel = Option<Result<T, E>>;

    fn is_ready(&self) -> bool {
        true
    }

    fn is_err(&self) -> bool {
        self.is_err()
    }

    fn poll(self) -> Result<AsyncResult<T, E>, Result<T, E>> {
        Ok(self.await())
    }

    fn ready<F: FnOnce(Result<T, E>) + Send + 'static>(self, f: F) -> Option<Result<T, E>> {
        f(self);
        None
    }

    fn await(self) -> AsyncResult<T, E> {
        self.map_err(|e| AsyncError::Failed(e))
    }
}

impl<A: Send + 'static> Cancel<A> for Option<A> {
    fn cancel(self) -> Option<A> {
        self
    }
}

/*
 *
 * ===== Async implementations =====
 *
 */

macro_rules! async_impl_body {
    ($ty:ty) => (
        fn is_ready(&self) -> bool {
            true
        }

        fn is_err(&self) -> bool {
            false
        }

        fn poll(self) -> Result<AsyncResult<$ty, ()>, $ty> {
            Ok(Ok(self))
        }

        fn ready<F: FnOnce($ty) + Send + 'static>(self, f: F) -> Option<$ty> {
            f(self);
            None
        }

        fn await(self) -> AsyncResult<$ty, ()> {
            Ok(self)
        }
    );
}

macro_rules! async_impl {
    ($ty:ty) => (
        impl Async for $ty {
            type Value  = $ty;
            type Error  = ();
            type Cancel = Option<$ty>;

            async_impl_body!($ty);
        }
    );

    ($ty:ty, $($rest:tt),*) => (
        async_impl!($ty);
        async_impl!($($rest),*);
    );
}

// For now, implement on as many concrete types as possible. One day, Rust will
// support specialization (hopefully) and this won't be necessary.
async_impl!(
    (), bool, String,
    u8, u16, u32, u64, usize,
    i8, i16, i32, i64, isize);

impl<E: Send + 'static> Async for Vec<E> {
    type Value = Vec<E>;
    type Error = ();
    type Cancel = Option<Vec<E>>;

    async_impl_body!(Vec<E>);
}

/*
 *
 * ===== AsyncResult =====
 *
 */

pub type AsyncResult<T, E> = Result<T, AsyncError<E>>;

#[derive(Eq, PartialEq)]
pub enum AsyncError<E: Send + 'static> {
    Failed(E),
    Aborted,
}

impl<E: Send + 'static> AsyncError<E> {
    pub fn failed(err: E) -> AsyncError<E> {
        AsyncError::Failed(err)
    }

    pub fn aborted() -> AsyncError<E> {
        AsyncError::Aborted
    }

    pub fn is_aborted(&self) -> bool {
        match *self {
            AsyncError::Aborted => true,
            _ => false,
        }
    }

    pub fn is_failed(&self) -> bool {
        match *self {
            AsyncError::Failed(..) => true,
            _ => false,
        }
    }

    pub fn unwrap(self) -> E {
        match self {
            AsyncError::Failed(err) => err,
            AsyncError::Aborted => panic!("unwrapping a cancellation error"),
        }
    }

    pub fn take(self) -> Option<E> {
        match self {
            AsyncError::Failed(err) => Some(err),
            _ => None,
        }
    }
}

impl<E: Send + 'static + fmt::Debug> fmt::Debug for AsyncError<E> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            AsyncError::Failed(ref e) => write!(fmt, "AsyncError::Failed({:?})", e),
            AsyncError::Aborted => write!(fmt, "AsyncError::Aborted"),
        }
    }
}

/*
 *
 * ===== BoxedReceive =====
 *
 */

// Needed to allow virtual dispatch to Receive
trait BoxedReceive<T> : Send + 'static {
    fn receive_boxed(self: Box<Self>, val: T);
}

impl<F: FnOnce(T) + Send + 'static, T> BoxedReceive<T> for F {
    fn receive_boxed(self: Box<F>, val: T) {
        (*self)(val)
    }
}
<|endoftext|>


<commit_before>extern crate c_ares_sys;
extern crate libc;

use std::ffi::{CStr, CString};
use std::mem;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::os::unix::io;
use std::ptr;

#[derive(Debug)]
#[repr(C)]
pub enum AresError {
    ENODATA = 1,
    EFORMERR,
    ESERVFAIL,
    ENOTFOUND,
    ENOTIMP,
    EREFUSED,
    EBADQUERY,
    EBADNAME,
    EBADFAMILY,
    EBADRESP,
    ECONNREFUSED,
    ETIMEOUT,
    EOF,
    EFILE,
    ENOMEM,
    EDESTRUCTION,
    EBADSTR,
    EBADFLAGS,
    ENONAME,
    EBADHINTS,
    ENOTINITIALIZED,
    ELOADIPHLPAPI,
    EADDRGETNETWORKPARAMS,
    ECANCELLED,
    UNKNOWN,
}

pub const SOCKET_BAD: io::RawFd = c_ares_sys::ARES_SOCKET_BAD;

pub struct AResult {
    pub ip_addrs: Vec<Ipv4Addr>,
}

pub struct AAAAResult {
    pub ip_addrs: Vec<Ipv6Addr>,
}

pub struct Channel {
    ares_channel: c_ares_sys::ares_channel,
}

unsafe impl Send for Channel { }

extern "C" fn socket_callback<F>(
    data: *mut libc::c_void,
    socket_fd: c_ares_sys::ares_socket_t,
    readable: libc::c_int,
    writable: libc::c_int)
    where F: FnOnce(io::RawFd, bool, bool) + 'static {
    let handler: Box<F> = unsafe { mem::transmute(data) };
    handler(socket_fd as io::RawFd, readable != 0, writable != 0);
}

extern "C" fn query_a_callback<F>(
    arg: *mut libc::c_void,
    status: libc::c_int,
    _timeouts: libc::c_int,
    abuf: *mut libc::c_uchar,
    alen: libc::c_int)
    where F: FnOnce(Result<AResult, AresError>) + 'static {
    let result = if status != c_ares_sys::ARES_SUCCESS {
        Err(ares_error(status))
    } else {
        let mut hostent: *mut hostent = ptr::null_mut();
        let parse_status = unsafe {
            c_ares_sys::ares_parse_a_reply(
                abuf,
                alen,
                &mut hostent as *mut *mut _ as *mut *mut c_ares_sys::Struct_hostent,
                ptr::null_mut(),
                ptr::null_mut())
        };
        if parse_status != c_ares_sys::ARES_SUCCESS {
            Err(ares_error(parse_status))
        } else {
            let mut answers = Vec::new();
            unsafe {
                let mut ptr = (*hostent).h_addr_list;
                while !(*ptr).is_null() {
                    let h_addr = *ptr;
                    let ip_addr = Ipv4Addr::new(
                        *h_addr as u8,
                        *h_addr.offset(1) as u8,
                        *h_addr.offset(2) as u8,
                        *h_addr.offset(3) as u8);
                    answers.push(ip_addr);
                    ptr = ptr.offset(1);
                }
                c_ares_sys::ares_free_hostent(hostent as *mut c_ares_sys::Struct_hostent);
            }
            Ok(AResult { ip_addrs: answers })
        }
    };

    let handler: Box<F> = unsafe { mem::transmute(arg) };
    handler(result);
}

extern "C" fn query_aaaa_callback<F>(
    arg: *mut libc::c_void,
    status: libc::c_int,
    _timeouts: libc::c_int,
    abuf: *mut libc::c_uchar,
    alen: libc::c_int)
    where F: FnOnce(Result<AAAAResult, AresError>) + 'static {
    let result = if status != c_ares_sys::ARES_SUCCESS {
        Err(ares_error(status))
    } else {
        let mut hostent: *mut hostent = ptr::null_mut();
        let parse_status = unsafe {
            c_ares_sys::ares_parse_aaaa_reply(
                abuf,
                alen,
                &mut hostent as *mut *mut _ as *mut *mut c_ares_sys::Struct_hostent,
                ptr::null_mut(),
                ptr::null_mut())
        };
        if parse_status != c_ares_sys::ARES_SUCCESS {
            Err(ares_error(parse_status))
        } else {
            let mut answers = Vec::new();
            unsafe {
                let mut ptr = (*hostent).h_addr_list;
                while !(*ptr).is_null() {
                    let h_addr = *ptr;
                    let ip_addr = Ipv6Addr::new(
                        ((*h_addr as u16) << 8) + *h_addr.offset(1) as u16,
                        ((*h_addr.offset(2) as u16) << 8) + *h_addr.offset(3) as u16,
                        ((*h_addr.offset(4) as u16) << 8) + *h_addr.offset(5) as u16,
                        ((*h_addr.offset(6) as u16) << 8) + *h_addr.offset(7) as u16,
                        ((*h_addr.offset(8) as u16) << 8) + *h_addr.offset(9) as u16,
                        ((*h_addr.offset(10) as u16) << 8) + *h_addr.offset(11) as u16,
                        ((*h_addr.offset(12) as u16) << 8) + *h_addr.offset(13) as u16,
                        ((*h_addr.offset(14) as u16) << 8) + *h_addr.offset(15) as u16);
                    answers.push(ip_addr);
                    ptr = ptr.offset(1);
                }
                c_ares_sys::ares_free_hostent(hostent as *mut c_ares_sys::Struct_hostent);
            }
            Ok(AAAAResult { ip_addrs: answers })
        }
    };

    let handler: Box<F> = unsafe { mem::transmute(arg) };
    handler(result);
}

impl Channel {
    pub fn new<F>(callback: F) -> Result<Channel, AresError>
        where F: FnOnce(io::RawFd, bool, bool) + 'static {
        let lib_rc = unsafe {
            c_ares_sys::ares_library_init(c_ares_sys::ARES_LIB_INIT_ALL)
        };
        match lib_rc {
            c_ares_sys::ARES_SUCCESS => (),
            _ => return Err(ares_error(lib_rc))
        }

        // TODO suport user-provided options
        let mut ares_channel = ptr::null_mut();
        let mut options = c_ares_sys::Struct_ares_options::default();
        options.flags = c_ares_sys::ARES_FLAG_STAYOPEN;
        options.timeout = 500;
        options.tries = 3;
        options.sock_state_cb = Some(socket_callback::<F>);
        options.sock_state_cb_data = unsafe {
            mem::transmute(Box::new(callback))
        };
        let optmask =
            c_ares_sys::ARES_OPT_FLAGS |
            c_ares_sys::ARES_OPT_TIMEOUT |
            c_ares_sys::ARES_OPT_TRIES |
            c_ares_sys::ARES_OPT_SOCK_STATE_CB;
        let channel_rc = unsafe {
            c_ares_sys::ares_init_options(&mut ares_channel, &mut options, optmask)
        };

        match channel_rc {
            c_ares_sys::ARES_SUCCESS => (),
            _ => {
                unsafe { c_ares_sys::ares_library_cleanup(); }
                return Err(ares_error(channel_rc))
            }
        }
        let channel = Channel {
            ares_channel: ares_channel,
        };

        // TODO ares_set_servers() here too?
        Ok(channel)
    }

    pub fn query_a<F>(&mut self, name: &str, handler: F)
        where F: FnOnce(Result<AResult, AresError>) + 'static {
        let c_name = CString::new(name).unwrap();
        unsafe {
            let c_arg: *mut libc::c_void = mem::transmute(Box::new(handler));
            c_ares_sys::ares_query(
                self.ares_channel,
                c_name.as_ptr(),
                DnsClass::IN as libc::c_int,
                QueryType::A as libc::c_int,
                Some(query_a_callback::<F>),
                c_arg);
        }
    }

    pub fn query_aaaa<F>(&mut self, name: &str, handler: F)
        where F: FnOnce(Result<AAAAResult, AresError>) + 'static {
        let c_name = CString::new(name).unwrap();
        unsafe {
            let c_arg: *mut libc::c_void = mem::transmute(Box::new(handler));
            c_ares_sys::ares_query(
                self.ares_channel,
                c_name.as_ptr(),
                DnsClass::IN as libc::c_int,
                QueryType::AAAA as libc::c_int,
                Some(query_aaaa_callback::<F>),
                c_arg);
        }
    }

    pub fn process_fd(&mut self, read_fd: io::RawFd, write_fd: io::RawFd) {
        unsafe {
            c_ares_sys::ares_process_fd(self.ares_channel, read_fd, write_fd);
        }
    }
}

impl Drop for Channel {
    fn drop(&mut self) {
        unsafe {
            c_ares_sys::ares_destroy(self.ares_channel);
            c_ares_sys::ares_library_cleanup();
        }
    }
}

pub fn str_error<'a>(code: AresError) -> &'a str {
    let buf = unsafe {
        let ptr = c_ares_sys::ares_strerror(code as libc::c_int);
        CStr::from_ptr(ptr).to_bytes()
    };
    std::str::from_utf8(buf).unwrap()
}

// Convert an error code from the library into a more strongly typed AresError.
fn ares_error(code: libc::c_int) -> AresError {
    match code {
        c_ares_sys::ARES_ENODATA => AresError::ENODATA,
        c_ares_sys::ARES_EFORMERR => AresError::EFORMERR,
        c_ares_sys::ARES_ESERVFAIL => AresError::ESERVFAIL,
        c_ares_sys::ARES_ENOTFOUND => AresError::ENOTFOUND,
        c_ares_sys::ARES_ENOTIMP => AresError::ENOTIMP,
        c_ares_sys::ARES_EREFUSED => AresError::EREFUSED,
        c_ares_sys::ARES_EBADQUERY => AresError::EBADQUERY,
        c_ares_sys::ARES_EBADNAME => AresError::EBADNAME,
        c_ares_sys::ARES_EBADFAMILY => AresError::EBADFAMILY,
        c_ares_sys::ARES_EBADRESP => AresError::EBADRESP,
        c_ares_sys::ARES_ECONNREFUSED => AresError::ECONNREFUSED,
        c_ares_sys::ARES_ETIMEOUT => AresError::ETIMEOUT,
        c_ares_sys::ARES_EOF => AresError::EOF,
        c_ares_sys::ARES_EFILE => AresError::EFILE,
        c_ares_sys::ARES_ENOMEM => AresError::ENOMEM,
        c_ares_sys::ARES_EDESTRUCTION => AresError::EDESTRUCTION,
        c_ares_sys::ARES_EBADSTR => AresError::EBADSTR,
        c_ares_sys::ARES_EBADFLAGS => AresError::EBADFLAGS,
        c_ares_sys::ARES_ENONAME => AresError::ENONAME,
        c_ares_sys::ARES_EBADHINTS => AresError::EBADHINTS,
        c_ares_sys::ARES_ENOTINITIALIZED => AresError::ENOTINITIALIZED,
        c_ares_sys::ARES_ELOADIPHLPAPI => AresError::ELOADIPHLPAPI,
        c_ares_sys::ARES_EADDRGETNETWORKPARAMS => AresError::EADDRGETNETWORKPARAMS,
        c_ares_sys::ARES_ECANCELLED => AresError::ECANCELLED,
        _ => AresError::UNKNOWN,
    }
}

#[repr(C)]
struct hostent {
    h_name: *mut libc::c_char,
    h_aliases: *mut *mut libc::c_char,
    h_addrtype: libc::c_int,
    h_length: libc::c_int,
    h_addr_list: *mut *mut libc::c_char,
}

// See arpa/nameser.h
enum QueryType {
    A = 1,
    AAAA = 28,
}

// See arpa/nameser.h
enum DnsClass {
   IN = 1,
}
<commit_msg>Remove unnecessary #[repr(C)]<commit_after>extern crate c_ares_sys;
extern crate libc;

use std::ffi::{CStr, CString};
use std::mem;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::os::unix::io;
use std::ptr;

#[derive(Debug)]
pub enum AresError {
    ENODATA = 1,
    EFORMERR,
    ESERVFAIL,
    ENOTFOUND,
    ENOTIMP,
    EREFUSED,
    EBADQUERY,
    EBADNAME,
    EBADFAMILY,
    EBADRESP,
    ECONNREFUSED,
    ETIMEOUT,
    EOF,
    EFILE,
    ENOMEM,
    EDESTRUCTION,
    EBADSTR,
    EBADFLAGS,
    ENONAME,
    EBADHINTS,
    ENOTINITIALIZED,
    ELOADIPHLPAPI,
    EADDRGETNETWORKPARAMS,
    ECANCELLED,
    UNKNOWN,
}

pub const SOCKET_BAD: io::RawFd = c_ares_sys::ARES_SOCKET_BAD;

pub struct AResult {
    pub ip_addrs: Vec<Ipv4Addr>,
}

pub struct AAAAResult {
    pub ip_addrs: Vec<Ipv6Addr>,
}

pub struct Channel {
    ares_channel: c_ares_sys::ares_channel,
}

unsafe impl Send for Channel { }

extern "C" fn socket_callback<F>(
    data: *mut libc::c_void,
    socket_fd: c_ares_sys::ares_socket_t,
    readable: libc::c_int,
    writable: libc::c_int)
    where F: FnOnce(io::RawFd, bool, bool) + 'static {
    let handler: Box<F> = unsafe { mem::transmute(data) };
    handler(socket_fd as io::RawFd, readable != 0, writable != 0);
}

extern "C" fn query_a_callback<F>(
    arg: *mut libc::c_void,
    status: libc::c_int,
    _timeouts: libc::c_int,
    abuf: *mut libc::c_uchar,
    alen: libc::c_int)
    where F: FnOnce(Result<AResult, AresError>) + 'static {
    let result = if status != c_ares_sys::ARES_SUCCESS {
        Err(ares_error(status))
    } else {
        let mut hostent: *mut hostent = ptr::null_mut();
        let parse_status = unsafe {
            c_ares_sys::ares_parse_a_reply(
                abuf,
                alen,
                &mut hostent as *mut *mut _ as *mut *mut c_ares_sys::Struct_hostent,
                ptr::null_mut(),
                ptr::null_mut())
        };
        if parse_status != c_ares_sys::ARES_SUCCESS {
            Err(ares_error(parse_status))
        } else {
            let mut answers = Vec::new();
            unsafe {
                let mut ptr = (*hostent).h_addr_list;
                while !(*ptr).is_null() {
                    let h_addr = *ptr;
                    let ip_addr = Ipv4Addr::new(
                        *h_addr as u8,
                        *h_addr.offset(1) as u8,
                        *h_addr.offset(2) as u8,
                        *h_addr.offset(3) as u8);
                    answers.push(ip_addr);
                    ptr = ptr.offset(1);
                }
                c_ares_sys::ares_free_hostent(hostent as *mut c_ares_sys::Struct_hostent);
            }
            Ok(AResult { ip_addrs: answers })
        }
    };

    let handler: Box<F> = unsafe { mem::transmute(arg) };
    handler(result);
}

extern "C" fn query_aaaa_callback<F>(
    arg: *mut libc::c_void,
    status: libc::c_int,
    _timeouts: libc::c_int,
    abuf: *mut libc::c_uchar,
    alen: libc::c_int)
    where F: FnOnce(Result<AAAAResult, AresError>) + 'static {
    let result = if status != c_ares_sys::ARES_SUCCESS {
        Err(ares_error(status))
    } else {
        let mut hostent: *mut hostent = ptr::null_mut();
        let parse_status = unsafe {
            c_ares_sys::ares_parse_aaaa_reply(
                abuf,
                alen,
                &mut hostent as *mut *mut _ as *mut *mut c_ares_sys::Struct_hostent,
                ptr::null_mut(),
                ptr::null_mut())
        };
        if parse_status != c_ares_sys::ARES_SUCCESS {
            Err(ares_error(parse_status))
        } else {
            let mut answers = Vec::new();
            unsafe {
                let mut ptr = (*hostent).h_addr_list;
                while !(*ptr).is_null() {
                    let h_addr = *ptr;
                    let ip_addr = Ipv6Addr::new(
                        ((*h_addr as u16) << 8) + *h_addr.offset(1) as u16,
                        ((*h_addr.offset(2) as u16) << 8) + *h_addr.offset(3) as u16,
                        ((*h_addr.offset(4) as u16) << 8) + *h_addr.offset(5) as u16,
                        ((*h_addr.offset(6) as u16) << 8) + *h_addr.offset(7) as u16,
                        ((*h_addr.offset(8) as u16) << 8) + *h_addr.offset(9) as u16,
                        ((*h_addr.offset(10) as u16) << 8) + *h_addr.offset(11) as u16,
                        ((*h_addr.offset(12) as u16) << 8) + *h_addr.offset(13) as u16,
                        ((*h_addr.offset(14) as u16) << 8) + *h_addr.offset(15) as u16);
                    answers.push(ip_addr);
                    ptr = ptr.offset(1);
                }
                c_ares_sys::ares_free_hostent(hostent as *mut c_ares_sys::Struct_hostent);
            }
            Ok(AAAAResult { ip_addrs: answers })
        }
    };

    let handler: Box<F> = unsafe { mem::transmute(arg) };
    handler(result);
}

impl Channel {
    pub fn new<F>(callback: F) -> Result<Channel, AresError>
        where F: FnOnce(io::RawFd, bool, bool) + 'static {
        let lib_rc = unsafe {
            c_ares_sys::ares_library_init(c_ares_sys::ARES_LIB_INIT_ALL)
        };
        match lib_rc {
            c_ares_sys::ARES_SUCCESS => (),
            _ => return Err(ares_error(lib_rc))
        }

        // TODO suport user-provided options
        let mut ares_channel = ptr::null_mut();
        let mut options = c_ares_sys::Struct_ares_options::default();
        options.flags = c_ares_sys::ARES_FLAG_STAYOPEN;
        options.timeout = 500;
        options.tries = 3;
        options.sock_state_cb = Some(socket_callback::<F>);
        options.sock_state_cb_data = unsafe {
            mem::transmute(Box::new(callback))
        };
        let optmask =
            c_ares_sys::ARES_OPT_FLAGS |
            c_ares_sys::ARES_OPT_TIMEOUT |
            c_ares_sys::ARES_OPT_TRIES |
            c_ares_sys::ARES_OPT_SOCK_STATE_CB;
        let channel_rc = unsafe {
            c_ares_sys::ares_init_options(&mut ares_channel, &mut options, optmask)
        };

        match channel_rc {
            c_ares_sys::ARES_SUCCESS => (),
            _ => {
                unsafe { c_ares_sys::ares_library_cleanup(); }
                return Err(ares_error(channel_rc))
            }
        }
        let channel = Channel {
            ares_channel: ares_channel,
        };

        // TODO ares_set_servers() here too?
        Ok(channel)
    }

    pub fn query_a<F>(&mut self, name: &str, handler: F)
        where F: FnOnce(Result<AResult, AresError>) + 'static {
        let c_name = CString::new(name).unwrap();
        unsafe {
            let c_arg: *mut libc::c_void = mem::transmute(Box::new(handler));
            c_ares_sys::ares_query(
                self.ares_channel,
                c_name.as_ptr(),
                DnsClass::IN as libc::c_int,
                QueryType::A as libc::c_int,
                Some(query_a_callback::<F>),
                c_arg);
        }
    }

    pub fn query_aaaa<F>(&mut self, name: &str, handler: F)
        where F: FnOnce(Result<AAAAResult, AresError>) + 'static {
        let c_name = CString::new(name).unwrap();
        unsafe {
            let c_arg: *mut libc::c_void = mem::transmute(Box::new(handler));
            c_ares_sys::ares_query(
                self.ares_channel,
                c_name.as_ptr(),
                DnsClass::IN as libc::c_int,
                QueryType::AAAA as libc::c_int,
                Some(query_aaaa_callback::<F>),
                c_arg);
        }
    }

    pub fn process_fd(&mut self, read_fd: io::RawFd, write_fd: io::RawFd) {
        unsafe {
            c_ares_sys::ares_process_fd(self.ares_channel, read_fd, write_fd);
        }
    }
}

impl Drop for Channel {
    fn drop(&mut self) {
        unsafe {
            c_ares_sys::ares_destroy(self.ares_channel);
            c_ares_sys::ares_library_cleanup();
        }
    }
}

pub fn str_error<'a>(code: AresError) -> &'a str {
    let buf = unsafe {
        let ptr = c_ares_sys::ares_strerror(code as libc::c_int);
        CStr::from_ptr(ptr).to_bytes()
    };
    std::str::from_utf8(buf).unwrap()
}

// Convert an error code from the library into a more strongly typed AresError.
fn ares_error(code: libc::c_int) -> AresError {
    match code {
        c_ares_sys::ARES_ENODATA => AresError::ENODATA,
        c_ares_sys::ARES_EFORMERR => AresError::EFORMERR,
        c_ares_sys::ARES_ESERVFAIL => AresError::ESERVFAIL,
        c_ares_sys::ARES_ENOTFOUND => AresError::ENOTFOUND,
        c_ares_sys::ARES_ENOTIMP => AresError::ENOTIMP,
        c_ares_sys::ARES_EREFUSED => AresError::EREFUSED,
        c_ares_sys::ARES_EBADQUERY => AresError::EBADQUERY,
        c_ares_sys::ARES_EBADNAME => AresError::EBADNAME,
        c_ares_sys::ARES_EBADFAMILY => AresError::EBADFAMILY,
        c_ares_sys::ARES_EBADRESP => AresError::EBADRESP,
        c_ares_sys::ARES_ECONNREFUSED => AresError::ECONNREFUSED,
        c_ares_sys::ARES_ETIMEOUT => AresError::ETIMEOUT,
        c_ares_sys::ARES_EOF => AresError::EOF,
        c_ares_sys::ARES_EFILE => AresError::EFILE,
        c_ares_sys::ARES_ENOMEM => AresError::ENOMEM,
        c_ares_sys::ARES_EDESTRUCTION => AresError::EDESTRUCTION,
        c_ares_sys::ARES_EBADSTR => AresError::EBADSTR,
        c_ares_sys::ARES_EBADFLAGS => AresError::EBADFLAGS,
        c_ares_sys::ARES_ENONAME => AresError::ENONAME,
        c_ares_sys::ARES_EBADHINTS => AresError::EBADHINTS,
        c_ares_sys::ARES_ENOTINITIALIZED => AresError::ENOTINITIALIZED,
        c_ares_sys::ARES_ELOADIPHLPAPI => AresError::ELOADIPHLPAPI,
        c_ares_sys::ARES_EADDRGETNETWORKPARAMS => AresError::EADDRGETNETWORKPARAMS,
        c_ares_sys::ARES_ECANCELLED => AresError::ECANCELLED,
        _ => AresError::UNKNOWN,
    }
}

#[repr(C)]
struct hostent {
    h_name: *mut libc::c_char,
    h_aliases: *mut *mut libc::c_char,
    h_addrtype: libc::c_int,
    h_length: libc::c_int,
    h_addr_list: *mut *mut libc::c_char,
}

// See arpa/nameser.h
enum QueryType {
    A = 1,
    AAAA = 28,
}

// See arpa/nameser.h
enum DnsClass {
   IN = 1,
}
<|endoftext|>


<commit_before>extern crate glob;
extern crate http;
extern crate hyper;
extern crate hyper_tls;
#[macro_use]
extern crate lazy_static;
extern crate libc;
extern crate nix;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate toml;

mod buffer;
mod cstructs;
mod error;
mod ghclient;
mod runfiles;
mod statics;
mod structs;

use buffer::Buffer;
use cstructs::{Group, Passwd, Spwd};
use ghclient::GithubClient;
use nix::errno::Errno;
use statics::CONF_PATH;
use std::ffi::CStr;
use std::io::{BufRead, Write};
use structs::{Config, Member, MemberGid, SectorGroup};

#[allow(dead_code)]
enum NssStatus {
    TryAgain,
    Unavail,
    NotFound,
    Success,
}

impl From<NssStatus> for libc::c_int {
    fn from(status: NssStatus) -> libc::c_int {
        match status {
            NssStatus::TryAgain => -2,
            NssStatus::Unavail => -1,
            NssStatus::NotFound => 0,
            NssStatus::Success => 1,
        }
    }
}

fn string_from(cstrptr: *const libc::c_char) -> String {
    let cstr: &CStr = unsafe { CStr::from_ptr(cstrptr) };
    String::from(cstr.to_str().unwrap_or(""))
}

macro_rules! succeed {
    () => {{
        return libc::c_int::from(NssStatus::Success);
    }};
    ($finalize:expr) => {{
        $finalize;
        return libc::c_int::from(NssStatus::Success);
    }};
}

macro_rules! fail {
    ($err_no_p:ident, $err_no:expr, $return_val:expr) => {{
        *$err_no_p = $err_no as libc::c_int;
        return libc::c_int::from($return_val);
    }};
}

macro_rules! get_or_again {
    ($getter:expr) => {{
        match $getter {
            Ok(ret) => ret,
            Err(_) => return libc::c_int::from(NssStatus::TryAgain),
        }
    }};
    ($getter:expr, $err_no_p:ident) => {{
        match $getter {
            Ok(ret) => ret,
            Err(_) => {
                *$err_no_p = Errno::EAGAIN as libc::c_int;
                return libc::c_int::from(NssStatus::TryAgain);
            }
        }
    }};
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getpwnam_r(cnameptr: *const libc::c_char, pwptr: *mut Passwd,
                                                 buf: *mut libc::c_char, buflen: libc::size_t,
                                                 errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let name = string_from(cnameptr);
    let client = get_or_again!(Config::new(&CONF_PATH).and_then(|c| Ok(GithubClient::new(&c))), errnop);
    let sectors = get_or_again!(client.get_sectors(), errnop);
    for sector in sectors {
        if let Some(member) = sector.members.get(&name) {
            match { (*pwptr).pack_args(&mut buffer, &member.login, member.id, sector.get_gid(), &client.conf) } {
                Ok(_) => succeed!(),
                Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
            }
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getpwuid_r(uid: libc::uid_t, pwptr: *mut Passwd, buf: *mut libc::c_char,
                                                 buflen: libc::size_t, errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let client = get_or_again!(Config::new(&CONF_PATH).and_then(|c| Ok(GithubClient::new(&c))), errnop);
    let sectors = get_or_again!(client.get_sectors(), errnop);
    for sector in sectors {
        for member in sector.members.values() {
            if uid == member.id as libc::uid_t {
                match { (*pwptr).pack_args(&mut buffer, &member.login, member.id, sector.get_gid(), &client.conf) } {
                    Ok(_) => succeed!(),
                    Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
                }
            }
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_setpwent() -> libc::c_int {
    let mut list_file = get_or_again!(runfiles::create());
    let client = get_or_again!(Config::new(&CONF_PATH).and_then(|c| Ok(GithubClient::new(&c))));
    let sectors = get_or_again!(client.get_sectors());
    for sector in sectors {
        for member in sector.members.values() {
            let mg = MemberGid { member: member.clone(),
                                 gid: sector.get_gid() };
            list_file.write_all(mg.to_string().as_bytes()).unwrap();
        }
    }
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getpwent_r(pwptr: *mut Passwd, buf: *mut libc::c_char, buflen: libc::size_t,
                                                 errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let (idx, idx_file, list) = get_or_again!(runfiles::open(), errnop);
    let client = get_or_again!(Config::new(&CONF_PATH).and_then(|c| Ok(GithubClient::new(&c))), errnop);
    if let Some(Ok(line)) = list.lines().nth(idx) {
        let mut buffer = Buffer::new(buf, buflen);
        let mg = get_or_again!(line.parse::<MemberGid>(), errnop);
        match { (*pwptr).pack_args(&mut buffer, &mg.member.login, mg.member.id, mg.gid, &client.conf) } {
            Ok(_) => succeed!(runfiles::increment(idx, idx_file)),
            Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_endpwent() -> libc::c_int {
    runfiles::cleanup().unwrap_or(());
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getspnam_r(cnameptr: *const libc::c_char, spptr: *mut Spwd,
                                                 buf: *mut libc::c_char, buflen: libc::size_t,
                                                 errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let name = string_from(cnameptr);
    let client = get_or_again!(Config::new(&CONF_PATH).and_then(|c| Ok(GithubClient::new(&c))), errnop);
    let sectors = get_or_again!(client.get_sectors(), errnop);
    for sector in sectors {
        if let Some(member) = sector.members.get(&name) {
            match { (*spptr).pack_args(&mut buffer, &member.login, &client.conf) } {
                Ok(_) => succeed!(),
                Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
            }
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_setspent() -> libc::c_int {
    let mut list_file = get_or_again!(runfiles::create());
    let client = get_or_again!(Config::new(&CONF_PATH).and_then(|c| Ok(GithubClient::new(&c))));
    let sectors = get_or_again!(client.get_sectors());
    for sector in sectors {
        for member in sector.members.values() {
            list_file.write_all(member.to_string().as_bytes()).unwrap();
        }
    }
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getspent_r(spptr: *mut Spwd, buf: *mut libc::c_char, buflen: libc::size_t,
                                                 errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let (idx, idx_file, list) = get_or_again!(runfiles::open(), errnop);
    let client = get_or_again!(Config::new(&CONF_PATH).and_then(|c| Ok(GithubClient::new(&c))), errnop);
    if let Some(Ok(line)) = list.lines().nth(idx) {
        let mut buffer = Buffer::new(buf, buflen);
        let member = get_or_again!(line.parse::<Member>(), errnop);
        match { (*spptr).pack_args(&mut buffer, &member.login, &client.conf) } {
            Ok(_) => succeed!(runfiles::increment(idx, idx_file)),
            Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_endspent() -> libc::c_int {
    runfiles::cleanup().unwrap_or(());
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getgrgid_r(gid: libc::gid_t, grptr: *mut Group, buf: *mut libc::c_char,
                                                 buflen: libc::size_t, errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let client = get_or_again!(Config::new(&CONF_PATH).and_then(|c| Ok(GithubClient::new(&c))), errnop);
    let sectors = get_or_again!(client.get_sectors(), errnop);
    for sector in sectors {
        let members: Vec<&str> = sector.members.values().map(|m| m.login.as_str()).collect();
        if gid as u64 == sector.get_gid() {
            match { (*grptr).pack_args(&mut buffer, &sector.get_group(), gid as u64, &members) } {
                Ok(_) => succeed!(),
                Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
            }
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getgrnam_r(cnameptr: *const libc::c_char, grptr: *mut Group,
                                                 buf: *mut libc::c_char, buflen: libc::size_t,
                                                 errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let name = string_from(cnameptr);
    let client = get_or_again!(Config::new(&CONF_PATH).and_then(|c| Ok(GithubClient::new(&c))), errnop);
    let sectors = get_or_again!(client.get_sectors(), errnop);
    for sector in sectors {
        let members: Vec<&str> = sector.members.values().map(|m| m.login.as_str()).collect();
        if name == sector.get_group() {
            match { (*grptr).pack_args(&mut buffer, &sector.get_group(), sector.get_gid(), &members) } {
                Ok(_) => succeed!(),
                Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
            }
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_setgrent() -> libc::c_int {
    let mut list_file = get_or_again!(runfiles::create());
    let client = get_or_again!(Config::new(&CONF_PATH).and_then(|c| Ok(GithubClient::new(&c))));
    let sectors = get_or_again!(client.get_sectors());
    for sector in sectors {
        list_file.write_all(sector.to_string().as_bytes()).unwrap();
    }
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getgrent_r(grptr: *mut Group, buf: *mut libc::c_char, buflen: libc::size_t,
                                                 errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let (idx, idx_file, list) = get_or_again!(runfiles::open(), errnop);
    if let Some(Ok(line)) = list.lines().nth(idx) {
        let mut buffer = Buffer::new(buf, buflen);
        let sector = get_or_again!(line.parse::<SectorGroup>(), errnop);
        let members: Vec<&str> = sector.members.keys().map(|k| k.as_str()).collect();
        match { (*grptr).pack_args(&mut buffer, &sector.get_group(), sector.get_gid(), &members) } {
            Ok(_) => succeed!(runfiles::increment(idx, idx_file)),
            Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_endgrent() -> libc::c_int {
    runfiles::cleanup().unwrap_or(());
    libc::c_int::from(NssStatus::Success)
}
<commit_msg>Use `from` instead of `as`<commit_after>extern crate glob;
extern crate http;
extern crate hyper;
extern crate hyper_tls;
#[macro_use]
extern crate lazy_static;
extern crate libc;
extern crate nix;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate toml;

mod buffer;
mod cstructs;
mod error;
mod ghclient;
mod runfiles;
mod statics;
mod structs;

use buffer::Buffer;
use cstructs::{Group, Passwd, Spwd};
use ghclient::GithubClient;
use nix::errno::Errno;
use statics::CONF_PATH;
use std::ffi::CStr;
use std::io::{BufRead, Write};
use structs::{Config, Member, MemberGid, SectorGroup};

#[allow(dead_code)]
enum NssStatus {
    TryAgain,
    Unavail,
    NotFound,
    Success,
}

impl From<NssStatus> for libc::c_int {
    fn from(status: NssStatus) -> libc::c_int {
        match status {
            NssStatus::TryAgain => -2,
            NssStatus::Unavail => -1,
            NssStatus::NotFound => 0,
            NssStatus::Success => 1,
        }
    }
}

fn string_from(cstrptr: *const libc::c_char) -> String {
    let cstr: &CStr = unsafe { CStr::from_ptr(cstrptr) };
    String::from(cstr.to_str().unwrap_or(""))
}

macro_rules! succeed {
    () => {{
        return libc::c_int::from(NssStatus::Success);
    }};
    ($finalize:expr) => {{
        $finalize;
        return libc::c_int::from(NssStatus::Success);
    }};
}

macro_rules! fail {
    ($err_no_p:ident, $err_no:expr, $return_val:expr) => {{
        *$err_no_p = $err_no as libc::c_int;
        return libc::c_int::from($return_val);
    }};
}

macro_rules! get_or_again {
    ($getter:expr) => {{
        match $getter {
            Ok(ret) => ret,
            Err(_) => return libc::c_int::from(NssStatus::TryAgain),
        }
    }};
    ($getter:expr, $err_no_p:ident) => {{
        match $getter {
            Ok(ret) => ret,
            Err(_) => {
                *$err_no_p = Errno::EAGAIN as libc::c_int;
                return libc::c_int::from(NssStatus::TryAgain);
            }
        }
    }};
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getpwnam_r(cnameptr: *const libc::c_char, pwptr: *mut Passwd,
                                                 buf: *mut libc::c_char, buflen: libc::size_t,
                                                 errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let name = string_from(cnameptr);
    let client = get_or_again!(Config::new(&CONF_PATH).and_then(|c| Ok(GithubClient::new(&c))), errnop);
    let sectors = get_or_again!(client.get_sectors(), errnop);
    for sector in sectors {
        if let Some(member) = sector.members.get(&name) {
            match { (*pwptr).pack_args(&mut buffer, &member.login, member.id, sector.get_gid(), &client.conf) } {
                Ok(_) => succeed!(),
                Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
            }
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getpwuid_r(uid: libc::uid_t, pwptr: *mut Passwd, buf: *mut libc::c_char,
                                                 buflen: libc::size_t, errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let client = get_or_again!(Config::new(&CONF_PATH).and_then(|c| Ok(GithubClient::new(&c))), errnop);
    let sectors = get_or_again!(client.get_sectors(), errnop);
    for sector in sectors {
        for member in sector.members.values() {
            if uid == member.id as libc::uid_t {
                match { (*pwptr).pack_args(&mut buffer, &member.login, member.id, sector.get_gid(), &client.conf) } {
                    Ok(_) => succeed!(),
                    Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
                }
            }
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_setpwent() -> libc::c_int {
    let mut list_file = get_or_again!(runfiles::create());
    let client = get_or_again!(Config::new(&CONF_PATH).and_then(|c| Ok(GithubClient::new(&c))));
    let sectors = get_or_again!(client.get_sectors());
    for sector in sectors {
        for member in sector.members.values() {
            let mg = MemberGid { member: member.clone(),
                                 gid: sector.get_gid() };
            list_file.write_all(mg.to_string().as_bytes()).unwrap();
        }
    }
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getpwent_r(pwptr: *mut Passwd, buf: *mut libc::c_char, buflen: libc::size_t,
                                                 errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let (idx, idx_file, list) = get_or_again!(runfiles::open(), errnop);
    let client = get_or_again!(Config::new(&CONF_PATH).and_then(|c| Ok(GithubClient::new(&c))), errnop);
    if let Some(Ok(line)) = list.lines().nth(idx) {
        let mut buffer = Buffer::new(buf, buflen);
        let mg = get_or_again!(line.parse::<MemberGid>(), errnop);
        match { (*pwptr).pack_args(&mut buffer, &mg.member.login, mg.member.id, mg.gid, &client.conf) } {
            Ok(_) => succeed!(runfiles::increment(idx, idx_file)),
            Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_endpwent() -> libc::c_int {
    runfiles::cleanup().unwrap_or(());
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getspnam_r(cnameptr: *const libc::c_char, spptr: *mut Spwd,
                                                 buf: *mut libc::c_char, buflen: libc::size_t,
                                                 errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let name = string_from(cnameptr);
    let client = get_or_again!(Config::new(&CONF_PATH).and_then(|c| Ok(GithubClient::new(&c))), errnop);
    let sectors = get_or_again!(client.get_sectors(), errnop);
    for sector in sectors {
        if let Some(member) = sector.members.get(&name) {
            match { (*spptr).pack_args(&mut buffer, &member.login, &client.conf) } {
                Ok(_) => succeed!(),
                Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
            }
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_setspent() -> libc::c_int {
    let mut list_file = get_or_again!(runfiles::create());
    let client = get_or_again!(Config::new(&CONF_PATH).and_then(|c| Ok(GithubClient::new(&c))));
    let sectors = get_or_again!(client.get_sectors());
    for sector in sectors {
        for member in sector.members.values() {
            list_file.write_all(member.to_string().as_bytes()).unwrap();
        }
    }
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getspent_r(spptr: *mut Spwd, buf: *mut libc::c_char, buflen: libc::size_t,
                                                 errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let (idx, idx_file, list) = get_or_again!(runfiles::open(), errnop);
    let client = get_or_again!(Config::new(&CONF_PATH).and_then(|c| Ok(GithubClient::new(&c))), errnop);
    if let Some(Ok(line)) = list.lines().nth(idx) {
        let mut buffer = Buffer::new(buf, buflen);
        let member = get_or_again!(line.parse::<Member>(), errnop);
        match { (*spptr).pack_args(&mut buffer, &member.login, &client.conf) } {
            Ok(_) => succeed!(runfiles::increment(idx, idx_file)),
            Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_endspent() -> libc::c_int {
    runfiles::cleanup().unwrap_or(());
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getgrgid_r(gid: libc::gid_t, grptr: *mut Group, buf: *mut libc::c_char,
                                                 buflen: libc::size_t, errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let client = get_or_again!(Config::new(&CONF_PATH).and_then(|c| Ok(GithubClient::new(&c))), errnop);
    let sectors = get_or_again!(client.get_sectors(), errnop);
    for sector in sectors {
        let members: Vec<&str> = sector.members.values().map(|m| m.login.as_str()).collect();
        if u64::from(gid) == sector.get_gid() {
            match { (*grptr).pack_args(&mut buffer, &sector.get_group(), u64::from(gid), &members) } {
                Ok(_) => succeed!(),
                Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
            }
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getgrnam_r(cnameptr: *const libc::c_char, grptr: *mut Group,
                                                 buf: *mut libc::c_char, buflen: libc::size_t,
                                                 errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let mut buffer = Buffer::new(buf, buflen);
    let name = string_from(cnameptr);
    let client = get_or_again!(Config::new(&CONF_PATH).and_then(|c| Ok(GithubClient::new(&c))), errnop);
    let sectors = get_or_again!(client.get_sectors(), errnop);
    for sector in sectors {
        let members: Vec<&str> = sector.members.values().map(|m| m.login.as_str()).collect();
        if name == sector.get_group() {
            match { (*grptr).pack_args(&mut buffer, &sector.get_group(), sector.get_gid(), &members) } {
                Ok(_) => succeed!(),
                Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
            }
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_setgrent() -> libc::c_int {
    let mut list_file = get_or_again!(runfiles::create());
    let client = get_or_again!(Config::new(&CONF_PATH).and_then(|c| Ok(GithubClient::new(&c))));
    let sectors = get_or_again!(client.get_sectors());
    for sector in sectors {
        list_file.write_all(sector.to_string().as_bytes()).unwrap();
    }
    libc::c_int::from(NssStatus::Success)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_getgrent_r(grptr: *mut Group, buf: *mut libc::c_char, buflen: libc::size_t,
                                                 errnop: *mut libc::c_int)
                                                 -> libc::c_int {
    let (idx, idx_file, list) = get_or_again!(runfiles::open(), errnop);
    if let Some(Ok(line)) = list.lines().nth(idx) {
        let mut buffer = Buffer::new(buf, buflen);
        let sector = get_or_again!(line.parse::<SectorGroup>(), errnop);
        let members: Vec<&str> = sector.members.keys().map(|k| k.as_str()).collect();
        match { (*grptr).pack_args(&mut buffer, &sector.get_group(), sector.get_gid(), &members) } {
            Ok(_) => succeed!(runfiles::increment(idx, idx_file)),
            Err(_) => fail!(errnop, Errno::ERANGE, NssStatus::TryAgain),
        }
    }
    fail!(errnop, Errno::ENOENT, NssStatus::NotFound)
}

#[no_mangle]
pub unsafe extern "C" fn _nss_sectora_endgrent() -> libc::c_int {
    runfiles::cleanup().unwrap_or(());
    libc::c_int::from(NssStatus::Success)
}
<|endoftext|>