1use futures::channel::oneshot;
4use futures::FutureExt;
5use serde::Deserialize;
6use std::net::{IpAddr, Ipv4Addr, SocketAddr};
7use std::sync::mpsc;
8use std::sync::{Arc, Mutex};
9use warp::Filter;
10
11mod device;
12mod error;
13mod output;
14mod snapshot;
15
16pub use device::Device;
17pub use error::Error;
18pub use output::Output;
19pub use snapshot::Metadata;
20
21pub struct Disposable(Option<oneshot::Sender<mpsc::Sender<()>>>);
23
24#[derive(Debug, Deserialize)]
26pub enum Message {
27 Snapshot {
30 test_name: String,
32
33 snapshot_name: String,
36 },
37
38 OpenURL(String),
40}
41
42impl Drop for Disposable {
43 fn drop(&mut self) {
44 if let Some(sender) = self.0.take() {
45 let (tx, rx) = mpsc::channel();
46 let _ = sender.send(tx);
47 rx.recv().unwrap();
48 }
49 }
50}
51
52fn handler(device: Device) -> impl warp::Filter<Extract = impl warp::Reply> + Clone {
53 let output = Arc::new(Mutex::new(Output::new("target/polyhorn-snapshots")));
54
55 warp::path!("polyhorn" / "tests" / String)
56 .and(warp::post())
57 .and(warp::body::json())
58 .map(move |_id, message: Message| {
59 match message {
60 Message::OpenURL(url) => {
61 device.open_url(&url).unwrap();
62 }
63 Message::Snapshot {
64 test_name,
65 snapshot_name,
66 } => {
67 let screenshot = device.screenshot().unwrap();
68
69 output.lock().unwrap().store(
70 snapshot::Metadata {
71 test_name: Some(test_name),
72 snapshot_name: Some(snapshot_name),
73 ..device.metadata()
74 },
75 screenshot,
76 );
77 }
78 }
79
80 "Ok"
81 })
82}
83
84pub fn serve(device: Device) -> (SocketAddr, Disposable) {
88 let (sender, receiver) = std::sync::mpsc::channel();
89
90 std::thread::spawn(move || {
91 let mut runtime = tokio::runtime::Runtime::new().unwrap();
92 runtime.block_on(async move {
93 let (drop_sender, drop_receiver) = oneshot::channel();
94
95 let server = warp::serve(handler(device));
96
97 let (addr, runloop) =
98 server.bind_ephemeral(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0));
99
100 sender.send((addr, Disposable(Some(drop_sender)))).unwrap();
101
102 let mut drop_receiver = drop_receiver.fuse();
103 let mut runloop = runloop.fuse();
104
105 futures::select! {
106 tx = drop_receiver => {
107 std::mem::drop(runloop);
108
109 tx.unwrap().send(()).unwrap();
110 },
111 _ = runloop => {}
112 };
113 });
114 });
115
116 receiver.recv().unwrap()
117}