Refactor loop and fix prompt
This commit is contained in:
parent
bddb79d0ca
commit
5ef1b4a2b8
2 changed files with 71 additions and 65 deletions
10
src/cli.rs
10
src/cli.rs
|
@ -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 {
|
||||||
|
|
98
src/repl.rs
98
src/repl.rs
|
@ -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,45 +41,17 @@ 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 {
|
|
||||||
read_buf[0] = 0;
|
|
||||||
while read_buf[0] == 0 {
|
|
||||||
stdin.read_exact(&mut read_buf).expect("Failed to read stdin");
|
|
||||||
}
|
|
||||||
match read_buf[0] as char {
|
|
||||||
';' => {
|
|
||||||
buf.push(read_buf[0]);
|
|
||||||
writer.write(buf.as_slice()).expect("Failed to write to MPS interpreter");
|
|
||||||
match player.save_m3u8(&mut playlist_writer) {
|
match player.save_m3u8(&mut playlist_writer) {
|
||||||
Ok(_) => {},
|
Ok(_) => {},
|
||||||
Err(e) => eprintln!("{}", e.message()),
|
Err(e) => eprintln!("{}", e.message()),
|
||||||
}
|
}
|
||||||
buf.clear();
|
|
||||||
playlist_writer.flush().expect("Failed to flush playlist to file");
|
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 {
|
|
||||||
read_buf[0] = 0;
|
|
||||||
while read_buf[0] == 0 {
|
|
||||||
stdin.read_exact(&mut read_buf).expect("Failed to read stdin");
|
|
||||||
}
|
|
||||||
match read_buf[0] as char {
|
|
||||||
';' => {
|
|
||||||
buf.push(read_buf[0]);
|
|
||||||
writer.write(buf.as_slice()).expect("Failed to write to MPS interpreter");
|
|
||||||
//ctrl.play().expect("Failed to start playback");
|
|
||||||
if args.wait {
|
if args.wait {
|
||||||
match ctrl.wait_for_empty() {
|
match ctrl.wait_for_empty() {
|
||||||
Ok(_) => {},
|
Ok(_) => {},
|
||||||
|
@ -78,20 +62,36 @@ pub fn repl(args: CliArgs) {
|
||||||
eprintln!("{}", e.message());
|
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");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue