use std::{env::args, path::PathBuf};
use serde::{Deserialize, Serialize};
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::{
UnixStream,
unix::{OwnedReadHalf, OwnedWriteHalf},
},
};
use tracing::info;
use crate::prelude::*;
pub mod error;
pub mod prelude;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum BuilderEvent {
Exit,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum BuilderResponse {
Ack,
}
#[derive(Debug, Clone, Copy)]
pub enum Action {
Build,
Run,
}
impl TryFrom<&str> for Action {
type Error = Error;
fn try_from(value: &str) -> Result<Self> {
if value == "build" {
return Ok(Action::Build);
}
if value == "run" {
return Ok(Action::Run);
}
Err(Error::InvalidAction(String::from(value)))
}
}
impl From<Action> for &'static str {
fn from(value: Action) -> Self {
match value {
Action::Build => "build",
Action::Run => "run",
}
}
}
impl From<Action> for String {
fn from(value: Action) -> Self {
let value: &str = value.into();
Self::from(value)
}
}
pub struct BuilderSdk {
board_config_name: String,
config_path: String,
action: Action,
}
impl BuilderSdk {
pub async fn init<F>(event_callback: F) -> Result<Self>
where
F: Fn(BuilderEvent) + Send + Sync + 'static,
{
let args: Vec<String> = std::env::args().into_iter().collect();
if args.len() < 5 {
return Err(Error::MissingArgs(5, args.len()));
}
let stream = UnixStream::connect(&args[4]).await?;
tokio::spawn(async move { BuilderSdk::start_event_loop(stream, event_callback) });
let action: Action = TryFrom::<&str>::try_from(&args[1])?;
Ok(Self {
config_path: args[2].clone(),
board_config_name: args[3].clone(),
action,
})
}
pub fn board_config_name(&self) -> &str {
&self.board_config_name
}
pub fn config_path(&self) -> PathBuf {
PathBuf::from(&self.config_path)
}
pub fn action(&self) -> Action {
self.action
}
fn parse_event(payload: &str) -> Result<BuilderEvent> {
Ok(serde_json::from_str(payload)?)
}
async fn start_event_loop<F>(stream: UnixStream, cb: F) -> Result<()>
where
F: Fn(BuilderEvent) + Send + Sync + 'static,
{
let mut payload = String::new();
let (mut rx, mut tx) = stream.into_split();
loop {
match rx.read_to_string(&mut payload).await {
Ok(0) => break,
Ok(n) => {
let event = BuilderSdk::parse_event(&payload)?;
info!("Received event from builder {:?}", event);
cb(event);
info!("Acking event to builder");
let response = serde_json::to_string(&BuilderResponse::Ack)?;
tx.write_all(response.as_bytes()).await;
tx.write_all(b"\n").await;
tx.flush().await;
}
Err(e) => return Err(Error::from(e)),
}
}
Ok(())
}
}