2022-01-03 22:53:57 +00:00
|
|
|
//! Read, Execute, Print Loop functionality
|
|
|
|
|
2022-01-04 02:15:28 +00:00
|
|
|
use std::io::{self, Read, Stdin, Write};
|
2022-01-03 22:53:57 +00:00
|
|
|
|
|
|
|
use mps_interpreter::MpsRunner;
|
2022-01-04 02:15:28 +00:00
|
|
|
use mps_player::{MpsController, MpsPlayer};
|
2022-01-03 22:53:57 +00:00
|
|
|
|
2022-01-04 01:15:19 +00:00
|
|
|
use super::channel_io::{channel_io, ChannelWriter};
|
2022-01-04 02:15:28 +00:00
|
|
|
use super::cli::CliArgs;
|
2022-01-04 01:15:19 +00:00
|
|
|
|
|
|
|
struct ReplState {
|
|
|
|
stdin: Stdin,
|
|
|
|
line_number: usize,
|
|
|
|
statement_buf: Vec<u8>,
|
|
|
|
writer: ChannelWriter,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ReplState {
|
|
|
|
fn new(chan_writer: ChannelWriter) -> Self {
|
|
|
|
Self {
|
|
|
|
stdin: io::stdin(),
|
|
|
|
line_number: 0,
|
|
|
|
statement_buf: Vec::new(),
|
|
|
|
writer: chan_writer,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-01-03 22:53:57 +00:00
|
|
|
|
|
|
|
pub fn repl(args: CliArgs) {
|
2022-01-04 01:15:19 +00:00
|
|
|
let (writer, reader) = channel_io();
|
2022-01-03 22:53:57 +00:00
|
|
|
let player_builder = move || {
|
|
|
|
let runner = MpsRunner::with_stream(reader);
|
|
|
|
let player = MpsPlayer::new(runner).unwrap();
|
|
|
|
player
|
|
|
|
};
|
2022-01-04 01:15:19 +00:00
|
|
|
let mut state = ReplState::new(writer);
|
2022-01-03 22:53:57 +00:00
|
|
|
if let Some(playlist_file) = &args.playlist {
|
|
|
|
println!("Playlist mode (output: `{}`)", playlist_file);
|
|
|
|
let mut player = player_builder();
|
2022-01-04 02:15:28 +00:00
|
|
|
let mut playlist_writer = io::BufWriter::new(std::fs::File::create(playlist_file).expect(
|
|
|
|
&format!("Abort: Cannot create writeable file `{}`", playlist_file),
|
|
|
|
));
|
2022-01-04 01:15:19 +00:00
|
|
|
read_loop(&args, &mut state, || {
|
|
|
|
match player.save_m3u8(&mut playlist_writer) {
|
2022-01-04 02:15:28 +00:00
|
|
|
Ok(_) => {}
|
2022-01-04 18:00:05 +00:00
|
|
|
Err(e) => {
|
|
|
|
eprintln!("{}", e.message());
|
|
|
|
// consume any further errors (this shouldn't actually write anything)
|
|
|
|
while let Err(e) = player.save_m3u8(&mut playlist_writer) {
|
|
|
|
eprintln!("{}", e.message());
|
|
|
|
}
|
|
|
|
},
|
2022-01-03 22:53:57 +00:00
|
|
|
}
|
2022-01-04 02:15:28 +00:00
|
|
|
playlist_writer
|
|
|
|
.flush()
|
|
|
|
.expect("Failed to flush playlist to file");
|
2022-01-04 01:15:19 +00:00
|
|
|
});
|
2022-01-03 22:53:57 +00:00
|
|
|
} else {
|
|
|
|
println!("Playback mode (output: audio device)");
|
|
|
|
let ctrl = MpsController::create_repl(player_builder);
|
2022-01-04 01:15:19 +00:00
|
|
|
read_loop(&args, &mut state, || {
|
|
|
|
if args.wait {
|
|
|
|
match ctrl.wait_for_empty() {
|
2022-01-04 02:15:28 +00:00
|
|
|
Ok(_) => {}
|
2022-01-04 01:15:19 +00:00
|
|
|
Err(e) => eprintln!("{}", e.message()),
|
|
|
|
}
|
|
|
|
} else {
|
2022-01-04 18:00:05 +00:00
|
|
|
// consume all incoming errors
|
|
|
|
let mut had_err = true;
|
|
|
|
while had_err {
|
|
|
|
let mut new_had_err = false;
|
|
|
|
for e in ctrl.check_ack() {
|
|
|
|
eprintln!("{}", e.message());
|
|
|
|
new_had_err = true;
|
|
|
|
}
|
|
|
|
had_err = new_had_err;
|
2022-01-04 01:15:19 +00:00
|
|
|
}
|
2022-01-03 22:53:57 +00:00
|
|
|
}
|
2022-01-04 01:15:19 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn read_loop<F: FnMut()>(args: &CliArgs, state: &mut ReplState, mut execute: F) -> ! {
|
2022-01-04 02:15:28 +00:00
|
|
|
let mut read_buf: [u8; 1] = [0];
|
2022-01-04 01:15:19 +00:00
|
|
|
prompt(&mut state.line_number, args);
|
|
|
|
loop {
|
|
|
|
read_buf[0] = 0;
|
|
|
|
while read_buf[0] == 0 {
|
|
|
|
// TODO: enable raw mode (char by char) reading of stdin
|
2022-01-04 02:15:28 +00:00
|
|
|
state
|
|
|
|
.stdin
|
|
|
|
.read(&mut read_buf)
|
|
|
|
.expect("Failed to read stdin");
|
2022-01-04 01:15:19 +00:00
|
|
|
}
|
|
|
|
match read_buf[0] as char {
|
|
|
|
'\n' => {
|
|
|
|
state.statement_buf.push(read_buf[0]);
|
2022-01-04 02:15:28 +00:00
|
|
|
let statement_result = std::str::from_utf8(state.statement_buf.as_slice());
|
|
|
|
if statement_result.is_ok() && statement_result.unwrap().starts_with("?") {
|
|
|
|
repl_commands(statement_result.unwrap());
|
|
|
|
} else {
|
|
|
|
state
|
|
|
|
.writer
|
|
|
|
.write(state.statement_buf.as_slice())
|
|
|
|
.expect("Failed to write to MPS interpreter");
|
|
|
|
execute();
|
|
|
|
}
|
2022-01-04 01:15:19 +00:00
|
|
|
state.statement_buf.clear();
|
|
|
|
prompt(&mut state.line_number, args);
|
2022-01-04 02:15:28 +00:00
|
|
|
}
|
2022-01-04 01:15:19 +00:00
|
|
|
_ => state.statement_buf.push(read_buf[0]),
|
2022-01-03 22:53:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline(always)]
|
2022-01-04 01:15:19 +00:00
|
|
|
fn prompt(line: &mut usize, args: &CliArgs) {
|
|
|
|
print!("{}{}", line, args.prompt);
|
2022-01-03 22:53:57 +00:00
|
|
|
*line += 1;
|
|
|
|
std::io::stdout().flush().expect("Failed to flush stdout");
|
|
|
|
}
|
2022-01-04 02:15:28 +00:00
|
|
|
|
|
|
|
fn repl_commands(command_str: &str) {
|
|
|
|
let words: Vec<&str> = command_str.split(" ").map(|s| s.trim()).collect();
|
|
|
|
match words[0] {
|
2022-01-04 02:22:22 +00:00
|
|
|
"?help" => println!("{}", super::help::HELP_STRING),
|
|
|
|
"?function" | "?functions" => println!("{}", super::help::FUNCTIONS),
|
|
|
|
"?filter" | "?filters" => println!("{}", super::help::FILTERS),
|
2022-01-04 02:15:28 +00:00
|
|
|
_ => println!("Unknown command, try ?help"),
|
|
|
|
}
|
|
|
|
}
|