use crate::bindings::golem::api::host::EnvironmentId;
use crate::bindings::golem::quota::types;
use crate::value_and_type::type_builder::TypeNodeBuilder;
use crate::value_and_type::wasi::Datetime;
use crate::value_and_type::{FromValueAndType, IntoValue};
use golem_wasm::{NodeBuilder, WitValueExtractor};
use std::time::Duration;
#[derive(Debug, Clone, PartialEq)]
pub struct FailedReservation {
pub estimated_wait: Option<Duration>,
}
impl From<types::FailedReservation> for FailedReservation {
fn from(raw: types::FailedReservation) -> Self {
Self {
estimated_wait: raw.estimated_wait_nanos.map(Duration::from_nanos),
}
}
}
impl std::fmt::Display for FailedReservation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.estimated_wait {
Some(d) => write!(f, "quota reservation failed (retry after {d:?})"),
None => write!(f, "quota reservation failed"),
}
}
}
impl std::error::Error for FailedReservation {}
#[must_use]
pub struct Reservation {
raw: types::Reservation,
}
impl Reservation {
pub fn commit(self, used: u64) {
types::Reservation::commit(self.raw, used);
}
}
pub struct QuotaToken {
raw: types::QuotaToken,
}
impl QuotaToken {
pub fn new(resource_name: &str, expected_use: u64) -> Self {
Self {
raw: types::QuotaToken::new(resource_name, expected_use),
}
}
pub fn reserve(&self, amount: u64) -> Result<Reservation, FailedReservation> {
self.raw
.reserve(amount)
.map(|raw| Reservation { raw })
.map_err(FailedReservation::from)
}
pub fn split(&mut self, child_expected_use: u64) -> QuotaToken {
QuotaToken {
raw: self.raw.split(child_expected_use),
}
}
pub fn merge(&mut self, other: QuotaToken) {
self.raw.merge(other.raw);
}
fn to_record(&self) -> types::QuotaTokenRecord {
self.raw.to_record()
}
fn from_record(record: &types::QuotaTokenRecord) -> QuotaToken {
QuotaToken {
raw: types::QuotaToken::from_record(record),
}
}
}
pub fn with_reservation<T, F>(token: &QuotaToken, amount: u64, f: F) -> Result<T, FailedReservation>
where
F: FnOnce(&Reservation) -> (u64, T),
{
let reservation = token.reserve(amount)?;
let (used, value) = f(&reservation);
reservation.commit(used);
Ok(value)
}
impl IntoValue for QuotaToken {
fn add_to_builder<T: NodeBuilder>(self, builder: T) -> T::Result {
let record = self.to_record();
let builder = builder.record();
let builder = record.environment_id.add_to_builder(builder.item());
let builder = record.resource_name.add_to_builder(builder.item());
let builder = record.expected_use.add_to_builder(builder.item());
let builder = record.last_credit.add_to_builder(builder.item());
let builder = record.last_credit_at.add_to_builder(builder.item());
builder.finish()
}
fn add_to_type_builder<T: TypeNodeBuilder>(builder: T) -> T::Result {
let builder = builder.record(
Some("QuotaTokenRecord".to_string()),
Some("golem:quota".to_string()),
);
let builder = <EnvironmentId>::add_to_type_builder(builder.field("environment-id"));
let builder = <String>::add_to_type_builder(builder.field("resource-name"));
let builder = <u64>::add_to_type_builder(builder.field("expected-use"));
let builder = <i64>::add_to_type_builder(builder.field("last-credit"));
let builder = <Datetime>::add_to_type_builder(builder.field("last-credit-at"));
builder.finish()
}
}
impl FromValueAndType for QuotaToken {
fn from_extractor<'a, 'b>(
extractor: &'a impl WitValueExtractor<'a, 'b>,
) -> Result<Self, String> {
let environment_id = <EnvironmentId>::from_extractor(
&extractor
.field(0)
.ok_or_else(|| "Missing environment-id".to_string())?,
)?;
let resource_name = <String>::from_extractor(
&extractor
.field(1)
.ok_or_else(|| "Missing resource-name".to_string())?,
)?;
let expected_use = <u64>::from_extractor(
&extractor
.field(2)
.ok_or_else(|| "Missing expected-use".to_string())?,
)?;
let last_credit = <i64>::from_extractor(
&extractor
.field(3)
.ok_or_else(|| "Missing last-credit".to_string())?,
)?;
let last_credit_at = <Datetime>::from_extractor(
&extractor
.field(4)
.ok_or_else(|| "Missing last-credit-at".to_string())?,
)?;
let record = types::QuotaTokenRecord {
environment_id,
resource_name,
expected_use,
last_credit,
last_credit_at,
};
Ok(QuotaToken::from_record(&record))
}
}