cel-cxx 0.2.5

A high-performance, type-safe Rust interface for Common Expression Language (CEL), build on top of cel-cpp with zero-cost FFI bindings via cxx
Documentation
use super::*;

pub(crate) fn value_from_rust<'a>(
    value: &rust::Value,
    arena: &'a Arena,
    descriptor_pool: &'a DescriptorPool,
    message_factory: &'a MessageFactory,
) -> cxx::UniquePtr<Value<'a>> {
    match value {
        rust::Value::Null => Value::new_null(),
        rust::Value::Bool(b) => {
            let bool_value = BoolValue::new(*b);
            Value::new_bool(&bool_value)
        }
        rust::Value::Int(i) => {
            let int_value = IntValue::new(*i);
            Value::new_int(&int_value)
        }
        rust::Value::Uint(u) => {
            let uint_value = UintValue::new(*u);
            Value::new_uint(&uint_value)
        }
        rust::Value::Double(d) => {
            let double_value = DoubleValue::new(*d);
            Value::new_double(&double_value)
        }
        rust::Value::String(s) => {
            let string_value = StringValue::new(arena, s);
            Value::new_string(&string_value)
        }
        rust::Value::Bytes(b) => {
            let bytes_value = BytesValue::new(arena, b);
            Value::new_bytes(&bytes_value)
        }
        rust::Value::Struct(s) => {
            // Deserialization errors become CEL error values rather than Rust errors.
            // This follows CEL semantics: expressions can observe errors as values
            // (e.g., via `try()` or short-circuit evaluation) rather than aborting.
            match MessageValue::from_bytes(
                arena,
                descriptor_pool,
                message_factory,
                s.type_name(),
                s.to_bytes(),
            ) {
                Ok(message_value) => Value::new_message(&message_value),
                Err(status) => {
                    let error_value = ErrorValue::new(status);
                    Value::new_error(&error_value)
                }
            }
        }
        rust::Value::Duration(d) => {
            let duration = (*d).into();
            let duration_value = DurationValue::new(duration);
            Value::new_duration(&duration_value)
        }
        rust::Value::Timestamp(t) => {
            let timestamp = (*t).into();
            let timestamp_value = TimestampValue::new(timestamp);
            Value::new_timestamp(&timestamp_value)
        }
        rust::Value::List(l) => {
            let mut list_value_builder = ListValueBuilder::new(arena);
            for v in l {
                let value = value_from_rust(v, arena, descriptor_pool, message_factory);
                list_value_builder.pin_mut().add(&value);
            }
            let list_value = list_value_builder.pin_mut().build();
            Value::new_list(&list_value)
        }
        rust::Value::Map(m) => {
            let mut map_value_builder = MapValueBuilder::new(arena);
            for (k, v) in m {
                let key = value_from_rust(
                    &k.clone().into_value(),
                    arena,
                    descriptor_pool,
                    message_factory,
                );
                let value = value_from_rust(v, arena, descriptor_pool, message_factory);
                map_value_builder.pin_mut().put(&key, &value);
            }
            let map_value = map_value_builder.pin_mut().build();
            Value::new_map(&map_value)
        }
        rust::Value::Unknown(..) => {
            let error_value = ErrorValue::new(
                super::Status::internal("cannot convert Unknown value to FFI"),
            );
            Value::new_error(&error_value)
        }
        rust::Value::Type(t) => {
            let type_ = super::type_from_rust(t, arena, descriptor_pool);
            let type_value = TypeValue::new(&type_);
            Value::new_type(&type_value)
        }
        rust::Value::Error(e) => {
            let status = e.into();
            let error_value = ErrorValue::new(status);
            Value::new_error(&error_value)
        }
        rust::Value::Opaque(o) => {
            let opaque_value = opaque_value_from_rust(o, arena, descriptor_pool);
            Value::new_opaque(&opaque_value)
        }
        rust::Value::Optional(o) => {
            let optional_value =
                optional_value_from_rust(o, arena, descriptor_pool, message_factory);
            Value::new_optional(&optional_value)
        }
    }
}

pub(crate) fn value_to_rust<'a>(
    value: &Value<'a>,
    arena: &'a Arena,
    descriptor_pool: &'a DescriptorPool,
    message_factory: &'a MessageFactory,
) -> Result<rust::Value, rust::Error> {
    match value.kind() {
        ValueKind::Null => Ok(rust::Value::Null),
        ValueKind::Bool => {
            let bool_value = value.get_bool();
            Ok(rust::Value::Bool(bool_value.native_value()))
        }
        ValueKind::Int => {
            let int_value = value.get_int();
            Ok(rust::Value::Int(int_value.native_value()))
        }
        ValueKind::Uint => {
            let uint_value = value.get_uint();
            Ok(rust::Value::Uint(uint_value.native_value()))
        }
        ValueKind::Double => {
            let double_value = value.get_double();
            Ok(rust::Value::Double(double_value.native_value()))
        }
        ValueKind::String => {
            let string_value = value.get_string();
            Ok(rust::Value::String(string_value.native_value().into()))
        }
        ValueKind::Bytes => {
            let bytes_value = value.get_bytes();
            Ok(rust::Value::Bytes(bytes_value.native_value().into()))
        }
        ValueKind::Struct => {
            let message_value = value.get_message();
            let type_name = message_value.type_name();
            let bytes = message_value
                .to_bytes()
                .map_err(|e| rust::Error::from(&e))?;
            Ok(rust::Value::Struct(rust::StructValue::from_bytes(
                type_name,
                bytes,
            )))
        }
        ValueKind::Duration => {
            let duration_value = value.get_duration();
            Ok(rust::Value::Duration(duration_value.native_value().into()))
        }
        ValueKind::Timestamp => {
            let timestamp_value = value.get_timestamp();
            Ok(rust::Value::Timestamp(
                timestamp_value.native_value().into(),
            ))
        }
        ValueKind::List => {
            let mut result = Vec::new();
            let list_value = value.get_list();
            let mut iter = list_value
                .new_iterator()
                .map_err(|e| rust::Error::from(&e))?;
            while iter.pin_mut().has_next() {
                let item = iter
                    .pin_mut()
                    .next1(descriptor_pool, message_factory, arena)
                    .map_err(|e| rust::Error::from(&e))?;
                result.push(value_to_rust(
                    &item,
                    arena,
                    descriptor_pool,
                    message_factory,
                )?);
            }
            Ok(rust::Value::List(result))
        }
        ValueKind::Map => {
            let mut result = std::collections::HashMap::new();
            let map_value = value.get_map();
            let mut iter = map_value
                .new_iterator()
                .map_err(|e| rust::Error::from(&e))?;
            while iter.pin_mut().has_next() {
                let (key, value) = iter
                    .pin_mut()
                    .next2(descriptor_pool, message_factory, arena)
                    .map_err(|e| rust::Error::from(&e))?;
                result.insert(
                    rust::MapKey::from_value(value_to_rust(
                        &key,
                        arena,
                        descriptor_pool,
                        message_factory,
                    )?)
                    .map_err(|v| rust::Error::invalid_argument(v.to_string()))?,
                    value_to_rust(&value, arena, descriptor_pool, message_factory)?,
                );
            }
            Ok(rust::Value::Map(result))
        }
        ValueKind::Unknown => {
            Err(rust::Error::internal("Unknown values are not supported"))
        }
        ValueKind::Type => {
            let type_value = value.get_type();
            Ok(rust::Value::Type(type_to_rust(type_value.native_value())))
        }
        ValueKind::Error => {
            let error_value = value.get_error();
            Ok(rust::Value::Error(rust::Error::from(&error_value.native_value())))
        }
        ValueKind::Opaque => {
            if value.is_optional() {
                let optional_value = value.get_optional();
                let result = if optional_value.has_value() {
                    rust::Optional::new(value_to_rust(
                        &optional_value.get_value(),
                        arena,
                        descriptor_pool,
                        message_factory,
                    )?)
                } else {
                    rust::Optional::none()
                };
                Ok(rust::Value::Optional(result))
            } else {
                let opaque_value = value.get_opaque();
                let result = opaque_value
                    .downcast::<FfiOpaqueValueImpl>()
                    .map(|v| v.0)
                    .ok_or(rust::Error::internal("Failed to downcast opaque value"))?;
                Ok(rust::Value::Opaque(result))
            }
        }
    }
}

#[derive(Debug, Clone, Eq)]
pub(crate) struct FfiOpaqueValueImpl(pub rust::OpaqueValue);
impl std::fmt::Display for FfiOpaqueValueImpl {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.0.fmt(f)
    }
}

impl core::cmp::PartialEq for FfiOpaqueValueImpl {
    fn eq(&self, other: &Self) -> bool {
        self.0.eq(&other.0)
    }
}

impl FfiOpaqueValue for FfiOpaqueValueImpl {
    fn opaque_type<'a>(
        &self,
        arena: &'a Arena,
        descriptor_pool: &'a DescriptorPool,
    ) -> OpaqueType<'a> {
        super::opaque_type_from_rust(&self.0.opaque_type(), arena, descriptor_pool)
    }
}

pub(crate) fn opaque_value_from_rust<'a>(
    value: &rust::OpaqueValue,
    arena: &'a Arena,
    descriptor_pool: &'a DescriptorPool,
) -> cxx::UniquePtr<OpaqueValue<'a>> {
    let ffi = FfiOpaqueValueImpl(value.clone());
    OpaqueValue::new(arena, descriptor_pool, ffi)
}

fn optional_value_from_rust<'a>(
    value: &rust::Optional<rust::Value>,
    arena: &'a Arena,
    descriptor_pool: &'a DescriptorPool,
    message_factory: &'a MessageFactory,
) -> cxx::UniquePtr<OptionalValue<'a>> {
    match value.as_option() {
        Some(v) => {
            let value = value_from_rust(v, arena, descriptor_pool, message_factory);
            OptionalValue::new(arena, &value)
        }
        None => OptionalValue::none(),
    }
}