Skip to main content

algocline_app/service/
mod.rs

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