1mod calendar;
2mod source;
3mod timestamp;
4
5pub use self::source::SystemTimeSource;
6#[cfg(test)]
7pub(crate) use geam_core::{
8 ExecutionError, HostError, HostModule, HostProviderSet, HostedExecution, InvariantError,
9 ModuleSource, PackageSource, ValueType, compile_typed_host_program, plan_host_program,
10};
11pub(crate) use geam_core::{
12 HostComponentProfile, HostFailure, HostProfile, HostProviderComponent,
13 HostProviderComponentRegistration, HostProviderModule, HostRegistrationError,
14};
15use geam_stdlib::{
16 Component as GleamStdlibComponent, GleamStdlibHostProfile, GleamStdlibRunState,
17 GleamStdlibStores, IoOutput,
18};
19use std::marker::PhantomData;
20use std::time::SystemTime;
21
22pub trait TimeSource: 'static {
24 fn system_time(&mut self) -> Result<SystemTime, HostFailure>;
26
27 fn local_offset_seconds(&mut self) -> Result<i32, HostFailure>;
29}
30
31pub trait GleamTimeHostProfile:
33 GleamStdlibHostProfile + HostComponentProfile<Component<Self::Source>>
34{
35 type Source: TimeSource;
37}
38
39#[derive(Debug, Clone, Copy)]
41pub struct Component<Source = SystemTimeSource>(PhantomData<fn() -> Source>);
42
43impl<Source> HostProviderComponent for Component<Source>
44where
45 Source: TimeSource,
46{
47 const ID: &'static str = "gleam_time";
48 type Stores = ();
49 type RunState = Source;
50}
51
52impl<Source> geam_core::__macro_support::ProviderPackage for Component<Source>
53where
54 Source: TimeSource,
55{
56 const PACKAGE: &'static str = "gleam_time";
57}
58
59#[derive(Default)]
61pub struct GleamTimeProfileStores {
62 stdlib: GleamStdlibStores,
63 time: (),
64}
65
66pub struct GleamTimeRunState<Source = SystemTimeSource> {
68 stdlib: GleamStdlibRunState,
69 source: Source,
70}
71
72#[derive(Debug, Clone, Copy)]
74pub struct GleamTimeProfile<Source = SystemTimeSource>(PhantomData<fn() -> Source>);
75
76impl<Source> GleamTimeRunState<Source> {
77 pub fn new(stdlib: GleamStdlibRunState, source: Source) -> Self {
79 Self { stdlib, source }
80 }
81
82 pub fn stdlib(&self) -> &GleamStdlibRunState {
84 &self.stdlib
85 }
86
87 pub fn stdlib_mut(&mut self) -> &mut GleamStdlibRunState {
89 &mut self.stdlib
90 }
91
92 pub fn source(&self) -> &Source {
94 &self.source
95 }
96
97 pub fn source_mut(&mut self) -> &mut Source {
99 &mut self.source
100 }
101}
102
103impl<Source> HostProfile for GleamTimeProfile<Source>
104where
105 Source: TimeSource,
106{
107 type RunState = GleamTimeRunState<Source>;
108 type ExternalStores = GleamTimeProfileStores;
109}
110
111impl<Source> HostComponentProfile<GleamStdlibComponent> for GleamTimeProfile<Source>
112where
113 Source: TimeSource,
114{
115 fn component_stores(stores: &Self::ExternalStores) -> &GleamStdlibStores {
116 &stores.stdlib
117 }
118
119 fn component_state(state: &mut Self::RunState) -> &mut GleamStdlibRunState {
120 &mut state.stdlib
121 }
122}
123
124impl<Source> HostComponentProfile<Component<Source>> for GleamTimeProfile<Source>
125where
126 Source: TimeSource,
127{
128 fn component_stores(stores: &Self::ExternalStores) -> &() {
129 &stores.time
130 }
131
132 fn component_state(state: &mut Self::RunState) -> &mut Source {
133 &mut state.source
134 }
135}
136
137impl<Source> GleamStdlibHostProfile for GleamTimeProfile<Source>
138where
139 Source: TimeSource,
140{
141 type Io = Vec<IoOutput>;
142}
143
144impl<Source> GleamTimeHostProfile for GleamTimeProfile<Source>
145where
146 Source: TimeSource,
147{
148 type Source = Source;
149}
150
151pub fn host_providers<Profile>() -> Result<Vec<HostProviderModule<Profile>>, HostRegistrationError>
153where
154 Profile: GleamTimeHostProfile,
155{
156 <Component<Profile::Source> as HostProviderComponentRegistration<Profile>>::providers()
157}
158
159impl<Profile, Source> HostProviderComponentRegistration<Profile> for Component<Source>
160where
161 Profile: GleamTimeHostProfile<Source = Source>,
162 Source: TimeSource,
163{
164 fn providers() -> Result<Vec<HostProviderModule<Profile>>, HostRegistrationError> {
165 [
166 calendar::host_provider::<Profile>,
167 timestamp::host_provider::<Profile>,
168 ]
169 .into_iter()
170 .map(|register| register())
171 .collect()
172 }
173}
174
175#[cfg(test)]
176mod effects;
177#[cfg(test)]
178mod test_support;
179
180#[cfg(test)]
181mod tests {
182 use super::test_support::ScriptedSource;
183 use super::{
184 Component, GleamTimeProfile, GleamTimeProfileStores, GleamTimeRunState, host_providers,
185 };
186 use crate::{
187 HostComponentProfile, HostProfile, HostProviderComponent, HostProviderComponentRegistration,
188 };
189 use geam_stdlib::{
190 Component as GleamStdlibComponent, GleamStdlibHostProfile, GleamStdlibRunState,
191 GleamStdlibStores, IoOutput,
192 };
193
194 struct CustomProfile;
195
196 #[derive(Default)]
197 struct CustomStores {
198 stdlib: GleamStdlibStores,
199 time: (),
200 }
201
202 struct CustomRunState {
203 stdlib: GleamStdlibRunState,
204 source: ScriptedSource,
205 }
206
207 impl HostProfile for CustomProfile {
208 type RunState = CustomRunState;
209 type ExternalStores = CustomStores;
210 }
211
212 impl HostComponentProfile<GleamStdlibComponent> for CustomProfile {
213 fn component_stores(stores: &Self::ExternalStores) -> &GleamStdlibStores {
214 &stores.stdlib
215 }
216
217 fn component_state(state: &mut Self::RunState) -> &mut GleamStdlibRunState {
218 &mut state.stdlib
219 }
220 }
221
222 impl GleamStdlibHostProfile for CustomProfile {
223 type Io = Vec<IoOutput>;
224 }
225
226 impl HostComponentProfile<Component<ScriptedSource>> for CustomProfile {
227 fn component_stores(stores: &Self::ExternalStores) -> &() {
228 &stores.time
229 }
230
231 fn component_state(state: &mut Self::RunState) -> &mut ScriptedSource {
232 &mut state.source
233 }
234 }
235
236 impl super::GleamTimeHostProfile for CustomProfile {
237 type Source = ScriptedSource;
238 }
239
240 #[test]
241 fn default_and_custom_profiles_project_owned_state_stores_source_and_io() {
242 let default_stores = GleamTimeProfileStores::default();
243 let custom_stores = CustomStores::default();
244 let mut default_state = GleamTimeRunState::new(
245 GleamStdlibRunState::from_seed([1; 32]),
246 ScriptedSource::default(),
247 );
248 let mut custom_state = CustomRunState {
249 stdlib: GleamStdlibRunState::from_seed([2; 32]),
250 source: ScriptedSource::default(),
251 };
252
253 assert!(std::ptr::eq(
254 <GleamTimeProfile<ScriptedSource> as HostComponentProfile<
255 GleamStdlibComponent,
256 >>::component_stores(&default_stores),
257 &default_stores.stdlib,
258 ));
259 assert!(std::ptr::eq(
260 <CustomProfile as HostComponentProfile<GleamStdlibComponent>>::component_stores(
261 &custom_stores,
262 ),
263 &custom_stores.stdlib,
264 ));
265 let default_stdlib = default_state.stdlib() as *const GleamStdlibRunState;
266 assert!(std::ptr::eq(
267 <GleamTimeProfile<ScriptedSource> as HostComponentProfile<
268 GleamStdlibComponent,
269 >>::component_state(&mut default_state),
270 default_stdlib,
271 ));
272 let custom_stdlib = &custom_state.stdlib as *const GleamStdlibRunState;
273 assert!(std::ptr::eq(
274 <CustomProfile as HostComponentProfile<GleamStdlibComponent>>::component_state(
275 &mut custom_state,
276 ),
277 custom_stdlib,
278 ));
279
280 let default_source = default_state.source() as *const ScriptedSource;
281 assert!(
282 std::ptr::eq(
283 <GleamTimeProfile<ScriptedSource> as HostComponentProfile<
284 Component<ScriptedSource>,
285 >>::component_state(&mut default_state),
286 default_source,
287 )
288 );
289 let custom_source = &custom_state.source as *const ScriptedSource;
290 assert!(std::ptr::eq(
291 <CustomProfile as HostComponentProfile<Component<ScriptedSource>>>::component_state(
292 &mut custom_state,
293 ),
294 custom_source,
295 ));
296 assert!(
297 std::ptr::eq(
298 <GleamTimeProfile<ScriptedSource> as HostComponentProfile<
299 Component<ScriptedSource>,
300 >>::component_stores(&default_stores),
301 &default_stores.time,
302 )
303 );
304 assert!(std::ptr::eq(
305 <CustomProfile as HostComponentProfile<Component<ScriptedSource>>>::component_stores(
306 &custom_stores,
307 ),
308 &custom_stores.time,
309 ));
310 assert!(default_state.stdlib_mut().take_io_outputs().is_empty());
311 assert!(default_state.stdlib().io_outputs().is_empty());
312 default_state.source_mut().offsets.push_back(Ok(3600));
313
314 assert!(default_state.stdlib().io_outputs().is_empty());
315 assert_eq!(default_state.source().offsets.len(), 1);
316 }
317
318 #[test]
319 fn registers_calendar_then_timestamp_without_external_types() {
320 assert_eq!(<Component as HostProviderComponent>::ID, "gleam_time");
321 let providers =
322 <Component as HostProviderComponentRegistration<GleamTimeProfile>>::providers()
323 .expect("Time component should register");
324 let facade =
325 host_providers::<GleamTimeProfile>().expect("official Time providers should register");
326 assert_eq!(
327 facade
328 .iter()
329 .map(|provider| provider.module().as_str())
330 .collect::<Vec<_>>(),
331 providers
332 .iter()
333 .map(|provider| provider.module().as_str())
334 .collect::<Vec<_>>(),
335 );
336
337 assert_eq!(
338 providers
339 .iter()
340 .map(|provider| (provider.package().as_str(), provider.module().as_str()))
341 .collect::<Vec<_>>(),
342 [
343 ("gleam_time", "gleam/time/calendar"),
344 ("gleam_time", "gleam/time/timestamp"),
345 ],
346 );
347 assert!(
348 providers
349 .iter()
350 .all(|provider| provider.external_types().count() == 0),
351 );
352 }
353}