Skip to main content

everruns_host/
session_file_system_factory.rs

1//! Deployment-selected session filesystem factories.
2
3use async_trait::async_trait;
4use everruns_core::{WorkspaceRootSet, session_files::SessionFileSystem};
5use everruns_provider::error::{AgentLoopError, Result};
6use std::any::{Any, TypeId};
7use std::collections::HashMap;
8use std::sync::Arc;
9
10/// Host-supplied values used by platform file-system factories.
11///
12/// The context is intentionally type-erased so this host contract can accept
13/// server-only dependencies such as `StorageBackend` or future object-storage
14/// clients without pulling them into `everruns-core`.
15#[derive(Clone, Default)]
16pub struct SessionFileSystemFactoryContext {
17    values: Arc<HashMap<TypeId, Arc<dyn Any + Send + Sync>>>,
18}
19
20impl SessionFileSystemFactoryContext {
21    pub fn new() -> Self {
22        Self::default()
23    }
24
25    pub fn with<T: Any + Send + Sync>(mut self, value: Arc<T>) -> Self {
26        let values = Arc::make_mut(&mut self.values);
27        values.insert(TypeId::of::<T>(), value);
28        self
29    }
30
31    pub fn get<T: Any + Send + Sync>(&self) -> Option<Arc<T>> {
32        self.values
33            .get(&TypeId::of::<T>())
34            .and_then(|value| value.clone().downcast::<T>().ok())
35    }
36
37    pub fn with_workspace_roots(self, roots: Arc<WorkspaceRootSet>) -> Self {
38        self.with(roots)
39    }
40
41    pub fn workspace_roots(&self) -> Option<Arc<WorkspaceRootSet>> {
42        self.get::<WorkspaceRootSet>()
43    }
44}
45
46/// Factory for deployment-selected session filesystem implementations.
47#[async_trait]
48pub trait SessionFileSystemFactory: Send + Sync {
49    /// Human-readable factory name for diagnostics.
50    fn name(&self) -> &'static str {
51        "SessionFileSystemFactory"
52    }
53
54    /// Whether this factory intentionally leaves filesystem selection to the
55    /// runtime default.
56    fn is_disabled(&self) -> bool {
57        false
58    }
59
60    /// Resolve a live filesystem from host-provided dependencies.
61    async fn create_session_file_system(
62        &self,
63        context: SessionFileSystemFactoryContext,
64    ) -> Result<Arc<dyn SessionFileSystem>>;
65}
66
67/// Default factory used when a platform does not configure session files.
68#[derive(Debug, Clone, Default)]
69pub struct DisabledSessionFileSystemFactory;
70
71#[async_trait]
72impl SessionFileSystemFactory for DisabledSessionFileSystemFactory {
73    fn name(&self) -> &'static str {
74        "DisabledSessionFileSystemFactory"
75    }
76
77    fn is_disabled(&self) -> bool {
78        true
79    }
80
81    async fn create_session_file_system(
82        &self,
83        _context: SessionFileSystemFactoryContext,
84    ) -> Result<Arc<dyn SessionFileSystem>> {
85        Err(AgentLoopError::config("session filesystem is disabled"))
86    }
87}
88
89/// Factory that returns one already-selected session filesystem.
90///
91/// Workspace providers use this adapter so the runtime consumes the same
92/// [`SessionFileSystem`] selected by the head instead of inventing a parallel
93/// filesystem abstraction.
94#[derive(Clone)]
95pub struct FixedSessionFileSystemFactory {
96    file_system: Arc<dyn SessionFileSystem>,
97}
98
99impl FixedSessionFileSystemFactory {
100    pub fn new(file_system: Arc<dyn SessionFileSystem>) -> Self {
101        Self { file_system }
102    }
103}
104
105#[async_trait]
106impl SessionFileSystemFactory for FixedSessionFileSystemFactory {
107    fn name(&self) -> &'static str {
108        "FixedSessionFileSystemFactory"
109    }
110
111    async fn create_session_file_system(
112        &self,
113        _context: SessionFileSystemFactoryContext,
114    ) -> Result<Arc<dyn SessionFileSystem>> {
115        Ok(self.file_system.clone())
116    }
117}