<commit_before>#![cfg(unix)]
#[macro_use]
mod util;

use mio::net::UnixListener;
use mio::{Interests, Poll, Token};
use std::io::{self, Read};
use std::os::unix::io::AsRawFd;
use std::os::unix::net;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Barrier};
use std::thread;
use tempdir::TempDir;
use util::{
    assert_send, assert_sync, assert_would_block, expect_events, expect_no_events, init_with_poll,
    ExpectEvent,
};

const DEFAULT_BUF_SIZE: usize = 64;
const TOKEN_1: Token = Token(0);
const TOKEN_2: Token = Token(1);

#[test]
fn unix_listener_send_and_sync() {
    assert_send::<UnixListener>();
    assert_sync::<UnixListener>();
}

#[test]
fn unix_listener_smoke() {
    #[allow(clippy::redundant_closure)]
    smoke_test(|path| UnixListener::bind(path));
}

#[test]
fn unix_listener_from_std() {
    smoke_test(|path| {
        let listener = net::UnixListener::bind(path).unwrap();
        // `std::os::unix::net::UnixStream`s are blocking by default, so make sure
        // it is in non-blocking mode before wrapping in a Mio equivalent.
        listener.set_nonblocking(true).unwrap();
        Ok(UnixListener::from_std(listener))
    })
}

#[test]
fn unix_listener_try_clone_same_poll() {
    let (mut poll, mut events) = init_with_poll();
    let barrier = Arc::new(Barrier::new(3));
    let dir = TempDir::new("unix_listener").unwrap();
    let path = dir.path().join("any");

    let listener1 = UnixListener::bind(&path).unwrap();
    let listener2 = listener1.try_clone().unwrap();
    assert_ne!(listener1.as_raw_fd(), listener2.as_raw_fd());

    let handle_1 = open_connections(path.clone(), 1, barrier.clone());
    poll.registry()
        .register(&listener1, TOKEN_1, Interests::READABLE)
        .unwrap();
    poll.registry()
        .register(&listener2, TOKEN_2, Interests::READABLE)
        .unwrap();
    expect_events(
        &mut poll,
        &mut events,
        vec![
            ExpectEvent::new(TOKEN_1, Interests::READABLE),
            ExpectEvent::new(TOKEN_2, Interests::READABLE),
        ],
    );

    listener1.accept().unwrap();

    let handle_2 = open_connections(path.clone(), 1, barrier.clone());
    expect_events(
        &mut poll,
        &mut events,
        vec![
            ExpectEvent::new(TOKEN_1, Interests::READABLE),
            ExpectEvent::new(TOKEN_2, Interests::READABLE),
        ],
    );

    listener2.accept().unwrap();

    assert_would_block(listener1.accept());
    assert_would_block(listener2.accept());

    assert!(listener1.take_error().unwrap().is_none());
    assert!(listener2.take_error().unwrap().is_none());

    barrier.wait();
    handle_1.join().unwrap();
    handle_2.join().unwrap();
}

#[test]
fn unix_listener_try_clone_different_poll() {
    let (mut poll1, mut events) = init_with_poll();
    let mut poll2 = Poll::new().unwrap();
    let barrier = Arc::new(Barrier::new(3));
    let dir = TempDir::new("unix_listener").unwrap();
    let path = dir.path().join("any");

    let listener1 = UnixListener::bind(&path).unwrap();
    let listener2 = listener1.try_clone().unwrap();
    assert_ne!(listener1.as_raw_fd(), listener2.as_raw_fd());

    let handle_1 = open_connections(path.clone(), 1, barrier.clone());
    poll1
        .registry()
        .register(&listener1, TOKEN_1, Interests::READABLE)
        .unwrap();
    poll2
        .registry()
        .register(&listener2, TOKEN_2, Interests::READABLE)
        .unwrap();
    expect_events(
        &mut poll1,
        &mut events,
        vec![ExpectEvent::new(TOKEN_1, Interests::READABLE)],
    );
    expect_events(
        &mut poll2,
        &mut events,
        vec![ExpectEvent::new(TOKEN_2, Interests::READABLE)],
    );

    listener1.accept().unwrap();

    let handle_2 = open_connections(path.clone(), 1, barrier.clone());
    expect_events(
        &mut poll1,
        &mut events,
        vec![ExpectEvent::new(TOKEN_1, Interests::READABLE)],
    );
    expect_events(
        &mut poll2,
        &mut events,
        vec![ExpectEvent::new(TOKEN_2, Interests::READABLE)],
    );

    listener2.accept().unwrap();

    assert_would_block(listener1.accept());
    assert_would_block(listener2.accept());

    assert!(listener1.take_error().unwrap().is_none());
    assert!(listener2.take_error().unwrap().is_none());

    barrier.wait();
    handle_1.join().unwrap();
    handle_2.join().unwrap();
}

#[test]
fn unix_listener_local_addr() {
    let (mut poll, mut events) = init_with_poll();
    let barrier = Arc::new(Barrier::new(2));
    let dir = TempDir::new("unix_listener").unwrap();
    let path = dir.path().join("any");

    let listener = UnixListener::bind(&path).unwrap();
    poll.registry()
        .register(
            &listener,
            TOKEN_1,
            Interests::WRITABLE.add(Interests::READABLE),
        )
        .unwrap();

    let handle = open_connections(path.clone(), 1, barrier.clone());
    expect_events(
        &mut poll,
        &mut events,
        vec![ExpectEvent::new(TOKEN_1, Interests::READABLE)],
    );

    let (stream, expected_addr) = listener.accept().unwrap();
    assert_eq!(stream.local_addr().unwrap().as_pathname().unwrap(), &path);
    assert!(expected_addr.as_pathname().is_none());

    barrier.wait();
    handle.join().unwrap();
}

#[test]
fn unix_listener_register() {
    let (mut poll, mut events) = init_with_poll();
    let dir = TempDir::new("unix_listener").unwrap();

    let listener = UnixListener::bind(dir.path().join("any")).unwrap();
    poll.registry()
        .register(&listener, TOKEN_1, Interests::READABLE)
        .unwrap();
    expect_no_events(&mut poll, &mut events)
}

#[test]
fn unix_listener_reregister() {
    let (mut poll, mut events) = init_with_poll();
    let barrier = Arc::new(Barrier::new(2));
    let dir = TempDir::new("unix_listener").unwrap();
    let path = dir.path().join("any");

    let listener = UnixListener::bind(&path).unwrap();
    poll.registry()
        .register(&listener, TOKEN_1, Interests::WRITABLE)
        .unwrap();

    let handle = open_connections(path.clone(), 1, barrier.clone());
    expect_no_events(&mut poll, &mut events);

    poll.registry()
        .reregister(&listener, TOKEN_1, Interests::READABLE)
        .unwrap();
    expect_events(
        &mut poll,
        &mut events,
        vec![ExpectEvent::new(TOKEN_1, Interests::READABLE)],
    );

    barrier.wait();
    handle.join().unwrap();
}

#[test]
fn unix_listener_deregister() {
    let (mut poll, mut events) = init_with_poll();
    let barrier = Arc::new(Barrier::new(2));
    let dir = TempDir::new("unix_listener").unwrap();
    let path = dir.path().join("any");

    let listener = UnixListener::bind(&path).unwrap();
    poll.registry()
        .register(&listener, TOKEN_1, Interests::READABLE)
        .unwrap();

    let handle = open_connections(path.clone(), 1, barrier.clone());

    poll.registry().deregister(&listener).unwrap();
    expect_no_events(&mut poll, &mut events);

    barrier.wait();
    handle.join().unwrap();
}

fn smoke_test<F>(new_listener: F)
where
    F: FnOnce(&Path) -> io::Result<UnixListener>,
{
    let (mut poll, mut events) = init_with_poll();
    let barrier = Arc::new(Barrier::new(2));
    let dir = TempDir::new("unix_listener").unwrap();
    let path = dir.path().join("any");

    let listener = new_listener(&path).unwrap();
    poll.registry()
        .register(
            &listener,
            TOKEN_1,
            Interests::WRITABLE.add(Interests::READABLE),
        )
        .unwrap();
    expect_no_events(&mut poll, &mut events);

    let handle = open_connections(path.clone(), 1, barrier.clone());
    expect_events(
        &mut poll,
        &mut events,
        vec![ExpectEvent::new(TOKEN_1, Interests::READABLE)],
    );

    let (mut stream, _) = listener.accept().unwrap();

    let mut buf = [0; DEFAULT_BUF_SIZE];
    assert_would_block(stream.read(&mut buf));

    assert_would_block(listener.accept());
    assert!(listener.take_error().unwrap().is_none());

    barrier.wait();
    handle.join().unwrap();
}

fn open_connections(
    path: PathBuf,
    n_connections: usize,
    barrier: Arc<Barrier>,
) -> thread::JoinHandle<()> {
    thread::spawn(move || {
        for _ in 0..n_connections {
            let conn = net::UnixStream::connect(path.clone()).unwrap();
            barrier.wait();
            drop(conn);
        }
    })
}
<commit_msg>Remove some redundant clones<commit_after>#![cfg(unix)]
#[macro_use]
mod util;

use mio::net::UnixListener;
use mio::{Interests, Poll, Token};
use std::io::{self, Read};
use std::os::unix::io::AsRawFd;
use std::os::unix::net;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Barrier};
use std::thread;
use tempdir::TempDir;
use util::{
    assert_send, assert_sync, assert_would_block, expect_events, expect_no_events, init_with_poll,
    ExpectEvent,
};

const DEFAULT_BUF_SIZE: usize = 64;
const TOKEN_1: Token = Token(0);
const TOKEN_2: Token = Token(1);

#[test]
fn unix_listener_send_and_sync() {
    assert_send::<UnixListener>();
    assert_sync::<UnixListener>();
}

#[test]
fn unix_listener_smoke() {
    #[allow(clippy::redundant_closure)]
    smoke_test(|path| UnixListener::bind(path));
}

#[test]
fn unix_listener_from_std() {
    smoke_test(|path| {
        let listener = net::UnixListener::bind(path).unwrap();
        // `std::os::unix::net::UnixStream`s are blocking by default, so make sure
        // it is in non-blocking mode before wrapping in a Mio equivalent.
        listener.set_nonblocking(true).unwrap();
        Ok(UnixListener::from_std(listener))
    })
}

#[test]
fn unix_listener_try_clone_same_poll() {
    let (mut poll, mut events) = init_with_poll();
    let barrier = Arc::new(Barrier::new(3));
    let dir = TempDir::new("unix_listener").unwrap();
    let path = dir.path().join("any");

    let listener1 = UnixListener::bind(&path).unwrap();
    let listener2 = listener1.try_clone().unwrap();
    assert_ne!(listener1.as_raw_fd(), listener2.as_raw_fd());

    let handle_1 = open_connections(path.clone(), 1, barrier.clone());
    poll.registry()
        .register(&listener1, TOKEN_1, Interests::READABLE)
        .unwrap();
    poll.registry()
        .register(&listener2, TOKEN_2, Interests::READABLE)
        .unwrap();
    expect_events(
        &mut poll,
        &mut events,
        vec![
            ExpectEvent::new(TOKEN_1, Interests::READABLE),
            ExpectEvent::new(TOKEN_2, Interests::READABLE),
        ],
    );

    listener1.accept().unwrap();

    let handle_2 = open_connections(path, 1, barrier.clone());
    expect_events(
        &mut poll,
        &mut events,
        vec![
            ExpectEvent::new(TOKEN_1, Interests::READABLE),
            ExpectEvent::new(TOKEN_2, Interests::READABLE),
        ],
    );

    listener2.accept().unwrap();

    assert_would_block(listener1.accept());
    assert_would_block(listener2.accept());

    assert!(listener1.take_error().unwrap().is_none());
    assert!(listener2.take_error().unwrap().is_none());

    barrier.wait();
    handle_1.join().unwrap();
    handle_2.join().unwrap();
}

#[test]
fn unix_listener_try_clone_different_poll() {
    let (mut poll1, mut events) = init_with_poll();
    let mut poll2 = Poll::new().unwrap();
    let barrier = Arc::new(Barrier::new(3));
    let dir = TempDir::new("unix_listener").unwrap();
    let path = dir.path().join("any");

    let listener1 = UnixListener::bind(&path).unwrap();
    let listener2 = listener1.try_clone().unwrap();
    assert_ne!(listener1.as_raw_fd(), listener2.as_raw_fd());

    let handle_1 = open_connections(path.clone(), 1, barrier.clone());
    poll1
        .registry()
        .register(&listener1, TOKEN_1, Interests::READABLE)
        .unwrap();
    poll2
        .registry()
        .register(&listener2, TOKEN_2, Interests::READABLE)
        .unwrap();
    expect_events(
        &mut poll1,
        &mut events,
        vec![ExpectEvent::new(TOKEN_1, Interests::READABLE)],
    );
    expect_events(
        &mut poll2,
        &mut events,
        vec![ExpectEvent::new(TOKEN_2, Interests::READABLE)],
    );

    listener1.accept().unwrap();

    let handle_2 = open_connections(path, 1, barrier.clone());
    expect_events(
        &mut poll1,
        &mut events,
        vec![ExpectEvent::new(TOKEN_1, Interests::READABLE)],
    );
    expect_events(
        &mut poll2,
        &mut events,
        vec![ExpectEvent::new(TOKEN_2, Interests::READABLE)],
    );

    listener2.accept().unwrap();

    assert_would_block(listener1.accept());
    assert_would_block(listener2.accept());

    assert!(listener1.take_error().unwrap().is_none());
    assert!(listener2.take_error().unwrap().is_none());

    barrier.wait();
    handle_1.join().unwrap();
    handle_2.join().unwrap();
}

#[test]
fn unix_listener_local_addr() {
    let (mut poll, mut events) = init_with_poll();
    let barrier = Arc::new(Barrier::new(2));
    let dir = TempDir::new("unix_listener").unwrap();
    let path = dir.path().join("any");

    let listener = UnixListener::bind(&path).unwrap();
    poll.registry()
        .register(
            &listener,
            TOKEN_1,
            Interests::WRITABLE.add(Interests::READABLE),
        )
        .unwrap();

    let handle = open_connections(path.clone(), 1, barrier.clone());
    expect_events(
        &mut poll,
        &mut events,
        vec![ExpectEvent::new(TOKEN_1, Interests::READABLE)],
    );

    let (stream, expected_addr) = listener.accept().unwrap();
    assert_eq!(stream.local_addr().unwrap().as_pathname().unwrap(), &path);
    assert!(expected_addr.as_pathname().is_none());

    barrier.wait();
    handle.join().unwrap();
}

#[test]
fn unix_listener_register() {
    let (mut poll, mut events) = init_with_poll();
    let dir = TempDir::new("unix_listener").unwrap();

    let listener = UnixListener::bind(dir.path().join("any")).unwrap();
    poll.registry()
        .register(&listener, TOKEN_1, Interests::READABLE)
        .unwrap();
    expect_no_events(&mut poll, &mut events)
}

#[test]
fn unix_listener_reregister() {
    let (mut poll, mut events) = init_with_poll();
    let barrier = Arc::new(Barrier::new(2));
    let dir = TempDir::new("unix_listener").unwrap();
    let path = dir.path().join("any");

    let listener = UnixListener::bind(&path).unwrap();
    poll.registry()
        .register(&listener, TOKEN_1, Interests::WRITABLE)
        .unwrap();

    let handle = open_connections(path, 1, barrier.clone());
    expect_no_events(&mut poll, &mut events);

    poll.registry()
        .reregister(&listener, TOKEN_1, Interests::READABLE)
        .unwrap();
    expect_events(
        &mut poll,
        &mut events,
        vec![ExpectEvent::new(TOKEN_1, Interests::READABLE)],
    );

    barrier.wait();
    handle.join().unwrap();
}

#[test]
fn unix_listener_deregister() {
    let (mut poll, mut events) = init_with_poll();
    let barrier = Arc::new(Barrier::new(2));
    let dir = TempDir::new("unix_listener").unwrap();
    let path = dir.path().join("any");

    let listener = UnixListener::bind(&path).unwrap();
    poll.registry()
        .register(&listener, TOKEN_1, Interests::READABLE)
        .unwrap();

    let handle = open_connections(path, 1, barrier.clone());

    poll.registry().deregister(&listener).unwrap();
    expect_no_events(&mut poll, &mut events);

    barrier.wait();
    handle.join().unwrap();
}

fn smoke_test<F>(new_listener: F)
where
    F: FnOnce(&Path) -> io::Result<UnixListener>,
{
    let (mut poll, mut events) = init_with_poll();
    let barrier = Arc::new(Barrier::new(2));
    let dir = TempDir::new("unix_listener").unwrap();
    let path = dir.path().join("any");

    let listener = new_listener(&path).unwrap();
    poll.registry()
        .register(
            &listener,
            TOKEN_1,
            Interests::WRITABLE.add(Interests::READABLE),
        )
        .unwrap();
    expect_no_events(&mut poll, &mut events);

    let handle = open_connections(path, 1, barrier.clone());
    expect_events(
        &mut poll,
        &mut events,
        vec![ExpectEvent::new(TOKEN_1, Interests::READABLE)],
    );

    let (mut stream, _) = listener.accept().unwrap();

    let mut buf = [0; DEFAULT_BUF_SIZE];
    assert_would_block(stream.read(&mut buf));

    assert_would_block(listener.accept());
    assert!(listener.take_error().unwrap().is_none());

    barrier.wait();
    handle.join().unwrap();
}

fn open_connections(
    path: PathBuf,
    n_connections: usize,
    barrier: Arc<Barrier>,
) -> thread::JoinHandle<()> {
    thread::spawn(move || {
        for _ in 0..n_connections {
            let conn = net::UnixStream::connect(path.clone()).unwrap();
            barrier.wait();
            drop(conn);
        }
    })
}
<|endoftext|>


<commit_before>//! Defines the interface for the session-level management of HTTP/2
//! communication. This is effectively an API that allows hooking into an
//! HTTP/2 connection in order to handle events arising on the connection.
use std::collections::HashMap;
use super::{StreamId, Header};

/// A trait that defines methods that need to be defined in order to track the
/// status of a `ClientConnection`.
///
/// These methods are effectively callbacks that the `ClientConnection` invokes
/// on particular events in the HTTP/2 frame stream.
///
/// TODO Allow the session to influence the `ClientConnection` state and raise
///      errors (i.e. make the return type -> HttpResult<()>.
pub trait Session {
    /// Notifies the `Session` that a new data chunk has arrived on the
    /// connection for a particular stream. Only the raw data is passed
    /// to the callback (all padding is already discarded by the connection).
    fn new_data_chunk(&mut self, stream_id: StreamId, data: &[u8]);
    /// Notifies the `Session` that headers have arrived for a particular
    /// stream. The given list of headers is already decoded by the connection.
    fn new_headers(&mut self, stream_id: StreamId, headers: Vec<Header>);
    /// Notifies the `Session` that a particular stream got closed by the peer.
    fn end_of_stream(&mut self, stream_id: StreamId);
}

/// A trait representing a single HTTP/2 client stream. An HTTP/2 connection
/// multiplexes a number of streams.
///
/// The trait defines which operations need to be defined by a type that should
/// be useable as an HTTP/2 stream. By implementing this trait, clients can only
/// implement stream-level logic, such as how the received data should be handled,
/// instead of tracking which streams exist and what their states are.
pub trait Stream {
    /// Create a new stream with the given ID
    fn new(stream_id: StreamId) -> Self;
    /// Handle a new data chunk that has arrived for the stream.
    fn new_data_chunk(&mut self, data: &[u8]);
    /// Set headers for a stream. A stream is only allowed to have one set of
    /// headers.
    fn set_headers(&mut self, headers: Vec<Header>);
    /// Close the stream.
    fn close(&mut self);

    /// Returns the ID of the stream.
    fn id(&self) -> StreamId;
    /// Returns whether the stream is closed.
    fn is_closed(&self) -> bool;
}

/// An implementation of the `Stream` trait that saves all headers and data
/// in memory.
pub struct DefaultStream {
    /// The ID of the stream
    pub stream_id: StreamId,
    /// The headers associated with the stream (i.e. the response headers)
    pub headers: Option<Vec<Header>>,
    /// The body of the stream (i.e. the response body)
    pub body: Vec<u8>,
    /// Whether the stream is already closed
    pub closed: bool,
}

impl DefaultStream {
    /// Create a new `DefaultStream` with the given ID.
    pub fn new(stream_id: StreamId) -> DefaultStream {
        DefaultStream {
            stream_id: stream_id,
            headers: None,
            body: Vec::new(),
            closed: false,
        }
    }
}

impl Stream for DefaultStream {
    fn new(stream_id: StreamId) -> DefaultStream {
        DefaultStream::new(stream_id)
    }

    fn new_data_chunk(&mut self, data: &[u8]) {
        self.body.push_all(data);
    }

    fn set_headers(&mut self, headers: Vec<Header>) {
        self.headers = Some(headers);
    }

    fn close(&mut self) {
        self.closed = true;
    }

    fn id(&self) -> StreamId {
        self.stream_id
    }

    fn is_closed(&self) -> bool {
        self.closed
    }
}

/// A simple implementation of the `Session` trait.
///
/// Keeps track of which streams are currently active by holding a `HashMap`
/// of stream IDs to `Stream` instances. Callbacks delegate to the corresponding
/// stream instance, after validating the received stream ID.
///
/// The purpose of the type is to make it easier for client implementations to
/// only handle stream-level events by providing a `Stream` implementation,
/// instead of having to implement the entire session management (tracking active
/// streams, etc.).
///
/// For example, by varying the `Stream` implementation it is easy to implement
/// a client that streams responses directly into a file on the local file system,
/// instead of keeping it in memory (like the `DefaultStream` does), without
/// having to change any HTTP/2-specific logic.
pub struct DefaultSession<S=DefaultStream> where S: Stream {
    streams: HashMap<StreamId, S>,
}

impl<S> DefaultSession<S> where S: Stream {
    /// Returns a new `DefaultSession` with no active streams.
    pub fn new() -> DefaultSession<S> {
        DefaultSession {
            streams: HashMap::new(),
        }
    }

    /// Returns a reference to a stream with the given ID, if such a stream is
    /// found in the `DefaultSession`.
    pub fn get_stream(&self, stream_id: StreamId) -> Option<&S> {
        self.streams.get(&stream_id)
    }

    /// Creates a new stream with the given ID in the session.
    pub fn new_stream(&mut self, stream_id: StreamId) {
        self.streams.insert(stream_id, Stream::new(stream_id));
    }
}

impl<S> Session for DefaultSession<S> where S: Stream {
    fn new_data_chunk(&mut self, stream_id: StreamId, data: &[u8]) {
        debug!("Data chunk for stream {}", stream_id);
        let mut stream = match self.streams.get_mut(&stream_id) {
            None => {
                debug!("Received a frame for an unknown stream!");
                return;
            },
            Some(stream) => stream,
        };
        // Now let the stream handle the data chunk
        stream.new_data_chunk(data);
    }

    fn new_headers(&mut self, stream_id: StreamId, headers: Vec<Header>) {
        debug!("Headers for stream {}", stream_id);
        let mut stream = match self.streams.get_mut(&stream_id) {
            None => {
                debug!("Received a frame for an unknown stream!");
                return;
            },
            Some(stream) => stream,
        };
        // Now let the stream handle the headers
        stream.set_headers(headers);
    }

    fn end_of_stream(&mut self, stream_id: StreamId) {
        debug!("End of stream {}", stream_id);
        let mut stream = match self.streams.get_mut(&stream_id) {
            None => {
                debug!("Received a frame for an unknown stream!");
                return;
            },
            Some(stream) => stream,
        };
        stream.close()
    }
}

#[cfg(test)]
mod tests {
    use super::{
        Session, DefaultSession,
        Stream, DefaultStream,
    };

    /// Tests that a `DefaultSession` notifies the correct stream when the
    /// appropriate callback is invoked.
    ///
    /// A better unit test would give a mock Stream to the `DefaultSession`,
    /// instead of testing both the `DefaultSession` and the `DefaultStream`
    /// in the same time...
    #[test]
    fn test_default_session_notifies_stream() {
        let mut session: DefaultSession = DefaultSession::new();
        session.new_stream(1);

        // Registering some data to stream 1...
        session.new_data_chunk(1, &[1, 2, 3]);
        // ...works.
        assert_eq!(session.get_stream(1).unwrap().body, vec![1, 2, 3]);
        // Some more...
        session.new_data_chunk(1, &[4]);
        // ...works.
        assert_eq!(session.get_stream(1).unwrap().body, vec![1, 2, 3, 4]);
        // Now headers?
        let headers = vec![(b":method".to_vec(), b"GET".to_vec())];
        session.new_headers(1, headers.clone());
        assert_eq!(session.get_stream(1).unwrap().headers.clone().unwrap(),
                   headers);
        // Add another stream in the mix
        session.new_stream(3);
        // and send it some data
        session.new_data_chunk(3, &[100]);
        assert_eq!(session.get_stream(3).unwrap().body, vec![100]);
        // Finally, the stream 1 ends...
        session.end_of_stream(1);
        // ...and gets closed.
        assert!(session.get_stream(1).unwrap().closed);
        // but not the other one.
        assert!(!session.get_stream(3).unwrap().closed);
    }
}
<commit_msg>http::session: Update the module top-level documentation<commit_after>//! Defines the interface for the session-level management of HTTP/2
//! communication. This is effectively an API that allows hooking into an
//! HTTP/2 connection in order to handle events arising on the connection.
//!
//! It also provides a default implementation of this interface, the
//! `DefaultSession`. This implementation is based on keeping a mapping of
//! valid stream IDs to instances of `Stream` objects. When the session
//! receives a callback for a particular stream ID, it first validates that
//! it represents a valid stream ID and then delegates to the appropriate
//! action of a `Stream`. This allows clients to easily vary the stream-level
//! logic, without worrying about handling the book-keeping tasks of which
//! streams are active.
use std::collections::HashMap;
use super::{StreamId, Header};

/// A trait that defines methods that need to be defined in order to track the
/// status of a `ClientConnection`.
///
/// These methods are effectively callbacks that the `ClientConnection` invokes
/// on particular events in the HTTP/2 frame stream.
///
/// TODO Allow the session to influence the `ClientConnection` state and raise
///      errors (i.e. make the return type -> HttpResult<()>.
pub trait Session {
    /// Notifies the `Session` that a new data chunk has arrived on the
    /// connection for a particular stream. Only the raw data is passed
    /// to the callback (all padding is already discarded by the connection).
    fn new_data_chunk(&mut self, stream_id: StreamId, data: &[u8]);
    /// Notifies the `Session` that headers have arrived for a particular
    /// stream. The given list of headers is already decoded by the connection.
    fn new_headers(&mut self, stream_id: StreamId, headers: Vec<Header>);
    /// Notifies the `Session` that a particular stream got closed by the peer.
    fn end_of_stream(&mut self, stream_id: StreamId);
}

/// A trait representing a single HTTP/2 client stream. An HTTP/2 connection
/// multiplexes a number of streams.
///
/// The trait defines which operations need to be defined by a type that should
/// be useable as an HTTP/2 stream. By implementing this trait, clients can only
/// implement stream-level logic, such as how the received data should be handled,
/// instead of tracking which streams exist and what their states are.
pub trait Stream {
    /// Create a new stream with the given ID
    fn new(stream_id: StreamId) -> Self;
    /// Handle a new data chunk that has arrived for the stream.
    fn new_data_chunk(&mut self, data: &[u8]);
    /// Set headers for a stream. A stream is only allowed to have one set of
    /// headers.
    fn set_headers(&mut self, headers: Vec<Header>);
    /// Close the stream.
    fn close(&mut self);

    /// Returns the ID of the stream.
    fn id(&self) -> StreamId;
    /// Returns whether the stream is closed.
    fn is_closed(&self) -> bool;
}

/// An implementation of the `Stream` trait that saves all headers and data
/// in memory.
pub struct DefaultStream {
    /// The ID of the stream
    pub stream_id: StreamId,
    /// The headers associated with the stream (i.e. the response headers)
    pub headers: Option<Vec<Header>>,
    /// The body of the stream (i.e. the response body)
    pub body: Vec<u8>,
    /// Whether the stream is already closed
    pub closed: bool,
}

impl DefaultStream {
    /// Create a new `DefaultStream` with the given ID.
    pub fn new(stream_id: StreamId) -> DefaultStream {
        DefaultStream {
            stream_id: stream_id,
            headers: None,
            body: Vec::new(),
            closed: false,
        }
    }
}

impl Stream for DefaultStream {
    fn new(stream_id: StreamId) -> DefaultStream {
        DefaultStream::new(stream_id)
    }

    fn new_data_chunk(&mut self, data: &[u8]) {
        self.body.push_all(data);
    }

    fn set_headers(&mut self, headers: Vec<Header>) {
        self.headers = Some(headers);
    }

    fn close(&mut self) {
        self.closed = true;
    }

    fn id(&self) -> StreamId {
        self.stream_id
    }

    fn is_closed(&self) -> bool {
        self.closed
    }
}

/// A simple implementation of the `Session` trait.
///
/// Keeps track of which streams are currently active by holding a `HashMap`
/// of stream IDs to `Stream` instances. Callbacks delegate to the corresponding
/// stream instance, after validating the received stream ID.
///
/// The purpose of the type is to make it easier for client implementations to
/// only handle stream-level events by providing a `Stream` implementation,
/// instead of having to implement the entire session management (tracking active
/// streams, etc.).
///
/// For example, by varying the `Stream` implementation it is easy to implement
/// a client that streams responses directly into a file on the local file system,
/// instead of keeping it in memory (like the `DefaultStream` does), without
/// having to change any HTTP/2-specific logic.
pub struct DefaultSession<S=DefaultStream> where S: Stream {
    streams: HashMap<StreamId, S>,
}

impl<S> DefaultSession<S> where S: Stream {
    /// Returns a new `DefaultSession` with no active streams.
    pub fn new() -> DefaultSession<S> {
        DefaultSession {
            streams: HashMap::new(),
        }
    }

    /// Returns a reference to a stream with the given ID, if such a stream is
    /// found in the `DefaultSession`.
    pub fn get_stream(&self, stream_id: StreamId) -> Option<&S> {
        self.streams.get(&stream_id)
    }

    /// Creates a new stream with the given ID in the session.
    pub fn new_stream(&mut self, stream_id: StreamId) {
        self.streams.insert(stream_id, Stream::new(stream_id));
    }
}

impl<S> Session for DefaultSession<S> where S: Stream {
    fn new_data_chunk(&mut self, stream_id: StreamId, data: &[u8]) {
        debug!("Data chunk for stream {}", stream_id);
        let mut stream = match self.streams.get_mut(&stream_id) {
            None => {
                debug!("Received a frame for an unknown stream!");
                return;
            },
            Some(stream) => stream,
        };
        // Now let the stream handle the data chunk
        stream.new_data_chunk(data);
    }

    fn new_headers(&mut self, stream_id: StreamId, headers: Vec<Header>) {
        debug!("Headers for stream {}", stream_id);
        let mut stream = match self.streams.get_mut(&stream_id) {
            None => {
                debug!("Received a frame for an unknown stream!");
                return;
            },
            Some(stream) => stream,
        };
        // Now let the stream handle the headers
        stream.set_headers(headers);
    }

    fn end_of_stream(&mut self, stream_id: StreamId) {
        debug!("End of stream {}", stream_id);
        let mut stream = match self.streams.get_mut(&stream_id) {
            None => {
                debug!("Received a frame for an unknown stream!");
                return;
            },
            Some(stream) => stream,
        };
        stream.close()
    }
}

#[cfg(test)]
mod tests {
    use super::{
        Session, DefaultSession,
        Stream, DefaultStream,
    };

    /// Tests that a `DefaultSession` notifies the correct stream when the
    /// appropriate callback is invoked.
    ///
    /// A better unit test would give a mock Stream to the `DefaultSession`,
    /// instead of testing both the `DefaultSession` and the `DefaultStream`
    /// in the same time...
    #[test]
    fn test_default_session_notifies_stream() {
        let mut session: DefaultSession = DefaultSession::new();
        session.new_stream(1);

        // Registering some data to stream 1...
        session.new_data_chunk(1, &[1, 2, 3]);
        // ...works.
        assert_eq!(session.get_stream(1).unwrap().body, vec![1, 2, 3]);
        // Some more...
        session.new_data_chunk(1, &[4]);
        // ...works.
        assert_eq!(session.get_stream(1).unwrap().body, vec![1, 2, 3, 4]);
        // Now headers?
        let headers = vec![(b":method".to_vec(), b"GET".to_vec())];
        session.new_headers(1, headers.clone());
        assert_eq!(session.get_stream(1).unwrap().headers.clone().unwrap(),
                   headers);
        // Add another stream in the mix
        session.new_stream(3);
        // and send it some data
        session.new_data_chunk(3, &[100]);
        assert_eq!(session.get_stream(3).unwrap().body, vec![100]);
        // Finally, the stream 1 ends...
        session.end_of_stream(1);
        // ...and gets closed.
        assert!(session.get_stream(1).unwrap().closed);
        // but not the other one.
        assert!(!session.get_stream(3).unwrap().closed);
    }
}
<|endoftext|>


<commit_before>use fragment::matching::AsStr;
use std::path::PathBuf;

#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct IndexedPath {
    pub path: PathBuf,
    pub path_string: String,
}

impl<'a> AsStr for &'a IndexedPath {
    fn as_str(&self) -> &str {
        &self.path_string
    }
}

impl IndexedPath {
    pub fn new(path: PathBuf) -> IndexedPath {
        let path_string = path.to_string_lossy().into_owned();

        IndexedPath{
            path: path,
            path_string: path_string
        }
    }
}
<commit_msg>Add IndexedPath type description<commit_after>use fragment::matching::AsStr;
use std::path::PathBuf;

/// This Path + String pair exists so that we can build the path's string
/// representation once when populating the index, rather than on each search.
/// It's also how we support case insensitive indexing, where we need to store
/// a lowercased search value without modifying the case-sensitive PathBuf.
#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct IndexedPath {
    pub path: PathBuf,
    pub path_string: String,
}

impl<'a> AsStr for &'a IndexedPath {
    fn as_str(&self) -> &str {
        &self.path_string
    }
}

impl IndexedPath {
    pub fn new(path: PathBuf) -> IndexedPath {
        let path_string = path.to_string_lossy().into_owned();

        IndexedPath{
            path: path,
            path_string: path_string
        }
    }
}
<|endoftext|>


<commit_before>extern crate chrono;
#[macro_use]
extern crate diesel;
#[macro_use]
extern crate diesel_codegen;
extern crate dotenv;

use std::env;

//use chrono::prelude::*;
use diesel::prelude::*;
use dotenv::dotenv;

pub mod schema {
	infer_schema!("dotenv:DATABASE_URL");
}

pub mod domain {
	use chrono;

	use super::schema::books;

	#[derive(Queryable, Identifiable, AsChangeset, Clone)]
	pub struct Book {
	    pub id : i32,
	    pub title : Option<String>,
	    pub save_date : Option<chrono::NaiveDateTime>,
	}

/*
	#[derive(Insertable)]
	#[table_name="books"]
	pub struct NewBook<'a> {
	    pub title : Option<&'a str>,
	    pub save_date : Option<&'a chrono::NaiveDateTime>,
	}
*/
}

fn main() {
	dotenv().ok();
    use self::schema::books::dsl::*;
	let database_url = env::var("DATABASE_URL").unwrap();
	let conn = diesel::sqlite::SqliteConnection::establish(&database_url).unwrap();
/*
	let t = "The Lord of the Rings";
	let sd = NaiveDateTime::from_timestamp(Local::now().timestamp(), 0);
    let nb = domain::NewBook{
        title : Some(&t),
        save_date : Some(&sd)
    };
    diesel::insert(&nb).into(books).execute(&conn).unwrap();
*/
    match books.load::<domain::Book>(&conn) {
    	Ok(vb) => {
    		for b in vb {
    			if let Some(t) = b.title {
		    		println!("{}", t);
    			}
    			if let Some(sd) = b.save_date {
		    		println!("{}", sd);
    			}
    		}
    	},
    	Err(e) => println!("{}", e)
    }
}
<commit_msg>Fixed formatting.<commit_after>extern crate chrono;
#[macro_use]
extern crate diesel;
#[macro_use]
extern crate diesel_codegen;
extern crate dotenv;

use std::env;

//use chrono::prelude::*;
use diesel::prelude::*;
use dotenv::dotenv;

pub mod schema {
    infer_schema!("dotenv:DATABASE_URL");
}

pub mod domain {
    use chrono;

    use super::schema::books;

    #[derive(Queryable, Identifiable, AsChangeset, Clone)]
    pub struct Book {
        pub id: i32,
        pub title: Option<String>,
        pub save_date: Option<chrono::NaiveDateTime>,
    }
/*
    #[derive(Insertable)]
    #[table_name="books"]
    pub struct NewBook<'a> {
        pub title: Option<&'a str>,
        pub save_date: Option<&'a chrono::NaiveDateTime>,
    }
*/
}

fn main() {
    dotenv().ok();
    use self::schema::books::dsl::*;
    let database_url = env::var("DATABASE_URL").unwrap();
    let conn = diesel::sqlite::SqliteConnection::establish(&database_url).unwrap();
/*
    let t = "The Lord of the Rings";
    let sd = NaiveDateTime::from_timestamp(Local::now().timestamp(), 0);
    let nb = domain::NewBook {
        title: Some(&t),
        save_date: Some(&sd),
    };
    diesel::insert(&nb).into(books).execute(&conn).unwrap();
*/
    match books.load::<domain::Book>(&conn) {
        Ok(vb) => {
            for b in vb {
                if let Some(t) = b.title {
                    println!("{}", t);
                }
                if let Some(sd) = b.save_date {
                    println!("{}", sd);
                }
            }
        }
        Err(e) => println!("{}", e),
    }
}
<|endoftext|>


<commit_before>extern crate sendgrid;

use sendgrid::mail::Mail;
use sendgrid::sg_client::SGClient;

fn main() {
    let mut env_vars = std::env::vars();
    let api_key_check = env_vars.find(|var| var.0 == "SENDGRID_API_KEY");
    let api_key: String;
    match api_key_check {
        Some(key) => api_key = key.1,
        None => panic!("Must supply API key in environment variables to use!")
    }

    let sg = SGClient::new(api_key);

    let mut mail_info = Mail::new();
    mail_info.add_to("garrettsquire@gmail.com");
    mail_info.add_from("garrett.squire@sendgrid.net");
    mail_info.add_subject("Test");
    mail_info.add_html("
Cool
");

    sg.send(mail_info);
}
<commit_msg>update example for the public<commit_after>extern crate sendgrid;

use sendgrid::mail::Mail;
use sendgrid::sg_client::SGClient;

fn main() {
    let mut env_vars = std::env::vars();
    let api_key_check = env_vars.find(|var| var.0 == "SENDGRID_API_KEY");
    let api_key: String;
    match api_key_check {
        Some(key) => api_key = key.1,
        None => panic!("Must supply API key in environment variables to use!")
    }

    let sg = SGClient::new(api_key);

    let mut mail_info = Mail::new();
    mail_info.add_to("some@email.com");
    mail_info.add_from("another@email.com");
    mail_info.add_subject("Rust is rad");
    mail_info.add_html("
Hello from SendGrid!
");

    sg.send(mail_info);
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Needed to derive `Serialize` on ServiceProperties
#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]
// For Docopt macro
#![plugin(docopt_macros)]

// Needed for IntoIter in controller.rs
#![feature(collections)]

// Needed for time functions
#![feature(time2)]

// Make linter fail for every warning
#![plugin(clippy)]
#![deny(clippy)]

#![cfg_attr(test, feature(const_fn))] // Dependency of stainless
#![cfg_attr(test, plugin(stainless))] // Test runner

#![feature(reflect_marker)]

#![feature(associated_consts)]

extern crate core;
extern crate docopt;
extern crate env_logger;
extern crate foxbox_users;
#[macro_use]
extern crate iron;
#[cfg(test)]
extern crate iron_test;
extern crate libc;
#[macro_use]
extern crate log;
extern crate mio;
extern crate mount;
extern crate router;
extern crate rustc_serialize;
extern crate serde;
extern crate staticfile;
extern crate unicase;
extern crate uuid;
extern crate ws;
extern crate multicast_dns;

// Need to be declared first so to let the macros be visible from other modules.
#[macro_use]
mod utils;

mod controller;
mod adapters;
mod http_server;
mod managed_process;
mod registration;
mod service;
mod service_router;
mod tunnel_controller;
mod ws_server;

mod stubs {
    #![allow(dead_code)]
    #![allow(unused_variables)]
    #![allow(boxed_local)]
    pub mod service;
}

use controller::{ Controller, FoxBox, DEFAULT_HTTP_PORT };
use tunnel_controller:: { TunnelConfig, Tunnel };
use multicast_dns::host::HostManager;

docopt!(Args derive Debug, "
Usage: foxbox [-v] [-h] [-n <hostname>] [-p <port>] [-w <wsport>] [-r <url>] [-i <iface>] [-t <tunnel>]

Options:
    -v, --verbose            Toggle verbose output.
    -n, --name <hostname>    Set local hostname.
    -p, --port <port>        Set port to listen on for http connections.
    -w, --wsport <wsport>    Set port to listen on for websocket.
    -r, --register <url>     Change the url of the registration endpoint.
    -i, --iface <iface>      Specify the local IP interface.
    -t, --tunnel <tunnel>    Set the tunnel endpoint's hostname. If omitted, the tunnel is disabled.
    -h, --help               Print this help menu.
",
        flag_name: Option<String>,
        flag_port: Option<u16>,
        flag_wsport: Option<u16>,
        flag_iface: Option<String>,
        flag_register: Option<String>,
        flag_tunnel: Option<String>);

/// Updates local host name with the provided host name string. If requested host name
/// is not available (used by anyone else on the same network) then collision
/// resolution logic is triggered and alternative name is chosen automatically
/// (host name plus "-2", "-3" and etc. postfix). This function blocks until host name
/// is updated and returns actual host name.
///
/// # Panics
///
/// Panics if provided host name is not valid non-FQDN host name.
///
/// # Arguments
///
/// * `hostname` - host name name we'd like to set (should be a valid non-FQDN host name).
fn update_hostname(hostname: String) -> Option<String> {
    let host_manager = HostManager::new();

    if !host_manager.is_valid_name(&hostname) {
        panic!("Host name `{}` is not a valid host name!", &hostname);
    }

    Some(host_manager.set_name(&hostname))
}

fn main() {
    env_logger::init().unwrap();

    let args: Args = Args::docopt().decode().unwrap_or_else(|e| e.exit());

    let registrar = registration::Registrar::new();
    registrar.start(args.flag_register, args.flag_iface);

    // Start the tunnel.
    if let Some(host) = args.flag_tunnel {
        let mut tunnel =
            Tunnel::new(TunnelConfig::new(args.flag_port.unwrap_or(DEFAULT_HTTP_PORT), host));
        tunnel.start().unwrap();
    }

    let mut controller = FoxBox::new(
        args.flag_verbose, args.flag_name.map_or(None, update_hostname), args.flag_port,
        args.flag_wsport);

    controller.run();
}

#[test]
fn options_are_good() {
    // short form options
    {
        let argv = || vec!["foxbox", "-p", "1234", "-n", "foobar",
                           "-w", "4567", "-v"];

        let args: Args = Args::docopt().argv(argv().into_iter())
            .decode()
            .unwrap_or_else(|e| e.exit());

        assert_eq!(args.flag_verbose, true);
        assert_eq!(args.flag_name, Some("foobar".to_string()));
        assert_eq!(args.flag_port, Some(1234));
        assert_eq!(args.flag_wsport, Some(4567));
        assert_eq!(args.flag_help, false);
    }
    // long form options
    {
        let argv = || vec!["foxbox", "--port", "1234",
                           "--name", "foobar", "--wsport", "4567",
                           "--verbose"];

        let args: Args = Args::docopt().argv(argv().into_iter())
            .decode()
            .unwrap_or_else(|e| e.exit());

        assert_eq!(args.flag_verbose, true);
        assert_eq!(args.flag_name, Some("foobar".to_string()));
        assert_eq!(args.flag_port, Some(1234));
        assert_eq!(args.flag_wsport, Some(4567));
        assert_eq!(args.flag_help, false);
    }
}
<commit_msg>Bubble up default environment values to main.rs<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// Needed to derive `Serialize` on ServiceProperties
#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]
// For Docopt macro
#![plugin(docopt_macros)]

// Needed for IntoIter in controller.rs
#![feature(collections)]

// Needed for time functions
#![feature(time2)]

// Make linter fail for every warning
#![plugin(clippy)]
#![deny(clippy)]

#![cfg_attr(test, feature(const_fn))] // Dependency of stainless
#![cfg_attr(test, plugin(stainless))] // Test runner

#![feature(reflect_marker)]

#![feature(associated_consts)]

extern crate core;
extern crate docopt;
extern crate env_logger;
extern crate foxbox_users;
#[macro_use]
extern crate iron;
#[cfg(test)]
extern crate iron_test;
extern crate libc;
#[macro_use]
extern crate log;
extern crate mio;
extern crate mount;
extern crate router;
extern crate rustc_serialize;
extern crate serde;
extern crate staticfile;
extern crate unicase;
extern crate uuid;
extern crate ws;
extern crate multicast_dns;

// Need to be declared first so to let the macros be visible from other modules.
#[macro_use]
mod utils;

mod controller;
mod adapters;
mod http_server;
mod managed_process;
mod registration;
mod service;
mod service_router;
mod tunnel_controller;
mod ws_server;

mod stubs {
    #![allow(dead_code)]
    #![allow(unused_variables)]
    #![allow(boxed_local)]
    pub mod service;
}

use controller::{ Controller, FoxBox, DEFAULT_HTTP_PORT };
use tunnel_controller:: { TunnelConfig, Tunnel };
use multicast_dns::host::HostManager;

docopt!(Args derive Debug, "
Usage: foxbox [-v] [-h] [-n <hostname>] [-p <port>] [-w <wsport>] [-r <url>] [-i <iface>] [-t <tunnel>]

Options:
    -v, --verbose            Toggle verbose output.
    -n, --name <hostname>    Set local hostname. [default: foxbox]
    -p, --port <port>        Set port to listen on for http connections. [default: 3000]
    -w, --wsport <wsport>    Set port to listen on for websocket. [default: 4000]
    -r, --register <url>     Change the url of the registration endpoint. [default: http://localhost:4242/register]
    -i, --iface <iface>      Specify the local IP interface.
    -t, --tunnel <tunnel>    Set the tunnel endpoint's hostname. If omitted, the tunnel is disabled.
    -h, --help               Print this help menu.
",
        flag_name: Option<String>,
        flag_port: Option<u16>,
        flag_wsport: Option<u16>,
        flag_iface: Option<String>,
        flag_register: Option<String>,
        flag_tunnel: Option<String>);

/// Updates local host name with the provided host name string. If requested host name
/// is not available (used by anyone else on the same network) then collision
/// resolution logic is triggered and alternative name is chosen automatically
/// (host name plus "-2", "-3" and etc. postfix). This function blocks until host name
/// is updated and returns actual host name.
///
/// # Panics
///
/// Panics if provided host name is not valid non-FQDN host name.
///
/// # Arguments
///
/// * `hostname` - host name name we'd like to set (should be a valid non-FQDN host name).
fn update_hostname(hostname: String) -> Option<String> {
    let host_manager = HostManager::new();

    if !host_manager.is_valid_name(&hostname) {
        panic!("Host name `{}` is not a valid host name!", &hostname);
    }

    Some(host_manager.set_name(&hostname))
}

fn main() {
    env_logger::init().unwrap();

    let args: Args = Args::docopt().decode().unwrap_or_else(|e| e.exit());

    let registrar = registration::Registrar::new();
    registrar.start(args.flag_register, args.flag_iface);

    // Start the tunnel.
    if let Some(host) = args.flag_tunnel {
        let mut tunnel =
            Tunnel::new(TunnelConfig::new(args.flag_port.unwrap_or(DEFAULT_HTTP_PORT), host));
        tunnel.start().unwrap();
    }

    let mut controller = FoxBox::new(
        args.flag_verbose, args.flag_name.map_or(None, update_hostname), args.flag_port,
        args.flag_wsport);

    controller.run();
}

#[cfg(test)]
describe! main {
    describe! args {
        it "should have default values" {
            let argv = || vec!["foxbox"];
            let args: super::super::Args = super::super::Args::docopt().argv(argv().into_iter())
                .decode().unwrap();

            assert_eq!(args.flag_verbose, false);
            assert_eq!(args.flag_name.unwrap(), "foxbox");
            assert_eq!(args.flag_port.unwrap(), 3000);
            assert_eq!(args.flag_wsport.unwrap(), 4000);
            assert_eq!(args.flag_register.unwrap(), "http://localhost:4242/register");
            assert_eq!(args.flag_iface, None);
            assert_eq!(args.flag_tunnel, None);
            assert_eq!(args.flag_help, false);
        }

        it "should support short form" {
            let argv = || vec!["foxbox",
                               "-v",
                               "-p", "1234",
                               "-n", "foobar",
                               "-w", "4567",
                               "-r", "http://foo.bar:6868/register",
                               "-i", "eth99",
                               "-t", "tunnel.host"];

           let args: super::super::Args = super::super::Args::docopt().argv(argv().into_iter())
               .decode().unwrap();

            assert_eq!(args.flag_verbose, true);
            assert_eq!(args.flag_name.unwrap(), "foobar");
            assert_eq!(args.flag_port.unwrap(), 1234);
            assert_eq!(args.flag_wsport.unwrap(), 4567);
            assert_eq!(args.flag_register.unwrap(), "http://foo.bar:6868/register");
            assert_eq!(args.flag_iface.unwrap(), "eth99");
            assert_eq!(args.flag_tunnel.unwrap(), "tunnel.host");
        }

        it "should support long form" {
            let argv = || vec!["foxbox",
                               "--verbose",
                               "--port", "1234",
                               "--name", "foobar",
                               "--wsport", "4567",
                               "--register", "http://foo.bar:6868/register",
                               "--iface", "eth99",
                               "--tunnel", "tunnel.host"];

            let args: super::super::Args = super::super::Args::docopt().argv(argv().into_iter())
                .decode().unwrap();

            assert_eq!(args.flag_verbose, true);
            assert_eq!(args.flag_name.unwrap(), "foobar");
            assert_eq!(args.flag_port.unwrap(), 1234);
            assert_eq!(args.flag_wsport.unwrap(), 4567);
            assert_eq!(args.flag_register.unwrap(), "http://foo.bar:6868/register");
            assert_eq!(args.flag_iface.unwrap(), "eth99");
            assert_eq!(args.flag_tunnel.unwrap(), "tunnel.host");
        }
    }
}
<|endoftext|>


<commit_before>
extern crate mprovision;
extern crate clap;

use std::fs;
use clap::{Arg, App, AppSettings, SubCommand};

fn main() {
    let subcommands = vec![
        SubCommand::with_name("count")
            .about("Counts provisioning profiles in a directory.")
            .arg(Arg::with_name("DIRECTORY")
                .help("Directory where to count provisioning profiles.")
                .required(false)),
        SubCommand::with_name("search")
            .about("Searches provisioning profile for provided text.")
            .arg(Arg::with_name("TEXT")
                .help("Text to search in provisioning profile.")
                .required(true))
            .arg(Arg::with_name("DIRECTORY")
                .help("Directory where to search provisioning profiles.")
                .required(false)),
    ];

    let matches = App::new("mprovision")
                      .setting(AppSettings::SubcommandRequired)
                      .version("0.1.0")
                      .about("A tool that helps iOS developers to manage mobileprovision files.")
                      .subcommands(subcommands)
                      .get_matches();

    if let Some(matches) = matches.subcommand_matches("count") {
        handle_count_subcommand(matches)
    }
    if let Some(matches) = matches.subcommand_matches("search") {
        let s = matches.value_of("TEXT").unwrap();
        match mprovision::search(matches.value_of("DIRECTORY"), s) {
            Ok(results) => {
                for result in results {
                    match result {
                        Ok(profile) => println!("{}\n", profile.description()),
                        Err(err) => println!("Error: {}", err),
                    }
                }
            },
            Err(err) => println!("Error: {}", err),
        }
    }
}

fn handle_count_subcommand(matches: &clap::ArgMatches) {

    fn show(result: mprovision::Result<Box<Iterator<Item = fs::DirEntry>>>) {
        match result {
            Ok(files) => println!("Found {} files.", files.count()),
            Err(err) => println!("Error: {}", err),
        }
    }

    if let Some(directory) = matches.value_of("DIRECTORY") {
        show(mprovision::files(directory));
    } else {
        match mprovision::directory() {
            Ok(directory) => show(mprovision::files(directory)),
            Err(err) => println!("Error: {}", err),
        }
    }
}
<commit_msg>Remove count command.<commit_after>
extern crate mprovision;
extern crate clap;

use clap::{Arg, App, AppSettings, SubCommand};

fn main() {
    let subcommands = vec![
        SubCommand::with_name("search")
            .about("Searches provisioning profile for provided text.")
            .arg(Arg::with_name("TEXT")
                .help("Text to search in provisioning profile.")
                .required(true))
            .arg(Arg::with_name("DIRECTORY")
                .help("Directory where to search provisioning profiles.")
                .required(false)),
    ];

    let matches = App::new("mprovision")
                      .setting(AppSettings::SubcommandRequired)
                      .version("0.1.0")
                      .about("A tool that helps iOS developers to manage mobileprovision files.")
                      .subcommands(subcommands)
                      .get_matches();

    if let Some(matches) = matches.subcommand_matches("search") {
        let s = matches.value_of("TEXT").unwrap();
        match mprovision::search(matches.value_of("DIRECTORY"), s) {
            Ok(results) => {
                for result in results {
                    match result {
                        Ok(profile) => println!("{}\n", profile.description()),
                        Err(err) => println!("Error: {}", err),
                    }
                }
            },
            Err(err) => println!("Error: {}", err),
        }
    }
}
<|endoftext|>


<commit_before>fn main() {
    println!("{}", check());
}

#[cfg(target_arch = "x86_64")]
fn check() -> bool {
    std::mem::size_of::<usize>() == 8
}

#[cfg(not(target_arch = "x86_64"))]
fn check() -> bool {
    std::mem::size_of::<usize>() == 8
}

#[test]
fn ok() {
    assert!(check());
}
<commit_msg>Fix the build<commit_after>fn main() {
    println!("{}", check());
}

#[cfg(target_arch = "x86_64")]
fn check() -> bool {
    std::mem::size_of::<usize>() == 8
}

#[cfg(not(target_arch = "x86_64"))]
fn check() -> bool {
    std::mem::size_of::<usize>() == 4
}

#[test]
fn ok() {
    assert!(check());
}
<|endoftext|>


<commit_before>extern crate rand;
mod game;
use game::{Game,GameState};
mod user_interface;
use user_interface::{UserInterface};
mod dictionary;
use dictionary::{Dict};
mod image;
use image::RewardingScheme;

use std::env;
use std::path::PathBuf;
use std::io::prelude::*;
use std::fs::File;
use std::io;
use std::error::Error;

const COMMANDLINE_HELP: &'static str = "\
Hangman is a paper and pencil guessing game for two or more players.  One player
thinks of a word, phrase or sentence and the other tries to guess it by
suggesting letters or numbers, within a certain number of guesses. In this
version for children the computer selects a word, phrase or sentence randomly
out of a word-list defined in a configuration file. In the course of the game
Ascii-Art images - designed for children - are progressively disclosed.

===================================
-----------------------------------
HANGMAN GAME
         ,.
        (_|,.
       ,' /, )_______
    __j o``-'        `
   (\")
    `-j                |
      `-._(           /
         |_\\  |--^.  /
        /_]'|_| /_)_/
           /_]'  /_]'

Lives:  1       Last guess: 3

 g o o _ _ l _ _ k

Type a letter then type [Enter]:
-----------------------------------
===================================

 Usage: hangman [FILE]...
        hangman (-c|--help)
        hangman

`[FILE]` are configuration files containing word-lists and optionally Ascii-Art
images.

When no `[FILE]` argument is given, `[FILE]` defaults to 'hangman-words.txt'. In
case no `[FILE]` is found, a template configuration file 'hangman-words.txt' is
written into the current working directory. Multiple `[FILE]`s are concatted.

`[FILE]` is an Ascii file containing 4 different line-types:

- lines starting with `#` is ignored.

- lines starting with `|` are part of an optional Ascii-Art image shown
  progressively in the course of the game. If not defined here, built in
  Ascii-Art images are used instead.

- lines starting with `:` are game modifier. They change the logic how the image
  is progressively disclosed:
   `:success-rewarding`       Every guessed character shows a bit more of the
                              image. This mode is default.
   `:traditional-rewarding`   Every lost live discloses a bit more of the
                  image. Choose this mode together with a
                              traditional gallows image (not built in).

- lines starting with `-` are _guessing strings_. At the beginning of the game
  one line is randomly chosen and all characters are hidden.  In
  order to give additional hints it is possible to enclose some characters with
  `+*+`.  These words are then displayed in clear. For example a config line:
  `+- Guess *me*+` is shown in the game as: `_ _ _ _ _ _ m e`.

";

const LIVES: u8 = 7;
const PATHSTR: &'static str = "hangman-words.txt";
const OFFSET:  (usize,usize) = (1,1);

const CONF_TEMPLATE: &'static str = "\
### This is a sample word-list for the hangman game

### Sample word-list
#   ----------------
#
# Before every game one line is randomly chosen.
# Empty lines and lines starting with # are ignored.
# Lines with guessing strings must start with '-'.
# Words enclosed with * are not hidden when the game starts:
#   - *guess *me
# shows in the game:
#   g u e s s   _ _
#

- *guess* me
- hang*man*
- good luck

# Lines starting with ':' are game modifier. They change
# the logic how the image is progressively disclosed:
#   ':success-rewarding'       Every guessed character shows a bit more of the
#                              image. This mode is default.
#   ':traditional-rewarding'   Every lost live discloses a bit more of the
#                  image. Choose this mode together with a
#                              traditional gallows image (not built in).

### Sample custom image
#   -------------------
#
# Instead of built in images a word list can use a
# custom image. Lines starting with '|' are interpreted
# as image-lines. Delete '#' in the following lines to
# try out this feature.

#:traditional-rewarding
#|  ______
#|  |    |
#|  |    O
#|  |   /|\\
#|  |    |
#|  |   / \\
#|__|_____
#||      |___
#||_________|
";

const CONF_DEMO: &'static str =
    "- *Demo: add own words to config file and *start* again!*";

// ------------------ MAIN ---------------------------------------------

pub fn read_config(pathstr: &PathBuf) -> Result<String, io::Error> {
        let mut f = File::open(pathstr)?;
        let mut s = String::new();
        f.read_to_string(&mut s)?;
        Ok(s)
}

pub fn write_config_template(pathstr: &PathBuf) -> Result<(), io::Error> {
        let mut file = File::create(&pathstr)?;
        file.write_all( CONF_TEMPLATE.as_bytes() )?;
        Ok(())
}

fn main() {
    // SHOW HELP TEXT
    match env::args().nth(1) {
        Some(ref a) if a == "-h" || a == "--help"  => {
            println!("{}", COMMANDLINE_HELP);
            return();
        },
        Some(_) => {},
        None => {},
    };

    // READ CONFIG

    // Read all config files given on command line
    let mut conf_file_paths: Vec<PathBuf> = env::args()
                                .skip(1)
                                .map(|s|PathBuf::from(s))
                                .collect();

    // if no conf_file_paths are given then use default config path
    if conf_file_paths.len() == 0 { conf_file_paths.push(PathBuf::from(PATHSTR)) };

    // read and concat all config files given on command line
    let cwd =  env::current_dir().unwrap();

    let mut config: String = String::new();
    for conf_file_path in &conf_file_paths {

        let path = conf_file_path;
        let c = match read_config(&path) {
            Ok(s)  => s,
            Err(_) => {
                match write_config_template(&path) {
                    Ok(_) => {
                        println!("As no config-file :\n\
                                  \t{:?}\n\
                                  was found a template file is written in the \
                                  current working directory.\n\
                                  \t{:?}\n\n\nPress [Enter] to enter demo mode.",
                                  path,cwd);
                        // wait for [Enter] key
                        let input = &mut String::new();
                        io::stdin().read_line(input).unwrap();
                        CONF_DEMO.to_string()
                    },
                    Err(why) => {
                        println!("Couldn't write hangman template \
                                  config-file:\n\t{:?}\n({})\n\n\
                                  Current working directory is:\n\t{:?}\n\n\
                                  Press [Enter] to enter demo mode.",
                                 path,Error::description(&why),cwd);
                        // wait for [Enter] key
                        let input = &mut String::new();
                        io::stdin().read_line(input).unwrap();
                        CONF_DEMO.to_string()

                    },
                }
            }
        };
        config.push_str(&c);
    }

    // INITIALISE GAME

    let mut ui = UserInterface::new(&config, OFFSET);

    let mut dict = Dict::new(&config);
    if dict.len() == 0 {
         println!("No guessing words in config-file(s):\n\
                   \t{:?}\n\
                   were found. Current working directory is:\n\
                   \t{:?}\n\n\nPress [Enter] to enter demo mode.",
                   conf_file_paths,cwd);
         // wait for [Enter] key
         let input = &mut String::new();
         io::stdin().read_line(input).unwrap();
         dict = Dict::new(CONF_DEMO);
    };

    // PLAY

    'playing: loop {

        let mut game = Game::new(&(dict.get_random_word()), LIVES);
        let chars_to_guess = game.visible_chars();

        // The game loop
        let mut line_buffer = String::new();
        let reader = io::stdin();
        print!("\x1b[2J");  // clear screen
        'running_game: loop {
            if  game.lives > 0 || ui.image.rewarding_scheme ==
                                 RewardingScheme::UnhideWhenLostLife  {
                 ui.image.disclose((game.lives as usize, LIVES as usize),
                      (game.visible_chars(), chars_to_guess)  );
            }
            ui.message= format!("{}",game);
            println!("{}",ui);

            match game.state() {
                GameState::Victory => {
                    println!("Congratulations! You won!");
                    break 'running_game;
                }
                GameState::Defeat => {
                    println!("Sorry, you lost! Better luck next time!");
                    break 'running_game;
                }
                _ => {}
            }

            print!("Type a letter then type [Enter]: ");
            io::stdout().flush().unwrap();

            // Read next char and send it
            line_buffer.clear();
            reader.read_line(&mut line_buffer).unwrap();

            game.guess(
            match line_buffer.chars().next() {
                Some(char_) => {char_}
                None => {continue 'running_game}
            });
        }

        print!("New game? Type [Y]es or [n]o: ");
        io::stdout().flush().unwrap();

        // Read next char
        line_buffer.clear();
        reader.read_line(&mut line_buffer).unwrap();
        println!{"(c) Jens Getreu, 2016."};

        match line_buffer.trim_right().chars().next() {
                Some(char_) if char_ == 'N' || char_ == 'n'=> {break 'playing}
                Some(_) => {}
                None => {}
        }
    }
}

<commit_msg>replace deprecated trim_right()<commit_after>extern crate rand;
mod game;
use game::{Game,GameState};
mod user_interface;
use user_interface::{UserInterface};
mod dictionary;
use dictionary::{Dict};
mod image;
use image::RewardingScheme;

use std::env;
use std::path::PathBuf;
use std::io::prelude::*;
use std::fs::File;
use std::io;
use std::error::Error;

const COMMANDLINE_HELP: &'static str = "\
Hangman is a paper and pencil guessing game for two or more players.  One player
thinks of a word, phrase or sentence and the other tries to guess it by
suggesting letters or numbers, within a certain number of guesses. In this
version for children the computer selects a word, phrase or sentence randomly
out of a word-list defined in a configuration file. In the course of the game
Ascii-Art images - designed for children - are progressively disclosed.

===================================
-----------------------------------
HANGMAN GAME
         ,.
        (_|,.
       ,' /, )_______
    __j o``-'        `
   (\")
    `-j                |
      `-._(           /
         |_\\  |--^.  /
        /_]'|_| /_)_/
           /_]'  /_]'

Lives:  1       Last guess: 3

 g o o _ _ l _ _ k

Type a letter then type [Enter]:
-----------------------------------
===================================

 Usage: hangman [FILE]...
        hangman (-c|--help)
        hangman

`[FILE]` are configuration files containing word-lists and optionally Ascii-Art
images.

When no `[FILE]` argument is given, `[FILE]` defaults to 'hangman-words.txt'. In
case no `[FILE]` is found, a template configuration file 'hangman-words.txt' is
written into the current working directory. Multiple `[FILE]`s are concatted.

`[FILE]` is an Ascii file containing 4 different line-types:

- lines starting with `#` is ignored.

- lines starting with `|` are part of an optional Ascii-Art image shown
  progressively in the course of the game. If not defined here, built in
  Ascii-Art images are used instead.

- lines starting with `:` are game modifier. They change the logic how the image
  is progressively disclosed:
   `:success-rewarding`       Every guessed character shows a bit more of the
                              image. This mode is default.
   `:traditional-rewarding`   Every lost live discloses a bit more of the
                  image. Choose this mode together with a
                              traditional gallows image (not built in).

- lines starting with `-` are _guessing strings_. At the beginning of the game
  one line is randomly chosen and all characters are hidden.  In
  order to give additional hints it is possible to enclose some characters with
  `+*+`.  These words are then displayed in clear. For example a config line:
  `+- Guess *me*+` is shown in the game as: `_ _ _ _ _ _ m e`.

";

const LIVES: u8 = 7;
const PATHSTR: &'static str = "hangman-words.txt";
const OFFSET:  (usize,usize) = (1,1);

const CONF_TEMPLATE: &'static str = "\
### This is a sample word-list for the hangman game

### Sample word-list
#   ----------------
#
# Before every game one line is randomly chosen.
# Empty lines and lines starting with # are ignored.
# Lines with guessing strings must start with '-'.
# Words enclosed with * are not hidden when the game starts:
#   - *guess *me
# shows in the game:
#   g u e s s   _ _
#

- *guess* me
- hang*man*
- good luck

# Lines starting with ':' are game modifier. They change
# the logic how the image is progressively disclosed:
#   ':success-rewarding'       Every guessed character shows a bit more of the
#                              image. This mode is default.
#   ':traditional-rewarding'   Every lost live discloses a bit more of the
#                  image. Choose this mode together with a
#                              traditional gallows image (not built in).

### Sample custom image
#   -------------------
#
# Instead of built in images a word list can use a
# custom image. Lines starting with '|' are interpreted
# as image-lines. Delete '#' in the following lines to
# try out this feature.

#:traditional-rewarding
#|  ______
#|  |    |
#|  |    O
#|  |   /|\\
#|  |    |
#|  |   / \\
#|__|_____
#||      |___
#||_________|
";

const CONF_DEMO: &'static str =
    "- *Demo: add own words to config file and *start* again!*";

// ------------------ MAIN ---------------------------------------------

pub fn read_config(pathstr: &PathBuf) -> Result<String, io::Error> {
        let mut f = File::open(pathstr)?;
        let mut s = String::new();
        f.read_to_string(&mut s)?;
        Ok(s)
}

pub fn write_config_template(pathstr: &PathBuf) -> Result<(), io::Error> {
        let mut file = File::create(&pathstr)?;
        file.write_all( CONF_TEMPLATE.as_bytes() )?;
        Ok(())
}

fn main() {
    // SHOW HELP TEXT
    match env::args().nth(1) {
        Some(ref a) if a == "-h" || a == "--help"  => {
            println!("{}", COMMANDLINE_HELP);
            return();
        },
        Some(_) => {},
        None => {},
    };

    // READ CONFIG

    // Read all config files given on command line
    let mut conf_file_paths: Vec<PathBuf> = env::args()
                                .skip(1)
                                .map(|s|PathBuf::from(s))
                                .collect();

    // if no conf_file_paths are given then use default config path
    if conf_file_paths.len() == 0 { conf_file_paths.push(PathBuf::from(PATHSTR)) };

    // read and concat all config files given on command line
    let cwd =  env::current_dir().unwrap();

    let mut config: String = String::new();
    for conf_file_path in &conf_file_paths {

        let path = conf_file_path;
        let c = match read_config(&path) {
            Ok(s)  => s,
            Err(_) => {
                match write_config_template(&path) {
                    Ok(_) => {
                        println!("As no config-file :\n\
                                  \t{:?}\n\
                                  was found a template file is written in the \
                                  current working directory.\n\
                                  \t{:?}\n\n\nPress [Enter] to enter demo mode.",
                                  path,cwd);
                        // wait for [Enter] key
                        let input = &mut String::new();
                        io::stdin().read_line(input).unwrap();
                        CONF_DEMO.to_string()
                    },
                    Err(why) => {
                        println!("Couldn't write hangman template \
                                  config-file:\n\t{:?}\n({})\n\n\
                                  Current working directory is:\n\t{:?}\n\n\
                                  Press [Enter] to enter demo mode.",
                                 path,Error::description(&why),cwd);
                        // wait for [Enter] key
                        let input = &mut String::new();
                        io::stdin().read_line(input).unwrap();
                        CONF_DEMO.to_string()

                    },
                }
            }
        };
        config.push_str(&c);
    }

    // INITIALISE GAME

    let mut ui = UserInterface::new(&config, OFFSET);

    let mut dict = Dict::new(&config);
    if dict.len() == 0 {
         println!("No guessing words in config-file(s):\n\
                   \t{:?}\n\
                   were found. Current working directory is:\n\
                   \t{:?}\n\n\nPress [Enter] to enter demo mode.",
                   conf_file_paths,cwd);
         // wait for [Enter] key
         let input = &mut String::new();
         io::stdin().read_line(input).unwrap();
         dict = Dict::new(CONF_DEMO);
    };

    // PLAY

    'playing: loop {

        let mut game = Game::new(&(dict.get_random_word()), LIVES);
        let chars_to_guess = game.visible_chars();

        // The game loop
        let mut line_buffer = String::new();
        let reader = io::stdin();
        print!("\x1b[2J");  // clear screen
        'running_game: loop {
            if  game.lives > 0 || ui.image.rewarding_scheme ==
                                 RewardingScheme::UnhideWhenLostLife  {
                 ui.image.disclose((game.lives as usize, LIVES as usize),
                      (game.visible_chars(), chars_to_guess)  );
            }
            ui.message= format!("{}",game);
            println!("{}",ui);

            match game.state() {
                GameState::Victory => {
                    println!("Congratulations! You won!");
                    break 'running_game;
                }
                GameState::Defeat => {
                    println!("Sorry, you lost! Better luck next time!");
                    break 'running_game;
                }
                _ => {}
            }

            print!("Type a letter then type [Enter]: ");
            io::stdout().flush().unwrap();

            // Read next char and send it
            line_buffer.clear();
            reader.read_line(&mut line_buffer).unwrap();

            game.guess(
            match line_buffer.chars().next() {
                Some(char_) => {char_}
                None => {continue 'running_game}
            });
        }

        print!("New game? Type [Y]es or [n]o: ");
        io::stdout().flush().unwrap();

        // Read next char
        line_buffer.clear();
        reader.read_line(&mut line_buffer).unwrap();
        println!{"(c) Jens Getreu, 2016."};

        match line_buffer.trim_end().chars().next() {
                Some(char_) if char_ == 'N' || char_ == 'n'=> {break 'playing}
                Some(_) => {}
                None => {}
        }
    }
}

<|endoftext|>