Skip to main content

buntrail_engine/remote/
operations.rs

1use super::*;
2pub(super) fn uncertain(id: OperationID, target: TargetIdentity, e: CoreError) -> OperationReceipt {
3    // Without explicit unsent/refusal evidence, transport failure cannot exclude execution.
4    OperationReceipt {
5        id,
6        state: OperationState::Unknown(ReconciliationEvidence {
7            target,
8            observed: ObservedState::Unobserved,
9            explanation: format!(
10                "{}; inspect the original operation; automatic replay is disabled",
11                e.message
12            ),
13        }),
14    }
15}
16impl OperationService for RemoteServices {
17    fn inspect(
18        &self,
19        id: OperationID,
20        target: TargetIdentity,
21    ) -> ServiceFuture<'_, Result<OperationReceipt, CoreError>> {
22        Box::pin(async move {
23            self.check(&target)?;
24            match self.call(w::Call::Inspect, Some(id))? {
25                w::Reply::Receipt { receipt } => {
26                    let r = receipt.core(&target)?;
27                    if r.id != id {
28                        return Err(w::invalid());
29                    }
30                    Ok(r)
31                }
32                _ => Err(w::invalid()),
33            }
34        })
35    }
36}
37
38impl RemoteServices {
39    pub(super) fn mutation_call(
40        &self,
41        call: w::Call,
42        id: OperationID,
43        target: &TargetIdentity,
44    ) -> OperationReceipt {
45        let lookup_sensitive = call.unprepared_worktree_mutation();
46        let mut replies = match self.stream(call, Some(id)) {
47            Ok(stream) => stream,
48            Err(error) if lookup_sensitive => return uncertain(id, target.clone(), error),
49            Err(error) => return buntrail_services::files::failure(id, target.clone(), error),
50        };
51        let result = (|| {
52            loop {
53                let frame = self.next_reply(&mut replies.request)?;
54                // Admission gates use the existing receipt with explicit no-effect evidence.
55                if matches!(frame.header.message, Message::Receipt { .. }) {
56                    let receipt = super::files::legacy_receipt(id, target, frame.header.message)?;
57                    replies.request.finish();
58                    return Ok(receipt);
59                }
60                if let Some(value) = replies.accept(frame)? {
61                    let w::Reply::Receipt { receipt } = value? else {
62                        return Err(w::invalid());
63                    };
64                    let receipt = receipt.core(target)?;
65                    if receipt.id != id {
66                        return Err(w::invalid());
67                    }
68                    return Ok(receipt);
69                }
70            }
71        })();
72        result.unwrap_or_else(|error| {
73            if lookup_sensitive {
74                uncertain(id, target.clone(), error)
75            } else {
76                request_failure(&replies.request, id, target, error)
77            }
78        })
79    }
80}
81
82pub(super) fn request_failure(
83    request: &transport::Request,
84    id: OperationID,
85    target: &TargetIdentity,
86    error: CoreError,
87) -> OperationReceipt {
88    if request.refused_before_send() {
89        buntrail_services::files::failure(id, target.clone(), error)
90    } else {
91        uncertain(id, target.clone(), error)
92    }
93}