///|
/// Sends a generated request and decodes the successful response.
///
/// The two arguments are the matched halves of one generated operation, so a
/// call reads as a single line:
///
/// let repo = gh.call(
/// @gen.repos_get_request("gaato", "mbt-sdk"),
/// @gen.repos_get_decode,
/// )
///
/// Failures arrive as `@runtime.SdkError`; `api_error` reads GitHub's error
/// body out of one.
pub async fn[T] GitHub::call(
self : GitHub,
request : @http.Request,
decode : (@http.Response) -> T raise @runtime.SdkError,
) -> T raise @runtime.SdkError {
decode(self.send(request))
}
///|
/// Sends a generated request and returns the successful response undecoded.
///
/// This is for the operations whose body a decoder cannot describe: `204` and
/// `304` replies, and the representations requested through a non-JSON `accept`
/// such as `application/vnd.github.diff`.
pub async fn GitHub::send(
self : GitHub,
request : @http.Request,
) -> @http.Response raise @runtime.SdkError {
self.client.send(request, bucket=rate_limit_bucket(request.url))
}
///|
/// The rate limit bucket a request URL belongs to.
///
/// GitHub meters search separately from the rest of the REST API, and the two
/// windows are far apart (30 requests per minute against 5,000 per hour), so
/// they must not share a limiter bucket. GraphQL is a third window, budgeted in
/// points rather than requests: the vendored specification lists `core`,
/// `graphql`, `search` and the rest as separate resources of
/// `/rate_limit`, and a GraphQL response names its own with
/// `x-ratelimit-resource: graphql`.
///
/// The URL is either the relative path a generated operation produced or the
/// absolute `Link` target of a following page, so the scheme and authority are
/// stripped first, along with the `/api/v3` mount a GitHub Enterprise Server
/// deployment puts in front of every path. GraphQL is not under that mount —
/// Enterprise Server serves it from `/api/graphql` — so both spellings appear
/// here.
fn rate_limit_bucket(url : String) -> String {
let path = request_path(url)
if path == "/graphql" || path == "/api/graphql" {
"graphql"
} else if path.has_prefix("/search/") {
"search"
} else {
"core"
}
}
///|
/// Extracts the path of an absolute or relative request URL.
fn request_path(url : String) -> String {
let mut rest = url
let mut absolute = true
if url.has_prefix("https://") {
rest = url[8:].to_owned()
} else if url.has_prefix("http://") {
rest = url[7:].to_owned()
} else if url.has_prefix("//") {
rest = url[2:].to_owned()
} else {
absolute = false
}
if absolute {
rest = match rest.find("/") {
Some(index) => rest[index:].to_owned()
None => "/"
}
}
if rest.find("#") is Some(index) {
rest = rest[:index].to_owned()
}
if rest.find("?") is Some(index) {
rest = rest[:index].to_owned()
}
if !rest.has_prefix("/") {
rest = "/" + rest
}
if rest.has_prefix("/api/v3/") {
rest = rest[7:].to_owned()
}
rest
}