From 133538881fbf8dc88b109ba6cc5b91c8ea4f4db5 Mon Sep 17 00:00:00 2001 From: Andrew Phillips Date: Sun, 17 Aug 2025 17:36:46 -0300 Subject: [PATCH] feat: combine magic plugins into single magic_file plugin Co-authored-by: aider (openai/andrew/openrouter/anthropic/claude-sonnet-4) --- src/meta_plugin.rs | 10 +--- src/meta_plugin/magic.rs | 121 ++++++++++++++++++++++++++------------- 2 files changed, 85 insertions(+), 46 deletions(-) diff --git a/src/meta_plugin.rs b/src/meta_plugin.rs index c9acea5..fa1238c 100644 --- a/src/meta_plugin.rs +++ b/src/meta_plugin.rs @@ -12,7 +12,7 @@ pub mod magic; use crate::meta_plugin::program::MetaPluginProgram; use crate::meta_plugin::digest::{DigestSha256MetaPlugin, ReadTimeMetaPlugin, ReadRateMetaPlugin}; use crate::meta_plugin::system::{CwdMetaPlugin, BinaryMetaPlugin, UidMetaPlugin, UserMetaPlugin, GidMetaPlugin, GroupMetaPlugin, ShellMetaPlugin, ShellPidMetaPlugin, KeepPidMetaPlugin, HostnameMetaPlugin, FullHostnameMetaPlugin}; -use crate::meta_plugin::magic::MagicMetaPlugin; +use crate::meta_plugin::magic::MagicFileMetaPlugin; #[derive(Debug, Eq, PartialEq, Clone, strum::EnumIter, strum::Display, strum::EnumString)] #[strum(ascii_case_insensitive)] @@ -20,9 +20,7 @@ pub enum MetaPluginType { FileMagic, FileMime, FileEncoding, - MagicFileType, - MagicMimeType, - MagicMimeEncoding, + MagicFile, LineCount, WordCount, Cwd, @@ -88,9 +86,7 @@ pub fn get_meta_plugin(meta_plugin_type: MetaPluginType) -> Box MetaPluginType::FileMagic => Box::new(MetaPluginProgram::new("file", vec!["-bE", "-"], "file_magic".to_string(), true)), MetaPluginType::FileMime => Box::new(MetaPluginProgram::new("file", vec!["-b", "--mime-type", "-"], "file_mime".to_string(), true)), MetaPluginType::FileEncoding => Box::new(MetaPluginProgram::new("file", vec!["-b", "--mime-encoding", "-"], "file_encoding".to_string(), true)), - MetaPluginType::MagicFileType => Box::new(MagicMetaPlugin::new_file_type()), - MetaPluginType::MagicMimeType => Box::new(MagicMetaPlugin::new_mime_type()), - MetaPluginType::MagicMimeEncoding => Box::new(MagicMetaPlugin::new_mime_encoding()), + MetaPluginType::MagicFile => Box::new(MagicFileMetaPlugin::new()), MetaPluginType::LineCount => Box::new(MetaPluginProgram::new("wc", vec!["-l"], "line_count".to_string(), true)), MetaPluginType::WordCount => Box::new(MetaPluginProgram::new("wc", vec!["-w"], "word_count".to_string(), true)), MetaPluginType::Cwd => Box::new(CwdMetaPlugin::new()), diff --git a/src/meta_plugin/magic.rs b/src/meta_plugin/magic.rs index 6171730..38c9f6d 100644 --- a/src/meta_plugin/magic.rs +++ b/src/meta_plugin/magic.rs @@ -2,54 +2,30 @@ use anyhow::Result; use magic::{Cookie, CookieFlags}; use std::io; use std::io::Write; +use rusqlite::Connection; use crate::meta_plugin::MetaPlugin; #[derive(Debug, Clone)] -pub struct MagicMetaPlugin { +pub struct MagicFileMetaPlugin { buffer: Vec, - meta_name: String, - cookie_flags: CookieFlags, + is_saved: bool, + item_id: Option, + conn: Option<*mut Connection>, } -impl MagicMetaPlugin { - pub fn new_file_type() -> MagicMetaPlugin { - MagicMetaPlugin { +impl MagicFileMetaPlugin { + pub fn new() -> MagicFileMetaPlugin { + MagicFileMetaPlugin { buffer: Vec::new(), - meta_name: "magic_file_type".to_string(), - cookie_flags: CookieFlags::empty(), + is_saved: false, + item_id: None, + conn: None, } } - pub fn new_mime_type() -> MagicMetaPlugin { - MagicMetaPlugin { - buffer: Vec::new(), - meta_name: "magic_mime_type".to_string(), - cookie_flags: CookieFlags::MIME_TYPE, - } - } - - pub fn new_mime_encoding() -> MagicMetaPlugin { - MagicMetaPlugin { - buffer: Vec::new(), - meta_name: "magic_mime_encoding".to_string(), - cookie_flags: CookieFlags::MIME_ENCODING, - } - } -} - -impl MetaPlugin for MagicMetaPlugin { - fn is_internal(&self) -> bool { - true - } - - fn create(&self) -> Result> { - // For meta plugins, we don't actually create a writer since we're buffering data internally - Ok(Box::new(DummyWriter)) - } - - fn finalize(&mut self) -> io::Result { - let cookie = Cookie::open(self.cookie_flags) + fn get_magic_result(&self, flags: CookieFlags) -> io::Result { + let cookie = Cookie::open(flags) .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("Failed to open magic cookie: {}", e)))?; cookie.load(&[] as &[&str]) @@ -64,7 +40,7 @@ impl MetaPlugin for MagicMetaPlugin { // For some magic results, we might want just the first part before semicolon or comma let cleaned = if trimmed.contains(';') { trimmed.split(';').next().unwrap_or(trimmed).trim() - } else if trimmed.contains(',') && self.meta_name.contains("mime") { + } else if trimmed.contains(',') && flags.contains(CookieFlags::MIME_TYPE | CookieFlags::MIME_ENCODING) { trimmed.split(',').next().unwrap_or(trimmed).trim() } else { trimmed @@ -73,12 +49,79 @@ impl MetaPlugin for MagicMetaPlugin { Ok(cleaned.to_string()) } + fn save_all_magic_metadata(&mut self) -> Result<()> { + if let (Some(conn), Some(item_id)) = (self.conn, self.item_id) { + let conn = unsafe { &*conn }; + + // Save file type + if let Ok(file_type) = self.get_magic_result(CookieFlags::empty()) { + let meta = crate::db::Meta { + id: item_id, + name: "magic_file_type".to_string(), + value: file_type, + }; + crate::db::store_meta(conn, meta)?; + } + + // Save MIME type + if let Ok(mime_type) = self.get_magic_result(CookieFlags::MIME_TYPE) { + let meta = crate::db::Meta { + id: item_id, + name: "magic_mime_type".to_string(), + value: mime_type, + }; + crate::db::store_meta(conn, meta)?; + } + + // Save MIME encoding + if let Ok(mime_encoding) = self.get_magic_result(CookieFlags::MIME_ENCODING) { + let meta = crate::db::Meta { + id: item_id, + name: "magic_mime_encoding".to_string(), + value: mime_encoding, + }; + crate::db::store_meta(conn, meta)?; + } + + self.is_saved = true; + } + Ok(()) + } +} + +impl MetaPlugin for MagicFileMetaPlugin { + fn is_internal(&self) -> bool { + true + } + + fn create(&self) -> Result> { + // For meta plugins, we don't actually create a writer since we're buffering data internally + Ok(Box::new(DummyWriter)) + } + + fn initialize(&mut self, conn: &Connection, item_id: i64) -> Result<()> { + self.item_id = Some(item_id); + self.conn = Some(conn as *const Connection as *mut Connection); + Ok(()) + } + + fn finalize(&mut self) -> io::Result { + // Save all magic metadata if not already saved + if !self.is_saved { + if let Err(e) = self.save_all_magic_metadata() { + return Err(io::Error::new(io::ErrorKind::Other, format!("Failed to save magic metadata: {}", e))); + } + } + // Return empty string since we save during finalize + Ok("".to_string()) + } + fn update(&mut self, data: &[u8]) { self.buffer.extend_from_slice(data); } fn meta_name(&mut self) -> String { - self.meta_name.clone() + "magic_file".to_string() } }