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