geam 0.1.1

Experimental Rust-embedded execution runtime for typed Gleam programs
use crate::host::ExternalPayloadLease;
use crate::plan::ExternalType;
use ecow::EcoString;

#[derive(Clone)]
pub struct ExternalValue {
    type_: ExternalType,
    identity: ExternalValueIdentity,
    inspection: EcoString,
    _lease: ExternalPayloadLease,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ExternalValueIdentity(u64);

impl ExternalValue {
    pub(crate) fn from_evaluated(
        type_: ExternalType,
        lease: ExternalPayloadLease,
        inspection: EcoString,
    ) -> Self {
        Self {
            type_,
            identity: ExternalValueIdentity(lease.id()),
            inspection,
            _lease: lease,
        }
    }

    pub fn type_(&self) -> &ExternalType {
        &self.type_
    }

    pub fn identity(&self) -> ExternalValueIdentity {
        self.identity
    }

    pub fn inspection(&self) -> &EcoString {
        &self.inspection
    }
}

impl PartialEq for ExternalValue {
    fn eq(&self, other: &Self) -> bool {
        self.identity == other.identity
    }
}

impl std::fmt::Debug for ExternalValue {
    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        formatter
            .debug_struct("ExternalValue")
            .field("type_", &self.type_)
            .field("identity", &self.identity)
            .field("inspection", &self.inspection)
            .finish_non_exhaustive()
    }
}

#[cfg(test)]
mod tests {
    use super::ExternalValue;
    use crate::host::HostExternalStore;
    use crate::plan::{ExternalType, ExternalTypeName};

    #[test]
    fn opaque_external_value_exposes_identity_and_inspection_without_payload_access() {
        let store = HostExternalStore::default();
        let source_hash = |_: &crate::host::HostExternalHashing<'_>, value: &usize| *value as u64;
        let inspect = |context: &crate::host::HostExternalInspection<'_>, value: &usize| {
            let stored = crate::host::HostStoredValue::<num_bigint::BigInt>::new(
                crate::runtime::StoredRuntimeValue::test_int((*value).into()),
            );
            format!("Resource({})", context.inspect_stored_value(&stored)).into()
        };
        let first = store.insert(7usize, |_, left, right| left == right, source_hash, inspect);
        let second = store.insert(7usize, |_, left, right| left == right, source_hash, inspect);
        let type_ = ExternalType::new(
            ExternalTypeName::new("domain".into(), "domain/resource".into(), "Resource".into()),
            Vec::new(),
        );
        let stored_equal =
            |_: &crate::runtime::StoredRuntimeValue, _: &crate::runtime::StoredRuntimeValue| false;
        let equality = crate::host::HostExternalEquality::new(&stored_equal);
        let stored_hash = |_: &crate::runtime::StoredRuntimeValue| 17;
        let stored_inspect = |_: &crate::runtime::StoredRuntimeValue| "7".into();
        let hashing = crate::host::HostExternalHashing::new(&stored_hash);
        let inspection = crate::host::HostExternalInspection::new(&stored_inspect);
        assert!(first.source_equal(&equality, &second));
        assert!(second.source_equal(&equality, &first));
        assert_eq!(first.source_hash(&hashing), 7);
        assert_eq!(first.inspection(&inspection), "Resource(7)");
        assert_eq!(second.inspection(&inspection), "Resource(7)");
        let first = ExternalValue::from_evaluated(type_.clone(), first, "Resource(7)".into());
        let second = ExternalValue::from_evaluated(type_.clone(), second, "Resource(7)".into());
        let cloned = first.clone();
        let debug = format!("{first:?}");

        assert_eq!(first.type_(), &type_);
        assert_eq!(first.inspection(), "Resource(7)");
        assert_eq!(first, cloned);
        assert_ne!(first, second);
        assert_eq!(first.identity(), cloned.identity());
        assert!(debug.contains("ExternalValue"));
        assert!(debug.contains("Resource(7)"));
        assert!(!debug.contains("7usize"));
    }
}