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