obeli-sk-wasm-workers 0.28.0

Internal package of obeli-sk
Documentation
use assert_matches::assert_matches;
use chrono::DateTime;
use concepts::FunctionFqn;
use concepts::prefixed_ulid::ExecutionIdDerived;
use concepts::storage::HistoryEventScheduleAt;
use concepts::{ExecutionId, prefixed_ulid::DelayId};
use indexmap::indexmap;
use std::ops::Deref as _;
use std::time::Duration;
use val_json::wast_val::WastVal;

pub(crate) use concepts::SUFFIX_FN_AWAIT_NEXT;
pub(crate) use concepts::SUFFIX_FN_GET;
pub(crate) use concepts::SUFFIX_FN_INVOKE;
pub(crate) use concepts::SUFFIX_FN_SCHEDULE;
pub(crate) use concepts::SUFFIX_FN_STUB;
pub(crate) use concepts::SUFFIX_FN_SUBMIT;

// Generate `obelisk:workflow:workflow-support
pub(crate) mod v3_0_0 {
    use chrono::DateTime;
    use chrono::Utc;
    use concepts::FunctionFqn;
    use concepts::prefixed_ulid::DelayId;
    use concepts::prefixed_ulid::ExecutionIdDerived;
    use concepts::storage::HistoryEventScheduleAt;
    use obelisk::types::execution as types_execution;
    use obelisk::types::time::Datetime;
    pub(crate) use obelisk::types::time::Duration as DurationEnum_3_0_0;
    pub(crate) use obelisk::types::time::ScheduleAt as ScheduleAt_3_0_0;
    pub(crate) use obelisk::workflow::workflow_support::ClosingStrategy as ClosingStrategy_3_0_0;
    use std::time::Duration;
    use std::time::UNIX_EPOCH;

    wasmtime::component::bindgen!({
        path: "host-wit-workflow/",
        inline: "package any:any;
        world bindings {
            import obelisk:workflow/workflow-support@3.0.0;
            }",
        world: "any:any/bindings",
        with: {
            "obelisk:types/execution/join-set": concepts::JoinSetId,
        },
        imports: {
            default: trappable | async
        }
    });

    impl From<DurationEnum_3_0_0> for Duration {
        fn from(value: DurationEnum_3_0_0) -> Self {
            match value {
                DurationEnum_3_0_0::Milliseconds(millis) => Duration::from_millis(millis),
                DurationEnum_3_0_0::Seconds(secs) => Duration::from_secs(secs),
                DurationEnum_3_0_0::Minutes(mins) => Duration::from_secs(u64::from(mins * 60)),
                DurationEnum_3_0_0::Hours(hours) => Duration::from_secs(u64::from(hours * 60 * 60)),
                DurationEnum_3_0_0::Days(days) => {
                    Duration::from_secs(u64::from(days * 24 * 60 * 60))
                }
            }
        }
    }

    impl From<Datetime> for DateTime<Utc> {
        fn from(
            Datetime {
                seconds,
                nanoseconds,
            }: Datetime,
        ) -> Self {
            let duration = Duration::new(seconds, nanoseconds);
            let systemtime = UNIX_EPOCH + duration;
            DateTime::<Utc>::from(systemtime)
        }
    }
    impl TryFrom<DateTime<Utc>> for Datetime {
        type Error = chrono::OutOfRangeError;
        fn try_from(value: DateTime<Utc>) -> Result<Self, Self::Error> {
            let epoch = DateTime::<Utc>::from(UNIX_EPOCH);
            let duration = value.signed_duration_since(epoch).to_std()?;
            Ok(Datetime {
                seconds: duration.as_secs(),
                nanoseconds: duration.subsec_nanos(),
            })
        }
    }

    impl From<ScheduleAt_3_0_0> for HistoryEventScheduleAt {
        fn from(value: ScheduleAt_3_0_0) -> Self {
            match value {
                ScheduleAt_3_0_0::Now => Self::Now,
                ScheduleAt_3_0_0::At(datetime) => Self::At(DateTime::from(datetime)),
                ScheduleAt_3_0_0::In(duration) => Self::In(Duration::from(duration)),
            }
        }
    }

    impl From<&FunctionFqn> for types_execution::Function {
        fn from(ffqn: &FunctionFqn) -> Self {
            Self {
                interface_name: ffqn.ifc_fqn.to_string(),
                function_name: ffqn.function_name.to_string(),
            }
        }
    }

    impl From<&ExecutionIdDerived> for types_execution::ExecutionId {
        fn from(value: &ExecutionIdDerived) -> Self {
            Self {
                id: value.to_string(),
            }
        }
    }

    impl From<&DelayId> for types_execution::DelayId {
        fn from(value: &DelayId) -> Self {
            Self {
                id: value.to_string(),
            }
        }
    }

    mod into_val {
        // From for Val, should be autogenerated.
        use super::obelisk::types::execution as types_execution;
        use wasmtime::component::Val;

        impl From<types_execution::Function> for Val {
            fn from(value: types_execution::Function) -> Self {
                Self::Record(vec![
                    (
                        "interface-name".to_string(),
                        Self::String(value.interface_name),
                    ),
                    (
                        "function-name".to_string(),
                        Self::String(value.function_name),
                    ),
                ])
            }
        }

        impl From<types_execution::ExecutionId> for Val {
            fn from(value: types_execution::ExecutionId) -> Self {
                Self::Record(vec![("id".to_string(), Self::String(value.id))])
            }
        }

        impl From<types_execution::DelayId> for Val {
            fn from(value: types_execution::DelayId) -> Self {
                Self::Record(vec![("id".to_string(), Self::String(value.id))])
            }
        }

        impl From<types_execution::ResponseId> for Val {
            fn from(value: types_execution::ResponseId) -> Self {
                match value {
                    types_execution::ResponseId::ExecutionId(execution_id) => Self::Variant(
                        "execution-id".to_string(),
                        Some(Box::new(Self::from(execution_id))),
                    ),
                    types_execution::ResponseId::DelayId(delay_id) => {
                        Self::Variant("delay-id".to_string(), Some(Box::new(Self::from(delay_id))))
                    }
                }
            }
        }

        impl From<types_execution::FunctionMismatch> for Val {
            fn from(value: types_execution::FunctionMismatch) -> Self {
                Self::Record(vec![
                    (
                        "specified-function".to_string(),
                        Val::from(value.specified_function),
                    ),
                    (
                        "actual-function".to_string(),
                        Self::Option(value.actual_function.map(|f| Box::new(Self::from(f)))),
                    ),
                    ("actual-id".to_string(), Self::from(value.actual_id)),
                ])
            }
        }

        impl From<types_execution::GetExtensionError> for Val {
            fn from(value: types_execution::GetExtensionError) -> Self {
                match value {
                    types_execution::GetExtensionError::FunctionMismatch(function_mismatch) => {
                        Self::Variant(
                            "function-mismatch".to_string(),
                            Some(Box::new(Val::from(function_mismatch))),
                        )
                    }
                    types_execution::GetExtensionError::NotFoundInProcessedResponses => {
                        Self::Variant("not-found-in-processed-responses".to_string(), None)
                    }
                }
            }
        }
    }
}

pub fn history_event_schedule_at_from_wast_val(
    scheduled_at: &WastVal,
) -> Result<HistoryEventScheduleAt, &'static str> {
    let WastVal::Variant(variant, val) = scheduled_at else {
        return Err("wrong type");
    };
    match (variant.as_str(), val) {
        ("now", None) => Ok(HistoryEventScheduleAt::Now),
        ("in", Some(duration)) => {
            if let &WastVal::Variant(key, value) = &duration.deref() {
                let duration = match (key.as_str(), value.as_deref()) {
                    ("milliseconds", Some(WastVal::U64(value))) => Duration::from_millis(*value),
                    ("seconds", Some(WastVal::U64(value))) => Duration::from_secs(*value),
                    ("minutes", Some(WastVal::U64(value))) => Duration::from_secs(*value * 60),
                    ("hours", Some(WastVal::U64(value))) => Duration::from_secs(*value * 60 * 60),
                    ("days", Some(WastVal::U64(value))) => {
                        Duration::from_secs(*value * 60 * 60 * 24)
                    }
                    _ => {
                        return Err(
                            "cannot convert `scheduled-at`, `in` variant: value must be one of the following keys: `milliseconds`(U64), `seconds`(U64), `minutes`(U32), `hours`(U32), `days`(U32)",
                        );
                    }
                };
                Ok(HistoryEventScheduleAt::In(duration))
            } else {
                Err("cannot convert `scheduled-at`, `in` variant: value must be a variant")
            }
        }
        ("at", Some(date_time)) if matches!(date_time.deref(), WastVal::Record(_)) => {
            let date_time =
                assert_matches!(date_time.deref(), WastVal::Record(keys_vals) => keys_vals)
                    .iter()
                    .map(|(k, v)| (k.as_str(), v))
                    .collect::<std::collections::HashMap<_, _>>();
            let seconds = date_time.get("seconds");
            let nanoseconds = date_time.get("nanoseconds");
            match (date_time.len(), seconds, nanoseconds) {
                (2, Some(WastVal::U64(seconds)), Some(WastVal::U32(nanoseconds))) => {
                    let date_time = v3_0_0::obelisk::types::time::Datetime {
                        seconds: *seconds,
                        nanoseconds: *nanoseconds,
                    };
                    let date_time = DateTime::from(date_time);
                    Ok(HistoryEventScheduleAt::At(date_time))
                }
                _ => Err(
                    "cannot convert `scheduled-at`, `at` variant: record must have exactly two keys: `seconds`(U64), `nanoseconds`(U32)",
                ),
            }
        }
        _ => Err("cannot convert `scheduled-at` variant, expected one of `now`, `in`, `at`"),
    }
}

pub(crate) fn execution_id_into_wast_val(execution_id: &ExecutionId) -> WastVal {
    WastVal::Record(indexmap! {"id".to_string() => WastVal::String(execution_id.to_string())})
}

pub(crate) fn execution_id_derived_into_wast_val(execution_id: &ExecutionIdDerived) -> WastVal {
    WastVal::Record(indexmap! {"id".to_string() => WastVal::String(execution_id.to_string())})
}

pub(crate) fn delay_id_into_wast_val(delay_id: &DelayId) -> WastVal {
    WastVal::Record(indexmap! {"id".to_string() => WastVal::String(delay_id.to_string())})
}

pub(crate) fn ffqn_into_wast_val(ffqn: &FunctionFqn) -> WastVal {
    WastVal::Record(indexmap! {
        "interface-name".to_string() => WastVal::String(ffqn.ifc_fqn.to_string()),
        "function-name".to_string() => WastVal::String(ffqn.function_name.to_string()),
    })
}