fix: use item service for proper item handling

Co-authored-by: aider (openai/andrew/openrouter/deepseek/deepseek-chat-v3.1) <aider@aider.chat>
This commit is contained in:
Andrew Phillips
2025-08-25 21:47:06 -03:00
parent 9b525445f3
commit e97807f7fa

View File

@@ -2,8 +2,8 @@ use anyhow::{anyhow, Context, Result};
use clap::Command; use clap::Command;
use std::io::Read; use std::io::Read;
use std::os::fd::FromRawFd; use std::os::fd::FromRawFd;
use std::str::FromStr;
use crate::config; use crate::config;
use crate::services::item_service::ItemService;
fn validate_diff_args(cmd: &mut Command, ids: &Vec<i64>, tags: &Vec<String>) { fn validate_diff_args(cmd: &mut Command, ids: &Vec<i64>, tags: &Vec<String>) {
if !tags.is_empty() { if !tags.is_empty() {
@@ -22,15 +22,12 @@ fn validate_diff_args(cmd: &mut Command, ids: &Vec<i64>, tags: &Vec<String>) {
} }
} }
use crate::services::item_service::ItemService;
use crate::services::types::ItemWithMeta;
fn fetch_and_validate_items( fn fetch_and_validate_items(
conn: &mut rusqlite::Connection, conn: &mut rusqlite::Connection,
ids: &Vec<i64>, ids: &Vec<i64>,
item_service: &ItemService, item_service: &ItemService,
) -> Result<(ItemWithMeta, ItemWithMeta)> { ) -> Result<(crate::services::types::ItemWithMeta, crate::services::types::ItemWithMeta)> {
// Fetch items, ensuring they exist. // Fetch items using the service, which handles validation
let item_a = item_service let item_a = item_service
.get_item(conn, ids[0]) .get_item(conn, ids[0])
.with_context(|| format!("Unable to find first item (ID: {}) in database", ids[0]))?; .with_context(|| format!("Unable to find first item (ID: {}) in database", ids[0]))?;
@@ -41,47 +38,29 @@ fn fetch_and_validate_items(
log::debug!("MAIN: Found item A {:?}", item_a.item); log::debug!("MAIN: Found item A {:?}", item_a.item);
log::debug!("MAIN: Found item B {:?}", item_b.item); log::debug!("MAIN: Found item B {:?}", item_b.item);
let item_a_id = item_a.item.id.ok_or_else(|| anyhow!("Item A missing ID"))?;
let item_b_id = item_b.item.id.ok_or_else(|| anyhow!("Item B missing ID"))?;
// Validate that item IDs are positive to prevent path traversal issues
if item_a_id <= 0 || item_b_id <= 0 {
return Err(anyhow::anyhow!(
"Invalid item ID: {} or {}",
item_a_id,
item_b_id
));
}
Ok((item_a, item_b)) Ok((item_a, item_b))
} }
fn setup_diff_paths_and_compression( fn setup_diff_paths_and_compression(
data_path: &std::path::PathBuf, item_service: &ItemService,
item_a: &ItemWithMeta, item_a: &crate::services::types::ItemWithMeta,
item_b: &ItemWithMeta, item_b: &crate::services::types::ItemWithMeta,
) -> Result<( ) -> Result<(
std::path::PathBuf, std::path::PathBuf,
crate::compression_engine::CompressionType,
std::path::PathBuf, std::path::PathBuf,
crate::compression_engine::CompressionType,
)> { )> {
let item_a_id = item_a.item.id.ok_or_else(|| anyhow::anyhow!("Item A missing ID"))?; let item_a_id = item_a.item.id.ok_or_else(|| anyhow::anyhow!("Item A missing ID"))?;
let item_b_id = item_b.item.id.ok_or_else(|| anyhow::anyhow!("Item B missing ID"))?; let item_b_id = item_b.item.id.ok_or_else(|| anyhow::anyhow!("Item B missing ID"))?;
// Use the service's data path to construct proper file paths
let data_path = item_service.get_data_path();
let mut item_path_a = data_path.clone(); let mut item_path_a = data_path.clone();
item_path_a.push(item_a_id.to_string()); item_path_a.push(item_a_id.to_string());
let compression_type_a =
crate::compression_engine::CompressionType::from_str(&item_a.item.compression)?;
log::debug!("MAIN: Item A has compression type {:?}", compression_type_a);
let mut item_path_b = data_path.clone(); let mut item_path_b = data_path.clone();
item_path_b.push(item_b_id.to_string()); item_path_b.push(item_b_id.to_string());
let compression_type_b =
crate::compression_engine::CompressionType::from_str(&item_b.item.compression)?;
log::debug!("MAIN: Item B has compression type {:?}", compression_type_b);
Ok((item_path_a, compression_type_a, item_path_b, compression_type_b)) Ok((item_path_a, item_path_b))
} }
fn setup_diff_pipes() -> Result<((libc::c_int, libc::c_int), (libc::c_int, libc::c_int)), anyhow::Error> { fn setup_diff_pipes() -> Result<((libc::c_int, libc::c_int), (libc::c_int, libc::c_int)), anyhow::Error> {
@@ -167,14 +146,20 @@ fn write_item_to_pipe(
compression_type: crate::compression_engine::CompressionType, compression_type: crate::compression_engine::CompressionType,
pipe_writer_raw: std::fs::File, pipe_writer_raw: std::fs::File,
) -> Result<(), anyhow::Error> { ) -> Result<(), anyhow::Error> {
use std::io::BufWriter; use std::io::{BufWriter, BufReader};
let mut buffered_pipe_writer = BufWriter::new(pipe_writer_raw); let mut buffered_pipe_writer = BufWriter::new(pipe_writer_raw);
let engine =
crate::compression_engine::get_compression_engine(compression_type).expect("Unable to get compression engine"); // Get a reader from the compression engine
let engine = crate::compression_engine::get_compression_engine(compression_type)
.map_err(|e| anyhow::anyhow!("Unable to get compression engine: {}", e))?;
let mut reader = engine.open(item_path)
.map_err(|e| anyhow::anyhow!("Failed to open item: {}", e))?;
log::debug!("THREAD: Sending item to diff"); log::debug!("THREAD: Sending item to diff");
engine std::io::copy(&mut reader, &mut buffered_pipe_writer)
.copy(item_path, &mut buffered_pipe_writer) .map_err(|e| anyhow::anyhow!("Failed to copy item to pipe: {}", e))?;
.map_err(|e| anyhow::anyhow!("Failed to copy/compress item: {}", e))?;
log::debug!("THREAD: Done sending item to diff"); log::debug!("THREAD: Done sending item to diff");
Ok(()) Ok(())
} }
@@ -317,8 +302,11 @@ pub fn mode_diff(
let item_a_tags: Vec<String> = item_a.tags.iter().map(|t| t.name.clone()).collect(); let item_a_tags: Vec<String> = item_a.tags.iter().map(|t| t.name.clone()).collect();
let item_b_tags: Vec<String> = item_b.tags.iter().map(|t| t.name.clone()).collect(); let item_b_tags: Vec<String> = item_b.tags.iter().map(|t| t.name.clone()).collect();
let (item_path_a, compression_type_a, item_path_b, compression_type_b) = let (item_path_a, item_path_b) = setup_diff_paths_and_compression(&item_service, &item_a, &item_b)?;
setup_diff_paths_and_compression(&data_path, &item_a, &item_b)?;
// Get compression types from the items
let compression_type_a = item_a.item.compression.parse()?;
let compression_type_b = item_b.item.compression.parse()?;
let ((fd_a_read, fd_a_write), (fd_b_read, fd_b_write)) = setup_diff_pipes()?; let ((fd_a_read, fd_a_write), (fd_b_read, fd_b_write)) = setup_diff_pipes()?;
let (_fd_a_read_guard, _fd_b_read_guard) = setup_fd_guards(fd_a_read, fd_b_read); let (_fd_a_read_guard, _fd_b_read_guard) = setup_fd_guards(fd_a_read, fd_b_read);