graphwalker_restful/
lib.rs1pub mod actor;
2mod authoring_dto;
3mod authoring_rest;
4pub mod rest;
5pub mod session;
6pub mod websocket;
7
8use std::net::SocketAddr;
9
10use axum::extract::{FromRef, State, WebSocketUpgrade};
11use axum::response::IntoResponse;
12use axum::routing::{get, post, put};
13use axum::Router;
14use tower_http::cors::CorsLayer;
15
16use crate::actor::spawn_machine_thread;
17use crate::session::SessionManager;
18
19pub use authoring_rest::MAX_AUTHORING_BODY_BYTES;
20
21#[derive(Clone)]
22pub(crate) struct RestApplicationState {
23 execution: rest::RestState,
24 drafts: graphwalker_service::DraftRegistry,
25}
26
27impl FromRef<RestApplicationState> for rest::RestState {
28 fn from_ref(state: &RestApplicationState) -> Self {
29 state.execution.clone()
30 }
31}
32
33impl FromRef<RestApplicationState> for graphwalker_service::DraftRegistry {
34 fn from_ref(state: &RestApplicationState) -> Self {
35 state.drafts.clone()
36 }
37}
38
39pub async fn start_rest_server(
40 port: u16,
41 seed: Option<u64>,
42) -> Result<(), Box<dyn std::error::Error>> {
43 let app = rest_router(seed);
44 let addr = SocketAddr::from(([0, 0, 0, 0], port));
45 eprintln!("GraphWalker REST server listening on http://{}", addr);
46
47 let listener = tokio::net::TcpListener::bind(addr).await?;
48 axum::serve(listener, app).await?;
49 Ok(())
50}
51
52pub fn rest_router(default_seed: Option<u64>) -> Router {
54 rest_router_with_drafts(default_seed, graphwalker_service::DraftRegistry::default())
55}
56
57pub fn rest_router_with_drafts(
59 default_seed: Option<u64>,
60 drafts: graphwalker_service::DraftRegistry,
61) -> Router {
62 build_rest_router(RestApplicationState {
63 execution: rest::RestState {
64 machine_tx: spawn_machine_thread(),
65 default_seed,
66 },
67 drafts,
68 })
69}
70
71pub async fn start_websocket_server(port: u16) -> Result<(), Box<dyn std::error::Error>> {
72 let session_mgr = SessionManager::new();
73 let app = build_websocket_router(session_mgr);
74 let addr = SocketAddr::from(([0, 0, 0, 0], port));
75 eprintln!("GraphWalker WebSocket server listening on ws://{}", addr);
76
77 let listener = tokio::net::TcpListener::bind(addr).await?;
78 axum::serve(listener, app).await?;
79 Ok(())
80}
81
82fn build_rest_router(state: RestApplicationState) -> Router {
83 Router::new()
84 .route("/graphwalker/load", post(rest::load))
85 .route("/graphwalker/hasNext", get(rest::has_next))
86 .route("/graphwalker/getNext", get(rest::get_next))
87 .route("/graphwalker/getData", get(rest::get_data))
88 .route("/graphwalker/setData/{script}", put(rest::set_data))
89 .route("/graphwalker/restart", put(rest::restart))
90 .route("/graphwalker/getStatistics", get(rest::get_statistics))
91 .merge(authoring_rest::routes())
92 .layer(CorsLayer::permissive())
93 .with_state(state)
94}
95
96fn build_websocket_router(session_mgr: SessionManager) -> Router {
97 Router::new()
98 .route("/", get(ws_upgrade_handler))
99 .route("/graphwalker", get(ws_upgrade_handler))
100 .layer(CorsLayer::permissive())
101 .with_state(session_mgr)
102}
103
104async fn ws_upgrade_handler(
105 ws: WebSocketUpgrade,
106 State(session_mgr): State<SessionManager>,
107) -> impl IntoResponse {
108 ws.on_upgrade(move |socket| websocket::handle_socket(socket, session_mgr))
109}