// The `wasi:keyvalue/cache` interface defines the operations of a single
// instance of a "cache", which is a non-durable, weakly-consistent key-value
// store. "Non-durable" means that caches are allowed and expected to
// arbitrarily discard key-value entries. "Weakly-consistent" means that there
// are essentially no guarantees that operations will agree on their results: a
// get following a set may not observe the set value; multiple gets may observe
// different previous set values; etc. The only guarantee is that values are
// not materialized "out of thin air": if a `get` returns a value, that value
// was passed to a `set` operation at some point in time in the past.
// Additionally, caches MUST make a best effort to respect the supplied
// Time-to-Live values (within the usual limitations around time in a
// distributed setting).
interface cache {
    use wasi:io/poll@0.2.3.{pollable};
    use types.{key, incoming-value, outgoing-value, error};

    // The `get` operation returns the value passed by a previous `set` for the
    // same key within the given TTL or none if there is no such value.
    get: func(k: key) -> future-get-result;

    // This block defines a special resource type used by `get` to emulate
    // `future<result<option<incoming-value>,error>>`. In the return value
    // of the `get` method, the outer `option` returns `none` when the pollable
    // is not yet ready and the inner `option` returns `none` when the
    // requested key wasn't present.
    resource future-get-result {
        future-get-result-get: func() -> option<result<option<incoming-value>, error>>;
        listen-to-future-get-result: func() -> pollable;
    }

    // The `exists` operation returns whether a value was previously `set` for
    // the given key within the TTL.
    exists: func(k: key) -> future-exists-result;

    // This block defines a special resource type used by `exists` to emulate
    // `future<result<bool,error>>`.
    resource future-exists-result {
        future-exists-result-get: func() -> option<result<bool, error>>;
        listen-to-future-exists-result: func() -> pollable;
    }

    // The `set` operation sets the given value for the given key for the given
    // time-to-live (TTL) duration, if supplied, specified in milliseconds. If
    // a TTL is not supplied, the key may be kept indefinitely (as-if a very
    // large TTL were used). If the key is already present in the cache, the
    // value is updated in-place. In the common case of computing and caching a
    // value if the given key is not already in the cache, consider using
    // `get-or-set` (below) intead of separate `get` and `set` operations.
    set: func(k: key, v: borrow<outgoing-value>, TTL-ms: option<u32>) -> future-result;

    // This block defines a special resource type used by `set` and `delete` to
    // emulate `future<result<_,error>>`.
    resource future-result {
        future-result-get: func() -> option<result<_, error>>;
        listen-to-future-result: func() -> pollable;
    }

    // The `get-or-set` operation asynchronously returns one of two cases
    // enumerated by `get-or-set-entry`: in the `occupied` case, the given key
    // already has a value present in the cache; in the `vacant` case, there
    // was no value and the caller should write a value into the returned
    // `vacancy`. This operation allows multiple concurrent `get-or-set`
    // invocations to rendezvous such that only one invocation receives the
    // `vacant` result while all other invocations wait until the vacancy is
    // filled before receiving an `occupied` result. Implementations are not
    // required to implement this rendezvous or to rendezvous in all possible
    // cases.
    variant get-or-set-entry {
      occupied(incoming-value),
      vacant(vacancy)
    }
    get-or-set: func(k: key) -> future-get-or-set-result;

    // This block defines a special resource type used by `get-or-set` to
    // emulate `future<result<get-or-set-entry,error>>`.
    resource future-get-or-set-result {
        future-get-or-set-result-get: func() -> option<result<get-or-set-entry, error>>;
        listen-to-future-get-or-set-result: func() -> pollable;
    }

    // The following block defines the `vacancy` resource type. (When resource
    // types are added, the `u32` type aliases can be replaced by proper
    // `resource` types.) When the caller of `get-or-set` receives a `vacancy`,
    // they must either call the `fill` method or drop the `vacancy` to
    // indicate an error that prevents calling `fill`. An implementation MAY
    // have a timeout that drops a vacancy that hasn't been filled in order
    // to unblock other waiting `get-or-set` callers.
    resource vacancy {
        vacancy-fill: func(TTL-ms: option<u32>) -> outgoing-value;
    }

    // The `delete` operation removes any value with the given key from the
    // cache. Like all cache operations, `delete` is weakly ordered and thus
    // concurrent `get` calls may still see deleted keys for a period of time.
    // Additionally, due to weak ordering, concurrent `set` calls for the same
    // key may or may not get deleted.
    delete: func(k: key) -> future-result;
}
