app
AI Flow: JLCPCB
Public Made by Adomby adom
The whole JLCPCB order, from gerbers to assembled boards in hand: proven part swaps, design rules, rotation checks, quote and order.
main
//! The project's flow folder (`<project>/<flow>/`): state.json plus run.jsonl, one line per command
//! with the time, the calling AI thread and what it did. Never rewrite earlier lines.
use serde_json::{json, Value};
use std::io::Write;
use std::path::{Path, PathBuf};
pub fn now() -> String {
let out = std::process::Command::new("date").args(["-u", "+%Y-%m-%dT%H:%M:%SZ"]).output();
out.map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string()).unwrap_or_default()
}
#[allow(dead_code)]
pub struct Project { pub dir: PathBuf, pub flow_dir: PathBuf, pub state: Value }
impl Project {
/// The nearest folder at or above `start` that holds `<flow>/state.json`.
pub fn find(start: &Path, flow: &str) -> Option<Project> {
let mut d = Some(start.to_path_buf());
while let Some(p) = d {
let s = p.join(flow).join("state.json");
if s.is_file() {
let state = std::fs::read_to_string(&s).ok().and_then(|t| serde_json::from_str(&t).ok()).unwrap_or(json!({}));
return Some(Project { flow_dir: p.join(flow), dir: p, state });
}
d = p.parent().map(Path::to_path_buf);
}
None
}
pub fn create(dir: &Path, flow: &str, state: Value) -> std::io::Result<Project> {
let flow_dir = dir.join(flow);
std::fs::create_dir_all(&flow_dir)?;
let p = Project { dir: dir.to_path_buf(), flow_dir, state };
p.save()?;
Ok(p)
}
pub fn save(&self) -> std::io::Result<()> {
std::fs::write(self.flow_dir.join("state.json"), serde_json::to_string_pretty(&self.state)? + "\n")
}
pub fn path(&self, rel: &str) -> PathBuf { self.flow_dir.join(rel) }
pub fn log(&self, thread: &str, ev: &str, data: Value) {
let line = json!({"t": now(), "aiThread": thread, "ev": ev, "data": data});
if let Ok(mut f) = std::fs::OpenOptions::new().create(true).append(true).open(self.flow_dir.join("run.jsonl")) {
let _ = writeln!(f, "{line}");
}
}
}