use std::{future::Future, net::SocketAddr};
use wry::{
application::{
event::{Event, StartCause, WindowEvent},
event_loop::{ControlFlow, EventLoop},
},
webview::WebViewBuilder,
};
pub use wry::application::window::{self, WindowBuilder};
pub trait EntryPoint {
fn url(&self) -> String;
}
impl EntryPoint for SocketAddr {
fn url(&self) -> String {
format!("http://{}:{}/", self.ip(), self.port())
}
}
impl EntryPoint for &SocketAddr {
fn url(&self) -> String {
format!("http://{}:{}/", self.ip(), self.port())
}
}
impl EntryPoint for String {
fn url(&self) -> String {
self.clone()
}
}
impl EntryPoint for &str {
fn url(&self) -> String {
String::from(*self)
}
}
trait DoneServing {
fn done_serving(&self) -> bool;
}
#[cfg(feature = "tokio")]
impl<T> DoneServing for (tokio::runtime::Runtime, tokio::task::JoinHandle<T>) {
fn done_serving(&self) -> bool {
self.1.is_finished()
}
}
#[cfg(feature = "asyncstd")]
impl<T> DoneServing
for (
std::sync::Arc<std::sync::Mutex<bool>>,
async_std::task::JoinHandle<T>,
)
{
fn done_serving(&self) -> bool {
let guard = self.0.lock().unwrap();
*guard
}
}
#[cfg(feature = "smol")]
impl<T> DoneServing for smol::Task<T> {
fn done_serving(&self) -> bool {
self.is_finished()
}
}
#[cfg(all(feature = "tokio", not(feature = "asyncstd"), not(feature = "smol")))]
fn run_server<F>(serve: F) -> impl DoneServing
where
F: Future<Output = ()> + Send + 'static,
{
let runtime = tokio::runtime::Runtime::new().unwrap();
let serving = runtime.spawn(serve);
(runtime, serving)
}
#[cfg(all(not(feature = "tokio"), feature = "asyncstd", not(feature = "smol")))]
fn run_server<F>(serve: F) -> impl DoneServing
where
F: Future<Output = ()> + Send + 'static,
{
use std::sync::{Arc, Mutex};
let finished = Arc::new(Mutex::new(false));
(
finished.clone(),
async_std::task::spawn(async move {
serve.await;
let mut guard = finished.lock().unwrap();
*guard = true;
}),
)
}
#[cfg(all(not(feature = "tokio"), not(feature = "asyncstd"), feature = "smol"))]
fn run_server<F>(serve: F) -> impl DoneServing
where
F: Future<Output = ()> + Send + 'static,
{
if let Err(_) = std::env::var("SMOL_THREADS") {
std::env::set_var(
"SMOL_THREADS",
std::thread::available_parallelism()
.unwrap_or(std::num::NonZeroUsize::new(1).unwrap())
.to_string(),
);
}
smol::spawn(serve)
}
#[cfg(any(
all(feature = "tokio", feature = "asyncstd"),
all(feature = "tokio", feature = "smol"),
all(feature = "asyncstd", feature = "smol"),
not(any(feature = "tokio", feature = "asyncstd", feature = "smol"))
))]
fn run_server<F>(serve: F) -> impl DoneServing {
compile_error!("Exactly one of the tokio, asyncstd, or smol features must be enabled.")
}
pub fn launch_with_windowbuilder<F>(
window_builder: WindowBuilder,
entrypoint: impl EntryPoint,
serve: F,
) -> wry::Result<()>
where
F: Future<Output = ()> + Send + 'static,
{
let serving = run_server(serve);
let entrypoint = entrypoint.url();
let event_loop = EventLoop::new();
let window = window_builder.build(&event_loop)?;
let _webview = WebViewBuilder::new(window)?
.with_url(&entrypoint)?
.build()?;
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
if serving.done_serving() {
*control_flow = ControlFlow::Exit;
} else {
match event {
Event::NewEvents(StartCause::Init) => {}
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
_ => {}
}
}
});
}
pub fn launch<F>(title: impl AsRef<str>, entrypoint: impl EntryPoint, serve: F) -> wry::Result<()>
where
F: Future<Output = ()> + Send + 'static,
{
launch_with_windowbuilder(
WindowBuilder::new().with_title(title.as_ref()),
entrypoint,
serve,
)
}