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:
Andrew Phillips
2025-08-16 14:51:30 -03:00
parent 28959a357c
commit 151ec151db
2 changed files with 104 additions and 0 deletions

View File

@@ -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(())
}
}