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
137#[must_use]
149#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
150pub fn parse_duration(s: &str) -> Option<chrono::Duration> {
151 let s = s.trim();
152 if s.is_empty() {
153 return None;
154 }
155 let (sign, rest) = match s.as_bytes()[0] {
157 b'-' => (-1i64, &s[1..]),
158 b'+' => (1i64, &s[1..]),
159 _ => (1i64, s),
160 };
161 let (num_str, unit) = if let Some(stripped) = rest.strip_suffix("ns") {
163 (stripped, "ns")
164 } else if let Some(stripped) = rest.strip_suffix("us") {
165 (stripped, "us")
166 } else if let Some(stripped) = rest.strip_suffix("µs") {
167 (stripped, "us")
168 } else if let Some(stripped) = rest.strip_suffix("ms") {
169 (stripped, "ms")
170 } else if let Some(stripped) = rest.strip_suffix('s') {
171 (stripped, "s")
172 } else if let Some(stripped) = rest.strip_suffix('m') {
173 (stripped, "m")
174 } else if let Some(stripped) = rest.strip_suffix('h') {
175 (stripped, "h")
176 } else {
177 return None;
178 };
179 let value: f64 = num_str.parse().ok()?;
180 let nanos_total: f64 = match unit {
181 "ns" => value,
182 "us" => value * 1_000.0,
183 "ms" => value * 1_000_000.0,
184 "s" => value * 1_000_000_000.0,
185 "m" => value * 60.0 * 1_000_000_000.0,
186 "h" => value * 3600.0 * 1_000_000_000.0,
187 _ => return None,
188 };
189 if !nanos_total.is_finite() {
190 return None;
191 }
192 let signed = (nanos_total as i64).checked_mul(sign)?;
193 Some(chrono::Duration::nanoseconds(signed))
194}
195
196#[must_use]
203pub fn parse_timestamp(s: &str) -> Option<chrono::DateTime<chrono::FixedOffset>> {
204 chrono::DateTime::parse_from_rfc3339(s).ok()
205}