use crate::{Error, Result, Value, Text};
use std::collections::HashSet;
use std::fmt::{self, Debug, Formatter};
use std::io::{self, Write, Read, BufRead, BufReader};
mod builder;
mod variable;
pub use builder::Builder;
pub use variable::Variable;
type SystemCommand = dyn FnMut(&str) -> Result<Text>;
pub struct Environment<'i, 'o, 'c> {
vars: HashSet<Variable>,
stdin: BufReader<&'i mut dyn Read>,
stdout: &'o mut dyn Write,
system: &'c mut SystemCommand
}
impl Debug for Environment<'_, '_, '_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("Environment")
.field("nvars", &self.vars.len())
.finish()
}
}
impl Default for Environment<'_, '_, '_> {
fn default() -> Self {
Self::new()
}
}
impl<'i, 'o, 'c> Environment<'i, 'o, 'c> {
#[must_use = "simply creating an environment doesn't do anything."]
pub fn new() -> Self {
Self::builder().build()
}
#[must_use = "simply creating a builder does nothing."]
pub fn builder() -> Builder<'i, 'o, 'c> {
Builder::default()
}
pub fn get<N: AsRef<str> + ToString + ?Sized>(&mut self, name: &N) -> Variable {
if let Some(inner) = self.vars.get(name.as_ref()) {
return inner.clone();
}
let variable = Variable::_new(name.to_string().into_boxed_str());
self.vars.insert(variable.clone());
variable
}
pub fn system(&mut self, cmd: &str) -> Result<Text> {
(self.system)(cmd)
}
pub fn run_str<S: AsRef<str>>(&mut self, input: S) -> Result<Value> {
self.run(input.as_ref().chars())
}
pub fn run<I>(&mut self, input: I) -> Result<Value>
where
I: IntoIterator<Item=char>
{
Value::parse(input, self)?.run(self)
}
}
impl Read for Environment<'_, '_, '_> {
#[inline]
fn read(&mut self, data: &mut [u8]) -> io::Result<usize> {
self.stdin.read(data)
}
}
impl BufRead for Environment<'_, '_, '_> {
fn fill_buf(&mut self) -> io::Result<&[u8]> {
self.stdin.fill_buf()
}
fn consume(&mut self, amnt: usize) {
self.stdin.consume(amnt)
}
fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
self.stdin.read_line(buf)
}
}
impl Write for Environment<'_, '_, '_> {
#[inline]
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
self.stdout.write(data)
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
self.stdout.flush()
}
}