ref:main
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RunnerConfig {
pub server_url: String,
pub runner_id: String,
pub runner_token: String,
pub name: String,
pub labels: Vec<String>,
pub work_dir: String,
pub parallel: u32,
#[serde(default = "default_poll_interval")]
pub poll_interval_ms: u64,
#[serde(default = "default_heartbeat_interval")]
pub heartbeat_interval_ms: u64,
#[serde(default)]
pub once: bool,
#[serde(default)]
pub ephemeral: bool,
#[serde(default = "default_cleanup")]
pub cleanup: String,
}
fn default_poll_interval() -> u64 {
5000
}
fn default_heartbeat_interval() -> u64 {
30000
}
fn default_cleanup() -> String {
"never".to_string()
}
impl RunnerConfig {
pub fn path(custom: Option<&str>) -> PathBuf {
if let Some(p) = custom {
return PathBuf::from(p);
}
dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join(".anvil-runner")
.join("config.json")
}
pub fn load(path: Option<&str>) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
let p = Self::path(path);
if !p.exists() {
return Err(format!(
"config not found at {} — run `anvil runner configure` first",
p.display()
)
.into());
}
let contents = std::fs::read_to_string(&p)?;
Ok(serde_json::from_str(&contents)?)
}
pub fn save(&self, path: Option<&str>) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let p = Self::path(path);
if let Some(parent) = p.parent() {
std::fs::create_dir_all(parent)?;
}
let contents = serde_json::to_string_pretty(self)?;
std::fs::write(&p, contents)?;
Ok(())
}
pub fn delete(path: Option<&str>) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let p = Self::path(path);
if p.exists() {
std::fs::remove_file(&p)?;
}
Ok(())
}
pub fn api_url(&self, path: &str) -> String {
let base = self.server_url.trim_end_matches('/');
format!("{base}/api/v1{path}")
}
pub fn auth_header(&self) -> String {
format!("Bearer {}", self.runner_token)
}
pub fn work_dir_path(&self) -> PathBuf {
PathBuf::from(&self.work_dir)
}
}