#[allow(unused_imports)]
use crate::*;
#[cfg(target_os = "android")]
mod async_sleep;
#[cfg(target_os = "android")]
#[sync_async::sync_async]
pub mod utils {
use super::*;
#[maybe_async]
pub fn run_blocking<T, F>(task: F) -> Result<T>
where
T: Send + 'static,
F: FnOnce() -> Result<T> + Send + 'static,
{
#[if_async] {
tauri::async_runtime::spawn_blocking(task).await?
}
#[if_sync] {
task()
}
}
#[maybe_async]
pub fn sleep(time: std::time::Duration) {
if time == std::time::Duration::ZERO {
return
}
#[if_async] {
async_sleep::sleep(time).await;
}
#[if_sync] {
std::thread::sleep(time);
}
}
}
#[derive(serde::Deserialize)]
#[serde(untagged)]
#[cfg_attr(not(target_os = "android"), allow(unused))]
#[cfg(any(feature = "commands", feature = "protocol_content", feature = "protocol_thumbnail"))]
pub enum AfsUriOrFsPath {
AfsUri(FileUri),
FsPath(tauri_plugin_fs::FilePath),
}
#[cfg(all(
target_os = "android",
any(feature = "commands", feature = "protocol_content", feature = "protocol_thumbnail")
))]
impl AfsUriOrFsPath {
pub fn try_into_content_or_safe_file_scheme_uri(self) -> Result<FileUri> {
match self {
AfsUriOrFsPath::AfsUri(uri) => {
if !uri.is_content_scheme() {
return Err(Error::invalid_uri_scheme(&uri.uri))
}
Ok(uri)
},
AfsUriOrFsPath::FsPath(path) => {
match path {
tauri_plugin_fs::FilePath::Path(path) => {
Ok(FileUri::from_path(tauri::path::SafePathBuf::new(path)?))
},
tauri_plugin_fs::FilePath::Url(url) => {
if url.scheme() != "content" {
return Err(Error::invalid_uri_scheme(url))
}
Ok(FileUri::from_uri(url))
}
}
},
}
}
}
#[cfg(all(target_os = "android", feature = "commands"))]
impl AfsUriOrFsPath {
pub fn try_into_content_uri(self) -> Result<FileUri> {
match self {
AfsUriOrFsPath::AfsUri(uri) => {
if !uri.is_content_scheme() {
return Err(Error::invalid_uri_scheme(&uri.uri))
}
Ok(uri)
},
AfsUriOrFsPath::FsPath(path) => {
match path {
tauri_plugin_fs::FilePath::Path(_) => {
Err(Error::with("invalid value: expected a content-scheme URI"))
},
tauri_plugin_fs::FilePath::Url(url) => {
if url.scheme() != "content" {
return Err(Error::invalid_uri_scheme(url))
}
Ok(FileUri::from_uri(url))
}
}
},
}
}
}
pub fn encode_android_uri_component(input: impl AsRef<str>) -> String {
const SAFE: &percent_encoding::AsciiSet = &percent_encoding::NON_ALPHANUMERIC
.remove(b'_')
.remove(b'-')
.remove(b'!')
.remove(b'.')
.remove(b'~')
.remove(b'\'')
.remove(b'(')
.remove(b')')
.remove(b'*');
percent_encoding::utf8_percent_encode(input.as_ref(), SAFE).to_string()
}
#[cfg(target_os = "android")]
pub fn range_to_offset_and_len(range: impl std::ops::RangeBounds<u64>) -> (u128, Option<u128>) {
use std::ops::Bound::{Included, Excluded, Unbounded};
let offset = match range.start_bound() {
Included(&v) => v as u128,
Excluded(&v) => v as u128 + 1,
Unbounded => 0,
};
let len = match range.end_bound() {
Included(&v) => Some((v as u128 + 1).saturating_sub(offset)),
Excluded(&v) => Some((v as u128).saturating_sub(offset)),
Unbounded => None,
};
(offset, len)
}
#[cfg(target_os = "android")]
pub fn saturate_u128_to_u64(val: u128) -> u64 {
u128::min(val, u64::MAX as u128) as u64
}
#[cfg(target_os = "android")]
pub fn validate_relative_path(path: &std::path::Path) -> Result<&std::path::Path> {
for component in path.components() {
use std::path::Component::*;
match component {
RootDir => return Err(crate::Error::with("must not start with root directory")),
ParentDir => return Err(crate::Error::with("must not contain parent directory, i.e., '..'")),
CurDir => return Err(crate::Error::with("must not contain current directory, i.e., '.'")),
Prefix(_) => (),
Normal(_) => (),
}
}
Ok(path)
}
#[cfg(target_os = "android")]
pub fn upgrade_bytes_ref<B: AsRef<[u8]>>(buf: B) -> Vec<u8> {
fn nonstatic_typeid<T>() -> std::any::TypeId
where
T: ?Sized,
{
trait NonStaticAny {
fn get_type_id(&self) -> std::any::TypeId
where
Self: 'static;
}
impl<T: ?Sized> NonStaticAny for std::marker::PhantomData<T> {
#[inline(always)]
fn get_type_id(&self) -> std::any::TypeId
where
Self: 'static,
{
std::any::TypeId::of::<T>()
}
}
let phantom_data = std::marker::PhantomData::<T>;
NonStaticAny::get_type_id(unsafe {
std::mem::transmute::<&dyn NonStaticAny, &(dyn NonStaticAny + 'static)>(&phantom_data)
})
}
unsafe fn try_transmute<Src, Target: 'static>(x: Src) -> std::result::Result<Target, Src> {
if nonstatic_typeid::<Src>() == std::any::TypeId::of::<Target>() {
let x = std::mem::ManuallyDrop::new(x);
Ok(std::mem::transmute_copy::<Src, Target>(&x))
}
else {
Err(x)
}
}
let buf = match unsafe { try_transmute::<B, Vec<u8>>(buf) } {
Ok(vec) => return vec,
Err(original_buf) => original_buf,
};
let buf = match unsafe { try_transmute::<B, String>(buf) } {
Ok(string) => return string.into_bytes(),
Err(original_buf) => original_buf,
};
buf.as_ref().to_owned()
}
#[cfg(target_os = "android")]
pub struct BoundedHashMap<K, V> {
map: std::collections::HashMap<K, V>,
order: std::collections::VecDeque<K>,
bound: usize,
}
#[cfg(target_os = "android")]
impl<K: Eq + std::hash::Hash + Clone, V> BoundedHashMap<K, V> {
pub fn with_bound(bound: usize) -> Self {
Self {
map: std::collections::HashMap::new(),
order: std::collections::VecDeque::new(),
bound,
}
}
pub fn insert(&mut self, key: K, value: V) {
if self.map.contains_key(&key) {
self.order.retain(|k| k != &key);
}
self.map.insert(key.clone(), value);
self.order.push_back(key);
if self.bound < self.map.len() {
if let Some(oldest_key) = self.order.pop_front() {
self.map.remove(&oldest_key);
}
}
}
pub fn get<Q>(&self, key: &Q) -> Option<&V>
where
Q: ?Sized + std::hash::Hash + Eq,
K: std::borrow::Borrow<Q>
{
self.map.get(key)
}
}