1use std::path::{Path, PathBuf};
2
3use serde::Deserialize;
4
5#[derive(Debug, Default, Deserialize)]
7#[serde(default)]
8pub struct RunConfig {
9 pub language: Option<String>,
11 pub timeout: Option<u64>,
13 pub timing: Option<bool>,
15 pub bench_iterations: Option<u32>,
17}
18
19impl RunConfig {
20 pub fn discover() -> Self {
23 let cwd = std::env::current_dir().ok();
24 let cwd = match cwd {
25 Some(ref p) => p.as_path(),
26 None => return Self::default(),
27 };
28
29 for dir in cwd.ancestors() {
30 for name in &["run.toml", ".runrc"] {
31 let candidate = dir.join(name);
32 if candidate.is_file() {
33 if let Ok(config) = Self::load(&candidate) {
34 return config;
35 }
36 }
37 }
38 }
39
40 Self::default()
41 }
42
43 pub fn load(path: &Path) -> Result<Self, String> {
44 let content = std::fs::read_to_string(path)
45 .map_err(|e| format!("failed to read {}: {e}", path.display()))?;
46 toml::from_str(&content)
47 .map_err(|e| format!("invalid config in {}: {e}", path.display()))
48 }
49
50 pub fn apply_env(&self) {
51 if let Some(secs) = self.timeout {
52 if std::env::var("RUN_TIMEOUT_SECS").is_err() {
53 unsafe { std::env::set_var("RUN_TIMEOUT_SECS", secs.to_string()); }
55 }
56 }
57 if let Some(true) = self.timing {
58 if std::env::var("RUN_TIMING").is_err() {
59 unsafe { std::env::set_var("RUN_TIMING", "1"); }
61 }
62 }
63 }
64
65 pub fn find_config_path() -> Option<PathBuf> {
66 let cwd = std::env::current_dir().ok()?;
67 for dir in cwd.ancestors() {
68 for name in &["run.toml", ".runrc"] {
69 let candidate = dir.join(name);
70 if candidate.is_file() {
71 return Some(candidate);
72 }
73 }
74 }
75 None
76 }
77}