knightrs/environment.rs
1//! Types relating to the execution of Knight code.
2//!
3//! See [`Environment`] for more details.
4
5use crate::{Error, Result, Value, Text};
6use std::collections::HashSet;
7use std::fmt::{self, Debug, Formatter};
8use std::io::{self, Write, Read, BufRead, BufReader};
9
10mod builder;
11mod variable;
12
13pub use builder::Builder;
14pub use variable::Variable;
15
16type SystemCommand = dyn FnMut(&str) -> Result<Text>;
17
18/// The execution environment for Knight programs.
19///
20/// To aid in embedding Knight in other systems, the [`Environment`] provides complete control over the stdin, stdout,
21/// and output of the ["system" (`` ` ``)](crate::function::system), in addition to keeping track of all relevant
22/// variables. Because of this, the environment must always be passed when calling [`Value::run`](crate::Value::run).
23///
24/// This is in contrast with most other Knight implementations, which usually have a singular, global "environment", and
25///
26/// # Examples
27/// ```rust,no_run
28/// # use knightrs::Environment;
29/// # use std::io::{Read, Write};
30/// let mut env = Environment::new();
31///
32/// // Write to stdout.
33/// writeln!(env, "Hello, world!");
34///
35/// // Read from stdin.
36/// let mut str = String::new();
37/// env.read_to_string(&mut str).expect("cant read from stdin!");
38///
39/// // execute command
40/// println!("The stdout of `ls -al` is {}", env.system("ls -al").expect("`ls -al` failed"));
41///
42/// // create a variable
43/// let var = env.get("foobar");
44/// assert_eq!(var, env.get("foobar")); // both variables are the same.
45/// ```
46pub struct Environment<'i, 'o, 'c> {
47 // We use a `HashSet` because we want the variable to own its name, which a `HashMap`
48 // wouldn't allow for. (or would have redundant allocations.)
49 vars: HashSet<Variable>,
50 stdin: BufReader<&'i mut dyn Read>,
51 stdout: &'o mut dyn Write,
52 system: &'c mut SystemCommand
53}
54
55impl Debug for Environment<'_, '_, '_> {
56 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
57 f.debug_struct("Environment")
58 .field("nvars", &self.vars.len())
59 .finish()
60 }
61}
62
63impl Default for Environment<'_, '_, '_> {
64 fn default() -> Self {
65 Self::new()
66 }
67}
68
69impl<'i, 'o, 'c> Environment<'i, 'o, 'c> {
70 /// Creates an empty [`Environment`].
71 ///
72 /// # Examples
73 /// ```rust
74 /// # use knightrs::Environment;
75 /// let env = Environment::new();
76 ///
77 /// // ...do stuff with `env`.
78 /// ```
79 #[must_use = "simply creating an environment doesn't do anything."]
80 pub fn new() -> Self {
81 Self::builder().build()
82 }
83
84 /// Creates a new [`Builder`].
85 ///
86 /// This is simply a helper function, and is provided so that you don't have to import [`Builder`].
87 ///
88 /// # Examples
89 /// ```rust
90 /// # use knightrs::Environment;
91 /// let env = Environment::builder().disable_system().build();
92 ///
93 /// // ... do stuff with `env`.
94 /// ```
95 #[must_use = "simply creating a builder does nothing."]
96 pub fn builder() -> Builder<'i, 'o, 'c> {
97 Builder::default()
98 }
99
100 /// Retrieves the variable with the given name.
101 ///
102 /// This method will always succeed; if this is the first time that `name` has been seen by `self`, a new (unassigned
103 /// ) variable will be created.
104 ///
105 /// # Examples
106 /// ```rust
107 /// # use knightrs::Environment;
108 /// let mut env = Environment::new();
109 /// let var = env.get("plato");
110 ///
111 /// assert_eq!(var, env.get("plato"));
112 /// ```
113 pub fn get<N: AsRef<str> + ToString + ?Sized>(&mut self, name: &N) -> Variable {
114 if let Some(inner) = self.vars.get(name.as_ref()) {
115 return inner.clone();
116 }
117
118 let variable = Variable::_new(name.to_string().into_boxed_str());
119
120 self.vars.insert(variable.clone());
121
122 variable
123 }
124
125 /// Executes `cmd` as a system command, returning the stdout of the child process.
126 ///
127 /// This will internally call the value that was set for [`Builder::system`]. See that function for more details
128 /// on, eg, the default value.
129 ///
130 /// # Examples
131 /// ```rust
132 /// # use knightrs::Environment;
133 /// let mut env = Environment::new();
134 ///
135 /// assert_eq!(env.system("echo 'hello, knight!'").unwrap().as_str(), "hello, knight!\n");
136 /// ```
137 pub fn system(&mut self, cmd: &str) -> Result<Text> {
138 (self.system)(cmd)
139 }
140
141 /// Runs the given string as Knight code, returning the result of its execution.
142 pub fn run_str<S: AsRef<str>>(&mut self, input: S) -> Result<Value> {
143 self.run(input.as_ref().chars())
144 }
145
146 /// Parses a [`Value`] from the given iterator and then runs the value.
147 pub fn run<I>(&mut self, input: I) -> Result<Value>
148 where
149 I: IntoIterator<Item=char>
150 {
151 Value::parse(input, self)?.run(self)
152 }
153}
154
155impl Read for Environment<'_, '_, '_> {
156 /// Read bytes into `data` from `self`'s `stdin`.
157 ///
158 /// The `stdin` can be customized at creation via [`Builder::stdin`].
159 #[inline]
160 fn read(&mut self, data: &mut [u8]) -> io::Result<usize> {
161 self.stdin.read(data)
162 }
163}
164
165impl BufRead for Environment<'_, '_, '_> {
166 fn fill_buf(&mut self) -> io::Result<&[u8]> {
167 self.stdin.fill_buf()
168 }
169
170 fn consume(&mut self, amnt: usize) {
171 self.stdin.consume(amnt)
172 }
173
174 fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
175 self.stdin.read_line(buf)
176 }
177}
178
179impl Write for Environment<'_, '_, '_> {
180 /// Writes `data`'s bytes into `self`'s `stdout`.
181 ///
182 /// The `stdin` can be customized at creation via [`Builder::stdin`].
183 #[inline]
184 fn write(&mut self, data: &[u8]) -> io::Result<usize> {
185 self.stdout.write(data)
186 }
187
188 #[inline]
189 fn flush(&mut self) -> io::Result<()> {
190 self.stdout.flush()
191 }
192}