Skip to main content

algocline_app/service/
mod.rs

1mod config;
2mod eval;
3mod eval_store;
4mod logging;
5pub(crate) mod path;
6mod pkg;
7pub mod resolve;
8mod run;
9mod scenario;
10mod status;
11mod transcript;
12
13#[cfg(test)]
14mod tests;
15
16use std::path::Path;
17use std::sync::Arc;
18
19use algocline_engine::{Executor, SessionRegistry};
20
21pub use config::{AppConfig, LogDirSource};
22pub use resolve::{QueryResponse, SearchPath};
23
24// ─── Application Service ────────────────────────────────────────
25
26/// Tracks which sessions are eval sessions and their strategy name.
27///
28/// `std::sync::Mutex` is used (not tokio) because all operations are
29/// single HashMap insert/remove/get completing in microseconds, and no
30/// `.await` is held across the lock. Called from async context but never
31/// held across yield points. Poison is silently skipped — eval tracking
32/// is non-critical metadata.
33type EvalSessions = std::sync::Mutex<std::collections::HashMap<String, String>>;
34
35/// Tracks session_id → strategy name for all strategy-based sessions (advice, eval).
36///
37/// Same locking rationale as `EvalSessions`. Used by `alc_status` and
38/// transcript logging. Poison is silently skipped — strategy name is
39/// non-critical metadata for observability.
40type SessionStrategies = std::sync::Mutex<std::collections::HashMap<String, String>>;
41
42#[derive(Clone)]
43pub struct AppService {
44    executor: Arc<Executor>,
45    registry: Arc<SessionRegistry>,
46    log_config: AppConfig,
47    /// Package search paths in priority order (first = highest).
48    search_paths: Vec<resolve::SearchPath>,
49    /// session_id → strategy name for eval sessions (cleared on completion).
50    eval_sessions: Arc<EvalSessions>,
51    /// session_id → strategy name for log/stats tracking (cleared on session completion).
52    session_strategies: Arc<SessionStrategies>,
53}
54
55impl AppService {
56    pub fn new(
57        executor: Arc<Executor>,
58        log_config: AppConfig,
59        search_paths: Vec<resolve::SearchPath>,
60    ) -> Self {
61        Self {
62            executor,
63            registry: Arc::new(SessionRegistry::new()),
64            log_config,
65            search_paths,
66            eval_sessions: Arc::new(std::sync::Mutex::new(std::collections::HashMap::new())),
67            session_strategies: Arc::new(std::sync::Mutex::new(std::collections::HashMap::new())),
68        }
69    }
70
71    /// Returns the log directory, or an error if file logging is unavailable.
72    fn require_log_dir(&self) -> Result<&Path, String> {
73        self.log_config
74            .log_dir
75            .as_deref()
76            .ok_or_else(|| "File logging is not available (no writable log directory)".to_string())
77    }
78}