Pudu programming language
Menu
Package

@chrismichaelps / pudu-lang-httpclient

Named HTTP clients for Pudu: a client factory, delegating handlers, pooled keep-alive connections, handler lifetimes, logging, and resilience

0.1.1Apache-2.01

InstallClose

Response.pudu

Pudu98 lines4.0 KB

GitHub ↗
1/** @HttpClient.Response.Module — what came back, and the request that produced it */2module PuduLangHttpClient.Response34import Std.Http as Http5import Std.Json as Json6import PuduLangHttpClient.Content as Content7import PuduLangHttpClient.Domain.Headers as Headers8import PuduLangHttpClient.Domain.RetryAfter as RetryAfter9import PuduLangHttpClient as HttpClient10import PuduLangHttpClient.Request as Request1112/** @HttpClient.Response.Response — a status, headers, a body, trailers, and the final request */13export type Response = {14  status: Http.Status,15  version: Http.Version,16  headers: Array[(Str, Str)],17  content: Content.Content,18  trailers: Array[(Str, Str)],19  request: Request.Request20}2122/// An HTTP/1.1 response with a status and no headers or body, answering the request.23export fn answer(request: &Request.Request, code: Int) -> Response {24  Response{status: Http.status(code), version: Http.Http11, headers: [], content: Content.empty(), trailers: [], request: *request}25}2627/// Whether the status is in the 2xx range.28export fn isSuccess(response: &Response) -> Bool { Http.isSuccess(&response.status) }2930/// The response when its status is a success, and `Unsuccessful` with its code and reason otherwise.31export fn ensureSuccess(response: Response) -> HttpClient.Outcome[Response] {32  if isSuccess(&response) { Ok(response) } else { Err(HttpClient.Unsuccessful(response.status.code, response.status.reason)) }33}3435/// The first value of a header, among the response's own headers and then its body's.36export fn header(response: &Response, name: Str) -> Option[Str] {37  match Headers.get(&response.headers, name) {38    case Some(found) => Some(found)39    case None => Headers.get(&response.content.headers, name)40  }41}4243/// Every value of a header, the response's own first and then its body's.44export fn headerValues(response: &Response, name: Str) -> Array[Str] {45  Headers.values(&response.headers, name).concat(Headers.values(&response.content.headers, name))46}4748/// The body as UTF-8 text.49export fn text(response: &Response) -> HttpClient.Outcome[Str] { Content.readText(&response.content) }5051/// The body's bytes.52export fn bytes(response: &Response) -> Bytes { response.content.payload }5354/// The body as a JSON document.55export fn json(response: &Response) -> HttpClient.Outcome[Json.Json] { Content.readJson(&response.content) }5657/// The body as a value that reads itself from JSON.58export fn readAs[T: Json.Decode](response: &Response) -> HttpClient.Outcome[T] { Content.readAs(&response.content) }5960/// The wait the response's `retry-after` header asks for, in milliseconds at `now` milliseconds61/// since the Unix epoch.62export fn retryAfter(response: &Response, now: Int) -> Option[Int] {63  let value = header(response, "retry-after") ?64  RetryAfter.delay(value, now)65}6667/** @HttpClient.Response.Answering — a response built one decision at a time, for stubs and tests */68export trait Answering {69  /// The response with a header value added after any the name has.70  fn withHeader(self: &Self, name: Str, value: Str) -> Self71  /// The response carrying a body.72  fn withContent(self: &Self, content: Content.Content) -> Self73  /// The response carrying UTF-8 plain text.74  fn withText(self: &Self, body: Str) -> Self75  /// The response carrying a JSON document.76  fn withJson(self: &Self, value: &Json.Json) -> Self77}7879impl Answering for Response {80  /// The response with a header value added after any the name has.81  fn withHeader(self: &Self, name: Str, value: Str) -> Self {82    if Headers.isContentHeader(name) {83      Response{..*self, content: Content.Content{..self.content, headers: Headers.add(&self.content.headers, name, value)}}84    } else {85      Response{..*self, headers: Headers.add(&self.headers, name, value)}86    }87  }8889  /// The response carrying a body.90  fn withContent(self: &Self, content: Content.Content) -> Self { Response{..*self, content: content} }9192  /// The response carrying UTF-8 plain text.93  fn withText(self: &Self, body: Str) -> Self { self.withContent(Content.plain(body)) }9495  /// The response carrying a JSON document.96  fn withJson(self: &Self, value: &Json.Json) -> Self { self.withContent(Content.json(value)) }97}98