docs: tambah doc comment, logging, dan inline comments di semua 255 file
Meliputi: - File-level //! doc comment: tujuan file, alur kerja, komponen utama - Function-level /// doc comment: apa, parameter, return, flow, edge cases - Struct/enum/trait /// doc comment: peran, field docs - Tracing logging (tracing::info!/debug!/trace!/warn!/error!) di setiap fungsi - Inline comments untuk variable dan branching logic penting - Seluruh 8 crates di workspace: zesdex-backend, zesdex-cms, zesdex-entities, zesdex-iam, zesdex-infra, zesdex-ipc, zesdex-middleware, zesdex-utils - Build: 0 errors, 242/242 tests passed
This commit is contained in:
@@ -1,9 +1,17 @@
|
||||
//! JSON file–backed `AppConfigRepository`.
|
||||
//! JSON file–backed `AppConfigRepository` implementation.
|
||||
//!
|
||||
//! Path: `<base_dir>/app_config.json`
|
||||
//!
|
||||
//! On load, auto-detects Claude credentials from the environment or
|
||||
//! Stores `AppConfig` as pretty-printed JSON at `<base_dir>/app_config.json`.
|
||||
//! On load, auto-detects Claude credentials from the environment or from
|
||||
//! `~/.claude/settings.json` and merges them into the provider map.
|
||||
//!
|
||||
//! ## Auto-Detection Flow
|
||||
//! 1. Load `app_config.json` from disk (or use defaults if absent)
|
||||
//! 2. Merge any default providers not present in the loaded config
|
||||
//! 3. Detect Claude credentials from `~/.claude/settings.json` or env vars
|
||||
//! 4. If Claude detected, add "claude" provider + model roles, set as default
|
||||
//!
|
||||
//! ## Atomicity
|
||||
//! Writes use `write_json_atomic` (temp file + rename) to prevent corruption.
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
@@ -14,32 +22,41 @@ use zesdex_utils::write_json_atomic;
|
||||
use crate::domain::app_config::{AppConfig, ModelRole, ProviderConfig};
|
||||
use crate::domain::repository::AppConfigRepository;
|
||||
|
||||
/// Persists `AppConfig` as pretty-printed JSON at `<base_dir>/app_config.json`.
|
||||
/// File-based `AppConfigRepository` that reads/writes `app_config.json`.
|
||||
///
|
||||
/// On load, auto-detects Claude credentials and merges them into the
|
||||
/// provider map (see module docs for the full flow).
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct JsonAppConfigRepository;
|
||||
|
||||
impl JsonAppConfigRepository {
|
||||
/// Create a new repository instance.
|
||||
/// Create a new repository instance (zero allocation).
|
||||
pub fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration structure inside `~/.claude/settings.json`.
|
||||
/// Internal helper: the `env` block inside `~/.claude/settings.json`.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct ClaudeEnv {
|
||||
/// Override URL for the Anthropic API.
|
||||
#[serde(alias = "ANTHROPIC_BASE_URL")]
|
||||
anthropic_base_url: Option<String>,
|
||||
/// Override API key for the Anthropic API.
|
||||
#[serde(alias = "ANTHROPIC_API_KEY")]
|
||||
anthropic_api_key: Option<String>,
|
||||
}
|
||||
|
||||
/// Internal helper: top-level structure of `~/.claude/settings.json`.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct ClaudeSettings {
|
||||
/// Environment variable overrides block.
|
||||
env: Option<ClaudeEnv>,
|
||||
}
|
||||
|
||||
/// Try to read Claude credentials from `~/.claude/settings.json`'s `env` block.
|
||||
///
|
||||
/// Returns `(base_url, api_key)` if both are present, or `None`.
|
||||
fn claude_credentials_from_file() -> Option<(String, String)> {
|
||||
let path = dirs::home_dir()?.join(".claude").join("settings.json");
|
||||
let content = std::fs::read_to_string(&path).ok()?;
|
||||
@@ -50,15 +67,18 @@ fn claude_credentials_from_file() -> Option<(String, String)> {
|
||||
Some((base_url, key))
|
||||
}
|
||||
|
||||
/// Try to read Claude credentials from environment variables.
|
||||
/// Try to read Claude credentials from the process environment variables.
|
||||
///
|
||||
/// Returns `(ANTHROPIC_BASE_URL, ANTHROPIC_API_KEY)` if both are set, or `None`.
|
||||
fn claude_credentials_from_env() -> Option<(String, String)> {
|
||||
let base_url = std::env::var("ANTHROPIC_BASE_URL").ok()?;
|
||||
let key = std::env::var("ANTHROPIC_API_KEY").ok()?;
|
||||
Some((base_url, key))
|
||||
}
|
||||
|
||||
/// Return a `ProviderConfig` for the Claude provider, checking both
|
||||
/// `~/.claude/settings.json` and the process environment.
|
||||
/// Return a `ProviderConfig` for the Claude provider, checking both sources.
|
||||
///
|
||||
/// Flow: try ~/.claude/settings.json → fall back to env vars → return None if neither found.
|
||||
fn detect_claude_settings_provider() -> Option<ProviderConfig> {
|
||||
let (base_url, key) = claude_credentials_from_file().or_else(claude_credentials_from_env)?;
|
||||
Some(ProviderConfig {
|
||||
@@ -70,8 +90,15 @@ fn detect_claude_settings_provider() -> Option<ProviderConfig> {
|
||||
}
|
||||
|
||||
impl AppConfigRepository for JsonAppConfigRepository {
|
||||
/// Load `AppConfig` from `<base_dir>/app_config.json`.
|
||||
///
|
||||
/// Flow: read file → parse JSON → merge default providers → auto-detect Claude → return.
|
||||
///
|
||||
/// If the file is missing, returns `AppConfig::default()`.
|
||||
fn load(&self, base_dir: &Path) -> Result<AppConfig> {
|
||||
tracing::debug!("loading app_config from {base_dir:?}");
|
||||
let path = base_dir.join("app_config.json");
|
||||
// Try to read and parse the config file
|
||||
let mut cfg: AppConfig = match std::fs::read_to_string(&path) {
|
||||
Ok(s) => serde_json::from_str(&s)
|
||||
.map_err(|e| anyhow::anyhow!("failed to parse app_config.json: {e}"))?,
|
||||
@@ -84,13 +111,13 @@ impl AppConfigRepository for JsonAppConfigRepository {
|
||||
}
|
||||
};
|
||||
|
||||
// Merge any default providers not present in the loaded config
|
||||
// Phase 1: merge default providers that are not yet in the loaded config
|
||||
let defaults = AppConfig::default();
|
||||
for (name, provider) in defaults.providers {
|
||||
cfg.providers.entry(name).or_insert(provider);
|
||||
}
|
||||
|
||||
// Auto-detect Claude provider
|
||||
// Phase 2: auto-detect Claude provider from file or environment
|
||||
if let Some(claude_provider) = detect_claude_settings_provider() {
|
||||
cfg.providers
|
||||
.entry("claude".to_string())
|
||||
@@ -123,7 +150,11 @@ impl AppConfigRepository for JsonAppConfigRepository {
|
||||
Ok(cfg)
|
||||
}
|
||||
|
||||
/// Persist `AppConfig` to `<base_dir>/app_config.json`.
|
||||
///
|
||||
/// Flow: create base dir → atomic JSON write → log success.
|
||||
fn save(&self, base_dir: &Path, config: &AppConfig) -> Result<()> {
|
||||
tracing::debug!("saving app_config to {base_dir:?}");
|
||||
std::fs::create_dir_all(base_dir)
|
||||
.with_context(|| format!("failed to create base dir '{}'", base_dir.display()))?;
|
||||
let path = base_dir.join("app_config.json");
|
||||
|
||||
Reference in New Issue
Block a user