160 lines
7.0 KiB
Plaintext
160 lines
7.0 KiB
Plaintext
// Note: this note isn't a doc comment.
|
|
|
|
// Note: the following comment block is a doc comment, because it start with `///`.
|
|
// It's also a comment about the interface defined in this file, because it starts with `@interface`
|
|
|
|
/// @interface WASI HTTP Core functionality.
|
|
///
|
|
/// Defines requests & responses and associated types, and `fetch`.
|
|
|
|
// `use` imports things from namespaces. The first identifier of an `ident1::ident2::[..]::identN`
|
|
// namespace chain needs to be resolved out of band. The remaining part of the chain is resolved
|
|
// as nested interfaces inside the definition of `ident1`.
|
|
// Note: I'm not sure if Result should just be an Interface Types thing instead of a WASI thing.
|
|
//use { Result, Option } from wasi::core
|
|
|
|
/// A blocking function for turning requests into responses.
|
|
/// @param req - a `request` object to process
|
|
/// @return resp - the `Result` object produced once the blocking operation has finished.
|
|
//
|
|
// Note: the syntax for function definitions is chosen with three goals in mind:
|
|
//
|
|
// 1. maximizing human parseability and intuitive understanding, by not presupposing
|
|
// familiarity with any specific notations. Having the `function` keyword isn't otherwise
|
|
// strictly required for disambiguation. This becomes even more important given that part of
|
|
// the "weirdness budget" needs to be used for having support for multiple return values.
|
|
//
|
|
// 2. uniformity in the face of multiple return values. Basically, it's always a mapping
|
|
// of N arguments to M return values, with the same syntax for both.
|
|
//
|
|
// 3. provide an obvious place for adding attributes, like `blocking` and `reentrant`.
|
|
//
|
|
// Note: the `blocking` attribute means that different lowering can be generated for this
|
|
// function, either based on synchronous, blocking calls, or on a callback-style interface.
|
|
fetch: /*blocking*/ func(req: handle request) -> handle response
|
|
|
|
/// A request resource, with lots of things missing for now.
|
|
// Note: `resource` blocks serve two purposes:
|
|
// 1. namespacing the definition of a Handle type with functions for operating on that handle
|
|
// 2. providing a way to define said functions such that they implicitly take the handle itself as
|
|
// the first argument. E.g., `method` in `request` is roughly equivalent to
|
|
// `request::method: func(self: handle request) -> (string)`
|
|
// ("Roughly" because the `resource` semantics allow us to generate better bindings for languages
|
|
// that have a concept of implicit receivers, such as `this` in JS.)
|
|
resource request {
|
|
|
|
/// A static function acting as a constructor
|
|
/// @return req - returns a new `request`
|
|
// Note: `static` here simply means that no implicit receiver argument will be passed.
|
|
// I.e., this is ~roughly~ analogous to a top-level definition of
|
|
// `request::request: func() -> (req: handle request)`
|
|
// whereas without `static`, it'd be analogous to
|
|
// `request::request: func(self: handle<request>) -> (req: handle<request>)`
|
|
static request: func() -> handle request
|
|
|
|
method: func() -> string
|
|
|
|
// Note: We could consider allowing the parens to be omitted for single return values, like so:
|
|
headers: func() -> handle headers
|
|
|
|
// Note: Or we could even allow leaving off the return value identifier, making it use the
|
|
// function name, like so:
|
|
body: func() -> handle body // This return type would be shorthand for `(body: body)`
|
|
}
|
|
|
|
/// A response resource, with lots of things missing for now.
|
|
resource response {
|
|
status: func() -> u16
|
|
headers: func() -> handle headers
|
|
body: func() -> handle body
|
|
}
|
|
|
|
/// A headers resource, with lots of things missing for now.
|
|
resource headers {
|
|
/// Return values for the given header name.
|
|
/// @param name - the header's name.
|
|
/// @return values - the values for this header, seperated by ", "
|
|
// Note: in reality, it might make sense for the values to be a sequence of some kind.
|
|
get: func(name: string) -> option<string>
|
|
}
|
|
|
|
/// A body resource.
|
|
/// Bodies are interesting in that they can be both sources and sinks, on both requests and responses.
|
|
resource body {
|
|
/// Read from a body.
|
|
/// @param dest - destination buffer to write into
|
|
/// @return result - a result containing the number of bytes written on success
|
|
// TODO: check if `out-buffer` is the right way to express this
|
|
// NOTE: s/expected/result/
|
|
read: func(dest: list<u8>) -> expected<u64, error>
|
|
|
|
/// Write to a body.
|
|
/// @param source - source buffer to read from
|
|
/// @return result - a result containing the number of bytes read on success
|
|
// TODO: check if `in-buffer` is the right way to express this
|
|
write: func(source: list<u8>) -> expected<u64, error>
|
|
}
|
|
/*
|
|
|
|
/// A nested interface, doing something neat and elaborate.
|
|
interface nested-interface1 {
|
|
/// An even better request resource than the one everybody's using.
|
|
resource better-request {
|
|
// TODO: do we need to have a ctor with special semantics? E.g. to generate actual
|
|
// constructors for languages that have them, such as JS?
|
|
new: func(request: handle<request>) -> handle<better-request>
|
|
// Note: sadly, the sauce better-request uses must remain secret, so it doesn't actually
|
|
// expose any of its functionality, and hence doesn't need any other methods.
|
|
}
|
|
/// Maps a request to an even better request.
|
|
// Note: the containing scope's members are in scope in a nested interface
|
|
fun: func(request: handle<request>) -> handle<response>
|
|
}
|
|
|
|
/// Another nested interface, doing something even more neat and elaborate.
|
|
/// It does this by adding shiny stuff to what nested-interface1 is doing.
|
|
interface nested-interface2 {
|
|
// Note: as mentioned in the comment on this file's first `use` statement, the first
|
|
// ident in a namespace chain needs to be resolved out of band. `self` is an exception to
|
|
// this rule, and allows referring to the outermost containing scope.
|
|
use self::nested-interface1::better-request
|
|
|
|
/// The secret sauce. It's so secret and magic that you're not allowed to touch it, quite sadly.
|
|
resource the-shiny {
|
|
}
|
|
|
|
/// Maps a better request to a plain old response.
|
|
/// @param request - the already pretty good request to add the shiny to
|
|
/// @return response - a boring, normal response
|
|
/// @return added-shiny - the shiny!
|
|
// Note: this just exists to demonstrate multiple return values, including their documentation
|
|
fun: func(request: better-request) -> tuple<handle<response>, handle<the-thiny>>
|
|
}
|
|
*/
|
|
|
|
/// An enum with some values
|
|
// Note: should we have a way to declare the underlying representation, along the lines of
|
|
// `enum error: u8 {..}`?
|
|
// Note: what about non-numeric types?
|
|
// Note: what about heterogeneous enums?
|
|
enum error {
|
|
overflow,
|
|
unavailable,
|
|
}
|
|
|
|
/// A function returning a Result, with the enum above as one of the options
|
|
/// @return result - a `Result` containing either the desired number, or an `error`
|
|
maybe-number: func() -> expected<u64, error>
|
|
|
|
/// A simple struct
|
|
record timestamp {
|
|
seconds: u64,
|
|
nanoseconds: u64,
|
|
}
|
|
|
|
/// A simple value
|
|
my-int: u32
|
|
|
|
/// A handle to a request
|
|
my-request: handle request
|