<commit_before>//! Tokio-based [EventStore](https://geteventstore.com/) client library in it's early stages.
//! Currently the most interesting API is the `tokio_service::service::Service` implemented by
//! `client::Client`, which allows sending values of `Package` to get back a `Future` of a response
//! `Package`. `Package` is the name for a frame in the protocol. See it's documentation for more
//! information.
//!
//! You can build values of `Package` using `builder::Builder` and it's functions. Actual payloads
//! are described as `Message` enum.
//!
//! The protocol is multiplexed so you can have multiple (hard limit is 128 currently) calls going
//! at any point in time. Current implementation is based on `tokio_proto::multiplex`, at the
//! moment using a custom fork. It does not yet support `tokio_proto::streaming::multiplex` which is
//! needed to support subscriptions.
//!
//! # Panics
//!
//! There should not be any panicing now that `adapted` and `raw` are separate.
//!
//! # Simplest example
//!
//! Example of writing to the database and simple handling of the response.
//!
//! ```no_run
//! extern crate futures;
//! extern crate tokio_core;
//! extern crate tokio_proto;
//! extern crate tokio_service;
//! extern crate eventstore_tcp;
//!
//! use std::net::SocketAddr;
//! use futures::Future;
//! use tokio_core::reactor::Core;
//! use tokio_service::Service;
//!
//! use eventstore_tcp::{EventStoreClient, Builder, AdaptedMessage, StreamVersion, ContentType};
//!
//! fn main() {
//!     let addr = "127.0.0.1:1113".parse::<SocketAddr>().unwrap();
//!     let mut core = Core::new().unwrap();
//!
//!     // connecting the client returns a future for an EventStoreClient
//!     // which implements tokio_service::Service
//!     let client = EventStoreClient::connect(&addr, &core.handle());
//!
//!     let value = client.and_then(|client| {
//!         // once the connection is made and EventStoreClient (`client`)
//!         // is created, send a WriteEvents request:
//!         client.call(Builder::write_events()
//!             .stream_id("my_stream-1")
//!             .expected_version(StreamVersion::from(42))
//!             .new_event()
//!                 .event_type("meaning_of_life")
//!                 .data("{ 'meaning': 42 }".as_bytes())
//!                 .data_content_type(ContentType::Json)
//!             .done()
//!             .build_package(None, None))
//!
//!         // call returns a future representing the response
//!     }).and_then(|resp| {
//!         // By default, `resp` is a `Package` that contains the raw protobuf defined message
//!         // (`RawMessage`). It is possible to refine it into AdaptedMessage which can fail:
//!         match resp.message.try_adapt().unwrap() {
//!             AdaptedMessage::WriteEventsCompleted(Ok(_)) =>
//!                 println!("Event was written successfully"),
//!             AdaptedMessage::WriteEventsCompleted(Err(fail)) =>
//!                 println!("Event writing failed: {:?}", fail),
//!             unexpected => println!("Unexpected response: {:#?}", unexpected),
//!         };
//!
//!         Ok(())
//!     });
//!
//!     core.run(value).unwrap();
//! }
//! ```
//!
//! More examples can be found in the aspiring command line tool under `examples/testclient`.
#![deny(missing_docs)]

#[macro_use]
extern crate bitflags;
extern crate quick_protobuf;
extern crate uuid;
extern crate byteorder;
#[macro_use]
extern crate error_chain;
extern crate futures;
extern crate tokio_io;
extern crate tokio_core;
extern crate tokio_proto;
extern crate tokio_service;
extern crate bytes;
#[macro_use]
extern crate derive_more;

#[cfg(test)]
extern crate rustc_serialize;

use std::str;

pub mod raw;
pub use raw::RawMessage;
pub use raw::client_messages::{WriteEvents, ResolvedIndexedEvent, EventRecord, ReadAllEvents};
pub use raw::client_messages::mod_NotHandled::{NotHandledReason, MasterInfo};

pub mod adapted;
pub use adapted::AdaptedMessage;

pub mod package;
pub use package::Package;

pub mod codec;

mod client;
pub use client::EventStoreClient;

pub mod builder;
pub use builder::Builder;

mod auth;
pub use auth::UsernamePassword;

mod errors {
    use std::str;
    use std::io;
    use std::fmt;

    /// Enum describing the locations where a result value can be missing
    #[derive(Debug, PartialEq)]
    pub enum ResultStatusKind {
        /// Missing from WriteEventsCompleted
        WriteEvents,
        /// Missing from ReadEventCompleted
        ReadEvent,
        /// Missing from ReadStreamEventsCompleted
        ReadStream,
    }

    impl fmt::Display for ResultStatusKind {
        fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
            use self::ResultStatusKind::*;
            write!(fmt, "{}", match self {
                &WriteEvents => "WriteEventsCompleted::result",
                &ReadEvent => "ReadEventCompleted::result",
                &ReadStream => "ReadStreamEventsCompleted::result",
            })
        }
    }

    error_chain! {
        foreign_links {
            InvalidUtf8(str::Utf8Error);
        }

        errors {
            InvalidFlags(flags: u8) {
                display("Invalid flags: 0x{:02x}", flags)
            }
            MissingResultField(which: ResultStatusKind) {
                display("Missing result field: {}", which)
            }
            InvalidStreamVersion(value: i32) {
                display("Invalid StreamVersion: {}", value)
            }
            InvalidEventNumber(value: i32) {
                display("Invalid EventNumber: {}", value)
            }
            InvalidLogPosition(value: i64) {
                display("Invalid LogPosition: {}", value)
            }
            UnsupportedDiscriminator(d: u8) {
                display("Unsupported discriminator 0x{:02x}", d)
            }
            UnimplementedConversion {
                display("Unimplemented conversion")
            }
            WriteEventsInvalidTransaction {
                display("Unexpected WriteEvents result: InvalidTransaction")
            }
        }
    }

    impl Into<io::Error> for Error {
        fn into(self) -> io::Error {
            io::Error::new(io::ErrorKind::Other, self)
        }
    }

    impl Into<io::Error> for ErrorKind {
        fn into(self) -> io::Error {
            Error::from(self).into()
        }
    }
}

use self::errors::{Error, ErrorKind};

/// The direction in which events are read.
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum ReadDirection {
    /// Read from first (event 0) to the latest
    Forward,
    /// Read from latest (highest event number) to the first (event 0)
    Backward
}

impl Copy for ReadDirection {}

/// `ExpectedVersion` represents the different modes of optimistic locking when writing to a stream
/// using `WriteEventsBuilder`.
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum ExpectedVersion {
    /// No optimistic locking
    Any,
    /// Expect a stream not to exist
    NewStream,
    /// Expect exact number of events in the stream
    Exact(StreamVersion)
}

impl Copy for ExpectedVersion {}

impl Into<i32> for ExpectedVersion {
    /// Returns the wire representation.
    fn into(self) -> i32 {
        use self::ExpectedVersion::*;
        match self {
            Any => -2,
            NewStream => -1,
            Exact(ver) => ver.into()
        }
    }
}

impl From<StreamVersion> for ExpectedVersion {
    fn from(ver: StreamVersion) -> Self {
        ExpectedVersion::Exact(ver)
    }
}

impl CustomTryFrom<i32> for ExpectedVersion {
    type Err = Error;

    fn try_from(val: i32) -> Result<ExpectedVersion, (i32, Self::Err)> {
        let ver = StreamVersion::try_from(val)?;
        Ok(ExpectedVersion::from(ver))
    }
}

/// `StreamVersion` represents the valid values for a stream version which is the same as the
/// event number of the latest event. As such, values are non-negative integers up to
/// `i32::max_value`. Negative values of `i32` have special meaning in the protocol, and are
/// restricted from being used with this type.
///
/// Conversions to StreamVersion are quite horrible until TryFrom is stabilized.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct StreamVersion(u32);

impl Copy for StreamVersion {}

impl StreamVersion {
    /// Converts the value to a StreamVersion or panics if the value is out of range
    pub fn from(version: u32) -> Self {
        Self::from_opt(version).expect("StreamVersion overflow")
    }

    /// Converts the value to a StreamVersion returning None if the input is out of range.
    pub fn from_opt(version: u32) -> Option<Self> {
        // TODO: MAX_VALUE might be some magical value, should be lower?
        if version < i32::max_value() as u32 {
            Some(StreamVersion(version))
        } else {
            None
        }
    }

    #[doc(hidden)]
    pub fn from_i32(version: i32) -> Self {
        Self::try_from(version).unwrap()

    }

    #[doc(hidden)]
    pub fn from_i32_opt(version: i32) -> Option<Self> {
        Self::try_from(version).ok()
    }
}

impl CustomTryFrom<i32> for StreamVersion {
    type Err = Error;

    fn try_from(val: i32) -> Result<Self, (i32, Self::Err)> {
        if val < 0 {
            Err((val, ErrorKind::InvalidStreamVersion(val).into()))
        } else {
            Ok(StreamVersion(val as u32))
        }
    }
}

impl Into<i32> for StreamVersion {
    /// Returns the wire representation.
    fn into(self) -> i32 {
        self.0 as i32
    }
}

/// `EventNumber` is similar to `StreamVersion` and `ExpectedVersion` but is used when specifying a
/// position to read from in the stream. Allows specifying the first or last (when reading
/// backwards) event in addition to exact event number.
#[derive(Debug, Clone, Eq)]
pub enum EventNumber {
    /// The first event in a stream
    First,
    /// Exactly the given event number
    Exact(StreamVersion),
    /// The last event in a stream
    Last,
}

impl Copy for EventNumber {}

impl PartialEq<EventNumber> for EventNumber {
    fn eq(&self, other: &EventNumber) -> bool {
        let val: i32 = (*self).into();
        let other: i32 = (*other).into();
        val == other
    }
}

impl From<StreamVersion> for EventNumber {
    fn from(ver: StreamVersion) -> Self {
        EventNumber::Exact(ver)
    }
}

impl CustomTryFrom<i32> for EventNumber {
    type Err = Error;

    fn try_from(val: i32) -> Result<Self, (i32, Self::Err)> {
        match val {
            0 => Ok(EventNumber::First),
            -1 => Ok(EventNumber::Last),
            x if x > 0 => Ok(EventNumber::Exact(StreamVersion::from_i32(x))),
            invalid => {
                Err((invalid, ErrorKind::InvalidEventNumber(val).into()))
            }
        }
    }
}

impl Into<i32> for EventNumber {
    /// Returns the wire representation.
    fn into(self) -> i32 {
        match self {
            EventNumber::First => 0,
            EventNumber::Exact(x) => x.into(),
            EventNumber::Last => -1
        }
    }
}

/// Content type of the event `data` or `metadata`.
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum ContentType {
    /// Raw bytes
    Bytes,
    /// JSON values usable with projections in EventStore
    Json
}

impl Copy for ContentType {}

impl Into<i32> for ContentType {
    fn into(self) -> i32 {
        match self {
            ContentType::Bytes => 0,
            ContentType::Json => 1,
        }
    }
}

/// Global unique position in the EventStore, used when reading all events.
/// Range -1..i64::max_value()
#[derive(Debug, Clone, Eq, PartialOrd, Ord)]
pub enum LogPosition {
    /// The first event ever
    First,
    /// Exact position
    Exact(u64),
    /// The last event written to the database at the moment
    Last,
}

impl Copy for LogPosition {}

impl PartialEq<LogPosition> for LogPosition {
    fn eq(&self, other: &LogPosition) -> bool {
        let left: i64 = (*self).into();
        let right: i64 = (*other).into();
        left == right
    }
}

impl From<i64> for LogPosition {
    fn from(val: i64) -> LogPosition {
        match LogPosition::from_i64_opt(val) {
            Some(x) => x,
            None => panic!("LogPosition undeflow: {}", val),
        }
    }
}

impl CustomTryFrom<i64> for LogPosition {
    type Err = Error;

    fn try_from(val: i64) -> Result<Self, (i64, Self::Err)> {
        match val {
            0 => Ok(LogPosition::First),
            -1 => Ok(LogPosition::Last),
            pos => {
                if pos > 0 {
                    Ok(LogPosition::Exact(pos as u64))
                } else {
                    Err((pos, ErrorKind::InvalidLogPosition(pos).into()))
                }
            }
        }
    }
}

impl Into<i64> for LogPosition {
    fn into(self) -> i64 {
        match self {
            LogPosition::First => 0,
            LogPosition::Exact(x) => x as i64,
            LogPosition::Last => -1,
        }
    }
}

impl LogPosition {
    /// Wraps the value into LogPosition or None, if it is larger than i64
    pub fn from_opt(pos: u64) -> Option<LogPosition> {
        match pos {
            0 => Some(LogPosition::First),
            pos => {
                if pos < i64::max_value() as u64 {
                    Some(LogPosition::Exact(pos))
                } else {
                    None
                }
            }
        }
    }

    #[doc(hidden)]
    pub fn from_i64_opt(pos: i64) -> Option<LogPosition> {
        Self::try_from(pos).ok()
    }
}

trait CustomTryFrom<T: Sized>: Sized {
    type Err;

    fn try_from(t: T) -> Result<Self, (T, Self::Err)>;
}

trait CustomTryInto<T: Sized>: Sized {
    type Err;

    fn try_into(self) -> Result<T, (Self, Self::Err)>;
}

impl<T, U> CustomTryInto<U> for T where U: CustomTryFrom<T> {
    type Err = U::Err;

    fn try_into(self) -> Result<U, (T, Self::Err)> {
        U::try_from(self)
    }
}
<commit_msg>misc-cleanup: Remove hand-written impl of the PartialEq Trait<commit_after>//! Tokio-based [EventStore](https://geteventstore.com/) client library in it's early stages.
//! Currently the most interesting API is the `tokio_service::service::Service` implemented by
//! `client::Client`, which allows sending values of `Package` to get back a `Future` of a response
//! `Package`. `Package` is the name for a frame in the protocol. See it's documentation for more
//! information.
//!
//! You can build values of `Package` using `builder::Builder` and it's functions. Actual payloads
//! are described as `Message` enum.
//!
//! The protocol is multiplexed so you can have multiple (hard limit is 128 currently) calls going
//! at any point in time. Current implementation is based on `tokio_proto::multiplex`, at the
//! moment using a custom fork. It does not yet support `tokio_proto::streaming::multiplex` which is
//! needed to support subscriptions.
//!
//! # Panics
//!
//! There should not be any panicing now that `adapted` and `raw` are separate.
//!
//! # Simplest example
//!
//! Example of writing to the database and simple handling of the response.
//!
//! ```no_run
//! extern crate futures;
//! extern crate tokio_core;
//! extern crate tokio_proto;
//! extern crate tokio_service;
//! extern crate eventstore_tcp;
//!
//! use std::net::SocketAddr;
//! use futures::Future;
//! use tokio_core::reactor::Core;
//! use tokio_service::Service;
//!
//! use eventstore_tcp::{EventStoreClient, Builder, AdaptedMessage, StreamVersion, ContentType};
//!
//! fn main() {
//!     let addr = "127.0.0.1:1113".parse::<SocketAddr>().unwrap();
//!     let mut core = Core::new().unwrap();
//!
//!     // connecting the client returns a future for an EventStoreClient
//!     // which implements tokio_service::Service
//!     let client = EventStoreClient::connect(&addr, &core.handle());
//!
//!     let value = client.and_then(|client| {
//!         // once the connection is made and EventStoreClient (`client`)
//!         // is created, send a WriteEvents request:
//!         client.call(Builder::write_events()
//!             .stream_id("my_stream-1")
//!             .expected_version(StreamVersion::from(42))
//!             .new_event()
//!                 .event_type("meaning_of_life")
//!                 .data("{ 'meaning': 42 }".as_bytes())
//!                 .data_content_type(ContentType::Json)
//!             .done()
//!             .build_package(None, None))
//!
//!         // call returns a future representing the response
//!     }).and_then(|resp| {
//!         // By default, `resp` is a `Package` that contains the raw protobuf defined message
//!         // (`RawMessage`). It is possible to refine it into AdaptedMessage which can fail:
//!         match resp.message.try_adapt().unwrap() {
//!             AdaptedMessage::WriteEventsCompleted(Ok(_)) =>
//!                 println!("Event was written successfully"),
//!             AdaptedMessage::WriteEventsCompleted(Err(fail)) =>
//!                 println!("Event writing failed: {:?}", fail),
//!             unexpected => println!("Unexpected response: {:#?}", unexpected),
//!         };
//!
//!         Ok(())
//!     });
//!
//!     core.run(value).unwrap();
//! }
//! ```
//!
//! More examples can be found in the aspiring command line tool under `examples/testclient`.
#![deny(missing_docs)]

#[macro_use]
extern crate bitflags;
extern crate quick_protobuf;
extern crate uuid;
extern crate byteorder;
#[macro_use]
extern crate error_chain;
extern crate futures;
extern crate tokio_io;
extern crate tokio_core;
extern crate tokio_proto;
extern crate tokio_service;
extern crate bytes;
#[macro_use]
extern crate derive_more;

#[cfg(test)]
extern crate rustc_serialize;

use std::str;

pub mod raw;
pub use raw::RawMessage;
pub use raw::client_messages::{WriteEvents, ResolvedIndexedEvent, EventRecord, ReadAllEvents};
pub use raw::client_messages::mod_NotHandled::{NotHandledReason, MasterInfo};

pub mod adapted;
pub use adapted::AdaptedMessage;

pub mod package;
pub use package::Package;

pub mod codec;

mod client;
pub use client::EventStoreClient;

pub mod builder;
pub use builder::Builder;

mod auth;
pub use auth::UsernamePassword;

mod errors {
    use std::str;
    use std::io;
    use std::fmt;

    /// Enum describing the locations where a result value can be missing
    #[derive(Debug, PartialEq)]
    pub enum ResultStatusKind {
        /// Missing from WriteEventsCompleted
        WriteEvents,
        /// Missing from ReadEventCompleted
        ReadEvent,
        /// Missing from ReadStreamEventsCompleted
        ReadStream,
    }

    impl fmt::Display for ResultStatusKind {
        fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
            use self::ResultStatusKind::*;
            write!(fmt, "{}", match self {
                &WriteEvents => "WriteEventsCompleted::result",
                &ReadEvent => "ReadEventCompleted::result",
                &ReadStream => "ReadStreamEventsCompleted::result",
            })
        }
    }

    error_chain! {
        foreign_links {
            InvalidUtf8(str::Utf8Error);
        }

        errors {
            InvalidFlags(flags: u8) {
                display("Invalid flags: 0x{:02x}", flags)
            }
            MissingResultField(which: ResultStatusKind) {
                display("Missing result field: {}", which)
            }
            InvalidStreamVersion(value: i32) {
                display("Invalid StreamVersion: {}", value)
            }
            InvalidEventNumber(value: i32) {
                display("Invalid EventNumber: {}", value)
            }
            InvalidLogPosition(value: i64) {
                display("Invalid LogPosition: {}", value)
            }
            UnsupportedDiscriminator(d: u8) {
                display("Unsupported discriminator 0x{:02x}", d)
            }
            UnimplementedConversion {
                display("Unimplemented conversion")
            }
            WriteEventsInvalidTransaction {
                display("Unexpected WriteEvents result: InvalidTransaction")
            }
        }
    }

    impl Into<io::Error> for Error {
        fn into(self) -> io::Error {
            io::Error::new(io::ErrorKind::Other, self)
        }
    }

    impl Into<io::Error> for ErrorKind {
        fn into(self) -> io::Error {
            Error::from(self).into()
        }
    }
}

use self::errors::{Error, ErrorKind};

/// The direction in which events are read.
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum ReadDirection {
    /// Read from first (event 0) to the latest
    Forward,
    /// Read from latest (highest event number) to the first (event 0)
    Backward
}

impl Copy for ReadDirection {}

/// `ExpectedVersion` represents the different modes of optimistic locking when writing to a stream
/// using `WriteEventsBuilder`.
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum ExpectedVersion {
    /// No optimistic locking
    Any,
    /// Expect a stream not to exist
    NewStream,
    /// Expect exact number of events in the stream
    Exact(StreamVersion)
}

impl Copy for ExpectedVersion {}

impl Into<i32> for ExpectedVersion {
    /// Returns the wire representation.
    fn into(self) -> i32 {
        use self::ExpectedVersion::*;
        match self {
            Any => -2,
            NewStream => -1,
            Exact(ver) => ver.into()
        }
    }
}

impl From<StreamVersion> for ExpectedVersion {
    fn from(ver: StreamVersion) -> Self {
        ExpectedVersion::Exact(ver)
    }
}

impl CustomTryFrom<i32> for ExpectedVersion {
    type Err = Error;

    fn try_from(val: i32) -> Result<ExpectedVersion, (i32, Self::Err)> {
        let ver = StreamVersion::try_from(val)?;
        Ok(ExpectedVersion::from(ver))
    }
}

/// `StreamVersion` represents the valid values for a stream version which is the same as the
/// event number of the latest event. As such, values are non-negative integers up to
/// `i32::max_value`. Negative values of `i32` have special meaning in the protocol, and are
/// restricted from being used with this type.
///
/// Conversions to StreamVersion are quite horrible until TryFrom is stabilized.
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct StreamVersion(u32);

impl Copy for StreamVersion {}

impl StreamVersion {
    /// Converts the value to a StreamVersion or panics if the value is out of range
    pub fn from(version: u32) -> Self {
        Self::from_opt(version).expect("StreamVersion overflow")
    }

    /// Converts the value to a StreamVersion returning None if the input is out of range.
    pub fn from_opt(version: u32) -> Option<Self> {
        // TODO: MAX_VALUE might be some magical value, should be lower?
        if version < i32::max_value() as u32 {
            Some(StreamVersion(version))
        } else {
            None
        }
    }

    #[doc(hidden)]
    pub fn from_i32(version: i32) -> Self {
        Self::try_from(version).unwrap()

    }

    #[doc(hidden)]
    pub fn from_i32_opt(version: i32) -> Option<Self> {
        Self::try_from(version).ok()
    }
}

impl CustomTryFrom<i32> for StreamVersion {
    type Err = Error;

    fn try_from(val: i32) -> Result<Self, (i32, Self::Err)> {
        if val < 0 {
            Err((val, ErrorKind::InvalidStreamVersion(val).into()))
        } else {
            Ok(StreamVersion(val as u32))
        }
    }
}

impl Into<i32> for StreamVersion {
    /// Returns the wire representation.
    fn into(self) -> i32 {
        self.0 as i32
    }
}

/// `EventNumber` is similar to `StreamVersion` and `ExpectedVersion` but is used when specifying a
/// position to read from in the stream. Allows specifying the first or last (when reading
/// backwards) event in addition to exact event number.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EventNumber {
    /// The first event in a stream
    First,
    /// Exactly the given event number
    Exact(StreamVersion),
    /// The last event in a stream
    Last,
}

impl Copy for EventNumber {}

impl From<StreamVersion> for EventNumber {
    fn from(ver: StreamVersion) -> Self {
        EventNumber::Exact(ver)
    }
}

impl CustomTryFrom<i32> for EventNumber {
    type Err = Error;

    fn try_from(val: i32) -> Result<Self, (i32, Self::Err)> {
        match val {
            0 => Ok(EventNumber::First),
            -1 => Ok(EventNumber::Last),
            x if x > 0 => Ok(EventNumber::Exact(StreamVersion::from_i32(x))),
            invalid => {
                Err((invalid, ErrorKind::InvalidEventNumber(val).into()))
            }
        }
    }
}

impl Into<i32> for EventNumber {
    /// Returns the wire representation.
    fn into(self) -> i32 {
        match self {
            EventNumber::First => 0,
            EventNumber::Exact(x) => x.into(),
            EventNumber::Last => -1
        }
    }
}

/// Content type of the event `data` or `metadata`.
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum ContentType {
    /// Raw bytes
    Bytes,
    /// JSON values usable with projections in EventStore
    Json
}

impl Copy for ContentType {}

impl Into<i32> for ContentType {
    fn into(self) -> i32 {
        match self {
            ContentType::Bytes => 0,
            ContentType::Json => 1,
        }
    }
}

/// Global unique position in the EventStore, used when reading all events.
/// Range -1..i64::max_value()
#[derive(Debug, Clone, Eq, PartialOrd, Ord)]
pub enum LogPosition {
    /// The first event ever
    First,
    /// Exact position
    Exact(u64),
    /// The last event written to the database at the moment
    Last,
}

impl Copy for LogPosition {}

impl PartialEq<LogPosition> for LogPosition {
    fn eq(&self, other: &LogPosition) -> bool {
        let left: i64 = (*self).into();
        let right: i64 = (*other).into();
        left == right
    }
}

impl From<i64> for LogPosition {
    fn from(val: i64) -> LogPosition {
        match LogPosition::from_i64_opt(val) {
            Some(x) => x,
            None => panic!("LogPosition undeflow: {}", val),
        }
    }
}

impl CustomTryFrom<i64> for LogPosition {
    type Err = Error;

    fn try_from(val: i64) -> Result<Self, (i64, Self::Err)> {
        match val {
            0 => Ok(LogPosition::First),
            -1 => Ok(LogPosition::Last),
            pos => {
                if pos > 0 {
                    Ok(LogPosition::Exact(pos as u64))
                } else {
                    Err((pos, ErrorKind::InvalidLogPosition(pos).into()))
                }
            }
        }
    }
}

impl Into<i64> for LogPosition {
    fn into(self) -> i64 {
        match self {
            LogPosition::First => 0,
            LogPosition::Exact(x) => x as i64,
            LogPosition::Last => -1,
        }
    }
}

impl LogPosition {
    /// Wraps the value into LogPosition or None, if it is larger than i64
    pub fn from_opt(pos: u64) -> Option<LogPosition> {
        match pos {
            0 => Some(LogPosition::First),
            pos => {
                if pos < i64::max_value() as u64 {
                    Some(LogPosition::Exact(pos))
                } else {
                    None
                }
            }
        }
    }

    #[doc(hidden)]
    pub fn from_i64_opt(pos: i64) -> Option<LogPosition> {
        Self::try_from(pos).ok()
    }
}

trait CustomTryFrom<T: Sized>: Sized {
    type Err;

    fn try_from(t: T) -> Result<Self, (T, Self::Err)>;
}

trait CustomTryInto<T: Sized>: Sized {
    type Err;

    fn try_into(self) -> Result<T, (Self, Self::Err)>;
}

impl<T, U> CustomTryInto<U> for T where U: CustomTryFrom<T> {
    type Err = U::Err;

    fn try_into(self) -> Result<U, (T, Self::Err)> {
        U::try_from(self)
    }
}
<|endoftext|>


<commit_before>
extern crate serialize;

use serialize::json;
use serialize::json::{Json, JsonObject, ToJson, I64};
use std::collections::TreeMap;

use mutable_json::MutableJson;

mod mutable_json;

#[deriving(Show)]
pub enum ValicoErrorKind {
	PresenceError,
	CoerceError,
	ValidationError,
    Other
}

#[deriving(Show)]
pub struct ValicoError {
    pub kind: ValicoErrorKind,
    pub message: String
}

pub type ValicoResult<T> = Result<T, TreeMap<String, ValicoError>>;

trait Coercer: Send + Sync {
	fn coerce(&self, &Json, &Option<Builder>) -> ValicoResult<Option<Json>>;
}

fn single_error(err: ValicoError) -> TreeMap<String, ValicoError> {
	let mut tree = TreeMap::new();
	tree.insert("0".to_string(), err);

	tree
}

fn singe_coerce_error(err_msg: String) -> TreeMap<String, ValicoError> {
	let mut tree = TreeMap::new();
	let error = ValicoError {
		kind: CoerceError,
		message: err_msg
	};
	tree.insert("0".to_string(), error);

	tree
}

struct StringCoercer;

impl Coercer for StringCoercer {
	fn coerce(&self, val: &Json, _: &Option<Builder>) -> ValicoResult<Option<Json>> {
		if val.is_string() {
			Ok(None)
		} else if val.is_number() {
			Ok(Some(val.to_string().to_json()))
		} else {
			Err(
				singe_coerce_error(format!("Can't coerce object {} to string", val))
			)
		}
	}
}

struct I64Coercer;

impl Coercer for I64Coercer {
	fn coerce(&self, val: &Json, _: &Option<Builder>) -> ValicoResult<Option<Json>> {
		if val.is_i64() {
			return Ok(None)
		} else if val.is_u64() {
			let val = val.as_u64().unwrap();
			return Ok(Some((val as i64).to_json()));
		} else if val.is_f64() {
			let val = val.as_f64().unwrap();
			return Ok(Some((val as i64).to_json()));
		} else if val.is_string() {
			let val = val.as_string().unwrap();
			let converted: Option<i64> = from_str(val);
			match converted {
				Some(num) => Ok(Some(num.to_json())),
				None => Err(singe_coerce_error(format!("Can't string value {} to i64", val)))
			}
		} else {
			Err(singe_coerce_error(format!("Can't coerce object {} to i64", val)))
		}
	}
}

struct U64Coercer;

impl Coercer for U64Coercer {
	fn coerce(&self, val: &Json, _: &Option<Builder>) -> ValicoResult<Option<Json>> {
		if val.is_u64() {
			return Ok(None)
		} else if val.is_i64() {
			let val = val.as_i64().unwrap();
			return Ok(Some((val as u64).to_json()));
		} else if val.is_f64() {
			let val = val.as_f64().unwrap();
			return Ok(Some((val as u64).to_json()));
		} else if val.is_string() {
			let val = val.as_string().unwrap();
			let converted: Option<u64> = from_str(val);
			match converted {
				Some(num) => Ok(Some(num.to_json())),
				None => Err(singe_coerce_error(format!("Can't string value {} to u64", val)))
			}
		} else {
			Err(singe_coerce_error(format!("Can't coerce object {} to u64", val)))
		}
	}
}

struct F64Coercer;

impl Coercer for F64Coercer {
	fn coerce(&self, val: &Json, _: &Option<Builder>) -> ValicoResult<Option<Json>> {
		if val.is_f64() {
			return Ok(None)
		} else if val.is_i64() {
			let val = val.as_i64().unwrap();
			return Ok(Some((val as f64).to_json()));
		} else if val.is_u64() {
			let val = val.as_u64().unwrap();
			return Ok(Some((val as f64).to_json()));
		} else if val.is_string() {
			let val = val.as_string().unwrap();
			let converted: Option<f64> = from_str(val);
			match converted {
				Some(num) => Ok(Some(num.to_json())),
				None => Err(singe_coerce_error(format!("Can't coerce string value {} to f64", val)))
			}
		} else {
			Err(singe_coerce_error(format!("Can't coerce object {} to f64", val)))
		}
	}
}

struct BooleanCoercer;

impl Coercer for BooleanCoercer {
	fn coerce(&self, val: &Json, _: &Option<Builder>) -> ValicoResult<Option<Json>> {
		if val.is_boolean() {
			Ok(None)
		} else if val.is_string() {
			let val = val.as_string().unwrap();
			if val == "true" {
				Ok(Some(true.to_json()))
			} else if val == "false" {
				Ok(Some(false.to_json()))
			} else {
				Err(singe_coerce_error(format!("Can't coerce string value {} to boolean. Correct values is 'true' and 'false'", val)))
			}
		} else {
			Err(singe_coerce_error(format!("Can't coerce object {} to boolean", val)))
		}
	}
}

struct NullCoercer;

impl Coercer for NullCoercer {
	fn coerce(&self, val: &Json, _: &Option<Builder>) -> ValicoResult<Option<Json>> {
		if val.is_null() {
			Ok(None)
		} else if val.is_string() {
			let val = val.as_string().unwrap();
			if val == "" {
				Ok(Some(json::Null))
			} else {
				Err(singe_coerce_error(format!("Can't coerce string value {} to null. Correct value is only empty string", val)))
			}
		} else {
			Err(singe_coerce_error(format!("Can't object {} to null", val)))
		}
	}
}

struct ListCoercer {
	sub_coercer: Option<Box<Coercer>>
}

impl ListCoercer {
	pub fn new() -> ListCoercer {
		ListCoercer {
			sub_coercer: None
		}
	}

	pub fn with_type(sub_coercer: Box<Coercer>) -> ListCoercer {
		ListCoercer {
			sub_coercer: Some(sub_coercer)
		}
	}
}

// impl Coercer for ListCoercer {
// 	fn coerce(&self, val: &Json, _: &Option<Builder>) -> ValicoResult<Option<Json>> {
// 		if val.is_list() {

// 		} else {
// 			Err(singe_coerce_error(format!("Can't object {} to null", val)))
// 		}
// 	}
// }

struct Param {
	pub name: String,
	pub coercer: Box<Coercer>,
	pub extra: Option<Builder>
}

impl Param {
	pub fn new(name: &str, coercer: Box<Coercer>) -> Param {
		Param {
			name: name.to_string(),
			coercer: coercer,
			extra: None
		}
	}

	pub fn new_with_extra(name: &str, coercer: Box<Coercer>, extra: Builder) -> Param {
		Param {
			name: name.to_string(),
			coercer: coercer,
			extra: Some(extra)
		}
	}

	pub fn process(&self, val: &mut Json) -> ValicoResult<Option<Json>> {
		return (*self.coercer).coerce(val, &self.extra)
	}
}

struct Builder {
	requires: Vec<Param>,
	optional: Vec<Param>
}

fn has_value(obj: &JsonObject, key: &String) -> bool {
	match obj.find(key) {
		Some(_) => true,
		None => false
	}
}

impl Builder {

	pub fn new() -> Builder {
		Builder {
			requires: vec![],
			optional: vec![]
		}
	}

	fn from_function(rules: |params: &mut Builder|) -> Builder {
		let mut builder = Builder::new();
		rules(&mut builder);

		builder
	}

	fn req(&mut self, name: &str) {
		let params = Param::new(name, box StringCoercer);
		self.requires.push(params);
	}

	fn req_type(&mut self, name: &str, coercer: Box<Coercer>) {
		let params = Param::new(name, coercer);
		self.requires.push(params);
	}

	fn req_nest(&mut self, name: &str, coercer: Box<Coercer>, extra: |params: &mut Builder|) {
		let extra_builder = Builder::from_function(extra);
		let params = Param::new_with_extra(name, coercer, extra_builder);
		self.requires.push(params);
	}

	fn process(&self, tree: &mut JsonObject)  {

		for param in self.requires.iter() {
			let ref name = param.name;
			let present = has_value(tree, name);
			if present {
				match param.process(tree.find_mut(name).unwrap()) {
					Ok(result) => {
						match result {
							Some(new_value) => { tree.insert(name.clone(), new_value); },
							None => ()
						}
					},
					Err(_) => ()
				}
			} else {
				// required error!
			}

		}

	}

	// pub fn optional(name: &str, kind: Coeletrcer);
	// pub fn group(name: &str);
	// pub fn mutually_exclusive();
}

#[test]
fn it_works() {
	use serialize::json;

	let params = Builder::from_function(|params: &mut Builder| {
		params.req("name");
		params.req_type("test", box StringCoercer);
		params.req_nest("test", box StringCoercer, |params: &mut Builder| {
			params.req("type");
		});
	});

	let mut json = json::from_str(r#"{"name": 1}"#).unwrap();
	params.process(json.as_object_mut().unwrap());
}
<commit_msg>feat: add object and list coercers<commit_after>
extern crate serialize;

use serialize::json;
use serialize::json::{Json, JsonObject, ToJson, I64};
use std::collections::TreeMap;

use mutable_json::MutableJson;

mod mutable_json;

#[deriving(Show)]
pub enum ValicoErrorKind {
	PresenceError,
	CoerceError,
	ValidationError,
    Other
}

#[deriving(Show)]
pub struct ValicoError {
    pub kind: ValicoErrorKind,
    pub message: String
}

pub type ValicoResult<T> = Result<T, TreeMap<String, ValicoError>>;

fn single_error(err: ValicoError) -> TreeMap<String, ValicoError> {
	let mut tree = TreeMap::new();
	tree.insert("0".to_string(), err);

	tree
}

fn singe_coerce_error(err_msg: String) -> TreeMap<String, ValicoError> {
	let mut tree = TreeMap::new();
	let error = ValicoError {
		kind: CoerceError,
		message: err_msg
	};
	tree.insert("0".to_string(), error);

	tree
}

trait Coercer: Send + Sync {
	fn coerce(&self, &mut Json, Option<&Builder>) -> ValicoResult<Option<Json>>;
}

struct StringCoercer;

impl Coercer for StringCoercer {
	fn coerce(&self, val: &mut Json, _: Option<&Builder>) -> ValicoResult<Option<Json>> {
		if val.is_string() {
			Ok(None)
		} else if val.is_number() {
			Ok(Some(val.to_string().to_json()))
		} else {
			Err(
				singe_coerce_error(format!("Can't coerce object {} to string", val))
			)
		}
	}
}

struct I64Coercer;

impl Coercer for I64Coercer {
	fn coerce(&self, val: &mut Json, _: Option<&Builder>) -> ValicoResult<Option<Json>> {
		if val.is_i64() {
			return Ok(None)
		} else if val.is_u64() {
			let val = val.as_u64().unwrap();
			return Ok(Some((val as i64).to_json()));
		} else if val.is_f64() {
			let val = val.as_f64().unwrap();
			return Ok(Some((val as i64).to_json()));
		} else if val.is_string() {
			let val = val.as_string().unwrap();
			let converted: Option<i64> = from_str(val);
			match converted {
				Some(num) => Ok(Some(num.to_json())),
				None => Err(singe_coerce_error(format!("Can't string value {} to i64", val)))
			}
		} else {
			Err(singe_coerce_error(format!("Can't coerce object {} to i64", val)))
		}
	}
}

struct U64Coercer;

impl Coercer for U64Coercer {
	fn coerce(&self, val: &mut Json, _: Option<&Builder>) -> ValicoResult<Option<Json>> {
		if val.is_u64() {
			return Ok(None)
		} else if val.is_i64() {
			let val = val.as_i64().unwrap();
			return Ok(Some((val as u64).to_json()));
		} else if val.is_f64() {
			let val = val.as_f64().unwrap();
			return Ok(Some((val as u64).to_json()));
		} else if val.is_string() {
			let val = val.as_string().unwrap();
			let converted: Option<u64> = from_str(val);
			match converted {
				Some(num) => Ok(Some(num.to_json())),
				None => Err(singe_coerce_error(format!("Can't string value {} to u64", val)))
			}
		} else {
			Err(singe_coerce_error(format!("Can't coerce object {} to u64", val)))
		}
	}
}

struct F64Coercer;

impl Coercer for F64Coercer {
	fn coerce(&self, val: &mut Json, _: Option<&Builder>) -> ValicoResult<Option<Json>> {
		if val.is_f64() {
			return Ok(None)
		} else if val.is_i64() {
			let val = val.as_i64().unwrap();
			return Ok(Some((val as f64).to_json()));
		} else if val.is_u64() {
			let val = val.as_u64().unwrap();
			return Ok(Some((val as f64).to_json()));
		} else if val.is_string() {
			let val = val.as_string().unwrap();
			let converted: Option<f64> = from_str(val);
			match converted {
				Some(num) => Ok(Some(num.to_json())),
				None => Err(singe_coerce_error(format!("Can't coerce string value {} to f64", val)))
			}
		} else {
			Err(singe_coerce_error(format!("Can't coerce object {} to f64", val)))
		}
	}
}

struct BooleanCoercer;

impl Coercer for BooleanCoercer {
	fn coerce(&self, val: &mut Json, _: Option<&Builder>) -> ValicoResult<Option<Json>> {
		if val.is_boolean() {
			Ok(None)
		} else if val.is_string() {
			let val = val.as_string().unwrap();
			if val == "true" {
				Ok(Some(true.to_json()))
			} else if val == "false" {
				Ok(Some(false.to_json()))
			} else {
				Err(singe_coerce_error(format!("Can't coerce string value {} to boolean. Correct values is 'true' and 'false'", val)))
			}
		} else {
			Err(singe_coerce_error(format!("Can't coerce object {} to boolean", val)))
		}
	}
}

struct NullCoercer;

impl Coercer for NullCoercer {
	fn coerce(&self, val: &mut Json, _: Option<&Builder>) -> ValicoResult<Option<Json>> {
		if val.is_null() {
			Ok(None)
		} else if val.is_string() {
			let val = val.as_string().unwrap();
			if val == "" {
				Ok(Some(json::Null))
			} else {
				Err(singe_coerce_error(format!("Can't coerce string value {} to null. Correct value is only empty string", val)))
			}
		} else {
			Err(singe_coerce_error(format!("Can't coerce object {} to null", val)))
		}
	}
}

struct ListCoercer {
	sub_coercer: Option<Box<Coercer + Send + Sync>>
}

impl ListCoercer {
	pub fn new() -> ListCoercer {
		ListCoercer {
			sub_coercer: None
		}
	}

	pub fn with_type(sub_coercer: Box<Coercer + Send + Sync>) -> ListCoercer {
		ListCoercer {
			sub_coercer: Some(sub_coercer)
		}
	}
}

impl Coercer for ListCoercer {
	fn coerce(&self, val: &mut Json, extra: Option<&Builder>) -> ValicoResult<Option<Json>> {
		if val.is_list() {
			let list = val.as_list_mut().unwrap();
			let mut errors = TreeMap::new();
			if extra.is_some() {
				for (idx, item) in list.iter_mut().enumerate() {
					if item.is_object() {
						// todo match
						extra.unwrap().process(item.as_object_mut().unwrap());
					} else {
						errors.insert(idx.to_string(), ValicoError {
							kind: CoerceError, message: format!("List item {} is not and object", item)
						});
					}
				}

				if errors.len() == 0 {
					Ok(None)
				} else {
					Err(errors)
				}
			} else if self.sub_coercer.is_some() {
				let sub_coercer = self.sub_coercer.as_ref().unwrap();
				let mut errors = TreeMap::new();
				for i in range(0, list.len() - 1) {
					match sub_coercer.coerce(list.get_mut(i), None) {
						Ok(Some(value)) => {
							list.remove(i);
							list.insert(i, value);
						},
						Ok(None) => (),
						Err(err) => {
							errors.insert(i.to_string(), ValicoError {
								kind: CoerceError, message: err.to_string()
							});
						}
					}
				}

				if errors.len() == 0 {
					Ok(None)
				} else {
					Err(errors)
				}
			} else {
				Ok(None)
			}
		} else {
			Err(singe_coerce_error(format!("Can't coerce object {} to null", val)))
		}
	}
}

struct ObjectCoercer;

impl Coercer for ObjectCoercer {
	fn coerce(&self, val: &mut Json, extra: Option<&Builder>) -> ValicoResult<Option<Json>> {
		if val.is_object() {
			// todo match
			extra.unwrap().process(val.as_object_mut().unwrap());
			Ok(None)
		} else {
			Err(singe_coerce_error(format!("Can't coerce non-object value {} to object", val)))
		}
	}
}

struct Param {
	pub name: String,
	pub coercer: Box<Coercer>,
	pub extra: Option<Builder>
}

impl Param {
	pub fn new(name: &str, coercer: Box<Coercer>) -> Param {
		Param {
			name: name.to_string(),
			coercer: coercer,
			extra: None
		}
	}

	pub fn new_with_extra(name: &str, coercer: Box<Coercer>, extra: Builder) -> Param {
		Param {
			name: name.to_string(),
			coercer: coercer,
			extra: Some(extra)
		}
	}

	pub fn process(&self, val: &mut Json) -> ValicoResult<Option<Json>> {
		return (*self.coercer).coerce(val, self.extra.as_ref())
	}
}

struct Builder {
	requires: Vec<Param>,
	optional: Vec<Param>
}

fn has_value(obj: &JsonObject, key: &String) -> bool {
	match obj.find(key) {
		Some(_) => true,
		None => false
	}
}

impl Builder {

	pub fn new() -> Builder {
		Builder {
			requires: vec![],
			optional: vec![]
		}
	}

	fn from_function(rules: |params: &mut Builder|) -> Builder {
		let mut builder = Builder::new();
		rules(&mut builder);

		builder
	}

	fn req(&mut self, name: &str) {
		let params = Param::new(name, box StringCoercer);
		self.requires.push(params);
	}

	fn req_type(&mut self, name: &str, coercer: Box<Coercer>) {
		let params = Param::new(name, coercer);
		self.requires.push(params);
	}

	fn req_nest(&mut self, name: &str, coercer: Box<Coercer>, extra: |params: &mut Builder|) {
		let extra_builder = Builder::from_function(extra);
		let params = Param::new_with_extra(name, coercer, extra_builder);
		self.requires.push(params);
	}

	fn process(&self, tree: &mut JsonObject)  {

		for param in self.requires.iter() {
			let ref name = param.name;
			let present = has_value(tree, name);
			if present {
				match param.process(tree.find_mut(name).unwrap()) {
					Ok(result) => {
						match result {
							Some(new_value) => { tree.insert(name.clone(), new_value); },
							None => ()
						}
					},
					Err(_) => ()
				}
			} else {
				// required error!
			}

		}

	}

	// pub fn optional(name: &str, kind: Coeletrcer);
	// pub fn group(name: &str);
	// pub fn mutually_exclusive();
}

#[test]
fn it_works() {
	use serialize::json;

	let params = Builder::from_function(|params: &mut Builder| {
		params.req("name");
		params.req_type("test", box StringCoercer);
		params.req_nest("test", box StringCoercer, |params: &mut Builder| {
			params.req("type");
		});
	});

	let mut json = json::from_str(r#"{"name": 1}"#).unwrap();
	params.process(json.as_object_mut().unwrap());
}
<|endoftext|>


<commit_before>#![feature(core, rustc_private, plugin_registrar)]

extern crate rustc;
extern crate syntax;

use rustc::plugin::registry::Registry;
use syntax::ptr::P;
use syntax::ext::base;
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::codemap::Span;
use syntax::ast;
use syntax::ast::Visibility;
use syntax::ast::ItemStatic;
use syntax::ast::Sign::Plus;
use syntax::ast::Mutability::MutImmutable;
use syntax::ast::Lit_::{LitChar, LitInt, LitStr};
use syntax::ast::Ty_::TyFixedLengthVec;
use syntax::ast::LitIntType::UnsuffixedIntLit;
use syntax::parse::token;

/// Construct a `ast::Ty_::TyPath` corresponding to `path`
fn make_ty_path<'cx>(cx: &'cx mut ExtCtxt, sp: Span, path: Vec<&str>) -> P<ast::Ty> {
    let path = path.iter().map(|p| { cx.ident_of(p) }).collect::<Vec<_>>();
    cx.ty_path(cx.path(sp, path))
}

/// Generate an `ast::Expr` for `'a' as libc::c_char`
fn make_char_cast<'cx>(cx: &'cx mut ExtCtxt, sp: Span, c: char) -> P<ast::Expr> {
    let path = make_ty_path(cx, sp, vec!["libc", "c_char"]);
    cx.expr_cast(sp, cx.expr_lit(sp, LitChar(c)), path)
}

/// Ensure `expr` is a string literal, and return either Some(&str) or None.
fn extract_literal<'cx, 'a>(cx: &'cx mut ExtCtxt, expr: &'a P<ast::Expr>) -> Option<&'a str> {
    if let ast::ExprLit(ref lit) = expr.node {
        if let ast::LitStr(ref s, _) = lit.node {
            Some(s.get())
        } else {
            cx.span_err(expr.span, "expected a string literal");
            None
        }
    } else {
        cx.span_err(expr.span, "expected a string literal");
        None
    }
}

/// Expansion for `declare_static_raw_cstr!`
/// `declare_static_raw_cstr!("foo", "hello")` -> `static foo: [libc::c_char; 6] = ['h' as libc::c_char, ..., 0 as libc::c_char];`
fn expand_declare_static_raw_cstr<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
                                     -> Box<base::MacResult + 'cx> {
    let exprs = match base::get_exprs_from_tts(cx, sp, tts) {
        Some(e) => e,
        None => return base::DummyResult::expr(sp)
    };

    let mut it = exprs.iter();
    let e_name = if let Some(expr) = it.next() {
        extract_literal(cx, expr)
    } else {
        cx.span_err(sp, "expected 2 arguments, found 0");
        None
    };
    let e_lit = if let Some(expr) = it.next() {
        extract_literal(cx, expr)
    } else {
        cx.span_err(sp, "expected 2 arguments, found 1");
        None
    };

    match (e_name, e_lit, it.count()) {
        // name, literal, and no other arguments remaining
        (Some(name), Some(s), 0) => {
            // append null terminator to make it a cstring
            let lit = format!("{}\0", s);

            // build RHS of the statement
            let rhs = lit.as_slice().chars().map(|c| { make_char_cast(cx, sp, c) }).collect::<Vec<_>>();
            let rhs = cx.expr_vec(sp, rhs);

            // the type of the item is a fixed length vector of libc::c_char
            let path = make_ty_path(cx, sp, vec!["libc", "c_char"]);
            let ty = cx.ty(sp, TyFixedLengthVec(path, cx.expr_lit(sp, LitInt(lit.len() as u64, UnsuffixedIntLit(Plus)))));

            // #[no_mangle] #[allow(dead_code, non_upper_case_globals)]
            let attrs = vec![
                cx.attribute(sp, cx.meta_word(sp, token::intern_and_get_ident("no_mangle"))),
                cx.attribute(sp,
                    cx.meta_list(sp,
                        token::intern_and_get_ident("allow"),
                        vec![
                            cx.meta_word(sp, token::intern_and_get_ident("dead_code")),
                            cx.meta_word(sp, token::intern_and_get_ident("non_upper_case_globals"))
                        ]))
            ];

            // XXX: have to manually construct the item here because we can't set the visibility otherwise.
            let item = P(ast::Item {
                ident: cx.ident_of(name),
                attrs: attrs,
                id: ast::DUMMY_NODE_ID,
                node: ast::ItemStatic(ty, MutImmutable, rhs),
                vis: Visibility::Public,
                span: sp
            });
            base::MacItems::new(vec![item].into_iter())
        }
        (_, _, rest) => {
            if rest > 0 {
                cx.span_err(sp, format!("expected 2 arguments, found {}", rest + 2).as_slice());
            }
            base::DummyResult::expr(sp)
        }
    }
}

/// Declare a `pub static [libc::c_char; N]`, where N is the length of the supplied string, plus 1.
/// The resulting item is a raw, null-terminated C string that becomes exported as an unmangled
/// symbol for use in dynamic libraries.
///
/// `export_cstr!(foo, "hello")` expands to `#[no_mangle] pub static foo: [libc::c_char; 6] = ['h' as libc::c_char, ..., 0 as libc::c_char];`
#[macro_export]
macro_rules! export_cstr {
    ($name:ident, $lit:expr) => (
        declare_static_raw_cstr!(stringify!($name), $lit);
    );
}

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("declare_static_raw_cstr", expand_declare_static_raw_cstr);
}

<commit_msg>Clean up attribute generation a bit<commit_after>#![feature(core, rustc_private, plugin_registrar)]

extern crate rustc;
extern crate syntax;

use rustc::plugin::registry::Registry;
use syntax::ptr::P;
use syntax::ext::base;
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::codemap::Span;
use syntax::ast;
use syntax::ast::Visibility;
use syntax::ast::ItemStatic;
use syntax::ast::Sign::Plus;
use syntax::ast::Mutability::MutImmutable;
use syntax::ast::Lit_::{LitChar, LitInt, LitStr};
use syntax::ast::Ty_::TyFixedLengthVec;
use syntax::ast::LitIntType::UnsuffixedIntLit;
use syntax::parse::token;

/// Construct a `ast::Ty_::TyPath` corresponding to `path`
fn make_ty_path<'cx>(cx: &'cx mut ExtCtxt, sp: Span, path: Vec<&str>) -> P<ast::Ty> {
    let path = path.iter().map(|p| { cx.ident_of(p) }).collect::<Vec<_>>();
    cx.ty_path(cx.path(sp, path))
}

/// Generate an `ast::Expr` for `'a' as libc::c_char`
fn make_char_cast<'cx>(cx: &'cx mut ExtCtxt, sp: Span, c: char) -> P<ast::Expr> {
    let path = make_ty_path(cx, sp, vec!["libc", "c_char"]);
    cx.expr_cast(sp, cx.expr_lit(sp, LitChar(c)), path)
}

/// Ensure `expr` is a string literal, and return either Some(&str) or None.
fn extract_literal<'cx, 'a>(cx: &'cx mut ExtCtxt, expr: &'a P<ast::Expr>) -> Option<&'a str> {
    if let ast::ExprLit(ref lit) = expr.node {
        if let ast::LitStr(ref s, _) = lit.node {
            Some(s.get())
        } else {
            cx.span_err(expr.span, "expected a string literal");
            None
        }
    } else {
        cx.span_err(expr.span, "expected a string literal");
        None
    }
}

/// Construct an attribute #[foo]
fn make_attr_word<'cx>(cx: &'cx mut ExtCtxt, sp: Span, name: &str) -> ast::Attribute {
    cx.attribute(sp, cx.meta_word(sp, token::intern_and_get_ident(name)))
}

/// Construct an attribute #[foo(bar)] or #[baz(biff, quux, ...)]
fn make_attr_list<'cx>(cx: &'cx mut ExtCtxt, sp: Span, name: &str, list: Vec<&str>) -> ast::Attribute {
   let words = list.iter().map(|w| { cx.meta_word(sp, token::intern_and_get_ident(w)) }).collect::<Vec<_>>();
   cx.attribute(sp, cx.meta_list(sp, token::intern_and_get_ident(name), words))
}

/// Expansion for `declare_static_raw_cstr!`
/// `declare_static_raw_cstr!("foo", "hello")` -> `static foo: [libc::c_char; 6] = ['h' as libc::c_char, ..., 0 as libc::c_char];`
fn expand_declare_static_raw_cstr<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
                                     -> Box<base::MacResult + 'cx> {
    let exprs = match base::get_exprs_from_tts(cx, sp, tts) {
        Some(e) => e,
        None => return base::DummyResult::expr(sp)
    };

    let mut it = exprs.iter();
    let e_name = if let Some(expr) = it.next() {
        extract_literal(cx, expr)
    } else {
        cx.span_err(sp, "expected 2 arguments, found 0");
        None
    };
    let e_lit = if let Some(expr) = it.next() {
        extract_literal(cx, expr)
    } else {
        cx.span_err(sp, "expected 2 arguments, found 1");
        None
    };

    match (e_name, e_lit, it.count()) {
        // name, literal, and no other arguments remaining
        (Some(name), Some(s), 0) => {
            // append null terminator to make it a cstring
            let lit = format!("{}\0", s);

            // build RHS of the statement
            let rhs = lit.as_slice().chars().map(|c| { make_char_cast(cx, sp, c) }).collect::<Vec<_>>();
            let rhs = cx.expr_vec(sp, rhs);

            // the type of the item is a fixed length vector of libc::c_char
            let path = make_ty_path(cx, sp, vec!["libc", "c_char"]);
            let ty = cx.ty(sp, TyFixedLengthVec(path, cx.expr_lit(sp, LitInt(lit.len() as u64, UnsuffixedIntLit(Plus)))));

            // #[no_mangle] #[allow(dead_code, non_upper_case_globals)]
            let attrs = vec![
                make_attr_word(cx, sp, "no_mangle"),
                make_attr_list(cx, sp, "allow", vec!["dead_code", "non_upper_case_globals"])
            ];

            // XXX: have to manually construct the item here because we can't set the visibility otherwise.
            let item = P(ast::Item {
                ident: cx.ident_of(name),
                attrs: attrs,
                id: ast::DUMMY_NODE_ID,
                node: ast::ItemStatic(ty, MutImmutable, rhs),
                vis: Visibility::Public,
                span: sp
            });
            base::MacItems::new(vec![item].into_iter())
        }
        (_, _, rest) => {
            if rest > 0 {
                cx.span_err(sp, format!("expected 2 arguments, found {}", rest + 2).as_slice());
            }
            base::DummyResult::expr(sp)
        }
    }
}

/// Declare a `pub static [libc::c_char; N]`, where N is the length of the supplied string, plus 1.
/// The resulting item is a raw, null-terminated C string that becomes exported as an unmangled
/// symbol for use in dynamic libraries.
///
/// `export_cstr!(foo, "hello")` expands to `#[no_mangle] pub static foo: [libc::c_char; 6] = ['h' as libc::c_char, ..., 0 as libc::c_char];`
#[macro_export]
macro_rules! export_cstr {
    ($name:ident, $lit:expr) => (
        declare_static_raw_cstr!(stringify!($name), $lit);
    );
}

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("declare_static_raw_cstr", expand_declare_static_raw_cstr);
}

<|endoftext|>


<commit_before>// Copyright 2017 CoreOS, Inc.
//
// 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.

#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

#[macro_use]
extern crate hyper;
extern crate reqwest;
#[cfg(test)]
extern crate mockito;

extern crate base64;

#[macro_use]
extern crate error_chain;

#[macro_use]
extern crate slog;
#[macro_use]
extern crate slog_scope;

#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_xml_rs;
extern crate serde_json;

extern crate pnet;

extern crate openssl;
extern crate openssh_keys;
extern crate update_ssh_keys;

extern crate users;
extern crate hostname;
extern crate tempdir;
extern crate nix;

extern crate ipnetwork;

mod providers;
mod metadata;
mod network;
mod retry;
mod util;

pub mod errors {
    error_chain!{
        links {
            PublicKey(::openssh_keys::errors::Error, ::openssh_keys::errors::ErrorKind);
            AuthorizedKeys(::update_ssh_keys::errors::Error, ::update_ssh_keys::errors::ErrorKind);
        }
        foreign_links {
            Log(::slog::Error);
            XmlDeserialize(::serde_xml_rs::Error);
            Base64Decode(::base64::DecodeError);
            Io(::std::io::Error);
            Reqwest(::reqwest::Error);
            Hyper(::hyper::error::Error);
        }
        errors {
            UnknownProvider(p: String) {
                description("unknown provider")
                display("unknown provider '{}'", p)
            }
        }
    }
}

use providers::*;
use metadata::Metadata;

use errors::*;

/// `fetch_metadata` is the generic, top-level function that is used by the main
/// function to fetch metadata. The configured provider is passed in and this
/// function dispatches the call to the correct provider-specific fetch function
pub fn fetch_metadata(provider: &str) -> Result<Metadata> {
    match provider {
        "azure" => azure::fetch_metadata(),
        "cloudstack-metadata" => cloudstack::network::fetch_metadata(),
        "cloudstack-configdrive" => cloudstack::configdrive::fetch_metadata(),
        "digitalocean" => digitalocean::fetch_metadata(),
        "ec2" => ec2::fetch_metadata(),
        "gce" => gce::fetch_metadata(),
        "openstack-metadata" => openstack::network::fetch_metadata(),
        "packet" => packet::fetch_metadata(),
        "vagrant-virtualbox" => vagrant_virtualbox::fetch_metadata(),
        _ => Err(errors::ErrorKind::UnknownProvider(provider.to_owned()).into()),
    }
}
<commit_msg>lib: use MetadataProvider trait for fetch_metadata<commit_after>// Copyright 2017 CoreOS, Inc.
//
// 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.

#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

#[macro_use]
extern crate hyper;
extern crate reqwest;
#[cfg(test)]
extern crate mockito;

extern crate base64;

#[macro_use]
extern crate error_chain;

#[macro_use]
extern crate slog;
#[macro_use]
extern crate slog_scope;

#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_xml_rs;
extern crate serde_json;

extern crate pnet;

extern crate openssl;
extern crate openssh_keys;
extern crate update_ssh_keys;

extern crate users;
extern crate hostname;
extern crate tempdir;
extern crate nix;

extern crate ipnetwork;

mod providers;
mod metadata;
mod network;
mod retry;
mod util;

pub mod errors {
    error_chain!{
        links {
            PublicKey(::openssh_keys::errors::Error, ::openssh_keys::errors::ErrorKind);
            AuthorizedKeys(::update_ssh_keys::errors::Error, ::update_ssh_keys::errors::ErrorKind);
        }
        foreign_links {
            Log(::slog::Error);
            XmlDeserialize(::serde_xml_rs::Error);
            Base64Decode(::base64::DecodeError);
            Io(::std::io::Error);
            Reqwest(::reqwest::Error);
            Hyper(::hyper::error::Error);
        }
        errors {
            UnknownProvider(p: String) {
                description("unknown provider")
                display("unknown provider '{}'", p)
            }
        }
    }
}

use providers::*;
use metadata::Metadata;

use errors::*;

macro_rules! box_result {
    ($exp:expr) => (Ok(Box::new($exp?)))
}

/// `fetch_metadata` is the generic, top-level function that is used by the main
/// function to fetch metadata. The configured provider is passed in and this
/// function dispatches the call to the correct provider-specific fetch function
pub fn fetch_metadata(provider: &str) -> Result<Box<providers::MetadataProvider>> {
    match provider {
        "azure" => box_result!(azure::Azure::new()),
        "cloudstack-metadata" => box_result!(cloudstack::network::CloudstackNetwork::new()),
        "cloudstack-configdrive" => box_result!(cloudstack::configdrive::ConfigDrive::new()),
        "digitalocean" => box_result!(digitalocean::DigitalOceanProvider::new()),
        "ec2" => box_result!(ec2::Ec2Provider::new()),
        "gce" => box_result!(gce::GceProvider::new()),
        "openstack-metadata" => box_result!(openstack::network::OpenstackProvider::new()),
        "packet" => box_result!(packet::PacketProvider::new()),
        "vagrant-virtualbox" => box_result!(vagrant_virtualbox::VagrantVirtualboxProvider::new()),
        _ => Err(errors::ErrorKind::UnknownProvider(provider.to_owned()).into()),
    }
}
<|endoftext|>


<commit_before>extern crate libc;

use std::os::raw;
use std::ffi::CString;
use std::mem::{transmute, size_of};

#[link(name = "klee")]
extern {
  fn klee_make_symbolic(data: *const raw::c_void, length: libc::size_t, name: *const raw::c_char);
}

pub unsafe fn any(data: *const raw::c_void, length: usize, name: &str) {
  let name_cstr = CString::new(name).unwrap();
  klee_make_symbolic(data, length as libc::size_t, name_cstr.as_ptr());
}

pub fn i32(data: *const i32, name: &str) {
  unsafe { any(transmute(data), size_of::<i32>(), name); }
}

pub fn u32(data: *const u32, name: &str) {
  unsafe { any(transmute(data), size_of::<u32>(), name); }
}

pub fn i64(data: *const i64, name: &str) {
  unsafe { any(transmute(data), size_of::<i64>(), name); }
}

pub fn u64(data: *const u64, name: &str) {
  unsafe { any(transmute(data), size_of::<u64>(), name); }
}

pub fn str(data: &str, name: &str) {
  unsafe { any(transmute(data.as_ptr()), data.len(), name); }
}

pub fn bytes(data: &[u8], name: &str) {
  unsafe { any(transmute(data.as_ptr()), data.len(), name); }
}
<commit_msg>Update lib.rs<commit_after>extern crate libc;

use std::os::raw;
use std::ffi::CString;
use std::mem::{transmute, size_of};

#[link(name = "klee")]
extern {
  fn klee_make_symbolic(data: *const raw::c_void, length: libc::size_t, name: *const raw::c_char);
  fn klee_int(name: *const raw::c_char) -> raw::c_int;
}

pub unsafe fn any(data: *const raw::c_void, length: usize, name: &str) {
  let name_cstr = CString::new(name).unwrap();
  klee_make_symbolic(data, length as libc::size_t, name_cstr.as_ptr());
}

pub fn new_i32(name: &str) -> i32 {
  let result;
  let name_cstr = CString::new(name).unwrap();
  unsafe {
    result = klee_int(name_cstr);
  }
  result
}

pub fn i32(data: *const i32, name: &str) {
  unsafe { any(transmute(data), size_of::<i32>(), name); }
}

pub fn u32(data: *const u32, name: &str) {
  unsafe { any(transmute(data), size_of::<u32>(), name); }
}

pub fn i64(data: *const i64, name: &str) {
  unsafe { any(transmute(data), size_of::<i64>(), name); }
}

pub fn u64(data: *const u64, name: &str) {
  unsafe { any(transmute(data), size_of::<u64>(), name); }
}

pub fn str(data: &str, name: &str) {
  unsafe { any(transmute(data.as_ptr()), data.len(), name); }
}

pub fn bytes(data: &[u8], name: &str) {
  unsafe { any(transmute(data.as_ptr()), data.len(), name); }
}
<|endoftext|>


<commit_before>extern crate toml;
extern crate walkdir;
extern crate shellexpand;
extern crate regex;
extern crate url;
#[macro_use]
extern crate log;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate error_chain;

use std::path::Path;
use config::Config;
use walkdir::WalkDirIterator;

pub fn resolve_query(query: &str) -> errors::Result<url::Url> {
  let re_scheme = regex::Regex::new(r"^[^:]+://").unwrap();
  if re_scheme.is_match(query) {
    url::Url::parse(query).map_err(Into::into)
  } else {
    if let Some(host) = query.split("/").next() {
      match host {
        "github.com" | "bitbucket.org" | "gitlab.com" => {
          url::Url::parse(&format!("https://{}.git", query)).map_err(Into::into)
        }
        _ => url::Url::parse(&format!("https://github.com/{}.git", query)).map_err(Into::into),
      }
    } else {
      url::Url::parse(&format!("https://github.com/{}.git", query)).map_err(Into::into)
    }
  }
}

pub fn list_repositories() -> errors::Result<()> {
  let config = Config::load()?;
  warn!("{:?}", config);

  let repos = walkdir::WalkDir::new(&config.root)
    .follow_links(true)
    .into_iter()
    .filter_entry(|ref entry| {
      entry.path()
        .parent()
        .map(|path| detect_vcs(&path).is_none())
        .unwrap_or(true)
    })
    .filter_map(Result::ok)
    .filter(|ref entry| detect_vcs(entry.path()).is_some());

  for repo in repos {
    println!("{}", repo.path().display());
  }

  Ok(())
}

fn detect_vcs(path: &Path) -> Option<&'static str> {
  [".git", ".svn", ".hg", "_darcs"]
    .into_iter()
    .find(|vcs| path.join(vcs).exists())
    .map(|s| *s)
}

/// defines error types (automatically generated by error_chain)
pub mod errors {
  error_chain!{
    foreign_links {
      Io(::std::io::Error);
      TomlDe(::toml::de::Error);
      ShellExpand(::shellexpand::LookupError<::std::env::VarError>);
      UrlParse(::url::ParseError);
    }
  }
}

/// defines configuration
pub mod config {
  use std::fs::File;
  use std::io::Read;
  use std::path::{Path, PathBuf};
  use toml;
  use shellexpand;
  use errors::Result;

  #[derive(Default, Deserialize)]
  struct RawConfig {
    root: Option<String>,
  }

  impl RawConfig {
    fn from_file<P: AsRef<Path>>(path: P) -> Result<Option<RawConfig>> {
      if !path.as_ref().is_file() {
        return Ok(None);
      }

      let mut content = String::new();
      File::open(path)?.read_to_string(&mut content)?;
      Ok(Some(toml::from_str(&content).ok().unwrap_or_default()))
    }

    fn merge(&mut self, other: RawConfig) {
      if let Some(root) = other.root {
        self.root = Some(root);
      }
    }
  }

  fn read_all_config() -> Result<RawConfig> {
    #[cfg_attr(rustfmt, rustfmt_skip)]
    const DEFAULT_CONFIG: &'static str =
      include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/rhqconfig"));

    #[cfg_attr(rustfmt, rustfmt_skip)]
    const CANDIDATES: &'static [&'static str] =
      &["~/.config/rhq/config", "~/.rhqconfig"];

    let mut config: RawConfig = toml::from_str(DEFAULT_CONFIG)
      .expect("failed to decode default config");
    for path in CANDIDATES {
      let path = shellexpand::full(path).unwrap().into_owned();
      if let Some(conf) = RawConfig::from_file(path)? {
        config.merge(conf);
      }
    }

    Ok(config)
  }

  /// configuration load from config.toml
  #[derive(Debug)]
  pub struct Config {
    pub root: PathBuf,
  }

  impl Config {
    pub fn load() -> Result<Config> {
      let raw_config = read_all_config()?;

      let root = raw_config.root.expect("entry 'root' is not found");
      let root = PathBuf::from(shellexpand::full(&root)?.into_owned());

      Ok(Config { root: root })
    }
  }
}
<commit_msg>scp-like pattern<commit_after>extern crate toml;
extern crate walkdir;
extern crate shellexpand;
extern crate regex;
extern crate url;
#[macro_use]
extern crate log;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate error_chain;

use std::path::Path;
use config::Config;
use walkdir::WalkDirIterator;
use regex::Regex;

pub fn resolve_query(query: &str) -> errors::Result<url::Url> {
  let re_hasscheme = Regex::new(r"^[^:]+://").unwrap();
  let re_scplike = Regex::new(r"^([^@]+@)?([^:]+):/?(.+)$").unwrap();
  if re_hasscheme.is_match(query) {
    info!("has scheme");
    url::Url::parse(query).map_err(Into::into)
  } else if let Some(cap) = re_scplike.captures(query) {
    info!("SCP-like");
    let url = format!("ssh://{}{}/{}.git",
                      cap.get(1).unwrap().as_str(),
                      cap.get(2).unwrap().as_str(),
                      cap.get(3).unwrap().as_str());
    url::Url::parse(&url).map_err(Into::into)
  } else {
    info!("query");
    if let Some(_) = query.split("/").next().and_then(|host| match host {
      "github.com" | "bitbucket.org" | "gitlab.com" => Some(host),
      _ => None,
    }) {
      url::Url::parse(&format!("https://{}.git", query)).map_err(Into::into)
    } else {
      url::Url::parse(&format!("https://github.com/{}.git", query)).map_err(Into::into)
    }
  }
}

pub fn list_repositories() -> errors::Result<()> {
  let config = Config::load()?;
  warn!("{:?}", config);

  let repos = walkdir::WalkDir::new(&config.root)
    .follow_links(true)
    .into_iter()
    .filter_entry(|ref entry| {
      entry.path()
        .parent()
        .map(|path| detect_vcs(&path).is_none())
        .unwrap_or(true)
    })
    .filter_map(Result::ok)
    .filter(|ref entry| detect_vcs(entry.path()).is_some());

  for repo in repos {
    println!("{}", repo.path().display());
  }

  Ok(())
}

fn detect_vcs(path: &Path) -> Option<&'static str> {
  [".git", ".svn", ".hg", "_darcs"]
    .into_iter()
    .find(|vcs| path.join(vcs).exists())
    .map(|s| *s)
}

/// defines error types (automatically generated by error_chain)
pub mod errors {
  error_chain!{
    foreign_links {
      Io(::std::io::Error);
      TomlDe(::toml::de::Error);
      ShellExpand(::shellexpand::LookupError<::std::env::VarError>);
      UrlParse(::url::ParseError);
    }
  }
}

/// defines configuration
pub mod config {
  use std::fs::File;
  use std::io::Read;
  use std::path::{Path, PathBuf};
  use toml;
  use shellexpand;
  use errors::Result;

  #[derive(Default, Deserialize)]
  struct RawConfig {
    root: Option<String>,
  }

  impl RawConfig {
    fn from_file<P: AsRef<Path>>(path: P) -> Result<Option<RawConfig>> {
      if !path.as_ref().is_file() {
        return Ok(None);
      }

      let mut content = String::new();
      File::open(path)?.read_to_string(&mut content)?;
      Ok(Some(toml::from_str(&content).ok().unwrap_or_default()))
    }

    fn merge(&mut self, other: RawConfig) {
      if let Some(root) = other.root {
        self.root = Some(root);
      }
    }
  }

  fn read_all_config() -> Result<RawConfig> {
    #[cfg_attr(rustfmt, rustfmt_skip)]
    const DEFAULT_CONFIG: &'static str =
      include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/rhqconfig"));

    #[cfg_attr(rustfmt, rustfmt_skip)]
    const CANDIDATES: &'static [&'static str] =
      &["~/.config/rhq/config", "~/.rhqconfig"];

    let mut config: RawConfig = toml::from_str(DEFAULT_CONFIG)
      .expect("failed to decode default config");
    for path in CANDIDATES {
      let path = shellexpand::full(path).unwrap().into_owned();
      if let Some(conf) = RawConfig::from_file(path)? {
        config.merge(conf);
      }
    }

    Ok(config)
  }

  /// configuration load from config.toml
  #[derive(Debug)]
  pub struct Config {
    pub root: PathBuf,
  }

  impl Config {
    pub fn load() -> Result<Config> {
      let raw_config = read_all_config()?;

      let root = raw_config.root.expect("entry 'root' is not found");
      let root = PathBuf::from(shellexpand::full(&root)?.into_owned());

      Ok(Config { root: root })
    }
  }
}
<|endoftext|>


<commit_before>use std::io;
use std::io::prelude::*;
use std::io::{BufReader};
use std::fs::File;
use std::process::{ChildStdout, Command, Stdio, ExitStatus};
use std::path::Path;
use std::thread;
use std::sync::Mutex;
use std::ops::DerefMut;

use std::os::unix::io::FromRawFd;
use std::os::unix::io::AsRawFd;

pub enum Output {
    Parent,
    Ignore,
    FailOnOutput,
    ToFile(Box<Path>),
}

pub fn output() -> DealWithOutput {
    DealWithOutput { stdout: None, stderr: Output::Parent }
}

impl DealWithOutput {
    pub fn stderr(&mut self, stderr: Output) -> &mut Self {
        self.stderr = stderr;
        self
    }

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

pub struct DealWithOutput { stderr: Output, stdout: Option<Output> }

// Feed the input as stdin to the process and wait for the process to finish
pub fn process_as_consumer<R: Read>(
    deal_with: &mut DealWithOutput,
    mut input: R,
    cmd_args: (String, Vec<String>))
    -> io::Result<ExitStatus>
{
    let mut cmd = build_command(cmd_args);

    cmd.stdin(Stdio::piped());
    setup_stderr(&deal_with.stderr, &mut cmd)?;
    match deal_with.stdout {
        None => {}
        Some(ref stdout) => {
            setup_stdout(&stdout, &mut cmd)?;
        }
    }

    let mut process = cmd.spawn()?;
    let mut stdin = process.stdin.take().expect("impossible! no stdin");

    output_optional_handle(&deal_with.stderr, &mut process.stderr)?;
    match deal_with.stdout {
        None => {}
        Some(ref stdout) => {
            output_optional_handle(&stdout, &mut process.stderr)?;
        }
    }

    io::copy(&mut input, &mut stdin)?;
    let status = process.wait()?;
    Ok(status)
}

// Provide an iterator interface to a process
//
// Start a process for the given exe and args.
// Return a buffer or the error encountered when starting the process
//
// Feed input (if given) to its stdin (in a separate thread)
// Return a reader of the stdout
//
// Check stder, and wiat for the exit code on separate threads
//
// If any of the threads have failures, including if the proces has a non-zero exit code,
// panic in the that separate thread.
// TODO: the caller should be signalled about the error
pub fn process_as_iterator<R>(deal_with: &mut DealWithOutput, input_opt: Option<R>, cmd_args: (String, Vec<String>)) -> io::Result<BufReader<ChildStdout>>
    where R: Read + Send + 'static
{
    let mut cmd = build_command(cmd_args);

    // setup stdout
    cmd.stdout(Stdio::piped());
    if let Some(_) = input_opt {
        cmd.stdin(Stdio::piped());
    }

    setup_stderr(&deal_with.stderr, &mut cmd)?;

    let mut process = cmd.spawn()?;
    let stdout = process.stdout.take().expect("impossible! no stdout");

    output_optional_handle(&deal_with.stderr, &mut process.stderr)?;

    // feed input to stdin
    if let Some(input) = input_opt {
        let mut stdin = process.stdin.take().expect("impossible! no stdin");
        let input_mutex = Mutex::new(input);
        thread::spawn(move || {
            let mut inp = input_mutex.lock().expect("locking stdin input");
            io::copy(inp.deref_mut(), &mut stdin)
              .expect("error writing stdin");
        });
    }

    // wait for the process to exit successfully
    thread::spawn(move || {
        // assert that the process exits successfully
        let result = process.wait()
              .expect("error writing for the process to stop");
        if !result.success() {
            panic!("non-zero exit code {:?}", result.code());
        }
    });

    Ok(BufReader::new(stdout))
}

fn setup_stderr(deal_with_stderr: &Output, cmd: &mut Command) -> io::Result<()> {
    // setup stderr
    match deal_with_stderr {
      &Output::Parent => {}
      &Output::Ignore => {
          cmd.stderr(Stdio::null());
      }
      &Output::FailOnOutput => {
          cmd.stderr(Stdio::piped());
      }
      &Output::ToFile(ref path) => {
          // for windows should be
          // cmd.stderr(Stdio::piped());
          let file = File::open(path)?;
          unsafe {
            cmd.stderr(Stdio::from_raw_fd(file.as_raw_fd()));
          }
      }
    }
    Ok(())
}

fn setup_stdout(deal_with_stdout: &Output, cmd: &mut Command) -> io::Result<()> {
    // setup stderr
    match deal_with_stdout {
      &Output::Parent => {}
      &Output::Ignore => {
          cmd.stdout(Stdio::null());
      }
      &Output::FailOnOutput => {
          cmd.stdout(Stdio::piped());
      }
      &Output::ToFile(ref path) => {
          // for windows should be
          // cmd.stdout(Stdio::piped());
          let file = File::open(path)?;
          unsafe {
            cmd.stdout(Stdio::from_raw_fd(file.as_raw_fd()));
          }
      }
    }
    Ok(())
}

// Build up the command from the arguments
fn build_command(cmd_args: (String, Vec<String>)) -> Command {
    // Build up the command from the arguments
    let (exe, args) = cmd_args;
    let mut cmd = Command::new(exe);
    for arg in args { cmd.arg(arg); }
    return cmd
}

fn output_optional_handle<R: Read + Send + 'static>(deal_with_output: &Output, opt_handle: &mut Option<R>) -> io::Result<()> {
    if let &Output::FailOnOutput = deal_with_output {
        let handle = opt_handle.take().expect("impossible! no stderr");
        let _ = thread::spawn(move || {
            let mut stderr_output = Vec::new();
            let size = handle.take(1).read_to_end(&mut stderr_output).expect("error reading stdout");
            if size > 0 {
              panic!("got unexpected stderr");
            }
        });
    }

    /* on unix this is already setup.
       On Windows:
    else {
        if let &Output::ToFile(ref path) = deal_with_output {
            let mut handle = opt_handle.take().expect("impossible! no stderr");
            let mut file = File::open(path)?;
            let _ = thread::spawn(move || {
                io::copy(&mut handle, &mut file)
                  .expect("error writing stderr to a log file");
            });
        }
    }
    */

    Ok(())
}
<commit_msg>use a PathBuf<commit_after>use std::io;
use std::io::prelude::*;
use std::io::{BufReader};
use std::fs::File;
use std::process::{ChildStdout, Command, Stdio, ExitStatus};
use std::path::{PathBuf};
use std::thread;
use std::sync::Mutex;
use std::ops::DerefMut;

use std::os::unix::io::FromRawFd;
use std::os::unix::io::AsRawFd;

pub enum Output {
    Parent,
    Ignore,
    FailOnOutput,
    ToFile(PathBuf),
}

pub fn output_to_file(path: &str) -> Output {
    Output::ToFile(PathBuf::from(path))
}

pub fn output() -> DealWithOutput {
    DealWithOutput { stdout: None, stderr: Output::Parent }
}

impl DealWithOutput {
    pub fn stderr(&mut self, stderr: Output) -> &mut Self {
        self.stderr = stderr;
        self
    }

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

pub struct DealWithOutput { stderr: Output, stdout: Option<Output> }

// Feed the input as stdin to the process and wait for the process to finish
pub fn process_as_consumer<R: Read>(
    deal_with: &mut DealWithOutput,
    mut input: R,
    cmd_args: (String, Vec<String>))
    -> io::Result<ExitStatus>
{
    let mut cmd = build_command(cmd_args);

    cmd.stdin(Stdio::piped());
    setup_stderr(&deal_with.stderr, &mut cmd)?;
    match deal_with.stdout {
        None => {}
        Some(ref stdout) => {
            setup_stdout(&stdout, &mut cmd)?;
        }
    }

    let mut process = cmd.spawn()?;
    let mut stdin = process.stdin.take().expect("impossible! no stdin");

    output_optional_handle(&deal_with.stderr, &mut process.stderr)?;
    match deal_with.stdout {
        None => {}
        Some(ref stdout) => {
            output_optional_handle(&stdout, &mut process.stderr)?;
        }
    }

    io::copy(&mut input, &mut stdin)?;
    let status = process.wait()?;
    Ok(status)
}

// Provide an iterator interface to a process
//
// Start a process for the given exe and args.
// Return a buffer or the error encountered when starting the process
//
// Feed input (if given) to its stdin (in a separate thread)
// Return a reader of the stdout
//
// Check stder, and wiat for the exit code on separate threads
//
// If any of the threads have failures, including if the proces has a non-zero exit code,
// panic in the that separate thread.
// TODO: the caller should be signalled about the error
pub fn process_as_iterator<R>(deal_with: &mut DealWithOutput, input_opt: Option<R>, cmd_args: (String, Vec<String>)) -> io::Result<BufReader<ChildStdout>>
    where R: Read + Send + 'static
{
    let mut cmd = build_command(cmd_args);

    // setup stdout
    cmd.stdout(Stdio::piped());
    if let Some(_) = input_opt {
        cmd.stdin(Stdio::piped());
    }

    setup_stderr(&deal_with.stderr, &mut cmd)?;

    let mut process = cmd.spawn()?;
    let stdout = process.stdout.take().expect("impossible! no stdout");

    output_optional_handle(&deal_with.stderr, &mut process.stderr)?;

    // feed input to stdin
    if let Some(input) = input_opt {
        let mut stdin = process.stdin.take().expect("impossible! no stdin");
        let input_mutex = Mutex::new(input);
        thread::spawn(move || {
            let mut inp = input_mutex.lock().expect("locking stdin input");
            io::copy(inp.deref_mut(), &mut stdin)
              .expect("error writing stdin");
        });
    }

    // wait for the process to exit successfully
    thread::spawn(move || {
        // assert that the process exits successfully
        let result = process.wait()
              .expect("error writing for the process to stop");
        if !result.success() {
            panic!("non-zero exit code {:?}", result.code());
        }
    });

    Ok(BufReader::new(stdout))
}

fn setup_stderr(deal_with_stderr: &Output, cmd: &mut Command) -> io::Result<()> {
    // setup stderr
    match deal_with_stderr {
      &Output::Parent => {}
      &Output::Ignore => {
          cmd.stderr(Stdio::null());
      }
      &Output::FailOnOutput => {
          cmd.stderr(Stdio::piped());
      }
      &Output::ToFile(ref path) => {
          // for windows should be
          // cmd.stderr(Stdio::piped());
          let file = File::open(path)?;
          unsafe {
            cmd.stderr(Stdio::from_raw_fd(file.as_raw_fd()));
          }
      }
    }
    Ok(())
}

fn setup_stdout(deal_with_stdout: &Output, cmd: &mut Command) -> io::Result<()> {
    // setup stderr
    match deal_with_stdout {
      &Output::Parent => {}
      &Output::Ignore => {
          cmd.stdout(Stdio::null());
      }
      &Output::FailOnOutput => {
          cmd.stdout(Stdio::piped());
      }
      &Output::ToFile(ref path) => {
          // for windows should be
          // cmd.stdout(Stdio::piped());
          let file = File::open(path)?;
          unsafe {
            cmd.stdout(Stdio::from_raw_fd(file.as_raw_fd()));
          }
      }
    }
    Ok(())
}

// Build up the command from the arguments
fn build_command(cmd_args: (String, Vec<String>)) -> Command {
    // Build up the command from the arguments
    let (exe, args) = cmd_args;
    let mut cmd = Command::new(exe);
    for arg in args { cmd.arg(arg); }
    return cmd
}

fn output_optional_handle<R: Read + Send + 'static>(deal_with_output: &Output, opt_handle: &mut Option<R>) -> io::Result<()> {
    if let &Output::FailOnOutput = deal_with_output {
        let handle = opt_handle.take().expect("impossible! no stderr");
        let _ = thread::spawn(move || {
            let mut stderr_output = Vec::new();
            let size = handle.take(1).read_to_end(&mut stderr_output).expect("error reading stdout");
            if size > 0 {
              panic!("got unexpected stderr");
            }
        });
    }

    /* on unix this is already setup.
       On Windows:
    else {
        if let &Output::ToFile(ref path) = deal_with_output {
            let mut handle = opt_handle.take().expect("impossible! no stderr");
            let mut file = File::open(path)?;
            let _ = thread::spawn(move || {
                io::copy(&mut handle, &mut file)
                  .expect("error writing stderr to a log file");
            });
        }
    }
    */

    Ok(())
}
<|endoftext|>