Skip to main content

ferroui_base/input/platform/
platform_data_transfer_item.rs

1use super::ClipboardError;
2use crate::input::{DataFormat, DataFormatOf, IAsyncDataTransferItem, IDataTransferItem, LocalBoxFuture};
3use std::any::Any;
4use std::cell::RefCell;
5use std::rc::Rc;
6
7/// The members a platform backend provides for a
8/// [`PlatformDataTransferItem`].
9pub trait PlatformDataTransferItemImpl {
10    /// Provides the formats supported by the item. Called until it
11    /// succeeds: a failure is reported to the caller and not kept.
12    fn provide_formats(&self) -> Result<Vec<DataFormat>, ClipboardError>;
13
14    /// Gets the value for a format the item supports.
15    fn try_get_raw_core(&self, format: &DataFormat) -> Result<Option<Rc<dyn Any>>, ClipboardError>;
16}
17
18struct SingleFormatItem {
19    format: DataFormat,
20    value: Rc<dyn Any>,
21}
22
23impl PlatformDataTransferItemImpl for SingleFormatItem {
24    fn provide_formats(&self) -> Result<Vec<DataFormat>, ClipboardError> {
25        Ok(vec![self.format.clone()])
26    }
27
28    fn try_get_raw_core(&self, format: &DataFormat) -> Result<Option<Rc<dyn Any>>, ClipboardError> {
29        if self.format == *format {
30            Ok(Some(self.value.clone()))
31        } else {
32            Ok(None)
33        }
34    }
35}
36
37/// The base of the synchronous data transfer items of the platform
38/// backends: caches the formats and only asks for the values of supported
39/// formats.
40pub struct PlatformDataTransferItem {
41    formats: RefCell<Option<Rc<[DataFormat]>>>,
42    platform_impl: Box<dyn PlatformDataTransferItemImpl>,
43}
44
45impl PlatformDataTransferItem {
46    /// Creates the item for a backend implementation.
47    pub fn new(platform_impl: impl PlatformDataTransferItemImpl + 'static) -> Rc<PlatformDataTransferItem> {
48        Rc::new(PlatformDataTransferItem { formats: RefCell::new(None), platform_impl: Box::new(platform_impl) })
49    }
50
51    /// Creates an item with a single format and its value.
52    pub fn create<T: 'static>(format: &DataFormatOf<T>, value: T) -> Rc<PlatformDataTransferItem> {
53        Self::new(SingleFormatItem { format: format.as_data_format().clone(), value: Rc::new(value) })
54    }
55
56    /// Gets the formats supported by this item.
57    ///
58    /// # Panics
59    /// Panics if the backend fails to provide them (where the reference
60    /// throws from the property); [`try_formats`](Self::try_formats) reports the
61    /// failure instead.
62    #[track_caller]
63    pub fn formats(&self) -> Rc<[DataFormat]> {
64        match self.try_formats() {
65            Ok(formats) => formats,
66            Err(error) => panic!("{error}"),
67        }
68    }
69
70    /// Gets the formats supported by this item, or the failure of the
71    /// backend providing them.
72    pub fn try_formats(&self) -> Result<Rc<[DataFormat]>, ClipboardError> {
73        if let Some(formats) = self.formats.borrow().as_ref() {
74            return Ok(formats.clone());
75        }
76
77        let formats: Rc<[DataFormat]> = Rc::from(self.platform_impl.provide_formats()?);
78        *self.formats.borrow_mut() = Some(formats.clone());
79        Ok(formats)
80    }
81
82    /// Gets whether the item supports a specific format.
83    #[track_caller]
84    pub fn contains(&self, format: &DataFormat) -> bool {
85        self.formats().iter().any(|candidate| candidate == format)
86    }
87
88    /// Tries to get a value for a given format. Returns `None` if the
89    /// format is not supported.
90    ///
91    /// # Panics
92    /// Panics if the backend fails to provide the formats or the value
93    /// (where the reference throws);
94    /// [`try_get_raw_result`](Self::try_get_raw_result) reports the failure
95    /// instead.
96    #[track_caller]
97    pub fn try_get_raw(&self, format: &DataFormat) -> Option<Rc<dyn Any>> {
98        match self.try_get_raw_result(format) {
99            Ok(value) => value,
100            Err(error) => panic!("{error}"),
101        }
102    }
103
104    /// Tries to get a value for a given format: `None` if the format is not
105    /// supported, or the failure of the backend providing the formats or
106    /// the value.
107    pub fn try_get_raw_result(&self, format: &DataFormat) -> Result<Option<Rc<dyn Any>>, ClipboardError> {
108        if self.try_formats()?.iter().any(|candidate| candidate == format) {
109            self.platform_impl.try_get_raw_core(format)
110        } else {
111            Ok(None)
112        }
113    }
114}
115
116impl IDataTransferItem for PlatformDataTransferItem {
117    fn formats(&self) -> Rc<[DataFormat]> {
118        PlatformDataTransferItem::formats(self)
119    }
120
121    fn try_get_raw(&self, format: &DataFormat) -> Option<Rc<dyn Any>> {
122        PlatformDataTransferItem::try_get_raw(self, format)
123    }
124}
125
126impl IAsyncDataTransferItem for PlatformDataTransferItem {
127    fn formats(&self) -> Rc<[DataFormat]> {
128        PlatformDataTransferItem::formats(self)
129    }
130
131    fn try_formats(&self) -> Result<Rc<[DataFormat]>, ClipboardError> {
132        PlatformDataTransferItem::try_formats(self)
133    }
134
135    fn try_get_raw_async(&self, format: &DataFormat) -> LocalBoxFuture<Result<Option<Rc<dyn Any>>, ClipboardError>> {
136        Box::pin(std::future::ready(PlatformDataTransferItem::try_get_raw_result(self, format)))
137    }
138}