1use downcast_rs::{Downcast, impl_downcast};
3use dyn_clone::{DynClone, clone_trait_object};
4use std::sync::atomic::{AtomicU64, Ordering};
5
6pub trait Isolation:
17 seal::Sealed + Downcast + DynClone + std::fmt::Debug + Send + Sync + 'static
18{
19 fn compatible(&self, other: &dyn Isolation) -> bool;
37
38 fn join(&self, other: &dyn Isolation) -> Option<Box<dyn Isolation>>;
57
58 fn enables_long_lived_circuits(&self) -> bool {
69 false
70 }
71}
72
73mod seal {
75 pub trait Sealed {}
77 impl<T: super::IsolationHelper> Sealed for T {}
78}
79
80impl_downcast!(Isolation);
81clone_trait_object!(Isolation);
82impl<T: Isolation> From<T> for Box<dyn Isolation> {
83 fn from(isolation: T) -> Self {
84 Box::new(isolation)
85 }
86}
87
88impl<T: IsolationHelper + Clone + std::fmt::Debug + Send + Sync + 'static> Isolation for T {
89 fn compatible(&self, other: &dyn Isolation) -> bool {
90 if let Some(other) = other.as_any().downcast_ref() {
91 self.compatible_same_type(other)
92 } else {
93 false
94 }
95 }
96
97 fn join(&self, other: &dyn Isolation) -> Option<Box<dyn Isolation>> {
98 if let Some(other) = other.as_any().downcast_ref() {
99 self.join_same_type(other)
100 .map(|res| Box::new(res) as Box<dyn Isolation>)
101 } else {
102 None
103 }
104 }
105
106 fn enables_long_lived_circuits(&self) -> bool {
107 IsolationHelper::enables_long_lived_circuits(self)
108 }
109}
110
111pub trait IsolationHelper: Sized {
123 fn compatible_same_type(&self, other: &Self) -> bool;
130
131 fn join_same_type(&self, other: &Self) -> Option<Self>;
135
136 fn enables_long_lived_circuits(&self) -> bool {
141 false
142 }
143}
144
145#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
194pub struct IsolationToken(u64);
195
196#[allow(clippy::new_without_default)]
197impl IsolationToken {
198 pub fn new() -> Self {
207 static COUNTER: AtomicU64 = AtomicU64::new(1);
209 let token = COUNTER.fetch_add(1, Ordering::Relaxed);
212 assert!(token < u64::MAX);
213 IsolationToken(token)
214 }
215
216 pub fn no_isolation() -> Self {
222 IsolationToken(0)
223 }
224}
225
226impl IsolationHelper for IsolationToken {
227 fn compatible_same_type(&self, other: &Self) -> bool {
228 self == other
229 }
230 fn join_same_type(&self, other: &Self) -> Option<Self> {
231 if self.compatible_same_type(other) {
232 Some(*self)
233 } else {
234 None
235 }
236 }
237
238 fn enables_long_lived_circuits(&self) -> bool {
239 false
240 }
241}
242
243macro_rules! tuple_impls {
245 ($(
246 $Tuple:ident {
247 $(($idx:tt) -> $T:ident)+
248 }
249 )+) => {
250 $(
251 impl<$($T:IsolationHelper),+> IsolationHelper for ($($T,)+) {
252 fn compatible_same_type(&self, other: &Self) -> bool {
253 $(self.$idx.compatible_same_type(&other.$idx))&&+
254 }
255
256 fn join_same_type(&self, other: &Self) -> Option<Self> {
257 Some((
258 $(self.$idx.join_same_type(&other.$idx)?,)+
259 ))
260 }
261
262 fn enables_long_lived_circuits(&self) -> bool {
263 $(self.$idx.enables_long_lived_circuits() || )+ false
264 }
265 }
266 )+
267 }
268}
269
270tuple_impls! {
271 Tuple1 {
272 (0) -> A
273 }
274 Tuple2 {
275 (0) -> A
276 (1) -> B
277 }
278 Tuple3 {
279 (0) -> A
280 (1) -> B
281 (2) -> C
282 }
283 Tuple4 {
284 (0) -> A
285 (1) -> B
286 (2) -> C
287 (3) -> D
288 }
289 Tuple5 {
290 (0) -> A
291 (1) -> B
292 (2) -> C
293 (3) -> D
294 (4) -> E
295 }
296 Tuple6 {
297 (0) -> A
298 (1) -> B
299 (2) -> C
300 (3) -> D
301 (4) -> E
302 (5) -> F
303 }
304 Tuple7 {
305 (0) -> A
306 (1) -> B
307 (2) -> C
308 (3) -> D
309 (4) -> E
310 (5) -> F
311 (6) -> G
312 }
313 Tuple8 {
314 (0) -> A
315 (1) -> B
316 (2) -> C
317 (3) -> D
318 (4) -> E
319 (5) -> F
320 (6) -> G
321 (7) -> H
322 }
323 Tuple9 {
324 (0) -> A
325 (1) -> B
326 (2) -> C
327 (3) -> D
328 (4) -> E
329 (5) -> F
330 (6) -> G
331 (7) -> H
332 (8) -> I
333 }
334 Tuple10 {
335 (0) -> A
336 (1) -> B
337 (2) -> C
338 (3) -> D
339 (4) -> E
340 (5) -> F
341 (6) -> G
342 (7) -> H
343 (8) -> I
344 (9) -> J
345 }
346 Tuple11 {
347 (0) -> A
348 (1) -> B
349 (2) -> C
350 (3) -> D
351 (4) -> E
352 (5) -> F
353 (6) -> G
354 (7) -> H
355 (8) -> I
356 (9) -> J
357 (10) -> K
358 }
359 Tuple12 {
360 (0) -> A
361 (1) -> B
362 (2) -> C
363 (3) -> D
364 (4) -> E
365 (5) -> F
366 (6) -> G
367 (7) -> H
368 (8) -> I
369 (9) -> J
370 (10) -> K
371 (11) -> L
372 }
373}
374
375#[derive(Clone, Debug, derive_builder::Builder)]
380pub struct StreamIsolation {
381 #[builder(default = "Box::new(IsolationToken::no_isolation())")]
383 stream_isolation: Box<dyn Isolation>,
384 #[builder(default = "IsolationToken::no_isolation()")]
387 owner_token: IsolationToken,
388}
389
390impl StreamIsolation {
391 pub fn no_isolation() -> Self {
393 StreamIsolationBuilder::new()
394 .build()
395 .expect("Bug constructing StreamIsolation")
396 }
397
398 pub fn builder() -> StreamIsolationBuilder {
401 StreamIsolationBuilder::new()
402 }
403}
404
405impl IsolationHelper for StreamIsolation {
406 fn compatible_same_type(&self, other: &StreamIsolation) -> bool {
407 self.owner_token == other.owner_token
408 && self
409 .stream_isolation
410 .compatible(other.stream_isolation.as_ref())
411 }
412
413 fn join_same_type(&self, other: &StreamIsolation) -> Option<StreamIsolation> {
414 if self.owner_token != other.owner_token {
415 return None;
416 }
417 self.stream_isolation
418 .join(other.stream_isolation.as_ref())
419 .map(|stream_isolation| StreamIsolation {
420 stream_isolation,
421 owner_token: self.owner_token,
422 })
423 }
424
425 fn enables_long_lived_circuits(&self) -> bool {
426 self.stream_isolation.enables_long_lived_circuits()
427 }
428}
429
430impl StreamIsolationBuilder {
431 pub fn new() -> Self {
433 StreamIsolationBuilder::default()
434 }
435}
436
437#[cfg(test)]
438pub(crate) mod test {
439 #![allow(clippy::unwrap_used)]
440 use super::*;
441
442 pub(crate) trait IsolationTokenEq {
445 fn isol_eq(&self, other: &Self) -> bool;
448 }
449
450 macro_rules! assert_isoleq {
451 { $arg1:expr, $arg2:expr } => {
452 assert!($arg1.isol_eq(&$arg2))
453 }
454 }
455 pub(crate) use assert_isoleq;
456
457 impl IsolationTokenEq for IsolationToken {
458 fn isol_eq(&self, other: &Self) -> bool {
459 self == other
460 }
461 }
462
463 impl<T: IsolationTokenEq> IsolationTokenEq for Option<T> {
464 fn isol_eq(&self, other: &Self) -> bool {
465 match (self, other) {
466 (Some(this), Some(other)) => this.isol_eq(other),
467 (None, None) => true,
468 _ => false,
469 }
470 }
471 }
472
473 impl<T: IsolationTokenEq + std::fmt::Debug> IsolationTokenEq for Vec<T> {
474 fn isol_eq(&self, other: &Self) -> bool {
475 if self.len() != other.len() {
476 return false;
477 }
478 self.iter()
479 .zip(other.iter())
480 .all(|(this, other)| this.isol_eq(other))
481 }
482 }
483
484 impl IsolationTokenEq for dyn Isolation {
485 fn isol_eq(&self, other: &Self) -> bool {
486 let this = self.as_any().downcast_ref::<IsolationToken>();
487 let other = other.as_any().downcast_ref::<IsolationToken>();
488 match (this, other) {
489 (Some(this), Some(other)) => this == other,
490 _ => false,
491 }
492 }
493 }
494
495 impl IsolationTokenEq for StreamIsolation {
496 fn isol_eq(&self, other: &Self) -> bool {
497 self.stream_isolation
498 .isol_eq(other.stream_isolation.as_ref())
499 && self.owner_token == other.owner_token
500 }
501 }
502
503 #[derive(PartialEq, Clone, Copy, Debug, Eq)]
504 struct OtherIsolation(usize);
505
506 impl IsolationHelper for OtherIsolation {
507 fn compatible_same_type(&self, other: &Self) -> bool {
508 self == other
509 }
510 fn join_same_type(&self, other: &Self) -> Option<Self> {
511 if self.compatible_same_type(other) {
512 Some(*self)
513 } else {
514 None
515 }
516 }
517 }
518
519 #[test]
520 fn isolation_token() {
521 let token_1 = IsolationToken::new();
522 let token_2 = IsolationToken::new();
523
524 assert!(token_1.compatible_same_type(&token_1));
525 assert!(token_2.compatible_same_type(&token_2));
526 assert!(!token_1.compatible_same_type(&token_2));
527
528 assert_eq!(token_1.join_same_type(&token_1), Some(token_1));
529 assert_eq!(token_2.join_same_type(&token_2), Some(token_2));
530 assert_eq!(token_1.join_same_type(&token_2), None);
531 }
532
533 #[test]
534 fn isolation_trait() {
535 let token_1: Box<dyn Isolation> = Box::new(IsolationToken::new());
536 let token_2: Box<dyn Isolation> = Box::new(IsolationToken::new());
537 let other_1: Box<dyn Isolation> = Box::new(OtherIsolation(0));
538 let other_2: Box<dyn Isolation> = Box::new(OtherIsolation(1));
539
540 assert!(token_1.compatible(token_1.as_ref()));
541 assert!(token_2.compatible(token_2.as_ref()));
542 assert!(!token_1.compatible(token_2.as_ref()));
543
544 assert!(other_1.compatible(other_1.as_ref()));
545 assert!(other_2.compatible(other_2.as_ref()));
546 assert!(!other_1.compatible(other_2.as_ref()));
547
548 assert!(!token_1.compatible(other_1.as_ref()));
549 assert!(!other_1.compatible(token_1.as_ref()));
550
551 assert!(token_1.join(token_1.as_ref()).is_some());
552 assert!(token_1.join(token_2.as_ref()).is_none());
553
554 assert!(other_1.join(other_1.as_ref()).is_some());
555 assert!(other_1.join(other_2.as_ref()).is_none());
556
557 assert!(token_1.join(other_1.as_ref()).is_none());
558 assert!(other_1.join(token_1.as_ref()).is_none());
559 }
560
561 #[test]
562 fn isolation_tuple() {
563 let token_1 = IsolationToken::new();
564 let token_2 = IsolationToken::new();
565 let other_1 = OtherIsolation(0);
566 let other_2 = OtherIsolation(1);
567
568 let token_12: Box<dyn Isolation> = Box::new((token_1, token_2));
569 let token_21: Box<dyn Isolation> = Box::new((token_2, token_1));
570 let mix_11: Box<dyn Isolation> = Box::new((token_1, other_1));
571 let mix_12: Box<dyn Isolation> = Box::new((token_1, other_2));
572 let revmix_11: Box<dyn Isolation> = Box::new((other_1, token_1));
573
574 let join_token = token_12.join(token_12.as_ref()).unwrap();
575 assert!(join_token.compatible(token_12.as_ref()));
576 let join_mix = mix_12.join(mix_12.as_ref()).unwrap();
577 assert!(join_mix.compatible(mix_12.as_ref()));
578
579 let isol_list = [token_12, token_21, mix_11, mix_12, revmix_11];
580
581 for (i, isol1) in isol_list.iter().enumerate() {
582 for (j, isol2) in isol_list.iter().enumerate() {
583 assert_eq!(isol1.compatible(isol2.as_ref()), i == j);
584 }
585 }
586 }
587
588 #[test]
589 fn build_isolation() {
590 let no_isolation = StreamIsolation::no_isolation();
591 let no_isolation2 = StreamIsolation::builder()
592 .owner_token(IsolationToken::no_isolation())
593 .stream_isolation(Box::new(IsolationToken::no_isolation()))
594 .build()
595 .unwrap();
596 assert_eq!(no_isolation.owner_token, no_isolation2.owner_token);
597 assert_eq!(
598 no_isolation
599 .stream_isolation
600 .as_ref()
601 .as_any()
602 .downcast_ref::<IsolationToken>(),
603 no_isolation2
604 .stream_isolation
605 .as_ref()
606 .as_any()
607 .downcast_ref::<IsolationToken>()
608 );
609 assert!(no_isolation.compatible(&no_isolation2));
610
611 let tok = IsolationToken::new();
612 let some_isolation = StreamIsolation::builder().owner_token(tok).build().unwrap();
613 let some_isolation2 = StreamIsolation::builder()
614 .stream_isolation(Box::new(tok))
615 .build()
616 .unwrap();
617 assert!(!no_isolation.compatible(&some_isolation));
618 assert!(!no_isolation.compatible(&some_isolation2));
619 assert!(!some_isolation.compatible(&some_isolation2));
620 assert!(some_isolation.compatible(&some_isolation));
621 }
622}