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