protovalidate_buffa/
cel.rs1#[must_use]
15pub fn now_local() -> chrono::DateTime<chrono::FixedOffset> {
16 chrono::Utc::now().fixed_offset()
17}
18
19pub trait CelScalar: Copy {
27 fn cel_int(self) -> i64;
30 fn cel_uint(self) -> u64;
33 fn cel_double(self) -> f64;
35}
36
37macro_rules! impl_cel_scalar_int {
38 ($($t:ty),*) => {
39 $(
40 impl CelScalar for $t {
41 #[inline]
42 #[allow(clippy::cast_possible_wrap, clippy::cast_sign_loss, clippy::cast_lossless, clippy::cast_precision_loss)]
43 fn cel_int(self) -> i64 { self as i64 }
44 #[inline]
45 #[allow(clippy::cast_sign_loss, clippy::cast_lossless)]
46 fn cel_uint(self) -> u64 { self as u64 }
47 #[inline]
48 #[allow(clippy::cast_lossless, clippy::cast_precision_loss)]
49 fn cel_double(self) -> f64 { self as f64 }
50 }
51 )*
52 };
53}
54impl_cel_scalar_int!(i32, i64, u32, u64);
55
56impl CelScalar for f32 {
57 #[inline]
58 #[allow(clippy::cast_possible_truncation)]
59 fn cel_int(self) -> i64 {
60 self as i64
61 }
62 #[inline]
63 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
64 fn cel_uint(self) -> u64 {
65 self as u64
66 }
67 #[inline]
68 fn cel_double(self) -> f64 {
69 f64::from(self)
70 }
71}
72
73impl CelScalar for f64 {
74 #[inline]
75 #[allow(clippy::cast_possible_truncation)]
76 fn cel_int(self) -> i64 {
77 self as i64
78 }
79 #[inline]
80 #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
81 fn cel_uint(self) -> u64 {
82 self as u64
83 }
84 #[inline]
85 fn cel_double(self) -> f64 {
86 self
87 }
88}
89
90impl<E: buffa::Enumeration + Copy> CelScalar for buffa::EnumValue<E> {
91 #[inline]
92 fn cel_int(self) -> i64 {
93 i64::from(self.to_i32())
94 }
95 #[inline]
96 #[allow(clippy::cast_sign_loss)]
97 fn cel_uint(self) -> u64 {
98 self.to_i32() as u64
99 }
100 #[inline]
101 fn cel_double(self) -> f64 {
102 f64::from(self.to_i32())
103 }
104}
105
106#[must_use]
114pub fn duration_from_secs_nanos(seconds: i64, nanos: i32) -> chrono::Duration {
115 chrono::Duration::seconds(seconds) + chrono::Duration::nanoseconds(i64::from(nanos))
116}
117
118#[must_use]
127pub fn timestamp_from_secs_nanos(
128 seconds: i64,
129 nanos: i32,
130) -> chrono::DateTime<chrono::FixedOffset> {
131 let nanos_u32 = u32::try_from(nanos.max(0)).unwrap_or(0);
132 let s = chrono::DateTime::<chrono::Utc>::from_timestamp(seconds, nanos_u32)
133 .unwrap_or_else(|| chrono::DateTime::<chrono::Utc>::from_timestamp(0, 0).unwrap());
134 s.fixed_offset()
135}
136
137fn split_unit(rest: &str) -> Option<(&str, &'static str)> {
142 const SUFFIXES: [(&str, &str); 7] = [
143 ("ns", "ns"),
144 ("us", "us"),
145 ("µs", "us"),
146 ("ms", "ms"),
147 ("s", "s"),
148 ("m", "m"),
149 ("h", "h"),
150 ];
151 SUFFIXES
152 .into_iter()
153 .find_map(|(suffix, unit)| rest.strip_suffix(suffix).map(|stripped| (stripped, unit)))
154}
155
156#[must_use]
168#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
169pub fn parse_duration(s: &str) -> Option<chrono::Duration> {
170 let s = s.trim();
171 if s.is_empty() {
172 return None;
173 }
174 let (sign, rest) = match s.as_bytes()[0] {
176 b'-' => (-1i64, &s[1..]),
177 b'+' => (1i64, &s[1..]),
178 _ => (1i64, s),
179 };
180 let (num_str, unit) = split_unit(rest)?;
182 let value: f64 = num_str.parse().ok()?;
183 let nanos_total: f64 = match unit {
184 "ns" => value,
185 "us" => value * 1_000.0,
186 "ms" => value * 1_000_000.0,
187 "s" => value * 1_000_000_000.0,
188 "m" => value * 60.0 * 1_000_000_000.0,
189 "h" => value * 3600.0 * 1_000_000_000.0,
190 _ => return None,
191 };
192 if !nanos_total.is_finite() {
193 return None;
194 }
195 let signed = (nanos_total as i64).checked_mul(sign)?;
196 Some(chrono::Duration::nanoseconds(signed))
197}
198
199#[must_use]
206pub fn parse_timestamp(s: &str) -> Option<chrono::DateTime<chrono::FixedOffset>> {
207 chrono::DateTime::parse_from_rfc3339(s).ok()
208}