use crate::core::ClipboardProvider;
pub struct SystemClipboard {
clipboard: Option<arboard::Clipboard>,
}
impl Default for SystemClipboard {
fn default() -> Self {
Self::new()
}
}
impl SystemClipboard {
pub fn new() -> Self {
let clipboard = arboard::Clipboard::new()
.map_err(|error| crate::app::console::warn(format_args!("portapapeles no disponible: {error}")))
.ok();
Self { clipboard }
}
}
impl ClipboardProvider for SystemClipboard {
fn read(&mut self) -> Option<String> {
self.clipboard.as_mut()?.get_text().ok()
}
fn read_image(&mut self) -> Result<Option<Vec<u8>>, String> {
let Some(clipboard) = self.clipboard.as_mut() else { return Ok(None) };
let image = match clipboard.get_image() {
Ok(image) => image,
Err(arboard::Error::ContentNotAvailable) => return Ok(None),
Err(error) => return Err(error.to_string()),
};
let width = u32::try_from(image.width).map_err(|_| "Imagen demasiado ancha")?;
let height = u32::try_from(image.height).map_err(|_| "Imagen demasiado alta")?;
let length = image.width.checked_mul(image.height).and_then(|pixels| pixels.checked_mul(4));
if width == 0 || height == 0 || length != Some(image.bytes.len()) || image.bytes.len() > 32 * 1024 * 1024 {
return Err("La imagen supera el límite de 32 MB o tiene dimensiones inválidas".into());
}
let mut bytes = Vec::new();
{
let mut encoder = png::Encoder::new(&mut bytes, width, height);
encoder.set_color(png::ColorType::Rgba);
encoder.set_depth(png::BitDepth::Eight);
let mut writer = encoder.write_header().map_err(|error| error.to_string())?;
writer.write_image_data(&image.bytes).map_err(|error| error.to_string())?;
}
Ok(Some(bytes))
}
fn write(&mut self, text: String) -> Result<(), String> {
match self.clipboard.as_mut() {
Some(clipboard) => clipboard.set_text(text).map_err(|error| error.to_string()),
None => Err("portapapeles no disponible".into()),
}
}
}