use crate::cli::parse::{FlagType, Parser};
use crate::command::{CommandHistory, CommandType};
use crate::page::Page;
use crate::CedError;
use crate::{cli::help, utils, CedResult, Command, Processor};
use std::str::FromStr;
pub fn start_main_loop() -> CedResult<()> {
let args: Vec<String> = std::env::args().collect();
let flags = Parser::new().parse_from_vec(&args[1..].to_vec());
let mut command_loop = CommandLoop::new();
let mut command_exit = false;
let mut write_confirm = false;
let mut import = None;
let mut execute = None;
let mut schema = None;
let mut command = None;
for item in flags.iter() {
match item.ftype {
FlagType::Version => help::print_version(),
FlagType::Help => help::print_binary_help_text(),
FlagType::Confirm => write_confirm = true,
FlagType::Argument => {
if !item.option.is_empty() {
if let Some(ext) = std::path::Path::new(&item.option).extension() {
if ext == "ced" {
execute.replace(item.option.clone());
command_exit = true;
continue;
}
}
import.replace(item.option.clone());
}
}
FlagType::Schema => {
if !item.option.is_empty() {
schema.replace(item.option.clone());
} else {
utils::write_to_stderr("WRN : Schema is empty thus not applied\n")?;
}
}
FlagType::Command => {
if !item.option.is_empty() {
command.replace(item.option.clone());
command_exit = true;
} else {
utils::write_to_stderr("WRN : Command is empty thus not executed\n")?;
}
}
FlagType::NoLog => {
command_loop.no_log();
}
FlagType::None => (),
}
if item.early_exit {
return Ok(());
}
}
command_loop.add_empty_page()?;
command_loop.processor.configure_preset(true)?;
if let Some(execute) = execute.as_ref() {
feed_execute(execute, &mut command_loop)?;
}
if let Some(import) = import.as_ref() {
feed_import(import, &mut command_loop)?;
}
if let Some(sch) = schema.as_ref() {
feed_schema(sch, &mut command_loop)?;
}
if let Some(cmd) = command.as_ref() {
feed_command(cmd, &mut command_loop, write_confirm)?;
}
if command_exit {
return Ok(());
}
if let Some(err) = command_loop.start_loop().err() {
println!("{}", err);
}
Ok(())
}
fn feed_execute(file: &str, command_loop: &mut CommandLoop) -> CedResult<()> {
command_loop.toggle_no_loop(true);
if let Err(err) =
command_loop.feed_command(&Command::from_str(&format!("execute {}", file))?, true)
{
eprintln!("{}", err);
command_loop.toggle_no_loop(false); return Ok(());
}
command_loop.toggle_no_loop(false);
Ok(())
}
fn feed_import(file: &str, command_loop: &mut CommandLoop) -> CedResult<()> {
if let Err(err) =
command_loop.feed_command(&Command::from_str(&format!("import {}", file))?, true)
{
eprintln!("{}", err);
return Ok(());
}
Ok(())
}
fn feed_schema(file: &str, command_loop: &mut CommandLoop) -> CedResult<()> {
if let Err(err) =
command_loop.feed_command(&Command::from_str(&format!("schema {} true", file))?, true)
{
eprintln!("{}", err);
return Ok(());
}
Ok(())
}
fn feed_command(
command: &str,
command_loop: &mut CommandLoop,
write_confirm: bool,
) -> CedResult<()> {
let command_split: Vec<&str> = command.split_terminator(';').collect();
for command in command_split {
let command = Command::from_str(command)?;
if command.command_type == CommandType::Write && write_confirm {
command_loop.feed_command(&Command::from_str("print")?, true)?;
utils::write_to_stdout("Overwrite ? (y/N) : ")?;
if utils::read_stdin(true)?.to_lowercase().as_str() != "y" {
return Ok(());
}
}
if let Err(err) = command_loop.feed_command(&command, true) {
eprintln!("{}", err);
return Ok(());
}
}
Ok(())
}
pub struct CommandLoop {
history: CommandHistory,
processor: Processor,
}
impl CommandLoop {
pub fn new() -> Self {
Self {
history: CommandHistory::new(),
processor: Processor::new(),
}
}
pub fn toggle_no_loop(&mut self, tv: bool) {
self.processor.no_loop = tv;
}
pub fn no_log(&mut self) {
self.processor.print_logs = false;
}
pub fn feed_command(&mut self, command: &Command, panic: bool) -> CedResult<()> {
self.execute_command(command, panic)?;
Ok(())
}
fn start_loop(&mut self) -> CedResult<()> {
let mut command = Command::default();
utils::write_to_stdout("Ced, a csv editor\n")?;
let mut read_byte = 1;
while read_byte != 0 && CommandType::Exit != command.command_type {
utils::write_to_stdout(">> ")?;
let mut input = String::new();
read_byte = utils::read_stdin_until_eof(true, &mut input)?;
if input.is_empty() {
continue;
}
command = Command::from_str(&input)?;
self.execute_command(&command, false)?;
}
Ok(())
}
fn execute_command(&mut self, command: &Command, panic: bool) -> CedResult<()> {
#[cfg(debug_assertions)]
utils::write_to_stdout(&format!("{:?}\n", command))?;
match command.command_type {
CommandType::History => {
self.print_history()?;
}
CommandType::Undo | CommandType::Redo => {
if command.command_type == CommandType::Undo {
if self.history.is_empty() {
return Ok(());
}
let backup = if self.history.is_newest() {
Some(
self.processor
.get_page_data(&self.processor.get_cursor().unwrap())?
.clone(),
)
} else {
None
};
self.undo(backup)?;
} else {
if self.history.is_empty() {
return Ok(());
}
self.redo()?;
}
return Ok(());
}
CommandType::Exit
| CommandType::Import
| CommandType::Export
| CommandType::Create
| CommandType::Write
| CommandType::None
| CommandType::Schema
| CommandType::SchemaInit
| CommandType::SchemaExport
| CommandType::PrintCell
| CommandType::PrintRow
| CommandType::PrintColumn
| CommandType::Print => (),
CommandType::Help | CommandType::Version => (),
_ => {
let cursor = self
.processor
.get_cursor()
.ok_or_else(|| CedError::InvalidPageOperation("Page is empty".to_string()))?;
self.history
.take_snapshot(self.processor.get_page_data(&cursor)?, command.command_type)
}
}
if let Err(err) = self.processor.execute_command(command) {
if panic {
return Err(err);
} else {
utils::write_to_stderr(&(err.to_string() + "\n"))?;
}
}
Ok(())
}
fn print_history(&self) -> CedResult<()> {
let print = self
.history
.memento_history
.iter()
.enumerate()
.map(|(idx, record)| format!("{} : {} \n", idx, record.command))
.collect::<String>();
utils::write_to_stdout(&print)
}
fn undo(&mut self, state_backup: Option<Page>) -> CedResult<()> {
let undo_target = self.history.get_undo();
if let Some(history) = undo_target {
let cursor = self
.processor
.get_cursor()
.ok_or_else(|| CedError::InvalidPageOperation("Page is empty".to_string()))?;
*self.processor.get_page_data_mut(&cursor)? = history.data.clone();
utils::write_to_stdout(&format!("Undo \"{:#?}\"\n", history.command))?;
}
if undo_target.is_some() {
if let Some(backup) = state_backup {
self.history.set_current_backup(backup)
}
}
Ok(())
}
fn redo(&mut self) -> CedResult<()> {
if let Some(history) = self.history.get_redo() {
let cursor = self
.processor
.get_cursor()
.ok_or_else(|| CedError::InvalidPageOperation("Page is empty".to_string()))?;
*self.processor.get_page_data_mut(&cursor)? = history.data.clone();
utils::write_to_stdout(&format!("Redo \"{:#?}\"\n", history.command))?;
}
Ok(())
}
fn add_empty_page(&mut self) -> CedResult<()> {
self.processor.add_page("\\EMPTY", "", false, None, false)?;
Ok(())
}
}