135 lines
4.1 KiB
Rust
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(())
|
|
}
|