summaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/control/Cargo.toml9
-rw-r--r--crates/control/src/lib.rs81
-rw-r--r--crates/web/Cargo.toml1
-rw-r--r--crates/web/src/main.rs20
4 files changed, 104 insertions, 7 deletions
diff --git a/crates/control/Cargo.toml b/crates/control/Cargo.toml
new file mode 100644
index 0000000..6934db5
--- /dev/null
+++ b/crates/control/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "argonaut-control"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+tokio = { workspace = true }
+tracing = { workspace = true }
+tracing-subscriber = { workspace = true }
diff --git a/crates/control/src/lib.rs b/crates/control/src/lib.rs
new file mode 100644
index 0000000..5691cf2
--- /dev/null
+++ b/crates/control/src/lib.rs
@@ -0,0 +1,81 @@
+//! Printer control related code
+//!
+//! To ensure the volume of web requests doesn't mess with printing, communication with the printer is done
+//! in a separate thread, on its own async executor.
+//!
+//! The main server spawns this thread by creating a [`Handle`] and communicates by sending [messages](crate::Message) over an MPSC channel.
+
+use std::{
+ mem::ManuallyDrop,
+ thread::{self, JoinHandle},
+};
+use tokio::{
+ runtime,
+ sync::mpsc::{self, Receiver, Sender},
+};
+use tracing::info;
+
+/// The maximum number of messages to buffer before 'blocking' new ones
+/// This provides a form of backpressure
+const CHANNEL_CAPACITY: usize = 100;
+
+/// A control message sent from the API to the printer control thread
+pub enum Message {
+ Quit,
+}
+
+/// A handle to the printer control thread, allowing for communication.
+pub struct Handle {
+ cmd_send: Sender<Message>,
+ thread_handle: ManuallyDrop<JoinHandle<()>>,
+}
+
+impl Handle {
+ /// Spawn a printer control thread, returning a handle
+ pub fn spawn() -> Self {
+ let (send, recv) = mpsc::channel(CHANNEL_CAPACITY);
+
+ let state = State { cmd_recv: recv };
+ let thread_handle = thread::spawn(move || {
+ let rt = runtime::Builder::new_current_thread()
+ .build()
+ .expect("error spawning printer control thread");
+
+ rt.block_on(async move { state.entrypoint().await });
+ });
+
+ Self {
+ cmd_send: send,
+ thread_handle: ManuallyDrop::new(thread_handle),
+ }
+ }
+}
+
+impl Drop for Handle {
+ fn drop(&mut self) {
+ let _ = self.cmd_send.blocking_send(Message::Quit);
+ unsafe {
+ info!("waiting for printer thread to exit...");
+ ManuallyDrop::take(&mut self.thread_handle)
+ .join()
+ .expect("error joining printer thread")
+ };
+ }
+}
+
+/// The state held by the printer control thread
+pub struct State {
+ cmd_recv: Receiver<Message>,
+}
+
+impl State {
+ /// The entrypoint of the printer control thread
+ pub async fn entrypoint(mut self) {
+ info!("listening for events");
+ while let Some(msg) = self.cmd_recv.recv().await {
+ match msg {
+ Message::Quit => break,
+ }
+ }
+ }
+}
diff --git a/crates/web/Cargo.toml b/crates/web/Cargo.toml
index 62a3504..2528a0a 100644
--- a/crates/web/Cargo.toml
+++ b/crates/web/Cargo.toml
@@ -4,6 +4,7 @@ version = "0.1.0"
edition = "2021"
[dependencies]
+argonaut-control = { path = "../control" }
axum = "0.7.5"
tracing = { workspace = true }
tracing-subscriber = { workspace = true }
diff --git a/crates/web/src/main.rs b/crates/web/src/main.rs
index f641ce0..d80b232 100644
--- a/crates/web/src/main.rs
+++ b/crates/web/src/main.rs
@@ -1,17 +1,23 @@
-use axum::{
- http::StatusCode,
- routing::{get, post},
- Json, Router,
-};
-use serde::{Deserialize, Serialize};
+use std::sync::Arc;
+
+use axum::{routing::get, Router};
+
+pub use argonaut_control::Handle as PrinterThread;
+use tracing::info;
#[tokio::main(flavor = "current_thread")]
async fn main() {
tracing_subscriber::fmt::init();
- let app = Router::new().route("/", get(root));
+ let printer_thread = PrinterThread::spawn();
+ let printer_thread = Arc::new(printer_thread);
+ let app = Router::new()
+ .route("/", get(root))
+ .with_state(printer_thread);
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
+
+ info!("init completed, listening");
axum::serve(listener, app).await.unwrap();
}