bitrouter_runtime/
paths.rs1use std::path::{Path, PathBuf};
2
3#[derive(Debug, Clone)]
10pub struct RuntimePaths {
11 pub home_dir: PathBuf,
12 pub config_file: PathBuf,
13 pub env_file: PathBuf,
14 pub runtime_dir: PathBuf,
15 pub log_dir: PathBuf,
16}
17
18impl RuntimePaths {
19 pub fn from_home(home: impl Into<PathBuf>) -> Self {
21 let home = home.into();
22 Self {
23 config_file: home.join("bitrouter.yaml"),
24 env_file: home.join(".env"),
25 runtime_dir: home.join("run"),
26 log_dir: home.join("logs"),
27 home_dir: home,
28 }
29 }
30}
31
32#[derive(Debug, Clone, Default)]
34pub struct PathOverrides {
35 pub config_file: Option<PathBuf>,
36 pub env_file: Option<PathBuf>,
37 pub runtime_dir: Option<PathBuf>,
38 pub log_dir: Option<PathBuf>,
39}
40
41impl PathOverrides {
42 pub fn apply(self, mut paths: RuntimePaths) -> RuntimePaths {
43 if let Some(v) = self.config_file {
44 paths.config_file = v;
45 }
46 if let Some(v) = self.env_file {
47 paths.env_file = v;
48 }
49 if let Some(v) = self.runtime_dir {
50 paths.runtime_dir = v;
51 }
52 if let Some(v) = self.log_dir {
53 paths.log_dir = v;
54 }
55 paths
56 }
57}
58
59pub fn resolve_home(explicit: Option<&Path>) -> RuntimePaths {
67 if let Some(dir) = explicit {
68 let abs = std::path::absolute(dir).unwrap_or_else(|_| dir.to_path_buf());
69 return RuntimePaths::from_home(abs);
70 }
71
72 let cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
74 if cwd.join("bitrouter.yaml").exists() {
75 return RuntimePaths::from_home(cwd);
76 }
77
78 if let Ok(home_env) = std::env::var("BITROUTER_HOME") {
80 let p = PathBuf::from(home_env);
81 if p.is_dir() {
82 return RuntimePaths::from_home(p);
83 }
84 }
85
86 let home = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
88 let default_home = home.join(".bitrouter");
89 if !default_home.exists() {
90 let _ = scaffold_home(&default_home);
91 }
92 RuntimePaths::from_home(default_home)
93}
94
95fn scaffold_home(home: &Path) -> std::io::Result<()> {
97 std::fs::create_dir_all(home)?;
98 std::fs::create_dir_all(home.join("run"))?;
99 std::fs::create_dir_all(home.join("logs"))?;
100
101 let config_path = home.join("bitrouter.yaml");
102 if !config_path.exists() {
103 std::fs::write(
104 &config_path,
105 "\
106# BitRouter configuration
107# See https://github.com/bitrouter/bitrouter for documentation.
108#
109# server:
110# listen: \"127.0.0.1:8787\"
111#
112# providers:
113# openai:
114# api_key: \"${OPENAI_API_KEY}\"
115#
116# models:
117# my-model:
118# strategy: priority
119# endpoints:
120# - provider: openai
121# model_id: gpt-4o
122",
123 )?;
124 }
125
126 let env_path = home.join(".env");
127 if !env_path.exists() {
128 std::fs::write(
129 &env_path,
130 "\
131# BitRouter environment variables
132# Place your API keys here. This file is ignored by git.
133#
134# OPENAI_API_KEY=sk-...
135# ANTHROPIC_API_KEY=sk-ant-...
136# GOOGLE_API_KEY=...
137",
138 )?;
139 }
140
141 let gitignore_path = home.join(".gitignore");
142 if !gitignore_path.exists() {
143 std::fs::write(
144 &gitignore_path,
145 "\
146logs/
147run/
148.env
149",
150 )?;
151 }
152
153 Ok(())
154}