diff options
author | HJ <thechairman@thechairman.info> | 2024-02-03 15:09:54 -0500 |
---|---|---|
committer | HJ <thechairman@thechairman.info> | 2024-02-03 15:09:54 -0500 |
commit | 96a3c5c179d8d3fff24eb2953e45f8dd15e2714c (patch) | |
tree | 0a67bc0cfeabe51740bb049c61f16f1ac3bdd4ff /aoc2023/build/packages/gleam_httpc/src | |
parent | 547fe03cf43105f46160e2dd9afff21637eaaf47 (diff) | |
download | gleam_aoc-96a3c5c179d8d3fff24eb2953e45f8dd15e2714c.tar.gz gleam_aoc-96a3c5c179d8d3fff24eb2953e45f8dd15e2714c.zip |
cleanup
Diffstat (limited to 'aoc2023/build/packages/gleam_httpc/src')
3 files changed, 235 insertions, 0 deletions
diff --git a/aoc2023/build/packages/gleam_httpc/src/gleam/httpc.gleam b/aoc2023/build/packages/gleam_httpc/src/gleam/httpc.gleam new file mode 100644 index 0000000..cf166c3 --- /dev/null +++ b/aoc2023/build/packages/gleam_httpc/src/gleam/httpc.gleam @@ -0,0 +1,105 @@ +import gleam/dynamic.{type Dynamic} +import gleam/http.{type Method} +import gleam/http/response.{type Response, Response} +import gleam/http/request.{type Request} +import gleam/bit_array +import gleam/result +import gleam/list +import gleam/uri + +type Charlist + +@external(erlang, "erlang", "binary_to_list") +fn binary_to_list(a: String) -> Charlist + +@external(erlang, "erlang", "list_to_binary") +fn list_to_binary(a: Charlist) -> String + +type ErlHttpOption + +type BodyFormat { + Binary +} + +type ErlOption { + BodyFormat(BodyFormat) +} + +@external(erlang, "httpc", "request") +fn erl_request( + a: Method, + b: #(Charlist, List(#(Charlist, Charlist)), Charlist, BitArray), + c: List(ErlHttpOption), + d: List(ErlOption), +) -> Result( + #(#(Charlist, Int, Charlist), List(#(Charlist, Charlist)), BitArray), + Dynamic, +) + +@external(erlang, "httpc", "request") +fn erl_request_no_body( + a: Method, + b: #(Charlist, List(#(Charlist, Charlist))), + c: List(ErlHttpOption), + d: List(ErlOption), +) -> Result( + #(#(Charlist, Int, Charlist), List(#(Charlist, Charlist)), BitArray), + Dynamic, +) + +fn charlist_header(header: #(String, String)) -> #(Charlist, Charlist) { + let #(k, v) = header + #(binary_to_list(k), binary_to_list(v)) +} + +fn string_header(header: #(Charlist, Charlist)) -> #(String, String) { + let #(k, v) = header + #(list_to_binary(k), list_to_binary(v)) +} + +// TODO: test +// TODO: refine error type +pub fn send_bits(req: Request(BitArray)) -> Result(Response(BitArray), Dynamic) { + let erl_url = + req + |> request.to_uri + |> uri.to_string + |> binary_to_list + let erl_headers = list.map(req.headers, charlist_header) + let erl_http_options = [] + let erl_options = [BodyFormat(Binary)] + + use response <- result.then(case req.method { + http.Options | http.Head | http.Get -> { + let erl_req = #(erl_url, erl_headers) + erl_request_no_body(req.method, erl_req, erl_http_options, erl_options) + } + _ -> { + let erl_content_type = + req + |> request.get_header("content-type") + |> result.unwrap("application/octet-stream") + |> binary_to_list + let erl_req = #(erl_url, erl_headers, erl_content_type, req.body) + erl_request(req.method, erl_req, erl_http_options, erl_options) + } + }) + + let #(#(_version, status, _status), headers, resp_body) = response + Ok(Response(status, list.map(headers, string_header), resp_body)) +} + +// TODO: test +// TODO: refine error type +pub fn send(req: Request(String)) -> Result(Response(String), Dynamic) { + use resp <- result.then( + req + |> request.map(bit_array.from_string) + |> send_bits, + ) + + case bit_array.to_string(resp.body) { + Ok(body) -> Ok(response.set_body(resp, body)) + Error(_) -> Error(dynamic.from("Response body was not valid UTF-8")) + } +} diff --git a/aoc2023/build/packages/gleam_httpc/src/gleam@httpc.erl b/aoc2023/build/packages/gleam_httpc/src/gleam@httpc.erl new file mode 100644 index 0000000..1d634df --- /dev/null +++ b/aoc2023/build/packages/gleam_httpc/src/gleam@httpc.erl @@ -0,0 +1,118 @@ +-module(gleam@httpc). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function]). + +-export([send_bits/1, send/1]). +-export_type([charlist/0, erl_http_option/0, body_format/0, erl_option/0]). + +-type charlist() :: any(). + +-type erl_http_option() :: any(). + +-type body_format() :: binary. + +-type erl_option() :: {body_format, body_format()}. + +-spec charlist_header({binary(), binary()}) -> {charlist(), charlist()}. +charlist_header(Header) -> + {K, V} = Header, + {erlang:binary_to_list(K), erlang:binary_to_list(V)}. + +-spec string_header({charlist(), charlist()}) -> {binary(), binary()}. +string_header(Header) -> + {K, V} = Header, + {erlang:list_to_binary(K), erlang:list_to_binary(V)}. + +-spec send_bits(gleam@http@request:request(bitstring())) -> {ok, + gleam@http@response:response(bitstring())} | + {error, gleam@dynamic:dynamic_()}. +send_bits(Req) -> + Erl_url = begin + _pipe = Req, + _pipe@1 = gleam@http@request:to_uri(_pipe), + _pipe@2 = gleam@uri:to_string(_pipe@1), + erlang:binary_to_list(_pipe@2) + end, + Erl_headers = gleam@list:map(erlang:element(3, Req), fun charlist_header/1), + Erl_http_options = [], + Erl_options = [{body_format, binary}], + gleam@result:then(case erlang:element(2, Req) of + options -> + Erl_req = {Erl_url, Erl_headers}, + httpc:request( + erlang:element(2, Req), + Erl_req, + Erl_http_options, + Erl_options + ); + + head -> + Erl_req = {Erl_url, Erl_headers}, + httpc:request( + erlang:element(2, Req), + Erl_req, + Erl_http_options, + Erl_options + ); + + get -> + Erl_req = {Erl_url, Erl_headers}, + httpc:request( + erlang:element(2, Req), + Erl_req, + Erl_http_options, + Erl_options + ); + + _ -> + Erl_content_type = begin + _pipe@3 = Req, + _pipe@4 = gleam@http@request:get_header( + _pipe@3, + <<"content-type"/utf8>> + ), + _pipe@5 = gleam@result:unwrap( + _pipe@4, + <<"application/octet-stream"/utf8>> + ), + erlang:binary_to_list(_pipe@5) + end, + Erl_req@1 = {Erl_url, + Erl_headers, + Erl_content_type, + erlang:element(4, Req)}, + httpc:request( + erlang:element(2, Req), + Erl_req@1, + Erl_http_options, + Erl_options + ) + end, fun(Response) -> + {{_, Status, _}, Headers, Resp_body} = Response, + {ok, + {response, + Status, + gleam@list:map(Headers, fun string_header/1), + Resp_body}} + end). + +-spec send(gleam@http@request:request(binary())) -> {ok, + gleam@http@response:response(binary())} | + {error, gleam@dynamic:dynamic_()}. +send(Req) -> + gleam@result:then( + begin + _pipe = Req, + _pipe@1 = gleam@http@request:map(_pipe, fun gleam_stdlib:identity/1), + send_bits(_pipe@1) + end, + fun(Resp) -> case gleam@bit_array:to_string(erlang:element(4, Resp)) of + {ok, Body} -> + {ok, gleam@http@response:set_body(Resp, Body)}; + + {error, _} -> + {error, + gleam@dynamic:from( + <<"Response body was not valid UTF-8"/utf8>> + )} + end end + ). diff --git a/aoc2023/build/packages/gleam_httpc/src/gleam_httpc.app.src b/aoc2023/build/packages/gleam_httpc/src/gleam_httpc.app.src new file mode 100644 index 0000000..c0d2b20 --- /dev/null +++ b/aoc2023/build/packages/gleam_httpc/src/gleam_httpc.app.src @@ -0,0 +1,12 @@ +{application, gleam_httpc, [ + {vsn, "2.1.1"}, + {applications, [gleam_erlang, + gleam_http, + gleam_stdlib, + gleeunit, + inets, + ssl]}, + {description, "Gleam bindings to Erlang's built in HTTP client, httpc"}, + {modules, [gleam@httpc]}, + {registered, []} +]}. |