<commit_before>use crate::enter;
use crate::unpark_mutex::UnparkMutex;
use futures_core::future::Future;
use futures_core::task::{Context, Poll};
use futures_task::{FutureObj, Spawn, SpawnError};
use futures_task::{ArcWake, waker_ref};
use futures_util::future::FutureExt;
use std::cmp;
use std::fmt;
use std::io;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::mpsc;
use std::sync::{Arc, Mutex};
use std::thread;

/// A general-purpose thread pool for scheduling tasks that poll futures to
/// completion.
///
/// The thread pool multiplexes any number of tasks onto a fixed number of
/// worker threads.
///
/// This type is a clonable handle to the threadpool itself.
/// Cloning it will only create a new reference, not a new threadpool.
///
/// This type is only available when the `thread-pool` feature of this
/// library is activated.
pub struct ThreadPool {
    state: Arc<PoolState>,
}

/// Thread pool configuration object.
///
/// This type is only available when the `thread-pool` feature of this
/// library is activated.
pub struct ThreadPoolBuilder {
    pool_size: usize,
    stack_size: usize,
    name_prefix: Option<String>,
    after_start: Option<Arc<dyn Fn(usize) + Send + Sync>>,
    before_stop: Option<Arc<dyn Fn(usize) + Send + Sync>>,
}

trait AssertSendSync: Send + Sync {}
impl AssertSendSync for ThreadPool {}

struct PoolState {
    tx: Mutex<mpsc::Sender<Message>>,
    rx: Mutex<mpsc::Receiver<Message>>,
    cnt: AtomicUsize,
    size: usize,
}

impl fmt::Debug for ThreadPool {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ThreadPool")
            .field("size", &self.state.size)
            .finish()
    }
}

impl fmt::Debug for ThreadPoolBuilder {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ThreadPoolBuilder")
            .field("pool_size", &self.pool_size)
            .field("name_prefix", &self.name_prefix)
            .finish()
    }
}

enum Message {
    Run(Task),
    Close,
}

impl ThreadPool {
    /// Creates a new thread pool with the default configuration.
    ///
    /// See documentation for the methods in
    /// [`ThreadPoolBuilder`](ThreadPoolBuilder) for details on the default
    /// configuration.
    pub fn new() -> Result<ThreadPool, io::Error> {
        ThreadPoolBuilder::new().create()
    }

    /// Create a default thread pool configuration, which can then be customized.
    ///
    /// See documentation for the methods in
    /// [`ThreadPoolBuilder`](ThreadPoolBuilder) for details on the default
    /// configuration.
    pub fn builder() -> ThreadPoolBuilder {
        ThreadPoolBuilder::new()
    }

    /// Spawns a future that will be run to completion.
    ///
    /// > **Note**: This method is similar to `Spawn::spawn_obj`, except that
    /// >           it is guaranteed to always succeed.
    pub fn spawn_obj_ok(&self, future: FutureObj<'static, ()>) {
        let task = Task {
            future,
            wake_handle: Arc::new(WakeHandle {
                exec: self.clone(),
                mutex: UnparkMutex::new(),
            }),
            exec: self.clone(),
        };
        self.state.send(Message::Run(task));
    }

    /// Spawns a task that polls the given future with output `()` to
    /// completion.
    ///
    /// ```
    /// use futures::executor::ThreadPool;
    ///
    /// let pool = ThreadPool::new().unwrap();
    ///
    /// let future = async { /* ... */ };
    /// pool.spawn_ok(future);
    /// ```
    ///
    /// > **Note**: This method is similar to `SpawnExt::spawn`, except that
    /// >           it is guaranteed to always succeed.
    pub fn spawn_ok<Fut>(&self, future: Fut)
    where
        Fut: Future<Output = ()> + Send + 'static,
    {
        self.spawn_obj_ok(FutureObj::new(Box::new(future)))
    }
}

impl Spawn for ThreadPool {
    fn spawn_obj(
        &self,
        future: FutureObj<'static, ()>,
    ) -> Result<(), SpawnError> {
        self.spawn_obj_ok(future);
        Ok(())
    }
}

impl PoolState {
    fn send(&self, msg: Message) {
        self.tx.lock().unwrap().send(msg).unwrap();
    }

    fn work(&self,
            idx: usize,
            after_start: Option<Arc<dyn Fn(usize) + Send + Sync>>,
            before_stop: Option<Arc<dyn Fn(usize) + Send + Sync>>) {
        let _scope = enter().unwrap();
        if let Some(after_start) = after_start {
            after_start(idx);
        }
        loop {
            let msg = self.rx.lock().unwrap().recv().unwrap();
            match msg {
                Message::Run(task) => task.run(),
                Message::Close => break,
            }
        }
        if let Some(before_stop) = before_stop {
            before_stop(idx);
        }
    }
}

impl Clone for ThreadPool {
    fn clone(&self) -> ThreadPool {
        self.state.cnt.fetch_add(1, Ordering::Relaxed);
        ThreadPool { state: self.state.clone() }
    }
}

impl Drop for ThreadPool {
    fn drop(&mut self) {
        if self.state.cnt.fetch_sub(1, Ordering::Relaxed) == 1 {
            for _ in 0..self.state.size {
                self.state.send(Message::Close);
            }
        }
    }
}

impl ThreadPoolBuilder {
    /// Create a default thread pool configuration.
    ///
    /// See the other methods on this type for details on the defaults.
    pub fn new() -> Self {
        Self {
            pool_size: cmp::max(1, num_cpus::get()),
            stack_size: 0,
            name_prefix: None,
            after_start: None,
            before_stop: None,
        }
    }

    /// Set size of a future ThreadPool
    ///
    /// The size of a thread pool is the number of worker threads spawned. By
    /// default, this is equal to the number of CPU cores.
    ///
    /// # Panics
    ///
    /// Panics if `pool_size == 0`.
    pub fn pool_size(&mut self, size: usize) -> &mut Self {
        assert!(size > 0);
        self.pool_size = size;
        self
    }

    /// Set stack size of threads in the pool.
    ///
    /// By default, worker threads use Rust's standard stack size.
    pub fn stack_size(&mut self, stack_size: usize) -> &mut Self {
        self.stack_size = stack_size;
        self
    }

    /// Set thread name prefix of a future ThreadPool.
    ///
    /// Thread name prefix is used for generating thread names. For example, if prefix is
    /// `my-pool-`, then threads in the pool will get names like `my-pool-1` etc.
    ///
    /// By default, worker threads are assigned Rust's standard thread name.
    pub fn name_prefix<S: Into<String>>(&mut self, name_prefix: S) -> &mut Self {
        self.name_prefix = Some(name_prefix.into());
        self
    }

    /// Execute the closure `f` immediately after each worker thread is started,
    /// but before running any tasks on it.
    ///
    /// This hook is intended for bookkeeping and monitoring.
    /// The closure `f` will be dropped after the `builder` is dropped
    /// and all worker threads in the pool have executed it.
    ///
    /// The closure provided will receive an index corresponding to the worker
    /// thread it's running on.
    pub fn after_start<F>(&mut self, f: F) -> &mut Self
        where F: Fn(usize) + Send + Sync + 'static
    {
        self.after_start = Some(Arc::new(f));
        self
    }

    /// Execute closure `f` just prior to shutting down each worker thread.
    ///
    /// This hook is intended for bookkeeping and monitoring.
    /// The closure `f` will be dropped after the `builder` is droppped
    /// and all threads in the pool have executed it.
    ///
    /// The closure provided will receive an index corresponding to the worker
    /// thread it's running on.
    pub fn before_stop<F>(&mut self, f: F) -> &mut Self
        where F: Fn(usize) + Send + Sync + 'static
    {
        self.before_stop = Some(Arc::new(f));
        self
    }

    /// Create a [`ThreadPool`](ThreadPool) with the given configuration.
    pub fn create(&mut self) -> Result<ThreadPool, io::Error> {
        let (tx, rx) = mpsc::channel();
        let pool = ThreadPool {
            state: Arc::new(PoolState {
                tx: Mutex::new(tx),
                rx: Mutex::new(rx),
                cnt: AtomicUsize::new(1),
                size: self.pool_size,
            }),
        };

        for counter in 0..self.pool_size {
            let state = pool.state.clone();
            let after_start = self.after_start.clone();
            let before_stop = self.before_stop.clone();
            let mut thread_builder = thread::Builder::new();
            if let Some(ref name_prefix) = self.name_prefix {
                thread_builder = thread_builder.name(format!("{}{}", name_prefix, counter));
            }
            if self.stack_size > 0 {
                thread_builder = thread_builder.stack_size(self.stack_size);
            }
            thread_builder.spawn(move || state.work(counter, after_start, before_stop))?;
        }
        Ok(pool)
    }
}

impl Default for ThreadPoolBuilder {
    fn default() -> Self {
        Self::new()
    }
}

/// A task responsible for polling a future to completion.
struct Task {
    future: FutureObj<'static, ()>,
    exec: ThreadPool,
    wake_handle: Arc<WakeHandle>,
}

struct WakeHandle {
    mutex: UnparkMutex<Task>,
    exec: ThreadPool,
}

impl Task {
    /// Actually run the task (invoking `poll` on the future) on the current
    /// thread.
    fn run(self) {
        let Task { mut future, wake_handle, mut exec } = self;
        let waker = waker_ref(&wake_handle);
        let mut cx = Context::from_waker(&waker);

        // Safety: The ownership of this `Task` object is evidence that
        // we are in the `POLLING`/`REPOLL` state for the mutex.
        unsafe {
            wake_handle.mutex.start_poll();

            loop {
                let res = future.poll_unpin(&mut cx);
                match res {
                    Poll::Pending => {}
                    Poll::Ready(()) => return wake_handle.mutex.complete(),
                }
                let task = Task {
                    future,
                    wake_handle: wake_handle.clone(),
                    exec,
                };
                match wake_handle.mutex.wait(task) {
                    Ok(()) => return, // we've waited
                    Err(task) => { // someone's notified us
                        future = task.future;
                        exec = task.exec;
                    }
                }
            }
        }
    }
}

impl fmt::Debug for Task {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Task")
            .field("contents", &"...")
            .finish()
    }
}

impl ArcWake for WakeHandle {
    fn wake_by_ref(arc_self: &Arc<Self>) {
        match arc_self.mutex.notify() {
            Ok(task) => arc_self.exec.state.send(Message::Run(task)),
            Err(()) => {}
        }
    }
}

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

    #[test]
    fn test_drop_after_start() {
        let (tx, rx) = mpsc::sync_channel(2);
        let _cpu_pool = ThreadPoolBuilder::new()
            .pool_size(2)
            .after_start(move |_| tx.send(1).unwrap()).create().unwrap();

        // After ThreadPoolBuilder is deconstructed, the tx should be droped
        // so that we can use rx as an iterator.
        let count = rx.into_iter().count();
        assert_eq!(count, 2);
    }
}
<commit_msg>Specify units for stack_size<commit_after>use crate::enter;
use crate::unpark_mutex::UnparkMutex;
use futures_core::future::Future;
use futures_core::task::{Context, Poll};
use futures_task::{FutureObj, Spawn, SpawnError};
use futures_task::{ArcWake, waker_ref};
use futures_util::future::FutureExt;
use std::cmp;
use std::fmt;
use std::io;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::mpsc;
use std::sync::{Arc, Mutex};
use std::thread;

/// A general-purpose thread pool for scheduling tasks that poll futures to
/// completion.
///
/// The thread pool multiplexes any number of tasks onto a fixed number of
/// worker threads.
///
/// This type is a clonable handle to the threadpool itself.
/// Cloning it will only create a new reference, not a new threadpool.
///
/// This type is only available when the `thread-pool` feature of this
/// library is activated.
pub struct ThreadPool {
    state: Arc<PoolState>,
}

/// Thread pool configuration object.
///
/// This type is only available when the `thread-pool` feature of this
/// library is activated.
pub struct ThreadPoolBuilder {
    pool_size: usize,
    stack_size: usize,
    name_prefix: Option<String>,
    after_start: Option<Arc<dyn Fn(usize) + Send + Sync>>,
    before_stop: Option<Arc<dyn Fn(usize) + Send + Sync>>,
}

trait AssertSendSync: Send + Sync {}
impl AssertSendSync for ThreadPool {}

struct PoolState {
    tx: Mutex<mpsc::Sender<Message>>,
    rx: Mutex<mpsc::Receiver<Message>>,
    cnt: AtomicUsize,
    size: usize,
}

impl fmt::Debug for ThreadPool {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ThreadPool")
            .field("size", &self.state.size)
            .finish()
    }
}

impl fmt::Debug for ThreadPoolBuilder {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ThreadPoolBuilder")
            .field("pool_size", &self.pool_size)
            .field("name_prefix", &self.name_prefix)
            .finish()
    }
}

enum Message {
    Run(Task),
    Close,
}

impl ThreadPool {
    /// Creates a new thread pool with the default configuration.
    ///
    /// See documentation for the methods in
    /// [`ThreadPoolBuilder`](ThreadPoolBuilder) for details on the default
    /// configuration.
    pub fn new() -> Result<ThreadPool, io::Error> {
        ThreadPoolBuilder::new().create()
    }

    /// Create a default thread pool configuration, which can then be customized.
    ///
    /// See documentation for the methods in
    /// [`ThreadPoolBuilder`](ThreadPoolBuilder) for details on the default
    /// configuration.
    pub fn builder() -> ThreadPoolBuilder {
        ThreadPoolBuilder::new()
    }

    /// Spawns a future that will be run to completion.
    ///
    /// > **Note**: This method is similar to `Spawn::spawn_obj`, except that
    /// >           it is guaranteed to always succeed.
    pub fn spawn_obj_ok(&self, future: FutureObj<'static, ()>) {
        let task = Task {
            future,
            wake_handle: Arc::new(WakeHandle {
                exec: self.clone(),
                mutex: UnparkMutex::new(),
            }),
            exec: self.clone(),
        };
        self.state.send(Message::Run(task));
    }

    /// Spawns a task that polls the given future with output `()` to
    /// completion.
    ///
    /// ```
    /// use futures::executor::ThreadPool;
    ///
    /// let pool = ThreadPool::new().unwrap();
    ///
    /// let future = async { /* ... */ };
    /// pool.spawn_ok(future);
    /// ```
    ///
    /// > **Note**: This method is similar to `SpawnExt::spawn`, except that
    /// >           it is guaranteed to always succeed.
    pub fn spawn_ok<Fut>(&self, future: Fut)
    where
        Fut: Future<Output = ()> + Send + 'static,
    {
        self.spawn_obj_ok(FutureObj::new(Box::new(future)))
    }
}

impl Spawn for ThreadPool {
    fn spawn_obj(
        &self,
        future: FutureObj<'static, ()>,
    ) -> Result<(), SpawnError> {
        self.spawn_obj_ok(future);
        Ok(())
    }
}

impl PoolState {
    fn send(&self, msg: Message) {
        self.tx.lock().unwrap().send(msg).unwrap();
    }

    fn work(&self,
            idx: usize,
            after_start: Option<Arc<dyn Fn(usize) + Send + Sync>>,
            before_stop: Option<Arc<dyn Fn(usize) + Send + Sync>>) {
        let _scope = enter().unwrap();
        if let Some(after_start) = after_start {
            after_start(idx);
        }
        loop {
            let msg = self.rx.lock().unwrap().recv().unwrap();
            match msg {
                Message::Run(task) => task.run(),
                Message::Close => break,
            }
        }
        if let Some(before_stop) = before_stop {
            before_stop(idx);
        }
    }
}

impl Clone for ThreadPool {
    fn clone(&self) -> ThreadPool {
        self.state.cnt.fetch_add(1, Ordering::Relaxed);
        ThreadPool { state: self.state.clone() }
    }
}

impl Drop for ThreadPool {
    fn drop(&mut self) {
        if self.state.cnt.fetch_sub(1, Ordering::Relaxed) == 1 {
            for _ in 0..self.state.size {
                self.state.send(Message::Close);
            }
        }
    }
}

impl ThreadPoolBuilder {
    /// Create a default thread pool configuration.
    ///
    /// See the other methods on this type for details on the defaults.
    pub fn new() -> Self {
        Self {
            pool_size: cmp::max(1, num_cpus::get()),
            stack_size: 0,
            name_prefix: None,
            after_start: None,
            before_stop: None,
        }
    }

    /// Set size of a future ThreadPool
    ///
    /// The size of a thread pool is the number of worker threads spawned. By
    /// default, this is equal to the number of CPU cores.
    ///
    /// # Panics
    ///
    /// Panics if `pool_size == 0`.
    pub fn pool_size(&mut self, size: usize) -> &mut Self {
        assert!(size > 0);
        self.pool_size = size;
        self
    }

    /// Set stack size of threads in the pool, in bytes.
    ///
    /// By default, worker threads use Rust's standard stack size.
    pub fn stack_size(&mut self, stack_size: usize) -> &mut Self {
        self.stack_size = stack_size;
        self
    }

    /// Set thread name prefix of a future ThreadPool.
    ///
    /// Thread name prefix is used for generating thread names. For example, if prefix is
    /// `my-pool-`, then threads in the pool will get names like `my-pool-1` etc.
    ///
    /// By default, worker threads are assigned Rust's standard thread name.
    pub fn name_prefix<S: Into<String>>(&mut self, name_prefix: S) -> &mut Self {
        self.name_prefix = Some(name_prefix.into());
        self
    }

    /// Execute the closure `f` immediately after each worker thread is started,
    /// but before running any tasks on it.
    ///
    /// This hook is intended for bookkeeping and monitoring.
    /// The closure `f` will be dropped after the `builder` is dropped
    /// and all worker threads in the pool have executed it.
    ///
    /// The closure provided will receive an index corresponding to the worker
    /// thread it's running on.
    pub fn after_start<F>(&mut self, f: F) -> &mut Self
        where F: Fn(usize) + Send + Sync + 'static
    {
        self.after_start = Some(Arc::new(f));
        self
    }

    /// Execute closure `f` just prior to shutting down each worker thread.
    ///
    /// This hook is intended for bookkeeping and monitoring.
    /// The closure `f` will be dropped after the `builder` is droppped
    /// and all threads in the pool have executed it.
    ///
    /// The closure provided will receive an index corresponding to the worker
    /// thread it's running on.
    pub fn before_stop<F>(&mut self, f: F) -> &mut Self
        where F: Fn(usize) + Send + Sync + 'static
    {
        self.before_stop = Some(Arc::new(f));
        self
    }

    /// Create a [`ThreadPool`](ThreadPool) with the given configuration.
    pub fn create(&mut self) -> Result<ThreadPool, io::Error> {
        let (tx, rx) = mpsc::channel();
        let pool = ThreadPool {
            state: Arc::new(PoolState {
                tx: Mutex::new(tx),
                rx: Mutex::new(rx),
                cnt: AtomicUsize::new(1),
                size: self.pool_size,
            }),
        };

        for counter in 0..self.pool_size {
            let state = pool.state.clone();
            let after_start = self.after_start.clone();
            let before_stop = self.before_stop.clone();
            let mut thread_builder = thread::Builder::new();
            if let Some(ref name_prefix) = self.name_prefix {
                thread_builder = thread_builder.name(format!("{}{}", name_prefix, counter));
            }
            if self.stack_size > 0 {
                thread_builder = thread_builder.stack_size(self.stack_size);
            }
            thread_builder.spawn(move || state.work(counter, after_start, before_stop))?;
        }
        Ok(pool)
    }
}

impl Default for ThreadPoolBuilder {
    fn default() -> Self {
        Self::new()
    }
}

/// A task responsible for polling a future to completion.
struct Task {
    future: FutureObj<'static, ()>,
    exec: ThreadPool,
    wake_handle: Arc<WakeHandle>,
}

struct WakeHandle {
    mutex: UnparkMutex<Task>,
    exec: ThreadPool,
}

impl Task {
    /// Actually run the task (invoking `poll` on the future) on the current
    /// thread.
    fn run(self) {
        let Task { mut future, wake_handle, mut exec } = self;
        let waker = waker_ref(&wake_handle);
        let mut cx = Context::from_waker(&waker);

        // Safety: The ownership of this `Task` object is evidence that
        // we are in the `POLLING`/`REPOLL` state for the mutex.
        unsafe {
            wake_handle.mutex.start_poll();

            loop {
                let res = future.poll_unpin(&mut cx);
                match res {
                    Poll::Pending => {}
                    Poll::Ready(()) => return wake_handle.mutex.complete(),
                }
                let task = Task {
                    future,
                    wake_handle: wake_handle.clone(),
                    exec,
                };
                match wake_handle.mutex.wait(task) {
                    Ok(()) => return, // we've waited
                    Err(task) => { // someone's notified us
                        future = task.future;
                        exec = task.exec;
                    }
                }
            }
        }
    }
}

impl fmt::Debug for Task {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("Task")
            .field("contents", &"...")
            .finish()
    }
}

impl ArcWake for WakeHandle {
    fn wake_by_ref(arc_self: &Arc<Self>) {
        match arc_self.mutex.notify() {
            Ok(task) => arc_self.exec.state.send(Message::Run(task)),
            Err(()) => {}
        }
    }
}

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

    #[test]
    fn test_drop_after_start() {
        let (tx, rx) = mpsc::sync_channel(2);
        let _cpu_pool = ThreadPoolBuilder::new()
            .pool_size(2)
            .after_start(move |_| tx.send(1).unwrap()).create().unwrap();

        // After ThreadPoolBuilder is deconstructed, the tx should be droped
        // so that we can use rx as an iterator.
        let count = rx.into_iter().count();
        assert_eq!(count, 2);
    }
}
<|endoftext|>


<commit_before>use crate::common::util::*;
#[cfg(not(windows))]
use std::os::unix::fs::PermissionsExt;

static TEST_DIR1: &str = "mkdir_test1";
static TEST_DIR2: &str = "mkdir_test2";
static TEST_DIR3: &str = "mkdir_test3";
static TEST_DIR4: &str = "mkdir_test4/mkdir_test4_1";
static TEST_DIR5: &str = "mkdir_test5/mkdir_test5_1";
static TEST_DIR6: &str = "mkdir_test6";
static TEST_FILE7: &str = "mkdir_test7";

#[test]
fn test_mkdir_mkdir() {
    new_ucmd!().arg(TEST_DIR1).succeeds();
}

#[test]
fn test_mkdir_verbose() {
    let expected = "mkdir: created directory 'mkdir_test1'\n";
    new_ucmd!()
        .arg(TEST_DIR1)
        .arg("-v")
        .run()
        .stdout_is(expected);
}

#[test]
fn test_mkdir_dup_dir() {
    let scene = TestScenario::new(util_name!());
    scene.ucmd().arg(TEST_DIR2).succeeds();
    scene.ucmd().arg(TEST_DIR2).fails();
}

#[test]
fn test_mkdir_mode() {
    new_ucmd!().arg("-m").arg("755").arg(TEST_DIR3).succeeds();
}

#[test]
fn test_mkdir_parent() {
    let scene = TestScenario::new(util_name!());
    scene.ucmd().arg("-p").arg(TEST_DIR4).succeeds();
    scene.ucmd().arg("-p").arg(TEST_DIR4).succeeds();
    scene.ucmd().arg("--parent").arg(TEST_DIR4).succeeds();
    scene.ucmd().arg("--parents").arg(TEST_DIR4).succeeds();
}

#[test]
fn test_mkdir_no_parent() {
    new_ucmd!().arg(TEST_DIR5).fails();
}

#[test]
fn test_mkdir_dup_dir_parent() {
    let scene = TestScenario::new(util_name!());
    scene.ucmd().arg(TEST_DIR6).succeeds();
    scene.ucmd().arg("-p").arg(TEST_DIR6).succeeds();
}

#[test]
fn test_mkdir_dup_file() {
    let scene = TestScenario::new(util_name!());
    scene.fixtures.touch(TEST_FILE7);
    scene.ucmd().arg(TEST_FILE7).fails();

    // mkdir should fail for a file even if -p is specified.
    scene.ucmd().arg("-p").arg(TEST_FILE7).fails();
}

#[test]
#[cfg(not(windows))]
fn test_symbolic_mode() {
    let (at, mut ucmd) = at_and_ucmd!();

    ucmd.arg("-m").arg("a=rwx").arg(TEST_DIR1).succeeds();
    let perms = at.metadata(TEST_DIR1).permissions().mode();
    assert_eq!(perms, 0o40777);
}

#[test]
#[cfg(not(windows))]
fn test_symbolic_alteration() {
    let (at, mut ucmd) = at_and_ucmd!();

    ucmd.arg("-m").arg("-w").arg(TEST_DIR1).succeeds();
    let perms = at.metadata(TEST_DIR1).permissions().mode();
    assert_eq!(perms, 0o40555);
}

#[test]
#[cfg(not(windows))]
fn test_multi_symbolic() {
    let (at, mut ucmd) = at_and_ucmd!();

    ucmd.arg("-m")
        .arg("u=rwx,g=rx,o=")
        .arg(TEST_DIR1)
        .succeeds();
    let perms = at.metadata(TEST_DIR1).permissions().mode();
    assert_eq!(perms, 0o40750);
}
<commit_msg>test/mkdir: added testing for umask compliance - tests for all permission combinations<commit_after>use crate::common::util::*;
#[cfg(not(windows))]
use std::os::unix::fs::PermissionsExt;
extern crate libc;
use self::libc::umask;

static TEST_DIR1: &str = "mkdir_test1";
static TEST_DIR2: &str = "mkdir_test2";
static TEST_DIR3: &str = "mkdir_test3";
static TEST_DIR4: &str = "mkdir_test4/mkdir_test4_1";
static TEST_DIR5: &str = "mkdir_test5/mkdir_test5_1";
static TEST_DIR6: &str = "mkdir_test6";
static TEST_FILE7: &str = "mkdir_test7";
static TEST_DIR8: &str = "mkdir_test8";

#[test]
fn test_mkdir_verbose() {
    let expected = "mkdir: created directory 'mkdir_test1'\n";
    new_ucmd!()
        .arg(TEST_DIR1)
        .arg("-v")
        .run()
        .stdout_is(expected);
}

#[test]
fn test_mkdir_dup_dir() {
    let scene = TestScenario::new(util_name!());
    scene.ucmd().arg(TEST_DIR2).succeeds();
    scene.ucmd().arg(TEST_DIR2).fails();
}

#[test]
fn test_mkdir_mode() {
    new_ucmd!().arg("-m").arg("755").arg(TEST_DIR3).succeeds();
}

#[test]
fn test_mkdir_parent() {
    let scene = TestScenario::new(util_name!());
    scene.ucmd().arg("-p").arg(TEST_DIR4).succeeds();
    scene.ucmd().arg("-p").arg(TEST_DIR4).succeeds();
    scene.ucmd().arg("--parent").arg(TEST_DIR4).succeeds();
    scene.ucmd().arg("--parents").arg(TEST_DIR4).succeeds();
}

#[test]
fn test_mkdir_no_parent() {
    new_ucmd!().arg(TEST_DIR5).fails();
}

#[test]
fn test_mkdir_dup_dir_parent() {
    let scene = TestScenario::new(util_name!());
    scene.ucmd().arg(TEST_DIR6).succeeds();
    scene.ucmd().arg("-p").arg(TEST_DIR6).succeeds();
}

#[test]
fn test_mkdir_dup_file() {
    let scene = TestScenario::new(util_name!());
    scene.fixtures.touch(TEST_FILE7);
    scene.ucmd().arg(TEST_FILE7).fails();

    // mkdir should fail for a file even if -p is specified.
    scene.ucmd().arg("-p").arg(TEST_FILE7).fails();
}

#[test]
#[cfg(not(windows))]
fn test_symbolic_mode() {
    let (at, mut ucmd) = at_and_ucmd!();

    ucmd.arg("-m").arg("a=rwx").arg(TEST_DIR1).succeeds();
    let perms = at.metadata(TEST_DIR1).permissions().mode();
    assert_eq!(perms, 0o40777);
}

#[test]
#[cfg(not(windows))]
fn test_symbolic_alteration() {
    let (at, mut ucmd) = at_and_ucmd!();

    ucmd.arg("-m").arg("-w").arg(TEST_DIR1).succeeds();
    let perms = at.metadata(TEST_DIR1).permissions().mode();
    assert_eq!(perms, 0o40555);
}

#[test]
#[cfg(not(windows))]
fn test_multi_symbolic() {
    let (at, mut ucmd) = at_and_ucmd!();

    ucmd.arg("-m")
        .arg("u=rwx,g=rx,o=")
        .arg(TEST_DIR1)
        .succeeds();
    let perms = at.metadata(TEST_DIR1).permissions().mode();
    assert_eq!(perms, 0o40750);
}

#[test]
#[cfg(not(windows))]
fn test_umask_compliance() {
    fn test_single_case(umask_set: u32) {
        let (at, mut ucmd) = at_and_ucmd!();

        let original_umask = unsafe { umask(umask_set) };

        ucmd.arg(TEST_DIR8).succeeds();
        let perms = at.metadata(TEST_DIR8).permissions().mode();

        assert_eq!(perms, (!umask_set & 0o0777) + 0o40000); // before compare, add the setguid, uid bits
        unsafe { umask(original_umask); } // set umask back to original
    }

    for i in 0o0..0o777 { // tests all permission combinations
        test_single_case(i);
    }

}
<|endoftext|>


<commit_before>#![feature(default_type_params)]

extern crate test;
extern crate time;
extern crate collections;

use collections::HashMap;
use std::hash::sip::SipHasher;
use std::os::args;
use test::black_box;
use time::precise_time_ns;

struct Eratostenes {
    count: u64,
    map: HashMap<u64, u64, SipHasher>,
}

impl Iterator<u64> for Eratostenes {
    fn next(&mut self) -> Option<u64> {
        loop {
            self.count += 1;
            let q = self.count;

            match self.map.pop(&q) {
                None => {
                    self.map.insert(q * q, q);

                    return Some(q);
                },
                Some(p) => {
                    let mut x = p + q;

                    while self.map.contains_key(&x) {
                        x += p;
                    }

                    self.map.insert(x, p);
                }
            }
        }
    }
}

fn eratostenes() -> Eratostenes {
    Eratostenes {
        count: 1,
        map: HashMap::new(),
    }
}

#[inline]
fn f() -> u64 {
    eratostenes().nth(10000).unwrap()
}

fn main() {
    match args().as_slice() {
        [_] => {
            println!("{}", f());
        },
        [_, ref iters] => {
            let iters: u64 = from_str(iters.as_slice()).unwrap();

            let start = precise_time_ns();
            for _ in range(0, iters) {
                black_box(f());
            }
            let end = precise_time_ns();

            println!("{}", end - start);
        },
        _ => unreachable!(),
    }
}
<commit_msg>007.rs: 33% speedup using HashMap with initial capacity<commit_after>#![feature(default_type_params)]

extern crate test;
extern crate time;
extern crate collections;

use collections::HashMap;
use std::hash::sip::SipHasher;
use std::os::args;
use test::black_box;
use time::precise_time_ns;

struct Eratostenes {
    count: u64,
    map: HashMap<u64, u64, SipHasher>,
}

impl Iterator<u64> for Eratostenes {
    fn next(&mut self) -> Option<u64> {
        loop {
            self.count += 1;
            let q = self.count;

            match self.map.pop(&q) {
                None => {
                    self.map.insert(q * q, q);

                    return Some(q);
                },
                Some(p) => {
                    let mut x = p + q;

                    while self.map.contains_key(&x) {
                        x += p;
                    }

                    self.map.insert(x, p);
                }
            }
        }
    }
}

fn eratostenes(capacity: uint) -> Eratostenes {
    Eratostenes {
        count: 1,
        map: HashMap::with_capacity(capacity),
    }
}

#[inline]
fn f() -> u64 {
    let target = 10000;
    eratostenes(target).nth(target).unwrap()
}

fn main() {
    match args().as_slice() {
        [_] => {
            println!("{}", f());
        },
        [_, ref iters] => {
            let iters: u64 = from_str(iters.as_slice()).unwrap();

            let start = precise_time_ns();
            for _ in range(0, iters) {
                black_box(f());
            }
            let end = precise_time_ns();

            println!("{}", end - start);
        },
        _ => unreachable!(),
    }
}
<|endoftext|>


<commit_before>use serde_json;
use std;

use std::collections::HashMap;
use std::hash::Hash;

use {Path, Root};

/// Trait for validating glTF JSON data against the 2.0 specification.
pub trait Validate {
    /// Validates only the invariants required for the library to function safely.
    fn validate_minimally<P, R>(&self, _root: &Root, _path: P, _report: &mut R)
    where
        P: Fn() -> Path,
        R: FnMut(&Fn() -> Path, Error),
    {
        // nop
    }

    /// Validates the data against the glTF 2.0 specification.
    ///
    /// # Notes
    ///
    /// The caller must also call `validate_minimally()` for full validation.
    fn validate_completely<P, R>(&self, _root: &Root, _path: P, _report: &mut R)
    where
        P: Fn() -> Path,
        R: FnMut(&Fn() -> Path, Error),
    {
        // nop
    }
}

/// Specifies what kind of error occured during validation.
#[derive(Clone, Debug, PartialEq)]
pub enum Error {
    /// An index was found to be out of bounds.
    IndexOutOfBounds,

    /// An invalid value was identified.
    Invalid,

    /// Some required data has been omitted.
    Missing,
}

/// Specifies a type that has been pre-validated during deserialization or otherwise.
#[derive(Debug, Eq, Hash, PartialEq)]
pub enum Checked<T> {
    /// The item is valid.
    Valid(T),

    /// The item is invalid.
    Invalid,
}

impl<T> Checked<T> {
    /// Converts from `Checked<T>` to `Checked<&T>`.
    pub fn as_ref(&self) -> Checked<&T> {
        match *self {
            Checked::Valid(ref item) => Checked::Valid(item),
            Checked::Invalid => Checked::Invalid,
        }
    }

    /// Takes ownership of the contained item if it is `Valid`.
    ///
    /// # Panics
    ///
    /// Panics if called on an `Invalid` item.
    pub fn unwrap(self) -> T {
        match self {
            Checked::Valid(item) => item,
            Checked::Invalid => panic!("attempted to unwrap an invalid item"),
        }
    }
}

impl<T: Clone> Clone for Checked<T> {
    fn clone(&self) -> Self {
        match *self {
            Checked::Valid(ref item) => Checked::Valid(item.clone()),
            Checked::Invalid => Checked::Invalid,
        }
    }
}

impl<T: Copy> Copy for Checked<T> {}

impl<T: Default> Default for Checked<T> {
    fn default() -> Self {
        Checked::Valid(T::default())
    }
}

impl<T> Validate for Checked<T> {
    fn validate_minimally<P, R>(&self, _root: &Root, path: P, report: &mut R)
        where P: Fn() -> Path, R: FnMut(&Fn() -> Path, Error)
    {
        match *self {
            Checked::Valid(_) => {},
            Checked::Invalid => report(&path, Error::Invalid),
        }
    }
}

impl<K: Eq + Hash + ToString + Validate, V: Validate> Validate for HashMap<K, V> {
    fn validate_minimally<P, R>(&self, root: &Root, path: P, report: &mut R)
        where P: Fn() -> Path, R: FnMut(&Fn() -> Path, Error)
    {
        for (key, value) in self.iter() {
            key.validate_minimally(root, || path().key(&key.to_string()), report);
            value.validate_minimally(root, || path().key(&key.to_string()), report);
        }
    }

    fn validate_completely<P, R>(&self, root: &Root, path: P, report: &mut R)
        where P: Fn() -> Path, R: FnMut(&Fn() -> Path, Error)
    {
        for (key, value) in self.iter() {
            key.validate_completely(root, || path().key(&key.to_string()), report);
            value.validate_completely(root, || path().key(&key.to_string()), report);
        }
    }
}

impl<T: Validate> Validate for Option<T> {
    fn validate_minimally<P, R>(&self, root: &Root, path: P, report: &mut R)
        where P: Fn() -> Path, R: FnMut(&Fn() -> Path, Error)
    {
        if let Some(value) = self.as_ref() {
            value.validate_minimally(root, path, report);
        }
    }

    fn validate_completely<P, R>(&self, root: &Root, path: P, report: &mut R)
        where P: Fn() -> Path, R: FnMut(&Fn() -> Path, Error)
    {
        if let Some(value) = self.as_ref() {
            value.validate_completely(root, path, report);
        }
    }
}

impl<T: Validate> Validate for Vec<T> {
    fn validate_minimally<P, R>(&self, root: &Root, path: P, report: &mut R)
        where P: Fn() -> Path, R: FnMut(&Fn() -> Path, Error)
    {
        for (index, value) in self.iter().enumerate() {
            value.validate_minimally(root, || path().index(index), report);
        }
    }

    fn validate_completely<P, R>(&self, root: &Root, path: P, report: &mut R)
        where P: Fn() -> Path, R: FnMut(&Fn() -> Path, Error)
    {
        for (index, value) in self.iter().enumerate() {
            value.validate_completely(root, || path().index(index), report);
        }
    }
}

impl std::error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::IndexOutOfBounds => "Index out of bounds",
            Error::Invalid => "Invalid value",
            Error::Missing => "Missing data",
        }
    }
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        use std::error::Error;
        write!(f, "{}", self.description())
    }
}

// These types are assumed to be always valid.
impl Validate for bool {}
impl Validate for u32 {}
impl Validate for i32 {}
impl Validate for f32 {}
impl Validate for [f32; 3] {}
impl Validate for [f32; 4] {}
impl Validate for [f32; 16] {}
impl Validate for () {}
impl Validate for String {}
impl Validate for serde_json::Value {}
<commit_msg>Add some derives for the error type<commit_after>use serde_json;
use std;

use std::collections::HashMap;
use std::hash::Hash;

use {Path, Root};

/// Trait for validating glTF JSON data against the 2.0 specification.
pub trait Validate {
    /// Validates only the invariants required for the library to function safely.
    fn validate_minimally<P, R>(&self, _root: &Root, _path: P, _report: &mut R)
    where
        P: Fn() -> Path,
        R: FnMut(&Fn() -> Path, Error),
    {
        // nop
    }

    /// Validates the data against the glTF 2.0 specification.
    ///
    /// # Notes
    ///
    /// The caller must also call `validate_minimally()` for full validation.
    fn validate_completely<P, R>(&self, _root: &Root, _path: P, _report: &mut R)
    where
        P: Fn() -> Path,
        R: FnMut(&Fn() -> Path, Error),
    {
        // nop
    }
}

/// Specifies what kind of error occured during validation.
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Error {
    /// An index was found to be out of bounds.
    IndexOutOfBounds,

    /// An invalid value was identified.
    Invalid,

    /// Some required data has been omitted.
    Missing,
}

/// Specifies a type that has been pre-validated during deserialization or otherwise.
#[derive(Debug, Eq, Hash, PartialEq)]
pub enum Checked<T> {
    /// The item is valid.
    Valid(T),

    /// The item is invalid.
    Invalid,
}

impl<T> Checked<T> {
    /// Converts from `Checked<T>` to `Checked<&T>`.
    pub fn as_ref(&self) -> Checked<&T> {
        match *self {
            Checked::Valid(ref item) => Checked::Valid(item),
            Checked::Invalid => Checked::Invalid,
        }
    }

    /// Takes ownership of the contained item if it is `Valid`.
    ///
    /// # Panics
    ///
    /// Panics if called on an `Invalid` item.
    pub fn unwrap(self) -> T {
        match self {
            Checked::Valid(item) => item,
            Checked::Invalid => panic!("attempted to unwrap an invalid item"),
        }
    }
}

impl<T: Clone> Clone for Checked<T> {
    fn clone(&self) -> Self {
        match *self {
            Checked::Valid(ref item) => Checked::Valid(item.clone()),
            Checked::Invalid => Checked::Invalid,
        }
    }
}

impl<T: Copy> Copy for Checked<T> {}

impl<T: Default> Default for Checked<T> {
    fn default() -> Self {
        Checked::Valid(T::default())
    }
}

impl<T> Validate for Checked<T> {
    fn validate_minimally<P, R>(&self, _root: &Root, path: P, report: &mut R)
        where P: Fn() -> Path, R: FnMut(&Fn() -> Path, Error)
    {
        match *self {
            Checked::Valid(_) => {},
            Checked::Invalid => report(&path, Error::Invalid),
        }
    }
}

impl<K: Eq + Hash + ToString + Validate, V: Validate> Validate for HashMap<K, V> {
    fn validate_minimally<P, R>(&self, root: &Root, path: P, report: &mut R)
        where P: Fn() -> Path, R: FnMut(&Fn() -> Path, Error)
    {
        for (key, value) in self.iter() {
            key.validate_minimally(root, || path().key(&key.to_string()), report);
            value.validate_minimally(root, || path().key(&key.to_string()), report);
        }
    }

    fn validate_completely<P, R>(&self, root: &Root, path: P, report: &mut R)
        where P: Fn() -> Path, R: FnMut(&Fn() -> Path, Error)
    {
        for (key, value) in self.iter() {
            key.validate_completely(root, || path().key(&key.to_string()), report);
            value.validate_completely(root, || path().key(&key.to_string()), report);
        }
    }
}

impl<T: Validate> Validate for Option<T> {
    fn validate_minimally<P, R>(&self, root: &Root, path: P, report: &mut R)
        where P: Fn() -> Path, R: FnMut(&Fn() -> Path, Error)
    {
        if let Some(value) = self.as_ref() {
            value.validate_minimally(root, path, report);
        }
    }

    fn validate_completely<P, R>(&self, root: &Root, path: P, report: &mut R)
        where P: Fn() -> Path, R: FnMut(&Fn() -> Path, Error)
    {
        if let Some(value) = self.as_ref() {
            value.validate_completely(root, path, report);
        }
    }
}

impl<T: Validate> Validate for Vec<T> {
    fn validate_minimally<P, R>(&self, root: &Root, path: P, report: &mut R)
        where P: Fn() -> Path, R: FnMut(&Fn() -> Path, Error)
    {
        for (index, value) in self.iter().enumerate() {
            value.validate_minimally(root, || path().index(index), report);
        }
    }

    fn validate_completely<P, R>(&self, root: &Root, path: P, report: &mut R)
        where P: Fn() -> Path, R: FnMut(&Fn() -> Path, Error)
    {
        for (index, value) in self.iter().enumerate() {
            value.validate_completely(root, || path().index(index), report);
        }
    }
}

impl std::error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::IndexOutOfBounds => "Index out of bounds",
            Error::Invalid => "Invalid value",
            Error::Missing => "Missing data",
        }
    }
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        use std::error::Error;
        write!(f, "{}", self.description())
    }
}

// These types are assumed to be always valid.
impl Validate for bool {}
impl Validate for u32 {}
impl Validate for i32 {}
impl Validate for f32 {}
impl Validate for [f32; 3] {}
impl Validate for [f32; 4] {}
impl Validate for [f32; 16] {}
impl Validate for () {}
impl Validate for String {}
impl Validate for serde_json::Value {}
<|endoftext|>


<commit_before>use futures::{future, FutureExt, StreamExt};

use crate::Metadata;

use futures::stream;

use crate::ServerResponseSink;
use crate::ServerResponseUnarySink;
use std::future::Future;
use std::task::Context;
use std::task::Poll;
use tokio::runtime::Handle;

pub struct ServerHandlerContext {
    pub ctx: httpbis::ServerHandlerContext,
    // TODO: move to request
    pub metadata: Metadata,
}

impl ServerHandlerContext {
    pub fn loop_remote(&self) -> Handle {
        self.ctx.loop_remote()
    }

    pub fn spawn_poll_fn<F>(&self, mut f: F)
    where
        F: FnMut(&mut Context<'_>) -> Poll<crate::Result<()>> + Send + 'static,
    {
        self.loop_remote()
            .spawn(future::poll_fn(move |cx| match f(cx) {
                Poll::Pending => Poll::Pending,
                Poll::Ready(Ok(())) => Poll::Ready(()),
                Poll::Ready(Err(e)) => {
                    warn!("poll_fn returned error: {:?}", e);
                    Poll::Ready(())
                }
            }));
    }

    pub fn pump<Resp, S>(&self, mut stream: S, mut dest: ServerResponseSink<Resp>)
    where
        Resp: Send + 'static,
        S: stream::Stream<Item = crate::Result<Resp>> + Unpin + Send + 'static,
    {
        self.spawn_poll_fn(move |cx| loop {
            if let Poll::Pending = dest.poll(cx)? {
                return Poll::Pending;
            }
            match stream.poll_next_unpin(cx)? {
                Poll::Pending => return Poll::Pending,
                Poll::Ready(Some(m)) => {
                    dest.send_data(m)?;
                }
                Poll::Ready(None) => {
                    dest.send_trailers(Metadata::new())?;
                    return Poll::Ready(Ok(()));
                }
            }
        })
    }

    pub fn pump_future<Resp, F>(&self, mut future: F, dest: ServerResponseUnarySink<Resp>)
    where
        Resp: Send + 'static,
        F: Future<Output = crate::Result<Resp>> + Unpin + Send + 'static,
    {
        let mut dest = Some(dest);
        self.spawn_poll_fn(move |cx| loop {
            match future.poll_unpin(cx)? {
                Poll::Pending => return Poll::Pending,
                Poll::Ready(m) => {
                    dest.take().unwrap().finish(m)?;
                    return Poll::Ready(Ok(()));
                }
            }
        })
    }
}
<commit_msg>Document, internal refactor ServerHandlerContext<commit_after>use futures::future;
use futures::StreamExt;

use crate::Metadata;

use futures::stream;

use crate::ServerResponseSink;
use crate::ServerResponseUnarySink;
use std::future::Future;
use std::task::Context;
use std::task::Poll;
use tokio::runtime::Handle;

/// An object passed to server handlers.
pub struct ServerHandlerContext {
    pub ctx: httpbis::ServerHandlerContext,
    // TODO: move to request
    pub metadata: Metadata,
}

impl ServerHandlerContext {
    /// Tokio event loop handle (can be used to spawn a future for example).
    pub fn loop_remote(&self) -> Handle {
        self.ctx.loop_remote()
    }

    /// Spawn a future, ignore result.
    pub fn spawn<F>(&self, f: F)
    where
        F: Future<Output = crate::Result<()>> + Send + 'static,
    {
        self.loop_remote().spawn(async {
            if let Err(e) = f.await {
                warn!("spaned future returned error: {:?}", e);
            }
        });
    }

    /// Spawn a poll_fn future. Function error is ignored.
    pub fn spawn_poll_fn<F>(&self, f: F)
    where
        F: FnMut(&mut Context<'_>) -> Poll<crate::Result<()>> + Send + 'static,
    {
        self.spawn(future::poll_fn(f))
    }

    /// Spawn a future which polls stream and sends items to [`ServerResponseSink`].
    pub fn pump<Resp, S>(&self, mut stream: S, mut dest: ServerResponseSink<Resp>)
    where
        Resp: Send + 'static,
        S: stream::Stream<Item = crate::Result<Resp>> + Unpin + Send + 'static,
    {
        self.spawn(async move {
            while let Some(m) = stream.next().await {
                dest.send_data(m?)?;
            }
            dest.send_trailers(Metadata::new())
        });
    }

    /// Spawn the provided future in tokio even loop, send result to given sink.
    pub fn pump_future<Resp, F>(&self, future: F, dest: ServerResponseUnarySink<Resp>)
    where
        Resp: Send + 'static,
        F: Future<Output = crate::Result<Resp>> + Unpin + Send + 'static,
    {
        self.spawn(async move {
            let m = future.await?;
            dest.finish(m)
        });
    }
}
<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use itertools::Itertools;

use libimagstore::store::Entry;

use toml_query::read::TomlValueReadExt;
use toml_query::insert::TomlValueInsertExt;

use error::TagErrorKind;
use error::TagError as TE;
use error::ResultExt;
use error::Result;
use tag::{Tag, TagSlice};
use tag::is_tag_str;

use toml::Value;

pub trait Tagable {

    fn get_tags(&self) -> Result<Vec<Tag>>;
    fn set_tags(&mut self, ts: &[Tag]) -> Result<()>;

    fn add_tag(&mut self, t: Tag) -> Result<()>;
    fn remove_tag(&mut self, t: Tag) -> Result<()>;

    fn has_tag(&self, t: TagSlice) -> Result<bool>;
    fn has_tags(&self, ts: &[Tag]) -> Result<bool>;

}

impl Tagable for Value {

    fn get_tags(&self) -> Result<Vec<Tag>> {
        let tags = try!(self.read("tag.values").chain_err(|| TagErrorKind::HeaderReadError));

        match tags {
            Some(&Value::Array(ref tags)) => {
                if !tags.iter().all(|t| is_match!(*t, Value::String(_))) {
                    return Err(TagErrorKind::TagTypeError.into());
                }
                if tags.iter().any(|t| match *t {
                    Value::String(ref s) => !is_tag_str(s).is_ok(),
                    _ => unreachable!()})
                {
                    return Err(TagErrorKind::NotATag.into());
                }

                Ok(tags.iter()
                    .cloned()
                    .map(|t| {
                        match t {
                           Value::String(s) => s,
                           _ => unreachable!(),
                        }
                    })
                    .collect())
            },
            None => Ok(vec![]),
            _ => Err(TagErrorKind::TagTypeError.into()),
        }
    }

    fn set_tags(&mut self, ts: &[Tag]) -> Result<()> {
        if ts.iter().any(|tag| !is_tag_str(tag).is_ok()) {
            debug!("Not a tag: '{}'", ts.iter().filter(|t| !is_tag_str(t).is_ok()).next().unwrap());
            return Err(TagErrorKind::NotATag.into());
        }

        let a = ts.iter().unique().map(|t| Value::String(t.clone())).collect();
        debug!("Setting tags = {:?}", a);
        self.insert("tag.values", Value::Array(a))
            .map(|_| ())
            .chain_err(|| TagErrorKind::HeaderWriteError)
    }

    fn add_tag(&mut self, t: Tag) -> Result<()> {
        if !try!(is_tag_str(&t).map(|_| true).map_err(|_| TE::from_kind(TagErrorKind::NotATag))) {
            debug!("Not a tag: '{}'", t);
            return Err(TagErrorKind::NotATag.into());
        }

        self.get_tags()
            .map(|mut tags| {
                debug!("Pushing tag = {:?} to list = {:?}", t, tags);
                tags.push(t);
                self.set_tags(&tags.into_iter().unique().collect::<Vec<_>>()[..])
            })
            .map(|_| ())
    }

    fn remove_tag(&mut self, t: Tag) -> Result<()> {
        if !try!(is_tag_str(&t).map(|_| true).map_err(|_| TE::from_kind(TagErrorKind::NotATag))) {
            debug!("Not a tag: '{}'", t);
            return Err(TagErrorKind::NotATag.into());
        }

        self.get_tags()
            .map(|mut tags| {
                tags.retain(|tag| tag.clone() != t);
                self.set_tags(&tags[..])
            })
            .map(|_| ())
    }

    fn has_tag(&self, t: TagSlice) -> Result<bool> {
        let tags = try!(self.read("tag.values").chain_err(|| TagErrorKind::HeaderReadError));

        if !tags.iter().all(|t| is_match!(*t, &Value::String(_))) {
            return Err(TagErrorKind::TagTypeError.into());
        }

        Ok(tags
           .iter()
           .any(|tag| {
               match *tag {
                   &Value::String(ref s) => { s == t },
                   _ => unreachable!()
               }
           }))
    }

    fn has_tags(&self, tags: &[Tag]) -> Result<bool> {
        let mut result = true;
        for tag in tags {
            result = result && try!(self.has_tag(tag));
        }

        Ok(result)
    }

}

impl Tagable for Entry {

    fn get_tags(&self) -> Result<Vec<Tag>> {
        self.get_header().get_tags()
    }

    fn set_tags(&mut self, ts: &[Tag]) -> Result<()> {
        self.get_header_mut().set_tags(ts)
    }

    fn add_tag(&mut self, t: Tag) -> Result<()> {
        self.get_header_mut().add_tag(t)
    }

    fn remove_tag(&mut self, t: Tag) -> Result<()> {
        self.get_header_mut().remove_tag(t)
    }

    fn has_tag(&self, t: TagSlice) -> Result<bool> {
        self.get_header().has_tag(t)
    }

    fn has_tags(&self, ts: &[Tag]) -> Result<bool> {
        self.get_header().has_tags(ts)
    }

}

<commit_msg>Replace uses of try!() macro with "?" operator<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use itertools::Itertools;

use libimagstore::store::Entry;

use toml_query::read::TomlValueReadExt;
use toml_query::insert::TomlValueInsertExt;

use error::TagErrorKind;
use error::TagError as TE;
use error::ResultExt;
use error::Result;
use tag::{Tag, TagSlice};
use tag::is_tag_str;

use toml::Value;

pub trait Tagable {

    fn get_tags(&self) -> Result<Vec<Tag>>;
    fn set_tags(&mut self, ts: &[Tag]) -> Result<()>;

    fn add_tag(&mut self, t: Tag) -> Result<()>;
    fn remove_tag(&mut self, t: Tag) -> Result<()>;

    fn has_tag(&self, t: TagSlice) -> Result<bool>;
    fn has_tags(&self, ts: &[Tag]) -> Result<bool>;

}

impl Tagable for Value {

    fn get_tags(&self) -> Result<Vec<Tag>> {
        let tags = self.read("tag.values").chain_err(|| TagErrorKind::HeaderReadError)?;

        match tags {
            Some(&Value::Array(ref tags)) => {
                if !tags.iter().all(|t| is_match!(*t, Value::String(_))) {
                    return Err(TagErrorKind::TagTypeError.into());
                }
                if tags.iter().any(|t| match *t {
                    Value::String(ref s) => !is_tag_str(s).is_ok(),
                    _ => unreachable!()})
                {
                    return Err(TagErrorKind::NotATag.into());
                }

                Ok(tags.iter()
                    .cloned()
                    .map(|t| {
                        match t {
                           Value::String(s) => s,
                           _ => unreachable!(),
                        }
                    })
                    .collect())
            },
            None => Ok(vec![]),
            _ => Err(TagErrorKind::TagTypeError.into()),
        }
    }

    fn set_tags(&mut self, ts: &[Tag]) -> Result<()> {
        if ts.iter().any(|tag| !is_tag_str(tag).is_ok()) {
            debug!("Not a tag: '{}'", ts.iter().filter(|t| !is_tag_str(t).is_ok()).next().unwrap());
            return Err(TagErrorKind::NotATag.into());
        }

        let a = ts.iter().unique().map(|t| Value::String(t.clone())).collect();
        debug!("Setting tags = {:?}", a);
        self.insert("tag.values", Value::Array(a))
            .map(|_| ())
            .chain_err(|| TagErrorKind::HeaderWriteError)
    }

    fn add_tag(&mut self, t: Tag) -> Result<()> {
        if !is_tag_str(&t).map(|_| true).map_err(|_| TE::from_kind(TagErrorKind::NotATag))? {
            debug!("Not a tag: '{}'", t);
            return Err(TagErrorKind::NotATag.into());
        }

        self.get_tags()
            .map(|mut tags| {
                debug!("Pushing tag = {:?} to list = {:?}", t, tags);
                tags.push(t);
                self.set_tags(&tags.into_iter().unique().collect::<Vec<_>>()[..])
            })
            .map(|_| ())
    }

    fn remove_tag(&mut self, t: Tag) -> Result<()> {
        if !is_tag_str(&t).map(|_| true).map_err(|_| TE::from_kind(TagErrorKind::NotATag))? {
            debug!("Not a tag: '{}'", t);
            return Err(TagErrorKind::NotATag.into());
        }

        self.get_tags()
            .map(|mut tags| {
                tags.retain(|tag| tag.clone() != t);
                self.set_tags(&tags[..])
            })
            .map(|_| ())
    }

    fn has_tag(&self, t: TagSlice) -> Result<bool> {
        let tags = self.read("tag.values").chain_err(|| TagErrorKind::HeaderReadError)?;

        if !tags.iter().all(|t| is_match!(*t, &Value::String(_))) {
            return Err(TagErrorKind::TagTypeError.into());
        }

        Ok(tags
           .iter()
           .any(|tag| {
               match *tag {
                   &Value::String(ref s) => { s == t },
                   _ => unreachable!()
               }
           }))
    }

    fn has_tags(&self, tags: &[Tag]) -> Result<bool> {
        let mut result = true;
        for tag in tags {
            result = result && self.has_tag(tag)?;
        }

        Ok(result)
    }

}

impl Tagable for Entry {

    fn get_tags(&self) -> Result<Vec<Tag>> {
        self.get_header().get_tags()
    }

    fn set_tags(&mut self, ts: &[Tag]) -> Result<()> {
        self.get_header_mut().set_tags(ts)
    }

    fn add_tag(&mut self, t: Tag) -> Result<()> {
        self.get_header_mut().add_tag(t)
    }

    fn remove_tag(&mut self, t: Tag) -> Result<()> {
        self.get_header_mut().remove_tag(t)
    }

    fn has_tag(&self, t: TagSlice) -> Result<bool> {
        self.get_header().has_tag(t)
    }

    fn has_tags(&self, ts: &[Tag]) -> Result<bool> {
        self.get_header().has_tags(ts)
    }

}

<|endoftext|>


<commit_before>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

#[macro_use]
mod macros;
mod direct_download;
mod download;
mod entry_point;
mod error;
pub(crate) mod install;
pub(crate) mod machine;
mod park;
mod poll;
mod prepare_local_install;
mod probe;
mod reboot;
mod validation;

#[cfg(test)]
mod tests;

use self::{
    direct_download::DirectDownload, download::Download, entry_point::EntryPoint, error::Error,
    install::Install, park::Park, poll::Poll, prepare_local_install::PrepareLocalInstall,
    probe::Probe, reboot::Reboot, validation::Validation,
};
use crate::{
    firmware::{self, Metadata, Transition},
    http_api,
    runtime_settings::RuntimeSettings,
    settings::Settings,
};
use async_trait::async_trait;
use derive_more::{Display, Error, From};
use slog_scope::{error, info, trace, warn};
use std::path::Path;

pub type Result<T> = std::result::Result<T, TransitionError>;

#[derive(Debug, Display, Error, From)]
pub enum TransitionError {
    #[display("some objects are not ready for use")]
    SomeObjectsAreNotReady,
    #[display("signature not found")]
    SignatureNotFound,
    #[display("channel communication as failed")]
    CommunicationFailed,

    Firmware(crate::firmware::Error),
    Installation(crate::object::Error),
    RuntimeSettings(crate::runtime_settings::Error),
    UpdatePackage(crate::update_package::Error),
    Client(cloud::Error),
    Uncompress(compress_tools::Error),
    SerdeJson(serde_json::error::Error),
    Io(std::io::Error),
    NonUtf8(std::string::FromUtf8Error),
    Process(easy_process::Error),
}

#[async_trait]
trait StateChangeImpl {
    async fn handle(
        self,
        context: &mut machine::Context,
    ) -> Result<(State, machine::StepTransition)>;

    fn name(&self) -> &'static str;

    /// All states downloading large files should overwrite this to return true.
    /// That way, a external request to abort download can be heeded.
    fn is_handling_download(&self) -> bool {
        false
    }

    /// A preemptive state is a state whose transition can be yield
    /// to handle a user's request. Any preemptive state should overwrite
    /// this method to return true.
    fn is_preemptive_state(&self) -> bool {
        false
    }
}

#[async_trait]
trait CallbackReporter: Sized + StateChangeImpl {
    async fn handle_with_callback(
        self,
        context: &mut machine::Context,
    ) -> Result<(State, machine::StepTransition)> {
        let transition =
            firmware::state_change_callback(&context.settings.firmware.metadata, self.name())?;

        match transition {
            Transition::Continue => Ok(self.handle(context).await?),
            Transition::Cancel => {
                info!(
                    "cancelling transition to '{}' due to state change callback request",
                    self.name()
                );
                Ok((State::EntryPoint(EntryPoint {}), machine::StepTransition::Immediate))
            }
        }
    }
}

#[async_trait]
trait ProgressReporter: CallbackReporter {
    fn package_uid(&self) -> String;
    fn report_enter_state_name(&self) -> &'static str;
    fn report_leave_state_name(&self) -> &'static str;

    async fn handle_and_report_progress(
        self,
        context: &mut machine::Context,
    ) -> Result<(State, machine::StepTransition)> {
        let server = context.server_address().to_owned();
        let firmware = &context.firmware.clone();
        let package_uid = &self.package_uid();
        let enter_state = self.report_enter_state_name();
        let leave_state = self.report_leave_state_name();
        let api = crate::CloudClient::new(&server);

        let report = |state, previous_state, error_message, current_log| {
            api.report(
                state,
                firmware.as_cloud_metadata(),
                package_uid,
                previous_state,
                error_message,
                current_log,
            )
        };

        if let Err(e) = report(enter_state, None, None, None).await {
            warn!("report failed: {}", e);
        }
        match self.handle(context).await {
            Ok((state, trans)) => {
                if let Err(e) = report(leave_state, None, None, None).await {
                    warn!("report failed: {}", e);
                };
                Ok((state, trans))
            }
            Err(e) => {
                if let Err(e) = report(
                    "error",
                    Some(enter_state),
                    Some(e.to_string()),
                    Some(crate::logger::get_memory_log()),
                )
                .await
                {
                    warn!("report failed: {}", e);
                }
                Err(e)
            }
        }
    }

    async fn handle_with_callback_and_report_progress(
        self,
        context: &mut machine::Context,
    ) -> Result<(State, machine::StepTransition)> {
        let transition =
            firmware::state_change_callback(&context.settings.firmware.metadata, self.name())?;

        match transition {
            Transition::Continue => Ok(self.handle_and_report_progress(context).await?),
            Transition::Cancel => {
                info!(
                    "cancelling transition to '{}' due to state change callback request",
                    self.name()
                );
                Ok((State::EntryPoint(EntryPoint {}), machine::StepTransition::Immediate))
            }
        }
    }
}

#[derive(Debug)]
enum State {
    Park(Park),
    EntryPoint(EntryPoint),
    Poll(Poll),
    Probe(Probe),
    Validation(Validation),
    Download(Download),
    Install(Install),
    Reboot(Reboot),
    DirectDownload(DirectDownload),
    PrepareLocalInstall(PrepareLocalInstall),
    Error(Error),
}

fn handle_startup_callbacks(
    settings: &Settings,
    runtime_settings: &mut RuntimeSettings,
) -> crate::Result<()> {
    if let Some(expected_set) = runtime_settings.update.upgrade_to_installation {
        info!("booting from a recent installation");
        if expected_set == firmware::installation_set::active()?.0 {
            match firmware::validate_callback(&settings.firmware.metadata)? {
                Transition::Cancel => {
                    warn!("validate callback has failed");
                    firmware::installation_set::swap_active()?;
                    warn!("swapped active installation set and running rollback");
                    firmware::rollback_callback(&settings.firmware.metadata)?;
                    runtime_settings.reset_installation_settings()?;
                    easy_process::run("reboot")?;
                }
                Transition::Continue => firmware::installation_set::validate()?,
            }
        }
        runtime_settings.reset_installation_settings()?;
    }
    Ok(())
}

#[async_trait]
impl StateChangeImpl for State {
    async fn handle(self, st: &mut machine::Context) -> Result<(State, machine::StepTransition)> {
        trace!("starting to handle: {}", self.name());
        self.move_to_next_state(st).await
    }

    fn name(&self) -> &'static str {
        self.inner_state().name()
    }

    fn is_handling_download(&self) -> bool {
        self.inner_state().is_handling_download()
    }

    fn is_preemptive_state(&self) -> bool {
        self.inner_state().is_preemptive_state()
    }
}

impl State {
    fn new() -> Self {
        State::EntryPoint(EntryPoint {})
    }

    async fn move_to_next_state(
        self,
        context: &mut machine::Context,
    ) -> Result<(Self, machine::StepTransition)> {
        match self {
            State::Park(s) => s.handle(context).await,
            State::EntryPoint(s) => s.handle(context).await,
            State::Poll(s) => s.handle(context).await,
            State::Probe(s) => s.handle_with_callback(context).await,
            State::Validation(s) => s.handle(context).await,
            State::DirectDownload(s) => s.handle(context).await,
            State::PrepareLocalInstall(s) => s.handle(context).await,
            State::Error(s) => s.handle_with_callback(context).await,
            State::Download(s) => s.handle_with_callback_and_report_progress(context).await,
            State::Install(s) => s.handle_with_callback_and_report_progress(context).await,
            State::Reboot(s) => s.handle_with_callback_and_report_progress(context).await,
        }
    }

    fn inner_state(&self) -> &dyn StateChangeImpl {
        match self {
            State::Error(s) => s,
            State::Park(s) => s,
            State::EntryPoint(s) => s,
            State::Poll(s) => s,
            State::Probe(s) => s,
            State::Validation(s) => s,
            State::DirectDownload(s) => s,
            State::PrepareLocalInstall(s) => s,
            State::Download(s) => s,
            State::Install(s) => s,
            State::Reboot(s) => s,
        }
    }
}

/// Runs the state machine up to completion handling all processing
/// states without extra manual work.
///
/// It supports following states, and transitions, as shown in the
/// below diagram:
///
/// ```text
///             .-------------------.
///             |                   v
/// Park <- EntryPoint -> Poll -> Probe -> Download -> Install -> Reboot
///             ^          ^        '          '          '
///             '          '        '          '          '
///             '          `--------'          '          '
///             `-------------------'          '          '
///             `------------------------------'          '
///             `-----------------------------------------'
/// ```
///
/// # Example
/// ```no_run
/// # extern crate updatehub;
/// # use std::path::PathBuf;
/// # async fn run() -> Result<(), updatehub::Error> {
/// use updatehub;
/// use std::path::PathBuf;
///
/// updatehub::logger::init(slog::Level::Info);
/// updatehub::run(&PathBuf::from("/etc/updatehub.conf")).await?;
/// # Ok(())
/// # }
/// ```
pub async fn run(settings: &Path) -> crate::Result<()> {
    crate::logger::start_memory_logging();
    let settings = Settings::load(settings)?;
    let listen_socket = settings.network.listen_socket.clone();
    let mut runtime_settings = RuntimeSettings::load(&settings.storage.runtime_settings)?;
    if !settings.storage.read_only {
        runtime_settings.enable_persistency();
    }
    let firmware = Metadata::from_path(&settings.firmware.metadata)?;

    if let Err(e) = handle_startup_callbacks(&settings, &mut runtime_settings) {
        error!("Failed to handle startup callbacks: {}", e);
    }

    let machine = machine::StateMachine::new(State::new(), settings, runtime_settings, firmware);
    let addr = machine.address();
    async_std::task::spawn_local(machine.start());

    http_api::Api::server(addr).listen(listen_socket).await?;

    info!("Server has gracefully stopped");
    Ok(())
}
<commit_msg>updatehub: Spawn machine's loop on the thread pool<commit_after>// Copyright (C) 2018 O.S. Systems Sofware LTDA
//
// SPDX-License-Identifier: Apache-2.0

#[macro_use]
mod macros;
mod direct_download;
mod download;
mod entry_point;
mod error;
pub(crate) mod install;
pub(crate) mod machine;
mod park;
mod poll;
mod prepare_local_install;
mod probe;
mod reboot;
mod validation;

#[cfg(test)]
mod tests;

use self::{
    direct_download::DirectDownload, download::Download, entry_point::EntryPoint, error::Error,
    install::Install, park::Park, poll::Poll, prepare_local_install::PrepareLocalInstall,
    probe::Probe, reboot::Reboot, validation::Validation,
};
use crate::{
    firmware::{self, Metadata, Transition},
    http_api,
    runtime_settings::RuntimeSettings,
    settings::Settings,
};
use async_trait::async_trait;
use derive_more::{Display, Error, From};
use slog_scope::{error, info, trace, warn};
use std::path::Path;

pub type Result<T> = std::result::Result<T, TransitionError>;

#[derive(Debug, Display, Error, From)]
pub enum TransitionError {
    #[display("some objects are not ready for use")]
    SomeObjectsAreNotReady,
    #[display("signature not found")]
    SignatureNotFound,
    #[display("channel communication as failed")]
    CommunicationFailed,

    Firmware(crate::firmware::Error),
    Installation(crate::object::Error),
    RuntimeSettings(crate::runtime_settings::Error),
    UpdatePackage(crate::update_package::Error),
    Client(cloud::Error),
    Uncompress(compress_tools::Error),
    SerdeJson(serde_json::error::Error),
    Io(std::io::Error),
    NonUtf8(std::string::FromUtf8Error),
    Process(easy_process::Error),
}

#[async_trait]
trait StateChangeImpl {
    async fn handle(
        self,
        context: &mut machine::Context,
    ) -> Result<(State, machine::StepTransition)>;

    fn name(&self) -> &'static str;

    /// All states downloading large files should overwrite this to return true.
    /// That way, a external request to abort download can be heeded.
    fn is_handling_download(&self) -> bool {
        false
    }

    /// A preemptive state is a state whose transition can be yield
    /// to handle a user's request. Any preemptive state should overwrite
    /// this method to return true.
    fn is_preemptive_state(&self) -> bool {
        false
    }
}

#[async_trait]
trait CallbackReporter: Sized + StateChangeImpl {
    async fn handle_with_callback(
        self,
        context: &mut machine::Context,
    ) -> Result<(State, machine::StepTransition)> {
        let transition =
            firmware::state_change_callback(&context.settings.firmware.metadata, self.name())?;

        match transition {
            Transition::Continue => Ok(self.handle(context).await?),
            Transition::Cancel => {
                info!(
                    "cancelling transition to '{}' due to state change callback request",
                    self.name()
                );
                Ok((State::EntryPoint(EntryPoint {}), machine::StepTransition::Immediate))
            }
        }
    }
}

#[async_trait]
trait ProgressReporter: CallbackReporter {
    fn package_uid(&self) -> String;
    fn report_enter_state_name(&self) -> &'static str;
    fn report_leave_state_name(&self) -> &'static str;

    async fn handle_and_report_progress(
        self,
        context: &mut machine::Context,
    ) -> Result<(State, machine::StepTransition)> {
        let server = context.server_address().to_owned();
        let firmware = &context.firmware.clone();
        let package_uid = &self.package_uid();
        let enter_state = self.report_enter_state_name();
        let leave_state = self.report_leave_state_name();
        let api = crate::CloudClient::new(&server);

        let report = |state, previous_state, error_message, current_log| {
            api.report(
                state,
                firmware.as_cloud_metadata(),
                package_uid,
                previous_state,
                error_message,
                current_log,
            )
        };

        if let Err(e) = report(enter_state, None, None, None).await {
            warn!("report failed: {}", e);
        }
        match self.handle(context).await {
            Ok((state, trans)) => {
                if let Err(e) = report(leave_state, None, None, None).await {
                    warn!("report failed: {}", e);
                };
                Ok((state, trans))
            }
            Err(e) => {
                if let Err(e) = report(
                    "error",
                    Some(enter_state),
                    Some(e.to_string()),
                    Some(crate::logger::get_memory_log()),
                )
                .await
                {
                    warn!("report failed: {}", e);
                }
                Err(e)
            }
        }
    }

    async fn handle_with_callback_and_report_progress(
        self,
        context: &mut machine::Context,
    ) -> Result<(State, machine::StepTransition)> {
        let transition =
            firmware::state_change_callback(&context.settings.firmware.metadata, self.name())?;

        match transition {
            Transition::Continue => Ok(self.handle_and_report_progress(context).await?),
            Transition::Cancel => {
                info!(
                    "cancelling transition to '{}' due to state change callback request",
                    self.name()
                );
                Ok((State::EntryPoint(EntryPoint {}), machine::StepTransition::Immediate))
            }
        }
    }
}

#[derive(Debug)]
enum State {
    Park(Park),
    EntryPoint(EntryPoint),
    Poll(Poll),
    Probe(Probe),
    Validation(Validation),
    Download(Download),
    Install(Install),
    Reboot(Reboot),
    DirectDownload(DirectDownload),
    PrepareLocalInstall(PrepareLocalInstall),
    Error(Error),
}

fn handle_startup_callbacks(
    settings: &Settings,
    runtime_settings: &mut RuntimeSettings,
) -> crate::Result<()> {
    if let Some(expected_set) = runtime_settings.update.upgrade_to_installation {
        info!("booting from a recent installation");
        if expected_set == firmware::installation_set::active()?.0 {
            match firmware::validate_callback(&settings.firmware.metadata)? {
                Transition::Cancel => {
                    warn!("validate callback has failed");
                    firmware::installation_set::swap_active()?;
                    warn!("swapped active installation set and running rollback");
                    firmware::rollback_callback(&settings.firmware.metadata)?;
                    runtime_settings.reset_installation_settings()?;
                    easy_process::run("reboot")?;
                }
                Transition::Continue => firmware::installation_set::validate()?,
            }
        }
        runtime_settings.reset_installation_settings()?;
    }
    Ok(())
}

#[async_trait]
impl StateChangeImpl for State {
    async fn handle(self, st: &mut machine::Context) -> Result<(State, machine::StepTransition)> {
        trace!("starting to handle: {}", self.name());
        self.move_to_next_state(st).await
    }

    fn name(&self) -> &'static str {
        self.inner_state().name()
    }

    fn is_handling_download(&self) -> bool {
        self.inner_state().is_handling_download()
    }

    fn is_preemptive_state(&self) -> bool {
        self.inner_state().is_preemptive_state()
    }
}

impl State {
    fn new() -> Self {
        State::EntryPoint(EntryPoint {})
    }

    async fn move_to_next_state(
        self,
        context: &mut machine::Context,
    ) -> Result<(Self, machine::StepTransition)> {
        match self {
            State::Park(s) => s.handle(context).await,
            State::EntryPoint(s) => s.handle(context).await,
            State::Poll(s) => s.handle(context).await,
            State::Probe(s) => s.handle_with_callback(context).await,
            State::Validation(s) => s.handle(context).await,
            State::DirectDownload(s) => s.handle(context).await,
            State::PrepareLocalInstall(s) => s.handle(context).await,
            State::Error(s) => s.handle_with_callback(context).await,
            State::Download(s) => s.handle_with_callback_and_report_progress(context).await,
            State::Install(s) => s.handle_with_callback_and_report_progress(context).await,
            State::Reboot(s) => s.handle_with_callback_and_report_progress(context).await,
        }
    }

    fn inner_state(&self) -> &dyn StateChangeImpl {
        match self {
            State::Error(s) => s,
            State::Park(s) => s,
            State::EntryPoint(s) => s,
            State::Poll(s) => s,
            State::Probe(s) => s,
            State::Validation(s) => s,
            State::DirectDownload(s) => s,
            State::PrepareLocalInstall(s) => s,
            State::Download(s) => s,
            State::Install(s) => s,
            State::Reboot(s) => s,
        }
    }
}

/// Runs the state machine up to completion handling all processing
/// states without extra manual work.
///
/// It supports following states, and transitions, as shown in the
/// below diagram:
///
/// ```text
///             .-------------------.
///             |                   v
/// Park <- EntryPoint -> Poll -> Probe -> Download -> Install -> Reboot
///             ^          ^        '          '          '
///             '          '        '          '          '
///             '          `--------'          '          '
///             `-------------------'          '          '
///             `------------------------------'          '
///             `-----------------------------------------'
/// ```
///
/// # Example
/// ```no_run
/// # extern crate updatehub;
/// # use std::path::PathBuf;
/// # async fn run() -> Result<(), updatehub::Error> {
/// use updatehub;
/// use std::path::PathBuf;
///
/// updatehub::logger::init(slog::Level::Info);
/// updatehub::run(&PathBuf::from("/etc/updatehub.conf")).await?;
/// # Ok(())
/// # }
/// ```
pub async fn run(settings: &Path) -> crate::Result<()> {
    crate::logger::start_memory_logging();
    let settings = Settings::load(settings)?;
    let listen_socket = settings.network.listen_socket.clone();
    let mut runtime_settings = RuntimeSettings::load(&settings.storage.runtime_settings)?;
    if !settings.storage.read_only {
        runtime_settings.enable_persistency();
    }
    let firmware = Metadata::from_path(&settings.firmware.metadata)?;

    if let Err(e) = handle_startup_callbacks(&settings, &mut runtime_settings) {
        error!("Failed to handle startup callbacks: {}", e);
    }

    let machine = machine::StateMachine::new(State::new(), settings, runtime_settings, firmware);
    let addr = machine.address();
    async_std::task::spawn(machine.start());

    http_api::Api::server(addr).listen(listen_socket).await?;

    info!("Server has gracefully stopped");
    Ok(())
}
<|endoftext|>


<commit_before>#![deny(missing_docs)]

use std::env;
use std::fmt;
use std::path::Path;
use std::path::PathBuf;

/// Error returned when a binary is not available.
#[derive(Debug)]
pub struct Error {
    os: &'static str,
    arch: &'static str,
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "protoc binary cannot be found for platform {}-{}",
            self.os, self.arch
        )
    }
}

impl std::error::Error for Error {}

/// Return a path to `protoc` binary.
///
/// This function returns an error when binary is not available for
/// the current operating system and architecture.
pub fn protoc_bin_path() -> Result<PathBuf, Error> {
    let protoc_bin_name = match (env::consts::OS, env::consts::ARCH) {
        ("linux", "x86") => "protoc-linux-x86_32",
        ("linux", "x86_64") => "protoc-linux-x86_64",
        ("linux", "aarch64") => "protoc-linux-aarch_64",
        ("linux", "powerpc64") => "protoc-linux-ppcle_64",
        ("macos", "x86_64") => "protoc-osx-x86_64",
        ("windows", _) => "protoc-win32.exe",
        (os, arch) => return Err(Error { os, arch }),
    };
    let protoc_bin_path = Path::new(env!("CARGO_MANIFEST_DIR"))
        .join("bin")
        .join(protoc_bin_name);
    assert!(
        protoc_bin_path.exists(),
        "internal: protoc not found {}",
        protoc_bin_name
    );
    Ok(protoc_bin_path)
}

#[cfg(test)]
mod test {
    use std::io::Read;
    use std::process;

    #[test]
    fn smoke() {
        let process = process::Command::new(super::protoc_bin_path().unwrap())
            .arg("--version")
            .stdin(process::Stdio::null())
            .stdout(process::Stdio::piped())
            .spawn()
            .unwrap();
        let mut stdout = String::new();
        process.stdout.unwrap().read_to_string(&mut stdout).unwrap();
        assert!(stdout.contains("libprotoc"), "stdout is: {:?}", stdout)
    }
}
<commit_msg>Crate doc<commit_after>//! `protoc` binary downloaded and stored inside the crate.
//!
//! Can be used to avoid downloading and installing `protoc` binary.

#![deny(missing_docs)]

use std::env;
use std::fmt;
use std::path::Path;
use std::path::PathBuf;

/// Error returned when a binary is not available.
#[derive(Debug)]
pub struct Error {
    os: &'static str,
    arch: &'static str,
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "protoc binary cannot be found for platform {}-{}",
            self.os, self.arch
        )
    }
}

impl std::error::Error for Error {}

/// Return a path to `protoc` binary.
///
/// This function returns an error when binary is not available for
/// the current operating system and architecture.
pub fn protoc_bin_path() -> Result<PathBuf, Error> {
    let protoc_bin_name = match (env::consts::OS, env::consts::ARCH) {
        ("linux", "x86") => "protoc-linux-x86_32",
        ("linux", "x86_64") => "protoc-linux-x86_64",
        ("linux", "aarch64") => "protoc-linux-aarch_64",
        ("linux", "powerpc64") => "protoc-linux-ppcle_64",
        ("macos", "x86_64") => "protoc-osx-x86_64",
        ("windows", _) => "protoc-win32.exe",
        (os, arch) => return Err(Error { os, arch }),
    };
    let protoc_bin_path = Path::new(env!("CARGO_MANIFEST_DIR"))
        .join("bin")
        .join(protoc_bin_name);
    assert!(
        protoc_bin_path.exists(),
        "internal: protoc not found {}",
        protoc_bin_name
    );
    Ok(protoc_bin_path)
}

#[cfg(test)]
mod test {
    use std::io::Read;
    use std::process;

    #[test]
    fn smoke() {
        let process = process::Command::new(super::protoc_bin_path().unwrap())
            .arg("--version")
            .stdin(process::Stdio::null())
            .stdout(process::Stdio::piped())
            .spawn()
            .unwrap();
        let mut stdout = String::new();
        process.stdout.unwrap().read_to_string(&mut stdout).unwrap();
        assert!(stdout.contains("libprotoc"), "stdout is: {:?}", stdout)
    }
}
<|endoftext|>