<commit_before>// Copyright 2018, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use futures_channel::{mpsc, oneshot};
use futures_core::stream::Stream;
use futures_core::task::Context;
use futures_core::{Async, Future, Never};

use MainContext;
use Source;
use Continue;
use Priority;

/// Represents a `Future` around a `glib::Source`. The future will
/// be resolved once the source has provided a value
pub struct SourceFuture<F, T> {
    create_source: Option<F>,
    source: Option<(Source, oneshot::Receiver<T>)>,
}

impl<F, T: 'static> SourceFuture<F, T>
where
    F: FnOnce(oneshot::Sender<T>) -> Source + Send + 'static,
{
    /// Create a new `SourceFuture`
    ///
    /// The provided closure should return a newly created `glib::Source` when called
    /// and pass the value provided by the source to the oneshot sender that is passed
    /// to the closure.
    pub fn new(create_source: F) -> Box<Future<Item = T, Error = Never>> {
        Box::new(SourceFuture {
            create_source: Some(create_source),
            source: None,
        })
    }
}

impl<F, T> Future for SourceFuture<F, T>
where
    F: FnOnce(oneshot::Sender<T>) -> Source + Send + 'static,
{
    type Item = T;
    type Error = Never;

    fn poll(&mut self, ctx: &mut Context) -> Result<Async<T>, Never> {
        let SourceFuture {
            ref mut create_source,
            ref mut source,
            ..
        } = *self;

        if let Some(create_source) = create_source.take() {
            let main_context = MainContext::ref_thread_default();
            match main_context {
                None => panic!("Polled from an Executor not backed by a GLib main context"),
                Some(ref main_context) => {
                    assert!(main_context.is_owner(), "Spawning futures only allowed if the thread is owning the MainContext");

                    // Channel for sending back the Source result to our future here.
                    //
                    // In theory we could directly continue polling the
                    // corresponding task from the Source callback,
                    // however this would break at the very least
                    // the g_main_current_source() API.
                    let (send, recv) = oneshot::channel();

                    let s = create_source(send);

                    s.attach(Some(main_context));
                    *source = Some((s, recv));
                }
            }
        }

        // At this point we must have a receiver
        let res = {
            let &mut (_, ref mut receiver) = source.as_mut().unwrap();
            receiver.poll(ctx)
        };
        match res {
            Err(_) => panic!("Source sender was unexpectedly closed"),
            Ok(Async::Ready(v)) => {
                // Get rid of the reference to the source, it triggered
                let _ = source.take();
                Ok(Async::Ready(v))
            }
            Ok(Async::Pending) => Ok(Async::Pending),
        }
    }
}

impl<T, F> Drop for SourceFuture<T, F> {
    fn drop(&mut self) {
        // Get rid of the source, we don't care anymore if it still triggers
        if let Some((source, _)) = self.source.take() {
            source.destroy();
        }
    }
}

/// Create a `Future` that will resolve after the given number of milliseconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn timeout_future(value: u32) -> Box<Future<Item = (), Error = Never>> {
    timeout_future_with_priority(::PRIORITY_DEFAULT, value)
}

/// Create a `Future` that will resolve after the given number of milliseconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn timeout_future_with_priority(priority: Priority, value: u32) -> Box<Future<Item = (), Error = Never>> {
    SourceFuture::new(move |send| {
        let mut send = Some(send);
        ::timeout_source_new(value, None, priority, move || {
            let _ = send.take().unwrap().send(());
            Continue(false)
        })
    })
}

/// Create a `Future` that will resolve after the given number of seconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn timeout_future_seconds(value: u32) -> Box<Future<Item = (), Error = Never>> {
    timeout_future_seconds_with_priority(::PRIORITY_DEFAULT, value)
}

/// Create a `Future` that will resolve after the given number of seconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn timeout_future_seconds_with_priority(priority: Priority, value: u32) -> Box<Future<Item = (), Error = Never>> {
    SourceFuture::new(move |send| {
        let mut send = Some(send);
        ::timeout_source_new_seconds(value, None, priority, move || {
            let _ = send.take().unwrap().send(());
            Continue(false)
        })
    })
}

/// Create a `Future` that will resolve once the child process with the given pid exits
///
/// The `Future` will resolve to the pid of the child process and the exit code.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn child_watch_future(pid: ::Pid) -> Box<Future<Item = (::Pid, i32), Error = Never>> {
    child_watch_future_with_priority(::PRIORITY_DEFAULT, pid)
}

/// Create a `Future` that will resolve once the child process with the given pid exits
///
/// The `Future` will resolve to the pid of the child process and the exit code.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn child_watch_future_with_priority(priority: Priority, pid: ::Pid) -> Box<Future<Item = (::Pid, i32), Error = Never>> {
    SourceFuture::new(move |send| {
        let mut send = Some(send);
        ::child_watch_source_new(pid, None, priority, move |pid, code| {
            let _ = send.take().unwrap().send((pid, code));
        })
    })
}

#[cfg(any(unix, feature = "dox"))]
/// Create a `Future` that will resolve once the given UNIX signal is raised
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn unix_signal_future(signum: i32) -> Box<Future<Item = (), Error = Never>> {
    unix_signal_future_with_priority(::PRIORITY_DEFAULT, signum)
}

#[cfg(any(unix, feature = "dox"))]
/// Create a `Future` that will resolve once the given UNIX signal is raised
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn unix_signal_future_with_priority(priority: Priority, signum: i32) -> Box<Future<Item = (), Error = Never>> {
    SourceFuture::new(move |send| {
        let mut send = Some(send);
        ::unix_signal_source_new(signum, None, priority, move || {
            let _ = send.take().unwrap().send(());
            Continue(false)
        })
    })
}

/// Represents a `Stream` around a `glib::Source`. The stream will
/// be provide all values that are provided by the source
pub struct SourceStream<F, T> {
    create_source: Option<F>,
    source: Option<(Source, mpsc::UnboundedReceiver<T>)>,
}

impl<F, T: 'static> SourceStream<F, T>
where
    F: FnOnce(mpsc::UnboundedSender<T>) -> Source + Send + 'static,
{
    /// Create a new `SourceStream`
    ///
    /// The provided closure should return a newly created `glib::Source` when called
    /// and pass the values provided by the source to the sender that is passed
    /// to the closure.
    pub fn new(create_source: F) -> Box<Stream<Item = T, Error = Never>> {
        Box::new(SourceStream {
            create_source: Some(create_source),
            source: None,
        })
    }
}

impl<F, T> Stream for SourceStream<F, T>
where
    F: FnOnce(mpsc::UnboundedSender<T>) -> Source + Send + 'static,
{
    type Item = T;
    type Error = Never;

    fn poll_next(&mut self, ctx: &mut Context) -> Result<Async<Option<T>>, Never> {
        let SourceStream {
            ref mut create_source,
            ref mut source,
            ..
        } = *self;

        if let Some(create_source) = create_source.take() {
            let main_context = MainContext::ref_thread_default();
            match main_context {
                None => panic!("Polled from an Executor not backed by a GLib main context"),
                Some(ref main_context) => {
                    assert!(main_context.is_owner(), "Spawning futures only allowed if the thread is owning the MainContext");

                    // Channel for sending back the Source result to our future here.
                    //
                    // In theory we could directly continue polling the
                    // corresponding task from the Source callback,
                    // however this would break at the very least
                    // the g_main_current_source() API.
                    let (send, recv) = mpsc::unbounded();

                    let s = create_source(send);

                    s.attach(Some(main_context));
                    *source = Some((s, recv));
                }
            }
        }

        // At this point we must have a receiver
        let res = {
            let &mut (_, ref mut receiver) = source.as_mut().unwrap();
            receiver.poll_next(ctx)
        };
        match res {
            Err(_) => panic!("Source sender was unexpectedly closed"),
            Ok(Async::Ready(v)) => {
                if v.is_none() {
                    // Get rid of the reference to the source, it triggered
                    let _ = source.take();
                }
                Ok(Async::Ready(v))
            }
            Ok(Async::Pending) => Ok(Async::Pending),
        }
    }
}

impl<T, F> Drop for SourceStream<T, F> {
    fn drop(&mut self) {
        // Get rid of the source, we don't care anymore if it still triggers
        if let Some((source, _)) = self.source.take() {
            source.destroy();
        }
    }
}

/// Create a `Stream` that will provide a value every given number of milliseconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn interval_stream(value: u32) -> Box<Stream<Item = (), Error = Never>> {
    interval_stream_with_priority(::PRIORITY_DEFAULT, value)
}

/// Create a `Stream` that will provide a value every given number of milliseconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn interval_stream_with_priority(priority: Priority, value: u32) -> Box<Stream<Item = (), Error = Never>> {
    SourceStream::new(move |send| {
        ::timeout_source_new(value, None, priority, move || {
            if send.unbounded_send(()).is_err() {
                Continue(false)
            } else {
                Continue(true)
            }
        })
    })
}

/// Create a `Stream` that will provide a value every given number of seconds.
///
/// The `Stream` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn interval_stream_seconds(value: u32) -> Box<Stream<Item = (), Error = Never>> {
    interval_stream_seconds_with_priority(::PRIORITY_DEFAULT, value)
}

/// Create a `Stream` that will provide a value every given number of seconds.
///
/// The `Stream` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn interval_stream_seconds_with_priority(priority: Priority, value: u32) -> Box<Stream<Item = (), Error = Never>> {
    SourceStream::new(move |send| {
        ::timeout_source_new_seconds(value, None, priority, move || {
            if send.unbounded_send(()).is_err() {
                Continue(false)
            } else {
                Continue(true)
            }
        })
    })
}

#[cfg(any(unix, feature = "dox"))]
/// Create a `Stream` that will provide a value whenever the given UNIX signal is raised
///
/// The `Stream` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn unix_signal_stream(signum: i32) -> Box<Stream<Item = (), Error = Never>> {
    unix_signal_stream_with_priority(::PRIORITY_DEFAULT, signum)
}

#[cfg(any(unix, feature = "dox"))]
/// Create a `Stream` that will provide a value whenever the given UNIX signal is raised
///
/// The `Stream` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn unix_signal_stream_with_priority(priority: Priority, signum: i32) -> Box<Stream<Item = (), Error = Never>> {
    SourceStream::new(move |send| {
        ::unix_signal_source_new(signum, None, priority, move || {
            if send.unbounded_send(()).is_err() {
                Continue(false)
            } else {
                Continue(true)
            }
        })
    })
}

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

    #[test]
    fn test_timeout() {
        let mut c = MainContext::new();

        let res = c.block_on(timeout_future(20)
            .and_then(move |_ctx| {
                Ok(())
            })
        );

        assert_eq!(res, Ok(()));
    }

    #[test]
    fn test_interval() {
        let mut c = MainContext::new();

        let mut count = 0;

        {
            let count = &mut count;
            let res = c.block_on(interval_stream(20)
                .take(2)
                .for_each(move |_ctx| {
                    *count = *count + 1;
                    Ok(())
                })
                .map(|_| ())
            );

            assert_eq!(res, Ok(()));
        }

        assert_eq!(count, 2);
    }
}
<commit_msg>Make sure that all SourceFutures/Streams implement Send<commit_after>// Copyright 2018, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use futures_channel::{mpsc, oneshot};
use futures_core::stream::Stream;
use futures_core::task::Context;
use futures_core::{Async, Future, Never};

use MainContext;
use Source;
use Continue;
use Priority;

/// Represents a `Future` around a `glib::Source`. The future will
/// be resolved once the source has provided a value
pub struct SourceFuture<F, T> {
    create_source: Option<F>,
    source: Option<(Source, oneshot::Receiver<T>)>,
}

impl<F, T: 'static> SourceFuture<F, T>
where
    F: FnOnce(oneshot::Sender<T>) -> Source + Send + 'static,
{
    /// Create a new `SourceFuture`
    ///
    /// The provided closure should return a newly created `glib::Source` when called
    /// and pass the value provided by the source to the oneshot sender that is passed
    /// to the closure.
    pub fn new(create_source: F) -> SourceFuture<F, T> {
        SourceFuture {
            create_source: Some(create_source),
            source: None,
        }
    }
}

impl<F, T> Future for SourceFuture<F, T>
where
    F: FnOnce(oneshot::Sender<T>) -> Source + Send + 'static,
{
    type Item = T;
    type Error = Never;

    fn poll(&mut self, ctx: &mut Context) -> Result<Async<T>, Never> {
        let SourceFuture {
            ref mut create_source,
            ref mut source,
            ..
        } = *self;

        if let Some(create_source) = create_source.take() {
            let main_context = MainContext::ref_thread_default();
            match main_context {
                None => panic!("Polled from an Executor not backed by a GLib main context"),
                Some(ref main_context) => {
                    assert!(main_context.is_owner(), "Spawning futures only allowed if the thread is owning the MainContext");

                    // Channel for sending back the Source result to our future here.
                    //
                    // In theory we could directly continue polling the
                    // corresponding task from the Source callback,
                    // however this would break at the very least
                    // the g_main_current_source() API.
                    let (send, recv) = oneshot::channel();

                    let s = create_source(send);

                    s.attach(Some(main_context));
                    *source = Some((s, recv));
                }
            }
        }

        // At this point we must have a receiver
        let res = {
            let &mut (_, ref mut receiver) = source.as_mut().unwrap();
            receiver.poll(ctx)
        };
        match res {
            Err(_) => panic!("Source sender was unexpectedly closed"),
            Ok(Async::Ready(v)) => {
                // Get rid of the reference to the source, it triggered
                let _ = source.take();
                Ok(Async::Ready(v))
            }
            Ok(Async::Pending) => Ok(Async::Pending),
        }
    }
}

impl<T, F> Drop for SourceFuture<T, F> {
    fn drop(&mut self) {
        // Get rid of the source, we don't care anymore if it still triggers
        if let Some((source, _)) = self.source.take() {
            source.destroy();
        }
    }
}

/// Create a `Future` that will resolve after the given number of milliseconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn timeout_future(value: u32) -> Box<Future<Item = (), Error = Never> + Send> {
    timeout_future_with_priority(::PRIORITY_DEFAULT, value)
}

/// Create a `Future` that will resolve after the given number of milliseconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn timeout_future_with_priority(priority: Priority, value: u32) -> Box<Future<Item = (), Error = Never> + Send> {
    Box::new(SourceFuture::new(move |send| {
        let mut send = Some(send);
        ::timeout_source_new(value, None, priority, move || {
            let _ = send.take().unwrap().send(());
            Continue(false)
        })
    }))
}

/// Create a `Future` that will resolve after the given number of seconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn timeout_future_seconds(value: u32) -> Box<Future<Item = (), Error = Never> + Send> {
    timeout_future_seconds_with_priority(::PRIORITY_DEFAULT, value)
}

/// Create a `Future` that will resolve after the given number of seconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn timeout_future_seconds_with_priority(priority: Priority, value: u32) -> Box<Future<Item = (), Error = Never> + Send> {
    Box::new(SourceFuture::new(move |send| {
        let mut send = Some(send);
        ::timeout_source_new_seconds(value, None, priority, move || {
            let _ = send.take().unwrap().send(());
            Continue(false)
        })
    }))
}

/// Create a `Future` that will resolve once the child process with the given pid exits
///
/// The `Future` will resolve to the pid of the child process and the exit code.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn child_watch_future(pid: ::Pid) -> Box<Future<Item = (::Pid, i32), Error = Never> + Send> {
    child_watch_future_with_priority(::PRIORITY_DEFAULT, pid)
}

/// Create a `Future` that will resolve once the child process with the given pid exits
///
/// The `Future` will resolve to the pid of the child process and the exit code.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn child_watch_future_with_priority(priority: Priority, pid: ::Pid) -> Box<Future<Item = (::Pid, i32), Error = Never> + Send> {
    Box::new(SourceFuture::new(move |send| {
        let mut send = Some(send);
        ::child_watch_source_new(pid, None, priority, move |pid, code| {
            let _ = send.take().unwrap().send((pid, code));
        })
    }))
}

#[cfg(any(unix, feature = "dox"))]
/// Create a `Future` that will resolve once the given UNIX signal is raised
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn unix_signal_future(signum: i32) -> Box<Future<Item = (), Error = Never> + Send> {
    unix_signal_future_with_priority(::PRIORITY_DEFAULT, signum)
}

#[cfg(any(unix, feature = "dox"))]
/// Create a `Future` that will resolve once the given UNIX signal is raised
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn unix_signal_future_with_priority(priority: Priority, signum: i32) -> Box<Future<Item = (), Error = Never> + Send> {
    Box::new(SourceFuture::new(move |send| {
        let mut send = Some(send);
        ::unix_signal_source_new(signum, None, priority, move || {
            let _ = send.take().unwrap().send(());
            Continue(false)
        })
    }))
}

/// Represents a `Stream` around a `glib::Source`. The stream will
/// be provide all values that are provided by the source
pub struct SourceStream<F, T> {
    create_source: Option<F>,
    source: Option<(Source, mpsc::UnboundedReceiver<T>)>,
}

impl<F, T: 'static> SourceStream<F, T>
where
    F: FnOnce(mpsc::UnboundedSender<T>) -> Source + Send + 'static,
{
    /// Create a new `SourceStream`
    ///
    /// The provided closure should return a newly created `glib::Source` when called
    /// and pass the values provided by the source to the sender that is passed
    /// to the closure.
    pub fn new(create_source: F) -> SourceStream<F, T> {
        SourceStream {
            create_source: Some(create_source),
            source: None,
        }
    }
}

impl<F, T> Stream for SourceStream<F, T>
where
    F: FnOnce(mpsc::UnboundedSender<T>) -> Source + Send + 'static,
{
    type Item = T;
    type Error = Never;

    fn poll_next(&mut self, ctx: &mut Context) -> Result<Async<Option<T>>, Never> {
        let SourceStream {
            ref mut create_source,
            ref mut source,
            ..
        } = *self;

        if let Some(create_source) = create_source.take() {
            let main_context = MainContext::ref_thread_default();
            match main_context {
                None => panic!("Polled from an Executor not backed by a GLib main context"),
                Some(ref main_context) => {
                    assert!(main_context.is_owner(), "Spawning futures only allowed if the thread is owning the MainContext");

                    // Channel for sending back the Source result to our future here.
                    //
                    // In theory we could directly continue polling the
                    // corresponding task from the Source callback,
                    // however this would break at the very least
                    // the g_main_current_source() API.
                    let (send, recv) = mpsc::unbounded();

                    let s = create_source(send);

                    s.attach(Some(main_context));
                    *source = Some((s, recv));
                }
            }
        }

        // At this point we must have a receiver
        let res = {
            let &mut (_, ref mut receiver) = source.as_mut().unwrap();
            receiver.poll_next(ctx)
        };
        match res {
            Err(_) => panic!("Source sender was unexpectedly closed"),
            Ok(Async::Ready(v)) => {
                if v.is_none() {
                    // Get rid of the reference to the source, it triggered
                    let _ = source.take();
                }
                Ok(Async::Ready(v))
            }
            Ok(Async::Pending) => Ok(Async::Pending),
        }
    }
}

impl<T, F> Drop for SourceStream<T, F> {
    fn drop(&mut self) {
        // Get rid of the source, we don't care anymore if it still triggers
        if let Some((source, _)) = self.source.take() {
            source.destroy();
        }
    }
}

/// Create a `Stream` that will provide a value every given number of milliseconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn interval_stream(value: u32) -> Box<Stream<Item = (), Error = Never> + Send> {
    interval_stream_with_priority(::PRIORITY_DEFAULT, value)
}

/// Create a `Stream` that will provide a value every given number of milliseconds.
///
/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn interval_stream_with_priority(priority: Priority, value: u32) -> Box<Stream<Item = (), Error = Never> + Send> {
    Box::new(SourceStream::new(move |send| {
        ::timeout_source_new(value, None, priority, move || {
            if send.unbounded_send(()).is_err() {
                Continue(false)
            } else {
                Continue(true)
            }
        })
    }))
}

/// Create a `Stream` that will provide a value every given number of seconds.
///
/// The `Stream` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn interval_stream_seconds(value: u32) -> Box<Stream<Item = (), Error = Never> + Send> {
    interval_stream_seconds_with_priority(::PRIORITY_DEFAULT, value)
}

/// Create a `Stream` that will provide a value every given number of seconds.
///
/// The `Stream` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn interval_stream_seconds_with_priority(priority: Priority, value: u32) -> Box<Stream<Item = (), Error = Never> + Send> {
    Box::new(SourceStream::new(move |send| {
        ::timeout_source_new_seconds(value, None, priority, move || {
            if send.unbounded_send(()).is_err() {
                Continue(false)
            } else {
                Continue(true)
            }
        })
    }))
}

#[cfg(any(unix, feature = "dox"))]
/// Create a `Stream` that will provide a value whenever the given UNIX signal is raised
///
/// The `Stream` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn unix_signal_stream(signum: i32) -> Box<Stream<Item = (), Error = Never> + Send> {
    unix_signal_stream_with_priority(::PRIORITY_DEFAULT, signum)
}

#[cfg(any(unix, feature = "dox"))]
/// Create a `Stream` that will provide a value whenever the given UNIX signal is raised
///
/// The `Stream` must be spawned on an `Executor` backed by a `glib::MainContext`.
pub fn unix_signal_stream_with_priority(priority: Priority, signum: i32) -> Box<Stream<Item = (), Error = Never> + Send> {
    Box::new(SourceStream::new(move |send| {
        ::unix_signal_source_new(signum, None, priority, move || {
            if send.unbounded_send(()).is_err() {
                Continue(false)
            } else {
                Continue(true)
            }
        })
    }))
}

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

    #[test]
    fn test_timeout() {
        let mut c = MainContext::new();

        let res = c.block_on(timeout_future(20)
            .and_then(move |_ctx| {
                Ok(())
            })
        );

        assert_eq!(res, Ok(()));
    }

    #[test]
    fn test_timeout_send() {
        let mut c = MainContext::new();
        let l = ::MainLoop::new(Some(&c), false);

        let l_clone = l.clone();
        c.spawn(timeout_future(20)
            .and_then(move |_ctx| {
                l_clone.quit();
                Ok(())
            })
        );

        l.run();
    }

    #[test]
    fn test_interval() {
        let mut c = MainContext::new();

        let mut count = 0;

        {
            let count = &mut count;
            let res = c.block_on(interval_stream(20)
                .take(2)
                .for_each(move |_ctx| {
                    *count = *count + 1;
                    Ok(())
                })
                .map(|_| ())
            );

            assert_eq!(res, Ok(()));
        }

        assert_eq!(count, 2);
    }
}
<|endoftext|>


<commit_before>use super::{imports::Imports, trampolines};
use crate::{
    analysis::trampolines::Trampoline,
    config::{self, gobjects::GObject},
    env::Env,
    library, nameutil,
    traits::*,
    version::Version,
};

#[derive(Debug)]
pub struct Info {
    pub connect_name: String,
    pub signal_name: String,
    pub action_emit_name: Option<String>,
    pub trampoline: Result<Trampoline, Vec<String>>,
    pub version: Option<Version>,
    pub deprecated_version: Option<Version>,
    pub doc_hidden: bool,
}

pub fn analyze(
    env: &Env,
    signals: &[library::Signal],
    type_tid: library::TypeId,
    in_trait: bool,
    obj: &GObject,
    imports: &mut Imports,
) -> Vec<Info> {
    let mut sns = Vec::new();

    for signal in signals {
        let configured_signals = obj.signals.matched(&signal.name);
        if !configured_signals.iter().all(|f| f.status.need_generate()) {
            continue;
        }
        if env.is_totally_deprecated(signal.deprecated_version) {
            continue;
        }

        let info = analyze_signal(
            env,
            signal,
            type_tid,
            in_trait,
            &configured_signals,
            obj,
            imports,
        );
        if let Some(info) = info {
            sns.push(info);
        }
    }

    sns
}

fn analyze_signal(
    env: &Env,
    signal: &library::Signal,
    type_tid: library::TypeId,
    in_trait: bool,
    configured_signals: &[&config::signals::Signal],
    obj: &GObject,
    imports: &mut Imports,
) -> Option<Info> {
    let mut used_types: Vec<String> = Vec::with_capacity(4);
    let version = configured_signals
        .iter()
        .filter_map(|f| f.version)
        .min()
        .or(signal.version);
    let deprecated_version = signal.deprecated_version;
    let doc_hidden = configured_signals.iter().any(|f| f.doc_hidden);

    let imports = &mut imports.with_defaults(version, &None);

    let connect_name = format!("connect_{}", nameutil::signal_to_snake(&signal.name));
    let trampoline = trampolines::analyze(
        env,
        signal,
        type_tid,
        in_trait,
        configured_signals,
        obj,
        &mut used_types,
        version,
    );

    let action_emit_name = if signal.is_action {
        imports.add("glib::object::ObjectExt");
        Some(format!("emit_{}", nameutil::signal_to_snake(&signal.name)))
    } else {
        None
    };

    if trampoline.is_ok() {
        imports.add_used_types(&used_types);
        if in_trait {
            imports.add("glib::object::Cast");
        } else {
            //To resolve a conflict with OSTree::ObjectType
            imports.add("glib::object::ObjectType as ObjectType_");
        }
        imports.add("glib::signal::connect_raw");
        imports.add("glib::signal::SignalHandlerId");
        imports.add("std::mem::transmute");
        imports.add("std::boxed::Box as Box_");
    }

    let info = Info {
        connect_name,
        signal_name: signal.name.clone(),
        trampoline,
        action_emit_name,
        version,
        deprecated_version,
        doc_hidden,
    };
    Some(info)
}
<commit_msg>Fix clippy::unnecessary_wraps<commit_after>use super::{imports::Imports, trampolines};
use crate::{
    analysis::trampolines::Trampoline,
    config::{self, gobjects::GObject},
    env::Env,
    library, nameutil,
    traits::*,
    version::Version,
};

#[derive(Debug)]
pub struct Info {
    pub connect_name: String,
    pub signal_name: String,
    pub action_emit_name: Option<String>,
    pub trampoline: Result<Trampoline, Vec<String>>,
    pub version: Option<Version>,
    pub deprecated_version: Option<Version>,
    pub doc_hidden: bool,
}

pub fn analyze(
    env: &Env,
    signals: &[library::Signal],
    type_tid: library::TypeId,
    in_trait: bool,
    obj: &GObject,
    imports: &mut Imports,
) -> Vec<Info> {
    let mut sns = Vec::new();

    for signal in signals {
        let configured_signals = obj.signals.matched(&signal.name);
        if !configured_signals.iter().all(|f| f.status.need_generate()) {
            continue;
        }
        if env.is_totally_deprecated(signal.deprecated_version) {
            continue;
        }

        let info = analyze_signal(
            env,
            signal,
            type_tid,
            in_trait,
            &configured_signals,
            obj,
            imports,
        );
        sns.push(info);
    }

    sns
}

fn analyze_signal(
    env: &Env,
    signal: &library::Signal,
    type_tid: library::TypeId,
    in_trait: bool,
    configured_signals: &[&config::signals::Signal],
    obj: &GObject,
    imports: &mut Imports,
) -> Info {
    let mut used_types: Vec<String> = Vec::with_capacity(4);
    let version = configured_signals
        .iter()
        .filter_map(|f| f.version)
        .min()
        .or(signal.version);
    let deprecated_version = signal.deprecated_version;
    let doc_hidden = configured_signals.iter().any(|f| f.doc_hidden);

    let imports = &mut imports.with_defaults(version, &None);

    let connect_name = format!("connect_{}", nameutil::signal_to_snake(&signal.name));
    let trampoline = trampolines::analyze(
        env,
        signal,
        type_tid,
        in_trait,
        configured_signals,
        obj,
        &mut used_types,
        version,
    );

    let action_emit_name = if signal.is_action {
        imports.add("glib::object::ObjectExt");
        Some(format!("emit_{}", nameutil::signal_to_snake(&signal.name)))
    } else {
        None
    };

    if trampoline.is_ok() {
        imports.add_used_types(&used_types);
        if in_trait {
            imports.add("glib::object::Cast");
        } else {
            //To resolve a conflict with OSTree::ObjectType
            imports.add("glib::object::ObjectType as ObjectType_");
        }
        imports.add("glib::signal::connect_raw");
        imports.add("glib::signal::SignalHandlerId");
        imports.add("std::mem::transmute");
        imports.add("std::boxed::Box as Box_");
    }

    let info = Info {
        connect_name,
        signal_name: signal.name.clone(),
        trampoline,
        action_emit_name,
        version,
        deprecated_version,
        doc_hidden,
    };

    info
}
<|endoftext|>


<commit_before>use base::world::{self, Chunk, HexPillar, PillarSection, PropType};
use base::math::*;
use glium::{self, DrawParameters, IndexBuffer, Program, VertexBuffer};
use glium::draw_parameters::{BackfaceCullingMode, DepthTest};
use glium::backend::Facade;
use glium::index::PrimitiveType;
use Camera;
use util::ToArr;
use std::f32::consts;
use world::plant_view::PlantView;

/// Graphical representation of the `base::Chunk`.
pub struct ChunkView {
    vertices: VertexBuffer<Vertex>,
    program: Program,
    pillars: Vec<PillarView>,
    index_buffer: IndexBuffer<u32>,
}

impl ChunkView {
    /// Creates the graphical representation of given chunk at the given chunk
    /// offset
    pub fn from_chunk<F: Facade>(chunk: &Chunk, offset: AxialPoint, facade: &F) -> Self {

        // Create one hexagon for this chunk
        // let mut hexagon_vertex_buffer = VecDeque::new();
        // for i in 0..6 {
        // let (x, y) = hex_corner(world::HEX_OUTER_RADIUS, i);
        //
        // hexagon_vertex_buffer.push_front(Vertex {
        // position: [x, y, world::PILLAR_STEP_HEIGHT],
        // });
        // hexagon_vertex_buffer.push_back(Vertex { position: [x, y, 0.0] });
        //
        // }
        //

        let mut vertices = Vec::new();
        let mut indices = Vec::new();
        let (mut top_vertices, mut top_indices) = get_top_hexagon_modell();
        vertices.append(&mut top_vertices);
        indices.append(&mut top_indices);
        let (mut bottom_vertices, mut bottom_indices) = get_bottom_hexagon_modell();
        vertices.append(&mut bottom_vertices);
        indices.append(&mut bottom_indices);
        let (mut north_vertices, mut north_indices) = get_side_hexagon_modell(4, 5, vertices.len());
        vertices.append(&mut north_vertices);
        indices.append(&mut north_indices);
        let (mut south_vertices, mut south_indices) = get_side_hexagon_modell(1, 2, vertices.len());
        vertices.append(&mut south_vertices);
        indices.append(&mut south_indices);
        let (mut ne_vertices, mut ne_indices) = get_side_hexagon_modell(5, 0, vertices.len());
        vertices.append(&mut ne_vertices);
        indices.append(&mut ne_indices);
        let (mut se_vertices, mut se_indices) = get_side_hexagon_modell(0, 1, vertices.len());
        vertices.append(&mut se_vertices);
        indices.append(&mut se_indices);
        let (mut nw_vertices, mut nw_indices) = get_side_hexagon_modell(3, 4, vertices.len());
        vertices.append(&mut nw_vertices);
        indices.append(&mut nw_indices);
        let (mut sw_vertices, mut sw_indices) = get_side_hexagon_modell(2, 3, vertices.len());
        vertices.append(&mut sw_vertices);
        indices.append(&mut sw_indices);

        // convert to vector
        // let final_buffer: Vec<_> = top_modell.0;

        let vbuf = VertexBuffer::new(facade, &vertices).unwrap();
        let prog = Program::from_source(facade,
                                        include_str!("chunk_std.vert"),
                                        include_str!("chunk_std.frag"),
                                        None)
            .unwrap();

        let mut pillars = Vec::new();
        for q in 0..world::CHUNK_SIZE * world::CHUNK_SIZE {
            let pos = offset.to_real() +
                      AxialVector::new((q / world::CHUNK_SIZE).into(),
                                       (q % world::CHUNK_SIZE).into())
                .to_real();
            let pillar = &chunk.pillars()[q as usize];
            pillars.push(PillarView::from_pillar(pos, pillar, facade));
        }

        // Indecies
        // let raw_index_buffer = [5, 0, 1, 2, 5, 1, 4, 5, 2, 3, 4, 2 /* TOP */, 6, 7,
        // 8, 8, 9,
        // 6, 9, 11, 6, 9, 10, 11 /* BOTTOM */, 6, 5, 4, 7, 6, 4, 6, 0,
        // 5, 11, 0, 6, 10, 1, 0, 11, 10, 0, 9, 2, 1, 10, 9, 1, 8, 3, 2, 9,
        // 8, 2, 7, 4, 3, 8, 7, 3 /* Body */];

        let ibuf = IndexBuffer::new(facade, PrimitiveType::TrianglesList, &indices).unwrap();

        ChunkView {
            vertices: vbuf,
            program: prog,
            pillars: pillars,
            index_buffer: ibuf,
        }
    }

    pub fn draw<S: glium::Surface>(&self, surface: &mut S, camera: &Camera) {
        for pillar in &self.pillars {
            for section in &pillar.sections {
                let height = section.top.units() - section.bottom.units();

                let uniforms = uniform! {
                    height: height as f32,
                    offset: [pillar.pos.x, pillar.pos.y, section.bottom.to_real()],
                    proj_matrix: camera.proj_matrix().to_arr(),
                    view_matrix: camera.view_matrix().to_arr(),
                    material_color: section.ground.get_color(),
                };
                let params = DrawParameters {
                    depth: glium::Depth {
                        write: true,
                        test: DepthTest::IfLess,
                        ..Default::default()
                    },
                    backface_culling: BackfaceCullingMode::CullCounterClockwise,
                    ..Default::default()
                };

                surface.draw(&self.vertices,
                          &self.index_buffer,
                          &self.program,
                          &uniforms,
                          &params)
                    .unwrap();
            }

            for plant in &pillar.plants {
                plant.draw(surface, camera);
            }
        }
    }
}

/// Vertex type used to render chunks (or hex pillars).
#[derive(Debug, Copy, Clone)]
pub struct Vertex {
    pub position: [f32; 3],
    pub normal: [f32; 3],
}

implement_vertex!(Vertex, position, normal);

pub struct PillarView {
    pos: Point2f,
    sections: Vec<PillarSection>,
    plants: Vec<PlantView>,
}

impl PillarView {
    fn from_pillar<F: Facade>(pos: Point2f, pillar: &HexPillar, facade: &F) -> PillarView {
        PillarView {
            pos: pos,
            sections: pillar.sections().to_vec(),
            plants: pillar.props()
                .iter()
                .map(|prop| {
                    match prop.prop {
                        PropType::Plant(ref plant) => {
                            let pos = Point3f::new(pos.x, pos.y, prop.baseline.to_real());
                            PlantView::from_plant(pos, plant, facade)
                        }
                    }
                })
                .collect(),
        }
    }
}

/// Calculates one Point-coordinates of a Hexagon
fn hex_corner(size: f32, i: i32) -> (f32, f32) {
    let angle_deg = 60.0 * (i as f32) + 30.0;
    let angle_rad = (consts::PI / 180.0) * angle_deg;

    (size * angle_rad.cos(), size * angle_rad.sin())
}

pub fn get_top_hexagon_modell() -> (Vec<Vertex>, Vec<u32>) {
    let mut vertices = Vec::new();

    for i in 0..6 {
        let (x, y) = hex_corner(world::HEX_OUTER_RADIUS, i);

        vertices.push(Vertex {
            position: [x, y, world::PILLAR_STEP_HEIGHT],
            normal: [0.0, 0.0, 1.0],
        });
    }

    vertices.push(Vertex {
        position: [0.0, 0.0, world::PILLAR_STEP_HEIGHT],
        normal: [0.0, 0.0, 1.0],
    });

    (vertices, vec![0, 6, 1, 5, 6, 0, 4, 6, 5, 3, 6, 4, 2, 6, 3, 1, 6, 2])
}

pub fn get_bottom_hexagon_modell() -> (Vec<Vertex>, Vec<u32>) {
    let mut vertices = Vec::new();

    for i in 0..6 {
        let (x, y) = hex_corner(world::HEX_OUTER_RADIUS, i);

        vertices.push(Vertex {
            position: [x, y, 0.0],
            normal: [0.0, 0.0, -1.0],
        });
    }

    vertices.push(Vertex {
        position: [0.0, 0.0, 0.0],
        normal: [0.0, 0.0, -1.0],
    });

    (vertices, vec![8, 13, 7, 7, 13, 12, 12, 13, 11, 11, 13, 10, 10, 13, 9, 9, 13, 8])
}

pub fn get_side_hexagon_modell(ind1: i32, ind2: i32, cur_len: usize) -> (Vec<Vertex>, Vec<u32>) {
    let mut vertices = Vec::new();

    let (x1, y1) = hex_corner(world::HEX_OUTER_RADIUS, ind1);
    let (x2, y2) = hex_corner(world::HEX_OUTER_RADIUS, ind2);
    let normal = [y1 + y2, x1 + x2, 0.0];

    vertices.push(Vertex {
        position: [x1, y1, world::PILLAR_STEP_HEIGHT],
        normal: normal,
    });
    vertices.push(Vertex {
        position: [x1, y1, 0.0],
        normal: normal,
    });
    vertices.push(Vertex {
        position: [x2, y2, world::PILLAR_STEP_HEIGHT],
        normal: normal,
    });
    vertices.push(Vertex {
        position: [x2, y2, 0.0],
        normal: normal,
    });

    (vertices,
     vec![cur_len as u32 + 0,
          cur_len as u32 + 2,
          cur_len as u32 + 1,
          cur_len as u32 + 1,
          cur_len as u32 + 2,
          cur_len as u32 + 3])
}
<commit_msg>added some comments and deleted unnecessary lines<commit_after>use base::world::{self, Chunk, HexPillar, PillarSection, PropType};
use base::math::*;
use glium::{self, DrawParameters, IndexBuffer, Program, VertexBuffer};
use glium::draw_parameters::{BackfaceCullingMode, DepthTest};
use glium::backend::Facade;
use glium::index::PrimitiveType;
use Camera;
use util::ToArr;
use std::f32::consts;
use world::plant_view::PlantView;

/// Graphical representation of the `base::Chunk`.
pub struct ChunkView {
    vertices: VertexBuffer<Vertex>,
    program: Program,
    pillars: Vec<PillarView>,
    index_buffer: IndexBuffer<u32>,
}

impl ChunkView {
    /// Creates the graphical representation of given chunk at the given chunk
    /// offset
    pub fn from_chunk<F: Facade>(chunk: &Chunk, offset: AxialPoint, facade: &F) -> Self {

        let mut vertices = Vec::new();
        let mut indices = Vec::new();
        let (mut top_vertices, mut top_indices) = get_top_hexagon_modell();
        vertices.append(&mut top_vertices);
        indices.append(&mut top_indices);
        let (mut bottom_vertices, mut bottom_indices) = get_bottom_hexagon_modell();
        vertices.append(&mut bottom_vertices);
        indices.append(&mut bottom_indices);
        let (mut north_vertices, mut north_indices) = get_side_hexagon_modell(4, 5, vertices.len());
        vertices.append(&mut north_vertices);
        indices.append(&mut north_indices);
        let (mut south_vertices, mut south_indices) = get_side_hexagon_modell(1, 2, vertices.len());
        vertices.append(&mut south_vertices);
        indices.append(&mut south_indices);
        let (mut ne_vertices, mut ne_indices) = get_side_hexagon_modell(5, 0, vertices.len());
        vertices.append(&mut ne_vertices);
        indices.append(&mut ne_indices);
        let (mut se_vertices, mut se_indices) = get_side_hexagon_modell(0, 1, vertices.len());
        vertices.append(&mut se_vertices);
        indices.append(&mut se_indices);
        let (mut nw_vertices, mut nw_indices) = get_side_hexagon_modell(3, 4, vertices.len());
        vertices.append(&mut nw_vertices);
        indices.append(&mut nw_indices);
        let (mut sw_vertices, mut sw_indices) = get_side_hexagon_modell(2, 3, vertices.len());
        vertices.append(&mut sw_vertices);
        indices.append(&mut sw_indices);

        let vbuf = VertexBuffer::new(facade, &vertices).unwrap();
        let prog = Program::from_source(facade,
                                        include_str!("chunk_std.vert"),
                                        include_str!("chunk_std.frag"),
                                        None)
            .unwrap();

        let mut pillars = Vec::new();
        for q in 0..world::CHUNK_SIZE * world::CHUNK_SIZE {
            let pos = offset.to_real() +
                      AxialVector::new((q / world::CHUNK_SIZE).into(),
                                       (q % world::CHUNK_SIZE).into())
                .to_real();
            let pillar = &chunk.pillars()[q as usize];
            pillars.push(PillarView::from_pillar(pos, pillar, facade));
        }

        let ibuf = IndexBuffer::new(facade, PrimitiveType::TrianglesList, &indices).unwrap();

        ChunkView {
            vertices: vbuf,
            program: prog,
            pillars: pillars,
            index_buffer: ibuf,
        }
    }

    pub fn draw<S: glium::Surface>(&self, surface: &mut S, camera: &Camera) {
        for pillar in &self.pillars {
            for section in &pillar.sections {
                let height = section.top.units() - section.bottom.units();

                let uniforms = uniform! {
                    height: height as f32,
                    offset: [pillar.pos.x, pillar.pos.y, section.bottom.to_real()],
                    proj_matrix: camera.proj_matrix().to_arr(),
                    view_matrix: camera.view_matrix().to_arr(),
                    material_color: section.ground.get_color(),
                };
                let params = DrawParameters {
                    depth: glium::Depth {
                        write: true,
                        test: DepthTest::IfLess,
                        ..Default::default()
                    },
                    backface_culling: BackfaceCullingMode::CullCounterClockwise,
                    ..Default::default()
                };

                surface.draw(&self.vertices,
                          &self.index_buffer,
                          &self.program,
                          &uniforms,
                          &params)
                    .unwrap();
            }

            for plant in &pillar.plants {
                plant.draw(surface, camera);
            }
        }
    }
}

/// Vertex type used to render chunks (or hex pillars).
#[derive(Debug, Copy, Clone)]
pub struct Vertex {
    pub position: [f32; 3],
    pub normal: [f32; 3],
}

implement_vertex!(Vertex, position, normal);

pub struct PillarView {
    pos: Point2f,
    sections: Vec<PillarSection>,
    plants: Vec<PlantView>,
}

impl PillarView {
    fn from_pillar<F: Facade>(pos: Point2f, pillar: &HexPillar, facade: &F) -> PillarView {
        PillarView {
            pos: pos,
            sections: pillar.sections().to_vec(),
            plants: pillar.props()
                .iter()
                .map(|prop| {
                    match prop.prop {
                        PropType::Plant(ref plant) => {
                            let pos = Point3f::new(pos.x, pos.y, prop.baseline.to_real());
                            PlantView::from_plant(pos, plant, facade)
                        }
                    }
                })
                .collect(),
        }
    }
}

/// Calculates one Point-coordinates of a Hexagon
fn hex_corner(size: f32, i: i32) -> (f32, f32) {
    let angle_deg = 60.0 * (i as f32) + 30.0;
    let angle_rad = (consts::PI / 180.0) * angle_deg;

    (size * angle_rad.cos(), size * angle_rad.sin())
}
/// Calculates the top face of the Hexagon and normals
pub fn get_top_hexagon_modell() -> (Vec<Vertex>, Vec<u32>) {
    let mut vertices = Vec::new();

    for i in 0..6 {
        let (x, y) = hex_corner(world::HEX_OUTER_RADIUS, i);

        vertices.push(Vertex {
            position: [x, y, world::PILLAR_STEP_HEIGHT],
            normal: [0.0, 0.0, 1.0],
        });
    }

    vertices.push(Vertex {
        position: [0.0, 0.0, world::PILLAR_STEP_HEIGHT],
        normal: [0.0, 0.0, 1.0],
    });

    (vertices, vec![0, 6, 1, 5, 6, 0, 4, 6, 5, 3, 6, 4, 2, 6, 3, 1, 6, 2])
}

/// Calculates the bottom face of the Hexagon and the normals
pub fn get_bottom_hexagon_modell() -> (Vec<Vertex>, Vec<u32>) {
    let mut vertices = Vec::new();

    for i in 0..6 {
        let (x, y) = hex_corner(world::HEX_OUTER_RADIUS, i);

        vertices.push(Vertex {
            position: [x, y, 0.0],
            normal: [0.0, 0.0, -1.0],
        });
    }

    vertices.push(Vertex {
        position: [0.0, 0.0, 0.0],
        normal: [0.0, 0.0, -1.0],
    });

    (vertices, vec![8, 13, 7, 7, 13, 12, 12, 13, 11, 11, 13, 10, 10, 13, 9, 9, 13, 8])
}

/// Calculates the sides of the Hexagon and normals
pub fn get_side_hexagon_modell(ind1: i32, ind2: i32, cur_len: usize) -> (Vec<Vertex>, Vec<u32>) {
    let mut vertices = Vec::new();

    let (x1, y1) = hex_corner(world::HEX_OUTER_RADIUS, ind1);
    let (x2, y2) = hex_corner(world::HEX_OUTER_RADIUS, ind2);
    let normal = [y1 + y2, x1 + x2, 0.0];

    vertices.push(Vertex {
        position: [x1, y1, world::PILLAR_STEP_HEIGHT],
        normal: normal,
    });
    vertices.push(Vertex {
        position: [x1, y1, 0.0],
        normal: normal,
    });
    vertices.push(Vertex {
        position: [x2, y2, world::PILLAR_STEP_HEIGHT],
        normal: normal,
    });
    vertices.push(Vertex {
        position: [x2, y2, 0.0],
        normal: normal,
    });

    (vertices,
     vec![cur_len as u32 + 0,
          cur_len as u32 + 2,
          cur_len as u32 + 1,
          cur_len as u32 + 1,
          cur_len as u32 + 2,
          cur_len as u32 + 3])
}
<|endoftext|>


<commit_before>// Copyright © 2018 Mozilla Foundation
//
// This program is made available under an ISC-style license.  See the
// accompanying file LICENSE for details.

extern crate cmake;
extern crate pkg_config;

use std::env;
use std::fs;
use std::path::Path;
use std::process::Command;

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(e) => e,
        Err(e) => panic!("{} failed with {}", stringify!($e), e),
    })
}

fn main() {
    let gecko_in_tree = env::var("CARGO_FEATURE_GECKO_IN_TREE").is_ok();
    if gecko_in_tree {
        return;
    }

    if env::var("LIBCUBEB_SYS_USE_PKG_CONFIG").is_ok() {
        if pkg_config::find_library("libcubeb").is_ok() {
            return;
        }
    }

    if !Path::new("libcubeb/.git").exists() {
        let _ = Command::new("git")
            .args(&["submodule", "update", "--init", "--recursive"])
            .status();
    }

    let target = env::var("TARGET").unwrap();
    //    let host = env::var("HOST").unwrap();
    let windows = target.contains("windows");
    let darwin = target.contains("darwin");
    let mut cfg = cmake::Config::new("libcubeb");

    let _ = fs::remove_dir_all(env::var("OUT_DIR").unwrap());
    t!(fs::create_dir_all(env::var("OUT_DIR").unwrap()));

    env::remove_var("DESTDIR");
    let dst = cfg.define("BUILD_SHARED_LIBS", "OFF")
        .define("BUILD_TESTS", "OFF")
        .build();

    if windows {
        println!("cargo:rustc-link-lib=static=cubeb");
        println!("cargo:rustc-link-lib=dylib=avrt");
        println!("cargo:rustc-link-lib=dylib=ole32");
        println!("cargo:rustc-link-lib=dylib=user32");
        println!("cargo:rustc-link-lib=dylib=winmm");
        println!("cargo:rustc-link-search=native={}/lib", dst.display());
    } else if darwin {
        println!("cargo:rustc-link-lib=static=cubeb");
        println!("cargo:rustc-link-lib=framework=AudioUnit");
        println!("cargo:rustc-link-lib=framework=CoreAudio");
        println!("cargo:rustc-link-lib=framework=CoreServices");
        println!("cargo:rustc-link-lib=dylib=c++");
        println!("cargo:rustc-link-search=native={}/lib", dst.display());
    } else {
        println!("cargo:rustc-link-lib=static=cubeb");
        println!("cargo:rustc-link-lib=dylib=stdc++");
        println!("cargo:rustc-link-search=native={}/lib", dst.display());
        println!("cargo:rustc-link-search=native={}/lib64", dst.display());

        // Ignore the result of find_library. We don't care if the
        // libraries are missing.
        let _ = pkg_config::find_library("alsa");
        let _ = pkg_config::find_library("libpulse");
        let _ = pkg_config::find_library("jack");
    }
}
<commit_msg>cubeb-sys: extract redundant build line out of the ifs<commit_after>// Copyright © 2018 Mozilla Foundation
//
// This program is made available under an ISC-style license.  See the
// accompanying file LICENSE for details.

extern crate cmake;
extern crate pkg_config;

use std::env;
use std::fs;
use std::path::Path;
use std::process::Command;

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(e) => e,
        Err(e) => panic!("{} failed with {}", stringify!($e), e),
    })
}

fn main() {
    let gecko_in_tree = env::var("CARGO_FEATURE_GECKO_IN_TREE").is_ok();
    if gecko_in_tree {
        return;
    }

    if env::var("LIBCUBEB_SYS_USE_PKG_CONFIG").is_ok() {
        if pkg_config::find_library("libcubeb").is_ok() {
            return;
        }
    }

    if !Path::new("libcubeb/.git").exists() {
        let _ = Command::new("git")
            .args(&["submodule", "update", "--init", "--recursive"])
            .status();
    }

    let target = env::var("TARGET").unwrap();
    //    let host = env::var("HOST").unwrap();
    let windows = target.contains("windows");
    let darwin = target.contains("darwin");
    let mut cfg = cmake::Config::new("libcubeb");

    let _ = fs::remove_dir_all(env::var("OUT_DIR").unwrap());
    t!(fs::create_dir_all(env::var("OUT_DIR").unwrap()));

    env::remove_var("DESTDIR");
    let dst = cfg.define("BUILD_SHARED_LIBS", "OFF")
        .define("BUILD_TESTS", "OFF")
        .build();

    println!("cargo:rustc-link-lib=static=cubeb");
    if windows {
        println!("cargo:rustc-link-lib=dylib=avrt");
        println!("cargo:rustc-link-lib=dylib=ole32");
        println!("cargo:rustc-link-lib=dylib=user32");
        println!("cargo:rustc-link-lib=dylib=winmm");
        println!("cargo:rustc-link-search=native={}/lib", dst.display());
    } else if darwin {
        println!("cargo:rustc-link-lib=framework=AudioUnit");
        println!("cargo:rustc-link-lib=framework=CoreAudio");
        println!("cargo:rustc-link-lib=framework=CoreServices");
        println!("cargo:rustc-link-lib=dylib=c++");
        println!("cargo:rustc-link-search=native={}/lib", dst.display());
    } else {
        println!("cargo:rustc-link-lib=dylib=stdc++");
        println!("cargo:rustc-link-search=native={}/lib", dst.display());
        println!("cargo:rustc-link-search=native={}/lib64", dst.display());

        // Ignore the result of find_library. We don't care if the
        // libraries are missing.
        let _ = pkg_config::find_library("alsa");
        let _ = pkg_config::find_library("libpulse");
        let _ = pkg_config::find_library("jack");
    }
}
<|endoftext|>


<commit_before>//! `cargo upgrade`
#![warn(missing_docs, missing_debug_implementations, missing_copy_implementations, trivial_casts,
       trivial_numeric_casts, unsafe_code, unstable_features, unused_import_braces,
       unused_qualifications)]

extern crate cargo_metadata;
extern crate docopt;
#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate serde_derive;
extern crate toml;

use std::collections::HashMap;
use std::io::{self, Write};
use std::path::Path;
use std::process;

extern crate cargo_edit;
use cargo_edit::{get_latest_dependency, Dependency, Manifest};

mod errors {
    error_chain!{
        links {
            CargoEditLib(::cargo_edit::Error, ::cargo_edit::ErrorKind);
        }

        foreign_links {
            // cargo-metadata doesn't (yet) export `ErrorKind`)
            Metadata(::cargo_metadata::Error);
        }
    }
}
use errors::*;

static USAGE: &'static str = r"
Upgrade all dependencies in a manifest file to the latest version.

Usage:
    cargo upgrade [--all] [--dependency <dep>...] [--manifest-path <path>] [options]
    cargo upgrade (-h | --help)
    cargo upgrade (-V | --version)

Options:
    --all                       Upgrade all packages in the workspace.
    -d --dependency <dep>       Specific dependency to upgrade. If this option is used, only the
                                specified dependencies will be upgraded.
    --manifest-path <path>      Path to the manifest to upgrade.
    --allow-prerelease          Include prerelease versions when fetching from crates.io (e.g.
                                '0.6.0-alpha'). Defaults to false.
    -h --help                   Show this help page.
    -V --version                Show version.

Dev, build, and all target dependencies will also be upgraded. Only dependencies from crates.io are
supported. Git/path dependencies will be ignored.

All packages in the workspace will be upgraded if the `--all` flag is supplied. The `--all` flag may
be supplied in the presence of a virtual manifest.
";

/// Docopts input args.
#[derive(Debug, Deserialize)]
struct Args {
    /// `--dependency -d <dep>`
    flag_dependency: Vec<String>,
    /// `--manifest-path <path>`
    flag_manifest_path: Option<String>,
    /// `--version`
    flag_version: bool,
    /// `--all`
    flag_all: bool,
    /// `--allow-prerelease`
    flag_allow_prerelease: bool,
}

fn is_version_dependency(dep: &toml::Value) -> bool {
    if let Some(table) = dep.as_table() {
        !table.contains_key("git") && !table.contains_key("path")
    } else {
        true
    }
}

fn update_manifest(
    manifest_path: &Option<String>,
    only_update: &[String],
    allow_prerelease: bool,
) -> Result<()> {
    let manifest_path = manifest_path.as_ref().map(From::from);
    let mut manifest = Manifest::open(&manifest_path)?;

    for (table_path, table) in manifest.get_sections() {
        for (name, old_value) in &table {
            if (only_update.is_empty() || only_update.contains(name)) &&
                is_version_dependency(old_value)
            {
                let latest_version = get_latest_dependency(name, allow_prerelease)?;

                manifest.update_table_entry(&table_path, &latest_version)?;
            }
        }
    }

    let mut file = Manifest::find_file(&manifest_path)?;
    manifest
        .write_to_file(&mut file)
        .chain_err(|| "Failed to write new manifest contents")
}

fn update_manifest_from_cache(
    manifest_path: &Option<String>,
    only_update: &[String],
    new_deps: &HashMap<String, Dependency>,
) -> Result<()> {
    let manifest_path = manifest_path.as_ref().map(From::from);
    let mut manifest = Manifest::open(&manifest_path)?;

    for (table_path, table) in manifest.get_sections() {
        for (name, old_value) in &table {
            if (only_update.is_empty() || only_update.contains(name)) &&
                is_version_dependency(old_value)
            {
                let latest_version = &new_deps[name];

                manifest.update_table_entry(&table_path, latest_version)?;
            }
        }
    }

    let mut file = Manifest::find_file(&manifest_path)?;
    manifest
        .write_to_file(&mut file)
        .chain_err(|| "Failed to write new manifest contents")
}

/// Get a list of the paths of all the (non-virtual) manifests in the workspace.
fn get_workspace_manifests(manifest_path: &Option<String>) -> Result<Vec<String>> {
    Ok(
        cargo_metadata::metadata_deps(manifest_path.as_ref().map(|p| Path::new(p)), true)
            .chain_err(|| "Failed to get metadata")?
            .packages
            .iter()
            .map(|p| p.manifest_path.clone())
            .collect(),
    )
}

/// Look up all current direct crates.io dependencies in the workspace. Then get the latest version
/// for each.
fn get_new_workspace_deps(
    manifest_path: &Option<String>,
    only_update: &[String],
    allow_prerelease: bool,
) -> Result<HashMap<String, Dependency>> {
    let mut new_deps = HashMap::new();

    cargo_metadata::metadata_deps(manifest_path.as_ref().map(|p| Path::new(p)), true)
        .chain_err(|| "Failed to get metadata")?
        .packages
        .iter()
        .flat_map(|package| package.dependencies.to_owned())
        .filter(|dependency| {
            only_update.is_empty() || only_update.contains(&dependency.name)
        })
        .map(|dependency| {
            if !new_deps.contains_key(&dependency.name) {
                new_deps.insert(
                    dependency.name.clone(),
                    get_latest_dependency(&dependency.name, allow_prerelease)?,
                );
            }
            Ok(())
        })
        .collect::<Result<Vec<()>>>()?;

    Ok(new_deps)
}

fn update_workspace_manifests(
    manifest_path: &Option<String>,
    only_update: &[String],
    allow_prerelease: bool,
) -> Result<()> {
    let new_deps = get_new_workspace_deps(manifest_path, only_update, allow_prerelease)?;

    get_workspace_manifests(manifest_path).and_then(|manifests| {
        for manifest in manifests {
            update_manifest_from_cache(&Some(manifest), only_update, &new_deps)?
        }

        Ok(())
    })
}

fn main() {
    let args = docopt::Docopt::new(USAGE)
        .and_then(|d| d.deserialize::<Args>())
        .unwrap_or_else(|err| err.exit());

    if args.flag_version {
        println!("cargo-upgrade version {}", env!("CARGO_PKG_VERSION"));
        process::exit(0);
    }

    let output = if args.flag_all {
        update_workspace_manifests(
            &args.flag_manifest_path,
            &args.flag_dependency,
            args.flag_allow_prerelease,
        )
    } else {
        update_manifest(
            &args.flag_manifest_path,
            &args.flag_dependency,
            args.flag_allow_prerelease,
        )
    };

    if let Err(err) = output {
        let mut stderr = io::stderr();

        writeln!(stderr, "Command failed due to unhandled error: {}\n", err).unwrap();

        for e in err.iter().skip(1) {
            writeln!(stderr, "Caused by: {}", e).unwrap();
        }

        if let Some(backtrace) = err.backtrace() {
            writeln!(stderr, "Backtrace: {:?}", backtrace).unwrap();
        }

        process::exit(1);
    }
}
<commit_msg>Commonise manifest upgrading<commit_after>//! `cargo upgrade`
#![warn(missing_docs, missing_debug_implementations, missing_copy_implementations, trivial_casts,
       trivial_numeric_casts, unsafe_code, unstable_features, unused_import_braces,
       unused_qualifications)]

extern crate cargo_metadata;
extern crate docopt;
#[macro_use]
extern crate error_chain;
#[macro_use]
extern crate serde_derive;
extern crate toml;

use std::collections::HashMap;
use std::io::{self, Write};
use std::path::Path;
use std::process;

extern crate cargo_edit;
use cargo_edit::{get_latest_dependency, Dependency, Manifest};

mod errors {
    error_chain!{
        links {
            CargoEditLib(::cargo_edit::Error, ::cargo_edit::ErrorKind);
        }

        foreign_links {
            // cargo-metadata doesn't (yet) export `ErrorKind`)
            Metadata(::cargo_metadata::Error);
        }
    }
}
use errors::*;

static USAGE: &'static str = r"
Upgrade all dependencies in a manifest file to the latest version.

Usage:
    cargo upgrade [--all] [--dependency <dep>...] [--manifest-path <path>] [options]
    cargo upgrade (-h | --help)
    cargo upgrade (-V | --version)

Options:
    --all                       Upgrade all packages in the workspace.
    -d --dependency <dep>       Specific dependency to upgrade. If this option is used, only the
                                specified dependencies will be upgraded.
    --manifest-path <path>      Path to the manifest to upgrade.
    --allow-prerelease          Include prerelease versions when fetching from crates.io (e.g.
                                '0.6.0-alpha'). Defaults to false.
    -h --help                   Show this help page.
    -V --version                Show version.

Dev, build, and all target dependencies will also be upgraded. Only dependencies from crates.io are
supported. Git/path dependencies will be ignored.

All packages in the workspace will be upgraded if the `--all` flag is supplied. The `--all` flag may
be supplied in the presence of a virtual manifest.
";

/// Docopts input args.
#[derive(Debug, Deserialize)]
struct Args {
    /// `--dependency -d <dep>`
    flag_dependency: Vec<String>,
    /// `--manifest-path <path>`
    flag_manifest_path: Option<String>,
    /// `--version`
    flag_version: bool,
    /// `--all`
    flag_all: bool,
    /// `--allow-prerelease`
    flag_allow_prerelease: bool,
}

fn is_version_dependency(dep: &toml::Value) -> bool {
    if let Some(table) = dep.as_table() {
        !table.contains_key("git") && !table.contains_key("path")
    } else {
        true
    }
}

/// Upgrade the specified manifest. Use the closure provided to get the new dependency versions.
fn upgrade_manifest_using_dependencies<F>(
    manifest_path: &Option<String>,
    only_update: &[String],
    new_dependency: F,
) -> Result<()>
where
    F: Fn(&String) -> cargo_edit::Result<Dependency>,
{
    let manifest_path = manifest_path.as_ref().map(From::from);
    let mut manifest = Manifest::open(&manifest_path)?;

    for (table_path, table) in manifest.get_sections() {
        for (name, old_value) in &table {
            if (only_update.is_empty() || only_update.contains(name)) &&
                is_version_dependency(old_value)
            {
                let latest_version = new_dependency(name)?;

                manifest.update_table_entry(&table_path, &latest_version)?;
            }
        }
    }

    let mut file = Manifest::find_file(&manifest_path)?;
    manifest
        .write_to_file(&mut file)
        .chain_err(|| "Failed to write new manifest contents")
}

fn upgrade_manifest(
    manifest_path: &Option<String>,
    only_update: &[String],
    allow_prerelease: bool,
) -> Result<()> {
    upgrade_manifest_using_dependencies(manifest_path, only_update, |name| {
        get_latest_dependency(name, allow_prerelease)
    })
}

fn upgrade_manifest_from_cache(
    manifest_path: &Option<String>,
    only_update: &[String],
    new_deps: &HashMap<String, Dependency>,
) -> Result<()> {
    upgrade_manifest_using_dependencies(
        manifest_path,
        only_update,
        |name| Ok(new_deps[name].clone()),
    )
}

/// Get a list of the paths of all the (non-virtual) manifests in the workspace.
fn get_workspace_manifests(manifest_path: &Option<String>) -> Result<Vec<String>> {
    Ok(
        cargo_metadata::metadata_deps(manifest_path.as_ref().map(|p| Path::new(p)), true)
            .chain_err(|| "Failed to get metadata")?
            .packages
            .iter()
            .map(|p| p.manifest_path.clone())
            .collect(),
    )
}

/// Look up all current direct crates.io dependencies in the workspace. Then get the latest version
/// for each.
fn get_new_workspace_deps(
    manifest_path: &Option<String>,
    only_update: &[String],
    allow_prerelease: bool,
) -> Result<HashMap<String, Dependency>> {
    let mut new_deps = HashMap::new();

    cargo_metadata::metadata_deps(manifest_path.as_ref().map(|p| Path::new(p)), true)
        .chain_err(|| "Failed to get metadata")?
        .packages
        .iter()
        .flat_map(|package| package.dependencies.to_owned())
        .filter(|dependency| {
            only_update.is_empty() || only_update.contains(&dependency.name)
        })
        .map(|dependency| {
            if !new_deps.contains_key(&dependency.name) {
                new_deps.insert(
                    dependency.name.clone(),
                    get_latest_dependency(&dependency.name, allow_prerelease)?,
                );
            }
            Ok(())
        })
        .collect::<Result<Vec<()>>>()?;

    Ok(new_deps)
}

fn upgrade_workspace_manifests(
    manifest_path: &Option<String>,
    only_update: &[String],
    allow_prerelease: bool,
) -> Result<()> {
    let new_deps = get_new_workspace_deps(manifest_path, only_update, allow_prerelease)?;

    get_workspace_manifests(manifest_path).and_then(|manifests| {
        for manifest in manifests {
            upgrade_manifest_from_cache(&Some(manifest), only_update, &new_deps)?
        }

        Ok(())
    })
}

fn main() {
    let args = docopt::Docopt::new(USAGE)
        .and_then(|d| d.deserialize::<Args>())
        .unwrap_or_else(|err| err.exit());

    if args.flag_version {
        println!("cargo-upgrade version {}", env!("CARGO_PKG_VERSION"));
        process::exit(0);
    }

    let output = if args.flag_all {
        upgrade_workspace_manifests(
            &args.flag_manifest_path,
            &args.flag_dependency,
            args.flag_allow_prerelease,
        )
    } else {
        upgrade_manifest(
            &args.flag_manifest_path,
            &args.flag_dependency,
            args.flag_allow_prerelease,
        )
    };

    if let Err(err) = output {
        let mut stderr = io::stderr();

        writeln!(stderr, "Command failed due to unhandled error: {}\n", err).unwrap();

        for e in err.iter().skip(1) {
            writeln!(stderr, "Caused by: {}", e).unwrap();
        }

        if let Some(backtrace) = err.backtrace() {
            writeln!(stderr, "Backtrace: {:?}", backtrace).unwrap();
        }

        process::exit(1);
    }
}
<|endoftext|>


<commit_before>use auto_enums::auto_enum;
use glob::{glob_with, MatchOptions};
use ion_shell::{
    expansion::{unescape, Expander},
    Shell,
};
use liner::{BasicCompleter, Completer, CursorPosition, Event, EventKind};
use std::{env, iter, path::PathBuf, str};

pub struct IonCompleter<'a, 'b> {
    shell:             &'b Shell<'a>,
    history_completer: Option<BasicCompleter>,
}

/// Escapes filenames from the completer so that special characters will be properly escaped.
///
/// NOTE: Perhaps we should submit a PR to Liner to add a &'static [u8] field to
/// `FilenameCompleter` so that we don't have to perform the escaping ourselves?
fn escape(input: &str) -> String {
    let mut output = Vec::with_capacity(input.len());
    for character in input.bytes() {
        match character {
            b'(' | b')' | b'[' | b']' | b'&' | b'$' | b'@' | b'{' | b'}' | b'<' | b'>' | b';'
            | b'"' | b'\'' | b'#' | b'^' | b'*' | b' ' => output.push(b'\\'),
            _ => (),
        }
        output.push(character);
    }
    unsafe { String::from_utf8_unchecked(output) }
}

impl<'a, 'b> IonCompleter<'a, 'b> {
    pub fn new(shell: &'b Shell<'a>) -> Self { IonCompleter { shell, history_completer: None } }
}

impl<'a, 'b> Completer for IonCompleter<'a, 'b> {
    fn completions(&mut self, start: &str) -> Vec<String> {
        let mut completions = IonFileCompleter::new(None, &self.shell).completions(start);

        if let Some(ref mut history) = &mut self.history_completer {
            let vars = self.shell.variables();

            completions.extend(history.completions(start));
            // Initialize a new completer from the definitions collected.
            // Creates a list of definitions from the shell environment that
            // will be used
            // in the creation of a custom completer.
            completions.extend(
                self.shell
                    .builtins()
                    .keys()
                    // Add built-in commands to the completer's definitions.
                    .map(ToString::to_string)
                    // Add the aliases to the completer's definitions.
                    .chain(vars.aliases().map(|(key, _)| key.to_string()))
                    // Add the list of available functions to the completer's
                    // definitions.
                    .chain(vars.functions().map(|(key, _)| key.to_string()))
                    // Add the list of available variables to the completer's
                    // definitions. TODO: We should make
                    // it free to do String->SmallString
                    //       and mostly free to go back (free if allocated)
                    .chain(vars.string_vars().map(|(s, _)| ["$", &s].concat()))
                    .filter(|s| s.starts_with(start)),
            );
            // Creates completers containing definitions from all directories
            // listed
            // in the environment's **$PATH** variable.
            let file_completers: Vec<_> = if let Some(paths) = env::var_os("PATH") {
                env::split_paths(&paths)
                    .map(|s| IonFileCompleter::new(Some(s), &self.shell))
                    .collect()
            } else {
                vec![IonFileCompleter::new(Some("/bin/".into()), &self.shell)]
            };
            // Merge the collected definitions with the file path definitions.
            completions.extend(MultiCompleter::new(file_completers).completions(start));
        }
        completions
    }

    fn on_event<W: std::io::Write>(&mut self, event: Event<'_, '_, W>) {
        if let EventKind::BeforeComplete = event.kind {
            let (words, pos) = event.editor.get_words_and_cursor_position();

            let filename = match pos {
                CursorPosition::InWord(index) => index > 0,
                CursorPosition::InSpace(Some(_), _) => true,
                CursorPosition::InSpace(None, _) => false,
                CursorPosition::OnWordLeftEdge(index) => index >= 1,
                CursorPosition::OnWordRightEdge(index) => words
                    .into_iter()
                    .nth(index)
                    .map(|(start, end)| event.editor.current_buffer().range(start, end))
                    .and_then(|filename| {
                        Some(complete_as_file(&env::current_dir().ok()?, &filename, index))
                    })
                    .filter(|&x| x)
                    .is_some(),
            };

            // Add the history list to the completer's definitions.
            // Map each underlying `liner::Buffer` into a `String`.
            self.history_completer = if filename {
                Some(BasicCompleter::new(
                    event
                        .editor
                        .context()
                        .history
                        .buffers
                        .iter()
                        .map(ToString::to_string)
                        .collect(),
                ))
            } else {
                None
            };
        }
    }
}

/// Performs escaping to an inner `FilenameCompleter` to enable a handful of special cases
/// needed by the shell, such as expanding '~' to a home directory, or adding a backslash
/// when a special character is contained within an expanded filename.
pub struct IonFileCompleter<'a, 'b> {
    shell: &'b Shell<'a>,
    /// The directory the expansion takes place in
    path: PathBuf,
}

impl<'a, 'b> IonFileCompleter<'a, 'b> {
    pub fn new(path: Option<PathBuf>, shell: &'b Shell<'a>) -> Self {
        let path = path.unwrap_or_default();
        IonFileCompleter { shell, path }
    }
}

impl<'a, 'b> Completer for IonFileCompleter<'a, 'b> {
    /// When the tab key is pressed, **Liner** will use this method to perform completions of
    /// filenames. As our `IonFileCompleter` is a wrapper around **Liner**'s
    /// `FilenameCompleter`,
    /// the purpose of our custom `Completer` is to expand possible `~` characters in the
    /// `start`
    /// value that we receive from the prompt, grab completions from the inner
    /// `FilenameCompleter`,
    /// and then escape the resulting filenames, as well as remove the expanded form of the `~`
    /// character and re-add the `~` character in it's place.
    fn completions(&mut self, start: &str) -> Vec<String> {
        // Dereferencing the raw pointers here should be entirely safe, theoretically,
        // because no changes will occur to either of the underlying references in the
        // duration between creation of the completers and execution of their
        // completions.
        let expanded = match self.shell.tilde(start) {
            Ok(expanded) => expanded,
            Err(why) => {
                eprintln!("ion: {}", why);
                return vec![start.into()];
            }
        };
        // Now we obtain completions for the `expanded` form of the `start` value.
        let completions = filename_completion(&expanded, &self.path);
        if expanded == start {
            return completions.collect();
        }
        // We can do that by obtaining the index position where the tilde character
        // ends. We don't search with `~` because we also want to
        // handle other tilde variants.
        let t_index = start.find('/').unwrap_or(1);
        // `tilde` is the tilde pattern, and `search` is the pattern that follows.
        let (tilde, search) = start.split_at(t_index);

        if search.len() < 2 {
            // If the length of the search pattern is less than 2, the search pattern is
            // empty, and thus the completions actually contain files and directories in
            // the home directory.

            // The tilde pattern will actually be our `start` command in itself,
            // and the completed form will be all of the characters beyond the length of
            // the expanded form of the tilde pattern.
            completions.map(|completion| [start, &completion[expanded.len()..]].concat()).collect()
        // To save processing time, we should get obtain the index position where our
        // search pattern begins, and re-use that index to slice the completions so
        // that we may re-add the tilde character with the completion that follows.
        } else if let Some(e_index) = expanded.rfind(search) {
            // And then we will need to take those completions and remove the expanded form
            // of the tilde pattern and replace it with that pattern yet again.
            completions
                .map(|completion| escape(&[tilde, &completion[e_index..]].concat()))
                .collect()
        } else {
            Vec::new()
        }
    }
}

#[auto_enum]
fn filename_completion<'a>(start: &'a str, path: &'a PathBuf) -> impl Iterator<Item = String> + 'a {
    let unescaped_start = unescape(start);

    let mut split_start = unescaped_start.split('/');
    let mut string = String::with_capacity(128);

    // When 'start' is an absolute path, "/..." gets split to ["", "..."]
    // So we skip the first element and add "/" to the start of the string
    if unescaped_start.starts_with('/') {
        split_start.next();
        string.push('/');
    } else {
        string.push_str(&path.to_string_lossy());
    }

    for element in split_start {
        string.push_str(element);
        if element != "." && element != ".." {
            string.push('*');
        }
        string.push('/');
    }

    string.pop(); // pop out the last '/' character
    if string.ends_with('.') {
        string.push('*')
    }

    let globs = glob_with(
        &string,
        MatchOptions {
            case_sensitive:              true,
            require_literal_separator:   true,
            require_literal_leading_dot: false,
        },
    )
    .ok()
    .map(|completions| {
        completions.filter_map(Result::ok).filter_map(move |file| {
            let out = file.to_str()?;
            let mut joined = String::with_capacity(out.len() + 3); // worst case senario
            if unescaped_start.starts_with("./") {
                joined.push_str("./");
            }
            joined.push_str(out);
            if file.is_dir() {
                joined.push('/');
            }
            Some(escape(&joined))
        })
    });

    #[auto_enum(Iterator)]
    match globs {
        Some(iter) => iter,
        None => iter::once(escape(start)),
    }
}

/// A completer that combines suggestions from multiple completers.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MultiCompleter<A>(Vec<A>);

impl<A> MultiCompleter<A> {
    pub fn new(completions: Vec<A>) -> Self { MultiCompleter(completions) }
}

impl<A> Completer for MultiCompleter<A>
where
    A: Completer,
{
    fn completions(&mut self, start: &str) -> Vec<String> {
        self.0.iter_mut().flat_map(|comp| comp.completions(start)).collect()
    }
}

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

    #[test]
    fn filename_completion() {
        let shell = Shell::default();
        let mut completer = IonFileCompleter::new(None, &shell);
        assert_eq!(completer.completions("testing"), vec!["testing/"]);
        assert_eq!(completer.completions("testing/file"), vec!["testing/file_with_text"]);
        assert_eq!(completer.completions("~"), vec!["~/"]);
        assert_eq!(completer.completions("tes/fil"), vec!["testing/file_with_text"]);
    }
}

/// Infer if the given filename is actually a partial filename
fn complete_as_file(current_dir: &PathBuf, filename: &str, index: usize) -> bool {
    let filename = filename.trim();
    let mut file = current_dir.clone();
    file.push(&filename);
    // If the user explicitly requests a file through this syntax then complete as
    // a file
    filename.starts_with('.') ||
    // If the file starts with a dollar sign, it's a variable, not a file
    (!filename.starts_with('$') &&
    // Once we are beyond the first string, assume its a file
    (index > 0 ||
    // If we are referencing a file that exists then just complete to that file
    file.exists() ||
    // If we have a partial file inside an existing directory, e.g. /foo/b when
    // /foo/bar exists, then treat it as file as long as `foo` isn't the
    // current directory, otherwise this would apply to any string `foo`
    file.parent().filter(|parent| parent.exists() && parent != current_dir).is_some()))
    // By default assume its not a file
}
<commit_msg>Attempt to fix issues with command completions.<commit_after>use auto_enums::auto_enum;
use glob::{glob_with, MatchOptions};
use ion_shell::{
    expansion::{unescape, Expander},
    Shell,
};
use liner::{Completer, CursorPosition, Event, EventKind};
use std::{env, iter, path::PathBuf, str};

pub struct IonCompleter<'a, 'b> {
    shell:             &'b Shell<'a>,
    complete_commands: bool,
}

/// Escapes filenames from the completer so that special characters will be properly escaped.
///
/// NOTE: Perhaps we should submit a PR to Liner to add a &'static [u8] field to
/// `FilenameCompleter` so that we don't have to perform the escaping ourselves?
fn escape(input: &str) -> String {
    let mut output = Vec::with_capacity(input.len());
    for character in input.bytes() {
        match character {
            b'(' | b')' | b'[' | b']' | b'&' | b'$' | b'@' | b'{' | b'}' | b'<' | b'>' | b';'
            | b'"' | b'\'' | b'#' | b'^' | b'*' | b' ' => output.push(b'\\'),
            _ => (),
        }
        output.push(character);
    }
    unsafe { String::from_utf8_unchecked(output) }
}

impl<'a, 'b> IonCompleter<'a, 'b> {
    pub fn new(shell: &'b Shell<'a>) -> Self { IonCompleter { shell, complete_commands: false } }
}

impl<'a, 'b> Completer for IonCompleter<'a, 'b> {
    fn completions(&mut self, start: &str) -> Vec<String> {
        let mut completions = IonFileCompleter::new(None, &self.shell, false).completions(start);

        if self.complete_commands {
            let vars = self.shell.variables();
            // Initialize a new completer from the definitions collected.
            // Creates a list of definitions from the shell environment that
            // will be used
            // in the creation of a custom completer.
            completions.extend(
                self.shell
                    .builtins()
                    .keys()
                    // Add built-in commands to the completer's definitions.
                    .map(ToString::to_string)
                    // Add the aliases to the completer's definitions.
                    .chain(vars.aliases().map(|(key, _)| key.to_string()))
                    // Add the list of available functions to the completer's
                    // definitions.
                    .chain(vars.functions().map(|(key, _)| key.to_string()))
                    // Add the list of available variables to the completer's
                    // definitions. TODO: We should make
                    // it free to do String->SmallString
                    //       and mostly free to go back (free if allocated)
                    .chain(vars.string_vars().map(|(s, _)| ["$", &s].concat()))
                    .filter(|s| s.starts_with(start)),
            );
            // Creates completers containing definitions from all directories
            // listed
            // in the environment's **$PATH** variable.
            let file_completers: Vec<_> = if let Some(paths) = env::var_os("PATH") {
                env::split_paths(&paths)
                    .map(|s| {
                        let s = if !s.to_string_lossy().ends_with("/") {
                            PathBuf::from(format!("{}/", s.to_string_lossy()))
                        } else {
                            s
                        };
                        IonFileCompleter::new(Some(s), &self.shell, true)
                    })
                    .collect()
            } else {
                vec![IonFileCompleter::new(Some("/bin/".into()), &self.shell, true)]
            };
            // Merge the collected definitions with the file path definitions.
            completions.extend(MultiCompleter::new(file_completers).completions(start));
        }

        completions
    }

    fn on_event<W: std::io::Write>(&mut self, event: Event<'_, '_, W>) {
        if let EventKind::BeforeComplete = event.kind {
            let (words, pos) = event.editor.get_words_and_cursor_position();

            self.complete_commands = match pos {
                CursorPosition::InWord(_) => false,
                CursorPosition::InSpace(Some(_), _) => false,
                CursorPosition::InSpace(None, _) => false,
                CursorPosition::OnWordLeftEdge(_) => false,
                CursorPosition::OnWordRightEdge(index) => if index == 0 {
                    true
                } else {
                    words
                    .into_iter()
                    .nth(index - 1)
                    .map(|(start, end)| event.editor.current_buffer().range(start, end))
                    .filter(|filename| filename.ends_with("|"))
                    .is_some()
                },
            };
       }
    }
}

/// Performs escaping to an inner `FilenameCompleter` to enable a handful of special cases
/// needed by the shell, such as expanding '~' to a home directory, or adding a backslash
/// when a special character is contained within an expanded filename.
pub struct IonFileCompleter<'a, 'b> {
    shell: &'b Shell<'a>,
    /// The directory the expansion takes place in
    path: PathBuf,
    for_command: bool,
}

impl<'a, 'b> IonFileCompleter<'a, 'b> {
    pub fn new(path: Option<PathBuf>, shell: &'b Shell<'a>, for_command: bool) -> Self {
        let path = path.unwrap_or_default();
        IonFileCompleter { shell, path, for_command }
    }
}

impl<'a, 'b> Completer for IonFileCompleter<'a, 'b> {
    /// When the tab key is pressed, **Liner** will use this method to perform completions of
    /// filenames. As our `IonFileCompleter` is a wrapper around **Liner**'s
    /// `FilenameCompleter`,
    /// the purpose of our custom `Completer` is to expand possible `~` characters in the
    /// `start`
    /// value that we receive from the prompt, grab completions from the inner
    /// `FilenameCompleter`,
    /// and then escape the resulting filenames, as well as remove the expanded form of the `~`
    /// character and re-add the `~` character in it's place.
    fn completions(&mut self, start: &str) -> Vec<String> {
        // Dereferencing the raw pointers here should be entirely safe, theoretically,
        // because no changes will occur to either of the underlying references in the
        // duration between creation of the completers and execution of their
        // completions.
        let expanded = match self.shell.tilde(start) {
            Ok(expanded) => expanded,
            Err(why) => {
                eprintln!("ion: {}", why);
                return vec![start.into()];
            }
        };
        // Now we obtain completions for the `expanded` form of the `start` value.
        let completions = filename_completion(&expanded, &self.path);
        if expanded == start {
            //return completions.map(|s| s.rsplit('/').nth(0).unwrap_or(&s).to_string()).collect();
            return if self.for_command {
                completions.map(|s| s.rsplit('/').nth(0).unwrap_or(&s).to_string()).collect()
            } else {
                completions.collect()
            };
        }
        // We can do that by obtaining the index position where the tilde character
        // ends. We don't search with `~` because we also want to
        // handle other tilde variants.
        let t_index = start.find('/').unwrap_or(1);
        // `tilde` is the tilde pattern, and `search` is the pattern that follows.
        let (tilde, search) = start.split_at(t_index);

        if search.len() < 2 {
            // If the length of the search pattern is less than 2, the search pattern is
            // empty, and thus the completions actually contain files and directories in
            // the home directory.

            // The tilde pattern will actually be our `start` command in itself,
            // and the completed form will be all of the characters beyond the length of
            // the expanded form of the tilde pattern.
            completions.map(|completion| [start, &completion[expanded.len()..]].concat()).collect()
        // To save processing time, we should get obtain the index position where our
        // search pattern begins, and re-use that index to slice the completions so
        // that we may re-add the tilde character with the completion that follows.
        } else if let Some(e_index) = expanded.rfind(search) {
            // And then we will need to take those completions and remove the expanded form
            // of the tilde pattern and replace it with that pattern yet again.
            completions
                .map(|completion| escape(&[tilde, &completion[e_index..]].concat()))
                .collect()
        } else {
            Vec::new()
        }
    }
}

#[auto_enum]
fn filename_completion<'a>(start: &'a str, path: &'a PathBuf) -> impl Iterator<Item = String> + 'a {
    let unescaped_start = unescape(start);

    let mut split_start = unescaped_start.split('/');
    let mut string = String::with_capacity(128);

    // When 'start' is an absolute path, "/..." gets split to ["", "..."]
    // So we skip the first element and add "/" to the start of the string
    if unescaped_start.starts_with('/') {
        split_start.next();
        string.push('/');
    } else {
        string.push_str(&path.to_string_lossy());
    }

    for element in split_start {
        string.push_str(element);
        if element != "." && element != ".." {
            string.push('*');
        }
        string.push('/');
    }

    string.pop(); // pop out the last '/' character
    if string.ends_with('.') {
        string.push('*')
    }
    let globs = glob_with(
        &string,
        MatchOptions {
            case_sensitive:              false, //true,
            require_literal_separator:   true,
            require_literal_leading_dot: false,
        },
    )
    .ok()
    .map(|completions| {
        completions.filter_map(Result::ok).filter_map(move |file| {
            let out = file.to_str()?;
            let mut joined = String::with_capacity(out.len() + 3); // worst case senario
            if unescaped_start.starts_with("./") {
                joined.push_str("./");
            }
            joined.push_str(out);
            if file.is_dir() {
                joined.push('/');
            }
            Some(escape(&joined))
        })
    });

    #[auto_enum(Iterator)]
    match globs {
        Some(iter) => iter,
        None => iter::once(escape(start)),
    }
}

/// A completer that combines suggestions from multiple completers.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MultiCompleter<A>(Vec<A>);

impl<A> MultiCompleter<A> {
    pub fn new(completions: Vec<A>) -> Self { MultiCompleter(completions) }
}

impl<A> Completer for MultiCompleter<A>
where
    A: Completer,
{
    fn completions(&mut self, start: &str) -> Vec<String> {
        self.0.iter_mut().flat_map(|comp| comp.completions(start)).collect()
    }
}

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

    #[test]
    fn filename_completion() {
        let shell = Shell::default();
        let mut completer = IonFileCompleter::new(None, &shell);
        assert_eq!(completer.completions("testing"), vec!["testing/"]);
        assert_eq!(completer.completions("testing/file"), vec!["testing/file_with_text"]);
        assert_eq!(completer.completions("~"), vec!["~/"]);
        assert_eq!(completer.completions("tes/fil"), vec!["testing/file_with_text"]);
    }
}<|endoftext|>