use crate::error::WebDriverResult;
use base64::{Engine, prelude::BASE64_STANDARD};
use std::convert::Infallible;
use std::future::Future;
use std::panic::AssertUnwindSafe;
use std::path::Path;
use std::sync::LazyLock;
use std::time::Duration;
use std::{io, thread};
const BOX_FUTURE_THRESHOLD: usize = 512;
static GLOBAL_RT: LazyLock<tokio::runtime::Handle> = LazyLock::new(|| {
fn no_unwind<T>(f: impl FnOnce() -> T) -> T {
let res = std::panic::catch_unwind(AssertUnwindSafe(f));
res.unwrap_or_else(|_| {
struct Abort;
impl Drop for Abort {
fn drop(&mut self) {
eprintln!("unrecoverable error reached aborting...");
std::process::abort()
}
}
let _abort_on_unwind = Abort;
unreachable!("thirtyfour global runtime panicked")
})
}
no_unwind(|| {
let rt = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();
let handle = rt.handle().clone();
thread::spawn(move || -> ! {
async fn forever() -> ! {
match std::future::pending::<Infallible>().await {}
}
no_unwind(move || rt.block_on(forever()))
});
handle
})
});
pub fn block_on<F>(future: F) -> F::Output
where
F: Future + Send,
F::Output: Send,
{
if size_of::<F>() > BOX_FUTURE_THRESHOLD {
block_on_inner(Box::pin(future))
} else {
block_on_inner(future)
}
}
fn block_on_inner<F: Future>(future: F) -> F::Output {
cfg_if::cfg_if! {
if #[cfg(feature = "tokio-multi-threaded")] {
use tokio::runtime::RuntimeFlavor;
match tokio::runtime::Handle::try_current() {
Ok(handle) if handle.runtime_flavor() == RuntimeFlavor::MultiThread => {
tokio::task::block_in_place(|| handle.block_on(future))
}
_ => GLOBAL_RT.block_on(future),
}
} else {
GLOBAL_RT.block_on(future)
}
}
}
pub fn spawn_blocked_future<Fn, F>(future: Fn)
where
Fn: FnOnce(bool) -> F,
F: Future + Send + 'static,
{
if size_of::<F>() > BOX_FUTURE_THRESHOLD {
spawn_blocked_future_inner(Box::new(future))
} else {
spawn_blocked_future_inner(future)
}
}
fn spawn_blocked_future_inner<Fn, F>(future: Fn)
where
Fn: FnOnce(bool) -> F,
F: Future + Send + 'static,
{
macro_rules! spawn_off {
($future: expr, $try_handle: expr) => {{
let future = $future;
match $try_handle {
Ok(handle) => {
let (tx, rx) = std::sync::mpsc::sync_channel(0);
let handle_clone = handle.clone();
handle.spawn_blocking(move || {
if tx.send(()).is_ok() {
handle_clone.block_on(future);
}
});
rx.recv().expect("spawned task should be able to be scheduled properly")
}
Err(_) => {
GLOBAL_RT.block_on(future);
}
}
}};
($future: expr) => {{ spawn_off!($future, tokio::runtime::Handle::try_current()) }};
}
cfg_if::cfg_if! {
if #[cfg(feature = "tokio-multi-threaded")] {
use tokio::runtime::RuntimeFlavor;
match tokio::runtime::Handle::try_current() {
Ok(handle) if handle.runtime_flavor() == RuntimeFlavor::MultiThread => {
tokio::task::block_in_place(|| {
handle.block_on(future(false))
});
}
maybe_handle => spawn_off!(future(true), maybe_handle),
}
} else {
spawn_off!(future(true))
}
}
}
pub(crate) async fn write_file(
path: impl AsRef<Path>,
bytes: impl Into<Vec<u8>>,
) -> io::Result<()> {
async fn inner(path: &Path, bytes: Vec<u8>) -> io::Result<()> {
let path = path.to_owned();
tokio::task::spawn_blocking(move || std::fs::write(path, bytes)).await?
}
inner(path.as_ref(), bytes.into()).await
}
pub async fn sleep(duration: Duration) {
tokio::time::sleep(duration).await
}
pub fn base64_encode(data: &[u8]) -> String {
BASE64_STANDARD.encode(data)
}
pub fn base64_decode(data: &str) -> WebDriverResult<Vec<u8>> {
let value = BASE64_STANDARD.decode(data)?;
Ok(value)
}