<commit_before>use crate::io::{AsyncRead, AsyncWrite, ReadBuf};

use std::future::Future;
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};

#[derive(Debug)]
pub(super) struct CopyBuffer {
    read_done: bool,
    need_flush: bool,
    pos: usize,
    cap: usize,
    amt: u64,
    buf: Box<[u8]>,
}

impl CopyBuffer {
    pub(super) fn new() -> Self {
        Self {
            read_done: false,
            need_flush: false,
            pos: 0,
            cap: 0,
            amt: 0,
            buf: vec![0; 2048].into_boxed_slice(),
        }
    }

    pub(super) fn poll_copy<R, W>(
        &mut self,
        cx: &mut Context<'_>,
        mut reader: Pin<&mut R>,
        mut writer: Pin<&mut W>,
    ) -> Poll<io::Result<u64>>
    where
        R: AsyncRead + ?Sized,
        W: AsyncWrite + ?Sized,
    {
        loop {
            // If our buffer is empty, then we need to read some data to
            // continue.
            if self.pos == self.cap && !self.read_done {
                let me = &mut *self;
                let mut buf = ReadBuf::new(&mut me.buf);

                match reader.as_mut().poll_read(cx, &mut buf) {
                    Poll::Ready(Ok(_)) => (),
                    Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
                    Poll::Pending => {
                        // Try flushing when the reader has no progress to avoid deadlock
                        // when the reader depends on buffered writer.
                        if self.need_flush {
                            ready!(writer.as_mut().poll_flush(cx))?;
                            self.need_flush = false;
                        }

                        return Poll::Pending;
                    }
                }

                let n = buf.filled().len();
                if n == 0 {
                    self.read_done = true;
                } else {
                    self.pos = 0;
                    self.cap = n;
                }
            }

            // If our buffer has some data, let's write it out!
            while self.pos < self.cap {
                let me = &mut *self;
                let i = ready!(writer.as_mut().poll_write(cx, &me.buf[me.pos..me.cap]))?;
                if i == 0 {
                    return Poll::Ready(Err(io::Error::new(
                        io::ErrorKind::WriteZero,
                        "write zero byte into writer",
                    )));
                } else {
                    self.pos += i;
                    self.amt += i as u64;
                    self.need_flush = true;
                }
            }

            // If pos larger than cap, this loop will never stop.
            // In particular, user's wrong poll_write implementation returning
            // incorrect written length may lead to thread blocking.
            debug_assert!(
                self.pos <= self.cap,
                "writer returned length larger than input slice"
            );

            // If we've written all the data and we've seen EOF, flush out the
            // data and finish the transfer.
            if self.pos == self.cap && self.read_done {
                ready!(writer.as_mut().poll_flush(cx))?;
                return Poll::Ready(Ok(self.amt));
            }
        }
    }
}

/// A future that asynchronously copies the entire contents of a reader into a
/// writer.
#[derive(Debug)]
#[must_use = "futures do nothing unless you `.await` or poll them"]
struct Copy<'a, R: ?Sized, W: ?Sized> {
    reader: &'a mut R,
    writer: &'a mut W,
    buf: CopyBuffer,
}

cfg_io_util! {
    /// Asynchronously copies the entire contents of a reader into a writer.
    ///
    /// This function returns a future that will continuously read data from
    /// `reader` and then write it into `writer` in a streaming fashion until
    /// `reader` returns EOF.
    ///
    /// On success, the total number of bytes that were copied from `reader` to
    /// `writer` is returned.
    ///
    /// This is an asynchronous version of [`std::io::copy`][std].
    ///
    /// [std]: std::io::copy
    ///
    /// # Errors
    ///
    /// The returned future will return an error immediately if any call to
    /// `poll_read` or `poll_write` returns an error.
    ///
    /// # Examples
    ///
    /// ```
    /// use tokio::io;
    ///
    /// # async fn dox() -> std::io::Result<()> {
    /// let mut reader: &[u8] = b"hello";
    /// let mut writer: Vec<u8> = vec![];
    ///
    /// io::copy(&mut reader, &mut writer).await?;
    ///
    /// assert_eq!(&b"hello"[..], &writer[..]);
    /// # Ok(())
    /// # }
    /// ```
    pub async fn copy<'a, R, W>(reader: &'a mut R, writer: &'a mut W) -> io::Result<u64>
    where
        R: AsyncRead + Unpin + ?Sized,
        W: AsyncWrite + Unpin + ?Sized,
    {
        Copy {
            reader,
            writer,
            buf: CopyBuffer::new()
        }.await
    }
}

impl<R, W> Future for Copy<'_, R, W>
where
    R: AsyncRead + Unpin + ?Sized,
    W: AsyncWrite + Unpin + ?Sized,
{
    type Output = io::Result<u64>;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
        let me = &mut *self;

        me.buf
            .poll_copy(cx, Pin::new(&mut *me.reader), Pin::new(&mut *me.writer))
    }
}
<commit_msg>io: update copy buffer size (#4209)<commit_after>use crate::io::{AsyncRead, AsyncWrite, ReadBuf};

use std::future::Future;
use std::io;
use std::pin::Pin;
use std::task::{Context, Poll};

#[derive(Debug)]
pub(super) struct CopyBuffer {
    read_done: bool,
    need_flush: bool,
    pos: usize,
    cap: usize,
    amt: u64,
    buf: Box<[u8]>,
}

impl CopyBuffer {
    pub(super) fn new() -> Self {
        Self {
            read_done: false,
            need_flush: false,
            pos: 0,
            cap: 0,
            amt: 0,
            buf: vec![0; super::DEFAULT_BUF_SIZE].into_boxed_slice(),
        }
    }

    pub(super) fn poll_copy<R, W>(
        &mut self,
        cx: &mut Context<'_>,
        mut reader: Pin<&mut R>,
        mut writer: Pin<&mut W>,
    ) -> Poll<io::Result<u64>>
    where
        R: AsyncRead + ?Sized,
        W: AsyncWrite + ?Sized,
    {
        loop {
            // If our buffer is empty, then we need to read some data to
            // continue.
            if self.pos == self.cap && !self.read_done {
                let me = &mut *self;
                let mut buf = ReadBuf::new(&mut me.buf);

                match reader.as_mut().poll_read(cx, &mut buf) {
                    Poll::Ready(Ok(_)) => (),
                    Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
                    Poll::Pending => {
                        // Try flushing when the reader has no progress to avoid deadlock
                        // when the reader depends on buffered writer.
                        if self.need_flush {
                            ready!(writer.as_mut().poll_flush(cx))?;
                            self.need_flush = false;
                        }

                        return Poll::Pending;
                    }
                }

                let n = buf.filled().len();
                if n == 0 {
                    self.read_done = true;
                } else {
                    self.pos = 0;
                    self.cap = n;
                }
            }

            // If our buffer has some data, let's write it out!
            while self.pos < self.cap {
                let me = &mut *self;
                let i = ready!(writer.as_mut().poll_write(cx, &me.buf[me.pos..me.cap]))?;
                if i == 0 {
                    return Poll::Ready(Err(io::Error::new(
                        io::ErrorKind::WriteZero,
                        "write zero byte into writer",
                    )));
                } else {
                    self.pos += i;
                    self.amt += i as u64;
                    self.need_flush = true;
                }
            }

            // If pos larger than cap, this loop will never stop.
            // In particular, user's wrong poll_write implementation returning
            // incorrect written length may lead to thread blocking.
            debug_assert!(
                self.pos <= self.cap,
                "writer returned length larger than input slice"
            );

            // If we've written all the data and we've seen EOF, flush out the
            // data and finish the transfer.
            if self.pos == self.cap && self.read_done {
                ready!(writer.as_mut().poll_flush(cx))?;
                return Poll::Ready(Ok(self.amt));
            }
        }
    }
}

/// A future that asynchronously copies the entire contents of a reader into a
/// writer.
#[derive(Debug)]
#[must_use = "futures do nothing unless you `.await` or poll them"]
struct Copy<'a, R: ?Sized, W: ?Sized> {
    reader: &'a mut R,
    writer: &'a mut W,
    buf: CopyBuffer,
}

cfg_io_util! {
    /// Asynchronously copies the entire contents of a reader into a writer.
    ///
    /// This function returns a future that will continuously read data from
    /// `reader` and then write it into `writer` in a streaming fashion until
    /// `reader` returns EOF.
    ///
    /// On success, the total number of bytes that were copied from `reader` to
    /// `writer` is returned.
    ///
    /// This is an asynchronous version of [`std::io::copy`][std].
    ///
    /// [std]: std::io::copy
    ///
    /// # Errors
    ///
    /// The returned future will return an error immediately if any call to
    /// `poll_read` or `poll_write` returns an error.
    ///
    /// # Examples
    ///
    /// ```
    /// use tokio::io;
    ///
    /// # async fn dox() -> std::io::Result<()> {
    /// let mut reader: &[u8] = b"hello";
    /// let mut writer: Vec<u8> = vec![];
    ///
    /// io::copy(&mut reader, &mut writer).await?;
    ///
    /// assert_eq!(&b"hello"[..], &writer[..]);
    /// # Ok(())
    /// # }
    /// ```
    pub async fn copy<'a, R, W>(reader: &'a mut R, writer: &'a mut W) -> io::Result<u64>
    where
        R: AsyncRead + Unpin + ?Sized,
        W: AsyncWrite + Unpin + ?Sized,
    {
        Copy {
            reader,
            writer,
            buf: CopyBuffer::new()
        }.await
    }
}

impl<R, W> Future for Copy<'_, R, W>
where
    R: AsyncRead + Unpin + ?Sized,
    W: AsyncWrite + Unpin + ?Sized,
{
    type Output = io::Result<u64>;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
        let me = &mut *self;

        me.buf
            .poll_copy(cx, Pin::new(&mut *me.reader), Pin::new(&mut *me.writer))
    }
}
<|endoftext|>


<commit_before>extern crate desync;
extern crate futures;

use desync::*;
use futures::stream;
use futures::executor;
use futures::sink::{SinkExt};
use futures::stream::{StreamExt};
use futures::channel::mpsc;

use std::sync::*;
use std::thread;
use std::time::Duration;

#[test]
fn pipe_in_simple_stream() {
    // Create a stream
    let stream  = vec![1, 2, 3];
    let stream  = stream::iter(stream);

    // Create an object for the stream to be piped into
    let obj     = Arc::new(Desync::new(vec![]));

    // Pipe the stream into the object
    pipe_in(Arc::clone(&obj), stream, |core: &mut Vec<Result<i32, ()>>, item| core.push(Ok(item)));

    // Delay to allow the messages to be processed on the stream
    thread::sleep(Duration::from_millis(10));

    // Once the stream is drained, the core should contain Ok(1), Ok(2), Ok(3)
    assert!(obj.sync(|core| core.clone()) == vec![Ok(1), Ok(2), Ok(3)])
}

#[test]
fn pipe_in_mpsc_receiver() {
    // Create a channel to send to the object
    let (sender, receiver) = mpsc::channel(0);

    // Create an object
    let obj = Arc::new(Desync::new(vec![]));

    // Add anything received to the vector via a pipe
    pipe_in(Arc::clone(&obj), receiver, |core, item| core.push(item));

    // Initially empty
    assert!(obj.sync(|core| core.clone()) == vec![]);

    // Send some values
    let send_values = async {
        sender.send(1).await.unwrap();
        sender.send(2).await.unwrap();
    };
    executor::block_on(send_values);

    // Delay to allow the messages to be processed on the stream
    // TODO: fix so this isn't needed. This happens because there's a race between when 'poll'
    // is called in the pipe and the 'async' call
    thread::sleep(Duration::from_millis(10));

    // Should be available on the core
    assert!(obj.sync(|core| core.clone()) == vec![1, 2]);
}

#[test]
fn pipe_through() {
    // Create a channel we'll use to send data to the pipe
    let (sender, receiver) = mpsc::channel(10);

    // Create an object to pipe through
    let obj = Arc::new(Desync::new(1));

    // Create a pipe that adds values from the stream to the value in the object
    let pipe_out = pipe(Arc::clone(&obj), receiver, |core, item| item + *core);

    // Start things running
    executor::block_on(async {
        sender.send(2).await.unwrap();
        assert!(pipe_out.next().await == Some(3));

        sender.send(42).await.unwrap();
        assert!(pipe_out.next().await == Some(43));

        // Changing the value should change the output
        obj.desync(|core| *core = 2);

        sender.send(42).await.unwrap();
        assert!(pipe_out.next().await == Some(43));
    });
}

#[test]
fn pipe_through_stream_closes() {
    let mut pipe_out_with_closed_stream = {
        // Create a channel we'll use to send data to the pipe
        let (sender, receiver) = mpsc::channel(10);

        // Create an object to pipe through
        let obj = Arc::new(Desync::new(1));

        // Create a pipe that adds values from the stream to the value in the object
        let pipe_out = pipe(Arc::clone(&obj), receiver, |core, item: Result<i32, ()>| item.map(|item| item + *core));

        // Start things running
        let mut sender      = executor::spawn(sender);
        let mut pipe_out    = executor::spawn(pipe_out);

        // Sending a value should add whatever is in the desync object
        sender.wait_send(2).unwrap();
        assert!(pipe_out.wait_stream() == Some(Ok(3)));

        pipe_out
    };

    // The sender is now closed (the sender and receiver are dropped after the block above), so the pipe should close too
    assert!(pipe_out_with_closed_stream.wait_stream() == None);
}

#[test]
fn pipe_through_produces_backpressure() {
    // Create a channel we'll use to send data to the pipe
    let (mut sender, receiver) = mpsc::channel(0);

    // Create an object to pipe through
    let obj = Arc::new(Desync::new(1));

    // Create a pipe that adds values from the stream to the value in the object
    let mut pipe_out    = pipe(Arc::clone(&obj), receiver, |core, item: Result<i32, ()>| item.map(|item| item + *core));

    // Set the backpressure depth to 3
    pipe_out.set_backpressure_depth(3);

    // Start things running. We never read from this pipe here
    let _pipe_out       = executor::spawn(pipe_out);

    // Send 3 events to the pipe. Wait a bit between them to allow for processing time
    for _x in 0..3 {
        assert!(sender.try_send(1) == Ok(()));

        // The wait here allows the message to flow through to the pipe (if we call try_send again before the pipe has a chance to accept the input)
        thread::sleep(Duration::from_millis(5));
    }

    // This will stick in the channel (pipe should not be accepting more input)
    assert!(sender.try_send(2) == Ok(()));
    thread::sleep(Duration::from_millis(5));

    // Channel will push back on this one
    let channel_full = sender.try_send(3);
    assert!(channel_full.is_err());
    assert!(channel_full.unwrap_err().is_full());
}<commit_msg>Switch over the other pipe tests to use the new style futures<commit_after>extern crate desync;
extern crate futures;

use desync::*;
use futures::stream;
use futures::executor;
use futures::sink::{SinkExt};
use futures::stream::{StreamExt};
use futures::channel::mpsc;

use std::sync::*;
use std::thread;
use std::time::Duration;

#[test]
fn pipe_in_simple_stream() {
    // Create a stream
    let stream  = vec![1, 2, 3];
    let stream  = stream::iter(stream);

    // Create an object for the stream to be piped into
    let obj     = Arc::new(Desync::new(vec![]));

    // Pipe the stream into the object
    pipe_in(Arc::clone(&obj), stream, |core: &mut Vec<Result<i32, ()>>, item| core.push(Ok(item)));

    // Delay to allow the messages to be processed on the stream
    thread::sleep(Duration::from_millis(10));

    // Once the stream is drained, the core should contain Ok(1), Ok(2), Ok(3)
    assert!(obj.sync(|core| core.clone()) == vec![Ok(1), Ok(2), Ok(3)])
}

#[test]
fn pipe_in_mpsc_receiver() {
    // Create a channel to send to the object
    let (mut sender, receiver) = mpsc::channel(0);

    // Create an object
    let obj = Arc::new(Desync::new(vec![]));

    // Add anything received to the vector via a pipe
    pipe_in(Arc::clone(&obj), receiver, |core, item| core.push(item));

    // Initially empty
    assert!(obj.sync(|core| core.clone()) == vec![]);

    // Send some values
    let send_values = async {
        sender.send(1).await.unwrap();
        sender.send(2).await.unwrap();
    };
    executor::block_on(send_values);

    // Delay to allow the messages to be processed on the stream
    // TODO: fix so this isn't needed. This happens because there's a race between when 'poll'
    // is called in the pipe and the 'async' call
    thread::sleep(Duration::from_millis(10));

    // Should be available on the core
    assert!(obj.sync(|core| core.clone()) == vec![1, 2]);
}

#[test]
fn pipe_through() {
    // Create a channel we'll use to send data to the pipe
    let (mut sender, receiver) = mpsc::channel(10);

    // Create an object to pipe through
    let obj             = Arc::new(Desync::new(1));

    // Create a pipe that adds values from the stream to the value in the object
    let mut pipe_out    = pipe(Arc::clone(&obj), receiver, |core, item| item + *core);

    // Start things running
    executor::block_on(async {
        sender.send(2).await.unwrap();
        assert!(pipe_out.next().await == Some(3));

        sender.send(42).await.unwrap();
        assert!(pipe_out.next().await == Some(43));

        // Changing the value should change the output
        obj.desync(|core| *core = 2);

        sender.send(42).await.unwrap();
        assert!(pipe_out.next().await == Some(43));
    });
}

#[test]
fn pipe_through_stream_closes() {
    let mut pipe_out_with_closed_stream = {
        // Create a channel we'll use to send data to the pipe
        let (mut sender, receiver) = mpsc::channel(10);

        // Create an object to pipe through
        let obj = Arc::new(Desync::new(1));

        // Create a pipe that adds values from the stream to the value in the object
        let mut pipe_out = pipe(Arc::clone(&obj), receiver, |core, item: i32| item + *core);

        // Start things running
        executor::block_on(async {
            sender.send(2).await.unwrap();
            assert!(pipe_out.next().await == Some(3))
        });

        pipe_out
    };

    executor::block_on(async {
        // The sender is now closed (the sender and receiver are dropped after the block above), so the pipe should close too
        assert!(pipe_out_with_closed_stream.next().await == None);
    });
}

#[test]
fn pipe_through_produces_backpressure() {
    // Create a channel we'll use to send data to the pipe
    let (mut sender, receiver) = mpsc::channel(0);

    // Create an object to pipe through
    let obj = Arc::new(Desync::new(1));

    // Create a pipe that adds values from the stream to the value in the object
    let mut pipe_out    = pipe(Arc::clone(&obj), receiver, |core, item: i32| item + *core);

    // Set the backpressure depth to 3
    pipe_out.set_backpressure_depth(3);

    // Start things running. We never read from this pipe here
    executor::block_on(async {
        // Send 3 events to the pipe. Wait a bit between them to allow for processing time
        for _x in 0..3 {
            assert!(sender.try_send(1) == Ok(()));

            // The wait here allows the message to flow through to the pipe (if we call try_send again before the pipe has a chance to accept the input)
            thread::sleep(Duration::from_millis(5));
        }

        // This will stick in the channel (pipe should not be accepting more input)
        assert!(sender.try_send(2) == Ok(()));
        thread::sleep(Duration::from_millis(5));

        // Channel will push back on this one
        let channel_full = sender.try_send(3);
        assert!(channel_full.is_err());
        assert!(channel_full.unwrap_err().is_full());
    });
}
<|endoftext|>


<commit_before>use view::View;
use views::IdView;

/// Makes a view wrappable in an [`IdView`].
///
/// [`IdView`]: ../views/struct.IdView.html
pub trait Identifiable: View + Sized {
    /// Wraps this view into an `IdView` with the given id.
    ///
    /// This is just a shortcut for `IdView::new(id, self)`
    ///
    /// You can use the given id to find the view in the layout tree.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// let mut siv = Cursive::new();
    /// siv.add_layer(
    ///     TextView::new("foo")
    ///         .with_id("text")
    ///         .fixed_width(10)
    /// );
    ///
    /// // You could call this from an event callback
    /// siv.call_on_id("text", |view: &mut TextView| {
    ///     view.set_content("New content!");
    /// });
    /// ```
    ///
    /// # Notes
    ///
    /// You should call this directly on the view you want to retrieve later,
    /// before other wrappers like [`fixed_width`]. Otherwise, you would be
    /// retrieving a [`BoxView`]!
    ///
    /// [`fixed_width`]: trait.Boxable.html#method.fixed_width
    /// [`BoxView`]: ../views/struct.BoxView.html
    ///
    fn with_id<S: Into<String>>(self, id: S) -> IdView<Self> {
        IdView::new(id, self)
    }
}

/// Any `View` implements this trait.
impl<T: View> Identifiable for T {}
<commit_msg>Add missing `use` statements to doc example<commit_after>use view::View;
use views::IdView;

/// Makes a view wrappable in an [`IdView`].
///
/// [`IdView`]: ../views/struct.IdView.html
pub trait Identifiable: View + Sized {
    /// Wraps this view into an `IdView` with the given id.
    ///
    /// This is just a shortcut for `IdView::new(id, self)`
    ///
    /// You can use the given id to find the view in the layout tree.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// # use cursive::Cursive;
    /// # use cursive::views::TextView;
    /// # use cursive::view::Boxable;
    /// use cursive::view::Identifiable;
    ///
    /// let mut siv = Cursive::new();
    /// siv.add_layer(
    ///     TextView::new("foo")
    ///         .with_id("text")
    ///         .fixed_width(10)
    /// );
    ///
    /// // You could call this from an event callback
    /// siv.call_on_id("text", |view: &mut TextView| {
    ///     view.set_content("New content!");
    /// });
    /// ```
    ///
    /// # Notes
    ///
    /// You should call this directly on the view you want to retrieve later,
    /// before other wrappers like [`fixed_width`]. Otherwise, you would be
    /// retrieving a [`BoxView`]!
    ///
    /// [`fixed_width`]: trait.Boxable.html#method.fixed_width
    /// [`BoxView`]: ../views/struct.BoxView.html
    ///
    fn with_id<S: Into<String>>(self, id: S) -> IdView<Self> {
        IdView::new(id, self)
    }
}

/// Any `View` implements this trait.
impl<T: View> Identifiable for T {}
<|endoftext|>


<commit_before>// Built-in Lints
#![deny(warnings, missing_debug_implementations, missing_copy_implementations)]
// Clippy lints
#![allow(
    clippy::option_map_unwrap_or_else,
    clippy::option_map_unwrap_or,
    clippy::match_same_arms,
    clippy::type_complexity
)]
#![warn(
    clippy::option_unwrap_used,
    clippy::result_unwrap_used,
    clippy::print_stdout,
    clippy::wrong_pub_self_convention,
    clippy::mut_mut,
    clippy::non_ascii_literal,
    clippy::similar_names,
    clippy::unicode_not_nfc,
    clippy::enum_glob_use,
    clippy::if_not_else,
    clippy::items_after_statements,
    clippy::used_underscore_binding
)]
#![cfg_attr(test, allow(clippy::option_unwrap_used, clippy::result_unwrap_used))]
//! Provides functions for maintaining database schema.
//!
//! A database migration always provides procedures to update the schema, as well as to revert
//! itself. Diesel's migrations are versioned, and run in order. Diesel also takes care of tracking
//! which migrations have already been run automatically. Your migrations don't need to be
//! idempotent, as Diesel will ensure no migration is run twice unless it has been reverted.
//!
//! Migrations should be placed in a `/migrations` directory at the root of your project (the same
//! directory as `Cargo.toml`). When any of these functions are run, Diesel will search for the
//! migrations directory in the current directory and its parents, stopping when it finds the
//! directory containing `Cargo.toml`.
//!
//! Individual migrations should be a folder containing exactly two files, `up.sql` and `down.sql`.
//! `up.sql` will be used to run the migration, while `down.sql` will be used for reverting it. The
//! folder itself should have the structure `{version}_{migration_name}`. It is recommended that
//! you use the timestamp of creation for the version.
//!
//! Migrations can either be run with the CLI or embedded into the compiled application
//! and executed with code, for example right after establishing a database connection.
//! For more information, consult the [`embed_migrations!`](../macro.embed_migrations.html) macro.
//!
//! ## Example
//!
//! ```text
//! # Directory Structure
//! - 20151219180527_create_users
//!     - up.sql
//!     - down.sql
//! - 20160107082941_create_posts
//!     - up.sql
//!     - down.sql
//! ```
//!
//! ```sql
//! -- 20151219180527_create_users/up.sql
//! CREATE TABLE users (
//!   id SERIAL PRIMARY KEY,
//!   name VARCHAR NOT NULL,
//!   hair_color VARCHAR
//! );
//! ```
//!
//! ```sql
//! -- 20151219180527_create_users/down.sql
//! DROP TABLE users;
//! ```
//!
//! ```sql
//! -- 20160107082941_create_posts/up.sql
//! CREATE TABLE posts (
//!   id SERIAL PRIMARY KEY,
//!   user_id INTEGER NOT NULL,
//!   title VARCHAR NOT NULL,
//!   body TEXT
//! );
//! ```
//!
//! ```sql
//! -- 20160107082941_create_posts/down.sql
//! DROP TABLE posts;
//! ```
#[macro_use]
extern crate diesel;

#[cfg(feature = "barrel")]
extern crate barrel;

#[doc(hidden)]
pub mod connection;
pub mod migration;
#[doc(hidden)]
pub mod schema;

#[doc(inline)]
pub use self::connection::MigrationConnection;
#[doc(inline)]
pub use self::migration::*;
pub use diesel::migration::*;

use std::fs::DirEntry;
use std::io::{stdout, Write};

use self::schema::__diesel_schema_migrations::dsl::*;
use diesel::expression_methods::*;
use diesel::{Connection, QueryDsl, QueryResult, RunQueryDsl};

use std::env;
use std::path::{Path, PathBuf};

pub static TIMESTAMP_FORMAT: &str = "%Y-%m-%d-%H%M%S";

/// Runs all migrations that have not yet been run. This function will print all progress to
/// stdout. This function will return an `Err` if some error occurs reading the migrations, or if
/// any migration fails to run. Each migration is run in its own transaction, so some migrations
/// may be committed, even if a later migration fails to run.
///
/// It should be noted that this runs all migrations that have not already been run, regardless of
/// whether or not their version is later than the latest run migration. This is generally not a
/// problem, and eases the more common case of two developers generating independent migrations on
/// a branch. Whoever created the second one will eventually need to run the first when both
/// branches are merged.
///
/// See the [module level documentation](index.html) for information on how migrations should be
/// structured, and where Diesel will look for them by default.
pub fn run_pending_migrations<Conn>(conn: &Conn) -> Result<(), RunMigrationsError>
where
    Conn: MigrationConnection,
{
    let migrations_dir = find_migrations_directory()?;
    run_pending_migrations_in_directory(conn, &migrations_dir, &mut stdout())
}

#[doc(hidden)]
pub fn run_pending_migrations_in_directory<Conn>(
    conn: &Conn,
    migrations_dir: &Path,
    output: &mut Write,
) -> Result<(), RunMigrationsError>
where
    Conn: MigrationConnection,
{
    let all_migrations = migrations_in_directory(migrations_dir)?;
    run_migrations(conn, all_migrations, output)
}

/// Compares migrations found in `migrations_dir` to those that have been applied.
/// Returns a list of pathbufs and whether they have been applied.
pub fn mark_migrations_in_directory<Conn>(
    conn: &Conn,
    migrations_dir: &Path,
) -> Result<Vec<(Box<Migration>, bool)>, RunMigrationsError>
where
    Conn: MigrationConnection,
{
    let migrations = migrations_in_directory(migrations_dir)?;
    setup_database(conn)?;
    let already_run = conn.previously_run_migration_versions()?;
    let migrations = migrations
        .into_iter()
        .map(|m| {
            let applied = already_run.contains(&m.version().to_string());
            (m, applied)
        })
        .collect();
    Ok(migrations)
}

// Returns true if there are outstanding migrations in the migrations directory, otherwise
// returns false. Returns an `Err` if there are problems with migration setup.
///
/// See the [module level documentation](index.html) for information on how migrations should be
/// structured, and where Diesel will look for them by default.
pub fn any_pending_migrations<Conn>(conn: &Conn) -> Result<bool, RunMigrationsError>
where
    Conn: MigrationConnection,
{
    let migrations_dir = find_migrations_directory()?;
    let all_migrations = migrations_in_directory(&migrations_dir)?;
    let already_run = conn.previously_run_migration_versions()?;

    let pending = all_migrations
        .into_iter()
        .any(|m| !already_run.contains(&m.version().to_string()));

    Ok(pending)
}

/// Reverts the last migration that was run. Returns the version that was reverted. Returns an
/// `Err` if no migrations have ever been run.
///
/// See the [module level documentation](index.html) for information on how migrations should be
/// structured, and where Diesel will look for them by default.
pub fn revert_latest_migration<Conn>(conn: &Conn) -> Result<String, RunMigrationsError>
where
    Conn: MigrationConnection,
{
    let migrations_dir = find_migrations_directory()?;
    revert_latest_migration_in_directory(conn, &migrations_dir)
}

pub fn revert_latest_migration_in_directory<Conn>(
    conn: &Conn,
    path: &Path,
) -> Result<String, RunMigrationsError>
where
    Conn: MigrationConnection,
{
    setup_database(conn)?;
    let latest_migration_version = conn
        .latest_run_migration_version()?
        .ok_or_else(|| RunMigrationsError::MigrationError(MigrationError::NoMigrationRun))?;
    revert_migration_with_version(conn, path, &latest_migration_version, &mut stdout())
        .map(|_| latest_migration_version)
}

#[doc(hidden)]
pub fn revert_migration_with_version<Conn: Connection>(
    conn: &Conn,
    migrations_dir: &Path,
    ver: &str,
    output: &mut Write,
) -> Result<(), RunMigrationsError> {
    migration_with_version(migrations_dir, ver)
        .map_err(Into::into)
        .and_then(|m| revert_migration(conn, &m, output))
}

#[doc(hidden)]
pub fn run_migration_with_version<Conn>(
    conn: &Conn,
    migrations_dir: &Path,
    ver: &str,
    output: &mut Write,
) -> Result<(), RunMigrationsError>
where
    Conn: MigrationConnection,
{
    migration_with_version(migrations_dir, ver)
        .map_err(Into::into)
        .and_then(|m| run_migration(conn, &*m, output))
}

fn migration_with_version(
    migrations_dir: &Path,
    ver: &str,
) -> Result<Box<Migration>, MigrationError> {
    let all_migrations = migrations_in_directory(migrations_dir)?;
    let migration = all_migrations.into_iter().find(|m| m.version() == ver);
    match migration {
        Some(m) => Ok(m),
        None => Err(MigrationError::UnknownMigrationVersion(ver.into())),
    }
}

#[doc(hidden)]
pub fn setup_database<Conn: Connection>(conn: &Conn) -> QueryResult<usize> {
    create_schema_migrations_table_if_needed(conn)
}

fn create_schema_migrations_table_if_needed<Conn: Connection>(conn: &Conn) -> QueryResult<usize> {
    conn.execute(
        "CREATE TABLE IF NOT EXISTS __diesel_schema_migrations (\
         version VARCHAR(50) PRIMARY KEY NOT NULL,\
         run_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP\
         )",
    )
}

#[doc(hidden)]
pub fn migration_paths_in_directory(path: &Path) -> Result<Vec<DirEntry>, MigrationError> {
    path.read_dir()?
        .filter_map(|entry| {
            let entry = match entry {
                Ok(e) => e,
                Err(e) => return Some(Err(e.into())),
            };
            if entry.file_name().to_string_lossy().starts_with('.') {
                None
            } else {
                Some(Ok(entry))
            }
        })
        .collect()
}

fn migrations_in_directory(path: &Path) -> Result<Vec<Box<Migration>>, MigrationError> {
    migration_paths_in_directory(path)?
        .iter()
        .map(|e| migration_from(e.path()))
        .collect()
}

/// Run all pending migrations in the given list. Apps should likely be calling
/// `run_pending_migrations` or `run_pending_migrations_in_directory` instead.
pub fn run_migrations<Conn, List>(
    conn: &Conn,
    migrations: List,
    output: &mut Write,
) -> Result<(), RunMigrationsError>
where
    Conn: MigrationConnection,
    List: IntoIterator,
    List::Item: Migration,
{
    setup_database(conn)?;
    let already_run = conn.previously_run_migration_versions()?;
    let mut pending_migrations: Vec<_> = migrations
        .into_iter()
        .filter(|m| !already_run.contains(&m.version().to_string()))
        .collect();

    pending_migrations.sort_by(|a, b| a.version().cmp(b.version()));
    for migration in pending_migrations {
        run_migration(conn, &migration, output)?;
    }
    Ok(())
}

fn run_migration<Conn>(
    conn: &Conn,
    migration: &Migration,
    output: &mut Write,
) -> Result<(), RunMigrationsError>
where
    Conn: MigrationConnection,
{
    conn.transaction(|| {
        if migration.version() != "00000000000000" {
            writeln!(output, "Running migration {}", name(&migration))?;
        }
        if let Err(e) = migration.run(conn) {
            writeln!(
                output,
                "Executing migration script {}",
                file_name(&migration, "up.sql")
            )?;
            return Err(e);
        }
        conn.insert_new_migration(migration.version())?;
        Ok(())
    })
}

fn revert_migration<Conn: Connection>(
    conn: &Conn,
    migration: &Migration,
    output: &mut Write,
) -> Result<(), RunMigrationsError> {
    conn.transaction(|| {
        writeln!(output, "Rolling back migration {}", name(&migration))?;
        if let Err(e) = migration.revert(conn) {
            writeln!(
                output,
                "Executing migration script {}",
                file_name(&migration, "down.sql")
            )?;
            return Err(e);
        }
        let target = __diesel_schema_migrations.filter(version.eq(migration.version()));
        ::diesel::delete(target).execute(conn)?;
        Ok(())
    })
}

/// Returns the directory containing migrations. Will look at for
/// $PWD/migrations. If it is not found, it will search the parents of the
/// current directory, until it reaches the root directory.  Returns
/// `MigrationError::MigrationDirectoryNotFound` if no directory is found.
pub fn find_migrations_directory() -> Result<PathBuf, MigrationError> {
    search_for_migrations_directory(&env::current_dir()?)
}

/// Searches for the migrations directory relative to the given path. See
/// `find_migrations_directory` for more details.
pub fn search_for_migrations_directory(path: &Path) -> Result<PathBuf, MigrationError> {
    let migration_path = path.join("migrations");
    if migration_path.is_dir() {
        Ok(migration_path)
    } else {
        path.parent()
            .map(search_for_migrations_directory)
            .unwrap_or(Err(MigrationError::MigrationDirectoryNotFound))
    }
}

#[cfg(test)]
mod tests {
    extern crate tempdir;

    use super::*;

    use self::tempdir::TempDir;
    use std::fs;

    #[test]
    fn migration_directory_not_found_if_no_migration_dir_exists() {
        let dir = TempDir::new("diesel").unwrap();

        assert_eq!(
            Err(MigrationError::MigrationDirectoryNotFound),
            search_for_migrations_directory(dir.path())
        );
    }

    #[test]
    fn migration_directory_defaults_to_pwd_slash_migrations() {
        let dir = TempDir::new("diesel").unwrap();
        let temp_path = dir.path().canonicalize().unwrap();
        let migrations_path = temp_path.join("migrations");

        fs::create_dir(&migrations_path).unwrap();

        assert_eq!(
            Ok(migrations_path),
            search_for_migrations_directory(&temp_path)
        );
    }

    #[test]
    fn migration_directory_checks_parents() {
        let dir = TempDir::new("diesel").unwrap();
        let temp_path = dir.path().canonicalize().unwrap();
        let migrations_path = temp_path.join("migrations");
        let child_path = temp_path.join("child");

        fs::create_dir(&child_path).unwrap();
        fs::create_dir(&migrations_path).unwrap();

        assert_eq!(
            Ok(migrations_path),
            search_for_migrations_directory(&child_path)
        );
    }
}
<commit_msg>Call `setup_database` in `any_pending_migrations`<commit_after>// Built-in Lints
#![deny(warnings, missing_debug_implementations, missing_copy_implementations)]
// Clippy lints
#![allow(
    clippy::option_map_unwrap_or_else,
    clippy::option_map_unwrap_or,
    clippy::match_same_arms,
    clippy::type_complexity
)]
#![warn(
    clippy::option_unwrap_used,
    clippy::result_unwrap_used,
    clippy::print_stdout,
    clippy::wrong_pub_self_convention,
    clippy::mut_mut,
    clippy::non_ascii_literal,
    clippy::similar_names,
    clippy::unicode_not_nfc,
    clippy::enum_glob_use,
    clippy::if_not_else,
    clippy::items_after_statements,
    clippy::used_underscore_binding
)]
#![cfg_attr(test, allow(clippy::option_unwrap_used, clippy::result_unwrap_used))]
//! Provides functions for maintaining database schema.
//!
//! A database migration always provides procedures to update the schema, as well as to revert
//! itself. Diesel's migrations are versioned, and run in order. Diesel also takes care of tracking
//! which migrations have already been run automatically. Your migrations don't need to be
//! idempotent, as Diesel will ensure no migration is run twice unless it has been reverted.
//!
//! Migrations should be placed in a `/migrations` directory at the root of your project (the same
//! directory as `Cargo.toml`). When any of these functions are run, Diesel will search for the
//! migrations directory in the current directory and its parents, stopping when it finds the
//! directory containing `Cargo.toml`.
//!
//! Individual migrations should be a folder containing exactly two files, `up.sql` and `down.sql`.
//! `up.sql` will be used to run the migration, while `down.sql` will be used for reverting it. The
//! folder itself should have the structure `{version}_{migration_name}`. It is recommended that
//! you use the timestamp of creation for the version.
//!
//! Migrations can either be run with the CLI or embedded into the compiled application
//! and executed with code, for example right after establishing a database connection.
//! For more information, consult the [`embed_migrations!`](../macro.embed_migrations.html) macro.
//!
//! ## Example
//!
//! ```text
//! # Directory Structure
//! - 20151219180527_create_users
//!     - up.sql
//!     - down.sql
//! - 20160107082941_create_posts
//!     - up.sql
//!     - down.sql
//! ```
//!
//! ```sql
//! -- 20151219180527_create_users/up.sql
//! CREATE TABLE users (
//!   id SERIAL PRIMARY KEY,
//!   name VARCHAR NOT NULL,
//!   hair_color VARCHAR
//! );
//! ```
//!
//! ```sql
//! -- 20151219180527_create_users/down.sql
//! DROP TABLE users;
//! ```
//!
//! ```sql
//! -- 20160107082941_create_posts/up.sql
//! CREATE TABLE posts (
//!   id SERIAL PRIMARY KEY,
//!   user_id INTEGER NOT NULL,
//!   title VARCHAR NOT NULL,
//!   body TEXT
//! );
//! ```
//!
//! ```sql
//! -- 20160107082941_create_posts/down.sql
//! DROP TABLE posts;
//! ```
#[macro_use]
extern crate diesel;

#[cfg(feature = "barrel")]
extern crate barrel;

#[doc(hidden)]
pub mod connection;
pub mod migration;
#[doc(hidden)]
pub mod schema;

#[doc(inline)]
pub use self::connection::MigrationConnection;
#[doc(inline)]
pub use self::migration::*;
pub use diesel::migration::*;

use std::fs::DirEntry;
use std::io::{stdout, Write};

use self::schema::__diesel_schema_migrations::dsl::*;
use diesel::expression_methods::*;
use diesel::{Connection, QueryDsl, QueryResult, RunQueryDsl};

use std::env;
use std::path::{Path, PathBuf};

pub static TIMESTAMP_FORMAT: &str = "%Y-%m-%d-%H%M%S";

/// Runs all migrations that have not yet been run. This function will print all progress to
/// stdout. This function will return an `Err` if some error occurs reading the migrations, or if
/// any migration fails to run. Each migration is run in its own transaction, so some migrations
/// may be committed, even if a later migration fails to run.
///
/// It should be noted that this runs all migrations that have not already been run, regardless of
/// whether or not their version is later than the latest run migration. This is generally not a
/// problem, and eases the more common case of two developers generating independent migrations on
/// a branch. Whoever created the second one will eventually need to run the first when both
/// branches are merged.
///
/// See the [module level documentation](index.html) for information on how migrations should be
/// structured, and where Diesel will look for them by default.
pub fn run_pending_migrations<Conn>(conn: &Conn) -> Result<(), RunMigrationsError>
where
    Conn: MigrationConnection,
{
    let migrations_dir = find_migrations_directory()?;
    run_pending_migrations_in_directory(conn, &migrations_dir, &mut stdout())
}

#[doc(hidden)]
pub fn run_pending_migrations_in_directory<Conn>(
    conn: &Conn,
    migrations_dir: &Path,
    output: &mut Write,
) -> Result<(), RunMigrationsError>
where
    Conn: MigrationConnection,
{
    let all_migrations = migrations_in_directory(migrations_dir)?;
    run_migrations(conn, all_migrations, output)
}

/// Compares migrations found in `migrations_dir` to those that have been applied.
/// Returns a list of pathbufs and whether they have been applied.
pub fn mark_migrations_in_directory<Conn>(
    conn: &Conn,
    migrations_dir: &Path,
) -> Result<Vec<(Box<Migration>, bool)>, RunMigrationsError>
where
    Conn: MigrationConnection,
{
    let migrations = migrations_in_directory(migrations_dir)?;
    setup_database(conn)?;
    let already_run = conn.previously_run_migration_versions()?;
    let migrations = migrations
        .into_iter()
        .map(|m| {
            let applied = already_run.contains(&m.version().to_string());
            (m, applied)
        })
        .collect();
    Ok(migrations)
}

// Returns true if there are outstanding migrations in the migrations directory, otherwise
// returns false. Returns an `Err` if there are problems with migration setup.
///
/// See the [module level documentation](index.html) for information on how migrations should be
/// structured, and where Diesel will look for them by default.
pub fn any_pending_migrations<Conn>(conn: &Conn) -> Result<bool, RunMigrationsError>
where
    Conn: MigrationConnection,
{
    let migrations_dir = find_migrations_directory()?;
    let all_migrations = migrations_in_directory(&migrations_dir)?;
    setup_database(conn)?;
    let already_run = conn.previously_run_migration_versions()?;

    let pending = all_migrations
        .into_iter()
        .any(|m| !already_run.contains(&m.version().to_string()));

    Ok(pending)
}

/// Reverts the last migration that was run. Returns the version that was reverted. Returns an
/// `Err` if no migrations have ever been run.
///
/// See the [module level documentation](index.html) for information on how migrations should be
/// structured, and where Diesel will look for them by default.
pub fn revert_latest_migration<Conn>(conn: &Conn) -> Result<String, RunMigrationsError>
where
    Conn: MigrationConnection,
{
    let migrations_dir = find_migrations_directory()?;
    revert_latest_migration_in_directory(conn, &migrations_dir)
}

pub fn revert_latest_migration_in_directory<Conn>(
    conn: &Conn,
    path: &Path,
) -> Result<String, RunMigrationsError>
where
    Conn: MigrationConnection,
{
    setup_database(conn)?;
    let latest_migration_version = conn
        .latest_run_migration_version()?
        .ok_or_else(|| RunMigrationsError::MigrationError(MigrationError::NoMigrationRun))?;
    revert_migration_with_version(conn, path, &latest_migration_version, &mut stdout())
        .map(|_| latest_migration_version)
}

#[doc(hidden)]
pub fn revert_migration_with_version<Conn: Connection>(
    conn: &Conn,
    migrations_dir: &Path,
    ver: &str,
    output: &mut Write,
) -> Result<(), RunMigrationsError> {
    migration_with_version(migrations_dir, ver)
        .map_err(Into::into)
        .and_then(|m| revert_migration(conn, &m, output))
}

#[doc(hidden)]
pub fn run_migration_with_version<Conn>(
    conn: &Conn,
    migrations_dir: &Path,
    ver: &str,
    output: &mut Write,
) -> Result<(), RunMigrationsError>
where
    Conn: MigrationConnection,
{
    migration_with_version(migrations_dir, ver)
        .map_err(Into::into)
        .and_then(|m| run_migration(conn, &*m, output))
}

fn migration_with_version(
    migrations_dir: &Path,
    ver: &str,
) -> Result<Box<Migration>, MigrationError> {
    let all_migrations = migrations_in_directory(migrations_dir)?;
    let migration = all_migrations.into_iter().find(|m| m.version() == ver);
    match migration {
        Some(m) => Ok(m),
        None => Err(MigrationError::UnknownMigrationVersion(ver.into())),
    }
}

#[doc(hidden)]
pub fn setup_database<Conn: Connection>(conn: &Conn) -> QueryResult<usize> {
    create_schema_migrations_table_if_needed(conn)
}

fn create_schema_migrations_table_if_needed<Conn: Connection>(conn: &Conn) -> QueryResult<usize> {
    conn.execute(
        "CREATE TABLE IF NOT EXISTS __diesel_schema_migrations (\
         version VARCHAR(50) PRIMARY KEY NOT NULL,\
         run_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP\
         )",
    )
}

#[doc(hidden)]
pub fn migration_paths_in_directory(path: &Path) -> Result<Vec<DirEntry>, MigrationError> {
    path.read_dir()?
        .filter_map(|entry| {
            let entry = match entry {
                Ok(e) => e,
                Err(e) => return Some(Err(e.into())),
            };
            if entry.file_name().to_string_lossy().starts_with('.') {
                None
            } else {
                Some(Ok(entry))
            }
        })
        .collect()
}

fn migrations_in_directory(path: &Path) -> Result<Vec<Box<Migration>>, MigrationError> {
    migration_paths_in_directory(path)?
        .iter()
        .map(|e| migration_from(e.path()))
        .collect()
}

/// Run all pending migrations in the given list. Apps should likely be calling
/// `run_pending_migrations` or `run_pending_migrations_in_directory` instead.
pub fn run_migrations<Conn, List>(
    conn: &Conn,
    migrations: List,
    output: &mut Write,
) -> Result<(), RunMigrationsError>
where
    Conn: MigrationConnection,
    List: IntoIterator,
    List::Item: Migration,
{
    setup_database(conn)?;
    let already_run = conn.previously_run_migration_versions()?;
    let mut pending_migrations: Vec<_> = migrations
        .into_iter()
        .filter(|m| !already_run.contains(&m.version().to_string()))
        .collect();

    pending_migrations.sort_by(|a, b| a.version().cmp(b.version()));
    for migration in pending_migrations {
        run_migration(conn, &migration, output)?;
    }
    Ok(())
}

fn run_migration<Conn>(
    conn: &Conn,
    migration: &Migration,
    output: &mut Write,
) -> Result<(), RunMigrationsError>
where
    Conn: MigrationConnection,
{
    conn.transaction(|| {
        if migration.version() != "00000000000000" {
            writeln!(output, "Running migration {}", name(&migration))?;
        }
        if let Err(e) = migration.run(conn) {
            writeln!(
                output,
                "Executing migration script {}",
                file_name(&migration, "up.sql")
            )?;
            return Err(e);
        }
        conn.insert_new_migration(migration.version())?;
        Ok(())
    })
}

fn revert_migration<Conn: Connection>(
    conn: &Conn,
    migration: &Migration,
    output: &mut Write,
) -> Result<(), RunMigrationsError> {
    conn.transaction(|| {
        writeln!(output, "Rolling back migration {}", name(&migration))?;
        if let Err(e) = migration.revert(conn) {
            writeln!(
                output,
                "Executing migration script {}",
                file_name(&migration, "down.sql")
            )?;
            return Err(e);
        }
        let target = __diesel_schema_migrations.filter(version.eq(migration.version()));
        ::diesel::delete(target).execute(conn)?;
        Ok(())
    })
}

/// Returns the directory containing migrations. Will look at for
/// $PWD/migrations. If it is not found, it will search the parents of the
/// current directory, until it reaches the root directory.  Returns
/// `MigrationError::MigrationDirectoryNotFound` if no directory is found.
pub fn find_migrations_directory() -> Result<PathBuf, MigrationError> {
    search_for_migrations_directory(&env::current_dir()?)
}

/// Searches for the migrations directory relative to the given path. See
/// `find_migrations_directory` for more details.
pub fn search_for_migrations_directory(path: &Path) -> Result<PathBuf, MigrationError> {
    let migration_path = path.join("migrations");
    if migration_path.is_dir() {
        Ok(migration_path)
    } else {
        path.parent()
            .map(search_for_migrations_directory)
            .unwrap_or(Err(MigrationError::MigrationDirectoryNotFound))
    }
}

#[cfg(test)]
mod tests {
    extern crate tempdir;

    use super::*;

    use self::tempdir::TempDir;
    use std::fs;

    #[test]
    fn migration_directory_not_found_if_no_migration_dir_exists() {
        let dir = TempDir::new("diesel").unwrap();

        assert_eq!(
            Err(MigrationError::MigrationDirectoryNotFound),
            search_for_migrations_directory(dir.path())
        );
    }

    #[test]
    fn migration_directory_defaults_to_pwd_slash_migrations() {
        let dir = TempDir::new("diesel").unwrap();
        let temp_path = dir.path().canonicalize().unwrap();
        let migrations_path = temp_path.join("migrations");

        fs::create_dir(&migrations_path).unwrap();

        assert_eq!(
            Ok(migrations_path),
            search_for_migrations_directory(&temp_path)
        );
    }

    #[test]
    fn migration_directory_checks_parents() {
        let dir = TempDir::new("diesel").unwrap();
        let temp_path = dir.path().canonicalize().unwrap();
        let migrations_path = temp_path.join("migrations");
        let child_path = temp_path.join("child");

        fs::create_dir(&child_path).unwrap();
        fs::create_dir(&migrations_path).unwrap();

        assert_eq!(
            Ok(migrations_path),
            search_for_migrations_directory(&child_path)
        );
    }
}
<|endoftext|>


<commit_before>use crate::utils::schemas;
use async_trait::async_trait;
use defaults::Defaults;
use enum_dispatch::enum_dispatch;
use eyre::{bail, Result};
use graph_triples::{relations, resources, Triple};
use once_cell::sync::Lazy;
use regex::Regex;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
use std::{fs, path::Path};
use strum::{Display, EnumIter, IntoEnumIterator};

/// Trait for project sources. This allows us to use `enum_dispatch` to
/// dispatch these methods based on the type of source.
#[async_trait]
#[enum_dispatch]
pub trait SourceTrait {
    /// Attempt to resole the source from an identifier
    fn resolve(&self, id: &str) -> Option<Source>;

    /// Generate a default name for the source
    ///
    /// Generated names do not need to be unique (that is guaranteed elsewhere)
    /// and should simply provide an recognizable, relatively short way to refer
    /// to a source
    fn default_name(&self) -> String;
}

/// A project source
#[enum_dispatch(SourceTrait)]
#[derive(
    PartialEq, Clone, Debug, Display, Defaults, EnumIter, JsonSchema, Deserialize, Serialize,
)]
#[def = "Null(Null{})"]
#[serde(tag = "type")]
pub enum Source {
    /// A null variant that exists only so that we can define a default source
    Null(Null),
    Elife(Elife),
    GitHub(GitHub),
}

/// Resolve a source from an identifier
///
/// Returns the first source that matches the identifier
pub fn resolve(id: &str) -> Result<Source> {
    for source in Source::iter() {
        if let Some(source) = source.resolve(id) {
            return Ok(source);
        }
    }
    bail!(
        "Unable to resolve the identifier '{}' into a project source",
        id
    )
}

/// A source-destination combination
///
/// Each source by destination combination should be unique to a project.
/// It is possible to have the same source being imported to multiple
/// destinations within a project and for multiple sources to used the same
/// destination (e.g. the root directory of the project).
#[skip_serializing_none]
#[derive(PartialEq, Clone, Debug, Defaults, JsonSchema, Deserialize, Serialize)]
#[serde(default)]
#[schemars(deny_unknown_fields)]
pub struct SourceDestination {
    /// The source from which files will be imported
    pub source: Source,

    /// The destination path within the project
    pub destination: Option<String>,

    /// Whether or not the source is active
    ///
    /// If the source is active an import job will be created for it
    /// each time the project is updated.
    #[def = "true"]
    active: bool,

    /// A list of file paths currently associated with the source,
    /// relative to the project root
    pub files: Option<Vec<String>>,
}

impl SourceDestination {
    /// Create a new `SourceDestination`
    pub fn new(source: Source, destination: Option<String>) -> SourceDestination {
        SourceDestination {
            source,
            destination,
            ..Default::default()
        }
    }

    /// Read a `SourceDestination` from a JSON file
    ///
    /// Only changes the properties that are NOT saved in the project.json file.
    pub fn read<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
        let path = path.as_ref();
        if !path.exists() {
            bail!("Project source file does not exist: {}", path.display())
        }
        let path = path.canonicalize()?;

        let json = fs::read_to_string(path)?;
        let source: SourceDestination = serde_json::from_str(&json)?;

        self.files = source.files;
        Ok(())
    }

    /// Generate a set of graph triples describing relation between the source
    /// and it's associated files.
    pub fn triples(&self, name: &str, project: &Path) -> Vec<Triple> {
        match &self.files {
            Some(files) => files
                .iter()
                .map(|file| {
                    (
                        resources::source(name),
                        relations::imports(self.active),
                        resources::file(&project.join(file)),
                    )
                })
                .collect(),
            None => Vec::new(),
        }
    }
}

#[derive(PartialEq, Clone, Debug, Defaults, JsonSchema, Deserialize, Serialize)]
#[schemars(deny_unknown_fields)]
pub struct Null {}

impl SourceTrait for Null {
    fn resolve(&self, _id: &str) -> Option<Source> {
        None
    }

    fn default_name(&self) -> String {
        "null".to_string()
    }
}

#[skip_serializing_none]
#[derive(PartialEq, Clone, Debug, Defaults, JsonSchema, Deserialize, Serialize)]
#[schemars(deny_unknown_fields)]
pub struct Elife {
    /// Number of the article
    pub article: u32,
}

impl SourceTrait for Elife {
    fn resolve(&self, id: &str) -> Option<Source> {
        static SIMPLE_REGEX: Lazy<Regex> =
            Lazy::new(|| Regex::new(r"^elife:(?://)?(\d+)").expect("Unable to create regex"));

        static URL_REGEX: Lazy<Regex> = Lazy::new(|| {
            Regex::new(r"^(?:https?://)?elifesciences\.org/articles/(\d+).*$")
                .expect("Unable to create regex")
        });

        let article = if let Some(captures) = SIMPLE_REGEX.captures(id) {
            Some(captures[1].parse().unwrap_or_default())
        } else {
            URL_REGEX
                .captures(id)
                .map(|captures| captures[1].parse().unwrap_or_default())
        };

        article.map(|article| Source::Elife(Elife { article }))
    }

    fn default_name(&self) -> String {
        format!("elife-{}", self.article)
    }
}

#[skip_serializing_none]
#[derive(PartialEq, Clone, Debug, Defaults, JsonSchema, Deserialize, Serialize)]
#[schemars(deny_unknown_fields)]
pub struct GitHub {
    /// Owner of the repository
    pub owner: String,

    /// Name of the repository
    pub name: String,

    /// Path within the repository
    pub path: Option<String>,
}

impl SourceTrait for GitHub {
    fn resolve(&self, id: &str) -> Option<Source> {
        static SIMPLE_REGEX: Lazy<Regex> = Lazy::new(|| {
            Regex::new(r"^github:(?://)?([a-z0-9\-]+)/([a-z0-9\-_]+)(?:/(.+))?$")
                .expect("Unable to create regex")
        });

        static URL_REGEX: Lazy<Regex> = Lazy::new(|| {
            Regex::new(r"^(?:https?://)?github\.com/([a-z0-9\-]+)/([a-z0-9\-_]+)/?(?:(?:tree|blob)/(?:[^/]+)/(.+))?$")
                .expect("Unable to create regex")
        });

        if let Some(captures) = SIMPLE_REGEX.captures(id) {
            Some(Source::GitHub(GitHub {
                owner: captures[1].to_string(),
                name: captures[2].to_string(),
                path: captures.get(3).map(|group| group.as_str().to_string()),
            }))
        } else {
            URL_REGEX.captures(id).map(|captures| {
                Source::GitHub(GitHub {
                    owner: captures[1].to_string(),
                    name: captures[2].to_string(),
                    path: captures.get(3).map(|group| group.as_str().to_string()),
                })
            })
        }
    }

    fn default_name(&self) -> String {
        format!("github-{}-{}", self.owner, self.name)
    }
}

/// Get JSON Schemas for this modules
pub fn schemas() -> Result<serde_json::Value> {
    let schemas = serde_json::Value::Array(vec![schemas::generate::<SourceDestination>()?]);
    Ok(schemas)
}

#[cfg(feature = "cli")]
pub mod commands {
    use super::*;
    use crate::projects::PROJECTS;
    use async_trait::async_trait;
    use cli_utils::{result, Result, Run};
    use structopt::StructOpt;

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Manage the current project's sources",
        setting = structopt::clap::AppSettings::ColoredHelp,
        setting = structopt::clap::AppSettings::VersionlessSubcommands
    )]
    pub struct Command {
        #[structopt(subcommand)]
        pub action: Action,
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder
    )]
    pub enum Action {
        List(List),
        Add(Add),
        Remove(Remove),
        Import(Import),
        Schemas(Schemas),
    }
    #[async_trait]
    impl Run for Command {
        async fn run(&self) -> Result {
            let Self { action } = self;
            match action {
                Action::List(action) => action.run().await,
                Action::Add(action) => action.run().await,
                Action::Remove(action) => action.run().await,
                Action::Import(action) => action.run().await,
                Action::Schemas(action) => action.run(),
            }
        }
    }

    /// List the sources for the current project
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct List {}
    #[async_trait]
    impl Run for List {
        async fn run(&self) -> Result {
            let project = PROJECTS.current(false).await?;
            result::value(project.sources)
        }
    }

    /// Add a source to the current project
    ///
    /// Does not import the source use the `import` command for that.
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Add {
        /// A URL or other identifier of the source
        pub source: String,

        /// The path to import the source to
        pub destination: Option<String>,

        /// The name to give the source
        pub name: Option<String>,
    }
    #[async_trait]
    impl Run for Add {
        async fn run(&self) -> Result {
            let mut project = PROJECTS.current(false).await?;
            let files = project
                .add_source(&self.source, self.destination.clone(), self.name.clone())
                .await?;
            result::value(files)
        }
    }

    /// Remove a source from the current project
    ///
    /// Note that this will remove a files imported from this source
    /// into the project.
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Remove {
        /// Name of the source to remove
        pub name: String,
    }
    #[async_trait]
    impl Run for Remove {
        async fn run(&self) -> Result {
            let mut project = PROJECTS.current(false).await?;
            project.remove_source(&self.name).await?;
            result::nothing()
        }
    }

    /// Import a source into the current project
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Import {
        /// A name or other identifier of the source
        pub source: String,

        /// The path to import the source to
        pub destination: Option<String>,
    }
    #[async_trait]
    impl Run for Import {
        async fn run(&self) -> Result {
            let mut project = PROJECTS.current(false).await?;
            let files = project
                .import_source(&self.source, self.destination.clone())
                .await?;
            result::value(files)
        }
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Get JSON Schemas for sources",
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Schemas {}

    impl Schemas {
        pub fn run(&self) -> Result {
            let schema = schemas()?;
            result::value(schema)
        }
    }
}

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

    #[test]
    fn elife_resolve() -> Result<()> {
        assert_eq!(
            resolve("elife:52258")?,
            Source::Elife(Elife { article: 52258 })
        );

        assert_eq!(
            resolve("elife://52258")?,
            Source::Elife(Elife { article: 52258 })
        );

        assert_eq!(
            resolve("https://elifesciences.org/articles/52258")?,
            Source::Elife(Elife { article: 52258 })
        );

        assert_eq!(
            resolve("elifesciences.org/articles/52258")?,
            Source::Elife(Elife { article: 52258 })
        );

        Ok(())
    }

    #[test]
    fn github_resolve() -> Result<()> {
        assert_eq!(
            resolve("github:owner/name")?,
            Source::GitHub(GitHub {
                owner: "owner".to_string(),
                name: "name".to_string(),
                path: None
            })
        );

        assert_eq!(
            resolve("github:owner/name/some/path/in/repo.md")?,
            Source::GitHub(GitHub {
                owner: "owner".to_string(),
                name: "name".to_string(),
                path: Some("some/path/in/repo.md".to_string())
            })
        );

        assert_eq!(
            resolve("https://github.com/owner/name/")?,
            Source::GitHub(GitHub {
                owner: "owner".to_string(),
                name: "name".to_string(),
                path: None
            })
        );

        assert_eq!(
            resolve("https://github.com/owner/name/tree/master/some/path/in/repo.md")?,
            Source::GitHub(GitHub {
                owner: "owner".to_string(),
                name: "name".to_string(),
                path: Some("some/path/in/repo.md".to_string())
            })
        );

        assert_eq!(
            resolve("https://github.com/owner/name/blob/master/some/path/in/repo.md")?,
            Source::GitHub(GitHub {
                owner: "owner".to_string(),
                name: "name".to_string(),
                path: Some("some/path/in/repo.md".to_string())
            })
        );

        Ok(())
    }
}
<commit_msg>chore(Source):  Refactor for new model<commit_after>use crate::utils::schemas;
use async_trait::async_trait;
use defaults::Defaults;
use enum_dispatch::enum_dispatch;
use eyre::{bail, Result};
use graph_triples::{relations::{self, NULL_RANGE}, resources, Triple};
use once_cell::sync::Lazy;
use regex::Regex;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
use std::{fs, path::Path};
use strum::{Display, EnumIter, IntoEnumIterator};

/// Trait for project sources. This allows us to use `enum_dispatch` to
/// dispatch these methods based on the type of source.
#[async_trait]
#[enum_dispatch]
pub trait SourceTrait {
    /// Attempt to resole the source from an identifier
    fn resolve(&self, id: &str) -> Option<Source>;

    /// Generate a default name for the source
    ///
    /// Generated names do not need to be unique (that is guaranteed elsewhere)
    /// and should simply provide an recognizable, relatively short way to refer
    /// to a source
    fn default_name(&self) -> String;
}

/// A project source
#[enum_dispatch(SourceTrait)]
#[derive(
    PartialEq, Clone, Debug, Display, Defaults, EnumIter, JsonSchema, Deserialize, Serialize,
)]
#[def = "Null(Null{})"]
#[serde(tag = "type")]
pub enum Source {
    /// A null variant that exists only so that we can define a default source
    Null(Null),
    Elife(Elife),
    GitHub(GitHub),
}

/// Resolve a source from an identifier
///
/// Returns the first source that matches the identifier
pub fn resolve(id: &str) -> Result<Source> {
    for source in Source::iter() {
        if let Some(source) = source.resolve(id) {
            return Ok(source);
        }
    }
    bail!(
        "Unable to resolve the identifier '{}' into a project source",
        id
    )
}

/// A source-destination combination
///
/// Each source by destination combination should be unique to a project.
/// It is possible to have the same source being imported to multiple
/// destinations within a project and for multiple sources to used the same
/// destination (e.g. the root directory of the project).
#[skip_serializing_none]
#[derive(PartialEq, Clone, Debug, Defaults, JsonSchema, Deserialize, Serialize)]
#[serde(default)]
#[schemars(deny_unknown_fields)]
pub struct SourceDestination {
    /// The source from which files will be imported
    pub source: Source,

    /// The destination path within the project
    pub destination: Option<String>,

    /// Whether or not the source is active
    ///
    /// If the source is active an import job will be created for it
    /// each time the project is updated.
    #[def = "true"]
    active: bool,

    /// A list of file paths currently associated with the source,
    /// relative to the project root
    pub files: Option<Vec<String>>,
}

impl SourceDestination {
    /// Create a new `SourceDestination`
    pub fn new(source: Source, destination: Option<String>) -> SourceDestination {
        SourceDestination {
            source,
            destination,
            ..Default::default()
        }
    }

    /// Read a `SourceDestination` from a JSON file
    ///
    /// Only changes the properties that are NOT saved in the project.json file.
    pub fn read<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
        let path = path.as_ref();
        if !path.exists() {
            bail!("Project source file does not exist: {}", path.display())
        }
        let path = path.canonicalize()?;

        let json = fs::read_to_string(path)?;
        let source: SourceDestination = serde_json::from_str(&json)?;

        self.files = source.files;
        Ok(())
    }

    /// Generate a set of graph triples describing relation between the source
    /// and it's associated files.
    pub fn triples(&self, name: &str, project: &Path) -> Vec<Triple> {
        match &self.files {
            Some(files) => files
                .iter()
                .map(|file| {
                    (
                        resources::source(name),
                        relations::imports(NULL_RANGE),
                        resources::file(&project.join(file)),
                    )
                })
                .collect(),
            None => Vec::new(),
        }
    }
}

#[derive(PartialEq, Clone, Debug, Defaults, JsonSchema, Deserialize, Serialize)]
#[schemars(deny_unknown_fields)]
pub struct Null {}

impl SourceTrait for Null {
    fn resolve(&self, _id: &str) -> Option<Source> {
        None
    }

    fn default_name(&self) -> String {
        "null".to_string()
    }
}

#[skip_serializing_none]
#[derive(PartialEq, Clone, Debug, Defaults, JsonSchema, Deserialize, Serialize)]
#[schemars(deny_unknown_fields)]
pub struct Elife {
    /// Number of the article
    pub article: u32,
}

impl SourceTrait for Elife {
    fn resolve(&self, id: &str) -> Option<Source> {
        static SIMPLE_REGEX: Lazy<Regex> =
            Lazy::new(|| Regex::new(r"^elife:(?://)?(\d+)").expect("Unable to create regex"));

        static URL_REGEX: Lazy<Regex> = Lazy::new(|| {
            Regex::new(r"^(?:https?://)?elifesciences\.org/articles/(\d+).*$")
                .expect("Unable to create regex")
        });

        let article = if let Some(captures) = SIMPLE_REGEX.captures(id) {
            Some(captures[1].parse().unwrap_or_default())
        } else {
            URL_REGEX
                .captures(id)
                .map(|captures| captures[1].parse().unwrap_or_default())
        };

        article.map(|article| Source::Elife(Elife { article }))
    }

    fn default_name(&self) -> String {
        format!("elife-{}", self.article)
    }
}

#[skip_serializing_none]
#[derive(PartialEq, Clone, Debug, Defaults, JsonSchema, Deserialize, Serialize)]
#[schemars(deny_unknown_fields)]
pub struct GitHub {
    /// Owner of the repository
    pub owner: String,

    /// Name of the repository
    pub name: String,

    /// Path within the repository
    pub path: Option<String>,
}

impl SourceTrait for GitHub {
    fn resolve(&self, id: &str) -> Option<Source> {
        static SIMPLE_REGEX: Lazy<Regex> = Lazy::new(|| {
            Regex::new(r"^github:(?://)?([a-z0-9\-]+)/([a-z0-9\-_]+)(?:/(.+))?$")
                .expect("Unable to create regex")
        });

        static URL_REGEX: Lazy<Regex> = Lazy::new(|| {
            Regex::new(r"^(?:https?://)?github\.com/([a-z0-9\-]+)/([a-z0-9\-_]+)/?(?:(?:tree|blob)/(?:[^/]+)/(.+))?$")
                .expect("Unable to create regex")
        });

        if let Some(captures) = SIMPLE_REGEX.captures(id) {
            Some(Source::GitHub(GitHub {
                owner: captures[1].to_string(),
                name: captures[2].to_string(),
                path: captures.get(3).map(|group| group.as_str().to_string()),
            }))
        } else {
            URL_REGEX.captures(id).map(|captures| {
                Source::GitHub(GitHub {
                    owner: captures[1].to_string(),
                    name: captures[2].to_string(),
                    path: captures.get(3).map(|group| group.as_str().to_string()),
                })
            })
        }
    }

    fn default_name(&self) -> String {
        format!("github-{}-{}", self.owner, self.name)
    }
}

/// Get JSON Schemas for this modules
pub fn schemas() -> Result<serde_json::Value> {
    let schemas = serde_json::Value::Array(vec![schemas::generate::<SourceDestination>()?]);
    Ok(schemas)
}

#[cfg(feature = "cli")]
pub mod commands {
    use super::*;
    use crate::projects::PROJECTS;
    use async_trait::async_trait;
    use cli_utils::{result, Result, Run};
    use structopt::StructOpt;

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Manage the current project's sources",
        setting = structopt::clap::AppSettings::ColoredHelp,
        setting = structopt::clap::AppSettings::VersionlessSubcommands
    )]
    pub struct Command {
        #[structopt(subcommand)]
        pub action: Action,
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder
    )]
    pub enum Action {
        List(List),
        Add(Add),
        Remove(Remove),
        Import(Import),
        Schemas(Schemas),
    }
    #[async_trait]
    impl Run for Command {
        async fn run(&self) -> Result {
            let Self { action } = self;
            match action {
                Action::List(action) => action.run().await,
                Action::Add(action) => action.run().await,
                Action::Remove(action) => action.run().await,
                Action::Import(action) => action.run().await,
                Action::Schemas(action) => action.run(),
            }
        }
    }

    /// List the sources for the current project
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct List {}
    #[async_trait]
    impl Run for List {
        async fn run(&self) -> Result {
            let project = PROJECTS.current(false).await?;
            result::value(project.sources)
        }
    }

    /// Add a source to the current project
    ///
    /// Does not import the source use the `import` command for that.
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Add {
        /// A URL or other identifier of the source
        pub source: String,

        /// The path to import the source to
        pub destination: Option<String>,

        /// The name to give the source
        pub name: Option<String>,
    }
    #[async_trait]
    impl Run for Add {
        async fn run(&self) -> Result {
            let mut project = PROJECTS.current(false).await?;
            let files = project
                .add_source(&self.source, self.destination.clone(), self.name.clone())
                .await?;
            result::value(files)
        }
    }

    /// Remove a source from the current project
    ///
    /// Note that this will remove a files imported from this source
    /// into the project.
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Remove {
        /// Name of the source to remove
        pub name: String,
    }
    #[async_trait]
    impl Run for Remove {
        async fn run(&self) -> Result {
            let mut project = PROJECTS.current(false).await?;
            project.remove_source(&self.name).await?;
            result::nothing()
        }
    }

    /// Import a source into the current project
    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder,
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Import {
        /// A name or other identifier of the source
        pub source: String,

        /// The path to import the source to
        pub destination: Option<String>,
    }
    #[async_trait]
    impl Run for Import {
        async fn run(&self) -> Result {
            let mut project = PROJECTS.current(false).await?;
            let files = project
                .import_source(&self.source, self.destination.clone())
                .await?;
            result::value(files)
        }
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Get JSON Schemas for sources",
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Schemas {}

    impl Schemas {
        pub fn run(&self) -> Result {
            let schema = schemas()?;
            result::value(schema)
        }
    }
}

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

    #[test]
    fn elife_resolve() -> Result<()> {
        assert_eq!(
            resolve("elife:52258")?,
            Source::Elife(Elife { article: 52258 })
        );

        assert_eq!(
            resolve("elife://52258")?,
            Source::Elife(Elife { article: 52258 })
        );

        assert_eq!(
            resolve("https://elifesciences.org/articles/52258")?,
            Source::Elife(Elife { article: 52258 })
        );

        assert_eq!(
            resolve("elifesciences.org/articles/52258")?,
            Source::Elife(Elife { article: 52258 })
        );

        Ok(())
    }

    #[test]
    fn github_resolve() -> Result<()> {
        assert_eq!(
            resolve("github:owner/name")?,
            Source::GitHub(GitHub {
                owner: "owner".to_string(),
                name: "name".to_string(),
                path: None
            })
        );

        assert_eq!(
            resolve("github:owner/name/some/path/in/repo.md")?,
            Source::GitHub(GitHub {
                owner: "owner".to_string(),
                name: "name".to_string(),
                path: Some("some/path/in/repo.md".to_string())
            })
        );

        assert_eq!(
            resolve("https://github.com/owner/name/")?,
            Source::GitHub(GitHub {
                owner: "owner".to_string(),
                name: "name".to_string(),
                path: None
            })
        );

        assert_eq!(
            resolve("https://github.com/owner/name/tree/master/some/path/in/repo.md")?,
            Source::GitHub(GitHub {
                owner: "owner".to_string(),
                name: "name".to_string(),
                path: Some("some/path/in/repo.md".to_string())
            })
        );

        assert_eq!(
            resolve("https://github.com/owner/name/blob/master/some/path/in/repo.md")?,
            Source::GitHub(GitHub {
                owner: "owner".to_string(),
                name: "name".to_string(),
                path: Some("some/path/in/repo.md".to_string())
            })
        );

        Ok(())
    }
}
<|endoftext|>


<commit_before>pub fn find_saddle_points(input: &[Vec<u64>]) -> Vec<(usize, usize)> {
    let mut row_max = vec![Vec::new(); input.len()];
    let mut col_min: Vec<Vec<(usize, u64)>> = vec![Vec::new(); input[0].len()];

    for (row, values) in input.iter().enumerate() {
        for (col, &value) in values.iter().enumerate() {
            let mut c = &mut col_min[col];

            if c.is_empty() || c[0].1 == value {
                c.push((row, value));
            } else if c[0].1 > value {
                c.clear();
                c.push((row, value));
            }

            let mut r = &mut row_max[row];

            if r.is_empty() || r[0] == value {
                r.push(value);
            } else if r[0] < value {
                r.clear();
                r.push(value);
            }
        }
    }

    col_min
        .iter()
        .enumerate()
        .fold(Vec::new(), |mut points, (col, values)| {
            let mut col_points = values
                .iter()
                .filter(|c| row_max[c.0].contains(&c.1))
                .map(|c| (c.0, col))
                .collect();

            points.append(&mut col_points);
            points
        })
}
<commit_msg>Clean up solution for Saddle Points<commit_after>pub fn find_saddle_points(input: &[Vec<u64>]) -> Vec<(usize, usize)> {
    let mut row_max = vec![None; input.len()];
    let mut col_min = vec![(None, Vec::new()); input[0].len()];

    for (row, values) in input.iter().enumerate() {
        for (col, &value) in values.iter().enumerate() {
            if row_max[row].filter(|&v| v >= value).is_none() {
                row_max[row] = Some(value);
            }

            let mut c = &mut col_min[col];

            if c.0.filter(|&v| v <= value).is_none() {
                c.1.clear();
            }

            if c.0.filter(|&v| v < value).is_none() {
                c.0 = Some(value);
                c.1.push(row);
            }
        }
    }

    col_min
        .iter()
        .enumerate()
        .fold(Vec::new(), |mut points, (col, (value, rows))| {
            let mut col_points = rows
                .iter()
                .filter(|&&row| row_max[row] == *value)
                .map(|&row| (row, col))
                .collect();

            points.append(&mut col_points);
            points
        })
}
<|endoftext|>


<commit_before>// src/controllers/test_controller.rs

/// Test Controller

//Import
//External
use nickel::{JsonBody, Request};
//Utilities
use ::dao::dao_manager::DAOManager;
use ::common::api_result::{ApiResult};
//Models
use ::models::test_model::{TestModel};
//Controllers
use ::controllers::controller_manager::ControllerManager;

#[derive(Clone)]
pub struct TestController{
    dao_manager: DAOManager
}

impl TestController{

    pub fn new(dao_manager: DAOManager) -> TestController{
        TestController{
            dao_manager: dao_manager
        }
    }

    /// Retrieve a Test
    ///
    /// # Arguments
    /// &self
    ///
    /// # Returns
    /// `APIResult` - Result
    pub fn retrieve(&self) -> ApiResult<TestModel>{
        let test = TestModel{
            field_1: Some(String::from("Field One")),
            field_2: Some(String::from("Field Two")),
            field_3: Some(String::from("Field Three"))
        };
        ApiResult::Success{result:test}
    }//end retrieve

    /// Imitate Saving a Test object
    /// Performs validation and returns result. Does not perform an actual save to the DB
    ///
    /// # Arguments
    /// &self
    /// req - nickel::Request
    ///
    /// # Returns
    /// `APIResult` - Result
    pub fn save(&self, req: &mut Request<ControllerManager>) -> ApiResult<TestModel>{
        let result;
        match req.json_as::<TestModel>(){
            Ok(test) => {
                let validationResult = test.validate();

                if validationResult.is_valid(){
                    result = ApiResult::Success{result:test}
                }else{
                    result = ApiResult::Invalid{validation:validationResult, request:test}
                }
            },
            Err(e) => {
                error!("{}",e);
                result = ApiResult::Failure{msg:"Invalid format. Unable to parse data."}
            }
        }
        result
    }//end save

    /// Imitate a hard failure
    /// Returns failure ApiResult
    ///
    /// # Arguments
    /// &self
    /// req - nickel::Request
    ///
    /// # Returns
    /// `APIResult` - Result
    pub fn failure(&self) -> ApiResult<TestModel>{
        error!("(Test) Fatal error occurred");
        ApiResult::Failure{msg:"Fatal error occurred"}
    }

}
<commit_msg>Cleanup: convert variable to snake case<commit_after>// src/controllers/test_controller.rs

/// Test Controller

//Import
//External
use nickel::{JsonBody, Request};
//Utilities
use ::dao::dao_manager::DAOManager;
use ::common::api_result::{ApiResult};
//Models
use ::models::test_model::{TestModel};
//Controllers
use ::controllers::controller_manager::ControllerManager;

#[derive(Clone)]
pub struct TestController{
    dao_manager: DAOManager
}

impl TestController{

    pub fn new(dao_manager: DAOManager) -> TestController{
        TestController{
            dao_manager: dao_manager
        }
    }

    /// Retrieve a Test
    ///
    /// # Arguments
    /// &self
    ///
    /// # Returns
    /// `APIResult` - Result
    pub fn retrieve(&self) -> ApiResult<TestModel>{
        let test = TestModel{
            field_1: Some(String::from("Field One")),
            field_2: Some(String::from("Field Two")),
            field_3: Some(String::from("Field Three"))
        };
        ApiResult::Success{result:test}
    }//end retrieve

    /// Imitate Saving a Test object
    /// Performs validation and returns result. Does not perform an actual save to the DB
    ///
    /// # Arguments
    /// &self
    /// req - nickel::Request
    ///
    /// # Returns
    /// `APIResult` - Result
    pub fn save(&self, req: &mut Request<ControllerManager>) -> ApiResult<TestModel>{
        let result;
        match req.json_as::<TestModel>(){
            Ok(test) => {
                let validation_result = test.validate();

                if validation_result.is_valid(){
                    result = ApiResult::Success{result:test}
                }else{
                    result = ApiResult::Invalid{validation:validation_result, request:test}
                }
            },
            Err(e) => {
                error!("{}",e);
                result = ApiResult::Failure{msg:"Invalid format. Unable to parse data."}
            }
        }
        result
    }//end save

    /// Imitate a hard failure
    /// Returns failure ApiResult
    ///
    /// # Arguments
    /// &self
    /// req - nickel::Request
    ///
    /// # Returns
    /// `APIResult` - Result
    pub fn failure(&self) -> ApiResult<TestModel>{
        error!("(Test) Fatal error occurred");
        ApiResult::Failure{msg:"Fatal error occurred"}
    }

}
<|endoftext|>


<commit_before>//! Mock request dispatcher and credentials for unit testing services

extern crate chrono;
extern crate futures;
extern crate http;
extern crate rusoto_core;
extern crate serde;
extern crate serde_json;

use std::fs::File;
use std::io::Read;
use std::collections::HashMap;
use std::time::Duration;

use rusoto_core::{DispatchSignedRequest, HttpDispatchError};
use rusoto_core::credential::{ProvideAwsCredentials, CredentialsError, AwsCredentials};
use rusoto_core::request::{Headers, HttpResponse};
use serde::Serialize;
use rusoto_core::signature::SignedRequest;
use futures::future::{FutureResult, ok};
use futures::stream::once;
use http::{HttpTryFrom, StatusCode};

pub struct MockCredentialsProvider;

impl ProvideAwsCredentials for MockCredentialsProvider {
    type Future = FutureResult<AwsCredentials, CredentialsError>;

    fn credentials(&self) -> Self::Future {
        ok(AwsCredentials::new("mock_key",
                               "mock_secret",
                               None,
                               None))
    }
}

pub struct MockRequestDispatcher {
    status: StatusCode,
    body: Vec<u8>,
    headers: HashMap<String, String>,
    request_checker: Option<Box<Fn(&SignedRequest) + Send + Sync>>,
}

impl MockRequestDispatcher {
    pub fn with_status(status: u16) -> MockRequestDispatcher {
        MockRequestDispatcher {
            status: StatusCode::try_from(status).unwrap(),
            body: b"".to_vec(),
            headers: HashMap::new(),
            request_checker: None,
        }
    }

    pub fn with_body(mut self, body: &str) -> MockRequestDispatcher {
        self.body = body.as_bytes().to_vec();
        self
    }

    pub fn with_json_body<B>(mut self, body: B) -> MockRequestDispatcher
        where B: Serialize {
        self.body = serde_json::to_vec(&body).expect("failed to deserialize into json");
        self
    }

    pub fn with_request_checker<F>(mut self, checker: F) -> MockRequestDispatcher
        where F: Fn(&SignedRequest) + Send + Sync + 'static {
        self.request_checker = Some(Box::new(checker));
        self
    }

    pub fn with_header(mut self, key: &str, value: &str) -> MockRequestDispatcher {
        self.headers.insert(key.into(), value.into());
        self
    }
}

impl DispatchSignedRequest for MockRequestDispatcher {
    type Future = FutureResult<HttpResponse, HttpDispatchError>;

    fn dispatch(&self, request: SignedRequest, _timeout: Option<Duration>) -> Self::Future {
        if self.request_checker.is_some() {
            self.request_checker.as_ref().unwrap()(&request);
        }
        ok(HttpResponse {
            status: self.status,
            body: Box::new(once(Ok(self.body.clone()))),
            headers: Headers::new(self.headers.iter().map(|(k, v)| (k.as_ref(), v.to_owned()))),
        })
    }
}

pub trait ReadMockResponse {
    fn read_response(dir_name: &str, file_name: &str) -> String;
}

pub struct MockResponseReader;

impl ReadMockResponse for MockResponseReader {
    fn read_response(dir_name: &str, response_name: &str) -> String {
        let file_name = format!("{}/{}", dir_name, response_name);

        let mut input_file = File::open(&file_name).expect("couldn't find file");

        let mut mock_response = String::new();

        input_file.read_to_string(&mut mock_response).expect(&format!(
	        "Failed to read {:?}",
	        file_name,
	    ));

        mock_response
    }
}
<commit_msg>document api<commit_after>//! Mock request dispatcher and credentials for unit testing services

extern crate chrono;
extern crate futures;
extern crate http;
extern crate rusoto_core;
extern crate serde;
extern crate serde_json;

use std::fs::File;
use std::io::Read;
use std::collections::HashMap;
use std::time::Duration;

use rusoto_core::{DispatchSignedRequest, HttpDispatchError};
use rusoto_core::credential::{ProvideAwsCredentials, CredentialsError, AwsCredentials};
use rusoto_core::request::{Headers, HttpResponse};
use serde::Serialize;
use rusoto_core::signature::SignedRequest;
use futures::future::{FutureResult, ok};
use futures::stream::once;
use http::{HttpTryFrom, StatusCode};

pub struct MockCredentialsProvider;

impl ProvideAwsCredentials for MockCredentialsProvider {
    type Future = FutureResult<AwsCredentials, CredentialsError>;

    fn credentials(&self) -> Self::Future {
        ok(AwsCredentials::new("mock_key",
                               "mock_secret",
                               None,
                               None))
    }
}

pub struct MockRequestDispatcher {
    status: StatusCode,
    body: Vec<u8>,
    headers: HashMap<String, String>,
    request_checker: Option<Box<Fn(&SignedRequest) + Send + Sync>>,
}

impl MockRequestDispatcher {
    pub fn with_status(status: u16) -> MockRequestDispatcher {
        MockRequestDispatcher {
            status: StatusCode::try_from(status).unwrap(),
            body: b"".to_vec(),
            headers: HashMap::new(),
            request_checker: None,
        }
    }

    pub fn with_body(mut self, body: &str) -> MockRequestDispatcher {
        self.body = body.as_bytes().to_vec();
        self
    }

    /// Mocks the json serialized response body what would be
    /// returned from AWS
    pub fn with_json_body<B>(mut self, body: B) -> MockRequestDispatcher
        where B: Serialize {
        self.body = serde_json::to_vec(&body).expect("failed to deserialize into json");
        self
    }

    pub fn with_request_checker<F>(mut self, checker: F) -> MockRequestDispatcher
        where F: Fn(&SignedRequest) + Send + Sync + 'static {
        self.request_checker = Some(Box::new(checker));
        self
    }

    pub fn with_header(mut self, key: &str, value: &str) -> MockRequestDispatcher {
        self.headers.insert(key.into(), value.into());
        self
    }
}

impl DispatchSignedRequest for MockRequestDispatcher {
    type Future = FutureResult<HttpResponse, HttpDispatchError>;

    fn dispatch(&self, request: SignedRequest, _timeout: Option<Duration>) -> Self::Future {
        if self.request_checker.is_some() {
            self.request_checker.as_ref().unwrap()(&request);
        }
        ok(HttpResponse {
            status: self.status,
            body: Box::new(once(Ok(self.body.clone()))),
            headers: Headers::new(self.headers.iter().map(|(k, v)| (k.as_ref(), v.to_owned()))),
        })
    }
}

pub trait ReadMockResponse {
    fn read_response(dir_name: &str, file_name: &str) -> String;
}

pub struct MockResponseReader;

impl ReadMockResponse for MockResponseReader {
    fn read_response(dir_name: &str, response_name: &str) -> String {
        let file_name = format!("{}/{}", dir_name, response_name);

        let mut input_file = File::open(&file_name).expect("couldn't find file");

        let mut mock_response = String::new();

        input_file.read_to_string(&mut mock_response).expect(&format!(
	        "Failed to read {:?}",
	        file_name,
	    ));

        mock_response
    }
}
<|endoftext|>


<commit_before>#![feature(std_misc)]

extern crate pkg_config;

use std::env;
use std::sync::{StaticMutex, MUTEX_INIT};
use std::path::PathBuf;

static LOCK: StaticMutex = MUTEX_INIT;

fn reset() {
    for (k, _) in env::vars() {
        if k.contains("PKG_CONFIG") || k.contains("DYNAMIC") ||
           k.contains("STATIC") {
            env::remove_var(&k);
        }
    }
    env::remove_var("TARGET");
    env::remove_var("HOST");
    env::set_var("PKG_CONFIG_PATH", &env::current_dir().unwrap().join("tests"));
}

fn find(name: &str) -> Result<pkg_config::Library, String> {
    pkg_config::find_library(name)
}

#[test]
fn cross_disabled() {
    let _g = LOCK.lock();
    reset();
    env::set_var("TARGET", "foo");
    env::set_var("HOST", "bar");
    find("foo").unwrap_err();
}

#[test]
fn cross_enabled() {
    let _g = LOCK.lock();
    reset();
    env::set_var("TARGET", "foo");
    env::set_var("HOST", "bar");
    env::set_var("PKG_CONFIG_ALLOW_CROSS", "1");
    find("foo").unwrap();
}

#[test]
fn package_disabled() {
    let _g = LOCK.lock();
    reset();
    env::set_var("FOO_NO_PKG_CONFIG", "1");
    find("foo").unwrap_err();
}

#[test]
fn output_ok() {
    let _g = LOCK.lock();
    reset();
    let lib = find("foo").unwrap();
    assert!(lib.libs.contains(&"gcc".to_string()));
    assert!(lib.libs.contains(&"coregrind-amd64-linux".to_string()));
    assert!(lib.link_paths.contains(&PathBuf::from("/usr/lib/valgrind")));
}

#[test]
fn framework() {
    let _g = LOCK.lock();
    reset();
    let lib = find("framework").unwrap();
    assert!(lib.frameworks.contains(&"foo".to_string()));
    assert!(lib.framework_paths.contains(&PathBuf::from("/usr/lib")));
}

#[test]
fn get_variable() {
    let _g = LOCK.lock();
    reset();
    let prefix = pkg_config::Config::get_variable("foo", "prefix").unwrap();
    assert_eq!(prefix, "/usr");
}

#[test]
fn version() {
    let _g = LOCK.lock();
    reset();
    assert_eq!(&find("foo").unwrap().version[..], "3.10.0.SVN");
}
<commit_msg>Update feature name<commit_after>#![feature(static_mutex)]

extern crate pkg_config;

use std::env;
use std::sync::{StaticMutex, MUTEX_INIT};
use std::path::PathBuf;

static LOCK: StaticMutex = MUTEX_INIT;

fn reset() {
    for (k, _) in env::vars() {
        if k.contains("PKG_CONFIG") || k.contains("DYNAMIC") ||
           k.contains("STATIC") {
            env::remove_var(&k);
        }
    }
    env::remove_var("TARGET");
    env::remove_var("HOST");
    env::set_var("PKG_CONFIG_PATH", &env::current_dir().unwrap().join("tests"));
}

fn find(name: &str) -> Result<pkg_config::Library, String> {
    pkg_config::find_library(name)
}

#[test]
fn cross_disabled() {
    let _g = LOCK.lock();
    reset();
    env::set_var("TARGET", "foo");
    env::set_var("HOST", "bar");
    find("foo").unwrap_err();
}

#[test]
fn cross_enabled() {
    let _g = LOCK.lock();
    reset();
    env::set_var("TARGET", "foo");
    env::set_var("HOST", "bar");
    env::set_var("PKG_CONFIG_ALLOW_CROSS", "1");
    find("foo").unwrap();
}

#[test]
fn package_disabled() {
    let _g = LOCK.lock();
    reset();
    env::set_var("FOO_NO_PKG_CONFIG", "1");
    find("foo").unwrap_err();
}

#[test]
fn output_ok() {
    let _g = LOCK.lock();
    reset();
    let lib = find("foo").unwrap();
    assert!(lib.libs.contains(&"gcc".to_string()));
    assert!(lib.libs.contains(&"coregrind-amd64-linux".to_string()));
    assert!(lib.link_paths.contains(&PathBuf::from("/usr/lib/valgrind")));
}

#[test]
fn framework() {
    let _g = LOCK.lock();
    reset();
    let lib = find("framework").unwrap();
    assert!(lib.frameworks.contains(&"foo".to_string()));
    assert!(lib.framework_paths.contains(&PathBuf::from("/usr/lib")));
}

#[test]
fn get_variable() {
    let _g = LOCK.lock();
    reset();
    let prefix = pkg_config::Config::get_variable("foo", "prefix").unwrap();
    assert_eq!(prefix, "/usr");
}

#[test]
fn version() {
    let _g = LOCK.lock();
    reset();
    assert_eq!(&find("foo").unwrap().version[..], "3.10.0.SVN");
}
<|endoftext|>