From 3175040e3abc13c7711875ffb8f948d2b2239d78 Mon Sep 17 00:00:00 2001 From: lewdum Date: Tue, 22 Oct 2024 18:33:44 -0300 Subject: [PATCH] prototype --- .gitignore | 1 + .vscode/settings.json | 3 +++ examples/info/README.md | 24 ++++++++++++++++++ examples/info/gleam.toml | 8 ++++++ examples/info/manifest.toml | 15 +++++++++++ examples/info/src/info.gleam | 13 ++++++++++ gleam.toml | 5 +++- manifest.toml | 18 +++++++++++++ src/apkallone.gleam | 15 ++++++++--- src/apkallone/internal.gleam | 36 ++++++++++++++++++++++++++ src/mastodon/instance.gleam | 13 ++++++++++ src/mastodon/model/instance.gleam | 42 +++++++++++++++++++++++++++++++ 12 files changed, 189 insertions(+), 4 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 examples/info/README.md create mode 100644 examples/info/gleam.toml create mode 100644 examples/info/manifest.toml create mode 100644 examples/info/src/info.gleam create mode 100644 manifest.toml create mode 100644 src/apkallone/internal.gleam create mode 100644 src/mastodon/instance.gleam create mode 100644 src/mastodon/model/instance.gleam diff --git a/.gitignore b/.gitignore index 599be4e..ace4004 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.beam *.ez /build +/examples/*/build erl_crash.dump diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..3eeae61 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "cSpell.words": ["apkallone", "httpc"] +} diff --git a/examples/info/README.md b/examples/info/README.md new file mode 100644 index 0000000..11ee796 --- /dev/null +++ b/examples/info/README.md @@ -0,0 +1,24 @@ +# info + +[![Package Version](https://img.shields.io/hexpm/v/info)](https://hex.pm/packages/info) +[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/info/) + +```sh +gleam add info@1 +``` +```gleam +import info + +pub fn main() { + // TODO: An example of the project in use +} +``` + +Further documentation can be found at . + +## Development + +```sh +gleam run # Run the project +gleam test # Run the tests +``` diff --git a/examples/info/gleam.toml b/examples/info/gleam.toml new file mode 100644 index 0000000..1c1d43f --- /dev/null +++ b/examples/info/gleam.toml @@ -0,0 +1,8 @@ +name = "info" +version = "1.0.0" + +[dependencies] +gleam_stdlib = ">= 0.34.0 and < 2.0.0" +apkallone = { path = "../.." } + +[dev-dependencies] diff --git a/examples/info/manifest.toml b/examples/info/manifest.toml new file mode 100644 index 0000000..afd6b59 --- /dev/null +++ b/examples/info/manifest.toml @@ -0,0 +1,15 @@ +# This file was generated by Gleam +# You typically do not need to edit this file + +packages = [ + { name = "apkallone", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_http", "gleam_httpc", "gleam_json", "gleam_stdlib"], source = "local", path = "../.." }, + { name = "gleam_erlang", version = "0.27.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "DE468F676D71B313C6C8C5334425CFCF827837333F8AB47B64D8A6D7AA40185D" }, + { name = "gleam_http", version = "3.7.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "EA66440C2269F7CED0F6845E5BD0DB68095775D627FA709A841CA78A398D6D56" }, + { name = "gleam_httpc", version = "3.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_http", "gleam_stdlib"], otp_app = "gleam_httpc", source = "hex", outer_checksum = "091CDD2BEC8092E82707BEA03FB5205A2BBBDE4A2F551E3C069E13B8BC0C428E" }, + { name = "gleam_json", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "CB10B0E7BF44282FB25162F1A24C1A025F6B93E777CCF238C4017E4EEF2CDE97" }, + { name = "gleam_stdlib", version = "0.40.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "86606B75A600BBD05E539EB59FABC6E307EEEA7B1E5865AFB6D980A93BCB2181" }, +] + +[requirements] +apkallone = { path = "../.." } +gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" } diff --git a/examples/info/src/info.gleam b/examples/info/src/info.gleam new file mode 100644 index 0000000..4ef87fe --- /dev/null +++ b/examples/info/src/info.gleam @@ -0,0 +1,13 @@ +import apkallone +import gleam/io +import mastodon/instance + +pub fn main() { + io.println_error("Fetching...") + + let assert Ok(instance) = + apkallone.Client("https://tech.lgbt") + |> instance.get_instance + + io.debug(instance) +} diff --git a/gleam.toml b/gleam.toml index a524cc3..4d877dd 100644 --- a/gleam.toml +++ b/gleam.toml @@ -1,5 +1,5 @@ name = "apkallone" -version = "1.0.0" +version = "0.1.0" # Fill out these fields if you intend to generate HTML documentation or publish # your project to the Hex package manager. @@ -14,6 +14,9 @@ version = "1.0.0" [dependencies] gleam_stdlib = ">= 0.34.0 and < 2.0.0" +gleam_httpc = ">= 3.0.0 and < 4.0.0" +gleam_json = ">= 2.0.0 and < 3.0.0" +gleam_http = ">= 3.7.0 and < 4.0.0" [dev-dependencies] gleeunit = ">= 1.0.0 and < 2.0.0" diff --git a/manifest.toml b/manifest.toml new file mode 100644 index 0000000..8905519 --- /dev/null +++ b/manifest.toml @@ -0,0 +1,18 @@ +# This file was generated by Gleam +# You typically do not need to edit this file + +packages = [ + { name = "gleam_erlang", version = "0.27.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "DE468F676D71B313C6C8C5334425CFCF827837333F8AB47B64D8A6D7AA40185D" }, + { name = "gleam_http", version = "3.7.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "EA66440C2269F7CED0F6845E5BD0DB68095775D627FA709A841CA78A398D6D56" }, + { name = "gleam_httpc", version = "3.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_http", "gleam_stdlib"], otp_app = "gleam_httpc", source = "hex", outer_checksum = "091CDD2BEC8092E82707BEA03FB5205A2BBBDE4A2F551E3C069E13B8BC0C428E" }, + { name = "gleam_json", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "CB10B0E7BF44282FB25162F1A24C1A025F6B93E777CCF238C4017E4EEF2CDE97" }, + { name = "gleam_stdlib", version = "0.40.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "86606B75A600BBD05E539EB59FABC6E307EEEA7B1E5865AFB6D980A93BCB2181" }, + { name = "gleeunit", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "F7A7228925D3EE7D0813C922E062BFD6D7E9310F0BEE585D3A42F3307E3CFD13" }, +] + +[requirements] +gleam_http = { version = ">= 3.7.0 and < 4.0.0" } +gleam_httpc = { version = ">= 3.0.0 and < 4.0.0" } +gleam_json = { version = ">= 2.0.0 and < 3.0.0" } +gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" } +gleeunit = { version = ">= 1.0.0 and < 2.0.0" } diff --git a/src/apkallone.gleam b/src/apkallone.gleam index 550933f..67abb4c 100644 --- a/src/apkallone.gleam +++ b/src/apkallone.gleam @@ -1,5 +1,14 @@ -import gleam/io +import gleam/dynamic.{type Dynamic} +import gleam/json.{type DecodeError} -pub fn main() { - io.println("Hello from apkallone!") +pub const user_agent = "Apkallone/0.1.0" + +pub type Client { + Client(instance: String) +} + +pub type Error { + BadInstance + FetchError(error: Dynamic) + DecodeError(error: DecodeError) } diff --git a/src/apkallone/internal.gleam b/src/apkallone/internal.gleam new file mode 100644 index 0000000..646f5b6 --- /dev/null +++ b/src/apkallone/internal.gleam @@ -0,0 +1,36 @@ +import gleam/dynamic.{type Decoder} +import gleam/http/request.{type Request} +import gleam/http/response.{type Response} +import gleam/httpc +import gleam/json +import gleam/result + +import apkallone.{type Client, type Error} + +pub fn prepare_request( + client: Client, + endpoint: String, +) -> Result(Request(String), Error) { + use req <- result.try( + request.to(client.instance <> endpoint) + |> result.replace_error(apkallone.BadInstance), + ) + + req + |> request.prepend_header("user-agent", apkallone.user_agent) + |> Ok +} + +pub fn send_request(req: Request(String)) -> Result(Response(String), Error) { + httpc.send(req) + |> result.map_error(apkallone.FetchError) +} + +pub fn decode_response( + res: Response(String), + decoder: Decoder(a), +) -> Result(a, Error) { + res.body + |> json.decode(decoder) + |> result.map_error(apkallone.DecodeError) +} diff --git a/src/mastodon/instance.gleam b/src/mastodon/instance.gleam new file mode 100644 index 0000000..404fa35 --- /dev/null +++ b/src/mastodon/instance.gleam @@ -0,0 +1,13 @@ +import gleam/result + +import apkallone +import apkallone/internal +import mastodon/model/instance.{type Instance} + +pub fn get_instance( + client: apkallone.Client, +) -> Result(Instance, apkallone.Error) { + use req <- result.try(internal.prepare_request(client, "/api/v2/instance")) + use res <- result.try(internal.send_request(req)) + internal.decode_response(res, instance.decode) +} diff --git a/src/mastodon/model/instance.gleam b/src/mastodon/model/instance.gleam new file mode 100644 index 0000000..83f7afe --- /dev/null +++ b/src/mastodon/model/instance.gleam @@ -0,0 +1,42 @@ +import gleam/dynamic.{type DecodeError, type Dynamic} + +// TODO +pub type Instance { + Instance( + domain: String, + title: String, + version: String, + description: String, + usage: Usage, + ) +} + +pub type Usage { + Usage(users: Users) +} + +pub type Users { + Users(active_month: Int) +} + +pub fn decode(dynamic: Dynamic) -> Result(Instance, List(DecodeError)) { + dynamic + |> dynamic.decode5( + Instance, + dynamic.field("domain", dynamic.string), + dynamic.field("title", dynamic.string), + dynamic.field("version", dynamic.string), + dynamic.field("description", dynamic.string), + dynamic.field("usage", decode_usage), + ) +} + +fn decode_usage(dynamic: Dynamic) -> Result(Usage, List(DecodeError)) { + dynamic + |> dynamic.decode1(Usage, dynamic.field("users", decode_users)) +} + +fn decode_users(dynamic: Dynamic) -> Result(Users, List(DecodeError)) { + dynamic + |> dynamic.decode1(Users, dynamic.field("active_month", dynamic.int)) +}