tinc_cel/
lib.rs

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