1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#![no_std]

extern crate alloc;

use alloc::{string::String, sync::Arc};
use core::fmt;
use fs_node::{DirRef, FileOrDir};
use hashbrown::HashMap;
use path::Path;

/// A structure that contains environment state for a given `Task` or group of
/// `Task`s.
///
/// A default environment can be created with the following state:
/// * The working directory is the `root` directory.
pub struct Environment {
    /// The "current working directory", i.e.,
    /// where a task's relative path begins upon first execution.
    pub working_dir: DirRef,
    pub variables: HashMap<String, String>,
}

impl Environment {
    /// Returns the absolute file path of the current working directory.
    #[doc(alias("working", "dir", "current", "getcwd"))]
    pub fn cwd(&self) -> String {
        let wd = self.working_dir.lock();
        wd.get_absolute_path()
    }

    /// Changes the current working directory.
    #[doc(alias("change"))]
    pub fn chdir(&mut self, path: &Path) -> Result<()> {
        for component in path.components() {
            let new = self.working_dir.lock().get(component.as_ref());
            match new {
                Some(FileOrDir::Dir(dir)) => {
                    self.working_dir = dir;
                }
                Some(FileOrDir::File(_)) => return Err(Error::NotADirectory),
                None => return Err(Error::NotFound),
            }
        }
        Ok(())
    }

    /// Returns the value of the environment variable with the given `key`.
    #[doc(alias("var"))]
    pub fn get(&self, key: &str) -> Option<&String> {
        self.variables.get(key)
    }

    /// Sets an environment variable with the given `key` and `value`.
    #[doc(alias("set_var"))]
    pub fn set(&mut self, key: String, value: String) {
        self.variables.insert(key, value);
    }

    /// Unsets the environment variable with the given `key`.
    #[doc(alias("remove_var"))]
    pub fn unset(&mut self, key: &str) {
        self.variables.remove(key);
    }
}

impl Default for Environment {
    fn default() -> Environment {
        Environment {
            working_dir: Arc::clone(root::get_root()),
            variables: HashMap::new(),
        }
    }
}

/// A specialized [`Result`] type for environment operations.
///
/// [`Result`]: core::result::Result
pub type Result<T> = core::result::Result<T, Error>;

/// The error type for environment operations.
pub enum Error {
    /// A filesystem node was, unexpectedly, not a directory.
    NotADirectory,
    /// A filesystem node wasn't found.
    NotFound,
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(match self {
            Error::NotADirectory => "not a directory",
            Error::NotFound => "entity not found",
        })
    }
}