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