feat: Add --filter option to --get mode for content filtering

Co-authored-by: aider (openai/andrew/openrouter/deepseek/deepseek-chat-v3.1) <aider@aider.chat>
This commit is contained in:
Andrew Phillips
2025-09-02 17:21:52 -03:00
parent 95b703b301
commit 44740518a7
2 changed files with 46 additions and 16 deletions

View File

@@ -8,6 +8,7 @@ use crate::services::item_service::ItemService;
use clap::Command; use clap::Command;
use is_terminal::IsTerminal; use is_terminal::IsTerminal;
use std::path::PathBuf; use std::path::PathBuf;
use std::io::Read;
pub fn mode_get( pub fn mode_get(
cmd: &mut Command, cmd: &mut Command,
@@ -16,6 +17,7 @@ pub fn mode_get(
tags: &mut Vec<String>, tags: &mut Vec<String>,
conn: &mut rusqlite::Connection, conn: &mut rusqlite::Connection,
data_path: PathBuf, data_path: PathBuf,
filters: &Vec<String>,
) -> Result<()> { ) -> Result<()> {
if !ids.is_empty() && !tags.is_empty() { if !ids.is_empty() && !tags.is_empty() {
cmd.error(clap::error::ErrorKind::InvalidValue, "Both ID and tags given, you must supply either IDs or tags when using --get").exit(); cmd.error(clap::error::ErrorKind::InvalidValue, "Both ID and tags given, you must supply either IDs or tags when using --get").exit();
@@ -46,10 +48,27 @@ pub fn mode_get(
} }
} }
// Use streaming approach to handle large files // Join all filter strings with | to create a single filter string
let mut reader = item_service.get_compression_service().stream_item_content( let filter_str = if filters.is_empty() {
data_path.join(item_id.to_string()), None
&item_with_meta.item.compression } else {
Some(filters.join(" | "))
};
// Get a reader that applies the filters
let (mut reader, _, _) = item_service.get_item_content_info_streaming(
conn,
item_id,
None, // head_bytes
None, // head_words
None, // head_lines
None, // tail_bytes
None, // tail_words
None, // tail_lines
None, // line_start
None, // line_end
None, // grep
filter_str,
)?; )?;
if detect_binary { if detect_binary {
@@ -62,10 +81,21 @@ pub fn mode_get(
)); ));
} }
// We need to create a new reader since we consumed some bytes // We need to create a new reader since we consumed some bytes
reader = item_service.get_compression_service().stream_item_content( let (new_reader, _, _) = item_service.get_item_content_info_streaming(
data_path.join(item_id.to_string()), conn,
&item_with_meta.item.compression item_id,
None,
None,
None,
None,
None,
None,
None,
None,
None,
filter_str,
)?; )?;
reader = new_reader;
} }
// Stream the content to stdout // Stream the content to stdout

View File

@@ -190,14 +190,13 @@ impl ItemService {
filter_parts.push(format!("tail_lines({})", lines)); filter_parts.push(format!("tail_lines({})", lines));
} }
// Use the provided filter string if available, otherwise build from parts // Handle the provided filter string
let filter_str = filter.or_else(|| { let filter_str = match (filter, filter_parts.is_empty()) {
if filter_parts.is_empty() { (Some(f), true) => Some(f),
None (Some(f), false) => Some(format!("{} | {}", f, filter_parts.join(" | "))),
} else { (None, true) => None,
Some(filter_parts.join(" | ")) (None, false) => Some(filter_parts.join(" | ")),
} };
});
// Create filter chain // Create filter chain
self.filter_service.create_filter_chain(filter_str.as_deref()) self.filter_service.create_filter_chain(filter_str.as_deref())
@@ -239,6 +238,7 @@ impl ItemService {
_line_start: Option<usize>, _line_start: Option<usize>,
_line_end: Option<usize>, _line_end: Option<usize>,
grep: Option<String>, grep: Option<String>,
filter: Option<String>,
) -> Result<(Box<dyn Read + Send>, String, bool), CoreError> { ) -> Result<(Box<dyn Read + Send>, String, bool), CoreError> {
let item_with_meta = self.get_item(conn, id)?; let item_with_meta = self.get_item(conn, id)?;
let item_id = item_with_meta.item.id.ok_or_else(|| CoreError::InvalidInput("Item missing ID".to_string()))?; let item_id = item_with_meta.item.id.ok_or_else(|| CoreError::InvalidInput("Item missing ID".to_string()))?;
@@ -257,7 +257,7 @@ impl ItemService {
// Create filter chain // Create filter chain
let filter_chain = self.create_filter_chain( let filter_chain = self.create_filter_chain(
grep, head_bytes, head_lines, tail_bytes, tail_lines, None grep, head_bytes, head_lines, tail_bytes, tail_lines, filter
)?; )?;
// Wrap the reader with filtering // Wrap the reader with filtering