praborrow_core/lib.rs
1//! Core primitives for distributed ownership enforcement.
2//!
3//! This crate provides `Sovereign<T>`, a wrapper type that tracks ownership
4//! across network boundaries. When a resource is "annexed" (moved to another node),
5//! local access is prohibited.
6//!
7//! # The Garuda Proof System
8//!
9//! With `praborrow-prover`, this crate now supports **formally verified** state
10//! transitions. Use `annex_verified()` to require SMT proof before annexation.
11//!
12//! # Safety
13//! Uses `UnsafeCell` and `AtomicU8` for interior mutability with thread-safety.
14//! The `Send`/`Sync` implementations are safe when `T` is `Send`/`Sync`.
15
16#![cfg_attr(not(feature = "std"), no_std)]
17
18extern crate alloc;
19
20use alloc::collections::BTreeMap;
21use alloc::string::String;
22use core::cell::UnsafeCell;
23use core::marker::PhantomData;
24use core::ops::{Deref, DerefMut};
25use core::sync::atomic::{AtomicU8, Ordering};
26
27/// The state of a Sovereign resource.
28/// 0: Domestic (Local jurisdiction)
29/// 1: Exiled (Foreign jurisdiction - moved to another node)
30#[derive(Debug, PartialEq, Eq, Clone, Copy)]
31#[repr(u8)]
32pub enum SovereignState {
33 Domestic = 0,
34 Exiled = 1,
35}
36
37impl core::fmt::Display for SovereignState {
38 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
39 match self {
40 SovereignState::Domestic => f.write_str("Domestic"),
41 SovereignState::Exiled => f.write_str("Exiled"),
42 }
43 }
44}
45
46/// A token that proves a resource has been returned to domestic jurisdiction.
47///
48/// This token is required to call `Sovereign::repatriate`. It can only be constructed
49/// by trusted system components (like the lease manager) that can guarantee the
50/// resource is safe to reclaim.
51pub struct RepatriationToken {
52 _private: (),
53}
54
55impl RepatriationToken {
56 /// Creates a new repatriation token.
57 ///
58 /// # Safety
59 ///
60 /// This function is unsafe because creating a token allows the holder to
61 /// repatriate a sovereign resource. The caller must guarantee that the
62 /// resource is indeed back in domestic jurisdiction and no longer accessed
63 /// remotely.
64 pub unsafe fn new() -> Self {
65 Self { _private: () }
66 }
67}
68
69/// A wrapper that enforces ownership semantics across network boundaries.
70///
71/// "Memory safety with sovereign integrity."
72pub struct Sovereign<T> {
73 inner: UnsafeCell<T>,
74 state: AtomicU8,
75}
76
77/// Error enforcing constitutional invariants.
78#[non_exhaustive]
79#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
80pub enum ConstitutionError {
81 #[error("Invariant violated: {expression}. Values: {values:?}")]
82 InvariantViolation {
83 expression: String,
84 values: BTreeMap<String, String>,
85 },
86}
87
88/// Error returned when accessing a Sovereign resource fails.
89#[non_exhaustive]
90#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
91pub enum SovereigntyError {
92 /// Resource is under foreign jurisdiction (Exiled).
93 #[error("SOVEREIGNTY VIOLATION: Resource is under foreign jurisdiction.")]
94 ForeignJurisdiction,
95}
96
97impl<T> Sovereign<T> {
98 /// Creates a new Sovereign resource under domestic jurisdiction.
99 ///
100 /// # Atomic Ordering
101 ///
102 /// Uses `SeqCst` ordering for maximum safety in distributed scenarios.
103 /// This ensures all threads see state changes in a consistent order,
104 /// which is critical for ownership semantics across network boundaries.
105 #[must_use = "Sovereign resources must be managed carefully"]
106 pub fn new(value: T) -> Self {
107 Self {
108 inner: UnsafeCell::new(value),
109 state: AtomicU8::new(SovereignState::Domestic as u8),
110 }
111 }
112
113 /// Creates a new Sovereign resource that is already under foreign jurisdiction.
114 ///
115 /// This is useful for nodes receiving data that has been transferred from
116 /// another node - the resource starts in an Exiled state.
117 ///
118 /// # Example
119 ///
120 /// ```
121 /// use praborrow_core::{Sovereign, SovereignState};
122 ///
123 /// let foreign_data = Sovereign::new_exiled(42i32);
124 /// assert!(foreign_data.is_exiled());
125 /// ```
126 #[must_use = "Sovereign resources must be managed carefully"]
127 pub fn new_exiled(value: T) -> Self {
128 Self {
129 inner: UnsafeCell::new(value),
130 state: AtomicU8::new(SovereignState::Exiled as u8),
131 }
132 }
133
134 /// Annexes the resource, moving it to foreign jurisdiction.
135 ///
136 /// Once annexed, the resource cannot be accessed locally.
137 /// Access attempts will result in a Sovereignty Violation.
138 #[must_use = "Annexation result should be checked"]
139 #[tracing::instrument(skip(self))]
140 pub fn annex(&self) -> Result<(), AnnexError> {
141 let current = self.state.load(Ordering::SeqCst);
142 if current == SovereignState::Exiled as u8 {
143 return Err(AnnexError::AlreadyExiled);
144 }
145
146 // Diplomatically transition state
147 self.state
148 .store(SovereignState::Exiled as u8, Ordering::SeqCst);
149
150 tracing::debug!(
151 from = "Domestic",
152 to = "Exiled",
153 "Resource annexed to foreign jurisdiction"
154 );
155
156 Ok(())
157 }
158
159 /// Returns a reference to the inner value without jurisdiction check.
160 ///
161 /// # Safety
162 ///
163 /// This is safe because we're returning a shared reference and the caller
164 /// is responsible for ensuring the resource is domestic. Use `try_get()`
165 /// for safe access with jurisdiction verification.
166 pub fn inner_ref(&self) -> &T {
167 // SAFETY: We're only reading, and this is safe when called from
168 // contexts that have already verified jurisdiction.
169 unsafe { &*self.inner.get() }
170 }
171
172 /// Returns the current state of the resource.
173 #[inline]
174 pub fn state(&self) -> SovereignState {
175 match self.state.load(Ordering::SeqCst) {
176 0 => SovereignState::Domestic,
177 _ => SovereignState::Exiled,
178 }
179 }
180
181 /// Returns `true` if the resource is under domestic jurisdiction.
182 #[inline]
183 pub fn is_domestic(&self) -> bool {
184 self.state.load(Ordering::SeqCst) == SovereignState::Domestic as u8
185 }
186
187 /// Returns `true` if the resource is under foreign jurisdiction (exiled).
188 #[inline]
189 pub fn is_exiled(&self) -> bool {
190 self.state.load(Ordering::SeqCst) == SovereignState::Exiled as u8
191 }
192
193 /// Attempts to get a reference to the value, returning an error if Exiled.
194 #[must_use = "Check jurisdiction result"]
195 pub fn try_get(&self) -> Result<&T, SovereigntyError> {
196 if self.is_exiled() {
197 return Err(SovereigntyError::ForeignJurisdiction);
198 }
199 // SAFETY: We verified the resource is domestic.
200 unsafe { Ok(&*self.inner.get()) }
201 }
202
203 /// Attempts to get a mutable reference to the value, returning an error if Exiled.
204 #[must_use = "Check jurisdiction result"]
205 pub fn try_get_mut(&mut self) -> Result<&mut T, SovereigntyError> {
206 if self.is_exiled() {
207 return Err(SovereigntyError::ForeignJurisdiction);
208 }
209 // SAFETY: We verified resource is domestic and have &mut self.
210 unsafe { Ok(&mut *self.inner.get()) }
211 }
212
213 /// Repatriates a resource, transitioning it from Exiled back to Domestic.
214 ///
215 /// Requires a `RepatriationToken` as proof that the resource is safe to reclaim.
216 ///
217 /// # Example
218 ///
219 /// ```
220 /// use praborrow_core::{Sovereign, SovereignState, RepatriationToken};
221 ///
222 /// let resource = Sovereign::new(42i32);
223 /// resource.annex().unwrap();
224 /// assert!(resource.is_exiled());
225 ///
226 /// // ... resource is sent to foreign node and returned ...
227 ///
228 /// // SAFETY: construction of token implies safety verification
229 /// let token = unsafe { RepatriationToken::new() };
230 /// resource.repatriate(token);
231 /// assert!(resource.is_domestic());
232 /// ```
233 #[tracing::instrument(skip(self, _token))]
234 pub fn repatriate(&self, _token: RepatriationToken) {
235 let previous = self
236 .state
237 .swap(SovereignState::Domestic as u8, Ordering::SeqCst);
238
239 if previous == SovereignState::Exiled as u8 {
240 tracing::debug!(
241 from = "Exiled",
242 to = "Domestic",
243 "Resource repatriated to domestic jurisdiction"
244 );
245 }
246 }
247
248 /// Checks if the resource is currently domestic, panicking if not.
249 ///
250 /// Used internally by Deref/DerefMut. Prefer `try_get()` for non-panicking access.
251 #[track_caller]
252 fn verify_jurisdiction(&self) {
253 if self.is_exiled() {
254 panic!("SOVEREIGNTY VIOLATION: Resource is under foreign jurisdiction.");
255 }
256 }
257
258 /// Maps a function over the domestic sovereign value.
259 ///
260 /// If the resource is Exiled, returns `Err(SovereigntyError::ForeignJurisdiction)`
261 /// without evaluating the function.
262 ///
263 /// # Example
264 ///
265 /// ```
266 /// use praborrow_core::Sovereign;
267 /// let s = Sovereign::new(5);
268 /// let result = s.map(|x| x * 2).unwrap();
269 /// assert_eq!(result, 10);
270 /// ```
271 pub fn map<F, U>(&self, f: F) -> Result<U, SovereigntyError>
272 where
273 F: FnOnce(&T) -> U,
274 {
275 if self.is_exiled() {
276 return Err(SovereigntyError::ForeignJurisdiction);
277 }
278 // SAFETY: We verified resource is domestic.
279 Ok(f(unsafe { &*self.inner.get() }))
280 }
281
282 /// Chains a function that returns a Result over the domestic sovereign value.
283 ///
284 /// This is useful for sequencing operations that might fail or themselves require
285 /// jurisdiction checks.
286 pub fn and_then<F, U>(&self, f: F) -> Result<U, SovereigntyError>
287 where
288 F: FnOnce(&T) -> Result<U, SovereigntyError>,
289 {
290 if self.is_exiled() {
291 return Err(SovereigntyError::ForeignJurisdiction);
292 }
293 // SAFETY: We verified resource is domestic.
294 f(unsafe { &*self.inner.get() })
295 }
296
297 /// Returns a reference to the inner value if it matches the predicate.
298 ///
299 /// # Returns
300 ///
301 /// - `Ok(Some(&T))` if domestic and predicate is true
302 /// - `Ok(None)` if domestic and predicate is false
303 /// - `Err(ForeignJurisdiction)` if exiled
304 pub fn filter<P>(&self, predicate: P) -> Result<Option<&T>, SovereigntyError>
305 where
306 P: FnOnce(&T) -> bool,
307 {
308 if self.is_exiled() {
309 return Err(SovereigntyError::ForeignJurisdiction);
310 }
311 // SAFETY: We verified resource is domestic.
312 let val = unsafe { &*self.inner.get() };
313 if predicate(val) {
314 Ok(Some(val))
315 } else {
316 Ok(None)
317 }
318 }
319
320 /// Modifies the value in-place if domestic.
321 ///
322 /// # Example
323 ///
324 /// ```
325 /// use praborrow_core::Sovereign;
326 /// let mut s = Sovereign::new(5);
327 /// s.modify(|x| *x += 1).unwrap();
328 /// assert_eq!(*s, 6);
329 /// ```
330 pub fn modify<F>(&mut self, f: F) -> Result<(), SovereigntyError>
331 where
332 F: FnOnce(&mut T),
333 {
334 if self.is_exiled() {
335 return Err(SovereigntyError::ForeignJurisdiction);
336 }
337 // SAFETY: We verified resource is domestic and have &mut self.
338 f(unsafe { &mut *self.inner.get() });
339 Ok(())
340 }
341}
342
343impl<T: core::fmt::Debug> core::fmt::Debug for Sovereign<T> {
344 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
345 let state = self.state();
346 match state {
347 SovereignState::Domestic => {
348 // SAFETY: We checked state is Domestic.
349 let val = unsafe { &*self.inner.get() };
350 f.debug_struct("Sovereign")
351 .field("state", &state)
352 .field("inner", val)
353 .finish()
354 }
355 SovereignState::Exiled => f
356 .debug_struct("Sovereign")
357 .field("state", &state)
358 .field("inner", &"<Inaccessible>")
359 .finish(),
360 }
361 }
362}
363
364impl<T> Deref for Sovereign<T> {
365 type Target = T;
366
367 #[track_caller]
368 fn deref(&self) -> &Self::Target {
369 self.verify_jurisdiction();
370 // SAFETY: We've verified the resource is domestic, so access is valid.
371 unsafe { &*self.inner.get() }
372 }
373}
374
375impl<T> DerefMut for Sovereign<T> {
376 #[track_caller]
377 fn deref_mut(&mut self) -> &mut Self::Target {
378 self.verify_jurisdiction();
379 // SAFETY: We've verified the resource is domestic and have &mut self.
380 unsafe { &mut *self.inner.get() }
381 }
382}
383
384// SAFETY: Sovereign<T> is Send/Sync if T is Send/Sync, as we use AtomicU8 for state
385// and check it before access. The UnsafeCell is protected by the atomic state check.
386unsafe impl<T: Send> Send for Sovereign<T> {}
387unsafe impl<T: Sync> Sync for Sovereign<T> {}
388
389/// Protocol for enforcing constitutional invariants (runtime checks).
390///
391/// Types implementing this trait can validate their internal state against
392/// a set of invariants defined via the `#[derive(Constitution)]` macro.
393pub trait CheckProtocol {
394 /// Enforces all invariants, returning an error if any are violated.
395 ///
396 /// # Returns
397 ///
398 /// - `Ok(())` if all invariants are satisfied
399 /// - `Err(ConstitutionError)` containing a description of the violated invariant
400 ///
401 /// # Example
402 ///
403 /// ```ignore
404 /// use praborrow_core::CheckProtocol;
405 ///
406 /// let data = MyStruct { value: -1 };
407 /// match data.enforce_law() {
408 /// Ok(()) => println!("All invariants satisfied"),
409 /// Err(e) => println!("Invariant violated: {}", e),
410 /// }
411 /// ```
412 fn enforce_law(&self) -> Result<(), ConstitutionError>;
413}
414
415/// A value carrying cryptographic proof of verification.
416///
417/// This type can only be constructed by successful SMT verification.
418/// Its existence in a type signature proves that formal verification occurred.
419///
420/// # Type Safety Guarantee
421///
422/// `ProofCarrying<T>` cannot be forged - the private `_proof` field
423/// ensures only the prover crate can construct it.
424#[derive(Debug)]
425pub struct ProofCarrying<T> {
426 /// The carried value.
427 pub value: T,
428 /// Private marker ensuring construction only via verification.
429 _proof: PhantomData<()>,
430}
431
432impl<T> ProofCarrying<T> {
433 /// Creates a new proof-carrying value.
434 ///
435 /// This should only be called after successful verification.
436 #[doc(hidden)]
437 pub fn new_unchecked(value: T) -> Self {
438 Self {
439 value,
440 _proof: PhantomData,
441 }
442 }
443
444 /// Extracts the inner value, consuming the proof.
445 pub fn into_inner(self) -> T {
446 self.value
447 }
448}
449
450impl<T: Clone> Clone for ProofCarrying<T> {
451 fn clone(&self) -> Self {
452 Self {
453 value: self.value.clone(),
454 _proof: PhantomData,
455 }
456 }
457}
458
459/// Error type for verified annexation operations.
460#[non_exhaustive]
461#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
462pub enum AnnexError {
463 /// Resource is already under foreign jurisdiction.
464 #[error("Resource is already under foreign jurisdiction")]
465 AlreadyExiled,
466 /// SMT verification failed.
467 #[error("Verification failed: {reason}")]
468 VerificationFailed { reason: String },
469 /// Prover encountered an error.
470 #[error("Prover error: {0}")]
471 ProverError(String),
472}
473
474/// Error returned when a lease operation fails.
475#[non_exhaustive]
476#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
477pub enum LeaseError {
478 /// Resource is already leased to another holder.
479 #[error("Resource is already leased to another holder")]
480 AlreadyLeased,
481 /// Resource is under foreign jurisdiction.
482 #[error("Resource is under foreign jurisdiction")]
483 ForeignJurisdiction,
484 /// Lease duration must be non-zero.
485 #[error("Lease duration must be non-zero")]
486 InvalidDuration,
487}
488
489/// Represents a lease on a Sovereign resource.
490pub struct Lease<T> {
491 /// The holder's unique identifier.
492 pub holder: u128,
493 /// Duration of the lease.
494 pub duration: core::time::Duration,
495 /// Phantom data for the resource type.
496 _phantom: PhantomData<T>,
497}
498
499impl<T> Lease<T> {
500 /// Creates a new lease.
501 ///
502 /// # Duration Validation
503 ///
504 /// If `duration` is zero, returns `Err(LeaseError::InvalidDuration)`.
505 pub fn new(holder: u128, duration: core::time::Duration) -> Result<Self, LeaseError> {
506 if duration.is_zero() {
507 return Err(LeaseError::InvalidDuration);
508 }
509
510 Ok(Self {
511 holder,
512 duration,
513 _phantom: PhantomData,
514 })
515 }
516
517 /// Returns the duration of the lease.
518 #[inline]
519 pub fn duration(&self) -> core::time::Duration {
520 self.duration
521 }
522
523 /// Returns the holder ID.
524 #[inline]
525 pub fn holder(&self) -> u128 {
526 self.holder
527 }
528}
529
530/// Trait for distributed borrow operations.
531pub trait DistributedBorrow<T> {
532 /// Attempt to acquire a lease on the resource.
533 fn try_hire(
534 &self,
535 candidate_id: u128,
536 term: core::time::Duration,
537 ) -> Result<Lease<T>, LeaseError>;
538}
539
540impl<T> DistributedBorrow<T> for Sovereign<T> {
541 fn try_hire(
542 &self,
543 candidate_id: u128,
544 term: core::time::Duration,
545 ) -> Result<Lease<T>, LeaseError> {
546 let current = self.state.load(Ordering::SeqCst);
547 if current == SovereignState::Exiled as u8 {
548 return Err(LeaseError::AlreadyLeased);
549 }
550
551 // Transition to exiled state (leased)
552 self.state
553 .store(SovereignState::Exiled as u8, Ordering::SeqCst);
554 Lease::<T>::new(candidate_id, term)
555 }
556}
557
558/// Extension trait for Sovereign types whose inner types implement formal verification.
559///
560/// This trait is automatically implemented for `Sovereign<T>` where `T` can be
561/// formally verified via the Garuda Proof System.
562pub trait VerifiedAnnex<T> {
563 /// Annexes the resource after successful formal verification.
564 ///
565 /// Unlike `annex()`, this method requires mathematical proof that all
566 /// invariants are satisfied before the state transition occurs.
567 ///
568 /// # Returns
569 ///
570 /// - `Ok(ProofCarrying<()>)` - Verification passed, resource is now Exiled
571 /// - `Err(AnnexError)` - Verification failed or resource already Exiled
572 ///
573 /// # Example
574 ///
575 /// ```ignore
576 /// use praborrow_core::{Sovereign, VerifiedAnnex};
577 ///
578 /// let resource = Sovereign::new(MyVerifiableStruct { balance: 100 });
579 ///
580 /// // This will run SMT verification before annexing
581 /// match resource.annex_verified() {
582 /// Ok(proof) => println!("Annexation proven safe!"),
583 /// Err(e) => println!("Cannot annex: {}", e),
584 /// }
585 /// ```
586 fn annex_verified(&self) -> Result<ProofCarrying<()>, AnnexError>;
587}
588
589// Note: The actual implementation of VerifiedAnnex requires praborrow-prover,
590// which would create a circular dependency. Instead, the implementation is
591// provided via blanket impl in praborrow-prover or via the facade crate.
592//
593// Users should use the `praborrow` facade crate for full functionality.
594
595#[cfg(test)]
596mod tests {
597 use super::*;
598
599 #[test]
600 fn test_sovereign_new() {
601 let s = Sovereign::new(42i32);
602 assert_eq!(s.state(), SovereignState::Domestic);
603 assert!(s.is_domestic());
604 assert!(!s.is_exiled());
605 }
606
607 #[test]
608 fn test_sovereign_new_exiled() {
609 let s = Sovereign::new_exiled(42i32);
610 assert_eq!(s.state(), SovereignState::Exiled);
611 assert!(s.is_exiled());
612 assert!(!s.is_domestic());
613 }
614
615 #[test]
616 fn test_sovereign_deref() {
617 let s = Sovereign::new(42i32);
618 assert_eq!(*s, 42);
619 }
620
621 #[test]
622 fn test_sovereign_deref_mut() {
623 let mut s = Sovereign::new(42i32);
624 *s = 100;
625 assert_eq!(*s, 100);
626 }
627
628 #[test]
629 fn test_sovereign_annex() {
630 let s = Sovereign::new(42i32);
631 assert!(s.annex().is_ok());
632 assert_eq!(s.state(), SovereignState::Exiled);
633 assert!(s.is_exiled());
634 }
635
636 #[test]
637 fn test_sovereign_double_annex() {
638 let s = Sovereign::new(42i32);
639 assert!(s.annex().is_ok());
640 assert!(s.annex().is_err());
641 }
642
643 #[test]
644 fn test_sovereign_repatriate() {
645 let s = Sovereign::new(42i32);
646 s.annex().unwrap();
647 assert!(s.is_exiled());
648
649 // SAFETY: In test context, we control both sides
650 // SAFETY: In test context, we control both sides
651 let token = unsafe { RepatriationToken::new() };
652 s.repatriate(token);
653 assert!(s.is_domestic());
654
655 // Should be able to access again
656 assert_eq!(*s, 42);
657 }
658
659 #[test]
660 #[should_panic(expected = "SOVEREIGNTY VIOLATION")]
661 fn test_sovereignty_violation() {
662 let s = Sovereign::new(42i32);
663 s.annex().unwrap();
664 let _ = *s; // This should panic
665 }
666
667 #[test]
668 fn test_try_get_domestic() {
669 let s = Sovereign::new(42i32);
670 assert_eq!(*s.try_get().unwrap(), 42);
671 }
672
673 #[test]
674 fn test_try_get_exiled() {
675 let s = Sovereign::new(42i32);
676 s.annex().unwrap();
677 assert!(matches!(
678 s.try_get(),
679 Err(SovereigntyError::ForeignJurisdiction)
680 ));
681 }
682
683 #[test]
684 fn test_proof_carrying() {
685 let proof = ProofCarrying::new_unchecked(42i32);
686 assert_eq!(proof.value, 42);
687 assert_eq!(proof.into_inner(), 42);
688 }
689
690 #[test]
691 fn test_annex_error_display() {
692 let e = AnnexError::AlreadyExiled;
693 assert!(e.to_string().contains("foreign jurisdiction"));
694
695 let e = AnnexError::VerificationFailed {
696 reason: "test".to_string(),
697 };
698 assert!(e.to_string().contains("test"));
699 }
700
701 #[test]
702 fn test_lease_zero_duration_fails() {
703 let lease = Lease::<i32>::new(1, core::time::Duration::ZERO);
704 assert!(matches!(lease, Err(LeaseError::InvalidDuration)));
705 }
706
707 #[test]
708 fn test_lease_normal_duration() {
709 let duration = core::time::Duration::from_secs(10);
710 let lease = Lease::<i32>::new(1, duration).unwrap();
711 assert_eq!(lease.duration(), duration);
712 assert_eq!(lease.holder(), 1);
713 }
714
715 #[test]
716 fn test_map_domestic() {
717 let s = Sovereign::new(10);
718 let res = s.map(|x| x * 2);
719 assert_eq!(res, Ok(20));
720 }
721
722 #[test]
723 fn test_map_exiled() {
724 let s = Sovereign::new(10);
725 s.annex().unwrap();
726 let res = s.map(|x| x * 2);
727 assert_eq!(res, Err(SovereigntyError::ForeignJurisdiction));
728 }
729
730 #[test]
731 fn test_and_then() {
732 let s = Sovereign::new(10);
733 let res = s.and_then(|x| {
734 if *x > 5 {
735 Ok(*x * 2)
736 } else {
737 Err(SovereigntyError::ForeignJurisdiction) // Just using this error for test
738 }
739 });
740 assert_eq!(res, Ok(20));
741 }
742
743 #[test]
744 fn test_filter() {
745 let s = Sovereign::new(10);
746
747 // Match
748 let res1 = s.filter(|x| *x > 5);
749 assert_eq!(res1, Ok(Some(&10)));
750
751 // No match
752 let res2 = s.filter(|x| *x < 5);
753 assert_eq!(res2, Ok(None));
754
755 // Exiled
756 s.annex().unwrap();
757 let res3 = s.filter(|x| *x > 5);
758 assert_eq!(res3, Err(SovereigntyError::ForeignJurisdiction));
759 }
760
761 #[test]
762 fn test_modify() {
763 let mut s = Sovereign::new(10);
764 s.modify(|x| *x += 1).unwrap();
765 assert_eq!(*s, 11);
766
767 s.annex().unwrap();
768 let res = s.modify(|x| *x += 1);
769 assert_eq!(res, Err(SovereigntyError::ForeignJurisdiction));
770 }
771}