ras_config/domain/
config.rs1use std::env;
2use std::path::PathBuf;
3use std::time::Duration;
4
5use ras_errors::AppError;
6use thiserror::Error;
7
8#[derive(Debug, Clone)]
9pub struct Config {
10 pub home_dir: PathBuf,
11 pub config_dir: PathBuf,
12 pub extensions_dir: PathBuf,
13 pub profiles_dir: PathBuf,
14 pub anthropic_api_key: Option<String>,
15 pub claude_code_oauth_enabled: bool,
16 pub cdp_timeout: Duration,
17 pub action_timeout: Duration,
18 pub anonymized_telemetry: bool,
19 pub cloud_sync: bool,
20}
21
22#[derive(Debug, Error)]
23pub enum ConfigError {
24 #[error("missing required env var: {0}")]
25 MissingEnv(String),
26 #[error("invalid value for {0}: {1}")]
27 InvalidValue(String, String),
28}
29
30impl From<ConfigError> for AppError {
31 fn from(e: ConfigError) -> Self {
32 Self::ValidationError(e.to_string())
33 }
34}
35
36impl Config {
37 pub fn from_env() -> Result<Self, ConfigError> {
38 let home_dir = env::var("RAS_HOME")
39 .ok()
40 .map(PathBuf::from)
41 .or_else(|| dirs_home())
42 .ok_or_else(|| ConfigError::MissingEnv("RAS_HOME".into()))?;
43 let config_dir = env::var("RAS_CONFIG_DIR")
44 .map(PathBuf::from)
45 .unwrap_or_else(|_| home_dir.join(".config").join("rust-ai-surfer"));
46 let extensions_dir = env::var("RAS_EXTENSIONS_DIR")
47 .map(PathBuf::from)
48 .unwrap_or_else(|_| config_dir.join("extensions"));
49 let profiles_dir = env::var("RAS_PROFILES_DIR")
50 .map(PathBuf::from)
51 .unwrap_or_else(|_| config_dir.join("profiles"));
52 Ok(Self {
53 home_dir,
54 config_dir,
55 extensions_dir,
56 profiles_dir,
57 anthropic_api_key: env::var("ANTHROPIC_API_KEY").ok(),
58 claude_code_oauth_enabled: env::var("RAS_CLAUDE_CODE_OAUTH").ok().as_deref()
59 != Some("0"),
60 cdp_timeout: parse_duration("RAS_CDP_TIMEOUT_S", 60)?,
61 action_timeout: parse_duration("RAS_ACTION_TIMEOUT_S", 180)?,
62 anonymized_telemetry: env::var("RAS_ANONYMIZED_TELEMETRY").ok().as_deref() != Some("0"),
63 cloud_sync: env::var("RAS_CLOUD_SYNC").ok().as_deref() == Some("1"),
64 })
65 }
66}
67
68fn parse_duration(key: &str, default_secs: u64) -> Result<Duration, ConfigError> {
69 match env::var(key) {
70 Ok(v) => v
71 .parse::<u64>()
72 .map(Duration::from_secs)
73 .map_err(|_| ConfigError::InvalidValue(key.into(), v)),
74 Err(_) => Ok(Duration::from_secs(default_secs)),
75 }
76}
77
78fn dirs_home() -> Option<PathBuf> {
79 env::var_os("HOME").map(PathBuf::from)
80}