1pub mod fs;
10pub use fs::{StdFileSystem, TokioFileSystem};
11
12use std::sync::atomic::{AtomicBool, Ordering};
13use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
14
15use seams_rs_core::{Clock, JoinError, JoinHandle, Sleeper, Spawner};
16
17#[derive(Debug, Default, Clone, Copy)]
19pub struct SystemClock;
20
21impl SystemClock {
22 pub fn new() -> Self {
24 Self
25 }
26}
27
28impl Clock for SystemClock {
29 fn now_ns(&self) -> u64 {
30 SystemTime::now()
31 .duration_since(UNIX_EPOCH)
32 .map(|d| d.as_nanos() as u64)
33 .unwrap_or(0)
34 }
35
36 fn now_instant(&self) -> Instant {
37 Instant::now()
38 }
39}
40
41#[derive(Debug, Default, Clone, Copy)]
43pub struct StdSleeper;
44
45impl StdSleeper {
46 pub fn new() -> Self {
48 Self
49 }
50}
51
52const POLL_INTERVAL: Duration = Duration::from_millis(10);
53
54impl Sleeper for StdSleeper {
55 fn sleep(&self, duration: Duration) {
56 std::thread::sleep(duration);
57 }
58
59 fn sleep_responsive(&self, total: Duration, shutdown: &AtomicBool) -> bool {
60 let deadline = Instant::now() + total;
61 loop {
62 if shutdown.load(Ordering::SeqCst) {
63 return true;
64 }
65 let now = Instant::now();
66 if now >= deadline {
67 return false;
68 }
69 let remaining = deadline - now;
70 std::thread::sleep(remaining.min(POLL_INTERVAL));
71 }
72 }
73}
74
75#[derive(Debug, Default, Clone, Copy)]
77pub struct StdSpawner;
78
79impl StdSpawner {
80 pub fn new() -> Self {
82 Self
83 }
84}
85
86struct StdJoinHandle<T> {
87 inner: std::thread::JoinHandle<T>,
88}
89
90impl<T: Send + 'static> JoinHandle<T> for StdJoinHandle<T> {
91 fn join(self: Box<Self>) -> Result<T, JoinError> {
92 self.inner.join().map_err(|payload| {
93 let msg = if let Some(s) = payload.downcast_ref::<&'static str>() {
94 (*s).to_string()
95 } else if let Some(s) = payload.downcast_ref::<String>() {
96 s.clone()
97 } else {
98 "panic".to_string()
99 };
100 JoinError::Panicked(msg)
101 })
102 }
103}
104
105impl Spawner for StdSpawner {
106 fn spawn_blocking<F, T>(&self, f: F) -> Box<dyn JoinHandle<T>>
107 where
108 F: FnOnce() -> T + Send + 'static,
109 T: Send + 'static,
110 {
111 Box::new(StdJoinHandle {
112 inner: std::thread::spawn(f),
113 })
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120 use seams_rs_core::contract_tests as ct;
121
122 #[test]
123 fn system_clock_now_ns() {
124 ct::clock_now_ns_monotonic(&SystemClock::new());
125 }
126
127 #[test]
128 fn system_clock_now_instant() {
129 ct::clock_now_instant_monotonic(&SystemClock::new());
130 }
131
132 #[test]
133 fn std_sleeper_sleep() {
134 ct::sleeper_sleep_waits(
135 &StdSleeper::new(),
136 Duration::from_millis(5),
137 Duration::from_millis(500),
138 );
139 }
140
141 #[test]
142 fn std_sleeper_before() {
143 ct::sleeper_responsive_shutdown_before(&StdSleeper::new());
144 }
145
146 #[test]
147 fn std_sleeper_during() {
148 ct::sleeper_responsive_shutdown_during(&StdSleeper::new());
149 }
150
151 #[test]
152 fn std_sleeper_no_shutdown() {
153 ct::sleeper_responsive_no_shutdown(&StdSleeper::new());
154 }
155
156 #[test]
157 fn std_spawner_value() {
158 ct::spawner_returns_value(&StdSpawner::new());
159 }
160
161 #[test]
162 fn std_spawner_panic() {
163 let prev = std::panic::take_hook();
164 std::panic::set_hook(Box::new(|_| {}));
165 ct::spawner_propagates_panic(&StdSpawner::new());
166 std::panic::set_hook(prev);
167 }
168}