use async_trait::async_trait;
use endbasic_core::console::{ClearType, Console, Key, Position};
use endbasic_core::program::{Metadata, Store};
use futures_lite::future::block_on;
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::collections::VecDeque;
use std::env;
use std::fs;
use std::io::{self, Write};
use std::process;
use std::rc::Rc;
#[derive(Default)]
struct IncompleteConsole {
buffer: VecDeque<Key>,
}
fn line_to_keys(s: String) -> VecDeque<Key> {
let mut keys = VecDeque::default();
for ch in s.chars() {
if ch == '\n' {
keys.push_back(Key::NewLine);
} else if ch == '\r' {
keys.push_back(Key::CarriageReturn);
} else {
keys.push_back(Key::Char(ch));
}
}
keys
}
#[async_trait(?Send)]
impl Console for IncompleteConsole {
fn clear(&mut self, _how: ClearType) -> io::Result<()> {
println!("IncompleteConsole::clear()");
Ok(())
}
fn color(&mut self, fg: Option<u8>, bg: Option<u8>) -> io::Result<()> {
println!("IncompleteConsole::color({:?}, {:?})", fg, bg);
Ok(())
}
fn enter_alt(&mut self) -> io::Result<()> {
println!("IncompleteConsole::enter_alt()");
Ok(())
}
fn hide_cursor(&mut self) -> io::Result<()> {
println!("IncompleteConsole::hide_cursor()");
Ok(())
}
fn is_interactive(&self) -> bool {
false
}
fn leave_alt(&mut self) -> io::Result<()> {
println!("IncompleteConsole::leave_alt()");
Ok(())
}
fn locate(&mut self, pos: Position) -> io::Result<()> {
println!("IncompleteConsole::locate({:?})", pos);
Ok(())
}
fn move_within_line(&mut self, off: i16) -> io::Result<()> {
println!("IncompleteConsole::move_within_line({})", off);
Ok(())
}
fn print(&mut self, text: &str) -> io::Result<()> {
println!("{}", text);
Ok(())
}
async fn read_key(&mut self) -> io::Result<Key> {
if self.buffer.is_empty() {
let mut line = String::new();
if io::stdin().read_line(&mut line)? == 0 {
return Ok(Key::Eof);
}
self.buffer = line_to_keys(line);
}
match self.buffer.pop_front() {
Some(key) => Ok(key),
None => Ok(Key::Eof),
}
}
fn show_cursor(&mut self) -> io::Result<()> {
println!("IncompleteConsole::show_cursor()");
Ok(())
}
fn size(&self) -> io::Result<Position> {
Ok(Position { row: 24, column: 80 })
}
fn write(&mut self, bytes: &[u8]) -> io::Result<()> {
let stdout = io::stdout();
let mut stdout = stdout.lock();
stdout.write_all(bytes)?;
stdout.flush()
}
}
#[derive(Default)]
pub(crate) struct IncompleteStore {}
impl Store for IncompleteStore {
fn delete(&mut self, name: &str) -> io::Result<()> {
println!("IncompleteStore::delete({})", name);
Ok(())
}
fn enumerate(&self) -> io::Result<BTreeMap<String, Metadata>> {
println!("IncompleteStore::enumerate()");
Ok(BTreeMap::default())
}
fn get(&self, name: &str) -> io::Result<String> {
println!("IncompleteStore::get({})", name);
Ok("".to_owned())
}
fn put(&mut self, name: &str, content: &str) -> io::Result<()> {
println!("IncompleteStore::put({}, {})", name, content);
Ok(())
}
}
fn main() {
let args: Vec<String> = env::args().collect();
let path = match args.as_slice() {
[_, path] => path,
_ => {
eprintln!("Usage error: expected a file name");
process::exit(1);
}
};
let console = Rc::from(RefCell::from(IncompleteConsole::default()));
let store = IncompleteStore::default();
let store = Rc::from(RefCell::from(endbasic_core::program::DemoStoreOverlay::new(store)));
let mut machine = {
let mut builder = endbasic_core::exec::MachineBuilder::default()
.add_commands(endbasic_core::console::all_commands(console.clone()))
.add_commands(endbasic_core::program::all_commands(console, store))
.add_functions(endbasic_core::strings::all_functions());
builder = endbasic_core::numerics::add_all(builder);
builder.build()
};
let mut input = match fs::File::open(path) {
Ok(file) => file,
Err(e) => {
eprintln!("ERROR: {}", e);
process::exit(1);
}
};
match block_on(machine.exec(&mut input)) {
Ok(()) => (),
Err(e) => {
eprintln!("ERROR: {}", e);
process::exit(1);
}
}
}