tinc_cel/
lib.rs

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