Skip to main content

ferroui_base/input/platform/
platform_async_data_transfer_item.rs

1use super::ClipboardError;
2use crate::input::{DataFormat, IAsyncDataTransferItem, LocalBoxFuture};
3use std::any::Any;
4use std::cell::RefCell;
5use std::rc::Rc;
6
7/// The members a platform backend provides for a
8/// [`PlatformAsyncDataTransferItem`].
9pub trait PlatformAsyncDataTransferItemImpl {
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_async(&self, format: &DataFormat) -> LocalBoxFuture<Result<Option<Rc<dyn Any>>, ClipboardError>>;
16}
17
18/// The base of the asynchronous data transfer items of the platform
19/// backends: caches the formats and only asks for the values of supported
20/// formats.
21pub struct PlatformAsyncDataTransferItem {
22    formats: RefCell<Option<Rc<[DataFormat]>>>,
23    platform_impl: Box<dyn PlatformAsyncDataTransferItemImpl>,
24}
25
26impl PlatformAsyncDataTransferItem {
27    /// Creates the item for a backend implementation.
28    pub fn new(platform_impl: impl PlatformAsyncDataTransferItemImpl + 'static) -> Rc<PlatformAsyncDataTransferItem> {
29        Rc::new(PlatformAsyncDataTransferItem { formats: RefCell::new(None), platform_impl: Box::new(platform_impl) })
30    }
31
32    /// Gets the formats supported by this item.
33    ///
34    /// # Panics
35    /// Panics if the backend fails to provide them (where the reference
36    /// throws from the property); [`try_formats`](Self::try_formats) reports the
37    /// failure instead.
38    #[track_caller]
39    pub fn formats(&self) -> Rc<[DataFormat]> {
40        match self.try_formats() {
41            Ok(formats) => formats,
42            Err(error) => panic!("{error}"),
43        }
44    }
45
46    /// Gets the formats supported by this item, or the failure of the
47    /// backend providing them.
48    pub fn try_formats(&self) -> Result<Rc<[DataFormat]>, ClipboardError> {
49        if let Some(formats) = self.formats.borrow().as_ref() {
50            return Ok(formats.clone());
51        }
52
53        let formats: Rc<[DataFormat]> = Rc::from(self.platform_impl.provide_formats()?);
54        *self.formats.borrow_mut() = Some(formats.clone());
55        Ok(formats)
56    }
57
58    /// Gets whether the item supports a specific format.
59    #[track_caller]
60    pub fn contains(&self, format: &DataFormat) -> bool {
61        self.formats().iter().any(|candidate| candidate == format)
62    }
63
64    /// Tries to get a value for a given format. The future resolves to
65    /// `None` if the format is not supported, and to an error if the
66    /// backend fails to provide the formats or the value.
67    pub fn try_get_raw_async(&self, format: &DataFormat) -> LocalBoxFuture<Result<Option<Rc<dyn Any>>, ClipboardError>> {
68        match self.try_formats() {
69            Ok(formats) if formats.iter().any(|candidate| candidate == format) => {
70                self.platform_impl.try_get_raw_core_async(format)
71            }
72            Ok(_) => Box::pin(std::future::ready(Ok(None))),
73            Err(error) => Box::pin(std::future::ready(Err(error))),
74        }
75    }
76}
77
78impl IAsyncDataTransferItem for PlatformAsyncDataTransferItem {
79    fn formats(&self) -> Rc<[DataFormat]> {
80        PlatformAsyncDataTransferItem::formats(self)
81    }
82
83    fn try_formats(&self) -> Result<Rc<[DataFormat]>, ClipboardError> {
84        PlatformAsyncDataTransferItem::try_formats(self)
85    }
86
87    fn try_get_raw_async(&self, format: &DataFormat) -> LocalBoxFuture<Result<Option<Rc<dyn Any>>, ClipboardError>> {
88        PlatformAsyncDataTransferItem::try_get_raw_async(self, format)
89    }
90}