use alloc::rc::Rc;
use core::any::Any;
use crate::{SharedString, api::Image};
#[cfg(feature = "ffi")]
pub mod ffi;
#[derive(Default, Clone, PartialEq)]
struct DataTransferInner {
image: Option<Image>,
plain_text: Option<SharedString>,
}
#[derive(Clone, Default)]
#[repr(C)]
pub struct DataTransfer {
inner: Option<Rc<DataTransferInner>>,
user_data: Option<Rc<dyn Any>>,
}
impl core::fmt::Debug for DataTransfer {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("DataTransfer")
.field("has_plain_text", &self.has_plain_text())
.field("has_image", &self.has_image())
.field("has_user_data", &self.user_data.is_some())
.finish()
}
}
impl PartialEq for DataTransfer {
fn eq(&self, other: &Self) -> bool {
self.inner == other.inner
&& self.user_data.as_ref().map(Rc::as_ptr) == other.user_data.as_ref().map(Rc::as_ptr)
}
}
impl From<SharedString> for DataTransfer {
fn from(value: SharedString) -> Self {
let mut out = DataTransfer::default();
out.set_plain_text(value);
out
}
}
impl From<Image> for DataTransfer {
fn from(value: Image) -> Self {
let mut out = DataTransfer::default();
out.set_image(value);
out
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum DataTransferError {
TypeNotFound,
}
impl core::error::Error for DataTransferError {}
impl core::fmt::Display for DataTransferError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::TypeNotFound => {
write!(f, "Type not supplied by data transfer")
}
}
}
}
impl DataTransfer {
pub fn set_image(&mut self, image: Image) -> &mut Self {
if matches!(image.0, crate::ImageInner::None) {
self.clear_image();
} else {
Rc::make_mut(self.inner.get_or_insert_default()).image = Some(image);
}
self
}
pub fn set_plain_text(&mut self, plain_text: SharedString) -> &mut Self {
if plain_text.is_empty() {
self.clear_plain_text();
} else {
Rc::make_mut(self.inner.get_or_insert_default()).plain_text = Some(plain_text);
}
self
}
fn clear_image(&mut self) {
let Some(inner_rc) = self.inner.as_mut() else { return };
if inner_rc.image.is_some() {
Rc::make_mut(inner_rc).image = None;
}
if inner_rc.image.is_none() && inner_rc.plain_text.is_none() {
self.inner = None;
}
}
fn clear_plain_text(&mut self) {
let Some(inner_rc) = self.inner.as_mut() else { return };
if inner_rc.plain_text.is_some() {
Rc::make_mut(inner_rc).plain_text = None;
}
if inner_rc.image.is_none() && inner_rc.plain_text.is_none() {
self.inner = None;
}
}
pub fn has_image(&self) -> bool {
self.inner.as_ref().is_some_and(|inner| inner.image.is_some())
}
pub fn has_plain_text(&self) -> bool {
self.inner.as_ref().is_some_and(|inner| inner.plain_text.is_some())
}
pub fn is_empty(&self) -> bool {
!self.has_plain_text() && !self.has_image() && self.user_data.is_none()
}
pub fn set_user_data(&mut self, value: Rc<dyn Any>) -> &mut Self {
self.user_data = Some(value);
self
}
pub fn plain_text(&self) -> Result<SharedString, DataTransferError> {
self.inner
.as_ref()
.and_then(|inner| inner.plain_text.clone())
.ok_or(DataTransferError::TypeNotFound)
}
pub fn image(&self) -> Result<Image, DataTransferError> {
self.inner
.as_ref()
.and_then(|inner| inner.image.clone())
.ok_or(DataTransferError::TypeNotFound)
}
pub fn user_data(&self) -> Option<Rc<dyn Any>> {
self.user_data.clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::graphics::{Rgba8Pixel, SharedPixelBuffer};
#[test]
fn set_plain_text_with_empty_string_clears() {
let mut dt = DataTransfer::default();
dt.set_plain_text("hello".into());
assert!(dt.has_plain_text());
dt.set_plain_text("".into());
assert!(!dt.has_plain_text());
assert!(dt.plain_text().is_err());
}
#[test]
fn set_image_with_default_image_clears() {
let mut dt = DataTransfer::default();
let buffer = SharedPixelBuffer::<Rgba8Pixel>::new(2, 2);
dt.set_image(Image::from_rgba8(buffer));
assert!(dt.has_image());
dt.set_image(Image::default());
assert!(!dt.has_image());
assert!(dt.image().is_err());
}
#[test]
fn set_plain_text_with_empty_string_on_default_stays_empty() {
let mut dt = DataTransfer::default();
dt.set_plain_text("".into());
assert!(!dt.has_plain_text());
assert!(dt.is_empty());
assert!(dt.inner.is_none());
assert_eq!(dt, DataTransfer::default());
}
#[test]
fn set_image_with_default_image_on_default_stays_empty() {
let mut dt = DataTransfer::default();
dt.set_image(Image::default());
assert!(!dt.has_image());
assert!(dt.is_empty());
assert!(dt.inner.is_none());
assert_eq!(dt, DataTransfer::default());
}
#[test]
fn cleared_transfer_compares_equal_to_default() {
let mut dt = DataTransfer::default();
dt.set_plain_text("hello".into());
dt.set_image(Image::from_rgba8(SharedPixelBuffer::<Rgba8Pixel>::new(2, 2)));
assert!(!dt.is_empty());
dt.set_plain_text("".into());
assert!(dt.inner.is_some(), "image still set, inner must remain");
dt.set_image(Image::default());
assert!(dt.is_empty());
assert!(dt.inner.is_none());
assert_eq!(dt, DataTransfer::default());
}
}