mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-08-31 08:39:24 +00:00
* refactor: split large files and consolidate test stubs for contributor velocity - Extract 7 Database sub-traits (ConversationStore, JobStore, SandboxStore, RoutineStore, ToolFailureStore, SettingsStore, WorkspaceStore) with Database as a supertrait combining them all - Split libsql_backend.rs (2769 lines) into src/db/libsql/ directory with one file per sub-trait implementation - Split config.rs (1753 lines) into src/config/ directory with 16 domain files - Consolidate 3 duplicate test LLM stubs into shared StubLlm in src/testing.rs - Split server.rs handlers into src/channels/web/handlers/ directory - Extract main.rs init phases into AppBuilder (src/app.rs) - Add developer setup script (scripts/dev-setup.sh) Co-Authored-By: Claude Opus 4.6 <[email protected]> * refactor: move heartbeat test from examples/ to tests/ Convert standalone example binary into a proper #[ignore] integration test, matching the convention of the other integration tests. Co-Authored-By: Claude Opus 4.6 <[email protected]> * style: fix rustfmt formatting for CI Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: address PR review comments from Copilot - tunnel.rs: replace .ok().flatten() with ? to propagate env var errors - secrets.rs: remove misleading "process-wide cache" comment - database.rs: use uppercase "DATABASE_URL" in error key - testing.rs: gate harness tests with #[cfg(feature = "libsql")] Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Illia Polosukhin <[email protected]> Co-authored-by: Claude Opus 4.6 <[email protected]>
41 lines
1.1 KiB
Rust
41 lines
1.1 KiB
Rust
use crate::error::ConfigError;
|
|
|
|
use super::INJECTED_VARS;
|
|
|
|
pub(crate) fn optional_env(key: &str) -> Result<Option<String>, ConfigError> {
|
|
// Check real env vars first (always win over injected secrets)
|
|
match std::env::var(key) {
|
|
Ok(val) if val.is_empty() => {}
|
|
Ok(val) => return Ok(Some(val)),
|
|
Err(std::env::VarError::NotPresent) => {}
|
|
Err(e) => {
|
|
return Err(ConfigError::ParseError(format!(
|
|
"failed to read {key}: {e}"
|
|
)));
|
|
}
|
|
}
|
|
|
|
// Fall back to thread-safe overlay (secrets injected from DB)
|
|
if let Some(val) = INJECTED_VARS.get().and_then(|map| map.get(key)) {
|
|
return Ok(Some(val.clone()));
|
|
}
|
|
|
|
Ok(None)
|
|
}
|
|
|
|
pub(crate) fn parse_optional_env<T>(key: &str, default: T) -> Result<T, ConfigError>
|
|
where
|
|
T: std::str::FromStr,
|
|
T::Err: std::fmt::Display,
|
|
{
|
|
optional_env(key)?
|
|
.map(|s| {
|
|
s.parse().map_err(|e| ConfigError::InvalidValue {
|
|
key: key.to_string(),
|
|
message: format!("{e}"),
|
|
})
|
|
})
|
|
.transpose()
|
|
.map(|opt| opt.unwrap_or(default))
|
|
}
|