use crate::client::KeepClient; use crate::compression_engine::CompressionType; use crate::filter_plugin::FilterChain; use anyhow::Result; use clap::Command; use is_terminal::IsTerminal; use log::debug; use std::io::{Read, Write}; use std::str::FromStr; pub fn mode( client: &KeepClient, _cmd: &mut Command, settings: &crate::config::Settings, ids: &[i64], tags: &[String], filter_chain: Option, ) -> Result<(), anyhow::Error> { debug!("CLIENT_GET: Getting item via remote server"); // Find the item ID let item_id = if !ids.is_empty() { ids[0] } else if !tags.is_empty() { // Find item by tags let items = client.list_items(tags, "newest", 0, 1)?; if items.is_empty() { return Err(anyhow::anyhow!("No items found matching tags: {:?}", tags)); } items[0].id } else { // Get latest item let items = client.list_items(&[], "newest", 0, 1)?; if items.is_empty() { return Err(anyhow::anyhow!("No items found")); } items[0].id }; // Get item info to determine compression type let item_info = client.get_item_info(item_id)?; // Get raw content from server let (raw_bytes, compression) = client.get_item_content_raw(item_id)?; // Check if binary content would be sent to TTY let is_text = item_info .metadata .get("text") .map(|v| v == "true") .unwrap_or(false); if std::io::stdout().is_terminal() && !is_text && !settings.force { // Check if content is binary let sample_len = std::cmp::min(raw_bytes.len(), 8192); if crate::common::is_binary::is_binary(&raw_bytes[..sample_len]) { return Err(anyhow::anyhow!( "Refusing to output binary data to a terminal. Use --force to override." )); } } // Decompress locally let compression_type = CompressionType::from_str(&compression).unwrap_or(CompressionType::None); let decompressed = match compression_type { CompressionType::GZip => { use flate2::read::GzDecoder; let mut decoder = GzDecoder::new(&raw_bytes[..]); let mut content = Vec::new(); decoder.read_to_end(&mut content)?; content } CompressionType::LZ4 => lz4_flex::decompress_size_prepended(&raw_bytes) .map_err(|e| anyhow::anyhow!("LZ4 decompression failed: {}", e))?, _ => raw_bytes, }; // Apply filters if present let output = if let Some(mut chain) = filter_chain { let mut filtered = Vec::new(); chain.filter(&mut &decompressed[..], &mut filtered)?; filtered } else { decompressed }; // Stream to stdout let stdout = std::io::stdout(); let mut stdout = stdout.lock(); stdout.write_all(&output)?; stdout.flush()?; Ok(()) }