diff --git a/src/cli/mod.rs b/src/cli/mod.rs index e5f76aa..519a910 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -50,6 +50,10 @@ pub enum Commands { /// Pop the most recent item with the specified tags (comma-separated) #[arg(long, short = 't', value_delimiter = ',')] tags: Option>, + + /// Custom output directory path (defaults to current directory) + #[arg(long = "output", short = 'o')] + output: Option, }, /// List all items in the stack diff --git a/src/cli/pop.rs b/src/cli/pop.rs index 8fea7f3..4972705 100644 --- a/src/cli/pop.rs +++ b/src/cli/pop.rs @@ -6,11 +6,37 @@ use crate::db::{establish_connection, get_stored_path, ItemManager}; use crate::fs; use crate::utils::numbers::parse_number_range; -/// Pop items from the stack and restore them to the current directory. -pub fn pop(numbers: Option, tags: Option>) -> Result<()> { +/// Pop items from the stack and restore them to the current directory or a specified output directory. +pub fn pop( + numbers: Option, + tags: Option>, + output: Option, +) -> Result<()> { let tag_vec = tags.unwrap_or_default(); let filter_by_tags = !tag_vec.is_empty(); + // Determine output directory (default to current directory if not specified) + let output_dir = match &output { + Some(path) => { + let dir_path = std::path::PathBuf::from(path); + // Check if the output directory exists and is a directory + if !dir_path.exists() { + return Err(anyhow!( + "Output directory does not exist: {}", + dir_path.display() + )); + } + if !dir_path.is_dir() { + return Err(anyhow!( + "Specified output path is not a directory: {}", + dir_path.display() + )); + } + dir_path + } + None => env::current_dir()?, + }; + // Connect to database let mut conn = establish_connection()?; @@ -25,11 +51,8 @@ pub fn pop(numbers: Option, tags: Option>) -> Result<()> { ItemManager::get_latest(&conn)?.ok_or_else(|| anyhow!("No items in the stack"))? }; - // Get current directory - let current_dir = env::current_dir()?; - - // Construct destination path - let dest_path = current_dir.join(&item.original_name); + // Construct destination path using output_dir + let dest_path = output_dir.join(&item.original_name); // Check if destination already exists if fs::check_destination_conflict(&dest_path) { @@ -123,8 +146,7 @@ pub fn pop(numbers: Option, tags: Option>) -> Result<()> { } } - // Get current directory - let current_dir = env::current_dir()?; + // Output directory is already determined above // Track statistics let mut success_count = 0; @@ -136,8 +158,8 @@ pub fn pop(numbers: Option, tags: Option>) -> Result<()> { // Process all items atomically (based on the initial state) for (display_number, item) in items_to_process { - // Construct destination path in current directory - let dest_path = current_dir.join(&item.original_name); + // Construct destination path in output directory + let dest_path = output_dir.join(&item.original_name); // Check if destination already exists if fs::check_destination_conflict(&dest_path) { diff --git a/src/db/item.rs b/src/db/item.rs index 300050c..3287596 100644 --- a/src/db/item.rs +++ b/src/db/item.rs @@ -29,8 +29,9 @@ impl StackItem { let naive_dt = chrono::NaiveDateTime::parse_from_str(&pushed_at_str, "%Y-%m-%d %H:%M:%S") .map_err(|e| anyhow!("Error parsing date: {}", e))?; // First interpret as UTC, then convert to local time - let pushed_at = chrono::DateTime::::from_naive_utc_and_offset(naive_dt, chrono::Utc) - .with_timezone(&Local); + let pushed_at = + chrono::DateTime::::from_naive_utc_and_offset(naive_dt, chrono::Utc) + .with_timezone(&Local); Ok(StackItem { id, diff --git a/src/main.rs b/src/main.rs index f485712..d458362 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,8 +20,12 @@ fn main() -> Result<()> { cli::push::push(&path, tags)?; } - Commands::Pop { numbers, tags } => { - cli::pop::pop(numbers, tags)?; + Commands::Pop { + numbers, + tags, + output, + } => { + cli::pop::pop(numbers, tags, output)?; } Commands::List { tags } => {