223 lines
7.5 KiB
Rust
223 lines
7.5 KiB
Rust
use anyhow::{Context, Error, Result, anyhow};
|
|
use clap::*;
|
|
use clap::error::ErrorKind;
|
|
use log::*;
|
|
|
|
use directories::ProjectDirs;
|
|
|
|
use keep::args::{Args, NumberOrString};
|
|
use keep::config::Settings;
|
|
use keep::db;
|
|
use keep::modes;
|
|
|
|
/**
|
|
* Main function to handle command-line arguments and execute the appropriate mode.
|
|
*/
|
|
fn main() -> Result<(), Error> {
|
|
use std::fs;
|
|
let proj_dirs = ProjectDirs::from("gt0.ca", "Andrew Phillips", "Keep");
|
|
|
|
let mut cmd = Args::command();
|
|
let args = Args::parse();
|
|
|
|
// Validate arguments based on mode
|
|
if let Err(e) = args.validate() {
|
|
cmd.error(ErrorKind::ValueValidation, e).exit();
|
|
}
|
|
|
|
stderrlog::new()
|
|
.module(module_path!())
|
|
.quiet(args.options.quiet)
|
|
.verbosity(usize::from(args.options.verbose + 2))
|
|
//.timestamp(stderrlog::Timestamp::Second)
|
|
.init()
|
|
.unwrap();
|
|
|
|
debug!("MAIN: Start");
|
|
|
|
// Determine default data directory
|
|
let default_dir = match proj_dirs {
|
|
Some(ref proj_dirs) => proj_dirs.data_dir().to_path_buf(),
|
|
None => return Err(anyhow!("Unable to determine data directory")),
|
|
};
|
|
|
|
// Create unified settings using the new config system
|
|
let settings = Settings::new(&args, default_dir)?;
|
|
|
|
debug!("MAIN: Loaded settings: {:?}", settings);
|
|
|
|
let ids = &mut Vec::new();
|
|
let tags = &mut Vec::new();
|
|
|
|
// For --info and --get modes, treat numeric strings as IDs
|
|
for v in args.ids_or_tags.iter() {
|
|
debug!("MAIN: Parsed value: {:?}", v);
|
|
match v.clone() {
|
|
NumberOrString::Number(num) => {
|
|
debug!("MAIN: Adding to ids: {}", num);
|
|
ids.push(num)
|
|
},
|
|
NumberOrString::Str(str) => {
|
|
// For --info and --get, try to parse strings as numbers to treat them as IDs
|
|
if args.mode.info || args.mode.get {
|
|
if let Ok(num) = str.parse::<i64>() {
|
|
debug!("MAIN: Adding parsed string to ids: {}", num);
|
|
ids.push(num);
|
|
continue;
|
|
} else if args.mode.info {
|
|
// --info only accepts numeric IDs
|
|
cmd.error(
|
|
ErrorKind::InvalidValue,
|
|
format!("--info requires numeric IDs, found: '{}'", str)
|
|
).exit();
|
|
}
|
|
}
|
|
// If not a number, or not using --info/--get, treat as tag
|
|
debug!("MAIN: Adding to tags: {}", str);
|
|
tags.push(str)
|
|
},
|
|
}
|
|
}
|
|
tags.sort();
|
|
tags.dedup();
|
|
|
|
/// Internal enum representing the parsed execution mode.
|
|
#[derive(PartialEq, Debug)]
|
|
enum KeepModes {
|
|
Unknown,
|
|
Save,
|
|
Get,
|
|
Diff,
|
|
List,
|
|
Delete,
|
|
Info,
|
|
Status,
|
|
StatusPlugins,
|
|
Server,
|
|
GenerateConfig,
|
|
}
|
|
|
|
let mut mode: KeepModes = KeepModes::Unknown;
|
|
|
|
if args.mode.save {
|
|
mode = KeepModes::Save;
|
|
} else if args.mode.get {
|
|
mode = KeepModes::Get;
|
|
} else if args.mode.diff {
|
|
mode = KeepModes::Diff;
|
|
} else if args.mode.list {
|
|
mode = KeepModes::List;
|
|
} else if args.mode.delete {
|
|
mode = KeepModes::Delete;
|
|
} else if args.mode.info {
|
|
mode = KeepModes::Info;
|
|
} else if args.mode.status {
|
|
mode = KeepModes::Status;
|
|
} else if args.mode.status_plugins {
|
|
mode = KeepModes::StatusPlugins;
|
|
} else if args.mode.server {
|
|
mode = KeepModes::Server;
|
|
} else if args.mode.generate_config {
|
|
mode = KeepModes::GenerateConfig;
|
|
}
|
|
|
|
if mode == KeepModes::Unknown {
|
|
if !ids.is_empty() {
|
|
mode = KeepModes::Get;
|
|
} else {
|
|
mode = KeepModes::Save;
|
|
}
|
|
}
|
|
|
|
// Validate output format usage
|
|
if let Some(output_format_str) = &settings.output_format {
|
|
if output_format_str != "table" && mode != KeepModes::Info && mode != KeepModes::Status && mode != KeepModes::StatusPlugins && mode != KeepModes::List {
|
|
cmd.error(
|
|
ErrorKind::InvalidValue,
|
|
"--output-format can only be used with --info, --status, --status-plugins, or --list modes"
|
|
).exit();
|
|
}
|
|
}
|
|
|
|
// Validate human-readable usage
|
|
if settings.human_readable && mode != KeepModes::List && mode != KeepModes::Info {
|
|
cmd.error(
|
|
ErrorKind::InvalidValue,
|
|
"--human-readable can only be used with --list and --info modes"
|
|
).exit();
|
|
}
|
|
|
|
// Validate server password usage
|
|
if settings.server_password().is_some() && mode != KeepModes::Server {
|
|
cmd.error(
|
|
ErrorKind::InvalidValue,
|
|
"--server-password can only be used with --server mode"
|
|
).exit();
|
|
}
|
|
|
|
debug!("MAIN: args: {:?}", args);
|
|
debug!("MAIN: ids: {:?}", ids);
|
|
debug!("MAIN: tags: {:?}", tags);
|
|
debug!("MAIN: mode: {:?}", mode);
|
|
debug!("MAIN: settings: {:?}", settings);
|
|
|
|
unsafe {
|
|
libc::umask(0o077);
|
|
}
|
|
|
|
let data_path = settings.dir.clone();
|
|
let mut db_path = data_path.clone();
|
|
db_path.push("keep-1.db");
|
|
|
|
debug!("MAIN: Data directory: {:?}", data_path);
|
|
debug!("MAIN: DB file: {:?}", db_path);
|
|
|
|
// Ensure data directory exists
|
|
fs::create_dir_all(&data_path)
|
|
.with_context(|| format!("Unable to create data directory {:?}", data_path))?;
|
|
|
|
// Initialize database
|
|
let mut conn = db::open(db_path.clone())?;
|
|
|
|
// Parse filter chain early for better error reporting
|
|
let filter_chain = if let Some(filter_str) = &args.item.filters {
|
|
match keep::filter_plugin::parse_filter_string(filter_str) {
|
|
Ok(chain) => Some(chain),
|
|
Err(e) => {
|
|
cmd.error(
|
|
ErrorKind::InvalidValue,
|
|
format!("Invalid filter string: {}", e)
|
|
).exit();
|
|
}
|
|
}
|
|
} else {
|
|
None
|
|
};
|
|
|
|
match mode {
|
|
KeepModes::Save => modes::save::mode_save(&mut cmd, &settings, ids, tags, &mut conn, data_path),
|
|
KeepModes::Get => modes::get::mode_get(&mut cmd, &settings, ids, tags, &mut conn, data_path, filter_chain),
|
|
KeepModes::Diff => modes::diff::mode_diff(&mut cmd, &settings, &settings, ids, tags, &mut conn, data_path),
|
|
KeepModes::List => modes::list::mode_list(&mut cmd, &settings, ids, tags, &mut conn, data_path),
|
|
KeepModes::Delete => modes::delete::mode_delete(&mut cmd, &settings, &settings, ids, tags, &mut conn, data_path),
|
|
KeepModes::Info => modes::info::mode_info(&mut cmd, &settings, ids, tags, &mut conn, data_path),
|
|
KeepModes::Status => modes::status::mode_status(&mut cmd, &settings, data_path, db_path),
|
|
KeepModes::StatusPlugins => modes::status_plugins::mode_status_plugins(&mut cmd, &settings, data_path, db_path),
|
|
KeepModes::Server => {
|
|
#[cfg(feature = "server")]
|
|
{
|
|
modes::server::mode_server(&mut cmd, &settings, &mut conn, data_path)
|
|
}
|
|
#[cfg(not(feature = "server"))]
|
|
{
|
|
cmd.error(
|
|
ErrorKind::MissingRequiredArgument,
|
|
"This binary was not compiled with server support. Recompile with --features server"
|
|
).exit();
|
|
}
|
|
},
|
|
KeepModes::GenerateConfig => modes::generate_config::mode_generate_config(&mut cmd, &settings),
|
|
KeepModes::Unknown => unreachable!(),
|
|
}
|
|
}
|