1#![cfg_attr(feature = "docs", doc = "## Feature flags")]
4#![cfg_attr(feature = "docs", doc = document_features::document_features!())]
5#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
12#![deny(missing_docs)]
13#![deny(unsafe_code)]
14#![deny(unreachable_pub)]
15#![deny(clippy::mod_module_files)]
16#![doc(hidden)]
17
18use std::borrow::Cow;
19use std::collections::{BTreeMap, HashMap};
20use std::hash::Hash;
21use std::sync::Arc;
22
23use bytes::Bytes;
24use float_cmp::ApproxEq;
25use num_traits::ToPrimitive;
26
27#[derive(Debug, thiserror::Error, PartialEq)]
28pub enum CelError<'a> {
29 #[error("index out of bounds: {0} is out of range for a list of length {1}")]
30 IndexOutOfBounds(usize, usize),
31 #[error("invalid type for indexing: {0}")]
32 IndexWithBadIndex(CelValue<'a>),
33 #[error("map key not found: {0:?}")]
34 MapKeyNotFound(CelValue<'a>),
35 #[error("bad operation: {left} {op} {right}")]
36 BadOperation {
37 left: CelValue<'a>,
38 right: CelValue<'a>,
39 op: &'static str,
40 },
41 #[error("bad unary operation: {op}{value}")]
42 BadUnaryOperation {
43 op: &'static str,
44 value: CelValue<'a>,
45 },
46 #[error("number out of range when performing {op}")]
47 NumberOutOfRange {
48 op: &'static str,
49 },
50 #[error("bad access when trying to member {member} on {container}")]
51 BadAccess {
52 member: CelValue<'a>,
53 container: CelValue<'a>,
54 },
55}
56
57#[derive(Clone, Debug)]
58pub enum CelString<'a> {
59 Owned(Arc<str>),
60 Borrowed(&'a str),
61}
62
63impl PartialEq for CelString<'_> {
64 fn eq(&self, other: &Self) -> bool {
65 self.as_ref() == other.as_ref()
66 }
67}
68
69impl Eq for CelString<'_> {}
70
71impl<'a> From<&'a str> for CelString<'a> {
72 fn from(value: &'a str) -> Self {
73 CelString::Borrowed(value)
74 }
75}
76
77impl From<String> for CelString<'_> {
78 fn from(value: String) -> Self {
79 CelString::Owned(value.into())
80 }
81}
82
83impl<'a> From<&'a String> for CelString<'a> {
84 fn from(value: &'a String) -> Self {
85 CelString::Borrowed(value.as_str())
86 }
87}
88
89impl From<&Arc<str>> for CelString<'static> {
90 fn from(value: &Arc<str>) -> Self {
91 CelString::Owned(value.clone())
92 }
93}
94
95impl From<Arc<str>> for CelString<'static> {
96 fn from(value: Arc<str>) -> Self {
97 CelString::Owned(value)
98 }
99}
100
101impl AsRef<str> for CelString<'_> {
102 fn as_ref(&self) -> &str {
103 match self {
104 Self::Borrowed(s) => s,
105 Self::Owned(s) => s,
106 }
107 }
108}
109
110impl std::ops::Deref for CelString<'_> {
111 type Target = str;
112
113 fn deref(&self) -> &Self::Target {
114 self.as_ref()
115 }
116}
117
118#[derive(Clone, Debug)]
119pub enum CelBytes<'a> {
120 Owned(Bytes),
121 Borrowed(&'a [u8]),
122}
123
124impl PartialEq for CelBytes<'_> {
125 fn eq(&self, other: &Self) -> bool {
126 self.as_ref() == other.as_ref()
127 }
128}
129
130impl Eq for CelBytes<'_> {}
131
132impl<'a> From<&'a [u8]> for CelBytes<'a> {
133 fn from(value: &'a [u8]) -> Self {
134 CelBytes::Borrowed(value)
135 }
136}
137
138impl From<Bytes> for CelBytes<'_> {
139 fn from(value: Bytes) -> Self {
140 CelBytes::Owned(value)
141 }
142}
143
144impl From<&Bytes> for CelBytes<'_> {
145 fn from(value: &Bytes) -> Self {
146 CelBytes::Owned(value.clone())
147 }
148}
149
150impl From<Vec<u8>> for CelBytes<'static> {
151 fn from(value: Vec<u8>) -> Self {
152 CelBytes::Owned(value.into())
153 }
154}
155
156impl<'a> From<&'a Vec<u8>> for CelBytes<'a> {
157 fn from(value: &'a Vec<u8>) -> Self {
158 CelBytes::Borrowed(value.as_slice())
159 }
160}
161
162impl AsRef<[u8]> for CelBytes<'_> {
163 fn as_ref(&self) -> &[u8] {
164 match self {
165 Self::Borrowed(s) => s,
166 Self::Owned(s) => s,
167 }
168 }
169}
170
171#[derive(Clone, Debug)]
172pub enum CelValue<'a> {
173 Bool(bool),
174 Number(NumberTy),
175 String(CelString<'a>),
176 Bytes(CelBytes<'a>),
177 List(Arc<[CelValue<'a>]>),
178 Map(Arc<[(CelValue<'a>, CelValue<'a>)]>),
179 Duration(chrono::Duration),
180 Timestamp(chrono::DateTime<chrono::FixedOffset>),
181 Enum(CelEnum<'a>),
182 Null,
183}
184
185impl PartialOrd for CelValue<'_> {
186 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
187 match (self, other) {
188 (CelValue::Number(l), CelValue::Number(r)) => l.partial_cmp(r),
189 (CelValue::String(_) | CelValue::Bytes(_), CelValue::String(_) | CelValue::Bytes(_)) => {
190 let l = match self {
191 CelValue::String(s) => s.as_ref().as_bytes(),
192 CelValue::Bytes(b) => b.as_ref(),
193 _ => unreachable!(),
194 };
195
196 let r = match other {
197 CelValue::String(s) => s.as_ref().as_bytes(),
198 CelValue::Bytes(b) => b.as_ref(),
199 _ => unreachable!(),
200 };
201
202 Some(l.cmp(r))
203 }
204 _ => None,
205 }
206 }
207}
208
209impl<'a> CelValue<'a> {
210 pub fn cel_access<'b>(container: impl CelValueConv<'a>, key: impl CelValueConv<'b>) -> Result<CelValue<'a>, CelError<'b>>
211 where
212 'a: 'b,
213 {
214 let key = key.conv();
215 match container.conv() {
216 CelValue::Map(map) => map
217 .iter()
218 .find(|(k, _)| k == &key)
219 .map(|(_, v)| v.clone())
220 .ok_or(CelError::MapKeyNotFound(key)),
221 CelValue::List(list) => {
222 if let Some(idx) = key.as_number().and_then(|n| n.to_usize()) {
223 list.get(idx).cloned().ok_or(CelError::IndexOutOfBounds(idx, list.len()))
224 } else {
225 Err(CelError::IndexWithBadIndex(key))
226 }
227 }
228 v => Err(CelError::BadAccess {
229 member: key,
230 container: v,
231 }),
232 }
233 }
234
235 pub fn cel_add(left: impl CelValueConv<'a>, right: impl CelValueConv<'a>) -> Result<CelValue<'a>, CelError<'a>> {
236 match (left.conv(), right.conv()) {
237 (CelValue::Number(l), CelValue::Number(r)) => Ok(CelValue::Number(l.cel_add(r)?)),
238 (CelValue::String(l), CelValue::String(r)) => Ok(CelValue::String(CelString::Owned(Arc::from(format!(
239 "{}{}",
240 l.as_ref(),
241 r.as_ref()
242 ))))),
243 (CelValue::Bytes(l), CelValue::Bytes(r)) => Ok(CelValue::Bytes(CelBytes::Owned({
244 let mut l = l.as_ref().to_vec();
245 l.extend_from_slice(r.as_ref());
246 Bytes::from(l)
247 }))),
248 (CelValue::List(l), CelValue::List(r)) => Ok(CelValue::List(l.iter().chain(r.iter()).cloned().collect())),
249 (CelValue::Map(l), CelValue::Map(r)) => Ok(CelValue::Map(l.iter().chain(r.iter()).cloned().collect())),
250 (left, right) => Err(CelError::BadOperation { left, right, op: "+" }),
251 }
252 }
253
254 pub fn cel_sub(left: impl CelValueConv<'a>, right: impl CelValueConv<'a>) -> Result<CelValue<'static>, CelError<'a>> {
255 match (left.conv(), right.conv()) {
256 (CelValue::Number(l), CelValue::Number(r)) => Ok(CelValue::Number(l.cel_sub(r)?)),
257 (left, right) => Err(CelError::BadOperation { left, right, op: "-" }),
258 }
259 }
260
261 pub fn cel_mul(left: impl CelValueConv<'a>, right: impl CelValueConv<'a>) -> Result<CelValue<'static>, CelError<'a>> {
262 match (left.conv(), right.conv()) {
263 (CelValue::Number(l), CelValue::Number(r)) => Ok(CelValue::Number(l.cel_mul(r)?)),
264 (left, right) => Err(CelError::BadOperation { left, right, op: "*" }),
265 }
266 }
267
268 pub fn cel_div(left: impl CelValueConv<'a>, right: impl CelValueConv<'a>) -> Result<CelValue<'static>, CelError<'a>> {
269 match (left.conv(), right.conv()) {
270 (CelValue::Number(l), CelValue::Number(r)) => Ok(CelValue::Number(l.cel_div(r)?)),
271 (left, right) => Err(CelError::BadOperation { left, right, op: "/" }),
272 }
273 }
274
275 pub fn cel_rem(left: impl CelValueConv<'a>, right: impl CelValueConv<'a>) -> Result<CelValue<'static>, CelError<'a>> {
276 match (left.conv(), right.conv()) {
277 (CelValue::Number(l), CelValue::Number(r)) => Ok(CelValue::Number(l.cel_rem(r)?)),
278 (left, right) => Err(CelError::BadOperation { left, right, op: "%" }),
279 }
280 }
281
282 fn as_number(&self) -> Option<NumberTy> {
283 match self {
284 CelValue::Number(n) => Some(*n),
285 _ => None,
286 }
287 }
288
289 pub fn cel_neg(input: impl CelValueConv<'a>) -> Result<CelValue<'static>, CelError<'a>> {
291 match input.conv() {
292 CelValue::Number(n) => Ok(CelValue::Number(n.cel_neg()?)),
293 value => Err(CelError::BadUnaryOperation { value, op: "-" }),
294 }
295 }
296
297 pub fn cel_lt(left: impl CelValueConv<'a>, right: impl CelValueConv<'a>) -> Result<bool, CelError<'a>> {
299 let left = left.conv();
300 let right = right.conv();
301 left.partial_cmp(&right)
302 .ok_or(CelError::BadOperation { left, right, op: "<" })
303 .map(|o| matches!(o, std::cmp::Ordering::Less))
304 }
305
306 pub fn cel_lte(left: impl CelValueConv<'a>, right: impl CelValueConv<'a>) -> Result<bool, CelError<'a>> {
308 let left = left.conv();
309 let right = right.conv();
310 left.partial_cmp(&right)
311 .ok_or(CelError::BadOperation { left, right, op: "<=" })
312 .map(|o| matches!(o, std::cmp::Ordering::Less | std::cmp::Ordering::Equal))
313 }
314
315 pub fn cel_gt(left: impl CelValueConv<'a>, right: impl CelValueConv<'a>) -> Result<bool, CelError<'a>> {
317 let left = left.conv();
318 let right = right.conv();
319 left.partial_cmp(&right)
320 .ok_or(CelError::BadOperation { left, right, op: ">" })
321 .map(|o| matches!(o, std::cmp::Ordering::Greater))
322 }
323
324 pub fn cel_gte(left: impl CelValueConv<'a>, right: impl CelValueConv<'a>) -> Result<bool, CelError<'a>> {
326 let left = left.conv();
327 let right = right.conv();
328 left.partial_cmp(&right)
329 .ok_or(CelError::BadOperation { left, right, op: ">=" })
330 .map(|o| matches!(o, std::cmp::Ordering::Greater | std::cmp::Ordering::Equal))
331 }
332
333 pub fn cel_eq(left: impl CelValueConv<'a>, right: impl CelValueConv<'a>) -> Result<bool, CelError<'a>> {
335 let left = left.conv();
336 let right = right.conv();
337 Ok(left == right)
338 }
339
340 pub fn cel_neq(left: impl CelValueConv<'a>, right: impl CelValueConv<'a>) -> Result<bool, CelError<'a>> {
342 let left = left.conv();
343 let right = right.conv();
344 Ok(left != right)
345 }
346
347 pub fn cel_contains(left: impl CelValueConv<'a>, right: impl CelValueConv<'a>) -> Result<bool, CelError<'a>> {
349 Self::cel_in(right, left).map_err(|err| match err {
350 CelError::BadOperation { left, right, op: "in" } => CelError::BadOperation {
351 left: right,
352 right: left,
353 op: "contains",
354 },
355 err => err,
357 })
358 }
359
360 pub fn cel_in(left: impl CelValueConv<'a>, right: impl CelValueConv<'a>) -> Result<bool, CelError<'a>> {
362 match (left.conv(), right.conv()) {
363 (left, CelValue::List(r)) => Ok(r.contains(&left)),
364 (left, CelValue::Map(r)) => Ok(r.iter().any(|(k, _)| k == &left)),
365 (left @ (CelValue::Bytes(_) | CelValue::String(_)), right @ (CelValue::Bytes(_) | CelValue::String(_))) => {
366 let r = match &right {
367 CelValue::Bytes(b) => b.as_ref(),
368 CelValue::String(s) => s.as_ref().as_bytes(),
369 _ => unreachable!(),
370 };
371
372 let l = match &left {
373 CelValue::Bytes(b) => b.as_ref(),
374 CelValue::String(s) => s.as_ref().as_bytes(),
375 _ => unreachable!(),
376 };
377
378 Ok(r.windows(l.len()).any(|w| w == l))
379 }
380 (left, right) => Err(CelError::BadOperation { left, right, op: "in" }),
381 }
382 }
383
384 pub fn cel_starts_with(left: impl CelValueConv<'a>, right: impl CelValueConv<'a>) -> Result<bool, CelError<'a>> {
385 match (left.conv(), right.conv()) {
386 (left @ (CelValue::Bytes(_) | CelValue::String(_)), right @ (CelValue::Bytes(_) | CelValue::String(_))) => {
387 let r = match &right {
388 CelValue::Bytes(b) => b.as_ref(),
389 CelValue::String(s) => s.as_ref().as_bytes(),
390 _ => unreachable!(),
391 };
392
393 let l = match &left {
394 CelValue::Bytes(b) => b.as_ref(),
395 CelValue::String(s) => s.as_ref().as_bytes(),
396 _ => unreachable!(),
397 };
398
399 Ok(l.starts_with(r))
400 }
401 (left, right) => Err(CelError::BadOperation {
402 left,
403 right,
404 op: "startsWith",
405 }),
406 }
407 }
408
409 pub fn cel_ends_with(left: impl CelValueConv<'a>, right: impl CelValueConv<'a>) -> Result<bool, CelError<'a>> {
410 match (left.conv(), right.conv()) {
411 (left @ (CelValue::Bytes(_) | CelValue::String(_)), right @ (CelValue::Bytes(_) | CelValue::String(_))) => {
412 let r = match &right {
413 CelValue::Bytes(b) => b.as_ref(),
414 CelValue::String(s) => s.as_ref().as_bytes(),
415 _ => unreachable!(),
416 };
417
418 let l = match &left {
419 CelValue::Bytes(b) => b.as_ref(),
420 CelValue::String(s) => s.as_ref().as_bytes(),
421 _ => unreachable!(),
422 };
423
424 Ok(l.ends_with(r))
425 }
426 (left, right) => Err(CelError::BadOperation {
427 left,
428 right,
429 op: "startsWith",
430 }),
431 }
432 }
433
434 pub fn cel_matches(value: impl CelValueConv<'a>, regex: ®ex::Regex) -> Result<bool, CelError<'a>> {
435 match value.conv() {
436 value @ (CelValue::Bytes(_) | CelValue::String(_)) => {
437 let maybe_str = match &value {
438 CelValue::Bytes(b) => std::str::from_utf8(b.as_ref()),
439 CelValue::String(s) => Ok(s.as_ref()),
440 _ => unreachable!(),
441 };
442
443 let Ok(input) = maybe_str else {
444 return Ok(false);
445 };
446
447 Ok(regex.is_match(input))
448 }
449 value => Err(CelError::BadUnaryOperation { op: "matches", value }),
450 }
451 }
452
453 pub fn cel_is_ipv4(value: impl CelValueConv<'a>) -> Result<bool, CelError<'a>> {
454 match value.conv() {
455 CelValue::String(s) => Ok(s.parse::<std::net::Ipv4Addr>().is_ok()),
456 CelValue::Bytes(b) => {
457 if b.as_ref().len() == 4 {
458 Ok(true)
459 } else if let Ok(s) = std::str::from_utf8(b.as_ref()) {
460 Ok(s.parse::<std::net::Ipv4Addr>().is_ok())
461 } else {
462 Ok(false)
463 }
464 }
465 value => Err(CelError::BadUnaryOperation { op: "isIpv4", value }),
466 }
467 }
468
469 pub fn cel_is_ipv6(value: impl CelValueConv<'a>) -> Result<bool, CelError<'a>> {
470 match value.conv() {
471 CelValue::String(s) => Ok(s.parse::<std::net::Ipv6Addr>().is_ok()),
472 CelValue::Bytes(b) => {
473 if b.as_ref().len() == 16 {
474 Ok(true)
475 } else if let Ok(s) = std::str::from_utf8(b.as_ref()) {
476 Ok(s.parse::<std::net::Ipv6Addr>().is_ok())
477 } else {
478 Ok(false)
479 }
480 }
481 value => Err(CelError::BadUnaryOperation { op: "isIpv6", value }),
482 }
483 }
484
485 pub fn cel_is_uuid(value: impl CelValueConv<'a>) -> Result<bool, CelError<'a>> {
486 match value.conv() {
487 CelValue::String(s) => Ok(s.parse::<uuid::Uuid>().is_ok()),
488 CelValue::Bytes(b) => {
489 if b.as_ref().len() == 16 {
490 Ok(true)
491 } else if let Ok(s) = std::str::from_utf8(b.as_ref()) {
492 Ok(s.parse::<uuid::Uuid>().is_ok())
493 } else {
494 Ok(false)
495 }
496 }
497 value => Err(CelError::BadUnaryOperation { op: "isUuid", value }),
498 }
499 }
500
501 pub fn cel_is_ulid(value: impl CelValueConv<'a>) -> Result<bool, CelError<'a>> {
502 match value.conv() {
503 CelValue::String(s) => Ok(s.parse::<ulid::Ulid>().is_ok()),
504 CelValue::Bytes(b) => {
505 if b.as_ref().len() == 16 {
506 Ok(true)
507 } else if let Ok(s) = std::str::from_utf8(b.as_ref()) {
508 Ok(s.parse::<ulid::Ulid>().is_ok())
509 } else {
510 Ok(false)
511 }
512 }
513 value => Err(CelError::BadUnaryOperation { op: "isUlid", value }),
514 }
515 }
516
517 pub fn cel_is_hostname(value: impl CelValueConv<'a>) -> Result<bool, CelError<'a>> {
518 match value.conv() {
519 CelValue::String(s) => Ok(matches!(url::Host::parse(&s), Ok(url::Host::Domain(_)))),
520 CelValue::Bytes(b) => {
521 if let Ok(s) = std::str::from_utf8(b.as_ref()) {
522 Ok(matches!(url::Host::parse(s), Ok(url::Host::Domain(_))))
523 } else {
524 Ok(false)
525 }
526 }
527 value => Err(CelError::BadUnaryOperation { op: "isHostname", value }),
528 }
529 }
530
531 pub fn cel_is_uri(value: impl CelValueConv<'a>) -> Result<bool, CelError<'a>> {
532 match value.conv() {
533 CelValue::String(s) => Ok(url::Url::parse(&s).is_ok()),
534 CelValue::Bytes(b) => {
535 if let Ok(s) = std::str::from_utf8(b.as_ref()) {
536 Ok(url::Url::parse(s).is_ok())
537 } else {
538 Ok(false)
539 }
540 }
541 value => Err(CelError::BadUnaryOperation { op: "isUri", value }),
542 }
543 }
544
545 pub fn cel_is_email(value: impl CelValueConv<'a>) -> Result<bool, CelError<'a>> {
546 match value.conv() {
547 CelValue::String(s) => Ok(email_address::EmailAddress::is_valid(&s)),
548 CelValue::Bytes(b) => {
549 if let Ok(s) = std::str::from_utf8(b.as_ref()) {
550 Ok(email_address::EmailAddress::is_valid(s))
551 } else {
552 Ok(false)
553 }
554 }
555 value => Err(CelError::BadUnaryOperation { op: "isEmail", value }),
556 }
557 }
558
559 pub fn cel_is_nan(value: impl CelValueConv<'a>) -> Result<bool, CelError<'a>> {
560 match value.conv() {
561 CelValue::Number(n) => match n {
562 NumberTy::I64(_) => Ok(false),
563 NumberTy::U64(_) => Ok(false),
564 NumberTy::F64(f) => Ok(f.is_nan()),
565 },
566 value => Err(CelError::BadUnaryOperation { op: "isNaN", value }),
567 }
568 }
569
570 pub fn cel_is_inf(value: impl CelValueConv<'a>) -> Result<bool, CelError<'a>> {
571 match value.conv() {
572 CelValue::Number(n) => match n {
573 NumberTy::I64(_) => Ok(false),
574 NumberTy::U64(_) => Ok(false),
575 NumberTy::F64(f) => Ok(f.is_infinite()),
576 },
577 value => Err(CelError::BadUnaryOperation { op: "isInf", value }),
578 }
579 }
580
581 pub fn cel_size(item: impl CelValueConv<'a>) -> Result<u64, CelError<'a>> {
582 match item.conv() {
583 Self::Bytes(b) => Ok(b.as_ref().len() as u64),
584 Self::String(s) => Ok(s.as_ref().len() as u64),
585 Self::List(l) => Ok(l.len() as u64),
586 Self::Map(m) => Ok(m.len() as u64),
587 item => Err(CelError::BadUnaryOperation { op: "size", value: item }),
588 }
589 }
590
591 pub fn cel_map(
592 item: impl CelValueConv<'a>,
593 map_fn: impl Fn(CelValue<'a>) -> Result<CelValue<'a>, CelError<'a>>,
594 ) -> Result<CelValue<'a>, CelError<'a>> {
595 match item.conv() {
596 CelValue::List(items) => Ok(CelValue::List(items.iter().cloned().map(map_fn).collect::<Result<_, _>>()?)),
597 CelValue::Map(map) => Ok(CelValue::List(
598 map.iter()
599 .map(|(key, _)| key)
600 .cloned()
601 .map(map_fn)
602 .collect::<Result<_, _>>()?,
603 )),
604 value => Err(CelError::BadUnaryOperation { op: "map", value }),
605 }
606 }
607
608 pub fn cel_filter(
609 item: impl CelValueConv<'a>,
610 map_fn: impl Fn(CelValue<'a>) -> Result<bool, CelError<'a>>,
611 ) -> Result<CelValue<'a>, CelError<'a>> {
612 let filter_map = |item: CelValue<'a>| match map_fn(item.clone()) {
613 Ok(false) => None,
614 Ok(true) => Some(Ok(item)),
615 Err(err) => Some(Err(err)),
616 };
617
618 match item.conv() {
619 CelValue::List(items) => Ok(CelValue::List(
620 items.iter().cloned().filter_map(filter_map).collect::<Result<_, _>>()?,
621 )),
622 CelValue::Map(map) => Ok(CelValue::List(
623 map.iter()
624 .map(|(key, _)| key)
625 .cloned()
626 .filter_map(filter_map)
627 .collect::<Result<_, _>>()?,
628 )),
629 value => Err(CelError::BadUnaryOperation { op: "filter", value }),
630 }
631 }
632
633 pub fn cel_all(
634 item: impl CelValueConv<'a>,
635 map_fn: impl Fn(CelValue<'a>) -> Result<bool, CelError<'a>>,
636 ) -> Result<bool, CelError<'a>> {
637 fn all<'a>(
638 mut iter: impl Iterator<Item = CelValue<'a>>,
639 map_fn: impl Fn(CelValue<'a>) -> Result<bool, CelError<'a>>,
640 ) -> Result<bool, CelError<'a>> {
641 loop {
642 let Some(item) = iter.next() else {
643 break Ok(true);
644 };
645
646 if !map_fn(item)? {
647 break Ok(false);
648 }
649 }
650 }
651
652 match item.conv() {
653 CelValue::List(items) => all(items.iter().cloned(), map_fn),
654 CelValue::Map(map) => all(map.iter().map(|(key, _)| key).cloned(), map_fn),
655 value => Err(CelError::BadUnaryOperation { op: "all", value }),
656 }
657 }
658
659 pub fn cel_exists(
660 item: impl CelValueConv<'a>,
661 map_fn: impl Fn(CelValue<'a>) -> Result<bool, CelError<'a>>,
662 ) -> Result<bool, CelError<'a>> {
663 fn exists<'a>(
664 mut iter: impl Iterator<Item = CelValue<'a>>,
665 map_fn: impl Fn(CelValue<'a>) -> Result<bool, CelError<'a>>,
666 ) -> Result<bool, CelError<'a>> {
667 loop {
668 let Some(item) = iter.next() else {
669 break Ok(false);
670 };
671
672 if map_fn(item)? {
673 break Ok(true);
674 }
675 }
676 }
677
678 match item.conv() {
679 CelValue::List(items) => exists(items.iter().cloned(), map_fn),
680 CelValue::Map(map) => exists(map.iter().map(|(key, _)| key).cloned(), map_fn),
681 value => Err(CelError::BadUnaryOperation { op: "existsOne", value }),
682 }
683 }
684
685 pub fn cel_exists_one(
686 item: impl CelValueConv<'a>,
687 map_fn: impl Fn(CelValue<'a>) -> Result<bool, CelError<'a>>,
688 ) -> Result<bool, CelError<'a>> {
689 fn exists_one<'a>(
690 mut iter: impl Iterator<Item = CelValue<'a>>,
691 map_fn: impl Fn(CelValue<'a>) -> Result<bool, CelError<'a>>,
692 ) -> Result<bool, CelError<'a>> {
693 let mut seen = false;
694 loop {
695 let Some(item) = iter.next() else {
696 break Ok(seen);
697 };
698
699 if map_fn(item)? {
700 if seen {
701 break Ok(false);
702 }
703
704 seen = true;
705 }
706 }
707 }
708
709 match item.conv() {
710 CelValue::List(items) => exists_one(items.iter().cloned(), map_fn),
711 CelValue::Map(map) => exists_one(map.iter().map(|(key, _)| key).cloned(), map_fn),
712 value => Err(CelError::BadUnaryOperation { op: "existsOne", value }),
713 }
714 }
715
716 pub fn cel_to_string(item: impl CelValueConv<'a>) -> CelValue<'a> {
717 match item.conv() {
718 item @ CelValue::String(_) => item,
719 CelValue::Bytes(CelBytes::Owned(bytes)) => {
720 CelValue::String(CelString::Owned(String::from_utf8_lossy(bytes.as_ref()).into()))
721 }
722 CelValue::Bytes(CelBytes::Borrowed(b)) => match String::from_utf8_lossy(b) {
723 Cow::Borrowed(b) => CelValue::String(CelString::Borrowed(b)),
724 Cow::Owned(o) => CelValue::String(CelString::Owned(o.into())),
725 },
726 item => CelValue::String(CelString::Owned(item.to_string().into())),
727 }
728 }
729
730 pub fn cel_to_bytes(item: impl CelValueConv<'a>) -> Result<CelValue<'a>, CelError<'a>> {
731 match item.conv() {
732 item @ CelValue::Bytes(_) => Ok(item.clone()),
733 CelValue::String(CelString::Owned(s)) => Ok(CelValue::Bytes(CelBytes::Owned(s.as_bytes().to_vec().into()))),
734 CelValue::String(CelString::Borrowed(s)) => Ok(CelValue::Bytes(CelBytes::Borrowed(s.as_bytes()))),
735 value => Err(CelError::BadUnaryOperation { op: "bytes", value }),
736 }
737 }
738
739 pub fn cel_to_int(item: impl CelValueConv<'a>) -> Result<CelValue<'a>, CelError<'a>> {
740 match item.conv() {
741 CelValue::String(s) => {
742 if let Ok(number) = s.as_ref().parse() {
743 Ok(CelValue::Number(NumberTy::I64(number)))
744 } else {
745 Ok(CelValue::Null)
746 }
747 }
748 CelValue::Number(number) => {
749 if let Ok(number) = number.to_int() {
750 Ok(CelValue::Number(number))
751 } else {
752 Ok(CelValue::Null)
753 }
754 }
755 value => Err(CelError::BadUnaryOperation { op: "int", value }),
756 }
757 }
758
759 pub fn cel_to_uint(item: impl CelValueConv<'a>) -> Result<CelValue<'a>, CelError<'a>> {
760 match item.conv() {
761 CelValue::String(s) => {
762 if let Ok(number) = s.as_ref().parse() {
763 Ok(CelValue::Number(NumberTy::U64(number)))
764 } else {
765 Ok(CelValue::Null)
766 }
767 }
768 CelValue::Number(number) => {
769 if let Ok(number) = number.to_uint() {
770 Ok(CelValue::Number(number))
771 } else {
772 Ok(CelValue::Null)
773 }
774 }
775 value => Err(CelError::BadUnaryOperation { op: "uint", value }),
776 }
777 }
778
779 pub fn cel_to_double(item: impl CelValueConv<'a>) -> Result<CelValue<'a>, CelError<'a>> {
780 match item.conv() {
781 CelValue::String(s) => {
782 if let Ok(number) = s.as_ref().parse() {
783 Ok(CelValue::Number(NumberTy::F64(number)))
784 } else {
785 Ok(CelValue::Null)
786 }
787 }
788 CelValue::Number(number) => {
789 if let Ok(number) = number.to_double() {
790 Ok(CelValue::Number(number))
791 } else {
792 Ok(CelValue::Null)
794 }
795 }
796 value => Err(CelError::BadUnaryOperation { op: "double", value }),
797 }
798 }
799
800 pub fn cel_to_enum(item: impl CelValueConv<'a>, path: impl CelValueConv<'a>) -> Result<CelValue<'a>, CelError<'a>> {
801 match (item.conv(), path.conv()) {
802 (CelValue::Number(number), CelValue::String(tag)) => {
803 let Some(value) = number.to_i32() else {
804 return Ok(CelValue::Null);
805 };
806
807 Ok(CelValue::Enum(CelEnum { tag, value }))
808 }
809 (CelValue::Enum(CelEnum { value, .. }), CelValue::String(tag)) => Ok(CelValue::Enum(CelEnum { tag, value })),
810 (value, path) => Err(CelError::BadOperation {
811 op: "enum",
812 left: value,
813 right: path,
814 }),
815 }
816 }
817}
818
819impl PartialEq for CelValue<'_> {
820 fn eq(&self, other: &Self) -> bool {
821 match (self, other) {
822 (CelValue::Bool(left), CelValue::Bool(right)) => left == right,
823 (left @ (CelValue::Bytes(_) | CelValue::String(_)), right @ (CelValue::Bytes(_) | CelValue::String(_))) => {
824 let left = match left {
825 CelValue::String(s) => s.as_bytes(),
826 CelValue::Bytes(b) => b.as_ref(),
827 _ => unreachable!(),
828 };
829
830 let right = match right {
831 CelValue::String(s) => s.as_bytes(),
832 CelValue::Bytes(b) => b.as_ref(),
833 _ => unreachable!(),
834 };
835
836 left == right
837 }
838 (CelValue::Duration(left), CelValue::Duration(right)) => left == right,
839 (CelValue::Duration(dur), CelValue::Number(seconds)) | (CelValue::Number(seconds), CelValue::Duration(dur)) => {
840 (dur.num_seconds() as f64) + dur.subsec_nanos() as f64 / 1_000_000_000.0 == *seconds
841 }
842 (CelValue::Timestamp(left), CelValue::Timestamp(right)) => left == right,
843 (CelValue::Enum(left), CelValue::Enum(right)) => left == right,
844 (CelValue::Enum(enum_), CelValue::Number(value)) | (CelValue::Number(value), CelValue::Enum(enum_)) => {
845 enum_.value == *value
846 }
847 (CelValue::List(left), CelValue::List(right)) => left == right,
848 (CelValue::Map(left), CelValue::Map(right)) => left == right,
849 (CelValue::Number(left), CelValue::Number(right)) => left == right,
850 (CelValue::Null, CelValue::Null) => true,
851 _ => false,
852 }
853 }
854}
855
856pub trait CelValueConv<'a> {
857 fn conv(self) -> CelValue<'a>;
858}
859
860impl CelValueConv<'_> for () {
861 fn conv(self) -> CelValue<'static> {
862 CelValue::Null
863 }
864}
865
866impl CelValueConv<'_> for bool {
867 fn conv(self) -> CelValue<'static> {
868 CelValue::Bool(self)
869 }
870}
871
872impl CelValueConv<'_> for i32 {
873 fn conv(self) -> CelValue<'static> {
874 CelValue::Number(NumberTy::I64(self as i64))
875 }
876}
877
878impl CelValueConv<'_> for u32 {
879 fn conv(self) -> CelValue<'static> {
880 CelValue::Number(NumberTy::U64(self as u64))
881 }
882}
883
884impl CelValueConv<'_> for i64 {
885 fn conv(self) -> CelValue<'static> {
886 CelValue::Number(NumberTy::I64(self))
887 }
888}
889
890impl CelValueConv<'_> for u64 {
891 fn conv(self) -> CelValue<'static> {
892 CelValue::Number(NumberTy::U64(self))
893 }
894}
895
896impl CelValueConv<'_> for f32 {
897 fn conv(self) -> CelValue<'static> {
898 CelValue::Number(NumberTy::F64(self as f64))
899 }
900}
901
902impl CelValueConv<'_> for f64 {
903 fn conv(self) -> CelValue<'static> {
904 CelValue::Number(NumberTy::F64(self))
905 }
906}
907
908impl<'a> CelValueConv<'a> for &'a str {
909 fn conv(self) -> CelValue<'a> {
910 CelValue::String(CelString::Borrowed(self))
911 }
912}
913
914impl CelValueConv<'_> for Bytes {
915 fn conv(self) -> CelValue<'static> {
916 CelValue::Bytes(CelBytes::Owned(self.clone()))
917 }
918}
919
920impl<'a> CelValueConv<'a> for &'a [u8] {
921 fn conv(self) -> CelValue<'a> {
922 CelValue::Bytes(CelBytes::Borrowed(self))
923 }
924}
925
926impl<'a, const N: usize> CelValueConv<'a> for &'a [u8; N] {
927 fn conv(self) -> CelValue<'a> {
928 (self as &[u8]).conv()
929 }
930}
931
932impl<'a> CelValueConv<'a> for &'a Vec<u8> {
933 fn conv(self) -> CelValue<'a> {
934 CelValue::Bytes(CelBytes::Borrowed(self))
935 }
936}
937
938impl<'a, T> CelValueConv<'a> for &'a [T]
939where
940 &'a T: CelValueConv<'a>,
941{
942 fn conv(self) -> CelValue<'a> {
943 CelValue::List(self.iter().map(CelValueConv::conv).collect())
944 }
945}
946
947impl<'a, T, const N: usize> CelValueConv<'a> for &'a [T; N]
948where
949 &'a T: CelValueConv<'a>,
950{
951 fn conv(self) -> CelValue<'a> {
952 (self as &[T]).conv()
953 }
954}
955
956impl<'a, T> CelValueConv<'a> for &'a Vec<T>
957where
958 &'a T: CelValueConv<'a>,
959{
960 fn conv(self) -> CelValue<'a> {
961 self.as_slice().conv()
962 }
963}
964
965impl<'a> CelValueConv<'a> for &'a String {
966 fn conv(self) -> CelValue<'a> {
967 self.as_str().conv()
968 }
969}
970
971impl<'a, T> CelValueConv<'a> for &T
972where
973 T: CelValueConv<'a> + Copy,
974{
975 fn conv(self) -> CelValue<'a> {
976 CelValueConv::conv(*self)
977 }
978}
979
980impl<'a> CelValueConv<'a> for &CelValue<'a> {
981 fn conv(self) -> CelValue<'a> {
982 self.clone()
983 }
984}
985
986impl std::fmt::Display for CelValue<'_> {
987 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
988 match self {
989 CelValue::Bool(b) => std::fmt::Display::fmt(b, f),
990 CelValue::Number(n) => std::fmt::Display::fmt(n, f),
991 CelValue::String(s) => std::fmt::Display::fmt(s.as_ref(), f),
992 CelValue::Bytes(b) => std::fmt::Debug::fmt(b.as_ref(), f),
993 CelValue::List(l) => {
994 let mut list = f.debug_list();
995 for item in l.iter() {
996 list.entry(&fmtools::fmt(|fmt| item.fmt(fmt)));
997 }
998 list.finish()
999 }
1000 CelValue::Map(m) => {
1001 let mut map = f.debug_map();
1002 for (key, value) in m.iter() {
1003 map.entry(&fmtools::fmt(|fmt| key.fmt(fmt)), &fmtools::fmt(|fmt| value.fmt(fmt)));
1004 }
1005 map.finish()
1006 }
1007 CelValue::Null => std::fmt::Display::fmt("null", f),
1008 CelValue::Duration(d) => std::fmt::Display::fmt(d, f),
1009 CelValue::Timestamp(t) => std::fmt::Display::fmt(t, f),
1010 #[cfg(feature = "runtime")]
1011 CelValue::Enum(e) => e.into_string().fmt(f),
1012 #[cfg(not(feature = "runtime"))]
1013 CelValue::Enum(_) => panic!("enum to string called during build-time"),
1014 }
1015 }
1016}
1017
1018impl CelValue<'_> {
1019 pub fn to_bool(&self) -> bool {
1020 match self {
1021 CelValue::Bool(b) => *b,
1022 CelValue::Number(n) => *n != 0,
1023 CelValue::String(s) => !s.as_ref().is_empty(),
1024 CelValue::Bytes(b) => !b.as_ref().is_empty(),
1025 CelValue::List(l) => !l.is_empty(),
1026 CelValue::Map(m) => !m.is_empty(),
1027 CelValue::Null => false,
1028 CelValue::Duration(d) => !d.is_zero(),
1029 CelValue::Timestamp(t) => t.timestamp_nanos_opt().unwrap_or_default() != 0,
1030 #[cfg(feature = "runtime")]
1031 CelValue::Enum(t) => t.is_valid(),
1032 #[cfg(not(feature = "runtime"))]
1033 CelValue::Enum(_) => panic!("enum to bool called during build-time"),
1034 }
1035 }
1036}
1037
1038#[derive(Clone, Copy, Debug)]
1039pub enum NumberTy {
1040 I64(i64),
1041 U64(u64),
1042 F64(f64),
1043}
1044
1045impl PartialOrd for NumberTy {
1046 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1047 NumberTy::promote(*self, *other).and_then(|(l, r)| match (l, r) {
1048 (NumberTy::I64(l), NumberTy::I64(r)) => Some(l.cmp(&r)),
1049 (NumberTy::U64(l), NumberTy::U64(r)) => Some(l.cmp(&r)),
1050 (NumberTy::F64(l), NumberTy::F64(r)) => Some(if l.approx_eq(r, float_cmp::F64Margin::default()) {
1051 std::cmp::Ordering::Equal
1052 } else {
1053 l.partial_cmp(&r).unwrap_or(std::cmp::Ordering::Equal)
1054 }),
1055 _ => None,
1057 })
1058 }
1059}
1060
1061impl NumberTy {
1062 pub fn cel_add(self, other: Self) -> Result<Self, CelError<'static>> {
1063 const ERROR: CelError<'static> = CelError::NumberOutOfRange { op: "addition" };
1064 match NumberTy::promote(self, other).ok_or(ERROR)? {
1065 (NumberTy::I64(l), NumberTy::I64(r)) => Ok(NumberTy::I64(l.checked_add(r).ok_or(ERROR)?)),
1066 (NumberTy::U64(l), NumberTy::U64(r)) => Ok(NumberTy::U64(l.checked_add(r).ok_or(ERROR)?)),
1067 (NumberTy::F64(l), NumberTy::F64(r)) => Ok(NumberTy::F64(l + r)),
1068 _ => Err(ERROR),
1070 }
1071 }
1072
1073 pub fn cel_sub(self, other: Self) -> Result<Self, CelError<'static>> {
1074 const ERROR: CelError<'static> = CelError::NumberOutOfRange { op: "subtraction" };
1075 match NumberTy::promote(self, other).ok_or(ERROR)? {
1076 (NumberTy::I64(l), NumberTy::I64(r)) => Ok(NumberTy::I64(l.checked_sub(r).ok_or(ERROR)?)),
1077 (NumberTy::U64(l), NumberTy::U64(r)) => Ok(NumberTy::U64(l.checked_sub(r).ok_or(ERROR)?)),
1078 (NumberTy::F64(l), NumberTy::F64(r)) => Ok(NumberTy::F64(l - r)),
1079 _ => Err(ERROR),
1081 }
1082 }
1083
1084 pub fn cel_mul(self, other: Self) -> Result<Self, CelError<'static>> {
1085 const ERROR: CelError<'static> = CelError::NumberOutOfRange { op: "multiplication" };
1086 match NumberTy::promote(self, other).ok_or(ERROR)? {
1087 (NumberTy::I64(l), NumberTy::I64(r)) => Ok(NumberTy::I64(l.checked_mul(r).ok_or(ERROR)?)),
1088 (NumberTy::U64(l), NumberTy::U64(r)) => Ok(NumberTy::U64(l.checked_mul(r).ok_or(ERROR)?)),
1089 (NumberTy::F64(l), NumberTy::F64(r)) => Ok(NumberTy::F64(l * r)),
1090 _ => Err(ERROR),
1092 }
1093 }
1094
1095 pub fn cel_div(self, other: Self) -> Result<Self, CelError<'static>> {
1096 if other == 0 {
1097 return Err(CelError::NumberOutOfRange { op: "division by zero" });
1098 }
1099
1100 const ERROR: CelError<'static> = CelError::NumberOutOfRange { op: "division" };
1101 match NumberTy::promote(self, other).ok_or(ERROR)? {
1102 (NumberTy::I64(l), NumberTy::I64(r)) => Ok(NumberTy::I64(l.checked_div(r).ok_or(ERROR)?)),
1103 (NumberTy::U64(l), NumberTy::U64(r)) => Ok(NumberTy::U64(l.checked_div(r).ok_or(ERROR)?)),
1104 (NumberTy::F64(l), NumberTy::F64(r)) => Ok(NumberTy::F64(l / r)),
1105 _ => Err(ERROR),
1107 }
1108 }
1109
1110 pub fn cel_rem(self, other: Self) -> Result<Self, CelError<'static>> {
1111 if other == 0 {
1112 return Err(CelError::NumberOutOfRange { op: "remainder by zero" });
1113 }
1114
1115 const ERROR: CelError<'static> = CelError::NumberOutOfRange { op: "remainder" };
1116 match NumberTy::promote(self, other).ok_or(ERROR)? {
1117 (NumberTy::I64(l), NumberTy::I64(r)) => Ok(NumberTy::I64(l.checked_rem(r).ok_or(ERROR)?)),
1118 (NumberTy::U64(l), NumberTy::U64(r)) => Ok(NumberTy::U64(l.checked_rem(r).ok_or(ERROR)?)),
1119 _ => Err(ERROR),
1120 }
1121 }
1122
1123 pub fn cel_neg(self) -> Result<NumberTy, CelError<'static>> {
1124 const ERROR: CelError<'static> = CelError::NumberOutOfRange { op: "negation" };
1125 match self {
1126 NumberTy::I64(n) => Ok(NumberTy::I64(n.checked_neg().ok_or(ERROR)?)),
1127 NumberTy::U64(n) => Ok(NumberTy::I64(n.to_i64().ok_or(ERROR)?.checked_neg().ok_or(ERROR)?)),
1128 NumberTy::F64(n) => Ok(NumberTy::F64(-n)),
1129 }
1130 }
1131
1132 pub fn to_int(self) -> Result<NumberTy, CelError<'static>> {
1133 const ERROR: CelError<'static> = CelError::NumberOutOfRange { op: "int" };
1134 match self {
1135 NumberTy::I64(n) => Ok(NumberTy::I64(n)),
1136 NumberTy::U64(n) => Ok(NumberTy::I64(n.to_i64().ok_or(ERROR)?)),
1137 NumberTy::F64(n) => Ok(NumberTy::I64(n.to_i64().ok_or(ERROR)?)),
1138 }
1139 }
1140
1141 pub fn to_uint(self) -> Result<NumberTy, CelError<'static>> {
1142 const ERROR: CelError<'static> = CelError::NumberOutOfRange { op: "int" };
1143 match self {
1144 NumberTy::I64(n) => Ok(NumberTy::U64(n.to_u64().ok_or(ERROR)?)),
1145 NumberTy::U64(n) => Ok(NumberTy::U64(n)),
1146 NumberTy::F64(n) => Ok(NumberTy::U64(n.to_u64().ok_or(ERROR)?)),
1147 }
1148 }
1149
1150 pub fn to_double(self) -> Result<NumberTy, CelError<'static>> {
1151 const ERROR: CelError<'static> = CelError::NumberOutOfRange { op: "int" };
1152 match self {
1153 NumberTy::I64(n) => Ok(NumberTy::F64(n.to_f64().ok_or(ERROR)?)),
1154 NumberTy::U64(n) => Ok(NumberTy::F64(n.to_f64().ok_or(ERROR)?)),
1155 NumberTy::F64(n) => Ok(NumberTy::F64(n)),
1156 }
1157 }
1158}
1159
1160impl std::fmt::Display for NumberTy {
1161 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1162 match self {
1163 NumberTy::I64(n) => std::fmt::Display::fmt(n, f),
1164 NumberTy::U64(n) => std::fmt::Display::fmt(n, f),
1165 NumberTy::F64(n) => write!(f, "{n:.2}"), }
1167 }
1168}
1169
1170impl PartialEq for NumberTy {
1171 fn eq(&self, other: &Self) -> bool {
1172 NumberTy::promote(*self, *other)
1173 .map(|(l, r)| match (l, r) {
1174 (NumberTy::I64(l), NumberTy::I64(r)) => l == r,
1175 (NumberTy::U64(l), NumberTy::U64(r)) => l == r,
1176 (NumberTy::F64(l), NumberTy::F64(r)) => l.approx_eq(r, float_cmp::F64Margin::default()),
1177 _ => false,
1179 })
1180 .unwrap_or(false)
1181 }
1182}
1183
1184macro_rules! impl_eq_number {
1185 ($ty:ty) => {
1186 impl PartialEq<$ty> for NumberTy {
1187 fn eq(&self, other: &$ty) -> bool {
1188 NumberTy::from(*other) == *self
1189 }
1190 }
1191
1192 impl PartialEq<NumberTy> for $ty {
1193 fn eq(&self, other: &NumberTy) -> bool {
1194 other == self
1195 }
1196 }
1197 };
1198}
1199
1200impl_eq_number!(i32);
1201impl_eq_number!(u32);
1202impl_eq_number!(i64);
1203impl_eq_number!(u64);
1204impl_eq_number!(f64);
1205
1206impl From<i32> for NumberTy {
1207 fn from(value: i32) -> Self {
1208 Self::I64(value as i64)
1209 }
1210}
1211
1212impl From<u32> for NumberTy {
1213 fn from(value: u32) -> Self {
1214 Self::U64(value as u64)
1215 }
1216}
1217
1218impl From<i64> for NumberTy {
1219 fn from(value: i64) -> Self {
1220 Self::I64(value)
1221 }
1222}
1223
1224impl From<u64> for NumberTy {
1225 fn from(value: u64) -> Self {
1226 Self::U64(value)
1227 }
1228}
1229
1230impl From<f64> for NumberTy {
1231 fn from(value: f64) -> Self {
1232 Self::F64(value)
1233 }
1234}
1235
1236impl From<f32> for NumberTy {
1237 fn from(value: f32) -> Self {
1238 Self::F64(value as f64)
1239 }
1240}
1241
1242impl CelValueConv<'_> for NumberTy {
1243 fn conv(self) -> CelValue<'static> {
1244 CelValue::Number(self)
1245 }
1246}
1247
1248impl<'a> CelValueConv<'a> for CelValue<'a> {
1249 fn conv(self) -> CelValue<'a> {
1250 self
1251 }
1252}
1253
1254macro_rules! impl_to_primitive_number {
1255 ($fn:ident, $ty:ty) => {
1256 fn $fn(&self) -> Option<$ty> {
1257 match self {
1258 NumberTy::I64(i) => i.$fn(),
1259 NumberTy::U64(u) => u.$fn(),
1260 NumberTy::F64(f) => f.$fn(),
1261 }
1262 }
1263 };
1264}
1265
1266impl num_traits::ToPrimitive for NumberTy {
1267 impl_to_primitive_number!(to_f32, f32);
1268
1269 impl_to_primitive_number!(to_f64, f64);
1270
1271 impl_to_primitive_number!(to_i128, i128);
1272
1273 impl_to_primitive_number!(to_i16, i16);
1274
1275 impl_to_primitive_number!(to_i32, i32);
1276
1277 impl_to_primitive_number!(to_i64, i64);
1278
1279 impl_to_primitive_number!(to_i8, i8);
1280
1281 impl_to_primitive_number!(to_u128, u128);
1282
1283 impl_to_primitive_number!(to_u16, u16);
1284
1285 impl_to_primitive_number!(to_u32, u32);
1286
1287 impl_to_primitive_number!(to_u64, u64);
1288}
1289
1290impl NumberTy {
1291 pub fn promote(left: Self, right: Self) -> Option<(Self, Self)> {
1292 match (left, right) {
1293 (NumberTy::I64(l), NumberTy::I64(r)) => Some((NumberTy::I64(l), NumberTy::I64(r))),
1294 (NumberTy::U64(l), NumberTy::U64(r)) => Some((NumberTy::U64(l), NumberTy::U64(r))),
1295 (NumberTy::F64(_), _) | (_, NumberTy::F64(_)) => Some((Self::F64(left.to_f64()?), Self::F64(right.to_f64()?))),
1296 (NumberTy::I64(_), _) | (_, NumberTy::I64(_)) => Some((Self::I64(left.to_i64()?), Self::I64(right.to_i64()?))),
1297 }
1298 }
1299}
1300
1301pub fn array_access<'a, 'b, T>(array: &'a [T], idx: impl CelValueConv<'b>) -> Result<&'a T, CelError<'b>> {
1302 let idx = idx.conv();
1303 match idx.as_number().and_then(|n| n.to_usize()) {
1304 Some(idx) => array.get(idx).ok_or(CelError::IndexOutOfBounds(idx, array.len())),
1305 _ => Err(CelError::IndexWithBadIndex(idx)),
1306 }
1307}
1308
1309macro_rules! impl_partial_eq {
1310 ($($ty:ty),*$(,)?) => {
1311 $(
1312 impl PartialEq<$ty> for CelValue<'_> {
1313 fn eq(&self, other: &$ty) -> bool {
1314 self == &other.conv()
1315 }
1316 }
1317
1318 impl PartialEq<CelValue<'_>> for $ty {
1319 fn eq(&self, other: &CelValue<'_>) -> bool {
1320 other == self
1321 }
1322 }
1323 )*
1324 };
1325}
1326
1327impl_partial_eq!(String, i32, i64, f64, f32, Vec<u8>, u32, u64);
1328
1329impl PartialEq<Bytes> for CelValue<'_> {
1330 fn eq(&self, other: &Bytes) -> bool {
1331 self == &other.clone().conv()
1332 }
1333}
1334
1335impl PartialEq<CelValue<'_>> for Bytes {
1336 fn eq(&self, other: &CelValue<'_>) -> bool {
1337 other == self
1338 }
1339}
1340
1341pub fn array_contains<'a, 'b, T: PartialEq<CelValue<'b>>>(array: &'a [T], value: impl CelValueConv<'b>) -> bool {
1342 let value = value.conv();
1343 array.iter().any(|v| v == &value)
1344}
1345
1346trait MapKeyCast {
1347 type Borrow: ToOwned + ?Sized;
1348
1349 fn make_key<'a>(key: &'a CelValue<'a>) -> Option<Cow<'a, Self::Borrow>>
1350 where
1351 Self::Borrow: ToOwned;
1352}
1353
1354macro_rules! impl_map_key_cast_number {
1355 ($ty:ty, $fn:ident) => {
1356 impl MapKeyCast for $ty {
1357 type Borrow = Self;
1358
1359 fn make_key<'a>(key: &'a CelValue<'a>) -> Option<Cow<'a, Self>> {
1360 match key {
1361 CelValue::Number(number) => number.$fn().map(Cow::Owned),
1362 _ => None,
1363 }
1364 }
1365 }
1366 };
1367}
1368
1369impl_map_key_cast_number!(i32, to_i32);
1370impl_map_key_cast_number!(u32, to_u32);
1371impl_map_key_cast_number!(i64, to_i64);
1372impl_map_key_cast_number!(u64, to_u64);
1373
1374impl MapKeyCast for String {
1375 type Borrow = str;
1376
1377 fn make_key<'a>(key: &'a CelValue<'a>) -> Option<Cow<'a, Self::Borrow>> {
1378 match key {
1379 CelValue::String(s) => Some(Cow::Borrowed(s.as_ref())),
1380 _ => None,
1381 }
1382 }
1383}
1384
1385trait Map<K, V> {
1386 fn get<Q>(&self, key: &Q) -> Option<&V>
1387 where
1388 K: std::borrow::Borrow<Q>,
1389 Q: std::hash::Hash + std::cmp::Ord + ?Sized;
1390}
1391
1392impl<K, V, S> Map<K, V> for HashMap<K, V, S>
1393where
1394 K: std::hash::Hash + std::cmp::Eq,
1395 S: std::hash::BuildHasher,
1396{
1397 fn get<Q>(&self, key: &Q) -> Option<&V>
1398 where
1399 K: std::borrow::Borrow<Q>,
1400 Q: std::hash::Hash + std::cmp::Eq + ?Sized,
1401 {
1402 HashMap::get(self, key)
1403 }
1404}
1405
1406impl<K, V> Map<K, V> for BTreeMap<K, V>
1407where
1408 K: std::cmp::Ord,
1409{
1410 fn get<Q>(&self, key: &Q) -> Option<&V>
1411 where
1412 K: std::borrow::Borrow<Q>,
1413 Q: std::cmp::Ord + ?Sized,
1414 {
1415 BTreeMap::get(self, key)
1416 }
1417}
1418
1419#[allow(private_bounds)]
1420pub fn map_access<'a, 'b, K, V>(map: &'a impl Map<K, V>, key: impl CelValueConv<'b>) -> Result<&'a V, CelError<'b>>
1421where
1422 K: Ord + Hash + MapKeyCast,
1423 K: std::borrow::Borrow<K::Borrow>,
1424 K::Borrow: std::cmp::Eq + std::hash::Hash + std::cmp::Ord,
1425{
1426 let key = key.conv();
1427 K::make_key(&key)
1428 .and_then(|key| map.get(&key))
1429 .ok_or(CelError::MapKeyNotFound(key))
1430}
1431
1432#[allow(private_bounds)]
1433pub fn map_contains<'a, 'b, K, V>(map: &'a impl Map<K, V>, key: impl CelValueConv<'b>) -> bool
1434where
1435 K: Ord + Hash + MapKeyCast,
1436 K: std::borrow::Borrow<K::Borrow>,
1437 K::Borrow: std::cmp::Eq + std::hash::Hash + std::cmp::Ord,
1438{
1439 let key = key.conv();
1440 K::make_key(&key).and_then(|key| map.get(&key)).is_some()
1441}
1442
1443pub trait CelBooleanConv {
1444 fn to_bool(&self) -> bool;
1445}
1446
1447impl CelBooleanConv for bool {
1448 fn to_bool(&self) -> bool {
1449 *self
1450 }
1451}
1452
1453impl CelBooleanConv for CelValue<'_> {
1454 fn to_bool(&self) -> bool {
1455 CelValue::to_bool(self)
1456 }
1457}
1458
1459impl<T: CelBooleanConv> CelBooleanConv for Option<T> {
1460 fn to_bool(&self) -> bool {
1461 self.as_ref().map(CelBooleanConv::to_bool).unwrap_or(false)
1462 }
1463}
1464
1465impl<T> CelBooleanConv for Vec<T> {
1466 fn to_bool(&self) -> bool {
1467 !self.is_empty()
1468 }
1469}
1470
1471impl<K, V> CelBooleanConv for BTreeMap<K, V> {
1472 fn to_bool(&self) -> bool {
1473 !self.is_empty()
1474 }
1475}
1476
1477impl<K, V> CelBooleanConv for HashMap<K, V> {
1478 fn to_bool(&self) -> bool {
1479 !self.is_empty()
1480 }
1481}
1482
1483impl<T> CelBooleanConv for &T
1484where
1485 T: CelBooleanConv,
1486{
1487 fn to_bool(&self) -> bool {
1488 CelBooleanConv::to_bool(*self)
1489 }
1490}
1491
1492impl CelBooleanConv for str {
1493 fn to_bool(&self) -> bool {
1494 !self.is_empty()
1495 }
1496}
1497
1498impl CelBooleanConv for String {
1499 fn to_bool(&self) -> bool {
1500 !self.is_empty()
1501 }
1502}
1503
1504impl<T: CelBooleanConv> CelBooleanConv for [T] {
1505 fn to_bool(&self) -> bool {
1506 !self.is_empty()
1507 }
1508}
1509
1510impl CelBooleanConv for Bytes {
1511 fn to_bool(&self) -> bool {
1512 !self.is_empty()
1513 }
1514}
1515
1516pub fn to_bool(value: impl CelBooleanConv) -> bool {
1517 value.to_bool()
1518}
1519
1520#[cfg(feature = "runtime")]
1521#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1522pub enum CelMode {
1523 Proto,
1524 Serde,
1525}
1526
1527#[cfg(feature = "runtime")]
1528thread_local! {
1529 static CEL_MODE: std::cell::Cell<CelMode> = const { std::cell::Cell::new(CelMode::Proto) };
1530}
1531
1532#[cfg(feature = "runtime")]
1533impl CelMode {
1534 pub fn set(self) {
1535 CEL_MODE.set(self);
1536 }
1537
1538 pub fn current() -> CelMode {
1539 CEL_MODE.get()
1540 }
1541
1542 pub fn is_json(self) -> bool {
1543 matches!(self, Self::Serde)
1544 }
1545
1546 pub fn is_proto(self) -> bool {
1547 matches!(self, Self::Proto)
1548 }
1549}
1550
1551#[derive(Debug, PartialEq, Clone)]
1552pub struct CelEnum<'a> {
1553 pub tag: CelString<'a>,
1554 pub value: i32,
1555}
1556
1557impl<'a> CelEnum<'a> {
1558 pub fn new(tag: CelString<'a>, value: i32) -> CelEnum<'a> {
1559 CelEnum { tag, value }
1560 }
1561
1562 #[cfg(feature = "runtime")]
1563 pub fn into_string(&self) -> CelValue<'static> {
1564 EnumVtable::from_tag(self.tag.as_ref())
1565 .map(|vt| match CEL_MODE.get() {
1566 CelMode::Serde => (vt.to_serde)(self.value),
1567 CelMode::Proto => (vt.to_proto)(self.value),
1568 })
1569 .unwrap_or(CelValue::Number(NumberTy::I64(self.value as i64)))
1570 }
1571
1572 #[cfg(feature = "runtime")]
1573 pub fn is_valid(&self) -> bool {
1574 EnumVtable::from_tag(self.tag.as_ref()).is_some_and(|vt| (vt.is_valid)(self.value))
1575 }
1576}
1577
1578#[cfg(feature = "runtime")]
1579#[derive(Debug, Copy, Clone)]
1580pub struct EnumVtable {
1581 pub proto_path: &'static str,
1582 pub is_valid: fn(i32) -> bool,
1583 pub to_serde: fn(i32) -> CelValue<'static>,
1584 pub to_proto: fn(i32) -> CelValue<'static>,
1585}
1586
1587#[cfg(feature = "runtime")]
1588impl EnumVtable {
1589 pub fn from_tag(tag: &str) -> Option<&'static EnumVtable> {
1590 static LOOKUP: std::sync::LazyLock<HashMap<&'static str, &'static EnumVtable>> =
1591 std::sync::LazyLock::new(|| TINC_CEL_ENUM_VTABLE.into_iter().map(|item| (item.proto_path, item)).collect());
1592
1593 LOOKUP.get(tag).copied()
1594 }
1595}
1596
1597#[cfg(feature = "runtime")]
1598#[linkme::distributed_slice]
1599pub static TINC_CEL_ENUM_VTABLE: [EnumVtable];
1600
1601#[cfg(test)]
1602#[cfg_attr(all(test, coverage_nightly), coverage(off))]
1603mod tests {
1604 use std::borrow::Cow;
1605 use std::cmp::Ordering;
1606 use std::collections::{BTreeMap, HashMap};
1607 use std::sync::Arc;
1608
1609 use bytes::Bytes;
1610 use chrono::{DateTime, Duration, FixedOffset};
1611 use num_traits::ToPrimitive;
1612 use regex::Regex;
1613 use uuid::Uuid;
1614
1615 use super::CelString;
1616 use crate::{
1617 CelBooleanConv, CelBytes, CelEnum, CelError, CelValue, CelValueConv, MapKeyCast, NumberTy, array_access,
1618 array_contains, map_access, map_contains,
1619 };
1620
1621 #[test]
1622 fn celstring_eq() {
1623 let b1 = CelString::Borrowed("foo");
1625 let b2 = CelString::Borrowed("foo");
1626 assert_eq!(b1, b2);
1627
1628 let o1 = CelString::Owned(Arc::from("foo"));
1630 let o2 = CelString::Owned(Arc::from("foo"));
1631 assert_eq!(o1, o2);
1632
1633 let b = CelString::Borrowed("foo");
1635 let o = CelString::Owned(Arc::from("foo"));
1636 assert_eq!(b, o.clone());
1637 assert_eq!(o, b);
1638
1639 let bar_b = CelString::Borrowed("bar");
1641 let bar_o = CelString::Owned(Arc::from("bar"));
1642 assert_ne!(b1, bar_b);
1643 assert_ne!(o1, bar_o);
1644 }
1645
1646 #[test]
1647 fn celstring_borrowed() {
1648 let original = String::from("hello");
1649 let cs: CelString = (&original).into();
1650
1651 match cs {
1652 CelString::Borrowed(s) => {
1653 assert_eq!(s, "hello");
1654 let orig_ptr = original.as_ptr();
1656 let borrow_ptr = s.as_ptr();
1657 assert_eq!(orig_ptr, borrow_ptr);
1658 }
1659 _ => panic!("expected CelString::Borrowed"),
1660 }
1661 }
1662
1663 #[test]
1664 fn celstring_owned() {
1665 let arc: Arc<str> = Arc::from("world");
1666 let cs: CelString<'static> = (&arc).into();
1667
1668 match cs {
1669 CelString::Owned(o) => {
1670 assert_eq!(o.as_ref(), "world");
1671 assert!(Arc::ptr_eq(&o, &arc));
1672 assert_eq!(Arc::strong_count(&arc), 2);
1673 }
1674 _ => panic!("expected CelString::Owned"),
1675 }
1676 }
1677
1678 #[test]
1679 fn borrowed_eq_borrowed() {
1680 let slice1: &[u8] = &[1, 2, 3];
1681 let slice2: &[u8] = &[1, 2, 3];
1682 let b1: CelBytes = slice1.into();
1683 let b2: CelBytes = slice2.into();
1684 assert_eq!(b1, b2);
1685 }
1686
1687 #[test]
1688 fn owned_eq_owned() {
1689 let data = vec![10, 20, 30];
1690 let o1: CelBytes<'static> = Bytes::from(data.clone()).into();
1691 let o2: CelBytes<'static> = Bytes::from(data.clone()).into();
1692 assert_eq!(o1, o2);
1693 }
1694
1695 #[test]
1696 fn borrowed_eq_owned() {
1697 let v = vec![5, 6, 7];
1698 let owned: CelBytes<'static> = Bytes::from(v.clone()).into();
1699 let borrowed: CelBytes = v.as_slice().into();
1700
1701 assert_eq!(owned, borrowed);
1703 assert_eq!(borrowed, owned);
1705 }
1706
1707 #[test]
1708 fn celbytes_neq() {
1709 let b1: CelBytes = (&[1, 2, 3][..]).into();
1710 let b2: CelBytes = (&[4, 5, 6][..]).into();
1711 assert_ne!(b1, b2);
1712
1713 let o1: CelBytes<'static> = Bytes::from(vec![1, 2, 3]).into();
1714 let o2: CelBytes<'static> = Bytes::from(vec![7, 8, 9]).into();
1715 assert_ne!(o1, o2);
1716 }
1717
1718 #[test]
1719 fn celbytes_borrowed_slice() {
1720 let arr: [u8; 4] = [9, 8, 7, 6];
1721 let cb: CelBytes = arr.as_slice().into();
1722 match cb {
1723 CelBytes::Borrowed(s) => {
1724 assert_eq!(s, arr.as_slice());
1725 assert_eq!(s.as_ptr(), arr.as_ptr());
1727 }
1728 _ => panic!("Expected CelBytes::Borrowed from slice"),
1729 }
1730 }
1731
1732 #[test]
1733 fn celbytes_bstr_owned() {
1734 let bytes = Bytes::from_static(b"rust");
1735 let cb: CelBytes = bytes.clone().into();
1736 match cb {
1737 CelBytes::Owned(b) => {
1738 assert_eq!(b, bytes);
1739 }
1740 _ => panic!("Expected CelBytes::Owned from Bytes"),
1741 }
1742 }
1743
1744 #[test]
1745 fn celbytes_vec_owned() {
1746 let data = vec![0x10, 0x20, 0x30];
1747 let cb: CelBytes<'static> = data.clone().into();
1748
1749 match cb {
1750 CelBytes::Owned(bytes) => {
1751 assert_eq!(bytes.as_ref(), &[0x10, 0x20, 0x30]);
1752 assert_eq!(bytes, Bytes::from(data));
1753 }
1754 _ => panic!("Expected CelBytes::Owned variant"),
1755 }
1756 }
1757
1758 #[test]
1759 fn celbytes_vec_borrowed() {
1760 let data = vec![4u8, 5, 6];
1761 let cb: CelBytes = (&data).into();
1762
1763 match cb {
1764 CelBytes::Borrowed(slice) => {
1765 assert_eq!(slice, data.as_slice());
1766
1767 let data_ptr = data.as_ptr();
1768 let slice_ptr = slice.as_ptr();
1769 assert_eq!(data_ptr, slice_ptr);
1770 }
1771 _ => panic!("Expected CelBytes::Borrowed variant"),
1772 }
1773 }
1774
1775 #[test]
1776 fn celvalue_partial_cmp() {
1777 let one = 1i32.conv();
1778 let two = 2i32.conv();
1779 assert_eq!(one.partial_cmp(&two), Some(Ordering::Less));
1780 assert_eq!(two.partial_cmp(&one), Some(Ordering::Greater));
1781 assert_eq!(one.partial_cmp(&1i32.conv()), Some(Ordering::Equal));
1782 }
1783
1784 #[test]
1785 fn celvalue_str_byte_partial_cmp() {
1786 let s1 = "abc".conv();
1787 let s2 = "abd".conv();
1788 assert_eq!(s1.partial_cmp(&s2), Some(Ordering::Less));
1789
1790 let b1 = Bytes::from_static(b"abc").conv();
1791 let b2 = Bytes::from_static(b"abd").conv();
1792 assert_eq!(b1.partial_cmp(&b2), Some(Ordering::Less));
1793
1794 assert_eq!(s1.partial_cmp(&b1), Some(Ordering::Equal));
1796 assert_eq!(b1.partial_cmp(&s2), Some(Ordering::Less));
1797 }
1798
1799 #[test]
1800 fn celvalue_mismatched_partial_cmp() {
1801 let num = 1i32.conv();
1802 let strv = "a".conv();
1803 assert_eq!(num.partial_cmp(&strv), None);
1804 assert_eq!(strv.partial_cmp(&num), None);
1805
1806 let binding = Vec::<i32>::new();
1807 let list = (&binding).conv();
1808 let map = CelValue::Map(Arc::from(vec![]));
1809 assert_eq!(list.partial_cmp(&map), None);
1810 }
1811
1812 fn make_list(vals: &[i32]) -> CelValue<'static> {
1814 let items: Vec<_> = vals.iter().map(|&n| n.conv()).collect();
1815 CelValue::List(Arc::from(items))
1816 }
1817
1818 fn make_map(pairs: &[(i32, i32)]) -> CelValue<'static> {
1819 let items: Vec<_> = pairs.iter().map(|&(k, v)| (k.conv(), v.conv())).collect();
1820 CelValue::Map(Arc::from(items))
1821 }
1822
1823 #[test]
1824 fn celvalue_pos_neg_ints() {
1825 let num = CelValue::Number(NumberTy::I64(42));
1826 assert_eq!(num.as_number(), Some(NumberTy::I64(42)));
1827
1828 let neg = CelValue::cel_neg(5i32);
1829 assert_eq!(neg.unwrap(), CelValue::Number(NumberTy::I64(-5)));
1830
1831 let err = CelValue::cel_neg("foo").unwrap_err();
1832 matches!(err, CelError::BadUnaryOperation { op: "-", .. });
1833 }
1834
1835 #[test]
1836 fn celvalue_map_keys() {
1837 let map = make_map(&[(1, 10), (2, 20)]);
1838 let v = CelValue::cel_access(map.clone(), 2i32).unwrap();
1839 assert_eq!(v, 20i32.conv());
1840
1841 let err = CelValue::cel_access(map, 3i32).unwrap_err();
1842 matches!(err, CelError::MapKeyNotFound(k) if k == 3i32.conv());
1843 }
1844
1845 #[test]
1846 fn celvalue_list_access() {
1847 let list = make_list(&[100, 200, 300]);
1848 let v = CelValue::cel_access(list.clone(), 1u32).unwrap();
1849 assert_eq!(v, 200i32.conv());
1850
1851 let err = CelValue::cel_access(list.clone(), 5i32).unwrap_err();
1852 matches!(err, CelError::IndexOutOfBounds(5, 3));
1853
1854 let err2 = CelValue::cel_access(list, "not_index").unwrap_err();
1855 matches!(err2, CelError::IndexWithBadIndex(k) if k == "not_index".conv());
1856 }
1857
1858 #[test]
1859 fn celvalue_bad_access() {
1860 let s = "hello".conv();
1861 let err = CelValue::cel_access(s.clone(), 0i32).unwrap_err();
1862 matches!(err, CelError::BadAccess { member, container } if member == 0i32.conv() && container == s);
1863 }
1864
1865 #[test]
1866 fn celvalue_add() {
1867 assert_eq!(CelValue::cel_add(3i32, 4i32).unwrap(), 7i32.conv());
1869 let s = CelValue::cel_add("foo", "bar").unwrap();
1871 assert_eq!(s, CelValue::String(CelString::Owned(Arc::from("foobar"))));
1872 let b = CelValue::cel_add(Bytes::from_static(b"ab"), Bytes::from_static(b"cd")).unwrap();
1874 assert_eq!(b, CelValue::Bytes(CelBytes::Owned(Bytes::from_static(b"abcd"))));
1875 let l = CelValue::cel_add(make_list(&[1, 2]), make_list(&[3])).unwrap();
1877 assert_eq!(l, make_list(&[1, 2, 3]));
1878 let m1 = make_map(&[(1, 1)]);
1880 let m2 = make_map(&[(2, 2)]);
1881 let m3 = CelValue::cel_add(m1.clone(), m2.clone()).unwrap();
1882 assert_eq!(m3, make_map(&[(1, 1), (2, 2)]));
1883 let err = CelValue::cel_add(1i32, "x").unwrap_err();
1885 matches!(err, CelError::BadOperation { op: "+", .. });
1886 }
1887
1888 #[test]
1889 fn celvalue_sub_mul_div_rem() {
1890 assert_eq!(CelValue::cel_sub(10i32, 3i32).unwrap(), 7i32.conv());
1892 assert!(matches!(
1893 CelValue::cel_sub(1i32, "x").unwrap_err(),
1894 CelError::BadOperation { op: "-", .. }
1895 ));
1896 assert_eq!(CelValue::cel_mul(6i32, 7i32).unwrap(), 42i32.conv());
1898 assert!(matches!(
1899 CelValue::cel_mul("a", 2i32).unwrap_err(),
1900 CelError::BadOperation { op: "*", .. }
1901 ));
1902 assert_eq!(CelValue::cel_div(8i32, 2i32).unwrap(), 4i32.conv());
1904 assert!(matches!(
1905 CelValue::cel_div(8i32, "x").unwrap_err(),
1906 CelError::BadOperation { op: "/", .. }
1907 ));
1908 assert_eq!(CelValue::cel_rem(9i32, 4i32).unwrap(), 1i32.conv());
1910 assert!(matches!(
1911 CelValue::cel_rem("a", 1i32).unwrap_err(),
1912 CelError::BadOperation { op: "%", .. }
1913 ));
1914 }
1915
1916 fn as_map(pairs: &[(i32, i32)]) -> CelValue<'static> {
1918 let items: Vec<_> = pairs.iter().map(|&(k, v)| (k.conv(), v.conv())).collect();
1919 CelValue::Map(Arc::from(items))
1920 }
1921
1922 #[test]
1923 fn celvalue_neq() {
1924 assert!(CelValue::cel_neq(1i32, 2i32).unwrap());
1925 assert!(!CelValue::cel_neq("foo", "foo").unwrap());
1926 }
1927
1928 #[test]
1929 fn celvalue_in_and_contains_ints() {
1930 let list = [1, 2, 3].conv();
1931 assert!(CelValue::cel_in(2i32, &list).unwrap());
1932 assert!(!CelValue::cel_in(4i32, &list).unwrap());
1933
1934 let map = as_map(&[(10, 100), (20, 200)]);
1935 assert!(CelValue::cel_in(10i32, &map).unwrap());
1936 assert!(!CelValue::cel_in(30i32, &map).unwrap());
1937
1938 assert!(CelValue::cel_contains(&list, 3i32).unwrap());
1940 assert!(!CelValue::cel_contains(&map, 30i32).unwrap());
1941 }
1942
1943 #[test]
1944 fn celvalue_contains_bad_operation() {
1945 let err = CelValue::cel_contains(1i32, "foo").unwrap_err();
1946 if let CelError::BadOperation { left, right, op } = err {
1947 assert_eq!(op, "contains");
1948 assert_eq!(left, 1i32.conv());
1949 assert_eq!(right, "foo".conv());
1950 } else {
1951 panic!("expected CelError::BadOperation with op=\"contains\"");
1952 }
1953 }
1954
1955 #[test]
1956 fn celvalue_in_and_contains_bytes() {
1957 let s = "hello world";
1958 let b = Bytes::from_static(b"hello world");
1959 let b_again = Bytes::from_static(b"hello world");
1960
1961 assert!(CelValue::cel_in("world", s).unwrap());
1963 assert!(CelValue::cel_in(Bytes::from_static(b"wor"), b).unwrap());
1964
1965 assert!(CelValue::cel_contains(s, "lo wo").unwrap());
1967 assert!(CelValue::cel_contains(b_again, Bytes::from_static(b"lo")).unwrap());
1968
1969 assert!(!CelValue::cel_in("abc", s).unwrap());
1971 assert!(!CelValue::cel_contains(s, "xyz").unwrap());
1972 }
1973
1974 #[test]
1975 fn celvalue_in_and_contains_bad_operations() {
1976 let err = CelValue::cel_in(1i32, "foo").unwrap_err();
1977 match err {
1978 CelError::BadOperation { op, .. } => assert_eq!(op, "in"),
1979 _ => panic!("Expected BadOperation"),
1980 }
1981
1982 let err2 = CelValue::cel_contains(1i32, "foo").unwrap_err();
1983 match err2 {
1984 CelError::BadOperation { op, .. } => assert_eq!(op, "contains"),
1985 _ => panic!("Expected BadOperation contains"),
1986 }
1987 }
1988
1989 #[test]
1990 fn celvalue_starts_with_and_ends_with() {
1991 assert!(CelValue::cel_starts_with("rustacean", "rust").unwrap());
1993 assert!(CelValue::cel_ends_with("rustacean", "acean").unwrap());
1994
1995 let b = Bytes::from_static(b"0123456");
1997 let b_again = Bytes::from_static(b"0123456");
1998 assert!(CelValue::cel_starts_with(b, Bytes::from_static(b"01")).unwrap());
1999 assert!(CelValue::cel_ends_with(b_again, Bytes::from_static(b"56")).unwrap());
2000
2001 let e1 = CelValue::cel_starts_with(123i32, "1").unwrap_err();
2003 assert!(matches!(e1, CelError::BadOperation { op, .. } if op=="startsWith"));
2004 let e2 = CelValue::cel_ends_with(123i32, "1").unwrap_err();
2005 assert!(matches!(e2, CelError::BadOperation { op, .. } if op=="startsWith"));
2006 }
2007
2008 #[test]
2009 fn celvalue_matches() {
2010 let re = Regex::new(r"^a.*z$").unwrap();
2011 assert!(CelValue::cel_matches("abcz", &re).unwrap());
2012
2013 let b = Bytes::from_static(b"abcz");
2014 assert!(CelValue::cel_matches(b, &re).unwrap());
2015
2016 let bad = CelValue::cel_matches(Bytes::from_static(&[0xff, 0xfe]), &Regex::new(".*").unwrap()).unwrap();
2018 assert!(!bad);
2019
2020 let err = CelValue::cel_matches(1i32, &re).unwrap_err();
2021 assert!(matches!(err, CelError::BadUnaryOperation { op, .. } if op=="matches"));
2022 }
2023
2024 #[test]
2025 fn celvalue_ip_and_uuid_hostname_uri_email() {
2026 assert!(CelValue::cel_is_ipv4("127.0.0.1").unwrap());
2028 assert!(CelValue::cel_is_ipv4(Bytes::from_static(&[127, 0, 0, 1])).unwrap());
2029 assert!(!CelValue::cel_is_ipv4(Bytes::from_static(b"notip")).unwrap());
2030 assert!(matches!(
2031 CelValue::cel_is_ipv4(true).unwrap_err(),
2032 CelError::BadUnaryOperation { op, .. } if op == "isIpv4"
2033 ));
2034
2035 assert!(CelValue::cel_is_ipv6("::1").unwrap());
2037 let octets = [0u8; 16];
2038 assert!(CelValue::cel_is_ipv6(&octets).unwrap());
2039 assert!(!CelValue::cel_is_ipv6(Bytes::from_static(b"bad")).unwrap());
2040 assert!(matches!(
2041 CelValue::cel_is_ipv6(1i32).unwrap_err(),
2042 CelError::BadUnaryOperation { op, .. } if op == "isIpv6"
2043 ));
2044
2045 let uuid_str_nil = Uuid::nil().to_string();
2047 assert!(CelValue::cel_is_uuid(&uuid_str_nil).unwrap());
2048 let uuid_str_max = Uuid::max().to_string();
2049 assert!(CelValue::cel_is_uuid(&uuid_str_max).unwrap());
2050
2051 let mut bytes16 = [0u8; 16];
2052 bytes16[0] = 1;
2053 assert!(CelValue::cel_is_uuid(&bytes16).unwrap());
2054 assert!(!CelValue::cel_is_uuid(Bytes::from_static(b"short")).unwrap());
2055 assert!(matches!(
2056 CelValue::cel_is_uuid(1i32).unwrap_err(),
2057 CelError::BadUnaryOperation { op, .. } if op == "isUuid"
2058 ));
2059
2060 assert!(CelValue::cel_is_hostname("example.com").unwrap());
2062 assert!(!CelValue::cel_is_hostname("not valid!").unwrap());
2063 assert!(matches!(
2064 CelValue::cel_is_hostname(1i32).unwrap_err(),
2065 CelError::BadUnaryOperation { op, .. } if op == "isHostname"
2066 ));
2067
2068 assert!(CelValue::cel_is_uri("https://rust-lang.org").unwrap());
2070 assert!(!CelValue::cel_is_uri(Bytes::from_static(b":bad")).unwrap());
2071 assert!(matches!(
2072 CelValue::cel_is_uri(1i32).unwrap_err(),
2073 CelError::BadUnaryOperation { op, .. } if op == "isUri"
2074 ));
2075
2076 assert!(CelValue::cel_is_email("user@example.com").unwrap());
2078 assert!(!CelValue::cel_is_email(Bytes::from_static(b"noatsign")).unwrap());
2079 assert!(matches!(
2080 CelValue::cel_is_email(1i32).unwrap_err(),
2081 CelError::BadUnaryOperation { op, .. } if op == "isEmail"
2082 ));
2083 }
2084
2085 #[test]
2086 fn celvalue_ipv4_invalid() {
2087 let invalid = Bytes::from_static(&[0xff, 0xfe, 0xff, 0xff, 0xff]);
2088 let result = CelValue::cel_is_ipv4(invalid).unwrap();
2089 assert!(!result, "Expected false for non-UTF8, non-4-byte input");
2090 }
2091
2092 #[test]
2093 fn celvalue_ipv6_invalid() {
2094 let invalid = Bytes::from_static(&[0xff, 0xfe, 0xff]);
2095 let result = CelValue::cel_is_ipv6(invalid).unwrap();
2096 assert!(!result, "Expected false for non-UTF8, non-16-byte input");
2097 }
2098
2099 #[test]
2100 fn celvalue_uuid_invalid() {
2101 let invalid = Bytes::from_static(&[0xff, 0xfe, 0xff]);
2103 let result = CelValue::cel_is_uuid(invalid).unwrap();
2104 assert!(!result, "Expected false for non-UTF8, non-16-byte input");
2105 }
2106
2107 #[test]
2108 fn celvalue_hostname_invalid() {
2109 let valid = CelValue::cel_is_hostname(Bytes::from_static(b"example.com")).unwrap();
2110 assert!(valid, "Expected true for valid hostname bytes");
2111
2112 let invalid = CelValue::cel_is_hostname(Bytes::from_static(&[0xff, 0xfe, 0xff])).unwrap();
2113 assert!(!invalid, "Expected false for invalid UTF-8 bytes");
2114 }
2115
2116 #[test]
2117 fn celvalue_uri_invalid() {
2118 let invalid = Bytes::from_static(&[0xff, 0xfe, 0xff]);
2119 let result = CelValue::cel_is_uri(invalid).unwrap();
2120 assert!(!result, "Expected false for invalid UTF-8 uri bytes");
2121 }
2122
2123 #[test]
2124 fn celvalue_email_invalid() {
2125 let invalid = Bytes::from_static(&[0xff, 0xfe, 0xff]);
2126 let result = CelValue::cel_is_email(invalid).unwrap();
2127 assert!(!result, "Expected false for invalid UTF-8 email bytes");
2128 }
2129
2130 #[test]
2131 fn celvalue_is_nan() {
2132 assert!(
2133 !CelValue::cel_is_nan(NumberTy::from(2.0)).unwrap(),
2134 "Expected false for valid number"
2135 );
2136 assert!(
2137 !CelValue::cel_is_nan(NumberTy::from(5)).unwrap(),
2138 "Expected false for valid number"
2139 );
2140 assert!(
2141 !CelValue::cel_is_nan(NumberTy::from(13u64)).unwrap(),
2142 "Expected false for valid number"
2143 );
2144 assert!(
2145 !CelValue::cel_is_nan(NumberTy::from(f64::INFINITY)).unwrap(),
2146 "Expected false for infinity"
2147 );
2148 assert!(
2149 !CelValue::cel_is_nan(NumberTy::from(f64::NEG_INFINITY)).unwrap(),
2150 "Expected false for neg infinity"
2151 );
2152 assert!(
2153 CelValue::cel_is_nan(NumberTy::from(f64::NAN)).unwrap(),
2154 "Expected true for nan"
2155 );
2156 assert!(matches!(
2157 CelValue::cel_is_nan("str").unwrap_err(),
2158 CelError::BadUnaryOperation { op, .. } if op == "isNaN"
2159 ));
2160 }
2161
2162 #[test]
2163 fn celvalue_is_inf() {
2164 assert!(
2165 !CelValue::cel_is_inf(NumberTy::from(2.0)).unwrap(),
2166 "Expected false for valid number"
2167 );
2168 assert!(
2169 !CelValue::cel_is_inf(NumberTy::from(5)).unwrap(),
2170 "Expected false for valid number"
2171 );
2172 assert!(
2173 !CelValue::cel_is_nan(NumberTy::from(13u64)).unwrap(),
2174 "Expected false for valid number"
2175 );
2176 assert!(
2177 CelValue::cel_is_inf(NumberTy::from(f64::INFINITY)).unwrap(),
2178 "Expected true for infinity"
2179 );
2180 assert!(
2181 CelValue::cel_is_inf(NumberTy::from(f64::NEG_INFINITY)).unwrap(),
2182 "Expected true for neg infinity"
2183 );
2184 assert!(
2185 !CelValue::cel_is_inf(NumberTy::from(f64::NAN)).unwrap(),
2186 "Expected false for nan"
2187 );
2188 assert!(matches!(
2189 CelValue::cel_is_inf("str").unwrap_err(),
2190 CelError::BadUnaryOperation { op, .. } if op == "isInf"
2191 ));
2192 }
2193
2194 #[test]
2195 fn celvalue_size() {
2196 assert_eq!(CelValue::cel_size(Bytes::from_static(b"abc")).unwrap(), 3);
2197 assert_eq!(CelValue::cel_size("hello").unwrap(), 5);
2198 assert_eq!(CelValue::cel_size([1, 2, 3].conv()).unwrap(), 3);
2199 assert_eq!(CelValue::cel_size(as_map(&[(1, 1), (2, 2)])).unwrap(), 2);
2200
2201 let err = CelValue::cel_size(123i32).unwrap_err();
2202 assert!(matches!(err, CelError::BadUnaryOperation { op, .. } if op=="size"));
2203 }
2204
2205 #[test]
2206 fn celvalue_map_and_filter() {
2207 let m = CelValue::cel_map([1, 2, 3].conv(), |v| {
2209 let n = v.as_number().unwrap().to_i64().unwrap();
2210 Ok((n * 2).conv())
2211 })
2212 .unwrap();
2213 assert_eq!(m, [2, 4, 6].conv());
2214
2215 let keys = CelValue::cel_map(as_map(&[(10, 100), (20, 200)]), Ok).unwrap();
2217 assert_eq!(keys, [10, 20].conv());
2218
2219 let f =
2221 CelValue::cel_filter([1, 2, 3, 4].conv(), |v| Ok(v.as_number().unwrap().to_i64().unwrap() % 2 == 0)).unwrap();
2222 assert_eq!(f, [2, 4].conv());
2223
2224 let fk = CelValue::cel_filter(as_map(&[(7, 70), (8, 80)]), |v| {
2226 Ok(v.as_number().unwrap().to_i64().unwrap() == 8)
2227 })
2228 .unwrap();
2229 assert_eq!(fk, [8].conv());
2230
2231 let err_map = CelValue::cel_map(1i32, |_| Ok(1i32.conv())).unwrap_err();
2233 assert!(matches!(err_map, CelError::BadUnaryOperation { op, .. } if op=="map"));
2234 let err_filter = CelValue::cel_filter(1i32, |_| Ok(true)).unwrap_err();
2235 assert!(matches!(err_filter, CelError::BadUnaryOperation { op, .. } if op=="filter"));
2236 }
2237
2238 #[test]
2239 fn celvalue_list_and_filter() {
2240 let list = [1i32, 2, 3].conv();
2241
2242 let err = CelValue::cel_filter(list, |v| {
2243 if v == 2i32.conv() {
2244 Err(CelError::BadUnaryOperation { op: "test", value: v })
2245 } else {
2246 Ok(true)
2247 }
2248 })
2249 .unwrap_err();
2250
2251 if let CelError::BadUnaryOperation { op, value } = err {
2252 assert_eq!(op, "test");
2253 assert_eq!(value, 2i32.conv());
2254 } else {
2255 panic!("expected BadUnaryOperation from map_fn");
2256 }
2257 }
2258
2259 #[test]
2260 fn celvalue_list_and_map_all() {
2261 let list = [1, 2, 3].conv();
2262 let all_pos = CelValue::cel_all(list.clone(), |v| Ok(v.as_number().unwrap().to_i64().unwrap() > 0)).unwrap();
2263 assert!(all_pos);
2264
2265 let list2 = [1, 0, 3].conv();
2266 let any_zero = CelValue::cel_all(list2, |v| Ok(v.as_number().unwrap().to_i64().unwrap() > 0)).unwrap();
2267 assert!(!any_zero);
2268
2269 let map = as_map(&[(2, 20), (4, 40)]);
2270 let all_keys = CelValue::cel_all(map.clone(), |v| Ok(v.as_number().unwrap().to_i64().unwrap() < 5)).unwrap();
2271 assert!(all_keys);
2272
2273 let map2 = as_map(&[(2, 20), (6, 60)]);
2274 let some_ge5 = CelValue::cel_all(map2, |v| Ok(v.as_number().unwrap().to_i64().unwrap() < 5)).unwrap();
2275 assert!(!some_ge5);
2276 }
2277
2278 #[test]
2279 fn celvalue_list_error_propagation() {
2280 let list = [1, 2, 3].conv();
2281 let err = CelValue::cel_all(list, |v| {
2282 if v == 2i32.conv() {
2283 Err(CelError::BadUnaryOperation {
2284 op: "all_test",
2285 value: v,
2286 })
2287 } else {
2288 Ok(true)
2289 }
2290 })
2291 .unwrap_err();
2292
2293 if let CelError::BadUnaryOperation { op, value } = err {
2294 assert_eq!(op, "all_test");
2295 assert_eq!(value, 2i32.conv());
2296 } else {
2297 panic!("Expected BadUnaryOperation from map_fn");
2298 }
2299 }
2300
2301 #[test]
2302 fn celvalue_all_bad_operation() {
2303 let err = CelValue::cel_all(42i32, |_| Ok(true)).unwrap_err();
2304 if let CelError::BadUnaryOperation { op, value } = err {
2305 assert_eq!(op, "all");
2306 assert_eq!(value, 42i32.conv());
2307 } else {
2308 panic!("Expected BadUnaryOperation with op=\"all\"");
2309 }
2310 }
2311
2312 #[test]
2313 fn celvalue_exists() {
2314 let list = [1, 2, 3].conv();
2315 let result = CelValue::cel_exists(list, |v| Ok(v == 2i32.conv())).unwrap();
2316 assert!(result);
2317 }
2318
2319 #[test]
2320 fn celvalue_exists_list_false() {
2321 let list = [1, 2, 3].conv();
2322 let result = CelValue::cel_exists(list, |_| Ok(false)).unwrap();
2323 assert!(!result);
2324 }
2325
2326 #[test]
2327 fn celvalue_exists_map_true() {
2328 let map = as_map(&[(10, 100), (20, 200)]);
2329 let result = CelValue::cel_exists(map, |v| Ok(v == 20i32.conv())).unwrap();
2330 assert!(result);
2331 }
2332
2333 #[test]
2334 fn celvalue_exists_map_false() {
2335 let map = as_map(&[(10, 100), (20, 200)]);
2336 let result = CelValue::cel_exists(map, |_| Ok(false)).unwrap();
2337 assert!(!result);
2338 }
2339
2340 #[test]
2341 fn celvalue_exists_list_propagates_error() {
2342 let list = [1, 2, 3].conv();
2343 let err = CelValue::cel_exists(list, |v| {
2344 if v == 2i32.conv() {
2345 Err(CelError::BadUnaryOperation {
2346 op: "exists_test",
2347 value: v,
2348 })
2349 } else {
2350 Ok(false)
2351 }
2352 })
2353 .unwrap_err();
2354
2355 if let CelError::BadUnaryOperation { op, value } = err {
2356 assert_eq!(op, "exists_test");
2357 assert_eq!(value, 2i32.conv());
2358 } else {
2359 panic!("Expected BadUnaryOperation from map_fn");
2360 }
2361 }
2362
2363 #[test]
2364 fn celvalue_exists_non_collection_error() {
2365 let err = CelValue::cel_exists(42i32, |_| Ok(true)).unwrap_err();
2366 if let CelError::BadUnaryOperation { op, value } = err {
2367 assert_eq!(op, "existsOne");
2368 assert_eq!(value, 42i32.conv());
2369 } else {
2370 panic!("Expected BadUnaryOperation with op=\"existsOne\"");
2371 }
2372 }
2373
2374 #[test]
2375 fn celvalue_exists_one_list() {
2376 let list = [1, 2, 3].conv();
2377 let result = CelValue::cel_exists_one(list, |v| Ok(v == 2i32.conv())).unwrap();
2378 assert!(result);
2379 }
2380
2381 #[test]
2382 fn celvalue_exists_one_list_zero() {
2383 let list = [1, 2, 3].conv();
2384 let result = CelValue::cel_exists_one(list, |_| Ok(false)).unwrap();
2385 assert!(!result);
2386 }
2387
2388 #[test]
2389 fn celvalue_exists_one_list_multiple() {
2390 let list = [1, 2, 2, 3].conv();
2391 let result = CelValue::cel_exists_one(list, |v| Ok(v == 2i32.conv())).unwrap();
2392 assert!(!result);
2393 }
2394
2395 #[test]
2396 fn celvalue_exists_one_map() {
2397 let map = as_map(&[(10, 100), (20, 200)]);
2398 let result = CelValue::cel_exists_one(map, |v| Ok(v == 20i32.conv())).unwrap();
2399 assert!(result);
2400 }
2401
2402 #[test]
2403 fn celvalue_exists_one_map_zero() {
2404 let map = as_map(&[(10, 100), (20, 200)]);
2405 let result = CelValue::cel_exists_one(map, |_| Ok(false)).unwrap();
2406 assert!(!result);
2407 }
2408
2409 #[test]
2410 fn celvalue_exists_one_map_multiple() {
2411 let map = as_map(&[(1, 10), (1, 20), (2, 30)]);
2412 let result = CelValue::cel_exists_one(map, |v| Ok(v == 1i32.conv())).unwrap();
2413 assert!(!result);
2414 }
2415
2416 #[test]
2417 fn celvalue_exists_one_propagates_error() {
2418 let list = [1, 2, 3].conv();
2419 let err = CelValue::cel_exists_one(list, |v| {
2420 if v == 2i32.conv() {
2421 Err(CelError::BadUnaryOperation {
2422 op: "test_one",
2423 value: v,
2424 })
2425 } else {
2426 Ok(false)
2427 }
2428 })
2429 .unwrap_err();
2430
2431 if let CelError::BadUnaryOperation { op, value } = err {
2432 assert_eq!(op, "test_one");
2433 assert_eq!(value, 2i32.conv());
2434 } else {
2435 panic!("Expected BadUnaryOperation from map_fn");
2436 }
2437 }
2438
2439 #[test]
2440 fn celvalue_exists_one_non_collection_error() {
2441 let err = CelValue::cel_exists_one(42i32, |_| Ok(true)).unwrap_err();
2442 if let CelError::BadUnaryOperation { op, value } = err {
2443 assert_eq!(op, "existsOne");
2444 assert_eq!(value, 42i32.conv());
2445 } else {
2446 panic!("Expected BadUnaryOperation with op=\"existsOne\"");
2447 }
2448 }
2449
2450 #[test]
2451 fn celvalue_to_string_variant_passthrough() {
2452 let original = "hello";
2453 let cv = original.conv();
2454 let out = CelValue::cel_to_string(cv.clone());
2455
2456 assert!(matches!(out, CelValue::String(_)));
2457 assert_eq!(out, cv);
2458 }
2459
2460 #[test]
2461 fn celvalue_to_string_owned_bytes() {
2462 let bytes = Bytes::from_static(b"foo");
2463 let out = CelValue::cel_to_string(bytes.clone());
2464
2465 assert_eq!(out, CelValue::String(CelString::Owned(Arc::from("foo"))));
2466 }
2467
2468 #[test]
2469 fn celvalue_to_string_borrowed_bytes() {
2470 let slice: &[u8] = b"bar";
2471 let out = CelValue::cel_to_string(slice);
2472
2473 match out {
2474 CelValue::String(CelString::Borrowed(s)) => assert_eq!(s, "bar"),
2475 _ => panic!("expected Borrowed variant"),
2476 }
2477 }
2478
2479 #[test]
2480 fn celvalue_to_string_borrowed_bytes_invalid_utf8_to_owned() {
2481 let slice: &[u8] = &[0xff, 0xfe];
2482 let out = CelValue::cel_to_string(slice);
2483
2484 match out {
2485 CelValue::String(CelString::Owned(o)) => {
2486 assert_eq!(o.as_ref(), "\u{FFFD}\u{FFFD}");
2487 }
2488 _ => panic!("expected Owned variant"),
2489 }
2490 }
2491
2492 #[test]
2493 fn celvalue_to_string_num_and_bool() {
2494 let out_num = CelValue::cel_to_string(42i32);
2495 assert_eq!(out_num, CelValue::String(CelString::Owned(Arc::from("42"))));
2496
2497 let out_bool = CelValue::cel_to_string(true);
2498 assert_eq!(out_bool, CelValue::String(CelString::Owned(Arc::from("true"))));
2499 }
2500
2501 #[test]
2502 fn celvalue_to_bytes_variant_passthrough() {
2503 let bytes = Bytes::from_static(b"xyz");
2504 let cv = CelValue::cel_to_bytes(bytes.clone()).unwrap();
2505 match cv {
2506 CelValue::Bytes(CelBytes::Owned(b)) => assert_eq!(b, bytes),
2507 _ => panic!("expected Owned bytes passthrough"),
2508 }
2509 }
2510
2511 #[test]
2512 fn celvalue_to_bytes_from_owned_string() {
2513 let owned_str = CelString::Owned(Arc::from("hello"));
2514 let cv_in = CelValue::String(owned_str.clone());
2515 let cv = CelValue::cel_to_bytes(cv_in).unwrap();
2516 match cv {
2517 CelValue::Bytes(CelBytes::Owned(b)) => {
2518 assert_eq!(b.as_ref(), b"hello");
2519 }
2520 _ => panic!("expected Owned bytes from Owned string"),
2521 }
2522 }
2523
2524 #[test]
2525 fn celvalue_to_bytes_from_borrowed_string() {
2526 let s = "world";
2527 let cv = CelValue::cel_to_bytes(s).unwrap();
2528 match cv {
2529 CelValue::Bytes(CelBytes::Borrowed(b)) => {
2530 assert_eq!(b, b"world");
2531 }
2532 _ => panic!("expected Borrowed bytes from Borrowed string"),
2533 }
2534 }
2535
2536 #[test]
2537 fn celvalue_error_on_non_string_bytes() {
2538 let err = CelValue::cel_to_bytes(123i32).unwrap_err();
2539 if let CelError::BadUnaryOperation { op, value } = err {
2540 assert_eq!(op, "bytes");
2541 assert_eq!(value, 123i32.conv());
2542 } else {
2543 panic!("expected BadUnaryOperation for non-bytes/string");
2544 }
2545 }
2546
2547 #[test]
2548 fn celvalue_to_int_from_string() {
2549 let result = CelValue::cel_to_int("123").unwrap();
2550 assert_eq!(result, CelValue::Number(NumberTy::I64(123)));
2551 }
2552
2553 #[test]
2554 fn celvalue_to_int_from_nan() {
2555 let result = CelValue::cel_to_int("not_a_number").unwrap();
2556 assert_eq!(result, CelValue::Null);
2557 }
2558
2559 #[test]
2560 fn celvalue_to_int_from_float() {
2561 let result = CelValue::cel_to_int(3.99f64).unwrap();
2562 assert_eq!(result, CelValue::Number(NumberTy::I64(3)));
2563 }
2564
2565 #[test]
2566 fn celvalue_to_int_too_large() {
2567 let large = u64::MAX.conv();
2568 let result = CelValue::cel_to_int(large).unwrap();
2569 assert_eq!(result, CelValue::Null);
2570 }
2571
2572 #[test]
2573 fn celvalue_to_int_from_bytes_bad_operation() {
2574 let err = CelValue::cel_to_int(&[1, 2, 3][..]).unwrap_err();
2575 if let CelError::BadUnaryOperation { op, value } = err {
2576 assert_eq!(op, "int");
2577 assert_eq!(value, (&[1, 2, 3][..]).conv());
2578 } else {
2579 panic!("Expected BadUnaryOperation for non-string/number");
2580 }
2581 }
2582
2583 #[test]
2584 fn celvalue_to_uint_from_string() {
2585 let result = CelValue::cel_to_uint("456").unwrap();
2586 assert_eq!(result, CelValue::Number(NumberTy::U64(456)));
2587 }
2588
2589 #[test]
2590 fn celvalue_to_uint_from_nan() {
2591 let result = CelValue::cel_to_uint("not_uint").unwrap();
2592 assert_eq!(result, CelValue::Null);
2593 }
2594
2595 #[test]
2596 fn celvalue_to_uint_from_int_float_uint() {
2597 let result_i = CelValue::cel_to_uint(42i32).unwrap();
2598 assert_eq!(result_i, CelValue::Number(NumberTy::U64(42)));
2599
2600 let result_f = CelValue::cel_to_uint(3.7f64).unwrap();
2601 assert_eq!(result_f, CelValue::Number(NumberTy::U64(3)));
2602
2603 let result_u = CelValue::cel_to_uint(100u64).unwrap();
2604 assert_eq!(result_u, CelValue::Number(NumberTy::U64(100)));
2605 }
2606
2607 #[test]
2608 fn celvalue_to_uint_neg_and_too_large() {
2609 let result_neg = CelValue::cel_to_uint(-5i32).unwrap();
2610 assert_eq!(result_neg, CelValue::Null);
2611
2612 let big = f64::INFINITY;
2613 let result_inf = CelValue::cel_to_uint(big).unwrap();
2614 assert_eq!(result_inf, CelValue::Null);
2615 }
2616
2617 #[test]
2618 fn celvalue_to_uint_from_bytes_bad_operation() {
2619 let err = CelValue::cel_to_uint(&[1, 2, 3][..]).unwrap_err();
2620 if let CelError::BadUnaryOperation { op, value } = err {
2621 assert_eq!(op, "uint");
2622 assert_eq!(value, (&[1, 2, 3][..]).conv());
2623 } else {
2624 panic!("Expected BadUnaryOperation for non-string/number");
2625 }
2626 }
2627
2628 #[test]
2629 fn celvalue_to_double_from_string_valid() {
2630 let result = CelValue::cel_to_double("3.141592653589793").unwrap();
2631 assert_eq!(result, CelValue::Number(NumberTy::F64(std::f64::consts::PI)));
2632 }
2633
2634 #[test]
2635 fn celvalue_to_double_from_string_invalid_returns_null() {
2636 let result = CelValue::cel_to_double("not_a_double").unwrap();
2637 assert_eq!(result, CelValue::Null);
2638 }
2639
2640 #[test]
2641 fn celvalue_to_double_from_integer_number() {
2642 let result = CelValue::cel_to_double(42i32).unwrap();
2643 assert_eq!(result, CelValue::Number(NumberTy::F64(42.0)));
2644 }
2645
2646 #[test]
2647 fn celvalue_to_double_from_f64_number() {
2648 let result = CelValue::cel_to_double(std::f64::consts::PI).unwrap();
2649 assert_eq!(result, CelValue::Number(NumberTy::F64(std::f64::consts::PI)));
2650 }
2651
2652 #[test]
2653 fn celvalue_to_double_from_nan() {
2654 let err = CelValue::cel_to_double(&[1, 2, 3][..]).unwrap_err();
2655 if let CelError::BadUnaryOperation { op, value } = err {
2656 assert_eq!(op, "double");
2657 assert_eq!(value, (&[1, 2, 3][..]).conv());
2658 } else {
2659 panic!("Expected BadUnaryOperation for non-string/number");
2660 }
2661 }
2662
2663 #[test]
2664 fn celvalue_to_enum_from_number_and_string() {
2665 let v = CelValue::cel_to_enum(10i32, "MyEnum").unwrap();
2666 assert_eq!(v, CelValue::Enum(CelEnum::new("MyEnum".into(), 10)));
2667 }
2668
2669 #[test]
2670 fn celvalue_to_enum_number_out_of_range() {
2671 let overflow = i32::MAX as i64 + 1;
2672 let v = CelValue::cel_to_enum(overflow, "Tag").unwrap();
2673 assert_eq!(v, CelValue::Null);
2674 }
2675
2676 #[test]
2677 fn celvalue_to_enum_from_enum_and_string() {
2678 let original = CelValue::Enum(CelEnum::new("Orig".into(), 42));
2679 let v = CelValue::cel_to_enum(original.clone(), "NewTag").unwrap();
2680 assert_eq!(v, CelValue::Enum(CelEnum::new("NewTag".into(), 42)));
2681 }
2682
2683 #[test]
2684 fn celvalue_to_enum_bad_operation_for_invalid_inputs() {
2685 let err = CelValue::cel_to_enum(true, 123i32).unwrap_err();
2686 if let CelError::BadOperation { op, left, right } = err {
2687 assert_eq!(op, "enum");
2688 assert_eq!(left, true.conv());
2689 assert_eq!(right, 123i32.conv());
2690 } else {
2691 panic!("Expected BadOperation for invalid cel_to_enum inputs");
2692 }
2693 }
2694
2695 #[test]
2696 fn celvalue_eq_bool_variants() {
2697 assert_eq!(CelValue::Bool(true), CelValue::Bool(true));
2698 assert_ne!(CelValue::Bool(true), CelValue::Bool(false));
2699 }
2700
2701 #[test]
2702 fn celvalue_eq_string_and_bytes_variants() {
2703 let s1 = "abc".conv();
2704 let s2 = "abc".conv();
2705 let b1 = Bytes::from_static(b"abc").conv();
2706 let b2 = Bytes::from_static(b"abc").conv();
2707 assert_eq!(s1, s2);
2708 assert_eq!(b1, b2);
2709
2710 assert_eq!(s1.clone(), b1.clone());
2711 assert_eq!(b1, s2);
2712 }
2713
2714 #[test]
2715 fn celvalue_eq_duration_and_number() {
2716 let dur = CelValue::Duration(chrono::Duration::seconds(5));
2717 let num = 5i32.conv();
2718
2719 assert_eq!(dur.clone(), num.clone());
2720 assert_eq!(num, dur);
2721 }
2722
2723 #[test]
2724 fn celvalue_eq_duration_variants() {
2725 use chrono::Duration;
2726
2727 let d1 = CelValue::Duration(Duration::seconds(42));
2728 let d2 = CelValue::Duration(Duration::seconds(42));
2729 let d3 = CelValue::Duration(Duration::seconds(43));
2730
2731 assert_eq!(d1, d2, "Two identical Durations should be equal");
2732 assert_ne!(d1, d3, "Different Durations should not be equal");
2733 }
2734
2735 #[test]
2736 fn celvalue_eq_timestamp_variants() {
2737 use chrono::{DateTime, FixedOffset};
2738
2739 let dt1: DateTime<FixedOffset> = DateTime::parse_from_rfc3339("2021-01-01T12:00:00+00:00").unwrap();
2740 let dt2: DateTime<FixedOffset> = DateTime::parse_from_rfc3339("2021-01-01T12:00:00+00:00").unwrap();
2741
2742 let t1 = CelValue::Timestamp(dt1);
2743 let t2 = CelValue::Timestamp(dt2);
2744 assert_eq!(t1, t2);
2745 }
2746
2747 #[test]
2748 fn celvalue_eq_enum_and_number_variants() {
2749 let e = CelValue::Enum(CelEnum::new("Tag".into(), 42));
2750 let n = 42i32.conv();
2751
2752 assert_eq!(e.clone(), n.clone());
2753 assert_eq!(n, e);
2754 }
2755
2756 #[test]
2757 fn celvalue_eq_list_and_map_variants() {
2758 let list1 = (&[1, 2, 3][..]).conv();
2759 let list2 = (&[1, 2, 3][..]).conv();
2760 assert_eq!(list1, list2);
2761
2762 let map1 = CelValue::Map(Arc::from(vec![(1i32.conv(), 10i32.conv()), (2i32.conv(), 20i32.conv())]));
2763 let map2 = CelValue::Map(Arc::from(vec![(1i32.conv(), 10i32.conv()), (2i32.conv(), 20i32.conv())]));
2764 assert_eq!(map1, map2);
2765 }
2766
2767 #[test]
2768 fn celvalue_eq_number_and_null_variants() {
2769 assert_eq!(1i32.conv(), 1i32.conv());
2770 assert_ne!(1i32.conv(), 2i32.conv());
2771 assert_eq!(CelValue::Null, CelValue::Null);
2772 }
2773
2774 #[test]
2775 fn celvalue_eq_mismatched_variants() {
2776 assert_ne!(CelValue::Bool(true), 1i32.conv());
2777 assert_ne!(
2778 CelValue::List(Arc::from(vec![].into_boxed_slice())),
2779 CelValue::Map(Arc::from(vec![].into_boxed_slice()))
2780 );
2781 }
2782
2783 #[test]
2784 fn celvalue_conv_unit_conv() {
2785 let v: CelValue = ().conv();
2786 assert_eq!(v, CelValue::Null);
2787 }
2788
2789 #[test]
2790 fn celvalue_display() {
2791 let ts: DateTime<FixedOffset> = DateTime::parse_from_rfc3339("2025-05-04T00:00:00+00:00").unwrap();
2792
2793 let map_val = CelValue::Map(Arc::from(vec![(1i32.conv(), "x".conv()), (2i32.conv(), "y".conv())]));
2795
2796 let outputs = vec![
2797 format!("{}", CelValue::Bool(false)),
2798 format!("{}", 42i32.conv()),
2799 format!("{}", "foo".conv()),
2800 format!("{}", Bytes::from_static(b"bar").conv()),
2801 format!("{}", (&[1, 2, 3][..]).conv()),
2802 format!("{}", CelValue::Null),
2803 format!("{}", CelValue::Duration(Duration::seconds(5))),
2804 format!("{}", CelValue::Timestamp(ts)),
2805 format!("{}", map_val),
2806 ]
2807 .join("\n");
2808
2809 insta::assert_snapshot!(outputs, @r###"
2810 false
2811 42
2812 foo
2813 [98, 97, 114]
2814 [1, 2, 3]
2815 null
2816 PT5S
2817 2025-05-04 00:00:00 +00:00
2818 {1: x, 2: y}
2819 "###);
2820 }
2821
2822 #[cfg(feature = "runtime")]
2823 #[test]
2824 fn celvalue_display_enum_runtime() {
2825 use crate::CelMode;
2826
2827 CelMode::set(CelMode::Proto);
2828
2829 let enum_val = CelValue::Enum(CelEnum::new(CelString::Owned("MyTag".into()), 123));
2830 assert_eq!(format!("{enum_val}"), "123");
2831
2832 CelMode::set(CelMode::Serde);
2833 let enum_val_json = CelValue::Enum(CelEnum::new(CelString::Owned("MyTag".into()), 456));
2834 assert_eq!(format!("{enum_val_json}"), "456");
2835 }
2836
2837 #[test]
2838 fn celvalue_to_bool_all_variants() {
2839 assert!(CelValue::Bool(true).to_bool());
2841 assert!(!CelValue::Bool(false).to_bool());
2842
2843 assert!(42i32.conv().to_bool());
2845 assert!(!0i32.conv().to_bool());
2846
2847 assert!(CelValue::String(CelString::Borrowed("hello")).to_bool());
2849 assert!(!CelValue::String(CelString::Borrowed("")).to_bool());
2850
2851 assert!(Bytes::from_static(b"x").conv().to_bool());
2853 assert!(!Bytes::from_static(b"").conv().to_bool());
2854
2855 let non_empty_list = (&[1, 2, 3][..]).conv();
2857 assert!(non_empty_list.to_bool());
2858 let empty_list = CelValue::List(Arc::from(Vec::<CelValue>::new().into_boxed_slice()));
2859 assert!(!empty_list.to_bool());
2860
2861 let non_empty_map = CelValue::Map(Arc::from(vec![(1i32.conv(), 2i32.conv())]));
2863 assert!(non_empty_map.to_bool());
2864 let empty_map = CelValue::Map(Arc::from(Vec::<(CelValue, CelValue)>::new().into_boxed_slice()));
2865 assert!(!empty_map.to_bool());
2866
2867 assert!(!CelValue::Null.to_bool());
2869
2870 assert!(CelValue::Duration(Duration::seconds(5)).to_bool());
2872 assert!(!CelValue::Duration(Duration::zero()).to_bool());
2873
2874 let epoch: DateTime<FixedOffset> = DateTime::parse_from_rfc3339("1970-01-01T00:00:00+00:00").unwrap();
2876 assert!(!CelValue::Timestamp(epoch).to_bool());
2877 let later: DateTime<FixedOffset> = DateTime::parse_from_rfc3339("2025-05-04T00:00:00+00:00").unwrap();
2878 assert!(CelValue::Timestamp(later).to_bool());
2879 }
2880
2881 #[test]
2882 fn numberty_partial_cmp_i64_variants() {
2883 let a = NumberTy::I64(1);
2884 let b = NumberTy::I64(2);
2885 assert_eq!(a.partial_cmp(&b), Some(Ordering::Less));
2886 assert_eq!(b.partial_cmp(&a), Some(Ordering::Greater));
2887 assert_eq!(a.partial_cmp(&a), Some(Ordering::Equal));
2888 }
2889
2890 #[test]
2891 fn numberty_partial_cmp_u64_variants() {
2892 let a = NumberTy::U64(10);
2893 let b = NumberTy::U64(20);
2894 assert_eq!(a.partial_cmp(&b), Some(Ordering::Less));
2895 assert_eq!(b.partial_cmp(&a), Some(Ordering::Greater));
2896 assert_eq!(b.partial_cmp(&b), Some(Ordering::Equal));
2897 }
2898
2899 #[test]
2900 fn numberty_partial_cmp_mixed_i64_u64() {
2901 let a = NumberTy::I64(3);
2902 let b = NumberTy::U64(4);
2903 assert_eq!(a.partial_cmp(&b), Some(Ordering::Less));
2905 assert_eq!(b.partial_cmp(&a), Some(Ordering::Greater));
2906
2907 let c = NumberTy::I64(5);
2908 let d = NumberTy::U64(5);
2909 assert_eq!(c.partial_cmp(&d), Some(Ordering::Equal));
2910 }
2911
2912 #[test]
2913 fn numberty_partial_cmp_f64_exact_and_order() {
2914 let x = NumberTy::F64(1.23);
2915 let y = NumberTy::F64(1.23);
2916 let z = NumberTy::F64(4.56);
2917
2918 assert_eq!(x.partial_cmp(&y), Some(Ordering::Equal));
2919 assert_eq!(x.partial_cmp(&z), Some(Ordering::Less));
2920 assert_eq!(z.partial_cmp(&x), Some(Ordering::Greater));
2921 }
2922
2923 #[test]
2924 fn numberty_partial_cmp_mixed_f64_and_integer() {
2925 let f = NumberTy::F64(2.0);
2926 let i = NumberTy::I64(2);
2927 assert_eq!(f.partial_cmp(&i), Some(Ordering::Equal));
2929 assert_eq!(i.partial_cmp(&f), Some(Ordering::Equal));
2930 }
2931
2932 #[test]
2933 fn numberty_cel_add_i64_success() {
2934 let a = NumberTy::I64(5);
2935 let b = NumberTy::I64(7);
2936 assert_eq!(a.cel_add(b).unwrap(), NumberTy::I64(12));
2937 }
2938
2939 #[test]
2940 fn numberty_cel_add_i64_overflow_errors() {
2941 let a = NumberTy::I64(i64::MAX);
2942 let b = NumberTy::I64(1);
2943 let err = a.cel_add(b).unwrap_err();
2944 assert!(matches!(err, CelError::NumberOutOfRange { op } if op=="addition"));
2945 }
2946
2947 #[test]
2948 fn numberty_cel_add_u64_success() {
2949 let a = NumberTy::U64(10);
2950 let b = NumberTy::U64(20);
2951 assert_eq!(a.cel_add(b).unwrap(), NumberTy::U64(30));
2952 }
2953
2954 #[test]
2955 fn numberty_cel_add_f64_success() {
2956 let a = NumberTy::F64(1.5);
2957 let b = NumberTy::F64(2.25);
2958 assert_eq!(a.cel_add(b).unwrap(), NumberTy::F64(3.75));
2959 }
2960
2961 #[test]
2962 fn numberty_cel_sub_i64_underflow_errors() {
2963 let a = NumberTy::I64(i64::MIN);
2964 let b = NumberTy::I64(1);
2965 let err = a.cel_sub(b).unwrap_err();
2966 assert!(matches!(err, CelError::NumberOutOfRange { op } if op=="subtraction"));
2967 }
2968
2969 #[test]
2970 fn numberty_cel_sub_u64_underflow_errors() {
2971 let a = NumberTy::U64(0);
2972 let b = NumberTy::U64(1);
2973 let err = a.cel_sub(b).unwrap_err();
2974 assert!(matches!(err, CelError::NumberOutOfRange { op } if op=="subtraction"));
2975 }
2976
2977 #[test]
2978 fn numberty_cel_sub_f64_success() {
2979 let a = NumberTy::F64(5.5);
2980 let b = NumberTy::F64(2.25);
2981 assert_eq!(a.cel_sub(b).unwrap(), NumberTy::F64(3.25));
2982 }
2983
2984 #[test]
2985 fn numberty_cel_mul_i64_overflow_errors() {
2986 let a = NumberTy::I64(i64::MAX / 2 + 1);
2987 let b = NumberTy::I64(2);
2988 let err = a.cel_mul(b).unwrap_err();
2989 assert!(matches!(err, CelError::NumberOutOfRange { op } if op=="multiplication"));
2990 }
2991
2992 #[test]
2993 fn numberty_cel_mul_u64_overflow_errors() {
2994 let a = NumberTy::U64(u64::MAX / 2 + 1);
2995 let b = NumberTy::U64(2);
2996 let err = a.cel_mul(b).unwrap_err();
2997 assert!(matches!(err, CelError::NumberOutOfRange { op } if op=="multiplication"));
2998 }
2999
3000 #[test]
3001 fn numberty_cel_mul_f64_success() {
3002 let a = NumberTy::F64(3.0);
3003 let b = NumberTy::F64(2.5);
3004 assert_eq!(a.cel_mul(b).unwrap(), NumberTy::F64(7.5));
3005 }
3006
3007 #[test]
3008 fn numberty_cel_div_by_zero_errors() {
3009 let a = NumberTy::I64(10);
3010 let b = NumberTy::I64(0);
3011 let err = a.cel_div(b).unwrap_err();
3012 assert!(matches!(err, CelError::NumberOutOfRange { op } if op=="division by zero"));
3013 }
3014
3015 #[test]
3016 fn numberty_cel_div_i64_success() {
3017 let a = NumberTy::I64(10);
3018 let b = NumberTy::I64(2);
3019 assert_eq!(a.cel_div(b).unwrap(), NumberTy::I64(5));
3020 }
3021
3022 #[test]
3023 fn numberty_cel_div_u64_success() {
3024 let a = NumberTy::U64(20);
3025 let b = NumberTy::U64(5);
3026 assert_eq!(a.cel_div(b).unwrap(), NumberTy::U64(4));
3027 }
3028
3029 #[test]
3030 fn numberty_cel_div_f64_success() {
3031 let a = NumberTy::F64(9.0);
3032 let b = NumberTy::F64(2.0);
3033 assert_eq!(a.cel_div(b).unwrap(), NumberTy::F64(4.5));
3034 }
3035
3036 #[test]
3037 fn numberty_cel_rem_by_zero_errors() {
3038 let a = NumberTy::I64(10);
3039 let b = NumberTy::I64(0);
3040 let err = a.cel_rem(b).unwrap_err();
3041 assert!(matches!(err, CelError::NumberOutOfRange { op } if op=="remainder by zero"));
3042 }
3043
3044 #[test]
3045 fn numberty_cel_rem_i64_success() {
3046 let a = NumberTy::I64(10);
3047 let b = NumberTy::I64(3);
3048 assert_eq!(a.cel_rem(b).unwrap(), NumberTy::I64(1));
3049 }
3050
3051 #[test]
3052 fn numberty_cel_rem_u64_success() {
3053 let a = NumberTy::U64(10);
3054 let b = NumberTy::U64(3);
3055 assert_eq!(a.cel_rem(b).unwrap(), NumberTy::U64(1));
3056 }
3057
3058 #[test]
3059 fn numberty_cel_rem_f64_errors() {
3060 let a = NumberTy::F64(10.0);
3061 let b = NumberTy::F64(3.0);
3062 let err = a.cel_rem(b).unwrap_err();
3063 assert!(matches!(err, CelError::NumberOutOfRange { op } if op=="remainder"));
3064 }
3065
3066 #[test]
3067 fn numberty_cel_neg_i64_success() {
3068 let a = NumberTy::I64(5);
3069 assert_eq!(a.cel_neg().unwrap(), NumberTy::I64(-5));
3070 }
3071
3072 #[test]
3073 fn numberty_cel_neg_i64_overflow_errors() {
3074 let a = NumberTy::I64(i64::MIN);
3075 let err = a.cel_neg().unwrap_err();
3076 assert!(matches!(err, CelError::NumberOutOfRange { op } if op=="negation"));
3077 }
3078
3079 #[test]
3080 fn numberty_cel_neg_u64_success() {
3081 let a = NumberTy::U64(5);
3082 assert_eq!(a.cel_neg().unwrap(), NumberTy::I64(-5));
3083 }
3084
3085 #[test]
3086 fn numberty_cel_neg_u64_overflow_errors() {
3087 let a = NumberTy::U64(1 << 63); let err = a.cel_neg().unwrap_err();
3089 assert!(matches!(err, CelError::NumberOutOfRange { op } if op=="negation"));
3090 }
3091
3092 #[test]
3093 fn numberty_cel_neg_f64_success() {
3094 let a = NumberTy::F64(2.5);
3095 assert_eq!(a.cel_neg().unwrap(), NumberTy::F64(-2.5));
3096 }
3097
3098 #[test]
3099 fn numberty_to_int_success_and_error() {
3100 assert_eq!(NumberTy::I64(42).to_int().unwrap(), NumberTy::I64(42));
3101 let err = NumberTy::F64(f64::INFINITY).to_int().unwrap_err();
3102 assert!(matches!(err, CelError::NumberOutOfRange { op } if op=="int"));
3103 }
3104
3105 #[test]
3106 fn numberty_to_uint_success_and_error() {
3107 assert_eq!(NumberTy::I64(42).to_uint().unwrap(), NumberTy::U64(42));
3108 let err = NumberTy::I64(-1).to_uint().unwrap_err();
3109 assert!(matches!(err, CelError::NumberOutOfRange { op } if op=="int"));
3110 }
3111
3112 #[test]
3113 fn numberty_to_double_always_success() {
3114 assert_eq!(NumberTy::I64(3).to_double().unwrap(), NumberTy::F64(3.0));
3115 assert_eq!(NumberTy::U64(4).to_double().unwrap(), NumberTy::F64(4.0));
3116 assert_eq!(NumberTy::F64(2.5).to_double().unwrap(), NumberTy::F64(2.5));
3117 }
3118
3119 #[test]
3120 fn numberty_from_u32_creates_u64_variant() {
3121 let input: u32 = 123;
3122 let nt: NumberTy = input.into();
3123 assert_eq!(nt, NumberTy::U64(123));
3124 }
3125
3126 #[test]
3127 fn numberty_from_i64_creates_i64_variant() {
3128 let input: i64 = -42;
3129 let nt: NumberTy = input.into();
3130 assert_eq!(nt, NumberTy::I64(-42));
3131 }
3132
3133 #[test]
3134 fn numberty_from_u64_creates_u64_variant() {
3135 let input: u64 = 9876543210;
3136 let nt: NumberTy = input.into();
3137 assert_eq!(nt, NumberTy::U64(9876543210));
3138 }
3139
3140 #[test]
3141 fn numberty_from_f32_matches_raw_cast_to_f64() {
3142 let input: f32 = 1.23;
3143 let expected = input as f64;
3144 let nt: NumberTy = input.into();
3145 match nt {
3146 NumberTy::F64(val) => assert_eq!(val, expected),
3147 _ => panic!("Expected F64 variant"),
3148 }
3149 }
3150
3151 #[test]
3152 fn numberty_conv_wraps_into_celvalue_number() {
3153 let nt = NumberTy::I64(-5);
3154 let cv: CelValue = nt.conv();
3155 assert_eq!(cv, CelValue::Number(NumberTy::I64(-5)));
3156 }
3157
3158 #[test]
3159 fn array_access_valid_index_returns_element() {
3160 let arr = [10, 20, 30];
3161 let v = array_access(&arr, 1u32).unwrap();
3163 assert_eq!(*v, 20);
3164
3165 let v2 = array_access(&arr, 2i64).unwrap();
3167 assert_eq!(*v2, 30);
3168 }
3169
3170 #[test]
3171 fn array_access_index_out_of_bounds_errors() {
3172 let arr = [1, 2];
3173 let err = array_access(&arr, 5i32).unwrap_err();
3174 if let CelError::IndexOutOfBounds(idx, len) = err {
3175 assert_eq!(idx, 5);
3176 assert_eq!(len, 2);
3177 } else {
3178 panic!("Expected IndexOutOfBounds, got {err:?}");
3179 }
3180 }
3181
3182 #[test]
3183 fn array_access_non_numeric_index_errors() {
3184 let arr = [100, 200];
3185 let err = array_access(&arr, "not_a_number").unwrap_err();
3186 if let CelError::IndexWithBadIndex(value) = err {
3187 assert_eq!(value, "not_a_number".conv());
3188 } else {
3189 panic!("Expected IndexWithBadIndex, got {err:?}");
3190 }
3191 }
3192
3193 #[test]
3194 fn celvalue_eq_string_and_string_conv() {
3195 let cv = CelValue::String(CelString::Owned(Arc::from("hello")));
3196 let s = "hello".to_string();
3197 assert_eq!(cv, s);
3198 assert_eq!(s, cv);
3199 }
3200
3201 #[test]
3202 fn celvalue_eq_i32_and_conv() {
3203 let cv = 42i32.conv();
3204 assert_eq!(cv, 42i32);
3205 assert_eq!(42i32, cv);
3206 }
3207
3208 #[test]
3209 fn celvalue_eq_i64_and_conv() {
3210 let cv = 123i64.conv();
3211 assert_eq!(cv, 123i64);
3212 assert_eq!(123i64, cv);
3213 }
3214
3215 #[test]
3216 fn celvalue_eq_u32_and_conv() {
3217 let cv = 7u32.conv();
3218 assert_eq!(cv, 7u32);
3219 assert_eq!(7u32, cv);
3220 }
3221
3222 #[test]
3223 fn celvalue_eq_u64_and_conv() {
3224 let cv = 99u64.conv();
3225 assert_eq!(cv, 99u64);
3226 assert_eq!(99u64, cv);
3227 }
3228
3229 #[test]
3230 fn celvalue_eq_f32_and_conv() {
3231 let cv = 1.5f32.conv();
3232 assert!(cv == 1.5f32);
3233 assert!(1.5f32 == cv);
3234 }
3235
3236 #[test]
3237 fn celvalue_eq_f64_and_conv() {
3238 let cv = 2.75f64.conv();
3239 assert_eq!(cv, 2.75f64);
3240 assert_eq!(2.75f64, cv);
3241 }
3242
3243 #[test]
3244 fn celvalue_eq_vec_u8_and_conv() {
3245 let vec = vec![10u8, 20, 30];
3246 let cv = (&vec).conv();
3247 assert_eq!(cv, vec);
3248 assert_eq!(vec, cv);
3249 }
3250
3251 #[test]
3252 fn celvalue_eq_bytes_variant() {
3253 let b = Bytes::from_static(b"xyz");
3254 let cv = CelValue::Bytes(CelBytes::Owned(b.clone()));
3255 assert_eq!(cv, b);
3256 }
3257
3258 #[test]
3259 fn bytes_eq_celvalue_variant() {
3260 let b = Bytes::from_static(b"hello");
3261 let cv = CelValue::Bytes(CelBytes::Owned(b.clone()));
3262 assert_eq!(b, cv);
3263 }
3264
3265 #[test]
3266 fn array_contains_with_integers() {
3267 let arr = [1i32, 2, 3];
3268 assert!(array_contains(&arr, 2i32));
3269 assert!(!array_contains(&arr, 4i32));
3270 }
3271
3272 #[test]
3273 fn array_contains_with_bytes() {
3274 let b1 = Bytes::from_static(b"a");
3275 let b2 = Bytes::from_static(b"b");
3276 let arr = [b1.clone(), b2.clone()];
3277 assert!(array_contains(&arr, b2.clone()));
3278 assert!(!array_contains(&arr, Bytes::from_static(b"c")));
3279 }
3280
3281 #[test]
3282 fn map_access_and_contains_with_hashmap_i32_key() {
3283 let mut hm: HashMap<i32, &str> = HashMap::new();
3284 hm.insert(5, "five");
3285
3286 let v = map_access(&hm, 5i32).unwrap();
3287 assert_eq!(*v, "five");
3288
3289 assert!(map_contains(&hm, 5i32));
3290 assert!(!map_contains(&hm, 6i32));
3291 }
3292
3293 #[test]
3294 fn map_access_and_contains_with_btreemap_u32_key() {
3295 let mut bt: BTreeMap<u32, &str> = BTreeMap::new();
3296 bt.insert(10, "ten");
3297
3298 let v = map_access(&bt, 10u32).unwrap();
3299 assert_eq!(*v, "ten");
3300
3301 assert!(map_contains(&bt, 10u32));
3302 assert!(!map_contains(&bt, 11u32));
3303 }
3304
3305 #[test]
3306 fn map_access_key_not_found_errors() {
3307 let mut hm: HashMap<i32, &str> = HashMap::new();
3308 hm.insert(1, "one");
3309
3310 let err = map_access(&hm, 2i32).unwrap_err();
3311 if let CelError::MapKeyNotFound(k) = err {
3312 assert_eq!(k, 2i32.conv());
3313 } else {
3314 panic!("Expected MapKeyNotFound");
3315 }
3316 }
3317
3318 #[test]
3319 fn map_key_cast_string_some_for_borrowed() {
3320 let cv = "hello".conv();
3321 let key: Option<Cow<str>> = <String as MapKeyCast>::make_key(&cv);
3322 match key {
3323 Some(Cow::Borrowed(s)) => assert_eq!(s, "hello"),
3324 _ => panic!("Expected Some(Cow::Borrowed)"),
3325 }
3326 }
3327
3328 #[test]
3329 fn map_key_cast_string_some_for_owned() {
3330 let arc: Arc<str> = Arc::from("world");
3331 let cv = CelValue::String(CelString::Owned(arc.clone()));
3332 let key: Option<Cow<str>> = <String as MapKeyCast>::make_key(&cv);
3333 match key {
3334 Some(Cow::Borrowed(s)) => assert_eq!(s, "world"),
3335 _ => panic!("Expected Some(Cow::Borrowed)"),
3336 }
3337 }
3338
3339 #[test]
3340 fn map_key_cast_string_none_for_non_string() {
3341 let cv = 42i32.conv();
3342 assert!(<String as MapKeyCast>::make_key(&cv).is_none());
3343 }
3344
3345 #[test]
3346 fn map_key_cast_number_none_for_non_number_value() {
3347 let cv = "not_a_number".conv();
3348 let result: Option<Cow<'_, i32>> = <i32 as MapKeyCast>::make_key(&cv);
3349 assert!(result.is_none(), "Expected None for non-Number CelValue");
3350 }
3351
3352 #[test]
3353 fn option_to_bool() {
3354 assert!(Some(true).to_bool(), "Some(true) should be true");
3355 assert!(!Some(false).to_bool(), "Some(false) should be false");
3356 let none: Option<bool> = None;
3357 assert!(!none.to_bool(), "None should be false");
3358 }
3359
3360 #[test]
3361 fn vec_to_bool() {
3362 let empty: Vec<i32> = Vec::new();
3363 assert!(!empty.to_bool(), "Empty Vec should be false");
3364 let non_empty = vec![1, 2, 3];
3365 assert!(non_empty.to_bool(), "Non-empty Vec should be true");
3366 }
3367
3368 #[test]
3369 fn btreemap_to_bool() {
3370 let mut map: BTreeMap<i32, i32> = BTreeMap::new();
3371 assert!(!map.to_bool(), "Empty BTreeMap should be false");
3372 map.insert(1, 10);
3373 assert!(map.to_bool(), "Non-empty BTreeMap should be true");
3374 }
3375
3376 #[test]
3377 fn hashmap_to_bool() {
3378 let mut map: HashMap<&str, i32> = HashMap::new();
3379 assert!(!map.to_bool(), "Empty HashMap should be false");
3380 map.insert("key", 42);
3381 assert!(map.to_bool(), "Non-empty HashMap should be true");
3382 }
3383
3384 #[test]
3385 fn str_and_string_to_bool() {
3386 assert!("hello".to_bool(), "Non-empty &str should be true");
3387 assert!(!"".to_bool(), "Empty &str should be false");
3388 let s = String::from("world");
3389 assert!(s.to_bool(), "Non-empty String should be true");
3390 let empty = String::new();
3391 assert!(!empty.to_bool(), "Empty String should be false");
3392 }
3393
3394 #[test]
3395 fn array_slice_to_bool() {
3396 let empty: [bool; 0] = [];
3397 assert!(!empty.to_bool(), "Empty [T] slice should be false");
3398 let non_empty = [true, false];
3399 assert!(non_empty.to_bool(), "Non-empty [T] slice should be true");
3400 }
3401
3402 #[test]
3403 fn bytes_to_bool() {
3404 let empty = Bytes::new();
3405 assert!(!empty.to_bool(), "Empty Bytes should be false");
3406 let non_empty = Bytes::from_static(b"x");
3407 assert!(non_empty.to_bool(), "Non-empty Bytes should be true");
3408 }
3409
3410 #[cfg(feature = "runtime")]
3411 #[test]
3412 fn celmode_json_and_proto_flags() {
3413 use crate::CelMode;
3414
3415 CelMode::set(CelMode::Serde);
3416 let current = CelMode::current();
3417 assert!(current.is_json(), "CelMode should report JSON when set to Json");
3418 assert!(!current.is_proto(), "CelMode should not report Proto when set to Json");
3419
3420 CelMode::set(CelMode::Proto);
3421 let current = CelMode::current();
3422 assert!(current.is_proto(), "CelMode should report Proto when set to Proto");
3423 assert!(!current.is_json(), "CelMode should not report JSON when set to Proto");
3424 }
3425}