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