1use crate::experiments::store::ExperimentStore;
4use crate::experiments::tracker;
5use anyhow::Result;
6use std::path::PathBuf;
7
8pub async fn run(id: Option<String>, follow: bool, grep: Option<String>) -> Result<()> {
10 let db_path = tracker::experiments_db_path();
11 let store = ExperimentStore::open(&db_path)?;
12
13 let exp_id = match id {
15 Some(id) => id,
16 None => {
17 let exps = store.list(1)?;
19 match exps.first() {
20 Some(exp) => exp.id.clone(),
21 None => {
22 println!("No experiments yet. Run `zernel run <script>` first.");
23 return Ok(());
24 }
25 }
26 }
27 };
28
29 let exp = store
31 .get(&exp_id)?
32 .ok_or_else(|| anyhow::anyhow!("experiment not found: {exp_id}"))?;
33
34 let log_path = experiment_log_path(&exp_id);
35
36 println!("Log for experiment: {} ({})", exp.id, exp.name);
37 println!(" Status: {}", exp.status);
38 if let Some(d) = exp.duration_secs {
39 println!(" Duration: {d:.1}s");
40 }
41 println!(" Log file: {}", log_path.display());
42 println!();
43
44 if !log_path.exists() {
45 println!("(no log file found — logs are saved for experiments run with zernel >= v0.1.0)");
46 return Ok(());
47 }
48
49 let content = std::fs::read_to_string(&log_path)?;
50
51 if let Some(ref pattern) = grep {
52 let mut found = 0;
54 for (i, line) in content.lines().enumerate() {
55 if line.contains(pattern.as_str()) {
56 println!("{:>6}: {line}", i + 1);
57 found += 1;
58 }
59 }
60 if found == 0 {
61 println!("No lines matching '{pattern}'");
62 } else {
63 println!("\n{found} matching line(s)");
64 }
65 } else if follow && exp.status.to_string() == "Running" {
66 println!("--- following (Ctrl+C to stop) ---");
68 println!();
69
70 print!("{content}");
72
73 let mut last_len = content.len();
75 loop {
76 tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
77 if let Ok(new_content) = std::fs::read_to_string(&log_path) {
78 if new_content.len() > last_len {
79 print!("{}", &new_content[last_len..]);
80 last_len = new_content.len();
81 }
82 }
83 }
84 } else {
85 print!("{content}");
87 }
88
89 Ok(())
90}
91
92pub fn experiment_log_path(exp_id: &str) -> PathBuf {
94 tracker::zernel_dir()
95 .join("experiments")
96 .join(exp_id)
97 .join("output.log")
98}