use std::{env::current_dir, ffi::OsString, path::PathBuf};
use clap::Parser;
use ghee_cli::{Cli, Commands};
use ghee_lang::Key;
use rustyline::error::ReadlineError;
use rustyline::DefaultEditor;
use thiserror::Error;
use ghee::{
cmd::{
commit, cp, create, del, get, idx, init, ins, log, ls, mv, reset, restore, rm, set, status,
touch, NewFileHandling,
},
APP_NAME, PKG_NAME, XDG_DIRS,
};
#[derive(Error, Debug)]
pub enum TableOpenErr {
#[error("Table path could not be opened because it doesn't exist")]
NoSuchPath,
}
fn repl() -> rustyline::Result<()> {
println!("{} {}", *APP_NAME, env!("CARGO_PKG_VERSION"));
println!();
let prompt = format!("{}$ ", *PKG_NAME);
let history_path: PathBuf = XDG_DIRS
.place_state_file("history.txt")
.unwrap_or_else(|e| panic!("Error placing history file: {}", e));
let mut rl = DefaultEditor::new()?;
if rl.load_history(&history_path).is_err() {
}
loop {
let readline = rl.readline(&prompt);
match readline {
Ok(line) => {
rl.add_history_entry(line.as_str())?;
match Cli::try_parse_from(
std::iter::once(OsString::from(PKG_NAME.as_str()))
.chain(line.split_terminator(" ").map(OsString::from)),
) {
Ok(args) => match args.command.as_ref() {
Some(cmd) => {
run_command(cmd);
}
None => {}
},
Err(e) => {
eprintln!("{}", e);
}
}
}
Err(ReadlineError::Interrupted) => {
println!("CTRL-C");
break;
}
Err(ReadlineError::Eof) => {
println!("CTRL-D");
break;
}
Err(err) => {
println!("Error: {:?}", err);
break;
}
}
}
rl.save_history(&history_path)
.unwrap_or_else(|e| panic!("Error saving history to {}: {}", history_path.display(), e));
Ok(())
}
fn run_command(cmd: &Commands) {
match cmd {
Commands::Cp {
src,
dest,
fields,
verbose,
} => {
cp(src, dest, fields, *verbose)
.unwrap_or_else(|e| panic!("Error copying xattr(s): {}", e));
}
Commands::Mv {
src,
dest,
fields,
verbose,
} => {
mv(src, dest, fields, *verbose)
.unwrap_or_else(|e| panic!("Error moving xattr(s): {}", e));
}
Commands::Rm {
paths,
fields,
flat,
force,
verbose,
} => {
rm(paths, fields, !*flat, *force, *verbose)
.unwrap_or_else(|e| panic!("Error removing xattr(s): {}", e));
}
Commands::Get {
paths,
fields,
json,
where_,
flat,
all,
sort,
visit_empty,
} => get(
paths,
fields,
*json,
where_,
!*flat,
*all,
*sort,
*visit_empty,
)
.unwrap_or_else(|e| {
panic!("Error getting record(s): {}", e);
}),
Commands::Set {
paths,
field_assignments,
flat,
verbose,
} => set(paths, field_assignments, !*flat, *verbose)
.unwrap_or_else(|e| panic!("Error setting xattrs: {}", e)),
Commands::Ins {
table_path,
records_path,
verbose,
} => {
ins(table_path, records_path, *verbose).unwrap_or_else(|e| {
panic!(
"Error inserting record(s) into {}: {}",
table_path.display(),
e
)
});
}
Commands::Del {
table_path,
where_,
key,
verbose,
} => {
del(table_path, where_, key, *verbose).unwrap_or_else(|e| {
panic!("Error deleting record from {}: {}", table_path.display(), e)
});
}
Commands::Idx {
src,
dest,
keys,
verbose,
} => {
let key = Key::new(keys.clone());
idx(src, dest.as_ref(), &key, *verbose).unwrap_or_else(|e| {
panic!(
"Error indexing src {} to dest {:?} with keys {:?}: {}",
src.display(),
dest,
keys,
e
)
});
}
Commands::Ls { paths, sort } => {
ls(paths, *sort).unwrap_or_else(|e| panic!("Error listing {:?}: {}", paths, e));
}
Commands::Init {
dir,
keys,
records_path,
verbose,
} => {
let key = Key::new(keys.clone());
init(dir, &key, *verbose)
.unwrap_or_else(|e| panic!("Error initializing table {}: {}", dir.display(), e));
if atty::isnt(atty::Stream::Stdin) {
ins(dir, records_path, *verbose).unwrap_or_else(|e| {
panic!("Error inserting record(s) into {}: {}", dir.display(), e)
});
}
}
Commands::Create {
dir,
keys,
records_path,
verbose,
} => {
let key = Key::new(keys.clone());
create(dir, &key, *verbose)
.unwrap_or_else(|e| panic!("Error creating table {}: {}", dir.display(), e));
if atty::isnt(atty::Stream::Stdin) {
ins(dir, records_path, *verbose).unwrap_or_else(|e| {
panic!("Error inserting record(s) into {}: {}", dir.display(), e)
});
}
}
Commands::Commit {
dir,
message,
verbose,
} => {
let cur = current_dir().unwrap();
let dir = dir.as_ref().unwrap_or(&cur);
let uuid = commit(dir, message, *verbose)
.unwrap_or_else(|e| panic!("Error committing {}: {}", dir.display(), e));
println!("{}", uuid);
}
Commands::Log { dir } => {
let cur = current_dir().unwrap();
let dir = dir.as_ref().unwrap_or(&cur);
log(dir).unwrap_or_else(|e| panic!("Could not print commit log: {}", e));
}
Commands::Touch { path, parents } => {
touch(path, *parents)
.unwrap_or_else(|e| panic!("Could not touch path {}: {}", path.display(), e));
}
Commands::Status { path } => {
let cur = current_dir().unwrap();
let path = path.as_ref().unwrap_or(&cur);
status(path)
.unwrap_or_else(|e| panic!("Error printing status for {}: {}", cur.display(), e));
}
Commands::Restore {
paths,
keep,
flat,
verbose,
} => {
let new_file_handling = if *keep {
NewFileHandling::Keep
} else {
NewFileHandling::Delete
};
restore(paths, !*flat, *verbose, new_file_handling)
.unwrap_or_else(|e| panic!("Error restoring paths {:?}: {}", paths, e));
}
Commands::Reset {
commit_uuid,
keep,
verbose,
} => {
let new_file_handling = if *keep {
NewFileHandling::Keep
} else {
NewFileHandling::Delete
};
reset(commit_uuid, *verbose, new_file_handling)
.unwrap_or_else(|e| panic!("Error resetting to {}: {}", commit_uuid, e));
}
}
}
fn main() {
let cli = Cli::parse();
match cli.command.as_ref() {
None => repl().unwrap_or_else(|e| panic!("Could not initialize REPL: {}", e)),
Some(command) => run_command(command),
}
}