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}