1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
use super::async_to_sync_data_transfer::AsyncToSyncDataTransfer;
use super::platform::ClipboardError;
use super::sync_to_async_data_transfer::SyncToAsyncDataTransfer;
use super::{
AsyncDataTransferItemExtensions, DataFormat, DataFormatOf, IAsyncDataTransfer, IAsyncDataTransferItem,
IDataTransfer, LocalBoxFuture,
};
use crate::logging::{LogEventLevel, Logger};
use crate::media::imaging::Bitmap;
use crate::platform::storage::IStorageItem;
use std::rc::Rc;
/// The items of an asynchronous data transfer that support a format, in
/// order.
pub struct AsyncDataTransferItems {
items: Rc<[Rc<dyn IAsyncDataTransferItem>]>,
format: DataFormat,
index: usize,
}
impl Iterator for AsyncDataTransferItems {
type Item = Rc<dyn IAsyncDataTransferItem>;
fn next(&mut self) -> Option<Self::Item> {
while let Some(item) = self.items.get(self.index) {
self.index += 1;
if item.contains(&self.format) {
return Some(item.clone());
}
}
None
}
}
impl dyn IAsyncDataTransfer {
/// Returns the data transfer as a synchronous one: itself if it also
/// implements [`IDataTransfer`], a wrapper waiting for the values
/// otherwise.
///
/// This is an implementation detail of the platform backends.
pub fn to_synchronous(self: Rc<Self>, log_area: &'static str) -> Rc<dyn IDataTransfer> {
if let Some(data_transfer) = self.clone().as_data_transfer() {
return data_transfer;
}
if let Some(logger) = Logger::try_get(LogEventLevel::Warning, log_area) {
logger.log(
None,
"Using a synchronous wrapper for IAsyncDataTransferItem. Consider implementing IDataTransfer instead.",
);
}
AsyncToSyncDataTransfer::new(self)
}
}
impl dyn IDataTransfer {
/// Returns the data transfer as an asynchronous one: itself if it also
/// implements [`IAsyncDataTransfer`], a wrapper otherwise.
///
/// This is an implementation detail of the platform backends.
pub fn to_asynchronous(self: Rc<Self>) -> Rc<dyn IAsyncDataTransfer> {
match self.clone().as_async_data_transfer() {
Some(data_transfer) => data_transfer,
None => SyncToAsyncDataTransfer::new(self),
}
}
}
// Keep the `try_get_xxx_async` methods in sync with the `try_get_xxx` ones
// of `DataTransferExtensions`.
/// Contains extension methods for [`IAsyncDataTransfer`].
pub trait AsyncDataTransferExtensions: IAsyncDataTransfer {
/// Gets whether the data transfer supports a specific format.
fn contains(&self, format: &DataFormat) -> bool {
self.formats().iter().any(|candidate| candidate == format)
}
/// Gets the list of [`IAsyncDataTransferItem`] contained in the data
/// transfer, filtered by a given format.
///
/// Some platforms (such as Windows and X11) may only support a single
/// data item for all formats except files.
fn get_items(&self, format: &DataFormat) -> AsyncDataTransferItems {
AsyncDataTransferItems { items: self.items(), format: format.clone(), index: 0 }
}
/// Tries to get a value for a given format from the data transfer. The
/// future resolves to `None` if the format is not supported, and to an
/// error if the platform providing the items, their formats or the
/// value fails.
///
/// If the data transfer contains several items supporting the format,
/// the first matching one will be returned.
fn try_get_value_async<T: Clone + 'static>(
&self,
format: &DataFormatOf<T>,
) -> LocalBoxFuture<Result<Option<T>, ClipboardError>> {
// The first item supporting the format is looked for before this
// returns, as in the reference; the items after it are not asked
// for their formats.
let item = self.try_items().and_then(|items| {
for item in items.iter() {
if item.try_contains(format)? {
return Ok(Some(item.clone()));
}
}
Ok(None)
});
match item {
Ok(Some(item)) => item.try_get_value_async(format),
Ok(None) => Box::pin(std::future::ready(Ok(None))),
Err(error) => Box::pin(std::future::ready(Err(error))),
}
}
/// Tries to get multiple values for a given format from the data
/// transfer. The future resolves to `None` if the format is not
/// supported, and to an error as soon as the platform providing the
/// items, their formats or one of the values fails.
fn try_get_values_async<T: Clone + 'static>(
&self,
format: &DataFormatOf<T>,
) -> LocalBoxFuture<Result<Option<Vec<T>>, ClipboardError>> {
let items = self.try_items();
let format = format.clone();
Box::pin(async move {
let mut results: Option<Vec<T>> = None;
// The items are queried one after the other, each on the thread
// the future is polled on; an item is asked for its formats
// when the items before it have delivered their values.
for item in items?.iter() {
if !item.try_contains(&format)? {
continue;
}
let Some(result) = item.try_get_value_async(&format).await? else { continue };
results.get_or_insert_with(Vec::new).push(result);
}
Ok(results)
})
}
/// Returns a text, if available, from the data transfer.
///
/// If the data transfer contains several items supporting
/// [`DataFormat::text`], the first matching one will be returned.
fn try_get_text_async(&self) -> LocalBoxFuture<Result<Option<String>, ClipboardError>> {
self.try_get_value_async(&DataFormat::text())
}
/// Returns a bitmap, if available, from the data transfer.
///
/// If the data transfer contains several items supporting
/// [`DataFormat::bitmap`], the first matching one will be returned.
fn try_get_bitmap_async(&self) -> LocalBoxFuture<Result<Option<Rc<Bitmap>>, ClipboardError>> {
self.try_get_value_async(&DataFormat::bitmap())
}
/// Returns a file (or folder), if available, from the data transfer.
///
/// See [`DataFormat::file`].
fn try_get_file_async(&self) -> LocalBoxFuture<Result<Option<Rc<dyn IStorageItem>>, ClipboardError>> {
self.try_get_value_async(&DataFormat::file())
}
/// Returns the files (or folders), if available, from the data
/// transfer.
///
/// See [`DataFormat::file`].
fn try_get_files_async(&self) -> LocalBoxFuture<Result<Option<Vec<Rc<dyn IStorageItem>>>, ClipboardError>> {
self.try_get_values_async(&DataFormat::file())
}
}
impl<I: IAsyncDataTransfer + ?Sized> AsyncDataTransferExtensions for I {}