Refactor loop and fix prompt

This commit is contained in:
NGnius (Graham) 2022-01-03 20:15:19 -05:00
parent bddb79d0ca
commit 5ef1b4a2b8
2 changed files with 71 additions and 65 deletions

View file

@ -6,12 +6,18 @@ use clap::{Parser};
pub struct CliArgs { pub struct CliArgs {
/// Script to run /// Script to run
pub file: Option<String>, pub file: Option<String>,
/// Generate m3u8 playlist /// Generate m3u8 playlist
#[clap(short, long)] #[clap(short, long)]
pub playlist: Option<String>, pub playlist: Option<String>,
/// In REPL mode, wait for all music in the queue to complete
#[clap(short, long)] /// In REPL mode, wait for all music in the queue to complete before accepting new input
#[clap(long)]
pub wait: bool, pub wait: bool,
/// In REPL mode, the prompt to display
#[clap(long, default_value_t = String::from("|"))]
pub prompt: String
} }
pub fn parse() -> CliArgs { pub fn parse() -> CliArgs {

View file

@ -1,27 +1,39 @@
//! Read, Execute, Print Loop functionality //! Read, Execute, Print Loop functionality
use std::io::{self, Write, Read}; use std::io::{self, Write, Read, Stdin};
use mps_interpreter::MpsRunner; use mps_interpreter::MpsRunner;
use mps_player::{MpsPlayer, MpsController}; use mps_player::{MpsPlayer, MpsController};
use super::cli::CliArgs; use super::cli::CliArgs;
use super::channel_io::channel_io; use super::channel_io::{channel_io, ChannelWriter};
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,
}
}
}
pub fn repl(args: CliArgs) { pub fn repl(args: CliArgs) {
let (mut writer, reader) = channel_io(); let (writer, reader) = channel_io();
let player_builder = move || { let player_builder = move || {
let runner = MpsRunner::with_stream(reader); let runner = MpsRunner::with_stream(reader);
let player = MpsPlayer::new(runner).unwrap(); let player = MpsPlayer::new(runner).unwrap();
player player
}; };
let stdin_t = io::stdin(); let mut state = ReplState::new(writer);
let mut stdin = stdin_t.lock();
let mut buf: Vec<u8> = Vec::new();
let mut read_buf = [0];
let mut current_line = 0;
// TODO: enable raw mode (char by char) reading of stdin
// TODO: generalize loop for better code reuse between playlist and playback mode
if let Some(playlist_file) = &args.playlist { if let Some(playlist_file) = &args.playlist {
println!("Playlist mode (output: `{}`)", playlist_file); println!("Playlist mode (output: `{}`)", playlist_file);
let mut player = player_builder(); let mut player = player_builder();
@ -29,69 +41,57 @@ pub fn repl(args: CliArgs) {
std::fs::File::create(playlist_file) std::fs::File::create(playlist_file)
.expect(&format!("Abort: Cannot create writeable file `{}`", playlist_file)) .expect(&format!("Abort: Cannot create writeable file `{}`", playlist_file))
); );
prompt(&mut current_line); read_loop(&args, &mut state, || {
loop { match player.save_m3u8(&mut playlist_writer) {
read_buf[0] = 0; Ok(_) => {},
while read_buf[0] == 0 { Err(e) => eprintln!("{}", e.message()),
stdin.read_exact(&mut read_buf).expect("Failed to read stdin");
} }
match read_buf[0] as char { playlist_writer.flush().expect("Failed to flush playlist to file");
';' => { });
buf.push(read_buf[0]);
writer.write(buf.as_slice()).expect("Failed to write to MPS interpreter");
match player.save_m3u8(&mut playlist_writer) {
Ok(_) => {},
Err(e) => eprintln!("{}", e.message()),
}
buf.clear();
playlist_writer.flush().expect("Failed to flush playlist to file");
prompt(&mut current_line);
},
/*
'\x27' => break, // ESC key
'\x03' => break, // Ctrl+C
*/
_ => buf.push(read_buf[0]),
}
}
} else { } else {
println!("Playback mode (output: audio device)"); println!("Playback mode (output: audio device)");
let ctrl = MpsController::create_repl(player_builder); let ctrl = MpsController::create_repl(player_builder);
prompt(&mut current_line); read_loop(&args, &mut state, || {
loop { if args.wait {
read_buf[0] = 0; match ctrl.wait_for_empty() {
while read_buf[0] == 0 { Ok(_) => {},
stdin.read_exact(&mut read_buf).expect("Failed to read stdin"); Err(e) => eprintln!("{}", e.message()),
} }
match read_buf[0] as char { } else {
';' => { for e in ctrl.check_ack() {
buf.push(read_buf[0]); eprintln!("{}", e.message());
writer.write(buf.as_slice()).expect("Failed to write to MPS interpreter"); }
//ctrl.play().expect("Failed to start playback");
if args.wait {
match ctrl.wait_for_empty() {
Ok(_) => {},
Err(e) => eprintln!("{}", e.message()),
}
} else {
for e in ctrl.check_ack() {
eprintln!("{}", e.message());
}
}
buf.clear();
prompt(&mut current_line);
},
'\x27' => break, // ESC key
'\x03' => break, // Ctrl+C
_ => buf.push(read_buf[0]),
} }
});
}
}
fn read_loop<F: FnMut()>(args: &CliArgs, state: &mut ReplState, mut execute: F) -> ! {
let mut read_buf: [u8;1] = [0];
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
state.stdin.read(&mut read_buf).expect("Failed to read stdin");
}
match read_buf[0] as char {
'\n' => {
state.statement_buf.push(read_buf[0]);
state.writer.write(state.statement_buf.as_slice())
.expect("Failed to write to MPS interpreter");
execute();
state.statement_buf.clear();
prompt(&mut state.line_number, args);
},
_ => state.statement_buf.push(read_buf[0]),
} }
} }
} }
#[inline(always)] #[inline(always)]
fn prompt(line: &mut usize) { fn prompt(line: &mut usize, args: &CliArgs) {
print!("{} |-> ", line); print!("{}{}", line, args.prompt);
*line += 1; *line += 1;
std::io::stdout().flush().expect("Failed to flush stdout"); std::io::stdout().flush().expect("Failed to flush stdout");
} }