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