<commit_before>//! Diesel is an ORM and query builder designed to reduce the boilerplate for database
//! interactions. [A getting started guide](http://diesel.rs/guides/getting-started/) can be
//! found on our website.
#![cfg_attr(feature = "unstable", feature(specialization))]
// Built-in Lints
#![deny(warnings, missing_debug_implementations, missing_copy_implementations)]
// Clippy lints
#![cfg_attr(feature = "clippy", allow(unstable_features))]
#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../clippy.toml")))]
#![cfg_attr(feature = "clippy",
           allow(option_map_unwrap_or_else, option_map_unwrap_or, match_same_arms,
                   type_complexity))]
#![cfg_attr(feature = "clippy",
           warn(option_unwrap_used, result_unwrap_used, print_stdout,
                  wrong_pub_self_convention, mut_mut, non_ascii_literal, similar_names,
                  unicode_not_nfc, enum_glob_use, if_not_else, items_after_statements,
                  used_underscore_binding))]
#![cfg_attr(all(test, feature = "clippy"), allow(option_unwrap_used, result_unwrap_used))]

#[cfg(feature = "postgres")]
#[macro_use]
extern crate bitflags;
extern crate byteorder;

#[macro_use]
mod macros;

#[cfg(test)]
#[macro_use]
extern crate cfg_if;

#[cfg(test)]
pub mod test_helpers;

pub mod associations;
pub mod backend;
pub mod connection;
#[macro_use]
pub mod expression;
pub mod expression_methods;
#[doc(hidden)]
pub mod insertable;
pub mod query_builder;
#[macro_use]
pub mod types;

#[deprecated(since = "0.10.0", note = "use `insertable` instead")]
#[cfg(feature = "with-deprecated")]
pub use self::insertable as persistable;

#[cfg(feature = "mysql")]
pub mod mysql;
#[cfg(feature = "postgres")]
pub mod pg;
#[cfg(feature = "sqlite")]
pub mod sqlite;

pub mod migrations;
mod query_dsl;
pub mod query_source;
pub mod result;
pub mod row;
mod util;

pub mod dsl {
    //! Includes various helper types and bare functions which are named too
    //! generically to be included in prelude, but are often used when using Diesel.

    #[doc(inline)]
    pub use helper_types::*;

    #[doc(inline)]
    pub use expression::dsl::*;
}

pub mod helper_types {
    //! Provide helper types for concisely writing the return type of functions.
    //! As with iterators, it is unfortunately difficult to return a partially
    //! constructed query without exposing the exact implementation of the
    //! function. Without higher kinded types, these various DSLs can't be
    //! combined into a single trait for boxing purposes.
    //!
    //! All types here are in the form `<FirstType as
    //! DslName<OtherTypes>>::Output`. So the return type of
    //! `users.filter(first_name.eq("John")).order(last_name.asc()).limit(10)` would
    //! be `Limit<Order<FindBy<users, first_name, &str>, Asc<last_name>>>`
    use super::query_dsl::*;
    use super::query_source::joins;

    #[doc(inline)]
    pub use expression::helper_types::*;

    /// Represents the return type of `.select(selection)`
    pub type Select<Source, Selection> = <Source as SelectDsl<Selection>>::Output;

    /// Represents the return type of `.filter(predicate)`
    pub type Filter<Source, Predicate> = <Source as FilterDsl<Predicate>>::Output;

    /// Represents the return type of `.filter(lhs.eq(rhs))`
    pub type FindBy<Source, Column, Value> = Filter<Source, Eq<Column, Value>>;

    /// Represents the return type of `.for_update()`
    pub type ForUpdate<Source> = <Source as ForUpdateDsl>::Output;

    /// Represents the return type of `.find(pk)`
    pub type Find<Source, PK> = <Source as FindDsl<PK>>::Output;

    /// Represents the return type of `.order(ordering)`
    pub type Order<Source, Ordering> = <Source as OrderDsl<Ordering>>::Output;

    /// Represents the return type of `.limit()`
    pub type Limit<Source> = <Source as LimitDsl>::Output;

    /// Represents the return type of `.offset()`
    pub type Offset<Source> = <Source as OffsetDsl>::Output;

    /// Represents the return type of `.inner_join(rhs)`
    pub type InnerJoin<Source, Rhs> = <Source as JoinWithImplicitOnClause<
        Rhs,
        joins::Inner,
    >>::Output;

    /// Represents the return type of `.left_join(rhs)`
    pub type LeftJoin<Source, Rhs> = <Source as JoinWithImplicitOnClause<
        Rhs,
        joins::LeftOuter,
    >>::Output;

    use super::associations::HasTable;
    use super::query_builder::{AsChangeset, IntoUpdateTarget, UpdateStatement};
    /// Represents the return type of `update(lhs).set(rhs)`
    pub type Update<Target, Changes> = UpdateStatement<
        <Target as HasTable>::Table,
        <Target as IntoUpdateTarget>::WhereClause,
        <Changes as AsChangeset>::Changeset,
    >;
}

pub mod prelude {
    //! Re-exports important traits and types. Meant to be glob imported when using Diesel.
    pub use associations::{GroupedBy, Identifiable};
    pub use connection::Connection;
    pub use expression::{AppearsOnTable, BoxableExpression, Expression, IntoSql,
                         SelectableExpression};
    pub use expression_methods::*;
    #[doc(inline)]
    pub use insertable::Insertable;
    pub use query_dsl::*;
    pub use query_source::{Column, JoinTo, QuerySource, Queryable, Table};
    pub use result::{ConnectionError, ConnectionResult, OptionalExtension, QueryResult};

    #[cfg(feature = "postgres")]
    pub use pg::PgConnection;
    #[cfg(feature = "sqlite")]
    pub use sqlite::SqliteConnection;
    #[cfg(feature = "mysql")]
    pub use mysql::MysqlConnection;
}

pub use prelude::*;
#[doc(inline)]
pub use query_builder::debug_query;
#[doc(inline)]
pub use query_builder::functions::{delete, insert_into, replace_into, select, update};
#[cfg(feature = "with-deprecated")]
#[doc(inline)]
#[allow(deprecated)]
pub use query_builder::functions::{insert, insert_default_values};
#[cfg(all(feature = "sqlite", feature = "with-deprecated"))]
#[allow(deprecated)]
pub use sqlite::query_builder::functions::*;
pub use result::Error::NotFound;
#[doc(inline)]
pub use types::structs::data_types;
<commit_msg>Add links to API landing page (#1198)<commit_after>//! # Diesel
//!
//! Diesel is an ORM and query builder designed to reduce the boilerplate for database interactions.
//!
//! * [Homepage](http://diesel.rs/)
//! * [Getting Started Guide](http://diesel.rs/guides/getting-started/)
//! * [Source Code](https://github.com/diesel-rs/diesel/)
//!
//! ## Crates
//!
//! * **Diesel**: core libary, documented here
//! * [**Diesel Codegen**](https://github.com/diesel-rs/diesel/tree/master/diesel_codegen): library of procedural macros
//! * [**Diesel CLI**](https://github.com/diesel-rs/diesel/tree/master/diesel_cli): command line tool that aids in managing your database schema
//!
#![cfg_attr(feature = "unstable", feature(specialization))]
// Built-in Lints
#![deny(warnings, missing_debug_implementations, missing_copy_implementations)]
// Clippy lints
#![cfg_attr(feature = "clippy", allow(unstable_features))]
#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../clippy.toml")))]
#![cfg_attr(feature = "clippy",
           allow(option_map_unwrap_or_else, option_map_unwrap_or, match_same_arms,
                   type_complexity))]
#![cfg_attr(feature = "clippy",
           warn(option_unwrap_used, result_unwrap_used, print_stdout,
                  wrong_pub_self_convention, mut_mut, non_ascii_literal, similar_names,
                  unicode_not_nfc, enum_glob_use, if_not_else, items_after_statements,
                  used_underscore_binding))]
#![cfg_attr(all(test, feature = "clippy"), allow(option_unwrap_used, result_unwrap_used))]

#[cfg(feature = "postgres")]
#[macro_use]
extern crate bitflags;
extern crate byteorder;

#[macro_use]
mod macros;

#[cfg(test)]
#[macro_use]
extern crate cfg_if;

#[cfg(test)]
pub mod test_helpers;

pub mod associations;
pub mod backend;
pub mod connection;
#[macro_use]
pub mod expression;
pub mod expression_methods;
#[doc(hidden)]
pub mod insertable;
pub mod query_builder;
#[macro_use]
pub mod types;

#[deprecated(since = "0.10.0", note = "use `insertable` instead")]
#[cfg(feature = "with-deprecated")]
pub use self::insertable as persistable;

#[cfg(feature = "mysql")]
pub mod mysql;
#[cfg(feature = "postgres")]
pub mod pg;
#[cfg(feature = "sqlite")]
pub mod sqlite;

pub mod migrations;
mod query_dsl;
pub mod query_source;
pub mod result;
pub mod row;
mod util;

pub mod dsl {
    //! Includes various helper types and bare functions which are named too
    //! generically to be included in prelude, but are often used when using Diesel.

    #[doc(inline)]
    pub use helper_types::*;

    #[doc(inline)]
    pub use expression::dsl::*;
}

pub mod helper_types {
    //! Provide helper types for concisely writing the return type of functions.
    //! As with iterators, it is unfortunately difficult to return a partially
    //! constructed query without exposing the exact implementation of the
    //! function. Without higher kinded types, these various DSLs can't be
    //! combined into a single trait for boxing purposes.
    //!
    //! All types here are in the form `<FirstType as
    //! DslName<OtherTypes>>::Output`. So the return type of
    //! `users.filter(first_name.eq("John")).order(last_name.asc()).limit(10)` would
    //! be `Limit<Order<FindBy<users, first_name, &str>, Asc<last_name>>>`
    use super::query_dsl::*;
    use super::query_source::joins;

    #[doc(inline)]
    pub use expression::helper_types::*;

    /// Represents the return type of `.select(selection)`
    pub type Select<Source, Selection> = <Source as SelectDsl<Selection>>::Output;

    /// Represents the return type of `.filter(predicate)`
    pub type Filter<Source, Predicate> = <Source as FilterDsl<Predicate>>::Output;

    /// Represents the return type of `.filter(lhs.eq(rhs))`
    pub type FindBy<Source, Column, Value> = Filter<Source, Eq<Column, Value>>;

    /// Represents the return type of `.for_update()`
    pub type ForUpdate<Source> = <Source as ForUpdateDsl>::Output;

    /// Represents the return type of `.find(pk)`
    pub type Find<Source, PK> = <Source as FindDsl<PK>>::Output;

    /// Represents the return type of `.order(ordering)`
    pub type Order<Source, Ordering> = <Source as OrderDsl<Ordering>>::Output;

    /// Represents the return type of `.limit()`
    pub type Limit<Source> = <Source as LimitDsl>::Output;

    /// Represents the return type of `.offset()`
    pub type Offset<Source> = <Source as OffsetDsl>::Output;

    /// Represents the return type of `.inner_join(rhs)`
    pub type InnerJoin<Source, Rhs> = <Source as JoinWithImplicitOnClause<
        Rhs,
        joins::Inner,
    >>::Output;

    /// Represents the return type of `.left_join(rhs)`
    pub type LeftJoin<Source, Rhs> = <Source as JoinWithImplicitOnClause<
        Rhs,
        joins::LeftOuter,
    >>::Output;

    use super::associations::HasTable;
    use super::query_builder::{AsChangeset, IntoUpdateTarget, UpdateStatement};
    /// Represents the return type of `update(lhs).set(rhs)`
    pub type Update<Target, Changes> = UpdateStatement<
        <Target as HasTable>::Table,
        <Target as IntoUpdateTarget>::WhereClause,
        <Changes as AsChangeset>::Changeset,
    >;
}

pub mod prelude {
    //! Re-exports important traits and types. Meant to be glob imported when using Diesel.
    pub use associations::{GroupedBy, Identifiable};
    pub use connection::Connection;
    pub use expression::{AppearsOnTable, BoxableExpression, Expression, IntoSql,
                         SelectableExpression};
    pub use expression_methods::*;
    #[doc(inline)]
    pub use insertable::Insertable;
    pub use query_dsl::*;
    pub use query_source::{Column, JoinTo, QuerySource, Queryable, Table};
    pub use result::{ConnectionError, ConnectionResult, OptionalExtension, QueryResult};

    #[cfg(feature = "postgres")]
    pub use pg::PgConnection;
    #[cfg(feature = "sqlite")]
    pub use sqlite::SqliteConnection;
    #[cfg(feature = "mysql")]
    pub use mysql::MysqlConnection;
}

pub use prelude::*;
#[doc(inline)]
pub use query_builder::debug_query;
#[doc(inline)]
pub use query_builder::functions::{delete, insert_into, replace_into, select, update};
#[cfg(feature = "with-deprecated")]
#[doc(inline)]
#[allow(deprecated)]
pub use query_builder::functions::{insert, insert_default_values};
#[cfg(all(feature = "sqlite", feature = "with-deprecated"))]
#[allow(deprecated)]
pub use sqlite::query_builder::functions::*;
pub use result::Error::NotFound;
#[doc(inline)]
pub use types::structs::data_types;
<|endoftext|>


<commit_before>// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Asynchronous console device which implementation can be shared by VMM and vhost-user.

use std::{collections::VecDeque, io, sync::Arc, thread};

use anyhow::Context;
use base::{error, warn, AsRawDescriptor, Event, FileSync, RawDescriptor};
use cros_async::{select2, AsyncResult, EventAsync, Executor, IntoAsync, IoSourceExt};
use data_model::DataInit;
use futures::FutureExt;
use hypervisor::ProtectionType;
use sync::Mutex;
use vm_memory::GuestMemory;
use vmm_vhost::message::VhostUserVirtioFeatures;

use crate::{
    serial_device::SerialInput,
    virtio::{
        self, async_device::AsyncQueueState, async_utils, base_features, copy_config,
        virtio_console_config, ConsoleError, DeviceType, Interrupt, Queue, SignalableInterrupt,
        VirtioDevice,
    },
    SerialDevice,
};

use super::{handle_input, process_transmit_queue, QUEUE_SIZES};

/// Wrapper that makes any `SerialInput` usable as an async source by providing an implementation of
/// `IntoAsync`.
struct AsyncSerialInput(Box<dyn SerialInput>);
impl AsRawDescriptor for AsyncSerialInput {
    fn as_raw_descriptor(&self) -> RawDescriptor {
        self.0.get_read_notifier().as_raw_descriptor()
    }
}
impl IntoAsync for AsyncSerialInput {}

async fn run_tx_queue<I: SignalableInterrupt>(
    mut queue: virtio::Queue,
    mem: GuestMemory,
    doorbell: I,
    kick_evt: EventAsync,
    output: &mut Box<dyn io::Write + Send>,
) {
    loop {
        if let Err(e) = kick_evt.next_val().await {
            error!("Failed to read kick event for tx queue: {}", e);
            break;
        }
        process_transmit_queue(&mem, &doorbell, &mut queue, output.as_mut());
    }
}

async fn run_rx_queue<I: SignalableInterrupt>(
    mut queue: virtio::Queue,
    mem: GuestMemory,
    doorbell: I,
    kick_evt: EventAsync,
    input: &dyn IoSourceExt<AsyncSerialInput>,
) {
    // Staging buffer, required because of `handle_input`'s API. We can probably remove this once
    // the regular virtio device is switched to async.
    let mut in_buffer = VecDeque::<u8>::new();
    let mut rx_buf = vec![0u8; 4096];
    let mut input_offset = 0u64;

    loop {
        match input.read_to_vec(Some(input_offset), rx_buf).await {
            // Input source has closed.
            Ok((0, _)) => break,
            Ok((size, v)) => {
                in_buffer.extend(&v[0..size]);
                input_offset += size as u64;
                rx_buf = v;
            }
            Err(e) => {
                error!("Failed to read console input: {}", e);
                return;
            }
        }

        // Submit all the data obtained during this read.
        while !in_buffer.is_empty() {
            match handle_input(&mem, &doorbell, &mut in_buffer, &mut queue) {
                Ok(()) => {}
                Err(ConsoleError::RxDescriptorsExhausted) => {
                    // Wait until a descriptor becomes available and try again.
                    if let Err(e) = kick_evt.next_val().await {
                        error!("Failed to read kick event for rx queue: {}", e);
                        return;
                    }
                }
            }
        }
    }
}

pub struct ConsoleDevice {
    input: Option<AsyncQueueState<AsyncSerialInput>>,
    output: Option<AsyncQueueState<Box<dyn io::Write + Send>>>,
    avail_features: u64,
}

impl ConsoleDevice {
    pub fn avail_features(&self) -> u64 {
        self.avail_features
    }

    pub fn start_receive_queue<I: SignalableInterrupt + 'static>(
        &mut self,
        ex: &Executor,
        mem: GuestMemory,
        queue: virtio::Queue,
        doorbell: I,
        kick_evt: Event,
    ) -> anyhow::Result<()> {
        let input_queue = match self.input.as_mut() {
            Some(input_queue) => input_queue,
            None => return Ok(()),
        };

        let kick_evt =
            EventAsync::new(kick_evt, ex).context("Failed to create EventAsync for kick_evt")?;

        let closure_ex = ex.clone();
        let rx_future = move |input, abort| {
            let async_input = closure_ex
                .async_from(input)
                .context("failed to create async input")?;

            Ok(async move {
                select2(
                    run_rx_queue(queue, mem, doorbell, kick_evt, async_input.as_ref())
                        .boxed_local(),
                    abort,
                )
                .await;

                async_input.into_source()
            })
        };

        input_queue.start(ex, rx_future)
    }

    pub fn stop_receive_queue(&mut self) -> AsyncResult<bool> {
        if let Some(queue) = self.input.as_mut() {
            queue.stop()
        } else {
            Ok(false)
        }
    }

    pub fn start_transmit_queue<I: SignalableInterrupt + 'static>(
        &mut self,
        ex: &Executor,
        mem: GuestMemory,
        queue: virtio::Queue,
        doorbell: I,
        kick_evt: Event,
    ) -> anyhow::Result<()> {
        let output_queue = match self.output.as_mut() {
            Some(output_queue) => output_queue,
            None => return Ok(()),
        };

        let kick_evt =
            EventAsync::new(kick_evt, ex).context("Failed to create EventAsync for kick_evt")?;

        let tx_future = |mut output, abort| {
            Ok(async move {
                select2(
                    run_tx_queue(queue, mem, doorbell, kick_evt, &mut output).boxed_local(),
                    abort,
                )
                .await;

                output
            })
        };

        output_queue.start(ex, tx_future)
    }

    pub fn stop_transmit_queue(&mut self) -> AsyncResult<bool> {
        if let Some(queue) = self.output.as_mut() {
            queue.stop()?;
            Ok(true)
        } else {
            Ok(false)
        }
    }
}

impl SerialDevice for ConsoleDevice {
    fn new(
        protected_vm: ProtectionType,
        _evt: Event,
        input: Option<Box<dyn SerialInput>>,
        output: Option<Box<dyn io::Write + Send>>,
        _sync: Option<Box<dyn FileSync + Send>>,
        _out_timestamp: bool,
        _keep_rds: Vec<RawDescriptor>,
    ) -> ConsoleDevice {
        let avail_features =
            virtio::base_features(protected_vm) | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits();
        ConsoleDevice {
            input: input.map(AsyncSerialInput).map(AsyncQueueState::Stopped),
            output: output.map(AsyncQueueState::Stopped),
            avail_features,
        }
    }
}

enum VirtioConsoleState {
    Stopped(ConsoleDevice),
    Running {
        kill_evt: Event,
        worker_thread: thread::JoinHandle<anyhow::Result<ConsoleDevice>>,
    },
    Broken,
}

/// Virtio console device.
pub struct AsyncConsole {
    state: VirtioConsoleState,
    base_features: u64,
    keep_rds: Vec<RawDescriptor>,
}

impl SerialDevice for AsyncConsole {
    fn new(
        protected_vm: ProtectionType,
        evt: Event,
        input: Option<Box<dyn SerialInput>>,
        output: Option<Box<dyn io::Write + Send>>,
        sync: Option<Box<dyn FileSync + Send>>,
        out_timestamp: bool,
        keep_rds: Vec<RawDescriptor>,
    ) -> AsyncConsole {
        AsyncConsole {
            state: VirtioConsoleState::Stopped(ConsoleDevice::new(
                protected_vm,
                evt,
                input,
                output,
                sync,
                out_timestamp,
                Default::default(),
            )),
            base_features: base_features(protected_vm),
            keep_rds,
        }
    }
}

impl Drop for AsyncConsole {
    fn drop(&mut self) {
        let _ = self.reset();
    }
}

impl VirtioDevice for AsyncConsole {
    fn keep_rds(&self) -> Vec<RawDescriptor> {
        self.keep_rds.clone()
    }

    fn features(&self) -> u64 {
        self.base_features
    }

    fn device_type(&self) -> DeviceType {
        DeviceType::Console
    }

    fn queue_max_sizes(&self) -> &[u16] {
        QUEUE_SIZES
    }

    fn read_config(&self, offset: u64, data: &mut [u8]) {
        let config = virtio_console_config {
            max_nr_ports: 1.into(),
            ..Default::default()
        };
        copy_config(data, 0, config.as_slice(), offset);
    }

    fn activate(
        &mut self,
        mem: GuestMemory,
        interrupt: Interrupt,
        mut queues: Vec<Queue>,
        mut queue_evts: Vec<Event>,
    ) {
        if queues.len() < 2 || queue_evts.len() < 2 {
            return;
        }

        // Reset the device if it was already running.
        if matches!(self.state, VirtioConsoleState::Running { .. }) {
            self.reset();
        }

        let state = std::mem::replace(&mut self.state, VirtioConsoleState::Broken);
        let console = match state {
            VirtioConsoleState::Running { .. } => {
                error!("device should not be running here. This is a bug.");
                return;
            }
            VirtioConsoleState::Stopped(console) => console,
            VirtioConsoleState::Broken => {
                warn!("device is broken and cannot be activated");
                return;
            }
        };

        let (self_kill_evt, kill_evt) = match Event::new().and_then(|e| Ok((e.try_clone()?, e))) {
            Ok(v) => v,
            Err(e) => {
                error!("failed creating kill Event pair: {}", e);
                return;
            }
        };

        let ex = Executor::new().expect("failed to create an executor");
        let receive_queue = queues.remove(0);
        let receive_evt = queue_evts.remove(0);
        let transmit_queue = queues.remove(0);
        let transmit_evt = queue_evts.remove(0);

        let worker_result = thread::Builder::new()
            .name("virtio_console".to_string())
            .spawn(move || {
                let mut console = console;
                let interrupt = Arc::new(Mutex::new(interrupt));

                console.start_receive_queue(
                    &ex,
                    mem.clone(),
                    receive_queue,
                    interrupt.clone(),
                    receive_evt,
                )?;

                console.start_transmit_queue(&ex, mem, transmit_queue, interrupt, transmit_evt)?;

                // Run until the kill event is signaled and cancel all tasks.
                ex.run_until(async {
                    async_utils::await_and_exit(&ex, kill_evt).await?;
                    if let Some(input) = console.input.as_mut() {
                        input.stop().context("failed to stop rx queue")?;
                    }
                    if let Some(output) = console.output.as_mut() {
                        output.stop().context("failed to stop tx queue")?;
                    }

                    Ok(console)
                })?
            });

        match worker_result {
            Err(e) => error!("failed to spawn virtio_console worker: {}", e),
            Ok(join_handle) => {
                self.state = VirtioConsoleState::Running {
                    kill_evt: self_kill_evt,
                    worker_thread: join_handle,
                };
            }
        }
    }

    fn reset(&mut self) -> bool {
        match std::mem::replace(&mut self.state, VirtioConsoleState::Broken) {
            // Stopped console is already in reset state.
            state @ VirtioConsoleState::Stopped(_) => {
                self.state = state;
                true
            }
            // Stop the worker thread and go back to `Stopped` state.
            VirtioConsoleState::Running {
                kill_evt,
                worker_thread,
            } => match kill_evt.write(1) {
                Ok(_) => {
                    let thread_res = match worker_thread.join() {
                        Ok(thread_res) => thread_res,
                        Err(_) => {
                            error!("worker thread has panicked");
                            return false;
                        }
                    };

                    match thread_res {
                        Ok(console) => {
                            self.state = VirtioConsoleState::Stopped(console);
                            true
                        }
                        Err(e) => {
                            error!("worker thread returned an error: {}", e);
                            false
                        }
                    }
                }
                Err(e) => {
                    error!("error while requesting worker thread to stop: {}", e);
                    error!("the worker thread will keep running");
                    false
                }
            },
            // We are broken and cannot reset properly.
            VirtioConsoleState::Broken => false,
        }
    }
}
<commit_msg>devices: virtio: console: Fix stuck vm when virtio-console has no output<commit_after>// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Asynchronous console device which implementation can be shared by VMM and vhost-user.

use std::{collections::VecDeque, io, sync::Arc, thread};

use anyhow::Context;
use base::{error, warn, AsRawDescriptor, Event, FileSync, RawDescriptor};
use cros_async::{select2, AsyncResult, EventAsync, Executor, IntoAsync, IoSourceExt};
use data_model::DataInit;
use futures::FutureExt;
use hypervisor::ProtectionType;
use sync::Mutex;
use vm_memory::GuestMemory;
use vmm_vhost::message::VhostUserVirtioFeatures;

use crate::{
    serial_device::SerialInput,
    virtio::{
        self, async_device::AsyncQueueState, async_utils, base_features, copy_config,
        virtio_console_config, ConsoleError, DeviceType, Interrupt, Queue, SignalableInterrupt,
        VirtioDevice,
    },
    SerialDevice,
};

use super::{handle_input, process_transmit_queue, QUEUE_SIZES};

/// Wrapper that makes any `SerialInput` usable as an async source by providing an implementation of
/// `IntoAsync`.
struct AsyncSerialInput(Box<dyn SerialInput>);
impl AsRawDescriptor for AsyncSerialInput {
    fn as_raw_descriptor(&self) -> RawDescriptor {
        self.0.get_read_notifier().as_raw_descriptor()
    }
}
impl IntoAsync for AsyncSerialInput {}

async fn run_tx_queue<I: SignalableInterrupt>(
    mut queue: virtio::Queue,
    mem: GuestMemory,
    doorbell: I,
    kick_evt: EventAsync,
    output: &mut Box<dyn io::Write + Send>,
) {
    loop {
        if let Err(e) = kick_evt.next_val().await {
            error!("Failed to read kick event for tx queue: {}", e);
            break;
        }
        process_transmit_queue(&mem, &doorbell, &mut queue, output.as_mut());
    }
}

async fn run_rx_queue<I: SignalableInterrupt>(
    mut queue: virtio::Queue,
    mem: GuestMemory,
    doorbell: I,
    kick_evt: EventAsync,
    input: &dyn IoSourceExt<AsyncSerialInput>,
) {
    // Staging buffer, required because of `handle_input`'s API. We can probably remove this once
    // the regular virtio device is switched to async.
    let mut in_buffer = VecDeque::<u8>::new();
    let mut rx_buf = vec![0u8; 4096];
    let mut input_offset = 0u64;

    loop {
        match input.read_to_vec(Some(input_offset), rx_buf).await {
            // Input source has closed.
            Ok((0, _)) => break,
            Ok((size, v)) => {
                in_buffer.extend(&v[0..size]);
                input_offset += size as u64;
                rx_buf = v;
            }
            Err(e) => {
                error!("Failed to read console input: {}", e);
                return;
            }
        }

        // Submit all the data obtained during this read.
        while !in_buffer.is_empty() {
            match handle_input(&mem, &doorbell, &mut in_buffer, &mut queue) {
                Ok(()) => {}
                Err(ConsoleError::RxDescriptorsExhausted) => {
                    // Wait until a descriptor becomes available and try again.
                    if let Err(e) = kick_evt.next_val().await {
                        error!("Failed to read kick event for rx queue: {}", e);
                        return;
                    }
                }
            }
        }
    }
}

pub struct ConsoleDevice {
    input: Option<AsyncQueueState<AsyncSerialInput>>,
    output: AsyncQueueState<Box<dyn io::Write + Send>>,
    avail_features: u64,
}

impl ConsoleDevice {
    pub fn avail_features(&self) -> u64 {
        self.avail_features
    }

    pub fn start_receive_queue<I: SignalableInterrupt + 'static>(
        &mut self,
        ex: &Executor,
        mem: GuestMemory,
        queue: virtio::Queue,
        doorbell: I,
        kick_evt: Event,
    ) -> anyhow::Result<()> {
        let input_queue = match self.input.as_mut() {
            Some(input_queue) => input_queue,
            None => return Ok(()),
        };

        let kick_evt =
            EventAsync::new(kick_evt, ex).context("Failed to create EventAsync for kick_evt")?;

        let closure_ex = ex.clone();
        let rx_future = move |input, abort| {
            let async_input = closure_ex
                .async_from(input)
                .context("failed to create async input")?;

            Ok(async move {
                select2(
                    run_rx_queue(queue, mem, doorbell, kick_evt, async_input.as_ref())
                        .boxed_local(),
                    abort,
                )
                .await;

                async_input.into_source()
            })
        };

        input_queue.start(ex, rx_future)
    }

    pub fn stop_receive_queue(&mut self) -> AsyncResult<bool> {
        if let Some(queue) = self.input.as_mut() {
            queue.stop()
        } else {
            Ok(false)
        }
    }

    pub fn start_transmit_queue<I: SignalableInterrupt + 'static>(
        &mut self,
        ex: &Executor,
        mem: GuestMemory,
        queue: virtio::Queue,
        doorbell: I,
        kick_evt: Event,
    ) -> anyhow::Result<()> {
        let kick_evt =
            EventAsync::new(kick_evt, ex).context("Failed to create EventAsync for kick_evt")?;

        let tx_future = |mut output, abort| {
            Ok(async move {
                select2(
                    run_tx_queue(queue, mem, doorbell, kick_evt, &mut output).boxed_local(),
                    abort,
                )
                .await;

                output
            })
        };

        self.output.start(ex, tx_future)
    }

    pub fn stop_transmit_queue(&mut self) -> AsyncResult<bool> {
        self.output.stop()
    }
}

impl SerialDevice for ConsoleDevice {
    fn new(
        protected_vm: ProtectionType,
        _evt: Event,
        input: Option<Box<dyn SerialInput>>,
        output: Option<Box<dyn io::Write + Send>>,
        _sync: Option<Box<dyn FileSync + Send>>,
        _out_timestamp: bool,
        _keep_rds: Vec<RawDescriptor>,
    ) -> ConsoleDevice {
        let avail_features =
            virtio::base_features(protected_vm) | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits();
        ConsoleDevice {
            input: input.map(AsyncSerialInput).map(AsyncQueueState::Stopped),
            output: AsyncQueueState::Stopped(output.unwrap_or_else(|| Box::new(io::sink()))),
            avail_features,
        }
    }
}

enum VirtioConsoleState {
    Stopped(ConsoleDevice),
    Running {
        kill_evt: Event,
        worker_thread: thread::JoinHandle<anyhow::Result<ConsoleDevice>>,
    },
    Broken,
}

/// Virtio console device.
pub struct AsyncConsole {
    state: VirtioConsoleState,
    base_features: u64,
    keep_rds: Vec<RawDescriptor>,
}

impl SerialDevice for AsyncConsole {
    fn new(
        protected_vm: ProtectionType,
        evt: Event,
        input: Option<Box<dyn SerialInput>>,
        output: Option<Box<dyn io::Write + Send>>,
        sync: Option<Box<dyn FileSync + Send>>,
        out_timestamp: bool,
        keep_rds: Vec<RawDescriptor>,
    ) -> AsyncConsole {
        AsyncConsole {
            state: VirtioConsoleState::Stopped(ConsoleDevice::new(
                protected_vm,
                evt,
                input,
                output,
                sync,
                out_timestamp,
                Default::default(),
            )),
            base_features: base_features(protected_vm),
            keep_rds,
        }
    }
}

impl Drop for AsyncConsole {
    fn drop(&mut self) {
        let _ = self.reset();
    }
}

impl VirtioDevice for AsyncConsole {
    fn keep_rds(&self) -> Vec<RawDescriptor> {
        self.keep_rds.clone()
    }

    fn features(&self) -> u64 {
        self.base_features
    }

    fn device_type(&self) -> DeviceType {
        DeviceType::Console
    }

    fn queue_max_sizes(&self) -> &[u16] {
        QUEUE_SIZES
    }

    fn read_config(&self, offset: u64, data: &mut [u8]) {
        let config = virtio_console_config {
            max_nr_ports: 1.into(),
            ..Default::default()
        };
        copy_config(data, 0, config.as_slice(), offset);
    }

    fn activate(
        &mut self,
        mem: GuestMemory,
        interrupt: Interrupt,
        mut queues: Vec<Queue>,
        mut queue_evts: Vec<Event>,
    ) {
        if queues.len() < 2 || queue_evts.len() < 2 {
            return;
        }

        // Reset the device if it was already running.
        if matches!(self.state, VirtioConsoleState::Running { .. }) {
            self.reset();
        }

        let state = std::mem::replace(&mut self.state, VirtioConsoleState::Broken);
        let console = match state {
            VirtioConsoleState::Running { .. } => {
                error!("device should not be running here. This is a bug.");
                return;
            }
            VirtioConsoleState::Stopped(console) => console,
            VirtioConsoleState::Broken => {
                warn!("device is broken and cannot be activated");
                return;
            }
        };

        let (self_kill_evt, kill_evt) = match Event::new().and_then(|e| Ok((e.try_clone()?, e))) {
            Ok(v) => v,
            Err(e) => {
                error!("failed creating kill Event pair: {}", e);
                return;
            }
        };

        let ex = Executor::new().expect("failed to create an executor");
        let receive_queue = queues.remove(0);
        let receive_evt = queue_evts.remove(0);
        let transmit_queue = queues.remove(0);
        let transmit_evt = queue_evts.remove(0);

        let worker_result = thread::Builder::new()
            .name("virtio_console".to_string())
            .spawn(move || {
                let mut console = console;
                let interrupt = Arc::new(Mutex::new(interrupt));

                console.start_receive_queue(
                    &ex,
                    mem.clone(),
                    receive_queue,
                    interrupt.clone(),
                    receive_evt,
                )?;

                console.start_transmit_queue(&ex, mem, transmit_queue, interrupt, transmit_evt)?;

                // Run until the kill event is signaled and cancel all tasks.
                ex.run_until(async {
                    async_utils::await_and_exit(&ex, kill_evt).await?;
                    if let Some(input) = console.input.as_mut() {
                        input.stop().context("failed to stop rx queue")?;
                    }
                    console.output.stop().context("failed to stop tx queue")?;

                    Ok(console)
                })?
            });

        match worker_result {
            Err(e) => error!("failed to spawn virtio_console worker: {}", e),
            Ok(join_handle) => {
                self.state = VirtioConsoleState::Running {
                    kill_evt: self_kill_evt,
                    worker_thread: join_handle,
                };
            }
        }
    }

    fn reset(&mut self) -> bool {
        match std::mem::replace(&mut self.state, VirtioConsoleState::Broken) {
            // Stopped console is already in reset state.
            state @ VirtioConsoleState::Stopped(_) => {
                self.state = state;
                true
            }
            // Stop the worker thread and go back to `Stopped` state.
            VirtioConsoleState::Running {
                kill_evt,
                worker_thread,
            } => match kill_evt.write(1) {
                Ok(_) => {
                    let thread_res = match worker_thread.join() {
                        Ok(thread_res) => thread_res,
                        Err(_) => {
                            error!("worker thread has panicked");
                            return false;
                        }
                    };

                    match thread_res {
                        Ok(console) => {
                            self.state = VirtioConsoleState::Stopped(console);
                            true
                        }
                        Err(e) => {
                            error!("worker thread returned an error: {}", e);
                            false
                        }
                    }
                }
                Err(e) => {
                    error!("error while requesting worker thread to stop: {}", e);
                    error!("the worker thread will keep running");
                    false
                }
            },
            // We are broken and cannot reset properly.
            VirtioConsoleState::Broken => false,
        }
    }
}
<|endoftext|>


<commit_before>//! Serializer and deserializer structure for a data.
//!
//! This module is intended for transforming incoming data (which are
//! `tungstenite::Message` objects) to JSON objects and preparing responses
//! for client before sending through transmitters.
//!

use std::sync::{Arc};

use error::{Result, PathfinderError};

use json::{parse as parse_json, JsonValue};
use tungstenite::protocol::{Message};

/// Type alias for JSON object
pub type JsonMessage = Arc<Box<JsonValue>>;

/// A specialized struct for deserializing incoming messages into JSON and
/// serializing responses into `tungstenite::Message` objects, so, that they
/// could be send to a client.
///
/// # Examples
///
/// Serializing a JSON object into message:
///
/// ```
/// use engine::{Serializer};
///
/// let instance = Serializer::new();
/// let json = object!{"test" => "serialize"};
/// let response = json.dump();
/// println!("{:?}", instance.serialize(response))
/// ```
///
/// Deserializing a message to JSON object:
///
/// ```
/// use engine::{Serializer};
/// use tungstenite::{Message};
///
/// let json = object!{"test" => "serialize"};
/// let message = Message::Text(json.dump());
/// let instance = Serializer::new();
/// println!("{:?}", instance.deserialize(&message))
/// ```
///
pub struct Serializer;

impl Serializer {
    /// Returns a new instance of `Serializer`.
    pub fn new() -> Serializer {
        Serializer {}
    }

    /// Converts a UTF-8 encoded `std::string::String` into an instance of
    /// the `tungstenite::Message` type, so that this message can be send to
    /// a client.
    pub fn serialize(&self, message: String) -> Result<Message> {
        Ok(Message::Text(message))
    }

    /// Transforms an instance of the `tungstenite::Message` type into JSON object.
    pub fn deserialize(&self, message: &Message) -> Result<JsonMessage> {
         let text_message = self.parse_into_text(message)?;
         let mut json_message = self.parse_into_json(text_message.as_str())?;
         json_message = self.validate_json(json_message)?;
         Ok(json_message)
    }

    /// Parses an instance of the `tungstenite::Message` type and returns a UTF-8
    /// encoded string of the `std::string::String` type.
    fn parse_into_text(&self, message: &Message) -> Result<String> {
        match message.clone().into_text() {
            Ok(text_message) => Ok(text_message),
            Err(err) => {
                let formatted_message = format!("{}", err);
                return Err(PathfinderError::DecodingError(formatted_message))
            }
        }
    }

    /// Parses a UTF-8 string and converts it into JSON object.
    fn parse_into_json(&self, message: &str) -> Result<JsonMessage> {
        match parse_json(message) {
            Ok(message) => Ok(Arc::new(Box::new(message))),
            Err(err) => {
                let formatted_message = format!("{}", err);
                return Err(PathfinderError::DecodingError(formatted_message))
            }
        }
    }

    /// Validates a JSON object on required fields and values.
    fn validate_json(&self, json: JsonMessage) -> Result<JsonMessage> {
        if json["url"].is_null() {
            let error_message = String::from("Key `url` is missing or value is `null`");
            return Err(PathfinderError::DecodingError(error_message));
        }

        if json.has_key("microservice") {
            let error_message = String::from("Key `microservice` must be not specified");
            return Err(PathfinderError::DecodingError(error_message));
        }

        Ok(json)
    }
}

#[cfg(test)]
mod tests {
    use super::{Serializer};
    use super::super::super::json::{Null};
    use super::super::super::tungstenite::{Message};

    #[test]
    fn test_serialize_returns_a_new_message_instance() {
        let instance = Serializer::new();
        let dictionary = object!{"test" => "value"};
        let test_string = dictionary.dump();
        let result = instance.serialize(test_string);

        assert_eq!(result.is_ok(), true);
        assert_eq!(result.unwrap().is_text(), true)
    }

    #[test]
    fn test_deserialize_returns_valid_json_object() {
        let dictionary = object!{"url" => "test"};
        let message = Message::Text(dictionary.dump());
        let instance = Serializer::new();
        let result = instance.deserialize(&message);

        assert_eq!(result.is_ok(), true);
        let unwrapped_result = result.unwrap();
        assert_eq!(unwrapped_result.has_key("url"), true);
        assert_eq!(unwrapped_result["url"], dictionary["url"]);
    }

    #[test]
    fn test_deserialize_returns_decoding_error_while_parsed_into_text() {
        let data = vec![0, 159, 146, 150];
        let message = Message::Binary(data);
        let instance = Serializer::new();
        let result = instance.deserialize(&message);

        assert_eq!(result.is_err(), true);
        assert_eq!(
            format!("{}", result.unwrap_err()),
            "Decoding error: UTF-8 encoding error"
        )
    }

    #[test]
    fn test_deserialize_returns_decoding_error_while_parsed_into_json() {
        let invalid_json = String::from(r#"{"url": "test""#);
        let message = Message::Text(invalid_json);
        let instance = Serializer::new();
        let result = instance.deserialize(&message);

        assert_eq!(result.is_err(), true);
        assert_eq!(
            format!("{}", result.unwrap_err()),
            "Decoding error: Unexpected end of JSON"
        )
    }

    #[test]
    fn test_deserialize_returns_validation_error_for_missing_url_key_in_json() {
        let dictionary = object!{"test" => "value"};
        let message = Message::Text(dictionary.dump());
        let instance = Serializer::new();
        let result = instance.deserialize(&message);

        assert_eq!(result.is_err(), true);
        assert_eq!(
            format!("{}", result.unwrap_err()),
            "Decoding error: Key `url` is missing or value is `null`"
        )
    }

    #[test]
    fn test_deserialize_returns_validation_error_for_invalid_url_value_in_json() {
        let dictionary = object!{"url" => Null};
        let message = Message::Text(dictionary.dump());
        let instance = Serializer::new();
        let result = instance.deserialize(&message);

        assert_eq!(result.is_err(), true);
        assert_eq!(
            format!("{}", result.unwrap_err()),
            "Decoding error: Key `url` is missing or value is `null`"
        )
    }

    #[test]
    fn test_deserialize_returns_validation_error_for_the_specified_matchmaking_key_in_json() {
        let dictionary = object!{"url" => "value", "microservice" => "some microservice"};
        let message = Message::Text(dictionary.dump());
        let instance = Serializer::new();
        let result = instance.deserialize(&message);

        assert_eq!(result.is_err(), true);
        assert_eq!(
            format!("{}", result.unwrap_err()),
            "Decoding error: Key `microservice` must be not specified"
        )
    }
}
<commit_msg>Fixed import during for serialiazer tests<commit_after>//! Serializer and deserializer structure for a data.
//!
//! This module is intended for transforming incoming data (which are
//! `tungstenite::Message` objects) to JSON objects and preparing responses
//! for client before sending through transmitters.
//!

use std::sync::{Arc};

use error::{Result, PathfinderError};

use json::{parse as parse_json, JsonValue};
use tungstenite::protocol::{Message};

/// Type alias for JSON object
pub type JsonMessage = Arc<Box<JsonValue>>;

/// A specialized struct for deserializing incoming messages into JSON and
/// serializing responses into `tungstenite::Message` objects, so, that they
/// could be send to a client.
///
/// # Examples
///
/// Serializing a JSON object into message:
///
/// ```
/// use engine::{Serializer};
///
/// let instance = Serializer::new();
/// let json = object!{"test" => "serialize"};
/// let response = json.dump();
/// println!("{:?}", instance.serialize(response))
/// ```
///
/// Deserializing a message to JSON object:
///
/// ```
/// use engine::{Serializer};
/// use tungstenite::{Message};
///
/// let json = object!{"test" => "serialize"};
/// let message = Message::Text(json.dump());
/// let instance = Serializer::new();
/// println!("{:?}", instance.deserialize(&message))
/// ```
///
pub struct Serializer;

impl Serializer {
    /// Returns a new instance of `Serializer`.
    pub fn new() -> Serializer {
        Serializer {}
    }

    /// Converts a UTF-8 encoded `std::string::String` into an instance of
    /// the `tungstenite::Message` type, so that this message can be send to
    /// a client.
    pub fn serialize(&self, message: String) -> Result<Message> {
        Ok(Message::Text(message))
    }

    /// Transforms an instance of the `tungstenite::Message` type into JSON object.
    pub fn deserialize(&self, message: &Message) -> Result<JsonMessage> {
         let text_message = self.parse_into_text(message)?;
         let mut json_message = self.parse_into_json(text_message.as_str())?;
         json_message = self.validate_json(json_message)?;
         Ok(json_message)
    }

    /// Parses an instance of the `tungstenite::Message` type and returns a UTF-8
    /// encoded string of the `std::string::String` type.
    fn parse_into_text(&self, message: &Message) -> Result<String> {
        match message.clone().into_text() {
            Ok(text_message) => Ok(text_message),
            Err(err) => {
                let formatted_message = format!("{}", err);
                return Err(PathfinderError::DecodingError(formatted_message))
            }
        }
    }

    /// Parses a UTF-8 string and converts it into JSON object.
    fn parse_into_json(&self, message: &str) -> Result<JsonMessage> {
        match parse_json(message) {
            Ok(message) => Ok(Arc::new(Box::new(message))),
            Err(err) => {
                let formatted_message = format!("{}", err);
                return Err(PathfinderError::DecodingError(formatted_message))
            }
        }
    }

    /// Validates a JSON object on required fields and values.
    fn validate_json(&self, json: JsonMessage) -> Result<JsonMessage> {
        if json["url"].is_null() {
            let error_message = String::from("Key `url` is missing or value is `null`");
            return Err(PathfinderError::DecodingError(error_message));
        }

        if json.has_key("microservice") {
            let error_message = String::from("Key `microservice` must be not specified");
            return Err(PathfinderError::DecodingError(error_message));
        }

        Ok(json)
    }
}

#[cfg(test)]
mod tests {
    use super::{Serializer};
    use super::super::json::{Null};
    use super::super::tungstenite::{Message};

    #[test]
    fn test_serialize_returns_a_new_message_instance() {
        let instance = Serializer::new();
        let dictionary = object!{"test" => "value"};
        let test_string = dictionary.dump();
        let result = instance.serialize(test_string);

        assert_eq!(result.is_ok(), true);
        assert_eq!(result.unwrap().is_text(), true)
    }

    #[test]
    fn test_deserialize_returns_valid_json_object() {
        let dictionary = object!{"url" => "test"};
        let message = Message::Text(dictionary.dump());
        let instance = Serializer::new();
        let result = instance.deserialize(&message);

        assert_eq!(result.is_ok(), true);
        let unwrapped_result = result.unwrap();
        assert_eq!(unwrapped_result.has_key("url"), true);
        assert_eq!(unwrapped_result["url"], dictionary["url"]);
    }

    #[test]
    fn test_deserialize_returns_decoding_error_while_parsed_into_text() {
        let data = vec![0, 159, 146, 150];
        let message = Message::Binary(data);
        let instance = Serializer::new();
        let result = instance.deserialize(&message);

        assert_eq!(result.is_err(), true);
        assert_eq!(
            format!("{}", result.unwrap_err()),
            "Decoding error: UTF-8 encoding error"
        )
    }

    #[test]
    fn test_deserialize_returns_decoding_error_while_parsed_into_json() {
        let invalid_json = String::from(r#"{"url": "test""#);
        let message = Message::Text(invalid_json);
        let instance = Serializer::new();
        let result = instance.deserialize(&message);

        assert_eq!(result.is_err(), true);
        assert_eq!(
            format!("{}", result.unwrap_err()),
            "Decoding error: Unexpected end of JSON"
        )
    }

    #[test]
    fn test_deserialize_returns_validation_error_for_missing_url_key_in_json() {
        let dictionary = object!{"test" => "value"};
        let message = Message::Text(dictionary.dump());
        let instance = Serializer::new();
        let result = instance.deserialize(&message);

        assert_eq!(result.is_err(), true);
        assert_eq!(
            format!("{}", result.unwrap_err()),
            "Decoding error: Key `url` is missing or value is `null`"
        )
    }

    #[test]
    fn test_deserialize_returns_validation_error_for_invalid_url_value_in_json() {
        let dictionary = object!{"url" => Null};
        let message = Message::Text(dictionary.dump());
        let instance = Serializer::new();
        let result = instance.deserialize(&message);

        assert_eq!(result.is_err(), true);
        assert_eq!(
            format!("{}", result.unwrap_err()),
            "Decoding error: Key `url` is missing or value is `null`"
        )
    }

    #[test]
    fn test_deserialize_returns_validation_error_for_the_specified_matchmaking_key_in_json() {
        let dictionary = object!{"url" => "value", "microservice" => "some microservice"};
        let message = Message::Text(dictionary.dump());
        let instance = Serializer::new();
        let result = instance.deserialize(&message);

        assert_eq!(result.is_err(), true);
        assert_eq!(
            format!("{}", result.unwrap_err()),
            "Decoding error: Key `microservice` must be not specified"
        )
    }
}
<|endoftext|>


<commit_before>/* Copyright 2016 Jordan Miner
 *
 * Licensed under the MIT license <LICENSE or
 * http://opensource.org/licenses/MIT>. This file may not be copied,
 * modified, or distributed except according to those terms.
 */

#[macro_use]
extern crate clear_coat;
extern crate iup_sys;
use iup_sys::*;

use clear_coat::*;
use clear_coat::common_attrs_cbs::*;

struct CursorsCanvas {
    canvas: Canvas,
}

impl CursorsCanvas {
    pub fn new() -> Self {
        CursorsCanvas {
            canvas: Canvas::new(),
        }
    }
}

unsafe impl Control for CursorsCanvas {
    fn handle(&self) -> *mut Ihandle {
        self.canvas.handle()
    }
}

fn create_cursors_page() -> Box<Control> {
    let page = vbox!(
        &CursorsCanvas::new()
    );
    Box::new(page)
}

fn main() {

    let dialog = Dialog::new();

    let tabs = Tabs::new();

    tabs.append_tabs(&[
        TabInfo::new(&*create_cursors_page()).title("Cursors"),
        TabInfo::new(&Fill::new()).title("File Dialog"),
    ]);

    dialog.append(&tabs).expect("failed to build the window");
    dialog.set_title("Showcase");

    dialog.show_xy(ScreenPosition::Center, ScreenPosition::Center)
          .expect("failed to show the window");
    main_loop();
}
<commit_msg>Fill out cursors tab page more<commit_after>/* Copyright 2016 Jordan Miner
 *
 * Licensed under the MIT license <LICENSE or
 * http://opensource.org/licenses/MIT>. This file may not be copied,
 * modified, or distributed except according to those terms.
 */

#[macro_use]
extern crate clear_coat;
extern crate iup_sys;
extern crate smallvec;

use std::rc::Rc;
use iup_sys::*;
use smallvec::SmallVec;

use clear_coat::*;
use clear_coat::common_attrs_cbs::*;

struct CursorsCanvas {
    canvas: Canvas,
}

impl CursorsCanvas {
    pub fn new() -> Self {
        CursorsCanvas {
            canvas: Canvas::new(),
        }
    }
}

impl CursorAttribute for CursorsCanvas {}

unsafe impl Control for CursorsCanvas {
    fn handle(&self) -> *mut Ihandle {
        self.canvas.handle()
    }
}

fn create_cursors_page() -> Box<Control> {
    let cursors_canvas = Rc::new(CursorsCanvas::new());

    let radios_info = [
        ("None", Cursor::None),
        ("Arrow", Cursor::Arrow),
        ("Busy", Cursor::Busy),
        ("Cross", Cursor::Cross),
        ("Hand", Cursor::Hand),
        ("Help", Cursor::Help),
        ("Move", Cursor::Move),
        ("ResizeN", Cursor::ResizeN),
        ("ResizeS", Cursor::ResizeS),
        ("ResizeNS", Cursor::ResizeNS),
        ("ResizeW", Cursor::ResizeW),
        ("ResizeE", Cursor::ResizeE),
        ("ResizeWE", Cursor::ResizeWE),
        ("ResizeNE", Cursor::ResizeNE),
        ("ResizeSW", Cursor::ResizeSW),
        ("ResizeNW", Cursor::ResizeNW),
        ("ResizeSE", Cursor::ResizeSE),
        ("Text", Cursor::Text),
    ];

    let mut radios = SmallVec::<[Toggle; 32]>::new();
    for &(text, cur) in radios_info.iter() {
        let toggle = Toggle::new();
        toggle.set_title(text);
        let cursors_canvas2 = cursors_canvas.clone();
        toggle.action_event().add(move |checked| {
            if checked { cursors_canvas2.set_cursor(cur); }
        });
        radios.push(toggle);
    }

    let grid = grid_box!(
        &radios[0],
        &radios[1],
        &radios[2],
        &radios[3],
        &radios[4],
        &radios[5],
        &radios[6],
        &radios[7],
        &radios[8],
        &radios[9],
        &radios[10],
        &radios[11],
        &radios[12],
        &radios[13],
        &radios[14],
        &radios[15],
        &radios[16],
        &radios[17],
    );
    grid.set_num_div(NumDiv::Fixed(2));
    let page = vbox!(
        &cursors_canvas,
        hbox!(
            fill!(),
            &grid,
            fill!(),
        ),
    );
    Box::new(page)
}

fn main() {

    let dialog = Dialog::new();

    let tabs = Tabs::new();

    tabs.append_tabs(&[
        TabInfo::new(&*create_cursors_page()).title("Cursors"),
        TabInfo::new(&Fill::new()).title("File Dialog"),
    ]);

    dialog.append(&tabs).expect("failed to build the window");
    dialog.set_title("Showcase");

    dialog.show_xy(ScreenPosition::Center, ScreenPosition::Center)
          .expect("failed to show the window");
    main_loop();
}
<|endoftext|>


<commit_before>#![feature(proc_macro_hygiene)]
#![feature(decl_macro)]
#![feature(rustc_private)]
#[macro_use] extern crate rocket;
#[macro_use] extern crate diesel;
extern crate serde_json;
extern crate serde;
extern crate dotenv;

use rocket::request::{Outcome, FromRequest};
use rocket::Outcome::Success;
use rocket::Request;

use rocket::Data;
use rocket::data::{self, FromDataSimple};
use rocket::http::Status;
use rocket::Outcome::Failure;
use serde::{Deserialize, Serialize};

use rocket_contrib::json::Json;

use diesel::prelude::*;
use diesel::pg::PgConnection;
use dotenv::dotenv;
use std::env;

use rocket::http::ContentType;

pub mod schema;
use schema::posts;

use std::rc::Rc;

#[derive(Serialize)]
#[derive(Debug)]
#[derive(Queryable)]
pub struct Post {
    pub id: i32,
    pub title: String,
    pub body: String,
    pub publihed: bool,
}

#[derive(Serialize, Deserialize)]
#[derive(Insertable)]
#[derive(Clone)]
#[derive(Debug)]
#[table_name="posts"]
pub struct NewPost{
    pub title: String,
    pub body: String,
}

impl FromDataSimple for NewPost {
    type Error = String;

    #[allow(unused_variables)]
    fn from_data(req: &Request, data: Data) -> data::Outcome<Self, String> {
        let reader = data.open();
        match serde_json::from_reader(reader).map(|val| val) {
            Ok(value) => Success(value),
            Err(e) => Failure((Status::BadRequest, e.to_string())),
        }
    }
}

// DB classes
#[derive(Clone)]
pub struct DBPost {}

impl DBPost {
    // DATABASE classes
        pub fn establish_connection(self) -> PgConnection {
            dotenv().ok();
            let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
            PgConnection::establish(&database_url).expect(&format!("Error connecting to {}", database_url))
        }

        pub fn create(self, post: NewPost) -> Post {
            let conn = self.establish_connection();
            diesel::insert_into(posts::table).values(&post).get_result(&conn).expect("Error saving!")
        }
        pub fn read(self) -> Vec<Post> {
            let conn = self.establish_connection();
            posts::table.load::<Post>(&conn).unwrap()
      }
      pub fn clear(self)  {
            let conn = self.establish_connection();
            diesel::delete(posts::table).execute(&conn);
      }
}

impl<'a, 'r> FromRequest<'a, 'r> for DBPost {
    type Error = ();
    fn from_request(_request: &'a Request<'r>) -> Outcome<Self, Self::Error> {
        Success(DBPost{})
    }
}

struct HelloWorldCase {
    db: Rc<Option<DBPost>>
}

impl HelloWorldCase {
    pub fn new() -> HelloWorldCase {
        HelloWorldCase{db: Rc::new(None)}
    }
    pub fn run(&self) -> Vec<Post> {
        //unwrap shared reference
        let db = DBPost{};
        let result = db.read();
        result
    }
}

struct AddNewPostCase {
    db: Rc<Option<DBPost>>,
    post: NewPost
}
impl AddNewPostCase  {
    pub fn new(post: NewPost) -> AddNewPostCase {
        AddNewPostCase{db: Rc::new(None), post: post}
    }
    pub fn run(&self) -> Post {
        //TODO find best way to do it... added generics?
        let db = DBPost{};
        let result = db.create(self.post.clone());
        result
    }
}

impl<'a, 'r> FromRequest<'a, 'r> for HelloWorldCase {
    type Error = ();
    fn from_request(_request: &'a Request<'r>) -> Outcome<Self, Self::Error> {
        Success(HelloWorldCase::new())
    }
}

#[get("/")]
fn get(db: DBPost) -> Json<Vec<Post>> {
    Json(HelloWorldCase{db: Rc::new(Some(db))}.run())
}
#[post("/", format="application/json", data="<post>")]
fn post(db: DBPost, post: NewPost) -> Json<Post> {
    Json(AddNewPostCase{db: Rc::new(Some(db)), post:post}.run())
}

fn main() {
    let db = DBPost{};
    rocket::ignite().manage(db).mount("/", routes![get, post]).launch();
}

#[test]
fn test1() {
    use rocket::local::Client;
    let db = DBPost{};
    let rocket = rocket::ignite().manage(db).mount("/", routes![get, post]);
    let client = Client::new(rocket).unwrap();
    let mut response = client.get("/").dispatch();
    assert_eq!(response.body_string().is_some(),true);
}

#[test]
fn test2() {
    use rocket::local::Client;
    let db = DBPost{};
    db.clone().clear();
    let rocket = rocket::ignite().manage(db).mount("/", routes![get, post]);
    let client = Client::new(rocket).unwrap();
    let mut response = client.post("/").header(ContentType::JSON).body("{\"title\": \"mytitle1\", \"body\": \"mybody1\"}").dispatch();
    assert_eq!(response.body_string().is_some(), true);
}
<commit_msg>Fixed rc references<commit_after>#![feature(proc_macro_hygiene)]
#![feature(decl_macro)]
#![feature(rustc_private)]
#[macro_use] extern crate rocket;
#[macro_use] extern crate diesel;
extern crate serde_json;
extern crate serde;
extern crate dotenv;

use rocket::request::{Outcome, FromRequest};
use rocket::Outcome::Success;
use rocket::Request;

use rocket::Data;
use rocket::data::{self, FromDataSimple};
use rocket::http::Status;
use rocket::http::ContentType;
use rocket::Outcome::Failure;
use serde::{Deserialize, Serialize};

use rocket_contrib::json::Json;

use diesel::prelude::*;
use diesel::pg::PgConnection;
use dotenv::dotenv;
use std::env;

pub mod schema;
use schema::posts;

use std::rc::Rc;

#[derive(Serialize)]
#[derive(Debug)]
#[derive(Queryable)]
pub struct Post {
    pub id: i32,
    pub title: String,
    pub body: String,
    pub publihed: bool,
}

#[derive(Serialize, Deserialize)]
#[derive(Insertable)]
#[derive(Clone)]
#[derive(Debug)]
#[table_name="posts"]
pub struct NewPost{
    pub title: String,
    pub body: String,
}

impl FromDataSimple for NewPost {
    type Error = String;

    #[allow(unused_variables)]
    fn from_data(req: &Request, data: Data) -> data::Outcome<Self, String> {
        let reader = data.open();
        match serde_json::from_reader(reader).map(|val| val) {
            Ok(value) => Success(value),
            Err(e) => Failure((Status::BadRequest, e.to_string())),
        }
    }
}

// DB classes
#[derive(Clone, Copy)]
pub struct DBPost {}

impl DBPost {
    // DATABASE classes
        pub fn establish_connection(self) -> PgConnection {
            dotenv().ok();
            let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
            PgConnection::establish(&database_url).expect(&format!("Error connecting to {}", database_url))
        }

        pub fn create(self, post: NewPost) -> Post {
            let conn = self.establish_connection();
            diesel::insert_into(posts::table).values(&post).get_result(&conn).expect("Error saving!")
        }
        pub fn read(self) -> Vec<Post> {
            let conn = self.establish_connection();
            posts::table.load::<Post>(&conn).unwrap()
      }
      pub fn clear(self)  {
            let conn = self.establish_connection();
            diesel::delete(posts::table).execute(&conn);
      }
}

impl<'a, 'r> FromRequest<'a, 'r> for DBPost {
    type Error = ();
    fn from_request(_request: &'a Request<'r>) -> Outcome<Self, Self::Error> {
        Success(DBPost{})
    }
}

struct GetPostsCase {
    db: Rc<DBPost>
}

impl GetPostsCase {
    pub fn new(db: DBPost) ->GetPostsCase {
        GetPostsCase{db: Rc::new(db)}
    }
    pub fn run(&self) -> Vec<Post> {
        //unwrap shared reference
        let result = self.db.read();
        result
    }
}

struct AddNewPostCase {
    db: Rc<DBPost>,
    post: NewPost
}
impl AddNewPostCase  {
    pub fn new(post: NewPost, db: DBPost) -> AddNewPostCase {
        AddNewPostCase{db: Rc::new(db), post: post}
    }
    pub fn run(&self) -> Post {
        //TODO find best way to do it... added generics?
        let result = self.db.create(self.post.clone());
        result
    }
}

impl<'a, 'r> FromRequest<'a, 'r> for GetPostsCase {
    type Error = ();
    fn from_request(_request: &'a Request<'r>) -> Outcome<Self, Self::Error> {
        Success(GetPostsCase::new(DBPost{}))
    }
}

#[get("/")]
fn get(db: DBPost) -> Json<Vec<Post>> {
    Json(GetPostsCase{db: Rc::new(db)}.run())
}
#[post("/", format="application/json", data="<post>")]
fn post(db: DBPost, post: NewPost) -> Json<Post> {
    Json(AddNewPostCase{db: Rc::new(db), post:post}.run())
}

fn main() {
    let db = DBPost{};
    rocket::ignite().manage(db).mount("/", routes![get, post]).launch();
}

#[test]
fn test1() {
    use rocket::local::Client;
    let db = DBPost{};
    let rocket = rocket::ignite().manage(db).mount("/", routes![get, post]);
    let client = Client::new(rocket).unwrap();
    let mut response = client.get("/").dispatch();
    assert_eq!(response.body_string().is_some(),true);
}

#[test]
fn test2() {
    use rocket::local::Client;
    let db = DBPost{};
    db.clone().clear();
    let rocket = rocket::ignite().manage(db).mount("/", routes![get, post]);
    let client = Client::new(rocket).unwrap();
    let mut response = client.post("/").header(ContentType::JSON).body("{\"title\": \"mytitle1\", \"body\": \"mybody1\"}").dispatch();
    assert_eq!(response.body_string().is_some(), true);
}
<|endoftext|>


<commit_before>extern crate portmidi as pm;

extern crate rustc_serialize;
extern crate docopt;

use std::time::Duration;
use std::thread;

const USAGE: &'static str = r#"
portmidi-rs: monitor-device example

Usage:
    monitor-device <id>

Options:
    -h --help   Show this screen.

Omitting <id> will list the available devices.
"#;

#[derive(Debug, RustcDecodable)]
struct Args {
    arg_id: i32,
}

fn print_devices(pm: &pm::PortMidi) {
    for dev in pm.devices().unwrap() {
        println!("{}", dev);
    }
}

fn main() {
    // initialize the PortMidi context.
    let context = pm::PortMidi::new().unwrap();

    // setup the command line interface
    let args: Args = docopt::Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|err| {
        print_devices(&context);
        err.exit();
    });

    // get the device info for the given id
    let info = context.device(args.arg_id).unwrap();
    println!("Listening on: {}) {}", info.id(), info.name());

    // get the device's input port
    let mut in_port = context.input_port(info, 1024).unwrap();

    while let Ok(_) = in_port.poll() {
        if let Ok(Some(event)) = in_port.read_n(1024) {
            println!("{:?}", event);
            // there is no blocking receive method in PortMidi, therefore
            // we have to sleep some time to prevent a busy-wait loop
            thread::sleep(Duration::new(0,50))
        }
    }
}
<commit_msg>s/id/device_id<commit_after>extern crate portmidi as pm;

extern crate rustc_serialize;
extern crate docopt;

use std::time::Duration;
use std::thread;

const USAGE: &'static str = r#"
portmidi-rs: monitor-device example

Usage:
    monitor-device <device-id>

Options:
    -h --help   Show this screen.

Omitting <device-id> will list the available devices.
"#;

#[derive(Debug, RustcDecodable)]
struct Args {
    arg_device_id: i32,
}

fn print_devices(pm: &pm::PortMidi) {
    for dev in pm.devices().unwrap() {
        println!("{}", dev);
    }
}

fn main() {
    // initialize the PortMidi context.
    let context = pm::PortMidi::new().unwrap();

    // setup the command line interface
    let args: Args = docopt::Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|err| {
        print_devices(&context);
        err.exit();
    });

    // get the device info for the given id
    let info = context.device(args.arg_device_id).unwrap();
    println!("Listening on: {}) {}", info.id(), info.name());

    // get the device's input port
    let mut in_port = context.input_port(info, 1024).unwrap();

    while let Ok(_) = in_port.poll() {
        if let Ok(Some(event)) = in_port.read_n(1024) {
            println!("{:?}", event);
            // there is no blocking receive method in PortMidi, therefore
            // we have to sleep some time to prevent a busy-wait loop
            thread::sleep(Duration::new(0,50))
        }
    }
}
<|endoftext|>


<commit_before>extern crate getopts;
use getopts::Options;
use std::env;
use std::fs::File;
use std::io;
use std::io::Read;

fn read_blend_file(inp: &str) -> io::Result<()> {
    // open file
    let file = try!(File::open(inp));
    // read 12 bytes from the Blender file
    let mut take = file.take(12u64);
    let mut header = String::new();
    try!(take.read_to_string(&mut header));
    println!("header = \"{}\"", header);
    // TODO: compare first 7 chars to "BLENDER"
    Ok(())
}

fn do_work(inp: &str, out: Option<String>) {
    println!("FILE = {}", inp);
    match out {
        Some(x) => println!("output file name: {}", x),
        None => println!("no output file name"),
    }
    read_blend_file(inp);
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} FILE [options]", program);
    print!("{}", opts.usage(&brief));
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    let mut opts = Options::new();
    opts.optopt("o", "", "set output file name", "NAME");
    opts.optflag("h", "help", "print this help menu");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };
    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }
    let output = matches.opt_str("o");
    let input = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        print_usage(&program, opts);
        return;
    };
    do_work(&input, output);
}
<commit_msg>Compare first 7 chars to "BLENDER".<commit_after>extern crate getopts;
use getopts::Options;
use std::env;
use std::fs::File;
use std::io;
use std::io::Read;

enum ParseError { InvalidHeader }

fn read_blend_file(inp: &str) -> io::Result<()> {
    // open file
    let file = try!(File::open(inp));
    // read 12 bytes from the Blender file
    let mut take = file.take(12u64);
    let mut header = String::new();
    try!(take.read_to_string(&mut header));
    println!("header = \"{}\"", header);
    // compare first 7 chars to "BLENDER"
    if header.len() >= 7 {
        let mut blender = header;
        blender.truncate(7); // first 7 chars
        if blender == "BLENDER" {
            println!("starts with \"BLENDER\" ...");
            // TODO: check version (next 5 chars in header)
            // TODO: check for 32-bit pointers vs. 64-bit pointers
        }
    } else {
        println!("WARNING: FILE is not a Blender file");
    }
    Ok(())
}

fn do_work(inp: &str, out: Option<String>) {
    println!("FILE = {}", inp);
    match out {
        Some(x) => println!("output file name: {}", x),
        None => println!("no output file name"),
    }
    read_blend_file(inp);
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} FILE [options]", program);
    print!("{}", opts.usage(&brief));
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    let mut opts = Options::new();
    opts.optopt("o", "", "set output file name", "NAME");
    opts.optflag("h", "help", "print this help menu");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!(f.to_string()) }
    };
    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }
    let output = matches.opt_str("o");
    let input = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        print_usage(&program, opts);
        return;
    };
    do_work(&input, output);
}
<|endoftext|>


<commit_before>//! Simple adaptors to relax the CorTeX conentions for agnostic third-party tooling
use std::fs::{create_dir_all, File};
use std::io::prelude::*;
use std::io::SeekFrom;
use std::io::{copy};
use std::io::{Seek, Write};
use std::iter::Iterator;
use std::path::Path;
use std::error::Error;

use tempdir::TempDir;
use tempfile::tempfile;

use walkdir::{DirEntry, WalkDir};
use zip::write::FileOptions;
use zip::ZipArchive;

/// Transform the ZIP provided by cortex into a TempDir,
/// for e.g. tools such as Engrafo that aren't ZIP-capable
pub fn extract_zip_to_tmpdir(path: &Path, tmpdir_prefix: &str) -> Result<TempDir, Box<dyn Error>> {
  let input_tmpdir = TempDir::new(tmpdir_prefix)?;
  let unpacked_dir_path = input_tmpdir.path().to_str().unwrap().to_string() + "/";

  // unpack the Zip file for engrafo
  let inputzip = File::open(&path)?;
  let mut input_archive = ZipArchive::new(inputzip)?;
  for i in 0..input_archive.len() {
    let mut file = input_archive.by_index(i)?;
    let extract_path = file.sanitized_name();
    let extract_pathname = extract_path.as_path().display();
    let full_pathname = format!("{}{}", unpacked_dir_path, extract_pathname);
    if (&*file.name()).ends_with('/') {
      create_dir_all(&full_pathname)?;
    } else {
      if let Some(p) = extract_path.parent() {
        if !p.exists() {
          let absolute_parent = format!("{}{}", unpacked_dir_path, p.display());
          create_dir_all(&absolute_parent)?;
        }
      }
      let mut extracted_file = File::create(&full_pathname)?;
      copy(&mut file, &mut extracted_file)?;
    }
  }
  Ok(input_tmpdir)
}

/// Adaptor that turns an output temporary directory (assuming the filnema conventions are _already_ ollowed)
/// into a ZIP file transmittable back to Cortex
pub fn archive_tmpdir_to_zip(tmpdir: TempDir) -> Result<File, Box<dyn Error>> {
  let dir_path = tmpdir.path().to_str().unwrap();
  archive_directory(dir_path)
}

const METHOD_DEFLATED: zip::CompressionMethod = zip::CompressionMethod::Deflated;

fn archive_directory(src_dir: &str) -> Result<File, Box<dyn Error>> {
  let method = METHOD_DEFLATED;

  let mut file = tempfile()?;

  let walkdir = WalkDir::new(src_dir.to_string());
  let it = walkdir.into_iter();

  zip_one_dir(&mut it.filter_map(Result::ok), src_dir, &mut file, method)?;

  file.seek(SeekFrom::Start(0))?;
  Ok(file)
}

fn zip_one_dir<T>(
  it: &mut dyn Iterator<Item = DirEntry>,
  prefix: &str,
  writer: &mut T,
  method: zip::CompressionMethod,
) -> zip::result::ZipResult<()>
where
  T: Write + Seek,
{
  let mut zip = zip::ZipWriter::new(writer);
  let options = FileOptions::default()
    .compression_method(method)
    .unix_permissions(0o755);

  let mut buffer = Vec::new();
  for entry in it {
    let path = entry.path();
    let name = path
      .strip_prefix(Path::new(prefix))
      .unwrap()
      .to_str()
      .unwrap();

    if path.is_file() {
      zip.start_file(name, options)?;
      let mut f = File::open(path)?;

      f.read_to_end(&mut buffer)?;
      zip.write_all(&*buffer)?;
      buffer.clear();
    }
  }
  zip.finish()?;
  Result::Ok(())
}
<commit_msg>update to preferred .zip method<commit_after>//! Simple adaptors to relax the CorTeX conentions for agnostic third-party tooling
use std::error::Error;
use std::fs::{create_dir_all, File};
use std::io::copy;
use std::io::prelude::*;
use std::io::SeekFrom;
use std::io::{Seek, Write};
use std::iter::Iterator;
use std::path::Path;

use tempdir::TempDir;
use tempfile::tempfile;

use walkdir::{DirEntry, WalkDir};
use zip::write::FileOptions;
use zip::ZipArchive;

/// Transform the ZIP provided by cortex into a TempDir,
/// for e.g. tools such as Engrafo that aren't ZIP-capable
pub fn extract_zip_to_tmpdir(path: &Path, tmpdir_prefix: &str) -> Result<TempDir, Box<dyn Error>> {
    let input_tmpdir = TempDir::new(tmpdir_prefix)?;
    let unpacked_dir_path = input_tmpdir.path().to_str().unwrap().to_string() + "/";

    // unpack the Zip file for engrafo
    let inputzip = File::open(&path)?;
    let mut input_archive = ZipArchive::new(inputzip)?;
    for i in 0..input_archive.len() {
        let mut file = input_archive.by_index(i)?;
        let extract_path = file.mangled_name();
        let extract_pathname = extract_path.as_path().display();
        let full_pathname = format!("{}{}", unpacked_dir_path, extract_pathname);
        if (&*file.name()).ends_with('/') {
            create_dir_all(&full_pathname)?;
        } else {
            if let Some(p) = extract_path.parent() {
                if !p.exists() {
                    let absolute_parent = format!("{}{}", unpacked_dir_path, p.display());
                    create_dir_all(&absolute_parent)?;
                }
            }
            let mut extracted_file = File::create(&full_pathname)?;
            copy(&mut file, &mut extracted_file)?;
        }
    }
    Ok(input_tmpdir)
}

/// Adaptor that turns an output temporary directory (assuming the filnema conventions are _already_ ollowed)
/// into a ZIP file transmittable back to Cortex
pub fn archive_tmpdir_to_zip(tmpdir: TempDir) -> Result<File, Box<dyn Error>> {
    let dir_path = tmpdir.path().to_str().unwrap();
    archive_directory(dir_path)
}

const METHOD_DEFLATED: zip::CompressionMethod = zip::CompressionMethod::Deflated;

fn archive_directory(src_dir: &str) -> Result<File, Box<dyn Error>> {
    let method = METHOD_DEFLATED;

    let mut file = tempfile()?;

    let walkdir = WalkDir::new(src_dir.to_string());
    let it = walkdir.into_iter();

    zip_one_dir(&mut it.filter_map(Result::ok), src_dir, &mut file, method)?;

    file.seek(SeekFrom::Start(0))?;
    Ok(file)
}

fn zip_one_dir<T>(
    it: &mut dyn Iterator<Item = DirEntry>,
    prefix: &str,
    writer: &mut T,
    method: zip::CompressionMethod,
) -> zip::result::ZipResult<()>
where
    T: Write + Seek,
{
    let mut zip = zip::ZipWriter::new(writer);
    let options = FileOptions::default()
        .compression_method(method)
        .unix_permissions(0o755);

    let mut buffer = Vec::new();
    for entry in it {
        let path = entry.path();
        let name = path
            .strip_prefix(Path::new(prefix))
            .unwrap()
            .to_str()
            .unwrap();

        if path.is_file() {
            zip.start_file(name, options)?;
            let mut f = File::open(path)?;

            f.read_to_end(&mut buffer)?;
            zip.write_all(&*buffer)?;
            buffer.clear();
        }
    }
    zip.finish()?;
    Result::Ok(())
}
<|endoftext|>


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

extern crate url;
use self::url::Host;
use error::SdpParserInternalError;
use std::convert::TryFrom;
use std::fmt;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::str::FromStr;

#[derive(Clone, Debug)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
pub enum Address {
    Fqdn(String),
    Ip(IpAddr),
}

impl fmt::Display for Address {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Address::Fqdn(fqdn) => fqdn.fmt(f),
            Address::Ip(ip) => ip.fmt(f),
        }
    }
}

impl FromStr for Address {
    type Err = SdpParserInternalError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let mut e: Option<SdpParserInternalError> = None;
        if s.find(':').is_some() {
            match IpAddr::from_str(s) {
                Ok(ip) => return Ok(Address::Ip(ip)),
                Err(err) => e = Some(err.into()),
            }
        }
        Host::parse(s)
            .and_then(|host| match host {
                Host::Domain(s) => Ok(Address::Fqdn(s)),
                Host::Ipv4(ip) => Ok(Address::Ip(IpAddr::V4(ip))),
                Host::Ipv6(ip) => Ok(Address::Ip(IpAddr::V6(ip))),
            })
            .map_err(|err| e.unwrap_or_else(|| err.into()))
    }
}

impl From<ExplicitlyTypedAddress> for Address {
    fn from(item: ExplicitlyTypedAddress) -> Self {
        match item {
            ExplicitlyTypedAddress::Fqdn { domain, .. } => Address::Fqdn(domain),
            ExplicitlyTypedAddress::Ip(ip) => Address::Ip(ip),
        }
    }
}

impl PartialEq for Address {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Address::Fqdn(a), Address::Fqdn(b)) => a.to_lowercase() == b.to_lowercase(),
            (Address::Ip(a), Address::Ip(b)) => a == b,
            (_, _) => false,
        }
    }
}

#[derive(Clone, Copy, PartialEq, Debug)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
pub enum AddressType {
    IpV4 = 4,
    IpV6 = 6,
}

impl fmt::Display for AddressType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            AddressType::IpV4 => "IP4",
            AddressType::IpV6 => "IP6",
        }
        .fmt(f)
    }
}

impl FromStr for AddressType {
    type Err = SdpParserInternalError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_uppercase().as_str() {
            "IP4" => Ok(AddressType::IpV4),
            "IP6" => Ok(AddressType::IpV6),
            _ => Err(SdpParserInternalError::UnknownAddressType(s.to_owned())),
        }
    }
}

pub trait AddressTyped {
    fn address_type(&self) -> AddressType;
}

impl AddressTyped for IpAddr {
    fn address_type(&self) -> AddressType {
        match self {
            IpAddr::V4(_) => AddressType::IpV4,
            IpAddr::V6(_) => AddressType::IpV6,
        }
    }
}

#[derive(Clone, Debug)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
pub enum ExplicitlyTypedAddress {
    Fqdn {
        address_type: AddressType,
        domain: String,
    },
    Ip(IpAddr),
}

impl fmt::Display for ExplicitlyTypedAddress {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "IN {} ", self.address_type())?;
        match self {
            ExplicitlyTypedAddress::Fqdn { domain, .. } => domain.fmt(f),
            ExplicitlyTypedAddress::Ip(ip) => ip.fmt(f),
        }
    }
}

impl AddressTyped for ExplicitlyTypedAddress {
    fn address_type(&self) -> AddressType {
        match self {
            ExplicitlyTypedAddress::Fqdn { address_type, .. } => *address_type,
            ExplicitlyTypedAddress::Ip(ip) => ip.address_type(),
        }
    }
}

impl From<IpAddr> for ExplicitlyTypedAddress {
    fn from(item: IpAddr) -> Self {
        ExplicitlyTypedAddress::Ip(item)
    }
}

impl From<Ipv4Addr> for ExplicitlyTypedAddress {
    fn from(item: Ipv4Addr) -> Self {
        ExplicitlyTypedAddress::Ip(IpAddr::V4(item))
    }
}

impl From<Ipv6Addr> for ExplicitlyTypedAddress {
    fn from(item: Ipv6Addr) -> Self {
        ExplicitlyTypedAddress::Ip(IpAddr::V6(item))
    }
}

impl TryFrom<(AddressType, &str)> for ExplicitlyTypedAddress {
    type Error = SdpParserInternalError;
    fn try_from(item: (AddressType, &str)) -> Result<Self, Self::Error> {
        match Address::from_str(item.1)? {
            Address::Ip(ip) => {
                if ip.address_type() != item.0 {
                    Err(SdpParserInternalError::AddressTypeMismatch {
                        found: ip.address_type(),
                        expected: item.0,
                    })
                } else {
                    Ok(ExplicitlyTypedAddress::Ip(ip))
                }
            }
            Address::Fqdn(domain) => Ok(ExplicitlyTypedAddress::Fqdn {
                address_type: item.0,
                domain,
            }),
        }
    }
}

impl PartialEq for ExplicitlyTypedAddress {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (
                ExplicitlyTypedAddress::Fqdn {
                    address_type: a1,
                    domain: d1,
                },
                ExplicitlyTypedAddress::Fqdn {
                    address_type: a2,
                    domain: d2,
                },
            ) => a1 == a2 && d1.to_lowercase() == d2.to_lowercase(),
            (ExplicitlyTypedAddress::Ip(a), ExplicitlyTypedAddress::Ip(b)) => a == b,
            (_, _) => false,
        }
    }
}

#[cfg(test)]
mod tests {
    use self::url::ParseError;
    use super::*;
    use std::error::Error;
    use std::net::{AddrParseError, Ipv4Addr, Ipv6Addr};

    #[derive(Debug)]
    enum ParseTestError {
        Host(ParseError),
        Ip(AddrParseError),
    }
    impl fmt::Display for ParseTestError {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            match self {
                ParseTestError::Host(inner) => inner.fmt(f),
                ParseTestError::Ip(inner) => inner.fmt(f),
            }
        }
    }
    impl From<ParseError> for ParseTestError {
        fn from(err: ParseError) -> Self {
            ParseTestError::Host(err)
        }
    }
    impl From<AddrParseError> for ParseTestError {
        fn from(err: AddrParseError) -> Self {
            ParseTestError::Ip(err)
        }
    }
    impl Error for ParseTestError {
        fn source(&self) -> Option<&(dyn Error + 'static)> {
            // Generic error, underlying cause isn't tracked.
            match self {
                ParseTestError::Host(a) => Some(a),
                ParseTestError::Ip(a) => Some(a),
            }
        }
    }
    #[test]
    fn test_domain_name_parsing() -> Result<(), ParseTestError> {
        let address = Host::parse("this.is.a.fqdn")?;
        if let Host::Domain(domain) = address {
            assert_eq!(domain, "this.is.a.fqdn");
        } else {
            panic!();
        }
        Ok(())
    }

    #[test]
    fn test_ipv4_address_parsing() -> Result<(), ParseTestError> {
        let address = Host::parse("1.0.0.1")?;
        if let Host::Ipv4(ip) = address {
            assert_eq!(ip, "1.0.0.1".parse::<Ipv4Addr>()?);
        } else {
            panic!();
        }
        Ok(())
    }

    #[test]
    fn test_ipv6_address_parsing() -> Result<(), ParseTestError> {
        let address = Host::parse("[::1]")?;
        if let Host::Ipv6(ip) = address {
            assert_eq!(ip, "::1".parse::<Ipv6Addr>()?);
        } else {
            panic!();
        }
        Ok(())
    }
}
<commit_msg>Clean up a new clippy lint present in Rust 1.45.0<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate url;
use self::url::Host;
use error::SdpParserInternalError;
use std::convert::TryFrom;
use std::fmt;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::str::FromStr;

#[derive(Clone, Debug)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
pub enum Address {
    Fqdn(String),
    Ip(IpAddr),
}

impl fmt::Display for Address {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Address::Fqdn(fqdn) => fqdn.fmt(f),
            Address::Ip(ip) => ip.fmt(f),
        }
    }
}

impl FromStr for Address {
    type Err = SdpParserInternalError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let mut e: Option<SdpParserInternalError> = None;
        if s.find(':').is_some() {
            match IpAddr::from_str(s) {
                Ok(ip) => return Ok(Address::Ip(ip)),
                Err(err) => e = Some(err.into()),
            }
        }
        Host::parse(s)
            .map(|host| match host {
                Host::Domain(s) => Address::Fqdn(s),
                Host::Ipv4(ip) => Address::Ip(IpAddr::V4(ip)),
                Host::Ipv6(ip) => Address::Ip(IpAddr::V6(ip)),
            })
            .map_err(|err| e.unwrap_or_else(|| err.into()))
    }
}

impl From<ExplicitlyTypedAddress> for Address {
    fn from(item: ExplicitlyTypedAddress) -> Self {
        match item {
            ExplicitlyTypedAddress::Fqdn { domain, .. } => Address::Fqdn(domain),
            ExplicitlyTypedAddress::Ip(ip) => Address::Ip(ip),
        }
    }
}

impl PartialEq for Address {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Address::Fqdn(a), Address::Fqdn(b)) => a.to_lowercase() == b.to_lowercase(),
            (Address::Ip(a), Address::Ip(b)) => a == b,
            (_, _) => false,
        }
    }
}

#[derive(Clone, Copy, PartialEq, Debug)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
pub enum AddressType {
    IpV4 = 4,
    IpV6 = 6,
}

impl fmt::Display for AddressType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            AddressType::IpV4 => "IP4",
            AddressType::IpV6 => "IP6",
        }
        .fmt(f)
    }
}

impl FromStr for AddressType {
    type Err = SdpParserInternalError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_uppercase().as_str() {
            "IP4" => Ok(AddressType::IpV4),
            "IP6" => Ok(AddressType::IpV6),
            _ => Err(SdpParserInternalError::UnknownAddressType(s.to_owned())),
        }
    }
}

pub trait AddressTyped {
    fn address_type(&self) -> AddressType;
}

impl AddressTyped for IpAddr {
    fn address_type(&self) -> AddressType {
        match self {
            IpAddr::V4(_) => AddressType::IpV4,
            IpAddr::V6(_) => AddressType::IpV6,
        }
    }
}

#[derive(Clone, Debug)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
pub enum ExplicitlyTypedAddress {
    Fqdn {
        address_type: AddressType,
        domain: String,
    },
    Ip(IpAddr),
}

impl fmt::Display for ExplicitlyTypedAddress {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "IN {} ", self.address_type())?;
        match self {
            ExplicitlyTypedAddress::Fqdn { domain, .. } => domain.fmt(f),
            ExplicitlyTypedAddress::Ip(ip) => ip.fmt(f),
        }
    }
}

impl AddressTyped for ExplicitlyTypedAddress {
    fn address_type(&self) -> AddressType {
        match self {
            ExplicitlyTypedAddress::Fqdn { address_type, .. } => *address_type,
            ExplicitlyTypedAddress::Ip(ip) => ip.address_type(),
        }
    }
}

impl From<IpAddr> for ExplicitlyTypedAddress {
    fn from(item: IpAddr) -> Self {
        ExplicitlyTypedAddress::Ip(item)
    }
}

impl From<Ipv4Addr> for ExplicitlyTypedAddress {
    fn from(item: Ipv4Addr) -> Self {
        ExplicitlyTypedAddress::Ip(IpAddr::V4(item))
    }
}

impl From<Ipv6Addr> for ExplicitlyTypedAddress {
    fn from(item: Ipv6Addr) -> Self {
        ExplicitlyTypedAddress::Ip(IpAddr::V6(item))
    }
}

impl TryFrom<(AddressType, &str)> for ExplicitlyTypedAddress {
    type Error = SdpParserInternalError;
    fn try_from(item: (AddressType, &str)) -> Result<Self, Self::Error> {
        match Address::from_str(item.1)? {
            Address::Ip(ip) => {
                if ip.address_type() != item.0 {
                    Err(SdpParserInternalError::AddressTypeMismatch {
                        found: ip.address_type(),
                        expected: item.0,
                    })
                } else {
                    Ok(ExplicitlyTypedAddress::Ip(ip))
                }
            }
            Address::Fqdn(domain) => Ok(ExplicitlyTypedAddress::Fqdn {
                address_type: item.0,
                domain,
            }),
        }
    }
}

impl PartialEq for ExplicitlyTypedAddress {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (
                ExplicitlyTypedAddress::Fqdn {
                    address_type: a1,
                    domain: d1,
                },
                ExplicitlyTypedAddress::Fqdn {
                    address_type: a2,
                    domain: d2,
                },
            ) => a1 == a2 && d1.to_lowercase() == d2.to_lowercase(),
            (ExplicitlyTypedAddress::Ip(a), ExplicitlyTypedAddress::Ip(b)) => a == b,
            (_, _) => false,
        }
    }
}

#[cfg(test)]
mod tests {
    use self::url::ParseError;
    use super::*;
    use std::error::Error;
    use std::net::{AddrParseError, Ipv4Addr, Ipv6Addr};

    #[derive(Debug)]
    enum ParseTestError {
        Host(ParseError),
        Ip(AddrParseError),
    }
    impl fmt::Display for ParseTestError {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            match self {
                ParseTestError::Host(inner) => inner.fmt(f),
                ParseTestError::Ip(inner) => inner.fmt(f),
            }
        }
    }
    impl From<ParseError> for ParseTestError {
        fn from(err: ParseError) -> Self {
            ParseTestError::Host(err)
        }
    }
    impl From<AddrParseError> for ParseTestError {
        fn from(err: AddrParseError) -> Self {
            ParseTestError::Ip(err)
        }
    }
    impl Error for ParseTestError {
        fn source(&self) -> Option<&(dyn Error + 'static)> {
            // Generic error, underlying cause isn't tracked.
            match self {
                ParseTestError::Host(a) => Some(a),
                ParseTestError::Ip(a) => Some(a),
            }
        }
    }
    #[test]
    fn test_domain_name_parsing() -> Result<(), ParseTestError> {
        let address = Host::parse("this.is.a.fqdn")?;
        if let Host::Domain(domain) = address {
            assert_eq!(domain, "this.is.a.fqdn");
        } else {
            panic!();
        }
        Ok(())
    }

    #[test]
    fn test_ipv4_address_parsing() -> Result<(), ParseTestError> {
        let address = Host::parse("1.0.0.1")?;
        if let Host::Ipv4(ip) = address {
            assert_eq!(ip, "1.0.0.1".parse::<Ipv4Addr>()?);
        } else {
            panic!();
        }
        Ok(())
    }

    #[test]
    fn test_ipv6_address_parsing() -> Result<(), ParseTestError> {
        let address = Host::parse("[::1]")?;
        if let Host::Ipv6(ip) = address {
            assert_eq!(ip, "::1".parse::<Ipv6Addr>()?);
        } else {
            panic!();
        }
        Ok(())
    }
}
<|endoftext|>