1use super::driver::DriverCore;
4use super::policy::{all_records, private_operands};
5use super::runtime::*;
6use super::*;
7use lace_sans_io::logic::{rx_disabled, tx_disabled};
8use lace_sans_io::runtime_values::{OpenEvent, PeerDiagnostics, finish_bounded, store_outcome};
9
10pub use bounded::BoundedInterlace;
11
12mod bounded;
13#[cfg(test)]
14mod store_stream_tests;
15#[cfg(test)]
16mod tests;
17
18#[must_use = "drive or drop the open interlace"]
19pub struct OpenInterlace {
20 pub(super) driver: DriverCore,
21}
22
23pub struct ForEachStream {
24 pub(super) interlace: OpenInterlace,
25 observer: Lace,
26}
27
28pub struct StoreStream {
31 interlace: OpenInterlace,
32 provider: Lace,
33 failure: Option<Error>,
36}
37
38fn route(
42 first: Participant,
43 second: Participant,
44 mut operands: [InterlaceModule; 2],
45 limits: InterlaceResourceLimits,
46) -> Result<OpenInterlace> {
47 let local_peer = match (&first, &second) {
48 (Participant::LocalPeer(_), Participant::LocalPeer(_)) => {
49 return Err(Error::policy("an interlace accepts at most one LocalPeer"));
50 }
51 (Participant::LocalPeer(_), Participant::Connected(_))
52 | (Participant::Connected(_), Participant::LocalPeer(_)) => {
53 return Err(Error::policy(
54 "a LocalPeer cannot interlace with a ConnectedPeer",
55 ));
56 }
57 (Participant::LocalPeer(peer), _) => Some((0, peer.operand().clone())),
58 (_, Participant::LocalPeer(peer)) => Some((1, peer.operand().clone())),
59 _ => None,
60 };
61 if let Some((slot, operand)) = &local_peer {
62 operands[*slot] = operand.clone();
63 }
64 let config = if first.is_local() && second.is_local() {
65 OperationConfig::Direct(Box::new(
66 DirectConfig::new(operands, empty_direct_authorities()).with_limits(limits),
67 ))
68 } else {
69 let [first_operand, second_operand] = operands;
70 let local_operand = if first.is_local() {
71 first_operand
72 } else {
73 second_operand
74 };
75 validate_operand_blob_size(&local_operand)?;
76 OperationConfig::Connected {
77 local_operand,
78 limits,
79 }
80 };
81 Ok(OpenInterlace {
82 driver: DriverCore::new(
83 claim_participant(first)?,
84 claim_participant(second)?,
85 config,
86 local_peer,
87 )?,
88 })
89}
90
91fn exhausted() -> Error {
92 Error::integration("interlace exhausted before fixed point")
93}
94
95async fn capture(
98 capture: Lace,
99 target: Participant,
100 policy: impl IntoPolicy,
101 limits: InterlaceResourceLimits,
102 saved: impl FnMut(Record),
103) -> Result<BoundedInterlaceOutcome> {
104 let operands = private_operands(policy.into_policy()?, tx_disabled())?;
105 let first = Participant::from(capture.handle());
106 let mut operation = BoundedInterlace::owning(route(first, target, operands, limits)?);
107 let collected = operation.collect(saved).await;
108 drop(operation);
109 Ok(finish_bounded(
110 collected,
111 capture.close().await.err(),
112 PeerDiagnostics::default(),
113 ))
114}
115
116pub async fn bounded_interlace(
117 first: impl Into<Participant>,
118 second: impl Into<Participant>,
119 policy: impl IntoPolicy,
120) -> Result<BoundedInterlaceOutcome> {
121 bounded_interlace_with_options(first, second, policy, InterlaceOperationOptions::default())
122 .await
123}
124pub async fn bounded_interlace_with_options(
125 first: impl Into<Participant>,
126 second: impl Into<Participant>,
127 policy: impl IntoPolicy,
128 operation: InterlaceOperationOptions,
129) -> Result<BoundedInterlaceOutcome> {
130 Ok(BoundedInterlace::new(first, second, policy, operation)?
131 .run()
132 .await)
133}
134
135pub async fn open_interlace(
136 first: impl Into<Participant>,
137 second: impl Into<Participant>,
138 policy: impl IntoPolicy,
139) -> Result<OpenInterlace> {
140 open_interlace_with_options(first, second, policy, InterlaceOperationOptions::default()).await
141}
142pub async fn open_interlace_with_options(
143 first: impl Into<Participant>,
144 second: impl Into<Participant>,
145 policy: impl IntoPolicy,
146 operation: InterlaceOperationOptions,
147) -> Result<OpenInterlace> {
148 let module = policy.into_policy()?;
149 let operands = [module.clone(), module];
150 route(first.into(), second.into(), operands, operation.limits)
151}
152pub async fn open_interlace_with_selector(
162 peer: ConnectedPeer,
163 selector: impl FnMut(Option<&PortText>) -> Option<(LaceHandle, SessionOperand)> + Send + 'static,
164 operation: InterlaceOperationOptions,
165) -> Result<OpenInterlace> {
166 let peer = claim_participant(peer.into())?;
167 Ok(OpenInterlace {
168 driver: DriverCore::selecting(peer, Box::new(selector), operation.limits)?,
169 })
170}
171impl OpenInterlace {
172 pub fn peer_port(&self) -> Option<PortText> {
175 self.driver.peer_port()
176 }
177 pub fn peer_by(&self) -> Option<ByValue> {
181 self.driver.peer_by()
182 }
183 pub fn outgoing_encoding(&self) -> Option<StreamEncoding> {
187 self.driver.outgoing_encoding()
188 }
189 pub fn incoming_encoding(&self) -> Option<StreamEncoding> {
192 self.driver.incoming_encoding()
193 }
194 pub fn replace_policy(&mut self, policy: impl IntoPolicy) -> Result<()> {
196 let module = policy.into_policy()?;
197 self.driver.replace_policy(module)
198 }
199 pub async fn next_event(&mut self) -> Result<Option<OpenEvent>> {
201 self.driver.next_event().await
202 }
203 pub async fn close(self) -> Result<OpenBoundaryOutcome> {
206 let reason = self.driver.close_cancelled().await?;
207 Ok(OpenBoundaryOutcome {
208 boundary: OpenBoundary::Closed(reason.clone()),
209 observations: vec![OpenEvent::Closed { reason }],
210 })
211 }
212 pub async fn run_until_fixed_point(&mut self) -> Result<OpenBoundaryOutcome> {
214 let mut observations = Vec::new();
215 loop {
216 match self.next_event().await? {
217 Some(event @ OpenEvent::FixedPoint) => {
218 observations.push(event);
219 return Ok(OpenBoundaryOutcome {
220 boundary: OpenBoundary::FixedPoint,
221 observations,
222 });
223 }
224 Some(OpenEvent::Closed { reason }) => {
225 observations.push(OpenEvent::Closed {
226 reason: reason.clone(),
227 });
228 return Ok(OpenBoundaryOutcome {
229 boundary: OpenBoundary::Closed(reason),
230 observations,
231 });
232 }
233 Some(event) => observations.push(event),
234 None => return Err(exhausted()),
235 }
236 }
237 }
238}
239
240pub async fn list(target: impl Into<Participant>, policy: impl IntoPolicy) -> Result<ListOutcome> {
241 list_with_options(target, policy, InterlaceOperationOptions::default()).await
242}
243pub async fn list_with_options(
244 target: impl Into<Participant>,
245 policy: impl IntoPolicy,
246 operation: InterlaceOperationOptions,
247) -> Result<ListOutcome> {
248 let mut records = Vec::new();
249 let outcome = capture(
250 Lace::empty_memory(),
251 target.into(),
252 policy,
253 operation.limits,
254 |record| records.push(record),
255 )
256 .await?;
257 Ok(ListOutcome {
258 terminal: outcome.terminal,
259 observations: outcome.observations,
260 records,
261 })
262}
263
264pub async fn get(target: impl Into<Participant>, policy: impl IntoPolicy) -> Result<GetOutcome> {
265 get_with_options(target, policy, InterlaceOperationOptions::default()).await
266}
267pub async fn get_with_options(
268 target: impl Into<Participant>,
269 policy: impl IntoPolicy,
270 operation: InterlaceOperationOptions,
271) -> Result<GetOutcome> {
272 let mut first = None;
273 let outcome = capture(
274 Lace::empty_memory(),
275 target.into(),
276 policy,
277 operation.limits,
278 |record| {
279 first.get_or_insert(record);
280 },
281 )
282 .await?;
283 Ok(GetOutcome {
284 terminal: outcome.terminal,
285 observations: outcome.observations,
286 record: first,
287 })
288}
289
290pub async fn store(
291 target: impl Into<Participant>,
292 records: impl IntoRecordBatch,
293) -> Result<StoreOutcome> {
294 store_with_options(target, records, InterlaceOperationOptions::default()).await
295}
296pub async fn store_with_options(
297 target: impl Into<Participant>,
298 records: impl IntoRecordBatch,
299 operation: InterlaceOperationOptions,
300) -> Result<StoreOutcome> {
301 store_inner(target.into(), records.into_records(), operation.limits).await
302}
303async fn store_inner(
304 target: Participant,
305 records: Vec<Record>,
306 limits: InterlaceResourceLimits,
307) -> Result<StoreOutcome> {
308 if records.is_empty() {
309 return Ok(StoreOutcome {
310 terminal: Terminal::Complete,
311 observations: Vec::new(),
312 records: Vec::new(),
313 });
314 }
315 let supplied = records
316 .iter()
317 .map(|record| record.hash())
318 .collect::<Vec<_>>();
319 let operands = private_operands(all_records().clone(), rx_disabled())?;
320 let source = Lace::memory(records);
321 let first = Participant::from(source.handle());
322 let mut operation = BoundedInterlace::owning(route(first, target, operands, limits)?);
323 let collected = operation.collect(drop).await;
324 drop(operation);
325 let outcome = finish_bounded(
326 collected,
327 source.close().await.err(),
328 PeerDiagnostics::default(),
329 );
330 Ok(store_outcome(outcome, supplied))
331}
332
333pub async fn store_stream(target: impl Into<Participant>) -> Result<StoreStream> {
337 store_stream_with_options(target, InterlaceOperationOptions::default()).await
338}
339pub async fn store_stream_with_options(
340 target: impl Into<Participant>,
341 operation: InterlaceOperationOptions,
342) -> Result<StoreStream> {
343 let target = target.into();
344 let operands = private_operands(all_records().clone(), rx_disabled())?;
345 let provider = Lace::empty_memory();
346 let first = Participant::from(provider.handle());
347 let interlace = match route(first, target, operands, operation.limits) {
348 Ok(interlace) => interlace,
349 Err(error) => {
350 return Err(
351 Error::combine([error].into_iter().chain(provider.close().await.err())).unwrap(),
352 );
353 }
354 };
355 let mut stream = StoreStream {
356 interlace,
357 provider,
358 failure: None,
359 };
360 match stream.fixed_point(None).await {
361 Ok(_) => Ok(stream),
362 Err(error) => {
363 stream.failure = Some(error.clone());
364 Err(stream.close().await.err().unwrap_or(error))
365 }
366 }
367}
368impl StoreStream {
369 pub async fn offer(&mut self, record: Record) -> Result<StoreRecordDisposition> {
375 if let Some(error) = &self.failure {
376 return Err(error.clone());
377 }
378 self.failure = Some(Error::integration(
379 "a cancelled offer left the store stream unsettled",
380 ));
381 let result = self.offer_inner(record).await;
382 self.failure = result.as_ref().err().cloned();
383 result
384 }
385
386 async fn offer_inner(&mut self, record: Record) -> Result<StoreRecordDisposition> {
387 let hash = record.hash();
388 let outcome = self
389 .provider
390 .owner
391 .start_store_execution_at(
392 StoreOp::StoreRecords {
393 records: vec![record],
394 },
395 Some(ParticipantPosition::First),
396 )
397 .receive()
398 .await?;
399 if let Some(error) = outcome.failure {
400 return Err(error);
401 }
402 match outcome.response {
403 Some(Ok(StoreResult::StoredRecords { outcomes, .. })) => match outcomes.as_slice() {
404 [StoreRecordOutcome::Duplicate { .. }] => Ok(StoreRecordDisposition::Skipped),
405 [StoreRecordOutcome::Stored { .. }] => Ok(if self.fixed_point(Some(hash)).await? {
406 StoreRecordDisposition::Saved
407 } else {
408 StoreRecordDisposition::Skipped
409 }),
410 _ => Err(Error::integration(
411 "provider store returned the wrong outcomes",
412 )),
413 },
414 _ => Err(Error::integration(
415 "provider store returned the wrong result",
416 )),
417 }
418 }
419
420 async fn fixed_point(&mut self, hash: Option<RecordHash>) -> Result<bool> {
423 let mut sent = false;
424 loop {
425 match self.interlace.next_event().await? {
426 Some(OpenEvent::FixedPoint) => return Ok(sent),
427 Some(OpenEvent::RecordSent {
428 position: ParticipantPosition::First,
429 record_hash,
430 }) => sent |= Some(record_hash) == hash,
431 Some(OpenEvent::Closed { reason }) => {
432 return Err(Error::peer_protocol(format!(
433 "store stream closed before an offer's fixed point: {reason:?}"
434 )));
435 }
436 Some(_) => {}
437 None => return Err(exhausted()),
438 }
439 }
440 }
441
442 pub async fn close(self) -> Result<()> {
445 let StoreStream {
446 interlace,
447 provider,
448 failure,
449 } = self;
450 let interlace = interlace.close().await.err();
451 let provider = provider.close().await.err();
452 Error::combine(failure.into_iter().chain(interlace).chain(provider)).map_or(Ok(()), Err)
453 }
454}
455
456pub async fn for_each(
457 target: impl Into<Participant>,
458 policy: impl IntoPolicy,
459) -> Result<ForEachStream> {
460 for_each_with_options(target, policy, InterlaceOperationOptions::default()).await
461}
462pub async fn for_each_with_options(
463 target: impl Into<Participant>,
464 policy: impl IntoPolicy,
465 operation: InterlaceOperationOptions,
466) -> Result<ForEachStream> {
467 let target = target.into();
468 let observer = Lace::empty_memory();
469 let operands = private_operands(policy.into_policy()?, tx_disabled())?;
470 let first = Participant::from(observer.handle());
471 Ok(ForEachStream {
472 interlace: route(first, target, operands, operation.limits)?,
473 observer,
474 })
475}
476impl ForEachStream {
477 pub async fn close(self) -> Result<OpenEvent> {
479 let ForEachStream {
480 interlace,
481 observer,
482 } = self;
483 let interlace_result = interlace.driver.close_cancelled().await;
484 let observer_result = observer.close().await;
485 if let Some(error) = Error::combine(
486 interlace_result
487 .as_ref()
488 .err()
489 .cloned()
490 .into_iter()
491 .chain(observer_result.err()),
492 ) {
493 return Err(error);
494 }
495 Ok(OpenEvent::Closed {
496 reason: interlace_result?,
497 })
498 }
499
500 pub async fn next_event(&mut self) -> Result<Option<OpenEvent>> {
501 loop {
502 match self.interlace.driver.next_event().await? {
503 Some(
504 event @ (OpenEvent::RecordSaved {
505 position: ParticipantPosition::First,
506 ..
507 }
508 | OpenEvent::FixedPoint
509 | OpenEvent::Closed { .. }),
510 ) => return Ok(Some(event)),
511 Some(_) => {}
512 None => return Ok(None),
513 }
514 }
515 }
516}