tinc_cel/
lib.rs

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