perseus/utils/decode_time_str.rs
1#[cfg(engine)]
2mod engine {
3 use super::InvalidDuration;
4 use chrono::Utc;
5 use std::time;
6
7 /// Represents a duration that can be computed relative to the current time.
8 /// This should be created through [`PerseusDuration::into_computed`] only.
9 #[derive(Debug, Clone)]
10 pub struct ComputedDuration(chrono::Duration);
11
12 impl ComputedDuration {
13 /// Get the timestamp of the duration added to the current time.
14 pub fn compute_timestamp(&self) -> String {
15 let current = Utc::now();
16 let datetime = current + self.0;
17 datetime.to_rfc3339()
18 }
19 }
20
21 /// A trait that represents anything we'll accept for specifying durations
22 /// for revalidation. Anything that implements thus must be able to be
23 /// transformed into a [`ComputedDuration`];
24 pub trait PerseusDuration {
25 /// Converts this into a `[ComputedDuration]` for use in Perseus'
26 /// internal revalidation systems.
27 fn into_computed(self) -> Result<ComputedDuration, InvalidDuration>;
28 }
29
30 // We'll accept strings, and standard library durations.
31 // We don't accept Chrono durations because that would create a difference to
32 // the browser dummy API (since Chrono is only used on the engine side), and
33 // Chrono durations can be trivially converted into standard ones.
34 impl PerseusDuration for chrono::Duration {
35 fn into_computed(self) -> Result<ComputedDuration, InvalidDuration> {
36 Ok(ComputedDuration(self))
37 }
38 }
39 impl PerseusDuration for &str {
40 // NOTE: Logic to define how we parse time strings is here
41 fn into_computed(self) -> Result<ComputedDuration, InvalidDuration> {
42 let mut duration = chrono::Duration::zero();
43
44 // A working variable to store the '123' part of an interval until we reach the
45 // indicator and can do the full conversion
46 let mut curr_duration_length = String::new();
47
48 for c in self.chars() {
49 // If we have a number, append it to the working cache
50 // If we have an indicator character, we'll match it to a duration
51 if c.is_numeric() {
52 curr_duration_length.push(c);
53 } else {
54 let interval_length: i64 = curr_duration_length.parse().unwrap(); // It's just a string of numbers, we know more than the compiler
55 if interval_length <= 0 {
56 return Err(InvalidDuration);
57 }
58
59 match c {
60 's' => duration = duration + chrono::Duration::seconds(interval_length),
61 'm' => duration = duration + chrono::Duration::minutes(interval_length),
62 'h' => duration = duration + chrono::Duration::hours(interval_length),
63 'd' => duration = duration + chrono::Duration::days(interval_length),
64 'w' => duration = duration + chrono::Duration::weeks(interval_length),
65 'M' => duration = duration + chrono::Duration::days(interval_length * 30), /* Assumed length of a month */
66 'y' => duration = duration + chrono::Duration::days(interval_length * 365), /* Assumed length of a year */
67 _ => return Err(InvalidDuration),
68 };
69
70 curr_duration_length = String::new();
71 }
72 }
73
74 Ok(ComputedDuration(duration))
75 }
76 }
77 impl PerseusDuration for time::Duration {
78 fn into_computed(self) -> Result<ComputedDuration, InvalidDuration> {
79 let duration = chrono::Duration::from_std(self).map_err(|_| InvalidDuration)?;
80 Ok(ComputedDuration(duration))
81 }
82 }
83}
84#[cfg(client)]
85mod browser {
86 use super::InvalidDuration;
87 use std::time;
88
89 /// Represents a duration that can be computed relative to the current time.
90 /// This should be created through [`PerseusDuration::into_computed`] only.
91 #[derive(Debug, Clone)]
92 pub struct ComputedDuration;
93
94 /// A trait that represents anything we'll accept for specifying durations
95 /// for revalidation. Anything that implements thus must be able to be
96 /// transformed into a [`ComputedDuration`];
97 pub trait PerseusDuration {
98 /// Converts this into a `[ComputedDuration]` for use in Perseus'
99 /// internal revalidation systems.
100 fn into_computed(self) -> Result<ComputedDuration, InvalidDuration>
101 where
102 Self: Sized,
103 {
104 // In the browser, this function should never be called
105 unreachable!("computed durations can only be created on the engine-side")
106 }
107 }
108
109 // Dummy implementations for the browser follow (we only need this for generic
110 // validation)
111 impl PerseusDuration for &str {}
112 impl PerseusDuration for time::Duration {}
113}
114
115/// An error type for invalid durations.
116#[derive(Debug)]
117pub struct InvalidDuration;
118
119#[cfg(client)]
120pub use browser::{ComputedDuration, PerseusDuration};
121#[cfg(engine)]
122pub use engine::{ComputedDuration, PerseusDuration};
123
124// // We can convert from our duration type into the standard library's
125// impl From<Duration> for time::Duration {
126// fn from(duration: Duration) -> Self {
127// let duration = chrono::Duration::seconds(duration.seconds)
128// + chrono::Duration::minutes(duration.minutes)
129// + chrono::Duration::hours(duration.hours)
130// + chrono::Duration::days(duration.days)
131// + chrono::Duration::weeks(duration.weeks)
132// + chrono::Duration::days(duration.months * 30) // Assumed length
133// of a month + chrono::Duration::days(duration.years * 365); //
134// Assumed length of a year
135
136// duration.to_std().unwrap()
137// }
138// }