Skip to main content

dada_debug/
server.rs

1use std::{
2    sync::{
3        Arc, Mutex,
4        mpsc::{Receiver, RecvTimeoutError},
5    },
6    time::Duration,
7};
8
9use axum::{Json, Router, routing::get};
10use dada_ir_ast::DebugEvent;
11use serde::{Deserialize, Serialize};
12
13pub fn main(port: u32, debug_rx: Receiver<DebugEvent>) -> anyhow::Result<()> {
14    tokio::runtime::Builder::new_current_thread()
15        .enable_all()
16        .build()?
17        .block_on(main_async(port, debug_rx))?;
18    Ok(())
19}
20
21async fn main_async(port: u32, debug_rx: Receiver<DebugEvent>) -> anyhow::Result<()> {
22    // initialize tracing
23    tracing_subscriber::fmt::init();
24
25    let state = Arc::new(State {
26        debug_events: Default::default(),
27        shutdown: Default::default(),
28    });
29
30    std::thread::spawn({
31        let state = state.clone();
32        move || record_events(debug_rx, state)
33    });
34
35    // build our application with a route
36    let app = Router::new()
37        // `GET /` goes to `root`
38        .route("/", get(root))
39        .route("/view/{event_index}", get(view))
40        .route("/assets/{file}", get(assets))
41        .route("/source/{*path}", get(source))
42        .route("/events", get(events))
43        .route("/events/{event_index}", get(event_data))
44        .with_state(state.clone());
45
46    // run our app with hyper, listening globally on port 3000
47    let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{port}"))
48        .await
49        .unwrap();
50
51    axum::serve(listener, app).await?;
52
53    *state.shutdown.lock().unwrap() = true;
54
55    Ok(())
56}
57
58fn respond_ok_or_500<B: Into<String>>(body: anyhow::Result<B>) -> axum::http::Response<String> {
59    match body {
60        Ok(body) => axum::http::Response::builder()
61            .status(200)
62            .body(body.into())
63            .unwrap(),
64        Err(err) => axum::http::Response::builder()
65            .status(500)
66            .body(crate::error::error(err))
67            .unwrap(),
68    }
69}
70
71fn respond_json_or_500<T: Serialize>(result: anyhow::Result<T>) -> axum::response::Result<Json<T>> {
72    match result {
73        Ok(data) => Ok(Json(data)),
74        Err(err) => Err(axum::response::Response::builder()
75            .status(500)
76            .body(crate::error::error(err))
77            .unwrap()
78            .into()),
79    }
80}
81
82async fn root(
83    axum::extract::State(state): axum::extract::State<Arc<State>>,
84) -> axum::http::Response<String> {
85    respond_ok_or_500(crate::root::root(&state).await)
86}
87
88async fn view(
89    axum::extract::Path(event_index): axum::extract::Path<usize>,
90    axum::extract::State(state): axum::extract::State<Arc<State>>,
91) -> axum::http::Response<String> {
92    respond_ok_or_500(crate::view::try_view(event_index, &state).await)
93}
94
95async fn assets(
96    axum::extract::Path(file): axum::extract::Path<String>,
97) -> axum::http::Response<String> {
98    respond_ok_or_500(crate::assets::try_asset(&file))
99}
100
101async fn events(
102    headers: axum::http::header::HeaderMap,
103    axum::extract::State(state): axum::extract::State<Arc<State>>,
104) -> axum::response::Result<Json<Vec<crate::root::RootEvent>>> {
105    respond_json_or_500(crate::events::events(&headers, &state).await)
106}
107
108async fn event_data(
109    headers: axum::http::header::HeaderMap,
110    axum::extract::Path(event_index): axum::extract::Path<usize>,
111    axum::extract::State(state): axum::extract::State<Arc<State>>,
112) -> axum::response::Result<Json<serde_json::Value>> {
113    respond_json_or_500(crate::events::try_event_data(&headers, event_index, &state).await)
114}
115
116#[derive(Deserialize, Debug)]
117struct SourceQueryArgs {
118    line: u32,
119    column: u32,
120}
121
122async fn source(
123    axum::extract::Path(path): axum::extract::Path<String>,
124    axum::extract::Query(line_col): axum::extract::Query<SourceQueryArgs>,
125) -> axum::http::Response<String> {
126    respond_ok_or_500(crate::source::try_source(
127        &path,
128        line_col.line,
129        line_col.column,
130    ))
131}
132
133pub struct State {
134    pub debug_events: Mutex<Vec<Arc<DebugEvent>>>,
135    pub shutdown: Mutex<bool>,
136}
137
138fn record_events(debug_rx: Receiver<DebugEvent>, state: Arc<State>) {
139    while !*state.shutdown.lock().unwrap() {
140        match debug_rx.recv_timeout(Duration::from_secs(1)) {
141            Ok(event) => {
142                state.debug_events.lock().unwrap().push(Arc::new(event));
143            }
144            Err(RecvTimeoutError::Disconnected) => return,
145            Err(RecvTimeoutError::Timeout) => (),
146        }
147    }
148}