app
Adom Chipsmith
Public Made by Adomby adom
STEP-native chip validator — 5-source cross-validation, OCCT-generated copyright-free 3D models with embedded signal annotations, datasheet-to-STEP pipeline.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
use crate::cli::{err, hint, DEFAULT_PORT};
use anyhow::Result;
use clap::Parser;
use colored::Colorize;
use std::time::Duration;
#[derive(Parser)]
pub struct Args {
/// Follow mode — keep polling and print new messages.
#[arg(short, long)]
pub follow: bool,
#[arg(long, default_value_t = 0)]
pub since: u64,
#[arg(long, default_value_t = 50)]
pub tail: usize,
/// Comma-separated levels: log,warn,error.
#[arg(long)]
pub level: Option<String>,
#[arg(long, default_value_t = DEFAULT_PORT)]
pub port: u16,
}
pub fn run(args: Args) -> Result<()> {
let level_filter: Option<Vec<String>> = args
.level
.as_deref()
.map(|s| s.split(',').map(|x| x.trim().to_lowercase()).collect());
let mut since = args.since;
let mut first = true;
loop {
let url = format!("http://127.0.0.1:{}/console?since={}", args.port, since);
let resp = match ureq::get(&url).timeout(Duration::from_secs(2)).call() {
Ok(r) => r.into_json::<serde_json::Value>()?,
Err(e) => {
err(format!("could not reach adom-step: {e}"));
hint("start it first with `adom-step view <file.step>`");
return Err(anyhow::anyhow!("unreachable"));
}
};
let next = resp
.get("next_since")
.and_then(|v| v.as_u64())
.unwrap_or(since);
let empty = vec![];
let msgs = resp
.get("messages")
.and_then(|v| v.as_array())
.unwrap_or(&empty);
let slice: Vec<&serde_json::Value> = if first && msgs.len() > args.tail {
msgs.iter().skip(msgs.len() - args.tail).collect()
} else {
msgs.iter().collect()
};
for m in slice {
let lvl = m.get("level").and_then(|v| v.as_str()).unwrap_or("log");
if let Some(filter) = &level_filter {
if !filter.contains(&lvl.to_string()) {
continue;
}
}
let seq = m.get("seq").and_then(|v| v.as_u64()).unwrap_or(0);
let ts = m.get("ts_ms").and_then(|v| v.as_u64()).unwrap_or(0);
let text = m.get("text").and_then(|v| v.as_str()).unwrap_or("");
let ts_s = format!("{}.{:03}", ts / 1000, ts % 1000);
let tag = match lvl {
"error" => "ERR".red().bold().to_string(),
"warn" => "WRN".yellow().bold().to_string(),
_ => "LOG".cyan().bold().to_string(),
};
println!("[{:>6}] {} {} {}", seq, ts_s.dimmed(), tag, text);
}
since = next;
first = false;
if !args.follow {
break;
}
std::thread::sleep(Duration::from_millis(400));
}
Ok(())
}