feat: add magic meta plugin implementation
Co-authored-by: aider (openai/andrew/openrouter/anthropic/claude-sonnet-4) <aider@aider.chat>
This commit is contained in:
@@ -11,6 +11,7 @@ pub mod system;
|
||||
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;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone, strum::EnumIter, strum::Display, strum::EnumString)]
|
||||
#[strum(ascii_case_insensitive)]
|
||||
@@ -18,6 +19,9 @@ pub enum MetaPluginType {
|
||||
FileMagic,
|
||||
FileMime,
|
||||
FileEncoding,
|
||||
MagicFileType,
|
||||
MagicMimeType,
|
||||
MagicMimeEncoding,
|
||||
LineCount,
|
||||
WordCount,
|
||||
Cwd,
|
||||
@@ -83,6 +87,9 @@ pub fn get_meta_plugin(meta_plugin_type: MetaPluginType) -> Box<dyn MetaPlugin>
|
||||
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::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()),
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
use anyhow::Result;
|
||||
use magic::{Cookie, CookieFlags};
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
|
||||
use crate::meta_plugin::MetaPlugin;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MagicMetaPlugin {
|
||||
buffer: Vec<u8>,
|
||||
meta_name: String,
|
||||
cookie_flags: CookieFlags,
|
||||
}
|
||||
|
||||
impl MagicMetaPlugin {
|
||||
pub fn new_file_type() -> MagicMetaPlugin {
|
||||
MagicMetaPlugin {
|
||||
buffer: Vec::new(),
|
||||
meta_name: "magic_file_type".to_string(),
|
||||
cookie_flags: CookieFlags::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<Box<dyn Write>> {
|
||||
// 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<String> {
|
||||
let cookie = Cookie::open(self.cookie_flags)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, format!("Failed to open magic cookie: {}", e)))?;
|
||||
|
||||
cookie.load(&Default::default())
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, format!("Failed to load magic database: {}", e)))?;
|
||||
|
||||
let result = cookie.buffer(&self.buffer)
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, format!("Failed to analyze buffer: {}", e)))?;
|
||||
|
||||
// Clean up the result - remove extra whitespace and take first part if needed
|
||||
let trimmed = result.trim();
|
||||
|
||||
// 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") {
|
||||
trimmed.split(',').next().unwrap_or(trimmed).trim()
|
||||
} else {
|
||||
trimmed
|
||||
};
|
||||
|
||||
Ok(cleaned.to_string())
|
||||
}
|
||||
|
||||
fn update(&mut self, data: &[u8]) {
|
||||
self.buffer.extend_from_slice(data);
|
||||
}
|
||||
|
||||
fn meta_name(&mut self) -> String {
|
||||
self.meta_name.clone()
|
||||
}
|
||||
}
|
||||
|
||||
// Dummy writer that implements Write but doesn't do anything
|
||||
// This is needed to satisfy the MetaPlugin trait requirements
|
||||
struct DummyWriter;
|
||||
|
||||
impl Write for DummyWriter {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user