fift 0.2.2

Rust implementation of the Fift esoteric language
Documentation
use std::io::BufRead;

pub trait Environment {
    fn now_ms(&self) -> u64;

    fn get_env(&self, name: &str) -> Option<String>;

    fn file_exists(&self, name: &str) -> bool;

    fn write_file(&mut self, name: &str, contents: &[u8]) -> std::io::Result<()>;

    fn read_file(&mut self, name: &str) -> std::io::Result<Vec<u8>>;

    fn read_file_part(&mut self, name: &str, offset: u64, len: u64) -> std::io::Result<Vec<u8>>;

    fn include(&self, name: &str) -> std::io::Result<SourceBlock>;
}

pub struct SourceBlock {
    name: String,
    buffer: Box<dyn BufRead>,
}

impl SourceBlock {
    pub fn new<N: Into<String>, B: BufRead + 'static>(name: N, buffer: B) -> Self {
        Self {
            name: name.into(),
            buffer: Box::new(buffer),
        }
    }

    pub fn name(&self) -> &str {
        &self.name
    }

    pub fn buffer_mut(&mut self) -> &mut dyn BufRead {
        &mut self.buffer
    }
}

pub struct EmptyEnvironment;

impl Environment for EmptyEnvironment {
    fn now_ms(&self) -> u64 {
        0
    }

    fn get_env(&self, _: &str) -> Option<String> {
        None
    }

    fn file_exists(&self, _: &str) -> bool {
        false
    }

    fn write_file(&mut self, _: &str, _: &[u8]) -> std::io::Result<()> {
        Ok(())
    }

    fn read_file(&mut self, name: &str) -> std::io::Result<Vec<u8>> {
        Err(not_found(name))
    }

    fn read_file_part(&mut self, name: &str, _: u64, _: u64) -> std::io::Result<Vec<u8>> {
        self.read_file(name)
    }

    fn include(&self, name: &str) -> std::io::Result<SourceBlock> {
        Err(not_found(name))
    }
}

fn not_found(name: &str) -> std::io::Error {
    std::io::Error::new(
        std::io::ErrorKind::NotFound,
        format!("`{name}` file not found"),
    )
}