forge_workspace/mcp/documents/
facade.rs1use std::sync::Arc;
9
10use async_trait::async_trait;
11use forge_primitives::SessionSlot;
12use forge_primitives::document::DocumentHandOffEnding;
13
14use crate::workspace::Workspace;
15
16#[async_trait]
18pub trait DocumentFacade: Send + Sync {
19 async fn hand_off(
27 &self,
28 seat: &SessionSlot,
29 path: &str,
30 reviewable: bool,
31 ) -> Result<(String, DocumentHandOffEnding), String>;
32}
33
34pub struct ProdDocumentFacade {
36 workspace: std::sync::Weak<Workspace>,
37}
38
39impl ProdDocumentFacade {
40 pub fn from_workspace(workspace: &Arc<Workspace>) -> Arc<dyn DocumentFacade> {
41 Arc::new(Self { workspace: Arc::downgrade(workspace) })
42 }
43}
44
45#[async_trait]
46impl DocumentFacade for ProdDocumentFacade {
47 async fn hand_off(
48 &self,
49 seat: &SessionSlot,
50 path: &str,
51 reviewable: bool,
52 ) -> Result<(String, DocumentHandOffEnding), String> {
53 let Some(workspace) = self.workspace.upgrade() else {
54 return Err("the workspace went away before the document was handed".to_owned());
55 };
56 workspace.hand_off_document(seat, path, reviewable).await
57 }
58}
59
60#[cfg(test)]
63pub struct MockDocumentFacade {
64 pub calls: parking_lot::Mutex<Vec<(String, bool)>>,
66 pub seats: parking_lot::Mutex<Vec<SessionSlot>>,
69 pub answer: parking_lot::Mutex<Result<(String, DocumentHandOffEnding), String>>,
71}
72
73#[cfg(test)]
74impl MockDocumentFacade {
75 pub fn new() -> Self {
76 Self {
77 calls: parking_lot::Mutex::new(Vec::new()),
78 seats: parking_lot::Mutex::new(Vec::new()),
79 answer: parking_lot::Mutex::new(Ok((
80 "/proj/docs/readme.md".to_owned(),
81 DocumentHandOffEnding::Approve { review_id: None, note: None },
82 ))),
83 }
84 }
85
86 pub fn into_arc(self) -> Arc<dyn DocumentFacade> {
87 Arc::new(self)
88 }
89}
90
91#[cfg(test)]
92#[async_trait]
93impl DocumentFacade for MockDocumentFacade {
94 async fn hand_off(
95 &self,
96 seat: &SessionSlot,
97 path: &str,
98 reviewable: bool,
99 ) -> Result<(String, DocumentHandOffEnding), String> {
100 self.seats.lock().push(seat.clone());
101 self.calls.lock().push((path.to_owned(), reviewable));
102 self.answer.lock().clone()
103 }
104}