diff options
Diffstat (limited to 'aoc2023/build/packages/gleam_http/src')
13 files changed, 0 insertions, 2476 deletions
diff --git a/aoc2023/build/packages/gleam_http/src/gleam/http.gleam b/aoc2023/build/packages/gleam_http/src/gleam/http.gleam deleted file mode 100644 index a892006..0000000 --- a/aoc2023/build/packages/gleam_http/src/gleam/http.gleam +++ /dev/null @@ -1,560 +0,0 @@ -//// Functions for working with HTTP data structures in Gleam. -//// -//// This module makes it easy to create and modify Requests and Responses, data types. -//// A general HTTP message type is defined that enables functions to work on both requests and responses. -//// -//// This module does not implement a HTTP client or HTTP server, but it can be used as a base for them. - -import gleam/dynamic.{type DecodeError, type Dynamic, DecodeError} -import gleam/string -import gleam/bit_array -import gleam/result -import gleam/list -import gleam/bool - -/// HTTP standard method as defined by [RFC 2616](https://tools.ietf.org/html/rfc2616), -/// and PATCH which is defined by [RFC 5789](https://tools.ietf.org/html/rfc5789). -pub type Method { - Get - Post - Head - Put - Delete - Trace - Connect - Options - Patch - - /// Non-standard but valid HTTP methods. - Other(String) -} - -// TODO: check if the a is a valid HTTP method (i.e. it is a token, as per the -// spec) and return Ok(Other(s)) if so. -pub fn parse_method(s) -> Result(Method, Nil) { - case string.lowercase(s) { - "connect" -> Ok(Connect) - "delete" -> Ok(Delete) - "get" -> Ok(Get) - "head" -> Ok(Head) - "options" -> Ok(Options) - "patch" -> Ok(Patch) - "post" -> Ok(Post) - "put" -> Ok(Put) - "trace" -> Ok(Trace) - _ -> Error(Nil) - } -} - -pub fn method_to_string(method: Method) -> String { - case method { - Connect -> "connect" - Delete -> "delete" - Get -> "get" - Head -> "head" - Options -> "options" - Patch -> "patch" - Post -> "post" - Put -> "put" - Trace -> "trace" - Other(s) -> s - } -} - -/// The two URI schemes for HTTP -/// -pub type Scheme { - Http - Https -} - -/// Convert a scheme into a string. -/// -/// # Examples -/// -/// > scheme_to_string(Http) -/// "http" -/// -/// > scheme_to_string(Https) -/// "https" -/// -pub fn scheme_to_string(scheme: Scheme) -> String { - case scheme { - Http -> "http" - Https -> "https" - } -} - -/// Parse a HTTP scheme from a string -/// -/// # Examples -/// -/// > scheme_from_string("http") -/// Ok(Http) -/// -/// > scheme_from_string("ftp") -/// Error(Nil) -/// -pub fn scheme_from_string(scheme: String) -> Result(Scheme, Nil) { - case string.lowercase(scheme) { - "http" -> Ok(Http) - "https" -> Ok(Https) - _ -> Error(Nil) - } -} - -pub fn method_from_dynamic(value: Dynamic) -> Result(Method, List(DecodeError)) { - case do_method_from_dynamic(value) { - Ok(method) -> Ok(method) - Error(_) -> Error([DecodeError("HTTP method", dynamic.classify(value), [])]) - } -} - -pub type MultipartHeaders { - /// The headers for the part have been fully parsed. - MultipartHeaders( - headers: List(Header), - /// The remaining content that has not yet been parsed. This will contain - /// the body for this part, if any, and can be parsed with the - /// `parse_multipart_body` function. - remaining: BitArray, - ) - /// More input is required to parse the headers for this part. - MoreRequiredForHeaders( - /// Call this function to continue parsing the headers for this part. - continuation: fn(BitArray) -> Result(MultipartHeaders, Nil), - ) -} - -pub type MultipartBody { - /// The body for the part has been fully parsed. - MultipartBody( - // The rest of the body for this part. The full body of the part is this - // concatenated onto the end of each chunk returned by any previous - // `MoreRequiredForBody` returns. - chunk: BitArray, - /// This is `True` if this was the last part in the multipart message, - /// otherwise there are more parts to parse. - done: Bool, - /// The remaining content that has not yet been parsed. This will contain - /// the next part if `done` is `False`, otherwise it will contain the - /// epilogue, if any. - remaining: BitArray, - ) - MoreRequiredForBody( - // The body that has been parsed so far. The full body of the part is this - // concatenated with the chunk returned by each `MoreRequiredForBody` return - // value, and the final `MultipartBody` return value. - chunk: BitArray, - /// Call this function to continue parsing the body for this part. - continuation: fn(BitArray) -> Result(MultipartBody, Nil), - ) -} - -/// Parse the headers for part of a multipart message, as defined in RFC 2045. -/// -/// This function skips any preamble before the boundary. The preamble may be -/// retrieved using `parse_multipart_body`. -/// -/// This function will accept input of any size, it is up to the caller to limit -/// it if needed. -/// -/// To enable streaming parsing of multipart messages, this function will return -/// a continuation if there is not enough data to fully parse the headers. -/// Further information is available in the documentation for `MultipartBody`. -/// -pub fn parse_multipart_headers( - data: BitArray, - boundary: String, -) -> Result(MultipartHeaders, Nil) { - let boundary = bit_array.from_string(boundary) - // TODO: rewrite this to use a bit pattern once JavaScript supports - // the `b:binary-size(bsize)` pattern. - let prefix = <<45, 45, boundary:bits>> - case bit_array.slice(data, 0, bit_array.byte_size(prefix)) == Ok(prefix) { - // There is no preamble, parse the headers. - True -> parse_headers_after_prelude(data, boundary) - // There is a preamble, skip it before parsing. - False -> skip_preamble(data, boundary) - } -} - -/// Parse the body for part of a multipart message, as defined in RFC 2045. The -/// body is everything until the next boundary. This function is generally to be -/// called after calling `parse_multipart_headers` for a given part. -/// -/// This function will accept input of any size, it is up to the caller to limit -/// it if needed. -/// -/// To enable streaming parsing of multipart messages, this function will return -/// a continuation if there is not enough data to fully parse the body, along -/// with the data that has been parsed so far. Further information is available -/// in the documentation for `MultipartBody`. -/// -pub fn parse_multipart_body( - data: BitArray, - boundary: String, -) -> Result(MultipartBody, Nil) { - boundary - |> bit_array.from_string - |> parse_body_with_bit_array(data, _) -} - -fn parse_body_with_bit_array( - data: BitArray, - boundary: BitArray, -) -> Result(MultipartBody, Nil) { - let bsize = bit_array.byte_size(boundary) - let prefix = bit_array.slice(data, 0, 2 + bsize) - case prefix == Ok(<<45, 45, boundary:bits>>) { - True -> Ok(MultipartBody(<<>>, done: False, remaining: data)) - False -> parse_body_loop(data, boundary, <<>>) - } -} - -fn parse_body_loop( - data: BitArray, - boundary: BitArray, - body: BitArray, -) -> Result(MultipartBody, Nil) { - let dsize = bit_array.byte_size(data) - let bsize = bit_array.byte_size(boundary) - let required = 6 + bsize - case data { - _ if dsize < required -> { - more_please_body(parse_body_loop(_, boundary, <<>>), body, data) - } - - // TODO: flatten this into a single case expression once JavaScript supports - // the `b:binary-size(bsize)` pattern. - // - // \r\n - <<13, 10, data:bytes>> -> { - let desired = <<45, 45, boundary:bits>> - let size = bit_array.byte_size(desired) - let dsize = bit_array.byte_size(data) - let prefix = bit_array.slice(data, 0, size) - let rest = bit_array.slice(data, size, dsize - size) - case prefix == Ok(desired), rest { - // --boundary\r\n - True, Ok(<<13, 10, _:bytes>>) -> - Ok(MultipartBody(body, done: False, remaining: data)) - - // --boundary-- - True, Ok(<<45, 45, data:bytes>>) -> - Ok(MultipartBody(body, done: True, remaining: data)) - - False, _ -> parse_body_loop(data, boundary, <<body:bits, 13, 10>>) - _, _ -> Error(Nil) - } - } - - <<char, data:bytes>> -> { - parse_body_loop(data, boundary, <<body:bits, char>>) - } - } -} - -fn parse_headers_after_prelude( - data: BitArray, - boundary: BitArray, -) -> Result(MultipartHeaders, Nil) { - let dsize = bit_array.byte_size(data) - let bsize = bit_array.byte_size(boundary) - let required_size = bsize + 4 - - // TODO: this could be written as a single case expression if JavaScript had - // support for the `b:binary-size(bsize)` pattern. Rewrite this once the - // compiler support this. - - use <- bool.guard( - when: dsize < required_size, - return: more_please_headers(parse_headers_after_prelude(_, boundary), data), - ) - - use prefix <- result.try(bit_array.slice(data, 0, required_size - 2)) - use second <- result.try(bit_array.slice(data, 2 + bsize, 2)) - let desired = <<45, 45, boundary:bits>> - - use <- bool.guard(prefix != desired, return: Error(Nil)) - - case second == <<45, 45>> { - // --boundary-- - // The last boundary. Return the epilogue. - True -> { - let rest_size = dsize - required_size - use data <- result.map(bit_array.slice(data, required_size, rest_size)) - MultipartHeaders([], remaining: data) - } - - // --boundary - False -> { - let start = required_size - 2 - let rest_size = dsize - required_size + 2 - use data <- result.try(bit_array.slice(data, start, rest_size)) - do_parse_headers(data) - } - } -} - -fn skip_preamble( - data: BitArray, - boundary: BitArray, -) -> Result(MultipartHeaders, Nil) { - let data_size = bit_array.byte_size(data) - let boundary_size = bit_array.byte_size(boundary) - let required = boundary_size + 4 - case data { - _ if data_size < required -> - more_please_headers(skip_preamble(_, boundary), data) - - // TODO: change this to use one non-nested case expression once the compiler - // supports the `b:binary-size(bsize)` pattern on JS. - // \r\n-- - <<13, 10, 45, 45, data:bytes>> -> { - case bit_array.slice(data, 0, boundary_size) { - // --boundary - Ok(prefix) if prefix == boundary -> { - let start = boundary_size - let length = bit_array.byte_size(data) - boundary_size - use rest <- result.try(bit_array.slice(data, start, length)) - do_parse_headers(rest) - } - Ok(_) -> skip_preamble(data, boundary) - Error(_) -> Error(Nil) - } - } - - <<_, data:bytes>> -> skip_preamble(data, boundary) - } -} - -fn skip_whitespace(data: BitArray) -> BitArray { - case data { - // Space or tab. - <<32, data:bytes>> | <<9, data:bytes>> -> skip_whitespace(data) - _ -> data - } -} - -fn do_parse_headers(data: BitArray) -> Result(MultipartHeaders, Nil) { - case data { - // \r\n\r\n - // We've reached the end, there are no headers. - <<13, 10, 13, 10, data:bytes>> -> Ok(MultipartHeaders([], remaining: data)) - - // \r\n - // Skip the line break after the boundary. - <<13, 10, data:bytes>> -> parse_header_name(data, [], <<>>) - - <<13>> | <<>> -> more_please_headers(do_parse_headers, data) - - _ -> Error(Nil) - } -} - -fn parse_header_name( - data: BitArray, - headers: List(Header), - name: BitArray, -) -> Result(MultipartHeaders, Nil) { - case skip_whitespace(data) { - // : - <<58, data:bytes>> -> - data - |> skip_whitespace - |> parse_header_value(headers, name, <<>>) - - <<char, data:bytes>> -> - parse_header_name(data, headers, <<name:bits, char>>) - - <<>> -> more_please_headers(parse_header_name(_, headers, name), data) - } -} - -fn parse_header_value( - data: BitArray, - headers: List(Header), - name: BitArray, - value: BitArray, -) -> Result(MultipartHeaders, Nil) { - let size = bit_array.byte_size(data) - case data { - // We need at least 4 bytes to check for the end of the headers. - _ if size < 4 -> - fn(data) { - data - |> skip_whitespace - |> parse_header_value(headers, name, value) - } - |> more_please_headers(data) - - // \r\n\r\n - <<13, 10, 13, 10, data:bytes>> -> { - use name <- result.try(bit_array.to_string(name)) - use value <- result.map(bit_array.to_string(value)) - let headers = list.reverse([#(string.lowercase(name), value), ..headers]) - MultipartHeaders(headers, data) - } - - // \r\n\s - // \r\n\t - <<13, 10, 32, data:bytes>> | <<13, 10, 9, data:bytes>> -> - parse_header_value(data, headers, name, value) - - // \r\n - <<13, 10, data:bytes>> -> { - use name <- result.try(bit_array.to_string(name)) - use value <- result.try(bit_array.to_string(value)) - let headers = [#(string.lowercase(name), value), ..headers] - parse_header_name(data, headers, <<>>) - } - - <<char, rest:bytes>> -> { - let value = <<value:bits, char>> - parse_header_value(rest, headers, name, value) - } - - _ -> Error(Nil) - } -} - -fn more_please_headers( - continuation: fn(BitArray) -> Result(MultipartHeaders, Nil), - existing: BitArray, -) -> Result(MultipartHeaders, Nil) { - Ok(MoreRequiredForHeaders(fn(more) { - use <- bool.guard(more == <<>>, return: Error(Nil)) - continuation(<<existing:bits, more:bits>>) - })) -} - -pub type ContentDisposition { - ContentDisposition(String, parameters: List(#(String, String))) -} - -pub fn parse_content_disposition( - header: String, -) -> Result(ContentDisposition, Nil) { - parse_content_disposition_type(header, "") -} - -fn parse_content_disposition_type( - header: String, - name: String, -) -> Result(ContentDisposition, Nil) { - case string.pop_grapheme(header) { - Error(Nil) -> Ok(ContentDisposition(name, [])) - - Ok(#(" ", rest)) | Ok(#("\t", rest)) | Ok(#(";", rest)) -> { - let result = parse_rfc_2045_parameters(rest, []) - use parameters <- result.map(result) - ContentDisposition(name, parameters) - } - - Ok(#(grapheme, rest)) -> - parse_content_disposition_type(rest, name <> string.lowercase(grapheme)) - } -} - -fn parse_rfc_2045_parameters( - header: String, - parameters: List(#(String, String)), -) -> Result(List(#(String, String)), Nil) { - case string.pop_grapheme(header) { - Error(Nil) -> Ok(list.reverse(parameters)) - - Ok(#(";", rest)) | Ok(#(" ", rest)) | Ok(#("\t", rest)) -> - parse_rfc_2045_parameters(rest, parameters) - - Ok(#(grapheme, rest)) -> { - let acc = string.lowercase(grapheme) - use #(parameter, rest) <- result.try(parse_rfc_2045_parameter(rest, acc)) - parse_rfc_2045_parameters(rest, [parameter, ..parameters]) - } - } -} - -fn parse_rfc_2045_parameter( - header: String, - name: String, -) -> Result(#(#(String, String), String), Nil) { - use #(grapheme, rest) <- result.try(string.pop_grapheme(header)) - case grapheme { - "=" -> parse_rfc_2045_parameter_value(rest, name) - _ -> parse_rfc_2045_parameter(rest, name <> string.lowercase(grapheme)) - } -} - -fn parse_rfc_2045_parameter_value( - header: String, - name: String, -) -> Result(#(#(String, String), String), Nil) { - case string.pop_grapheme(header) { - Error(Nil) -> Error(Nil) - Ok(#("\"", rest)) -> parse_rfc_2045_parameter_quoted_value(rest, name, "") - Ok(#(grapheme, rest)) -> - Ok(parse_rfc_2045_parameter_unquoted_value(rest, name, grapheme)) - } -} - -fn parse_rfc_2045_parameter_quoted_value( - header: String, - name: String, - value: String, -) -> Result(#(#(String, String), String), Nil) { - case string.pop_grapheme(header) { - Error(Nil) -> Error(Nil) - Ok(#("\"", rest)) -> Ok(#(#(name, value), rest)) - Ok(#("\\", rest)) -> { - use #(grapheme, rest) <- result.try(string.pop_grapheme(rest)) - parse_rfc_2045_parameter_quoted_value(rest, name, value <> grapheme) - } - Ok(#(grapheme, rest)) -> - parse_rfc_2045_parameter_quoted_value(rest, name, value <> grapheme) - } -} - -fn parse_rfc_2045_parameter_unquoted_value( - header: String, - name: String, - value: String, -) -> #(#(String, String), String) { - case string.pop_grapheme(header) { - Error(Nil) -> #(#(name, value), header) - - Ok(#(";", rest)) | Ok(#(" ", rest)) | Ok(#("\t", rest)) -> #( - #(name, value), - rest, - ) - - Ok(#(grapheme, rest)) -> - parse_rfc_2045_parameter_unquoted_value(rest, name, value <> grapheme) - } -} - -fn more_please_body( - continuation: fn(BitArray) -> Result(MultipartBody, Nil), - chunk: BitArray, - existing: BitArray, -) -> Result(MultipartBody, Nil) { - fn(more) { - use <- bool.guard(more == <<>>, return: Error(Nil)) - continuation(<<existing:bits, more:bits>>) - } - |> MoreRequiredForBody(chunk, _) - |> Ok -} - -@target(erlang) -@external(erlang, "gleam_http_native", "decode_method") -fn do_method_from_dynamic(a: Dynamic) -> Result(Method, nil) - -@target(javascript) -@external(javascript, "../gleam_http_native.mjs", "decode_method") -fn do_method_from_dynamic(a: Dynamic) -> Result(Method, Nil) - -/// A HTTP header is a key-value pair. Header keys should be all lowercase -/// characters. -pub type Header = - #(String, String) diff --git a/aoc2023/build/packages/gleam_http/src/gleam/http/cookie.gleam b/aoc2023/build/packages/gleam_http/src/gleam/http/cookie.gleam deleted file mode 100644 index e9ccb55..0000000 --- a/aoc2023/build/packages/gleam_http/src/gleam/http/cookie.gleam +++ /dev/null @@ -1,128 +0,0 @@ -import gleam/result -import gleam/int -import gleam/list -import gleam/regex -import gleam/string -import gleam/option.{type Option, Some} -import gleam/http.{type Scheme} - -/// Policy options for the SameSite cookie attribute -/// -/// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite -pub type SameSitePolicy { - Lax - Strict - None -} - -fn same_site_to_string(policy) { - case policy { - Lax -> "Lax" - Strict -> "Strict" - None -> "None" - } -} - -/// Attributes of a cookie when sent to a client in the `set-cookie` header. -pub type Attributes { - Attributes( - max_age: Option(Int), - domain: Option(String), - path: Option(String), - secure: Bool, - http_only: Bool, - same_site: Option(SameSitePolicy), - ) -} - -/// Helper to create sensible default attributes for a set cookie. -/// -/// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#Attributes -pub fn defaults(scheme: Scheme) { - Attributes( - max_age: option.None, - domain: option.None, - path: option.Some("/"), - secure: scheme == http.Https, - http_only: True, - same_site: Some(Lax), - ) -} - -const epoch = "Expires=Thu, 01 Jan 1970 00:00:00 GMT" - -fn cookie_attributes_to_list(attributes) { - let Attributes( - max_age: max_age, - domain: domain, - path: path, - secure: secure, - http_only: http_only, - same_site: same_site, - ) = attributes - [ - // Expires is a deprecated attribute for cookies, it has been replaced with MaxAge - // MaxAge is widely supported and so Expires values are not set. - // Only when deleting cookies is the exception made to use the old format, - // to ensure complete clearup of cookies if required by an application. - case max_age { - option.Some(0) -> option.Some([epoch]) - _ -> option.None - }, - option.map(max_age, fn(max_age) { ["Max-Age=", int.to_string(max_age)] }), - option.map(domain, fn(domain) { ["Domain=", domain] }), - option.map(path, fn(path) { ["Path=", path] }), - case secure { - True -> option.Some(["Secure"]) - False -> option.None - }, - case http_only { - True -> option.Some(["HttpOnly"]) - False -> option.None - }, - option.map( - same_site, - fn(same_site) { ["SameSite=", same_site_to_string(same_site)] }, - ), - ] - |> list.filter_map(option.to_result(_, Nil)) -} - -pub fn set_header(name: String, value: String, attributes: Attributes) -> String { - [[name, "=", value], ..cookie_attributes_to_list(attributes)] - |> list.map(string.join(_, "")) - |> string.join("; ") -} - -/// Parse a list of cookies from a header string. Any malformed cookies will be -/// discarded. -/// -pub fn parse(cookie_string: String) -> List(#(String, String)) { - let assert Ok(re) = regex.from_string("[,;]") - regex.split(re, cookie_string) - |> list.filter_map(fn(pair) { - case string.split_once(string.trim(pair), "=") { - Ok(#("", _)) -> Error(Nil) - Ok(#(key, value)) -> { - let key = string.trim(key) - let value = string.trim(value) - use _ <- result.then(check_token(key)) - use _ <- result.then(check_token(value)) - Ok(#(key, value)) - } - Error(Nil) -> Error(Nil) - } - }) -} - -fn check_token(token: String) -> Result(Nil, Nil) { - case string.pop_grapheme(token) { - Error(Nil) -> Ok(Nil) - Ok(#(" ", _)) -> Error(Nil) - Ok(#("\t", _)) -> Error(Nil) - Ok(#("\r", _)) -> Error(Nil) - Ok(#("\n", _)) -> Error(Nil) - Ok(#("\f", _)) -> Error(Nil) - Ok(#(_, rest)) -> check_token(rest) - } -} diff --git a/aoc2023/build/packages/gleam_http/src/gleam/http/request.gleam b/aoc2023/build/packages/gleam_http/src/gleam/http/request.gleam deleted file mode 100644 index 0bf9af9..0000000 --- a/aoc2023/build/packages/gleam_http/src/gleam/http/request.gleam +++ /dev/null @@ -1,267 +0,0 @@ -import gleam/result -// TODO: validate_req -import gleam/http.{type Header, type Method, type Scheme, Get} -import gleam/http/cookie -import gleam/option.{type Option} -import gleam/uri.{type Uri, Uri} -import gleam/list -import gleam/string -import gleam/string_builder - -// TODO: document -pub type Request(body) { - Request( - method: Method, - headers: List(Header), - body: body, - scheme: Scheme, - host: String, - port: Option(Int), - path: String, - query: Option(String), - ) -} - -/// Return the uri that a request was sent to. -/// -pub fn to_uri(request: Request(a)) -> Uri { - Uri( - scheme: option.Some(http.scheme_to_string(request.scheme)), - userinfo: option.None, - host: option.Some(request.host), - port: request.port, - path: request.path, - query: request.query, - fragment: option.None, - ) -} - -/// Construct a request from a URI. -/// -pub fn from_uri(uri: Uri) -> Result(Request(String), Nil) { - use scheme <- result.then( - uri.scheme - |> option.unwrap("") - |> http.scheme_from_string, - ) - use host <- result.then( - uri.host - |> option.to_result(Nil), - ) - let req = - Request( - method: Get, - headers: [], - body: "", - scheme: scheme, - host: host, - port: uri.port, - path: uri.path, - query: uri.query, - ) - Ok(req) -} - -/// Get the value for a given header. -/// -/// If the request does not have that header then `Error(Nil)` is returned. -/// -pub fn get_header(request: Request(body), key: String) -> Result(String, Nil) { - list.key_find(request.headers, string.lowercase(key)) -} - -/// Set the header with the given value under the given header key. -/// -/// If already present, it is replaced. -pub fn set_header( - request: Request(body), - key: String, - value: String, -) -> Request(body) { - let headers = list.key_set(request.headers, string.lowercase(key), value) - Request(..request, headers: headers) -} - -/// Prepend the header with the given value under the given header key. -/// -/// Similar to `set_header` except if the header already exists it prepends -/// another header with the same key. -pub fn prepend_header( - request: Request(body), - key: String, - value: String, -) -> Request(body) { - let headers = [#(string.lowercase(key), value), ..request.headers] - Request(..request, headers: headers) -} - -// TODO: record update syntax, which can't be done currently as body type changes -/// Set the body of the request, overwriting any existing body. -/// -pub fn set_body(req: Request(old_body), body: new_body) -> Request(new_body) { - let Request( - method: method, - headers: headers, - scheme: scheme, - host: host, - port: port, - path: path, - query: query, - .., - ) = req - Request( - method: method, - headers: headers, - body: body, - scheme: scheme, - host: host, - port: port, - path: path, - query: query, - ) -} - -/// Update the body of a request using a given function. -/// -pub fn map( - request: Request(old_body), - transform: fn(old_body) -> new_body, -) -> Request(new_body) { - request.body - |> transform - |> set_body(request, _) -} - -/// Return the non-empty segments of a request path. -/// -/// # Examples -/// -/// ```gleam -/// > new() -/// > |> set_path("/one/two/three") -/// > |> path_segments -/// ["one", "two", "three"] -/// ``` -/// -pub fn path_segments(request: Request(body)) -> List(String) { - request.path - |> uri.path_segments -} - -/// Decode the query of a request. -pub fn get_query(request: Request(body)) -> Result(List(#(String, String)), Nil) { - case request.query { - option.Some(query_string) -> uri.parse_query(query_string) - option.None -> Ok([]) - } -} - -// TODO: escape -/// Set the query of the request. -/// -pub fn set_query( - req: Request(body), - query: List(#(String, String)), -) -> Request(body) { - let pair = fn(t: #(String, String)) { - string_builder.from_strings([t.0, "=", t.1]) - } - let query = - query - |> list.map(pair) - |> list.intersperse(string_builder.from_string("&")) - |> string_builder.concat - |> string_builder.to_string - |> option.Some - Request(..req, query: query) -} - -/// Set the method of the request. -/// -pub fn set_method(req: Request(body), method: Method) -> Request(body) { - Request(..req, method: method) -} - -/// A request with commonly used default values. This request can be used as -/// an initial value and then update to create the desired request. -/// -pub fn new() -> Request(String) { - Request( - method: Get, - headers: [], - body: "", - scheme: http.Https, - host: "localhost", - port: option.None, - path: "", - query: option.None, - ) -} - -/// Construct a request from a URL string -/// -pub fn to(url: String) -> Result(Request(String), Nil) { - url - |> uri.parse - |> result.then(from_uri) -} - -/// Set the scheme (protocol) of the request. -/// -pub fn set_scheme(req: Request(body), scheme: Scheme) -> Request(body) { - Request(..req, scheme: scheme) -} - -/// Set the method of the request. -/// -pub fn set_host(req: Request(body), host: String) -> Request(body) { - Request(..req, host: host) -} - -/// Set the port of the request. -/// -pub fn set_port(req: Request(body), port: Int) -> Request(body) { - Request(..req, port: option.Some(port)) -} - -/// Set the path of the request. -/// -pub fn set_path(req: Request(body), path: String) -> Request(body) { - Request(..req, path: path) -} - -/// Send a cookie with a request -/// -/// Multiple cookies are added to the same cookie header. -pub fn set_cookie(req: Request(body), name: String, value: String) { - let new_cookie_string = string.join([name, value], "=") - - let #(cookies_string, headers) = case list.key_pop(req.headers, "cookie") { - Ok(#(cookies_string, headers)) -> { - let cookies_string = - string.join([cookies_string, new_cookie_string], "; ") - #(cookies_string, headers) - } - Error(Nil) -> #(new_cookie_string, req.headers) - } - - Request(..req, headers: [#("cookie", cookies_string), ..headers]) -} - -/// Fetch the cookies sent in a request. -/// -/// Note badly formed cookie pairs will be ignored. -/// RFC6265 specifies that invalid cookie names/attributes should be ignored. -pub fn get_cookies(req) -> List(#(String, String)) { - let Request(headers: headers, ..) = req - - headers - |> list.filter_map(fn(header) { - let #(name, value) = header - case name { - "cookie" -> Ok(cookie.parse(value)) - _ -> Error(Nil) - } - }) - |> list.flatten() -} diff --git a/aoc2023/build/packages/gleam_http/src/gleam/http/response.gleam b/aoc2023/build/packages/gleam_http/src/gleam/http/response.gleam deleted file mode 100644 index 87f9140..0000000 --- a/aoc2023/build/packages/gleam_http/src/gleam/http/response.gleam +++ /dev/null @@ -1,141 +0,0 @@ -import gleam/result -import gleam/http.{type Header} -import gleam/http/cookie -import gleam/list -import gleam/string -import gleam/option - -// TODO: document -pub type Response(body) { - Response(status: Int, headers: List(Header), body: body) -} - -/// Update the body of a response using a given result returning function. -/// -/// If the given function returns an `Ok` value the body is set, if it returns -/// an `Error` value then the error is returned. -/// -pub fn try_map( - response: Response(old_body), - transform: fn(old_body) -> Result(new_body, error), -) -> Result(Response(new_body), error) { - use body <- result.then(transform(response.body)) - Ok(set_body(response, body)) -} - -/// Construct an empty Response. -/// -/// The body type of the returned response is `String` and could be set with a -/// call to `set_body`. -/// -pub fn new(status: Int) -> Response(String) { - Response(status: status, headers: [], body: "") -} - -/// Get the value for a given header. -/// -/// If the response does not have that header then `Error(Nil)` is returned. -/// -pub fn get_header(response: Response(body), key: String) -> Result(String, Nil) { - list.key_find(response.headers, string.lowercase(key)) -} - -/// Set the header with the given value under the given header key. -/// -/// If the response already has that key, it is replaced. -pub fn set_header( - response: Response(body), - key: String, - value: String, -) -> Response(body) { - let headers = list.key_set(response.headers, string.lowercase(key), value) - Response(..response, headers: headers) -} - -/// Prepend the header with the given value under the given header key. -/// -/// Similar to `set_header` except if the header already exists it prepends -/// another header with the same key. -pub fn prepend_header( - response: Response(body), - key: String, - value: String, -) -> Response(body) { - let headers = [#(string.lowercase(key), value), ..response.headers] - Response(..response, headers: headers) -} - -/// Set the body of the response, overwriting any existing body. -/// -pub fn set_body( - response: Response(old_body), - body: new_body, -) -> Response(new_body) { - let Response(status: status, headers: headers, ..) = response - Response(status: status, headers: headers, body: body) -} - -/// Update the body of a response using a given function. -/// -pub fn map( - response: Response(old_body), - transform: fn(old_body) -> new_body, -) -> Response(new_body) { - response.body - |> transform - |> set_body(response, _) -} - -/// Create a response that redirects to the given uri. -/// -pub fn redirect(uri: String) -> Response(String) { - Response( - status: 303, - headers: [#("location", uri)], - body: string.append("You are being redirected to ", uri), - ) -} - -/// Fetch the cookies sent in a response. -/// -/// Badly formed cookies will be discarded. -/// -pub fn get_cookies(resp) -> List(#(String, String)) { - let Response(headers: headers, ..) = resp - headers - |> list.filter_map(fn(header) { - let #(name, value) = header - case name { - "set-cookie" -> Ok(cookie.parse(value)) - _ -> Error(Nil) - } - }) - |> list.flatten() -} - -/// Set a cookie value for a client -/// -pub fn set_cookie( - response: Response(t), - name: String, - value: String, - attributes: cookie.Attributes, -) -> Response(t) { - prepend_header( - response, - "set-cookie", - cookie.set_header(name, value, attributes), - ) -} - -/// Expire a cookie value for a client -/// -/// Note: The attributes value should be the same as when the response cookie was set. -pub fn expire_cookie( - response: Response(t), - name: String, - attributes: cookie.Attributes, -) -> Response(t) { - let attrs = cookie.Attributes(..attributes, max_age: option.Some(0)) - set_cookie(response, name, "", attrs) -} diff --git a/aoc2023/build/packages/gleam_http/src/gleam/http/service.gleam b/aoc2023/build/packages/gleam_http/src/gleam/http/service.gleam deleted file mode 100644 index 3dfac87..0000000 --- a/aoc2023/build/packages/gleam_http/src/gleam/http/service.gleam +++ /dev/null @@ -1,82 +0,0 @@ -import gleam/http.{Delete, Patch, Post, Put} -import gleam/http/request.{type Request} -import gleam/http/response.{type Response} -import gleam/list -import gleam/result - -// TODO: document -pub type Service(in, out) = - fn(Request(in)) -> Response(out) - -pub type Middleware(before_req, before_resp, after_req, after_resp) = - fn(Service(before_req, before_resp)) -> Service(after_req, after_resp) - -/// A middleware that transform the response body returned by the service using -/// a given function. -/// -pub fn map_response_body( - service: Service(req, a), - with mapper: fn(a) -> b, -) -> Service(req, b) { - fn(req) { - req - |> service - |> response.map(mapper) - } -} - -/// A middleware that prepends a header to the request. -/// -pub fn prepend_response_header( - service: Service(req, resp), - key: String, - value: String, -) -> Service(req, resp) { - fn(req) { - req - |> service - |> response.prepend_header(key, value) - } -} - -fn ensure_post(req: Request(a)) { - case req.method { - Post -> Ok(req) - _ -> Error(Nil) - } -} - -fn get_override_method(request: Request(t)) -> Result(http.Method, Nil) { - use query_params <- result.then(request.get_query(request)) - use method <- result.then(list.key_find(query_params, "_method")) - use method <- result.then(http.parse_method(method)) - case method { - Put | Patch | Delete -> Ok(method) - _ -> Error(Nil) - } -} - -/// A middleware that overrides an incoming POST request with a method given in -/// the request's `_method` query paramerter. This is useful as web browsers -/// typically only support GET and POST requests, but our application may -/// expect other HTTP methods that are more semantically correct. -/// -/// The methods PUT, PATCH, and DELETE are accepted for overriding, all others -/// are ignored. -/// -/// The `_method` query paramerter can be specified in a HTML form like so: -/// -/// <form method="POST" action="/item/1?_method=DELETE"> -/// <button type="submit">Delete item</button> -/// </form> -/// -pub fn method_override(service: Service(req, resp)) -> Service(req, resp) { - fn(request) { - request - |> ensure_post - |> result.then(get_override_method) - |> result.map(request.set_method(request, _)) - |> result.unwrap(request) - |> service - } -} diff --git a/aoc2023/build/packages/gleam_http/src/gleam@http.erl b/aoc2023/build/packages/gleam_http/src/gleam@http.erl deleted file mode 100644 index 91ee6e8..0000000 --- a/aoc2023/build/packages/gleam_http/src/gleam@http.erl +++ /dev/null @@ -1,626 +0,0 @@ --module(gleam@http). --compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]). - --export([parse_method/1, method_to_string/1, scheme_to_string/1, scheme_from_string/1, parse_content_disposition/1, parse_multipart_body/2, method_from_dynamic/1, parse_multipart_headers/2]). --export_type([method/0, scheme/0, multipart_headers/0, multipart_body/0, content_disposition/0]). - --type method() :: get | - post | - head | - put | - delete | - trace | - connect | - options | - patch | - {other, binary()}. - --type scheme() :: http | https. - --type multipart_headers() :: {multipart_headers, - list({binary(), binary()}), - bitstring()} | - {more_required_for_headers, - fun((bitstring()) -> {ok, multipart_headers()} | {error, nil})}. - --type multipart_body() :: {multipart_body, bitstring(), boolean(), bitstring()} | - {more_required_for_body, - bitstring(), - fun((bitstring()) -> {ok, multipart_body()} | {error, nil})}. - --type content_disposition() :: {content_disposition, - binary(), - list({binary(), binary()})}. - --spec parse_method(binary()) -> {ok, method()} | {error, nil}. -parse_method(S) -> - case gleam@string:lowercase(S) of - <<"connect"/utf8>> -> - {ok, connect}; - - <<"delete"/utf8>> -> - {ok, delete}; - - <<"get"/utf8>> -> - {ok, get}; - - <<"head"/utf8>> -> - {ok, head}; - - <<"options"/utf8>> -> - {ok, options}; - - <<"patch"/utf8>> -> - {ok, patch}; - - <<"post"/utf8>> -> - {ok, post}; - - <<"put"/utf8>> -> - {ok, put}; - - <<"trace"/utf8>> -> - {ok, trace}; - - _ -> - {error, nil} - end. - --spec method_to_string(method()) -> binary(). -method_to_string(Method) -> - case Method of - connect -> - <<"connect"/utf8>>; - - delete -> - <<"delete"/utf8>>; - - get -> - <<"get"/utf8>>; - - head -> - <<"head"/utf8>>; - - options -> - <<"options"/utf8>>; - - patch -> - <<"patch"/utf8>>; - - post -> - <<"post"/utf8>>; - - put -> - <<"put"/utf8>>; - - trace -> - <<"trace"/utf8>>; - - {other, S} -> - S - end. - --spec scheme_to_string(scheme()) -> binary(). -scheme_to_string(Scheme) -> - case Scheme of - http -> - <<"http"/utf8>>; - - https -> - <<"https"/utf8>> - end. - --spec scheme_from_string(binary()) -> {ok, scheme()} | {error, nil}. -scheme_from_string(Scheme) -> - case gleam@string:lowercase(Scheme) of - <<"http"/utf8>> -> - {ok, http}; - - <<"https"/utf8>> -> - {ok, https}; - - _ -> - {error, nil} - end. - --spec skip_whitespace(bitstring()) -> bitstring(). -skip_whitespace(Data) -> - case Data of - <<32, Data@1/binary>> -> - skip_whitespace(Data@1); - - <<9, Data@1/binary>> -> - skip_whitespace(Data@1); - - _ -> - Data - end. - --spec more_please_headers( - fun((bitstring()) -> {ok, multipart_headers()} | {error, nil}), - bitstring() -) -> {ok, multipart_headers()} | {error, nil}. -more_please_headers(Continuation, Existing) -> - {ok, - {more_required_for_headers, - fun(More) -> - gleam@bool:guard( - More =:= <<>>, - {error, nil}, - fun() -> - Continuation(<<Existing/bitstring, More/bitstring>>) - end - ) - end}}. - --spec parse_rfc_2045_parameter_quoted_value(binary(), binary(), binary()) -> {ok, - {{binary(), binary()}, binary()}} | - {error, nil}. -parse_rfc_2045_parameter_quoted_value(Header, Name, Value) -> - case gleam@string:pop_grapheme(Header) of - {error, nil} -> - {error, nil}; - - {ok, {<<"\""/utf8>>, Rest}} -> - {ok, {{Name, Value}, Rest}}; - - {ok, {<<"\\"/utf8>>, Rest@1}} -> - gleam@result:'try'( - gleam@string:pop_grapheme(Rest@1), - fun(_use0) -> - {Grapheme, Rest@2} = _use0, - parse_rfc_2045_parameter_quoted_value( - Rest@2, - Name, - <<Value/binary, Grapheme/binary>> - ) - end - ); - - {ok, {Grapheme@1, Rest@3}} -> - parse_rfc_2045_parameter_quoted_value( - Rest@3, - Name, - <<Value/binary, Grapheme@1/binary>> - ) - end. - --spec parse_rfc_2045_parameter_unquoted_value(binary(), binary(), binary()) -> {{binary(), - binary()}, - binary()}. -parse_rfc_2045_parameter_unquoted_value(Header, Name, Value) -> - case gleam@string:pop_grapheme(Header) of - {error, nil} -> - {{Name, Value}, Header}; - - {ok, {<<";"/utf8>>, Rest}} -> - {{Name, Value}, Rest}; - - {ok, {<<" "/utf8>>, Rest}} -> - {{Name, Value}, Rest}; - - {ok, {<<"\t"/utf8>>, Rest}} -> - {{Name, Value}, Rest}; - - {ok, {Grapheme, Rest@1}} -> - parse_rfc_2045_parameter_unquoted_value( - Rest@1, - Name, - <<Value/binary, Grapheme/binary>> - ) - end. - --spec parse_rfc_2045_parameter_value(binary(), binary()) -> {ok, - {{binary(), binary()}, binary()}} | - {error, nil}. -parse_rfc_2045_parameter_value(Header, Name) -> - case gleam@string:pop_grapheme(Header) of - {error, nil} -> - {error, nil}; - - {ok, {<<"\""/utf8>>, Rest}} -> - parse_rfc_2045_parameter_quoted_value(Rest, Name, <<""/utf8>>); - - {ok, {Grapheme, Rest@1}} -> - {ok, - parse_rfc_2045_parameter_unquoted_value(Rest@1, Name, Grapheme)} - end. - --spec parse_rfc_2045_parameter(binary(), binary()) -> {ok, - {{binary(), binary()}, binary()}} | - {error, nil}. -parse_rfc_2045_parameter(Header, Name) -> - gleam@result:'try'( - gleam@string:pop_grapheme(Header), - fun(_use0) -> - {Grapheme, Rest} = _use0, - case Grapheme of - <<"="/utf8>> -> - parse_rfc_2045_parameter_value(Rest, Name); - - _ -> - parse_rfc_2045_parameter( - Rest, - <<Name/binary, - (gleam@string:lowercase(Grapheme))/binary>> - ) - end - end - ). - --spec parse_rfc_2045_parameters(binary(), list({binary(), binary()})) -> {ok, - list({binary(), binary()})} | - {error, nil}. -parse_rfc_2045_parameters(Header, Parameters) -> - case gleam@string:pop_grapheme(Header) of - {error, nil} -> - {ok, gleam@list:reverse(Parameters)}; - - {ok, {<<";"/utf8>>, Rest}} -> - parse_rfc_2045_parameters(Rest, Parameters); - - {ok, {<<" "/utf8>>, Rest}} -> - parse_rfc_2045_parameters(Rest, Parameters); - - {ok, {<<"\t"/utf8>>, Rest}} -> - parse_rfc_2045_parameters(Rest, Parameters); - - {ok, {Grapheme, Rest@1}} -> - Acc = gleam@string:lowercase(Grapheme), - gleam@result:'try'( - parse_rfc_2045_parameter(Rest@1, Acc), - fun(_use0) -> - {Parameter, Rest@2} = _use0, - parse_rfc_2045_parameters(Rest@2, [Parameter | Parameters]) - end - ) - end. - --spec parse_content_disposition_type(binary(), binary()) -> {ok, - content_disposition()} | - {error, nil}. -parse_content_disposition_type(Header, Name) -> - case gleam@string:pop_grapheme(Header) of - {error, nil} -> - {ok, {content_disposition, Name, []}}; - - {ok, {<<" "/utf8>>, Rest}} -> - Result = parse_rfc_2045_parameters(Rest, []), - gleam@result:map( - Result, - fun(Parameters) -> {content_disposition, Name, Parameters} end - ); - - {ok, {<<"\t"/utf8>>, Rest}} -> - Result = parse_rfc_2045_parameters(Rest, []), - gleam@result:map( - Result, - fun(Parameters) -> {content_disposition, Name, Parameters} end - ); - - {ok, {<<";"/utf8>>, Rest}} -> - Result = parse_rfc_2045_parameters(Rest, []), - gleam@result:map( - Result, - fun(Parameters) -> {content_disposition, Name, Parameters} end - ); - - {ok, {Grapheme, Rest@1}} -> - parse_content_disposition_type( - Rest@1, - <<Name/binary, (gleam@string:lowercase(Grapheme))/binary>> - ) - end. - --spec parse_content_disposition(binary()) -> {ok, content_disposition()} | - {error, nil}. -parse_content_disposition(Header) -> - parse_content_disposition_type(Header, <<""/utf8>>). - --spec more_please_body( - fun((bitstring()) -> {ok, multipart_body()} | {error, nil}), - bitstring(), - bitstring() -) -> {ok, multipart_body()} | {error, nil}. -more_please_body(Continuation, Chunk, Existing) -> - _pipe = fun(More) -> - gleam@bool:guard( - More =:= <<>>, - {error, nil}, - fun() -> Continuation(<<Existing/bitstring, More/bitstring>>) end - ) - end, - _pipe@1 = {more_required_for_body, Chunk, _pipe}, - {ok, _pipe@1}. - --spec parse_body_loop(bitstring(), bitstring(), bitstring()) -> {ok, - multipart_body()} | - {error, nil}. -parse_body_loop(Data, Boundary, Body) -> - Dsize = erlang:byte_size(Data), - Bsize = erlang:byte_size(Boundary), - Required = 6 + Bsize, - case Data of - _ when Dsize < Required -> - more_please_body( - fun(_capture) -> parse_body_loop(_capture, Boundary, <<>>) end, - Body, - Data - ); - - <<13, 10, Data@1/binary>> -> - Desired = <<45, 45, Boundary/bitstring>>, - Size = erlang:byte_size(Desired), - Dsize@1 = erlang:byte_size(Data@1), - Prefix = gleam_stdlib:bit_array_slice(Data@1, 0, Size), - Rest = gleam_stdlib:bit_array_slice(Data@1, Size, Dsize@1 - Size), - case {Prefix =:= {ok, Desired}, Rest} of - {true, {ok, <<13, 10, _/binary>>}} -> - {ok, {multipart_body, Body, false, Data@1}}; - - {true, {ok, <<45, 45, Data@2/binary>>}} -> - {ok, {multipart_body, Body, true, Data@2}}; - - {false, _} -> - parse_body_loop( - Data@1, - Boundary, - <<Body/bitstring, 13, 10>> - ); - - {_, _} -> - {error, nil} - end; - - <<Char, Data@3/binary>> -> - parse_body_loop(Data@3, Boundary, <<Body/bitstring, Char>>) - end. - --spec parse_body_with_bit_array(bitstring(), bitstring()) -> {ok, - multipart_body()} | - {error, nil}. -parse_body_with_bit_array(Data, Boundary) -> - Bsize = erlang:byte_size(Boundary), - Prefix = gleam_stdlib:bit_array_slice(Data, 0, 2 + Bsize), - case Prefix =:= {ok, <<45, 45, Boundary/bitstring>>} of - true -> - {ok, {multipart_body, <<>>, false, Data}}; - - false -> - parse_body_loop(Data, Boundary, <<>>) - end. - --spec parse_multipart_body(bitstring(), binary()) -> {ok, multipart_body()} | - {error, nil}. -parse_multipart_body(Data, Boundary) -> - _pipe = Boundary, - _pipe@1 = gleam_stdlib:identity(_pipe), - parse_body_with_bit_array(Data, _pipe@1). - --spec method_from_dynamic(gleam@dynamic:dynamic_()) -> {ok, method()} | - {error, list(gleam@dynamic:decode_error())}. -method_from_dynamic(Value) -> - case gleam_http_native:decode_method(Value) of - {ok, Method} -> - {ok, Method}; - - {error, _} -> - {error, - [{decode_error, - <<"HTTP method"/utf8>>, - gleam@dynamic:classify(Value), - []}]} - end. - --spec parse_header_value( - bitstring(), - list({binary(), binary()}), - bitstring(), - bitstring() -) -> {ok, multipart_headers()} | {error, nil}. -parse_header_value(Data, Headers, Name, Value) -> - Size = erlang:byte_size(Data), - case Data of - _ when Size < 4 -> - _pipe@2 = fun(Data@1) -> _pipe = Data@1, - _pipe@1 = skip_whitespace(_pipe), - parse_header_value(_pipe@1, Headers, Name, Value) end, - more_please_headers(_pipe@2, Data); - - <<13, 10, 13, 10, Data@2/binary>> -> - gleam@result:'try'( - gleam@bit_array:to_string(Name), - fun(Name@1) -> - gleam@result:map( - gleam@bit_array:to_string(Value), - fun(Value@1) -> - Headers@1 = gleam@list:reverse( - [{gleam@string:lowercase(Name@1), Value@1} | - Headers] - ), - {multipart_headers, Headers@1, Data@2} - end - ) - end - ); - - <<13, 10, 32, Data@3/binary>> -> - parse_header_value(Data@3, Headers, Name, Value); - - <<13, 10, 9, Data@3/binary>> -> - parse_header_value(Data@3, Headers, Name, Value); - - <<13, 10, Data@4/binary>> -> - gleam@result:'try'( - gleam@bit_array:to_string(Name), - fun(Name@2) -> - gleam@result:'try'( - gleam@bit_array:to_string(Value), - fun(Value@2) -> - Headers@2 = [{gleam@string:lowercase(Name@2), - Value@2} | - Headers], - parse_header_name(Data@4, Headers@2, <<>>) - end - ) - end - ); - - <<Char, Rest/binary>> -> - Value@3 = <<Value/bitstring, Char>>, - parse_header_value(Rest, Headers, Name, Value@3); - - _ -> - {error, nil} - end. - --spec parse_header_name(bitstring(), list({binary(), binary()}), bitstring()) -> {ok, - multipart_headers()} | - {error, nil}. -parse_header_name(Data, Headers, Name) -> - case skip_whitespace(Data) of - <<58, Data@1/binary>> -> - _pipe = Data@1, - _pipe@1 = skip_whitespace(_pipe), - parse_header_value(_pipe@1, Headers, Name, <<>>); - - <<Char, Data@2/binary>> -> - parse_header_name(Data@2, Headers, <<Name/bitstring, Char>>); - - <<>> -> - more_please_headers( - fun(_capture) -> parse_header_name(_capture, Headers, Name) end, - Data - ) - end. - --spec do_parse_headers(bitstring()) -> {ok, multipart_headers()} | {error, nil}. -do_parse_headers(Data) -> - case Data of - <<13, 10, 13, 10, Data@1/binary>> -> - {ok, {multipart_headers, [], Data@1}}; - - <<13, 10, Data@2/binary>> -> - parse_header_name(Data@2, [], <<>>); - - <<13>> -> - more_please_headers(fun do_parse_headers/1, Data); - - <<>> -> - more_please_headers(fun do_parse_headers/1, Data); - - _ -> - {error, nil} - end. - --spec parse_headers_after_prelude(bitstring(), bitstring()) -> {ok, - multipart_headers()} | - {error, nil}. -parse_headers_after_prelude(Data, Boundary) -> - Dsize = erlang:byte_size(Data), - Bsize = erlang:byte_size(Boundary), - Required_size = Bsize + 4, - gleam@bool:guard( - Dsize < Required_size, - more_please_headers( - fun(_capture) -> parse_headers_after_prelude(_capture, Boundary) end, - Data - ), - fun() -> - gleam@result:'try'( - gleam_stdlib:bit_array_slice(Data, 0, Required_size - 2), - fun(Prefix) -> - gleam@result:'try'( - gleam_stdlib:bit_array_slice(Data, 2 + Bsize, 2), - fun(Second) -> - Desired = <<45, 45, Boundary/bitstring>>, - gleam@bool:guard( - Prefix /= Desired, - {error, nil}, - fun() -> case Second =:= <<45, 45>> of - true -> - Rest_size = Dsize - Required_size, - gleam@result:map( - gleam_stdlib:bit_array_slice( - Data, - Required_size, - Rest_size - ), - fun(Data@1) -> - {multipart_headers, - [], - Data@1} - end - ); - - false -> - Start = Required_size - 2, - Rest_size@1 = (Dsize - Required_size) - + 2, - gleam@result:'try'( - gleam_stdlib:bit_array_slice( - Data, - Start, - Rest_size@1 - ), - fun(Data@2) -> - do_parse_headers(Data@2) - end - ) - end end - ) - end - ) - end - ) - end - ). - --spec skip_preamble(bitstring(), bitstring()) -> {ok, multipart_headers()} | - {error, nil}. -skip_preamble(Data, Boundary) -> - Data_size = erlang:byte_size(Data), - Boundary_size = erlang:byte_size(Boundary), - Required = Boundary_size + 4, - case Data of - _ when Data_size < Required -> - more_please_headers( - fun(_capture) -> skip_preamble(_capture, Boundary) end, - Data - ); - - <<13, 10, 45, 45, Data@1/binary>> -> - case gleam_stdlib:bit_array_slice(Data@1, 0, Boundary_size) of - {ok, Prefix} when Prefix =:= Boundary -> - Start = Boundary_size, - Length = erlang:byte_size(Data@1) - Boundary_size, - gleam@result:'try'( - gleam_stdlib:bit_array_slice(Data@1, Start, Length), - fun(Rest) -> do_parse_headers(Rest) end - ); - - {ok, _} -> - skip_preamble(Data@1, Boundary); - - {error, _} -> - {error, nil} - end; - - <<_, Data@2/binary>> -> - skip_preamble(Data@2, Boundary) - end. - --spec parse_multipart_headers(bitstring(), binary()) -> {ok, - multipart_headers()} | - {error, nil}. -parse_multipart_headers(Data, Boundary) -> - Boundary@1 = gleam_stdlib:identity(Boundary), - Prefix = <<45, 45, Boundary@1/bitstring>>, - case gleam_stdlib:bit_array_slice(Data, 0, erlang:byte_size(Prefix)) =:= {ok, - Prefix} of - true -> - parse_headers_after_prelude(Data, Boundary@1); - - false -> - skip_preamble(Data, Boundary@1) - end. diff --git a/aoc2023/build/packages/gleam_http/src/gleam@http@cookie.erl b/aoc2023/build/packages/gleam_http/src/gleam@http@cookie.erl deleted file mode 100644 index 9d6d13e..0000000 --- a/aoc2023/build/packages/gleam_http/src/gleam@http@cookie.erl +++ /dev/null @@ -1,153 +0,0 @@ --module(gleam@http@cookie). --compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]). - --export([defaults/1, set_header/3, parse/1]). --export_type([same_site_policy/0, attributes/0]). - --type same_site_policy() :: lax | strict | none. - --type attributes() :: {attributes, - gleam@option:option(integer()), - gleam@option:option(binary()), - gleam@option:option(binary()), - boolean(), - boolean(), - gleam@option:option(same_site_policy())}. - --spec same_site_to_string(same_site_policy()) -> binary(). -same_site_to_string(Policy) -> - case Policy of - lax -> - <<"Lax"/utf8>>; - - strict -> - <<"Strict"/utf8>>; - - none -> - <<"None"/utf8>> - end. - --spec defaults(gleam@http:scheme()) -> attributes(). -defaults(Scheme) -> - {attributes, - none, - none, - {some, <<"/"/utf8>>}, - Scheme =:= https, - true, - {some, lax}}. - --spec cookie_attributes_to_list(attributes()) -> list(list(binary())). -cookie_attributes_to_list(Attributes) -> - {attributes, Max_age, Domain, Path, Secure, Http_only, Same_site} = Attributes, - _pipe = [case Max_age of - {some, 0} -> - {some, [<<"Expires=Thu, 01 Jan 1970 00:00:00 GMT"/utf8>>]}; - - _ -> - none - end, gleam@option:map( - Max_age, - fun(Max_age@1) -> - [<<"Max-Age="/utf8>>, gleam@int:to_string(Max_age@1)] - end - ), gleam@option:map( - Domain, - fun(Domain@1) -> [<<"Domain="/utf8>>, Domain@1] end - ), gleam@option:map(Path, fun(Path@1) -> [<<"Path="/utf8>>, Path@1] end), case Secure of - true -> - {some, [<<"Secure"/utf8>>]}; - - false -> - none - end, case Http_only of - true -> - {some, [<<"HttpOnly"/utf8>>]}; - - false -> - none - end, gleam@option:map( - Same_site, - fun(Same_site@1) -> - [<<"SameSite="/utf8>>, same_site_to_string(Same_site@1)] - end - )], - gleam@list:filter_map( - _pipe, - fun(_capture) -> gleam@option:to_result(_capture, nil) end - ). - --spec set_header(binary(), binary(), attributes()) -> binary(). -set_header(Name, Value, Attributes) -> - _pipe = [[Name, <<"="/utf8>>, Value] | - cookie_attributes_to_list(Attributes)], - _pipe@1 = gleam@list:map( - _pipe, - fun(_capture) -> gleam@string:join(_capture, <<""/utf8>>) end - ), - gleam@string:join(_pipe@1, <<"; "/utf8>>). - --spec check_token(binary()) -> {ok, nil} | {error, nil}. -check_token(Token) -> - case gleam@string:pop_grapheme(Token) of - {error, nil} -> - {ok, nil}; - - {ok, {<<" "/utf8>>, _}} -> - {error, nil}; - - {ok, {<<"\t"/utf8>>, _}} -> - {error, nil}; - - {ok, {<<"\r"/utf8>>, _}} -> - {error, nil}; - - {ok, {<<"\n"/utf8>>, _}} -> - {error, nil}; - - {ok, {<<"\f"/utf8>>, _}} -> - {error, nil}; - - {ok, {_, Rest}} -> - check_token(Rest) - end. - --spec parse(binary()) -> list({binary(), binary()}). -parse(Cookie_string) -> - _assert_subject = gleam@regex:from_string(<<"[,;]"/utf8>>), - {ok, Re} = case _assert_subject of - {ok, _} -> _assert_subject; - _assert_fail -> - erlang:error(#{gleam_error => let_assert, - message => <<"Assertion pattern match failed"/utf8>>, - value => _assert_fail, - module => <<"gleam/http/cookie"/utf8>>, - function => <<"parse"/utf8>>, - line => 101}) - end, - _pipe = gleam@regex:split(Re, Cookie_string), - gleam@list:filter_map( - _pipe, - fun(Pair) -> - case gleam@string:split_once(gleam@string:trim(Pair), <<"="/utf8>>) of - {ok, {<<""/utf8>>, _}} -> - {error, nil}; - - {ok, {Key, Value}} -> - Key@1 = gleam@string:trim(Key), - Value@1 = gleam@string:trim(Value), - gleam@result:then( - check_token(Key@1), - fun(_) -> - gleam@result:then( - check_token(Value@1), - fun(_) -> {ok, {Key@1, Value@1}} end - ) - end - ); - - {error, nil} -> - {error, nil} - end - end - ). diff --git a/aoc2023/build/packages/gleam_http/src/gleam@http@request.erl b/aoc2023/build/packages/gleam_http/src/gleam@http@request.erl deleted file mode 100644 index 630788d..0000000 --- a/aoc2023/build/packages/gleam_http/src/gleam@http@request.erl +++ /dev/null @@ -1,202 +0,0 @@ --module(gleam@http@request). --compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]). - --export([to_uri/1, from_uri/1, get_header/2, set_header/3, prepend_header/3, set_body/2, map/2, path_segments/1, get_query/1, set_query/2, set_method/2, new/0, to/1, set_scheme/2, set_host/2, set_port/2, set_path/2, set_cookie/3, get_cookies/1]). --export_type([request/1]). - --type request(FYJ) :: {request, - gleam@http:method(), - list({binary(), binary()}), - FYJ, - gleam@http:scheme(), - binary(), - gleam@option:option(integer()), - binary(), - gleam@option:option(binary())}. - --spec to_uri(request(any())) -> gleam@uri:uri(). -to_uri(Request) -> - {uri, - {some, gleam@http:scheme_to_string(erlang:element(5, Request))}, - none, - {some, erlang:element(6, Request)}, - erlang:element(7, Request), - erlang:element(8, Request), - erlang:element(9, Request), - none}. - --spec from_uri(gleam@uri:uri()) -> {ok, request(binary())} | {error, nil}. -from_uri(Uri) -> - gleam@result:then( - begin - _pipe = erlang:element(2, Uri), - _pipe@1 = gleam@option:unwrap(_pipe, <<""/utf8>>), - gleam@http:scheme_from_string(_pipe@1) - end, - fun(Scheme) -> - gleam@result:then( - begin - _pipe@2 = erlang:element(4, Uri), - gleam@option:to_result(_pipe@2, nil) - end, - fun(Host) -> - Req = {request, - get, - [], - <<""/utf8>>, - Scheme, - Host, - erlang:element(5, Uri), - erlang:element(6, Uri), - erlang:element(7, Uri)}, - {ok, Req} - end - ) - end - ). - --spec get_header(request(any()), binary()) -> {ok, binary()} | {error, nil}. -get_header(Request, Key) -> - gleam@list:key_find(erlang:element(3, Request), gleam@string:lowercase(Key)). - --spec set_header(request(FYT), binary(), binary()) -> request(FYT). -set_header(Request, Key, Value) -> - Headers = gleam@list:key_set( - erlang:element(3, Request), - gleam@string:lowercase(Key), - Value - ), - erlang:setelement(3, Request, Headers). - --spec prepend_header(request(FYW), binary(), binary()) -> request(FYW). -prepend_header(Request, Key, Value) -> - Headers = [{gleam@string:lowercase(Key), Value} | - erlang:element(3, Request)], - erlang:setelement(3, Request, Headers). - --spec set_body(request(any()), FZB) -> request(FZB). -set_body(Req, Body) -> - {request, Method, Headers, _, Scheme, Host, Port, Path, Query} = Req, - {request, Method, Headers, Body, Scheme, Host, Port, Path, Query}. - --spec map(request(FZD), fun((FZD) -> FZF)) -> request(FZF). -map(Request, Transform) -> - _pipe = erlang:element(4, Request), - _pipe@1 = Transform(_pipe), - set_body(Request, _pipe@1). - --spec path_segments(request(any())) -> list(binary()). -path_segments(Request) -> - _pipe = erlang:element(8, Request), - gleam@uri:path_segments(_pipe). - --spec get_query(request(any())) -> {ok, list({binary(), binary()})} | - {error, nil}. -get_query(Request) -> - case erlang:element(9, Request) of - {some, Query_string} -> - gleam@uri:parse_query(Query_string); - - none -> - {ok, []} - end. - --spec set_query(request(FZP), list({binary(), binary()})) -> request(FZP). -set_query(Req, Query) -> - Pair = fun(T) -> - gleam@string_builder:from_strings( - [erlang:element(1, T), <<"="/utf8>>, erlang:element(2, T)] - ) - end, - Query@1 = begin - _pipe = Query, - _pipe@1 = gleam@list:map(_pipe, Pair), - _pipe@2 = gleam@list:intersperse( - _pipe@1, - gleam@string_builder:from_string(<<"&"/utf8>>) - ), - _pipe@3 = gleam@string_builder:concat(_pipe@2), - _pipe@4 = gleam@string_builder:to_string(_pipe@3), - {some, _pipe@4} - end, - erlang:setelement(9, Req, Query@1). - --spec set_method(request(FZT), gleam@http:method()) -> request(FZT). -set_method(Req, Method) -> - erlang:setelement(2, Req, Method). - --spec new() -> request(binary()). -new() -> - {request, - get, - [], - <<""/utf8>>, - https, - <<"localhost"/utf8>>, - none, - <<""/utf8>>, - none}. - --spec to(binary()) -> {ok, request(binary())} | {error, nil}. -to(Url) -> - _pipe = Url, - _pipe@1 = gleam@uri:parse(_pipe), - gleam@result:then(_pipe@1, fun from_uri/1). - --spec set_scheme(request(GAA), gleam@http:scheme()) -> request(GAA). -set_scheme(Req, Scheme) -> - erlang:setelement(5, Req, Scheme). - --spec set_host(request(GAD), binary()) -> request(GAD). -set_host(Req, Host) -> - erlang:setelement(6, Req, Host). - --spec set_port(request(GAG), integer()) -> request(GAG). -set_port(Req, Port) -> - erlang:setelement(7, Req, {some, Port}). - --spec set_path(request(GAJ), binary()) -> request(GAJ). -set_path(Req, Path) -> - erlang:setelement(8, Req, Path). - --spec set_cookie(request(GAM), binary(), binary()) -> request(GAM). -set_cookie(Req, Name, Value) -> - New_cookie_string = gleam@string:join([Name, Value], <<"="/utf8>>), - {Cookies_string@2, Headers@1} = case gleam@list:key_pop( - erlang:element(3, Req), - <<"cookie"/utf8>> - ) of - {ok, {Cookies_string, Headers}} -> - Cookies_string@1 = gleam@string:join( - [Cookies_string, New_cookie_string], - <<"; "/utf8>> - ), - {Cookies_string@1, Headers}; - - {error, nil} -> - {New_cookie_string, erlang:element(3, Req)} - end, - erlang:setelement( - 3, - Req, - [{<<"cookie"/utf8>>, Cookies_string@2} | Headers@1] - ). - --spec get_cookies(request(any())) -> list({binary(), binary()}). -get_cookies(Req) -> - {request, _, Headers, _, _, _, _, _, _} = Req, - _pipe = Headers, - _pipe@1 = gleam@list:filter_map( - _pipe, - fun(Header) -> - {Name, Value} = Header, - case Name of - <<"cookie"/utf8>> -> - {ok, gleam@http@cookie:parse(Value)}; - - _ -> - {error, nil} - end - end - ), - gleam@list:flatten(_pipe@1). diff --git a/aoc2023/build/packages/gleam_http/src/gleam@http@response.erl b/aoc2023/build/packages/gleam_http/src/gleam@http@response.erl deleted file mode 100644 index b073c1d..0000000 --- a/aoc2023/build/packages/gleam_http/src/gleam@http@response.erl +++ /dev/null @@ -1,97 +0,0 @@ --module(gleam@http@response). --compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]). - --export([new/1, get_header/2, set_header/3, prepend_header/3, set_body/2, try_map/2, map/2, redirect/1, get_cookies/1, set_cookie/4, expire_cookie/3]). --export_type([response/1]). - --type response(GFN) :: {response, integer(), list({binary(), binary()}), GFN}. - --spec new(integer()) -> response(binary()). -new(Status) -> - {response, Status, [], <<""/utf8>>}. - --spec get_header(response(any()), binary()) -> {ok, binary()} | {error, nil}. -get_header(Response, Key) -> - gleam@list:key_find( - erlang:element(3, Response), - gleam@string:lowercase(Key) - ). - --spec set_header(response(GGC), binary(), binary()) -> response(GGC). -set_header(Response, Key, Value) -> - Headers = gleam@list:key_set( - erlang:element(3, Response), - gleam@string:lowercase(Key), - Value - ), - erlang:setelement(3, Response, Headers). - --spec prepend_header(response(GGF), binary(), binary()) -> response(GGF). -prepend_header(Response, Key, Value) -> - Headers = [{gleam@string:lowercase(Key), Value} | - erlang:element(3, Response)], - erlang:setelement(3, Response, Headers). - --spec set_body(response(any()), GGK) -> response(GGK). -set_body(Response, Body) -> - {response, Status, Headers, _} = Response, - {response, Status, Headers, Body}. - --spec try_map(response(GFO), fun((GFO) -> {ok, GFQ} | {error, GFR})) -> {ok, - response(GFQ)} | - {error, GFR}. -try_map(Response, Transform) -> - gleam@result:then( - Transform(erlang:element(4, Response)), - fun(Body) -> {ok, set_body(Response, Body)} end - ). - --spec map(response(GGM), fun((GGM) -> GGO)) -> response(GGO). -map(Response, Transform) -> - _pipe = erlang:element(4, Response), - _pipe@1 = Transform(_pipe), - set_body(Response, _pipe@1). - --spec redirect(binary()) -> response(binary()). -redirect(Uri) -> - {response, - 303, - [{<<"location"/utf8>>, Uri}], - gleam@string:append(<<"You are being redirected to "/utf8>>, Uri)}. - --spec get_cookies(response(any())) -> list({binary(), binary()}). -get_cookies(Resp) -> - {response, _, Headers, _} = Resp, - _pipe = Headers, - _pipe@1 = gleam@list:filter_map( - _pipe, - fun(Header) -> - {Name, Value} = Header, - case Name of - <<"set-cookie"/utf8>> -> - {ok, gleam@http@cookie:parse(Value)}; - - _ -> - {error, nil} - end - end - ), - gleam@list:flatten(_pipe@1). - --spec set_cookie( - response(GGT), - binary(), - binary(), - gleam@http@cookie:attributes() -) -> response(GGT). -set_cookie(Response, Name, Value, Attributes) -> - prepend_header( - Response, - <<"set-cookie"/utf8>>, - gleam@http@cookie:set_header(Name, Value, Attributes) - ). - --spec expire_cookie(response(GGW), binary(), gleam@http@cookie:attributes()) -> response(GGW). -expire_cookie(Response, Name, Attributes) -> - Attrs = erlang:setelement(2, Attributes, {some, 0}), - set_cookie(Response, Name, <<""/utf8>>, Attrs). diff --git a/aoc2023/build/packages/gleam_http/src/gleam@http@service.erl b/aoc2023/build/packages/gleam_http/src/gleam@http@service.erl deleted file mode 100644 index d07b31f..0000000 --- a/aoc2023/build/packages/gleam_http/src/gleam@http@service.erl +++ /dev/null @@ -1,82 +0,0 @@ --module(gleam@http@service). --compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]). - --export([map_response_body/2, prepend_response_header/3, method_override/1]). - --spec map_response_body( - fun((gleam@http@request:request(GJL)) -> gleam@http@response:response(GJM)), - fun((GJM) -> GJP) -) -> fun((gleam@http@request:request(GJL)) -> gleam@http@response:response(GJP)). -map_response_body(Service, Mapper) -> - fun(Req) -> _pipe = Req, - _pipe@1 = Service(_pipe), - gleam@http@response:map(_pipe@1, Mapper) end. - --spec prepend_response_header( - fun((gleam@http@request:request(GJS)) -> gleam@http@response:response(GJT)), - binary(), - binary() -) -> fun((gleam@http@request:request(GJS)) -> gleam@http@response:response(GJT)). -prepend_response_header(Service, Key, Value) -> - fun(Req) -> _pipe = Req, - _pipe@1 = Service(_pipe), - gleam@http@response:prepend_header(_pipe@1, Key, Value) end. - --spec ensure_post(gleam@http@request:request(GJY)) -> {ok, - gleam@http@request:request(GJY)} | - {error, nil}. -ensure_post(Req) -> - case erlang:element(2, Req) of - post -> - {ok, Req}; - - _ -> - {error, nil} - end. - --spec get_override_method(gleam@http@request:request(any())) -> {ok, - gleam@http:method()} | - {error, nil}. -get_override_method(Request) -> - gleam@result:then( - gleam@http@request:get_query(Request), - fun(Query_params) -> - gleam@result:then( - gleam@list:key_find(Query_params, <<"_method"/utf8>>), - fun(Method) -> - gleam@result:then( - gleam@http:parse_method(Method), - fun(Method@1) -> case Method@1 of - put -> - {ok, Method@1}; - - patch -> - {ok, Method@1}; - - delete -> - {ok, Method@1}; - - _ -> - {error, nil} - end end - ) - end - ) - end - ). - --spec method_override( - fun((gleam@http@request:request(GKF)) -> gleam@http@response:response(GKG)) -) -> fun((gleam@http@request:request(GKF)) -> gleam@http@response:response(GKG)). -method_override(Service) -> - fun(Request) -> _pipe = Request, - _pipe@1 = ensure_post(_pipe), - _pipe@2 = gleam@result:then(_pipe@1, fun get_override_method/1), - _pipe@3 = gleam@result:map( - _pipe@2, - fun(_capture) -> - gleam@http@request:set_method(Request, _capture) - end - ), - _pipe@4 = gleam@result:unwrap(_pipe@3, Request), - Service(_pipe@4) end. diff --git a/aoc2023/build/packages/gleam_http/src/gleam_http.app.src b/aoc2023/build/packages/gleam_http/src/gleam_http.app.src deleted file mode 100644 index c37ad54..0000000 --- a/aoc2023/build/packages/gleam_http/src/gleam_http.app.src +++ /dev/null @@ -1,12 +0,0 @@ -{application, gleam_http, [ - {vsn, "3.5.2"}, - {applications, [gleam_stdlib, - gleeunit]}, - {description, "Types and functions for Gleam HTTP clients and servers"}, - {modules, [gleam@http, - gleam@http@cookie, - gleam@http@request, - gleam@http@response, - gleam@http@service]}, - {registered, []} -]}. diff --git a/aoc2023/build/packages/gleam_http/src/gleam_http_native.erl b/aoc2023/build/packages/gleam_http/src/gleam_http_native.erl deleted file mode 100644 index bb499bb..0000000 --- a/aoc2023/build/packages/gleam_http/src/gleam_http_native.erl +++ /dev/null @@ -1,88 +0,0 @@ --module(gleam_http_native). --export([decode_method/1]). - -decode_method(Term) -> - case Term of - "connect" -> {ok, connect}; - "delete" -> {ok, delete}; - "get" -> {ok, get}; - "head" -> {ok, head}; - "options" -> {ok, options}; - "patch" -> {ok, patch}; - "post" -> {ok, post}; - "put" -> {ok, put}; - "trace" -> {ok, trace}; - "CONNECT" -> {ok, connect}; - "DELETE" -> {ok, delete}; - "GET" -> {ok, get}; - "HEAD" -> {ok, head}; - "OPTIONS" -> {ok, options}; - "PATCH" -> {ok, patch}; - "POST" -> {ok, post}; - "PUT" -> {ok, put}; - "TRACE" -> {ok, trace}; - "Connect" -> {ok, connect}; - "Delete" -> {ok, delete}; - "Get" -> {ok, get}; - "Head" -> {ok, head}; - "Options" -> {ok, options}; - "Patch" -> {ok, patch}; - "Post" -> {ok, post}; - "Put" -> {ok, put}; - "Trace" -> {ok, trace}; - 'connect' -> {ok, connect}; - 'delete' -> {ok, delete}; - 'get' -> {ok, get}; - 'head' -> {ok, head}; - 'options' -> {ok, options}; - 'patch' -> {ok, patch}; - 'post' -> {ok, post}; - 'put' -> {ok, put}; - 'trace' -> {ok, trace}; - 'CONNECT' -> {ok, connect}; - 'DELETE' -> {ok, delete}; - 'GET' -> {ok, get}; - 'HEAD' -> {ok, head}; - 'OPTIONS' -> {ok, options}; - 'PATCH' -> {ok, patch}; - 'POST' -> {ok, post}; - 'PUT' -> {ok, put}; - 'TRACE' -> {ok, trace}; - 'Connect' -> {ok, connect}; - 'Delete' -> {ok, delete}; - 'Get' -> {ok, get}; - 'Head' -> {ok, head}; - 'Options' -> {ok, options}; - 'Patch' -> {ok, patch}; - 'Post' -> {ok, post}; - 'Put' -> {ok, put}; - 'Trace' -> {ok, trace}; - <<"connect">> -> {ok, connect}; - <<"delete">> -> {ok, delete}; - <<"get">> -> {ok, get}; - <<"head">> -> {ok, head}; - <<"options">> -> {ok, options}; - <<"patch">> -> {ok, patch}; - <<"post">> -> {ok, post}; - <<"put">> -> {ok, put}; - <<"trace">> -> {ok, trace}; - <<"CONNECT">> -> {ok, connect}; - <<"DELETE">> -> {ok, delete}; - <<"GET">> -> {ok, get}; - <<"HEAD">> -> {ok, head}; - <<"OPTIONS">> -> {ok, options}; - <<"PATCH">> -> {ok, patch}; - <<"POST">> -> {ok, post}; - <<"PUT">> -> {ok, put}; - <<"TRACE">> -> {ok, trace}; - <<"Connect">> -> {ok, connect}; - <<"Delete">> -> {ok, delete}; - <<"Get">> -> {ok, get}; - <<"Head">> -> {ok, head}; - <<"Options">> -> {ok, options}; - <<"Patch">> -> {ok, patch}; - <<"Post">> -> {ok, post}; - <<"Put">> -> {ok, put}; - <<"Trace">> -> {ok, trace}; - _ -> {error, nil} - end. diff --git a/aoc2023/build/packages/gleam_http/src/gleam_http_native.mjs b/aoc2023/build/packages/gleam_http/src/gleam_http_native.mjs deleted file mode 100644 index c871a8b..0000000 --- a/aoc2023/build/packages/gleam_http/src/gleam_http_native.mjs +++ /dev/null @@ -1,38 +0,0 @@ -import { Ok, Error } from "./gleam.mjs"; -import { - Get, - Post, - Head, - Put, - Delete, - Trace, - Connect, - Options, - Patch, -} from "./gleam/http.mjs"; - -export function decode_method(value) { - try { - switch (value.toLowerCase()) { - case "get": - return new Ok(new Get()); - case "post": - return new Ok(new Post()); - case "head": - return new Ok(new Head()); - case "put": - return new Ok(new Put()); - case "delete": - return new Ok(new Delete()); - case "trace": - return new Ok(new Trace()); - case "connect": - return new Ok(new Connect()); - case "options": - return new Ok(new Options()); - case "patch": - return new Ok(new Patch()); - } - } catch {} - return new Error(undefined); -} |