1use std::{env::args, path::PathBuf};
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
39use crate::prelude::*;
40pub mod error;
41pub mod prelude;
42
43#[derive(Debug, Clone, Serialize, Deserialize)]
45pub enum BuilderEvent {
46 Exit,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
52pub enum BuilderResponse {
53 Ack,
55}
56#[derive(Debug, Clone, Copy)]
57pub enum Action {
58 Build,
59 Run,
60}
61
62impl TryFrom<&str> for Action {
63 type Error = Error;
64
65 fn try_from(value: &str) -> Result<Self> {
66 if value == "build" {
67 return Ok(Action::Build);
68 }
69 if value == "run" {
70 return Ok(Action::Run);
71 }
72 Err(Error::InvalidAction(String::from(value)))
73 }
74}
75
76impl From<Action> for &'static str {
77 fn from(value: Action) -> Self {
78 match value {
79 Action::Build => "build",
80 Action::Run => "run",
81 }
82 }
83}
84
85impl From<Action> for String {
86 fn from(value: Action) -> Self {
87 let value: &str = value.into();
88 Self::from(value)
89 }
90}
91
92pub struct BuilderSdk {
97 board_name: String,
99 board_config_name: String,
101 config_path: String,
103 action: Action,
105}
106
107impl BuilderSdk {
108 pub async fn init<F>(event_callback: F) -> Result<Self>
130 where
131 F: Fn(BuilderEvent) + Send + Sync + 'static,
132 {
133 let args: Vec<String> = std::env::args().into_iter().collect();
134 if args.len() < 6 {
135 return Err(Error::MissingArgs(6, args.len()));
136 }
137
138 let action: Action = TryFrom::<&str>::try_from(&args[1])?;
139
140 let stream = UnixStream::connect(&args[5]).await?;
141 tokio::spawn(async move { BuilderSdk::start_event_loop(stream, event_callback) });
142
143 Ok(Self {
144 config_path: args[2].clone(),
145 board_name: args[3].clone(),
146 board_config_name: args[4].clone(),
147 action,
148 })
149 }
150 pub fn action(&self) -> Action {
152 self.action
153 }
154 pub fn config_path(&self) -> PathBuf {
156 PathBuf::from(&self.config_path)
157 }
158 pub fn board_name(&self) -> &str {
160 &self.board_name
161 }
162 pub fn board_config_name(&self) -> &str {
164 &self.board_config_name
165 }
166 fn parse_event(payload: &str) -> Result<BuilderEvent> {
168 Ok(serde_json::from_str(payload)?)
169 }
170 async fn start_event_loop<F>(stream: UnixStream, cb: F) -> Result<()>
172 where
173 F: Fn(BuilderEvent) + Send + Sync + 'static,
174 {
175 let mut payload = String::new();
176 let (mut rx, mut tx) = stream.into_split();
177
178 loop {
179 match rx.read_to_string(&mut payload).await {
180 Ok(0) => break,
181 Ok(n) => {
182 let event = BuilderSdk::parse_event(&payload)?;
183 info!("Received event from builder {:?}", event);
184 cb(event);
185 info!("Acking event to builder");
186 let response = serde_json::to_string(&BuilderResponse::Ack)?;
187 tx.write_all(response.as_bytes()).await;
188 tx.write_all(b"\n").await;
189 tx.flush().await;
190 }
191 Err(e) => return Err(Error::from(e)),
192 }
193 }
194 Ok(())
195 }
196}