cratefield_runtime_native/
runtime.rs1use std::sync::Arc;
21use std::sync::atomic::{AtomicBool, Ordering};
22
23use cratefield_core::{
24 Blob, Captcha, Database, HarnessConfig, KeyValue, Mailer, Port, Ports, RateLimiter, Runtime,
25 UlidIdGen,
26};
27
28use crate::config::EnvConfig;
29use crate::ports::{ReqwestClient, SpawnDefer, TokioClock};
30
31static WARNED_SIGNER: AtomicBool = AtomicBool::new(false);
32
33fn warn_once(flag: &AtomicBool, message: &str) {
34 if !flag.load(Ordering::Relaxed) {
35 flag.store(true, Ordering::Relaxed);
36 tracing::warn!("{message}");
37 }
38}
39
40#[derive(Clone, Default)]
51pub struct Native {
52 db: Option<Arc<dyn Database>>,
53 rate_limiter: Option<Arc<dyn RateLimiter>>,
54 kv: Option<Arc<dyn KeyValue>>,
55 blob: Option<Arc<dyn Blob>>,
56 mailer: Option<Arc<dyn Mailer>>,
57 captcha: Option<Arc<dyn Captcha>>,
58}
59
60impl Native {
61 pub fn new() -> Self {
62 Self::default()
63 }
64
65 #[must_use]
69 pub fn db(mut self, db: impl Database + 'static) -> Self {
70 self.db = Some(Arc::new(db));
71 self
72 }
73
74 #[must_use]
76 pub fn db_arc(mut self, db: Arc<dyn Database>) -> Self {
77 self.db = Some(db);
78 self
79 }
80
81 #[must_use]
84 pub fn rate_limiter(mut self, limiter: impl RateLimiter + 'static) -> Self {
85 self.rate_limiter = Some(Arc::new(limiter));
86 self
87 }
88
89 #[must_use]
91 pub fn rate_limiter_arc(mut self, limiter: Arc<dyn RateLimiter>) -> Self {
92 self.rate_limiter = Some(limiter);
93 self
94 }
95
96 #[must_use]
98 pub fn kv(mut self, kv: impl KeyValue + 'static) -> Self {
99 self.kv = Some(Arc::new(kv));
100 self
101 }
102
103 #[must_use]
105 pub fn kv_arc(mut self, kv: Arc<dyn KeyValue>) -> Self {
106 self.kv = Some(kv);
107 self
108 }
109
110 #[must_use]
113 pub fn blob_arc(mut self, blob: Arc<dyn Blob>) -> Self {
114 self.blob = Some(blob);
115 self
116 }
117
118 #[must_use]
119 pub fn mailer(mut self, mailer: impl Mailer + 'static) -> Self {
120 self.mailer = Some(Arc::new(mailer));
121 self
122 }
123
124 #[must_use]
125 pub fn mailer_arc(mut self, mailer: Arc<dyn Mailer>) -> Self {
126 self.mailer = Some(mailer);
127 self
128 }
129
130 #[must_use]
131 pub fn captcha(mut self, captcha: impl Captcha + 'static) -> Self {
132 self.captcha = Some(Arc::new(captcha));
133 self
134 }
135
136 #[must_use]
137 pub fn captcha_arc(mut self, captcha: Arc<dyn Captcha>) -> Self {
138 self.captcha = Some(captcha);
139 self
140 }
141
142 #[must_use]
152 pub fn ports(&self) -> Ports {
153 let config: Arc<dyn cratefield_core::Config> = Arc::new(EnvConfig);
154 let mut ports = Ports::with_config(Arc::clone(&config));
155
156 ports.db.clone_from(&self.db);
157 ports.rate_limiter.clone_from(&self.rate_limiter);
158 ports.kv.clone_from(&self.kv);
159 ports.blob.clone_from(&self.blob);
160 ports.mailer.clone_from(&self.mailer);
161 ports.captcha.clone_from(&self.captcha);
162
163 match HarnessConfig::from_config(&*config) {
164 Ok(parsed) => ports.signer = Some(Arc::new(parsed.signer())),
165 Err(_) => warn_once(
166 &WARNED_SIGNER,
167 "HARNESS_SECRET missing or invalid: Signer port not provided",
168 ),
169 }
170
171 ports.http = Some(Arc::new(ReqwestClient::new()));
172 ports.clock = Some(Arc::new(TokioClock));
173 ports.id_gen = Some(Arc::new(UlidIdGen));
174 ports.defer = Some(Arc::new(SpawnDefer));
175 ports
176 }
177}
178
179impl Runtime for Native {
180 fn provides(&self) -> Vec<Port> {
184 let mut provided = vec![
185 Port::Signer,
186 Port::HttpClient,
187 Port::Clock,
188 Port::IdGen,
189 Port::Defer,
190 ];
191 if self.db.is_some() {
192 provided.push(Port::Db);
193 }
194 if self.rate_limiter.is_some() {
195 provided.push(Port::RateLimiter);
196 }
197 if self.kv.is_some() {
198 provided.push(Port::KeyValue);
199 }
200 if self.blob.is_some() {
201 provided.push(Port::Blob);
202 }
203 if self.mailer.is_some() {
204 provided.push(Port::Mailer);
205 }
206 if self.captcha.is_some() {
207 provided.push(Port::Captcha);
208 }
209 provided
210 }
211}
212
213pub(crate) fn clone_ports(ports: &Ports) -> Ports {
218 let mut snapshot = Ports::with_config(Arc::clone(&ports.config));
219 snapshot.db.clone_from(&ports.db);
220 snapshot.mailer.clone_from(&ports.mailer);
221 snapshot.captcha.clone_from(&ports.captcha);
222 snapshot.rate_limiter.clone_from(&ports.rate_limiter);
223 snapshot.signer.clone_from(&ports.signer);
224 snapshot.kv.clone_from(&ports.kv);
225 snapshot.blob.clone_from(&ports.blob);
226 snapshot.http.clone_from(&ports.http);
227 snapshot.clock.clone_from(&ports.clock);
228 snapshot.id_gen.clone_from(&ports.id_gen);
229 snapshot.defer.clone_from(&ports.defer);
230 snapshot
231}