ej_builder_sdk/lib.rs
1//! Builder SDK for the EJ framework.
2//!
3//! Provides communication interface between builders and the EJ dispatcher.
4//!
5//! # Usage
6//!
7//! ```rust, no_run
8//! use ej_builder_sdk::{BuilderSdk, BuilderEvent};
9//!
10//! #[tokio::main]
11//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
12//! let sdk = BuilderSdk::init(|event| {
13//! match event {
14//! BuilderEvent::Exit => {
15//! // Cleanup logic here
16//! println!("Received exit signal");
17//! std::process::exit(0);
18//! }
19//! }
20//! }).await.unwrap();
21//!
22//! // Builder logic here
23//! Ok(())
24//! }
25//! ```
26
27use std::env::args;
28
29use serde::{Deserialize, Serialize};
30use tokio::{
31 io::{AsyncReadExt, AsyncWriteExt},
32 net::{
33 UnixStream,
34 unix::{OwnedReadHalf, OwnedWriteHalf},
35 },
36};
37use tracing::info;
38
39pub mod error;
40/// Error type for builder SDK operations.
41pub use crate::error::Error;
42
43/// Result type for builder SDK operations.
44pub type Result<T> = core::result::Result<T, Error>;
45
46/// Events sent from the dispatcher to the builder.
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub enum BuilderEvent {
49 /// Request to exit the builder.
50 Exit,
51}
52
53/// Responses sent from the builder to the dispatcher.
54#[derive(Debug, Clone, Serialize, Deserialize)]
55pub enum BuilderResponse {
56 /// Acknowledge receipt of an event.
57 Ack,
58}
59
60/// Builder SDK for communicating with the EJ dispatcher.
61///
62/// Handles Unix socket communication and event processing between
63/// the builder and dispatcher.
64pub struct BuilderSdk {
65 /// The board configuration name.
66 board_config_name: String,
67 /// The path to the config.toml file.
68 config_path: String,
69}
70
71impl BuilderSdk {
72 /// Initialize the builder SDK and start event processing.
73 ///
74 /// Sets up Unix socket communication with the dispatcher and starts
75 /// an async event loop to handle incoming events.
76 ///
77 /// # Arguments
78 ///
79 /// * `event_callback` - Function called when events are received
80 ///
81 /// # Examples
82 ///
83 /// ```rust,no_run
84 /// use ej_builder_sdk::{BuilderSdk, BuilderEvent};
85 /// # tokio_test::block_on(async {
86 /// let sdk = BuilderSdk::init(|event| {
87 /// match event {
88 /// BuilderEvent::Exit => std::process::exit(0),
89 /// }
90 /// }).await.unwrap();
91 /// # });
92 /// ```
93 pub async fn init<F>(event_callback: F) -> Result<Self>
94 where
95 F: Fn(BuilderEvent) + Send + Sync + 'static,
96 {
97 let args: Vec<String> = std::env::args().into_iter().collect();
98 if args.len() < 4 {
99 return Err(Error::MissingArgs(3, args.len()));
100 }
101
102 let stream = UnixStream::connect(&args[3]).await?;
103 tokio::spawn(async move { BuilderSdk::start_event_loop(stream, event_callback) });
104
105 Ok(Self {
106 board_config_name: args[1].clone(),
107 config_path: args[2].clone(),
108 })
109 }
110 /// Get the board configuration name.
111 pub fn board_config_name(&self) -> &str {
112 &self.board_config_name
113 }
114 /// Get the path to the config.toml file.
115 pub fn config_path(&self) -> &str {
116 &self.config_path
117 }
118 /// Parse event data from JSON string.
119 fn parse_event(payload: &str) -> Result<BuilderEvent> {
120 Ok(serde_json::from_str(payload)?)
121 }
122 /// Start the event loop for processing dispatcher messages.
123 async fn start_event_loop<F>(stream: UnixStream, cb: F) -> Result<()>
124 where
125 F: Fn(BuilderEvent) + Send + Sync + 'static,
126 {
127 let mut payload = String::new();
128 let (mut rx, mut tx) = stream.into_split();
129
130 loop {
131 match rx.read_to_string(&mut payload).await {
132 Ok(0) => break,
133 Ok(n) => {
134 let event = BuilderSdk::parse_event(&payload)?;
135 info!("Received event from builder {:?}", event);
136 cb(event);
137 info!("Acking event to builder");
138 let response = serde_json::to_string(&BuilderResponse::Ack)?;
139 tx.write_all(response.as_bytes()).await;
140 tx.write_all(b"\n").await;
141 tx.flush().await;
142 }
143 Err(e) => return Err(Error::from(e)),
144 }
145 }
146 Ok(())
147 }
148}