tinc_cel/
lib.rs

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