///|
/// Where a parameter is carried, the OpenAPI `in` locations: the path, the query
/// string, a header, or a cookie.
pub(all) enum ParamLoc {
InPath
InQuery
InHeader
InCookie
} derive(Eq)
///|
/// The OpenAPI `in` string for a location.
pub fn loc_str(l : ParamLoc) -> String {
match l {
InPath => "path"
InQuery => "query"
InHeader => "header"
InCookie => "cookie"
}
}
///|
/// A single request parameter descriptor: its `name`, `loc`ation, scalar
/// `schema`, whether it is `required`, an optional `description`, its value
/// `constraints`, the `default` that stands in when the request omits it, and
/// the `alias` it travels under on the wire. Path parameters are always required
/// (OpenAPI requires it); the constructor keeps the caller's value but
/// validation treats path params as mandatory.
pub(all) struct Param {
name : String
loc : ParamLoc
schema : @schema.Schema
required : Bool
description : String
constraints : Array[@schema.Constraint]
default : Json?
alias_ : String
} derive(Eq)
///|
/// Build a parameter descriptor. `schema` defaults to a string and `required` to
/// `true`. `constraints` are the Pydantic-style bounds FastAPI reads off a
/// `Query(ge=…, max_length=…)` — emitted into the parameter's documented schema
/// *and* enforced on the inbound value. `default` is emitted and makes the
/// parameter optional, since a value that has one is never missing. `alias` is
/// the name the parameter travels under when that differs from the one the code
/// calls it (← `Query(alias="item-query")`).
pub fn Param::new(
name : String,
loc : ParamLoc,
schema~ : @schema.Schema = SStr,
required~ : Bool = true,
description~ : String = "",
constraints~ : Array[@schema.Constraint] = [],
default? : Json,
alias_~ : String = "",
) -> Param {
{ name, loc, schema, required, description, constraints, default, alias_, }
}
///|
/// The name this parameter travels under: its `alias` when it has one, else its
/// own name. This is what is read off the request, what the document calls it,
/// and what a validation error's `loc` points at — all three being the client's
/// view of the parameter.
pub fn Param::key(self : Param) -> String {
if self.alias_ == "" {
self.name
} else {
self.alias_
}
}
///|
/// Whether a request must carry this parameter. One with a default never must —
/// its absence is answered by the default, exactly as a Python default argument
/// is.
pub fn Param::is_required(self : Param) -> Bool {
self.required && self.default is None
}
///|
/// A single response descriptor: the HTTP `status`, a human `description`, and an
/// optional body `schema` (`None` for an empty body, e.g. `204`).
pub(all) struct ResponseSpec {
status : Int32
description : String
body : @schema.Schema?
} derive(Eq)
///|
/// Build a response descriptor. `description` defaults to `"OK"` and there is no
/// body unless one is given.
pub fn ResponseSpec::new(
status : Int32,
description~ : String = "OK",
body? : @schema.Schema,
) -> ResponseSpec {
{ status, description, body, }
}
///|
/// The runtime endpoint descriptor a route can carry — the one first-class value
/// that replaces FastAPI reading a handler's signature. Walked once for the
/// OpenAPI operation (parameters + `requestBody` + `responses`, with every named
/// object hoisted into `components/schemas`) and for request validation.
pub(all) struct Endpoint {
params : Array[Param]
request_body : @schema.Schema?
request_required : Bool
responses : Array[ResponseSpec]
} derive(Eq)
///|
/// Build an endpoint descriptor. Everything is optional: a bare `Endpoint::new()`
/// describes an endpoint with no parameters, no body, and (on emit) a default
/// `200 OK` response.
pub fn Endpoint::new(
params~ : Array[Param] = [],
request_body? : @schema.Schema,
request_required~ : Bool = true,
responses~ : Array[ResponseSpec] = [],
) -> Endpoint {
{ params, request_body, request_required, responses, }
}
// -- OpenAPI emission for an endpoint -----------------------------------------
///|
pub extend ParamLoc with Eq::{not_equal, equal}
///|
pub extend Param with Eq::{not_equal, equal}
///|
pub extend ResponseSpec with Eq::{not_equal, equal}
///|
pub extend Endpoint with Eq::{not_equal, equal}