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