Files
keep/src/modes/save.rs
Andrew Phillips 538a67ee14 docs: Add comprehensive rustdoc to save mode functions and types
Co-authored-by: aider (openai/andrew/openrouter/sonoma-sky-alpha) <aider@aider.chat>
2025-09-10 12:54:57 -03:00

135 lines
4.1 KiB
Rust

use anyhow::Result;
use clap::Command;
use std::io::{Read, Write};
use crate::config;
use crate::services::item_service::ItemService;
/// Validates save mode arguments and exits with error if invalid.
///
/// This function checks that no item IDs are provided for save mode,
/// as save operations create new items rather than modifying existing ones.
///
/// # Arguments
///
/// * `cmd` - Mutable reference to the Clap command for error reporting.
/// * `ids` - Reference to the vector of item IDs (should be empty for save mode).
///
/// # Panics
///
/// Exits the program via Clap error if IDs are provided.
fn validate_save_args(cmd: &mut Command, ids: &Vec<i64>) {
if !ids.is_empty() {
cmd.error(
clap::error::ErrorKind::InvalidValue,
"ID given, you cannot supply IDs when using --save",
)
.exit();
}
}
/// A tee reader that duplicates input to both a reader and a writer as it reads.
///
/// This struct implements the `Read` trait and forwards all read operations to
/// an underlying reader while simultaneously writing the same data to a writer.
/// It's useful for saving content to a file while also echoing it to stdout.
///
/// # Fields
///
/// * `reader` - The underlying reader providing the data source.
/// * `writer` - The writer receiving copies of all read data.
struct TeeReader<R: Read, W: Write> {
reader: R,
writer: W,
}
impl<R: Read, W: Write> Read for TeeReader<R, W> {
/// Reads data from the underlying reader and duplicates it to the writer.
///
/// This implementation reads from the inner reader and then writes the same
/// bytes to the writer. If the read returns 0 bytes (EOF), it returns 0.
///
/// # Arguments
///
/// * `buf` - Buffer to fill with data from the reader.
///
/// # Returns
///
/// * `io::Result<usize>` - Number of bytes read, or an I/O error.
///
/// # Errors
///
/// Returns an error if the underlying read or write operations fail.
///
/// # Examples
///
/// ```
/// let mut tee = TeeReader {
/// reader: std::io::Cursor::new(b"Hello, world!"),
/// writer: std::io::sink(),
/// };
/// let mut buf = [0; 5];
/// let n = tee.read(&mut buf).unwrap();
/// assert_eq!(n, 5);
/// assert_eq!(&buf[..n], b"Hello");
/// ```
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let n = self.reader.read(buf)?;
if n > 0 {
self.writer.write_all(&buf[..n])?;
}
Ok(n)
}
}
/// Main save mode function.
///
/// This function handles the save operation by reading from stdin, duplicating
/// the input to stdout (for real-time display), and saving the content to the
/// item service. It validates arguments, creates the tee reader, and processes
/// the save operation.
///
/// # Arguments
///
/// * `cmd` - Mutable reference to the Clap command for error handling.
/// * `settings` - Application settings containing configuration.
/// * `ids` - Mutable vector of item IDs (should be empty for save mode).
/// * `tags` - Mutable vector of tags to associate with the new item.
/// * `conn` - Mutable reference to the database connection.
/// * `data_path` - Path to the data storage directory.
///
/// # Returns
///
/// * `Result<(), anyhow::Error>` - Success or error if save fails.
///
/// # Examples
///
/// ```
/// // In CLI context, this would be called internally
/// mode_save(&mut cmd, &settings, &mut vec![], &mut vec!["important".to_string()], &mut conn, data_path)?;
/// ```
pub fn mode_save(
cmd: &mut Command,
settings: &config::Settings,
ids: &mut Vec<i64>,
tags: &mut Vec<String>,
conn: &mut rusqlite::Connection,
data_path: std::path::PathBuf,
) -> Result<(), anyhow::Error> {
validate_save_args(cmd, ids);
let item_service = ItemService::new(data_path);
let stdin = std::io::stdin();
let stdout = std::io::stdout();
let tee_reader = TeeReader {
reader: stdin.lock(),
writer: stdout.lock(),
};
item_service.save_item(tee_reader, cmd, settings, tags, conn)?;
Ok(())
}