<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/.

#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]
#![cfg_attr(not(feature = "clippy"), allow(unknown_lints))]

#![allow(doc_markdown)]

extern crate devicemapper;
extern crate libstratis;
#[macro_use]
extern crate log;
extern crate env_logger;
extern crate clap;
#[cfg(feature="dbus_enabled")]
extern crate dbus;
extern crate libc;
extern crate libudev;
extern crate nix;

#[cfg(test)]
extern crate quickcheck;

use std::cell::RefCell;
use std::env;
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
use std::os::unix::io::AsRawFd;
use std::path::PathBuf;
use std::process::exit;
use std::rc::Rc;

use clap::{App, Arg};
use env_logger::LogBuilder;
use libc::pid_t;
use log::{LogLevelFilter, SetLoggerError};
use nix::fcntl::{flock, FlockArg};
use nix::unistd::getpid;

#[cfg(feature="dbus_enabled")]
use dbus::WatchEvent;

use devicemapper::Device;

use libstratis::engine::{Engine, SimEngine, StratEngine};
use libstratis::stratis::{StratisError, StratisResult, VERSION};

const STRATISD_PID_PATH: &str = "/var/run/stratisd.pid";

/// If writing a program error to stderr fails, panic.
fn print_err(err: StratisError) -> () {
    eprintln!("{}", err);
}

/// Configure and initialize the logger.
/// If debug is true, log at debug level. Otherwise read log configuration
/// parameters from the environment if RUST_LOG is set. Otherwise, just
/// accept the default configuration.
fn initialize_log(debug: bool) -> Result<(), SetLoggerError> {
    let mut builder = LogBuilder::new();
    if debug {
        builder.filter(Some("stratisd"), LogLevelFilter::Debug);
        builder.filter(Some("libstratis"), LogLevelFilter::Debug);
    } else if let Ok(s) = env::var("RUST_LOG") {
        builder.parse(&s);
    };

    builder.init()
}

/// Given a udev event check to see if it's an add and if it is return the device node and
/// devicemapper::Device.
fn handle_udev_add(event: &libudev::Event) -> Option<(Device, PathBuf)> {
    if event.event_type() == libudev::EventType::Add {
        let device = event.device();
        return device
                   .devnode()
                   .and_then(|devnode| {
                                 device
                                     .devnum()
                                     .and_then(|devnum| {
                                                   Some((Device::from(devnum),
                                                         PathBuf::from(devnode)))
                                               })
                             });
    }
    None
}

/// To ensure only one instance of stratisd runs at a time, acquire an
/// exclusive lock. Return an error if lock attempt fails.
fn create_pid_file() -> StratisResult<File> {
    let mut f = OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .open(STRATISD_PID_PATH)?;
    match flock(f.as_raw_fd(), FlockArg::LockExclusiveNonblock) {
        Ok(_) => {
            f.write(format!("{}\n", getpid()).as_bytes())?;
            Ok(f)
        }
        Err(_) => {
            let mut buf = String::new();
            f.read_to_string(&mut buf)?;
            // pidfile is supposed to contain pid of holder. But you never
            // know so be paranoid.
            let pid_str = buf.split_whitespace()
                .next()
                .and_then(|s| s.parse::<pid_t>().ok())
                .map(|pid| format!("{}", pid))
                .unwrap_or_else(|| "<unknown>".into());
            Err(StratisError::Error(format!("Daemon already running with pid: {}", pid_str)))
        }
    }
}

fn run() -> StratisResult<()> {

    // Exit immediately if stratisd is already running
    let _ = create_pid_file()?;

    let matches = App::new("stratis")
        .version(VERSION)
        .about("Stratis storage management")
        .arg(Arg::with_name("debug")
                 .long("debug")
                 .help("Print additional output for debugging"))
        .arg(Arg::with_name("sim")
                 .long("sim")
                 .help("Use simulator engine"))
        .get_matches();

    initialize_log(matches.is_present("debug"))
        .expect("This is the first and only invocation of this method; it must succeed.");

    // We must setup a udev listener before we initialize the
    // engine. It is possible that a device may appear after the engine has read the
    // /dev directory but before it has completed initialization. Unless the udev
    // event has been recorded, the engine will be unaware of the existence of the
    // device.
    // This is especially important since stratisd is required to be able to run
    // during early boot.
    let context = libudev::Context::new()?;
    let mut monitor = libudev::Monitor::new(&context)?;
    monitor.match_subsystem_devtype("block", "disk")?;
    let mut udev = monitor.listen()?;

    let engine: Rc<RefCell<Engine>> = {
        if matches.is_present("sim") {
            info!("Using SimEngine");
            Rc::new(RefCell::new(SimEngine::default()))
        } else {
            info!("Using StratEngine");
            Rc::new(RefCell::new(StratEngine::initialize()?))
        }
    };

    /*
    The file descriptor array indexes are laid out in the following:

    0   == Always udev fd index
    1   == engine index if eventable
    1/2 == Start of dbus client file descriptor(s), 1 if engine is not eventable, else 2
    */
    const FD_INDEX_UDEV: usize = 0;
    const FD_INDEX_ENGINE: usize = 1;

    let mut fds = Vec::new();

    fds.push(libc::pollfd {
                 fd: udev.as_raw_fd(),
                 revents: 0,
                 events: libc::POLLIN,
             });

    let eventable = engine.borrow().get_eventable();

    // The variable _dbus_client_index_start is only used when dbus support is compiled in, thus
    // we denote the value as not needed to compile when dbus support is not included.
    let (engine_eventable, poll_timeout, _dbus_client_index_start) = match eventable {
        Some(ref evt) => {
            fds.push(libc::pollfd {
                         fd: evt.get_pollable_fd(),
                         revents: 0,
                         events: libc::POLLIN,
                     });

            // Don't timeout if eventable, we are event driven
            (true, -1, FD_INDEX_ENGINE + 1)
        }

        // We periodically need to timeout as we are not event driven
        None => (false, 10000, FD_INDEX_ENGINE),
    };

    #[cfg(feature="dbus_enabled")]
    let (mut dbus_conn, mut tree, base_object_path, mut dbus_context) =
        libstratis::dbus_api::connect(Rc::clone(&engine))?;

    loop {
        // Process any udev block events
        if fds[FD_INDEX_UDEV].revents != 0 {
            loop {
                match udev.receive_event() {
                    Some(event) => {
                        if let Some((device, devnode)) = handle_udev_add(&event) {

                            // If block evaluate returns an error we are going to ignore it as
                            // there is nothing we can do for a device we are getting errors with.
                            let pool_uuid = engine
                                .borrow_mut()
                                .block_evaluate(device, devnode)
                                .unwrap_or(None);

                            // We need to pretend that we aren't using the variable _pool_uuid so
                            // that we can conditionally compile out the register_pool when dbus
                            // is not enabled.
                            if let Some(_pool_uuid) = pool_uuid {
                                #[cfg(feature="dbus_enabled")]
                                libstratis::dbus_api::register_pool(&mut dbus_conn,
                                                                    &Rc::clone(&engine),
                                                                    &mut dbus_context,
                                                                    &mut tree,
                                                                    _pool_uuid,
                                                                    &base_object_path)?;
                            }
                        }
                    }
                    None => {
                        break;
                    }
                };
            }
        }

        // Handle engine events, if the engine is eventable
        if engine_eventable {
            if fds[FD_INDEX_ENGINE].revents != 0 {
                fds[FD_INDEX_ENGINE].revents = 0;

                eventable
                    .as_ref()
                    .expect("eventable.is_some()")
                    .clear_event()?;

                engine.borrow_mut().evented()?;
            }
        } else {
            // Unconditionally call evented() if engine has no eventable.
            // This looks like a bad idea, but the only engine that has
            // no eventable is the sim engine, and for that engine, evented()
            // is essentially a no-op.
            engine.borrow_mut().evented()?;
        }

        // Iterate through D-Bus file descriptors (if enabled)
        #[cfg(feature="dbus_enabled")]
        {
            for pfd in fds[_dbus_client_index_start..]
                    .iter()
                    .filter(|pfd| pfd.revents != 0) {
                for item in dbus_conn.watch_handle(pfd.fd, WatchEvent::from_revents(pfd.revents)) {
                    if let Err(r) = libstratis::dbus_api::handle(&dbus_conn,
                                                                 &item,
                                                                 &mut tree,
                                                                 &dbus_context) {
                        print_err(From::from(r));
                    }
                }
            }

            // Refresh list of dbus fds to poll for every time. This can change as
            // D-Bus clients come and go.
            fds.truncate(_dbus_client_index_start);

            fds.extend(dbus_conn.watch_fds().iter().map(|w| w.to_pollfd()));
        }

        let r = unsafe { libc::poll(fds.as_mut_ptr(), fds.len() as libc::c_ulong, poll_timeout) };
        assert!(r >= 0);
    }
}

fn main() {
    let error_code = match run() {
        Ok(_) => 0,
        Err(err) => {
            print_err(err);
            1
        }
    };
    exit(error_code);
}
<commit_msg>Turn loop expression into a while-let expression<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/.

#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]
#![cfg_attr(not(feature = "clippy"), allow(unknown_lints))]

#![allow(doc_markdown)]

extern crate devicemapper;
extern crate libstratis;
#[macro_use]
extern crate log;
extern crate env_logger;
extern crate clap;
#[cfg(feature="dbus_enabled")]
extern crate dbus;
extern crate libc;
extern crate libudev;
extern crate nix;

#[cfg(test)]
extern crate quickcheck;

use std::cell::RefCell;
use std::env;
use std::fs::{File, OpenOptions};
use std::io::{Read, Write};
use std::os::unix::io::AsRawFd;
use std::path::PathBuf;
use std::process::exit;
use std::rc::Rc;

use clap::{App, Arg};
use env_logger::LogBuilder;
use libc::pid_t;
use log::{LogLevelFilter, SetLoggerError};
use nix::fcntl::{flock, FlockArg};
use nix::unistd::getpid;

#[cfg(feature="dbus_enabled")]
use dbus::WatchEvent;

use devicemapper::Device;

use libstratis::engine::{Engine, SimEngine, StratEngine};
use libstratis::stratis::{StratisError, StratisResult, VERSION};

const STRATISD_PID_PATH: &str = "/var/run/stratisd.pid";

/// If writing a program error to stderr fails, panic.
fn print_err(err: StratisError) -> () {
    eprintln!("{}", err);
}

/// Configure and initialize the logger.
/// If debug is true, log at debug level. Otherwise read log configuration
/// parameters from the environment if RUST_LOG is set. Otherwise, just
/// accept the default configuration.
fn initialize_log(debug: bool) -> Result<(), SetLoggerError> {
    let mut builder = LogBuilder::new();
    if debug {
        builder.filter(Some("stratisd"), LogLevelFilter::Debug);
        builder.filter(Some("libstratis"), LogLevelFilter::Debug);
    } else if let Ok(s) = env::var("RUST_LOG") {
        builder.parse(&s);
    };

    builder.init()
}

/// Given a udev event check to see if it's an add and if it is return the device node and
/// devicemapper::Device.
fn handle_udev_add(event: &libudev::Event) -> Option<(Device, PathBuf)> {
    if event.event_type() == libudev::EventType::Add {
        let device = event.device();
        return device
                   .devnode()
                   .and_then(|devnode| {
                                 device
                                     .devnum()
                                     .and_then(|devnum| {
                                                   Some((Device::from(devnum),
                                                         PathBuf::from(devnode)))
                                               })
                             });
    }
    None
}

/// To ensure only one instance of stratisd runs at a time, acquire an
/// exclusive lock. Return an error if lock attempt fails.
fn create_pid_file() -> StratisResult<File> {
    let mut f = OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .open(STRATISD_PID_PATH)?;
    match flock(f.as_raw_fd(), FlockArg::LockExclusiveNonblock) {
        Ok(_) => {
            f.write(format!("{}\n", getpid()).as_bytes())?;
            Ok(f)
        }
        Err(_) => {
            let mut buf = String::new();
            f.read_to_string(&mut buf)?;
            // pidfile is supposed to contain pid of holder. But you never
            // know so be paranoid.
            let pid_str = buf.split_whitespace()
                .next()
                .and_then(|s| s.parse::<pid_t>().ok())
                .map(|pid| format!("{}", pid))
                .unwrap_or_else(|| "<unknown>".into());
            Err(StratisError::Error(format!("Daemon already running with pid: {}", pid_str)))
        }
    }
}

fn run() -> StratisResult<()> {

    // Exit immediately if stratisd is already running
    let _ = create_pid_file()?;

    let matches = App::new("stratis")
        .version(VERSION)
        .about("Stratis storage management")
        .arg(Arg::with_name("debug")
                 .long("debug")
                 .help("Print additional output for debugging"))
        .arg(Arg::with_name("sim")
                 .long("sim")
                 .help("Use simulator engine"))
        .get_matches();

    initialize_log(matches.is_present("debug"))
        .expect("This is the first and only invocation of this method; it must succeed.");

    // We must setup a udev listener before we initialize the
    // engine. It is possible that a device may appear after the engine has read the
    // /dev directory but before it has completed initialization. Unless the udev
    // event has been recorded, the engine will be unaware of the existence of the
    // device.
    // This is especially important since stratisd is required to be able to run
    // during early boot.
    let context = libudev::Context::new()?;
    let mut monitor = libudev::Monitor::new(&context)?;
    monitor.match_subsystem_devtype("block", "disk")?;
    let mut udev = monitor.listen()?;

    let engine: Rc<RefCell<Engine>> = {
        if matches.is_present("sim") {
            info!("Using SimEngine");
            Rc::new(RefCell::new(SimEngine::default()))
        } else {
            info!("Using StratEngine");
            Rc::new(RefCell::new(StratEngine::initialize()?))
        }
    };

    /*
    The file descriptor array indexes are laid out in the following:

    0   == Always udev fd index
    1   == engine index if eventable
    1/2 == Start of dbus client file descriptor(s), 1 if engine is not eventable, else 2
    */
    const FD_INDEX_UDEV: usize = 0;
    const FD_INDEX_ENGINE: usize = 1;

    let mut fds = Vec::new();

    fds.push(libc::pollfd {
                 fd: udev.as_raw_fd(),
                 revents: 0,
                 events: libc::POLLIN,
             });

    let eventable = engine.borrow().get_eventable();

    // The variable _dbus_client_index_start is only used when dbus support is compiled in, thus
    // we denote the value as not needed to compile when dbus support is not included.
    let (engine_eventable, poll_timeout, _dbus_client_index_start) = match eventable {
        Some(ref evt) => {
            fds.push(libc::pollfd {
                         fd: evt.get_pollable_fd(),
                         revents: 0,
                         events: libc::POLLIN,
                     });

            // Don't timeout if eventable, we are event driven
            (true, -1, FD_INDEX_ENGINE + 1)
        }

        // We periodically need to timeout as we are not event driven
        None => (false, 10000, FD_INDEX_ENGINE),
    };

    #[cfg(feature="dbus_enabled")]
    let (mut dbus_conn, mut tree, base_object_path, mut dbus_context) =
        libstratis::dbus_api::connect(Rc::clone(&engine))?;

    loop {
        // Process any udev block events
        if fds[FD_INDEX_UDEV].revents != 0 {
            while let Some(event) = udev.receive_event() {
                if let Some((device, devnode)) = handle_udev_add(&event) {

                    // If block evaluate returns an error we are going to ignore it as
                    // there is nothing we can do for a device we are getting errors with.
                    let pool_uuid = engine
                        .borrow_mut()
                        .block_evaluate(device, devnode)
                        .unwrap_or(None);

                    // We need to pretend that we aren't using the variable _pool_uuid so
                    // that we can conditionally compile out the register_pool when dbus
                    // is not enabled.
                    if let Some(_pool_uuid) = pool_uuid {
                                #[cfg(feature="dbus_enabled")]
                        libstratis::dbus_api::register_pool(&mut dbus_conn,
                                                            &Rc::clone(&engine),
                                                            &mut dbus_context,
                                                            &mut tree,
                                                            _pool_uuid,
                                                            &base_object_path)?;
                    }
                }
            }
        }

        // Handle engine events, if the engine is eventable
        if engine_eventable {
            if fds[FD_INDEX_ENGINE].revents != 0 {
                fds[FD_INDEX_ENGINE].revents = 0;

                eventable
                    .as_ref()
                    .expect("eventable.is_some()")
                    .clear_event()?;

                engine.borrow_mut().evented()?;
            }
        } else {
            // Unconditionally call evented() if engine has no eventable.
            // This looks like a bad idea, but the only engine that has
            // no eventable is the sim engine, and for that engine, evented()
            // is essentially a no-op.
            engine.borrow_mut().evented()?;
        }

        // Iterate through D-Bus file descriptors (if enabled)
        #[cfg(feature="dbus_enabled")]
        {
            for pfd in fds[_dbus_client_index_start..]
                    .iter()
                    .filter(|pfd| pfd.revents != 0) {
                for item in dbus_conn.watch_handle(pfd.fd, WatchEvent::from_revents(pfd.revents)) {
                    if let Err(r) = libstratis::dbus_api::handle(&dbus_conn,
                                                                 &item,
                                                                 &mut tree,
                                                                 &dbus_context) {
                        print_err(From::from(r));
                    }
                }
            }

            // Refresh list of dbus fds to poll for every time. This can change as
            // D-Bus clients come and go.
            fds.truncate(_dbus_client_index_start);

            fds.extend(dbus_conn.watch_fds().iter().map(|w| w.to_pollfd()));
        }

        let r = unsafe { libc::poll(fds.as_mut_ptr(), fds.len() as libc::c_ulong, poll_timeout) };
        assert!(r >= 0);
    }
}

fn main() {
    let error_code = match run() {
        Ok(_) => 0,
        Err(err) => {
            print_err(err);
            1
        }
    };
    exit(error_code);
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;

extern crate arguments;
extern crate random;
extern crate sql;
extern crate sqlite;
extern crate streamer;
extern crate term;
extern crate time;

use log::LogLevel;
use std::error::Error;
use streamer::{Config, Result, System};

const USAGE: &'static str = "
Usage: streamer [options]

Options:
    --config <path>          Configuration file (required).
    --length <time>          Time span to simulate in seconds [default: 10].
    --output <path>          Output file for power and temperature profiles.

    --verbose                Display progress information.
    --help                   Display this message.
";

macro_rules! raise(
    ($message:expr) => (return Err(Box::new(::streamer::ErrorString($message.to_string()))));
);

macro_rules! ok(
    ($result:expr) => (match $result {
        Ok(result) => result,
        Err(error) => return Err(Box::new(error)),
    });
);

macro_rules! some(
    ($option:expr, $($arg:tt)*) => (match $option {
        Some(value) => value,
        _ => raise!($($arg)*),
    });
);

mod logger;
mod output;

use output::Output;

fn main() {
    start().unwrap_or_else(|error| fail(&*error));
}

fn start() -> Result<()> {
    let arguments = ok!(arguments::parse(std::env::args()));

    if arguments.get::<bool>("help").unwrap_or(false) {
        help();
    }

    if arguments.get::<bool>("verbose").unwrap_or(false) {
        logger::setup(LogLevel::Info);
    } else {
        logger::setup(LogLevel::Warn);
    }

    let mut system = {
        let config = some!(arguments.get::<String>("config"), "a configuration file is required");
        let config = try!(Config::new(config));
        let seed = config.get::<i64>("seed").map(|&seed| seed as u64).unwrap_or(0);
        let seed = if seed > 0 { seed } else { time::now().to_timespec().sec as u64 };
        let seed = [0x12345678 & seed, 0x87654321 & seed];
        try!(System::new(&config, &random::default().seed(seed)))
    };

    let length = arguments.get::<f64>("length").unwrap_or(10.0);
    if length <= 0.0 {
        raise!("the time span should be positive");
    }

    let mut output = try!(output::new(&system, arguments.get::<String>("output")));

    info!(target: "Streamer", "Simulating {} seconds with a time step of {} seconds...", length,
          system.time_step());
    let start = time::now();

    while let Some((event, power, temperature)) = system.next() {
        if event.time > length {
            break;
        }
        info!(target: "Streamer", "{} | {:2} queued", event,
              system.stats().arrived - system.stats().started);
        try!(output.next((event, power, temperature)));
    }

    info!(target: "Streamer", "Well done in {:.2} seconds.",
          (time::now() - start).num_milliseconds() as f64 / 1000.0);

    Ok(())
}

fn help() -> ! {
    println!("{}", USAGE.trim());
    std::process::exit(0);
}

fn fail(error: &Error) -> ! {
    use std::io::{stderr, Write};
    stderr().write_all(format!("Error: {}.\n", error).as_bytes()).unwrap();
    std::process::exit(1);
}
<commit_msg>Show the result of the last iteration<commit_after>#[macro_use]
extern crate log;

extern crate arguments;
extern crate random;
extern crate sql;
extern crate sqlite;
extern crate streamer;
extern crate term;
extern crate time;

use log::LogLevel;
use std::error::Error;
use streamer::{Config, Result, System};

const USAGE: &'static str = "
Usage: streamer [options]

Options:
    --config <path>          Configuration file (required).
    --length <time>          Time span to simulate in seconds [default: 10].
    --output <path>          Output file for power and temperature profiles.

    --verbose                Display progress information.
    --help                   Display this message.
";

macro_rules! raise(
    ($message:expr) => (return Err(Box::new(::streamer::ErrorString($message.to_string()))));
);

macro_rules! ok(
    ($result:expr) => (match $result {
        Ok(result) => result,
        Err(error) => return Err(Box::new(error)),
    });
);

macro_rules! some(
    ($option:expr, $($arg:tt)*) => (match $option {
        Some(value) => value,
        _ => raise!($($arg)*),
    });
);

mod logger;
mod output;

use output::Output;

fn main() {
    start().unwrap_or_else(|error| fail(&*error));
}

fn start() -> Result<()> {
    let arguments = ok!(arguments::parse(std::env::args()));

    if arguments.get::<bool>("help").unwrap_or(false) {
        help();
    }

    if arguments.get::<bool>("verbose").unwrap_or(false) {
        logger::setup(LogLevel::Info);
    } else {
        logger::setup(LogLevel::Warn);
    }

    let mut system = {
        let config = some!(arguments.get::<String>("config"), "a configuration file is required");
        let config = try!(Config::new(config));
        let seed = config.get::<i64>("seed").map(|&seed| seed as u64).unwrap_or(0);
        let seed = if seed > 0 { seed } else { time::now().to_timespec().sec as u64 };
        let seed = [0x12345678 & seed, 0x87654321 & seed];
        try!(System::new(&config, &random::default().seed(seed)))
    };

    let length = arguments.get::<f64>("length").unwrap_or(10.0);
    if length <= 0.0 {
        raise!("the time span should be positive");
    }

    let mut output = try!(output::new(&system, arguments.get::<String>("output")));

    info!(target: "Streamer", "Simulating {} seconds with a time step of {} seconds...", length,
          system.time_step());
    let start = time::now();

    while let Some((event, power, temperature)) = system.next() {
        let last = event.time > length;
        info!(target: "Streamer", "{} | {:2} queued", event,
              system.stats().arrived - system.stats().started);
        try!(output.next((event, power, temperature)));
        if last {
            break;
        }
    }

    info!(target: "Streamer", "Well done in {:.2} seconds.",
          (time::now() - start).num_milliseconds() as f64 / 1000.0);

    Ok(())
}

fn help() -> ! {
    println!("{}", USAGE.trim());
    std::process::exit(0);
}

fn fail(error: &Error) -> ! {
    use std::io::{stderr, Write};
    stderr().write_all(format!("Error: {}.\n", error).as_bytes()).unwrap();
    std::process::exit(1);
}
<|endoftext|>


<commit_before>//! Command buffers.
//!
//! A command buffer collects a list of commands to be submitted to the device.
//! Each command buffer has specific capabilities for graphics, compute or transfer operations,
//! and can be either a "primary" command buffer or a "secondary" command buffer.  Operations
//! always start from a primary command buffer, but a primary command buffer can contain calls
//! to secondary command buffers that contain snippets of commands that do specific things, similar
//! to function calls.
//!
//! All the possible commands are implemented in the `RawCommandBuffer` trait, and then the `CommandBuffer`
//! and related types make a generic, strongly-typed wrapper around it that only expose the methods that
//! are valid for the capabilities it provides.

// TODO: Document pipelines and subpasses better.

use Backend;
use queue::capability::Supports;
use std::marker::PhantomData;

mod compute;
mod graphics;
mod raw;
mod render_pass;
mod transfer;

pub use self::graphics::*;
pub use self::raw::{
    ClearValueRaw, ClearColorRaw, ClearDepthStencilRaw, DescriptorSetOffset,
    RawCommandBuffer, CommandBufferFlags, Level as RawLevel, CommandBufferInheritanceInfo,
};
pub use self::render_pass::*;
pub use self::transfer::*;

use std::borrow::{Cow};

/// Trait indicating how many times a Submit object can be submitted to a command buffer.
pub trait Shot {
    ///
    const FLAGS: CommandBufferFlags;
}
/// Indicates a Submit that can only be submitted once.
pub enum OneShot { }
impl Shot for OneShot { const FLAGS: CommandBufferFlags = CommandBufferFlags::ONE_TIME_SUBMIT; }

/// Indicates a Submit that can be submitted multiple times.
pub enum MultiShot { }
impl Shot for MultiShot { const FLAGS: CommandBufferFlags = CommandBufferFlags::EMPTY; }

/// A trait indicating the level of a command buffer.
pub trait Level { }

/// Indicates a primary command buffer.

/// Vulkan describes a primary command buffer as one which can be directly submitted
/// to a queue, and can execute `Secondary` command buffers.
pub enum Primary { }
impl Level for Primary { }

/// Indicates a secondary command buffer.
///
/// Vulkan describes a secondary command buffer as one which cannot be directly submitted
/// to a queue, but can be executed by a primary command buffer. This allows
/// multiple secondary command buffers to be constructed which do specific
/// things and can then be composed together into primary command buffers.
pub enum Secondary { }
impl Level for Secondary { }

/// Thread-safe finished command buffer for submission.
pub struct Submit<B: Backend, C, S, L>(pub(crate) B::CommandBuffer, pub(crate) PhantomData<(C, S, L)>);
impl<B: Backend, C, S, L> Submit<B, C, S, L> {
    fn new(buffer: B::CommandBuffer) -> Self {
        Submit(buffer, PhantomData)
    }
}
unsafe impl<B: Backend, C, S, L> Send for Submit<B, C, S, L> {}

/// A trait representing a command buffer that can be added to a `Submission`.
pub unsafe trait Submittable<'a, B: Backend, C, L: Level> {
    /// Unwraps the object into its underlying command buffer.
    unsafe fn into_buffer(self) -> Cow<'a, B::CommandBuffer>;
}

unsafe impl<'a, B: Backend, C, L: Level> Submittable<'a, B, C, L> for Submit<B, C, OneShot, L> {
    unsafe fn into_buffer(self) -> Cow<'a, B::CommandBuffer> { Cow::Owned(self.0) }
}

unsafe impl<'a, B: Backend, C, L: Level> Submittable<'a, B, C, L> for &'a Submit<B, C, MultiShot, L> {
    unsafe fn into_buffer(self) -> Cow<'a, B::CommandBuffer> { Cow::Borrowed(&self.0) }
}

/// A convenience alias for not typing out the full signature of a secondary command buffer.
pub type SecondaryCommandBuffer<'a, B, C, S = OneShot> = CommandBuffer<'a, B, C, S, Secondary>;

/// A strongly-typed command buffer that will only implement methods that are valid for the operations
/// it supports.
pub struct CommandBuffer<'a, B: Backend, C, S: Shot = OneShot, L: Level = Primary> {
    pub(crate) raw: &'a mut B::CommandBuffer,
    pub(crate) _marker: PhantomData<(C, S, L)>
}

impl<'a, B: Backend, C, S: Shot, L: Level> CommandBuffer<'a, B, C, S, L> {
    /// Create a new typed command buffer from a raw command pool.
    pub unsafe fn new(raw: &'a mut B::CommandBuffer) -> Self {
        CommandBuffer {
            raw: raw,
            _marker: PhantomData,
        }
    }

    /// Get a reference to the raw command buffer
    pub fn as_raw(&self) -> &B::CommandBuffer {
        &*self.raw
    }

    /// Get a mutable reference to the raw command buffer
    pub fn as_raw_mut(&mut self) -> &mut B::CommandBuffer {
        self.raw
    }

    /// Finish recording commands to the command buffers.
    ///
    /// The command buffer will be consumed and can't be modified further.
    /// The command pool must be reset to able to re-record commands.
    pub fn finish(self) -> Submit<B, C, S, L> {
        self.raw.finish();
        let raw = self.raw.clone();

        ::std::mem::forget(self);

        Submit::new(raw)
    }

    /// Downgrade a command buffer to a lesser capability type.
    ///
    /// This is safe as a downgraded version can't be `submit`'ed
    /// since `submit` requires `self` by move.
    pub fn downgrade<D>(&mut self) -> &mut CommandBuffer<'a, B, D, S>
    where
        C: Supports<D>
    {
        unsafe { ::std::mem::transmute(self) }
    }
}

impl<'a, B: Backend, C, S: Shot> CommandBuffer<'a, B, C, S, Primary> {
    /// Identical to the `RawCommandBuffer` method of the same name.
    pub fn execute_commands<I, K>(&mut self, submits: I)
    where
        I: IntoIterator,
        I::Item: Submittable<'a, B, K, Secondary>,
        C: Supports<K>,
    {
        let submits = submits.into_iter().collect::<Vec<_>>();
        self.raw.execute_commands(submits.into_iter().map(|submit| unsafe { submit.into_buffer() }));
    }
}

impl<'a, B: Backend, C, S: Shot, L: Level> Drop for CommandBuffer<'a, B, C, S, L> {
    fn drop(&mut self) {
        self.raw.finish();
    }
}

<commit_msg>Field init shorthand RFC 1682<commit_after>//! Command buffers.
//!
//! A command buffer collects a list of commands to be submitted to the device.
//! Each command buffer has specific capabilities for graphics, compute or transfer operations,
//! and can be either a "primary" command buffer or a "secondary" command buffer.  Operations
//! always start from a primary command buffer, but a primary command buffer can contain calls
//! to secondary command buffers that contain snippets of commands that do specific things, similar
//! to function calls.
//!
//! All the possible commands are implemented in the `RawCommandBuffer` trait, and then the `CommandBuffer`
//! and related types make a generic, strongly-typed wrapper around it that only expose the methods that
//! are valid for the capabilities it provides.

// TODO: Document pipelines and subpasses better.

use Backend;
use queue::capability::Supports;
use std::marker::PhantomData;

mod compute;
mod graphics;
mod raw;
mod render_pass;
mod transfer;

pub use self::graphics::*;
pub use self::raw::{
    ClearValueRaw, ClearColorRaw, ClearDepthStencilRaw, DescriptorSetOffset,
    RawCommandBuffer, CommandBufferFlags, Level as RawLevel, CommandBufferInheritanceInfo,
};
pub use self::render_pass::*;
pub use self::transfer::*;

use std::borrow::{Cow};

/// Trait indicating how many times a Submit object can be submitted to a command buffer.
pub trait Shot {
    ///
    const FLAGS: CommandBufferFlags;
}
/// Indicates a Submit that can only be submitted once.
pub enum OneShot { }
impl Shot for OneShot { const FLAGS: CommandBufferFlags = CommandBufferFlags::ONE_TIME_SUBMIT; }

/// Indicates a Submit that can be submitted multiple times.
pub enum MultiShot { }
impl Shot for MultiShot { const FLAGS: CommandBufferFlags = CommandBufferFlags::EMPTY; }

/// A trait indicating the level of a command buffer.
pub trait Level { }

/// Indicates a primary command buffer.

/// Vulkan describes a primary command buffer as one which can be directly submitted
/// to a queue, and can execute `Secondary` command buffers.
pub enum Primary { }
impl Level for Primary { }

/// Indicates a secondary command buffer.
///
/// Vulkan describes a secondary command buffer as one which cannot be directly submitted
/// to a queue, but can be executed by a primary command buffer. This allows
/// multiple secondary command buffers to be constructed which do specific
/// things and can then be composed together into primary command buffers.
pub enum Secondary { }
impl Level for Secondary { }

/// Thread-safe finished command buffer for submission.
pub struct Submit<B: Backend, C, S, L>(pub(crate) B::CommandBuffer, pub(crate) PhantomData<(C, S, L)>);
impl<B: Backend, C, S, L> Submit<B, C, S, L> {
    fn new(buffer: B::CommandBuffer) -> Self {
        Submit(buffer, PhantomData)
    }
}
unsafe impl<B: Backend, C, S, L> Send for Submit<B, C, S, L> {}

/// A trait representing a command buffer that can be added to a `Submission`.
pub unsafe trait Submittable<'a, B: Backend, C, L: Level> {
    /// Unwraps the object into its underlying command buffer.
    unsafe fn into_buffer(self) -> Cow<'a, B::CommandBuffer>;
}

unsafe impl<'a, B: Backend, C, L: Level> Submittable<'a, B, C, L> for Submit<B, C, OneShot, L> {
    unsafe fn into_buffer(self) -> Cow<'a, B::CommandBuffer> { Cow::Owned(self.0) }
}

unsafe impl<'a, B: Backend, C, L: Level> Submittable<'a, B, C, L> for &'a Submit<B, C, MultiShot, L> {
    unsafe fn into_buffer(self) -> Cow<'a, B::CommandBuffer> { Cow::Borrowed(&self.0) }
}

/// A convenience alias for not typing out the full signature of a secondary command buffer.
pub type SecondaryCommandBuffer<'a, B, C, S = OneShot> = CommandBuffer<'a, B, C, S, Secondary>;

/// A strongly-typed command buffer that will only implement methods that are valid for the operations
/// it supports.
pub struct CommandBuffer<'a, B: Backend, C, S: Shot = OneShot, L: Level = Primary> {
    pub(crate) raw: &'a mut B::CommandBuffer,
    pub(crate) _marker: PhantomData<(C, S, L)>
}

impl<'a, B: Backend, C, S: Shot, L: Level> CommandBuffer<'a, B, C, S, L> {
    /// Create a new typed command buffer from a raw command pool.
    pub unsafe fn new(raw: &'a mut B::CommandBuffer) -> Self {
        CommandBuffer {
            raw,
            _marker: PhantomData,
        }
    }

    /// Get a reference to the raw command buffer
    pub fn as_raw(&self) -> &B::CommandBuffer {
        &*self.raw
    }

    /// Get a mutable reference to the raw command buffer
    pub fn as_raw_mut(&mut self) -> &mut B::CommandBuffer {
        self.raw
    }

    /// Finish recording commands to the command buffers.
    ///
    /// The command buffer will be consumed and can't be modified further.
    /// The command pool must be reset to able to re-record commands.
    pub fn finish(self) -> Submit<B, C, S, L> {
        self.raw.finish();
        let raw = self.raw.clone();

        ::std::mem::forget(self);

        Submit::new(raw)
    }

    /// Downgrade a command buffer to a lesser capability type.
    ///
    /// This is safe as a downgraded version can't be `submit`'ed
    /// since `submit` requires `self` by move.
    pub fn downgrade<D>(&mut self) -> &mut CommandBuffer<'a, B, D, S>
    where
        C: Supports<D>
    {
        unsafe { ::std::mem::transmute(self) }
    }
}

impl<'a, B: Backend, C, S: Shot> CommandBuffer<'a, B, C, S, Primary> {
    /// Identical to the `RawCommandBuffer` method of the same name.
    pub fn execute_commands<I, K>(&mut self, submits: I)
    where
        I: IntoIterator,
        I::Item: Submittable<'a, B, K, Secondary>,
        C: Supports<K>,
    {
        let submits = submits.into_iter().collect::<Vec<_>>();
        self.raw.execute_commands(submits.into_iter().map(|submit| unsafe { submit.into_buffer() }));
    }
}

impl<'a, B: Backend, C, S: Shot, L: Level> Drop for CommandBuffer<'a, B, C, S, L> {
    fn drop(&mut self) {
        self.raw.finish();
    }
}

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013-2014, David Renshaw (dwrenshaw@gmail.com)
 *
 * See the LICENSE file in the capnproto-rust root directory.
 */

use common::Word;
use layout::{StructReader, StructBuilder, StructSize, PointerBuilder, PointerReader};

pub trait FromStructReader<'a> {
    fn new(reader : StructReader<'a>) -> Self;
}

pub trait HasStructSize {
    fn struct_size(unused_self : Option<Self>) -> StructSize;
}

pub trait FromStructBuilder<'a> {
    fn new(structBuilder : StructBuilder<'a>) -> Self;
}

pub trait FromPointerReader<'a> {
    fn get_from_pointer(reader : &PointerReader<'a>) -> Self;
}

pub trait FromPointerReaderRefDefault<'a> {
    fn get_from_pointer(reader : &PointerReader<'a>, default_value : *const Word) -> Self;
}

pub trait FromPointerBuilder<'a> {
    fn init_pointer(PointerBuilder<'a>, u32) -> Self;
    fn get_from_pointer(builder : PointerBuilder<'a>) -> Self;
}

pub trait FromPointerBuilderRefDefault<'a> {
    fn get_from_pointer(builder : PointerBuilder<'a>, default_value : *const Word) -> Self;
}

pub trait SetPointerBuilder<To> {
    fn set_pointer_builder<'a>(PointerBuilder<'a>, Self);
}

/// Because `#[deriving(ToPrimitive)]` is not supported, using our own custom trait is more
/// convenient than using `ToPrimitive`.
pub trait ToU16 {
    fn to_u16(self) -> u16;
}

<commit_msg>Added HasTypeId trait<commit_after>/*
 * Copyright (c) 2013-2014, David Renshaw (dwrenshaw@gmail.com)
 *
 * See the LICENSE file in the capnproto-rust root directory.
 */

use common::Word;
use layout::{StructReader, StructBuilder, StructSize, PointerBuilder, PointerReader};

pub trait FromStructReader<'a> {
    fn new(reader : StructReader<'a>) -> Self;
}

pub trait HasStructSize {
    fn struct_size(unused_self : Option<Self>) -> StructSize;
}

pub trait FromStructBuilder<'a> {
    fn new(structBuilder : StructBuilder<'a>) -> Self;
}

pub trait FromPointerReader<'a> {
    fn get_from_pointer(reader : &PointerReader<'a>) -> Self;
}

pub trait FromPointerReaderRefDefault<'a> {
    fn get_from_pointer(reader : &PointerReader<'a>, default_value : *const Word) -> Self;
}

pub trait FromPointerBuilder<'a> {
    fn init_pointer(PointerBuilder<'a>, u32) -> Self;
    fn get_from_pointer(builder : PointerBuilder<'a>) -> Self;
}

pub trait FromPointerBuilderRefDefault<'a> {
    fn get_from_pointer(builder : PointerBuilder<'a>, default_value : *const Word) -> Self;
}

pub trait SetPointerBuilder<To> {
    fn set_pointer_builder<'a>(PointerBuilder<'a>, Self);
}

pub trait HasTypeId {
    fn type_id(unused_self : Option<Self>) -> u64;
}

/// Because `#[deriving(ToPrimitive)]` is not supported, using our own custom trait is more
/// convenient than using `ToPrimitive`.
pub trait ToU16 {
    fn to_u16(self) -> u16;
}

<|endoftext|>


<commit_before>error_chain! {
    errors {
        // Tun
        TunNameTooLong
        InvalidTunName
        InvalidTunAddress

        // Crypto
        InitCryptoFailed
        CryptoPasswordAlreadySetted
        InvalidMessage
	}

    foreign_links {
        Io(::std::io::Error);
        Nul(::std::ffi::NulError);
        ParseNum(::std::num::ParseIntError);
    }
}
<commit_msg>remove unused errors<commit_after>error_chain! {
    errors {
        // Tun
        TunNameTooLong
        InvalidTunName
        InvalidTunAddress

        // Crypto
        InitCryptoFailed
	}

    foreign_links {
        Io(::std::io::Error);
        Nul(::std::ffi::NulError);
        ParseNum(::std::num::ParseIntError);
    }
}
<|endoftext|>


<commit_before>use super::{directory_stack::DirectoryStack, variables::Variables};
use glob::glob;
use liner::{Completer, FilenameCompleter};

/// Performs escaping to an inner `FilenameCompleter` to enable a handful of special cases
/// needed by the shell, such as expanding '~' to a home directory, or adding a backslash
/// when a special character is contained within an expanded filename.
pub(crate) struct IonFileCompleter {
    /// The completer that this completer is  handling.
    inner: FilenameCompleter,
    /// A pointer to the directory stack in the shell.
    dir_stack: *const DirectoryStack,
    /// A pointer to the variables map in the shell.
    vars: *const Variables,
}

impl IonFileCompleter {
    pub(crate) fn new(
        path: Option<&str>,
        dir_stack: *const DirectoryStack,
        vars: *const Variables,
    ) -> IonFileCompleter {
        IonFileCompleter {
            inner: FilenameCompleter::new(path),
            dir_stack,
            vars,
        }
    }
}

impl Completer for IonFileCompleter {
    /// When the tab key is pressed, **Liner** will use this method to perform completions of
    /// filenames. As our `IonFileCompleter` is a wrapper around **Liner**'s
    /// `FilenameCompleter`,
    /// the purpose of our custom `Completer` is to expand possible `~` characters in the
    /// `start`
    /// value that we receive from the prompt, grab completions from the inner
    /// `FilenameCompleter`,
    /// and then escape the resulting filenames, as well as remove the expanded form of the `~`
    /// character and re-add the `~` character in it's place.
    fn completions(&self, start: &str) -> Vec<String> {
        // Only if the first character is a tilde character will we perform expansions
        if start.starts_with('~') {
            // Dereferencing the raw pointers here should be entirely safe, theoretically,
            // because no changes will occur to either of the underlying references in the
            // duration between creation of the completers and execution of their
            // completions.
            if let Some(expanded) = unsafe { (*self.vars).tilde_expansion(start, &*self.dir_stack) }
            {
                // Now we obtain completions for the `expanded` form of the `start` value.
                let mut iterator = filename_completion(&expanded, |x| self.inner.completions(x));

                // And then we will need to take those completions and remove the expanded form
                // of the tilde pattern and replace it with that pattern yet again.
                let mut completions = Vec::new();

                // We can do that by obtaining the index position where the tilde character
                // ends. We don't search with `~` because we also want to
                // handle other tilde variants.
                let t_index = start.find('/').unwrap_or(1);
                // `tilde` is the tilde pattern, and `search` is the pattern that follows.
                let (tilde, search) = start.split_at(t_index as usize);

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

                    // The tilde pattern will actually be our `start` command in itself,
                    // and the completed form will be all of the characters beyond the length of
                    // the expanded form of the tilde pattern.
                    for completion in iterator {
                        completions.push([start, &completion[expanded.len()..]].concat());
                    }
                } else {
                    // To save processing time, we should get obtain the index position where our
                    // search pattern begins, and re-use that index to slice the completions so
                    // that we may re-add the tilde character with the completion that follows.
                    if let Some(completion) = iterator.next() {
                        if let Some(e_index) = expanded.rfind(search) {
                            completions.push(escape(&[tilde, &completion[e_index..]].concat()));
                            for completion in iterator {
                                let expanded = &completion[e_index..];
                                completions.push(escape(&[tilde, expanded].concat()));
                            }
                        }
                    }
                }

                return completions;
            }
        }

        filename_completion(&start, |x| self.inner.completions(x)).collect()
    }
}

fn filename_completion<'a, LC>(start: &'a str, liner_complete: LC) -> impl Iterator<Item = String> + 'a
where
    LC: Fn(&str) -> Vec<String> + 'a
{
    let unescaped_start = unescape(start);

    let split_start = unescaped_start.split("/");
    let mut string = String::with_capacity(5);

    // When 'start' is an absolute path, "/..." gets split to ["", "..."]
    // So we skip the first element and add "/" to the start of the string
    let mut start_for_glob = if unescaped_start.starts_with("/") {
        string.push('/');
        split_start.skip(1).fold(string, |mut state, element| {
            state.push_str(element);
            state.push_str("*/");
            state
        })
    } else {
        split_start.fold(string, |mut state, element| {
            state.push_str(element);
            state.push_str("*/");
            state
        })
    };
    start_for_glob.pop(); // pop out the last '/' character

    let iter_inner_glob: Box<Iterator<Item = String>> = match glob(&start_for_glob) {
        Ok(completions) => {
            let mut iter = completions
                .filter_map(Result::ok)
                .map(|x| x.to_string_lossy().into_owned())
                .peekable();
            if iter.peek().is_some() {
                Box::new(iter)
            } else {
                Box::new(Some(escape(start)).into_iter())
            }
        }
        _ => Box::new(Some(escape(start)).into_iter()),
    };

    // Use Liner::Completer as well, to preserve the previous behaviour
    // around single-directory completions
    iter_inner_glob.flat_map(move |path| {
        liner_complete(&path)
            .into_iter()
            .map(|x| escape(x.as_str()))
    })
}

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

/// Unescapes filenames to be passed into the completer
fn unescape(input: &str) -> String {
    let mut output = Vec::with_capacity(input.len());
    let mut bytes = input.bytes();
    while let Some(b) = bytes.next() {
        match b {
            b'\\' => if let Some(next) = bytes.next() {
                output.push(next);
            } else {
                output.push(b'\\')
            },
            _ => output.push(b),
        }
    }
    unsafe { String::from_utf8_unchecked(output) }
}

/// A completer that combines suggestions from multiple completers.
#[derive(Clone, Eq, PartialEq)]
pub(crate) struct MultiCompleter<A, B>
where
    A: Completer,
    B: Completer,
{
    a: Vec<A>,
    b: B,
}

impl<A, B> MultiCompleter<A, B>
where
    A: Completer,
    B: Completer,
{
    pub(crate) fn new(a: Vec<A>, b: B) -> MultiCompleter<A, B> { MultiCompleter { a, b } }
}

impl<A, B> Completer for MultiCompleter<A, B>
where
    A: Completer,
    B: Completer,
{
    fn completions(&self, start: &str) -> Vec<String> {
        let mut completions = self.b.completions(start);
        for x in &self.a {
            completions.extend_from_slice(&x.completions(start));
        }
        completions
    }
}

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

    #[test]
    fn filename_completion() {
        let current_dir = env::current_dir().expect("Unable to get current directory");

        let completer = IonFileCompleter::new(
            current_dir.to_str(),
            &DirectoryStack::new(),
            &Variables::default(),
        );
        assert_eq!(completer.completions("testing"), vec!["testing/"]);
        assert_eq!(
            completer.completions("testing/file"),
            vec!["testing/file_with_text"]
        );

        assert_eq!(completer.completions("~"), vec!["~/"]);

        assert_eq!(
            completer.completions("tes/fil"),
            vec!["testing/file_with_text"]
        );
    }
}
<commit_msg>Further reduce allocations in filename completer<commit_after>use super::{directory_stack::DirectoryStack, variables::Variables};
use glob::glob;
use liner::{Completer, FilenameCompleter};
use smallvec::SmallVec;
use std::{iter, str};

/// Performs escaping to an inner `FilenameCompleter` to enable a handful of special cases
/// needed by the shell, such as expanding '~' to a home directory, or adding a backslash
/// when a special character is contained within an expanded filename.
pub(crate) struct IonFileCompleter {
    /// The completer that this completer is  handling.
    inner: FilenameCompleter,
    /// A pointer to the directory stack in the shell.
    dir_stack: *const DirectoryStack,
    /// A pointer to the variables map in the shell.
    vars: *const Variables,
}

impl IonFileCompleter {
    pub(crate) fn new(
        path: Option<&str>,
        dir_stack: *const DirectoryStack,
        vars: *const Variables,
    ) -> IonFileCompleter {
        IonFileCompleter {
            inner: FilenameCompleter::new(path),
            dir_stack,
            vars,
        }
    }
}

impl Completer for IonFileCompleter {
    /// When the tab key is pressed, **Liner** will use this method to perform completions of
    /// filenames. As our `IonFileCompleter` is a wrapper around **Liner**'s
    /// `FilenameCompleter`,
    /// the purpose of our custom `Completer` is to expand possible `~` characters in the
    /// `start`
    /// value that we receive from the prompt, grab completions from the inner
    /// `FilenameCompleter`,
    /// and then escape the resulting filenames, as well as remove the expanded form of the `~`
    /// character and re-add the `~` character in it's place.
    fn completions(&self, start: &str) -> Vec<String> {
        // Only if the first character is a tilde character will we perform expansions
        if start.starts_with('~') {
            // Dereferencing the raw pointers here should be entirely safe, theoretically,
            // because no changes will occur to either of the underlying references in the
            // duration between creation of the completers and execution of their
            // completions.
            if let Some(expanded) = unsafe { (*self.vars).tilde_expansion(start, &*self.dir_stack) }
            {
                // Now we obtain completions for the `expanded` form of the `start` value.
                let mut iterator = filename_completion(&expanded, |x| self.inner.completions(x));

                // And then we will need to take those completions and remove the expanded form
                // of the tilde pattern and replace it with that pattern yet again.
                let mut completions = Vec::new();

                // We can do that by obtaining the index position where the tilde character
                // ends. We don't search with `~` because we also want to
                // handle other tilde variants.
                let t_index = start.find('/').unwrap_or(1);
                // `tilde` is the tilde pattern, and `search` is the pattern that follows.
                let (tilde, search) = start.split_at(t_index as usize);

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

                    // The tilde pattern will actually be our `start` command in itself,
                    // and the completed form will be all of the characters beyond the length of
                    // the expanded form of the tilde pattern.
                    for completion in iterator {
                        completions.push([start, &completion[expanded.len()..]].concat());
                    }
                } else {
                    // To save processing time, we should get obtain the index position where our
                    // search pattern begins, and re-use that index to slice the completions so
                    // that we may re-add the tilde character with the completion that follows.
                    if let Some(completion) = iterator.next() {
                        if let Some(e_index) = expanded.rfind(search) {
                            completions.push(escape(&[tilde, &completion[e_index..]].concat()));
                            for completion in iterator {
                                let expanded = &completion[e_index..];
                                completions.push(escape(&[tilde, expanded].concat()));
                            }
                        }
                    }
                }

                return completions;
            }
        }

        filename_completion(&start, |x| self.inner.completions(x)).collect()
    }
}

fn filename_completion<'a, LC>(start: &'a str, liner_complete: LC) -> impl Iterator<Item = String> + 'a
where
    LC: Fn(&str) -> Vec<String> + 'a
{
    let unescaped_start = unescape(start);

    let split_start = unescaped_start.split("/");
    let mut string: SmallVec<[u8; 128]> = SmallVec::with_capacity(128);

    // When 'start' is an absolute path, "/..." gets split to ["", "..."]
    // So we skip the first element and add "/" to the start of the string
    let skip = if unescaped_start.starts_with("/") {
        string.push(b'/');
        1
    } else {
        0
    };

    for element in split_start.skip(skip) {
        string.extend_from_slice(element.as_bytes());
        string.extend_from_slice(b"*/");
    }

    string.pop(); // pop out the last '/' character
    let string = unsafe { &str::from_utf8_unchecked(&string) };

    let globs = glob(string).ok().and_then(|completions| {
        let mut completions = completions
            .filter_map(Result::ok)
            .map(|x| x.to_string_lossy().into_owned());

        if let Some(first) = completions.next() {
            Some(iter::once(first).chain(completions))
        } else {
            None
        }
    });

    let iter_inner_glob: Box<Iterator<Item = String>> = match globs {
        Some(iter) => Box::new(iter),
        None => Box::new(iter::once(escape(start)))
    };

    // Use Liner::Completer as well, to preserve the previous behaviour
    // around single-directory completions
    iter_inner_glob.flat_map(move |path| {
        liner_complete(&path)
            .into_iter()
            .map(|x| escape(x.as_str()))
    })
}

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

/// Unescapes filenames to be passed into the completer
fn unescape(input: &str) -> String {
    let mut output = Vec::with_capacity(input.len());
    let mut bytes = input.bytes();
    while let Some(b) = bytes.next() {
        match b {
            b'\\' => if let Some(next) = bytes.next() {
                output.push(next);
            } else {
                output.push(b'\\')
            },
            _ => output.push(b),
        }
    }
    unsafe { String::from_utf8_unchecked(output) }
}

/// A completer that combines suggestions from multiple completers.
#[derive(Clone, Eq, PartialEq)]
pub(crate) struct MultiCompleter<A, B>
where
    A: Completer,
    B: Completer,
{
    a: Vec<A>,
    b: B,
}

impl<A, B> MultiCompleter<A, B>
where
    A: Completer,
    B: Completer,
{
    pub(crate) fn new(a: Vec<A>, b: B) -> MultiCompleter<A, B> { MultiCompleter { a, b } }
}

impl<A, B> Completer for MultiCompleter<A, B>
where
    A: Completer,
    B: Completer,
{
    fn completions(&self, start: &str) -> Vec<String> {
        let mut completions = self.b.completions(start);
        for x in &self.a {
            completions.extend_from_slice(&x.completions(start));
        }
        completions
    }
}

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

    #[test]
    fn filename_completion() {
        let current_dir = env::current_dir().expect("Unable to get current directory");

        let completer = IonFileCompleter::new(
            current_dir.to_str(),
            &DirectoryStack::new(),
            &Variables::default(),
        );
        assert_eq!(completer.completions("testing"), vec!["testing/"]);
        assert_eq!(
            completer.completions("testing/file"),
            vec!["testing/file_with_text"]
        );

        assert_eq!(completer.completions("~"), vec!["~/"]);

        assert_eq!(
            completer.completions("tes/fil"),
            vec!["testing/file_with_text"]
        );
    }
}
<|endoftext|>


<commit_before>use super::{colors::Colors, flow_control::Function};
use crate::{
    expansion,
    shell::IonError,
    types::{self, Array},
};
use nix::unistd::{geteuid, gethostname, getpid, getuid};
use scopes::{Namespace, Scope, Scopes};
use std::{env, ffi::CStr, rc::Rc};
use unicode_segmentation::UnicodeSegmentation;

/// Contain a dynamically-typed variable value
pub use types_rs::Value;
/// A structure containing dynamically-typed values organised in scopes
pub struct Variables(Scopes<types::Str, Value<Rc<Function>>>);

impl Variables {
    /// Get all strings
    pub fn string_vars(&self) -> impl Iterator<Item = (&types::Str, &types::Str)> {
        self.0.scopes().flat_map(|map| {
            map.iter().filter_map(|(key, val)| {
                if let types_rs::Value::Str(val) = val {
                    Some((key, val))
                } else {
                    None
                }
            })
        })
    }

    /// Get all aliases
    pub fn aliases(&self) -> impl Iterator<Item = (&types::Str, &types::Str)> {
        self.0.scopes().rev().flat_map(|map| {
            map.iter().filter_map(|(key, possible_alias)| {
                if let types_rs::Value::Alias(alias) = possible_alias {
                    Some((key, &**alias))
                } else {
                    None
                }
            })
        })
    }

    /// Get all the functions
    pub fn functions(&self) -> impl Iterator<Item = (&types::Str, &Rc<Function>)> {
        self.0.scopes().rev().flat_map(|map| {
            map.iter().filter_map(|(key, val)| {
                if let types_rs::Value::Function(val) = val {
                    Some((key, val))
                } else {
                    None
                }
            })
        })
    }

    /// Get all the variables
    pub fn variables(&self) -> impl Iterator<Item = (&types::Str, &Value<Rc<Function>>)> {
        self.0.scopes().rev().flat_map(|map| {
            map.iter().filter_map(|(key, val)| match val {
                val @ Value::Array(_)
                | val @ Value::Str(_)
                | val @ Value::HashMap(_)
                | val @ Value::BTreeMap(_) => Some((key, val)),
                _ => None,
            })
        })
    }

    /// Get all the array values
    pub fn arrays(&self) -> impl Iterator<Item = (&types::Str, &types::Array<Rc<Function>>)> {
        self.0.scopes().rev().flat_map(|map| {
            map.iter().filter_map(|(key, val)| {
                if let types_rs::Value::Array(val) = val {
                    Some((key, val))
                } else {
                    None
                }
            })
        })
    }

    /// Create a new scope. If namespace is true, variables won't be droppable across the scope
    /// boundary
    pub fn new_scope(&mut self, namespace: bool) { self.0.new_scope(namespace) }

    /// Exit the current scope
    pub fn pop_scope(&mut self) { self.0.pop_scope() }

    pub(crate) fn pop_scopes<'b>(
        &'b mut self,
        index: usize,
    ) -> impl Iterator<Item = Scope<types::Str, Value<Rc<Function>>>> + 'b {
        self.0.pop_scopes(index)
    }

    pub(crate) fn append_scopes(&mut self, scopes: Vec<Scope<types::Str, Value<Rc<Function>>>>) {
        self.0.append_scopes(scopes)
    }

    #[must_use]
    pub(crate) fn index_scope_for_var(&self, name: &str) -> Option<usize> {
        self.0.index_scope_for_var(name)
    }

    /// Set a variable to a value in the current scope. If a variable already exists in a writable
    /// scope, it is updated, else a new variable is created in the current scope, possibly
    /// shadowing other variables
    pub fn set<T: Into<Value<Rc<Function>>>>(&mut self, name: &str, value: T) {
        let value = value.into();
        if let Some(val) = self.0.get_mut(name) {
            std::mem::replace(val, value);
        } else {
            self.0.set(name, value);
        }
    }

    /// Obtains the value for the **MWD** variable.
    ///
    /// Further minimizes the directory path in the same manner that Fish does by default.
    /// That is, if more than two parents are visible in the path, all parent directories
    /// of the current directory will be reduced to a single character.
    #[must_use]
    fn get_minimal_directory(&self) -> types::Str {
        let swd = self.get_simplified_directory();

        {
            // Temporarily borrow the `swd` variable while we attempt to assemble a minimal
            // variant of the directory path. If that is not possible, we will cancel the
            // borrow and return `swd` itself as the minified path.
            let elements = swd.split('/').filter(|s| !s.is_empty()).collect::<Vec<&str>>();
            if elements.len() > 2 {
                let mut output = types::Str::new();
                for element in &elements[..elements.len() - 1] {
                    let mut segmenter = UnicodeSegmentation::graphemes(*element, true);
                    let grapheme = segmenter.next().unwrap();
                    output.push_str(grapheme);
                    if grapheme == "." {
                        output.push_str(segmenter.next().unwrap());
                    }
                    output.push('/');
                }
                output.push_str(elements[elements.len() - 1]);
                return output;
            }
        }

        swd
    }

    /// Obtains the value for the **SWD** variable.
    ///
    /// Useful for getting smaller prompts, this will produce a simplified variant of the
    /// working directory which the leading `HOME` prefix replaced with a tilde character.
    #[must_use]
    fn get_simplified_directory(&self) -> types::Str {
        let home = self.get_str("HOME").unwrap_or_else(|_| "?".into());
        env::var("PWD").unwrap().replace(&*home, "~").into()
    }

    /// Indicates if name is valid for functions and variables
    #[must_use]
    pub fn is_valid_name(name: &str) -> bool {
        let mut iter = name.chars();
        iter.next().map_or(false, |c| c.is_alphabetic() || c == '_')
            && iter.all(|c| c.is_alphanumeric() || c == '_')
    }

    /// Remove a variable from the current scope. If the value can't be removed (it is outside a
    /// function or does not exist), returns None
    pub fn remove(&mut self, name: &str) -> Option<Value<Rc<Function>>> {
        if name.starts_with("super::") || name.starts_with("global::") {
            // Cannot mutate outer namespace
            return None;
        }
        self.0.remove_variable(name)
    }

    /// Get the string value associated with a name on the current scope. This includes fetching
    /// env vars, colors & hexes and some extra values like MWD and SWD
    pub fn get_str(&self, name: &str) -> expansion::Result<types::Str, IonError> {
        use expansion::Error;
        match name {
            "MWD" => return Ok(self.get_minimal_directory()),
            "SWD" => return Ok(self.get_simplified_directory()),
            _ => (),
        }
        // If the parsed name contains the '::' pattern, then a namespace was
        // designated. Find it.
        match name.find("::").map(|pos| (&name[..pos], &name[pos + 2..])) {
            Some(("c", variable)) | Some(("color", variable)) => {
                Ok(Colors::collect(variable)?.to_string().into())
            }
            Some(("x", variable)) | Some(("hex", variable)) => {
                let c = u8::from_str_radix(variable, 16)
                    .map_err(|cause| Error::InvalidHex(variable.into(), cause))?;
                Ok((c as char).to_string().into())
            }
            Some(("env", variable)) => match env::var(variable) {
                Ok(v) => Ok(v.into()),
                Err(_) => Ok("".into()),
            },
            Some(("super", _)) | Some(("global", _)) | None => {
                // Otherwise, it's just a simple variable name.
                match self.get(name) {
                    Some(Value::Str(val)) => Ok(val.clone()),
                    _ => env::var(name).map(Into::into).map_err(|_| Error::VarNotFound),
                }
            }
            Some((..)) => Err(Error::UnsupportedNamespace(name.into())),
        }
    }

    /// Get a variable on the current scope
    #[must_use]
    pub fn get(&self, mut name: &str) -> Option<&Value<Rc<Function>>> {
        const GLOBAL_NS: &str = "global::";
        const SUPER_NS: &str = "super::";

        let namespace = if name.starts_with(GLOBAL_NS) {
            name = &name[GLOBAL_NS.len()..];
            // Go up as many namespaces as possible
            Namespace::Global
        } else if name.starts_with(SUPER_NS) {
            let mut up = 0;
            while name.starts_with(SUPER_NS) {
                name = &name[SUPER_NS.len()..];
                up += 1;
            }

            Namespace::Specific(up)
        } else {
            Namespace::Any
        };
        self.0.get(name, namespace)
    }

    /// Get a mutable access to a variable on the current scope
    #[must_use]
    pub fn get_mut(&mut self, name: &str) -> Option<&mut Value<Rc<Function>>> {
        if name.starts_with("super::") || name.starts_with("global::") {
            // Cannot mutate outer namespace
            return None;
        }
        self.0.get_mut(name)
    }
}

impl Default for Variables {
    #[must_use]
    fn default() -> Self {
        let mut map: Scopes<types::Str, Value<Rc<Function>>> = Scopes::with_capacity(64);
        map.set("HISTORY_SIZE", "1000");
        map.set("HISTFILE_SIZE", "100000");
        map.set(
            "PROMPT",
            "${x::1B}]0;${USER}: \
             ${PWD}${x::07}${c::0x55,bold}${USER}${c::default}:${c::0x4B}${SWD}${c::default}# \
             ${c::reset}",
        );

        // Set the PID, UID, and EUID variables.
        map.set("PID", Value::Str(getpid().to_string().into()));
        map.set("UID", Value::Str(getuid().to_string().into()));
        map.set("EUID", Value::Str(geteuid().to_string().into()));

        map.set("CDPATH", Array::new());

        // Initialize the HOST variable
        let mut host_name = [0_u8; 512];
        env::set_var(
            "HOST",
            &gethostname(&mut host_name)
                .ok()
                .map_or_else(|| "?".into(), CStr::to_string_lossy)
                .as_ref(),
        );

        Variables(map)
    }
}

#[cfg(test)]
pub(crate) mod tests {
    use super::*;
    use crate::{
        expansion::{Expander, Result, Select},
        shell::IonError,
    };
    use serial_test_derive::serial;

    pub struct VariableExpander(pub Variables);

    impl Expander for VariableExpander {
        type Error = IonError;

        fn string(&self, var: &str) -> Result<types::Str, IonError> { self.0.get_str(var) }

        fn array(
            &self,
            _variable: &str,
            _selection: &Select<types::Str>,
        ) -> Result<types::Args, Self::Error> {
            Err(expansion::Error::VarNotFound)
        }

        fn command(
            &mut self,
            cmd: &str,
            _set_cmd_duration: bool,
        ) -> Result<types::Str, Self::Error> {
            Ok(cmd.into())
        }

        fn tilde(&self, input: &str) -> Result<types::Str, Self::Error> { Ok(input.into()) }

        fn map_keys(&self, _name: &str) -> Result<types::Args, Self::Error> {
            Err(expansion::Error::VarNotFound)
        }

        fn map_values(&self, _name: &str) -> Result<types::Args, Self::Error> {
            Err(expansion::Error::VarNotFound)
        }
    }

    #[test]
    fn undefined_variable_errors() {
        let variables = Variables::default();
        assert!(VariableExpander(variables).expand_string("$FOO").is_err());
    }

    #[test]
    fn set_var_and_expand_a_variable() {
        let mut variables = Variables::default();
        variables.set("FOO", "BAR");
        let expanded = VariableExpander(variables).expand_string("$FOO").unwrap().join("");
        assert_eq!("BAR", &expanded);
    }

    #[test]
    #[serial]
    fn minimal_directory_var_should_compact_path() {
        let variables = Variables::default();
        env::set_var("PWD", "/var/log/nix");
        assert_eq!(
            types::Str::from("v/l/nix"),
            variables.get_str("MWD").expect("no value returned"),
        );
    }

    #[test]
    #[serial]
    fn minimal_directory_var_shouldnt_compact_path() {
        let variables = Variables::default();
        env::set_var("PWD", "/var/log");
        assert_eq!(
            types::Str::from("/var/log"),
            variables.get_str("MWD").expect("no value returned"),
        );
    }
}
<commit_msg>now uses unwrap_or_default()<commit_after>use super::{colors::Colors, flow_control::Function};
use crate::{
    expansion,
    shell::IonError,
    types::{self, Array},
};
use nix::unistd::{geteuid, gethostname, getpid, getuid};
use scopes::{Namespace, Scope, Scopes};
use std::{env, ffi::CStr, rc::Rc};
use unicode_segmentation::UnicodeSegmentation;

/// Contain a dynamically-typed variable value
pub use types_rs::Value;
/// A structure containing dynamically-typed values organised in scopes
pub struct Variables(Scopes<types::Str, Value<Rc<Function>>>);

impl Variables {
    /// Get all strings
    pub fn string_vars(&self) -> impl Iterator<Item = (&types::Str, &types::Str)> {
        self.0.scopes().flat_map(|map| {
            map.iter().filter_map(|(key, val)| {
                if let types_rs::Value::Str(val) = val {
                    Some((key, val))
                } else {
                    None
                }
            })
        })
    }

    /// Get all aliases
    pub fn aliases(&self) -> impl Iterator<Item = (&types::Str, &types::Str)> {
        self.0.scopes().rev().flat_map(|map| {
            map.iter().filter_map(|(key, possible_alias)| {
                if let types_rs::Value::Alias(alias) = possible_alias {
                    Some((key, &**alias))
                } else {
                    None
                }
            })
        })
    }

    /// Get all the functions
    pub fn functions(&self) -> impl Iterator<Item = (&types::Str, &Rc<Function>)> {
        self.0.scopes().rev().flat_map(|map| {
            map.iter().filter_map(|(key, val)| {
                if let types_rs::Value::Function(val) = val {
                    Some((key, val))
                } else {
                    None
                }
            })
        })
    }

    /// Get all the variables
    pub fn variables(&self) -> impl Iterator<Item = (&types::Str, &Value<Rc<Function>>)> {
        self.0.scopes().rev().flat_map(|map| {
            map.iter().filter_map(|(key, val)| match val {
                val @ Value::Array(_)
                | val @ Value::Str(_)
                | val @ Value::HashMap(_)
                | val @ Value::BTreeMap(_) => Some((key, val)),
                _ => None,
            })
        })
    }

    /// Get all the array values
    pub fn arrays(&self) -> impl Iterator<Item = (&types::Str, &types::Array<Rc<Function>>)> {
        self.0.scopes().rev().flat_map(|map| {
            map.iter().filter_map(|(key, val)| {
                if let types_rs::Value::Array(val) = val {
                    Some((key, val))
                } else {
                    None
                }
            })
        })
    }

    /// Create a new scope. If namespace is true, variables won't be droppable across the scope
    /// boundary
    pub fn new_scope(&mut self, namespace: bool) { self.0.new_scope(namespace) }

    /// Exit the current scope
    pub fn pop_scope(&mut self) { self.0.pop_scope() }

    pub(crate) fn pop_scopes<'b>(
        &'b mut self,
        index: usize,
    ) -> impl Iterator<Item = Scope<types::Str, Value<Rc<Function>>>> + 'b {
        self.0.pop_scopes(index)
    }

    pub(crate) fn append_scopes(&mut self, scopes: Vec<Scope<types::Str, Value<Rc<Function>>>>) {
        self.0.append_scopes(scopes)
    }

    #[must_use]
    pub(crate) fn index_scope_for_var(&self, name: &str) -> Option<usize> {
        self.0.index_scope_for_var(name)
    }

    /// Set a variable to a value in the current scope. If a variable already exists in a writable
    /// scope, it is updated, else a new variable is created in the current scope, possibly
    /// shadowing other variables
    pub fn set<T: Into<Value<Rc<Function>>>>(&mut self, name: &str, value: T) {
        let value = value.into();
        if let Some(val) = self.0.get_mut(name) {
            std::mem::replace(val, value);
        } else {
            self.0.set(name, value);
        }
    }

    /// Obtains the value for the **MWD** variable.
    ///
    /// Further minimizes the directory path in the same manner that Fish does by default.
    /// That is, if more than two parents are visible in the path, all parent directories
    /// of the current directory will be reduced to a single character.
    #[must_use]
    fn get_minimal_directory(&self) -> types::Str {
        let swd = self.get_simplified_directory();

        {
            // Temporarily borrow the `swd` variable while we attempt to assemble a minimal
            // variant of the directory path. If that is not possible, we will cancel the
            // borrow and return `swd` itself as the minified path.
            let elements = swd.split('/').filter(|s| !s.is_empty()).collect::<Vec<&str>>();
            if elements.len() > 2 {
                let mut output = types::Str::new();
                for element in &elements[..elements.len() - 1] {
                    let mut segmenter = UnicodeSegmentation::graphemes(*element, true);
                    let grapheme = segmenter.next().unwrap();
                    output.push_str(grapheme);
                    if grapheme == "." {
                        output.push_str(segmenter.next().unwrap());
                    }
                    output.push('/');
                }
                output.push_str(elements[elements.len() - 1]);
                return output;
            }
        }

        swd
    }

    /// Obtains the value for the **SWD** variable.
    ///
    /// Useful for getting smaller prompts, this will produce a simplified variant of the
    /// working directory which the leading `HOME` prefix replaced with a tilde character.
    #[must_use]
    fn get_simplified_directory(&self) -> types::Str {
        let home = self.get_str("HOME").unwrap_or_else(|_| "?".into());
        env::var("PWD").unwrap().replace(&*home, "~").into()
    }

    /// Indicates if name is valid for functions and variables
    #[must_use]
    pub fn is_valid_name(name: &str) -> bool {
        let mut iter = name.chars();
        iter.next().map_or(false, |c| c.is_alphabetic() || c == '_')
            && iter.all(|c| c.is_alphanumeric() || c == '_')
    }

    /// Remove a variable from the current scope. If the value can't be removed (it is outside a
    /// function or does not exist), returns None
    pub fn remove(&mut self, name: &str) -> Option<Value<Rc<Function>>> {
        if name.starts_with("super::") || name.starts_with("global::") {
            // Cannot mutate outer namespace
            return None;
        }
        self.0.remove_variable(name)
    }

    /// Get the string value associated with a name on the current scope. This includes fetching
    /// env vars, colors & hexes and some extra values like MWD and SWD
    pub fn get_str(&self, name: &str) -> expansion::Result<types::Str, IonError> {
        use expansion::Error;
        match name {
            "MWD" => return Ok(self.get_minimal_directory()),
            "SWD" => return Ok(self.get_simplified_directory()),
            _ => (),
        }
        // If the parsed name contains the '::' pattern, then a namespace was
        // designated. Find it.
        match name.find("::").map(|pos| (&name[..pos], &name[pos + 2..])) {
            Some(("c", variable)) | Some(("color", variable)) => {
                Ok(Colors::collect(variable)?.to_string().into())
            }
            Some(("x", variable)) | Some(("hex", variable)) => {
                let c = u8::from_str_radix(variable, 16)
                    .map_err(|cause| Error::InvalidHex(variable.into(), cause))?;
                Ok((c as char).to_string().into())
            }
            Some(("env", variable)) => Ok(env::var(variable).unwrap_or_default().into()),
            Some(("super", _)) | Some(("global", _)) | None => {
                // Otherwise, it's just a simple variable name.
                match self.get(name) {
                    Some(Value::Str(val)) => Ok(val.clone()),
                    _ => env::var(name).map(Into::into).map_err(|_| Error::VarNotFound),
                }
            }
            Some((..)) => Err(Error::UnsupportedNamespace(name.into())),
        }
    }

    /// Get a variable on the current scope
    #[must_use]
    pub fn get(&self, mut name: &str) -> Option<&Value<Rc<Function>>> {
        const GLOBAL_NS: &str = "global::";
        const SUPER_NS: &str = "super::";

        let namespace = if name.starts_with(GLOBAL_NS) {
            name = &name[GLOBAL_NS.len()..];
            // Go up as many namespaces as possible
            Namespace::Global
        } else if name.starts_with(SUPER_NS) {
            let mut up = 0;
            while name.starts_with(SUPER_NS) {
                name = &name[SUPER_NS.len()..];
                up += 1;
            }

            Namespace::Specific(up)
        } else {
            Namespace::Any
        };
        self.0.get(name, namespace)
    }

    /// Get a mutable access to a variable on the current scope
    #[must_use]
    pub fn get_mut(&mut self, name: &str) -> Option<&mut Value<Rc<Function>>> {
        if name.starts_with("super::") || name.starts_with("global::") {
            // Cannot mutate outer namespace
            return None;
        }
        self.0.get_mut(name)
    }
}

impl Default for Variables {
    #[must_use]
    fn default() -> Self {
        let mut map: Scopes<types::Str, Value<Rc<Function>>> = Scopes::with_capacity(64);
        map.set("HISTORY_SIZE", "1000");
        map.set("HISTFILE_SIZE", "100000");
        map.set(
            "PROMPT",
            "${x::1B}]0;${USER}: \
             ${PWD}${x::07}${c::0x55,bold}${USER}${c::default}:${c::0x4B}${SWD}${c::default}# \
             ${c::reset}",
        );

        // Set the PID, UID, and EUID variables.
        map.set("PID", Value::Str(getpid().to_string().into()));
        map.set("UID", Value::Str(getuid().to_string().into()));
        map.set("EUID", Value::Str(geteuid().to_string().into()));

        map.set("CDPATH", Array::new());

        // Initialize the HOST variable
        let mut host_name = [0_u8; 512];
        env::set_var(
            "HOST",
            &gethostname(&mut host_name)
                .ok()
                .map_or_else(|| "?".into(), CStr::to_string_lossy)
                .as_ref(),
        );

        Variables(map)
    }
}

#[cfg(test)]
pub(crate) mod tests {
    use super::*;
    use crate::{
        expansion::{Expander, Result, Select},
        shell::IonError,
    };
    use serial_test_derive::serial;

    pub struct VariableExpander(pub Variables);

    impl Expander for VariableExpander {
        type Error = IonError;

        fn string(&self, var: &str) -> Result<types::Str, IonError> { self.0.get_str(var) }

        fn array(
            &self,
            _variable: &str,
            _selection: &Select<types::Str>,
        ) -> Result<types::Args, Self::Error> {
            Err(expansion::Error::VarNotFound)
        }

        fn command(
            &mut self,
            cmd: &str,
            _set_cmd_duration: bool,
        ) -> Result<types::Str, Self::Error> {
            Ok(cmd.into())
        }

        fn tilde(&self, input: &str) -> Result<types::Str, Self::Error> { Ok(input.into()) }

        fn map_keys(&self, _name: &str) -> Result<types::Args, Self::Error> {
            Err(expansion::Error::VarNotFound)
        }

        fn map_values(&self, _name: &str) -> Result<types::Args, Self::Error> {
            Err(expansion::Error::VarNotFound)
        }
    }

    #[test]
    fn undefined_variable_errors() {
        let variables = Variables::default();
        assert!(VariableExpander(variables).expand_string("$FOO").is_err());
    }

    #[test]
    fn set_var_and_expand_a_variable() {
        let mut variables = Variables::default();
        variables.set("FOO", "BAR");
        let expanded = VariableExpander(variables).expand_string("$FOO").unwrap().join("");
        assert_eq!("BAR", &expanded);
    }

    #[test]
    #[serial]
    fn minimal_directory_var_should_compact_path() {
        let variables = Variables::default();
        env::set_var("PWD", "/var/log/nix");
        assert_eq!(
            types::Str::from("v/l/nix"),
            variables.get_str("MWD").expect("no value returned"),
        );
    }

    #[test]
    #[serial]
    fn minimal_directory_var_shouldnt_compact_path() {
        let variables = Variables::default();
        env::set_var("PWD", "/var/log");
        assert_eq!(
            types::Str::from("/var/log"),
            variables.get_str("MWD").expect("no value returned"),
        );
    }
}
<|endoftext|>


<commit_before>// DragonFlyBSD's __error function is declared with "static inline", so it must
// be implemented in the libc crate, as a pointer to a static thread_local.
f! {
    #[deprecated(since = "0.2.77", "Use `__errno_location()` instead")]
    pub fn __error() -> *mut ::c_int {
        &mut errno
    }
}

extern "C" {
    #[thread_local]
    pub static mut errno: ::c_int;
}
<commit_msg>Fix `deprecated` attribute for `__error`<commit_after>// DragonFlyBSD's __error function is declared with "static inline", so it must
// be implemented in the libc crate, as a pointer to a static thread_local.
f! {
    #[deprecated(since = "0.2.77", note = "Use `__errno_location()` instead")]
    pub fn __error() -> *mut ::c_int {
        &mut errno
    }
}

extern "C" {
    #[thread_local]
    pub static mut errno: ::c_int;
}
<|endoftext|>