Skip to main content

winit_core/
data_transfer.rs

1//! Cross-platform abstractions related to data transfer (i.e. clipboard and drag-and-drop).
2//!
3//! > **NOTE**: Interacting with the clipboard is currently not implemented in Winit, and
4//! > this API is only used for drag-and-drop.
5//!
6//! # Quickstart
7//!
8//! The API in this module is used for both sending and receiving data. The flow is detailed below,
9//! but to quickly get started, the relevant APIs are the following:
10//!
11//! ### Receiving a drag-and-drop operation
12//!
13//! - [`DragEntered`](crate::event::WindowEvent::DragEntered) - informs a window that a new drag
14//!   operation has started.
15//! - [`data_transfer`](crate::event_loop::ActiveEventLoop::data_transfer) - get metadata about the
16//!   incoming transfer.
17//! - [`DataTransfer`] - metadata about the incoming transfer, in particular the available types
18//! - [`set_valid_dnd_actions`](crate::event_loop::ActiveEventLoop::set_valid_dnd_actions) - the
19//!   application must set at least some actions as valid in order for the drag to be considered
20//!   accepted.
21//! - [`fetch_data_transfer`](crate::event_loop::ActiveEventLoop::fetch_data_transfer) - request the
22//!   actual data, with a specific type, from the data transfer.
23//! - [`DataTransferReceived`](crate::event::WindowEvent::DataTransferReceived) - the actual data,
24//!   with a specific type, has been received.
25//! - [`TypedData`] - provides methods to read the actual data
26//!
27//! ### Sending a drag-and-drop operation
28//!
29//! - [`DataTransferSend`] - the core trait which defines data to be sent
30//! - [`DataTransferSendBuilder`] - helper to create a new outgoing data transfer from a set of
31//!   types and callbacks that supply data of that type
32//! - [`ActiveEventLoop::start_drag`](crate::event_loop::ActiveEventLoop::start_drag) - the
33//!   application calls this to start a new drag operation
34//! - [`OutgoingDragDropped`](crate::event::WindowEvent::OutgoingDragDropped)/
35//!   [`OutgoingDragCanceled`](crate::event::WindowEvent::OutgoingDragCanceled) - the application
36//!   receives this when the user has ended the drag operation, by dropping the data or by canceling
37//!   the operation respectively
38//!
39//! # Detailed flow
40//!
41//! ## Receiving a drag-and-drop operation
42//!
43//! On all platforms, the process looks something like this:
44//!
45//! - A data transfer advertises a set of types which the data can be interpreted as. While the
46//!   precise implementation depends on platform, there's a set of types which can be safely
47//!   transferred between applications on all platforms (see [`TypeHint`]).
48//!   - For example, if you copy or drag text from a web page, the browser may advertise the text
49//!     formatted using HTML, the text formatted as RTF, and the text with all formatting removed
50//!     simultaneously.
51//! - An application receiving a data transfer chooses one or more types that it understands and
52//!   requests the data in those formats (in practice, it will usually only request a single
53//!   format).
54//! - The source application converts the data stored in its memory to the requested format and
55//!   asynchronously sends it to the target application
56//!
57//! On some platforms, the data is sometimes available synchronously, but all platforms have at
58//! least some method of sending the data asynchronously and some types of data that may _only_ be
59//! sent using the asynchronous interface. Because of this, the API in winit must be asynchronous.
60//!
61//! The flow for a user application that implements drag-and-drop would look something like this:
62//!
63//! - The application receives a [`DragEntered`](crate::event::WindowEvent::DragEntered) event. This
64//!   event supplies a [`DataTransferId`] which can be used to request information or operations on
65//!   the dragged data by using methods on [`Window`](crate::window::Window).
66//! - To make sure that the operating system displays the correct cursor, and that modifier keys
67//!   will change the selected drag action correctly, the application should call
68//!   [`set_valid_dnd_actions`](crate::event_loop::ActiveEventLoop::set_valid_dnd_actions). See
69//!   documentation on that method for details.
70//! - As the drag operation continues, the window will receive
71//!   [`DragPosition`](crate::event::WindowEvent::DragPosition) events.
72//! - At any point during this operation, the receiving application may request either the available
73//!   types or even the data being transferred. This may be useful in cases where the application
74//!   wants to preload the data. For example, an image editor may want to display the image on the
75//!   canvas during the drag operation.
76//! - When the user tries to drop the data onto the window, that window will receive either a
77//!   [`DragDropped`](crate::event::WindowEvent::DragDropped) or
78//!   [`DragLeft`](crate::event::WindowEvent::DragLeft) event if the drag operation was accepted or
79//!   rejected, respectively. See documentation for
80//!   [`set_valid_dnd_actions`](crate::event_loop::ActiveEventLoop::set_valid_dnd_actions) for
81//!   details on accepting/rejecting a drag.
82//!
83//! ## Sending a drag-and-drop operation
84//!
85//! As the source application cannot interact with the ongoing drag while it is in-flight, this flow
86//! is a lot simpler.
87//!
88//! - The application creates a [`DataTransferSend`] with a set of types and associated data. For
89//!   most cases, this can be done with [`DataTransferSendBuilder`].
90//! - The application passes this [`DataTransferSend`] to
91//!   [`ActiveEventLoop::start_drag`](crate::event_loop::ActiveEventLoop::start_drag)`. This is also
92//!   where metadata is set, such as the icon that will be shown during the drag operation.
93//! - When the drag operation completes, the application receives
94//!   [`OutgoingDragDropped`](crate::event::WindowEvent::OutgoingDragDropped) with the resultant
95//!   action, or [`OutgoingDragCanceled`](crate::event::WindowEvent::OutgoingDragCanceled), and
96//!   handles it appropriately. For example, if the drag was successful and the operation is
97//!   [`DndAction::Move`](crate::event_loop::DndAction::Move), then the application would delete the
98//!   source object, since the data has now been transferred somewhere else.
99
100#![warn(missing_docs)]
101
102use std::any::Any;
103use std::ops::ControlFlow;
104use std::path::{Path, PathBuf};
105use std::{fmt, io};
106
107/// Unique identifier for a data transfer.
108#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
109pub struct DataTransferId(i64);
110
111impl DataTransferId {
112    /// Convert the [`DataTransferId`] into the underlying integer.
113    ///
114    /// This is useful if you need to pass the ID across an FFI boundary, or store it in an atomic.
115    pub const fn into_raw(self) -> i64 {
116        self.0
117    }
118
119    /// Construct a [`DataTransferId`] from the underlying integer.
120    ///
121    /// This should only be called with integers returned from [`DataTransferId::into_raw`].
122    pub const fn from_raw(id: i64) -> Self {
123        Self(id)
124    }
125}
126
127/// The set of types supported cross-platform.
128#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
129#[non_exhaustive]
130pub enum TypeHint {
131    /// Plain UTF-8 text (see [`TypedData::try_as_string`]).
132    ///
133    /// **Note for platform implementations**: this hint is _only_ for UTF-8 text. If the platform
134    /// returns plaintext in some format other than UTF-8 by default, a [`TypedData`]
135    /// implementation marked with this type hint should convert to UTF-8.
136    Plaintext,
137    /// A list of URIs in the format defined by the `text/uri-list` MIME type, encoded as UTF-8 (see
138    /// [`TypedData::try_as_uris`]).
139    ///
140    /// **Note for platform implementations**: this hint is _only_ for URIs encoded precisely in the
141    /// format specified above. If the platform uses a different format, a [`TypedData`]
142    /// implementation marked with this type hint should convert to that format.
143    UriList,
144    /// A HTML-formatted string
145    Html,
146    /// An RTF-formatted string
147    Rtf,
148    /// Audio
149    Audio {
150        /// An optional hint for the encoding of the supplied bytes, specified using the standard
151        /// file extension for that audio format, lowercase and without the leading `.`.
152        extension_hint: Option<&'static str>,
153    },
154    /// Image data
155    Image {
156        /// An optional hint for the encoding of the supplied bytes, specified using the standard
157        /// file extension for that image format, lowercase and without the leading `.`.
158        extension_hint: Option<&'static str>,
159    },
160}
161
162impl TypeHint {
163    /// Check whether the two type hints "match".
164    ///
165    /// This is subtly different to direct equality. If one of the types is an image or audio with a
166    /// `None` extension hint, then the other type just needs to match variant (i.e. image/audio),
167    /// the extension does not also have to be `None`.
168    pub fn matches(&self, other: &Self) -> bool {
169        match (self, other) {
170            (Self::Plaintext, Self::Plaintext)
171            | (Self::UriList, Self::UriList)
172            | (Self::Html, Self::Html)
173            | (Self::Rtf, Self::Rtf) => true,
174
175            (
176                Self::Audio { extension_hint: this_ext },
177                Self::Audio { extension_hint: other_ext },
178            )
179            | (
180                Self::Image { extension_hint: this_ext },
181                Self::Image { extension_hint: other_ext },
182            ) => match (this_ext, other_ext) {
183                (Some(this_ext), Some(other_ext)) => this_ext == other_ext,
184                (None, _) | (_, None) => true,
185            },
186
187            _ => false,
188        }
189    }
190}
191
192/// The type of a data transfer.
193///
194/// [`hint`](TransferType::hint) can be called to get the type in
195/// a cross-platform format (see [`TypeHint`])
196pub trait TransferType: Any + fmt::Debug {
197    /// Get the cross-platform representation of this type.
198    ///
199    /// If this returns `None`, then this is a platform-dependent type that has no cross-platform
200    /// equivalent.
201    fn hint(&self) -> Option<TypeHint>;
202
203    /// Check whether two dynamically-typed transfer types are equivalent.
204    // Can't use a `PartialEq` bound because it causes a dependency cycle.
205    fn matches(&self, other: &dyn TransferType) -> bool;
206}
207
208impl TransferType for TypeHint {
209    fn hint(&self) -> Option<TypeHint> {
210        Some(*self)
211    }
212
213    fn matches(&self, other: &dyn TransferType) -> bool {
214        other.hint().is_some_and(|hint| self.matches(&hint))
215    }
216}
217
218impl_dyn_casting!(TransferType);
219
220// Replicates the cfg for `url::Url::parse`
221#[cfg(any(unix, windows, target_os = "redox", target_os = "wasi", target_os = "hermit"))]
222fn default_try_as_file_paths<T: TypedData + ?Sized>(data: &T) -> io::Result<Vec<PathBuf>> {
223    data.try_as_uris().and_then(|uris| {
224        uris.into_iter()
225            .map(|uri_string| {
226                Ok(url::Url::parse(&uri_string)
227                    .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?
228                    .to_file_path()
229                    .map_err(|()| io::ErrorKind::InvalidData)?)
230            })
231            .collect()
232    })
233}
234
235// Replicates the cfg for `url::Url::parse`
236//
237// It doesn't matter that this is unimplemented on the web, as we don't currently support
238// drag-and-drop for web targets and the web platform can't directly access paths anyway.
239#[cfg(not(any(unix, windows, target_os = "redox", target_os = "wasi", target_os = "hermit")))]
240fn default_try_as_file_paths<T: TypedData + ?Sized>(_: &T) -> io::Result<Vec<PathBuf>> {
241    Err(io::ErrorKind::Unsupported.into())
242}
243
244/// Data that has been fetched from a data transfer
245///
246/// ### Blocking
247///
248/// Note that this type provides a blocking interface. In cases where reading this type directly on
249/// the event loop would cause a deadlock, the backend will make a best-effort attempt to return an
250/// error with [`io::ErrorKind::Deadlock`]. For now, the only way to access the data is via blocking
251/// on the event loop, so simply retrying the next time an event is received that references the
252/// data transfer should be enough to ensure that the data is accessible.
253pub trait TypedData: Any + fmt::Debug + Send + Sync {
254    /// The type of this `TypedData`.
255    fn type_(&self) -> &dyn TransferType;
256
257    /// If this value is readable as bytes, return a reader than can be used to read those bytes.
258    ///
259    /// On some platforms, the reader must be driven incrementally upon each
260    /// [`WindowEvent::DataTransferReceived`](crate::event::WindowEvent::DataTransferReceived)`. If
261    /// you don't need to stream the data and just want the bytes in a single buffer, use
262    /// [`TypedData::try_as_bytes`].
263    fn try_read(&self) -> Option<Box<dyn io::BufRead>>;
264
265    /// If this value is readable as bytes, return those bytes.
266    ///
267    /// If this returns [`WouldBlock`](std::io::ErrorKind::WouldBlock), then it should be called
268    /// again upon next receiving
269    /// [`WindowEvent::DataTransferReceived`](crate::event::WindowEvent::DataTransferReceived)
270    fn try_as_bytes(&self) -> io::Result<Vec<u8>> {
271        let mut reader = self
272            .try_read()
273            .ok_or_else(|| io::Error::other("This `TypedData` is not readable as bytes"))?;
274
275        let mut out = Vec::new();
276
277        reader.read_to_end(&mut out)?;
278
279        Ok(out)
280    }
281
282    /// Read this value as a list of URIs.
283    ///
284    /// If this value is not readable as URIs, return an error.
285    ///
286    /// The returned `String`s should be interpreted as URIs conforming to [RFC 3986](https://www.rfc-editor.org/info/rfc3986/).
287    ///
288    /// If this returns [`WouldBlock`](std::io::ErrorKind::WouldBlock), then it should be called
289    /// again upon next receiving
290    /// [`WindowEvent::DataTransferReceived`](crate::event::WindowEvent::DataTransferReceived)
291    fn try_as_uris(&self) -> io::Result<Vec<String>>;
292
293    /// Read this value as a list of paths.
294    ///
295    /// This is provided as a convenience method to avoid the need for the user to manually parse
296    /// the result of [`try_as_uris`](TypedData::try_as_uris). `try_as_uris` should be preferred
297    /// when the extra complexity is acceptable, as it is more generic.
298    ///
299    /// If this value is not readable as URIs, return an error.
300    ///
301    /// If this returns [`WouldBlock`](std::io::ErrorKind::WouldBlock), then it should be called
302    /// again upon next receiving
303    /// [`WindowEvent::DataTransferReceived`](crate::event::WindowEvent::DataTransferReceived)
304    fn try_as_file_paths(&self) -> io::Result<Vec<PathBuf>> {
305        default_try_as_file_paths(self)
306    }
307
308    /// Read this value as a plain text string.
309    ///
310    /// If this value is not readable as a string, return an error.
311    ///
312    /// If this returns [`WouldBlock`](std::io::ErrorKind::WouldBlock), then it should be called
313    /// again upon next receiving
314    /// [`WindowEvent::DataTransferReceived`](crate::event::WindowEvent::DataTransferReceived)
315    fn try_as_string(&self) -> io::Result<String>;
316}
317
318// Required for `WindowEvent` to implement `PartialEq` - we just implement this on a best-effort
319// basis.
320impl PartialEq for dyn TypedData {
321    fn eq(&self, other: &Self) -> bool {
322        std::ptr::addr_eq(self, other)
323    }
324}
325
326impl_dyn_casting!(TypedData);
327
328/// Metadata about a data transfer. This does not allow actually receiving data, as that is an
329/// asynchronous operation. To fetch the data from the source application, see
330/// [`ActiveEventLoop::fetch_data_transfer`](crate::event_loop::ActiveEventLoop::fetch_data_transfer).
331pub trait DataTransfer: Any + fmt::Debug {
332    /// Iterate over each type advertized by this `DataTransfer`. This is just a minor optimization,
333    /// in most cases you should probably use [`has_type`](DataTransfer::has_type) or
334    /// [`available_types`](DataTransfer::available_types).
335    fn for_each_available_type<'this>(
336        &'this self,
337        func: &'_ mut dyn FnMut(&'this dyn TransferType) -> ControlFlow<()>,
338    );
339
340    /// Display the list of all available types.
341    ///
342    /// This is useful if more-complex type matching is required, but for most cases
343    /// [`has_type`](DataTransfer::has_type) should be used.
344    fn available_types(&self) -> Vec<&'_ dyn TransferType> {
345        let mut out = Vec::new();
346
347        self.for_each_available_type(&mut |ty| {
348            out.push(ty);
349            ControlFlow::Continue(())
350        });
351
352        out
353    }
354
355    /// Check if the supplied type is provided by this [`DataTransfer`].
356    ///
357    /// Supplying a [`TypeHint`] as the type is supported on all platforms, but if some
358    /// platform-specific type is required then that platform's implementation of `TransferType` can
359    /// be used.
360    fn has_type(&self, type_: &dyn TransferType) -> bool {
361        let mut found = false;
362        self.for_each_available_type(&mut |haystack| {
363            if haystack.matches(type_) {
364                found = true;
365                ControlFlow::Break(())
366            } else {
367                ControlFlow::Continue(())
368            }
369        });
370
371        found
372    }
373}
374
375impl_dyn_casting!(DataTransfer);
376
377/// Kinds of data that can be sent via a `DataTransfer`.
378///
379/// Some kinds of data cannot be represented by just a binary blob in a cross-platform way.
380/// File URIs on Windows and macOS are represented as arrays of strings, and strings have
381/// different encoding on different platforms. To allow this to be represented, we allow
382/// supplying strings and URIs separately from binary blobs.
383#[derive(Debug, Clone, PartialEq, Eq, Hash)]
384#[non_exhaustive]
385pub enum SendData {
386    /// List of URIs.
387    ///
388    /// These should conform to [RFC 3986](https://www.rfc-editor.org/info/rfc3986/).
389    /// If you just want to send file paths, see [`SendData::from_file_paths`].
390    ///
391    /// Note that `SendData` implements `From<String>` and `From<Vec<u8>>`, but _not_
392    /// `From<Vec<String>>`, as it is not necessarily obvious to a reader that `Vec<String>`
393    /// will be interpreted as a URI list. However, it _does_ implement [`From<Url>`](url::Url),
394    /// if you are using the [`url`](https://docs.rs/url/2) crate.
395    Uris(Vec<String>),
396    /// String
397    ///
398    /// This can also be constructed with the [`From<String>`](std::string::String) implementation.
399    String(String),
400    /// Binary blob
401    ///
402    /// This can also be constructed with the [`From<Vec<u8>>`](std::vec::Vec) implementation.
403    Bytes(Vec<u8>),
404}
405
406impl SendData {
407    /// Create [`SendData::Uris`] from an iterator of [`Path`]s.
408    ///
409    /// All paths must be absolute, and on Windows must include either a drive prefix (e.g. `C:\`)
410    /// or a UNC prefix (`\\`). See documentation for [`url::Url::from_file_path`].
411    pub fn from_file_paths<I>(paths: I) -> Option<Self>
412    where
413        I: IntoIterator,
414        I::Item: AsRef<Path>,
415    {
416        // Replicates the cfg for `url::Url::from_file_path`
417        #[cfg(any(unix, windows, target_os = "redox", target_os = "wasi", target_os = "hermit"))]
418        fn from_file_paths_impl<I>(paths: I) -> Option<SendData>
419        where
420            I: IntoIterator,
421            I::Item: AsRef<Path>,
422        {
423            paths
424                .into_iter()
425                .map(url::Url::from_file_path)
426                .map(|result| result.map(String::from))
427                .collect::<Result<Vec<_>, ()>>()
428                .map(SendData::Uris)
429                .ok()
430        }
431
432        // Replicates the cfg for `url::Url::from_file_path`
433        //
434        // It doesn't matter that this is unimplemented on the web, as we don't currently support
435        // drag-and-drop for web targets and the web platform can't directly access paths
436        // anyway.
437        #[cfg(not(any(
438            unix,
439            windows,
440            target_os = "redox",
441            target_os = "wasi",
442            target_os = "hermit"
443        )))]
444        fn from_file_paths_impl<I>(_: I) -> Option<SendData> {
445            None
446        }
447
448        from_file_paths_impl(paths)
449    }
450}
451
452// We monomorphize these `From` implementations instead of making them generic, in order to
453// prevent accidentally casting to the wrong type.
454impl From<String> for SendData {
455    fn from(value: String) -> Self {
456        Self::String(value)
457    }
458}
459
460impl From<Vec<u8>> for SendData {
461    fn from(value: Vec<u8>) -> Self {
462        Self::Bytes(value)
463    }
464}
465
466impl From<Vec<url::Url>> for SendData {
467    fn from(value: Vec<url::Url>) -> Self {
468        Self::Uris(value.into_iter().map(Into::into).collect())
469    }
470}
471
472/// Trait for sending data via a data transfer.
473///
474/// See [`ActiveEventLoop::start_drag`](crate::event_loop::ActiveEventLoop::start_drag) for where
475/// this is used. To build an implementation of this trait dynamically in a cross-platform way, use
476/// [`DataTransferSendBuilder`].
477pub trait DataTransferSend: DataTransfer + Send {
478    /// Get the data for the specified type, or `None` if this value does not supply the given data
479    /// type.
480    fn data_for_type(&self, type_: &dyn TransferType) -> Option<SendData>;
481}
482
483impl_dyn_casting!(DataTransferSend);
484
485type SendDataCallback<T> = Box<dyn Fn(&T, &dyn TransferType) -> Option<SendData> + Send>;
486
487/// Dynamic builder for an implementation of [`DataTransferSend`].
488///
489/// On all platforms, inter-application data transfer (i.e. clipboard and drag-and-drop) works like
490/// so:
491///
492/// - The source advertises a set of types that it can transfer.
493/// - The destination picks one or more of those types to receive.
494/// - The source sends the data for that type.
495///
496/// This type abstracts that in a way that allows data to be sent cross-platform. `T` is an optional
497/// state value, which allows the user to have a single source of truth for their data, converting
498/// it lazily to the requested type.
499pub struct DataTransferSendBuilder<T> {
500    state: T,
501    types: Vec<(Box<dyn TransferType + Send>, SendDataCallback<T>)>,
502}
503
504impl<T> fmt::Debug for DataTransferSendBuilder<T>
505where
506    T: fmt::Debug,
507{
508    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
509        f.debug_struct("NewDataTransferBuilder").field("state", &self.state).finish_non_exhaustive()
510    }
511}
512
513impl<T> DataTransfer for DataTransferSendBuilder<T>
514where
515    T: fmt::Debug + Send + 'static,
516{
517    fn for_each_available_type<'this>(
518        &'this self,
519        func: &'_ mut dyn FnMut(&'this dyn TransferType) -> ControlFlow<()>,
520    ) {
521        let _ = self.types.iter().try_for_each(|(ty, _)| func(&**ty));
522    }
523}
524
525impl<T> DataTransferSend for DataTransferSendBuilder<T>
526where
527    T: fmt::Debug + Send + 'static,
528{
529    fn data_for_type(&self, type_: &dyn TransferType) -> Option<SendData> {
530        self.data_for_type(type_)
531    }
532}
533
534impl<T> DataTransferSendBuilder<T> {
535    /// Create a new [`DataTransferSendBuilder`], with a state value which acts as
536    /// the single source of truth for the underlying data.
537    pub fn new(state: T) -> Self {
538        Self { state, types: vec![] }
539    }
540}
541
542impl<T> DataTransferSendBuilder<T> {
543    fn data_for_type(&self, type_: &dyn TransferType) -> Option<SendData> {
544        let (_, func) = self.types.iter().find(|(ty, _)| ty.matches(type_))?;
545
546        func(&self.state, type_)
547    }
548
549    /// Add a callback which converts the builder's state to the given type. In
550    /// most cases, `type_` will be [`TypeHint`].
551    pub fn add_type<Ty, F, O>(&mut self, type_: Ty, func: F) -> &mut Self
552    where
553        Ty: TransferType + Send,
554        F: Fn(&T, &dyn TransferType) -> Option<O> + Send + 'static,
555        O: Into<SendData>,
556    {
557        self.types
558            .push((Box::new(type_), Box::new(move |state, ty| func(state, ty).map(Into::into))));
559        self
560    }
561
562    /// Return a new builder, adding a callback which converts the builder's state
563    /// to the given type.
564    ///
565    /// For cross-platform use, `type_` will be [`TypeHint`]. The closure additionally receives
566    /// a [`TransferType`], which is not necessarily the same as `type_` for the following reasons:
567    ///
568    /// - The OS may have multiple types which are equivalent to the supplied type
569    /// - `TypeHint::Audio` and `TypeHint::Image` with `extension_hint: None` will advertise all
570    ///   supported audio and image formats, in which case the closure may receive a type with an
571    ///   extension chosen by the receiving application.
572    pub fn with_type<Ty, F, O>(mut self, type_: Ty, func: F) -> Self
573    where
574        Ty: TransferType + Send,
575        F: Fn(&T, &dyn TransferType) -> Option<O> + Send + 'static,
576        O: Into<SendData>,
577    {
578        self.add_type(type_, func);
579        self
580    }
581}
582
583impl<T> DataTransferSendBuilder<T>
584where
585    T: fmt::Debug + Send + 'static,
586{
587    /// Consume the builder, returning an implementation of [`DataTransferSend`].
588    ///
589    /// Note that this is only provided for explicitness and ergonomics. [`DataTransferSendBuilder`]
590    /// implements [`DataTransferSend`] and this method is equivalent to [`Box::new`].
591    pub fn build(self) -> Box<dyn DataTransferSend> {
592        Box::new(self)
593    }
594}