Run cargo fmt on schala-repl code

This commit is contained in:
Greg Shuflin 2021-10-07 01:19:35 -07:00
parent 77bf42be6c
commit c9a4c83fce
9 changed files with 799 additions and 655 deletions

View File

@ -1,80 +1,76 @@
use std::time;
use std::collections::HashSet; use std::collections::HashSet;
use std::time;
pub trait ProgrammingLanguageInterface { pub trait ProgrammingLanguageInterface {
fn get_language_name(&self) -> String; fn get_language_name(&self) -> String;
fn get_source_file_suffix(&self) -> String; fn get_source_file_suffix(&self) -> String;
fn run_computation(&mut self, _request: ComputationRequest) -> ComputationResponse { fn run_computation(&mut self, _request: ComputationRequest) -> ComputationResponse {
ComputationResponse { ComputationResponse {
main_output: Err(format!("Computation pipeline not implemented")), main_output: Err(format!("Computation pipeline not implemented")),
global_output_stats: GlobalOutputStats::default(), global_output_stats: GlobalOutputStats::default(),
debug_responses: vec![], debug_responses: vec![],
}
} }
}
fn request_meta(&mut self, _request: LangMetaRequest) -> LangMetaResponse { fn request_meta(&mut self, _request: LangMetaRequest) -> LangMetaResponse {
LangMetaResponse::Custom { kind: format!("not-implemented"), value: format!("") } LangMetaResponse::Custom {
} kind: format!("not-implemented"),
value: format!(""),
}
}
} }
pub struct ComputationRequest<'a> { pub struct ComputationRequest<'a> {
pub source: &'a str, pub source: &'a str,
pub debug_requests: HashSet<DebugAsk>, pub debug_requests: HashSet<DebugAsk>,
} }
pub struct ComputationResponse { pub struct ComputationResponse {
pub main_output: Result<String, String>, pub main_output: Result<String, String>,
pub global_output_stats: GlobalOutputStats, pub global_output_stats: GlobalOutputStats,
pub debug_responses: Vec<DebugResponse>, pub debug_responses: Vec<DebugResponse>,
} }
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub struct GlobalOutputStats { pub struct GlobalOutputStats {
pub total_duration: time::Duration, pub total_duration: time::Duration,
pub stage_durations: Vec<(String, time::Duration)> pub stage_durations: Vec<(String, time::Duration)>,
} }
#[derive(Debug, Clone, Hash, Eq, PartialEq, Deserialize, Serialize)] #[derive(Debug, Clone, Hash, Eq, PartialEq, Deserialize, Serialize)]
pub enum DebugAsk { pub enum DebugAsk {
Timing, Timing,
ByStage { stage_name: String, token: Option<String> }, ByStage {
stage_name: String,
token: Option<String>,
},
} }
impl DebugAsk { impl DebugAsk {
pub fn is_for_stage(&self, name: &str) -> bool { pub fn is_for_stage(&self, name: &str) -> bool {
match self { match self {
DebugAsk::ByStage { stage_name, .. } if stage_name == name => true, DebugAsk::ByStage { stage_name, .. } if stage_name == name => true,
_ => false _ => false,
}
} }
}
} }
pub struct DebugResponse { pub struct DebugResponse {
pub ask: DebugAsk, pub ask: DebugAsk,
pub value: String pub value: String,
} }
pub enum LangMetaRequest { pub enum LangMetaRequest {
StageNames, StageNames,
Docs { Docs { source: String },
source: String, Custom { kind: String, value: String },
}, ImmediateDebug(DebugAsk),
Custom {
kind: String,
value: String
},
ImmediateDebug(DebugAsk),
} }
pub enum LangMetaResponse { pub enum LangMetaResponse {
StageNames(Vec<String>), StageNames(Vec<String>),
Docs { Docs { doc_string: String },
doc_string: String, Custom { kind: String, value: String },
}, ImmediateDebug(DebugResponse),
Custom {
kind: String,
value: String
},
ImmediateDebug(DebugResponse),
} }

View File

@ -1,91 +1,95 @@
#![feature(box_patterns, box_syntax, proc_macro_hygiene, decl_macro)] #![feature(box_patterns, box_syntax, proc_macro_hygiene, decl_macro)]
#![feature(plugin)] #![feature(plugin)]
extern crate getopts;
extern crate linefeed;
extern crate itertools;
extern crate colored; extern crate colored;
extern crate getopts;
extern crate itertools;
extern crate linefeed;
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
extern crate serde_json;
extern crate includedir; extern crate includedir;
extern crate phf; extern crate phf;
extern crate serde_json;
use std::collections::HashSet; use std::collections::HashSet;
use std::path::Path;
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
use std::path::Path;
use std::process::exit; use std::process::exit;
mod repl;
mod language; mod language;
mod repl;
pub use language::{ProgrammingLanguageInterface, pub use language::{
ComputationRequest, ComputationResponse, ComputationRequest, ComputationResponse, DebugAsk, DebugResponse, GlobalOutputStats,
LangMetaRequest, LangMetaResponse, LangMetaRequest, LangMetaResponse, ProgrammingLanguageInterface,
DebugResponse, DebugAsk, GlobalOutputStats}; };
include!(concat!(env!("OUT_DIR"), "/static.rs")); include!(concat!(env!("OUT_DIR"), "/static.rs"));
const VERSION_STRING: &'static str = "0.1.0"; const VERSION_STRING: &'static str = "0.1.0";
pub fn start_repl(langs: Vec<Box<dyn ProgrammingLanguageInterface>>) { pub fn start_repl(langs: Vec<Box<dyn ProgrammingLanguageInterface>>) {
let options = command_line_options().parse(std::env::args()).unwrap_or_else(|e| { let options = command_line_options()
println!("{:?}", e); .parse(std::env::args())
exit(1); .unwrap_or_else(|e| {
}); println!("{:?}", e);
exit(1);
});
if options.opt_present("help") { if options.opt_present("help") {
println!("{}", command_line_options().usage("Schala metainterpreter")); println!("{}", command_line_options().usage("Schala metainterpreter"));
exit(0); exit(0);
} }
match options.free[..] { match options.free[..] {
[] | [_] => { [] | [_] => {
let mut repl = repl::Repl::new(langs); let mut repl = repl::Repl::new(langs);
repl.run_repl(); repl.run_repl();
} }
[_, ref filename, ..] => { [_, ref filename, ..] => {
run_noninteractive(filename, langs); run_noninteractive(filename, langs);
} }
}; };
} }
fn run_noninteractive(filename: &str, languages: Vec<Box<dyn ProgrammingLanguageInterface>>) { fn run_noninteractive(filename: &str, languages: Vec<Box<dyn ProgrammingLanguageInterface>>) {
let path = Path::new(filename); let path = Path::new(filename);
let ext = path.extension().and_then(|e| e.to_str()).unwrap_or_else(|| { let ext = path
println!("Source file lacks extension"); .extension()
exit(1); .and_then(|e| e.to_str())
}); .unwrap_or_else(|| {
let mut language = Box::new(languages.into_iter().find(|lang| lang.get_source_file_suffix() == ext) println!("Source file lacks extension");
.unwrap_or_else(|| { exit(1);
println!("Extension .{} not recognized", ext); });
exit(1); let mut language = Box::new(
})); languages
.into_iter()
.find(|lang| lang.get_source_file_suffix() == ext)
.unwrap_or_else(|| {
println!("Extension .{} not recognized", ext);
exit(1);
}),
);
let mut source_file = File::open(path).unwrap(); let mut source_file = File::open(path).unwrap();
let mut buffer = String::new(); let mut buffer = String::new();
source_file.read_to_string(&mut buffer).unwrap(); source_file.read_to_string(&mut buffer).unwrap();
let request = ComputationRequest { let request = ComputationRequest {
source: &buffer, source: &buffer,
debug_requests: HashSet::new(), debug_requests: HashSet::new(),
}; };
let response = language.run_computation(request); let response = language.run_computation(request);
match response.main_output { match response.main_output {
Ok(s) => println!("{}", s), Ok(s) => println!("{}", s),
Err(s) => println!("{}", s) Err(s) => println!("{}", s),
}; };
} }
fn command_line_options() -> getopts::Options { fn command_line_options() -> getopts::Options {
let mut options = getopts::Options::new(); let mut options = getopts::Options::new();
options.optflag("h", options.optflag("h", "help", "Show help text");
"help", options.optflag("w", "webapp", "Start up web interpreter");
"Show help text"); options
options.optflag("w",
"webapp",
"Start up web interpreter");
options
} }

View File

@ -1,4 +1,4 @@
use super::{Repl, InterpreterDirectiveOutput}; use super::{InterpreterDirectiveOutput, Repl};
use crate::repl::directive_actions::DirectiveAction; use crate::repl::directive_actions::DirectiveAction;
use colored::*; use colored::*;
@ -7,93 +7,116 @@ use colored::*;
/// and then execute it with any remaining arguments /// and then execute it with any remaining arguments
#[derive(Clone)] #[derive(Clone)]
pub enum CommandTree { pub enum CommandTree {
Terminal { Terminal {
name: String, name: String,
children: Vec<CommandTree>, children: Vec<CommandTree>,
help_msg: Option<String>, help_msg: Option<String>,
action: DirectiveAction, action: DirectiveAction,
}, },
NonTerminal { NonTerminal {
name: String, name: String,
children: Vec<CommandTree>, children: Vec<CommandTree>,
help_msg: Option<String>, help_msg: Option<String>,
action: DirectiveAction, action: DirectiveAction,
}, },
Top(Vec<CommandTree>), Top(Vec<CommandTree>),
} }
impl CommandTree { impl CommandTree {
pub fn nonterm_no_further_tab_completions(s: &str, help: Option<&str>) -> CommandTree { pub fn nonterm_no_further_tab_completions(s: &str, help: Option<&str>) -> CommandTree {
CommandTree::NonTerminal {name: s.to_string(), help_msg: help.map(|x| x.to_string()), children: vec![], action: DirectiveAction::Null } CommandTree::NonTerminal {
} name: s.to_string(),
help_msg: help.map(|x| x.to_string()),
pub fn terminal(s: &str, help: Option<&str>, children: Vec<CommandTree>, action: DirectiveAction) -> CommandTree { children: vec![],
CommandTree::Terminal {name: s.to_string(), help_msg: help.map(|x| x.to_string()), children, action} action: DirectiveAction::Null,
} }
pub fn nonterm(s: &str, help: Option<&str>, children: Vec<CommandTree>) -> CommandTree {
CommandTree::NonTerminal {
name: s.to_string(),
help_msg: help.map(|x| x.to_string()),
children,
action: DirectiveAction::Null
} }
}
pub fn get_cmd(&self) -> &str { pub fn terminal(
match self { s: &str,
CommandTree::Terminal { name, .. } => name.as_str(), help: Option<&str>,
CommandTree::NonTerminal {name, ..} => name.as_str(), children: Vec<CommandTree>,
CommandTree::Top(_) => "", action: DirectiveAction,
) -> CommandTree {
CommandTree::Terminal {
name: s.to_string(),
help_msg: help.map(|x| x.to_string()),
children,
action,
}
} }
}
pub fn get_help(&self) -> &str {
match self {
CommandTree::Terminal { help_msg, ..} => help_msg.as_ref().map(|s| s.as_str()).unwrap_or("<no help text provided>"),
CommandTree::NonTerminal { help_msg, .. } => help_msg.as_ref().map(|s| s.as_str()).unwrap_or("<no help text provided>"),
CommandTree::Top(_) => ""
}
}
pub fn get_children(&self) -> &Vec<CommandTree> {
use CommandTree::*;
match self {
Terminal { children, .. } |
NonTerminal { children, .. } |
Top(children) => children
}
}
pub fn get_subcommands(&self) -> Vec<&str> {
self.get_children().iter().map(|x| x.get_cmd()).collect()
}
pub fn perform(&self, repl: &mut Repl, arguments: &Vec<&str>) -> InterpreterDirectiveOutput { pub fn nonterm(s: &str, help: Option<&str>, children: Vec<CommandTree>) -> CommandTree {
let mut dir_pointer: &CommandTree = self; CommandTree::NonTerminal {
let mut idx = 0; name: s.to_string(),
help_msg: help.map(|x| x.to_string()),
let res: Result<(DirectiveAction, usize), String> = loop { children,
match dir_pointer { action: DirectiveAction::Null,
CommandTree::Top(subcommands) | CommandTree::NonTerminal { children: subcommands, .. } => { }
let next_command = match arguments.get(idx) { }
Some(cmd) => cmd,
None => break Err(format!("Command requires arguments")) pub fn get_cmd(&self) -> &str {
}; match self {
idx += 1; CommandTree::Terminal { name, .. } => name.as_str(),
match subcommands.iter().find(|sc| sc.get_cmd() == *next_command) { CommandTree::NonTerminal { name, .. } => name.as_str(),
Some(command_tree) => { CommandTree::Top(_) => "",
dir_pointer = command_tree; }
}, }
None => break Err(format!("Command {} not found", next_command)) pub fn get_help(&self) -> &str {
}; match self {
}, CommandTree::Terminal { help_msg, .. } => help_msg
CommandTree::Terminal { action, .. } => { .as_ref()
break Ok((action.clone(), idx)); .map(|s| s.as_str())
}, .unwrap_or("<no help text provided>"),
} CommandTree::NonTerminal { help_msg, .. } => help_msg
}; .as_ref()
.map(|s| s.as_str())
match res { .unwrap_or("<no help text provided>"),
Ok((action, idx)) => action.perform(repl, &arguments[idx..]), CommandTree::Top(_) => "",
Err(err) => Some(err.red().to_string()) }
}
pub fn get_children(&self) -> &Vec<CommandTree> {
use CommandTree::*;
match self {
Terminal { children, .. } | NonTerminal { children, .. } | Top(children) => children,
}
}
pub fn get_subcommands(&self) -> Vec<&str> {
self.get_children().iter().map(|x| x.get_cmd()).collect()
}
pub fn perform(&self, repl: &mut Repl, arguments: &Vec<&str>) -> InterpreterDirectiveOutput {
let mut dir_pointer: &CommandTree = self;
let mut idx = 0;
let res: Result<(DirectiveAction, usize), String> = loop {
match dir_pointer {
CommandTree::Top(subcommands)
| CommandTree::NonTerminal {
children: subcommands,
..
} => {
let next_command = match arguments.get(idx) {
Some(cmd) => cmd,
None => break Err(format!("Command requires arguments")),
};
idx += 1;
match subcommands.iter().find(|sc| sc.get_cmd() == *next_command) {
Some(command_tree) => {
dir_pointer = command_tree;
}
None => break Err(format!("Command {} not found", next_command)),
};
}
CommandTree::Terminal { action, .. } => {
break Ok((action.clone(), idx));
}
}
};
match res {
Ok((action, idx)) => action.perform(repl, &arguments[idx..]),
Err(err) => Some(err.red().to_string()),
}
} }
}
} }

View File

@ -1,132 +1,145 @@
use super::{Repl, InterpreterDirectiveOutput}; use super::{InterpreterDirectiveOutput, Repl};
use crate::language::{DebugAsk, DebugResponse, LangMetaRequest, LangMetaResponse};
use crate::repl::help::help; use crate::repl::help::help;
use crate::language::{LangMetaRequest, LangMetaResponse, DebugAsk, DebugResponse};
use std::fmt::Write as FmtWrite; use std::fmt::Write as FmtWrite;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum DirectiveAction { pub enum DirectiveAction {
Null, Null,
Help, Help,
QuitProgram, QuitProgram,
ListPasses, ListPasses,
ShowImmediate, ShowImmediate,
Show, Show,
Hide, Hide,
TotalTimeOff, TotalTimeOff,
TotalTimeOn, TotalTimeOn,
StageTimeOff, StageTimeOff,
StageTimeOn, StageTimeOn,
Doc, Doc,
} }
impl DirectiveAction { impl DirectiveAction {
pub fn perform(&self, repl: &mut Repl, arguments: &[&str]) -> InterpreterDirectiveOutput { pub fn perform(&self, repl: &mut Repl, arguments: &[&str]) -> InterpreterDirectiveOutput {
use DirectiveAction::*; use DirectiveAction::*;
match self { match self {
Null => None, Null => None,
Help => help(repl, arguments), Help => help(repl, arguments),
QuitProgram => { QuitProgram => {
repl.save_before_exit(); repl.save_before_exit();
::std::process::exit(0) ::std::process::exit(0)
}, }
ListPasses => { ListPasses => {
let language_state = repl.get_cur_language_state(); let language_state = repl.get_cur_language_state();
let pass_names = match language_state.request_meta(LangMetaRequest::StageNames) { let pass_names = match language_state.request_meta(LangMetaRequest::StageNames) {
LangMetaResponse::StageNames(names) => names, LangMetaResponse::StageNames(names) => names,
_ => vec![], _ => vec![],
}; };
let mut buf = String::new(); let mut buf = String::new();
for pass in pass_names.iter().map(|name| Some(name)).intersperse(None) { for pass in pass_names.iter().map(|name| Some(name)).intersperse(None) {
match pass { match pass {
Some(pass) => write!(buf, "{}", pass).unwrap(), Some(pass) => write!(buf, "{}", pass).unwrap(),
None => write!(buf, " -> ").unwrap(), None => write!(buf, " -> ").unwrap(),
} }
}
Some(buf)
}
ShowImmediate => {
let cur_state = repl.get_cur_language_state();
let stage_name = match arguments.get(0) {
Some(s) => s.to_string(),
None => return Some(format!("Must specify a thing to debug")),
};
let meta = LangMetaRequest::ImmediateDebug(DebugAsk::ByStage {
stage_name: stage_name.clone(),
token: None,
});
let meta_response = cur_state.request_meta(meta);
let response = match meta_response {
LangMetaResponse::ImmediateDebug(DebugResponse { ask, value }) => match ask {
DebugAsk::ByStage {
stage_name: ref this_stage_name,
..
} if *this_stage_name == stage_name => value,
_ => return Some(format!("Wrong debug stage")),
},
_ => return Some(format!("Invalid language meta response")),
};
Some(response)
}
Show => {
let this_stage_name = match arguments.get(0) {
Some(s) => s.to_string(),
None => return Some(format!("Must specify a stage to show")),
};
let token = arguments.get(1).map(|s| s.to_string());
repl.options.debug_asks.retain(|ask| match ask {
DebugAsk::ByStage { stage_name, .. } if *stage_name == this_stage_name => false,
_ => true,
});
let ask = DebugAsk::ByStage {
stage_name: this_stage_name,
token,
};
repl.options.debug_asks.insert(ask);
None
}
Hide => {
let stage_name_to_remove = match arguments.get(0) {
Some(s) => s.to_string(),
None => return Some(format!("Must specify a stage to hide")),
};
repl.options.debug_asks.retain(|ask| match ask {
DebugAsk::ByStage { stage_name, .. } if *stage_name == stage_name_to_remove => {
false
}
_ => true,
});
None
}
TotalTimeOff => total_time_off(repl, arguments),
TotalTimeOn => total_time_on(repl, arguments),
StageTimeOff => stage_time_off(repl, arguments),
StageTimeOn => stage_time_on(repl, arguments),
Doc => doc(repl, arguments),
} }
Some(buf)
},
ShowImmediate => {
let cur_state = repl.get_cur_language_state();
let stage_name = match arguments.get(0) {
Some(s) => s.to_string(),
None => return Some(format!("Must specify a thing to debug")),
};
let meta = LangMetaRequest::ImmediateDebug(DebugAsk::ByStage { stage_name: stage_name.clone(), token: None });
let meta_response = cur_state.request_meta(meta);
let response = match meta_response {
LangMetaResponse::ImmediateDebug(DebugResponse { ask, value }) => match ask {
DebugAsk::ByStage { stage_name: ref this_stage_name, ..} if *this_stage_name == stage_name => value,
_ => return Some(format!("Wrong debug stage"))
},
_ => return Some(format!("Invalid language meta response")),
};
Some(response)
},
Show => {
let this_stage_name = match arguments.get(0) {
Some(s) => s.to_string(),
None => return Some(format!("Must specify a stage to show")),
};
let token = arguments.get(1).map(|s| s.to_string());
repl.options.debug_asks.retain(|ask| match ask {
DebugAsk::ByStage { stage_name, .. } if *stage_name == this_stage_name => false,
_ => true
});
let ask = DebugAsk::ByStage { stage_name: this_stage_name, token };
repl.options.debug_asks.insert(ask);
None
},
Hide => {
let stage_name_to_remove = match arguments.get(0) {
Some(s) => s.to_string(),
None => return Some(format!("Must specify a stage to hide")),
};
repl.options.debug_asks.retain(|ask| match ask {
DebugAsk::ByStage { stage_name, .. } if *stage_name == stage_name_to_remove => false,
_ => true
});
None
},
TotalTimeOff => total_time_off(repl, arguments),
TotalTimeOn => total_time_on(repl, arguments),
StageTimeOff => stage_time_off(repl, arguments),
StageTimeOn => stage_time_on(repl, arguments),
Doc => doc(repl, arguments),
} }
}
} }
fn total_time_on(repl: &mut Repl, _: &[&str]) -> InterpreterDirectiveOutput { fn total_time_on(repl: &mut Repl, _: &[&str]) -> InterpreterDirectiveOutput {
repl.options.show_total_time = true; repl.options.show_total_time = true;
None None
} }
fn total_time_off(repl: &mut Repl, _: &[&str]) -> InterpreterDirectiveOutput { fn total_time_off(repl: &mut Repl, _: &[&str]) -> InterpreterDirectiveOutput {
repl.options.show_total_time = false; repl.options.show_total_time = false;
None None
} }
fn stage_time_on(repl: &mut Repl, _: &[&str]) -> InterpreterDirectiveOutput { fn stage_time_on(repl: &mut Repl, _: &[&str]) -> InterpreterDirectiveOutput {
repl.options.show_stage_times = true; repl.options.show_stage_times = true;
None None
} }
fn stage_time_off(repl: &mut Repl, _: &[&str]) -> InterpreterDirectiveOutput { fn stage_time_off(repl: &mut Repl, _: &[&str]) -> InterpreterDirectiveOutput {
repl.options.show_stage_times = false; repl.options.show_stage_times = false;
None None
} }
fn doc(repl: &mut Repl, arguments: &[&str]) -> InterpreterDirectiveOutput { fn doc(repl: &mut Repl, arguments: &[&str]) -> InterpreterDirectiveOutput {
arguments.get(0).map(|cmd| { arguments
let source = cmd.to_string(); .get(0)
let meta = LangMetaRequest::Docs { source }; .map(|cmd| {
let cur_state = repl.get_cur_language_state(); let source = cmd.to_string();
match cur_state.request_meta(meta) { let meta = LangMetaRequest::Docs { source };
LangMetaResponse::Docs { doc_string } => Some(doc_string), let cur_state = repl.get_cur_language_state();
_ => Some(format!("Invalid doc response")) match cur_state.request_meta(meta) {
} LangMetaResponse::Docs { doc_string } => Some(doc_string),
}).unwrap_or(Some(format!(":docs needs an argument"))) _ => Some(format!("Invalid doc response")),
}
})
.unwrap_or(Some(format!(":docs needs an argument")))
} }

View File

@ -2,54 +2,103 @@ use crate::repl::command_tree::CommandTree;
use crate::repl::directive_actions::DirectiveAction; use crate::repl::directive_actions::DirectiveAction;
pub fn directives_from_pass_names(pass_names: &Vec<String>) -> CommandTree { pub fn directives_from_pass_names(pass_names: &Vec<String>) -> CommandTree {
let passes_directives: Vec<CommandTree> = pass_names.iter() let passes_directives: Vec<CommandTree> = pass_names
.map(|pass_name| { .iter()
if pass_name == "parsing" { .map(|pass_name| {
CommandTree::nonterm(pass_name, None, vec![ if pass_name == "parsing" {
CommandTree::nonterm_no_further_tab_completions("compact", None), CommandTree::nonterm(
CommandTree::nonterm_no_further_tab_completions("expanded", None), pass_name,
CommandTree::nonterm_no_further_tab_completions("trace", None), None,
]) vec![
} else { CommandTree::nonterm_no_further_tab_completions("compact", None),
CommandTree::nonterm_no_further_tab_completions(pass_name, None) CommandTree::nonterm_no_further_tab_completions("expanded", None),
} CommandTree::nonterm_no_further_tab_completions("trace", None),
}) ],
.collect(); )
CommandTree::Top(get_list(&passes_directives, true)) } else {
CommandTree::nonterm_no_further_tab_completions(pass_name, None)
}
})
.collect();
CommandTree::Top(get_list(&passes_directives, true))
} }
fn get_list(passes_directives: &Vec<CommandTree>, include_help: bool) -> Vec<CommandTree> { fn get_list(passes_directives: &Vec<CommandTree>, include_help: bool) -> Vec<CommandTree> {
use DirectiveAction::*; use DirectiveAction::*;
vec![ vec![
CommandTree::terminal("exit", Some("exit the REPL"), vec![], QuitProgram), CommandTree::terminal("exit", Some("exit the REPL"), vec![], QuitProgram),
CommandTree::terminal("quit", Some("exit the REPL"), vec![], QuitProgram), CommandTree::terminal("quit", Some("exit the REPL"), vec![], QuitProgram),
CommandTree::terminal("help", Some("Print this help message"), if include_help { get_list(passes_directives, false) } else { vec![] }, Help), CommandTree::terminal(
CommandTree::nonterm("debug", "help",
Some("Configure debug information"), Some("Print this help message"),
vec![ if include_help {
CommandTree::terminal("list-passes", Some("List all registered compiler passes"), vec![], ListPasses), get_list(passes_directives, false)
CommandTree::terminal("show-immediate", None, passes_directives.clone(), ShowImmediate), } else {
CommandTree::terminal("show", Some("Show debug output for a specific pass"), passes_directives.clone(), Show), vec![]
CommandTree::terminal("hide", Some("Hide debug output for a specific pass"), passes_directives.clone(), Hide), },
CommandTree::nonterm("total-time", None, vec![ Help,
CommandTree::terminal("on", None, vec![], TotalTimeOn), ),
CommandTree::terminal("off", None, vec![], TotalTimeOff), CommandTree::nonterm(
]), "debug",
CommandTree::nonterm("stage-times", Some("Computation time per-stage"), vec![ Some("Configure debug information"),
CommandTree::terminal("on", None, vec![], StageTimeOn), vec![
CommandTree::terminal("off", None, vec![], StageTimeOff), CommandTree::terminal(
]) "list-passes",
] Some("List all registered compiler passes"),
), vec![],
CommandTree::nonterm("lang", ListPasses,
Some("switch between languages, or go directly to a langauge by name"), ),
vec![ CommandTree::terminal(
CommandTree::nonterm_no_further_tab_completions("next", None), "show-immediate",
CommandTree::nonterm_no_further_tab_completions("prev", None), None,
CommandTree::nonterm("go", None, vec![]), passes_directives.clone(),
] ShowImmediate,
), ),
CommandTree::terminal("doc", Some("Get language-specific help for an item"), vec![], Doc), CommandTree::terminal(
] "show",
Some("Show debug output for a specific pass"),
passes_directives.clone(),
Show,
),
CommandTree::terminal(
"hide",
Some("Hide debug output for a specific pass"),
passes_directives.clone(),
Hide,
),
CommandTree::nonterm(
"total-time",
None,
vec![
CommandTree::terminal("on", None, vec![], TotalTimeOn),
CommandTree::terminal("off", None, vec![], TotalTimeOff),
],
),
CommandTree::nonterm(
"stage-times",
Some("Computation time per-stage"),
vec![
CommandTree::terminal("on", None, vec![], StageTimeOn),
CommandTree::terminal("off", None, vec![], StageTimeOff),
],
),
],
),
CommandTree::nonterm(
"lang",
Some("switch between languages, or go directly to a langauge by name"),
vec![
CommandTree::nonterm_no_further_tab_completions("next", None),
CommandTree::nonterm_no_further_tab_completions("prev", None),
CommandTree::nonterm("go", None, vec![]),
],
),
CommandTree::terminal(
"doc",
Some("Get language-specific help for an item"),
vec![],
Doc,
),
]
} }

View File

@ -1,59 +1,83 @@
use std::fmt::Write as FmtWrite; use std::fmt::Write as FmtWrite;
use colored::*;
use super::command_tree::CommandTree; use super::command_tree::CommandTree;
use super::{Repl, InterpreterDirectiveOutput}; use super::{InterpreterDirectiveOutput, Repl};
use colored::*;
pub fn help(repl: &mut Repl, arguments: &[&str]) -> InterpreterDirectiveOutput { pub fn help(repl: &mut Repl, arguments: &[&str]) -> InterpreterDirectiveOutput {
match arguments { match arguments {
[] => return global_help(repl), [] => return global_help(repl),
commands => { commands => {
let dirs = repl.get_directives(); let dirs = repl.get_directives();
Some(match get_directive_from_commands(commands, &dirs) { Some(match get_directive_from_commands(commands, &dirs) {
None => format!("Directive `{}` not found", commands.last().unwrap()), None => format!("Directive `{}` not found", commands.last().unwrap()),
Some(dir) => { Some(dir) => {
let mut buf = String::new(); let mut buf = String::new();
let cmd = dir.get_cmd(); let cmd = dir.get_cmd();
let children = dir.get_children(); let children = dir.get_children();
writeln!(buf, "`{}` - {}", cmd, dir.get_help()).unwrap(); writeln!(buf, "`{}` - {}", cmd, dir.get_help()).unwrap();
for sub in children.iter() { for sub in children.iter() {
writeln!(buf, "\t`{} {}` - {}", cmd, sub.get_cmd(), sub.get_help()).unwrap(); writeln!(buf, "\t`{} {}` - {}", cmd, sub.get_cmd(), sub.get_help())
} .unwrap();
buf }
buf
}
})
} }
})
} }
}
} }
fn get_directive_from_commands<'a>(commands: &[&str], dirs: &'a CommandTree) -> Option<&'a CommandTree> { fn get_directive_from_commands<'a>(
let mut directive_list = dirs.get_children(); commands: &[&str],
let mut matched_directive = None; dirs: &'a CommandTree,
for cmd in commands { ) -> Option<&'a CommandTree> {
let found = directive_list.iter().find(|directive| directive.get_cmd() == *cmd); let mut directive_list = dirs.get_children();
if let Some(dir) = found { let mut matched_directive = None;
directive_list = dir.get_children(); for cmd in commands {
} let found = directive_list
.iter()
.find(|directive| directive.get_cmd() == *cmd);
if let Some(dir) = found {
directive_list = dir.get_children();
}
matched_directive = found; matched_directive = found;
} }
matched_directive matched_directive
} }
fn global_help(repl: &mut Repl) -> InterpreterDirectiveOutput { fn global_help(repl: &mut Repl) -> InterpreterDirectiveOutput {
let mut buf = String::new(); let mut buf = String::new();
let sigil = repl.interpreter_directive_sigil; let sigil = repl.interpreter_directive_sigil;
writeln!(buf, "{} version {}", "Schala REPL".bright_red().bold(), crate::VERSION_STRING).unwrap(); writeln!(
writeln!(buf, "-----------------------").unwrap(); buf,
"{} version {}",
"Schala REPL".bright_red().bold(),
crate::VERSION_STRING
)
.unwrap();
writeln!(buf, "-----------------------").unwrap();
for directive in repl.get_directives().get_children() { for directive in repl.get_directives().get_children() {
writeln!(buf, "{}{} - {}", sigil, directive.get_cmd(), directive.get_help()).unwrap(); writeln!(
} buf,
"{}{} - {}",
sigil,
directive.get_cmd(),
directive.get_help()
)
.unwrap();
}
let ref lang = repl.get_cur_language_state(); let ref lang = repl.get_cur_language_state();
writeln!(buf, "").unwrap(); writeln!(buf, "").unwrap();
writeln!(buf, "Language-specific help for {}", lang.get_language_name()).unwrap(); writeln!(
writeln!(buf, "-----------------------").unwrap(); buf,
Some(buf) "Language-specific help for {}",
lang.get_language_name()
)
.unwrap();
writeln!(buf, "-----------------------").unwrap();
Some(buf)
} }

View File

@ -1,8 +1,9 @@
use std::sync::Arc;
use std::collections::HashSet; use std::collections::HashSet;
use std::sync::Arc;
use crate::language::{ProgrammingLanguageInterface, use crate::language::{
ComputationRequest, LangMetaResponse, LangMetaRequest}; ComputationRequest, LangMetaRequest, LangMetaResponse, ProgrammingLanguageInterface,
};
mod command_tree; mod command_tree;
use self::command_tree::CommandTree; use self::command_tree::CommandTree;
@ -21,231 +22,253 @@ const OPTIONS_SAVE_FILE: &'static str = ".schala_repl";
type InterpreterDirectiveOutput = Option<String>; type InterpreterDirectiveOutput = Option<String>;
pub struct Repl { pub struct Repl {
pub interpreter_directive_sigil: char, pub interpreter_directive_sigil: char,
line_reader: ::linefeed::interface::Interface<::linefeed::terminal::DefaultTerminal>, line_reader: ::linefeed::interface::Interface<::linefeed::terminal::DefaultTerminal>,
language_states: Vec<Box<dyn ProgrammingLanguageInterface>>, language_states: Vec<Box<dyn ProgrammingLanguageInterface>>,
options: ReplOptions, options: ReplOptions,
} }
#[derive(Clone)] #[derive(Clone)]
enum PromptStyle { enum PromptStyle {
Normal, Normal,
Multiline Multiline,
} }
impl Repl { impl Repl {
pub fn new(initial_states: Vec<Box<dyn ProgrammingLanguageInterface>>) -> Repl { pub fn new(initial_states: Vec<Box<dyn ProgrammingLanguageInterface>>) -> Repl {
use linefeed::Interface; use linefeed::Interface;
let line_reader = Interface::new("schala-repl").unwrap(); let line_reader = Interface::new("schala-repl").unwrap();
let interpreter_directive_sigil = ':'; let interpreter_directive_sigil = ':';
Repl { Repl {
interpreter_directive_sigil, interpreter_directive_sigil,
line_reader, line_reader,
language_states: initial_states, language_states: initial_states,
options: ReplOptions::new(), options: ReplOptions::new(),
}
}
pub fn run_repl(&mut self) {
println!("Schala MetaInterpreter version {}", crate::VERSION_STRING);
println!("Type {}help for help with the REPL", self.interpreter_directive_sigil);
self.load_options();
self.handle_repl_loop();
self.save_before_exit();
println!("Exiting...");
}
fn load_options(&mut self) {
self.line_reader.load_history(HISTORY_SAVE_FILE).unwrap_or(());
match ReplOptions::load_from_file(OPTIONS_SAVE_FILE) {
Ok(options) => {
self.options = options;
},
Err(()) => ()
};
}
fn handle_repl_loop(&mut self) {
use linefeed::ReadResult::*;
let sigil = self.interpreter_directive_sigil;
'main: loop {
macro_rules! match_or_break {
($line:expr) => {
match $line {
Err(e) => {
println!("readline IO Error: {}", e);
break 'main;
},
Ok(Eof) | Ok(Signal(_)) => break 'main,
Ok(Input(ref input)) => input,
}
} }
} }
self.update_line_reader();
let line = self.line_reader.read_line();
let input: &str = match_or_break!(line);
self.line_reader.add_history_unique(input.to_string()); pub fn run_repl(&mut self) {
let mut chars = input.chars().peekable(); println!("Schala MetaInterpreter version {}", crate::VERSION_STRING);
let repl_responses = match chars.nth(0) { println!(
Some(ch) if ch == sigil => { "Type {}help for help with the REPL",
if chars.peek() == Some(&'{') { self.interpreter_directive_sigil
let mut buf = String::new(); );
buf.push_str(input.get(2..).unwrap()); self.load_options();
'multiline: loop { self.handle_repl_loop();
self.set_prompt(PromptStyle::Multiline); self.save_before_exit();
let new_line = self.line_reader.read_line(); println!("Exiting...");
let new_input = match_or_break!(new_line); }
if new_input.starts_with(":}") {
break 'multiline; fn load_options(&mut self) {
} else { self.line_reader
buf.push_str(new_input); .load_history(HISTORY_SAVE_FILE)
buf.push_str("\n"); .unwrap_or(());
} match ReplOptions::load_from_file(OPTIONS_SAVE_FILE) {
Ok(options) => {
self.options = options;
} }
self.handle_input(&buf) Err(()) => (),
} else { };
match self.handle_interpreter_directive(input) { }
Some(directive_output) => println!("<> {}", directive_output),
None => (), fn handle_repl_loop(&mut self) {
use linefeed::ReadResult::*;
let sigil = self.interpreter_directive_sigil;
'main: loop {
macro_rules! match_or_break {
($line:expr) => {
match $line {
Err(e) => {
println!("readline IO Error: {}", e);
break 'main;
}
Ok(Eof) | Ok(Signal(_)) => break 'main,
Ok(Input(ref input)) => input,
}
};
} }
continue self.update_line_reader();
} let line = self.line_reader.read_line();
}, let input: &str = match_or_break!(line);
_ => self.handle_input(input)
};
for repl_response in repl_responses.iter() { self.line_reader.add_history_unique(input.to_string());
println!("{}", repl_response); let mut chars = input.chars().peekable();
} let repl_responses = match chars.nth(0) {
} Some(ch) if ch == sigil => {
} if chars.peek() == Some(&'{') {
let mut buf = String::new();
buf.push_str(input.get(2..).unwrap());
'multiline: loop {
self.set_prompt(PromptStyle::Multiline);
let new_line = self.line_reader.read_line();
let new_input = match_or_break!(new_line);
if new_input.starts_with(":}") {
break 'multiline;
} else {
buf.push_str(new_input);
buf.push_str("\n");
}
}
self.handle_input(&buf)
} else {
match self.handle_interpreter_directive(input) {
Some(directive_output) => println!("<> {}", directive_output),
None => (),
}
continue;
}
}
_ => self.handle_input(input),
};
fn update_line_reader(&mut self) { for repl_response in repl_responses.iter() {
let tab_complete_handler = TabCompleteHandler::new(self.interpreter_directive_sigil, self.get_directives()); println!("{}", repl_response);
self.line_reader.set_completer(Arc::new(tab_complete_handler)); //TODO fix this here }
self.set_prompt(PromptStyle::Normal); }
}
fn set_prompt(&mut self, prompt_style: PromptStyle) {
let prompt_str = match prompt_style {
PromptStyle::Normal => ">> ".to_string(),
PromptStyle::Multiline => ">| ".to_string(),
};
self.line_reader.set_prompt(&prompt_str).unwrap();
}
fn save_before_exit(&self) {
self.line_reader.save_history(HISTORY_SAVE_FILE).unwrap_or(());
self.options.save_to_file(OPTIONS_SAVE_FILE);
}
fn handle_interpreter_directive(&mut self, input: &str) -> InterpreterDirectiveOutput {
let mut iter = input.chars();
iter.next();
let arguments: Vec<&str> = iter
.as_str()
.split_whitespace()
.collect();
if arguments.len() < 1 {
return None;
} }
let directives = self.get_directives(); fn update_line_reader(&mut self) {
directives.perform(self, &arguments) let tab_complete_handler =
} TabCompleteHandler::new(self.interpreter_directive_sigil, self.get_directives());
self.line_reader
fn get_cur_language_state(&mut self) -> &mut Box<dyn ProgrammingLanguageInterface> { .set_completer(Arc::new(tab_complete_handler)); //TODO fix this here
//TODO this is obviously not complete self.set_prompt(PromptStyle::Normal);
&mut self.language_states[0]
}
fn handle_input(&mut self, input: &str) -> Vec<ReplResponse> {
let mut debug_requests = HashSet::new();
for ask in self.options.debug_asks.iter() {
debug_requests.insert(ask.clone());
} }
let request = ComputationRequest { source: input, debug_requests }; fn set_prompt(&mut self, prompt_style: PromptStyle) {
let ref mut language_state = self.get_cur_language_state(); let prompt_str = match prompt_style {
let response = language_state.run_computation(request); PromptStyle::Normal => ">> ".to_string(),
response::handle_computation_response(response, &self.options) PromptStyle::Multiline => ">| ".to_string(),
} };
fn get_directives(&mut self) -> CommandTree { self.line_reader.set_prompt(&prompt_str).unwrap();
let language_state = self.get_cur_language_state(); }
let pass_names = match language_state.request_meta(LangMetaRequest::StageNames) {
LangMetaResponse::StageNames(names) => names,
_ => vec![],
};
directives_from_pass_names(&pass_names) fn save_before_exit(&self) {
} self.line_reader
.save_history(HISTORY_SAVE_FILE)
.unwrap_or(());
self.options.save_to_file(OPTIONS_SAVE_FILE);
}
fn handle_interpreter_directive(&mut self, input: &str) -> InterpreterDirectiveOutput {
let mut iter = input.chars();
iter.next();
let arguments: Vec<&str> = iter.as_str().split_whitespace().collect();
if arguments.len() < 1 {
return None;
}
let directives = self.get_directives();
directives.perform(self, &arguments)
}
fn get_cur_language_state(&mut self) -> &mut Box<dyn ProgrammingLanguageInterface> {
//TODO this is obviously not complete
&mut self.language_states[0]
}
fn handle_input(&mut self, input: &str) -> Vec<ReplResponse> {
let mut debug_requests = HashSet::new();
for ask in self.options.debug_asks.iter() {
debug_requests.insert(ask.clone());
}
let request = ComputationRequest {
source: input,
debug_requests,
};
let ref mut language_state = self.get_cur_language_state();
let response = language_state.run_computation(request);
response::handle_computation_response(response, &self.options)
}
fn get_directives(&mut self) -> CommandTree {
let language_state = self.get_cur_language_state();
let pass_names = match language_state.request_meta(LangMetaRequest::StageNames) {
LangMetaResponse::StageNames(names) => names,
_ => vec![],
};
directives_from_pass_names(&pass_names)
}
} }
struct TabCompleteHandler { struct TabCompleteHandler {
sigil: char, sigil: char,
top_level_commands: CommandTree, top_level_commands: CommandTree,
} }
use linefeed::complete::{Completion, Completer}; use linefeed::complete::{Completer, Completion};
use linefeed::terminal::Terminal; use linefeed::terminal::Terminal;
impl TabCompleteHandler { impl TabCompleteHandler {
fn new(sigil: char, top_level_commands: CommandTree) -> TabCompleteHandler { fn new(sigil: char, top_level_commands: CommandTree) -> TabCompleteHandler {
TabCompleteHandler { TabCompleteHandler {
top_level_commands, top_level_commands,
sigil, sigil,
}
} }
}
} }
impl<T: Terminal> Completer<T> for TabCompleteHandler { impl<T: Terminal> Completer<T> for TabCompleteHandler {
fn complete(&self, word: &str, prompter: &::linefeed::prompter::Prompter<T>, start: usize, _end: usize) -> Option<Vec<Completion>> { fn complete(
let line = prompter.buffer(); &self,
word: &str,
prompter: &::linefeed::prompter::Prompter<T>,
start: usize,
_end: usize,
) -> Option<Vec<Completion>> {
let line = prompter.buffer();
if !line.starts_with(self.sigil) { if !line.starts_with(self.sigil) {
return None; return None;
}
let mut words = line[1..(if start == 0 { 1 } else { start })].split_whitespace();
let mut completions = Vec::new();
let mut command_tree: Option<&CommandTree> = Some(&self.top_level_commands);
loop {
match words.next() {
None => {
let top = match command_tree {
Some(CommandTree::Top(_)) => true,
_ => false
};
let word = if top { word.get(1..).unwrap() } else { word };
for cmd in command_tree.map(|x| x.get_subcommands()).unwrap_or(vec![]).into_iter() {
if cmd.starts_with(word) {
completions.push(Completion {
completion: format!("{}{}", if top { ":" } else { "" }, cmd),
display: Some(cmd.to_string()),
suffix: ::linefeed::complete::Suffix::Some(' ')
})
}
}
break;
},
Some(s) => {
let new_ptr: Option<&CommandTree> = command_tree.and_then(|cm| match cm {
CommandTree::Top(children) => children.iter().find(|c| c.get_cmd() == s),
CommandTree::NonTerminal { children, .. } => children.iter().find(|c| c.get_cmd() == s),
CommandTree::Terminal { children, .. } => children.iter().find(|c| c.get_cmd() == s),
});
command_tree = new_ptr;
} }
}
let mut words = line[1..(if start == 0 { 1 } else { start })].split_whitespace();
let mut completions = Vec::new();
let mut command_tree: Option<&CommandTree> = Some(&self.top_level_commands);
loop {
match words.next() {
None => {
let top = match command_tree {
Some(CommandTree::Top(_)) => true,
_ => false,
};
let word = if top { word.get(1..).unwrap() } else { word };
for cmd in command_tree
.map(|x| x.get_subcommands())
.unwrap_or(vec![])
.into_iter()
{
if cmd.starts_with(word) {
completions.push(Completion {
completion: format!("{}{}", if top { ":" } else { "" }, cmd),
display: Some(cmd.to_string()),
suffix: ::linefeed::complete::Suffix::Some(' '),
})
}
}
break;
}
Some(s) => {
let new_ptr: Option<&CommandTree> = command_tree.and_then(|cm| match cm {
CommandTree::Top(children) => children.iter().find(|c| c.get_cmd() == s),
CommandTree::NonTerminal { children, .. } => {
children.iter().find(|c| c.get_cmd() == s)
}
CommandTree::Terminal { children, .. } => {
children.iter().find(|c| c.get_cmd() == s)
}
});
command_tree = new_ptr;
}
}
}
Some(completions)
} }
Some(completions)
}
} }

View File

@ -1,47 +1,46 @@
use crate::language::DebugAsk; use crate::language::DebugAsk;
use std::io::{Read, Write};
use std::collections::HashSet; use std::collections::HashSet;
use std::fs::File; use std::fs::File;
use std::io::{Read, Write};
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct ReplOptions { pub struct ReplOptions {
pub debug_asks: HashSet<DebugAsk>, pub debug_asks: HashSet<DebugAsk>,
pub show_total_time: bool, pub show_total_time: bool,
pub show_stage_times: bool, pub show_stage_times: bool,
} }
impl ReplOptions { impl ReplOptions {
pub fn new() -> ReplOptions { pub fn new() -> ReplOptions {
ReplOptions { ReplOptions {
debug_asks: HashSet::new(), debug_asks: HashSet::new(),
show_total_time: true, show_total_time: true,
show_stage_times: false, show_stage_times: false,
}
} }
}
pub fn save_to_file(&self, filename: &str) { pub fn save_to_file(&self, filename: &str) {
let res = File::create(filename) let res = File::create(filename).and_then(|mut file| {
.and_then(|mut file| { let buf = crate::serde_json::to_string(self).unwrap();
let buf = crate::serde_json::to_string(self).unwrap(); file.write_all(buf.as_bytes())
file.write_all(buf.as_bytes()) });
}); if let Err(err) = res {
if let Err(err) = res { println!("Error saving {} file {}", filename, err);
println!("Error saving {} file {}", filename, err); }
} }
}
pub fn load_from_file(filename: &str) -> Result<ReplOptions, ()> { pub fn load_from_file(filename: &str) -> Result<ReplOptions, ()> {
File::open(filename) File::open(filename)
.and_then(|mut file| { .and_then(|mut file| {
let mut contents = String::new(); let mut contents = String::new();
file.read_to_string(&mut contents)?; file.read_to_string(&mut contents)?;
Ok(contents) Ok(contents)
}) })
.and_then(|contents| { .and_then(|contents| {
let output: ReplOptions = crate::serde_json::from_str(&contents)?; let output: ReplOptions = crate::serde_json::from_str(&contents)?;
Ok(output) Ok(output)
}) })
.map_err(|_| ()) .map_err(|_| ())
} }
} }

View File

@ -3,65 +3,78 @@ use std::fmt;
use std::fmt::Write; use std::fmt::Write;
use super::ReplOptions; use super::ReplOptions;
use crate::language::{ DebugAsk, ComputationResponse}; use crate::language::{ComputationResponse, DebugAsk};
pub struct ReplResponse { pub struct ReplResponse {
label: Option<String>, label: Option<String>,
text: String, text: String,
color: Option<Color> color: Option<Color>,
} }
impl fmt::Display for ReplResponse { impl fmt::Display for ReplResponse {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut buf = String::new(); let mut buf = String::new();
if let Some(ref label) = self.label { if let Some(ref label) = self.label {
write!(buf, "({})", label).unwrap(); write!(buf, "({})", label).unwrap();
}
write!(buf, "=> {}", self.text).unwrap();
write!(
f,
"{}",
match self.color {
Some(c) => buf.color(c),
None => buf.normal(),
}
)
} }
write!(buf, "=> {}", self.text).unwrap();
write!(f, "{}", match self.color {
Some(c) => buf.color(c),
None => buf.normal()
})
}
} }
pub fn handle_computation_response(
response: ComputationResponse,
options: &ReplOptions,
) -> Vec<ReplResponse> {
let mut responses = vec![];
pub fn handle_computation_response(response: ComputationResponse, options: &ReplOptions) -> Vec<ReplResponse> { if options.show_total_time {
let mut responses = vec![]; responses.push(ReplResponse {
label: Some("Total time".to_string()),
text: format!("{:?}", response.global_output_stats.total_duration),
color: None,
});
}
if options.show_total_time { if options.show_stage_times {
responses.push(ReplResponse { responses.push(ReplResponse {
label: Some("Total time".to_string()), label: Some("Stage times".to_string()),
text: format!("{:?}", response.global_output_stats.total_duration), text: format!("{:?}", response.global_output_stats.stage_durations),
color: None, color: None,
});
}
for debug_resp in response.debug_responses {
let stage_name = match debug_resp.ask {
DebugAsk::ByStage { stage_name, .. } => stage_name,
_ => continue,
};
responses.push(ReplResponse {
label: Some(stage_name.to_string()),
text: debug_resp.value,
color: Some(Color::Red),
});
}
responses.push(match response.main_output {
Ok(s) => ReplResponse {
label: None,
text: s,
color: None,
},
Err(e) => ReplResponse {
label: Some("Error".to_string()),
text: e,
color: Some(Color::Red),
},
}); });
}
if options.show_stage_times { responses
responses.push(ReplResponse {
label: Some("Stage times".to_string()),
text: format!("{:?}", response.global_output_stats.stage_durations),
color: None,
});
}
for debug_resp in response.debug_responses {
let stage_name = match debug_resp.ask {
DebugAsk::ByStage { stage_name, .. } => stage_name,
_ => continue,
};
responses.push(ReplResponse {
label: Some(stage_name.to_string()),
text: debug_resp.value,
color: Some(Color::Red),
});
}
responses.push(match response.main_output {
Ok(s) => ReplResponse { label: None, text: s, color: None },
Err(e) => ReplResponse { label: Some("Error".to_string()), text: e, color: Some(Color::Red) },
});
responses
} }