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