<commit_before>extern crate rustc_serialize;
extern crate dylib;

use std::env;
use std::borrow::ToOwned;
use std::collections::BTreeMap;
use core::workspaces::Workspaces;
use window_system::*;
use window_manager::WindowManager;
use handlers::{ KeyHandler, MouseHandler, ManageHook, StartupHook, LogHook };
use handlers::default::{ exit, restart, start_terminal };
use layout::{ Layout, TallLayout };

use std::mem;
use std::error::Error;
use std::fs::File;
use std::io::Write;
use std::fs::{ read_dir, create_dir_all };
use std::process::Command;
use std::process::Child;
use self::dylib::DynamicLibrary;
use std::rc::Rc;
use std::sync::RwLock;
use std::thread::spawn;
use std::path::PathBuf;
use std::path::Path;
use std::fs::metadata;

pub struct GeneralConfig {
    /// Whether focus follows mouse movements or
    /// only click events and keyboard movements.
    pub focus_follows_mouse: bool,
    /// Border color for focused windows.
    pub focus_border_color: u32,
    /// Border color for unfocused windows.
    pub border_color: u32,
    /// Border width. This is the same for both, focused and unfocused.
    pub border_width: u32,
    /// Default terminal to start
    pub terminal: (String, String),
    /// Keybind for the terminal
    /// Path to the logfile
    pub logfile: String,
    /// Default tags for workspaces
    pub tags: Vec<String>,
    /// Default launcher application
    pub launcher: String,
    pub mod_mask: KeyModifiers,
    pub pipes: Vec<Rc<RwLock<Child>>>,
    pub layout: Box<Layout>
}

impl Clone for GeneralConfig {
    fn clone(&self) -> GeneralConfig {
        GeneralConfig {
            focus_follows_mouse: self.focus_follows_mouse,
            focus_border_color: self.focus_border_color,
            border_color: self.border_color,
            border_width: self.border_width,
            terminal: self.terminal.clone(),
            logfile: self.logfile.clone(),
            tags: self.tags.clone(),
            launcher: self.launcher.clone(),
            mod_mask: self.mod_mask.clone(),
            pipes: self.pipes.clone(),
            layout: self.layout.copy()
        }
    }
}

pub struct InternalConfig {
    pub library: Option<DynamicLibrary>,
    pub key_handlers: BTreeMap<KeyCommand, KeyHandler>,
    pub mouse_handlers: BTreeMap<MouseCommand, MouseHandler>,
    pub manage_hook: ManageHook,
    pub startup_hook: StartupHook,
    pub loghook: Option<LogHook>,
    pub wtftw_dir: String,
}

impl InternalConfig {
    pub fn new(manage_hook: ManageHook, startup_hook: StartupHook, home: String) -> InternalConfig {
        InternalConfig {
            library: None,
            key_handlers: BTreeMap::new(),
            mouse_handlers: BTreeMap::new(),
            manage_hook: manage_hook,
            startup_hook: startup_hook,
            loghook: None,
            wtftw_dir: format!("{}/.wtftw", home)
        }
    }
}

/// Common configuration options for the window manager.
pub struct Config {
    pub general: GeneralConfig,
    pub internal: InternalConfig
}

impl Config {
    /// Create the Config from a json file
    pub fn initialize() -> Config {
        let home = env::home_dir().unwrap_or(PathBuf::from("./")).into_os_string().into_string().unwrap();
        // Default version of the config, for fallback
        let general_config =
            GeneralConfig {
                focus_follows_mouse: true,
                focus_border_color:  0x00B6FFB0,
                border_color:        0x00444444,
                border_width:        2,
                mod_mask:            MOD1MASK,
                terminal:            ("xterm".to_owned(), "".to_owned()),
                logfile:             format!("{}/.wtftw.log", home),
                tags:                vec!(
                    "1: term".to_owned(),
                    "2: web".to_owned(),
                    "3: code".to_owned(),
                    "4: media".to_owned()),
                launcher:            "dmenu_run".to_owned(),
                pipes:               Vec::new(),
                layout:              Box::new(TallLayout { num_master: 1, increment_ratio: 0.3/100.0, ratio: 0.5 }),
            };

        let internal_config = InternalConfig::new(
            Box::new(move |a, _, _| a.clone()),
            Box::new(move |a, _, _| a.clone()),
            //Box::new(Config::default_manage_hook),
            //Box::new(Config::default_startup_hook),
            home);

        Config {
            general: general_config,
            internal: internal_config
        }
    }

    pub fn default_manage_hook(m: Workspaces, _: Rc<WindowSystem>, _: Window) -> Workspaces {
        m
    }

    pub fn default_startup_hook(m: WindowManager, _: Rc<WindowSystem>, _: &Config) -> WindowManager {
        m
    }

    pub fn default_configuration(&mut self, w: &WindowSystem) {
        let mod_mask = self.general.mod_mask.clone();
        self.add_key_handler(w.get_keycode_from_string("Return"), mod_mask | SHIFTMASK,
            Box::new(|m, ws, c| start_terminal(m, ws, c)));
        self.add_key_handler(w.get_keycode_from_string("q"), mod_mask,
            Box::new(|m, ws, c| restart(m, ws, c)));
        self.add_key_handler(w.get_keycode_from_string("q"), mod_mask | SHIFTMASK,
            Box::new(|m, ws, c| exit(m, ws, c)));
    }

    pub fn get_mod_mask(&self) -> KeyModifiers {
        self.general.mod_mask.clone()
    }

    pub fn add_key_handler(&mut self, key: u64, mask: KeyModifiers, keyhandler: KeyHandler) {
        self.internal.key_handlers.insert(KeyCommand::new(key, mask), keyhandler);
    }

    pub fn add_mouse_handler(&mut self, button: MouseButton, mask: KeyModifiers,
                             mousehandler: MouseHandler) {
        self.internal.mouse_handlers.insert(MouseCommand::new(button, mask), mousehandler);
    }

    pub fn set_manage_hook(&mut self, hook: ManageHook) {
        self.internal.manage_hook = hook;
    }

    pub fn set_log_hook(&mut self, hook: LogHook) {
        self.internal.loghook = Some(hook);
    }

    pub fn compile_and_call(&mut self, m: &mut WindowManager, w: &WindowSystem) {
        let toml = format!("{}/Cargo.toml", self.internal.wtftw_dir.clone());

        if !path_exists(&self.internal.wtftw_dir.clone()) {
            match create_dir_all(Path::new(&self.internal.wtftw_dir.clone())) {
                Ok(()) => (),
                Err(e) => panic!(format!("mkdir: {} failed with error {}", self.internal.wtftw_dir.clone(), e))
            }
        }

        if path_exists(&toml.clone()) {
            let file = File::create(Path::new(&toml).as_os_str());
            file.unwrap().write("[project]\n\
                                     name = \"config\"\n\
                                     version = \"0.0.0\"\n\
                                     authors = [\"wtftw\"]\n\n\
                                     [dependencies.wtftw_contrib]
                                     git = \"https://github.com/Kintaro/wtftw-contrib.git\"\n
                                     [dependencies.wtftw]\n\
                                     git = \"https://github.com/Kintaro/wtftw.git\"\n\n\
                                     [lib]\n\
                                     name = \"config\"\n\
                                     crate-type = [\"dylib\"]".as_bytes()).unwrap();
        }

        let config_source = format!("{}/src/config.rs", self.internal.wtftw_dir.clone());
        if path_exists(&config_source) && self.compile() {
            self.call(m, w)
        } else {
            self.default_configuration(w);
        }
    }

    pub fn compile(&self) -> bool {
        info!("updating dependencies");
        Command::new("cargo")
            .current_dir(&Path::new(&self.internal.wtftw_dir.clone()))
            .arg("update")
            .env("RUST_LOG", "none")
            .output().unwrap();
        info!("compiling config module");
        let output = Command::new("cargo")
            .current_dir(&Path::new(&self.internal.wtftw_dir.clone()))
            .arg("build")//.arg("--release")
            .env("RUST_LOG", "none")
            .output();

        match output {
            Ok(o) => {
                if o.status.success() {
                    info!("config module compiled");
                    true
                } else {
                    error!("error compiling config module");

                    spawn(move || {
                        Command::new("xmessage").arg("\"error compiling config module. run 'cargo build' in ~/.wtftw to get more info.\"").spawn().unwrap();
                    });
                    false
                }
            },
            Err(err) => {
                error!("error compiling config module");
                spawn(move || {
                    Command::new("xmessage").arg(err.description()).spawn().unwrap();
                });
                false
            }
        }
    }

    pub fn call(&mut self, m: &mut WindowManager, w: &WindowSystem) {
        debug!("looking for config module");
        let mut contents = read_dir(&Path::new(&format!("{}/target/debug", self.internal.wtftw_dir.clone()))).unwrap();
        let libname = contents.find(|x| {
                            match x {
                                &Ok(ref y) => y.path().into_os_string().as_os_str().to_str().unwrap().contains("libconfig"),
                                &Err(_) => false
                            }
        });

        if let Ok(lib) = DynamicLibrary::open(Some(&Path::new(&libname.unwrap().unwrap().path().as_os_str().to_str().unwrap()))) {
            unsafe {
                if let Ok(symbol) = lib.symbol("configure") {
                    let result = mem::transmute::<*mut u8, extern fn(&mut WindowManager,
                                                        &WindowSystem,
                                                        &mut Config)>(symbol);

                    self.internal.library = Some(lib);
                    result(m, w, self);
                } else {
                    error!("Error loading config module")
                }
            }
        }
    }
}

fn path_exists(path: &String) -> bool {
    metadata(path).is_ok()
}
<commit_msg>Fix reversed boolean logic<commit_after>extern crate rustc_serialize;
extern crate dylib;

use std::env;
use std::borrow::ToOwned;
use std::collections::BTreeMap;
use core::workspaces::Workspaces;
use window_system::*;
use window_manager::WindowManager;
use handlers::{ KeyHandler, MouseHandler, ManageHook, StartupHook, LogHook };
use handlers::default::{ exit, restart, start_terminal };
use layout::{ Layout, TallLayout };

use std::mem;
use std::error::Error;
use std::fs::File;
use std::io::Write;
use std::fs::{ read_dir, create_dir_all };
use std::process::Command;
use std::process::Child;
use self::dylib::DynamicLibrary;
use std::rc::Rc;
use std::sync::RwLock;
use std::thread::spawn;
use std::path::PathBuf;
use std::path::Path;
use std::fs::metadata;

pub struct GeneralConfig {
    /// Whether focus follows mouse movements or
    /// only click events and keyboard movements.
    pub focus_follows_mouse: bool,
    /// Border color for focused windows.
    pub focus_border_color: u32,
    /// Border color for unfocused windows.
    pub border_color: u32,
    /// Border width. This is the same for both, focused and unfocused.
    pub border_width: u32,
    /// Default terminal to start
    pub terminal: (String, String),
    /// Keybind for the terminal
    /// Path to the logfile
    pub logfile: String,
    /// Default tags for workspaces
    pub tags: Vec<String>,
    /// Default launcher application
    pub launcher: String,
    pub mod_mask: KeyModifiers,
    pub pipes: Vec<Rc<RwLock<Child>>>,
    pub layout: Box<Layout>
}

impl Clone for GeneralConfig {
    fn clone(&self) -> GeneralConfig {
        GeneralConfig {
            focus_follows_mouse: self.focus_follows_mouse,
            focus_border_color: self.focus_border_color,
            border_color: self.border_color,
            border_width: self.border_width,
            terminal: self.terminal.clone(),
            logfile: self.logfile.clone(),
            tags: self.tags.clone(),
            launcher: self.launcher.clone(),
            mod_mask: self.mod_mask.clone(),
            pipes: self.pipes.clone(),
            layout: self.layout.copy()
        }
    }
}

pub struct InternalConfig {
    pub library: Option<DynamicLibrary>,
    pub key_handlers: BTreeMap<KeyCommand, KeyHandler>,
    pub mouse_handlers: BTreeMap<MouseCommand, MouseHandler>,
    pub manage_hook: ManageHook,
    pub startup_hook: StartupHook,
    pub loghook: Option<LogHook>,
    pub wtftw_dir: String,
}

impl InternalConfig {
    pub fn new(manage_hook: ManageHook, startup_hook: StartupHook, home: String) -> InternalConfig {
        InternalConfig {
            library: None,
            key_handlers: BTreeMap::new(),
            mouse_handlers: BTreeMap::new(),
            manage_hook: manage_hook,
            startup_hook: startup_hook,
            loghook: None,
            wtftw_dir: format!("{}/.wtftw", home)
        }
    }
}

/// Common configuration options for the window manager.
pub struct Config {
    pub general: GeneralConfig,
    pub internal: InternalConfig
}

impl Config {
    /// Create the Config from a json file
    pub fn initialize() -> Config {
        let home = env::home_dir().unwrap_or(PathBuf::from("./")).into_os_string().into_string().unwrap();
        // Default version of the config, for fallback
        let general_config =
            GeneralConfig {
                focus_follows_mouse: true,
                focus_border_color:  0x00B6FFB0,
                border_color:        0x00444444,
                border_width:        2,
                mod_mask:            MOD1MASK,
                terminal:            ("xterm".to_owned(), "".to_owned()),
                logfile:             format!("{}/.wtftw.log", home),
                tags:                vec!(
                    "1: term".to_owned(),
                    "2: web".to_owned(),
                    "3: code".to_owned(),
                    "4: media".to_owned()),
                launcher:            "dmenu_run".to_owned(),
                pipes:               Vec::new(),
                layout:              Box::new(TallLayout { num_master: 1, increment_ratio: 0.3/100.0, ratio: 0.5 }),
            };

        let internal_config = InternalConfig::new(
            Box::new(move |a, _, _| a.clone()),
            Box::new(move |a, _, _| a.clone()),
            //Box::new(Config::default_manage_hook),
            //Box::new(Config::default_startup_hook),
            home);

        Config {
            general: general_config,
            internal: internal_config
        }
    }

    pub fn default_manage_hook(m: Workspaces, _: Rc<WindowSystem>, _: Window) -> Workspaces {
        m
    }

    pub fn default_startup_hook(m: WindowManager, _: Rc<WindowSystem>, _: &Config) -> WindowManager {
        m
    }

    pub fn default_configuration(&mut self, w: &WindowSystem) {
        let mod_mask = self.general.mod_mask.clone();
        self.add_key_handler(w.get_keycode_from_string("Return"), mod_mask | SHIFTMASK,
            Box::new(|m, ws, c| start_terminal(m, ws, c)));
        self.add_key_handler(w.get_keycode_from_string("q"), mod_mask,
            Box::new(|m, ws, c| restart(m, ws, c)));
        self.add_key_handler(w.get_keycode_from_string("q"), mod_mask | SHIFTMASK,
            Box::new(|m, ws, c| exit(m, ws, c)));
    }

    pub fn get_mod_mask(&self) -> KeyModifiers {
        self.general.mod_mask.clone()
    }

    pub fn add_key_handler(&mut self, key: u64, mask: KeyModifiers, keyhandler: KeyHandler) {
        self.internal.key_handlers.insert(KeyCommand::new(key, mask), keyhandler);
    }

    pub fn add_mouse_handler(&mut self, button: MouseButton, mask: KeyModifiers,
                             mousehandler: MouseHandler) {
        self.internal.mouse_handlers.insert(MouseCommand::new(button, mask), mousehandler);
    }

    pub fn set_manage_hook(&mut self, hook: ManageHook) {
        self.internal.manage_hook = hook;
    }

    pub fn set_log_hook(&mut self, hook: LogHook) {
        self.internal.loghook = Some(hook);
    }

    pub fn compile_and_call(&mut self, m: &mut WindowManager, w: &WindowSystem) {
        let toml = format!("{}/Cargo.toml", self.internal.wtftw_dir.clone());

        if !path_exists(&self.internal.wtftw_dir.clone()) {
            match create_dir_all(Path::new(&self.internal.wtftw_dir.clone())) {
                Ok(()) => (),
                Err(e) => panic!(format!("mkdir: {} failed with error {}", self.internal.wtftw_dir.clone(), e))
            }
        }

        if !path_exists(&toml.clone()) {
            let file = File::create(Path::new(&toml).as_os_str());
            file.unwrap().write("[project]\n\
                                     name = \"config\"\n\
                                     version = \"0.0.0\"\n\
                                     authors = [\"wtftw\"]\n\n\
                                     [dependencies.wtftw_contrib]
                                     git = \"https://github.com/Kintaro/wtftw-contrib.git\"\n
                                     [dependencies.wtftw]\n\
                                     git = \"https://github.com/Kintaro/wtftw.git\"\n\n\
                                     [lib]\n\
                                     name = \"config\"\n\
                                     crate-type = [\"dylib\"]".as_bytes()).unwrap();
        }

        let config_source = format!("{}/src/config.rs", self.internal.wtftw_dir.clone());
        if path_exists(&config_source) && self.compile() {
            self.call(m, w)
        } else {
            self.default_configuration(w);
        }
    }

    pub fn compile(&self) -> bool {
        info!("updating dependencies");
        Command::new("cargo")
            .current_dir(&Path::new(&self.internal.wtftw_dir.clone()))
            .arg("update")
            .env("RUST_LOG", "none")
            .output().unwrap();
        info!("compiling config module");
        let output = Command::new("cargo")
            .current_dir(&Path::new(&self.internal.wtftw_dir.clone()))
            .arg("build")//.arg("--release")
            .env("RUST_LOG", "none")
            .output();

        match output {
            Ok(o) => {
                if o.status.success() {
                    info!("config module compiled");
                    true
                } else {
                    error!("error compiling config module");

                    spawn(move || {
                        Command::new("xmessage").arg("\"error compiling config module. run 'cargo build' in ~/.wtftw to get more info.\"").spawn().unwrap();
                    });
                    false
                }
            },
            Err(err) => {
                error!("error compiling config module");
                spawn(move || {
                    Command::new("xmessage").arg(err.description()).spawn().unwrap();
                });
                false
            }
        }
    }

    pub fn call(&mut self, m: &mut WindowManager, w: &WindowSystem) {
        debug!("looking for config module");
        let mut contents = read_dir(&Path::new(&format!("{}/target/debug", self.internal.wtftw_dir.clone()))).unwrap();
        let libname = contents.find(|x| {
                            match x {
                                &Ok(ref y) => y.path().into_os_string().as_os_str().to_str().unwrap().contains("libconfig"),
                                &Err(_) => false
                            }
        });

        if let Ok(lib) = DynamicLibrary::open(Some(&Path::new(&libname.unwrap().unwrap().path().as_os_str().to_str().unwrap()))) {
            unsafe {
                if let Ok(symbol) = lib.symbol("configure") {
                    let result = mem::transmute::<*mut u8, extern fn(&mut WindowManager,
                                                        &WindowSystem,
                                                        &mut Config)>(symbol);

                    self.internal.library = Some(lib);
                    result(m, w, self);
                } else {
                    error!("Error loading config module")
                }
            }
        }
    }
}

fn path_exists(path: &String) -> bool {
    metadata(path).is_ok()
}
<|endoftext|>


<commit_before>//! Connection pooling via r2d2.
//!
//! Note: This module requires enabling the `r2d2` feature

extern crate r2d2;

pub use self::r2d2::*;

/// A re-export of [`r2d2::Error`], which is only used by methods on [`r2d2::Pool`].
///
/// [`r2d2::Error`]: r2d2::Error
/// [`r2d2::Pool`]: r2d2::Pool
pub type PoolError = self::r2d2::Error;

use std::convert::Into;
use std::fmt;
use std::marker::PhantomData;

use crate::connection::{SimpleConnection, TransactionManager};
use crate::deserialize::FromSqlRow;
use crate::expression::QueryMetadata;
use crate::prelude::*;
use crate::query_builder::{AsQuery, QueryFragment, QueryId};
use crate::query_dsl::load_dsl::CompatibleType;

/// An r2d2 connection manager for use with Diesel.
///
/// See the [r2d2 documentation] for usage examples.
///
/// [r2d2 documentation]: r2d2
#[derive(Clone)]
pub struct ConnectionManager<T> {
    database_url: String,
    _marker: PhantomData<T>,
}

impl<T> fmt::Debug for ConnectionManager<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "ConnectionManager<{}>", std::any::type_name::<T>())
    }
}

unsafe impl<T: Send + 'static> Sync for ConnectionManager<T> {}

impl<T> ConnectionManager<T> {
    /// Returns a new connection manager,
    /// which establishes connections to the given database URL.
    pub fn new<S: Into<String>>(database_url: S) -> Self {
        ConnectionManager {
            database_url: database_url.into(),
            _marker: PhantomData,
        }
    }
}

/// The error used when managing connections with `r2d2`.
#[derive(Debug)]
pub enum Error {
    /// An error occurred establishing the connection
    ConnectionError(ConnectionError),

    /// An error occurred pinging the database
    QueryError(crate::result::Error),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::ConnectionError(ref e) => e.fmt(f),
            Error::QueryError(ref e) => e.fmt(f),
        }
    }
}

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

/// A trait indicating a connection could be used inside a r2d2 pool
pub trait R2D2Connection: Connection {
    /// Check if a connection is still valid
    fn ping(&mut self) -> QueryResult<()>;
}

#[cfg(feature = "postgres")]
impl R2D2Connection for crate::pg::PgConnection {
    fn ping(&mut self) -> QueryResult<()> {
        self.execute("SELECT 1").map(|_| ())
    }
}

#[cfg(feature = "mysql")]
impl R2D2Connection for crate::mysql::MysqlConnection {
    fn ping(&mut self) -> QueryResult<()> {
        self.execute("SELECT 1").map(|_| ())
    }
}

#[cfg(feature = "sqlite")]
impl R2D2Connection for crate::sqlite::SqliteConnection {
    fn ping(&mut self) -> QueryResult<()> {
        self.execute("SELECT 1").map(|_| ())
    }
}

impl<T> ManageConnection for ConnectionManager<T>
where
    T: R2D2Connection + Send + 'static,
{
    type Connection = T;
    type Error = Error;

    fn connect(&self) -> Result<T, Error> {
        T::establish(&self.database_url).map_err(Error::ConnectionError)
    }

    fn is_valid(&self, conn: &mut T) -> Result<(), Error> {
        conn.ping().map_err(Error::QueryError)
    }

    fn has_broken(&self, _conn: &mut T) -> bool {
        false
    }
}

impl<M> SimpleConnection for PooledConnection<M>
where
    M: ManageConnection,
    M::Connection: R2D2Connection + Send + 'static,
{
    fn batch_execute(&mut self, query: &str) -> QueryResult<()> {
        (&mut **self).batch_execute(query)
    }
}

impl<M> Connection for PooledConnection<M>
where
    M: ManageConnection,
    M::Connection: Connection + R2D2Connection + Send + 'static,
{
    type Backend = <M::Connection as Connection>::Backend;
    type TransactionManager =
        PoolTransactionManager<<M::Connection as Connection>::TransactionManager>;

    fn establish(_: &str) -> ConnectionResult<Self> {
        Err(ConnectionError::BadConnection(String::from(
            "Cannot directly establish a pooled connection",
        )))
    }

    fn execute(&mut self, query: &str) -> QueryResult<usize> {
        (&mut **self).execute(query)
    }

    fn load<T, U, ST>(&mut self, source: T) -> QueryResult<Vec<U>>
    where
        T: AsQuery,
        T::Query: QueryFragment<Self::Backend> + QueryId,
        T::SqlType: CompatibleType<U, Self::Backend, SqlType = ST>,
        U: FromSqlRow<ST, Self::Backend>,
        Self::Backend: QueryMetadata<T::SqlType>,
    {
        (&mut **self).load(source)
    }

    fn execute_returning_count<T>(&mut self, source: &T) -> QueryResult<usize>
    where
        T: QueryFragment<Self::Backend> + QueryId,
    {
        (&mut **self).execute_returning_count(source)
    }

    fn transaction_state(
        &mut self,
    ) -> &mut <Self::TransactionManager as TransactionManager<Self>>::TransactionStateData {
        (&mut **self).transaction_state()
    }

    fn begin_test_transaction(&mut self) -> QueryResult<()> {
        (&mut **self).begin_test_transaction()
    }
}

#[doc(hidden)]
#[allow(missing_debug_implementations)]
pub struct PoolTransactionManager<T>(std::marker::PhantomData<T>);

impl<M, T> TransactionManager<PooledConnection<M>> for PoolTransactionManager<T>
where
    M: ManageConnection,
    M::Connection: Connection<TransactionManager = T> + R2D2Connection,
    T: TransactionManager<M::Connection>,
{
    type TransactionStateData = T::TransactionStateData;

    fn begin_transaction(conn: &mut PooledConnection<M>) -> QueryResult<()> {
        T::begin_transaction(&mut **conn)
    }

    fn rollback_transaction(conn: &mut PooledConnection<M>) -> QueryResult<()> {
        T::rollback_transaction(&mut **conn)
    }

    fn commit_transaction(conn: &mut PooledConnection<M>) -> QueryResult<()> {
        T::commit_transaction(&mut **conn)
    }

    fn get_transaction_depth(conn: &mut PooledConnection<M>) -> u32 {
        T::get_transaction_depth(&mut **conn)
    }
}

impl<M> crate::migration::MigrationConnection for PooledConnection<M>
where
    M: ManageConnection,
    M::Connection: crate::migration::MigrationConnection,
    Self: Connection,
{
    fn setup(&mut self) -> QueryResult<usize> {
        (&mut **self).setup()
    }
}

impl<Changes, Output, M> crate::query_dsl::UpdateAndFetchResults<Changes, Output>
    for PooledConnection<M>
where
    M: ManageConnection,
    M::Connection: crate::query_dsl::UpdateAndFetchResults<Changes, Output>,
    Self: Connection,
{
    fn update_and_fetch(&mut self, changeset: Changes) -> QueryResult<Output> {
        (&mut **self).update_and_fetch(changeset)
    }
}

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

    use crate::r2d2::*;
    use crate::test_helpers::*;

    #[test]
    fn establish_basic_connection() {
        let manager = ConnectionManager::<TestConnection>::new(database_url());
        let pool = Arc::new(Pool::builder().max_size(2).build(manager).unwrap());

        let (s1, r1) = mpsc::channel();
        let (s2, r2) = mpsc::channel();

        let pool1 = Arc::clone(&pool);
        let t1 = thread::spawn(move || {
            let conn = pool1.get().unwrap();
            s1.send(()).unwrap();
            r2.recv().unwrap();
            drop(conn);
        });

        let pool2 = Arc::clone(&pool);
        let t2 = thread::spawn(move || {
            let conn = pool2.get().unwrap();
            s2.send(()).unwrap();
            r1.recv().unwrap();
            drop(conn);
        });

        t1.join().unwrap();
        t2.join().unwrap();

        pool.get().unwrap();
    }

    #[test]
    fn is_valid() {
        let manager = ConnectionManager::<TestConnection>::new(database_url());
        let pool = Pool::builder()
            .max_size(1)
            .test_on_check_out(true)
            .build(manager)
            .unwrap();

        pool.get().unwrap();
    }

    #[test]
    fn pooled_connection_impls_connection() {
        use crate::select;
        use crate::sql_types::Text;

        let manager = ConnectionManager::<TestConnection>::new(database_url());
        let pool = Pool::builder()
            .max_size(1)
            .test_on_check_out(true)
            .build(manager)
            .unwrap();
        let mut conn = pool.get().unwrap();

        let query = select("foo".into_sql::<Text>());
        assert_eq!("foo", query.get_result::<String>(&mut conn).unwrap());
    }
}
<commit_msg>Report connections as broken if the thread is currently panicking.<commit_after>//! Connection pooling via r2d2.
//!
//! Note: This module requires enabling the `r2d2` feature

extern crate r2d2;

pub use self::r2d2::*;

/// A re-export of [`r2d2::Error`], which is only used by methods on [`r2d2::Pool`].
///
/// [`r2d2::Error`]: r2d2::Error
/// [`r2d2::Pool`]: r2d2::Pool
pub type PoolError = self::r2d2::Error;

use std::convert::Into;
use std::fmt;
use std::marker::PhantomData;

use crate::connection::{SimpleConnection, TransactionManager};
use crate::deserialize::FromSqlRow;
use crate::expression::QueryMetadata;
use crate::prelude::*;
use crate::query_builder::{AsQuery, QueryFragment, QueryId};
use crate::query_dsl::load_dsl::CompatibleType;

/// An r2d2 connection manager for use with Diesel.
///
/// See the [r2d2 documentation] for usage examples.
///
/// [r2d2 documentation]: r2d2
#[derive(Clone)]
pub struct ConnectionManager<T> {
    database_url: String,
    _marker: PhantomData<T>,
}

impl<T> fmt::Debug for ConnectionManager<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "ConnectionManager<{}>", std::any::type_name::<T>())
    }
}

unsafe impl<T: Send + 'static> Sync for ConnectionManager<T> {}

impl<T> ConnectionManager<T> {
    /// Returns a new connection manager,
    /// which establishes connections to the given database URL.
    pub fn new<S: Into<String>>(database_url: S) -> Self {
        ConnectionManager {
            database_url: database_url.into(),
            _marker: PhantomData,
        }
    }
}

/// The error used when managing connections with `r2d2`.
#[derive(Debug)]
pub enum Error {
    /// An error occurred establishing the connection
    ConnectionError(ConnectionError),

    /// An error occurred pinging the database
    QueryError(crate::result::Error),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::ConnectionError(ref e) => e.fmt(f),
            Error::QueryError(ref e) => e.fmt(f),
        }
    }
}

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

/// A trait indicating a connection could be used inside a r2d2 pool
pub trait R2D2Connection: Connection {
    /// Check if a connection is still valid
    fn ping(&mut self) -> QueryResult<()>;
}

#[cfg(feature = "postgres")]
impl R2D2Connection for crate::pg::PgConnection {
    fn ping(&mut self) -> QueryResult<()> {
        self.execute("SELECT 1").map(|_| ())
    }
}

#[cfg(feature = "mysql")]
impl R2D2Connection for crate::mysql::MysqlConnection {
    fn ping(&mut self) -> QueryResult<()> {
        self.execute("SELECT 1").map(|_| ())
    }
}

#[cfg(feature = "sqlite")]
impl R2D2Connection for crate::sqlite::SqliteConnection {
    fn ping(&mut self) -> QueryResult<()> {
        self.execute("SELECT 1").map(|_| ())
    }
}

impl<T> ManageConnection for ConnectionManager<T>
where
    T: R2D2Connection + Send + 'static,
{
    type Connection = T;
    type Error = Error;

    fn connect(&self) -> Result<T, Error> {
        T::establish(&self.database_url).map_err(Error::ConnectionError)
    }

    fn is_valid(&self, conn: &mut T) -> Result<(), Error> {
        conn.ping().map_err(Error::QueryError)
    }

    fn has_broken(&self, _conn: &mut T) -> bool {
        std::thread::panicking()
    }
}

impl<M> SimpleConnection for PooledConnection<M>
where
    M: ManageConnection,
    M::Connection: R2D2Connection + Send + 'static,
{
    fn batch_execute(&mut self, query: &str) -> QueryResult<()> {
        (&mut **self).batch_execute(query)
    }
}

impl<M> Connection for PooledConnection<M>
where
    M: ManageConnection,
    M::Connection: Connection + R2D2Connection + Send + 'static,
{
    type Backend = <M::Connection as Connection>::Backend;
    type TransactionManager =
        PoolTransactionManager<<M::Connection as Connection>::TransactionManager>;

    fn establish(_: &str) -> ConnectionResult<Self> {
        Err(ConnectionError::BadConnection(String::from(
            "Cannot directly establish a pooled connection",
        )))
    }

    fn execute(&mut self, query: &str) -> QueryResult<usize> {
        (&mut **self).execute(query)
    }

    fn load<T, U, ST>(&mut self, source: T) -> QueryResult<Vec<U>>
    where
        T: AsQuery,
        T::Query: QueryFragment<Self::Backend> + QueryId,
        T::SqlType: CompatibleType<U, Self::Backend, SqlType = ST>,
        U: FromSqlRow<ST, Self::Backend>,
        Self::Backend: QueryMetadata<T::SqlType>,
    {
        (&mut **self).load(source)
    }

    fn execute_returning_count<T>(&mut self, source: &T) -> QueryResult<usize>
    where
        T: QueryFragment<Self::Backend> + QueryId,
    {
        (&mut **self).execute_returning_count(source)
    }

    fn transaction_state(
        &mut self,
    ) -> &mut <Self::TransactionManager as TransactionManager<Self>>::TransactionStateData {
        (&mut **self).transaction_state()
    }

    fn begin_test_transaction(&mut self) -> QueryResult<()> {
        (&mut **self).begin_test_transaction()
    }
}

#[doc(hidden)]
#[allow(missing_debug_implementations)]
pub struct PoolTransactionManager<T>(std::marker::PhantomData<T>);

impl<M, T> TransactionManager<PooledConnection<M>> for PoolTransactionManager<T>
where
    M: ManageConnection,
    M::Connection: Connection<TransactionManager = T> + R2D2Connection,
    T: TransactionManager<M::Connection>,
{
    type TransactionStateData = T::TransactionStateData;

    fn begin_transaction(conn: &mut PooledConnection<M>) -> QueryResult<()> {
        T::begin_transaction(&mut **conn)
    }

    fn rollback_transaction(conn: &mut PooledConnection<M>) -> QueryResult<()> {
        T::rollback_transaction(&mut **conn)
    }

    fn commit_transaction(conn: &mut PooledConnection<M>) -> QueryResult<()> {
        T::commit_transaction(&mut **conn)
    }

    fn get_transaction_depth(conn: &mut PooledConnection<M>) -> u32 {
        T::get_transaction_depth(&mut **conn)
    }
}

impl<M> crate::migration::MigrationConnection for PooledConnection<M>
where
    M: ManageConnection,
    M::Connection: crate::migration::MigrationConnection,
    Self: Connection,
{
    fn setup(&mut self) -> QueryResult<usize> {
        (&mut **self).setup()
    }
}

impl<Changes, Output, M> crate::query_dsl::UpdateAndFetchResults<Changes, Output>
    for PooledConnection<M>
where
    M: ManageConnection,
    M::Connection: crate::query_dsl::UpdateAndFetchResults<Changes, Output>,
    Self: Connection,
{
    fn update_and_fetch(&mut self, changeset: Changes) -> QueryResult<Output> {
        (&mut **self).update_and_fetch(changeset)
    }
}

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

    use crate::r2d2::*;
    use crate::test_helpers::*;

    #[test]
    fn establish_basic_connection() {
        let manager = ConnectionManager::<TestConnection>::new(database_url());
        let pool = Arc::new(Pool::builder().max_size(2).build(manager).unwrap());

        let (s1, r1) = mpsc::channel();
        let (s2, r2) = mpsc::channel();

        let pool1 = Arc::clone(&pool);
        let t1 = thread::spawn(move || {
            let conn = pool1.get().unwrap();
            s1.send(()).unwrap();
            r2.recv().unwrap();
            drop(conn);
        });

        let pool2 = Arc::clone(&pool);
        let t2 = thread::spawn(move || {
            let conn = pool2.get().unwrap();
            s2.send(()).unwrap();
            r1.recv().unwrap();
            drop(conn);
        });

        t1.join().unwrap();
        t2.join().unwrap();

        pool.get().unwrap();
    }

    #[test]
    fn is_valid() {
        let manager = ConnectionManager::<TestConnection>::new(database_url());
        let pool = Pool::builder()
            .max_size(1)
            .test_on_check_out(true)
            .build(manager)
            .unwrap();

        pool.get().unwrap();
    }

    #[test]
    fn pooled_connection_impls_connection() {
        use crate::select;
        use crate::sql_types::Text;

        let manager = ConnectionManager::<TestConnection>::new(database_url());
        let pool = Pool::builder()
            .max_size(1)
            .test_on_check_out(true)
            .build(manager)
            .unwrap();
        let mut conn = pool.get().unwrap();

        let query = select("foo".into_sql::<Text>());
        assert_eq!("foo", query.get_result::<String>(&mut conn).unwrap());
    }
}
<|endoftext|>


<commit_before>use bip_handshake::{HandshakerBuilder, InitiateMessage, Protocol, DiscoveryInfo};
use bip_handshake::transports::TcpTransport;

use bip_util::bt::{self};
use tokio_core::reactor::{Core};
use futures::Future;
use futures::stream::Stream;
use futures::sink::Sink;

#[test]
fn positive_connect() {
    let mut core = Core::new().unwrap();

    let mut handshaker_one_addr = "127.0.0.1:0".parse().unwrap();
    let handshaker_one_pid = [4u8; bt::PEER_ID_LEN].into();

    let handshaker_one = HandshakerBuilder::new()
        .with_bind_addr(handshaker_one_addr)
        .with_peer_id(handshaker_one_pid)
        .build::<TcpTransport>(core.handle()).unwrap();

    handshaker_one_addr.set_port(handshaker_one.port());

    let mut handshaker_two_addr = "127.0.0.1:0".parse().unwrap();
    let handshaker_two_pid = [5u8; bt::PEER_ID_LEN].into();

    let handshaker_two = HandshakerBuilder::new()
        .with_bind_addr(handshaker_two_addr)
        .with_peer_id(handshaker_two_pid)
        .build::<TcpTransport>(core.handle()).unwrap();

    handshaker_two_addr.set_port(handshaker_two.port());

    let (item_one, item_two) = core.run(handshaker_one
        .send(InitiateMessage::new(Protocol::BitTorrent, [55u8; bt::INFO_HASH_LEN].into(), handshaker_two_addr))
        .map_err(|_| ())
        .and_then(|handshaker_one| {
            handshaker_one.into_future()
                .join(handshaker_two.into_future())
                .map_err(|_| ())
        })
        .map(|((opt_item_one, _), (opt_item_two, _))| {
            (opt_item_one.unwrap(), opt_item_two.unwrap())
        })
    ).unwrap();

    assert_eq!(handshaker_one_addr, *item_two.address());
    assert_eq!(handshaker_two_addr, *item_one.address());

    assert_eq!(handshaker_one_pid, *item_two.peer_id());
    assert_eq!(handshaker_two_pid, *item_one.peer_id());
}<commit_msg>Dont Assert On The Initiator's Address In Integ Test<commit_after>use bip_handshake::{HandshakerBuilder, InitiateMessage, Protocol, DiscoveryInfo};
use bip_handshake::transports::TcpTransport;

use bip_util::bt::{self};
use tokio_core::reactor::{Core};
use futures::Future;
use futures::stream::Stream;
use futures::sink::Sink;

#[test]
fn positive_connect() {
    let mut core = Core::new().unwrap();

    let mut handshaker_one_addr = "127.0.0.1:0".parse().unwrap();
    let handshaker_one_pid = [4u8; bt::PEER_ID_LEN].into();

    let handshaker_one = HandshakerBuilder::new()
        .with_bind_addr(handshaker_one_addr)
        .with_peer_id(handshaker_one_pid)
        .build::<TcpTransport>(core.handle()).unwrap();

    handshaker_one_addr.set_port(handshaker_one.port());

    let mut handshaker_two_addr = "127.0.0.1:0".parse().unwrap();
    let handshaker_two_pid = [5u8; bt::PEER_ID_LEN].into();

    let handshaker_two = HandshakerBuilder::new()
        .with_bind_addr(handshaker_two_addr)
        .with_peer_id(handshaker_two_pid)
        .build::<TcpTransport>(core.handle()).unwrap();

    handshaker_two_addr.set_port(handshaker_two.port());

    let (item_one, item_two) = core.run(handshaker_one
        .send(InitiateMessage::new(Protocol::BitTorrent, [55u8; bt::INFO_HASH_LEN].into(), handshaker_two_addr))
        .map_err(|_| ())
        .and_then(|handshaker_one| {
            handshaker_one.into_future()
                .join(handshaker_two.into_future())
                .map_err(|_| ())
        })
        .map(|((opt_item_one, _), (opt_item_two, _))| {
            (opt_item_one.unwrap(), opt_item_two.unwrap())
        })
    ).unwrap();

    // Result from handshaker one should match handshaker two's listen address
    assert_eq!(handshaker_two_addr, *item_one.address());

    assert_eq!(handshaker_one_pid, *item_two.peer_id());
    assert_eq!(handshaker_two_pid, *item_one.peer_id());
}<|endoftext|>


<commit_before>
mod internal
{

  ///
  /// Compile-time assertion that two types have the same size.
  ///

  #[ cfg( feature = "ct" ) ]
  #[macro_export]
  macro_rules! ct_type_same_size
  {
    ( $Type1:ty, $Type2:ty $(,)? ) =>
    {{
      let _ = core::mem::transmute::< $Type1, $Type2 >;
      true
    }}
  }

  ///
  /// Compile-time assertion that memory behind two references have the same size.
  ///

  #[ cfg( feature = "ct" ) ]
  #[macro_export]
  macro_rules! ct_ptr_same_size
  {
    ( $Ins1:expr, $Ins2:expr $(,)? ) =>
    {{
      #[ allow( unsafe_code, unknown_lints, forget_copy, useless_transmute ) ]
      let _ = || unsafe
      {
        let mut ins1 = core::ptr::read( $Ins1 );
        core::ptr::write( &mut ins1, core::mem::transmute( core::ptr::read( $Ins2 ) ) );
        core::mem::forget( ins1 );
      };
      true
    }}
  }

  ///
  /// Compile-time assertion that two values have the same size.
  ///
  /// Does not consume values.
  ///

  #[ cfg( feature = "ct" ) ]
  #[macro_export]
  macro_rules! ct_mem_same_size
  {
    ( $Ins1:expr, $Ins2:expr $(,)? ) =>
    {{
      $crate::ct_ptr_same_size!( &$Ins1, &$Ins2 )
    }}
  }

  pub use ct_type_same_size;
  pub use ct_ptr_same_size;
  pub use ct_mem_same_size;
}

/// Own namespace of the module.
pub mod protected
{
  pub use super::orphan::*;
}

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

/// Orphan namespace of the module.
pub mod orphan
{
  pub use super::exposed::*;
}

/// Exposed namespace of the module.
pub mod exposed
{
  pub use super::prelude::*;
}

/// Prelude to use essentials: `use my_module::prelude::*`.
pub mod prelude
{
  pub use super::internal::
  {
    ct_type_same_size,
    ct_ptr_same_size,
    ct_mem_same_size,
  };
}
<commit_msg>!build<commit_after>
mod internal
{

  ///
  /// Compile-time assertion that two types have the same size.
  ///

  #[ cfg( feature = "ct" ) ]
  #[macro_export]
  macro_rules! ct_type_same_size
  {
    ( $Type1:ty, $Type2:ty $(,)? ) =>
    {{
      let _ = core::mem::transmute::< $Type1, $Type2 >;
      true
    }}
  }

  ///
  /// Compile-time assertion that memory behind two references have the same size.
  ///

  #[ cfg( feature = "ct" ) ]
  #[macro_export]
  macro_rules! ct_ptr_same_size
  {
    ( $Ins1:expr, $Ins2:expr $(,)? ) =>
    {{
      #[ allow( unsafe_code, unknown_lints, forget_copy, useless_transmute ) ]
      let _ = || unsafe
      {
        let mut ins1 = core::ptr::read( $Ins1 );
        core::ptr::write( &mut ins1, core::mem::transmute( core::ptr::read( $Ins2 ) ) );
        core::mem::forget( ins1 );
      };
      true
    }}
  }

  ///
  /// Compile-time assertion that two values have the same size.
  ///
  /// Does not consume values.
  ///

  #[ cfg( feature = "ct" ) ]
  #[macro_export]
  macro_rules! ct_mem_same_size
  {
    ( $Ins1:expr, $Ins2:expr $(,)? ) =>
    {{
      $crate::ct_ptr_same_size!( &$Ins1, &$Ins2 )
    }}
  }

  pub use ct_type_same_size;
  pub use ct_ptr_same_size;
  pub use ct_mem_same_size;
}

/// Own namespace of the module.
pub mod protected
{
  pub use super::orphan::*;
}

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

/// Orphan namespace of the module.
pub mod orphan
{
  pub use super::exposed::*;
}

/// Exposed namespace of the module.
pub mod exposed
{
  pub use super::prelude::*;
}

/// Prelude to use essentials: `use my_module::prelude::*`.
pub mod prelude
{
  pub use super::internal::
  {
    ct_type_same_size,
    ct_ptr_same_size,
    ct_mem_same_size,
  };
}

<|endoftext|>


<commit_before>use binaries::BinaryInstallation;
use kernel::{
    async_trait::async_trait,
    eyre::{bail, eyre, Result},
    stencila_schema::{CodeError, Node},
    Kernel, KernelStatus, KernelTrait,
};
use serde::Serialize;
use std::{env, fs};
use tokio::{
    io::{BufReader, BufWriter},
    process::{Child, ChildStderr, ChildStdin, ChildStdout},
};

// Re-exports for the convenience of crates that implement `MicroKernelTrait`
pub use kernel;

/// The Unicode code point used as the separator between results
/// (both "outputs" on `stderr` and "messages" on `stderr`)
const RES_SEP: char = '\u{10ABBA}';

/// The Unicode code point used as the separator between individual
/// Microkernel "transactions".
const TRANS_SEP: char = '\u{10ACDC}';

#[derive(Debug, Serialize)]
pub struct MicroKernel {
    /// The language of the kernel
    ///
    /// Used to be able to return a `Kernel` spec.
    language: String,

    /// The resolved binary for the kernel
    binary: BinaryInstallation,

    /// Arguments that should be supplied to the runtime binary
    ///
    /// Use the argument `"{{script}}"` as a placeholder for the name
    /// of the script file.
    args: Vec<String>,

    /// The script that runs the kernel
    script: (String, String),

    /// Other files that the kernel uses (e.g. codec)
    others: Vec<(String, String)>,

    /// The code template for setting a variable
    set_template: String,

    /// The code template for getting a variable
    get_template: String,

    /// The current status of the kernel
    status: KernelStatus,

    /// The child process of the kernel
    #[serde(skip)]
    child: Option<Child>,

    /// The writer for the stdin stream of the child process
    #[serde(skip)]
    stdin: Option<BufWriter<ChildStdin>>,

    /// The reader for the stdout stream of the child process
    #[serde(skip)]
    stdout: Option<BufReader<ChildStdout>>,

    /// The reader for the stderr stream of the child process
    #[serde(skip)]
    stderr: Option<BufReader<ChildStderr>>,
}

impl MicroKernel {
    /// Create a new `MicroKernel`
    ///
    /// This function will error if no runtime matching the semver requirements
    /// in `runtime` is found on the `system`.
    pub async fn new(
        language: &str,
        runtime: (&str, &str, &[&str]),
        script: (&str, &str),
        others: &[(&str, &str)],
        set_template: &str,
        get_template: &str,
    ) -> Result<Self> {
        let (name, semver, args) = runtime;
        let binary = match binaries::require(name, semver).await {
            Ok(binary) => binary,
            Err(error) => bail!("Unable to find or install runtime for kernel: {}", error),
        };

        let kernel = Self {
            language: language.to_string(),
            binary,
            args: args.iter().map(|arg| arg.to_string()).collect(),
            script: (script.0.to_string(), script.1.to_string()),
            others: others
                .iter()
                .map(|file| (file.0.to_string(), file.1.to_string()))
                .collect(),
            set_template: set_template.into(),
            get_template: get_template.into(),
            status: KernelStatus::Pending,
            child: None,
            stdin: None,
            stdout: None,
            stderr: None,
        };

        Ok(kernel)
    }
}

/// Include a file as a (name, content) tuple
#[macro_export]
macro_rules! include_file {
    ($name:literal) => {
        ($name, include_str!($name))
    };
}

#[async_trait]
impl KernelTrait for MicroKernel {
    /// Get the [`Kernel`] specification
    fn spec(&self) -> Kernel {
        Kernel {
            language: self.language.clone(),
        }
    }

    /// Start the kernel
    ///
    /// An override of `KernelTrait::start` that searches for the preferred executable
    /// and runs it using specified commands, including the kernel script file if specified
    /// in the arguments.
    async fn start(&mut self) -> Result<()> {
        // Resolve the directory where kernels ar run
        let user_data_dir = dirs::data_dir().unwrap_or_else(|| env::current_dir().unwrap());
        let dir = match env::consts::OS {
            "macos" | "windows" => user_data_dir.join("Stencila").join("Microkernels"),
            _ => user_data_dir.join("stencila").join("microkernels"),
        };
        fs::create_dir_all(&dir)?;

        // Copy over script file
        let script_path = dir.join(&self.script.0);
        fs::write(&script_path, &self.script.1)?;

        // Copy over other files
        for other in &self.others {
            fs::write(dir.join(&other.0), &other.1)?;
        }

        // Construct args array, inserting script path where appropriate
        let args: Vec<String> = self
            .args
            .iter()
            .map(|arg| match arg.as_str() {
                "{{script}}" => script_path.display().to_string(),
                _ => arg.to_string(),
            })
            .collect();

        // Start child process
        let mut child = self.binary.interact(&args)?;

        let stdin = child
            .stdin
            .take()
            .ok_or_else(|| eyre!("Child has no stdin handle"))?;
        let stdout = child
            .stdout
            .take()
            .ok_or_else(|| eyre!("Child has no stdout handle"))?;
        let stderr = child
            .stderr
            .take()
            .ok_or_else(|| eyre!("Child has no stderr handle"))?;

        self.child = Some(child);
        self.stdin = Some(BufWriter::new(stdin));
        self.stdout = Some(BufReader::new(stdout));
        self.stderr = Some(BufReader::new(stderr));
        self.status = KernelStatus::Idle;

        Ok(())
    }

    /// Stop the kernel
    ///
    /// An override of `KernelTrait::stop` that kills the child process.
    async fn stop(&mut self) -> Result<()> {
        if let Some(child) = self.child.as_mut() {
            self.status = KernelStatus::Stopping;
            child.kill().await?;
            self.child = None;
            self.status = KernelStatus::Finished;
        }
        Ok(())
    }

    /// Get the status of the kernel
    async fn status(&self) -> Result<KernelStatus> {
        Ok(self.status.clone())
    }

    /// Get a symbol from the kernel
    async fn get(&mut self, name: &str) -> Result<Node> {
        let code = self.get_template.replace("{{name}}", name);

        let (outputs, messages) = self.exec(&code).await?;

        if let Some(output) = outputs.first() {
            Ok(output.clone())
        } else {
            // TODO: When messages include CodeWarning etc log those
            // and only bail on CodeError
            let message = messages
                .first()
                .map(|message| message.error_message.clone())
                .unwrap_or_else(|| "Unknown error".to_string());
            bail!("Unable to get symbol `{}`: {}", name, message)
        }
    }

    /// Set a symbol in the kernel
    async fn set(&mut self, name: &str, value: Node) -> Result<()> {
        let json = serde_json::to_string(&value)?;
        let code = self
            .set_template
            .replace("{{name}}", name)
            .replace("{{json}}", &json);

        let (.., messages) = self.exec(&code).await?;

        if messages.is_empty() {
            Ok(())
        } else {
            // TODO: When messages include CodeWarning etc log those
            // and only bail on CodeError
            let message = messages
                .first()
                .map(|message| message.error_message.clone())
                .unwrap_or_else(|| "Unknown error".to_string());
            bail!("Unable to set symbol `{}`: {}", name, message)
        }
    }

    /// Execute some code in the kernel
    async fn exec(&mut self, code: &str) -> Result<(Vec<Node>, Vec<CodeError>)> {
        use tokio::io::AsyncBufReadExt;
        use tokio::io::AsyncWriteExt;

        let stdin = self
            .stdin
            .as_mut()
            .expect("Kernel should have started and have stdin");
        let stdout = self
            .stdout
            .as_mut()
            .expect("Kernel should have started and have stdout");
        let stderr = self
            .stderr
            .as_mut()
            .expect("Kernel should have started and have stderr");

        // Send code to the kernel
        tracing::debug!("Sending on stdin");
        if let Err(error) = stdin
            .write_all([&code.replace("\n", "\\n"), "\n"].concat().as_bytes())
            .await
        {
            self.status = KernelStatus::Failed;
            bail!("When writing code to kernel: {}", error)
        }
        if let Err(error) = stdin.flush().await {
            self.status = KernelStatus::Failed;
            bail!("When flushing code to kernel: {}", error)
        }

        // Capture outputs separating them as we go
        let mut output = String::new();
        let mut outputs = Vec::new();
        loop {
            let line = match stdout.lines().next_line().await {
                Ok(Some(line)) => line,
                Ok(None) => break,
                Err(error) => {
                    self.status = KernelStatus::Failed;
                    bail!("When receiving outputs from kernel: {}", error)
                }
            };

            tracing::debug!("Received on stdout: {}", line);
            if let Some(line) = line.strip_suffix(RES_SEP) {
                output.push_str(line);
                if !output.is_empty() {
                    outputs.push(output.clone());
                    output.clear();
                }
            } else if let Some(line) = line.strip_suffix(TRANS_SEP) {
                output.push_str(line);
                if !output.is_empty() {
                    outputs.push(output.clone());
                }
                break;
            } else {
                output.push_str(&line);
                output.push('\n');
            }
        }

        // Attempt to parse each output as JSON into a `Node`, falling back to a string.
        let outputs: Vec<Node> = outputs
            .iter()
            .map(|output| -> Node {
                serde_json::from_str(output).unwrap_or_else(|_| Node::String(output.clone()))
            })
            .collect();

        // Capture messages separating them as we go
        let mut message = String::new();
        let mut messages = Vec::new();
        loop {
            let line = match stderr.lines().next_line().await {
                Ok(Some(line)) => line,
                Ok(None) => break,
                Err(error) => {
                    self.status = KernelStatus::Failed;
                    bail!("When receiving messages from kernel: {}", error)
                }
            };

            tracing::debug!("Received on stderr: {}", line);
            if let Some(line) = line.strip_suffix(RES_SEP) {
                message.push_str(line);
                if !message.is_empty() {
                    messages.push(message.clone());
                    message.clear();
                }
            } else if let Some(line) = line.strip_suffix(TRANS_SEP) {
                message.push_str(line);
                if !message.is_empty() {
                    messages.push(message.clone());
                }
                break;
            } else {
                message.push_str(&line);
                message.push('\n');
            }
        }

        // Attempt to parse each message as JSON into a `CodeMessage`.
        let messages: Vec<CodeError> = messages
            .iter()
            .map(|message| -> CodeError {
                serde_json::from_str(message).unwrap_or_else(|_| CodeError {
                    error_message: message.into(),
                    ..Default::default()
                })
            })
            .collect();

        Ok((outputs, messages))
    }
}
<commit_msg>refactor(Microkernel): Make escaping more obvious<commit_after>use binaries::BinaryInstallation;
use kernel::{
    async_trait::async_trait,
    eyre::{bail, eyre, Result},
    stencila_schema::{CodeError, Node},
    Kernel, KernelStatus, KernelTrait,
};
use serde::Serialize;
use std::{env, fs};
use tokio::{
    io::{BufReader, BufWriter},
    process::{Child, ChildStderr, ChildStdin, ChildStdout},
};

// Re-exports for the convenience of crates that implement `MicroKernelTrait`
pub use kernel;

/// The Unicode code point used as the separator between results
/// (both "outputs" on `stderr` and "messages" on `stderr`)
const RES_SEP: char = '\u{10ABBA}';

/// The Unicode code point used as the separator between individual
/// Microkernel "transactions".
const TRANS_SEP: char = '\u{10ACDC}';

#[derive(Debug, Serialize)]
pub struct MicroKernel {
    /// The language of the kernel
    ///
    /// Used to be able to return a `Kernel` spec.
    language: String,

    /// The resolved binary for the kernel
    binary: BinaryInstallation,

    /// Arguments that should be supplied to the runtime binary
    ///
    /// Use the argument `"{{script}}"` as a placeholder for the name
    /// of the script file.
    args: Vec<String>,

    /// The script that runs the kernel
    script: (String, String),

    /// Other files that the kernel uses (e.g. codec)
    others: Vec<(String, String)>,

    /// The code template for setting a variable
    set_template: String,

    /// The code template for getting a variable
    get_template: String,

    /// The current status of the kernel
    status: KernelStatus,

    /// The child process of the kernel
    #[serde(skip)]
    child: Option<Child>,

    /// The writer for the stdin stream of the child process
    #[serde(skip)]
    stdin: Option<BufWriter<ChildStdin>>,

    /// The reader for the stdout stream of the child process
    #[serde(skip)]
    stdout: Option<BufReader<ChildStdout>>,

    /// The reader for the stderr stream of the child process
    #[serde(skip)]
    stderr: Option<BufReader<ChildStderr>>,
}

impl MicroKernel {
    /// Create a new `MicroKernel`
    ///
    /// This function will error if no runtime matching the semver requirements
    /// in `runtime` is found on the `system`.
    pub async fn new(
        language: &str,
        runtime: (&str, &str, &[&str]),
        script: (&str, &str),
        others: &[(&str, &str)],
        set_template: &str,
        get_template: &str,
    ) -> Result<Self> {
        let (name, semver, args) = runtime;
        let binary = match binaries::require(name, semver).await {
            Ok(binary) => binary,
            Err(error) => bail!("Unable to find or install runtime for kernel: {}", error),
        };

        let kernel = Self {
            language: language.to_string(),
            binary,
            args: args.iter().map(|arg| arg.to_string()).collect(),
            script: (script.0.to_string(), script.1.to_string()),
            others: others
                .iter()
                .map(|file| (file.0.to_string(), file.1.to_string()))
                .collect(),
            set_template: set_template.into(),
            get_template: get_template.into(),
            status: KernelStatus::Pending,
            child: None,
            stdin: None,
            stdout: None,
            stderr: None,
        };

        Ok(kernel)
    }
}

/// Include a file as a (name, content) tuple
#[macro_export]
macro_rules! include_file {
    ($name:literal) => {
        ($name, include_str!($name))
    };
}

#[async_trait]
impl KernelTrait for MicroKernel {
    /// Get the [`Kernel`] specification
    fn spec(&self) -> Kernel {
        Kernel {
            language: self.language.clone(),
        }
    }

    /// Start the kernel
    ///
    /// An override of `KernelTrait::start` that searches for the preferred executable
    /// and runs it using specified commands, including the kernel script file if specified
    /// in the arguments.
    async fn start(&mut self) -> Result<()> {
        // Resolve the directory where kernels ar run
        let user_data_dir = dirs::data_dir().unwrap_or_else(|| env::current_dir().unwrap());
        let dir = match env::consts::OS {
            "macos" | "windows" => user_data_dir.join("Stencila").join("Microkernels"),
            _ => user_data_dir.join("stencila").join("microkernels"),
        };
        fs::create_dir_all(&dir)?;

        // Copy over script file
        let script_path = dir.join(&self.script.0);
        fs::write(&script_path, &self.script.1)?;

        // Copy over other files
        for other in &self.others {
            fs::write(dir.join(&other.0), &other.1)?;
        }

        // Construct args array, inserting script path where appropriate
        let args: Vec<String> = self
            .args
            .iter()
            .map(|arg| match arg.as_str() {
                "{{script}}" => script_path.display().to_string(),
                _ => arg.to_string(),
            })
            .collect();

        // Start child process
        let mut child = self.binary.interact(&args)?;

        let stdin = child
            .stdin
            .take()
            .ok_or_else(|| eyre!("Child has no stdin handle"))?;
        let stdout = child
            .stdout
            .take()
            .ok_or_else(|| eyre!("Child has no stdout handle"))?;
        let stderr = child
            .stderr
            .take()
            .ok_or_else(|| eyre!("Child has no stderr handle"))?;

        self.child = Some(child);
        self.stdin = Some(BufWriter::new(stdin));
        self.stdout = Some(BufReader::new(stdout));
        self.stderr = Some(BufReader::new(stderr));
        self.status = KernelStatus::Idle;

        Ok(())
    }

    /// Stop the kernel
    ///
    /// An override of `KernelTrait::stop` that kills the child process.
    async fn stop(&mut self) -> Result<()> {
        if let Some(child) = self.child.as_mut() {
            self.status = KernelStatus::Stopping;
            child.kill().await?;
            self.child = None;
            self.status = KernelStatus::Finished;
        }
        Ok(())
    }

    /// Get the status of the kernel
    async fn status(&self) -> Result<KernelStatus> {
        Ok(self.status.clone())
    }

    /// Get a symbol from the kernel
    async fn get(&mut self, name: &str) -> Result<Node> {
        let code = self.get_template.replace("{{name}}", name);

        let (outputs, messages) = self.exec(&code).await?;

        if let Some(output) = outputs.first() {
            Ok(output.clone())
        } else {
            // TODO: When messages include CodeWarning etc log those
            // and only bail on CodeError
            let message = messages
                .first()
                .map(|message| message.error_message.clone())
                .unwrap_or_else(|| "Unknown error".to_string());
            bail!("Unable to get symbol `{}`: {}", name, message)
        }
    }

    /// Set a symbol in the kernel
    async fn set(&mut self, name: &str, value: Node) -> Result<()> {
        let json = serde_json::to_string(&value)?;
        let code = self
            .set_template
            .replace("{{name}}", name)
            .replace("{{json}}", &json);

        let (.., messages) = self.exec(&code).await?;

        if messages.is_empty() {
            Ok(())
        } else {
            // TODO: When messages include CodeWarning etc log those
            // and only bail on CodeError
            let message = messages
                .first()
                .map(|message| message.error_message.clone())
                .unwrap_or_else(|| "Unknown error".to_string());
            bail!("Unable to set symbol `{}`: {}", name, message)
        }
    }

    /// Execute some code in the kernel
    async fn exec(&mut self, code: &str) -> Result<(Vec<Node>, Vec<CodeError>)> {
        use tokio::io::AsyncBufReadExt;
        use tokio::io::AsyncWriteExt;

        let stdin = self
            .stdin
            .as_mut()
            .expect("Kernel should have started and have stdin");
        let stdout = self
            .stdout
            .as_mut()
            .expect("Kernel should have started and have stdout");
        let stderr = self
            .stderr
            .as_mut()
            .expect("Kernel should have started and have stderr");

        // Send code to the kernel
        tracing::debug!("Sending on stdin");
        let escaped = code.replace("\n", "\\n");
        if let Err(error) = stdin
            .write_all([&escaped, "\n"].concat().as_bytes())
            .await
        {
            self.status = KernelStatus::Failed;
            bail!("When writing code to kernel: {}", error)
        }
        if let Err(error) = stdin.flush().await {
            self.status = KernelStatus::Failed;
            bail!("When flushing code to kernel: {}", error)
        }

        // Capture outputs separating them as we go
        let mut output = String::new();
        let mut outputs = Vec::new();
        loop {
            let line = match stdout.lines().next_line().await {
                Ok(Some(line)) => line,
                Ok(None) => break,
                Err(error) => {
                    self.status = KernelStatus::Failed;
                    bail!("When receiving outputs from kernel: {}", error)
                }
            };

            tracing::debug!("Received on stdout: {}", line);
            if let Some(line) = line.strip_suffix(RES_SEP) {
                output.push_str(line);
                if !output.is_empty() {
                    outputs.push(output.clone());
                    output.clear();
                }
            } else if let Some(line) = line.strip_suffix(TRANS_SEP) {
                output.push_str(line);
                if !output.is_empty() {
                    outputs.push(output.clone());
                }
                break;
            } else {
                output.push_str(&line);
                output.push('\n');
            }
        }

        // Attempt to parse each output as JSON into a `Node`, falling back to a string.
        let outputs: Vec<Node> = outputs
            .iter()
            .map(|output| -> Node {
                serde_json::from_str(output).unwrap_or_else(|_| Node::String(output.clone()))
            })
            .collect();

        // Capture messages separating them as we go
        let mut message = String::new();
        let mut messages = Vec::new();
        loop {
            let line = match stderr.lines().next_line().await {
                Ok(Some(line)) => line,
                Ok(None) => break,
                Err(error) => {
                    self.status = KernelStatus::Failed;
                    bail!("When receiving messages from kernel: {}", error)
                }
            };

            tracing::debug!("Received on stderr: {}", line);
            if let Some(line) = line.strip_suffix(RES_SEP) {
                message.push_str(line);
                if !message.is_empty() {
                    messages.push(message.clone());
                    message.clear();
                }
            } else if let Some(line) = line.strip_suffix(TRANS_SEP) {
                message.push_str(line);
                if !message.is_empty() {
                    messages.push(message.clone());
                }
                break;
            } else {
                message.push_str(&line);
                message.push('\n');
            }
        }

        // Attempt to parse each message as JSON into a `CodeMessage`.
        let messages: Vec<CodeError> = messages
            .iter()
            .map(|message| -> CodeError {
                serde_json::from_str(message).unwrap_or_else(|_| CodeError {
                    error_message: message.into(),
                    ..Default::default()
                })
            })
            .collect();

        Ok((outputs, messages))
    }
}
<|endoftext|>


<commit_before>// Re-exports
pub use kernels::{KernelSelector, KernelSpace, TaskResult};

mod executable;
pub use executable::*;

mod compile;
pub use compile::*;

mod execute;
pub use execute::*;

#[cfg(test)]
mod tests {
    use super::*;
    use codec::CodecTrait;
    use codec_md::MdCodec;
    use eyre::Result;
    use graph::{PlanOptions, PlanOrdering};
    use graph_triples::ResourceInfo;
    use kernels::{Kernel, KernelType};
    use node_address::Slot;
    use node_patch::{Operation, Patch};
    use test_snaps::{
        fixtures,
        insta::{self, assert_json_snapshot},
        snapshot_set_suffix,
    };
    use tokio::sync::mpsc;

    /// Higher level tests of the top level functions in this crate
    #[tokio::test]
    async fn md_articles() -> Result<()> {
        let fixtures = fixtures();

        // So that test results are not dependant upon the the machine the test is run on or how
        // the test is compiled only use built-in kernels
        let kernels: Vec<Kernel> = kernels::available()
            .await?
            .into_iter()
            .filter(|kernel| matches!(kernel.r#type, KernelType::Builtin))
            .collect();

        let mut settings = insta::Settings::clone_current();
        settings.set_prepend_module_to_snapshot(false);
        settings.bind_to_thread();

        for name in ["code.md", "code-relations.md"] {
            let path = fixtures.join("articles").join(name);

            // Load the article
            let mut root = MdCodec::from_path(&path, None).await?;

            // Strip the fixtures path so it does not differ between machines
            let path = path.strip_prefix(&fixtures)?;
            let project = path.parent().unwrap();

            // Compile the article and snapshot the result
            let (patch_sender, mut patch_receiver) = mpsc::unbounded_channel();
            tokio::spawn(async move {
                while let Some(_patch) = patch_receiver.recv().await {
                    // Ignore for this test
                }
            });
            let (addresses, graph) = compile(path, project, &mut root, &patch_sender)?;
            snapshot_set_suffix(&[name, "-compile"].concat(), || {
                assert_json_snapshot!((&addresses, &graph))
            });

            // Generate various execution plans for the article using alternative options
            // and snapshot them all. Always specify `max_concurrency` to avoid differences
            // due to machine (number of CPUs)
            for (suffix, options) in [
                (
                    "appearance",
                    PlanOptions {
                        ordering: PlanOrdering::Appearance,
                        max_concurrency: 10,
                    },
                ),
                (
                    "appearance-concurrency-0",
                    PlanOptions {
                        ordering: PlanOrdering::Appearance,
                        max_concurrency: 0,
                    },
                ),
                (
                    "topological",
                    PlanOptions {
                        ordering: PlanOrdering::Topological,
                        max_concurrency: 10,
                    },
                ),
                (
                    "topological-concurrency-0",
                    PlanOptions {
                        ordering: PlanOrdering::Topological,
                        max_concurrency: 0,
                    },
                ),
            ] {
                let plan = graph
                    .plan(None, Some(kernels.clone()), Some(options))
                    .await?;
                snapshot_set_suffix(&[name, "-", suffix].concat(), || {
                    assert_json_snapshot!(&plan)
                });
            }

            // Execute the article (with topological execution plan) and snapshot the resultant patches
            let plan = graph
                .plan(
                    None,
                    Some(kernels.clone()),
                    Some(PlanOptions {
                        ordering: PlanOrdering::Topological,
                        max_concurrency: 10,
                    }),
                )
                .await?;

            let (resource_info_sender, mut resource_info_receiver) =
                mpsc::channel::<ResourceInfo>(1);
            tokio::spawn(async move {
                while let Some(_resource_info) = resource_info_receiver.recv().await {
                    // Ignore for this test
                }
            });

            let (patch_sender, mut patch_receiver) = mpsc::unbounded_channel::<Patch>();
            let patches = tokio::spawn(async move {
                let mut patches = Vec::new();
                while let Some(mut patch) = patch_receiver.recv().await {
                    // Redact execute time and duration from patch because they will
                    // change across test runs
                    for op in patch.ops.iter_mut() {
                        if let Operation::Add { address, value, .. } = op {
                            if let Some(Slot::Name(name)) = address.back() {
                                if name == "execute_ended" || name == "execute_duration" {
                                    *value = Box::new("<redacted>".to_string());
                                }
                            }
                        }
                    }
                    patches.push(patch);
                }
                patches
            });

            execute(
                &plan,
                &mut root,
                &addresses,
                resource_info_sender,
                patch_sender,
                None,
            )
            .await?;

            let patches = patches.await?;
            snapshot_set_suffix(&[name, "-patches"].concat(), || {
                assert_json_snapshot!(&patches);
            });
        }

        Ok(())
    }
}
<commit_msg>test(Node execution): Use references<commit_after>// Re-exports
pub use kernels::{KernelSelector, KernelSpace, TaskResult};

mod executable;
pub use executable::*;

mod compile;
pub use compile::*;

mod execute;
pub use execute::*;

#[cfg(test)]
mod tests {
    use super::*;
    use codec::CodecTrait;
    use codec_md::MdCodec;
    use eyre::Result;
    use graph::{PlanOptions, PlanOrdering};
    use graph_triples::ResourceInfo;
    use kernels::{Kernel, KernelType};
    use node_address::Slot;
    use node_patch::{Operation, Patch};
    use test_snaps::{
        fixtures,
        insta::{self, assert_json_snapshot},
        snapshot_set_suffix,
    };
    use tokio::sync::mpsc;

    /// Higher level tests of the top level functions in this crate
    #[tokio::test]
    async fn md_articles() -> Result<()> {
        let fixtures = fixtures();

        // So that test results are not dependant upon the the machine the test is run on or how
        // the test is compiled only use built-in kernels
        let kernels: Vec<Kernel> = kernels::available()
            .await?
            .into_iter()
            .filter(|kernel| matches!(kernel.r#type, KernelType::Builtin))
            .collect();

        let mut settings = insta::Settings::clone_current();
        settings.set_prepend_module_to_snapshot(false);
        settings.bind_to_thread();

        for name in ["code.md", "code-relations.md"] {
            let path = fixtures.join("articles").join(name);

            // Load the article
            let mut root = MdCodec::from_path(&path, None).await?;

            // Strip the fixtures path so it does not differ between machines
            let path = path.strip_prefix(&fixtures)?;
            let project = path.parent().unwrap();

            // Compile the article and snapshot the result
            let (patch_sender, mut patch_receiver) = mpsc::unbounded_channel();
            tokio::spawn(async move {
                while let Some(_patch) = patch_receiver.recv().await {
                    // Ignore for this test
                }
            });
            let (addresses, graph) = compile(path, project, &mut root, &patch_sender)?;
            snapshot_set_suffix(&[name, "-compile"].concat(), || {
                assert_json_snapshot!((&addresses, &graph))
            });

            // Generate various execution plans for the article using alternative options
            // and snapshot them all. Always specify `max_concurrency` to avoid differences
            // due to machine (number of CPUs)
            for (suffix, options) in [
                (
                    "appearance",
                    PlanOptions {
                        ordering: PlanOrdering::Appearance,
                        max_concurrency: 10,
                    },
                ),
                (
                    "appearance-concurrency-0",
                    PlanOptions {
                        ordering: PlanOrdering::Appearance,
                        max_concurrency: 0,
                    },
                ),
                (
                    "topological",
                    PlanOptions {
                        ordering: PlanOrdering::Topological,
                        max_concurrency: 10,
                    },
                ),
                (
                    "topological-concurrency-0",
                    PlanOptions {
                        ordering: PlanOrdering::Topological,
                        max_concurrency: 0,
                    },
                ),
            ] {
                let plan = graph
                    .plan(None, Some(kernels.clone()), Some(options))
                    .await?;
                snapshot_set_suffix(&[name, "-", suffix].concat(), || {
                    assert_json_snapshot!(&plan)
                });
            }

            // Execute the article (with topological execution plan) and snapshot the resultant patches
            let plan = graph
                .plan(
                    None,
                    Some(kernels.clone()),
                    Some(PlanOptions {
                        ordering: PlanOrdering::Topological,
                        max_concurrency: 10,
                    }),
                )
                .await?;

            let (resource_info_sender, mut resource_info_receiver) =
                mpsc::channel::<ResourceInfo>(1);
            tokio::spawn(async move {
                while let Some(_resource_info) = resource_info_receiver.recv().await {
                    // Ignore for this test
                }
            });

            let (patch_sender, mut patch_receiver) = mpsc::unbounded_channel::<Patch>();
            let patches = tokio::spawn(async move {
                let mut patches = Vec::new();
                while let Some(mut patch) = patch_receiver.recv().await {
                    // Redact execute time and duration from patch because they will
                    // change across test runs
                    for op in patch.ops.iter_mut() {
                        if let Operation::Add { address, value, .. } = op {
                            if let Some(Slot::Name(name)) = address.back() {
                                if name == "execute_ended" || name == "execute_duration" {
                                    *value = Box::new("<redacted>".to_string());
                                }
                            }
                        }
                    }
                    patches.push(patch);
                }
                patches
            });

            execute(
                &plan,
                &mut root,
                &addresses,
                &resource_info_sender,
                &patch_sender,
                None,
            )
            .await?;

            let patches = patches.await?;
            snapshot_set_suffix(&[name, "-patches"].concat(), || {
                assert_json_snapshot!(&patches);
            });
        }

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