#![warn(missing_docs)]
use std::any::Any;
use std::ops::ControlFlow;
use std::path::{Path, PathBuf};
use std::{fmt, io};
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DataTransferId(i64);
impl DataTransferId {
pub const fn into_raw(self) -> i64 {
self.0
}
pub const fn from_raw(id: i64) -> Self {
Self(id)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum TypeHint {
Plaintext,
UriList,
Html,
Rtf,
Audio {
extension_hint: Option<&'static str>,
},
Image {
extension_hint: Option<&'static str>,
},
}
impl TypeHint {
pub fn matches(&self, other: &Self) -> bool {
match (self, other) {
(Self::Plaintext, Self::Plaintext)
| (Self::UriList, Self::UriList)
| (Self::Html, Self::Html)
| (Self::Rtf, Self::Rtf) => true,
(
Self::Audio { extension_hint: this_ext },
Self::Audio { extension_hint: other_ext },
)
| (
Self::Image { extension_hint: this_ext },
Self::Image { extension_hint: other_ext },
) => match (this_ext, other_ext) {
(Some(this_ext), Some(other_ext)) => this_ext == other_ext,
(None, _) | (_, None) => true,
},
_ => false,
}
}
}
pub trait TransferType: Any + fmt::Debug {
fn hint(&self) -> Option<TypeHint>;
fn matches(&self, other: &dyn TransferType) -> bool;
}
impl TransferType for TypeHint {
fn hint(&self) -> Option<TypeHint> {
Some(*self)
}
fn matches(&self, other: &dyn TransferType) -> bool {
other.hint().is_some_and(|hint| self.matches(&hint))
}
}
impl_dyn_casting!(TransferType);
#[cfg(any(unix, windows, target_os = "redox", target_os = "wasi", target_os = "hermit"))]
fn default_try_as_file_paths<T: TypedData + ?Sized>(data: &T) -> io::Result<Vec<PathBuf>> {
data.try_as_uris().and_then(|uris| {
uris.into_iter()
.map(|uri_string| {
Ok(url::Url::parse(&uri_string)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?
.to_file_path()
.map_err(|()| io::ErrorKind::InvalidData)?)
})
.collect()
})
}
#[cfg(not(any(unix, windows, target_os = "redox", target_os = "wasi", target_os = "hermit")))]
fn default_try_as_file_paths<T: TypedData + ?Sized>(_: &T) -> io::Result<Vec<PathBuf>> {
Err(io::ErrorKind::Unsupported.into())
}
pub trait TypedData: Any + fmt::Debug + Send + Sync {
fn type_(&self) -> &dyn TransferType;
fn try_read(&self) -> Option<Box<dyn io::BufRead>>;
fn try_as_bytes(&self) -> io::Result<Vec<u8>> {
let mut reader = self
.try_read()
.ok_or_else(|| io::Error::other("This `TypedData` is not readable as bytes"))?;
let mut out = Vec::new();
reader.read_to_end(&mut out)?;
Ok(out)
}
fn try_as_uris(&self) -> io::Result<Vec<String>>;
fn try_as_file_paths(&self) -> io::Result<Vec<PathBuf>> {
default_try_as_file_paths(self)
}
fn try_as_string(&self) -> io::Result<String>;
}
impl PartialEq for dyn TypedData {
fn eq(&self, other: &Self) -> bool {
std::ptr::addr_eq(self, other)
}
}
impl_dyn_casting!(TypedData);
pub trait DataTransfer: Any + fmt::Debug {
fn for_each_available_type<'this>(
&'this self,
func: &'_ mut dyn FnMut(&'this dyn TransferType) -> ControlFlow<()>,
);
fn available_types(&self) -> Vec<&'_ dyn TransferType> {
let mut out = Vec::new();
self.for_each_available_type(&mut |ty| {
out.push(ty);
ControlFlow::Continue(())
});
out
}
fn has_type(&self, type_: &dyn TransferType) -> bool {
let mut found = false;
self.for_each_available_type(&mut |haystack| {
if haystack.matches(type_) {
found = true;
ControlFlow::Break(())
} else {
ControlFlow::Continue(())
}
});
found
}
}
impl_dyn_casting!(DataTransfer);
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum SendData {
Uris(Vec<String>),
String(String),
Bytes(Vec<u8>),
}
impl SendData {
pub fn from_file_paths<I>(paths: I) -> Option<Self>
where
I: IntoIterator,
I::Item: AsRef<Path>,
{
#[cfg(any(unix, windows, target_os = "redox", target_os = "wasi", target_os = "hermit"))]
fn from_file_paths_impl<I>(paths: I) -> Option<SendData>
where
I: IntoIterator,
I::Item: AsRef<Path>,
{
paths
.into_iter()
.map(url::Url::from_file_path)
.map(|result| result.map(String::from))
.collect::<Result<Vec<_>, ()>>()
.map(SendData::Uris)
.ok()
}
#[cfg(not(any(
unix,
windows,
target_os = "redox",
target_os = "wasi",
target_os = "hermit"
)))]
fn from_file_paths_impl<I>(_: I) -> Option<SendData> {
None
}
from_file_paths_impl(paths)
}
}
impl From<String> for SendData {
fn from(value: String) -> Self {
Self::String(value)
}
}
impl From<Vec<u8>> for SendData {
fn from(value: Vec<u8>) -> Self {
Self::Bytes(value)
}
}
impl From<Vec<url::Url>> for SendData {
fn from(value: Vec<url::Url>) -> Self {
Self::Uris(value.into_iter().map(Into::into).collect())
}
}
pub trait DataTransferSend: DataTransfer + Send {
fn data_for_type(&self, type_: &dyn TransferType) -> Option<SendData>;
}
impl_dyn_casting!(DataTransferSend);
type SendDataCallback<T> = Box<dyn Fn(&T, &dyn TransferType) -> Option<SendData> + Send>;
pub struct DataTransferSendBuilder<T> {
state: T,
types: Vec<(Box<dyn TransferType + Send>, SendDataCallback<T>)>,
}
impl<T> fmt::Debug for DataTransferSendBuilder<T>
where
T: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("NewDataTransferBuilder").field("state", &self.state).finish_non_exhaustive()
}
}
impl<T> DataTransfer for DataTransferSendBuilder<T>
where
T: fmt::Debug + Send + 'static,
{
fn for_each_available_type<'this>(
&'this self,
func: &'_ mut dyn FnMut(&'this dyn TransferType) -> ControlFlow<()>,
) {
let _ = self.types.iter().try_for_each(|(ty, _)| func(&**ty));
}
}
impl<T> DataTransferSend for DataTransferSendBuilder<T>
where
T: fmt::Debug + Send + 'static,
{
fn data_for_type(&self, type_: &dyn TransferType) -> Option<SendData> {
self.data_for_type(type_)
}
}
impl<T> DataTransferSendBuilder<T> {
pub fn new(state: T) -> Self {
Self { state, types: vec![] }
}
}
impl<T> DataTransferSendBuilder<T> {
fn data_for_type(&self, type_: &dyn TransferType) -> Option<SendData> {
let (_, func) = self.types.iter().find(|(ty, _)| ty.matches(type_))?;
func(&self.state, type_)
}
pub fn add_type<Ty, F, O>(&mut self, type_: Ty, func: F) -> &mut Self
where
Ty: TransferType + Send,
F: Fn(&T, &dyn TransferType) -> Option<O> + Send + 'static,
O: Into<SendData>,
{
self.types
.push((Box::new(type_), Box::new(move |state, ty| func(state, ty).map(Into::into))));
self
}
pub fn with_type<Ty, F, O>(mut self, type_: Ty, func: F) -> Self
where
Ty: TransferType + Send,
F: Fn(&T, &dyn TransferType) -> Option<O> + Send + 'static,
O: Into<SendData>,
{
self.add_type(type_, func);
self
}
}
impl<T> DataTransferSendBuilder<T>
where
T: fmt::Debug + Send + 'static,
{
pub fn build(self) -> Box<dyn DataTransferSend> {
Box::new(self)
}
}