rrule-rust 2.0.0-next.1 → 2.0.0-next.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,128 @@
1
+ use super::RRuleTimezone;
2
+ use chrono::DateTime;
3
+ use chrono::Datelike;
4
+ use chrono::TimeZone;
5
+ use chrono::Timelike;
6
+ use napi::bindgen_prelude::*;
7
+ use napi_derive::napi;
8
+ use std::str::FromStr;
9
+
10
+ #[napi(js_name = "RRuleDateTime")]
11
+ pub struct RRuleDateTime {
12
+ date_time: chrono::DateTime<rrule::Tz>,
13
+ }
14
+
15
+ #[napi]
16
+ impl RRuleDateTime {
17
+ #[napi(constructor)]
18
+ pub fn new_with_js_date(
19
+ date: napi::Either<napi::JsDate, f64>,
20
+ timezone: Option<String>,
21
+ ) -> napi::Result<Self> {
22
+ let timestamp = match date {
23
+ Either::A(date) => date.value_of(),
24
+ Either::B(date) => Ok(date),
25
+ }?;
26
+
27
+ let mut date_time = rrule::Tz::LOCAL.timestamp_nanos((timestamp * 1_000_000f64) as i64);
28
+ if let Some(timezone) = timezone {
29
+ let tz: rrule::Tz = RRuleTimezone::from_str(&timezone)?.into();
30
+
31
+ date_time = date_time.with_timezone(&tz);
32
+ }
33
+
34
+ Ok(RRuleDateTime::new_with_date_time(date_time))
35
+ }
36
+
37
+ pub fn new_with_date_time(date_time: DateTime<rrule::Tz>) -> Self {
38
+ RRuleDateTime { date_time }
39
+ }
40
+
41
+ #[napi(getter)]
42
+ pub fn timestamp(&self) -> i64 {
43
+ self.date_time.timestamp_millis()
44
+ }
45
+
46
+ #[napi(getter)]
47
+ pub fn timezone(&self) -> RRuleTimezone {
48
+ RRuleTimezone::new_with_tz(self.date_time.timezone())
49
+ }
50
+
51
+ #[napi(getter)]
52
+ pub fn day(&self) -> u32 {
53
+ self.date_time.day()
54
+ }
55
+
56
+ #[napi(getter)]
57
+ pub fn month(&self) -> u32 {
58
+ self.date_time.month()
59
+ }
60
+
61
+ #[napi(getter)]
62
+ pub fn year(&self) -> i32 {
63
+ self.date_time.year()
64
+ }
65
+
66
+ #[napi(getter)]
67
+ pub fn hour(&self) -> u32 {
68
+ self.date_time.hour()
69
+ }
70
+
71
+ #[napi(getter)]
72
+ pub fn minute(&self) -> u32 {
73
+ self.date_time.minute()
74
+ }
75
+
76
+ #[napi(getter)]
77
+ pub fn second(&self) -> u32 {
78
+ self.date_time.second()
79
+ }
80
+
81
+ #[napi(getter)]
82
+ pub fn millisecond(&self) -> u32 {
83
+ let nanoseconds = self.date_time.nanosecond();
84
+ nanoseconds / 1_000_000
85
+ }
86
+
87
+ #[napi(getter)]
88
+ pub fn to_string(&self) -> String {
89
+ self.date_time.to_string()
90
+ }
91
+
92
+ #[napi(ts_return_type = "Date")]
93
+ pub fn to_date(&self, env: Env) -> napi::Result<napi::JsDate> {
94
+ env.create_date(self.date_time.timestamp_millis() as f64)
95
+ }
96
+
97
+ #[napi(ts_return_type = "Date")]
98
+ pub fn to_utc_date(&self, env: Env) -> napi::Result<napi::JsDate> {
99
+ env.create_date(self.date_time.naive_utc().and_utc().timestamp_millis() as f64)
100
+ }
101
+ }
102
+
103
+ impl From<DateTime<rrule::Tz>> for RRuleDateTime {
104
+ fn from(date_time: DateTime<rrule::Tz>) -> Self {
105
+ RRuleDateTime::new_with_date_time(date_time)
106
+ }
107
+ }
108
+
109
+ impl Into<DateTime<rrule::Tz>> for RRuleDateTime {
110
+ fn into(self) -> DateTime<rrule::Tz> {
111
+ self.date_time
112
+ }
113
+ }
114
+
115
+ impl From<napi::JsDate> for RRuleDateTime {
116
+ fn from(date: napi::JsDate) -> Self {
117
+ RRuleDateTime::new_with_js_date(napi::Either::A(date), None).unwrap()
118
+ }
119
+ }
120
+
121
+ impl From<napi::Either<&RRuleDateTime, napi::JsDate>> for RRuleDateTime {
122
+ fn from(date: napi::Either<&RRuleDateTime, napi::JsDate>) -> Self {
123
+ match date {
124
+ Either::A(date) => RRuleDateTime::new_with_date_time(date.date_time),
125
+ Either::B(date) => RRuleDateTime::new_with_js_date(napi::Either::A(date), None).unwrap(),
126
+ }
127
+ }
128
+ }
@@ -0,0 +1,258 @@
1
+ use super::{RRule, RRuleDateTime};
2
+ use chrono::DateTime;
3
+ use napi::{
4
+ bindgen_prelude::{Reference, SharedReference},
5
+ iterator::Generator,
6
+ Env,
7
+ };
8
+ use napi_derive::napi;
9
+ use replace_with::replace_with_or_abort;
10
+
11
+ #[napi(js_name = "RRuleSet")]
12
+ pub struct RRuleSet {
13
+ rrule_set: rrule::RRuleSet,
14
+ }
15
+
16
+ #[napi]
17
+ impl RRuleSet {
18
+ #[napi(constructor)]
19
+ pub fn new(dtstart: napi::Either<&RRuleDateTime, napi::JsDate>) -> napi::Result<Self> {
20
+ let rrule_set = rrule::RRuleSet::new(RRuleDateTime::from(dtstart).into());
21
+
22
+ Ok(RRuleSet { rrule_set })
23
+ }
24
+
25
+ #[napi(factory, ts_return_type = "RRuleSet")]
26
+ pub fn parse(str: String) -> napi::Result<Self> {
27
+ let rrule_set: rrule::RRuleSet = str
28
+ .parse()
29
+ .map_err(|e| napi::Error::new(napi::Status::GenericFailure, e))?;
30
+
31
+ Ok(RRuleSet { rrule_set })
32
+ }
33
+
34
+ #[napi]
35
+ pub fn to_string(&self) -> napi::Result<String> {
36
+ Ok(self.rrule_set.to_string())
37
+ }
38
+
39
+ #[napi]
40
+ pub fn add_rrule(&mut self, js_rrule: &RRule) -> napi::Result<&Self> {
41
+ let dt_start = self.rrule_set.get_dt_start().clone();
42
+ let rrule = js_rrule.validate(dt_start)?;
43
+
44
+ replace_with_or_abort(&mut self.rrule_set, |self_| self_.rrule(rrule));
45
+
46
+ Ok(self)
47
+ }
48
+
49
+ #[napi]
50
+ pub fn add_exrule(&mut self, js_rrule: &RRule) -> napi::Result<&Self> {
51
+ let rrule = js_rrule.validate(*self.rrule_set.get_dt_start())?;
52
+
53
+ replace_with_or_abort(&mut self.rrule_set, |self_| self_.exrule(rrule));
54
+
55
+ Ok(self)
56
+ }
57
+
58
+ #[napi]
59
+ pub fn add_exdate(
60
+ &mut self,
61
+ date_time: napi::Either<&RRuleDateTime, napi::JsDate>,
62
+ ) -> napi::Result<&Self> {
63
+ replace_with_or_abort(&mut self.rrule_set, |self_| {
64
+ self_.exdate(RRuleDateTime::from(date_time).into())
65
+ });
66
+
67
+ Ok(self)
68
+ }
69
+
70
+ #[napi]
71
+ pub fn add_rdate(
72
+ &mut self,
73
+ date_time: napi::Either<&RRuleDateTime, napi::JsDate>,
74
+ ) -> napi::Result<&Self> {
75
+ replace_with_or_abort(&mut self.rrule_set, |self_| {
76
+ self_.rdate(RRuleDateTime::from(date_time).into())
77
+ });
78
+
79
+ Ok(self)
80
+ }
81
+
82
+ #[napi(getter)]
83
+ pub fn dtstart(&self) -> RRuleDateTime {
84
+ RRuleDateTime::new_with_date_time(self.rrule_set.get_dt_start().clone())
85
+ }
86
+
87
+ #[napi]
88
+ pub fn get_rrules(&self) -> Vec<RRule> {
89
+ return self
90
+ .rrule_set
91
+ .get_rrule()
92
+ .iter()
93
+ .map(|rrule| RRule::from(to_unvalidated(rrule)))
94
+ .collect();
95
+ }
96
+
97
+ #[napi]
98
+ pub fn get_exrules(&self) -> Vec<RRule> {
99
+ return self
100
+ .rrule_set
101
+ .get_exrule()
102
+ .iter()
103
+ .map(|rrule| RRule::from(to_unvalidated(rrule)))
104
+ .collect();
105
+ }
106
+
107
+ #[napi]
108
+ pub fn get_exdates(&self) -> Vec<RRuleDateTime> {
109
+ return self
110
+ .rrule_set
111
+ .get_exdate()
112
+ .iter()
113
+ .map(|date| RRuleDateTime::new_with_date_time(date.clone()))
114
+ .collect();
115
+ }
116
+
117
+ #[napi]
118
+ pub fn get_rdates(&self) -> Vec<RRuleDateTime> {
119
+ return self
120
+ .rrule_set
121
+ .get_rdate()
122
+ .iter()
123
+ .map(|date| RRuleDateTime::new_with_date_time(date.clone()))
124
+ .collect();
125
+ }
126
+
127
+ fn is_after(
128
+ &self,
129
+ timestamp: DateTime<rrule::Tz>,
130
+ after_timestamp: DateTime<rrule::Tz>,
131
+ inclusive: Option<bool>,
132
+ ) -> bool {
133
+ let inclusive = inclusive.unwrap_or(false);
134
+
135
+ if inclusive && timestamp < after_timestamp {
136
+ return false;
137
+ } else if !inclusive && timestamp <= after_timestamp {
138
+ return false;
139
+ }
140
+
141
+ true
142
+ }
143
+
144
+ fn is_before(
145
+ &self,
146
+ timestamp: DateTime<rrule::Tz>,
147
+ before_timestamp: DateTime<rrule::Tz>,
148
+ inclusive: Option<bool>,
149
+ ) -> bool {
150
+ let inclusive = inclusive.unwrap_or(false);
151
+
152
+ if inclusive && timestamp > before_timestamp {
153
+ return false;
154
+ } else if !inclusive && timestamp >= before_timestamp {
155
+ return false;
156
+ }
157
+
158
+ true
159
+ }
160
+
161
+ #[napi]
162
+ pub fn all(&self, limit: Option<u32>) -> Vec<RRuleDateTime> {
163
+ let iter = self.rrule_set.into_iter();
164
+ if let Some(limit) = limit {
165
+ return iter
166
+ .take(limit as usize)
167
+ .map(|date| RRuleDateTime::new_with_date_time(date))
168
+ .collect();
169
+ } else {
170
+ return iter
171
+ .map(|date| RRuleDateTime::new_with_date_time(date))
172
+ .collect();
173
+ }
174
+ }
175
+
176
+ #[napi]
177
+ pub fn between(
178
+ &self,
179
+ after: napi::Either<&RRuleDateTime, napi::JsDate>,
180
+ before: napi::Either<&RRuleDateTime, napi::JsDate>,
181
+ inclusive: Option<bool>,
182
+ ) -> napi::Result<Vec<RRuleDateTime>> {
183
+ let _after = RRuleDateTime::from(after).into();
184
+ let _before = RRuleDateTime::from(before).into();
185
+ return Ok(
186
+ self
187
+ .rrule_set
188
+ .into_iter()
189
+ .take_while(|date| {
190
+ let is_before = self.is_before(*date, _before, inclusive);
191
+
192
+ is_before
193
+ })
194
+ .filter(|date| {
195
+ let is_after = self.is_after(*date, _after, inclusive);
196
+
197
+ is_after
198
+ })
199
+ .map(|date| RRuleDateTime::new_with_date_time(date))
200
+ .collect::<Vec<_>>(),
201
+ );
202
+ }
203
+
204
+ #[napi]
205
+ pub fn occurrences(&self, this: Reference<RRuleSet>, env: Env) -> napi::Result<Occurrences> {
206
+ let iter = this.share_with(env, |set| Ok(set.rrule_set.into_iter()))?;
207
+ Ok(Occurrences { iter })
208
+ }
209
+ }
210
+
211
+ #[napi(iterator)]
212
+ pub struct Occurrences {
213
+ iter: SharedReference<RRuleSet, rrule::RRuleSetIter<'static>>,
214
+ }
215
+
216
+ #[napi]
217
+ impl Generator for Occurrences {
218
+ type Yield = RRuleDateTime;
219
+ type Next = ();
220
+ type Return = ();
221
+
222
+ fn next(&mut self, _next: Option<Self::Next>) -> Option<Self::Yield> {
223
+ self
224
+ .iter
225
+ .next()
226
+ .map(|date| RRuleDateTime::new_with_date_time(date))
227
+ }
228
+ }
229
+
230
+ fn to_unvalidated(rrule: &rrule::RRule) -> rrule::RRule<rrule::Unvalidated> {
231
+ let by_month = rrule
232
+ .get_by_month()
233
+ .iter()
234
+ .map(|m| chrono::Month::try_from(*m).unwrap())
235
+ .collect::<Vec<_>>();
236
+ let mut unvalidated = rrule::RRule::new(rrule.get_freq())
237
+ .interval(rrule.get_interval())
238
+ .week_start(rrule.get_week_start())
239
+ .by_set_pos(rrule.get_by_set_pos().to_vec())
240
+ .by_month(&by_month)
241
+ .by_month_day(rrule.get_by_month_day().to_vec())
242
+ .by_year_day(rrule.get_by_year_day().to_vec())
243
+ .by_week_no(rrule.get_by_week_no().to_vec())
244
+ .by_weekday(rrule.get_by_weekday().to_vec())
245
+ .by_hour(rrule.get_by_hour().to_vec())
246
+ .by_minute(rrule.get_by_minute().to_vec())
247
+ .by_second(rrule.get_by_second().to_vec());
248
+
249
+ if let Some(count) = rrule.get_count() {
250
+ unvalidated = unvalidated.count(count);
251
+ }
252
+
253
+ if let Some(until) = rrule.get_until() {
254
+ unvalidated = unvalidated.until(*until);
255
+ }
256
+
257
+ unvalidated
258
+ }
@@ -0,0 +1,52 @@
1
+ use napi_derive::napi;
2
+ use std::str::FromStr;
3
+
4
+ #[derive(Debug)]
5
+ #[napi(js_name = "RRuleTimezone")]
6
+ pub struct RRuleTimezone {
7
+ tz: rrule::Tz,
8
+ }
9
+
10
+ #[napi]
11
+ impl RRuleTimezone {
12
+ #[napi(constructor)]
13
+ pub fn new(tz: String) -> napi::Result<Self> {
14
+ Ok(tz.parse()?)
15
+ }
16
+
17
+ pub fn new_with_tz(tz: rrule::Tz) -> Self {
18
+ RRuleTimezone { tz }
19
+ }
20
+
21
+ /**
22
+ * The name of the timezone. If the timezone is local, it will return "Local".
23
+ */
24
+ #[napi(getter)]
25
+ pub fn name(&self) -> String {
26
+ self.tz.name().to_string()
27
+ }
28
+
29
+ #[napi(getter)]
30
+ pub fn is_local(&self) -> bool {
31
+ self.tz.is_local()
32
+ }
33
+ }
34
+
35
+ impl Into<rrule::Tz> for RRuleTimezone {
36
+ fn into(self) -> rrule::Tz {
37
+ self.tz
38
+ }
39
+ }
40
+
41
+ impl FromStr for RRuleTimezone {
42
+ type Err = napi::Error;
43
+
44
+ fn from_str(str: &str) -> Result<Self, Self::Err> {
45
+ str
46
+ .parse::<chrono_tz::Tz>()
47
+ .map(|tz| RRuleTimezone {
48
+ tz: rrule::Tz::Tz(tz),
49
+ })
50
+ .map_err(|err| napi::Error::new(napi::Status::GenericFailure, err.to_string()))
51
+ }
52
+ }
@@ -0,0 +1,40 @@
1
+ use napi_derive::napi;
2
+
3
+ #[napi(js_name = "Weekday")]
4
+ pub enum Weekday {
5
+ Monday,
6
+ Tuesday,
7
+ Wednesday,
8
+ Thursday,
9
+ Friday,
10
+ Saturday,
11
+ Sunday,
12
+ }
13
+
14
+ impl From<rrule::Weekday> for Weekday {
15
+ fn from(weekday: rrule::Weekday) -> Self {
16
+ match weekday {
17
+ rrule::Weekday::Mon => Weekday::Monday,
18
+ rrule::Weekday::Tue => Weekday::Tuesday,
19
+ rrule::Weekday::Wed => Weekday::Wednesday,
20
+ rrule::Weekday::Thu => Weekday::Thursday,
21
+ rrule::Weekday::Fri => Weekday::Friday,
22
+ rrule::Weekday::Sat => Weekday::Saturday,
23
+ rrule::Weekday::Sun => Weekday::Sunday,
24
+ }
25
+ }
26
+ }
27
+
28
+ impl Into<rrule::Weekday> for Weekday {
29
+ fn into(self) -> rrule::Weekday {
30
+ match self {
31
+ Weekday::Monday => rrule::Weekday::Mon,
32
+ Weekday::Tuesday => rrule::Weekday::Tue,
33
+ Weekday::Wednesday => rrule::Weekday::Wed,
34
+ Weekday::Thursday => rrule::Weekday::Thu,
35
+ Weekday::Friday => rrule::Weekday::Fri,
36
+ Weekday::Saturday => rrule::Weekday::Sat,
37
+ Weekday::Sunday => rrule::Weekday::Sun,
38
+ }
39
+ }
40
+ }
package/src/js.rs ADDED
@@ -0,0 +1,17 @@
1
+ mod frequency;
2
+ mod month;
3
+ mod n_weekday;
4
+ mod rrule;
5
+ mod rrule_datetime;
6
+ mod rrule_set;
7
+ mod rrule_timezone;
8
+ mod weekday;
9
+
10
+ pub use frequency::Frequency;
11
+ pub use month::Month;
12
+ pub use n_weekday::NWeekday;
13
+ pub use rrule::RRule;
14
+ pub use rrule_datetime::RRuleDateTime;
15
+ pub use rrule_set::RRuleSet;
16
+ pub use rrule_timezone::RRuleTimezone;
17
+ pub use weekday::Weekday;