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