bash-builtins 0.4.1

This crate provides utilities to implement loadable builtins for bash.
Documentation
//! Bash builtin to access shell variables.

use bash_builtins::variables::{self, Variable};
use bash_builtins::{builtin_metadata, Args, Builtin, Error, Result};
use std::io::{self, BufWriter, Write};

builtin_metadata!(name = "usevars", create = UseVars::default);

#[derive(Default)]
struct UseVars;

impl Builtin for UseVars {
    fn call(&mut self, args: &mut Args) -> Result<()> {
        let stdout_handle = io::stdout();
        let mut output = BufWriter::new(stdout_handle.lock());

        for name in args.string_arguments() {
            let mut name_parts = name?.splitn(2, '=');
            match (name_parts.next(), name_parts.next()) {
                (Some(name), None) => {
                    if name.contains('[') {
                        get_array(name)?;
                    } else if let Some(var) = variables::find(name) {
                        write_var(&mut output, name, var)?
                    }
                }

                (Some(name), Some("")) => {
                    if variables::unset(name) {
                        writeln!(&mut output, "unset: {}", name)?;
                    }
                }

                (Some(name), Some(value)) => {
                    if name.contains('[') {
                        set_array(name, value)?;
                    } else {
                        variables::set(name, value)?;
                    }
                }

                _ => (),
            }
        }

        Ok(())
    }
}

fn write_var(mut output: impl Write, name: &str, var: Variable) -> io::Result<()> {
    match var {
        Variable::Str(s) => writeln!(&mut output, "{} = {:?}", name, s)?,

        Variable::Array(a) => {
            for (idx, elem) in a.iter().enumerate() {
                writeln!(&mut output, "{}[{}] = {:?}", name, idx, elem)?;
            }
        }

        Variable::Assoc(a) => {
            for (key, value) in a.iter() {
                writeln!(&mut output, "{}[{:?}] = {:?}", name, key, value)?;
            }
        }
    }

    Ok(())
}

fn parse_array_ref(name: &str) -> Result<(&str, &str)> {
    let (open, close) = match (name.find('['), name.find(']')) {
        (Some(a), Some(b)) if b + 1 == name.len() => (a, b),
        _ => Err(Error::Usage)?,
    };

    let var_name = &name[..open];
    let key = &name[open + 1..close];

    Ok((var_name, key))
}

fn get_array(name: &str) -> Result<()> {
    let (var_name, key) = parse_array_ref(name)?;

    let value = if let Ok(index) = key.parse() {
        variables::array_get(var_name, index)
    } else {
        variables::assoc_get(var_name, key)
    };

    println!("{} = {:?}", name, value);

    Ok(())
}

fn set_array(name: &str, value: &str) -> Result<()> {
    let (var_name, key) = parse_array_ref(name)?;

    if let Ok(index) = key.parse() {
        variables::array_set(var_name, index, value)?;
    } else {
        variables::assoc_set(var_name, key, value)?;
    }

    Ok(())
}