use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct ClientConfig {
pub api_url: String,
pub format: String,
pub log_level: String,
pub run: ClientRunConfig,
pub slurm: ClientSlurmConfig,
pub hpc: ClientHpcConfig,
pub watch: ClientWatchConfig,
pub tls: ClientTlsConfig,
}
impl Default for ClientConfig {
fn default() -> Self {
Self {
api_url: "http://localhost:8080/torc-service/v1".to_string(),
format: "table".to_string(),
log_level: "info".to_string(),
run: ClientRunConfig::default(),
slurm: ClientSlurmConfig::default(),
hpc: ClientHpcConfig::default(),
watch: ClientWatchConfig::default(),
tls: ClientTlsConfig::default(),
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct ClientTlsConfig {
pub ca_cert: Option<String>,
pub insecure: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct ClientRunConfig {
pub poll_interval: f64,
pub max_parallel_jobs: Option<i64>,
pub output_dir: PathBuf,
pub num_cpus: Option<i64>,
pub memory_gb: Option<f64>,
pub num_gpus: Option<i64>,
}
impl Default for ClientRunConfig {
fn default() -> Self {
Self {
poll_interval: 5.0,
max_parallel_jobs: None,
output_dir: PathBuf::from("torc_output"),
num_cpus: None,
memory_gb: None,
num_gpus: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct ClientSlurmConfig {
pub poll_interval: i32,
pub keep_submission_scripts: bool,
pub strict_scheduler_match: bool,
}
impl Default for ClientSlurmConfig {
fn default() -> Self {
Self {
poll_interval: 30,
keep_submission_scripts: false,
strict_scheduler_match: false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct ClientWatchConfig {
pub poll_interval: u64,
pub max_retries: u32,
pub retry_cooldown: u64,
pub model: String,
pub cache_path: Option<PathBuf>,
pub rate_limit_per_minute: u32,
pub audit_log_path: Option<PathBuf>,
pub api_key: Option<String>,
}
impl Default for ClientWatchConfig {
fn default() -> Self {
Self {
poll_interval: 30,
max_retries: 3,
retry_cooldown: 60,
model: "claude-sonnet-4-20250514".to_string(),
cache_path: None,
rate_limit_per_minute: 10,
audit_log_path: None,
api_key: None,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct ClientHpcConfig {
pub default_account: Option<String>,
pub profile_overrides: HashMap<String, HpcProfileOverride>,
pub custom_profiles: HashMap<String, HpcProfileConfig>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(default)]
pub struct HpcProfileOverride {
pub default_account: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HpcProfileConfig {
pub display_name: String,
#[serde(default)]
pub description: String,
#[serde(default)]
pub detect_env_var: Option<String>,
#[serde(default)]
pub detect_hostname: Option<String>,
#[serde(default)]
pub default_account: Option<String>,
#[serde(default = "default_charge_factor")]
pub charge_factor_cpu: f64,
#[serde(default = "default_charge_factor_gpu")]
pub charge_factor_gpu: f64,
#[serde(default)]
pub partitions: Vec<HpcPartitionConfig>,
}
fn default_charge_factor() -> f64 {
1.0
}
fn default_charge_factor_gpu() -> f64 {
10.0
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HpcPartitionConfig {
pub name: String,
#[serde(default)]
pub description: String,
pub cpus_per_node: u32,
pub memory_mb: u64,
pub max_walltime_secs: u64,
#[serde(default)]
pub gpus_per_node: Option<u32>,
#[serde(default)]
pub gpu_type: Option<String>,
#[serde(default)]
pub gpu_memory_gb: Option<u32>,
#[serde(default)]
pub shared: bool,
#[serde(default)]
pub requires_explicit_request: bool,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_client_config_defaults() {
let config = ClientConfig::default();
assert_eq!(
config.api_url,
"http://localhost:8080/torc-service/v1".to_string()
);
assert_eq!(config.format, "table");
assert_eq!(config.log_level, "info");
}
#[test]
fn test_run_config_defaults() {
let config = ClientRunConfig::default();
assert_eq!(config.poll_interval, 5.0);
assert!(config.max_parallel_jobs.is_none());
assert_eq!(config.output_dir, PathBuf::from("torc_output"));
}
}