use anyhow::{anyhow, Result}; use std::io::{Write}; use crate::common::is_binary::is_binary; use crate::common::PIPESIZE; use crate::config; use crate::services::item_service::ItemService; use clap::Command; use is_terminal::IsTerminal; use std::path::PathBuf; pub fn mode_get( cmd: &mut Command, settings: &config::Settings, ids: &mut Vec, tags: &mut Vec, conn: &mut rusqlite::Connection, data_path: PathBuf, ) -> Result<()> { if !ids.is_empty() && !tags.is_empty() { cmd.error(clap::error::ErrorKind::InvalidValue, "Both ID and tags given, you must supply exactly one ID or at least one tag when using --get").exit(); } else if ids.len() > 1 { cmd.error(clap::error::ErrorKind::InvalidValue, "More than one ID given, you must supply exactly one ID or at least one tag when using --get").exit(); } let item_service = ItemService::new(data_path.clone()); let item_with_meta = item_service.find_item(conn, ids, tags, &std::collections::HashMap::new()) .map_err(|e| anyhow!("Unable to find matching item in database: {}", e))?; let item_id = item_with_meta.item.id.unwrap(); // Determine if we should detect binary data let mut detect_binary = !settings.force && std::io::stdout().is_terminal(); if detect_binary { let meta_map = item_with_meta.meta_as_map(); if let Some(text_val) = meta_map.get("text") { if text_val == "true" { detect_binary = false; } else if text_val == "false" { return Err(anyhow!( "Refusing to output binary data to TTY, use --force to override" )); } } } // Use streaming approach to handle large files let mut reader = item_service.get_compression_service().stream_item_content( data_path.join(item_id.to_string()), &item_with_meta.item.compression )?; if detect_binary { // Read only the first 8192 bytes for binary detection let mut sample_buffer = vec![0; PIPESIZE]; let bytes_read = reader.read(&mut sample_buffer)?; if is_binary(&sample_buffer[..bytes_read]) { return Err(anyhow!( "Refusing to output binary data to TTY, use --force to override" )); } // We need to create a new reader since we consumed some bytes reader = item_service.get_compression_service().stream_item_content( data_path.join(item_id.to_string()), &item_with_meta.item.compression )?; } // Stream the content to stdout let mut stdout = std::io::stdout(); let mut buffer = [0; PIPESIZE]; loop { let bytes_read = reader.read(&mut buffer)?; if bytes_read == 0 { break; } stdout.write_all(&buffer[..bytes_read])?; } Ok(()) }