use rustyline::{DefaultEditor, Result as RustyResult};
use std::io;
use std::path::Path;
use std::str::FromStr;
use tokio::runtime::Runtime;
use super::commands::{ReplCommand, SessionContext};
use super::display::print_repl_help;
use super::handlers::execute_command;
use super::{color_blue, color_blue_bold, color_dimmed, color_green_bold, color_red_bold};
use data_gov::DataGovClient;
pub struct DataGovRepl {
client: DataGovClient,
rt: Runtime,
ctx: SessionContext,
}
impl DataGovRepl {
pub fn new(client: DataGovClient) -> io::Result<Self> {
let rt = Runtime::new()?;
Ok(Self {
client,
rt,
ctx: SessionContext::default(),
})
}
pub fn run(&mut self) -> RustyResult<()> {
println!("{}", color_blue_bold("πΊπΈ Data.gov Interactive Explorer"));
println!(
"{}",
color_dimmed("Type 'help' for available commands, 'quit' to exit")
);
println!();
let mut rl = DefaultEditor::new()?;
loop {
let prompt = self.build_prompt();
let readline = rl.readline(&prompt);
match readline {
Ok(line) => {
let trimmed = line.trim();
if trimmed.is_empty() || trimmed.starts_with('#') {
continue;
}
rl.add_history_entry(line.as_str())?;
match ReplCommand::from_str(&line) {
Ok(command) => {
if let ReplCommand::Quit = command {
println!("Goodbye! π");
break;
}
if let Err(e) = self.handle_command(command) {
println!("{} {}", color_red_bold("Error:"), e);
}
}
Err(e) => {
println!("{} {}", color_red_bold("Invalid command:"), e);
}
}
}
Err(rustyline::error::ReadlineError::Interrupted) => {
println!("CTRL-C");
break;
}
Err(rustyline::error::ReadlineError::Eof) => {
println!("CTRL-D");
break;
}
Err(err) => {
println!("Error: {:?}", err);
break;
}
}
}
Ok(())
}
fn build_prompt(&self) -> String {
let label = self.ctx.prompt_label();
if label.is_empty() {
format!("{} ", color_green_bold("data.gov>"))
} else {
format!(
"{}\n{} ",
color_dimmed(&label),
color_green_bold("data.gov>")
)
}
}
fn handle_command(&mut self, command: ReplCommand) -> Result<(), Box<dyn std::error::Error>> {
match &command {
ReplCommand::SetDir { path } => {
self.handle_setdir(path)?;
return Ok(());
}
ReplCommand::Help => {
print_repl_help();
return Ok(());
}
_ => {}
}
execute_command(&self.client, &self.rt, command, &mut self.ctx)?;
Ok(())
}
fn handle_setdir(&mut self, path: &Path) -> Result<(), Box<dyn std::error::Error>> {
let new_config = self
.client
.config()
.clone()
.with_download_dir(path.to_path_buf());
self.rt.block_on(async {
let temp_client = DataGovClient::with_config(new_config.clone())?;
temp_client.validate_download_dir().await?;
self.client = temp_client;
Ok::<(), data_gov::DataGovError>(())
})?;
println!(
"{} Download directory set to: {}",
color_green_bold("Success!"),
color_blue(&path.display().to_string())
);
Ok(())
}
}