myrmic_sdk/host_functions/cell/
timers.rs1use core::ffi::c_int;
2use core::time::Duration;
3
4use myrmic_common::cells::{Command, CreateTimerRequest};
5
6use crate::error::{ApiError, ErrorCode};
7use crate::{ApiResult, Callback, Void};
8
9mod c_functions {
10 use core::ffi::c_int;
11
12 #[link(wasm_import_module = "cell")]
13 unsafe extern "C" {
14
15 pub(super) fn create_timer(buffer: *const u8, length: c_int) -> c_int;
26
27 pub(super) fn cancel_timer(id: c_int) -> c_int;
36 }
37}
38
39#[must_use = "dropping the handle loses the ability to cancel the timer — store it in cell state"]
45#[derive(serde::Serialize, serde::Deserialize)]
46pub struct TimerHandle {
47 id: u32,
48}
49
50impl TimerHandle {
51 pub fn cancel(self) -> ApiResult<()> {
53 unsafe { c_functions::cancel_timer(self.id as c_int) }.to_result()
56 }
57}
58
59#[must_use]
61pub fn interval(cb: Callback<Void>, period: Duration) -> IntervalBuilder {
62 IntervalBuilder {
63 export_name: cb.into(),
64 delay: None,
65 period,
66 count: None,
67 fixed_delay: false,
68 }
69}
70
71#[must_use]
73pub fn interval_at(cb: Callback<Void>, delay: Duration, period: Duration) -> IntervalBuilder {
74 IntervalBuilder {
75 export_name: cb.into(),
76 delay: Some(delay),
77 period,
78 count: None,
79 fixed_delay: false,
80 }
81}
82
83#[must_use]
85pub fn delay(cb: Callback<Void>, delay: Duration) -> DelayBuilder {
86 DelayBuilder {
87 export_name: cb.into(),
88 delay,
89 }
90}
91
92pub struct IntervalBuilder {
94 export_name: Command,
95 delay: Option<Duration>,
96 period: Duration,
97 count: Option<u32>,
98 fixed_delay: bool,
99}
100
101impl IntervalBuilder {
102 #[must_use]
104 pub fn count(mut self, count: u32) -> Self {
105 self.count = Some(count);
106 self
107 }
108
109 #[must_use]
111 pub fn fixed_delay(mut self) -> Self {
112 self.fixed_delay = true;
113 self
114 }
115
116 pub fn build(self) -> ApiResult<TimerHandle> {
118 if self.count == Some(0) {
119 let _ = crate::warn!("timer count of 0 is invalid — timer would never fire");
120 return Err(ApiError::Usage);
121 }
122 let request = CreateTimerRequest {
123 export_name: self.export_name.as_ref().into(),
124 delay_ms: self.delay.map_or(0, |d| d.as_millis() as u64),
125 period_ms: self.period.as_millis() as u64,
126 count: self.count,
127 fixed_delay: self.fixed_delay,
128 };
129 send_create_request(request)
130 }
131}
132
133pub struct DelayBuilder {
135 export_name: Command,
136 delay: Duration,
137}
138
139impl DelayBuilder {
140 pub fn build(self) -> ApiResult<TimerHandle> {
142 let request = CreateTimerRequest {
143 export_name: self.export_name.as_ref().into(),
144 delay_ms: self.delay.as_millis() as u64,
145 period_ms: 0,
146 count: Some(1),
147 fixed_delay: false,
148 };
149 send_create_request(request)
150 }
151}
152
153fn send_create_request(request: CreateTimerRequest) -> ApiResult<TimerHandle> {
154 let bytes = postcard::to_allocvec(&request).expect("serialization should not fail");
155 let id = unsafe { c_functions::create_timer(bytes.as_ptr(), bytes.len() as c_int) };
158 if id >= 0 {
159 Ok(TimerHandle { id: id as u32 })
160 } else {
161 Err(id.into())
162 }
163}