tinc_cel/
lib.rs

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