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}