together_companion/
together_companion.rs1#![no_std]
16
17extern crate alloc;
18
19use alloc::rc::Rc;
20use core::cell::Cell;
21
22use serde::Serialize;
23use standard_plugin::daemon::{Context, DaemonPlugin, process};
24use standard_plugin::prelude::{Error, Json, Target, format};
25
26#[derive(Serialize)]
27struct Status<'a> {
28 state: &'a str,
29 builds: u32,
30}
31
32fn publish(cx: &Context, state: &str, builds: u32) {
35 let _ = cx
36 .values()
37 .set("together.status", &Status { state, builds });
38 let _ = cx.events().emit("together.changed", &(), Target::Viewers);
39}
40
41async fn rebuild(cx: Context, builds: Rc<Cell<u32>>, running: Rc<Cell<bool>>) {
43 publish(&cx, "building", builds.get());
44 let passed = process::spawn("make", &[], None)
45 .and_then(|child| child.wait())
46 .is_ok_and(|status| status == 0);
47 builds.set(builds.get() + 1);
48 running.set(false);
49 publish(&cx, if passed { "passed" } else { "failed" }, builds.get());
50}
51
52#[standard_plugin::daemon]
53struct Companion {
54 builds: Rc<Cell<u32>>,
55 running: Rc<Cell<bool>>,
56}
57
58impl DaemonPlugin for Companion {
59 fn activate(_cx: &Context) -> Self {
60 Self {
61 builds: Rc::new(Cell::new(0)),
62 running: Rc::new(Cell::new(false)),
63 }
64 }
65
66 async fn run(&self, cx: Context) {
67 publish(&cx, "idle", self.builds.get());
68 }
69
70 fn call(&self, method: &str, _request: Json, cx: &Context) -> Result<Json, Error> {
74 match method {
75 "rebuild" => {
76 let started = !self.running.replace(true);
77 if started {
78 cx.spawn(rebuild(
79 cx.clone(),
80 self.builds.clone(),
81 self.running.clone(),
82 ));
83 }
84 Json::from_value(&started)
85 }
86 other => Err(Error::Invalid(format!("unknown method {other}"))),
87 }
88 }
89}