1use crate::error::ffi::TemporalError;
2
3#[diplomat::bridge]
4#[diplomat::abi_rename = "temporal_rs_{0}"]
5pub mod ffi {
6 use crate::error::ffi::TemporalError;
7 use crate::options::ffi::ToStringRoundingOptions;
8 use crate::options::ffi::{RoundingOptions, Unit};
9 use crate::provider::ffi::Provider;
10 use crate::zoned_date_time::ffi::RelativeTo;
11 use alloc::boxed::Box;
12 use alloc::string::String;
13 use core::str::FromStr;
14 use diplomat_runtime::DiplomatOption;
15 use diplomat_runtime::{DiplomatStr, DiplomatStr16};
16 use num_traits::FromPrimitive;
17
18 #[diplomat::opaque]
19 pub struct Duration(pub(crate) temporal_rs::Duration);
20
21 #[diplomat::opaque]
22 #[diplomat::transparent_convert]
23 pub struct DateDuration(pub(crate) temporal_rs::duration::DateDuration);
24
25 pub struct PartialDuration {
26 pub years: DiplomatOption<i64>,
27 pub months: DiplomatOption<i64>,
28 pub weeks: DiplomatOption<i64>,
29 pub days: DiplomatOption<i64>,
30 pub hours: DiplomatOption<i64>,
31 pub minutes: DiplomatOption<i64>,
32 pub seconds: DiplomatOption<i64>,
33 pub milliseconds: DiplomatOption<i64>,
34 pub microseconds: DiplomatOption<f64>,
35 pub nanoseconds: DiplomatOption<f64>,
36 }
37
38 #[diplomat::enum_convert(temporal_rs::Sign)]
39 pub enum Sign {
40 Positive = 1,
41 Zero = 0,
42 Negative = -1,
43 }
44
45 impl PartialDuration {
46 pub fn is_empty(self) -> bool {
47 temporal_rs::partial::PartialDuration::try_from(self)
48 .map(|p| p.is_empty())
49 .unwrap_or(false)
50 }
51 }
52
53 impl DateDuration {
54 pub fn try_new(
55 years: i64,
56 months: i64,
57 weeks: i64,
58 days: i64,
59 ) -> Result<Box<Self>, TemporalError> {
60 temporal_rs::duration::DateDuration::new(years, months, weeks, days)
61 .map(|x| Box::new(DateDuration(x)))
62 .map_err(Into::into)
63 }
64
65 pub fn abs(&self) -> Box<Self> {
66 Box::new(Self(self.0.abs()))
67 }
68 pub fn negated(&self) -> Box<Self> {
69 Box::new(Self(self.0.negated()))
70 }
71
72 pub fn sign(&self) -> Sign {
73 self.0.sign().into()
74 }
75 }
76 impl Duration {
77 pub fn create(
79 years: i64,
80 months: i64,
81 weeks: i64,
82 days: i64,
83 hours: i64,
84 minutes: i64,
85 seconds: i64,
86 milliseconds: i64,
87 microseconds: f64,
88 nanoseconds: f64,
89 ) -> Result<Box<Self>, TemporalError> {
90 Self::try_new(
91 years,
92 months,
93 weeks,
94 days,
95 hours,
96 minutes,
97 seconds,
98 milliseconds,
99 microseconds,
100 nanoseconds,
101 )
102 }
103
104 pub fn try_new(
105 years: i64,
106 months: i64,
107 weeks: i64,
108 days: i64,
109 hours: i64,
110 minutes: i64,
111 seconds: i64,
112 milliseconds: i64,
113 microseconds: f64,
114 nanoseconds: f64,
115 ) -> Result<Box<Self>, TemporalError> {
116 temporal_rs::Duration::new(
117 years,
118 months,
119 weeks,
120 days,
121 hours,
122 minutes,
123 seconds,
124 milliseconds,
125 i128::from_f64(microseconds).ok_or(TemporalError::range("μs out of range"))?,
126 i128::from_f64(nanoseconds).ok_or(TemporalError::range("ms out of range"))?,
127 )
128 .map(|x| Box::new(Duration(x)))
129 .map_err(Into::into)
130 }
131
132 pub fn from_partial_duration(partial: PartialDuration) -> Result<Box<Self>, TemporalError> {
133 temporal_rs::Duration::from_partial_duration(partial.try_into()?)
134 .map(|x| Box::new(Duration(x)))
135 .map_err(Into::into)
136 }
137
138 pub fn from_utf8(s: &DiplomatStr) -> Result<Box<Self>, TemporalError> {
139 temporal_rs::Duration::from_utf8(s)
140 .map(|c| Box::new(Self(c)))
141 .map_err(Into::into)
142 }
143
144 pub fn from_utf16(s: &DiplomatStr16) -> Result<Box<Self>, TemporalError> {
145 let s = String::from_utf16(s).map_err(|_| temporal_rs::TemporalError::range())?;
147 temporal_rs::Duration::from_str(&s)
148 .map(|c| Box::new(Self(c)))
149 .map_err(Into::into)
150 }
151
152 pub fn is_time_within_range(&self) -> bool {
153 self.0.is_time_within_range()
154 }
155
156 pub fn years(&self) -> i64 {
160 self.0.years()
161 }
162 pub fn months(&self) -> i64 {
163 self.0.months()
164 }
165 pub fn weeks(&self) -> i64 {
166 self.0.weeks()
167 }
168 pub fn days(&self) -> i64 {
169 self.0.days()
170 }
171 pub fn hours(&self) -> i64 {
172 self.0.hours()
173 }
174 pub fn minutes(&self) -> i64 {
175 self.0.minutes()
176 }
177 pub fn seconds(&self) -> i64 {
178 self.0.seconds()
179 }
180 pub fn milliseconds(&self) -> i64 {
181 self.0.milliseconds()
182 }
183 pub fn microseconds(&self) -> f64 {
184 f64::from_i128(self.0.microseconds()).unwrap_or(0.)
189 }
190 pub fn nanoseconds(&self) -> f64 {
191 f64::from_i128(self.0.nanoseconds()).unwrap_or(0.)
196 }
197
198 pub fn sign(&self) -> Sign {
199 self.0.sign().into()
200 }
201
202 pub fn is_zero(&self) -> bool {
203 self.0.is_zero()
204 }
205
206 pub fn abs(&self) -> Box<Self> {
207 Box::new(Self(self.0.abs()))
208 }
209 pub fn negated(&self) -> Box<Self> {
210 Box::new(Self(self.0.negated()))
211 }
212
213 pub fn add(&self, other: &Self) -> Result<Box<Self>, TemporalError> {
214 self.0
215 .add(&other.0)
216 .map(|x| Box::new(Duration(x)))
217 .map_err(Into::into)
218 }
219
220 pub fn subtract(&self, other: &Self) -> Result<Box<Self>, TemporalError> {
221 self.0
222 .subtract(&other.0)
223 .map(|x| Box::new(Duration(x)))
224 .map_err(Into::into)
225 }
226
227 pub fn to_string(
228 &self,
229 options: ToStringRoundingOptions,
230 write: &mut DiplomatWrite,
231 ) -> Result<(), TemporalError> {
232 use core::fmt::Write;
233 let string = self.0.as_temporal_string(options.into())?;
234 let _ = write.write_str(&string);
236
237 Ok(())
238 }
239
240 #[cfg(feature = "compiled_data")]
241 pub fn round(
242 &self,
243 options: RoundingOptions,
244 relative_to: RelativeTo,
245 ) -> Result<Box<Self>, TemporalError> {
246 self.round_with_provider(options, relative_to, &Provider::compiled())
247 }
248 pub fn round_with_provider<'p>(
249 &self,
250 options: RoundingOptions,
251 relative_to: RelativeTo,
252 p: &Provider<'p>,
253 ) -> Result<Box<Self>, TemporalError> {
254 with_provider!(p, |p| self.0.round_with_provider(
255 options.try_into()?,
256 relative_to.into(),
257 p
258 ))
259 .map(|x| Box::new(Duration(x)))
260 .map_err(Into::into)
261 }
262
263 #[cfg(feature = "compiled_data")]
264 pub fn compare(&self, other: &Self, relative_to: RelativeTo) -> Result<i8, TemporalError> {
265 self.compare_with_provider(other, relative_to, &Provider::compiled())
266 }
267 pub fn compare_with_provider<'p>(
268 &self,
269 other: &Self,
270 relative_to: RelativeTo,
271 p: &Provider<'p>,
272 ) -> Result<i8, TemporalError> {
273 with_provider!(p, |p| self.0.compare_with_provider(
276 &other.0,
277 relative_to.into(),
278 p
279 ))
280 .map(|x| x as i8)
281 .map_err(Into::into)
282 }
283
284 #[cfg(feature = "compiled_data")]
285 pub fn total(&self, unit: Unit, relative_to: RelativeTo) -> Result<f64, TemporalError> {
286 self.total_with_provider(unit, relative_to, &Provider::compiled())
287 }
288 pub fn total_with_provider<'p>(
289 &self,
290 unit: Unit,
291 relative_to: RelativeTo,
292 p: &Provider<'p>,
293 ) -> Result<f64, TemporalError> {
294 with_provider!(p, |p| self.0.total_with_provider(
295 unit.into(),
296 relative_to.into(),
297 p
298 ))
299 .map(|x| x.as_inner())
300 .map_err(Into::into)
301 }
302
303 #[allow(clippy::should_implement_trait)]
304 pub fn clone(&self) -> Box<Self> {
305 Box::new(Self(self.0))
306 }
307 }
308}
309
310impl TryFrom<ffi::PartialDuration> for temporal_rs::partial::PartialDuration {
311 type Error = TemporalError;
312 fn try_from(other: ffi::PartialDuration) -> Result<Self, TemporalError> {
313 use num_traits::FromPrimitive;
314 Ok(Self {
315 years: other.years.into_option(),
316 months: other.months.into_option(),
317 weeks: other.weeks.into_option(),
318 days: other.days.into_option(),
319 hours: other.hours.into_option(),
320 minutes: other.minutes.into_option(),
321 seconds: other.seconds.into_option(),
322 milliseconds: other.milliseconds.into_option(),
323 microseconds: other
324 .microseconds
325 .into_option()
326 .map(|v| i128::from_f64(v).ok_or(TemporalError::range("μs out of range")))
327 .transpose()?,
328 nanoseconds: other
329 .nanoseconds
330 .into_option()
331 .map(|v| i128::from_f64(v).ok_or(TemporalError::range("ns out of range")))
332 .transpose()?,
333 })
334 }
335}