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