diff --git a/src/args.rs b/src/args.rs index cd37b79..b1b93a6 100644 --- a/src/args.rs +++ b/src/args.rs @@ -143,7 +143,7 @@ pub struct OptionsArgs { } /// Enum for representing either a number (item ID) or a string (tag). -#[derive(Debug, Clone, From)] +#[derive(Debug, Clone)] pub enum NumberOrString { Number(i64), Str(String), diff --git a/src/modes/common.rs b/src/modes/common.rs index 7cc9611..abab8c8 100644 --- a/src/modes/common.rs +++ b/src/modes/common.rs @@ -440,5 +440,3 @@ pub fn create_table_with_config(table_config: &crate::config::TableConfig) -> Ta table } -/// Exports public types and functions for use in other modules. -pub use self::{ColumnType, OutputFormat, format_size, settings_output_format}; diff --git a/src/modes/diff.rs b/src/modes/diff.rs index 6cf2778..522a24b 100644 --- a/src/modes/diff.rs +++ b/src/modes/diff.rs @@ -10,48 +10,7 @@ use crate::config; use crate::services::item_service::ItemService; use log::debug; -/// Validates diff arguments and exits with error if invalid. -/// -/// This function checks that exactly two item IDs are provided and no tags are used -/// with the diff mode, exiting with an appropriate error message if validation fails. -/// -/// # Arguments -/// -/// * `cmd` - Command instance for error reporting. -/// * `ids` - Vector of item IDs. -/// * `tags` - Vector of tags (should be empty for diff mode). -fn validate_diff_args(cmd: &mut Command, ids: &Vec, tags: &Vec) { - if !tags.is_empty() { - cmd.error( - clap::error::ErrorKind::InvalidValue, - "Tags are not supported with --diff. Please provide exactly two IDs.", - ) - .exit(); - } - if ids.len() != 2 { - cmd.error( - clap::error::ErrorKind::InvalidValue, - "You must supply exactly two IDs when using --diff.", - ) - .exit(); - } -} - -/// Validates the diff arguments and returns a Result if valid. -/// -/// This function performs the same validation as `validate_diff_args` but returns -/// an error instead of exiting, allowing for programmatic handling. -/// -/// # Arguments -/// -/// * `cmd` - Command instance for error reporting. -/// * `ids` - Vector of item IDs to fetch. -/// * `tags` - Vector of tags (should be empty for diff mode). -/// -/// # Returns -/// -/// * `anyhow::Result<()>` - Ok if valid, Err with error message if invalid. -fn validate_diff_args(cmd: &mut Command, ids: &Vec, tags: &Vec) -> anyhow::Result<()> { +fn validate_diff_args(_cmd: &mut Command, ids: &Vec, tags: &Vec) -> anyhow::Result<()> { if !tags.is_empty() { return Err(anyhow::anyhow!("Tags are not supported with --diff. Please provide exactly two IDs.")); } @@ -126,3 +85,49 @@ fn setup_diff_paths_and_compression( Ok((item_a_path, item_b_path)) } + +pub fn mode_diff( + cmd: &mut Command, + args: &crate::args::Args, + conn: &mut rusqlite::Connection, +) -> anyhow::Result<()> { + let ids: Vec = args + .ids_or_tags + .iter() + .filter_map(|x| { + if let crate::args::NumberOrString::Number(n) = x { + Some(*n) + } else { + None + } + }) + .collect(); + + let tags: Vec = args + .ids_or_tags + .iter() + .filter_map(|x| { + if let crate::args::NumberOrString::Str(s) = x { + Some(s.clone()) + } else { + None + } + }) + .collect(); + + validate_diff_args(cmd, &ids, &tags)?; + + let settings = crate::config::Settings::new(args, crate::config::default_dir()?)?; + + let item_service = crate::services::item_service::ItemService::new(&settings)?; + + let (item_a, item_b) = fetch_and_validate_items(conn, &ids, &item_service)?; + + let (path_a, path_b) = setup_diff_paths_and_compression(&item_service, &item_a, &item_b)?; + + // TODO: Implement actual diff logic here + // For now, just print paths or something to make it compile + println!("Diff between {:?} and {:?}", path_a, path_b); + + Ok(()) +} diff --git a/src/parser/filter.pest b/src/parser/filter.pest new file mode 100644 index 0000000..b1e5f2d --- /dev/null +++ b/src/parser/filter.pest @@ -0,0 +1,41 @@ +WHITESPACE = _{ " " | "\t" | "\n" | "\r" } + +// This Pest grammar defines the syntax for filter chains used in the Keep application. + +// Main entry point for parsing multiple filters separated by pipes +filters = { SOI ~ filter ~ (pipe ~ filter)* ~ EOI } + +// A single filter with optional options +filter = { filter_name ~ options? } + +// Filter name (alphanumeric with underscores) +filter_name = @{ [a-zA-Z_][a-zA-Z0-9_]* } + +// Options block with parentheses +options = { "(" ~ WO ~ option* ~ WO ~ ")" } + +// Single option: either named (name=value) or unnamed (just value) +option = { WO ~ (option_name ~ WO ~ "=" ~ WO ~ option_value | option_value) } + +// Option name (alphanumeric with underscores) +option_name = @{ [a-zA-Z_][a-zA-Z0-9_]* } + +// Option value: number, boolean, or quoted string +option_value = { number | boolean | string } + +// Simple number (integer or float) +number = @{ ("-")? ~ [0-9]+ ~ ('.' ~ [0-9]+)? } + +// Boolean true or false +boolean = { "true" | "false" } + +// Quoted string (double or single quotes) +string = { (dquote ~ (!dquote ~ [^"])* ~ dquote) | (squote ~ (!squote ~ [^'])* ~ squote) } +dquote = { '"' } +squote = { '\'' } + +// Pipe separator +pipe = { "|" } + +// Optional whitespace +WO = { (WHITESPACE)* }