diff --git a/.gitignore b/.gitignore index 11adbd7..26d5892 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ erl_crash.dump *.ez plum_mail-*.tar + +src/glance/config.gleam diff --git a/Dockerfile b/Dockerfile index 7859ee3..18c18d7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM gleamlang/gleam:0.12.1 +FROM gleamlang/gleam:0.13.2 # NOTE these two should not be needed if using the midas container WORKDIR /opt/app @@ -7,8 +7,9 @@ RUN mix local.hex --force && mix local.rebar --force COPY . . RUN mix deps.get # NOTE there is a bug which means gleam_otp is not compiling properly -RUN gleam build && mix compile -# Unsure why this step is necessay with compilation orders with Gleam + Mix -RUN mix test --no-start --exclude test +# TODO +# RUN mix deps.compile env && gleam build && mix compile +# # Unsure why this step is necessay with compilation orders with Gleam + Mix +# RUN mix test --no-start --exclude test CMD ["./bin/start"] diff --git a/README.md b/README.md index 0dfac9e..e01fda3 100644 --- a/README.md +++ b/README.md @@ -21,3 +21,7 @@ This app uses the container stack. ``` heroku stack:set -a did-glance container ``` + + +gleam build-stuff providers +escript provide env env.spec diff --git a/env/.formatter.exs b/env/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/env/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/env/.gitignore b/env/.gitignore new file mode 100644 index 0000000..11adbd7 --- /dev/null +++ b/env/.gitignore @@ -0,0 +1,8 @@ +/_build/ +/deps/ +/doc/ +/gen/ +/.fetch +erl_crash.dump +*.ez +plum_mail-*.tar diff --git a/env/gleam.toml b/env/gleam.toml new file mode 100644 index 0000000..afc1745 --- /dev/null +++ b/env/gleam.toml @@ -0,0 +1 @@ +name = "env" diff --git a/env/lib/mix/tasks/compiler/env.ex b/env/lib/mix/tasks/compiler/env.ex new file mode 100644 index 0000000..33f2a27 --- /dev/null +++ b/env/lib/mix/tasks/compiler/env.ex @@ -0,0 +1,28 @@ +defmodule Mix.Tasks.Compile.Env do + use Mix.Task.Compiler + + def run(_args) do + # case Mix.shell().cmd( + # # "gleam compile-package --src /opt/app/provider --name provider --out /opt/app/src/provider" + # "gleam build /opt/app/provider" + # ) do + # 0 -> {:ok, []} + # status -> exit(status) + # end + IO.inspect(:code.all_loaded()) + + # root = Application.app_dir(:glance) + root = System.cwd() + + Path.wildcard(root <> "/src/**/*.env") + |> Enum.each(fn file -> + contents = + File.read!(file) + |> :gleam@env.provide() + + File.write(String.replace(file, ".env", ".gleam"), contents) + end) + + :ok + end +end diff --git a/env/mix.exs b/env/mix.exs new file mode 100644 index 0000000..9abd84c --- /dev/null +++ b/env/mix.exs @@ -0,0 +1,28 @@ +defmodule Env.MixProject do + use Mix.Project + + def project do + [ + app: :env, + version: "0.1.0", + elixir: "~> 1.11", + start_permanent: Mix.env() == :prod, + erlc_paths: ["src", "gen"], + compilers: [:gleam | Mix.compilers()], + deps: deps() + ] + end + + def application do + [ + extra_applications: [:logger] + ] + end + + defp deps do + [ + {:mix_gleam, "~> 0.1.0"}, + {:gleam_stdlib, "~> 0.13.0"} + ] + end +end diff --git a/env/mix.lock b/env/mix.lock new file mode 100644 index 0000000..5a5df05 --- /dev/null +++ b/env/mix.lock @@ -0,0 +1,4 @@ +%{ + "gleam_stdlib": {:hex, :gleam_stdlib, "0.13.0", "604a40e0fbe6c688651a5ad5e892913ed314ab626d18d361a7fd8bf367907551", [:rebar3], [], "hexpm", "35a005f4daca2775687e46f4e20cec799563a698a16f8bcc0cf281522ad717f1"}, + "mix_gleam": {:hex, :mix_gleam, "0.1.0", "a0cee5d30de865124a32ca6cd53b64c3e2ac57f12adf6e47b88fb673f47c716e", [:mix], [], "hexpm", "9ff518e6aab444c7f2e74038f9383020ef89810cf1f4402911f33b202ffd72e7"}, +} diff --git a/env/src/gleam/env.gleam b/env/src/gleam/env.gleam new file mode 100644 index 0000000..f2183e9 --- /dev/null +++ b/env/src/gleam/env.gleam @@ -0,0 +1,30 @@ + +// escript gleam_providers "gleam/env" "filename" +// generate "escript fof" +// call already running +// This is useful as is. +// Can generate from .spec.json +// Do mix deps.compile +// Have a providers compiler +// Load up all modules called provider +// Find extension from provider +// Read all files and pass them to the generate module +// gleam_providers as a package +// compile deps +// When running the mix task, it should have all deps +// called gleam@provider@something +// Mix gleam.provide json_schema "foo.com" +// Help with the rust, hash in to map of already defined +// but return no error in that case. + +// Mix compilers -> escripts -> extension + +// provide gleam/env +// Do the hard thing and call external BUT I have to do rust and a syntax + +// Take the module +// search for a function and pass the arguments +// make it into a thing you blat down on it's own +// better than external OS command +// gleam/provider/csv.derive("foo.text") (foo.com) +// How to add dependency to escript, just looks like a function call diff --git a/env/test/env_test.exs b/env/test/env_test.exs new file mode 100644 index 0000000..3667dd8 --- /dev/null +++ b/env/test/env_test.exs @@ -0,0 +1,8 @@ +defmodule EnvTest do + use ExUnit.Case + doctest Env + + test "greets the world" do + assert Env.hello() == :world + end +end diff --git a/env/test/test_helper.exs b/env/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/env/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start() diff --git a/gleam_providers/_build/dev/lib/gleam_providers/.mix/compile.app_tracer b/gleam_providers/_build/dev/lib/gleam_providers/.mix/compile.app_tracer new file mode 100644 index 0000000..c46ec9e Binary files /dev/null and b/gleam_providers/_build/dev/lib/gleam_providers/.mix/compile.app_tracer differ diff --git a/gleam_providers/_build/dev/lib/gleam_providers/.mix/compile.elixir b/gleam_providers/_build/dev/lib/gleam_providers/.mix/compile.elixir new file mode 100644 index 0000000..822182e Binary files /dev/null and b/gleam_providers/_build/dev/lib/gleam_providers/.mix/compile.elixir differ diff --git a/gleam_providers/_build/dev/lib/gleam_providers/.mix/compile.elixir_scm b/gleam_providers/_build/dev/lib/gleam_providers/.mix/compile.elixir_scm new file mode 100644 index 0000000..2177fd9 Binary files /dev/null and b/gleam_providers/_build/dev/lib/gleam_providers/.mix/compile.elixir_scm differ diff --git a/gleam_providers/_build/dev/lib/gleam_providers/.mix/compile.erlang b/gleam_providers/_build/dev/lib/gleam_providers/.mix/compile.erlang new file mode 100644 index 0000000..be7a6e8 Binary files /dev/null and b/gleam_providers/_build/dev/lib/gleam_providers/.mix/compile.erlang differ diff --git a/gleam_providers/_build/dev/lib/gleam_providers/.mix/compile.lock b/gleam_providers/_build/dev/lib/gleam_providers/.mix/compile.lock new file mode 100644 index 0000000..e69de29 diff --git a/gleam_providers/_build/dev/lib/gleam_providers/.mix/compile.protocols b/gleam_providers/_build/dev/lib/gleam_providers/.mix/compile.protocols new file mode 100644 index 0000000..51d52fc Binary files /dev/null and b/gleam_providers/_build/dev/lib/gleam_providers/.mix/compile.protocols differ diff --git a/gleam_providers/_build/dev/lib/gleam_providers/consolidated/Elixir.Collectable.beam b/gleam_providers/_build/dev/lib/gleam_providers/consolidated/Elixir.Collectable.beam new file mode 100644 index 0000000..78b65cc Binary files /dev/null and b/gleam_providers/_build/dev/lib/gleam_providers/consolidated/Elixir.Collectable.beam differ diff --git a/gleam_providers/_build/dev/lib/gleam_providers/consolidated/Elixir.Enumerable.beam b/gleam_providers/_build/dev/lib/gleam_providers/consolidated/Elixir.Enumerable.beam new file mode 100644 index 0000000..677081c Binary files /dev/null and b/gleam_providers/_build/dev/lib/gleam_providers/consolidated/Elixir.Enumerable.beam differ diff --git a/gleam_providers/_build/dev/lib/gleam_providers/consolidated/Elixir.IEx.Info.beam b/gleam_providers/_build/dev/lib/gleam_providers/consolidated/Elixir.IEx.Info.beam new file mode 100644 index 0000000..775e5df Binary files /dev/null and b/gleam_providers/_build/dev/lib/gleam_providers/consolidated/Elixir.IEx.Info.beam differ diff --git a/gleam_providers/_build/dev/lib/gleam_providers/consolidated/Elixir.Inspect.beam b/gleam_providers/_build/dev/lib/gleam_providers/consolidated/Elixir.Inspect.beam new file mode 100644 index 0000000..72e616e Binary files /dev/null and b/gleam_providers/_build/dev/lib/gleam_providers/consolidated/Elixir.Inspect.beam differ diff --git a/gleam_providers/_build/dev/lib/gleam_providers/consolidated/Elixir.List.Chars.beam b/gleam_providers/_build/dev/lib/gleam_providers/consolidated/Elixir.List.Chars.beam new file mode 100644 index 0000000..73bdd0d Binary files /dev/null and b/gleam_providers/_build/dev/lib/gleam_providers/consolidated/Elixir.List.Chars.beam differ diff --git a/gleam_providers/_build/dev/lib/gleam_providers/consolidated/Elixir.String.Chars.beam b/gleam_providers/_build/dev/lib/gleam_providers/consolidated/Elixir.String.Chars.beam new file mode 100644 index 0000000..203dbfd Binary files /dev/null and b/gleam_providers/_build/dev/lib/gleam_providers/consolidated/Elixir.String.Chars.beam differ diff --git a/gleam_providers/_build/dev/lib/gleam_providers/ebin/Elixir.GleamProviders.CLI.beam b/gleam_providers/_build/dev/lib/gleam_providers/ebin/Elixir.GleamProviders.CLI.beam new file mode 100644 index 0000000..34261a2 Binary files /dev/null and b/gleam_providers/_build/dev/lib/gleam_providers/ebin/Elixir.GleamProviders.CLI.beam differ diff --git a/gleam_providers/_build/dev/lib/gleam_providers/ebin/csv.beam b/gleam_providers/_build/dev/lib/gleam_providers/ebin/csv.beam new file mode 100644 index 0000000..79313c5 Binary files /dev/null and b/gleam_providers/_build/dev/lib/gleam_providers/ebin/csv.beam differ diff --git a/gleam_providers/_build/dev/lib/gleam_providers/ebin/gleam_providers.app b/gleam_providers/_build/dev/lib/gleam_providers/ebin/gleam_providers.app new file mode 100644 index 0000000..b5c534c --- /dev/null +++ b/gleam_providers/_build/dev/lib/gleam_providers/ebin/gleam_providers.app @@ -0,0 +1,7 @@ +{application,gleam_providers, + [{applications,[kernel,stdlib,elixir,logger,mix_gleam, + gleam_stdlib]}, + {description,"gleam_providers"}, + {modules,['Elixir.GleamProviders.CLI',csv]}, + {registered,[]}, + {vsn,"0.1.0"}]}. diff --git a/gleam_providers/_build/dev/lib/gleam_stdlib/.mix/compile.fetch b/gleam_providers/_build/dev/lib/gleam_stdlib/.mix/compile.fetch new file mode 100644 index 0000000..e69de29 diff --git a/gleam_providers/_build/dev/lib/gleam_stdlib/ebin b/gleam_providers/_build/dev/lib/gleam_stdlib/ebin new file mode 120000 index 0000000..ac730a6 --- /dev/null +++ b/gleam_providers/_build/dev/lib/gleam_stdlib/ebin @@ -0,0 +1 @@ +../../../../deps/gleam_stdlib/ebin \ No newline at end of file diff --git a/gleam_providers/_build/dev/lib/gleam_stdlib/mix.rebar.config b/gleam_providers/_build/dev/lib/gleam_stdlib/mix.rebar.config new file mode 100644 index 0000000..07c9254 --- /dev/null +++ b/gleam_providers/_build/dev/lib/gleam_stdlib/mix.rebar.config @@ -0,0 +1,5 @@ +{erl_opts,[debug_info]}. +{src_dirs,["src","gen/src"]}. +{profiles,[{test,[{src_dirs,["src","test","gen/src","gen/test"]}]}]}. +{deps,[]}. +{overrides,[]}. diff --git a/gleam_providers/_build/dev/lib/mix_gleam/.mix/compile.app_tracer b/gleam_providers/_build/dev/lib/mix_gleam/.mix/compile.app_tracer new file mode 100644 index 0000000..82c7f00 Binary files /dev/null and b/gleam_providers/_build/dev/lib/mix_gleam/.mix/compile.app_tracer differ diff --git a/gleam_providers/_build/dev/lib/mix_gleam/.mix/compile.elixir b/gleam_providers/_build/dev/lib/mix_gleam/.mix/compile.elixir new file mode 100644 index 0000000..bb3f465 Binary files /dev/null and b/gleam_providers/_build/dev/lib/mix_gleam/.mix/compile.elixir differ diff --git a/gleam_providers/_build/dev/lib/mix_gleam/.mix/compile.elixir_scm b/gleam_providers/_build/dev/lib/mix_gleam/.mix/compile.elixir_scm new file mode 100644 index 0000000..8acead1 Binary files /dev/null and b/gleam_providers/_build/dev/lib/mix_gleam/.mix/compile.elixir_scm differ diff --git a/gleam_providers/_build/dev/lib/mix_gleam/.mix/compile.fetch b/gleam_providers/_build/dev/lib/mix_gleam/.mix/compile.fetch new file mode 100644 index 0000000..e69de29 diff --git a/gleam_providers/_build/dev/lib/mix_gleam/ebin/Elixir.Mix.Tasks.Compile.Gleam.beam b/gleam_providers/_build/dev/lib/mix_gleam/ebin/Elixir.Mix.Tasks.Compile.Gleam.beam new file mode 100644 index 0000000..4dc1028 Binary files /dev/null and b/gleam_providers/_build/dev/lib/mix_gleam/ebin/Elixir.Mix.Tasks.Compile.Gleam.beam differ diff --git a/gleam_providers/_build/dev/lib/mix_gleam/ebin/mix_gleam.app b/gleam_providers/_build/dev/lib/mix_gleam/ebin/mix_gleam.app new file mode 100644 index 0000000..93a8a70 --- /dev/null +++ b/gleam_providers/_build/dev/lib/mix_gleam/ebin/mix_gleam.app @@ -0,0 +1,6 @@ +{application,mix_gleam, + [{applications,[kernel,stdlib,elixir]}, + {description,"Compile Gleam code with mix"}, + {modules,['Elixir.Mix.Tasks.Compile.Gleam']}, + {registered,[]}, + {vsn,"0.1.0"}]}. diff --git a/gleam_providers/deps/gleam_stdlib/.fetch b/gleam_providers/deps/gleam_stdlib/.fetch new file mode 100644 index 0000000..e69de29 diff --git a/gleam_providers/deps/gleam_stdlib/.hex b/gleam_providers/deps/gleam_stdlib/.hex new file mode 100644 index 0000000..eb97c31 Binary files /dev/null and b/gleam_providers/deps/gleam_stdlib/.hex differ diff --git a/gleam_providers/deps/gleam_stdlib/.rebar3/rebar_compiler_erl/source.dag b/gleam_providers/deps/gleam_stdlib/.rebar3/rebar_compiler_erl/source.dag new file mode 100644 index 0000000..390cb2d Binary files /dev/null and b/gleam_providers/deps/gleam_stdlib/.rebar3/rebar_compiler_erl/source.dag differ diff --git a/gleam_providers/deps/gleam_stdlib/CHANGELOG.md b/gleam_providers/deps/gleam_stdlib/CHANGELOG.md new file mode 100644 index 0000000..172f370 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/CHANGELOG.md @@ -0,0 +1,205 @@ +# Changelog + +## v0.13.0 - 2021-01-13 + +- The `int` module gains the `absolute_value`, `sum` and `product` functions. +- The `float` module gains the `sum` and `product` functions. +- The `result` module gains the `lazy_or`, `lazy_unwrap`, and `replace_error` functions. +- The `bool` module gains the `nand`, `nor`, `exclusive_nor`, and `exclusive_or` functions. +- The `bit_builder` module gains the `from_string_builder` function. +- The `list` modules gains the `index_fold`, `permutations`, and `try_fold` functions. +- Breaking change in `queue.from_list`. The head element in the list becomes the first element in the queue. +- Fix `queue.pop_back` and `queue.pop_front` + +## v0.12.0 - 2020-11-04 + +- The `function` module gains `curry2` to `curry6`. +- The `list` module gains the `each`, and `partition` functions. +- The `int` and `float` modules gain the `negate` function. +- The `int` module gains the `to_float` function. +- The `result` module gains the `all` function. +- The `dynamic` module gains the `option`, `result` and `typed_result` + functions. +- The `uri` module gains the `percent_encode` and `percent_decode` functions. +- The `os` module gains the `erlang_timestamp` function. +- The `iterator` module gains the `append`, `flatten`, `flat_map`, `step`, + and `find` functions. + +## v0.11.0 - 2020-08-22 + +- Fix `uri.parse_query` to handle the case where query parameters are present + without a value. +- The types for `list.find_map` have been relaxed. +- The `dynamic.typed_list` argument label has changed from `containing` to + `of`. +- The `dynamic` module gains the `any` function. +- The `bit_builder` module gains the `from_string` function. +- The `list` module gains the `key_set` and `unzip` function. +- The `function` module gains the `rescue` function. +- The `float` module gains the `power`, `square_root`, and `absolute_value` + functions. + +## v0.10.1 - 2020-07-01 + +- Fix `dynamic.string` to check that binary contains only utf8 characters. + +## v0.10.0 - 2020-06-30 + +- `bit_string` module created with `from_string`, `byte_size`, `append`, + `part`, `to_string`, `is_utf8`, `int_to_u32` and `int_from_u32` functions. +- The `bit_builder` module has been introduced with `prepend`, `append`, + `prepend_builder`, `append_builder`, `prepend_string`, `append_string`, + `concat`, `from_bit_string`, `to_bit_string`, and `byte_size` functions. +- The `iodata` module has been renamed to `string_builder`. +- `os` module created with `get_env`, `insert_env`, `delete_env` and + `system_time`. +- The `string` module gains the `split_once` and `utf_codepoint` functions. +- The `dynamic` module gains the `bit_string` function. +- The `uri` module gains the `origin` and `merge` function. +- The `io.debug` function returns the printed term. +- The `dynamic.list` function has been renamed to `dynamic.typed_list`. +- The `dynamic.opaque_list` function has been renamed to `dynamic.list`. +- The `dynamic.tuple2_of` function has been renamed to `dynamic.typed_tuple2`. +- The `list.traverse` function has been renamed to `list.try_map`. +- The `list.traverse` first argument gains the label `over`. +- The `option` module gains the the `map`, `flatten`, `then` and `or` + functions. +- The `result` module gains the the `or` function. +- Created the `regex` module with the `from_string`, `compile`, `check`, + `split` and `scan` functions. +- The `list` module gains the the `pop`, `pop_map` and `key_pop` functions. +- `base` module created with `encode64`, `decode64`, `url_encode64` and + `url_decode64`. + +## v0.9.0 - 2020-05-26 + +- Created the `iterator` module with the `unfold`, `repeatedly`, `repeat`, + `from_list`, `fold`, `run`, `to_list`, `take`, `drop`, `map`, `filter`, + `cycle`, and `range` functions. +- Created the `set` module with the `new`, `insert`, `delete`, `to_list`, + `from_list`, `fold`, `take`, `union`, `intersection`, and `contains` + functions. +- Created the `io` module with the `print`, `println`, and `debug` functions. +- Created the `queue` module with the `new`, `from_list`, `to_list`, + `is_empty`, `length`, `push_back`, `push_front`, `pop_back`, `pop_front`, + `reverse`, `is_logically_equal`, and `is_equal` functions. +- Created the `option` module containing the `Option` type and the `is_some` + and `is_none` functions. +- Created the `option` module containing the `Option` type and the `is_some`, + `is_none`, `to_result`, `from_result` and `unwrap` functions. +- Removed the `Option` alias and the `none` function from the `result` module. +- The `result` module gains the `nil_error` function. +- The `string` module gains `trim`, `trim_left`, `trim_right`, `starts_with`, + `ends_with`, `slice`, `pad_left`, `pad_right` `drop_left`, `drop_right`, + `pop_grapheme` and `to_graphemes' functions. +- `uri` module created with `parse`, `parse_query`, `path_segments`, + `query_to_string` and `to_string`. +- The `dynamic` module gains the `map`, `opaque_list`, `tuple2`, and + `tuple2_of` functions. +- The `list` module gains the `filter_map` function. +- The `list.contains` label `has` has been changed to `any`. +- The `list.sort` label `sort_by` has been changed to `by`. +- The `list.fold`'s first argument gained the label `over`. +- The `map.fold`'s first argument gained the label `over`. +- The `map.take`'s `drop` arguement has been changed to `keeping`. + +## v0.8.0 - 2020-04-28 + +- The error type for `atom.from_string` has been renamed to `FromStringError`. +- The `string` module gains `contains` and `repeat` functions. +- The `expect` module has been renamed to `should`. Functions in the module + starting with `is_` have been changed to `be_`. +- The `string.replace` and `iodata.replace` `all` arguement label has been + changed to `each`. +- The `string` module gains `is_empty`, `join` and `concat` functions. +- The `int` module gains `is_even` and `is_odd` functions. +- The `list.length` function now accepts a labelled argument. +- The `list.length` function now accepts a labelled argument. +- The the second argument of `bool.compare`, `float.compare`, `int.compare`, + and `order.compare` now have the label `with`. +- The `dynamic.unsafe_coerce` function now only accepts Dynamic data. +- The `dynamic` decoder functions no longer print the entire value in their + error messages, to avoid large errors. + +## v0.7.0 - 2020-03-03 + +- The `result` module gains an `Option` type alias. +- The `function` module has been created with `identity`, `compose`, and + `flip` functions. +- The error type of `list.find_map` is now `Nil`. +- The labels for `list.split` are now `split(list: _, at: _)`. + +## v0.6.0 - 2019-12-23 + +- Syntax has been updated for Gleam v0.6.0. +- The `dynamic` module gains an `element` for decoding tuples. + +## v0.5.0 - 2019-12-16 + +- Syntax has been updated for Gleam v0.5. +- Labels have been added to functions throughout the stdlib. +- `map.fetch` has been renamed to `map.get` and `map.put` to `map.insert`. +- `list.find` has been renamed `list.find_map` and a new `list.find` has been + introduced. +- The `pair` module gains the `map_first`, and `map_second` functions. +- The `pair.Pair` type has been replaced with a 2 element anonymous struct. +- The `triple` module has been removed. +- The `string` module gains the `compare` function. +- The `float` module gains the `max`, and `min` functions. +- The `int` module gains the `max`, and `min` functions. +- The `Any` type and module have been renamed to `Dynamic`. + +## v0.4.0 - 2019-09-19 + +- Syntax has been updated for Gleam v0.4. +- The `map_dict` module has been renamed to `map`. +- `list:sort` now requires a compare function as comparison operators + now only work on Ints. +- `list:sort`'s performance has been slightly optimised. +- The `float` module gains a `compare` function. +- `any.tuple` has been renamed `any.pair`. +- The `tuple` module has been renamed to `pair` and has a `Pair` type. +- `pair.fetch` has been replaced with `list.key_find`. +- `triple` module has been created with type `Triple`. +- The error type for `float.parse`, `int.parse`, `list.head`, `list.tail`, + `list.find`, `list.at`, `map.fetch`, and `map.update` is now `Nil`. + +## v0.3.1 - 2019-08-08 + +- `result:map_error` has been relaxed to allow mapping to a different error + type. + +## v0.3.0 - 2019-06-25 + +- The `map_dict` module gains a `fold` function. +- All modules moved under the `std` namespace. +- The `http` module has been split out into the `gleam_http` package. + +## v0.2.0 - 2019-05-11 + +- Library renamed to `gleam_stdlib`. +- The `map_dict` module gains `update`, `merge` and `delete` functions. +- The `bool` module gains a `compare` function. +- The `int` module gains a `compare` function. +- The `list` module gains `range`, `repeat`, `split`, `split_while` and + `strict_zip` functions. + +## v0.1.2 - 2019-04-25 + +- The `list` module gains `at`, `all`, `any`, `index_map`, `intersperse`, + `sort`, `unique`, and `zip` functions. +- `map_dict:Map` renamed to `map_dict:MapDict`. +- The `map_dict` module gains `drop`, and `take` functions. +- The `str` module gains `append` function and loses `from_int`, `parse_int`, + `from_float`, `parse_float`, and `base_from_int`. +- `int` module created with `parse`, `to_string`, and `to_base_string`. +- `float` module created with `ceiling`, `floor`, `round`, `truncate`, + `parse`, and `to_string`. + +## v0.1.1 - 2019-04-17 + +- Included missing gleam.toml in hex package. + +## v0.1.0 - 2019-04-15 + +- Initial release! diff --git a/gleam_providers/deps/gleam_stdlib/LICENSE b/gleam_providers/deps/gleam_stdlib/LICENSE new file mode 100644 index 0000000..5ae8e16 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/LICENSE @@ -0,0 +1,191 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2018, Louis Pilfold . + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/gleam_providers/deps/gleam_stdlib/README.md b/gleam_providers/deps/gleam_stdlib/README.md new file mode 100644 index 0000000..111d5d8 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/README.md @@ -0,0 +1,15 @@ +# stdlib + +GitHub release +Discord chat +![CI](https://github.com/gleam-lang/stdlib/workflows/CI/badge.svg?branch=main) + +Gleam's standard library! +Documentation available on [HexDocs](https://hexdocs.pm/gleam_stdlib/). + +## Quick reference + +```sh +# Run the unit tests +rebar3 eunit +``` diff --git a/gleam_providers/deps/gleam_stdlib/ebin/gleam@atom.beam b/gleam_providers/deps/gleam_stdlib/ebin/gleam@atom.beam new file mode 100644 index 0000000..0fe72ac Binary files /dev/null and b/gleam_providers/deps/gleam_stdlib/ebin/gleam@atom.beam differ diff --git a/gleam_providers/deps/gleam_stdlib/ebin/gleam@base.beam b/gleam_providers/deps/gleam_stdlib/ebin/gleam@base.beam new file mode 100644 index 0000000..6650890 Binary files /dev/null and b/gleam_providers/deps/gleam_stdlib/ebin/gleam@base.beam differ diff --git a/gleam_providers/deps/gleam_stdlib/ebin/gleam@bit_builder.beam b/gleam_providers/deps/gleam_stdlib/ebin/gleam@bit_builder.beam new file mode 100644 index 0000000..6dfebed Binary files /dev/null and b/gleam_providers/deps/gleam_stdlib/ebin/gleam@bit_builder.beam differ diff --git a/gleam_providers/deps/gleam_stdlib/ebin/gleam@bit_string.beam b/gleam_providers/deps/gleam_stdlib/ebin/gleam@bit_string.beam new file mode 100644 index 0000000..db6f30a Binary files /dev/null and b/gleam_providers/deps/gleam_stdlib/ebin/gleam@bit_string.beam differ diff --git a/gleam_providers/deps/gleam_stdlib/ebin/gleam@bool.beam b/gleam_providers/deps/gleam_stdlib/ebin/gleam@bool.beam new file mode 100644 index 0000000..bff2ee2 Binary files /dev/null and b/gleam_providers/deps/gleam_stdlib/ebin/gleam@bool.beam differ diff --git a/gleam_providers/deps/gleam_stdlib/ebin/gleam@dynamic.beam b/gleam_providers/deps/gleam_stdlib/ebin/gleam@dynamic.beam new file mode 100644 index 0000000..41a3126 Binary files /dev/null and b/gleam_providers/deps/gleam_stdlib/ebin/gleam@dynamic.beam differ diff --git a/gleam_providers/deps/gleam_stdlib/ebin/gleam@float.beam b/gleam_providers/deps/gleam_stdlib/ebin/gleam@float.beam new file mode 100644 index 0000000..f104495 Binary files /dev/null and b/gleam_providers/deps/gleam_stdlib/ebin/gleam@float.beam differ diff --git a/gleam_providers/deps/gleam_stdlib/ebin/gleam@function.beam b/gleam_providers/deps/gleam_stdlib/ebin/gleam@function.beam new file mode 100644 index 0000000..f37fd32 Binary files /dev/null and b/gleam_providers/deps/gleam_stdlib/ebin/gleam@function.beam differ diff --git a/gleam_providers/deps/gleam_stdlib/ebin/gleam@int.beam b/gleam_providers/deps/gleam_stdlib/ebin/gleam@int.beam new file mode 100644 index 0000000..42b56f7 Binary files /dev/null and b/gleam_providers/deps/gleam_stdlib/ebin/gleam@int.beam differ diff --git a/gleam_providers/deps/gleam_stdlib/ebin/gleam@io.beam b/gleam_providers/deps/gleam_stdlib/ebin/gleam@io.beam new file mode 100644 index 0000000..6428465 Binary files /dev/null and b/gleam_providers/deps/gleam_stdlib/ebin/gleam@io.beam differ diff --git a/gleam_providers/deps/gleam_stdlib/ebin/gleam@iterator.beam b/gleam_providers/deps/gleam_stdlib/ebin/gleam@iterator.beam new file mode 100644 index 0000000..7dc1b39 Binary files /dev/null and b/gleam_providers/deps/gleam_stdlib/ebin/gleam@iterator.beam differ diff --git a/gleam_providers/deps/gleam_stdlib/ebin/gleam@list.beam b/gleam_providers/deps/gleam_stdlib/ebin/gleam@list.beam new file mode 100644 index 0000000..acb9ddd Binary files /dev/null and b/gleam_providers/deps/gleam_stdlib/ebin/gleam@list.beam differ diff --git a/gleam_providers/deps/gleam_stdlib/ebin/gleam@map.beam b/gleam_providers/deps/gleam_stdlib/ebin/gleam@map.beam new file mode 100644 index 0000000..6bf04d3 Binary files /dev/null and b/gleam_providers/deps/gleam_stdlib/ebin/gleam@map.beam differ diff --git a/gleam_providers/deps/gleam_stdlib/ebin/gleam@option.beam b/gleam_providers/deps/gleam_stdlib/ebin/gleam@option.beam new file mode 100644 index 0000000..f6f9920 Binary files /dev/null and b/gleam_providers/deps/gleam_stdlib/ebin/gleam@option.beam differ diff --git a/gleam_providers/deps/gleam_stdlib/ebin/gleam@order.beam b/gleam_providers/deps/gleam_stdlib/ebin/gleam@order.beam new file mode 100644 index 0000000..73ea52b Binary files /dev/null and b/gleam_providers/deps/gleam_stdlib/ebin/gleam@order.beam differ diff --git a/gleam_providers/deps/gleam_stdlib/ebin/gleam@os.beam b/gleam_providers/deps/gleam_stdlib/ebin/gleam@os.beam new file mode 100644 index 0000000..79f4ed3 Binary files /dev/null and b/gleam_providers/deps/gleam_stdlib/ebin/gleam@os.beam differ diff --git a/gleam_providers/deps/gleam_stdlib/ebin/gleam@pair.beam b/gleam_providers/deps/gleam_stdlib/ebin/gleam@pair.beam new file mode 100644 index 0000000..56c8ce0 Binary files /dev/null and b/gleam_providers/deps/gleam_stdlib/ebin/gleam@pair.beam differ diff --git a/gleam_providers/deps/gleam_stdlib/ebin/gleam@queue.beam b/gleam_providers/deps/gleam_stdlib/ebin/gleam@queue.beam new file mode 100644 index 0000000..c334c7a Binary files /dev/null and b/gleam_providers/deps/gleam_stdlib/ebin/gleam@queue.beam differ diff --git a/gleam_providers/deps/gleam_stdlib/ebin/gleam@regex.beam b/gleam_providers/deps/gleam_stdlib/ebin/gleam@regex.beam new file mode 100644 index 0000000..f614401 Binary files /dev/null and b/gleam_providers/deps/gleam_stdlib/ebin/gleam@regex.beam differ diff --git a/gleam_providers/deps/gleam_stdlib/ebin/gleam@result.beam b/gleam_providers/deps/gleam_stdlib/ebin/gleam@result.beam new file mode 100644 index 0000000..d18aa42 Binary files /dev/null and b/gleam_providers/deps/gleam_stdlib/ebin/gleam@result.beam differ diff --git a/gleam_providers/deps/gleam_stdlib/ebin/gleam@set.beam b/gleam_providers/deps/gleam_stdlib/ebin/gleam@set.beam new file mode 100644 index 0000000..977765f Binary files /dev/null and b/gleam_providers/deps/gleam_stdlib/ebin/gleam@set.beam differ diff --git a/gleam_providers/deps/gleam_stdlib/ebin/gleam@should.beam b/gleam_providers/deps/gleam_stdlib/ebin/gleam@should.beam new file mode 100644 index 0000000..56eae13 Binary files /dev/null and b/gleam_providers/deps/gleam_stdlib/ebin/gleam@should.beam differ diff --git a/gleam_providers/deps/gleam_stdlib/ebin/gleam@string.beam b/gleam_providers/deps/gleam_stdlib/ebin/gleam@string.beam new file mode 100644 index 0000000..65e740f Binary files /dev/null and b/gleam_providers/deps/gleam_stdlib/ebin/gleam@string.beam differ diff --git a/gleam_providers/deps/gleam_stdlib/ebin/gleam@string_builder.beam b/gleam_providers/deps/gleam_stdlib/ebin/gleam@string_builder.beam new file mode 100644 index 0000000..00097e3 Binary files /dev/null and b/gleam_providers/deps/gleam_stdlib/ebin/gleam@string_builder.beam differ diff --git a/gleam_providers/deps/gleam_stdlib/ebin/gleam@uri.beam b/gleam_providers/deps/gleam_stdlib/ebin/gleam@uri.beam new file mode 100644 index 0000000..4b38ea4 Binary files /dev/null and b/gleam_providers/deps/gleam_stdlib/ebin/gleam@uri.beam differ diff --git a/gleam_providers/deps/gleam_stdlib/ebin/gleam_stdlib.app b/gleam_providers/deps/gleam_stdlib/ebin/gleam_stdlib.app new file mode 100644 index 0000000..bf40756 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/ebin/gleam_stdlib.app @@ -0,0 +1,16 @@ +{application,gleam_stdlib, + [{description,"A standard library for the Gleam programming language"}, + {vsn,"0.13.0"}, + {registered,[]}, + {applications,[kernel,stdlib]}, + {env,[]}, + {modules,[gleam@atom,gleam@base,gleam@bit_builder, + gleam@bit_string,gleam@bool,gleam@dynamic,gleam@float, + gleam@function,gleam@int,gleam@io,gleam@iterator, + gleam@list,gleam@map,gleam@option,gleam@order, + gleam@os,gleam@pair,gleam@queue,gleam@regex, + gleam@result,gleam@set,gleam@should,gleam@string, + gleam@string_builder,gleam@uri,gleam_stdlib]}, + {licenses,["Apache 2.0"]}, + {links,[{"GitHub","https://github.com/gleam-lang/stdlib"}]}, + {include_files,["gleam.toml","gen"]}]}. diff --git a/gleam_providers/deps/gleam_stdlib/ebin/gleam_stdlib.beam b/gleam_providers/deps/gleam_stdlib/ebin/gleam_stdlib.beam new file mode 100644 index 0000000..9b1a6b2 Binary files /dev/null and b/gleam_providers/deps/gleam_stdlib/ebin/gleam_stdlib.beam differ diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@atom.erl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@atom.erl new file mode 100644 index 0000000..1150544 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@atom.erl @@ -0,0 +1,13 @@ +-module(gleam@atom). +-compile(no_auto_import). + +-export([from_string/1, create_from_string/1, to_string/1]). + +from_string(A) -> + gleam_stdlib:atom_from_string(A). + +create_from_string(A) -> + gleam_stdlib:atom_create_from_string(A). + +to_string(A) -> + gleam_stdlib:atom_to_string(A). diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@base.erl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@base.erl new file mode 100644 index 0000000..d97743c --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@base.erl @@ -0,0 +1,50 @@ +-module(gleam@base). +-compile(no_auto_import). + +-export([encode64/2, decode64/1, url_encode64/2, url_decode64/1]). + +encode64(Input, Padding) -> + Encoded = base64:encode(Input), + case Padding of + true -> + Encoded; + + false -> + gleam@string:replace(Encoded, <<"="/utf8>>, <<""/utf8>>) + end. + +decode64(Encoded) -> + Padded = case gleam@bit_string:byte_size( + gleam@bit_string:from_string(Encoded) + ) + rem 4 of + 0 -> + Encoded; + + N -> + gleam@string:append( + Encoded, + gleam@string:repeat(<<"="/utf8>>, 4 - N) + ) + end, + gleam_stdlib:base_decode64(Padded). + +url_encode64(Input, Padding) -> + gleam@string:replace( + gleam@string:replace( + encode64(Input, Padding), + <<"+"/utf8>>, + <<"-"/utf8>> + ), + <<"/"/utf8>>, + <<"_"/utf8>> + ). + +url_decode64(Encoded) -> + decode64( + gleam@string:replace( + gleam@string:replace(Encoded, <<"-"/utf8>>, <<"+"/utf8>>), + <<"_"/utf8>>, + <<"/"/utf8>> + ) + ). diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@bit_builder.erl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@bit_builder.erl new file mode 100644 index 0000000..c0343f2 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@bit_builder.erl @@ -0,0 +1,40 @@ +-module(gleam@bit_builder). +-compile(no_auto_import). + +-export([prepend/2, append/2, prepend_builder/2, append_builder/2, prepend_string/2, append_string/2, concat/1, from_string/1, from_string_builder/1, from_bit_string/1, to_bit_string/1, byte_size/1]). + +prepend(A, B) -> + gleam_stdlib:iodata_prepend(A, B). + +append(A, B) -> + gleam_stdlib:iodata_append(A, B). + +prepend_builder(A, B) -> + gleam_stdlib:iodata_prepend(A, B). + +append_builder(A, B) -> + gleam_stdlib:iodata_append(A, B). + +prepend_string(A, B) -> + gleam_stdlib:iodata_prepend(A, B). + +append_string(A, B) -> + gleam_stdlib:iodata_append(A, B). + +concat(A) -> + gleam_stdlib:identity(A). + +from_string(A) -> + gleam_stdlib:wrap_list(A). + +from_string_builder(A) -> + gleam_stdlib:identity(A). + +from_bit_string(A) -> + gleam_stdlib:wrap_list(A). + +to_bit_string(A) -> + erlang:list_to_bitstring(A). + +byte_size(A) -> + erlang:iolist_size(A). diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@bit_string.erl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@bit_string.erl new file mode 100644 index 0000000..3ca279b --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@bit_string.erl @@ -0,0 +1,43 @@ +-module(gleam@bit_string). +-compile(no_auto_import). + +-export([from_string/1, byte_size/1, append/2, part/3, int_to_u32/1, int_from_u32/1, is_utf8/1, to_string/1]). + +from_string(A) -> + gleam_stdlib:identity(A). + +byte_size(A) -> + erlang:byte_size(A). + +append(A, B) -> + gleam_stdlib:bit_string_append(A, B). + +part(A, B, C) -> + gleam_stdlib:bit_string_part_(A, B, C). + +int_to_u32(A) -> + gleam_stdlib:bit_string_int_to_u32(A). + +int_from_u32(A) -> + gleam_stdlib:bit_string_int_from_u32(A). + +is_utf8(Bits) -> + case Bits of + <<>> -> + true; + + <<_/utf8, Rest/binary>> -> + is_utf8(Rest); + + _ -> + false + end. + +to_string(Bits) -> + case is_utf8(Bits) of + true -> + {ok, gleam_stdlib:identity(Bits)}; + + false -> + {error, nil} + end. diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@bool.erl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@bool.erl new file mode 100644 index 0000000..0945e4e --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@bool.erl @@ -0,0 +1,115 @@ +-module(gleam@bool). +-compile(no_auto_import). + +-export([negate/1, nor/2, nand/2, exclusive_or/2, exclusive_nor/2, compare/2, max/2, min/2, to_int/1]). + +negate(Bool) -> + case Bool of + true -> + false; + + false -> + true + end. + +nor(A, B) -> + case {A, B} of + {false, false} -> + true; + + {false, true} -> + false; + + {true, false} -> + false; + + {true, true} -> + false + end. + +nand(A, B) -> + case {A, B} of + {false, false} -> + true; + + {false, true} -> + true; + + {true, false} -> + true; + + {true, true} -> + false + end. + +exclusive_or(A, B) -> + case {A, B} of + {false, false} -> + false; + + {false, true} -> + true; + + {true, false} -> + true; + + {true, true} -> + false + end. + +exclusive_nor(A, B) -> + case {A, B} of + {false, false} -> + true; + + {false, true} -> + false; + + {true, false} -> + false; + + {true, true} -> + true + end. + +compare(A, B) -> + case {A, B} of + {true, true} -> + eq; + + {true, false} -> + gt; + + {false, false} -> + eq; + + {false, true} -> + lt + end. + +max(A, B) -> + case A of + true -> + true; + + false -> + B + end. + +min(A, B) -> + case A of + false -> + false; + + true -> + B + end. + +to_int(Bool) -> + case Bool of + false -> + 0; + + true -> + 1 + end. diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@dynamic.erl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@dynamic.erl new file mode 100644 index 0000000..2ee700b --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@dynamic.erl @@ -0,0 +1,146 @@ +-module(gleam@dynamic). +-compile(no_auto_import). + +-export([from/1, unsafe_coerce/1, bit_string/1, string/1, int/1, float/1, atom/1, bool/1, thunk/1, list/1, result/1, typed_result/3, typed_list/2, option/2, field/2, element/2, tuple2/1, typed_tuple2/3, map/1, any/2]). + +from(A) -> + gleam_stdlib:identity(A). + +unsafe_coerce(A) -> + gleam_stdlib:identity(A). + +bit_string(A) -> + gleam_stdlib:decode_bit_string(A). + +string(From) -> + gleam@result:then( + gleam_stdlib:decode_bit_string(From), + fun(Raw) -> case gleam@bit_string:to_string(Raw) of + {ok, String} -> + {ok, String}; + + {error, nil} -> + {error, <<"Expected a string, got a bit_string"/utf8>>} + end end + ). + +int(A) -> + gleam_stdlib:decode_int(A). + +float(A) -> + gleam_stdlib:decode_float(A). + +atom(A) -> + gleam_stdlib:decode_atom(A). + +bool(A) -> + gleam_stdlib:decode_bool(A). + +thunk(A) -> + gleam_stdlib:decode_thunk(A). + +list(A) -> + gleam_stdlib:decode_list(A). + +result(From) -> + case gleam_stdlib:decode_tuple2(From) of + {error, Gleam@try_error} -> {error, Gleam@try_error}; + {ok, {Key, Val}} -> + case gleam_stdlib:decode_atom(Key) of + {error, Gleam@try_error@1} -> {error, Gleam@try_error@1}; + {ok, Tag} -> + Ok_atom = gleam@atom:create_from_string(<<"ok"/utf8>>), + Error_atom = gleam@atom:create_from_string(<<"error"/utf8>>), + case Tag of + Tag@1 when Tag@1 =:= Ok_atom -> + {ok, {ok, Val}}; + + Tag@2 when Tag@2 =:= Error_atom -> + {ok, {error, Val}}; + + Tag@3 -> + {error, + gleam@string_builder:to_string( + gleam@string_builder:append( + gleam@string_builder:append( + gleam@string_builder:from_string( + <<"Expected a tag of \"ok\" or \"error\", got \""/utf8>> + ), + gleam@atom:to_string(Tag@3) + ), + <<"\""/utf8>> + ) + )} + end + end + end. + +typed_result(Dynamic, Decode_ok, Decode_error) -> + case result(Dynamic) of + {error, Gleam@try_error} -> {error, Gleam@try_error}; + {ok, Inner_result} -> + case Inner_result of + {ok, Raw} -> + gleam@result:map(Decode_ok(Raw), fun(A) -> {ok, A} end); + + {error, Raw@1} -> + gleam@result:map( + Decode_error(Raw@1), + fun(A) -> {error, A} end + ) + end + end. + +typed_list(Dynamic, Decoder_type) -> + gleam@result:then( + gleam_stdlib:decode_list(Dynamic), + fun(Gleam@capture_variable) -> + gleam@list:try_map(Gleam@capture_variable, Decoder_type) + end + ). + +option(Dynamic, Decoder) -> + {ok, Null} = gleam@atom:from_string(<<"null"/utf8>>), + case {gleam_stdlib:decode_atom(Dynamic), Decoder(Dynamic)} of + {{ok, Atom}, _} when Atom =:= Null -> + {ok, none}; + + {_, {ok, Result}} -> + {ok, {some, Result}}; + + {_, {error, Msg}} -> + {error, Msg} + end. + +field(A, B) -> + gleam_stdlib:decode_field(A, B). + +element(A, B) -> + gleam_stdlib:decode_element(A, B). + +tuple2(A) -> + gleam_stdlib:decode_tuple2(A). + +typed_tuple2(Tup, Decode_first, Decode_second) -> + case gleam_stdlib:decode_tuple2(Tup) of + {error, Gleam@try_error} -> {error, Gleam@try_error}; + {ok, {First, Second}} -> + case Decode_first(First) of + {error, Gleam@try_error@1} -> {error, Gleam@try_error@1}; + {ok, A} -> + case Decode_second(Second) of + {error, Gleam@try_error@2} -> {error, Gleam@try_error@2}; + {ok, B} -> + {ok, {A, B}} + end + end + end. + +map(A) -> + gleam_stdlib:decode_map(A). + +any(Data, Decoders) -> + gleam@result:map_error( + gleam@list:find_map(Decoders, fun(Decoder) -> Decoder(Data) end), + fun(_) -> <<"Unexpected value"/utf8>> end + ). diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@float.erl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@float.erl new file mode 100644 index 0000000..48b5920 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@float.erl @@ -0,0 +1,103 @@ +-module(gleam@float). +-compile(no_auto_import). + +-export([parse/1, to_string/1, compare/2, min/2, max/2, ceiling/1, floor/1, round/1, truncate/1, absolute_value/1, power/2, square_root/1, negate/1, sum/1, product/1]). + +parse(A) -> + gleam_stdlib:parse_float(A). + +to_string(F) -> + gleam@string_builder:to_string(gleam@string_builder:from_float(F)). + +compare(A, B) -> + case A =:= B of + true -> + eq; + + false -> + case A < B of + true -> + lt; + + false -> + gt + end + end. + +min(A, B) -> + case A < B of + true -> + A; + + false -> + B + end. + +max(A, B) -> + case A > B of + true -> + A; + + false -> + B + end. + +ceiling(A) -> + math:ceil(A). + +floor(A) -> + math:floor(A). + +round(A) -> + erlang:round(A). + +truncate(A) -> + erlang:trunc(A). + +absolute_value(A) -> + erlang:abs(A). + +power(A, B) -> + math:pow(A, B). + +square_root(Number) -> + case Number < 0.0 of + true -> + {error, nil}; + + false -> + {ok, math:pow(Number, 0.5)} + end. + +negate(X) -> + -1.0 * X. + +sum(Numbers) -> + do_sum(Numbers, 0.0). + +do_sum(Numbers, Initial) -> + case Numbers of + [] -> + Initial; + + [X | Rest] -> + do_sum(Rest, X + Initial) + end. + +product(Numbers) -> + case Numbers of + [] -> + 0.0; + + _ -> + do_product(Numbers, 1.0) + end. + +do_product(Numbers, Initial) -> + case Numbers of + [] -> + Initial; + + [X | Rest] -> + do_product(Rest, X * Initial) + end. diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@function.erl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@function.erl new file mode 100644 index 0000000..1499e2d --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@function.erl @@ -0,0 +1,41 @@ +-module(gleam@function). +-compile(no_auto_import). + +-export([compose/2, curry2/1, curry3/1, curry4/1, curry5/1, curry6/1, flip/1, identity/1, rescue/1]). + +compose(Fun1, Fun2) -> + fun(A) -> Fun2(Fun1(A)) end. + +curry2(Fun) -> + fun(A) -> fun(B) -> Fun(A, B) end end. + +curry3(Fun) -> + fun(A) -> fun(B) -> fun(C) -> Fun(A, B, C) end end end. + +curry4(Fun) -> + fun(A) -> fun(B) -> fun(C) -> fun(D) -> Fun(A, B, C, D) end end end end. + +curry5(Fun) -> + fun(A) -> + fun(B) -> + fun(C) -> fun(D) -> fun(E) -> Fun(A, B, C, D, E) end end end + end + end. + +curry6(Fun) -> + fun(A) -> + fun(B) -> + fun(C) -> + fun(D) -> fun(E) -> fun(F) -> Fun(A, B, C, D, E, F) end end end + end + end + end. + +flip(Fun) -> + fun(B, A) -> Fun(A, B) end. + +identity(X) -> + X. + +rescue(A) -> + gleam_stdlib:rescue(A). diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@int.erl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@int.erl new file mode 100644 index 0000000..95e3d6f --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@int.erl @@ -0,0 +1,97 @@ +-module(gleam@int). +-compile(no_auto_import). + +-export([absolute_value/1, parse/1, to_string/1, to_base_string/2, to_float/1, compare/2, min/2, max/2, is_even/1, is_odd/1, negate/1, sum/1, product/1]). + +absolute_value(Num) -> + case Num >= 0 of + true -> + Num; + + false -> + Num * -1 + end. + +parse(A) -> + gleam_stdlib:parse_int(A). + +to_string(A) -> + erlang:integer_to_binary(A). + +to_base_string(A, B) -> + erlang:integer_to_binary(A, B). + +to_float(A) -> + erlang:float(A). + +compare(A, B) -> + case A =:= B of + true -> + eq; + + false -> + case A < B of + true -> + lt; + + false -> + gt + end + end. + +min(A, B) -> + case A < B of + true -> + A; + + false -> + B + end. + +max(A, B) -> + case A > B of + true -> + A; + + false -> + B + end. + +is_even(X) -> + (X rem 2) =:= 0. + +is_odd(X) -> + (X rem 2) /= 0. + +negate(X) -> + -1 * X. + +sum(Numbers) -> + do_sum(Numbers, 0). + +do_sum(Numbers, Initial) -> + case Numbers of + [] -> + Initial; + + [X | Rest] -> + do_sum(Rest, X + Initial) + end. + +product(Numbers) -> + case Numbers of + [] -> + 0; + + _ -> + do_product(Numbers, 1) + end. + +do_product(Numbers, Initial) -> + case Numbers of + [] -> + Initial; + + [X | Rest] -> + do_product(Rest, X * Initial) + end. diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@io.erl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@io.erl new file mode 100644 index 0000000..7009a03 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@io.erl @@ -0,0 +1,16 @@ +-module(gleam@io). +-compile(no_auto_import). + +-export([print/1, println/1, debug/1]). + +print(String) -> + io:fwrite(String, []), + nil. + +println(String) -> + io:fwrite(<<"~ts\n"/utf8>>, [String]), + nil. + +debug(Term) -> + io:fwrite(<<"~tp\n"/utf8>>, [Term]), + Term. diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@iterator.erl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@iterator.erl new file mode 100644 index 0000000..753afec --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@iterator.erl @@ -0,0 +1,200 @@ +-module(gleam@iterator). +-compile(no_auto_import). + +-export([unfold/2, repeatedly/1, repeat/1, from_list/1, fold/3, run/1, to_list/1, step/1, take/2, drop/2, map/2, append/2, flatten/1, flat_map/2, filter/2, cycle/1, range/2, find/2]). + +do_unfold(Initial, F) -> + fun() -> case F(Initial) of + {next, X, Acc} -> + {continue, X, do_unfold(Acc, F)}; + + done -> + stop + end end. + +unfold(Initial, F) -> + {iterator, do_unfold(Initial, F)}. + +repeatedly(F) -> + unfold(nil, fun(_) -> {next, F(), nil} end). + +repeat(X) -> + repeatedly(fun() -> X end). + +from_list(List) -> + Yield = fun(Acc) -> case Acc of + [] -> + done; + + [Head | Tail] -> + {next, Head, Tail} + end end, + unfold(List, Yield). + +do_fold(Continuation, Initial, F) -> + case Continuation() of + {continue, Element, Iterator} -> + do_fold(Iterator, F(Element, Initial), F); + + stop -> + Initial + end. + +fold(Iterator, Initial, F) -> + do_fold(erlang:element(2, Iterator), Initial, F). + +run(Iterator) -> + fold(Iterator, nil, fun(_, _) -> nil end). + +to_list(Iterator) -> + gleam@list:reverse(fold(Iterator, [], fun(E, Acc) -> [E | Acc] end)). + +step(Iterator) -> + case (erlang:element(2, Iterator))() of + stop -> + done; + + {continue, E, A} -> + {next, E, {iterator, A}} + end. + +do_take(Continuation, Desired, Acc) -> + case Desired > 0 of + true -> + case Continuation() of + {continue, Element, Iterator} -> + do_take(Iterator, Desired - 1, [Element | Acc]); + + stop -> + gleam@list:reverse(Acc) + end; + + false -> + gleam@list:reverse(Acc) + end. + +take(Iterator, Desired) -> + do_take(erlang:element(2, Iterator), Desired, []). + +do_drop(Continuation, Desired) -> + case Desired > 0 of + true -> + case Continuation() of + {continue, _, Iterator} -> + do_drop(Iterator, Desired - 1); + + stop -> + fun() -> stop end + end; + + false -> + Continuation + end. + +drop(Iterator, Desired) -> + {iterator, do_drop(erlang:element(2, Iterator), Desired)}. + +do_map(Continuation, F) -> + fun() -> case Continuation() of + {continue, E, Continuation@1} -> + {continue, F(E), do_map(Continuation@1, F)}; + + stop -> + stop + end end. + +map(Iterator, F) -> + {iterator, do_map(erlang:element(2, Iterator), F)}. + +do_append(First, Second) -> + fun() -> case First() of + {continue, E, First@1} -> + {continue, E, do_append(First@1, Second)}; + + stop -> + Second() + end end. + +append(First, Second) -> + {iterator, do_append(erlang:element(2, First), erlang:element(2, Second))}. + +do_flatten(Continuation) -> + fun() -> case Continuation() of + {continue, E, Continuation@1} -> + (do_append(erlang:element(2, E), do_flatten(Continuation@1)))(); + + stop -> + stop + end end. + +flatten(Iterator) -> + {iterator, do_flatten(erlang:element(2, Iterator))}. + +flat_map(Iterator, F) -> + flatten(map(Iterator, F)). + +do_filter(Continuation, Predicate) -> + fun() -> case Continuation() of + {continue, E, Iterator} -> + case Predicate(E) of + true -> + {continue, E, do_filter(Iterator, Predicate)}; + + false -> + (do_filter(Iterator, Predicate))() + end; + + stop -> + stop + end end. + +filter(Iterator, Predicate) -> + {iterator, do_filter(erlang:element(2, Iterator), Predicate)}. + +do_cycle(Next, Reset) -> + fun() -> case Next() of + {continue, E, Iterator} -> + {continue, E, do_cycle(Iterator, Reset)}; + + stop -> + (do_cycle(Reset, Reset))() + end end. + +cycle(Iterator) -> + {iterator, + do_cycle(erlang:element(2, Iterator), erlang:element(2, Iterator))}. + +do_range(Current, Limit, Inc) -> + case Current =:= Limit of + true -> + fun() -> stop end; + + false -> + fun() -> + {continue, Current, do_range(Current + Inc, Limit, Inc)} + end + end. + +range(Start, Stop) -> + {iterator, do_range(Start, Stop, case Start < Stop of + true -> + 1; + + false -> + -1 + end)}. + +find(Haystack, Is_desired) -> + case (erlang:element(2, Haystack))() of + {continue, Element, Continuation} -> + case Is_desired(Element) of + true -> + {ok, Element}; + + false -> + find({iterator, Continuation}, Is_desired) + end; + + stop -> + {error, nil} + end. diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@iterator_Iterator.hrl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@iterator_Iterator.hrl new file mode 100644 index 0000000..64bf931 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@iterator_Iterator.hrl @@ -0,0 +1 @@ +-record(iterator, {continuation}). diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@iterator_Next.hrl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@iterator_Next.hrl new file mode 100644 index 0000000..f012d50 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@iterator_Next.hrl @@ -0,0 +1 @@ +-record(next, {element, accumulator}). diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@list.erl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@list.erl new file mode 100644 index 0000000..787452e --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@list.erl @@ -0,0 +1,565 @@ +-module(gleam@list). +-compile(no_auto_import). + +-export([length/1, reverse/1, is_empty/1, contains/2, head/1, tail/1, filter/2, filter_map/2, map/2, index_map/2, try_map/2, drop/2, take/2, new/0, append/2, flatten/1, fold/3, fold_right/3, index_fold/3, try_fold/3, find/2, find_map/2, all/2, any/2, zip/2, strict_zip/2, unzip/1, intersperse/2, at/2, unique/1, sort/2, range/2, repeat/2, split/2, split_while/2, key_find/2, pop/2, pop_map/2, key_pop/2, key_set/3, each/2, partition/2, permutations/1]). + +length(A) -> + erlang:length(A). + +reverse(A) -> + lists:reverse(A). + +is_empty(List) -> + List =:= []. + +contains(List, Elem) -> + case List of + [] -> + false; + + [Head | Rest] -> + (Head =:= Elem) orelse contains(Rest, Elem) + end. + +head(List) -> + case List of + [] -> + {error, nil}; + + [X | _] -> + {ok, X} + end. + +tail(List) -> + case List of + [] -> + {error, nil}; + + [_ | Xs] -> + {ok, Xs} + end. + +do_filter(List, Fun, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [X | Xs] -> + New_acc = case Fun(X) of + true -> + [X | Acc]; + + false -> + Acc + end, + do_filter(Xs, Fun, New_acc) + end. + +filter(List, Predicate) -> + do_filter(List, Predicate, []). + +do_filter_map(List, Fun, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [X | Xs] -> + New_acc = case Fun(X) of + {ok, X@1} -> + [X@1 | Acc]; + + {error, _} -> + Acc + end, + do_filter_map(Xs, Fun, New_acc) + end. + +filter_map(List, Fun) -> + do_filter_map(List, Fun, []). + +do_map(List, Fun, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [X | Xs] -> + do_map(Xs, Fun, [Fun(X) | Acc]) + end. + +map(List, Fun) -> + do_map(List, Fun, []). + +do_index_map(List, Fun, Index, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [X | Xs] -> + do_index_map(Xs, Fun, Index + 1, [Fun(Index, X) | Acc]) + end. + +index_map(List, Fun) -> + do_index_map(List, Fun, 0, []). + +do_try_map(List, Fun, Acc) -> + case List of + [] -> + {ok, lists:reverse(Acc)}; + + [X | Xs] -> + case Fun(X) of + {ok, Y} -> + do_try_map(Xs, Fun, [Y | Acc]); + + {error, Error} -> + {error, Error} + end + end. + +try_map(List, Fun) -> + do_try_map(List, Fun, []). + +drop(List, N) -> + case N =< 0 of + true -> + List; + + false -> + case List of + [] -> + []; + + [_ | Xs] -> + drop(Xs, N - 1) + end + end. + +do_take(List, N, Acc) -> + case N =< 0 of + true -> + lists:reverse(Acc); + + false -> + case List of + [] -> + lists:reverse(Acc); + + [X | Xs] -> + do_take(Xs, N - 1, [X | Acc]) + end + end. + +take(List, N) -> + do_take(List, N, []). + +new() -> + []. + +append(A, B) -> + lists:append(A, B). + +do_flatten(Lists, Acc) -> + case Lists of + [] -> + Acc; + + [L | Rest] -> + do_flatten(Rest, lists:append(Acc, L)) + end. + +flatten(Lists) -> + do_flatten(Lists, []). + +fold(List, Initial, Fun) -> + case List of + [] -> + Initial; + + [X | Rest] -> + fold(Rest, Fun(X, Initial), Fun) + end. + +fold_right(List, Initial, Fun) -> + case List of + [] -> + Initial; + + [X | Rest] -> + Fun(X, fold_right(Rest, Initial, Fun)) + end. + +do_index_fold(Over, Acc, With, Index) -> + case Over of + [] -> + Acc; + + [First | Rest] -> + do_index_fold(Rest, With(Index, First, Acc), With, Index + 1) + end. + +index_fold(Over, Initial, Fun) -> + do_index_fold(Over, Initial, Fun, 0). + +try_fold(Collection, Accumulator, Fun) -> + case Collection of + [] -> + {ok, Accumulator}; + + [First | Rest] -> + case Fun(First, Accumulator) of + {ok, Next_accumulator} -> + try_fold(Rest, Next_accumulator, Fun); + + {error, Err} -> + {error, Err} + end + end. + +find(Haystack, Is_desired) -> + case Haystack of + [] -> + {error, nil}; + + [X | Rest] -> + case Is_desired(X) of + true -> + {ok, X}; + + _ -> + find(Rest, Is_desired) + end + end. + +find_map(Haystack, Fun) -> + case Haystack of + [] -> + {error, nil}; + + [X | Rest] -> + case Fun(X) of + {ok, X@1} -> + {ok, X@1}; + + _ -> + find_map(Rest, Fun) + end + end. + +all(List, Predicate) -> + case List of + [] -> + true; + + [X | Rest] -> + case Predicate(X) of + true -> + all(Rest, Predicate); + + _ -> + false + end + end. + +any(List, Predicate) -> + case List of + [] -> + false; + + [X | Rest] -> + case Predicate(X) of + false -> + any(Rest, Predicate); + + _ -> + true + end + end. + +zip(Xs, Ys) -> + case {Xs, Ys} of + {[], _} -> + []; + + {_, []} -> + []; + + {[X | Xs@1], [Y | Ys@1]} -> + [{X, Y} | zip(Xs@1, Ys@1)] + end. + +strict_zip(L1, L2) -> + case erlang:length(L1) =:= erlang:length(L2) of + true -> + {ok, zip(L1, L2)}; + + false -> + {error, length_mismatch} + end. + +do_unzip(Input, Xs, Ys) -> + case Input of + [] -> + {lists:reverse(Xs), lists:reverse(Ys)}; + + [{X, Y} | Rest] -> + do_unzip(Rest, [X | Xs], [Y | Ys]) + end. + +unzip(Input) -> + do_unzip(Input, [], []). + +intersperse(List, Elem) -> + case List of + [] -> + List; + + [_] -> + List; + + [X | Rest] -> + [X, Elem | intersperse(Rest, Elem)] + end. + +at(List, Index) -> + case Index < 0 of + true -> + {error, nil}; + + false -> + case List of + [] -> + {error, nil}; + + [X | Rest] -> + case Index =:= 0 of + true -> + {ok, X}; + + false -> + at(Rest, Index - 1) + end + end + end. + +unique(List) -> + case List of + [] -> + []; + + [X | Rest] -> + [X | unique(filter(Rest, fun(Y) -> Y /= X end))] + end. + +merge_sort(A, B, Compare) -> + case {A, B} of + {[], _} -> + B; + + {_, []} -> + A; + + {[Ax | Ar], [Bx | Br]} -> + case Compare(Ax, Bx) of + lt -> + [Ax | merge_sort(Ar, B, Compare)]; + + _ -> + [Bx | merge_sort(A, Br, Compare)] + end + end. + +do_sort(List, Compare, List_length) -> + case List_length < 2 of + true -> + List; + + false -> + Split_length = List_length div 2, + A_list = take(List, Split_length), + B_list = drop(List, Split_length), + merge_sort( + do_sort(A_list, Compare, Split_length), + do_sort(B_list, Compare, List_length - Split_length), + Compare + ) + end. + +sort(List, Compare) -> + do_sort(List, Compare, erlang:length(List)). + +range(Start, Stop) -> + case gleam@int:compare(Start, Stop) of + eq -> + []; + + gt -> + [Start | range(Start - 1, Stop)]; + + lt -> + [Start | range(Start + 1, Stop)] + end. + +do_repeat(A, Times, Acc) -> + case Times =< 0 of + true -> + Acc; + + false -> + do_repeat(A, Times - 1, [A | Acc]) + end. + +repeat(A, Times) -> + do_repeat(A, Times, []). + +do_split(List, N, Taken) -> + case N =< 0 of + true -> + {lists:reverse(Taken), List}; + + false -> + case List of + [] -> + {lists:reverse(Taken), []}; + + [X | Xs] -> + do_split(Xs, N - 1, [X | Taken]) + end + end. + +split(List, Index) -> + do_split(List, Index, []). + +do_split_while(List, F, Acc) -> + case List of + [] -> + {lists:reverse(Acc), []}; + + [X | Xs] -> + case F(X) of + false -> + {lists:reverse(Acc), List}; + + _ -> + do_split_while(Xs, F, [X | Acc]) + end + end. + +split_while(List, Predicate) -> + do_split_while(List, Predicate, []). + +key_find(Keyword_list, Desired_key) -> + find_map(Keyword_list, fun(Keyword) -> {Key, Value} = Keyword, + case Key =:= Desired_key of + true -> + {ok, Value}; + + false -> + {error, nil} + end end). + +do_pop(Haystack, Predicate, Checked) -> + case Haystack of + [] -> + {error, nil}; + + [X | Rest] -> + case Predicate(X) of + true -> + {ok, {X, lists:append(lists:reverse(Checked), Rest)}}; + + false -> + do_pop(Rest, Predicate, [X | Checked]) + end + end. + +pop(Haystack, Is_desired) -> + do_pop(Haystack, Is_desired, []). + +do_pop_map(Haystack, Mapper, Checked) -> + case Haystack of + [] -> + {error, nil}; + + [X | Rest] -> + case Mapper(X) of + {ok, Y} -> + {ok, {Y, lists:append(lists:reverse(Checked), Rest)}}; + + {error, _} -> + do_pop_map(Rest, Mapper, [X | Checked]) + end + end. + +pop_map(Haystack, Is_desired) -> + do_pop_map(Haystack, Is_desired, []). + +key_pop(Haystack, Key) -> + pop_map(Haystack, fun(Entry) -> {K, V} = Entry, + case K of + K@1 when K@1 =:= Key -> + {ok, V}; + + _ -> + {error, nil} + end end). + +key_set(List, Key, Value) -> + case List of + [] -> + [{Key, Value}]; + + [{K, _} | Rest] when K =:= Key -> + [{Key, Value} | Rest]; + + [First | Rest@1] -> + [First | key_set(Rest@1, Key, Value)] + end. + +each(List, F) -> + case List of + [] -> + nil; + + [X | Xs] -> + F(X), + each(Xs, F) + end. + +do_partition(List, Categorise, Trues, Falses) -> + case List of + [] -> + {lists:reverse(Trues), lists:reverse(Falses)}; + + [X | Xs] -> + case Categorise(X) of + true -> + do_partition(Xs, Categorise, [X | Trues], Falses); + + false -> + do_partition(Xs, Categorise, Trues, [X | Falses]) + end + end. + +partition(List, Categorise) -> + do_partition(List, Categorise, [], []). + +permutations(L) -> + case L of + [] -> + [[]]; + + _ -> + flatten( + map( + L, + fun(X) -> + map( + permutations(filter(L, fun(Y) -> Y /= X end)), + fun(Gleam@capture_variable) -> + lists:append([X], Gleam@capture_variable) + end + ) + end + ) + ) + end. diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@map.erl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@map.erl new file mode 100644 index 0000000..54cfad3 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@map.erl @@ -0,0 +1,64 @@ +-module(gleam@map). +-compile(no_auto_import). + +-export([size/1, to_list/1, from_list/1, has_key/2, new/0, get/2, insert/3, map_values/2, keys/1, values/1, filter/2, take/2, merge/2, delete/2, drop/2, update/3, fold/3]). + +size(A) -> + maps:size(A). + +to_list(A) -> + maps:to_list(A). + +from_list(A) -> + maps:from_list(A). + +has_key(Map, Key) -> + maps:is_key(Key, Map). + +new() -> + maps:new(). + +get(A, B) -> + gleam_stdlib:map_get(A, B). + +insert(Map, Key, Value) -> + maps:put(Key, Value, Map). + +map_values(Map, Fun) -> + maps:map(Fun, Map). + +keys(A) -> + maps:keys(A). + +values(A) -> + maps:values(A). + +filter(Map, Property) -> + maps:filter(Property, Map). + +take(Map, Desired_keys) -> + maps:with(Desired_keys, Map). + +merge(A, B) -> + maps:merge(A, B). + +delete(Map, Key) -> + maps:remove(Key, Map). + +drop(Map, Disallowed_keys) -> + gleam@list:fold(Disallowed_keys, Map, fun(Key, Acc) -> delete(Acc, Key) end). + +update(Map, Key, Fun) -> + insert(Map, Key, Fun(gleam_stdlib:map_get(Map, Key))). + +do_fold(List, Initial, Fun) -> + case List of + [] -> + Initial; + + [{K, V} | Tail] -> + do_fold(Tail, Fun(K, V, Initial), Fun) + end. + +fold(Map, Initial, Fun) -> + do_fold(maps:to_list(Map), Initial, Fun). diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@option.erl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@option.erl new file mode 100644 index 0000000..ac93fbc --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@option.erl @@ -0,0 +1,73 @@ +-module(gleam@option). +-compile(no_auto_import). + +-export([is_some/1, is_none/1, to_result/2, from_result/1, unwrap/2, map/2, flatten/1, then/2, 'or'/2]). + +is_some(Option) -> + Option /= none. + +is_none(Option) -> + Option =:= none. + +to_result(Option, E) -> + case Option of + {some, A} -> + {ok, A}; + + _ -> + {error, E} + end. + +from_result(Result) -> + case Result of + {ok, A} -> + {some, A}; + + _ -> + none + end. + +unwrap(Option, Default) -> + case Option of + {some, X} -> + X; + + none -> + Default + end. + +map(Option, Fun) -> + case Option of + {some, X} -> + {some, Fun(X)}; + + none -> + none + end. + +flatten(Option) -> + case Option of + {some, X} -> + X; + + none -> + none + end. + +then(Option, Fun) -> + case Option of + {some, X} -> + Fun(X); + + none -> + none + end. + +'or'(First, Second) -> + case First of + {some, _} -> + First; + + none -> + Second + end. diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@order.erl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@order.erl new file mode 100644 index 0000000..6306636 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@order.erl @@ -0,0 +1,67 @@ +-module(gleam@order). +-compile(no_auto_import). + +-export([reverse/1, to_int/1, compare/2, max/2, min/2]). + +reverse(Order) -> + case Order of + lt -> + gt; + + eq -> + eq; + + gt -> + lt + end. + +to_int(Order) -> + case Order of + lt -> + -1; + + eq -> + 0; + + gt -> + 1 + end. + +compare(A, B) -> + case {A, B} of + {X, Y} when X =:= Y -> + eq; + + {lt, _} -> + lt; + + {eq, gt} -> + lt; + + {_, _} -> + gt + end. + +max(A, B) -> + case {A, B} of + {gt, _} -> + gt; + + {eq, lt} -> + eq; + + {_, _} -> + B + end. + +min(A, B) -> + case {A, B} of + {lt, _} -> + lt; + + {eq, gt} -> + eq; + + {_, _} -> + B + end. diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@os.erl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@os.erl new file mode 100644 index 0000000..c24e6b6 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@os.erl @@ -0,0 +1,32 @@ +-module(gleam@os). +-compile(no_auto_import). + +-export([get_env/0, insert_env/2, delete_env/1, system_time/1, erlang_timestamp/0]). + +get_env() -> + gleam@map:from_list( + gleam@list:map( + os:getenv(), + fun(Char_list) -> + {ok, Value} = gleam@string:split_once( + erlang:list_to_binary(Char_list), + <<"="/utf8>> + ), + Value + end + ) + ). + +insert_env(Key, Value) -> + os:putenv(erlang:binary_to_list(Key), erlang:binary_to_list(Value)), + nil. + +delete_env(Key) -> + os:unsetenv(erlang:binary_to_list(Key)), + nil. + +system_time(A) -> + os:system_time(A). + +erlang_timestamp() -> + os:timestamp(). diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@pair.erl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@pair.erl new file mode 100644 index 0000000..3c07918 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@pair.erl @@ -0,0 +1,24 @@ +-module(gleam@pair). +-compile(no_auto_import). + +-export([first/1, second/1, swap/1, map_first/2, map_second/2]). + +first(Pair) -> + {A, _} = Pair, + A. + +second(Pair) -> + {_, A} = Pair, + A. + +swap(Pair) -> + {A, B} = Pair, + {B, A}. + +map_first(Pair, Fun) -> + {A, B} = Pair, + {Fun(A), B}. + +map_second(Pair, Fun) -> + {A, B} = Pair, + {A, Fun(B)}. diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@queue.erl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@queue.erl new file mode 100644 index 0000000..8681d6b --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@queue.erl @@ -0,0 +1,101 @@ +-module(gleam@queue). +-compile(no_auto_import). + +-export([new/0, from_list/1, to_list/1, is_empty/1, length/1, push_back/2, push_front/2, pop_back/1, pop_front/1, reverse/1, is_logically_equal/3, is_equal/2]). + +new() -> + {queue, [], []}. + +from_list(List) -> + {queue, [], List}. + +to_list(Queue) -> + gleam@list:append( + erlang:element(3, Queue), + gleam@list:reverse(erlang:element(2, Queue)) + ). + +is_empty(Queue) -> + (erlang:element(2, Queue) =:= []) andalso (erlang:element(3, Queue) =:= []). + +length(Queue) -> + gleam@list:length(erlang:element(2, Queue)) + gleam@list:length( + erlang:element(3, Queue) + ). + +push_back(Queue, Item) -> + {queue, [Item | erlang:element(2, Queue)], erlang:element(3, Queue)}. + +push_front(Queue, Item) -> + {queue, erlang:element(2, Queue), [Item | erlang:element(3, Queue)]}. + +pop_back(Queue) -> + case Queue of + {queue, [], []} -> + {error, nil}; + + {queue, [], Out} -> + pop_back({queue, gleam@list:reverse(Out), []}); + + {queue, [First | Rest], Out@1} -> + Queue@1 = {queue, Rest, Out@1}, + {ok, {First, Queue@1}} + end. + +pop_front(Queue) -> + case Queue of + {queue, [], []} -> + {error, nil}; + + {queue, In, []} -> + pop_front({queue, [], gleam@list:reverse(In)}); + + {queue, In@1, [First | Rest]} -> + Queue@1 = {queue, In@1, Rest}, + {ok, {First, Queue@1}} + end. + +reverse(Queue) -> + {queue, erlang:element(3, Queue), erlang:element(2, Queue)}. + +check_equal(Xs, X_tail, Ys, Y_tail, Eq) -> + case {Xs, X_tail, Ys, Y_tail} of + {[], [], [], []} -> + true; + + {[X | Xs@1], _, [Y | Ys@1], _} -> + case Eq(X, Y) of + false -> + false; + + true -> + check_equal(Xs@1, X_tail, Ys@1, Y_tail, Eq) + end; + + {[], [_ | _], _, _} -> + check_equal(gleam@list:reverse(X_tail), [], Ys, Y_tail, Eq); + + {_, _, [], [_ | _]} -> + check_equal(Xs, X_tail, gleam@list:reverse(Y_tail), [], Eq); + + {_, _, _, _} -> + false + end. + +is_logically_equal(A, B, Element_is_equal) -> + check_equal( + erlang:element(3, A), + erlang:element(2, A), + erlang:element(3, B), + erlang:element(2, B), + Element_is_equal + ). + +is_equal(A, B) -> + check_equal( + erlang:element(3, A), + erlang:element(2, A), + erlang:element(3, B), + erlang:element(2, B), + fun(A@1, B@1) -> A@1 =:= B@1 end + ). diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@queue_Queue.hrl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@queue_Queue.hrl new file mode 100644 index 0000000..c68023a --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@queue_Queue.hrl @@ -0,0 +1 @@ +-record(queue, {in, out}). diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@regex.erl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@regex.erl new file mode 100644 index 0000000..e5f0a47 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@regex.erl @@ -0,0 +1,19 @@ +-module(gleam@regex). +-compile(no_auto_import). + +-export([compile/2, from_string/1, check/2, split/2, scan/2]). + +compile(A, B) -> + gleam_stdlib:compile_regex(A, B). + +from_string(Pattern) -> + gleam_stdlib:compile_regex(Pattern, {options, false, false}). + +check(A, B) -> + gleam_stdlib:regex_match(A, B). + +split(A, B) -> + gleam_stdlib:regex_split(A, B). + +scan(A, B) -> + gleam_stdlib:regex_scan(A, B). diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@regex_CompileError.hrl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@regex_CompileError.hrl new file mode 100644 index 0000000..368b2cf --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@regex_CompileError.hrl @@ -0,0 +1 @@ +-record(compile_error, {error, byte_index}). diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@regex_Match.hrl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@regex_Match.hrl new file mode 100644 index 0000000..d5f59ae --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@regex_Match.hrl @@ -0,0 +1 @@ +-record(match, {content, byte_index, submatches}). diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@regex_Options.hrl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@regex_Options.hrl new file mode 100644 index 0000000..74a91be --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@regex_Options.hrl @@ -0,0 +1 @@ +-record(options, {case_insensitive, multi_line}). diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@result.erl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@result.erl new file mode 100644 index 0000000..1842e0c --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@result.erl @@ -0,0 +1,103 @@ +-module(gleam@result). +-compile(no_auto_import). + +-export([is_ok/1, is_error/1, map/2, map_error/2, flatten/1, then/2, unwrap/2, lazy_unwrap/2, nil_error/1, 'or'/2, lazy_or/2, all/1, replace_error/2]). + +is_ok(Result) -> + case Result of + {error, _} -> + false; + + {ok, _} -> + true + end. + +is_error(Result) -> + case Result of + {ok, _} -> + false; + + {error, _} -> + true + end. + +map(Result, Fun) -> + case Result of + {ok, X} -> + {ok, Fun(X)}; + + {error, E} -> + {error, E} + end. + +map_error(Result, Fun) -> + case Result of + {ok, X} -> + {ok, X}; + + {error, Error} -> + {error, Fun(Error)} + end. + +flatten(Result) -> + case Result of + {ok, X} -> + X; + + {error, Error} -> + {error, Error} + end. + +then(Result, Fun) -> + case Result of + {ok, X} -> + Fun(X); + + {error, E} -> + {error, E} + end. + +unwrap(Result, Default) -> + case Result of + {ok, V} -> + V; + + {error, _} -> + Default + end. + +lazy_unwrap(Result, Default) -> + case Result of + {ok, V} -> + V; + + {error, _} -> + Default() + end. + +nil_error(Result) -> + map_error(Result, fun(_) -> nil end). + +'or'(First, Second) -> + case First of + {ok, _} -> + First; + + {error, _} -> + Second + end. + +lazy_or(First, Second) -> + case First of + {ok, _} -> + First; + + {error, _} -> + Second() + end. + +all(Results) -> + gleam@list:try_map(Results, fun(X) -> X end). + +replace_error(Result, Error) -> + map_error(Result, fun(_) -> Error end). diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@set.erl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@set.erl new file mode 100644 index 0000000..48704fc --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@set.erl @@ -0,0 +1,62 @@ +-module(gleam@set). +-compile(no_auto_import). + +-export([new/0, size/1, insert/2, contains/2, delete/2, to_list/1, from_list/1, fold/3, filter/2, take/2, union/2, intersection/2]). + +new() -> + {set, gleam@map:new()}. + +size(Set) -> + gleam@map:size(erlang:element(2, Set)). + +insert(Set, Member) -> + {set, gleam@map:insert(erlang:element(2, Set), Member, [])}. + +contains(Set, Member) -> + gleam@result:is_ok(gleam@map:get(erlang:element(2, Set), Member)). + +delete(Set, Member) -> + {set, gleam@map:delete(erlang:element(2, Set), Member)}. + +to_list(Set) -> + gleam@map:keys(erlang:element(2, Set)). + +from_list(Members) -> + Map = gleam@list:fold( + Members, + gleam@map:new(), + fun(K, M) -> gleam@map:insert(M, K, []) end + ), + {set, Map}. + +fold(Set, Initial, Reducer) -> + gleam@map:fold( + erlang:element(2, Set), + Initial, + fun(K, _, A) -> Reducer(K, A) end + ). + +filter(Set, Property) -> + {set, + gleam@map:filter(erlang:element(2, Set), fun(M, _) -> Property(M) end)}. + +take(Set, Desired) -> + {set, gleam@map:take(erlang:element(2, Set), Desired)}. + +order(First, Second) -> + case gleam@map:size(erlang:element(2, First)) + > gleam@map:size(erlang:element(2, Second)) of + true -> + {First, Second}; + + false -> + {Second, First} + end. + +union(First, Second) -> + {Larger, Smaller} = order(First, Second), + fold(Smaller, Larger, fun(M, A) -> insert(A, M) end). + +intersection(First, Second) -> + {Larger, Smaller} = order(First, Second), + take(Larger, to_list(Smaller)). diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@set_Set.hrl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@set_Set.hrl new file mode 100644 index 0000000..2207177 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@set_Set.hrl @@ -0,0 +1 @@ +-record(set, {map}). diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@should.erl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@should.erl new file mode 100644 index 0000000..5d5db91 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@should.erl @@ -0,0 +1,25 @@ +-module(gleam@should). +-compile(no_auto_import). + +-export([equal/2, not_equal/2, be_true/1, be_false/1, be_ok/1, be_error/1, fail/0]). + +equal(A, B) -> + gleam_stdlib:should_equal(A, B). + +not_equal(A, B) -> + gleam_stdlib:should_not_equal(A, B). + +be_true(Actual) -> + gleam_stdlib:should_equal(Actual, true). + +be_false(Actual) -> + gleam_stdlib:should_equal(Actual, false). + +be_ok(A) -> + gleam_stdlib:should_be_ok(A). + +be_error(A) -> + gleam_stdlib:should_be_error(A). + +fail() -> + be_true(false). diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@string.erl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@string.erl new file mode 100644 index 0000000..58397bb --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@string.erl @@ -0,0 +1,162 @@ +-module(gleam@string). +-compile(no_auto_import). + +-export([is_empty/1, length/1, reverse/1, replace/3, lowercase/1, uppercase/1, compare/2, slice/3, drop_left/2, drop_right/2, contains/2, starts_with/2, ends_with/2, split/2, split_once/2, append/2, concat/1, repeat/2, join/2, pad_left/3, pad_right/3, trim/1, trim_left/1, trim_right/1, pop_grapheme/1, to_graphemes/1, utf_codepoint/1]). + +is_empty(Str) -> + Str =:= <<""/utf8>>. + +length(A) -> + string:length(A). + +reverse(String) -> + gleam@string_builder:to_string( + gleam@string_builder:reverse(gleam@string_builder:from_string(String)) + ). + +replace(String, Pattern, Substitute) -> + gleam@string_builder:to_string( + gleam@string_builder:replace( + gleam@string_builder:from_string(String), + Pattern, + Substitute + ) + ). + +lowercase(A) -> + string:lowercase(A). + +uppercase(A) -> + string:uppercase(A). + +compare(A, B) -> + gleam_stdlib:compare_strings(A, B). + +slice(String, Idx, Len) -> + case Len < 0 of + true -> + <<""/utf8>>; + + false -> + case Idx < 0 of + true -> + Translated_idx = string:length(String) + Idx, + case Translated_idx < 0 of + true -> + <<""/utf8>>; + + false -> + string:slice(String, Translated_idx, Len) + end; + + false -> + string:slice(String, Idx, Len) + end + end. + +drop_left(String, Num_graphemes) -> + case Num_graphemes < 0 of + true -> + String; + + false -> + slice(String, Num_graphemes, string:length(String) - Num_graphemes) + end. + +drop_right(String, Num_graphemes) -> + case Num_graphemes < 0 of + true -> + String; + + false -> + slice(String, 0, string:length(String) - Num_graphemes) + end. + +contains(Haystack, Needle) -> + gleam@result:is_error(gleam@dynamic:atom(string:find(Haystack, Needle))). + +starts_with(A, B) -> + gleam_stdlib:string_starts_with(A, B). + +ends_with(A, B) -> + gleam_stdlib:string_ends_with(A, B). + +split(X, Substring) -> + gleam@list:map( + gleam@string_builder:split( + gleam@string_builder:from_string(X), + Substring + ), + fun gleam@string_builder:to_string/1 + ). + +split_once(X, Substring) -> + case string:split(X, Substring) of + [First, Rest] -> + {ok, {First, Rest}}; + + _ -> + {error, nil} + end. + +append(First, Second) -> + gleam@string_builder:to_string( + gleam@string_builder:append( + gleam@string_builder:from_string(First), + Second + ) + ). + +concat(Strings) -> + gleam@string_builder:to_string(gleam@string_builder:from_strings(Strings)). + +repeat(String, Times) -> + concat(gleam@iterator:take(gleam@iterator:repeat(String), Times)). + +join(Strings, Separator) -> + concat(gleam@list:intersperse(Strings, Separator)). + +pad_left(String, Length, Pad_string) -> + gleam_stdlib:string_pad(String, Length, leading, Pad_string). + +pad_right(String, Length, Pad_string) -> + gleam_stdlib:string_pad(String, Length, trailing, Pad_string). + +trim(String) -> + string:trim(String, both). + +trim_left(String) -> + string:trim(String, leading). + +trim_right(String) -> + string:trim(String, trailing). + +pop_grapheme(A) -> + gleam_stdlib:string_pop_grapheme(A). + +to_graphemes(String) -> + case gleam_stdlib:string_pop_grapheme(String) of + {ok, {Grapheme, Rest}} -> + [Grapheme | to_graphemes(Rest)]; + + _ -> + [] + end. + +utf_codepoint(Value) -> + case Value of + I when I > 1114111 -> + {error, nil}; + + 65534 -> + {error, nil}; + + 65535 -> + {error, nil}; + + I@1 when (I@1 >= 55296) andalso (I@1 =< 57343) -> + {error, nil}; + + I@2 -> + {ok, gleam_stdlib:identity(I@2)} + end. diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@string_builder.erl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@string_builder.erl new file mode 100644 index 0000000..a732733 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@string_builder.erl @@ -0,0 +1,55 @@ +-module(gleam@string_builder). +-compile(no_auto_import). + +-export([prepend/2, append/2, prepend_builder/2, append_builder/2, from_strings/1, concat/1, from_string/1, to_string/1, byte_size/1, from_float/1, lowercase/1, uppercase/1, reverse/1, split/2, replace/3, is_equal/2, is_empty/1]). + +prepend(A, B) -> + gleam_stdlib:iodata_prepend(A, B). + +append(A, B) -> + gleam_stdlib:iodata_append(A, B). + +prepend_builder(A, B) -> + gleam_stdlib:iodata_prepend(A, B). + +append_builder(A, B) -> + gleam_stdlib:iodata_append(A, B). + +from_strings(A) -> + gleam_stdlib:identity(A). + +concat(A) -> + gleam_stdlib:identity(A). + +from_string(A) -> + gleam_stdlib:identity(A). + +to_string(A) -> + erlang:iolist_to_binary(A). + +byte_size(A) -> + erlang:iolist_size(A). + +from_float(A) -> + io_lib_format:fwrite_g(A). + +lowercase(A) -> + string:lowercase(A). + +uppercase(A) -> + string:uppercase(A). + +reverse(A) -> + string:reverse(A). + +split(Iodata, Pattern) -> + string:split(Iodata, Pattern, all). + +replace(Iodata, Pattern, Substitute) -> + string:replace(Iodata, Pattern, Substitute, all). + +is_equal(A, B) -> + string:equal(A, B). + +is_empty(A) -> + string:is_empty(A). diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@uri.erl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@uri.erl new file mode 100644 index 0000000..6006dc4 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@uri.erl @@ -0,0 +1,240 @@ +-module(gleam@uri). +-compile(no_auto_import). + +-export([erl_parse/1, parse/1, parse_query/1, query_to_string/1, percent_encode/1, percent_decode/1, path_segments/1, to_string/1, origin/1, merge/2]). + +erl_parse(A) -> + uri_string:parse(A). + +parse(String) -> + case gleam@result:nil_error(gleam@dynamic:map(uri_string:parse(String))) of + {error, Gleam@try_error} -> {error, Gleam@try_error}; + {ok, Uri_map} -> + Get = fun(K, Decode_type) -> + gleam@option:from_result( + gleam@result:then( + gleam@map:get(Uri_map, gleam@dynamic:from(K)), + gleam@function:compose( + Decode_type, + fun gleam@result:nil_error/1 + ) + ) + ) + end, + Uri = {uri, + Get(scheme, fun gleam@dynamic:string/1), + Get(userinfo, fun gleam@dynamic:string/1), + Get(host, fun gleam@dynamic:string/1), + Get(port, fun gleam@dynamic:int/1), + gleam@option:unwrap( + Get(path, fun gleam@dynamic:string/1), + <<""/utf8>> + ), + Get(query, fun gleam@dynamic:string/1), + Get(fragment, fun gleam@dynamic:string/1)}, + {ok, Uri} + end. + +parse_query(Query) -> + Bool_value = fun(X) -> + gleam@result:map(gleam@dynamic:bool(X), fun(_) -> <<""/utf8>> end) + end, + Query_param = fun(Gleam@capture_variable) -> + gleam@dynamic:typed_tuple2( + Gleam@capture_variable, + fun gleam@dynamic:string/1, + fun(Gleam@capture_variable@1) -> + gleam@dynamic:any( + Gleam@capture_variable@1, + [fun gleam@dynamic:string/1, Bool_value] + ) + end + ) + end, + gleam@result:nil_error( + gleam@dynamic:typed_list(uri_string:dissect_query(Query), Query_param) + ). + +query_to_string(Query) -> + gleam@result:unwrap( + gleam@dynamic:string( + uri_string:compose_query(Query, [{encoding, utf8}]) + ), + <<""/utf8>> + ). + +percent_encode(Value) -> + gleam@string:replace( + query_to_string([{<<"k"/utf8>>, Value}]), + <<"k="/utf8>>, + <<""/utf8>> + ). + +percent_decode(Value) -> + gleam@result:map( + gleam@result:then( + parse_query(gleam@string:concat([<<"k="/utf8>>, Value])), + fun gleam@list:head/1 + ), + fun gleam@pair:second/1 + ). + +do_remove_dot_segments(Input, Accumulator) -> + case Input of + [] -> + gleam@list:reverse(Accumulator); + + [Segment | Rest] -> + Accumulator@5 = case {Segment, Accumulator} of + {<<""/utf8>>, Accumulator@1} -> + Accumulator@1; + + {<<"."/utf8>>, Accumulator@2} -> + Accumulator@2; + + {<<".."/utf8>>, []} -> + []; + + {<<".."/utf8>>, [_ | Accumulator@3]} -> + Accumulator@3; + + {Segment@1, Accumulator@4} -> + [Segment@1 | Accumulator@4] + end, + do_remove_dot_segments(Rest, Accumulator@5) + end. + +remove_dot_segments(Input) -> + do_remove_dot_segments(Input, []). + +path_segments(Path) -> + remove_dot_segments(gleam@string:split(Path, <<"/"/utf8>>)). + +to_string(Uri) -> + Field = fun(Key, Value) -> case Value of + {some, V} -> + {ok, {Key, gleam@dynamic:from(V)}}; + + none -> + {error, nil} + end end, + gleam@result:unwrap( + gleam@dynamic:string( + uri_string:recompose( + gleam@map:from_list( + gleam@list:filter_map( + [Field(scheme, erlang:element(2, Uri)), + Field(userinfo, erlang:element(3, Uri)), + Field(host, erlang:element(4, Uri)), + Field(port, erlang:element(5, Uri)), + Field(path, {some, erlang:element(6, Uri)}), + Field(query, erlang:element(7, Uri)), + Field(fragment, erlang:element(8, Uri))], + fun(X) -> X end + ) + ) + ) + ), + <<""/utf8>> + ). + +origin(Uri) -> + {uri, Scheme, _, Host, Port, _, _, _} = Uri, + case Scheme of + {some, <<"https"/utf8>>} -> + Origin = {uri, Scheme, none, Host, Port, <<""/utf8>>, none, none}, + {ok, to_string(Origin)}; + + {some, <<"http"/utf8>>} -> + Origin = {uri, Scheme, none, Host, Port, <<""/utf8>>, none, none}, + {ok, to_string(Origin)}; + + _ -> + {error, nil} + end. + +drop_last(Elements) -> + gleam@list:take(Elements, gleam@list:length(Elements) - 1). + +join_segments(Segments) -> + gleam@string:join([<<""/utf8>> | Segments], <<"/"/utf8>>). + +merge(Base, Relative) -> + case Base of + {uri, {some, _}, _, {some, _}, _, _, _, _} -> + case Relative of + {uri, _, _, {some, _}, _, _, _, _} -> + Path = join_segments( + remove_dot_segments( + gleam@string:split( + erlang:element(6, Relative), + <<"/"/utf8>> + ) + ) + ), + Resolved = {uri, + gleam@option:'or'( + erlang:element(2, Relative), + erlang:element(2, Base) + ), + none, + erlang:element(4, Relative), + erlang:element(5, Relative), + Path, + erlang:element(7, Relative), + erlang:element(8, Relative)}, + {ok, Resolved}; + + {uri, none, _, none, _, _, _, _} -> + {New_path, New_query} = case erlang:element(6, Relative) of + <<""/utf8>> -> + {erlang:element(6, Base), + gleam@option:'or'( + erlang:element(7, Relative), + erlang:element(7, Base) + )}; + + _ -> + Path_segments = case gleam@string:starts_with( + erlang:element(6, Relative), + <<"/"/utf8>> + ) of + true -> + gleam@string:split( + erlang:element(6, Relative), + <<"/"/utf8>> + ); + + false -> + gleam@list:append( + drop_last( + gleam@string:split( + erlang:element(6, Base), + <<"/"/utf8>> + ) + ), + gleam@string:split( + erlang:element(6, Relative), + <<"/"/utf8>> + ) + ) + end, + Path@1 = join_segments( + remove_dot_segments(Path_segments) + ), + {Path@1, erlang:element(7, Relative)} + end, + Resolved@1 = {uri, + erlang:element(2, Base), + none, + erlang:element(4, Base), + erlang:element(5, Base), + New_path, + New_query, + erlang:element(8, Relative)}, + {ok, Resolved@1} + end; + + _ -> + {error, nil} + end. diff --git a/gleam_providers/deps/gleam_stdlib/gen/src/gleam@uri_Uri.hrl b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@uri_Uri.hrl new file mode 100644 index 0000000..0d17e1e --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/src/gleam@uri_Uri.hrl @@ -0,0 +1 @@ +-record(uri, {scheme, userinfo, host, port, path, query, fragment}). diff --git a/gleam_providers/deps/gleam_stdlib/gen/test/gleam@atom_test.erl b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@atom_test.erl new file mode 100644 index 0000000..91a16bd --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@atom_test.erl @@ -0,0 +1,43 @@ +-module(gleam@atom_test). +-compile(no_auto_import). + +-export([from_string_test/0, create_from_string_test/0, to_string_test/0]). + +from_string_test() -> + gleam@should:be_ok(gleam@atom:from_string(<<"ok"/utf8>>)), + gleam@should:be_ok(gleam@atom:from_string(<<"expect"/utf8>>)), + gleam@should:equal( + gleam@atom:from_string( + <<"this is not an atom we have seen before"/utf8>> + ), + {error, atom_not_loaded} + ). + +create_from_string_test() -> + gleam@should:equal( + {ok, gleam@atom:create_from_string(<<"ok"/utf8>>)}, + gleam@atom:from_string(<<"ok"/utf8>>) + ), + gleam@should:equal( + {ok, gleam@atom:create_from_string(<<"expect"/utf8>>)}, + gleam@atom:from_string(<<"expect"/utf8>>) + ), + gleam@should:equal( + {ok, + gleam@atom:create_from_string( + <<"this is another atom we have not seen before"/utf8>> + )}, + gleam@atom:from_string( + <<"this is another atom we have not seen before"/utf8>> + ) + ). + +to_string_test() -> + gleam@should:equal( + gleam@atom:to_string(gleam@atom:create_from_string(<<"ok"/utf8>>)), + <<"ok"/utf8>> + ), + gleam@should:equal( + gleam@atom:to_string(gleam@atom:create_from_string(<<"expect"/utf8>>)), + <<"expect"/utf8>> + ). diff --git a/gleam_providers/deps/gleam_stdlib/gen/test/gleam@base_test.erl b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@base_test.erl new file mode 100644 index 0000000..c51d9f3 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@base_test.erl @@ -0,0 +1,84 @@ +-module(gleam@base_test). +-compile(no_auto_import). + +-export([encode64_test/0, decode64_test/0, url_encode64_test/0, url_decode64_test/0]). + +encode64_test() -> + gleam@should:equal( + gleam@base:encode64(erlang:list_to_binary([255, 127, 254, 252]), true), + <<"/3/+/A=="/utf8>> + ), + gleam@should:equal( + gleam@base:encode64(erlang:list_to_binary([255, 127, 254, 252]), false), + <<"/3/+/A"/utf8>> + ), + gleam@should:equal( + gleam@base:encode64(erlang:list_to_binary([0, 0, 0]), true), + <<"AAAA"/utf8>> + ), + gleam@should:equal( + gleam@base:encode64(erlang:list_to_binary([]), true), + <<""/utf8>> + ). + +decode64_test() -> + gleam@should:equal( + gleam@base:decode64(<<"/3/+/A=="/utf8>>), + {ok, erlang:list_to_binary([255, 127, 254, 252])} + ), + gleam@should:equal( + gleam@base:decode64(<<"/3/+/A"/utf8>>), + {ok, erlang:list_to_binary([255, 127, 254, 252])} + ), + gleam@should:equal( + gleam@base:decode64(<<"AAAA"/utf8>>), + {ok, erlang:list_to_binary([0, 0, 0])} + ), + gleam@should:equal( + gleam@base:decode64(<<""/utf8>>), + {ok, erlang:list_to_binary([])} + ), + gleam@should:equal(gleam@base:decode64(<<")!"/utf8>>), {error, nil}). + +url_encode64_test() -> + gleam@should:equal( + gleam@base:url_encode64( + erlang:list_to_binary([255, 127, 254, 252]), + true + ), + <<"_3_-_A=="/utf8>> + ), + gleam@should:equal( + gleam@base:url_encode64( + erlang:list_to_binary([255, 127, 254, 252]), + false + ), + <<"_3_-_A"/utf8>> + ), + gleam@should:equal( + gleam@base:url_encode64(erlang:list_to_binary([0, 0, 0]), true), + <<"AAAA"/utf8>> + ), + gleam@should:equal( + gleam@base:url_encode64(erlang:list_to_binary([]), true), + <<""/utf8>> + ). + +url_decode64_test() -> + gleam@should:equal( + gleam@base:url_decode64(<<"_3_-_A=="/utf8>>), + {ok, erlang:list_to_binary([255, 127, 254, 252])} + ), + gleam@should:equal( + gleam@base:url_decode64(<<"_3_-_A"/utf8>>), + {ok, erlang:list_to_binary([255, 127, 254, 252])} + ), + gleam@should:equal( + gleam@base:url_decode64(<<"AAAA"/utf8>>), + {ok, erlang:list_to_binary([0, 0, 0])} + ), + gleam@should:equal( + gleam@base:url_decode64(<<""/utf8>>), + {ok, erlang:list_to_binary([])} + ), + gleam@should:equal(gleam@base:url_decode64(<<")!"/utf8>>), {error, nil}). diff --git a/gleam_providers/deps/gleam_stdlib/gen/test/gleam@bit_builder_test.erl b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@bit_builder_test.erl new file mode 100644 index 0000000..3810a06 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@bit_builder_test.erl @@ -0,0 +1,75 @@ +-module(gleam@bit_builder_test). +-compile(no_auto_import). + +-export([builder_test/0, builder_with_strings_test/0, builder_with_builders_test/0, concat_test/0, from_bit_string_test/0, from_string_test/0]). + +builder_test() -> + Data = gleam@bit_builder:prepend( + gleam@bit_builder:append( + gleam@bit_builder:append( + gleam@bit_builder:from_bit_string(<<1>>), + <<2>> + ), + <<3>> + ), + <<0>> + ), + gleam@should:equal(gleam@bit_builder:to_bit_string(Data), <<0, 1, 2, 3>>), + gleam@should:equal(gleam@bit_builder:byte_size(Data), 4). + +builder_with_strings_test() -> + Data = gleam@bit_builder:prepend_string( + gleam@bit_builder:append_string( + gleam@bit_builder:append_string( + gleam@bit_builder:from_bit_string(<<1>>), + <<"2"/utf8>> + ), + <<"3"/utf8>> + ), + <<"0"/utf8>> + ), + gleam@should:equal( + gleam@bit_builder:to_bit_string(Data), + <<"0"/utf8, 1, "2"/utf8, "3"/utf8>> + ), + gleam@should:equal(gleam@bit_builder:byte_size(Data), 4). + +builder_with_builders_test() -> + Data = gleam@bit_builder:prepend_builder( + gleam@bit_builder:append_builder( + gleam@bit_builder:append_builder( + gleam@bit_builder:from_bit_string(<<1>>), + gleam@bit_builder:from_bit_string(<<2>>) + ), + gleam@bit_builder:from_bit_string(<<3>>) + ), + gleam@bit_builder:from_bit_string(<<0>>) + ), + gleam@should:equal(gleam@bit_builder:to_bit_string(Data), <<0, 1, 2, 3>>), + gleam@should:equal(gleam@bit_builder:byte_size(Data), 4). + +concat_test() -> + gleam@should:equal( + gleam@bit_builder:to_bit_string( + gleam@bit_builder:concat( + [gleam@bit_builder:from_bit_string(<<1, 2>>), + gleam@bit_builder:from_bit_string(<<3, 4>>), + gleam@bit_builder:from_bit_string(<<5, 6>>)] + ) + ), + <<1, 2, 3, 4, 5, 6>> + ). + +from_bit_string_test() -> + gleam@should:equal( + gleam@bit_builder:to_bit_string(gleam@bit_builder:from_bit_string(<<>>)), + <<>> + ). + +from_string_test() -> + gleam@should:equal( + gleam@bit_builder:to_bit_string( + gleam@bit_builder:from_string(<<""/utf8>>) + ), + <<>> + ). diff --git a/gleam_providers/deps/gleam_stdlib/gen/test/gleam@bit_string_test.erl b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@bit_string_test.erl new file mode 100644 index 0000000..282cd0d --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@bit_string_test.erl @@ -0,0 +1,129 @@ +-module(gleam@bit_string_test). +-compile(no_auto_import). + +-export([length_test/0, append_test/0, part_test/0, u32_test/0, to_string_test/0]). + +length_test() -> + gleam@should:equal( + gleam@bit_string:byte_size( + gleam@bit_string:from_string(<<"hello"/utf8>>) + ), + 5 + ), + gleam@should:equal( + gleam@bit_string:byte_size(gleam@bit_string:from_string(<<""/utf8>>)), + 0 + ). + +append_test() -> + gleam@should:equal( + gleam@bit_string:append( + gleam@bit_string:from_string(<<"Test"/utf8>>), + gleam@bit_string:from_string(<<" Me"/utf8>>) + ), + gleam@bit_string:from_string(<<"Test Me"/utf8>>) + ), + {ok, Zero_32bit} = gleam@bit_string:int_to_u32(0), + gleam@should:equal( + gleam@bit_string:append( + Zero_32bit, + gleam@bit_string:from_string(<<""/utf8>>) + ), + Zero_32bit + ). + +part_test() -> + gleam@should:equal( + gleam@bit_string:part( + gleam@bit_string:from_string(<<"hello"/utf8>>), + 0, + 5 + ), + {ok, gleam@bit_string:from_string(<<"hello"/utf8>>)} + ), + gleam@should:equal( + gleam@bit_string:part( + gleam@bit_string:from_string(<<"hello"/utf8>>), + 0, + 0 + ), + {ok, gleam@bit_string:from_string(<<""/utf8>>)} + ), + gleam@should:equal( + gleam@bit_string:part( + gleam@bit_string:from_string(<<"hello"/utf8>>), + 2, + 2 + ), + {ok, gleam@bit_string:from_string(<<"ll"/utf8>>)} + ), + gleam@should:equal( + gleam@bit_string:part( + gleam@bit_string:from_string(<<"hello"/utf8>>), + 5, + -2 + ), + {ok, gleam@bit_string:from_string(<<"lo"/utf8>>)} + ), + gleam@should:equal( + gleam@bit_string:part(gleam@bit_string:from_string(<<""/utf8>>), 0, 0), + {ok, gleam@bit_string:from_string(<<""/utf8>>)} + ), + gleam@should:equal( + gleam@bit_string:part( + gleam@bit_string:from_string(<<"hello"/utf8>>), + 6, + 0 + ), + {error, nil} + ), + gleam@should:equal( + gleam@bit_string:part( + gleam@bit_string:from_string(<<"hello"/utf8>>), + -1, + 1 + ), + {error, nil} + ), + gleam@should:equal( + gleam@bit_string:part( + gleam@bit_string:from_string(<<"hello"/utf8>>), + 1, + 6 + ), + {error, nil} + ). + +u32_test() -> + {ok, Bin} = gleam@bit_string:int_to_u32(0), + gleam@should:equal(4, gleam@bit_string:byte_size(Bin)), + gleam@should:equal({ok, 0}, gleam@bit_string:int_from_u32(Bin)), + {ok, Bin@1} = gleam@bit_string:int_to_u32(4294967295), + gleam@should:equal(4, gleam@bit_string:byte_size(Bin@1)), + gleam@should:equal({ok, 4294967295}, gleam@bit_string:int_from_u32(Bin@1)), + gleam@should:equal( + {error, nil}, + gleam@bit_string:int_from_u32(gleam@bit_string:from_string(<<""/utf8>>)) + ), + gleam@should:equal( + {error, nil}, + gleam@bit_string:int_from_u32( + gleam@bit_string:from_string(<<"12345"/utf8>>) + ) + ). + +to_string_test() -> + gleam@should:equal(gleam@bit_string:to_string(<<>>), {ok, <<""/utf8>>}), + gleam@should:equal( + gleam@bit_string:to_string(<<""/utf8>>), + {ok, <<""/utf8>>} + ), + gleam@should:equal( + gleam@bit_string:to_string(<<"Hello"/utf8>>), + {ok, <<"Hello"/utf8>>} + ), + gleam@should:equal( + gleam@bit_string:to_string(<<"ø"/utf8>>), + {ok, <<"ø"/utf8>>} + ), + gleam@should:equal(gleam@bit_string:to_string(<<65535:16>>), {error, nil}). diff --git a/gleam_providers/deps/gleam_stdlib/gen/test/gleam@bool_test.erl b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@bool_test.erl new file mode 100644 index 0000000..c4afc56 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@bool_test.erl @@ -0,0 +1,54 @@ +-module(gleam@bool_test). +-compile(no_auto_import). + +-export([negate_test/0, nor_test/0, nand_test/0, exclusive_or_test/0, exclusive_nor_test/0, compare_test/0, max_test/0, min_test/0, to_int_test/0]). + +negate_test() -> + gleam@should:be_false(gleam@bool:negate(true)), + gleam@should:be_true(gleam@bool:negate(false)). + +nor_test() -> + gleam@should:be_true(gleam@bool:nor(false, false)), + gleam@should:be_false(gleam@bool:nor(false, true)), + gleam@should:be_false(gleam@bool:nor(true, false)), + gleam@should:be_false(gleam@bool:nor(true, true)). + +nand_test() -> + gleam@should:be_true(gleam@bool:nand(false, false)), + gleam@should:be_true(gleam@bool:nand(false, true)), + gleam@should:be_true(gleam@bool:nand(true, false)), + gleam@should:be_false(gleam@bool:nand(true, true)). + +exclusive_or_test() -> + gleam@should:be_false(gleam@bool:exclusive_or(true, true)), + gleam@should:be_false(gleam@bool:exclusive_or(false, false)), + gleam@should:be_true(gleam@bool:exclusive_or(true, false)), + gleam@should:be_true(gleam@bool:exclusive_or(false, true)). + +exclusive_nor_test() -> + gleam@should:be_true(gleam@bool:exclusive_nor(false, false)), + gleam@should:be_false(gleam@bool:exclusive_nor(false, true)), + gleam@should:be_false(gleam@bool:exclusive_nor(true, false)), + gleam@should:be_true(gleam@bool:exclusive_nor(true, true)). + +compare_test() -> + gleam@should:equal(gleam@bool:compare(true, true), eq), + gleam@should:equal(gleam@bool:compare(true, false), gt), + gleam@should:equal(gleam@bool:compare(false, false), eq), + gleam@should:equal(gleam@bool:compare(false, true), lt). + +max_test() -> + gleam@should:equal(gleam@bool:max(true, true), true), + gleam@should:equal(gleam@bool:max(true, false), true), + gleam@should:equal(gleam@bool:max(false, false), false), + gleam@should:equal(gleam@bool:max(false, true), true). + +min_test() -> + gleam@should:equal(gleam@bool:min(true, true), true), + gleam@should:equal(gleam@bool:min(true, false), false), + gleam@should:equal(gleam@bool:min(false, false), false), + gleam@should:equal(gleam@bool:min(false, true), false). + +to_int_test() -> + gleam@should:equal(gleam@bool:to_int(true), 1), + gleam@should:equal(gleam@bool:to_int(false), 0). diff --git a/gleam_providers/deps/gleam_stdlib/gen/test/gleam@dynamic_test.erl b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@dynamic_test.erl new file mode 100644 index 0000000..4f0347b --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@dynamic_test.erl @@ -0,0 +1,396 @@ +-module(gleam@dynamic_test). +-compile(no_auto_import). + +-export([bit_string_test/0, string_test/0, int_test/0, float_test/0, thunk_test/0, bool_test/0, atom_test/0, typed_list_test/0, option_test/0, field_test/0, element_test/0, tuple2_test/0, typed_tuple2_test/0, map_test/0, list_test/0, result_test/0, typed_result_test/0]). + +bit_string_test() -> + gleam@should:equal( + gleam@dynamic:bit_string(gleam@dynamic:from(<<""/utf8>>)), + {ok, <<""/utf8>>} + ), + gleam@should:equal( + gleam@dynamic:bit_string(gleam@dynamic:from(<<"Hello"/utf8>>)), + {ok, <<"Hello"/utf8>>} + ), + gleam@should:equal( + gleam@dynamic:bit_string(gleam@dynamic:from(<<65535:16>>)), + {ok, <<65535:16>>} + ), + gleam@should:equal( + gleam@dynamic:bit_string(gleam@dynamic:from(1)), + {error, <<"Expected a bit_string, got an int"/utf8>>} + ), + gleam@should:equal( + gleam@dynamic:bit_string(gleam@dynamic:from([])), + {error, <<"Expected a bit_string, got a list"/utf8>>} + ). + +string_test() -> + gleam@should:equal( + gleam@dynamic:string(gleam@dynamic:from(<<""/utf8>>)), + {ok, <<""/utf8>>} + ), + gleam@should:equal( + gleam@dynamic:string(gleam@dynamic:from(<<"Hello"/utf8>>)), + {ok, <<"Hello"/utf8>>} + ), + gleam@should:equal( + gleam@dynamic:string(gleam@dynamic:from(<<65535:16>>)), + {error, <<"Expected a string, got a bit_string"/utf8>>} + ), + gleam@should:equal( + gleam@dynamic:string(gleam@dynamic:from(1)), + {error, <<"Expected a bit_string, got an int"/utf8>>} + ), + gleam@should:equal( + gleam@dynamic:string(gleam@dynamic:from([])), + {error, <<"Expected a bit_string, got a list"/utf8>>} + ). + +int_test() -> + gleam@should:equal(gleam@dynamic:int(gleam@dynamic:from(1)), {ok, 1}), + gleam@should:equal(gleam@dynamic:int(gleam@dynamic:from(2)), {ok, 2}), + gleam@should:equal( + gleam@dynamic:int(gleam@dynamic:from(1.0)), + {error, <<"Expected an int, got a float"/utf8>>} + ), + gleam@should:equal( + gleam@dynamic:int(gleam@dynamic:from([])), + {error, <<"Expected an int, got a list"/utf8>>} + ). + +float_test() -> + gleam@should:equal(gleam@dynamic:float(gleam@dynamic:from(1.0)), {ok, 1.0}), + gleam@should:equal(gleam@dynamic:float(gleam@dynamic:from(2.2)), {ok, 2.2}), + gleam@should:equal( + gleam@dynamic:float(gleam@dynamic:from(1)), + {error, <<"Expected a float, got an int"/utf8>>} + ), + gleam@should:equal( + gleam@dynamic:float(gleam@dynamic:from([])), + {error, <<"Expected a float, got a list"/utf8>>} + ). + +thunk_test() -> + gleam@should:be_ok(gleam@dynamic:thunk(gleam@dynamic:from(fun() -> 1 end))), + gleam@should:equal( + gleam@result:map( + gleam@dynamic:thunk(gleam@dynamic:from(fun() -> 1 end)), + fun(F) -> F() end + ), + {ok, gleam@dynamic:from(1)} + ), + gleam@should:be_error( + gleam@dynamic:thunk(gleam@dynamic:from(fun(X) -> X end)) + ), + gleam@should:be_error(gleam@dynamic:thunk(gleam@dynamic:from(1))), + gleam@should:be_error(gleam@dynamic:thunk(gleam@dynamic:from([]))). + +bool_test() -> + gleam@should:equal(gleam@dynamic:bool(gleam@dynamic:from(true)), {ok, true}), + gleam@should:equal( + gleam@dynamic:bool(gleam@dynamic:from(false)), + {ok, false} + ), + gleam@should:equal( + gleam@dynamic:bool(gleam@dynamic:from(1)), + {error, <<"Expected a bool, got an int"/utf8>>} + ), + gleam@should:equal( + gleam@dynamic:bool(gleam@dynamic:from([])), + {error, <<"Expected a bool, got a list"/utf8>>} + ). + +atom_test() -> + gleam@should:equal( + gleam@dynamic:atom( + gleam@dynamic:from(gleam@atom:create_from_string(<<""/utf8>>)) + ), + {ok, gleam@atom:create_from_string(<<""/utf8>>)} + ), + gleam@should:equal( + gleam@dynamic:atom( + gleam@dynamic:from(gleam@atom:create_from_string(<<"ok"/utf8>>)) + ), + {ok, gleam@atom:create_from_string(<<"ok"/utf8>>)} + ), + gleam@should:be_error(gleam@dynamic:atom(gleam@dynamic:from(1))), + gleam@should:be_error(gleam@dynamic:atom(gleam@dynamic:from([]))). + +typed_list_test() -> + gleam@should:equal( + gleam@dynamic:typed_list( + gleam@dynamic:from([]), + fun gleam@dynamic:string/1 + ), + {ok, []} + ), + gleam@should:equal( + gleam@dynamic:typed_list( + gleam@dynamic:from([]), + fun gleam@dynamic:int/1 + ), + {ok, []} + ), + gleam@should:equal( + gleam@dynamic:typed_list( + gleam@dynamic:from([1, 2, 3]), + fun gleam@dynamic:int/1 + ), + {ok, [1, 2, 3]} + ), + gleam@should:equal( + gleam@dynamic:typed_list( + gleam@dynamic:from([[1], [2], [3]]), + fun(Gleam@capture_variable) -> + gleam@dynamic:typed_list( + Gleam@capture_variable, + fun gleam@dynamic:int/1 + ) + end + ), + {ok, [[1], [2], [3]]} + ), + gleam@should:be_error( + gleam@dynamic:typed_list( + gleam@dynamic:from(1), + fun gleam@dynamic:string/1 + ) + ), + gleam@should:be_error( + gleam@dynamic:typed_list( + gleam@dynamic:from(1.0), + fun gleam@dynamic:int/1 + ) + ), + gleam@should:be_error( + gleam@dynamic:typed_list( + gleam@dynamic:from([<<""/utf8>>]), + fun gleam@dynamic:int/1 + ) + ), + gleam@should:be_error( + gleam@dynamic:typed_list( + gleam@dynamic:from( + [gleam@dynamic:from(1), + gleam@dynamic:from(<<"not an int"/utf8>>)] + ), + fun gleam@dynamic:int/1 + ) + ). + +option_test() -> + {ok, Null} = gleam@atom:from_string(<<"null"/utf8>>), + gleam@should:equal( + gleam@dynamic:option(gleam@dynamic:from(1), fun gleam@dynamic:int/1), + {ok, {some, 1}} + ), + gleam@should:equal( + gleam@dynamic:option(gleam@dynamic:from(Null), fun gleam@dynamic:int/1), + {ok, none} + ), + gleam@should:be_error( + gleam@dynamic:option(gleam@dynamic:from(1), fun gleam@dynamic:string/1) + ). + +field_test() -> + {ok, Ok_atom} = gleam@atom:from_string(<<"ok"/utf8>>), + {ok, Error_atom} = gleam@atom:from_string(<<"error"/utf8>>), + gleam@should:equal( + gleam@dynamic:field( + gleam@dynamic:from(gleam@map:insert(gleam@map:new(), Ok_atom, 1)), + Ok_atom + ), + {ok, gleam@dynamic:from(1)} + ), + gleam@should:equal( + gleam@dynamic:field( + gleam@dynamic:from( + gleam@map:insert( + gleam@map:insert(gleam@map:new(), Ok_atom, 3), + Error_atom, + 1 + ) + ), + Ok_atom + ), + {ok, gleam@dynamic:from(3)} + ), + gleam@should:be_error( + gleam@dynamic:field(gleam@dynamic:from(gleam@map:new()), Ok_atom) + ), + gleam@should:be_error(gleam@dynamic:field(gleam@dynamic:from(1), Ok_atom)), + gleam@should:be_error(gleam@dynamic:field(gleam@dynamic:from([]), [])). + +element_test() -> + {ok, Ok_atom} = gleam@atom:from_string(<<"ok"/utf8>>), + Ok_one_tuple = {Ok_atom, 1}, + gleam@should:equal( + gleam@dynamic:element(gleam@dynamic:from(Ok_one_tuple), 0), + {ok, gleam@dynamic:from(Ok_atom)} + ), + gleam@should:equal( + gleam@dynamic:element(gleam@dynamic:from(Ok_one_tuple), 1), + {ok, gleam@dynamic:from(1)} + ), + gleam@should:be_error( + gleam@dynamic:element(gleam@dynamic:from(Ok_one_tuple), 2) + ), + gleam@should:be_error( + gleam@dynamic:element(gleam@dynamic:from(Ok_one_tuple), -1) + ), + gleam@should:be_error(gleam@dynamic:element(gleam@dynamic:from(1), 0)), + gleam@should:be_error( + gleam@dynamic:element( + gleam@dynamic:from(gleam@map:insert(gleam@map:new(), 1, Ok_atom)), + 0 + ) + ). + +tuple2_test() -> + gleam@should:equal( + gleam@dynamic:tuple2(gleam@dynamic:from({1, 2})), + {ok, {gleam@dynamic:from(1), gleam@dynamic:from(2)}} + ), + gleam@should:equal( + gleam@dynamic:tuple2(gleam@dynamic:from({1, <<""/utf8>>})), + {ok, {gleam@dynamic:from(1), gleam@dynamic:from(<<""/utf8>>)}} + ), + gleam@should:equal( + gleam@dynamic:tuple2(gleam@dynamic:from({1, 2, 3})), + {error, <<"Expected a 2 element tuple, got a 3 element tuple"/utf8>>} + ), + gleam@should:equal( + gleam@dynamic:tuple2(gleam@dynamic:from(1)), + {error, <<"Expected a 2 element tuple, got an int"/utf8>>} + ). + +typed_tuple2_test() -> + gleam@should:equal( + gleam@dynamic:typed_tuple2( + gleam@dynamic:from({1, 2}), + fun gleam@dynamic:int/1, + fun gleam@dynamic:int/1 + ), + {ok, {1, 2}} + ), + gleam@should:equal( + gleam@dynamic:typed_tuple2( + gleam@dynamic:from({1, <<""/utf8>>}), + fun gleam@dynamic:int/1, + fun gleam@dynamic:string/1 + ), + {ok, {1, <<""/utf8>>}} + ), + gleam@should:equal( + gleam@dynamic:typed_tuple2( + gleam@dynamic:from({1, <<""/utf8>>}), + fun gleam@dynamic:int/1, + fun gleam@dynamic:int/1 + ), + {error, <<"Expected an int, got a binary"/utf8>>} + ), + gleam@should:equal( + gleam@dynamic:typed_tuple2( + gleam@dynamic:from({1, 2, 3}), + fun gleam@dynamic:int/1, + fun gleam@dynamic:int/1 + ), + {error, <<"Expected a 2 element tuple, got a 3 element tuple"/utf8>>} + ), + gleam@should:equal( + gleam@dynamic:typed_tuple2( + gleam@dynamic:from(1), + fun gleam@dynamic:int/1, + fun gleam@dynamic:int/1 + ), + {error, <<"Expected a 2 element tuple, got an int"/utf8>>} + ). + +map_test() -> + gleam@should:equal( + gleam@dynamic:map(gleam@dynamic:from(gleam@map:new())), + {ok, gleam@map:new()} + ), + gleam@should:equal( + gleam@dynamic:map(gleam@dynamic:from(1)), + {error, <<"Expected a map, got an int"/utf8>>} + ). + +list_test() -> + gleam@should:equal(gleam@dynamic:list(gleam@dynamic:from([])), {ok, []}), + gleam@should:equal( + gleam@dynamic:list(gleam@dynamic:from([1, 2])), + {ok, [gleam@dynamic:from(1), gleam@dynamic:from(2)]} + ), + gleam@should:equal( + gleam@dynamic:list( + gleam@dynamic:from([gleam@dynamic:from(1), gleam@dynamic:from(2.0)]) + ), + {ok, [gleam@dynamic:from(1), gleam@dynamic:from(2.0)]} + ), + gleam@should:equal( + gleam@dynamic:list(gleam@dynamic:from(1)), + {error, <<"Expected a list, got an int"/utf8>>} + ). + +result_test() -> + gleam@should:equal( + gleam@dynamic:result(gleam@dynamic:from({ok, 1})), + {ok, {ok, gleam@dynamic:from(1)}} + ), + gleam@should:equal( + gleam@dynamic:result(gleam@dynamic:from({error, <<"error"/utf8>>})), + {ok, {error, gleam@dynamic:from(<<"error"/utf8>>)}} + ), + gleam@should:equal( + gleam@dynamic:result(gleam@dynamic:from(1)), + {error, <<"Expected a 2 element tuple, got an int"/utf8>>} + ), + Tag = gleam@atom:create_from_string(<<"bad"/utf8>>), + gleam@should:equal( + gleam@dynamic:result(gleam@dynamic:from({Tag, <<"value"/utf8>>})), + {error, <<"Expected a tag of \"ok\" or \"error\", got \"bad\""/utf8>>} + ). + +typed_result_test() -> + gleam@should:equal( + gleam@dynamic:typed_result( + gleam@dynamic:from({ok, 1}), + fun gleam@dynamic:int/1, + fun gleam@dynamic:string/1 + ), + {ok, {ok, 1}} + ), + gleam@should:equal( + gleam@dynamic:typed_result( + gleam@dynamic:from({error, <<"error"/utf8>>}), + fun gleam@dynamic:int/1, + fun gleam@dynamic:string/1 + ), + {ok, {error, <<"error"/utf8>>}} + ), + gleam@should:equal( + gleam@dynamic:typed_result( + gleam@dynamic:from({ok, <<"1"/utf8>>}), + fun gleam@dynamic:int/1, + fun gleam@dynamic:string/1 + ), + {error, <<"Expected an int, got a binary"/utf8>>} + ), + gleam@should:equal( + gleam@dynamic:typed_result( + gleam@dynamic:from({error, 1}), + fun gleam@dynamic:int/1, + fun gleam@dynamic:string/1 + ), + {error, <<"Expected a bit_string, got an int"/utf8>>} + ), + gleam@should:equal( + gleam@dynamic:typed_result( + gleam@dynamic:from(1), + fun gleam@dynamic:int/1, + fun gleam@dynamic:string/1 + ), + {error, <<"Expected a 2 element tuple, got an int"/utf8>>} + ). diff --git a/gleam_providers/deps/gleam_stdlib/gen/test/gleam@float_test.erl b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@float_test.erl new file mode 100644 index 0000000..0b9217d --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@float_test.erl @@ -0,0 +1,105 @@ +-module(gleam@float_test). +-compile(no_auto_import). + +-export([parse_test/0, to_string_test/0, compare_test/0, ceiling_test/0, floor_test/0, round_test/0, truncate_test/0, min_test/0, max_test/0, absolute_value_test/0, power_test/0, square_root_test/0, negate_test/0, sum_test/0, product_test/0]). + +parse_test() -> + gleam@should:equal(gleam@float:parse(<<"1.23"/utf8>>), {ok, 1.23}), + gleam@should:equal(gleam@float:parse(<<"5.0"/utf8>>), {ok, 5.0}), + gleam@should:equal( + gleam@float:parse(<<"0.123456789"/utf8>>), + {ok, 0.123456789} + ), + gleam@should:equal(gleam@float:parse(<<""/utf8>>), {error, nil}), + gleam@should:equal(gleam@float:parse(<<"what"/utf8>>), {error, nil}), + gleam@should:equal(gleam@float:parse(<<"1"/utf8>>), {error, nil}). + +to_string_test() -> + gleam@should:equal(gleam@float:to_string(123.0), <<"123.0"/utf8>>), + gleam@should:equal(gleam@float:to_string(-8.1), <<"-8.1"/utf8>>). + +compare_test() -> + gleam@should:equal(gleam@float:compare(0.0, 0.0), eq), + gleam@should:equal(gleam@float:compare(0.1, 0.1), eq), + gleam@should:equal(gleam@float:compare(0.0, 0.1), lt), + gleam@should:equal(gleam@float:compare(-2.0, -1.9), lt), + gleam@should:equal(gleam@float:compare(2.0, 1.9), gt), + gleam@should:equal(gleam@float:compare(-1.9, -2.0), gt). + +ceiling_test() -> + gleam@should:equal(gleam@float:ceiling(8.1), 9.0), + gleam@should:equal(gleam@float:ceiling(-8.1), -8.0), + gleam@should:equal(gleam@float:ceiling(-8.0), -8.0). + +floor_test() -> + gleam@should:equal(gleam@float:floor(8.1), 8.0), + gleam@should:equal(gleam@float:floor(-8.1), -9.0), + gleam@should:equal(gleam@float:floor(-8.0), -8.0). + +round_test() -> + gleam@should:equal(gleam@float:round(8.1), 8), + gleam@should:equal(gleam@float:round(8.4), 8), + gleam@should:equal(gleam@float:round(8.499), 8), + gleam@should:equal(gleam@float:round(8.5), 9), + gleam@should:equal(gleam@float:round(-8.1), -8), + gleam@should:equal(gleam@float:round(-7.5), -8). + +truncate_test() -> + gleam@should:equal(gleam@float:truncate(8.1), 8), + gleam@should:equal(gleam@float:truncate(8.4), 8), + gleam@should:equal(gleam@float:truncate(8.499), 8), + gleam@should:equal(gleam@float:truncate(8.5), 8), + gleam@should:equal(gleam@float:truncate(-8.1), -8), + gleam@should:equal(gleam@float:truncate(-7.5), -7). + +min_test() -> + gleam@should:equal(gleam@float:min(0.0, 0.0), 0.0), + gleam@should:equal(gleam@float:min(0.3, 1.5), 0.3), + gleam@should:equal(gleam@float:min(1.0, 0.0), 0.0), + gleam@should:equal(gleam@float:min(-1.7, 2.5), -1.7), + gleam@should:equal(gleam@float:min(-2.2, -2.2), -2.2), + gleam@should:equal(gleam@float:min(-1.0, -1.0), -1.0), + gleam@should:equal(gleam@float:min(-1.1, -1.0), -1.1). + +max_test() -> + gleam@should:equal(gleam@float:max(0.0, 0.0), 0.0), + gleam@should:equal(gleam@float:max(0.3, 1.5), 1.5), + gleam@should:equal(gleam@float:max(1.0, 0.0), 1.0), + gleam@should:equal(gleam@float:max(-1.7, 2.5), 2.5), + gleam@should:equal(gleam@float:max(-2.2, -2.2), -2.2), + gleam@should:equal(gleam@float:max(-1.0, -1.0), -1.0), + gleam@should:equal(gleam@float:max(-1.1, -1.0), -1.0). + +absolute_value_test() -> + gleam@should:equal(gleam@float:absolute_value(-1.0), 1.0), + gleam@should:equal(gleam@float:absolute_value(-20.6), 20.6), + gleam@should:equal(gleam@float:absolute_value(0.0), 0.0), + gleam@should:equal(gleam@float:absolute_value(1.0), 1.0), + gleam@should:equal(gleam@float:absolute_value(25.2), 25.2). + +power_test() -> + gleam@should:equal(gleam@float:power(2.0, 2.0), 4.0), + gleam@should:equal(gleam@float:power(-5.0, 3.0), -125.0), + gleam@should:equal(gleam@float:power(10.5, 0.0), 1.0), + gleam@should:equal(gleam@float:power(16.0, 0.5), 4.0), + gleam@should:equal(gleam@float:power(2.0, -1.0), 0.5). + +square_root_test() -> + gleam@should:equal(gleam@float:square_root(4.0), {ok, 2.0}), + gleam@should:equal(gleam@float:square_root(16.0), {ok, 4.0}), + gleam@should:equal(gleam@float:square_root(0.0), {ok, 0.0}), + gleam@should:equal(gleam@float:square_root(-4.0), {error, nil}). + +negate_test() -> + gleam@should:equal(gleam@float:negate(-1.0), 1.0), + gleam@should:equal(gleam@float:negate(2.0), -2.0), + gleam@should:equal(gleam@float:negate(0.0), 0.0). + +sum_test() -> + gleam@should:equal(gleam@float:sum([]), 0.0), + gleam@should:equal(gleam@float:sum([1.0, 2.2, 3.3]), 6.5). + +product_test() -> + gleam@should:equal(gleam@float:product([]), 0.0), + gleam@should:equal(gleam@float:product([4.0]), 4.0), + gleam@should:equal(gleam@float:product([2.5, 3.2, 4.2]), 33.6). diff --git a/gleam_providers/deps/gleam_stdlib/gen/test/gleam@function_test.erl b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@function_test.erl new file mode 100644 index 0000000..826d22f --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@function_test.erl @@ -0,0 +1,86 @@ +-module(gleam@function_test). +-compile(no_auto_import). + +-export([compose_test/0, curry2_test/0, curry3_test/0, curry4_test/0, curry5_test/0, curry6_test/0, flip_test/0, identity_test/0, rescue_test/0]). + +compose_test() -> + Add_two = fun(Int) -> Int + 2 end, + Add_three = fun(Int@1) -> Int@1 + 3 end, + Add_five = gleam@function:compose(Add_two, Add_three), + gleam@should:equal(Add_five(1), 6), + Head_to_string = gleam@function:compose( + gleam@function:compose( + fun gleam@list:head/1, + fun(Gleam@capture_variable) -> + gleam@result:unwrap(Gleam@capture_variable, 0) + end + ), + fun gleam@int:to_string/1 + ), + gleam@should:equal(Head_to_string([1]), <<"1"/utf8>>), + gleam@should:equal(Head_to_string([]), <<"0"/utf8>>). + +curry2_test() -> + Fun = fun(A, B) -> A + B end, + Curried = gleam@function:curry2(Fun), + gleam@should:equal((Curried(1))(2), 3). + +curry3_test() -> + Fun = fun(A, B, C) -> (A + B) + C end, + Curried = gleam@function:curry3(Fun), + gleam@should:equal(((Curried(1))(2))(4), 7). + +curry4_test() -> + Fun = fun(A, B, C, D) -> ((A + B) + C) + D end, + Curried = gleam@function:curry4(Fun), + gleam@should:equal((((Curried(1))(2))(4))(8), 15). + +curry5_test() -> + Fun = fun(A, B, C, D, E) -> (((A + B) + C) + D) + E end, + Curried = gleam@function:curry5(Fun), + gleam@should:equal(((((Curried(1))(2))(4))(8))(16), 31). + +curry6_test() -> + Fun = fun(A, B, C, D, E, F) -> ((((A + B) + C) + D) + E) + F end, + Curried = gleam@function:curry6(Fun), + gleam@should:equal((((((Curried(1))(2))(4))(8))(16))(32), 63). + +flip_test() -> + Fun = fun(S, I) -> + gleam@string:append( + gleam@string:append( + gleam@string:append( + gleam@string:append(<<"String: '"/utf8>>, S), + <<"', Int: '"/utf8>> + ), + gleam@int:to_string(I) + ), + <<"'"/utf8>> + ) + end, + Flipped_fun = gleam@function:flip(Fun), + gleam@should:equal( + Fun(<<"Bob"/utf8>>, 1), + <<"String: 'Bob', Int: '1'"/utf8>> + ), + gleam@should:equal( + Flipped_fun(2, <<"Alice"/utf8>>), + <<"String: 'Alice', Int: '2'"/utf8>> + ). + +identity_test() -> + gleam@should:equal(gleam@function:identity(1), 1), + gleam@should:equal(gleam@function:identity(<<""/utf8>>), <<""/utf8>>), + gleam@should:equal(gleam@function:identity([]), []), + gleam@should:equal(gleam@function:identity({1, 2.0}), {1, 2.0}). + +rescue_test() -> + gleam@should:equal(gleam@function:rescue(fun() -> 1 end), {ok, 1}), + gleam@should:equal( + gleam@function:rescue(fun() -> erlang:throw(1) end), + {error, {thrown, gleam@dynamic:from(1)}} + ), + gleam@should:equal( + gleam@function:rescue(fun() -> erlang:error(<<""/utf8>>) end), + {error, {errored, gleam@dynamic:from(<<""/utf8>>)}} + ). diff --git a/gleam_providers/deps/gleam_stdlib/gen/test/gleam@int_test.erl b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@int_test.erl new file mode 100644 index 0000000..cd4375e --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@int_test.erl @@ -0,0 +1,87 @@ +-module(gleam@int_test). +-compile(no_auto_import). + +-export([absolute_value_test/0, to_string_test/0, parse_test/0, to_base_string_test/0, to_float_test/0, compare_test/0, min_test/0, max_test/0, is_even_test/0, is_odd_test/0, negate_test/0, sum_test/0, product_test/0]). + +absolute_value_test() -> + gleam@should:equal(gleam@int:absolute_value(123), 123), + gleam@should:equal(gleam@int:absolute_value(-123), 123). + +to_string_test() -> + gleam@should:equal(gleam@int:to_string(123), <<"123"/utf8>>), + gleam@should:equal(gleam@int:to_string(-123), <<"-123"/utf8>>), + gleam@should:equal(gleam@int:to_string(123), <<"123"/utf8>>). + +parse_test() -> + gleam@should:equal(gleam@int:parse(<<"123"/utf8>>), {ok, 123}), + gleam@should:equal(gleam@int:parse(<<"-123"/utf8>>), {ok, -123}), + gleam@should:equal(gleam@int:parse(<<"0123"/utf8>>), {ok, 123}), + gleam@should:equal(gleam@int:parse(<<""/utf8>>), {error, nil}), + gleam@should:equal(gleam@int:parse(<<"what"/utf8>>), {error, nil}), + gleam@should:equal(gleam@int:parse(<<"1.23"/utf8>>), {error, nil}). + +to_base_string_test() -> + gleam@should:equal(gleam@int:to_base_string(100, 16), <<"64"/utf8>>), + gleam@should:equal(gleam@int:to_base_string(-100, 16), <<"-64"/utf8>>). + +to_float_test() -> + gleam@should:equal(gleam@int:to_float(1), 1.0), + gleam@should:equal(gleam@int:to_float(5), 5.0), + gleam@should:equal(gleam@int:to_float(0), 0.0), + gleam@should:equal(gleam@int:to_float(-5), -5.0). + +compare_test() -> + gleam@should:equal(gleam@int:compare(0, 0), eq), + gleam@should:equal(gleam@int:compare(1, 1), eq), + gleam@should:equal(gleam@int:compare(0, 1), lt), + gleam@should:equal(gleam@int:compare(-2, -1), lt), + gleam@should:equal(gleam@int:compare(2, 1), gt), + gleam@should:equal(gleam@int:compare(-1, -2), gt). + +min_test() -> + gleam@should:equal(gleam@int:min(0, 0), 0), + gleam@should:equal(gleam@int:min(0, 1), 0), + gleam@should:equal(gleam@int:min(1, 0), 0), + gleam@should:equal(gleam@int:min(-1, 2), -1), + gleam@should:equal(gleam@int:min(2, -2), -2), + gleam@should:equal(gleam@int:min(-1, -1), -1). + +max_test() -> + gleam@should:equal(gleam@int:max(0, 0), 0), + gleam@should:equal(gleam@int:max(0, 1), 1), + gleam@should:equal(gleam@int:max(1, 0), 1), + gleam@should:equal(gleam@int:max(-1, 2), 2), + gleam@should:equal(gleam@int:max(2, -2), 2), + gleam@should:equal(gleam@int:max(-1, -1), -1). + +is_even_test() -> + gleam@should:be_true(gleam@int:is_even(0)), + gleam@should:be_true(gleam@int:is_even(2)), + gleam@should:be_true(gleam@int:is_even(-2)), + gleam@should:be_true(gleam@int:is_even(10006)), + gleam@should:be_false(gleam@int:is_even(1)), + gleam@should:be_false(gleam@int:is_even(-3)), + gleam@should:be_false(gleam@int:is_even(10005)). + +is_odd_test() -> + gleam@should:be_false(gleam@int:is_odd(0)), + gleam@should:be_false(gleam@int:is_odd(2)), + gleam@should:be_false(gleam@int:is_odd(-2)), + gleam@should:be_false(gleam@int:is_odd(10006)), + gleam@should:be_true(gleam@int:is_odd(1)), + gleam@should:be_true(gleam@int:is_odd(-3)), + gleam@should:be_true(gleam@int:is_odd(10005)). + +negate_test() -> + gleam@should:equal(gleam@int:negate(-1), 1), + gleam@should:equal(gleam@int:negate(2), -2), + gleam@should:equal(gleam@int:negate(0), 0). + +sum_test() -> + gleam@should:equal(gleam@int:sum([]), 0), + gleam@should:equal(gleam@int:sum([1, 2, 3]), 6). + +product_test() -> + gleam@should:equal(gleam@int:product([]), 0), + gleam@should:equal(gleam@int:product([4]), 4), + gleam@should:equal(gleam@int:product([1, 2, 3]), 6). diff --git a/gleam_providers/deps/gleam_stdlib/gen/test/gleam@iterator_test.erl b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@iterator_test.erl new file mode 100644 index 0000000..e0b680a --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@iterator_test.erl @@ -0,0 +1,235 @@ +-module(gleam@iterator_test). +-compile(no_auto_import). + +-export([to_from_list_test/0, step_test/0, take_test/0, fold_test/0, map_test/0, flat_map_test/0, append_test/0, flatten_test/0, filter_test/0, repeat_test/0, cycle_test/0, unfold_test/0, range_test/0, drop_test/0, find_test/0]). + +to_from_list_test() -> + Test = fun(Subject) -> + gleam@should:equal( + gleam@iterator:to_list(gleam@iterator:from_list(Subject)), + Subject + ) + end, + Test([]), + Test([1]), + Test([1, 2]), + Test([1, 2, 4, 8]). + +step_test() -> + Test = fun(Subject) -> + Step = gleam@iterator:step(gleam@iterator:from_list(Subject)), + case Subject of + [] -> + gleam@should:equal(Step, done); + + [H | T] -> + gleam@should:equal(Step, {next, H, gleam@iterator:from_list(T)}) + end + end, + Test([]), + Test([1]), + Test([1, 2]), + Test([1, 2, 3]). + +take_test() -> + Test = fun(N, Subject) -> + gleam@should:equal( + gleam@iterator:take(gleam@iterator:from_list(Subject), N), + gleam@list:take(Subject, N) + ) + end, + Test(0, []), + Test(1, []), + Test(-1, []), + Test(0, [0]), + Test(1, [0]), + Test(-1, [0]), + Test(0, [0, 1, 2, 3, 4]), + Test(1, [0, 1, 2, 3, 4]), + Test(2, [0, 1, 2, 3, 4]), + Test(22, [0, 1, 2, 3, 4]). + +fold_test() -> + Test = fun(Subject, Acc, F) -> + gleam@should:equal( + gleam@iterator:fold(gleam@iterator:from_list(Subject), Acc, F), + gleam@list:fold(Subject, Acc, F) + ) + end, + F@1 = fun(E, Acc@1) -> [E | Acc@1] end, + Test([], [], F@1), + Test([1], [], F@1), + Test([1, 2, 3], [], F@1), + Test([1, 2, 3, 4, 5, 6, 7, 8], [], F@1). + +map_test() -> + Test = fun(Subject, F) -> + gleam@should:equal( + gleam@iterator:to_list( + gleam@iterator:map(gleam@iterator:from_list(Subject), F) + ), + gleam@list:map(Subject, F) + ) + end, + F@1 = fun(E) -> E * 2 end, + Test([], F@1), + Test([1], F@1), + Test([1, 2, 3], F@1), + Test([1, 2, 3, 4, 5, 6, 7, 8], F@1). + +flat_map_test() -> + Test = fun(Subject, F) -> + gleam@should:equal( + gleam@iterator:to_list( + gleam@iterator:flat_map(gleam@iterator:from_list(Subject), F) + ), + gleam@list:flatten( + gleam@list:map( + gleam@list:map(Subject, F), + fun gleam@iterator:to_list/1 + ) + ) + ) + end, + F@1 = fun(I) -> gleam@iterator:range(I, I + 2) end, + Test([], F@1), + Test([1], F@1), + Test([1, 2], F@1). + +append_test() -> + Test = fun(Left, Right) -> + gleam@should:equal( + gleam@iterator:to_list( + gleam@iterator:append( + gleam@iterator:from_list(Left), + gleam@iterator:from_list(Right) + ) + ), + gleam@list:flatten([Left, Right]) + ) + end, + Test([], []), + Test([1], [2]), + Test([1, 2], [3, 4]). + +flatten_test() -> + Test = fun(Lists) -> + gleam@should:equal( + gleam@iterator:to_list( + gleam@iterator:flatten( + gleam@iterator:from_list( + gleam@list:map(Lists, fun gleam@iterator:from_list/1) + ) + ) + ), + gleam@list:flatten(Lists) + ) + end, + Test([[], []]), + Test([[1], [2]]), + Test([[1, 2], [3, 4]]). + +filter_test() -> + Test = fun(Subject, F) -> + gleam@should:equal( + gleam@iterator:to_list( + gleam@iterator:filter(gleam@iterator:from_list(Subject), F) + ), + gleam@list:filter(Subject, F) + ) + end, + Even = fun(X) -> (X rem 2) =:= 0 end, + Test([], Even), + Test([1], Even), + Test([1, 2], Even), + Test([1, 2, 3], Even), + Test([1, 2, 3, 4], Even), + Test([1, 2, 3, 4, 5], Even), + Test([1, 2, 3, 4, 5, 6], Even). + +repeat_test() -> + gleam@should:equal( + gleam@iterator:take(gleam@iterator:repeat(1), 5), + [1, 1, 1, 1, 1] + ). + +cycle_test() -> + gleam@should:equal( + gleam@iterator:take( + gleam@iterator:cycle(gleam@iterator:from_list([1, 2, 3])), + 9 + ), + [1, 2, 3, 1, 2, 3, 1, 2, 3] + ). + +unfold_test() -> + gleam@should:equal( + gleam@iterator:take( + gleam@iterator:unfold(2, fun(Acc) -> {next, Acc, Acc * 2} end), + 5 + ), + [2, 4, 8, 16, 32] + ), + gleam@should:equal( + gleam@iterator:take(gleam@iterator:unfold(2, fun(_) -> done end), 5), + [] + ), + gleam@should:equal( + gleam@iterator:to_list(gleam@iterator:unfold(5, fun(N) -> case N of + 0 -> + done; + + N@1 -> + {next, N@1, N@1 - 1} + end end)), + [5, 4, 3, 2, 1] + ). + +range_test() -> + Test = fun(A, B, Expected) -> + gleam@should:equal( + gleam@iterator:to_list(gleam@iterator:range(A, B)), + Expected + ) + end, + Test(0, 0, []), + Test(1, 1, []), + Test(-1, -1, []), + Test(0, 1, [0]), + Test(0, 5, [0, 1, 2, 3, 4]), + Test(1, -5, [1, 0, -1, -2, -3, -4]). + +drop_test() -> + gleam@should:equal( + gleam@iterator:to_list( + gleam@iterator:drop(gleam@iterator:range(0, 10), 5) + ), + [5, 6, 7, 8, 9] + ). + +find_test() -> + gleam@should:equal( + gleam@iterator:find(gleam@iterator:range(0, 10), fun(E) -> E =:= 5 end), + {ok, 5} + ), + gleam@should:equal( + gleam@iterator:find( + gleam@iterator:range(0, 10), + fun(E@1) -> E@1 > 10 end + ), + {error, nil} + ), + gleam@should:equal( + gleam@iterator:find(gleam@iterator:from_list([]), fun(_) -> true end), + {error, nil} + ), + gleam@should:equal( + gleam@iterator:find( + gleam@iterator:unfold( + {cat, 1}, + fun(Cat) -> {next, Cat, {cat, erlang:element(2, Cat) + 1}} end + ), + fun(Cat@1) -> erlang:element(2, Cat@1) =:= 10 end + ), + {ok, {cat, 10}} + ). diff --git a/gleam_providers/deps/gleam_stdlib/gen/test/gleam@list_test.erl b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@list_test.erl new file mode 100644 index 0000000..da9e063 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@list_test.erl @@ -0,0 +1,380 @@ +-module(gleam@list_test). +-compile(no_auto_import). + +-export([length_test/0, reverse_test/0, is_empty_test/0, contains_test/0, head_test/0, tail_test/0, filter_test/0, filter_map_test/0, map_test/0, try_map_test/0, drop_test/0, take_test/0, new_test/0, append_test/0, flatten_test/0, fold_test/0, fold_right_test/0, find_map_test/0, find_test/0, all_test/0, any_test/0, zip_test/0, strict_zip_test/0, unzip_test/0, intersperse_test/0, at_test/0, unique_test/0, sort_test/0, index_map_test/0, range_test/0, repeat_test/0, split_test/0, split_while_test/0, key_find_test/0, pop_test/0, pop_map_test/0, key_pop_test/0, key_set_test/0, partition_test/0, permutations_test/0]). + +length_test() -> + gleam@should:equal(gleam@list:length([]), 0), + gleam@should:equal(gleam@list:length([1]), 1), + gleam@should:equal(gleam@list:length([1, 1]), 2), + gleam@should:equal(gleam@list:length([1, 1, 1]), 3). + +reverse_test() -> + gleam@should:equal(gleam@list:reverse([]), []), + gleam@should:equal(gleam@list:reverse([1, 2, 3, 4, 5]), [5, 4, 3, 2, 1]). + +is_empty_test() -> + gleam@should:be_true(gleam@list:is_empty([])), + gleam@should:be_false(gleam@list:is_empty([1])). + +contains_test() -> + gleam@should:be_true(gleam@list:contains([0, 4, 5, 1], 1)), + gleam@should:be_false(gleam@list:contains([0, 4, 5, 7], 1)), + gleam@should:be_false(gleam@list:contains([], 1)). + +head_test() -> + gleam@should:equal(gleam@list:head([0, 4, 5, 7]), {ok, 0}), + gleam@should:equal(gleam@list:head([]), {error, nil}). + +tail_test() -> + gleam@should:equal(gleam@list:tail([0, 4, 5, 7]), {ok, [4, 5, 7]}), + gleam@should:equal(gleam@list:tail([0]), {ok, []}), + gleam@should:equal(gleam@list:tail([]), {error, nil}). + +filter_test() -> + gleam@should:equal(gleam@list:filter([], fun(_) -> true end), []), + gleam@should:equal( + gleam@list:filter([0, 4, 5, 7, 3], fun(_) -> true end), + [0, 4, 5, 7, 3] + ), + gleam@should:equal( + gleam@list:filter([0, 4, 5, 7, 3], fun(X) -> X > 4 end), + [5, 7] + ), + gleam@should:equal( + gleam@list:filter([0, 4, 5, 7, 3], fun(X@1) -> X@1 < 4 end), + [0, 3] + ). + +filter_map_test() -> + gleam@should:equal( + gleam@list:filter_map([2, 4, 6, 1], fun(X) -> {ok, X + 1} end), + [3, 5, 7, 2] + ), + gleam@should:equal( + gleam@list:filter_map([2, 4, 6, 1], fun(A) -> {error, A} end), + [] + ). + +map_test() -> + gleam@should:equal(gleam@list:map([], fun(X) -> X * 2 end), []), + gleam@should:equal( + gleam@list:map([0, 4, 5, 7, 3], fun(X@1) -> X@1 * 2 end), + [0, 8, 10, 14, 6] + ). + +try_map_test() -> + Fun = fun(X) -> case ((X =:= 6) orelse (X =:= 5)) orelse (X =:= 4) of + true -> + {ok, X * 2}; + + false -> + {error, X} + end end, + gleam@should:equal( + gleam@list:try_map([5, 6, 5, 6], Fun), + {ok, [10, 12, 10, 12]} + ), + gleam@should:equal(gleam@list:try_map([4, 6, 5, 7, 3], Fun), {error, 7}). + +drop_test() -> + gleam@should:equal(gleam@list:drop([], 5), []), + gleam@should:equal(gleam@list:drop([1, 2, 3, 4, 5, 6, 7, 8], 5), [6, 7, 8]). + +take_test() -> + gleam@should:equal(gleam@list:take([], 5), []), + gleam@should:equal( + gleam@list:take([1, 2, 3, 4, 5, 6, 7, 8], 5), + [1, 2, 3, 4, 5] + ). + +new_test() -> + gleam@should:equal(gleam@list:new(), []). + +append_test() -> + gleam@should:equal(gleam@list:append([1], [2, 3]), [1, 2, 3]). + +flatten_test() -> + gleam@should:equal(gleam@list:flatten([]), []), + gleam@should:equal(gleam@list:flatten([[]]), []), + gleam@should:equal(gleam@list:flatten([[], [], []]), []), + gleam@should:equal(gleam@list:flatten([[1, 2], [], [3, 4]]), [1, 2, 3, 4]). + +fold_test() -> + gleam@should:equal( + gleam@list:fold([1, 2, 3], [], fun(X, Acc) -> [X | Acc] end), + [3, 2, 1] + ). + +fold_right_test() -> + gleam@should:equal( + gleam@list:fold_right([1, 2, 3], [], fun(X, Acc) -> [X | Acc] end), + [1, 2, 3] + ). + +find_map_test() -> + F = fun(X) -> case X of + 2 -> + {ok, 4}; + + _ -> + {error, nil} + end end, + gleam@should:equal(gleam@list:find_map([1, 2, 3], F), {ok, 4}), + gleam@should:equal(gleam@list:find_map([1, 3, 2], F), {ok, 4}), + gleam@should:equal(gleam@list:find_map([1, 3], F), {error, nil}). + +find_test() -> + Is_two = fun(X) -> X =:= 2 end, + gleam@should:equal(gleam@list:find([1, 2, 3], Is_two), {ok, 2}), + gleam@should:equal(gleam@list:find([1, 3, 2], Is_two), {ok, 2}), + gleam@should:equal(gleam@list:find([1, 3], Is_two), {error, nil}). + +all_test() -> + gleam@should:equal( + gleam@list:all([1, 2, 3, 4, 5], fun(X) -> X > 0 end), + true + ), + gleam@should:equal( + gleam@list:all([1, 2, 3, 4, 5], fun(X@1) -> X@1 < 0 end), + false + ), + gleam@should:equal(gleam@list:all([], fun(_) -> false end), true). + +any_test() -> + gleam@should:equal( + gleam@list:any([1, 2, 3, 4, 5], fun(X) -> X =:= 2 end), + true + ), + gleam@should:equal( + gleam@list:any([1, 2, 3, 4, 5], fun(X@1) -> X@1 < 0 end), + false + ), + gleam@should:equal(gleam@list:any([], fun(_) -> false end), false). + +zip_test() -> + gleam@should:equal(gleam@list:zip([], [1, 2, 3]), []), + gleam@should:equal(gleam@list:zip([1, 2], []), []), + gleam@should:equal( + gleam@list:zip([1, 2, 3], [4, 5, 6]), + [{1, 4}, {2, 5}, {3, 6}] + ), + gleam@should:equal(gleam@list:zip([5, 6], [1, 2, 3]), [{5, 1}, {6, 2}]), + gleam@should:equal(gleam@list:zip([5, 6, 7], [1, 2]), [{5, 1}, {6, 2}]). + +strict_zip_test() -> + gleam@should:equal( + gleam@list:strict_zip([], [1, 2, 3]), + {error, length_mismatch} + ), + gleam@should:equal( + gleam@list:strict_zip([1, 2], []), + {error, length_mismatch} + ), + gleam@should:equal( + gleam@list:strict_zip([1, 2, 3], [4, 5, 6]), + {ok, [{1, 4}, {2, 5}, {3, 6}]} + ), + gleam@should:equal( + gleam@list:strict_zip([5, 6], [1, 2, 3]), + {error, length_mismatch} + ), + gleam@should:equal( + gleam@list:strict_zip([5, 6, 7], [1, 2]), + {error, length_mismatch} + ). + +unzip_test() -> + gleam@should:equal(gleam@list:unzip([{1, 2}, {3, 4}]), {[1, 3], [2, 4]}), + gleam@should:equal(gleam@list:unzip([]), {[], []}). + +intersperse_test() -> + gleam@should:equal(gleam@list:intersperse([1, 2, 3], 4), [1, 4, 2, 4, 3]), + gleam@should:equal(gleam@list:intersperse([], 2), []). + +at_test() -> + gleam@should:equal(gleam@list:at([1, 2, 3], 2), {ok, 3}), + gleam@should:equal(gleam@list:at([1, 2, 3], 5), {error, nil}), + gleam@should:equal(gleam@list:at([], 0), {error, nil}), + gleam@should:equal(gleam@list:at([1, 2, 3, 4, 5, 6], -1), {error, nil}). + +unique_test() -> + gleam@should:equal( + gleam@list:unique([1, 1, 2, 3, 4, 4, 4, 5, 6]), + [1, 2, 3, 4, 5, 6] + ), + gleam@should:equal( + gleam@list:unique([7, 1, 45, 6, 2, 47, 2, 7, 5]), + [7, 1, 45, 6, 2, 47, 5] + ), + gleam@should:equal(gleam@list:unique([3, 4, 5]), [3, 4, 5]), + gleam@should:equal(gleam@list:unique([]), []). + +sort_test() -> + gleam@should:equal( + gleam@list:sort([4, 3, 6, 5, 4], fun gleam@int:compare/2), + [3, 4, 4, 5, 6] + ), + gleam@should:equal( + gleam@list:sort([4, 3, 6, 5, 4, 1], fun gleam@int:compare/2), + [1, 3, 4, 4, 5, 6] + ), + gleam@should:equal( + gleam@list:sort([4.1, 3.1, 6.1, 5.1, 4.1], fun gleam@float:compare/2), + [3.1, 4.1, 4.1, 5.1, 6.1] + ), + gleam@should:equal(gleam@list:sort([], fun gleam@int:compare/2), []). + +index_map_test() -> + gleam@should:equal( + gleam@list:index_map([3, 4, 5], fun(I, X) -> {I, X} end), + [{0, 3}, {1, 4}, {2, 5}] + ), + F = fun(I@1, X@1) -> gleam@string:append(X@1, gleam@int:to_string(I@1)) end, + gleam@should:equal( + gleam@list:index_map([<<"a"/utf8>>, <<"b"/utf8>>, <<"c"/utf8>>], F), + [<<"a0"/utf8>>, <<"b1"/utf8>>, <<"c2"/utf8>>] + ). + +range_test() -> + gleam@should:equal(gleam@list:range(0, 0), []), + gleam@should:equal(gleam@list:range(1, 1), []), + gleam@should:equal(gleam@list:range(-1, -1), []), + gleam@should:equal(gleam@list:range(0, 1), [0]), + gleam@should:equal(gleam@list:range(0, 5), [0, 1, 2, 3, 4]), + gleam@should:equal(gleam@list:range(1, -5), [1, 0, -1, -2, -3, -4]). + +repeat_test() -> + gleam@should:equal(gleam@list:repeat(1, -10), []), + gleam@should:equal(gleam@list:repeat(1, 0), []), + gleam@should:equal(gleam@list:repeat(2, 3), [2, 2, 2]), + gleam@should:equal( + gleam@list:repeat(<<"x"/utf8>>, 5), + [<<"x"/utf8>>, <<"x"/utf8>>, <<"x"/utf8>>, <<"x"/utf8>>, <<"x"/utf8>>] + ). + +split_test() -> + gleam@should:equal(gleam@list:split([], 0), {[], []}), + gleam@should:equal( + gleam@list:split([0, 1, 2, 3, 4], 0), + {[], [0, 1, 2, 3, 4]} + ), + gleam@should:equal( + gleam@list:split([0, 1, 2, 3, 4], -2), + {[], [0, 1, 2, 3, 4]} + ), + gleam@should:equal( + gleam@list:split([0, 1, 2, 3, 4], 1), + {[0], [1, 2, 3, 4]} + ), + gleam@should:equal( + gleam@list:split([0, 1, 2, 3, 4], 3), + {[0, 1, 2], [3, 4]} + ), + gleam@should:equal( + gleam@list:split([0, 1, 2, 3, 4], 9), + {[0, 1, 2, 3, 4], []} + ). + +split_while_test() -> + gleam@should:equal( + gleam@list:split_while([], fun(X) -> X =< 5 end), + {[], []} + ), + gleam@should:equal( + gleam@list:split_while([1, 2, 3, 4, 5], fun(X@1) -> X@1 =< 5 end), + {[1, 2, 3, 4, 5], []} + ), + gleam@should:equal( + gleam@list:split_while([1, 2, 3, 4, 5], fun(X@2) -> X@2 =:= 2 end), + {[], [1, 2, 3, 4, 5]} + ), + gleam@should:equal( + gleam@list:split_while([1, 2, 3, 4, 5], fun(X@3) -> X@3 =< 3 end), + {[1, 2, 3], [4, 5]} + ), + gleam@should:equal( + gleam@list:split_while([1, 2, 3, 4, 5], fun(X@4) -> X@4 =< -3 end), + {[], [1, 2, 3, 4, 5]} + ). + +key_find_test() -> + Proplist = [{0, <<"1"/utf8>>}, {1, <<"2"/utf8>>}], + gleam@should:equal(gleam@list:key_find(Proplist, 0), {ok, <<"1"/utf8>>}), + gleam@should:equal(gleam@list:key_find(Proplist, 1), {ok, <<"2"/utf8>>}), + gleam@should:equal(gleam@list:key_find(Proplist, 2), {error, nil}). + +pop_test() -> + gleam@should:equal( + gleam@list:pop([1, 2, 3], fun(X) -> X > 2 end), + {ok, {3, [1, 2]}} + ), + gleam@should:equal( + gleam@list:pop([1, 2, 3], fun(X@1) -> X@1 > 4 end), + {error, nil} + ), + gleam@should:equal(gleam@list:pop([], fun(_) -> true end), {error, nil}). + +pop_map_test() -> + gleam@should:equal( + gleam@list:pop_map( + [<<"foo"/utf8>>, <<"2"/utf8>>, <<"3"/utf8>>], + fun gleam@int:parse/1 + ), + {ok, {2, [<<"foo"/utf8>>, <<"3"/utf8>>]}} + ), + gleam@should:equal( + gleam@list:pop_map( + [<<"foo"/utf8>>, <<"bar"/utf8>>], + fun gleam@int:parse/1 + ), + {error, nil} + ), + gleam@should:equal( + gleam@list:pop_map([], fun gleam@int:parse/1), + {error, nil} + ). + +key_pop_test() -> + gleam@should:equal( + gleam@list:key_pop([{<<"a"/utf8>>, 0}, {<<"b"/utf8>>, 1}], <<"a"/utf8>>), + {ok, {0, [{<<"b"/utf8>>, 1}]}} + ), + gleam@should:equal( + gleam@list:key_pop([{<<"a"/utf8>>, 0}, {<<"b"/utf8>>, 1}], <<"b"/utf8>>), + {ok, {1, [{<<"a"/utf8>>, 0}]}} + ), + gleam@should:equal( + gleam@list:key_pop([{<<"a"/utf8>>, 0}, {<<"b"/utf8>>, 1}], <<"c"/utf8>>), + {error, nil} + ). + +key_set_test() -> + gleam@should:equal( + gleam@list:key_set([{5, 0}, {4, 1}], 4, 100), + [{5, 0}, {4, 100}] + ), + gleam@should:equal( + gleam@list:key_set([{5, 0}, {4, 1}], 1, 100), + [{5, 0}, {4, 1}, {1, 100}] + ). + +partition_test() -> + gleam@should:equal( + gleam@list:partition([1, 2, 3, 4, 5, 6, 7], fun gleam@int:is_odd/1), + {[1, 3, 5, 7], [2, 4, 6]} + ). + +permutations_test() -> + gleam@should:equal(gleam@list:permutations([1, 2]), [[1, 2], [2, 1]]), + Expected = [[1, 2, 3], + [1, 3, 2], + [2, 1, 3], + [2, 3, 1], + [3, 1, 2], + [3, 2, 1]], + gleam@should:equal(gleam@list:permutations([1, 2, 3]), Expected), + gleam@should:equal( + gleam@list:permutations([<<"a"/utf8>>, <<"b"/utf8>>]), + [[<<"a"/utf8>>, <<"b"/utf8>>], [<<"b"/utf8>>, <<"a"/utf8>>]] + ). diff --git a/gleam_providers/deps/gleam_stdlib/gen/test/gleam@map_test.erl b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@map_test.erl new file mode 100644 index 0000000..b5d83cf --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@map_test.erl @@ -0,0 +1,188 @@ +-module(gleam@map_test). +-compile(no_auto_import). + +-export([from_list_test/0, has_key_test/0, new_test/0, get_test/0, insert_test/0, map_values_test/0, keys_test/0, values_test/0, take_test/0, drop_test/0, merge_test/0, delete_test/0, update_test/0, fold_test/0]). + +from_list_test() -> + gleam@should:equal(gleam@map:size(gleam@map:from_list([{4, 0}, {1, 0}])), 2), + gleam@should:equal( + gleam@map:from_list([{1, 0}, {1, 1}]), + gleam@map:from_list([{1, 1}]) + ). + +has_key_test() -> + gleam@should:be_false(gleam@map:has_key(gleam@map:from_list([]), 1)), + gleam@should:be_true(gleam@map:has_key(gleam@map:from_list([{1, 0}]), 1)), + gleam@should:be_true( + gleam@map:has_key(gleam@map:from_list([{4, 0}, {1, 0}]), 1) + ), + gleam@should:be_false( + gleam@map:has_key(gleam@map:from_list([{4, 0}, {1, 0}]), 0) + ). + +new_test() -> + gleam@should:equal(gleam@map:size(gleam@map:new()), 0), + gleam@should:equal(gleam@map:to_list(gleam@map:new()), []). + +get_test() -> + Proplist = [{4, 0}, {1, 1}], + M = gleam@map:from_list(Proplist), + gleam@should:equal(gleam@map:get(M, 4), {ok, 0}), + gleam@should:equal(gleam@map:get(M, 1), {ok, 1}), + gleam@should:equal(gleam@map:get(M, 2), {error, nil}). + +insert_test() -> + gleam@should:equal( + gleam@map:insert( + gleam@map:insert( + gleam@map:insert(gleam@map:new(), <<"a"/utf8>>, 0), + <<"b"/utf8>>, + 1 + ), + <<"c"/utf8>>, + 2 + ), + gleam@map:from_list( + [{<<"a"/utf8>>, 0}, {<<"b"/utf8>>, 1}, {<<"c"/utf8>>, 2}] + ) + ). + +map_values_test() -> + gleam@should:equal( + gleam@map:map_values( + gleam@map:from_list([{1, 0}, {2, 1}, {3, 2}]), + fun(K, V) -> K + V end + ), + gleam@map:from_list([{1, 1}, {2, 3}, {3, 5}]) + ). + +keys_test() -> + gleam@should:equal( + gleam@map:keys( + gleam@map:from_list( + [{<<"a"/utf8>>, 0}, {<<"b"/utf8>>, 1}, {<<"c"/utf8>>, 2}] + ) + ), + [<<"a"/utf8>>, <<"b"/utf8>>, <<"c"/utf8>>] + ). + +values_test() -> + gleam@should:equal( + gleam@map:values( + gleam@map:from_list( + [{<<"a"/utf8>>, 0}, {<<"b"/utf8>>, 1}, {<<"c"/utf8>>, 2}] + ) + ), + [0, 1, 2] + ). + +take_test() -> + gleam@should:equal( + gleam@map:take( + gleam@map:from_list( + [{<<"a"/utf8>>, 0}, {<<"b"/utf8>>, 1}, {<<"c"/utf8>>, 2}] + ), + [<<"a"/utf8>>, <<"b"/utf8>>, <<"d"/utf8>>] + ), + gleam@map:from_list([{<<"a"/utf8>>, 0}, {<<"b"/utf8>>, 1}]) + ). + +drop_test() -> + gleam@should:equal( + gleam@map:drop( + gleam@map:from_list( + [{<<"a"/utf8>>, 0}, {<<"b"/utf8>>, 1}, {<<"c"/utf8>>, 2}] + ), + [<<"a"/utf8>>, <<"b"/utf8>>, <<"d"/utf8>>] + ), + gleam@map:from_list([{<<"c"/utf8>>, 2}]) + ). + +merge_test() -> + A = gleam@map:from_list( + [{<<"a"/utf8>>, 2}, {<<"c"/utf8>>, 4}, {<<"d"/utf8>>, 3}] + ), + B = gleam@map:from_list( + [{<<"a"/utf8>>, 0}, {<<"b"/utf8>>, 1}, {<<"c"/utf8>>, 2}] + ), + gleam@should:equal( + gleam@map:merge(A, B), + gleam@map:from_list( + [{<<"a"/utf8>>, 0}, + {<<"b"/utf8>>, 1}, + {<<"c"/utf8>>, 2}, + {<<"d"/utf8>>, 3}] + ) + ), + gleam@should:equal( + gleam@map:merge(B, A), + gleam@map:from_list( + [{<<"a"/utf8>>, 2}, + {<<"b"/utf8>>, 1}, + {<<"c"/utf8>>, 4}, + {<<"d"/utf8>>, 3}] + ) + ). + +delete_test() -> + gleam@should:equal( + gleam@map:delete( + gleam@map:delete( + gleam@map:from_list( + [{<<"a"/utf8>>, 0}, {<<"b"/utf8>>, 1}, {<<"c"/utf8>>, 2}] + ), + <<"a"/utf8>> + ), + <<"d"/utf8>> + ), + gleam@map:from_list([{<<"b"/utf8>>, 1}, {<<"c"/utf8>>, 2}]) + ). + +update_test() -> + Dict = gleam@map:from_list( + [{<<"a"/utf8>>, 0}, {<<"b"/utf8>>, 1}, {<<"c"/utf8>>, 2}] + ), + Inc_or_zero = fun(X) -> case X of + {ok, I} -> + I + 1; + + {error, _} -> + 0 + end end, + gleam@should:equal( + gleam@map:update(Dict, <<"a"/utf8>>, Inc_or_zero), + gleam@map:from_list( + [{<<"a"/utf8>>, 1}, {<<"b"/utf8>>, 1}, {<<"c"/utf8>>, 2}] + ) + ), + gleam@should:equal( + gleam@map:update(Dict, <<"b"/utf8>>, Inc_or_zero), + gleam@map:from_list( + [{<<"a"/utf8>>, 0}, {<<"b"/utf8>>, 2}, {<<"c"/utf8>>, 2}] + ) + ), + gleam@should:equal( + gleam@map:update(Dict, <<"z"/utf8>>, Inc_or_zero), + gleam@map:from_list( + [{<<"a"/utf8>>, 0}, + {<<"b"/utf8>>, 1}, + {<<"c"/utf8>>, 2}, + {<<"z"/utf8>>, 0}] + ) + ). + +fold_test() -> + Dict = gleam@map:from_list( + [{<<"a"/utf8>>, 0}, + {<<"b"/utf8>>, 1}, + {<<"c"/utf8>>, 2}, + {<<"d"/utf8>>, 3}] + ), + Add = fun(_, V, Acc) -> V + Acc end, + gleam@should:equal(gleam@map:fold(Dict, 0, Add), 6), + Concat = fun(K, _, Acc@1) -> gleam@string:append(Acc@1, K) end, + gleam@should:equal( + gleam@map:fold(Dict, <<""/utf8>>, Concat), + <<"abcd"/utf8>> + ), + gleam@should:equal(gleam@map:fold(gleam@map:from_list([]), 0, Add), 0). diff --git a/gleam_providers/deps/gleam_stdlib/gen/test/gleam@option_test.erl b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@option_test.erl new file mode 100644 index 0000000..b38c30f --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@option_test.erl @@ -0,0 +1,69 @@ +-module(gleam@option_test). +-compile(no_auto_import). + +-export([is_some_test/0, is_none_test/0, to_result_test/0, from_result_test/0, unwrap_option_test/0, map_option_test/0, flatten_option_test/0, then_option_test/0, or_option_test/0]). + +is_some_test() -> + gleam@should:be_true(gleam@option:is_some({some, 1})), + gleam@should:be_false(gleam@option:is_some(none)). + +is_none_test() -> + gleam@should:be_false(gleam@option:is_none({some, 1})), + gleam@should:be_true(gleam@option:is_none(none)). + +to_result_test() -> + gleam@should:equal( + gleam@option:to_result({some, 1}, <<"possible_error"/utf8>>), + {ok, 1} + ), + gleam@should:equal( + gleam@option:to_result(none, <<"possible_error"/utf8>>), + {error, <<"possible_error"/utf8>>} + ). + +from_result_test() -> + gleam@should:equal(gleam@option:from_result({ok, 1}), {some, 1}), + gleam@should:equal( + gleam@option:from_result({error, <<"some_error"/utf8>>}), + none + ). + +unwrap_option_test() -> + gleam@should:equal(gleam@option:unwrap({some, 1}, 0), 1), + gleam@should:equal(gleam@option:unwrap(none, 0), 0). + +map_option_test() -> + gleam@should:equal( + gleam@option:map({some, 1}, fun(X) -> X + 1 end), + {some, 2} + ), + gleam@should:equal( + gleam@option:map({some, 1}, fun(_) -> <<"2"/utf8>> end), + {some, <<"2"/utf8>>} + ), + gleam@should:equal(gleam@option:map(none, fun(X@1) -> X@1 + 1 end), none). + +flatten_option_test() -> + gleam@should:equal(gleam@option:flatten({some, {some, 1}}), {some, 1}), + gleam@should:equal(gleam@option:flatten({some, none}), none), + gleam@should:equal(gleam@option:flatten(none), none). + +then_option_test() -> + gleam@should:equal( + gleam@option:then({some, 1}, fun(X) -> {some, X + 1} end), + {some, 2} + ), + gleam@should:equal( + gleam@option:then({some, 1}, fun(_) -> {some, <<"2"/utf8>>} end), + {some, <<"2"/utf8>>} + ), + gleam@should:equal( + gleam@option:then(none, fun(X@1) -> {some, X@1 + 1} end), + none + ). + +or_option_test() -> + gleam@should:equal(gleam@option:'or'({some, 1}, {some, 2}), {some, 1}), + gleam@should:equal(gleam@option:'or'({some, 1}, none), {some, 1}), + gleam@should:equal(gleam@option:'or'(none, {some, 2}), {some, 2}), + gleam@should:equal(gleam@option:'or'(none, none), none). diff --git a/gleam_providers/deps/gleam_stdlib/gen/test/gleam@order_test.erl b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@order_test.erl new file mode 100644 index 0000000..771e72c --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@order_test.erl @@ -0,0 +1,47 @@ +-module(gleam@order_test). +-compile(no_auto_import). + +-export([reverse_test/0, to_int_test/0, compare_test/0, max_test/0, min_test/0]). + +reverse_test() -> + gleam@should:equal(gleam@order:reverse(lt), gt), + gleam@should:equal(gleam@order:reverse(eq), eq), + gleam@should:equal(gleam@order:reverse(gt), lt). + +to_int_test() -> + gleam@should:equal(gleam@order:to_int(lt), -1), + gleam@should:equal(gleam@order:to_int(eq), 0), + gleam@should:equal(gleam@order:to_int(gt), 1). + +compare_test() -> + gleam@should:equal(gleam@order:compare(lt, lt), eq), + gleam@should:equal(gleam@order:compare(lt, eq), lt), + gleam@should:equal(gleam@order:compare(lt, gt), lt), + gleam@should:equal(gleam@order:compare(eq, lt), gt), + gleam@should:equal(gleam@order:compare(eq, eq), eq), + gleam@should:equal(gleam@order:compare(eq, gt), lt), + gleam@should:equal(gleam@order:compare(gt, lt), gt), + gleam@should:equal(gleam@order:compare(gt, eq), gt), + gleam@should:equal(gleam@order:compare(gt, gt), eq). + +max_test() -> + gleam@should:equal(gleam@order:max(lt, lt), lt), + gleam@should:equal(gleam@order:max(lt, eq), eq), + gleam@should:equal(gleam@order:max(lt, gt), gt), + gleam@should:equal(gleam@order:max(eq, lt), eq), + gleam@should:equal(gleam@order:max(eq, eq), eq), + gleam@should:equal(gleam@order:max(eq, gt), gt), + gleam@should:equal(gleam@order:max(gt, lt), gt), + gleam@should:equal(gleam@order:max(gt, eq), gt), + gleam@should:equal(gleam@order:max(gt, gt), gt). + +min_test() -> + gleam@should:equal(gleam@order:min(lt, lt), lt), + gleam@should:equal(gleam@order:min(lt, eq), lt), + gleam@should:equal(gleam@order:min(lt, gt), lt), + gleam@should:equal(gleam@order:min(eq, lt), lt), + gleam@should:equal(gleam@order:min(eq, eq), eq), + gleam@should:equal(gleam@order:min(eq, gt), eq), + gleam@should:equal(gleam@order:min(gt, lt), lt), + gleam@should:equal(gleam@order:min(gt, eq), eq), + gleam@should:equal(gleam@order:min(gt, gt), gt). diff --git a/gleam_providers/deps/gleam_stdlib/gen/test/gleam@os_test.erl b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@os_test.erl new file mode 100644 index 0000000..2f7c2a3 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@os_test.erl @@ -0,0 +1,44 @@ +-module(gleam@os_test). +-compile(no_auto_import). + +-export([env_test/0, system_time_test/0, erlang_timestamp_test/0]). + +env_test() -> + gleam@os:insert_env(<<"GLEAM_TEST"/utf8>>, <<"hello"/utf8>>), + gleam@should:equal( + gleam@map:get(gleam@os:get_env(), <<"GLEAM_TEST"/utf8>>), + {ok, <<"hello"/utf8>>} + ), + gleam@os:delete_env(<<"GLEAM_TEST"/utf8>>), + gleam@should:equal( + gleam@map:get(gleam@os:get_env(), <<"GLEAM_TEST"/utf8>>), + {error, nil} + ). + +system_time_test() -> + June_12_2020 = 1591966971, + gleam@should:equal(gleam@os:system_time(second) > June_12_2020, true), + gleam@should:equal( + gleam@os:system_time(second) + < (June_12_2020 + * 1000), + true + ), + gleam@should:equal( + gleam@os:system_time(millisecond) + > (June_12_2020 + * 1000), + true + ), + gleam@should:equal( + gleam@os:system_time(millisecond) + < (June_12_2020 + * 1000000), + true + ). + +erlang_timestamp_test() -> + June_12_2020 = 1591966971000000, + {Mega_seconds, Seconds, Micro_seconds} = gleam@os:erlang_timestamp(), + Stamp_as_micro = (((Mega_seconds * 1000000) + Seconds) * 1000000) + Micro_seconds, + gleam@should:be_true(Stamp_as_micro > June_12_2020). diff --git a/gleam_providers/deps/gleam_stdlib/gen/test/gleam@pair_test.erl b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@pair_test.erl new file mode 100644 index 0000000..d073e19 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@pair_test.erl @@ -0,0 +1,29 @@ +-module(gleam@pair_test). +-compile(no_auto_import). + +-export([first_test/0, second_test/0, swap_test/0, map_first_test/0, map_second_test/0]). + +first_test() -> + gleam@should:equal(gleam@pair:first({1, 2}), 1), + gleam@should:equal(gleam@pair:first({<<"abc"/utf8>>, []}), <<"abc"/utf8>>). + +second_test() -> + gleam@should:equal(gleam@pair:second({1, 2}), 2), + gleam@should:equal(gleam@pair:second({<<"abc"/utf8>>, []}), []). + +swap_test() -> + gleam@should:equal(gleam@pair:swap({1, <<"2"/utf8>>}), {<<"2"/utf8>>, 1}). + +map_first_test() -> + Inc = fun(A) -> A + 1 end, + gleam@should:equal(gleam@pair:map_first({1, 2}, Inc), {2, 2}), + gleam@should:equal(gleam@pair:map_first({8, 2}, Inc), {9, 2}), + gleam@should:equal(gleam@pair:map_first({0, -2}, Inc), {1, -2}), + gleam@should:equal(gleam@pair:map_first({-10, 20}, Inc), {-9, 20}). + +map_second_test() -> + Dec = fun(A) -> A - 1 end, + gleam@should:equal(gleam@pair:map_second({1, 2}, Dec), {1, 1}), + gleam@should:equal(gleam@pair:map_second({8, 2}, Dec), {8, 1}), + gleam@should:equal(gleam@pair:map_second({0, -2}, Dec), {0, -3}), + gleam@should:equal(gleam@pair:map_second({-10, 20}, Dec), {-10, 19}). diff --git a/gleam_providers/deps/gleam_stdlib/gen/test/gleam@queue_test.erl b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@queue_test.erl new file mode 100644 index 0000000..fa86461 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@queue_test.erl @@ -0,0 +1,288 @@ +-module(gleam@queue_test). +-compile(no_auto_import). + +-export([from_and_to_list_test/0, is_empty_test/0, length_test/0, push_back_test/0, push_front_test/0, push_test/0, pop_back_test/0, pop_back_after_push_back_test/0, pop_back_after_push_test/0, pop_back_empty_test/0, pop_front_test/0, pop_front_after_push_front_test/0, pop_front_after_push_test/0, pop_front_empty_test/0, reverse_test/0, is_equal_test/0, is_logically_equal_test/0]). + +from_and_to_list_test() -> + gleam@should:equal(gleam@queue:from_list([]), gleam@queue:new()), + gleam@should:equal( + gleam@queue:to_list(gleam@queue:from_list([1, 2, 3])), + [1, 2, 3] + ). + +is_empty_test() -> + gleam@should:be_true(gleam@queue:is_empty(gleam@queue:new())), + gleam@should:be_false( + gleam@queue:is_empty(gleam@queue:from_list([<<""/utf8>>])) + ). + +length_test() -> + Test = fun(Input) -> + gleam@should:equal( + gleam@queue:length(gleam@queue:from_list(Input)), + gleam@list:length(Input) + ) + end, + Test([]), + Test([1]), + Test([1, 2]), + Test([1, 2, 1]), + Test([1, 2, 1, 5, 2, 7, 2, 7, 8, 4, 545]). + +push_back_test() -> + gleam@should:equal( + gleam@queue:to_list( + gleam@queue:push_back(gleam@queue:from_list([1, 2]), 3) + ), + [1, 2, 3] + ), + gleam@should:equal( + gleam@queue:to_list( + gleam@queue:push_back( + gleam@queue:push_back( + gleam@queue:push_back(gleam@queue:new(), 1), + 2 + ), + 3 + ) + ), + [1, 2, 3] + ). + +push_front_test() -> + gleam@should:equal( + gleam@queue:to_list( + gleam@queue:push_front( + gleam@queue:push_front(gleam@queue:from_list([2, 3]), 1), + 0 + ) + ), + [0, 1, 2, 3] + ). + +push_test() -> + gleam@should:equal( + gleam@queue:to_list( + gleam@queue:push_back( + gleam@queue:push_front( + gleam@queue:push_back( + gleam@queue:push_front(gleam@queue:new(), <<"b"/utf8>>), + <<"x"/utf8>> + ), + <<"a"/utf8>> + ), + <<"y"/utf8>> + ) + ), + [<<"a"/utf8>>, <<"b"/utf8>>, <<"x"/utf8>>, <<"y"/utf8>>] + ). + +pop_back_test() -> + {ok, Tup} = gleam@queue:pop_back(gleam@queue:from_list([1, 2, 3])), + gleam@should:equal(gleam@pair:first(Tup), 3), + gleam@should:be_true( + gleam@queue:is_equal( + gleam@pair:second(Tup), + gleam@queue:from_list([1, 2]) + ) + ). + +pop_back_after_push_back_test() -> + {ok, Tup} = gleam@queue:pop_back( + gleam@queue:push_back( + gleam@queue:push_back( + gleam@queue:push_back(gleam@queue:new(), 1), + 2 + ), + 3 + ) + ), + gleam@should:equal(gleam@pair:first(Tup), 3). + +pop_back_after_push_test() -> + {ok, Tup} = gleam@queue:pop_back( + gleam@queue:push_back( + gleam@queue:push_front( + gleam@queue:push_back( + gleam@queue:push_front(gleam@queue:new(), <<"b"/utf8>>), + <<"x"/utf8>> + ), + <<"a"/utf8>> + ), + <<"y"/utf8>> + ) + ), + gleam@should:equal(gleam@pair:first(Tup), <<"y"/utf8>>). + +pop_back_empty_test() -> + gleam@should:equal( + gleam@queue:pop_back(gleam@queue:from_list([])), + {error, nil} + ). + +pop_front_test() -> + {ok, Tup} = gleam@queue:pop_front(gleam@queue:from_list([1, 2, 3])), + gleam@should:equal(gleam@pair:first(Tup), 1), + gleam@should:be_true( + gleam@queue:is_equal( + gleam@pair:second(Tup), + gleam@queue:from_list([2, 3]) + ) + ). + +pop_front_after_push_front_test() -> + {ok, Tup} = gleam@queue:pop_front( + gleam@queue:push_front( + gleam@queue:push_front( + gleam@queue:push_front(gleam@queue:new(), 3), + 2 + ), + 1 + ) + ), + gleam@should:equal(gleam@pair:first(Tup), 1). + +pop_front_after_push_test() -> + {ok, Tup} = gleam@queue:pop_front( + gleam@queue:push_front( + gleam@queue:push_back( + gleam@queue:push_front(gleam@queue:new(), <<"b"/utf8>>), + <<"x"/utf8>> + ), + <<"a"/utf8>> + ) + ), + gleam@should:equal(gleam@pair:first(Tup), <<"a"/utf8>>). + +pop_front_empty_test() -> + gleam@should:equal( + gleam@queue:pop_front(gleam@queue:from_list([])), + {error, nil} + ). + +reverse_test() -> + gleam@should:equal( + gleam@queue:to_list( + gleam@queue:reverse(gleam@queue:from_list([1, 2, 3])) + ), + [3, 2, 1] + ), + gleam@should:equal( + gleam@queue:to_list( + gleam@queue:reverse( + gleam@queue:push_back( + gleam@queue:push_back( + gleam@queue:push_back(gleam@queue:new(), 1), + 2 + ), + 3 + ) + ) + ), + [3, 2, 1] + ), + gleam@should:equal( + gleam@queue:to_list( + gleam@queue:reverse( + gleam@queue:push_front( + gleam@queue:push_front( + gleam@queue:push_front(gleam@queue:new(), 1), + 2 + ), + 3 + ) + ) + ), + [1, 2, 3] + ), + gleam@should:equal( + gleam@queue:to_list( + gleam@queue:reverse( + gleam@queue:push_back( + gleam@queue:push_back( + gleam@queue:push_front( + gleam@queue:push_front(gleam@queue:new(), 1), + 2 + ), + 3 + ), + 4 + ) + ) + ), + [4, 3, 1, 2] + ). + +is_equal_test() -> + Should_equal = fun(A, B) -> + gleam@should:be_true(gleam@queue:is_equal(A, B)) + end, + Should_not_equal = fun(A@1, B@1) -> + gleam@should:be_false(gleam@queue:is_equal(A@1, B@1)) + end, + Should_equal(gleam@queue:new(), gleam@queue:new()), + Should_equal( + gleam@queue:push_front(gleam@queue:new(), 1), + gleam@queue:push_back(gleam@queue:new(), 1) + ), + Should_equal( + gleam@queue:push_front(gleam@queue:new(), 1), + gleam@queue:push_front(gleam@queue:new(), 1) + ), + Should_equal( + gleam@queue:push_back(gleam@queue:push_back(gleam@queue:new(), 1), 2), + gleam@queue:push_front(gleam@queue:push_front(gleam@queue:new(), 2), 1) + ), + Should_not_equal( + gleam@queue:push_back(gleam@queue:new(), 1), + gleam@queue:push_front(gleam@queue:push_front(gleam@queue:new(), 2), 1) + ), + Should_not_equal( + gleam@queue:push_back(gleam@queue:push_back(gleam@queue:new(), 2), 1), + gleam@queue:push_front(gleam@queue:push_front(gleam@queue:new(), 2), 1) + ). + +is_logically_equal_test() -> + Both_even_or_odd = fun(A, B) -> + gleam@int:is_even(A) + =:= gleam@int:is_even(B) + end, + Should_equal = fun(A@1, B@1) -> + gleam@should:be_true( + gleam@queue:is_logically_equal(A@1, B@1, Both_even_or_odd) + ) + end, + Should_not_equal = fun(A@2, B@2) -> + gleam@should:be_false( + gleam@queue:is_logically_equal(A@2, B@2, Both_even_or_odd) + ) + end, + Should_equal(gleam@queue:new(), gleam@queue:new()), + Should_equal( + gleam@queue:push_front(gleam@queue:new(), 3), + gleam@queue:push_back(gleam@queue:new(), 1) + ), + Should_equal( + gleam@queue:push_front(gleam@queue:new(), 4), + gleam@queue:push_back(gleam@queue:new(), 2) + ), + Should_equal( + gleam@queue:push_front(gleam@queue:new(), 3), + gleam@queue:push_front(gleam@queue:new(), 1) + ), + Should_equal( + gleam@queue:push_back(gleam@queue:push_back(gleam@queue:new(), 3), 4), + gleam@queue:push_front(gleam@queue:push_front(gleam@queue:new(), 2), 1) + ), + Should_not_equal( + gleam@queue:push_back(gleam@queue:new(), 1), + gleam@queue:push_front(gleam@queue:push_front(gleam@queue:new(), 2), 1) + ), + Should_not_equal( + gleam@queue:push_back(gleam@queue:push_back(gleam@queue:new(), 2), 1), + gleam@queue:push_front(gleam@queue:push_front(gleam@queue:new(), 2), 1) + ), + Should_not_equal( + gleam@queue:push_back(gleam@queue:push_back(gleam@queue:new(), 4), 3), + gleam@queue:push_front(gleam@queue:push_front(gleam@queue:new(), 2), 1) + ). diff --git a/gleam_providers/deps/gleam_stdlib/gen/test/gleam@regex_test.erl b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@regex_test.erl new file mode 100644 index 0000000..ea95075 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@regex_test.erl @@ -0,0 +1,59 @@ +-module(gleam@regex_test). +-compile(no_auto_import). + +-export([from_string_test/0, compile_test/0, check_test/0, split_test/0, scan_test/0]). + +from_string_test() -> + {ok, Re} = gleam@regex:from_string(<<"[0-9]"/utf8>>), + gleam@should:equal(gleam@regex:check(Re, <<"abc123"/utf8>>), true), + gleam@should:equal(gleam@regex:check(Re, <<"abcxyz"/utf8>>), false), + {error, From_string_err} = gleam@regex:from_string(<<"[0-9"/utf8>>), + gleam@should:equal( + From_string_err, + {compile_error, <<"missing terminating ] for character class"/utf8>>, 4} + ). + +compile_test() -> + Options = {options, true, false}, + {ok, Re} = gleam@regex:compile(<<"[A-B]"/utf8>>, Options), + gleam@should:equal(gleam@regex:check(Re, <<"abc123"/utf8>>), true), + Options@1 = {options, false, true}, + {ok, Re@1} = gleam@regex:compile(<<"^[0-9]"/utf8>>, Options@1), + gleam@should:equal(gleam@regex:check(Re@1, <<"abc\n123"/utf8>>), true). + +check_test() -> + {ok, Re} = gleam@regex:from_string(<<"^f.o.?"/utf8>>), + gleam@should:equal(gleam@regex:check(Re, <<"foo"/utf8>>), true), + gleam@should:equal(gleam@regex:check(Re, <<"boo"/utf8>>), false). + +split_test() -> + {ok, Re} = gleam@regex:from_string(<<" *, *"/utf8>>), + gleam@should:equal( + gleam@regex:split(Re, <<"foo,32, 4, 9 ,0"/utf8>>), + [<<"foo"/utf8>>, + <<"32"/utf8>>, + <<"4"/utf8>>, + <<"9"/utf8>>, + <<"0"/utf8>>] + ). + +scan_test() -> + {ok, Re} = gleam@regex:from_string(<<"Gl\\w+"/utf8>>), + gleam@should:equal( + gleam@regex:scan(Re, <<"!Gleam"/utf8>>), + [{match, <<"Gleam"/utf8>>, 1, []}] + ), + gleam@should:equal( + gleam@regex:scan(Re, <<"हGleam"/utf8>>), + [{match, <<"Gleam"/utf8>>, 3, []}] + ), + gleam@should:equal( + gleam@regex:scan(Re, <<"𐍈Gleam"/utf8>>), + [{match, <<"Gleam"/utf8>>, 4, []}] + ), + {ok, Re@1} = gleam@regex:from_string(<<"[oi]n a(.?) (\\w+)"/utf8>>), + gleam@should:equal( + gleam@regex:scan(Re@1, <<"I am on a boat in a lake."/utf8>>), + [{match, <<"on a boat"/utf8>>, 5, [none, {some, <<"boat"/utf8>>}]}, + {match, <<"in a lake"/utf8>>, 15, [none, {some, <<"lake"/utf8>>}]}] + ). diff --git a/gleam_providers/deps/gleam_stdlib/gen/test/gleam@result_test.erl b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@result_test.erl new file mode 100644 index 0000000..3920ba2 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@result_test.erl @@ -0,0 +1,133 @@ +-module(gleam@result_test). +-compile(no_auto_import). + +-export([is_ok_test/0, is_error_test/0, map_test/0, map_error_test/0, flatten_test/0, then_test/0, unwrap_test/0, lazy_unwrap_test/0, nil_error_test/0, or_test/0, lazy_or_test/0, all_test/0]). + +is_ok_test() -> + gleam@should:be_true(gleam@result:is_ok({ok, 1})), + gleam@should:be_false(gleam@result:is_ok({error, 1})). + +is_error_test() -> + gleam@should:be_false(gleam@result:is_error({ok, 1})), + gleam@should:be_true(gleam@result:is_error({error, 1})). + +map_test() -> + gleam@should:equal(gleam@result:map({ok, 1}, fun(X) -> X + 1 end), {ok, 2}), + gleam@should:equal( + gleam@result:map({ok, 1}, fun(_) -> <<"2"/utf8>> end), + {ok, <<"2"/utf8>>} + ), + gleam@should:equal( + gleam@result:map({error, 1}, fun(X@1) -> X@1 + 1 end), + {error, 1} + ). + +map_error_test() -> + gleam@should:equal( + gleam@result:map_error({ok, 1}, fun(X) -> X + 1 end), + {ok, 1} + ), + gleam@should:equal( + gleam@result:map_error( + {error, 1}, + fun(X@1) -> {<<"ok"/utf8>>, X@1 + 1} end + ), + {error, {<<"ok"/utf8>>, 2}} + ). + +flatten_test() -> + gleam@should:equal(gleam@result:flatten({ok, {ok, 1}}), {ok, 1}), + gleam@should:equal(gleam@result:flatten({ok, {error, 1}}), {error, 1}), + gleam@should:equal(gleam@result:flatten({error, 1}), {error, 1}), + gleam@should:equal( + gleam@result:flatten({error, {error, 1}}), + {error, {error, 1}} + ). + +then_test() -> + gleam@should:equal( + gleam@result:then({error, 1}, fun(X) -> {ok, X + 1} end), + {error, 1} + ), + gleam@should:equal( + gleam@result:then({ok, 1}, fun(X@1) -> {ok, X@1 + 1} end), + {ok, 2} + ), + gleam@should:equal( + gleam@result:then({ok, 1}, fun(_) -> {ok, <<"type change"/utf8>>} end), + {ok, <<"type change"/utf8>>} + ), + gleam@should:equal( + gleam@result:then({ok, 1}, fun(_) -> {error, 1} end), + {error, 1} + ). + +unwrap_test() -> + gleam@should:equal(gleam@result:unwrap({ok, 1}, 50), 1), + gleam@should:equal(gleam@result:unwrap({error, <<"nope"/utf8>>}, 50), 50). + +lazy_unwrap_test() -> + gleam@should:equal(gleam@result:lazy_unwrap({ok, 1}, fun() -> 50 end), 1), + gleam@should:equal( + gleam@result:lazy_unwrap({error, <<"nope"/utf8>>}, fun() -> 50 end), + 50 + ). + +nil_error_test() -> + gleam@should:equal( + gleam@result:nil_error({error, <<"error_string"/utf8>>}), + {error, nil} + ), + gleam@should:equal(gleam@result:nil_error({error, 123}), {error, nil}), + gleam@should:equal(gleam@result:nil_error({ok, 1}), {ok, 1}). + +or_test() -> + gleam@should:equal(gleam@result:'or'({ok, 1}, {ok, 2}), {ok, 1}), + gleam@should:equal( + gleam@result:'or'({ok, 1}, {error, <<"Error 2"/utf8>>}), + {ok, 1} + ), + gleam@should:equal( + gleam@result:'or'({error, <<"Error 1"/utf8>>}, {ok, 2}), + {ok, 2} + ), + gleam@should:equal( + gleam@result:'or'( + {error, <<"Error 1"/utf8>>}, + {error, <<"Error 2"/utf8>>} + ), + {error, <<"Error 2"/utf8>>} + ). + +lazy_or_test() -> + gleam@should:equal( + gleam@result:lazy_or({ok, 1}, fun() -> {ok, 2} end), + {ok, 1} + ), + gleam@should:equal( + gleam@result:lazy_or({ok, 1}, fun() -> {error, <<"Error 2"/utf8>>} end), + {ok, 1} + ), + gleam@should:equal( + gleam@result:lazy_or({error, <<"Error 1"/utf8>>}, fun() -> {ok, 2} end), + {ok, 2} + ), + gleam@should:equal( + gleam@result:lazy_or( + {error, <<"Error 1"/utf8>>}, + fun() -> {error, <<"Error 2"/utf8>>} end + ), + {error, <<"Error 2"/utf8>>} + ). + +all_test() -> + gleam@should:equal( + gleam@result:all([{ok, 1}, {ok, 2}, {ok, 3}]), + {ok, [1, 2, 3]} + ), + gleam@should:equal( + gleam@result:all( + [{ok, 1}, {error, <<"a"/utf8>>}, {error, <<"b"/utf8>>}, {ok, 3}] + ), + {error, <<"a"/utf8>>} + ). diff --git a/gleam_providers/deps/gleam_stdlib/gen/test/gleam@set_test.erl b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@set_test.erl new file mode 100644 index 0000000..f714527 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@set_test.erl @@ -0,0 +1,101 @@ +-module(gleam@set_test). +-compile(no_auto_import). + +-export([size_test/0, contains_test/0, delete_test/0, to_list_test/0, from_list_test/0, fold_test/0, filter_test/0, take_test/0, union_test/0, intersection_test/0]). + +size_test() -> + gleam@should:equal(gleam@set:size(gleam@set:new()), 0), + gleam@should:equal( + gleam@set:size( + gleam@set:insert(gleam@set:insert(gleam@set:new(), 1), 2) + ), + 2 + ), + gleam@should:equal( + gleam@set:size( + gleam@set:insert( + gleam@set:insert(gleam@set:insert(gleam@set:new(), 1), 1), + 2 + ) + ), + 2 + ). + +contains_test() -> + gleam@should:be_true( + gleam@set:contains(gleam@set:insert(gleam@set:new(), 1), 1) + ), + gleam@should:be_false(gleam@set:contains(gleam@set:new(), 1)). + +delete_test() -> + gleam@should:be_false( + gleam@set:contains( + gleam@set:delete(gleam@set:insert(gleam@set:new(), 1), 1), + 1 + ) + ). + +to_list_test() -> + gleam@should:equal( + gleam@list:sort( + gleam@set:to_list( + gleam@set:insert( + gleam@set:insert(gleam@set:insert(gleam@set:new(), 2), 3), + 4 + ) + ), + fun gleam@int:compare/2 + ), + [2, 3, 4] + ). + +from_list_test() -> + gleam@should:equal( + gleam@list:sort( + gleam@set:to_list(gleam@set:from_list([1, 1, 2, 4, 3, 2])), + fun gleam@int:compare/2 + ), + [1, 2, 3, 4] + ). + +fold_test() -> + gleam@set:fold(gleam@set:from_list([1, 3, 9]), 0, fun(M, A) -> M + A end). + +filter_test() -> + gleam@should:equal( + gleam@set:to_list( + gleam@set:filter( + gleam@set:from_list([1, 4, 6, 3, 675, 44, 67]), + fun gleam@int:is_even/1 + ) + ), + [4, 6, 44] + ). + +take_test() -> + gleam@should:equal( + gleam@set:take(gleam@set:from_list([1, 2, 3]), [1, 3, 5]), + gleam@set:from_list([1, 3]) + ). + +union_test() -> + gleam@should:equal( + gleam@set:to_list( + gleam@set:union( + gleam@set:from_list([1, 2]), + gleam@set:from_list([2, 3]) + ) + ), + [1, 2, 3] + ). + +intersection_test() -> + gleam@should:equal( + gleam@set:to_list( + gleam@set:intersection( + gleam@set:from_list([1, 2]), + gleam@set:from_list([2, 3]) + ) + ), + [2] + ). diff --git a/gleam_providers/deps/gleam_stdlib/gen/test/gleam@string_builder_test.erl b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@string_builder_test.erl new file mode 100644 index 0000000..1fc23da --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@string_builder_test.erl @@ -0,0 +1,126 @@ +-module(gleam@string_builder_test). +-compile(no_auto_import). + +-export([string_builder_test/0, lowercase_test/0, uppercase_test/0, split_test/0, is_equal_test/0, is_empty_test/0]). + +string_builder_test() -> + Data = gleam@string_builder:prepend( + gleam@string_builder:append( + gleam@string_builder:append( + gleam@string_builder:from_string(<<"ello"/utf8>>), + <<","/utf8>> + ), + <<" world!"/utf8>> + ), + <<"H"/utf8>> + ), + gleam@should:equal( + gleam@string_builder:to_string(Data), + <<"Hello, world!"/utf8>> + ), + gleam@should:equal(gleam@string_builder:byte_size(Data), 13), + Data@1 = gleam@string_builder:prepend_builder( + gleam@string_builder:append_builder( + gleam@string_builder:append_builder( + gleam@string_builder:from_string(<<"ello"/utf8>>), + gleam@string_builder:from_string(<<","/utf8>>) + ), + gleam@string_builder:concat( + [gleam@string_builder:from_string(<<" wo"/utf8>>), + gleam@string_builder:from_string(<<"rld!"/utf8>>)] + ) + ), + gleam@string_builder:from_string(<<"H"/utf8>>) + ), + gleam@should:equal( + gleam@string_builder:to_string(Data@1), + <<"Hello, world!"/utf8>> + ), + gleam@should:equal(gleam@string_builder:byte_size(Data@1), 13). + +lowercase_test() -> + gleam@should:equal( + gleam@string_builder:to_string( + gleam@string_builder:lowercase( + gleam@string_builder:from_strings( + [<<"Gleam"/utf8>>, <<"Gleam"/utf8>>] + ) + ) + ), + <<"gleamgleam"/utf8>> + ). + +uppercase_test() -> + gleam@should:equal( + gleam@string_builder:to_string( + gleam@string_builder:uppercase( + gleam@string_builder:from_strings( + [<<"Gleam"/utf8>>, <<"Gleam"/utf8>>] + ) + ) + ), + <<"GLEAMGLEAM"/utf8>> + ). + +split_test() -> + gleam@should:equal( + gleam@string_builder:split( + gleam@string_builder:from_string(<<"Gleam,Erlang,Elixir"/utf8>>), + <<","/utf8>> + ), + [gleam@string_builder:from_string(<<"Gleam"/utf8>>), + gleam@string_builder:from_string(<<"Erlang"/utf8>>), + gleam@string_builder:from_string(<<"Elixir"/utf8>>)] + ), + gleam@should:equal( + gleam@string_builder:split( + gleam@string_builder:from_strings( + [<<"Gleam, Erl"/utf8>>, <<"ang,Elixir"/utf8>>] + ), + <<", "/utf8>> + ), + [gleam@string_builder:from_string(<<"Gleam"/utf8>>), + gleam@string_builder:from_strings( + [<<"Erl"/utf8>>, <<"ang,Elixir"/utf8>>] + )] + ). + +is_equal_test() -> + gleam@should:be_true( + gleam@string_builder:is_equal( + gleam@string_builder:from_string(<<"12"/utf8>>), + gleam@string_builder:from_strings([<<"1"/utf8>>, <<"2"/utf8>>]) + ) + ), + gleam@should:be_true( + gleam@string_builder:is_equal( + gleam@string_builder:from_string(<<"12"/utf8>>), + gleam@string_builder:from_string(<<"12"/utf8>>) + ) + ), + gleam@should:be_false( + gleam@string_builder:is_equal( + gleam@string_builder:from_string(<<"12"/utf8>>), + gleam@string_builder:from_string(<<"2"/utf8>>) + ) + ). + +is_empty_test() -> + gleam@should:be_true( + gleam@string_builder:is_empty( + gleam@string_builder:from_string(<<""/utf8>>) + ) + ), + gleam@should:be_false( + gleam@string_builder:is_empty( + gleam@string_builder:from_string(<<"12"/utf8>>) + ) + ), + gleam@should:be_true( + gleam@string_builder:is_empty(gleam@string_builder:from_strings([])) + ), + gleam@should:be_true( + gleam@string_builder:is_empty( + gleam@string_builder:from_strings([<<""/utf8>>, <<""/utf8>>]) + ) + ). diff --git a/gleam_providers/deps/gleam_stdlib/gen/test/gleam@string_test.erl b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@string_test.erl new file mode 100644 index 0000000..2ee365e --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@string_test.erl @@ -0,0 +1,269 @@ +-module(gleam@string_test). +-compile(no_auto_import). + +-export([length_test/0, lowercase_test/0, uppercase_test/0, reverse_test/0, split_test/0, split_once_test/0, replace_test/0, append_test/0, compare_test/0, contains_test/0, concat_test/0, repeat_test/0, join_test/0, trim_test/0, trim_left_test/0, trim_right_test/0, starts_with_test/0, ends_with_test/0, slice_test/0, drop_left_test/0, drop_right_test/0, pad_left_test/0, pad_right_test/0, pop_grapheme_test/0, to_graphemes_test/0, utf_codepoint_test/0]). + +length_test() -> + gleam@should:equal(gleam@string:length(<<"ß↑e̊"/utf8>>), 3), + gleam@should:equal(gleam@string:length(<<"Gleam"/utf8>>), 5), + gleam@should:equal(gleam@string:length(<<""/utf8>>), 0). + +lowercase_test() -> + gleam@should:equal( + gleam@string:lowercase(<<"Gleam"/utf8>>), + <<"gleam"/utf8>> + ). + +uppercase_test() -> + gleam@should:equal( + gleam@string:uppercase(<<"Gleam"/utf8>>), + <<"GLEAM"/utf8>> + ). + +reverse_test() -> + gleam@should:equal(gleam@string:reverse(<<"Gleam"/utf8>>), <<"maelG"/utf8>>). + +split_test() -> + gleam@should:equal( + gleam@string:split(<<"Gleam,Erlang,Elixir"/utf8>>, <<","/utf8>>), + [<<"Gleam"/utf8>>, <<"Erlang"/utf8>>, <<"Elixir"/utf8>>] + ), + gleam@should:equal( + gleam@string:split(<<"Gleam, Erlang,Elixir"/utf8>>, <<", "/utf8>>), + [<<"Gleam"/utf8>>, <<"Erlang,Elixir"/utf8>>] + ). + +split_once_test() -> + gleam@should:equal( + gleam@string:split_once(<<"Gleam,Erlang,Elixir"/utf8>>, <<","/utf8>>), + {ok, {<<"Gleam"/utf8>>, <<"Erlang,Elixir"/utf8>>}} + ), + gleam@should:equal( + gleam@string:split_once(<<"Gleam"/utf8>>, <<","/utf8>>), + {error, nil} + ), + gleam@should:equal( + gleam@string:split_once(<<""/utf8>>, <<","/utf8>>), + {error, nil} + ). + +replace_test() -> + gleam@should:equal( + gleam@string:replace( + <<"Gleam,Erlang,Elixir"/utf8>>, + <<","/utf8>>, + <<"++"/utf8>> + ), + <<"Gleam++Erlang++Elixir"/utf8>> + ). + +append_test() -> + gleam@should:equal( + gleam@string:append(<<"Test"/utf8>>, <<" Me"/utf8>>), + <<"Test Me"/utf8>> + ). + +compare_test() -> + gleam@should:equal(gleam@string:compare(<<""/utf8>>, <<""/utf8>>), eq), + gleam@should:equal(gleam@string:compare(<<"a"/utf8>>, <<""/utf8>>), gt), + gleam@should:equal(gleam@string:compare(<<"a"/utf8>>, <<"A"/utf8>>), gt), + gleam@should:equal(gleam@string:compare(<<"A"/utf8>>, <<"B"/utf8>>), lt), + gleam@should:equal(gleam@string:compare(<<"t"/utf8>>, <<"ABC"/utf8>>), gt). + +contains_test() -> + gleam@should:equal( + gleam@string:contains(<<"gleam"/utf8>>, <<"ea"/utf8>>), + true + ), + gleam@should:equal( + gleam@string:contains(<<"gleam"/utf8>>, <<"x"/utf8>>), + false + ), + gleam@should:equal( + gleam@string:contains(<<"bellwether"/utf8>>, <<"bell"/utf8>>), + true + ). + +concat_test() -> + gleam@should:equal( + gleam@string:concat( + [<<"Hello"/utf8>>, <<", "/utf8>>, <<"world!"/utf8>>] + ), + <<"Hello, world!"/utf8>> + ). + +repeat_test() -> + gleam@should:equal(gleam@string:repeat(<<"hi"/utf8>>, 3), <<"hihihi"/utf8>>), + gleam@should:equal(gleam@string:repeat(<<"hi"/utf8>>, 0), <<""/utf8>>), + gleam@should:equal(gleam@string:repeat(<<"hi"/utf8>>, -1), <<""/utf8>>). + +join_test() -> + gleam@should:equal( + gleam@string:join([<<"Hello"/utf8>>, <<"world!"/utf8>>], <<", "/utf8>>), + <<"Hello, world!"/utf8>> + ), + gleam@should:equal( + gleam@string:join([<<"Hello"/utf8>>, <<"world!"/utf8>>], <<"-"/utf8>>), + <<"Hello-world!"/utf8>> + ). + +trim_test() -> + gleam@should:equal( + gleam@string:trim(<<" hats \n"/utf8>>), + <<"hats"/utf8>> + ). + +trim_left_test() -> + gleam@should:equal( + gleam@string:trim_left(<<" hats \n"/utf8>>), + <<"hats \n"/utf8>> + ). + +trim_right_test() -> + gleam@should:equal( + gleam@string:trim_right(<<" hats \n"/utf8>>), + <<" hats"/utf8>> + ). + +starts_with_test() -> + gleam@should:equal( + gleam@string:starts_with(<<"theory"/utf8>>, <<""/utf8>>), + true + ), + gleam@should:equal( + gleam@string:starts_with(<<"theory"/utf8>>, <<"the"/utf8>>), + true + ), + gleam@should:equal( + gleam@string:starts_with(<<"theory"/utf8>>, <<"ory"/utf8>>), + false + ), + gleam@should:equal( + gleam@string:starts_with(<<"theory"/utf8>>, <<"theory2"/utf8>>), + false + ). + +ends_with_test() -> + gleam@should:equal( + gleam@string:ends_with(<<"theory"/utf8>>, <<""/utf8>>), + true + ), + gleam@should:equal( + gleam@string:ends_with(<<"theory"/utf8>>, <<"ory"/utf8>>), + true + ), + gleam@should:equal( + gleam@string:ends_with(<<"theory"/utf8>>, <<"the"/utf8>>), + false + ), + gleam@should:equal( + gleam@string:ends_with(<<"theory"/utf8>>, <<"theory2"/utf8>>), + false + ). + +slice_test() -> + gleam@should:equal( + gleam@string:slice(<<"gleam"/utf8>>, 1, 2), + <<"le"/utf8>> + ), + gleam@should:equal( + gleam@string:slice(<<"gleam"/utf8>>, 1, 10), + <<"leam"/utf8>> + ), + gleam@should:equal(gleam@string:slice(<<"gleam"/utf8>>, 10, 3), <<""/utf8>>), + gleam@should:equal( + gleam@string:slice(<<"gleam"/utf8>>, -2, 2), + <<"am"/utf8>> + ), + gleam@should:equal( + gleam@string:slice(<<"gleam"/utf8>>, -12, 2), + <<""/utf8>> + ), + gleam@should:equal(gleam@string:slice(<<"gleam"/utf8>>, 2, -3), <<""/utf8>>). + +drop_left_test() -> + gleam@should:equal( + gleam@string:drop_left(<<"gleam"/utf8>>, 2), + <<"eam"/utf8>> + ), + gleam@should:equal(gleam@string:drop_left(<<"gleam"/utf8>>, 6), <<""/utf8>>), + gleam@should:equal( + gleam@string:drop_left(<<"gleam"/utf8>>, -2), + <<"gleam"/utf8>> + ). + +drop_right_test() -> + gleam@should:equal( + gleam@string:drop_right(<<"gleam"/utf8>>, 2), + <<"gle"/utf8>> + ), + gleam@should:equal( + gleam@string:drop_right(<<"gleam"/utf8>>, 5), + <<""/utf8>> + ), + gleam@should:equal( + gleam@string:drop_right(<<"gleam"/utf8>>, -2), + <<"gleam"/utf8>> + ). + +pad_left_test() -> + gleam@should:equal( + gleam@string:pad_left(<<"121"/utf8>>, 5, <<"."/utf8>>), + <<"..121"/utf8>> + ), + gleam@should:equal( + gleam@string:pad_left(<<"121"/utf8>>, 3, <<"."/utf8>>), + <<"121"/utf8>> + ), + gleam@should:equal( + gleam@string:pad_left(<<"121"/utf8>>, 2, <<"."/utf8>>), + <<"121"/utf8>> + ), + gleam@should:equal( + gleam@string:pad_left(<<"121"/utf8>>, 5, <<"XY"/utf8>>), + <<"XYXY121"/utf8>> + ). + +pad_right_test() -> + gleam@should:equal( + gleam@string:pad_right(<<"121"/utf8>>, 5, <<"."/utf8>>), + <<"121.."/utf8>> + ), + gleam@should:equal( + gleam@string:pad_right(<<"121"/utf8>>, 3, <<"."/utf8>>), + <<"121"/utf8>> + ), + gleam@should:equal( + gleam@string:pad_right(<<"121"/utf8>>, 2, <<"."/utf8>>), + <<"121"/utf8>> + ), + gleam@should:equal( + gleam@string:pad_right(<<"121"/utf8>>, 5, <<"XY"/utf8>>), + <<"121XYXY"/utf8>> + ). + +pop_grapheme_test() -> + gleam@should:equal( + gleam@string:pop_grapheme(<<"gleam"/utf8>>), + {ok, {<<"g"/utf8>>, <<"leam"/utf8>>}} + ), + gleam@should:equal( + gleam@string:pop_grapheme(<<"g"/utf8>>), + {ok, {<<"g"/utf8>>, <<""/utf8>>}} + ), + gleam@should:equal(gleam@string:pop_grapheme(<<""/utf8>>), {error, nil}). + +to_graphemes_test() -> + gleam@should:equal( + gleam@string:to_graphemes(<<"abc"/utf8>>), + [<<"a"/utf8>>, <<"b"/utf8>>, <<"c"/utf8>>] + ), + gleam@should:equal(gleam@string:to_graphemes(<<"a"/utf8>>), [<<"a"/utf8>>]), + gleam@should:equal(gleam@string:to_graphemes(<<""/utf8>>), []). + +utf_codepoint_test() -> + gleam@should:be_error(gleam@string:utf_codepoint(1114444)), + gleam@should:be_error(gleam@string:utf_codepoint(65534)), + gleam@should:be_error(gleam@string:utf_codepoint(55296)), + {ok, Snake} = gleam@string:utf_codepoint(128013), + gleam@should:equal(<>, <<"🐍"/utf8>>). diff --git a/gleam_providers/deps/gleam_stdlib/gen/test/gleam@uri_test.erl b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@uri_test.erl new file mode 100644 index 0000000..4bea178 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gen/test/gleam@uri_test.erl @@ -0,0 +1,290 @@ +-module(gleam@uri_test). +-compile(no_auto_import). + +-export([full_parse_test/0, parse_only_path_test/0, parse_only_host_test/0, error_parsing_uri_test/0, full_uri_to_string_test/0, path_only_uri_to_string_test/0, parse_query_string_test/0, parse_empty_query_string_test/0, parse_query_string_with_empty_test/0, error_parsing_query_test/0, query_to_string_test/0, empty_query_to_string_test/0, percent_encode_test/0, percent_encode_consistency_test/0, percent_decode_test/0, percent_decode_consistency_test/0, parse_segments_test/0, origin_test/0, merge_test/0]). + +full_parse_test() -> + {ok, Parsed} = gleam@uri:parse( + <<"https://foo:bar@example.com:1234/path?query=true#fragment"/utf8>> + ), + gleam@should:equal(erlang:element(2, Parsed), {some, <<"https"/utf8>>}), + gleam@should:equal(erlang:element(3, Parsed), {some, <<"foo:bar"/utf8>>}), + gleam@should:equal( + erlang:element(4, Parsed), + {some, <<"example.com"/utf8>>} + ), + gleam@should:equal(erlang:element(5, Parsed), {some, 1234}), + gleam@should:equal(erlang:element(6, Parsed), <<"/path"/utf8>>), + gleam@should:equal(erlang:element(7, Parsed), {some, <<"query=true"/utf8>>}), + gleam@should:equal(erlang:element(8, Parsed), {some, <<"fragment"/utf8>>}). + +parse_only_path_test() -> + {ok, Parsed} = gleam@uri:parse(<<""/utf8>>), + gleam@should:equal(erlang:element(2, Parsed), none), + gleam@should:equal(erlang:element(3, Parsed), none), + gleam@should:equal(erlang:element(4, Parsed), none), + gleam@should:equal(erlang:element(5, Parsed), none), + gleam@should:equal(erlang:element(6, Parsed), <<""/utf8>>), + gleam@should:equal(erlang:element(7, Parsed), none), + gleam@should:equal(erlang:element(8, Parsed), none). + +parse_only_host_test() -> + {ok, Parsed} = gleam@uri:parse(<<"//"/utf8>>), + gleam@should:equal(erlang:element(2, Parsed), none), + gleam@should:equal(erlang:element(3, Parsed), none), + gleam@should:equal(erlang:element(4, Parsed), {some, <<""/utf8>>}), + gleam@should:equal(erlang:element(5, Parsed), none), + gleam@should:equal(erlang:element(6, Parsed), <<""/utf8>>), + gleam@should:equal(erlang:element(7, Parsed), none), + gleam@should:equal(erlang:element(8, Parsed), none). + +error_parsing_uri_test() -> + gleam@should:equal(gleam@uri:parse(<<"::"/utf8>>), {error, nil}). + +full_uri_to_string_test() -> + Test_uri = {uri, + {some, <<"https"/utf8>>}, + {some, <<"foo:bar"/utf8>>}, + {some, <<"example.com"/utf8>>}, + {some, 1234}, + <<"/path"/utf8>>, + {some, <<"query=true"/utf8>>}, + {some, <<"fragment"/utf8>>}}, + gleam@should:equal( + gleam@uri:to_string(Test_uri), + <<"https://foo:bar@example.com:1234/path?query=true#fragment"/utf8>> + ). + +path_only_uri_to_string_test() -> + Test_uri = {uri, none, none, none, none, <<"/"/utf8>>, none, none}, + gleam@should:equal(gleam@uri:to_string(Test_uri), <<"/"/utf8>>). + +parse_query_string_test() -> + {ok, Parsed} = gleam@uri:parse_query(<<"foo+bar=1&city=%C3%B6rebro"/utf8>>), + gleam@should:equal( + Parsed, + [{<<"foo bar"/utf8>>, <<"1"/utf8>>}, + {<<"city"/utf8>>, <<"örebro"/utf8>>}] + ). + +parse_empty_query_string_test() -> + {ok, Parsed} = gleam@uri:parse_query(<<""/utf8>>), + gleam@should:equal(Parsed, []). + +parse_query_string_with_empty_test() -> + gleam@should:equal( + gleam@uri:parse_query(<<"present"/utf8>>), + {ok, [{<<"present"/utf8>>, <<""/utf8>>}]} + ). + +error_parsing_query_test() -> + gleam@should:equal(gleam@uri:parse_query(<<"%C2"/utf8>>), {error, nil}). + +query_to_string_test() -> + Query_string = gleam@uri:query_to_string( + [{<<"foo bar"/utf8>>, <<"1"/utf8>>}, + {<<"city"/utf8>>, <<"örebro"/utf8>>}] + ), + gleam@should:equal(Query_string, <<"foo+bar=1&city=%C3%B6rebro"/utf8>>). + +empty_query_to_string_test() -> + Query_string = gleam@uri:query_to_string([]), + gleam@should:equal(Query_string, <<""/utf8>>). + +percent_codec_fixtures() -> + [{<<" "/utf8>>, <<"+"/utf8>>}, + {<<","/utf8>>, <<"%2C"/utf8>>}, + {<<";"/utf8>>, <<"%3B"/utf8>>}, + {<<":"/utf8>>, <<"%3A"/utf8>>}, + {<<"!"/utf8>>, <<"%21"/utf8>>}, + {<<"?"/utf8>>, <<"%3F"/utf8>>}, + {<<"'"/utf8>>, <<"%27"/utf8>>}, + {<<"("/utf8>>, <<"%28"/utf8>>}, + {<<")"/utf8>>, <<"%29"/utf8>>}, + {<<"["/utf8>>, <<"%5B"/utf8>>}, + {<<"@"/utf8>>, <<"%40"/utf8>>}, + {<<"/"/utf8>>, <<"%2F"/utf8>>}, + {<<"\\"/utf8>>, <<"%5C"/utf8>>}, + {<<"&"/utf8>>, <<"%26"/utf8>>}, + {<<"#"/utf8>>, <<"%23"/utf8>>}, + {<<"="/utf8>>, <<"%3D"/utf8>>}, + {<<"~"/utf8>>, <<"%7E"/utf8>>}, + {<<"ñ"/utf8>>, <<"%C3%B1"/utf8>>}, + {<<"-"/utf8>>, <<"-"/utf8>>}, + {<<"_"/utf8>>, <<"_"/utf8>>}, + {<<"."/utf8>>, <<"."/utf8>>}, + {<<"*"/utf8>>, <<"*"/utf8>>}, + {<<"100% great"/utf8>>, <<"100%25+great"/utf8>>}]. + +percent_encode_test() -> + gleam@list:map(percent_codec_fixtures(), fun(T) -> {A, B} = T, + gleam@should:equal(gleam@uri:percent_encode(A), B) end). + +percent_encode_consistency_test() -> + K = <<"foo bar[]"/utf8>>, + V = <<"ñaña (,:*~)"/utf8>>, + Query_string = gleam@uri:query_to_string([{K, V}]), + Encoded_key = gleam@uri:percent_encode(K), + Encoded_value = gleam@uri:percent_encode(V), + Manual_query_string = gleam@string:concat( + [Encoded_key, <<"="/utf8>>, Encoded_value] + ), + gleam@should:equal(Query_string, Manual_query_string). + +percent_decode_test() -> + gleam@list:map(percent_codec_fixtures(), fun(T) -> {A, B} = T, + gleam@should:equal(gleam@uri:percent_decode(B), {ok, A}) end). + +percent_decode_consistency_test() -> + K = <<"foo+bar[]"/utf8>>, + V = <<"%C3%B6rebro"/utf8>>, + Query = gleam@string:concat([K, <<"="/utf8>>, V]), + {ok, Parsed} = gleam@uri:parse_query(Query), + {ok, Decoded_key} = gleam@uri:percent_decode(K), + {ok, Decoded_value} = gleam@uri:percent_decode(V), + gleam@should:equal(Parsed, [{Decoded_key, Decoded_value}]). + +parse_segments_test() -> + gleam@should:equal(gleam@uri:path_segments(<<"/"/utf8>>), []), + gleam@should:equal( + gleam@uri:path_segments(<<"/foo/bar"/utf8>>), + [<<"foo"/utf8>>, <<"bar"/utf8>>] + ), + gleam@should:equal(gleam@uri:path_segments(<<"////"/utf8>>), []), + gleam@should:equal( + gleam@uri:path_segments(<<"/foo//bar"/utf8>>), + [<<"foo"/utf8>>, <<"bar"/utf8>>] + ), + gleam@should:equal(gleam@uri:path_segments(<<"/."/utf8>>), []), + gleam@should:equal( + gleam@uri:path_segments(<<"/.foo"/utf8>>), + [<<".foo"/utf8>>] + ), + gleam@should:equal( + gleam@uri:path_segments(<<"/../bar"/utf8>>), + [<<"bar"/utf8>>] + ), + gleam@should:equal( + gleam@uri:path_segments(<<"../bar"/utf8>>), + [<<"bar"/utf8>>] + ), + gleam@should:equal( + gleam@uri:path_segments(<<"/foo/../bar"/utf8>>), + [<<"bar"/utf8>>] + ). + +origin_test() -> + {ok, Parsed} = gleam@uri:parse(<<"http://example.test/path?foo#bar"/utf8>>), + gleam@should:equal( + gleam@uri:origin(Parsed), + {ok, <<"http://example.test"/utf8>>} + ), + {ok, Parsed@1} = gleam@uri:parse(<<"http://example.test:8080"/utf8>>), + gleam@should:equal( + gleam@uri:origin(Parsed@1), + {ok, <<"http://example.test:8080"/utf8>>} + ), + {ok, Parsed@2} = gleam@uri:parse(<<"https://example.test"/utf8>>), + gleam@should:equal( + gleam@uri:origin(Parsed@2), + {ok, <<"https://example.test"/utf8>>} + ), + {ok, Parsed@3} = gleam@uri:parse(<<"http:///path"/utf8>>), + gleam@should:equal(gleam@uri:origin(Parsed@3), {ok, <<"http://"/utf8>>}), + {ok, Parsed@4} = gleam@uri:parse(<<"http://"/utf8>>), + gleam@should:equal(gleam@uri:origin(Parsed@4), {ok, <<"http://"/utf8>>}), + {ok, Parsed@5} = gleam@uri:parse(<<"/path"/utf8>>), + gleam@should:equal(gleam@uri:origin(Parsed@5), {error, nil}), + {ok, Parsed@6} = gleam@uri:parse(<<"file:///dev/null"/utf8>>), + gleam@should:equal(gleam@uri:origin(Parsed@6), {error, nil}). + +merge_test() -> + {ok, A} = gleam@uri:parse(<<"/relative"/utf8>>), + {ok, B} = gleam@uri:parse(<<""/utf8>>), + gleam@should:equal(gleam@uri:merge(A, B), {error, nil}), + {ok, A@1} = gleam@uri:parse(<<"http://google.com/foo"/utf8>>), + {ok, B@1} = gleam@uri:parse(<<"http://example.com/baz"/utf8>>), + gleam@should:equal( + gleam@uri:merge(A@1, B@1), + gleam@uri:parse(<<"http://example.com/baz"/utf8>>) + ), + {ok, A@2} = gleam@uri:parse(<<"http://google.com/foo"/utf8>>), + {ok, B@2} = gleam@uri:parse( + <<"http://example.com/.././bar/../../baz"/utf8>> + ), + gleam@should:equal( + gleam@uri:merge(A@2, B@2), + gleam@uri:parse(<<"http://example.com/baz"/utf8>>) + ), + {ok, A@3} = gleam@uri:parse(<<"http://google.com/foo"/utf8>>), + {ok, B@3} = gleam@uri:parse(<<"//example.com/baz"/utf8>>), + gleam@should:equal( + gleam@uri:merge(A@3, B@3), + gleam@uri:parse(<<"http://example.com/baz"/utf8>>) + ), + {ok, A@4} = gleam@uri:parse(<<"http://google.com/foo"/utf8>>), + {ok, B@4} = gleam@uri:parse(<<"//example.com/.././bar/../../../baz"/utf8>>), + gleam@should:equal( + gleam@uri:merge(A@4, B@4), + gleam@uri:parse(<<"http://example.com/baz"/utf8>>) + ), + {ok, A@5} = gleam@uri:parse(<<"http://example.com/foo/bar"/utf8>>), + {ok, B@5} = gleam@uri:parse(<<"/baz"/utf8>>), + gleam@should:equal( + gleam@uri:merge(A@5, B@5), + gleam@uri:parse(<<"http://example.com/baz"/utf8>>) + ), + {ok, A@6} = gleam@uri:parse(<<"http://example.com/foo/bar"/utf8>>), + {ok, B@6} = gleam@uri:parse(<<"baz"/utf8>>), + gleam@should:equal( + gleam@uri:merge(A@6, B@6), + gleam@uri:parse(<<"http://example.com/foo/baz"/utf8>>) + ), + {ok, A@7} = gleam@uri:parse(<<"http://example.com/foo/"/utf8>>), + {ok, B@7} = gleam@uri:parse(<<"baz"/utf8>>), + gleam@should:equal( + gleam@uri:merge(A@7, B@7), + gleam@uri:parse(<<"http://example.com/foo/baz"/utf8>>) + ), + {ok, A@8} = gleam@uri:parse(<<"http://example.com"/utf8>>), + {ok, B@8} = gleam@uri:parse(<<"baz"/utf8>>), + gleam@should:equal( + gleam@uri:merge(A@8, B@8), + gleam@uri:parse(<<"http://example.com/baz"/utf8>>) + ), + {ok, A@9} = gleam@uri:parse(<<"http://example.com"/utf8>>), + {ok, B@9} = gleam@uri:parse(<<"/.././bar/../../../baz"/utf8>>), + gleam@should:equal( + gleam@uri:merge(A@9, B@9), + gleam@uri:parse(<<"http://example.com/baz"/utf8>>) + ), + {ok, A@10} = gleam@uri:parse(<<"http://example.com/foo/bar"/utf8>>), + {ok, B@10} = gleam@uri:parse(<<""/utf8>>), + gleam@should:equal( + gleam@uri:merge(A@10, B@10), + gleam@uri:parse(<<"http://example.com/foo/bar"/utf8>>) + ), + {ok, A@11} = gleam@uri:parse(<<"http://example.com/foo/bar"/utf8>>), + {ok, B@11} = gleam@uri:parse(<<"#fragment"/utf8>>), + gleam@should:equal( + gleam@uri:merge(A@11, B@11), + gleam@uri:parse(<<"http://example.com/foo/bar#fragment"/utf8>>) + ), + {ok, A@12} = gleam@uri:parse(<<"http://example.com/foo/bar"/utf8>>), + {ok, B@12} = gleam@uri:parse(<<"?query"/utf8>>), + gleam@should:equal( + gleam@uri:merge(A@12, B@12), + gleam@uri:parse(<<"http://example.com/foo/bar?query"/utf8>>) + ), + {ok, A@13} = gleam@uri:parse(<<"http://example.com/foo/bar?query1"/utf8>>), + {ok, B@13} = gleam@uri:parse(<<"?query2"/utf8>>), + gleam@should:equal( + gleam@uri:merge(A@13, B@13), + gleam@uri:parse(<<"http://example.com/foo/bar?query2"/utf8>>) + ), + {ok, A@14} = gleam@uri:parse(<<"http://example.com/foo/bar?query"/utf8>>), + {ok, B@14} = gleam@uri:parse(<<""/utf8>>), + gleam@should:equal( + gleam@uri:merge(A@14, B@14), + gleam@uri:parse(<<"http://example.com/foo/bar?query"/utf8>>) + ). diff --git a/gleam_providers/deps/gleam_stdlib/gleam.toml b/gleam_providers/deps/gleam_stdlib/gleam.toml new file mode 100644 index 0000000..7088fd7 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/gleam.toml @@ -0,0 +1,7 @@ +name = "gleam_stdlib" +version = "0.12.0" + +[repository] +type = "github" +user = "gleam-lang" +repo = "stdlib" diff --git a/gleam_providers/deps/gleam_stdlib/hex_metadata.config b/gleam_providers/deps/gleam_stdlib/hex_metadata.config new file mode 100644 index 0000000..ae6e719 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/hex_metadata.config @@ -0,0 +1,56 @@ +{<<"app">>,<<"gleam_stdlib">>}. +{<<"build_tools">>,[<<"rebar3">>]}. +{<<"description">>, + <<"A standard library for the Gleam programming language">>}. +{<<"files">>, + [<<"CHANGELOG.md">>,<<"LICENSE">>,<<"README.md">>, + <<"gen/src/gleam@atom.erl">>,<<"gen/src/gleam@base.erl">>, + <<"gen/src/gleam@bit_builder.erl">>,<<"gen/src/gleam@bit_string.erl">>, + <<"gen/src/gleam@bool.erl">>,<<"gen/src/gleam@dynamic.erl">>, + <<"gen/src/gleam@float.erl">>,<<"gen/src/gleam@function.erl">>, + <<"gen/src/gleam@int.erl">>,<<"gen/src/gleam@io.erl">>, + <<"gen/src/gleam@iterator.erl">>,<<"gen/src/gleam@iterator_Iterator.hrl">>, + <<"gen/src/gleam@iterator_Next.hrl">>,<<"gen/src/gleam@list.erl">>, + <<"gen/src/gleam@map.erl">>,<<"gen/src/gleam@option.erl">>, + <<"gen/src/gleam@order.erl">>,<<"gen/src/gleam@os.erl">>, + <<"gen/src/gleam@pair.erl">>,<<"gen/src/gleam@queue.erl">>, + <<"gen/src/gleam@queue_Queue.hrl">>,<<"gen/src/gleam@regex.erl">>, + <<"gen/src/gleam@regex_CompileError.hrl">>, + <<"gen/src/gleam@regex_Match.hrl">>,<<"gen/src/gleam@regex_Options.hrl">>, + <<"gen/src/gleam@result.erl">>,<<"gen/src/gleam@set.erl">>, + <<"gen/src/gleam@set_Set.hrl">>,<<"gen/src/gleam@should.erl">>, + <<"gen/src/gleam@string.erl">>,<<"gen/src/gleam@string_builder.erl">>, + <<"gen/src/gleam@uri.erl">>,<<"gen/src/gleam@uri_Uri.hrl">>, + <<"gen/test/gleam@atom_test.erl">>,<<"gen/test/gleam@base_test.erl">>, + <<"gen/test/gleam@bit_builder_test.erl">>, + <<"gen/test/gleam@bit_string_test.erl">>,<<"gen/test/gleam@bool_test.erl">>, + <<"gen/test/gleam@dynamic_test.erl">>,<<"gen/test/gleam@float_test.erl">>, + <<"gen/test/gleam@function_test.erl">>,<<"gen/test/gleam@int_test.erl">>, + <<"gen/test/gleam@iterator_test.erl">>,<<"gen/test/gleam@list_test.erl">>, + <<"gen/test/gleam@map_test.erl">>,<<"gen/test/gleam@option_test.erl">>, + <<"gen/test/gleam@order_test.erl">>,<<"gen/test/gleam@os_test.erl">>, + <<"gen/test/gleam@pair_test.erl">>,<<"gen/test/gleam@queue_test.erl">>, + <<"gen/test/gleam@regex_test.erl">>,<<"gen/test/gleam@result_test.erl">>, + <<"gen/test/gleam@set_test.erl">>, + <<"gen/test/gleam@string_builder_test.erl">>, + <<"gen/test/gleam@string_test.erl">>,<<"gen/test/gleam@uri_test.erl">>, + <<"gleam.toml">>,<<"rebar.config">>,<<"rebar.lock">>, + <<"src/gleam/atom.gleam">>,<<"src/gleam/base.gleam">>, + <<"src/gleam/bit_builder.gleam">>,<<"src/gleam/bit_string.gleam">>, + <<"src/gleam/bool.gleam">>,<<"src/gleam/dynamic.gleam">>, + <<"src/gleam/float.gleam">>,<<"src/gleam/function.gleam">>, + <<"src/gleam/int.gleam">>,<<"src/gleam/io.gleam">>, + <<"src/gleam/iterator.gleam">>,<<"src/gleam/list.gleam">>, + <<"src/gleam/map.gleam">>,<<"src/gleam/option.gleam">>, + <<"src/gleam/order.gleam">>,<<"src/gleam/os.gleam">>, + <<"src/gleam/pair.gleam">>,<<"src/gleam/queue.gleam">>, + <<"src/gleam/regex.gleam">>,<<"src/gleam/result.gleam">>, + <<"src/gleam/set.gleam">>,<<"src/gleam/should.gleam">>, + <<"src/gleam/string.gleam">>,<<"src/gleam/string_builder.gleam">>, + <<"src/gleam/uri.gleam">>,<<"src/gleam_stdlib.app.src">>, + <<"src/gleam_stdlib.erl">>]}. +{<<"licenses">>,[<<"Apache 2.0">>]}. +{<<"links">>,[{<<"GitHub">>,<<"https://github.com/gleam-lang/stdlib">>}]}. +{<<"name">>,<<"gleam_stdlib">>}. +{<<"requirements">>,[]}. +{<<"version">>,<<"0.13.0">>}. diff --git a/gleam_providers/deps/gleam_stdlib/rebar.config b/gleam_providers/deps/gleam_stdlib/rebar.config new file mode 100644 index 0000000..c5ef014 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/rebar.config @@ -0,0 +1,10 @@ +{erl_opts, [debug_info, warnings_as_errors]}. +{src_dirs, ["src", "gen/src"]}. + +{profiles, [ + {test, [{src_dirs, ["src", "test", "gen/src", "gen/test"]}]} +]}. + +{project_plugins, [rebar_gleam]}. + +{deps, []}. diff --git a/gleam_providers/deps/gleam_stdlib/rebar.lock b/gleam_providers/deps/gleam_stdlib/rebar.lock new file mode 100644 index 0000000..57afcca --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/rebar.lock @@ -0,0 +1 @@ +[]. diff --git a/gleam_providers/deps/gleam_stdlib/src/gleam/atom.gleam b/gleam_providers/deps/gleam_stdlib/src/gleam/atom.gleam new file mode 100644 index 0000000..d53c7ad --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/src/gleam/atom.gleam @@ -0,0 +1,62 @@ +/// Atom is a special string-like data-type that is most commonly used for +/// interfacing with code written in other BEAM languages such as Erlang and +/// Elixir. It is preferable to define your own custom types to use instead of +/// atoms where possible. +/// +/// Atoms are not used much in typical Gleam code! +/// +/// ## Creating atoms +/// +/// We can create atoms with the the [`create_from_string`](#create_from_string) +/// function, though we must be careful when doing so as atoms are never +/// garbage collected. If we create too many atoms (for example, if we convert +/// user input into atoms) we may hit the max limit of atoms and cause the +/// virtual machine to crash. +/// +pub external type Atom + +/// An error returned when no atom is found in the virtual machine's atom table +/// for a given string when calling the [`from_string`](#from_string) function. +pub type FromStringError { + AtomNotLoaded +} + +/// Find an existing Atom for the given String. +/// +/// If no atom is found in the virtual machine's atom table for the String then +/// an error is returned. +/// +/// ## Examples +/// +/// > from_string("ok") +/// Ok(create_from_string("ok")) +/// +/// > from_string("some_new_atom") +/// Error(AtomNotLoaded) +/// +pub external fn from_string(String) -> Result(Atom, FromStringError) = + "gleam_stdlib" "atom_from_string" + +/// Create an atom from a string, inserting a new value into the virtual +/// machine's atom table if an atom does not already exist for the given +/// string. +/// +/// We must be careful when using this function as there is a limit to the +/// number of atom that can fit in the virtual machine's atom table. Never +/// convert user input into atoms as filling the atom table will cause the +/// virtual machine to crash! +/// +pub external fn create_from_string(String) -> Atom = + "gleam_stdlib" "atom_create_from_string" + +/// Retuns a `String` corresponding to the text representation of the given +/// `Atom`. +/// +/// ## Examples +/// +/// > let ok_atom = create_from_string("ok") +/// > to_string(ok_atom) +/// "ok" +/// +pub external fn to_string(Atom) -> String = + "gleam_stdlib" "atom_to_string" diff --git a/gleam_providers/deps/gleam_stdlib/src/gleam/base.gleam b/gleam_providers/deps/gleam_stdlib/src/gleam/base.gleam new file mode 100644 index 0000000..c1c3824 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/src/gleam/base.gleam @@ -0,0 +1,41 @@ +import gleam/bit_string.{BitString} +import gleam/string + +external fn erl_encode64(BitString) -> String = + "base64" "encode" + +external fn erl_decode64(String) -> Result(BitString, Nil) = + "gleam_stdlib" "base_decode64" + +/// Encodes a BitString into a base 64 encoded string. +pub fn encode64(input: BitString, padding: Bool) -> String { + let encoded = erl_encode64(input) + case padding { + True -> encoded + False -> string.replace(encoded, "=", "") + } +} + +/// Decodes a base 64 encoded string into a BitString. +pub fn decode64(encoded: String) -> Result(BitString, Nil) { + let padded = case bit_string.byte_size(bit_string.from_string(encoded)) % 4 { + 0 -> encoded + n -> string.append(encoded, string.repeat("=", 4 - n)) + } + erl_decode64(padded) +} + +/// Encodes a BitString into a base 64 encoded string with URL and filename safe alphabet. +pub fn url_encode64(input: BitString, padding: Bool) -> String { + encode64(input, padding) + |> string.replace("+", "-") + |> string.replace("/", "_") +} + +/// Decodes a base 64 encoded string with URL and filename safe alphabet into a BitString. +pub fn url_decode64(encoded: String) -> Result(BitString, Nil) { + encoded + |> string.replace("-", "+") + |> string.replace("_", "/") + |> decode64() +} diff --git a/gleam_providers/deps/gleam_stdlib/src/gleam/bit_builder.gleam b/gleam_providers/deps/gleam_stdlib/src/gleam/bit_builder.gleam new file mode 100644 index 0000000..9d8ecc1 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/src/gleam/bit_builder.gleam @@ -0,0 +1,102 @@ +import gleam/bit_string.{BitString} +import gleam/string_builder.{StringBuilder} + +/// BitBuilder is a type used for efficiently concatenating bits to create bit +/// strings. +/// +/// If we append one bit string to another the bit strings must be copied to a +/// new location in memory so that they can sit together. This behaviour +/// enables efficient reading of the string but copying can be expensive, +/// especially if we want to join many bit strings together. +/// +/// BitBuilder is different in that it can be joined together in constant +/// time using minimal memory, and then can be efficiently converted to a +/// bit string using the `to_bit_string` function. +/// +pub external type BitBuilder + +/// Prepend a bit string to the start of a builder. +/// +/// Runs in constant time. +/// +pub external fn prepend(to: BitBuilder, prefix: BitString) -> BitBuilder = + "gleam_stdlib" "iodata_prepend" + +/// Append a bit string to the end of a builder. +/// +/// Runs in constant time. +/// +pub external fn append(to: BitBuilder, suffix: BitString) -> BitBuilder = + "gleam_stdlib" "iodata_append" + +/// Prepend a builder onto the start of another. +/// +/// Runs in constant time. +/// +pub external fn prepend_builder( + to: BitBuilder, + prefix: BitBuilder, +) -> BitBuilder = + "gleam_stdlib" "iodata_prepend" + +/// Append a builder onto the end of another. +/// +/// Runs in constant time. +/// +pub external fn append_builder(to: BitBuilder, suffix: BitBuilder) -> BitBuilder = + "gleam_stdlib" "iodata_append" + +/// Prepend a string onto the start of a builder. +/// +/// Runs in constant time. +/// +pub external fn prepend_string(to: BitBuilder, prefix: String) -> BitBuilder = + "gleam_stdlib" "iodata_prepend" + +/// Append a string onto the end of a builder. +/// +/// Runs in constant time. +/// +pub external fn append_string(to: BitBuilder, suffix: String) -> BitBuilder = + "gleam_stdlib" "iodata_append" + +/// Joins a list of builders into a single builders. +/// +/// Runs in constant time. +/// +pub external fn concat(List(BitBuilder)) -> BitBuilder = + "gleam_stdlib" "identity" + +/// Create a new builder from a string. +/// +/// Runs in constant time. +/// +pub external fn from_string(String) -> BitBuilder = + "gleam_stdlib" "wrap_list" + +/// Create a new builder from a string builder. +/// +/// Runs in constant time. +/// +pub external fn from_string_builder(StringBuilder) -> BitBuilder = + "gleam_stdlib" "identity" + +/// Create a new builder from a bit string. +/// +/// Runs in constant time. +/// +pub external fn from_bit_string(BitString) -> BitBuilder = + "gleam_stdlib" "wrap_list" + +/// Turns an builder into a bit string. +/// +/// This function is implemented natively by the virtual machine and is highly +/// optimised. +/// +pub external fn to_bit_string(BitBuilder) -> BitString = + "erlang" "list_to_bitstring" + +/// Returns the size of the builder's content in bytes. +/// +pub external fn byte_size(BitBuilder) -> Int = + "erlang" "iolist_size" diff --git a/gleam_providers/deps/gleam_stdlib/src/gleam/bit_string.gleam b/gleam_providers/deps/gleam_stdlib/src/gleam/bit_string.gleam new file mode 100644 index 0000000..d0b91a0 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/src/gleam/bit_string.gleam @@ -0,0 +1,78 @@ +//// Working with raw bit string data. +//// The BitString type should be used instead of a String type when not utf8 +//// encoded. + +pub type BitString = + BitString + +/// Convert a UTF-8 String type into a raw BitString type. +/// +pub external fn from_string(String) -> BitString = + "gleam_stdlib" "identity" + +/// Returns an integer which is the number of bytes in the bit string. +/// +pub external fn byte_size(BitString) -> Int = + "erlang" "byte_size" + +/// Create a new bit string by joining two binaries. +/// +/// ## Examples +/// +/// > append(to: from_string("butter"), suffix: from_string("fly")) +/// from_string("butterfly") +/// +pub external fn append(first: BitString, second: BitString) -> BitString = + "gleam_stdlib" "bit_string_append" + +/// Extracts part of a bit string. +/// +/// BitString part will start at given position and continue up to specified +/// length. +/// A negative length can be used to extract bytes at the end of a bit string. +/// +pub external fn part( + string: BitString, + position: Int, + length: Int, +) -> Result(BitString, Nil) = + "gleam_stdlib" "bit_string_part_" + +/// Convert an integer to unsigned 32 bits. +/// +/// Returns an error if integer is less than zero or equal to or larger than +/// 2^32. +/// +pub external fn int_to_u32(Int) -> Result(BitString, Nil) = + "gleam_stdlib" "bit_string_int_to_u32" + +/// Convert unsigned 32 bits to an integer. +/// +/// Returns an error if the bit string is not 32 bits in length. +/// +pub external fn int_from_u32(BitString) -> Result(Int, Nil) = + "gleam_stdlib" "bit_string_int_from_u32" + +/// Test to see whether a bit string is valid UTF-8. +/// +pub fn is_utf8(bits: BitString) -> Bool { + case bits { + <<>> -> True + <<_:utf8, rest:binary>> -> is_utf8(rest) + _ -> False + } +} + +external fn unsafe_to_string(BitString) -> String = + "gleam_stdlib" "identity" + +/// Convert a bit string to a string. +/// +/// Returns an error if the bit string is invalid UTF-8 data. +/// +pub fn to_string(bits: BitString) -> Result(String, Nil) { + case is_utf8(bits) { + True -> Ok(unsafe_to_string(bits)) + False -> Error(Nil) + } +} diff --git a/gleam_providers/deps/gleam_stdlib/src/gleam/bool.gleam b/gleam_providers/deps/gleam_stdlib/src/gleam/bool.gleam new file mode 100644 index 0000000..da2615b --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/src/gleam/bool.gleam @@ -0,0 +1,205 @@ +import gleam/order.{Order} + +/// A type with two possible values, True and False. Used to indicate whether +/// things are... true or false! +/// +/// Often is it clearer and offers more type safety to define a custom type +/// than to use Bool. For example, rather than having a `is_teacher: Bool` +/// field consider having a `role: SchoolRole` field where SchoolRole is a custom +/// type that can be either Student or Teacher. +/// +pub type Bool = + Bool + +/// Returns the opposite bool value. +/// +/// This is the same as the `!` or `not` operators in some other languages. +/// +/// ## Examples +/// +/// > negate(True) +/// False +/// +/// > negate(False) +/// True +/// +pub fn negate(bool: Bool) -> Bool { + case bool { + True -> False + False -> True + } +} + +/// Returns the nor of two bools +/// +/// ## Examples +/// +/// > nor(False, False) +/// True +/// +/// > nor(False, True) +/// False +/// +/// > nor(True, False) +/// False +/// +/// > nor(True, True) +/// False +/// +pub fn nor(a: Bool, b: Bool) -> Bool { + case a, b { + False, False -> True + False, True -> False + True, False -> False + True, True -> False + } +} + +/// Returns the nand of two bools +/// +/// ## Examples +/// +/// > nand(False, False) +/// True +/// +/// > nand(False, True) +/// True +/// +/// > nand(True, False) +/// True +/// +/// > nand(True, True) +/// False +/// +pub fn nand(a: Bool, b: Bool) -> Bool { + case a, b { + False, False -> True + False, True -> True + True, False -> True + True, True -> False + } +} + +/// Returns the exclusive or of two bools +/// +/// ## Examples +/// +/// > exclusive_or(False, False) +/// False +/// +/// > exclusive_or(False, True) +/// True +/// +/// > exclusive_or(True, False) +/// True +/// +/// > exclusive_or(True, True) +/// False +/// +pub fn exclusive_or(a: Bool, b: Bool) -> Bool { + case a, b { + False, False -> False + False, True -> True + True, False -> True + True, True -> False + } +} + +/// Returns the exclusive nor of two bools +/// +/// ## Examples +/// +/// > exclusive_nor(False, False) +/// True +/// +/// > exclusive_nor(False, True) +/// False +/// +/// > exclusive_nor(True, False) +/// False +/// +/// > exclusive_nor(True, True) +/// True +/// +pub fn exclusive_nor(a: Bool, b: Bool) -> Bool { + case a, b { + False, False -> True + False, True -> False + True, False -> False + True, True -> True + } +} + +/// Compares two bools and returns the first values Order to the second. +/// +/// ## Examples +/// +/// > import gleam/order +/// > compare(True, False) +/// order.Gt +/// +pub fn compare(a: Bool, with b: Bool) -> Order { + case a, b { + True, True -> order.Eq + True, False -> order.Gt + False, False -> order.Eq + False, True -> order.Lt + } +} + +/// Returns True if either bool value is True. +/// +/// ## Examples +/// +/// > max(True, False) +/// True +/// +/// > max(False, True) +/// True +/// +/// > max(False, False) +/// False +/// +pub fn max(a: Bool, b: Bool) -> Bool { + case a { + True -> True + False -> b + } +} + +/// Returns False if either bool value is False. +/// +/// ## Examples +/// +/// > max(True, False) +/// False +/// +/// > max(False, True) +/// False +/// +/// > max(False, False) +/// False +/// +pub fn min(a: Bool, b: Bool) -> Bool { + case a { + False -> False + True -> b + } +} + +/// Returns a numeric representation of the given bool. +/// +/// ## Examples +/// +/// > to_int(True) +/// 1 +/// +/// > to_int(False) +/// 0 +/// +pub fn to_int(bool: Bool) -> Int { + case bool { + False -> 0 + True -> 1 + } +} diff --git a/gleam_providers/deps/gleam_stdlib/src/gleam/dynamic.gleam b/gleam_providers/deps/gleam_stdlib/src/gleam/dynamic.gleam new file mode 100644 index 0000000..9356083 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/src/gleam/dynamic.gleam @@ -0,0 +1,414 @@ +import gleam/atom +import gleam/bit_string.{BitString} +import gleam/list +import gleam/map.{Map} +import gleam/option.{None, Option, Some} +import gleam/result +import gleam/string_builder + +/// `Dynamic` data is data that we don"t know the type of yet. +/// We likely get data like this from interop with Erlang, or from +/// IO with the outside world. +pub external type Dynamic + +pub type Decoder(t) = + fn(Dynamic) -> Result(t, String) + +/// Convert any Gleam data into `Dynamic` data. +/// +pub external fn from(a) -> Dynamic = + "gleam_stdlib" "identity" + +/// Unsafely cast a Dynamic value into any other type. +/// +/// This is an escape hatch for the type system that may be useful when wrapping +/// native Erlang APIs. It is to be used as a last measure only! +/// +/// If you can avoid using this function, do! +/// +pub external fn unsafe_coerce(Dynamic) -> a = + "gleam_stdlib" "identity" + +/// Check to see whether a Dynamic value is a bit_string, and return the bit_string if +/// it is. +/// +/// ## Examples +/// +/// > bit_string(from("Hello")) == bit_string.from_string("Hello") +/// True +/// +/// > bit_string(from(123)) +/// Error("Expected a BitString, got `123`") +/// +pub external fn bit_string(from: Dynamic) -> Result(BitString, String) = + "gleam_stdlib" "decode_bit_string" + +/// Check to see whether a Dynamic value is a string, and return the string if +/// it is. +/// +/// ## Examples +/// +/// > string(from("Hello")) +/// Ok("Hello") +/// +/// > string(from(123)) +/// Error("Expected a String, got `123`") +/// +pub fn string(from: Dynamic) -> Result(String, String) { + bit_string(from) + |> result.then(fn(raw) { + case bit_string.to_string(raw) { + Ok(string) -> Ok(string) + Error(Nil) -> Error("Expected a string, got a bit_string") + } + }) +} + +/// Check to see whether a Dynamic value is an int, and return the int if it +/// is. +/// +/// ## Examples +/// +/// > int(from(123)) +/// Ok(123) +/// +/// > int(from("Hello")) +/// Error("Expected an Int, got `\"Hello World\"`") +/// +pub external fn int(from: Dynamic) -> Result(Int, String) = + "gleam_stdlib" "decode_int" + +/// Check to see whether a Dynamic value is an float, and return the float if +/// it is. +/// +/// ## Examples +/// +/// > float(from(2.0)) +/// Ok(2.0) +/// +/// > float(from(123)) +/// Error("Expected a Float, got `123`") +/// +pub external fn float(from: Dynamic) -> Result(Float, String) = + "gleam_stdlib" "decode_float" + +/// Check to see whether a Dynamic value is an atom, and return the atom if +/// it is. +/// +/// ## Examples +/// +/// > import gleam/atom +/// > atom(from(atom.create_from_string("hello"))) +/// OK("hello") +/// +/// > atom(from(123)) +/// Error("Expected an Atom, got `123`") +/// +pub external fn atom(from: Dynamic) -> Result(atom.Atom, String) = + "gleam_stdlib" "decode_atom" + +/// Check to see whether a Dynamic value is an bool, and return the bool if +/// it is. +/// +/// ## Examples +/// +/// > bool(from(True)) +/// Ok(True) +/// +/// > bool(from(123)) +/// Error("Expected a Bool, got `123`") +/// +pub external fn bool(from: Dynamic) -> Result(Bool, String) = + "gleam_stdlib" "decode_bool" + +/// Check to see whether a Dynamic value is a function that takes no arguments, +/// and return the function if it is. +/// +/// ## Examples +/// +/// > import gleam/result +/// > let f = fn() { 1 } +/// > thunk(from(f)) |> result.is_ok +/// True +/// +/// > thunk(from(123)) +/// Error("Expected a zero arity function, got `123`") +/// +pub external fn thunk(from: Dynamic) -> Result(fn() -> Dynamic, String) = + "gleam_stdlib" "decode_thunk" + +/// Check to see whether a Dynamic value is a list, and return the list if it +/// is. +/// +/// If you wish to decode all the elements in the list use the `typed_list` +/// instead. +/// +/// ## Examples +/// +/// > list(from(["a", "b", "c"])) +/// Ok([from("a"), from("b"), from("c")]) +/// +/// > list(1) +/// Error("Expected an Int, got a binary") +/// +pub external fn list(from: Dynamic) -> Result(List(Dynamic), String) = + "gleam_stdlib" "decode_list" + +/// Check to see whether a Dynamic value is a result, and return the result if +/// it is +/// +/// ## Examples +/// +/// > result(from(Ok(1))) +/// Ok(Ok(from(1))) +/// +/// > result(from(Error("boom"))) +/// Ok(Error(from("boom"))) +/// +/// > result(from(123)) +/// Error("Expected a 2 element tuple, got an int") +/// +pub fn result(from: Dynamic) -> Result(Result(Dynamic, Dynamic), String) { + try tuple(key, val) = tuple2(from) + try tag = atom(key) + + let ok_atom = atom.create_from_string("ok") + let error_atom = atom.create_from_string("error") + + case tag { + tag if tag == ok_atom -> Ok(Ok(val)) + tag if tag == error_atom -> Ok(Error(val)) + tag -> + "Expected a tag of \"ok\" or \"error\", got \"" + |> string_builder.from_string + |> string_builder.append(atom.to_string(tag)) + |> string_builder.append("\"") + |> string_builder.to_string + |> Error + } +} + +/// Check to see whether a Dynamic value is a result of a particular type, and +/// return the result if it is +/// +/// The `ok` and `error` arguments are decoders for decoding the `Ok` and +/// `Error` values of the result. +/// +/// ## Examples +/// +/// > typed_result(of: from(Ok(1)), ok: int, error: string) +/// Ok(Ok(1)) +/// +/// > typed_result(of: from(Error("boom")), ok: int, error: string) +/// Ok(Error("boom")) +/// +/// > typed_result(of: from(123), ok: int, error: string) +/// Error("Expected a 2 element tuple, got an int") +/// +pub fn typed_result( + of dynamic: Dynamic, + ok decode_ok: Decoder(a), + error decode_error: Decoder(e), +) -> Result(Result(a, e), String) { + try inner_result = result(dynamic) + + case inner_result { + Ok(raw) -> + raw + |> decode_ok + |> result.map(Ok) + Error(raw) -> + raw + |> decode_error + |> result.map(Error) + } +} + +/// Check to see whether a Dynamic value is a list of a particular type, and +/// return the list if it is. +/// +/// The second argument is a decoder function used to decode the elements of +/// the list. The list is only decoded if all elements in the list can be +/// successfully decoded using this function. +/// +/// If you do not wish to decode all the elements in the list use the `list` +/// function instead. +/// +/// ## Examples +/// +/// > typed_list(from(["a", "b", "c"]), of: string) +/// Ok(["a", "b", "c"]) +/// +/// > typed_list(from([1, 2, 3]), of: string) +/// Error("Expected an Int, got a binary") +/// +/// > typed_list(from("ok"), of: string) +/// Error("Expected a List, got a binary") +/// +pub fn typed_list( + from dynamic: Dynamic, + of decoder_type: fn(Dynamic) -> Result(inner, String), +) -> Result(List(inner), String) { + dynamic + |> list + |> result.then(list.try_map(_, decoder_type)) +} + +/// Check to see if a Dynamic value is an Option of a particular type, and return +/// the Option if it is. +/// +/// The second argument is a decoder function used to decode the elements of +/// the list. The list is only decoded if all elements in the list can be +/// successfully decoded using this function. +/// +/// ## Examples +/// +/// > option(from("Hello"), string) +/// Ok(Some("Hello")) +/// +/// > option(from(atom.from_string("null")), string) +/// Ok(None) +/// +/// > option(from(123), string) +/// Error("Expected a bit_string, got an int") +/// +pub fn option( + from dynamic: Dynamic, + of decoder: Decoder(inner), +) -> Result(Option(inner), String) { + let Ok(null) = atom.from_string("null") + case atom(dynamic), decoder(dynamic) { + Ok(atom), _ if atom == null -> Ok(None) + _, Ok(result) -> Ok(Some(result)) + _, Error(msg) -> Error(msg) + } +} + +/// Check to see if a Dynamic value is a map with a specific field, and return +/// the value of the field if it is. +/// +/// This will not succeed on a record. +/// +/// ## Examples +/// +/// > import gleam/map +/// > field(from(map.new("Hello", "World")), "Hello") +/// Ok(Dynamic) +/// +/// > field(from(123), "Hello") +/// Error("Expected a map with key `\"Hello\"`, got an Int") +/// +pub external fn field(from: Dynamic, named: a) -> Result(Dynamic, String) = + "gleam_stdlib" "decode_field" + +/// Check to see if the Dynamic value is a tuple large enough to have a certain +/// index, and return the value of that index if it is. +/// +/// ## Examples +/// +/// > element(from(tuple(1, 2)), 0) +/// Ok(from(1)) +/// +/// > element(from(tuple(1, 2)), 2) +/// Error("Expected a tuple of at least 3 size, got a tuple of 2 size") +/// +/// > element(from(""), 2) +/// Error("Expected a tuple, got a binary") +/// +pub external fn element(from: Dynamic, position: Int) -> Result(Dynamic, String) = + "gleam_stdlib" "decode_element" + +/// Check to see if the Dynamic value is a 2 element tuple. +/// +/// If you do not wish to decode all the elements in the tuple use the +/// `typed_tuple2` function instead. +/// +/// ## Examples +/// +/// > tuple2(from(tuple(1, 2))) +/// Ok(tuple(from(1), from(2))) +/// +/// > tuple2(from(tuple(1, 2))) +/// Error("Expected a 2 element tuple") +/// +/// > tuple2(from("")) +/// Error("Expected a tuple, got a binary") +/// +pub external fn tuple2(from: Dynamic) -> Result(tuple(Dynamic, Dynamic), String) = + "gleam_stdlib" "decode_tuple2" + +/// Check to see if the Dynamic value is a 2 element tuple containing two +/// specifically typed elements. +/// +/// If you wish to decode all the elements in the list use the `typed_tuple2` +/// instead. +/// +/// ## Examples +/// +/// > typed_tuple2(from(tuple(1, 2)), int, int) +/// Ok(tuple(1, 2)) +/// +/// > typed_tuple2(from(tuple(1, 2.0)), int, float) +/// Ok(tuple(1, 2.0)) +/// +/// > typed_tuple2(from(tuple(1, 2, 3)), int, float) +/// Error("Expected a 2 element tuple, got a 3 element tuple") +/// +/// > typed_tuple2(from(""), int, float) +/// Error("Expected a tuple, got a binary") +/// +pub fn typed_tuple2( + from tup: Dynamic, + first decode_first: Decoder(a), + second decode_second: Decoder(b), +) -> Result(tuple(a, b), String) { + try tuple(first, second) = tuple2(tup) + try a = decode_first(first) + try b = decode_second(second) + Ok(tuple(a, b)) +} + +/// Check to see if the Dynamic value is map. +/// +/// ## Examples +/// +/// > import gleam/map +/// > map(from(map.new())) +/// Ok(map.new()) +/// +/// > map(from(1)) +/// Error("Expected a 2 element tuple, got an int") +/// +/// > map(from("")) +/// Error("Expected a map, got a binary") +/// +pub external fn map(from: Dynamic) -> Result(Map(Dynamic, Dynamic), String) = + "gleam_stdlib" "decode_map" + +/// Join multiple decoders into one. When run they will each be tried in turn +/// until one succeeds, or they all fail. +/// +/// ## Examples +/// +/// > import gleam/result +/// > let bool_or_string = any(_, of: [ +/// > string, +/// > fn(x) { result.map(bool(x), fn(_) { "a bool" }) } +/// > ]) +/// > bool_or_string(from("ok")) +/// Ok("ok") +/// +/// > bool_or_string(from(True)) +/// Ok("a bool") +/// +/// > bool_or_string(from(1)) +/// Error("Unexpected value") +/// +pub fn any( + from data: Dynamic, + of decoders: List(Decoder(t)), +) -> Result(t, String) { + decoders + |> list.find_map(fn(decoder) { decoder(data) }) + |> result.map_error(fn(_) { "Unexpected value" }) +} diff --git a/gleam_providers/deps/gleam_stdlib/src/gleam/float.gleam b/gleam_providers/deps/gleam_stdlib/src/gleam/float.gleam new file mode 100644 index 0000000..880fbfc --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/src/gleam/float.gleam @@ -0,0 +1,213 @@ +import gleam/string_builder +import gleam/order.{Order} + +pub type Float = + Float + +/// Attempts to parse a string as a float, returning `Error(Nil)` if it was not +/// possible. +/// +/// ## Examples +/// > parse("2.3") +/// Ok(2.3) +/// +/// > parse("ABC") +/// Error(Nil) +/// +pub external fn parse(String) -> Result(Float, Nil) = + "gleam_stdlib" "parse_float" + +/// Return the string representation of the provided float. +/// +/// ## Examples +/// > to_string(2.3) +/// "2.3" +/// +pub fn to_string(f: Float) -> String { + f + |> string_builder.from_float + |> string_builder.to_string +} + +/// Compares two floats, returning an order. +/// +/// ## Examples +/// > compare(2.0, 2.3) +/// Lt +/// +pub fn compare(a: Float, with b: Float) -> Order { + case a == b { + True -> order.Eq + False -> + case a <. b { + True -> order.Lt + False -> order.Gt + } + } +} + +/// Compares two floats, returning the smaller of the two. +/// +/// ## Examples +/// +/// > min(2.0, 2.3) +/// 2.0 +/// +pub fn min(a: Float, b: Float) -> Float { + case a <. b { + True -> a + False -> b + } +} + +/// Compares two floats, returning the larger of the two. +/// +/// ## Examples +/// +/// > max(2.0, 2.3) +/// 2.3 +/// +pub fn max(a: Float, b: Float) -> Float { + case a >. b { + True -> a + False -> b + } +} + +/// Rounds the value to the next highest whole number as a float. +/// +/// ## Examples +/// +/// > ceiling(2.3) +/// 3.0 +/// +pub external fn ceiling(Float) -> Float = + "math" "ceil" + +/// Rounds the value to the next lowest whole number as a float. +/// +/// ## Examples +/// +/// > floor(2.3) +/// 2.0 +/// +pub external fn floor(Float) -> Float = + "math" "floor" + +/// Rounds the value to the nearest whole number as an int. +/// +/// ## Examples +/// +/// > round(2.3) +/// 2 +/// +/// > round(2.5) +/// 3 +/// +pub external fn round(Float) -> Int = + "erlang" "round" + +/// Returns the value as an int, truncating all decimal digits. +/// +/// ## Examples +/// +/// > truncate(2.4343434847383438) +/// 2 +/// +pub external fn truncate(Float) -> Int = + "erlang" "trunc" + +/// Returns the absolute value of the input as a float. +/// +/// ## Examples +/// +/// > absolute_value(-12.5) +/// 12.5 +/// +/// > absolute_value(10.2) +/// 10.2 +/// +pub external fn absolute_value(Float) -> Float = + "erlang" "abs" + +/// Returns the results of the base being raised to the power of the +/// exponent, as a float. +/// +/// ## Examples +/// +/// > power(2.0, 2.0) +/// 4.0 +/// +/// > power(8.0, 1.5) +/// 64.0 +/// +pub external fn power(base: Float, exponent: Float) -> Float = + "math" "pow" + +/// Returns the square root of the input as a float. +/// +/// ## Examples +/// +/// > square_root(4.0) +/// Ok(2.0) +/// +/// > square_root(-16.0) +/// Error(Nil) +/// +pub fn square_root(number: Float) -> Result(Float, Nil) { + case number <. 0.0 { + True -> Error(Nil) + False -> Ok(power(number, 0.5)) + } +} + +/// Returns the negative of the value provided +/// +/// ## Examples +/// +/// > negate(1.) +/// -1. +/// +pub fn negate(x: Float) -> Float { + -1. *. x +} + +/// Sums a list of Floats. +/// +/// ## Example +/// +/// > sum([1.0, 2.2, 3.3]) +/// 6.5 +/// +pub fn sum(numbers: List(Float)) -> Float { + numbers + |> do_sum(0.0) +} + +fn do_sum(numbers: List(Float), initial: Float) -> Float { + case numbers { + [] -> initial + [x, ..rest] -> do_sum(rest, x +. initial) + } +} + +/// Multiplies a list of Floats and returns the product. +/// +/// ## Example +/// +/// > product([2.5, 3.2, 4.2]) +/// 33.6 +/// +pub fn product(numbers: List(Float)) -> Float { + case numbers { + [] -> 0. + _ -> do_product(numbers, 1.) + } +} + +fn do_product(numbers: List(Float), initial: Float) -> Float { + case numbers { + [] -> initial + [x, ..rest] -> do_product(rest, x *. initial) + } +} diff --git a/gleam_providers/deps/gleam_stdlib/src/gleam/function.gleam b/gleam_providers/deps/gleam_stdlib/src/gleam/function.gleam new file mode 100644 index 0000000..08e7efd --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/src/gleam/function.gleam @@ -0,0 +1,71 @@ +import gleam/dynamic.{Dynamic} + +/// Takes two functions and chains them together to form one function that takes +/// the input from the first and returns the output of the second. +/// +pub fn compose(fun1: fn(a) -> b, fun2: fn(b) -> c) -> fn(a) -> c { + fn(a) { fun2(fun1(a)) } +} + +/// Takes a function with arity two +/// and returns a curried equivalent. +/// fn(a, b) -> c becomes fn(a) -> fn(b) -> c +pub fn curry2(fun: fn(a, b) -> value) { + fn(a) { fn(b) { fun(a, b) } } +} + +/// Takes a function with arity three +/// and returns a curried equivalent. +/// fn(a, b, c) -> d becomes fn(a) -> fn(b) -> fn(c) -> d +pub fn curry3(fun: fn(a, b, c) -> value) { + fn(a) { fn(b) { fn(c) { fun(a, b, c) } } } +} + +/// Takes a function with arity four +/// and returns a curried equivalent. +pub fn curry4(fun: fn(a, b, c, d) -> value) { + fn(a) { fn(b) { fn(c) { fn(d) { fun(a, b, c, d) } } } } +} + +/// Takes a function with arity five +/// and returns a curried equivalent. +pub fn curry5(fun: fn(a, b, c, d, e) -> value) { + fn(a) { fn(b) { fn(c) { fn(d) { fn(e) { fun(a, b, c, d, e) } } } } } +} + +/// Takes a function with arity six +/// and returns a curried equivalent. +pub fn curry6(fun: fn(a, b, c, d, e, f) -> value) { + fn(a) { + fn(b) { fn(c) { fn(d) { fn(e) { fn(f) { fun(a, b, c, d, e, f) } } } } } + } +} + +/// Takes a function that takes two arguments and returns a new function that +/// takes the same two arguments, but in reverse order. +/// +pub fn flip(fun: fn(a, b) -> c) -> fn(b, a) -> c { + fn(b, a) { fun(a, b) } +} + +/// A function that always returns its input value. +/// +pub fn identity(x: a) -> a { + x +} + +pub type Exception { + Exited(Dynamic) + Thrown(Dynamic) + Errored(Dynamic) +} + +/// Gleam doesn't offer any way to raise exceptions, but they may still occur +/// due to bugs when working with unsafe code, such as when calling Erlang +/// function. +/// +/// This function will catch any error thrown and convert it into a result +/// rather than crashing the process. +/// +pub external fn rescue(fn() -> a) -> Result(a, Exception) = + "gleam_stdlib" "rescue" diff --git a/gleam_providers/deps/gleam_stdlib/src/gleam/int.gleam b/gleam_providers/deps/gleam_stdlib/src/gleam/int.gleam new file mode 100644 index 0000000..4c30ddc --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/src/gleam/int.gleam @@ -0,0 +1,207 @@ +import gleam/order.{Order} + +pub type Int = + Int + +/// Returns the absolute value of the input. +/// +/// ## Examples +/// +/// > absolute_value(-12) +/// 12 +/// +/// > absolute_value(10) +/// 10 +/// +pub fn absolute_value(num: Int) -> Int { + case num >= 0 { + True -> num + False -> num * -1 + } +} + +/// Parse a given string as an int if possible. +/// +/// ## Examples +/// +/// > parse("2") +/// Ok(2) +/// +/// > parse("ABC") +/// Error(Nil) +/// +pub external fn parse(String) -> Result(Int, Nil) = + "gleam_stdlib" "parse_int" + +/// Print a given int to a string. +/// +/// ## Examples +/// +/// > to_string(2) +/// "2" +/// +pub external fn to_string(Int) -> String = + "erlang" "integer_to_binary" + +/// Print a given int to a string using the base number provided. +/// +/// ## Examples +/// +/// > to_base_string(2, 2) +/// "10" +/// +/// > to_base_string(48, 16) +/// "30" +/// +/// > to_base_string(48, 36) +/// "1C" +/// +pub external fn to_base_string(Int, Int) -> String = + "erlang" "integer_to_binary" + +/// Takes an int and returns its value as a float +/// +/// ## Examples +/// +/// > to_float(5) +/// 5. +/// +/// > to_float(0) +/// 0. +/// +/// > to_float(-3) +/// -3. +/// +pub external fn to_float(a: Int) -> Float = + "erlang" "float" + +/// Compares two ints, returning an order. +/// +/// ## Examples +/// +/// > compare(2, 3) +/// Lt +/// +/// > compare(4, 3) +/// Gt +/// +/// > compare(3, 3) +/// Eq +/// +pub fn compare(a: Int, with b: Int) -> Order { + case a == b { + True -> order.Eq + False -> + case a < b { + True -> order.Lt + False -> order.Gt + } + } +} + +/// Compares two int, returning the smaller of the two. +/// +/// ## Examples +/// +/// > min(2, 3) +/// 2 +/// +pub fn min(a: Int, b: Int) -> Int { + case a < b { + True -> a + False -> b + } +} + +/// Compares two int, returning the larger of the two. +/// +/// ## Examples +/// +/// > max(2, 3) +/// 3 +/// +pub fn max(a: Int, b: Int) -> Int { + case a > b { + True -> a + False -> b + } +} + +/// Returns whether the value provided is even. +/// +/// ## Examples +/// +/// > is_even(2) +/// True +/// +/// > is_even(3) +/// False +/// +pub fn is_even(x: Int) -> Bool { + x % 2 == 0 +} + +/// Returns whether the value provided is odd. +/// +/// ## Examples +/// +/// > is_odd(3) +/// True +/// +/// > is_odd(2) +/// False +/// +pub fn is_odd(x: Int) -> Bool { + x % 2 != 0 +} + +/// Returns the negative of the value provided +/// +/// ## Examples +/// +/// > negate(1) +/// -1 +/// +pub fn negate(x: Int) -> Int { + -1 * x +} + +/// Sums a list of Ints. +/// +/// ## Example +/// +/// > sum([1, 2, 3]) +/// 6 +/// +pub fn sum(numbers: List(Int)) -> Int { + numbers + |> do_sum(0) +} + +fn do_sum(numbers: List(Int), initial: Int) -> Int { + case numbers { + [] -> initial + [x, ..rest] -> do_sum(rest, x + initial) + } +} + +/// Multiplies a list of Ints and returns the product. +/// +/// ## Example +/// +/// > product([2, 3, 4]) +/// 24 +/// +pub fn product(numbers: List(Int)) -> Int { + case numbers { + [] -> 0 + _ -> do_product(numbers, 1) + } +} + +fn do_product(numbers: List(Int), initial: Int) -> Int { + case numbers { + [] -> initial + [x, ..rest] -> do_product(rest, x * initial) + } +} diff --git a/gleam_providers/deps/gleam_stdlib/src/gleam/io.gleam b/gleam_providers/deps/gleam_stdlib/src/gleam/io.gleam new file mode 100644 index 0000000..2b083b1 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/src/gleam/io.gleam @@ -0,0 +1,57 @@ +external type DoNotLeak + +external fn erl_print(String, List(a)) -> DoNotLeak = + "io" "fwrite" + +/// Writes a string to standard output. +/// +/// ## Example +/// +/// > io.print("Hi mum") +/// // -> Hi mum +/// Nil +/// +pub fn print(string: String) -> Nil { + erl_print(string, []) + Nil +} + +/// Writes a string to standard output, appending a newline to the end. +/// +/// ## Example +/// +/// > io.println("Hi mum") +/// // -> Hi mum +/// Nil +/// +pub fn println(string: String) -> Nil { + erl_print("~ts\n", [string]) + Nil +} + +/// Print a value to standard output using Erlang syntax. +/// +/// The value is returned after being printed so it can be used in pipelines. +/// +/// ## Example +/// +/// > io.debug("Hi mum") +/// // -> <<"Hi mum">> +/// "Hi mum" +/// +/// > io.debug(Ok(1)) +/// // -> {ok, 1} +/// Ok(1) +/// +/// > import list +/// > [1, 2] +/// > |> list.map(fn(x) { x + 1 }) +/// > |> io.debug +/// > |> list.map(fn(x) { x * 2 }) +/// // -> [2, 3] +/// [4, 6] +/// +pub fn debug(term: anything) -> anything { + erl_print("~tp\n", [term]) + term +} diff --git a/gleam_providers/deps/gleam_stdlib/src/gleam/iterator.gleam b/gleam_providers/deps/gleam_stdlib/src/gleam/iterator.gleam new file mode 100644 index 0000000..81ef2ce --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/src/gleam/iterator.gleam @@ -0,0 +1,496 @@ +import gleam/list + +// Internal private representation of an Iterator +type Action(element) { + Stop + Continue(element, fn() -> Action(element)) +} + +/// An iterator is a lazily evaluated sequence of element. +/// +/// Iterators are useful when working with collections that are too large to +/// fit in memory (or those that are infinite in size) as they only require the +/// elements currently being processed to be in memory. +/// +/// As a lazy data structure no work is done when an iterator is filters, +/// mapped, etc, instead a new iterator is returned with these transformations +/// applied to the stream. Once the stream has all the required transformations +/// applied it can be evaluated using functions such as `fold` and `take`. +/// +pub opaque type Iterator(element) { + Iterator(continuation: fn() -> Action(element)) +} + +// Public API for iteration +pub type Step(element, accumulator) { + Next(element: element, accumulator: accumulator) + Done +} + +// Creating Iterators +fn do_unfold( + initial: acc, + f: fn(acc) -> Step(element, acc), +) -> fn() -> Action(element) { + fn() { + case f(initial) { + Next(x, acc) -> Continue(x, do_unfold(acc, f)) + Done -> Stop + } + } +} + +/// Create an iterator from a given function and accumulator. +/// +/// The function is called on the accumulator and return either `Done`, +/// indicating the iterator has no more elements, or `Next` which contains a +/// new element and accumulator. The element is yielded by the iterator and the +/// new accumulator is used with the function to compute the next element in +/// the sequence. +/// +/// ## Examples +/// +/// > unfold(from: 5, with: fn(n) { +/// > case n { +/// > 0 -> Done +/// > n -> Next(element: n, accumulator: n - 1) +/// > } +/// > }) +/// > |> to_list +/// [5, 4, 3, 2, 1] +/// +pub fn unfold( + from initial: acc, + with f: fn(acc) -> Step(element, acc), +) -> Iterator(element) { + initial + |> do_unfold(f) + |> Iterator +} + +// TODO: test +/// Create an iterator that yields values created by calling a given function +/// repeatedly. +/// +pub fn repeatedly(f: fn() -> element) -> Iterator(element) { + unfold(Nil, fn(_) { Next(f(), Nil) }) +} + +/// Create an iterator that returns the same value infinitely. +/// +/// ## Examples +/// +/// > repeat(10) +/// > |> take(4) +/// > |> to_list +/// [10, 10, 10, 10] +/// +pub fn repeat(x: element) -> Iterator(element) { + repeatedly(fn() { x }) +} + +/// Create an iterator the yields each element in a given list. +/// +/// ## Examples +/// +/// > from_list([1, 2, 3, 4]) |> to_list +/// [1, 2, 3, 4] +/// +pub fn from_list(list: List(element)) -> Iterator(element) { + let yield = fn(acc) { + case acc { + [] -> Done + [head, ..tail] -> Next(head, tail) + } + } + unfold(list, yield) +} + +// Consuming Iterators +fn do_fold( + continuation: fn() -> Action(e), + initial: acc, + f: fn(e, acc) -> acc, +) -> acc { + case continuation() { + Continue(element, iterator) -> do_fold(iterator, f(element, initial), f) + Stop -> initial + } +} + +/// Reduce an iterator of elements into a single value by calling a given +/// function on each element in turn. +/// +/// If called on an iterator of infinite length then this function will never +/// return. +/// +/// If you do not care about the end value and only wish to evaluate the +/// iterator for side effects consider using the `run` function instead. +/// +/// ## Examples +/// +/// > [1, 2, 3, 4] +/// > |> from_list +/// > |> fold(from: 0, with: fn(element, acc) { element + acc }) +/// 10 +/// +pub fn fold( + over iterator: Iterator(e), + from initial: acc, + with f: fn(e, acc) -> acc, +) -> acc { + iterator.continuation + |> do_fold(initial, f) +} + +// TODO: test +/// Evaluate all elements in a given stream. This function is useful for when +/// you wish to trigger any side effects that would occur when evaluating +/// the iterator. +/// +pub fn run(iterator: Iterator(e)) -> Nil { + fold(iterator, Nil, fn(_, _) { Nil }) +} + +/// Evaluate an iterator and return all the elements as a list. +/// +/// If called on an iterator of infinite length then this function will never +/// return. +/// +/// ## Examples +/// +/// > [1, 2, 3] |> from_list |> map(fn(x) { x * 2 }) |> to_list +/// [2, 4, 6] +/// +pub fn to_list(iterator: Iterator(element)) -> List(element) { + iterator + |> fold([], fn(e, acc) { [e, ..acc] }) + |> list.reverse +} + +/// Eagerly access the first value of an interator, returning a `Next` +/// that contains the first value and the rest of the iterator. +/// +/// If called on an empty iterator, `Done` is returned. +/// +/// ## Examples +/// +/// > assert Next(head, tail) = +/// > [1, 2, 3, 4] +/// > |> from_list +/// > |> step +/// > head +/// 1 +/// > tail |> to_list +/// [2, 3, 4] +/// +/// > [] +/// > |> from_list +/// > |> step +/// Done +/// +pub fn step(iterator: Iterator(e)) -> Step(e, Iterator(e)) { + case iterator.continuation() { + Stop -> Done + Continue(e, a) -> Next(e, Iterator(a)) + } +} + +fn do_take( + continuation: fn() -> Action(e), + desired: Int, + acc: List(e), +) -> List(e) { + case desired > 0 { + True -> + case continuation() { + Continue(element, iterator) -> + do_take(iterator, desired - 1, [element, ..acc]) + Stop -> + acc + |> list.reverse + } + False -> + acc + |> list.reverse + } +} + +/// Evaluate a desired number of elements from an iterator and return them in a +/// list. +/// +/// If the iterator does not have enough elements all of them are returned. +/// +/// ## Examples +/// +/// > [1, 2, 3, 4, 5] |> from_list |> take(up_to: 3) +/// [1, 2, 3] +/// +/// > [1, 2] |> from_list |> take(up_to: 3) +/// [1, 2] +/// +pub fn take(from iterator: Iterator(e), up_to desired: Int) -> List(e) { + iterator.continuation + |> do_take(desired, []) +} + +fn do_drop(continuation: fn() -> Action(e), desired: Int) -> fn() -> Action(e) { + case desired > 0 { + True -> + case continuation() { + Continue(_, iterator) -> do_drop(iterator, desired - 1) + Stop -> fn() { Stop } + } + False -> continuation + } +} + +/// Evaluate and discard the first N elements in an iterator, returning a new +/// iterator. +/// +/// If the iterator does not have enough elements an empty iterator is +/// returned. +/// +/// This function does not evaluate the elements of the iterator, the +/// computation is performed when the iterator is later run. +/// +/// ## Examples +/// +/// > [1, 2, 3, 4, 5] |> from_list |> drop(up_to: 3) |> to_list +/// [4, 5] +/// +/// > [1, 2] |> from_list |> drop(up_to: 3) |> to_list +/// [] +/// +pub fn drop(from iterator: Iterator(e), up_to desired: Int) -> Iterator(e) { + iterator.continuation + |> do_drop(desired) + |> Iterator +} + +fn do_map(continuation: fn() -> Action(a), f: fn(a) -> b) -> fn() -> Action(b) { + fn() { + case continuation() { + Continue(e, continuation) -> Continue(f(e), do_map(continuation, f)) + Stop -> Stop + } + } +} + +/// Create an iterator from an existing iterator and a transformation function. +/// +/// Each element in the new iterator will be the result of calling the given +/// function on the elements in the given iterator. +/// +/// This function does not evaluate the elements of the iterator, the +/// computation is performed when the iterator is later run. +/// +/// ## Examples +/// +/// > [1, 2, 3] |> from_list |> map(fn(x) { x * 2 }) |> to_list +/// [2, 4, 6] +/// +pub fn map(over iterator: Iterator(a), with f: fn(a) -> b) -> Iterator(b) { + iterator.continuation + |> do_map(f) + |> Iterator +} + +fn do_append( + first: fn() -> Action(a), + second: fn() -> Action(a), +) -> fn() -> Action(a) { + fn() { + case first() { + Continue(e, first) -> Continue(e, do_append(first, second)) + Stop -> second() + } + } +} + +/// Append two iterators, producing a new iterator. +/// +/// This function does not evaluate the elements of the iterators, the +/// computation is performed when the resulting iterator is later run. +/// +/// ## Examples +/// +/// > [1, 2] |> from_list |> append([3, 4] |> from_list) |> to_list +/// [1, 2, 3, 4] +/// +pub fn append(to first: Iterator(a), suffix second: Iterator(a)) -> Iterator(a) { + first.continuation + |> do_append(second.continuation) + |> Iterator +} + +fn do_flatten(continuation: fn() -> Action(Iterator(a))) -> fn() -> Action(a) { + fn() { + case continuation() { + Continue(e, continuation) -> + do_append(e.continuation, do_flatten(continuation))() + Stop -> Stop + } + } +} + +/// Flatten an iterator of iterator of iterators, creating a new iterator. +/// +/// This function does not evaluate the elements of the iterator, the +/// computation is performed when the iterator is later run. +/// +/// ## Examples +/// +/// > [[1, 2], [3, 4]] |> list.map(from_list) |> flatten |> to_list +/// [1, 2, 3, 4] +/// +pub fn flatten(iterator: Iterator(Iterator(a))) -> Iterator(a) { + iterator.continuation + |> do_flatten + |> Iterator +} + +/// Create an iterator from an existing iterator and a transformation function. +/// +/// Each element in the new iterator will be the result of calling the given +/// function on the elements in the given iterator and then flattening the +/// results. +/// +/// This function does not evaluate the elements of the iterator, the +/// computation is performed when the iterator is later run. +/// +/// ## Examples +/// +/// > [1, 2] |> from_list |> flat_map(fn(x) { from_list([x, x + 1]) }) |> to_list +/// [1, 2, 2, 3] +/// +pub fn flat_map( + over iterator: Iterator(a), + with f: fn(a) -> Iterator(b), +) -> Iterator(b) { + iterator + |> map(f) + |> flatten +} + +fn do_filter( + continuation: fn() -> Action(e), + predicate: fn(e) -> Bool, +) -> fn() -> Action(e) { + fn() { + case continuation() { + Continue(e, iterator) -> + case predicate(e) { + True -> Continue(e, do_filter(iterator, predicate)) + False -> do_filter(iterator, predicate)() + } + Stop -> Stop + } + } +} + +/// Create an iterator from an existing iterator and a predicate function. +/// +/// The new iterator will contain elements from the first iterator for which +/// the given function returns `True`. +/// +/// This function does not evaluate the elements of the iterator, the +/// computation is performed when the iterator is later run. +/// +/// ## Examples +/// +/// > import gleam/int +/// > [1, 2, 3, 4] |> from_list |> filter(int.is_even) |> to_list +/// [2, 4] +/// +pub fn filter( + iterator: Iterator(a), + for predicate: fn(a) -> Bool, +) -> Iterator(a) { + iterator.continuation + |> do_filter(predicate) + |> Iterator +} + +fn do_cycle(next: fn() -> Action(a), reset: fn() -> Action(a)) { + fn() { + case next() { + Continue(e, iterator) -> Continue(e, do_cycle(iterator, reset)) + Stop -> do_cycle(reset, reset)() + } + } +} + +/// Create an iterator that repeats a given iterator infinitely. +/// +/// ## Examples +/// +/// > [1, 2] |> from_list |> cycle |> take(6) +/// [1, 2, 1, 2, 1, 2] +/// +pub fn cycle(iterator: Iterator(a)) -> Iterator(a) { + iterator.continuation + |> do_cycle(iterator.continuation) + |> Iterator +} + +fn do_range(current, limit, inc) -> fn() -> Action(Int) { + case current == limit { + True -> fn() { Stop } + False -> fn() { Continue(current, do_range(current + inc, limit, inc)) } + } +} + +/// Create an iterator of ints, starting at a given start int and stepping by +/// one to a given end int. +/// +/// ## Examples +/// +/// > range(from: 1, to: 5) |> to_list +/// [1, 2, 3, 4] +/// +/// > range(from: 1, to: -2) |> to_list +/// [1, 0, -1] +/// +/// > range(from: 0, to: 0) |> to_list +/// [] +/// +pub fn range(from start: Int, to stop: Int) -> Iterator(Int) { + case start < stop { + True -> 1 + False -> -1 + } + |> do_range(start, stop, _) + |> Iterator +} + +/// Find the first element in a given iterator for which the given function returns +/// True. +/// +/// Returns `Error(Nil)` if the function does not return True for any of the +/// elements. +/// +/// ## Examples +/// +/// > find(from_list([1, 2, 3]), fn(x) { x > 2 }) +/// Ok(3) +/// +/// > find(from_list([1, 2, 3]), fn(x) { x > 4 }) +/// Error(Nil) +/// +/// > find(from_list([]), fn(x) { True }) +/// Error(Nil) +/// +pub fn find( + in haystack: Iterator(a), + one_that is_desired: fn(a) -> Bool, +) -> Result(a, Nil) { + case haystack.continuation() { + Continue(element, continuation) -> + case is_desired(element) { + True -> Ok(element) + False -> find(Iterator(continuation), is_desired) + } + Stop -> Error(Nil) + } +} diff --git a/gleam_providers/deps/gleam_stdlib/src/gleam/list.gleam b/gleam_providers/deps/gleam_stdlib/src/gleam/list.gleam new file mode 100644 index 0000000..dbfa421 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/src/gleam/list.gleam @@ -0,0 +1,1141 @@ +//// Lists are an ordered sequence of elements and are one of the most common +//// data types in Gleam. +//// +//// New elements can be added and removed from the front of a list in +//// constant time, while adding and removing from the end requires traversing +//// the copying the whole list, so keep this in mind when designing your +//// programs. +//// +//// There is a dedicated syntax for prefixing to a list: +//// +//// let new_list = [1, 2, ..existing_list] +//// +//// And a matching syntax for getting the first elements of a list: +//// +//// case list { +//// [first_element, ..rest] -> first_element +//// _ -> "this pattern matches when the list is empty" +//// } +//// + +import gleam/int +import gleam/pair +import gleam/order.{Order} + +pub type List(elements) = + List(elements) + +/// An error value returned by the `strict_zip` function. +/// +pub type LengthMismatch { + LengthMismatch +} + +/// Count the number of elements in a given list. +/// +/// This function has to traverse the list to determine the number of elements, +/// so it runs in linear time. +/// +/// This function is natively implemented by the virtual machine and is highly +/// optimised. +/// +/// ## Examples +/// +/// > length([]) +/// 0 +/// +/// > length([1]) +/// 1 +/// +/// > length([1, 2]) +/// 2 +/// +pub external fn length(of: List(a)) -> Int = + "erlang" "length" + +/// Create a new list from a given list containing the same elements but in the +/// opposite order. +/// +/// This function has to traverse the list to create the new reversed list, so +/// it runs in linear time. +/// +/// This function is natively implemented by the virtual machine and is highly +/// optimised. +/// +/// ## Examples +/// +/// > reverse([]) +/// [] +/// +/// > reverse([1]) +/// [1] +/// +/// > reverse([1, 2]) +/// [2, 1] +/// +pub external fn reverse(List(a)) -> List(a) = + "lists" "reverse" + +/// Determine whether or not the list is empty. +/// +/// This function runs in constant time. +/// +/// ## Examples +/// +/// > is_empty([]) +/// True +/// +/// > is_empty([1]) +/// False +/// +/// > is_empty([1, 1]) +/// False +/// +pub fn is_empty(list: List(a)) -> Bool { + list == [] +} + +/// Determine whether or not a given element exists within a given list. +/// +/// This function traverses the list to find the element, so it runs in linear +/// time. +/// +/// ## Examples +/// +/// > [] |> contains(any: 0) +/// True +/// +/// > [0] |> contains(any: 0) +/// True +/// +/// > [1] |> contains(any: 0) +/// False +/// +/// > [1, 1] |> contains(any: 0) +/// False +/// +/// > [1, 0] |> contains(any: 0) +/// True +/// +pub fn contains(list: List(a), any elem: a) -> Bool { + case list { + [] -> False + [head, ..rest] -> head == elem || contains(rest, elem) + } +} + +/// Get the first element from the start of the list, if there is one. +/// +/// ## Examples +/// +/// > head([]) +/// Error(Nil) +/// +/// > head([0]) +/// Ok(0) +/// +/// > head([1, 2]) +/// Ok(1) +/// +pub fn head(list: List(a)) -> Result(a, Nil) { + case list { + [] -> Error(Nil) + [x, .._] -> Ok(x) + } +} + +/// Get the list minus the first element. If the list is empty `Error(Nil)` is +/// returned. +/// +/// This function runs in constant time and does not make a copy of the list. +/// +/// ## Examples +/// +/// > tail([]) +/// Error(Nil) +/// +/// > tail([0]) +/// Ok([]) +/// +/// > tail([1, 2]) +/// Ok([2]) +/// +pub fn tail(list: List(a)) -> Result(List(a), Nil) { + case list { + [] -> Error(Nil) + [_, ..xs] -> Ok(xs) + } +} + +fn do_filter(list: List(a), fun: fn(a) -> Bool, acc: List(a)) -> List(a) { + case list { + [] -> reverse(acc) + [x, ..xs] -> { + let new_acc = case fun(x) { + True -> [x, ..acc] + False -> acc + } + do_filter(xs, fun, new_acc) + } + } +} + +/// Returns a new list containing only the elements from the first list for +/// which the given functions returns `True`. +/// +/// ## Examples +/// +/// > filter([2, 4, 6, 1], fn(x) { x > 2 }) +/// [4, 6] +/// +/// > filter([2, 4, 6, 1], fn(x) { x > 6 }) +/// [] +/// +pub fn filter(list: List(a), for predicate: fn(a) -> Bool) -> List(a) { + do_filter(list, predicate, []) +} + +fn do_filter_map( + list: List(a), + fun: fn(a) -> Result(b, e), + acc: List(b), +) -> List(b) { + case list { + [] -> reverse(acc) + [x, ..xs] -> { + let new_acc = case fun(x) { + Ok(x) -> [x, ..acc] + Error(_) -> acc + } + do_filter_map(xs, fun, new_acc) + } + } +} + +/// Returns a new list containing only the elements from the first list for +/// which the given functions returns `Ok(_)`. +/// +/// ## Examples +/// +/// > filter_map([2, 4, 6, 1], Error) +/// [] +/// +/// > filter_map([2, 4, 6, 1], fn(x) { Ok(x + 1) }) +/// [3, 4, 6, 2] +/// +pub fn filter_map(list: List(a), with fun: fn(a) -> Result(b, e)) -> List(b) { + do_filter_map(list, fun, []) +} + +fn do_map(list: List(a), fun: fn(a) -> b, acc: List(b)) -> List(b) { + case list { + [] -> reverse(acc) + [x, ..xs] -> do_map(xs, fun, [fun(x), ..acc]) + } +} + +/// Returns a new list containing only the elements of the first list after the +/// function has been applied to each one. +/// +/// ## Examples +/// +/// > map([2, 4, 6], fn(x) { x * 2 }) +/// [4, 8, 12] +/// +pub fn map(list: List(a), with fun: fn(a) -> b) -> List(b) { + do_map(list, fun, []) +} + +fn do_index_map( + list: List(a), + fun: fn(Int, a) -> b, + index: Int, + acc: List(b), +) -> List(b) { + case list { + [] -> reverse(acc) + [x, ..xs] -> do_index_map(xs, fun, index + 1, [fun(index, x), ..acc]) + } +} + +/// Returns a new list containing only the elements of the first list after the +/// function has been applied to each one and their index. +/// +/// The index starts at 0, so the first element is 0, the second is 1, and so +/// on. +/// +/// ## Examples +/// +/// > index_map(["a", "b"], fn(i, x) { tuple(i, x) }) +/// [tuple(0, "a"), tuple(1, "b")] +/// +pub fn index_map(list: List(a), with fun: fn(Int, a) -> b) -> List(b) { + do_index_map(list, fun, 0, []) +} + +fn do_try_map( + list: List(a), + fun: fn(a) -> Result(b, e), + acc: List(b), +) -> Result(List(b), e) { + case list { + [] -> Ok(reverse(acc)) + [x, ..xs] -> + case fun(x) { + Ok(y) -> do_try_map(xs, fun, [y, ..acc]) + Error(error) -> Error(error) + } + } +} + +/// Takes a function that returns a Result applies it to each element in a +/// given list in tern. +/// +/// If the function returns `Ok(new_value)` for all elements in the list then a +/// list of the new values is returned. +/// +/// If the function returns `Error(reason)` for any of the elements then it is +/// returned immediately. None of the elements in the list are processed after +/// one returns an `Error`. +/// +/// ## Examples +/// +/// > try_map([1, 2, 3], fn(x) { Ok(x + 2) }) +/// Ok([3, 4, 5]) +/// +/// > try_map([1, 2, 3], fn(x) { Error(0) }) +/// Error(0) +/// +/// > try_map([[1], [2, 3]], head) +/// Ok([1, 2]) +/// +/// > try_map([[1], [], [2]], head) +/// Error(Nil) +/// +pub fn try_map( + over list: List(a), + with fun: fn(a) -> Result(b, e), +) -> Result(List(b), e) { + do_try_map(list, fun, []) +} + +/// Returns a list that is the given list with up to the given number of +/// elements removed from the front of the list. +/// +/// If the element has less than the number of elements an empty list is +/// returned. +/// +/// This function runs in linear time but does not copy the list. +/// +/// ## Examples +/// +/// > drop([1, 2, 3, 4], 2) +/// [3, 4] +/// +/// > drop([1, 2, 3, 4], 9) +/// [] +/// +pub fn drop(from list: List(a), up_to n: Int) -> List(a) { + case n <= 0 { + True -> list + False -> + case list { + [] -> [] + [_, ..xs] -> drop(xs, n - 1) + } + } +} + +fn do_take(list: List(a), n: Int, acc: List(a)) -> List(a) { + case n <= 0 { + True -> reverse(acc) + False -> + case list { + [] -> reverse(acc) + [x, ..xs] -> do_take(xs, n - 1, [x, ..acc]) + } + } +} + +/// Returns a list containing the first given number of elements from the given +/// list. +/// +/// If the element has less than the number of elements then the full list is +/// returned. +/// +/// This function runs in linear time but does not copy the list. +/// +/// ## Examples +/// +/// > take([1, 2, 3, 4], 2) +/// [1, 2] +/// +/// > take([1, 2, 3, 4], 9) +/// [1, 2, 3, 4] +/// +pub fn take(from list: List(a), up_to n: Int) -> List(a) { + do_take(list, n, []) +} + +/// Returns a new empty list. +/// +/// ## Examples +/// +/// > new() +/// [] +/// +pub fn new() -> List(a) { + [] +} + +/// Join one list onto the end of another. +/// +/// This function runs in linear time, and it traverses and copies the first +/// list. +/// +/// ## Examples +/// +/// > append([1, 2], [3]) +/// [1, 2, 3] +/// +pub external fn append(List(a), List(a)) -> List(a) = + "lists" "append" + +fn do_flatten(lists: List(List(a)), acc: List(a)) -> List(a) { + case lists { + [] -> acc + [l, ..rest] -> do_flatten(rest, append(acc, l)) + } +} + +/// Flattens a list of lists into a single list. +/// +/// This function runs in linear time, and it traverses and copies all the +/// inner lists. +/// +/// ## Examples +/// +/// > flatten([[1], [2, 3], []]) +/// [1, 2, 3] +/// +pub fn flatten(lists: List(List(a))) -> List(a) { + do_flatten(lists, []) +} + +/// Reduce a list of elements into a single value by calling a given function +/// on each element, going from left to right. +/// +/// `fold([1, 2, 3], 0, add)` is the equivalent of `add(3, add(2, add(1, 0)))`. +/// +/// This function runs in linear time. +/// +pub fn fold(over list: List(a), from initial: b, with fun: fn(a, b) -> b) -> b { + case list { + [] -> initial + [x, ..rest] -> fold(rest, fun(x, initial), fun) + } +} + +/// Reduce a list of elements into a single value by calling a given function +/// on each element, going from right to left. +/// +/// `fold_right([1, 2, 3], 0, add)` is the equivalent of +/// `add(1, add(2, add(3, 0)))`. +/// +/// This function runs in linear time. +/// +/// Unlike `fold` this function is not tail recursive. Where possible use +/// `fold` instead as it will use less memory. +/// +pub fn fold_right(list: List(a), from initial: b, with fun: fn(a, b) -> b) -> b { + case list { + [] -> initial + [x, ..rest] -> fun(x, fold_right(rest, initial, fun)) + } +} + +fn do_index_fold( + over: List(a), + acc: b, + with: fn(Int, a, b) -> b, + index: Int, +) -> b { + case over { + [] -> acc + [first, ..rest] -> + do_index_fold(rest, with(index, first, acc), with, index + 1) + } +} + +/// Like fold but the folding function also receives the index of the current element. +/// +/// ## Examples +/// +/// ``` +/// ["a", "b", "c"] +/// |> list.index_fold([], fn(index, item, acc) { ... }) +/// ``` +/// +pub fn index_fold( + over over: List(a), + from initial: b, + with fun: fn(Int, a, b) -> b, +) -> b { + do_index_fold(over, initial, fun, 0) +} + +/// A variant of fold that might fail. +/// +/// The folding function should return `Result(accumulator, error) +/// If the returned value is `Ok(accumulator)` try_fold will try the next value in the list. +/// If the returned value is `Error(error)` try_fold will stop and return that error. +/// +/// ## Examples +/// +/// ``` +/// [1, 2, 3, 4] +/// |> try_fold(0, fn(i, acc) { +/// case i < 3 { +/// True -> Ok(acc + i) +/// False -> Error(Nil) +/// } +/// }) +/// ``` +/// +pub fn try_fold( + over collection: List(a), + from accumulator: b, + with fun: fn(a, b) -> Result(b, e), +) -> Result(b, e) { + case collection { + [] -> Ok(accumulator) + [first, ..rest] -> + case fun(first, accumulator) { + Ok(next_accumulator) -> try_fold(rest, next_accumulator, fun) + Error(err) -> Error(err) + } + } +} + +/// Find the first element in a given list for which the given function returns +/// True. +/// +/// Returns `Error(Nil)` if no the function does not return True for any of the +/// elements. +/// +/// ## Examples +/// +/// > find([1, 2, 3], fn(x) { x > 2 }) +/// Ok(3) +/// +/// > find([1, 2, 3], fn(x) { x > 4 }) +/// Error(Nil) +/// +/// > find([], fn(x) { True }) +/// Error(Nil) +/// +pub fn find( + in haystack: List(a), + one_that is_desired: fn(a) -> Bool, +) -> Result(a, Nil) { + case haystack { + [] -> Error(Nil) + [x, ..rest] -> + case is_desired(x) { + True -> Ok(x) + _ -> find(in: rest, one_that: is_desired) + } + } +} + +/// Find the first element in a given list for which the given function returns +/// `Ok(new_value)` and return the new value for that element. +/// +/// Returns `Error(Nil)` if no the function does not return Ok for any of the +/// elements. +/// +/// ## Examples +/// +/// > find_map([[], [2], [3]], head) +/// Ok(2) +/// +/// > find_map([[], []], head) +/// Error(Nil) +/// +/// > find_map([], head) +/// Error(Nil) +/// +pub fn find_map( + in haystack: List(a), + with fun: fn(a) -> Result(b, c), +) -> Result(b, Nil) { + case haystack { + [] -> Error(Nil) + [x, ..rest] -> + case fun(x) { + Ok(x) -> Ok(x) + _ -> find_map(in: rest, with: fun) + } + } +} + +/// Returns True if the given function returns True for all the elements in +/// the given list. If the function returns False for any of the elements it +/// immediately returns False without checking the rest of the list. +/// +/// ## Examples +/// +/// > all([], fn(x) { x > 3 }) +/// True +/// +/// > all([4, 5], fn(x) { x > 3 }) +/// True +/// +/// > all([4, 3], fn(x) { x > 3 }) +/// False +/// +pub fn all(in list: List(a), satisfying predicate: fn(a) -> Bool) -> Bool { + case list { + [] -> True + [x, ..rest] -> + case predicate(x) { + True -> all(rest, predicate) + _ -> False + } + } +} + +/// Returns True if the given function returns True for any the elements in +/// the given list. If the function returns True for any of the elements it +/// immediately returns True without checking the rest of the list. +/// +/// ## Examples +/// +/// > any([], fn(x) { x > 3 }) +/// False +/// +/// > any([4, 5], fn(x) { x > 3 }) +/// False +/// +/// > any([4, 3], fn(x) { x > 3 }) +/// True +/// +/// > any([3, 4], fn(x) { x > 3 }) +/// True +/// +pub fn any(in list: List(a), satisfying predicate: fn(a) -> Bool) -> Bool { + case list { + [] -> False + [x, ..rest] -> + case predicate(x) { + False -> any(rest, predicate) + _ -> True + } + } +} + +/// Takes two lists and returns a single list of 2 item tuples. +/// +/// If one of the lists is longer than the other the remaining elements from +/// the longer list are not used. +/// +/// ## Examples +/// +/// > zip([], []) +/// [] +/// +/// > zip([1, 2], [3]) +/// [tuple(1, 3)] +/// +/// > zip([1], [3, 4]) +/// [tuple(1, 3)] +/// +/// > zip([1, 2], [3, 4]) +/// [tuple(1, 3), tuple(2, 4)] +/// +pub fn zip(xs: List(a), ys: List(b)) -> List(tuple(a, b)) { + case xs, ys { + [], _ -> [] + _, [] -> [] + [x, ..xs], [y, ..ys] -> [tuple(x, y), ..zip(xs, ys)] + } +} + +/// Takes two lists and returns a single list of 2 item tuples. +/// +/// If one of the lists is longer than the other an Error is returned. +/// +/// ## Examples +/// +/// > strict_zip([], []) +/// Ok([]) +/// +/// > strict_zip([1, 2], [3]) +/// Error(LengthMismatch) +/// +/// > strict_zip([1], [3, 4]) +/// Error(LengthMismatch) +/// +/// > strict_zip([1, 2], [3, 4]) +/// Ok([tuple(1, 3), tuple(2, 4)]) +/// +pub fn strict_zip( + l1: List(a), + l2: List(b), +) -> Result(List(tuple(a, b)), LengthMismatch) { + case length(of: l1) == length(of: l2) { + True -> Ok(zip(l1, l2)) + False -> Error(LengthMismatch) + } +} + +fn do_unzip(input, xs, ys) { + case input { + [] -> tuple(reverse(xs), reverse(ys)) + [tuple(x, y), ..rest] -> do_unzip(rest, [x, ..xs], [y, ..ys]) + } +} + +/// Takes a single list of 2 item tuples and returns two lists. +/// +/// ## Examples +/// +/// > unzip([tuple(1, 2), tuple(3, 4)]) +/// tuple([1, 3], [2, 4]) +/// +/// > unzip([]) +/// tuple([], []) +/// +pub fn unzip(input: List(tuple(a, b))) -> tuple(List(a), List(b)) { + do_unzip(input, [], []) +} + +/// Insert a given value between each existing element in a given list. +/// +/// This function runs in linear time and copies the list. +/// +/// ## Examples +/// +/// > intersperse([1, 1, 1], 2) +/// [1, 2, 1, 2, 1] +/// +/// > intersperse([], 2) +/// [] +/// +pub fn intersperse(list: List(a), with elem: a) -> List(a) { + case list { + [] | [_] -> list + [x, ..rest] -> [x, elem, ..intersperse(rest, elem)] + } +} + +/// Return the element in the Nth position in the list, with 0 being the first +/// position. +/// +/// Error(Nil) is returned if the list is not long enough for the given index. +/// +/// ## Examples +/// +/// > at([1, 2, 3], 1) +/// Ok(2) +/// +/// > at([1, 2, 3], 5) +/// Error(Nil) +/// +pub fn at(in list: List(a), get index: Int) -> Result(a, Nil) { + case index < 0 { + True -> Error(Nil) + False -> + case list { + [] -> Error(Nil) + [x, ..rest] -> + case index == 0 { + True -> Ok(x) + False -> at(rest, index - 1) + } + } + } +} + +/// Remove any duplicate elements from a given list. +/// +/// This function returns in log-linear time (n log n). +/// +/// ## Examples +/// +/// > unique([1, 1, 1, 4, 7, 3, 3, 4]) +/// [1, 4, 7, 3] +/// +pub fn unique(list: List(a)) -> List(a) { + case list { + [] -> [] + [x, ..rest] -> [x, ..unique(filter(rest, fn(y) { y != x }))] + } +} + +fn merge_sort(a: List(a), b: List(a), compare: fn(a, a) -> Order) -> List(a) { + case a, b { + [], _ -> b + _, [] -> a + [ax, ..ar], [bx, ..br] -> + case compare(ax, bx) { + order.Lt -> [ax, ..merge_sort(ar, b, compare)] + _ -> [bx, ..merge_sort(a, br, compare)] + } + } +} + +fn do_sort( + list: List(a), + compare: fn(a, a) -> Order, + list_length: Int, +) -> List(a) { + case list_length < 2 { + True -> list + False -> { + let split_length = list_length / 2 + let a_list = take(list, split_length) + let b_list = drop(list, split_length) + merge_sort( + do_sort(a_list, compare, split_length), + do_sort(b_list, compare, list_length - split_length), + compare, + ) + } + } +} + +/// Sort from smallest to largest based upon the ordering specified by a given +/// function. +/// +/// ## Examples +/// +/// > import gleam/int +/// > list.sort([4, 3, 6, 5, 4, 1, 2], by: int.compare) +/// [1, 2, 3, 4, 4, 5, 6] +/// +pub fn sort(list: List(a), by compare: fn(a, a) -> Order) -> List(a) { + do_sort(list, compare, length(list)) +} + +/// Create a list of ints ranging from a given start and finish. +/// +/// ## Examples +/// +/// > range(0, 0) +/// [] +/// +/// > range(0, 5) +/// [0, 1, 2, 3, 4] +/// +/// > range(1, -5) +/// [1, 0, -1, -2, -3, -4] +/// +pub fn range(from start: Int, to stop: Int) -> List(Int) { + case int.compare(start, stop) { + order.Eq -> [] + order.Gt -> [start, ..range(start - 1, stop)] + order.Lt -> [start, ..range(start + 1, stop)] + } +} + +fn do_repeat(a: a, times: Int, acc: List(a)) -> List(a) { + case times <= 0 { + True -> acc + False -> do_repeat(a, times - 1, [a, ..acc]) + } +} + +/// Build a list of a given value a given number of times. +/// +/// ## Examples +/// +/// > repeat("a", times: 0) +/// [] +/// +/// > repeat("a", times: 5) +/// ["a", "a", "a", "a", "a"] +/// +pub fn repeat(item a: a, times times: Int) -> List(a) { + do_repeat(a, times, []) +} + +fn do_split(list: List(a), n: Int, taken: List(a)) -> tuple(List(a), List(a)) { + case n <= 0 { + True -> tuple(reverse(taken), list) + False -> + case list { + [] -> tuple(reverse(taken), []) + [x, ..xs] -> do_split(xs, n - 1, [x, ..taken]) + } + } +} + +/// Split a list in two before the given index. +/// +/// If the list is not long enough to have the given index the before list will +/// be the input list, and the after list will be empty. +/// +/// ## Examples +/// +/// > split([6, 7, 8, 9], 0) +/// tuple([], [6, 7, 8, 9]) +/// +/// > split([6, 7, 8, 9], 2) +/// tuple([6, 7], [8, 9]) +/// +/// > split([6, 7, 8, 9], 4) +/// tuple([6, 7, 8, 9], []) +/// +pub fn split(list list: List(a), at index: Int) -> tuple(List(a), List(a)) { + do_split(list, index, []) +} + +fn do_split_while( + list: List(a), + f: fn(a) -> Bool, + acc: List(a), +) -> tuple(List(a), List(a)) { + case list { + [] -> tuple(reverse(acc), []) + [x, ..xs] -> + case f(x) { + False -> tuple(reverse(acc), list) + _ -> do_split_while(xs, f, [x, ..acc]) + } + } +} + +/// Split a list in two before the first element that a given function returns +/// False for. +/// +/// If the function returns True for all elements the first list will be the +/// input list, and the second list will be empty. +/// +/// ## Examples +/// +/// > split_while([1, 2, 3, 4, 5], fn(x) { x <= 3 }) +/// tuple([1, 2, 3], [4, 5]) +/// +/// > split_while([1, 2, 3, 4, 5], fn(x) { x <= 5 }) +/// tuple([1, 2, 3, 4, 5], []) +/// +pub fn split_while( + list list: List(a), + while predicate: fn(a) -> Bool, +) -> tuple(List(a), List(a)) { + do_split_while(list, predicate, []) +} + +/// Given a list of 2 element tuples, find the first tuple that has a given +/// key as the first element and return the second element. +/// +/// If no tuple is found with the given key then `Error(Nil)` is returned. +/// +/// This function may be useful for interacting with Erlang code where lists of +/// tuples are common. +/// +/// ## Examples +/// +/// > key_find([tuple("a", 0), tuple("b", 1)], "a") +/// Ok(0) +/// +/// > key_find([tuple("a", 0), tuple("b", 1)], "b") +/// Ok(1) +/// +/// > key_find([tuple("a", 0), tuple("b", 1)], "c") +/// Error(Nil) +/// +pub fn key_find( + in keyword_list: List(tuple(k, v)), + find desired_key: k, +) -> Result(v, Nil) { + find_map( + keyword_list, + fn(keyword) { + let tuple(key, value) = keyword + case key == desired_key { + True -> Ok(value) + False -> Error(Nil) + } + }, + ) +} + +fn do_pop(haystack, predicate, checked) { + case haystack { + [] -> Error(Nil) + [x, ..rest] -> + case predicate(x) { + True -> Ok(tuple(x, append(reverse(checked), rest))) + False -> do_pop(rest, predicate, [x, ..checked]) + } + } +} + +/// Remove the first element in a given list for which the predicate funtion returns `True`. +/// +/// Returns `Error(Nil)` if no the function does not return True for any of the +/// elements. +/// +/// ## Examples +/// +/// > pop([1, 2, 3], fn(x) { x > 2 }) +/// Ok(tuple(3, [1, 2])) +/// +/// > pop([1, 2, 3], fn(x) { x > 4 }) +/// Error(Nil) +/// +/// > pop([], fn(x) { True }) +/// Error(Nil) +/// +pub fn pop( + in haystack: List(a), + one_that is_desired: fn(a) -> Bool, +) -> Result(tuple(a, List(a)), Nil) { + do_pop(haystack, is_desired, []) +} + +fn do_pop_map(haystack, mapper, checked) { + case haystack { + [] -> Error(Nil) + [x, ..rest] -> + case mapper(x) { + Ok(y) -> Ok(tuple(y, append(reverse(checked), rest))) + Error(_) -> do_pop_map(rest, mapper, [x, ..checked]) + } + } +} + +/// Removes the first element in a given list for which the given function returns +/// `Ok(new_value)` and return the new value as well as list with the value removed. +/// +/// Returns `Error(Nil)` if no the function does not return Ok for any of the +/// elements. +/// +/// ## Examples +/// +/// > pop_map([[], [2], [3]], head) +/// Ok(tuple(2, [[], [3]])) +/// +/// > pop_map([[], []], head) +/// Error(Nil) +/// +/// > pop_map([], head) +/// Error(Nil) +/// +pub fn pop_map( + in haystack: List(a), + one_that is_desired: fn(a) -> Result(b, c), +) -> Result(tuple(b, List(a)), Nil) { + do_pop_map(haystack, is_desired, []) +} + +/// Given a list of 2 element tuples, find the first tuple that has a given +/// key as the first element. This function will return the second element +/// of the found tuple and list with tuple removed. +/// +/// If no tuple is found with the given key then `Error(Nil)` is returned. +/// +/// ## Examples +/// +/// > key_pop([tuple("a", 0), tuple("b", 1)], "a") +/// Ok(tuple(0, [tuple("b", 1)]) +/// +/// > key_pop([tuple("a", 0), tuple("b", 1)], "b") +/// Ok(tuple(1, [tuple("a", 0)]) +/// +/// > key_pop([tuple("a", 0), tuple("b", 1)], "c") +/// Error(Nil) +/// +pub fn key_pop( + haystack: List(tuple(k, v)), + key: k, +) -> Result(tuple(v, List(tuple(k, v))), Nil) { + pop_map( + haystack, + fn(entry) { + let tuple(k, v) = entry + case k { + k if k == key -> Ok(v) + _ -> Error(Nil) + } + }, + ) +} + +/// Given a list of 2 element tuples, insert a key and value into the list. +/// +/// If there was already a tuple with the key then it is replaced, otherwise it +/// is added to the end of the list. +/// +/// +/// ## Examples +/// +/// > key_set([tuple(5, 0), tuple(4, 1)], 4, 100) +/// [tuple(5, 0), tuple(4, 100)] +/// +/// > key_set([tuple(5, 0), tuple(4, 1)], 1, 100) +/// [tuple(5, 0), tuple(4, 1), tuple(1, 100)] +/// +pub fn key_set(list: List(tuple(a, b)), key: a, value: b) -> List(tuple(a, b)) { + case list { + [] -> [tuple(key, value)] + [tuple(k, _), ..rest] if k == key -> [tuple(key, value), ..rest] + [first, ..rest] -> [first, ..key_set(rest, key, value)] + } +} + +/// Call a function for each element in a list, discarding the results. +/// +pub fn each(list: List(a), f: fn(a) -> b) -> Nil { + case list { + [] -> Nil + [x, ..xs] -> { + f(x) + each(xs, f) + } + } +} + +fn do_partition(list, categorise, trues, falses) { + case list { + [] -> tuple(reverse(trues), reverse(falses)) + [x, ..xs] -> + case categorise(x) { + True -> do_partition(xs, categorise, [x, ..trues], falses) + False -> do_partition(xs, categorise, trues, [x, ..falses]) + } + } +} + +pub fn partition( + list: List(a), + with categorise: fn(a) -> Bool, +) -> tuple(List(a), List(a)) { + do_partition(list, categorise, [], []) +} + +/// Return all the permutations of a list +/// All values must be unique +/// +/// ## Examples +/// +/// > permutations([1, 2]) +/// [[1, 2], [2, 1]] +/// +pub fn permutations(l: List(a)) -> List(List(a)) { + case l { + [] -> [[]] + _ -> + map( + l, + fn(x) { + filter(l, fn(y) { y != x }) + |> permutations + |> map(append([x], _)) + }, + ) + |> flatten + } +} diff --git a/gleam_providers/deps/gleam_stdlib/src/gleam/map.gleam b/gleam_providers/deps/gleam_stdlib/src/gleam/map.gleam new file mode 100644 index 0000000..488a315 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/src/gleam/map.gleam @@ -0,0 +1,325 @@ +import gleam/result +import gleam/list + +/// A dictionary of keys and values. +/// +/// Any type can be used for the keys and values of a map, but all the keys +/// must be of the same type and all the values must be of the same type. +/// +/// Each key can only be present in a map once. +/// +/// Maps are not ordered in any way, and any unintentional ordering is not to +/// be relied upon in your code as it may change in future versions of Erlang +/// or Gleam. +/// +/// See [the Erlang map module](https://erlang.org/doc/man/maps.html) for more +/// information. +/// +pub external type Map(key, value) + +/// Determine the number of key-value pairs in the map. +/// This function runs in constant time and does not need to iterate the map. +/// +/// ## Examples +/// +/// > new() |> size() +/// 0 +/// +/// > new() |> insert("key", "value") |> size() +/// 1 +/// +/// +pub external fn size(Map(k, v)) -> Int = + "maps" "size" + +/// Convert the map to a list of 2-element tuples `tuple(key, value)`, one for +/// each key-value pair in the map. +/// +/// The tuples in the list have no specific order. +/// +/// ## Examples +/// +/// > new() |> to_list() +/// [] +/// +/// > new() |> insert("key", 0) |> to_list() +/// [tuple("key", 0)] +/// +pub external fn to_list(Map(key, value)) -> List(tuple(key, value)) = + "maps" "to_list" + +/// Convert a list of 2-element tuples `tuple(key, value)` to a map. +/// +/// If two tuples have the same key the last one in the list will be the one +/// that is present in the map. +/// +pub external fn from_list(List(tuple(key, value))) -> Map(key, value) = + "maps" "from_list" + +external fn is_key(key, Map(key, v)) -> Bool = + "maps" "is_key" + +/// Determind whether or not a value present in the map for a given key. +/// +/// ## Examples +/// +/// > new() |> insert("a", 0) |> has_key("a") +/// True +/// +/// > new() |> insert("a", 0) |> has_key("b") +/// False +/// +pub fn has_key(map: Map(k, v), key: k) -> Bool { + is_key(key, map) +} + +/// Create a fresh map that contains no values. +/// +pub external fn new() -> Map(key, value) = + "maps" "new" + +/// Fetch a value from a map for a given key. +/// +/// The map may not have a value for the key, so the value is wrapped in a +/// Result. +/// +/// ## Examples +/// +/// > new() |> insert("a", 0) |> get("a") +/// Ok(0) +/// +/// > new() |> insert("a", 0) |> get("b") +/// Error(Nil) +/// +pub external fn get(from: Map(key, value), get: key) -> Result(value, Nil) = + "gleam_stdlib" "map_get" + +external fn erl_insert(key, value, Map(key, value)) -> Map(key, value) = + "maps" "put" + +/// Insert a value into the map with the given key. +/// +/// If the map already has a value for the given key then the value is +/// replaced with the new value. +/// +/// ## Examples +/// +/// > new() |> insert("a", 0) |> to_list +/// [tuple("a", 0)] +/// +/// > new() |> insert("a", 0) |> insert("a", 5) |> to_list +/// [tuple("a", 5)] +/// +pub fn insert(into map: Map(k, v), for key: k, insert value: v) -> Map(k, v) { + erl_insert(key, value, map) +} + +external fn erl_map_values(fn(key, a) -> b, Map(key, value)) -> Map(key, b) = + "maps" "map" + +/// Update all values in a given map by calling a given function on each key +/// and value. +/// +/// ## Examples +/// +/// > [tuple(3, 3), tuple(2, 4)] +/// > |> from_list +/// > |> map_values(fn(key, value) { key * value }) +/// [tuple(3, 9), tuple(2, 8)] +/// +/// +pub fn map_values(in map: Map(k, v), with fun: fn(k, v) -> w) -> Map(k, w) { + erl_map_values(fun, map) +} + +/// Get a list of all keys in a given map. +/// +/// Maps are not ordered so the keys are not returned in any specific order. Do +/// not write code that relies on the order keys are returned by this function +/// as it may change in later versions of Gleam or Erlang. +/// +/// ## Examples +/// +/// > keys([tuple("a", 0), tuple("b", 1)]) +/// ["a", "b"] +/// +pub external fn keys(Map(keys, v)) -> List(keys) = + "maps" "keys" + +/// Get a list of all values in a given map. +/// +/// Maps are not ordered so the values are not returned in any specific order. Do +/// not write code that relies on the order values are returned by this function +/// as it may change in later versions of Gleam or Erlang. +/// +/// ## Examples +/// +/// > keys(from_list([tuple("a", 0), tuple("b", 1)])) +/// [0, 1] +/// +pub external fn values(Map(k, values)) -> List(values) = + "maps" "values" + +external fn erl_filter( + fn(key, value) -> Bool, + Map(key, value), +) -> Map(key, value) = + "maps" "filter" + +/// Create a new map from a given map, minus any entries that a given function +/// returns False for. +/// +/// ## Examples +/// +/// > from_list([tuple("a", 0), tuple("b", 1)]) +/// > |> filter(fn(key, value) { value != 0 }) +/// from_list([tuple("b", 1)]) +/// +/// > from_list([tuple("a", 0), tuple("b", 1)]) +/// > |> filter(fn(key, value) { True }) +/// from_list([tuple("a", 0), tuple("b", 1)]) +/// +pub fn filter(in map: Map(k, v), for property: fn(k, v) -> Bool) -> Map(k, v) { + erl_filter(property, map) +} + +external fn erl_take(List(k), Map(k, v)) -> Map(k, v) = + "maps" "with" + +/// Create a new map from a given map, only including any entries for which the +/// keys are in a given list. +/// +/// ## Examples +/// +/// > from_list([tuple("a", 0), tuple("b", 1)]) +/// > |> take(["b"]) +/// from_list([tuple("b", 1)]) +/// +/// > from_list([tuple("a", 0), tuple("b", 1)]) +/// > |> take(["a", "b", "c"]) +/// from_list([tuple("a", 0), tuple("b", 1)]) +/// +pub fn take(from map: Map(k, v), keeping desired_keys: List(k)) -> Map(k, v) { + erl_take(desired_keys, map) +} + +/// Create a new map from a pair of given maps by combining their entries. +/// +/// If there are entries with the same keys in both maps the entry from the +/// second map takes precedence. +/// +/// ## Examples +/// +/// > let a = from_list([tuple("a", 0), tuple("b", 1)]) +/// > let b = from_list([tuple("b", 2), tuple("c", 3)]) +/// > merge(a, b) +/// from_list([tuple("a", 0), tuple("b", 2), tuple("c", 3)]) +/// +pub external fn merge(into: Map(k, v), merge: Map(k, v)) -> Map(k, v) = + "maps" "merge" + +external fn erl_delete(k, Map(k, v)) -> Map(k, v) = + "maps" "remove" + +/// Create a new map from a given map with all the same entries except for the +/// one with a given key, if it exists. +/// +/// ## Examples +/// +/// > delete([tuple("a", 0), tuple("b", 1)], "a") +/// from_list([tuple("b", 1)]) +/// +/// > delete([tuple("a", 0), tuple("b", 1)], "c") +/// from_list([tuple("a", 0), tuple("b", 1)]) +/// +pub fn delete(from map: Map(k, v), delete key: k) -> Map(k, v) { + erl_delete(key, map) +} + +/// Create a new map from a given map with all the same entries except any with +/// keys found in a given list. +/// +/// ## Examples +/// +/// > drop([tuple("a", 0), tuple("b", 1)], ["a"]) +/// from_list([tuple("b", 2)]) +/// +/// > delete([tuple("a", 0), tuple("b", 1)], ["c"]) +/// from_list([tuple("a", 0), tuple("b", 1)]) +/// +/// > drop([tuple("a", 0), tuple("b", 1)], ["a", "b", "c"]) +/// from_list([]) +/// +pub fn drop(from map: Map(k, v), drop disallowed_keys: List(k)) -> Map(k, v) { + list.fold(disallowed_keys, map, fn(key, acc) { delete(acc, key) }) +} + +/// Create a new map with one entry updated using a given function. +/// +/// If there was not an entry in the map for the given key then the function +/// gets `Error(Nil)` as its argument, otherwise it gets `Ok(value)`. +/// +/// ## Example +/// +/// > let increment = fn(x) { +/// > case x { +/// > Ok(i) -> i + 1 +/// > Error(Nil) -> 0 +/// > } +/// > } +/// > let map = from_list([tuple("a", 0)]) +/// > +/// > update(map, "a" increment) +/// from_list([tuple("a", 1)]) +/// +/// > update(map, "b" increment) +/// from_list([tuple("a", 0), tuple("b", 0)]) +/// +pub fn update( + in map: Map(k, v), + update key: k, + with fun: fn(Result(v, Nil)) -> v, +) -> Map(k, v) { + map + |> get(key) + |> fun + |> insert(map, key, _) +} + +fn do_fold( + list: List(tuple(k, v)), + initial: acc, + fun: fn(k, v, acc) -> acc, +) -> acc { + case list { + [] -> initial + [tuple(k, v), ..tail] -> do_fold(tail, fun(k, v, initial), fun) + } +} + +/// Combine all entries into a single value by calling a given function on each +/// one. +/// +/// Maps are not ordered so the values are not returned in any specific order. Do +/// not write code that relies on the order entries are used by this function +/// as it may change in later versions of Gleam or Erlang. +/// +/// # Examples +/// +/// > let map = from_list([tuple("a", 1), tuple("b", 3), tuple("c", 9)]) +/// > fold(map, 0, fn(key, value, accumulator) { accumulator + value }) +/// 13 +/// +/// > import gleam/string.{append} +/// > fold(map, "", fn(key, value, accumulator) { append(accumulator, value) }) +/// "abc" +/// +pub fn fold( + over map: Map(k, v), + from initial: acc, + with fun: fn(k, v, acc) -> acc, +) -> acc { + map + |> to_list + |> do_fold(initial, fun) +} diff --git a/gleam_providers/deps/gleam_stdlib/src/gleam/option.gleam b/gleam_providers/deps/gleam_stdlib/src/gleam/option.gleam new file mode 100644 index 0000000..b10ee1a --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/src/gleam/option.gleam @@ -0,0 +1,182 @@ +/// Option represents a value that may be present or not. Some means the value is +/// present, None means the value is not. +/// +/// This is Gleam's alternative to having a value that could be Null, as is +/// possible in some other languages. +/// +pub type Option(a) { + Some(a) + None +} + +/// Check whether the option is a Some value. +/// +/// ## Examples +/// +/// > is_some(Some(1)) +/// True +/// +/// > is_some(None) +/// False +/// +pub fn is_some(option: Option(a)) -> Bool { + option != None +} + +/// Check whether the option is a None value. +/// +/// ## Examples +/// +/// > is_none(Some(1)) +/// False +/// +/// > is_none(None) +/// True +/// +pub fn is_none(option: Option(a)) -> Bool { + option == None +} + +/// Converts an Option type to a Result type +/// +/// ## Examples +/// +/// > to_result(Some(1), "some_error") +/// Ok(1) +/// > to_result(None, "some_error") +/// Error("some_error") +/// +pub fn to_result(option: Option(a), e) -> Result(a, e) { + case option { + Some(a) -> Ok(a) + _ -> Error(e) + } +} + +/// Converts a Result type to an Option type +/// +/// ## Examples +/// +/// > from_result(Ok(1)) +/// Some(1) +/// > from_result(Error"some_error")) +/// None +/// +pub fn from_result(result: Result(a, e)) -> Option(a) { + case result { + Ok(a) -> Some(a) + _ -> None + } +} + +/// Extract the value from an option, returning a default value if there is none. +/// +/// ## Examples +/// +/// > unwrap(Some(1), 0) +/// 1 +/// +/// > unwrap(None, 0) +/// 0 +/// +pub fn unwrap(option: Option(a), or default: a) -> a { + case option { + Some(x) -> x + None -> default + } +} + +/// Update a value held within the Some of an Option by calling a given function +/// on it. +/// +/// If the option is a None rather than Some the function is not called and the +/// option stays the same. +/// +/// ## Examples +/// +/// > map(over: Some(1), with: fn(x) { x + 1 }) +/// Some(2) +/// +/// > map(over: None, with: fn(x) { x + 1 }) +/// None +/// +pub fn map(over option: Option(a), with fun: fn(a) -> b) -> Option(b) { + case option { + Some(x) -> Some(fun(x)) + None -> None + } +} + +/// Merge a nested Option into a single layer. +/// +/// ## Examples +/// +/// > flatten(Some(Some(1))) +/// Some(1) +/// +/// > flatten(Some(None)) +/// None +/// +/// > flatten(None) +/// None +/// +pub fn flatten(option: Option(Option(a))) -> Option(a) { + case option { + Some(x) -> x + None -> None + } +} + +/// Update a value held within the Some of an Option by calling a given function +/// on it, where the given function also returns an Option. The two Options are +/// then merged together into one Option. +/// +/// If the Option is a None rather than Some the function is not called and the +/// Option stays the same. +/// +/// This function is the equivalent of calling `map` followed by `flatten`, and +/// it is useful for chaining together multiple functions that return Options. +/// +/// ## Examples +/// +/// > then(Some(1), fn(x) { Some(x + 1) }) +/// Some(2) +/// +/// > then(Some(1), fn(x) { Some(tuple("a", x)) }) +/// Some(tuple("a", 1)) +/// +/// > then(Some(1), fn(x) { None }) +/// None +/// +/// > then(None, fn(x) { Some(x + 1) }) +/// None +/// +pub fn then(option: Option(a), apply fun: fn(a) -> Option(b)) -> Option(b) { + case option { + Some(x) -> fun(x) + None -> None + } +} + +/// Return the first value if it is Some, otherwise return the second value. +/// +/// ## Examples +/// +/// > or(Some(1), Some(2)) +/// Some(1) +/// +/// > or(Some(1), None) +/// Some(1) +/// +/// > or(None, Some(2)) +/// Some(2) +/// +/// > or(None, None) +/// None +/// +pub fn or(first: Option(a), second: Option(a)) -> Option(a) { + case first { + Some(_) -> first + None -> second + } +} diff --git a/gleam_providers/deps/gleam_stdlib/src/gleam/order.gleam b/gleam_providers/deps/gleam_stdlib/src/gleam/order.gleam new file mode 100644 index 0000000..c030db4 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/src/gleam/order.gleam @@ -0,0 +1,101 @@ +/// Represents the result of a single comparison to determine the precise +/// ordering of two values. +/// +pub type Order { + /// Less-than + Lt + + /// Equal + Eq + + /// Greater than + Gt +} + +/// Inverts an order, so less-than becomes greater-than and greater-than +/// becomes less-than. +/// +/// ## Examples +/// +/// > reverse(Lt) +/// Gt +/// +/// > reverse(Eq) +/// Eq +/// +/// > reverse(Lt) +/// Gt +/// +pub fn reverse(order: Order) -> Order { + case order { + Lt -> Gt + Eq -> Eq + Gt -> Lt + } +} + +/// Produces a numeric representation of the order. +/// +/// ## Examples +/// +/// > to_int(Lt) +/// -1 +/// +/// > to_int(Eq) +/// 0 +/// +/// > to_int(Gt) +/// 1 +/// +pub fn to_int(order: Order) -> Int { + case order { + Lt -> -1 + Eq -> 0 + Gt -> 1 + } +} + +/// Compares two Order values to one another, producing a new Order. +/// +/// ## Examples +/// +/// > compare(Eq, with: Lt) +/// Gt +/// +pub fn compare(a: Order, with b: Order) -> Order { + case a, b { + x, y if x == y -> Eq + Lt, _ | Eq, Gt -> Lt + _, _ -> Gt + } +} + +/// Returns the largest of two orders. +/// +/// ## Examples +/// +/// > max(Eq, Lt) +/// Eq +/// +pub fn max(a: Order, b: Order) -> Order { + case a, b { + Gt, _ -> Gt + Eq, Lt -> Eq + _, _ -> b + } +} + +/// Returns the smallest of two orders. +/// +/// ## Examples +/// +/// > min(Eq, Lt) +/// Lt +/// +pub fn min(a: Order, b: Order) -> Order { + case a, b { + Lt, _ -> Lt + Eq, Gt -> Eq + _, _ -> b + } +} diff --git a/gleam_providers/deps/gleam_stdlib/src/gleam/os.gleam b/gleam_providers/deps/gleam_stdlib/src/gleam/os.gleam new file mode 100644 index 0000000..0623dd5 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/src/gleam/os.gleam @@ -0,0 +1,66 @@ +//// Function to interact with the host operating system. + +import gleam/list +import gleam/map.{Map} +import gleam/string + +// Internal type for erlang interop. +external type CharList + +external fn os_getenv() -> List(CharList) = + "os" "getenv" + +external fn os_putenv(key: CharList, value: CharList) -> Bool = + "os" "putenv" + +external fn os_unsetenv(key: CharList) -> Bool = + "os" "unsetenv" + +external fn char_list_to_string(CharList) -> String = + "erlang" "list_to_binary" + +external fn string_to_char_list(String) -> CharList = + "erlang" "binary_to_list" + +/// Return all environment variables set on the system. +pub fn get_env() -> Map(String, String) { + list.map( + os_getenv(), + fn(char_list) { + assert Ok(value) = string.split_once(char_list_to_string(char_list), "=") + value + }, + ) + |> map.from_list() +} + +/// Set an environment variable. +pub fn insert_env(key: String, value: String) -> Nil { + os_putenv(string_to_char_list(key), string_to_char_list(value)) + Nil +} + +/// Delete an environment variable. +pub fn delete_env(key: String) -> Nil { + os_unsetenv(string_to_char_list(key)) + Nil +} + +pub type TimeUnit { + Second + Millisecond + Microsecond + Nanosecond +} + +/// Return the current OS system time. +/// +/// https://erlang.org/doc/apps/erts/time_correction.html#OS_System_Time +pub external fn system_time(TimeUnit) -> Int = + "os" "system_time" + +/// Return the current OS system time as a tuple of Ints +/// +/// http://erlang.org/doc/man/os.html#timestamp-0 +pub external fn erlang_timestamp() -> tuple(Int, Int, Int) = + "os" "timestamp" diff --git a/gleam_providers/deps/gleam_stdlib/src/gleam/pair.gleam b/gleam_providers/deps/gleam_stdlib/src/gleam/pair.gleam new file mode 100644 index 0000000..f0de5a9 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/src/gleam/pair.gleam @@ -0,0 +1,61 @@ +/// Returns the first element in a pair. +/// +/// ## Examples +/// +/// > first(tuple(1, 2)) +/// 1 +/// +pub fn first(pair: tuple(a, b)) -> a { + let tuple(a, _) = pair + a +} + +/// Returns the second element in a pair. +/// +/// ## Examples +/// +/// > second(tuple(1, 2)) +/// 2 +/// +pub fn second(pair: tuple(a, b)) -> b { + let tuple(_, a) = pair + a +} + +/// Returns a new pair with the elements swapped. +/// +/// ## Examples +/// +/// > swap(tuple(1, 2)) +/// tuple(2, 1) +/// +pub fn swap(pair: tuple(a, b)) -> tuple(b, a) { + let tuple(a, b) = pair + tuple(b, a) +} + +/// Returns a new pair with the first element having had `with` applied to +/// it. +/// +/// ## Examples +/// +/// > tuple(1, 2) |> map_first(fn(n) { n * 2 }) +/// 2 +/// +pub fn map_first(of pair: tuple(a, b), with fun: fn(a) -> c) -> tuple(c, b) { + let tuple(a, b) = pair + tuple(fun(a), b) +} + +/// Returns a new pair with the second element having had `with` applied to +/// it. +/// +/// ## Examples +/// +/// > tuple(1, 2) |> map_second(fn(n) { n * 2 }) +/// 4 +/// +pub fn map_second(of pair: tuple(a, b), with fun: fn(b) -> c) -> tuple(a, c) { + let tuple(a, b) = pair + tuple(a, fun(b)) +} diff --git a/gleam_providers/deps/gleam_stdlib/src/gleam/queue.gleam b/gleam_providers/deps/gleam_stdlib/src/gleam/queue.gleam new file mode 100644 index 0000000..27c5f9d --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/src/gleam/queue.gleam @@ -0,0 +1,254 @@ +import gleam/list + +/// A queue is an order collection of elements. It is similar to a list, but +/// unlike a list elements can be added to or removed from either the front or +/// the back in a performant fashion. +/// +/// The internal representation may be different for two queues with the same +/// elements in the same order if the queues were constructed in different +/// ways. This is the price paid for a queue's fast access at both the front +/// and the back. +/// +/// Because of unpredictable internal representation the equality operator `==` +/// may return surprising results, and the `is_equal` and `is_logically_equal` +/// functions are the recommended way to test queues for equality. +/// +pub opaque type Queue(element) { + Queue(in: List(element), out: List(element)) +} + +/// Create a fresh queue that contains no values. +/// +pub fn new() -> Queue(a) { + Queue(in: [], out: []) +} + +/// Convert a list of elements into a queue of the same elements in the same +/// order. The head element in the list becomes the front element in the queue. +/// +/// This function runs in constant time. +/// +/// # Examples +/// +/// > [1, 2, 3] |> from_list |> length +/// 3 +/// +pub fn from_list(list: List(a)) -> Queue(a) { + Queue(in: [], out: list) +} + +/// Convert a queue of elements into a list of the same elements in the same +/// order. The front element in the queue becomes the head element in the list. +/// +/// This function runs in linear time. +/// +/// # Examples +/// +/// > new() |> push_back(1) |> push_back(2) |> to_list +/// [1, 2] +/// +pub fn to_list(queue: Queue(a)) -> List(a) { + queue.out + |> list.append(list.reverse(queue.in)) +} + +/// Determine whether or not the queue is empty. +/// +/// This function runs in constant time. +/// +/// ## Examples +/// +/// > [] |> from_list |> is_empty +/// True +/// +/// > [1] |> from_list |> is_empty +/// False +/// +/// > [1, 2] |> from_list |> is_empty +/// False +/// +pub fn is_empty(queue: Queue(a)) -> Bool { + queue.in == [] && queue.out == [] +} + +/// Count the number of elements in a given queue. +/// +/// This function has to traverse the queue to determine the number of elements, +/// so it runs in linear time. +/// +/// ## Examples +/// +/// > length(from_list([])) +/// 0 +/// +/// > length(from_list([1])) +/// 1 +/// +/// > length(from_list([1, 2])) +/// 2 +/// +pub fn length(queue: Queue(a)) -> Int { + list.length(queue.in) + list.length(queue.out) +} + +/// Push an element onto the back of the queue. +/// +/// # Examples +/// +/// > [1, 2] |> from_list |> push_back(3) |> to_list +/// [1, 2, 3] +/// +pub fn push_back(onto queue: Queue(a), this item: a) -> Queue(a) { + Queue(in: [item, ..queue.in], out: queue.out) +} + +/// Push an element onto the front of the queue. +/// +/// # Examples +/// +/// > [0, 0] |> from_list |> push_front(1) |> to_list +/// [1, 0, 0] +/// +pub fn push_front(onto queue: Queue(a), this item: a) -> Queue(a) { + Queue(in: queue.in, out: [item, ..queue.out]) +} + +/// Get the last element from the queue, returning the +/// element and a new queue without that element. +/// +/// This function typically runs in constant time, but will occasionally run in +/// linear time. +/// +/// # Examples +/// +/// > queue.new() +/// > |> queue.push_back(0) +/// > |> queue.push_back(1) +/// > |> queue.pop_back() +/// Ok(tuple(1, queue.push_front(queue.new(), 0))) +/// +/// > queue.new() +/// > |> queue.push_front(0) +/// > |> queue.pop_back() +/// Ok(tuple(0, queue.new())) +/// +/// > queue.new() +/// > |> queue.pop_back() +/// Error(Nil) +/// +pub fn pop_back(from queue: Queue(a)) -> Result(tuple(a, Queue(a)), Nil) { + case queue { + Queue(in: [], out: []) -> Error(Nil) + Queue(in: [], out: out) -> pop_back(Queue(in: list.reverse(out), out: [])) + Queue(in: [first, ..rest], out: out) -> { + let queue = Queue(in: rest, out: out) + Ok(tuple(first, queue)) + } + } +} + +/// Get the first element from the queue, returning the +/// element and a new queue without that element. +/// +/// This function typically runs in constant time, but will occasionally run in +/// linear time. +/// +/// # Examples +/// +/// > queue.new() +/// > |> queue.push_front(1) +/// > |> queue.push_front(0) +/// > |> queue.pop_front() +/// Ok(tuple(0, queue.push_back(queue.new(), 1))) +/// +/// > queue.new() +/// > |> queue.push_back(0) +/// > |> queue.pop_front() +/// Ok(tuple(0, queue.new())) +/// +/// > queue.new() +/// > |> queue.pop_back() +/// Error(Nil) +/// +pub fn pop_front(from queue: Queue(a)) -> Result(tuple(a, Queue(a)), Nil) { + case queue { + Queue(in: [], out: []) -> Error(Nil) + Queue(in: in, out: []) -> pop_front(Queue(in: [], out: list.reverse(in))) + Queue(in: in, out: [first, ..rest]) -> { + let queue = Queue(in: in, out: rest) + Ok(tuple(first, queue)) + } + } +} + +/// Create a new queue from a given queue containing the same elements, but in +/// the opposite order. +/// +/// This function runs in constant time. +/// +/// ## Examples +/// +/// > reverse(from_list([])) +/// [] +/// +/// > reverse(from_list([1])) +/// [1] +/// +/// > reverse(from_list([1, 2])) +/// [2, 1] +/// +pub fn reverse(queue: Queue(a)) -> Queue(a) { + Queue(in: queue.out, out: queue.in) +} + +fn check_equal( + xs: List(t), + x_tail: List(t), + ys: List(t), + y_tail: List(t), + eq: fn(t, t) -> Bool, +) -> Bool { + case xs, x_tail, ys, y_tail { + [], [], [], [] -> True + [x, ..xs], _, [y, ..ys], _ -> + case eq(x, y) { + False -> False + True -> check_equal(xs, x_tail, ys, y_tail, eq) + } + [], [_, .._], _, _ -> check_equal(list.reverse(x_tail), [], ys, y_tail, eq) + _, _, [], [_, .._] -> check_equal(xs, x_tail, list.reverse(y_tail), [], eq) + _, _, _, _ -> False + } +} + +/// Check whether two queues have equal elements in the same order, where the +/// equality of elements is determined by a given equality checking function. +/// +/// This function is useful as the internal representation may be different for +/// two queues with the same elements in the same order depending on how they +/// were constructed, so the equality operator `==` may return surprising +/// results. +/// +/// This function runs in linear time multiplied by the time taken by the +/// element equality checking function. +/// +pub fn is_logically_equal( + a: Queue(t), + to b: Queue(t), + checking element_is_equal: fn(t, t) -> Bool, +) -> Bool { + check_equal(a.out, a.in, b.out, b.in, element_is_equal) +} + +/// Check whether two queues have the same elements in the same order. +/// +/// This function is useful as the internal representation may be different for +/// two queues with the same elements in the same order depending on how they +/// were constructed, so the equality operator `==` may return surprising +/// results. +/// +/// This function runs in linear time. +/// +pub fn is_equal(a: Queue(t), to b: Queue(t)) -> Bool { + check_equal(a.out, a.in, b.out, b.in, fn(a, b) { a == b }) +} diff --git a/gleam_providers/deps/gleam_stdlib/src/gleam/regex.gleam b/gleam_providers/deps/gleam_stdlib/src/gleam/regex.gleam new file mode 100644 index 0000000..4317cd5 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/src/gleam/regex.gleam @@ -0,0 +1,123 @@ +//// This module contains regular expression matching functions for strings. +//// The matching algorithms of the library are based on the PCRE library, but not +//// all of the PCRE library is interfaced and some parts of the library go beyond +//// what PCRE offers. Currently PCRE version 8.40 (release date 2017-01-11) is used. + +import gleam/option.{Option} + +pub external type Regex + +/// The details about a particular match: +/// +pub type Match { + Match( + /// The full string of the match. + content: String, + /// The byte index of the match in the original string. + byte_index: Int, + /// A Regex can have subpatterns, sup-parts that are in parentheses. + submatches: List(Option(String)), + ) +} + +/// When a regular expression fails to compile: +/// +pub type CompileError { + CompileError( + /// The problem encountered that caused the compilation to fail + error: String, + /// The byte index into the string to where the problem was found + byte_index: Int, + ) +} + +pub type Options { + Options(case_insensitive: Bool, multi_line: Bool) +} + +/// Create a Regex with some additional options. +/// +/// ## Examples +/// +/// > let options = Options(case_insensitive: False, multi_line: True) +/// > assert Ok(re) = compile("^[0-9]", with: options) +/// > match(re, "abc\n123") +/// True +/// +/// > let options = Options(case_insensitive: True, multi_line: False) +/// > assert Ok(re) = compile("[A-Z]", with: options) +/// > match(re, "abc123") +/// True +/// +pub external fn compile(String, with: Options) -> Result(Regex, CompileError) = + "gleam_stdlib" "compile_regex" + +/// Create a new Regex. +/// +/// ## Examples +/// +/// > assert Ok(re) = from_string("[0-9]") +/// > match(re, "abc123") +/// True +/// +/// > match(re, "abcxyz") +/// False +/// +/// > from_string("[0-9") +/// Error( +/// CompileError( +/// error: "missing terminating ] for character class", +/// byte_index: 4 +/// ) +/// ) +/// +pub fn from_string(pattern: String) -> Result(Regex, CompileError) { + compile(pattern, Options(case_insensitive: False, multi_line: False)) +} + +/// Returns a boolean indicating whether there was a match or not. +/// +/// ## Examples +/// +/// > assert Ok(re) = from_string("^f.o.?") +/// > check(with: re, content: "foo") +/// True +/// +/// > check(with: re, content: "boo") +/// False +/// +pub external fn check(with: Regex, content: String) -> Bool = + "gleam_stdlib" "regex_match" + +/// Split a string +/// +/// ## Examples +/// +/// > assert Ok(re) = from_string(" *, *") +/// > split(with: re, content: "foo,32, 4, 9 ,0") +/// ["foo", "32", "4", "9", "0"] +/// +pub external fn split(with: Regex, content: String) -> List(String) = + "gleam_stdlib" "regex_split" + +/// Collects all matches of the regular expression. +/// +/// ## Examples +/// +/// > assert Ok(re) = regex.from_string("[oi]n a (\\w+)") +/// > regex.scan(with: re, content: "I am on a boat in a lake.") +/// [ +/// Match( +/// content: "on a boat", +/// byte_index: 5, +/// submatches: [Some("boat")] +/// ), +/// Match( +/// content: "in a lake", +/// byte_index: 15, +/// submatches: [Some("lake")] +/// ) +/// ] +/// +pub external fn scan(with: Regex, content: String) -> List(Match) = + "gleam_stdlib" "regex_scan" diff --git a/gleam_providers/deps/gleam_stdlib/src/gleam/result.gleam b/gleam_providers/deps/gleam_stdlib/src/gleam/result.gleam new file mode 100644 index 0000000..626548f --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/src/gleam/result.gleam @@ -0,0 +1,266 @@ +import gleam/list + +/// Result represents the result of something that may succeed or not. +/// `Ok` means it was successful, `Error` means it was not successful. +/// +pub type Result(success, error) = + Result(success, error) + +/// Nil is a type used to represent the absence of something, similar to null +/// or undefined in other languages. +/// +/// Unlike some other languages values cannot be implicitly nil. +/// +pub type Nil = + Nil + +/// Check whether the result is an Ok value. +/// +/// ## Examples +/// +/// > is_ok(Ok(1)) +/// True +/// +/// > is_ok(Error(Nil)) +/// False +/// +pub fn is_ok(result: Result(a, e)) -> Bool { + case result { + Error(_) -> False + Ok(_) -> True + } +} + +/// Check whether the result is an Error value. +/// +/// ## Examples +/// +/// > is_error(Ok(1)) +/// False +/// +/// > is_error(Error(Nil)) +/// True +/// +pub fn is_error(result: Result(a, e)) -> Bool { + case result { + Ok(_) -> False + Error(_) -> True + } +} + +/// Update a value held within the Ok of a result by calling a given function +/// on it. +/// +/// If the result is an Error rather than OK the function is not called and the +/// result stays the same. +/// +/// ## Examples +/// +/// > map(over: Ok(1), with: fn(x) { x + 1 }) +/// Ok(2) +/// +/// > map(over: Error(1), with: fn(x) { x + 1 }) +/// Error(1) +/// +pub fn map(over result: Result(a, e), with fun: fn(a) -> b) -> Result(b, e) { + case result { + Ok(x) -> Ok(fun(x)) + Error(e) -> Error(e) + } +} + +/// Update a value held within the Error of a result by calling a given function +/// on it. +/// +/// If the result is Ok rather than Error the function is not called and the +/// result stays the same. +/// +/// ## Examples +/// +/// > map_error(over: Error(1), with: fn(x) { x + 1 }) +/// Error(2) +/// +/// > map_error(over: Ok(1), with: fn(x) { x + 1 }) +/// Ok(1) +/// +pub fn map_error( + over result: Result(a, e), + with fun: fn(e) -> f, +) -> Result(a, f) { + case result { + Ok(x) -> Ok(x) + Error(error) -> Error(fun(error)) + } +} + +/// Merge a nested Result into a single layer. +/// +/// ## Examples +/// +/// > flatten(Ok(Ok(1))) +/// Ok(1) +/// +/// > flatten(Ok(Error("")) +/// Error("") +/// +/// > flatten(Error(Nil)) +/// Error(Nil) +/// +pub fn flatten(result: Result(Result(a, e), e)) -> Result(a, e) { + case result { + Ok(x) -> x + Error(error) -> Error(error) + } +} + +/// Update a value held within the Ok of a result by calling a given function +/// on it, where the given function also returns a result. The two results are +/// then merged together into one result. +/// +/// If the result is an Error rather than OK the function is not called and the +/// result stays the same. +/// +/// This function is the equivalent of calling `map` followed by `flatten`, and +/// it is useful for chaining together multiple functions that may fail. +/// +/// ## Examples +/// +/// > then(Ok(1), fn(x) { Ok(x + 1) }) +/// Ok(2) +/// +/// > then(Ok(1), fn(x) { Ok(tuple("a", x)) }) +/// Ok(tuple("a", 1)) +/// +/// > then(Ok(1), fn(x) { Error("Oh no") }) +/// Error("Oh no") +/// +/// > then(Error(Nil), fn(x) { Ok(x + 1) }) +/// Error(Nil) +/// +pub fn then( + result: Result(a, e), + apply fun: fn(a) -> Result(b, e), +) -> Result(b, e) { + case result { + Ok(x) -> fun(x) + Error(e) -> Error(e) + } +} + +/// Extract the Ok value from a result, returning a default value if the result +/// is an Error. +/// +/// ## Examples +/// +/// > unwrap(Ok(1), 0) +/// 1 +/// +/// > unwrap(Error(""), 0) +/// 0 +/// +pub fn unwrap(result: Result(a, e), or default: a) -> a { + case result { + Ok(v) -> v + Error(_) -> default + } +} + +/// Extract the Ok value from a result, evaluating the default function if the result +/// is an Error. +/// +/// ## Examples +/// +/// > unwrap(Ok(1), fn() { 0 }) +/// 1 +/// +/// > unwrap(Error(""), fn() { 0 }) +/// 0 +/// +pub fn lazy_unwrap(result: Result(a, e), or default: fn() -> a) -> a { + case result { + Ok(v) -> v + Error(_) -> default() + } +} + +/// Transforms any error into Error(Nil) +/// +/// ## Examples +/// +/// > nil_error(Error(1)) +/// Error(Nil) +/// +/// > nil_error(Ok(1)) +/// Ok(1) +/// +pub fn nil_error(result: Result(a, e)) -> Result(a, Nil) { + map_error(result, fn(_) { Nil }) +} + +/// Return the first value if it is Ok, otherwise return the second value. +/// +/// ## Examples +/// +/// > or(Ok(1), Ok(2)) +/// Ok(1) +/// +/// > or(Ok(1), Error("Error 2")) +/// Ok(1) +/// +/// > or(Error("Error 1"), Ok(2)) +/// Ok(2) +/// +/// > or(Error("Error 1"), Error("Error 2")) +/// Error("Error 2") +/// +pub fn or(first: Result(a, e), second: Result(a, e)) -> Result(a, e) { + case first { + Ok(_) -> first + Error(_) -> second + } +} + +/// Return the first value if it is Ok, otherwise evaluates the given function for a fallback value. +/// +/// ## Examples +/// +/// > or(Ok(1), Ok(2)) +/// Ok(1) +/// +/// > or(Ok(1), Error("Error 2")) +/// Ok(1) +/// +/// > or(Error("Error 1"), Ok(2)) +/// Ok(2) +/// +/// > or(Error("Error 1"), Error("Error 2")) +/// Error("Error 2") +/// +pub fn lazy_or( + first: Result(a, e), + second: fn() -> Result(a, e), +) -> Result(a, e) { + case first { + Ok(_) -> first + Error(_) -> second() + } +} + +/// Combine a list of results into a single result. +/// If all elements in the list are Ok then returns an Ok holding the list of values. +/// If any element is Error then returns the first error. +/// +/// ## Examples +/// > all([Ok(1), Ok(2)]) +/// Ok([1, 2]) +/// +/// > all([Ok(1), Error("e")]) +/// Error("e") +pub fn all(results: List(Result(a, e))) -> Result(List(a), e) { + list.try_map(results, fn(x) { x }) +} + +pub fn replace_error(result: Result(a, e1), error: e2) -> Result(a, e2) { + result + |> map_error(fn(_) { error }) +} diff --git a/gleam_providers/deps/gleam_stdlib/src/gleam/set.gleam b/gleam_providers/deps/gleam_stdlib/src/gleam/set.gleam new file mode 100644 index 0000000..0455081 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/src/gleam/set.gleam @@ -0,0 +1,210 @@ +import gleam/map.{Map} +import gleam/result +import gleam/list + +/// A set is a collection of unique members of the same type. +/// +/// It is implemented using the `gleam/map` module, so inserts and lookups have +/// logarithmic time complexity. +/// +pub opaque type Set(member) { + // A list is used as the map value as an empty list has the smallest + // representation in Erlang's binary format + Set(map: Map(member, List(Nil))) +} + +/// Create a new empty set. +/// +pub fn new() -> Set(member) { + Set(map.new()) +} + +/// Get the number of members in a set. +/// +/// This function runs in constant time. +/// +/// ## Examples +/// +/// > new() |> insert(1) |> insert(2) |> size +/// 2 +/// +pub fn size(set: Set(member)) -> Int { + map.size(set.map) +} + +/// Insert an member into the set. +/// +/// This function runs in logarithmic time. +/// +/// ## Examples +/// +/// > new() |> insert(1) |> insert(2) |> size +/// 2 +/// +pub fn insert(into set: Set(member), this member: member) -> Set(member) { + Set(map: map.insert(set.map, member, [])) +} + +/// Check whether a set contains a given member. +/// +/// This function runs in logarithmic time. +/// +/// ## Examples +/// +/// > new() |> insert(2) |> contains(2) +/// True +/// +/// > new() |> insert(2) |> contains(1) +/// False +/// +pub fn contains(in set: Set(member), this member: member) -> Bool { + set.map + |> map.get(member) + |> result.is_ok +} + +/// Remove an member from a set. If the set does not contain the member then +/// the set is returned unchanged. +/// +/// This function runs in logarithmic time. +/// +/// ## Examples +/// +/// > new() |> insert(2) |> delete(2) |> contains(1) +/// False +/// +pub fn delete(from set: Set(member), this member: member) -> Set(member) { + Set(map: map.delete(set.map, member)) +} + +/// Convert the set into a list of the contained members. +/// +/// The list has no specific ordering, any unintentional ordering may change in +/// future versions of Gleam or Erlang. +/// +/// This function runs in linear time. +/// +/// ## Examples +/// +/// > new() |> insert(2) |> to_list +/// [2] +/// +pub fn to_list(set: Set(member)) -> List(member) { + map.keys(set.map) +} + +/// Create a new set of the members in a given list. +/// +/// This function runs in loglinear time. +/// +/// ## Examples +/// +/// > import gleam/list +/// > [1, 1, 2, 4, 3, 2] |> from_list |> to_list |> list.sort +/// [1, 3, 3, 4] +/// +pub fn from_list(members: List(member)) -> Set(member) { + let map = + list.fold( + over: members, + from: map.new(), + with: fn(k, m) { map.insert(m, k, []) }, + ) + Set(map) +} + +/// Combine all entries into a single value by calling a given function on each +/// one. +/// +/// Sets are not ordered so the values are not returned in any specific order. +/// Do not write code that relies on the order entries are used by this +/// function as it may change in later versions of Gleam or Erlang. +/// +/// # Examples +/// +/// > from_list([1, 3, 9]) +/// > |> fold(0, fn(member, accumulator) { accumulator + member }) +/// 13 +/// +pub fn fold( + over set: Set(member), + from initial: acc, + with reducer: fn(member, acc) -> acc, +) -> acc { + map.fold(over: set.map, from: initial, with: fn(k, _, a) { reducer(k, a) }) +} + +/// Create a new set from an existing set, minus any members that a given +/// function returns False for. +/// +/// This function runs in loglinear time. +/// +/// ## Examples +/// +/// > import gleam/int +/// > from_list([1, 4, 6, 3, 675, 44, 67]) +/// > |> filter(for: int.is_even) +/// > |> to_list +/// [4, 6, 44] +/// +pub fn filter( + in set: Set(member), + for property: fn(member) -> Bool, +) -> Set(member) { + Set(map.filter(in: set.map, for: fn(m, _) { property(m) })) +} + +/// Create a new map from a given map, only including any members which are in +/// a given list. +/// +/// This function runs in loglinear time. +/// +/// ## Examples +/// +/// > from_list([1, 2, 3]) |> take([1, 3, 5]) |> to_list +/// [1, 3] +/// +pub fn take(from set: Set(member), keeping desired: List(member)) -> Set(member) { + Set(map.take(from: set.map, keeping: desired)) +} + +fn order( + first: Set(member), + second: Set(member), +) -> tuple(Set(member), Set(member)) { + case map.size(first.map) > map.size(second.map) { + True -> tuple(first, second) + False -> tuple(second, first) + } +} + +/// Create a new set that contains all members of both given sets. +/// +/// This function runs in loglinear time. +/// +/// ## Examples +/// +/// > union(from_list([1, 2]), from_list([2, 3])) |> to_list +/// [1, 2, 3] +/// +pub fn union(of first: Set(member), and second: Set(member)) -> Set(member) { + let tuple(larger, smaller) = order(first, second) + fold(over: smaller, from: larger, with: fn(m, a) { insert(a, m) }) +} + +/// Create a new set that contains members that are present in both given sets. +/// +/// This function runs in loglinear time. +/// +/// ## Examples +/// +/// > intersection(from_list([1, 2]), from_list([2, 3])) |> to_list +/// [2] +/// +pub fn intersection( + of first: Set(member), + and second: Set(member), +) -> Set(member) { + let tuple(larger, smaller) = order(first, second) + take(from: larger, keeping: to_list(smaller)) +} diff --git a/gleam_providers/deps/gleam_stdlib/src/gleam/should.gleam b/gleam_providers/deps/gleam_stdlib/src/gleam/should.gleam new file mode 100644 index 0000000..7c3e08e --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/src/gleam/should.gleam @@ -0,0 +1,35 @@ +//// A module for testing your Gleam code. The functions found here are +//// compatible with the Erlang eunit test framework. +//// +//// More information on running eunit can be found in [the rebar3 +//// documentation](https://rebar3.org/docs/testing/eunit/). + +// TODO: Move this module into another package so it can be used as a +// dep only in test. +pub external type Expectation + +pub external fn equal(a, a) -> Expectation = + "gleam_stdlib" "should_equal" + +pub external fn not_equal(a, a) -> Expectation = + "gleam_stdlib" "should_not_equal" + +pub fn be_true(actual: Bool) -> Expectation { + actual + |> equal(True) +} + +pub fn be_false(actual: Bool) -> Expectation { + actual + |> equal(False) +} + +pub external fn be_ok(Result(a, b)) -> Expectation = + "gleam_stdlib" "should_be_ok" + +pub external fn be_error(Result(a, b)) -> Expectation = + "gleam_stdlib" "should_be_error" + +pub fn fail() -> Expectation { + be_true(False) +} diff --git a/gleam_providers/deps/gleam_stdlib/src/gleam/string.gleam b/gleam_providers/deps/gleam_stdlib/src/gleam/string.gleam new file mode 100644 index 0000000..5f83acb --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/src/gleam/string.gleam @@ -0,0 +1,463 @@ +//// Strings in Gleam are UTF-8 binaries. They can be written in your code a +//// text surrounded by `"double quotes"`. + +import gleam/string_builder +import gleam/dynamic.{Dynamic} +import gleam/iterator +import gleam/list +import gleam/order +import gleam/result + +pub type String = + String + +/// A UtfCodepoint is the integer representation of a valid UTF codepoint +pub type UtfCodepoint = + UtfCodepoint + +/// Determine if a string is empty. +/// +/// ## Examples +/// +/// > is_empty("") +/// True +/// +/// > is_empty("the world") +/// False +/// +pub fn is_empty(str: String) -> Bool { + str == "" +} + +/// Get the number of grapheme clusters in a given string. +/// +/// This function has to iterate across the whole string to count the number of +/// graphemes, so it runs in linear time. +/// +/// ## Examples +/// +/// > length("Gleam") +/// 5 +/// +/// > length("ß↑e̊") +/// 3 +/// +/// > length("") +/// 0 +/// +pub external fn length(String) -> Int = + "string" "length" + +/// +/// Reverse a string. +/// +/// This function has to iterate across the whole string so it runs in linear +/// time. +/// +/// ## Examples +/// +/// > reverse("stressed") +/// "desserts" +/// +pub fn reverse(string: String) -> String { + string + |> string_builder.from_string + |> string_builder.reverse + |> string_builder.to_string +} + +/// Create a new string by replacing all occurrences of a given substring. +/// +/// ## Examples +/// +/// > replace("www.example.com", each: ".", with: "-") +/// "www-example-com" +/// +/// > replace("a,b,c,d,e", each: ",", with: "/") +/// "a/b/c/d/e" +/// +pub fn replace( + in string: String, + each pattern: String, + with substitute: String, +) -> String { + string + |> string_builder.from_string + |> string_builder.replace(each: pattern, with: substitute) + |> string_builder.to_string +} + +/// Create a new string with all the graphemes in the input string converted to +/// lowercase. +/// +/// Useful for case-insensitive comparisons. +/// +/// ## Examples +/// +/// > lowercase("X-FILES") +/// "x-files" +/// +pub external fn lowercase(String) -> String = + "string" "lowercase" + +/// Create a new string with all the graphemes in the input string converted to +/// uppercase. +/// +/// Useful for case-insensitive comparisons and VIRTUAL YELLING. +/// +/// ## Examples +/// +/// > uppercase("skinner") +/// "SKINNER" +/// +pub external fn uppercase(String) -> String = + "string" "uppercase" + +/// Compares two strings to see which is "larger" by comparing their graphemes. +/// +/// This does not compare the size or length of the given strings. +/// +/// ## Examples +/// +/// > compare("Anthony", "Anthony") +/// order.Eq +/// +/// > compare("A", "B") +/// order.Lt +/// +pub external fn compare(String, String) -> order.Order = + "gleam_stdlib" "compare_strings" + +external fn erl_slice(String, Int, Int) -> String = + "string" "slice" + +/// Take a substring given a start and end Grapheme indexes. Negative indexes +/// are taken starting from the *end* of the list. +/// +/// ## Examples +/// > slice(from: "gleam", at_index: 1, length: 2) +/// "le" +/// +/// > slice(from: "gleam", at_index: 1, length: 10) +/// "leam" +/// +/// > slice(from: "gleam", at_index: 10, length: 3) +/// "" +/// +/// > slice(from: "gleam", at_index: -2, length: 2) +/// "am" +/// +/// > slice(from: "gleam", at_index: -12, length: 2) +/// "" +/// +pub fn slice(from string: String, at_index idx: Int, length len: Int) -> String { + case len < 0 { + True -> "" + False -> + case idx < 0 { + True -> { + let translated_idx = length(string) + idx + case translated_idx < 0 { + True -> "" + False -> erl_slice(string, translated_idx, len) + } + } + False -> erl_slice(string, idx, len) + } + } +} + +/// Drop *n* Graphemes from the left side of a string. +/// +/// ## Examples +/// > drop_left(from: "The Lone Gunmen", up_to: 2) +/// "e Lone Gunmen" +/// +pub fn drop_left(from string: String, up_to num_graphemes: Int) -> String { + case num_graphemes < 0 { + True -> string + False -> slice(string, num_graphemes, length(string) - num_graphemes) + } +} + +/// Drop *n* Graphemes from the right side of a string. +/// +/// ## Examples +/// > drop_right(from: "Cigarette Smoking Man", up_to: 2) +/// "Cigarette Smoking M" +/// +pub fn drop_right(from string: String, up_to num_graphemes: Int) -> String { + case num_graphemes < 0 { + True -> string + False -> slice(string, 0, length(string) - num_graphemes) + } +} + +external fn erl_contains(String, String) -> Dynamic = + "string" "find" + +/// Check if the first string contains the second. +/// +/// ## Examples +/// +/// > contains(does: "theory", contain: "ory") +/// True +/// +/// > contains(does: "theory", contain: "the") +/// True +/// +/// > contains(does: "theory", contain: "THE") +/// False +/// +pub fn contains(does haystack: String, contain needle: String) -> Bool { + haystack + |> erl_contains(needle) + |> dynamic.atom + |> result.is_error +} + +/// See if the first string starts with the second one. +/// +/// ## Examples +/// +/// > starts_with("theory", "ory") +/// False +/// +pub external fn starts_with(String, String) -> Bool = + "gleam_stdlib" "string_starts_with" + +/// See if the first string ends with the second one. +/// +/// ## Examples +/// +/// > ends_with("theory", "ory") +/// True +/// +pub external fn ends_with(String, String) -> Bool = + "gleam_stdlib" "string_ends_with" + +/// Create a list of strings by splitting a given string on a given substring. +/// +/// ## Examples +/// +/// > split("home/gleam/desktop/", on: "/") +/// ["home", "gleam", "desktop", ""] +/// +pub fn split(x: String, on substring: String) -> List(String) { + x + |> string_builder.from_string + |> string_builder.split(on: substring) + |> list.map(with: string_builder.to_string) +} + +external fn erl_split(String, String) -> List(String) = + "string" "split" + +/// Splits a string a single time on the given substring. +/// +/// Returns an error if substring not present. +/// +/// ## Examples +/// +/// > split_once("home/gleam/desktop/", on: "/") +/// Ok(tuple("home", "gleam/desktop/")) +/// +/// > split_once("home/gleam/desktop/", on: "?") +/// Error(Nil) +/// +pub fn split_once( + x: String, + on substring: String, +) -> Result(tuple(String, String), Nil) { + case erl_split(x, substring) { + [first, rest] -> Ok(tuple(first, rest)) + _ -> Error(Nil) + } +} + +/// Create a new string by joining two strings together. +/// +/// This function copies both strings and runs in linear time. If you find +/// yourself joining strings frequently consider using the [string_builder](../string_builder) +/// module as it can append strings much faster! +/// +/// ## Examples +/// +/// > append(to: "butter", suffix: "fly") +/// "butterfly" +/// +pub fn append(to first: String, suffix second: String) -> String { + first + |> string_builder.from_string + |> string_builder.append(second) + |> string_builder.to_string +} + +/// Create a new string by joining many strings together. +/// +/// This function copies both strings and runs in linear time. If you find +/// yourself joining strings frequently consider using the [string_builder](../string_builder) +/// module as it can append strings much faster! +/// +/// ## Examples +/// +/// > concat(["never", "the", "less"]) +/// "nevertheless" +/// +pub fn concat(strings: List(String)) -> String { + strings + |> string_builder.from_strings + |> string_builder.to_string +} + +/// Create a new string by repeating a string a given number of times. +/// +/// This function runs in linear time. +/// +/// ## Examples +/// +/// > repeat("ha", times: 3) +/// "hahaha" +/// +pub fn repeat(string: String, times times: Int) -> String { + iterator.repeat(string) + |> iterator.take(times) + |> concat +} + +/// Join many strings together with a given separator. +/// +/// This function runs in linear time. +/// +/// ## Examples +/// +/// > join(["home","evan","Desktop"], with: "/") +/// "home/evan/Desktop" +/// +pub fn join(strings: List(String), with separator: String) -> String { + strings + |> list.intersperse(with: separator) + |> concat +} + +type Direction { + Leading + Trailing + Both +} + +external fn erl_pad(String, Int, Direction, String) -> String = + "gleam_stdlib" "string_pad" + +/// Pad a string on the left until it has at least given number of Graphemes. +/// +/// ## Examples +/// +/// > pad_left("121", to: 5, with: ".") +/// "..121" +/// +/// > pad_left("121", to: 3, with: ".") +/// "121" +/// +/// > pad_left("121", to: 2, with: ".") +/// "121" +/// +pub fn pad_left(string: String, to length: Int, with pad_string: String) { + erl_pad(string, length, Leading, pad_string) +} + +/// Pad a string on the right until it has a given length. +/// +/// ## Examples +/// +/// > pad_right("121", to: 5, with: ".") +/// "121.." +/// +/// > pad_right("121", to: 3, with: ".") +/// "121" +/// +/// > pad_right("121", to: 2, with: ".") +/// "121" +/// +pub fn pad_right(string: String, to length: Int, with pad_string: String) { + erl_pad(string, length, Trailing, pad_string) +} + +external fn erl_trim(String, Direction) -> String = + "string" "trim" + +/// Get rid of whitespace on both sides of a String. +/// +/// ## Examples +/// +/// > trim(" hats \n") +/// "hats" +/// +pub fn trim(string: String) -> String { + erl_trim(string, Both) +} + +/// Get rid of whitespace on the left of a String. +/// +/// ## Examples +/// +/// > trim_left(" hats \n") +/// "hats \n" +/// +pub fn trim_left(string: String) -> String { + erl_trim(string, Leading) +} + +/// Get rid of whitespace on the right of a String. +/// +/// ## Examples +/// +/// > trim_right(" hats \n") +/// " hats" +/// +pub fn trim_right(string: String) -> String { + erl_trim(string, Trailing) +} + +/// Split a non-empty string into its head and tail. This lets you +/// pattern match on strings exactly as you would with lists. +/// +/// ## Examples +/// > pop_grapheme("gleam") +/// Ok(tuple("g", "leam")) +/// +/// > pop_grapheme("") +/// Error(Nil) +/// +pub external fn pop_grapheme( + string: String, +) -> Result(tuple(String, String), Nil) = + "gleam_stdlib" "string_pop_grapheme" + +/// Convert a string to a list of Graphemes. +/// +/// > to_graphemes("abc") +/// ["a", "b", "c"] +/// +pub fn to_graphemes(string: String) -> List(String) { + case pop_grapheme(string) { + Ok(tuple(grapheme, rest)) -> [grapheme, ..to_graphemes(rest)] + _ -> [] + } +} + +external fn int_to_utf_codepoint(Int) -> UtfCodepoint = + "gleam_stdlib" "identity" + +/// Convert an integer to a UtfCodepoint +/// +/// Returns an error if the integer does not represent a valid UTF codepoint. +/// +pub fn utf_codepoint(value: Int) -> Result(UtfCodepoint, Nil) { + case value { + i if i > 1114111 -> Error(Nil) + 65534 | 65535 -> Error(Nil) + i if i >= 55296 && i <= 57343 -> Error(Nil) + i -> Ok(int_to_utf_codepoint(i)) + } +} diff --git a/gleam_providers/deps/gleam_stdlib/src/gleam/string_builder.gleam b/gleam_providers/deps/gleam_stdlib/src/gleam/string_builder.gleam new file mode 100644 index 0000000..5acf8e1 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/src/gleam/string_builder.gleam @@ -0,0 +1,168 @@ +/// StringBuilder is a type used for efficiently building strings. +/// +/// When we append one string to another the strings must be copied to a +/// new location in memory so that they can sit together. This behaviour +/// enables efficient reading of the string but copying can be expensive, +/// especially if we want to join many strings together. +/// +/// StringBuilder is different in that it can be joined together in constant time +/// using minimal memory, and then can be efficiently converted to a string +/// using the `to_string` function. +/// +pub external type StringBuilder + +/// Prepend a String onto the start of some StringBuilder. +/// +/// Runs in constant time. +/// +pub external fn prepend(to: StringBuilder, prefix: String) -> StringBuilder = + "gleam_stdlib" "iodata_prepend" + +/// Append a String onto the end of some StringBuilder. +/// +/// Runs in constant time. +/// +pub external fn append(to: StringBuilder, suffix: String) -> StringBuilder = + "gleam_stdlib" "iodata_append" + +/// Prepend some StringBuilder onto the start of another. +/// +/// Runs in constant time. +/// +pub external fn prepend_builder( + to: StringBuilder, + prefix: StringBuilder, +) -> StringBuilder = + "gleam_stdlib" "iodata_prepend" + +/// Append some StringBuilder onto the end of another. +/// +/// Runs in constant time. +/// +pub external fn append_builder( + to: StringBuilder, + suffix: StringBuilder, +) -> StringBuilder = + "gleam_stdlib" "iodata_append" + +/// Convert a list of strings into a builder. +/// +/// Runs in constant time. +/// +pub external fn from_strings(List(String)) -> StringBuilder = + "gleam_stdlib" "identity" + +/// Joins a list of builders into a single builder. +/// +/// Runs in constant time. +/// +pub external fn concat(List(StringBuilder)) -> StringBuilder = + "gleam_stdlib" "identity" + +/// Convert a string into a builder. +/// +/// Runs in constant time. +/// +pub external fn from_string(String) -> StringBuilder = + "gleam_stdlib" "identity" + +/// Turns an `StringBuilder` into a `String` +/// +/// This function is implemented natively by the virtual machine and is highly +/// optimised. +/// +pub external fn to_string(StringBuilder) -> String = + "erlang" "iolist_to_binary" + +/// Returns the size of the StringBuilder in bytes. +/// +pub external fn byte_size(StringBuilder) -> Int = + "erlang" "iolist_size" + +/// Creates a builder containing the textual representation of a given float. +/// +pub external fn from_float(Float) -> StringBuilder = + "io_lib_format" "fwrite_g" + +/// Converts a builder to a new builder where the contents have been +/// lowercased. +/// +pub external fn lowercase(StringBuilder) -> StringBuilder = + "string" "lowercase" + +/// Converts a builder to a new builder where the contents have been +/// uppercased. +/// +pub external fn uppercase(StringBuilder) -> StringBuilder = + "string" "uppercase" + +/// Converts a builder to a new builder with the contents reversed. +/// +pub external fn reverse(StringBuilder) -> StringBuilder = + "string" "reverse" + +type Direction { + All +} + +external fn erl_split(StringBuilder, String, Direction) -> List(StringBuilder) = + "string" "split" + +/// Splits a builder on a given pattern into a list of builders. +/// +pub fn split(iodata: StringBuilder, on pattern: String) -> List(StringBuilder) { + erl_split(iodata, pattern, All) +} + +external fn erl_replace( + StringBuilder, + String, + String, + Direction, +) -> StringBuilder = + "string" "replace" + +/// Replaces all instances of a pattern with a given string substitute. +/// +pub fn replace( + in iodata: StringBuilder, + each pattern: String, + with substitute: String, +) -> StringBuilder { + erl_replace(iodata, pattern, substitute, All) +} + +/// Compare two builders to determine if they have the same textual content. +/// +/// Comparing two iodata using the `==` operator may return False even if they +/// have the same content as they may have been build in different ways, so +/// using this function is often preferred. +/// +/// ## Examples +/// +/// > from_strings(["a", "b"]) == new("ab") +/// False +/// +/// > is_equal(from_strings(["a", "b"]), new("ab")) +/// True +/// +/// +pub external fn is_equal(StringBuilder, StringBuilder) -> Bool = + "string" "equal" + +/// Inspect a builder to determine if it is equivalent to an empty string. +/// +/// ## Examples +/// +/// > new("ok") |> is_empty +/// False +/// +/// > new("") |> is_empty +/// True +/// +/// > from_strings([]) |> is_empty +/// True +/// +/// +pub external fn is_empty(StringBuilder) -> Bool = + "string" "is_empty" diff --git a/gleam_providers/deps/gleam_stdlib/src/gleam/uri.gleam b/gleam_providers/deps/gleam_stdlib/src/gleam/uri.gleam new file mode 100644 index 0000000..2ff542a --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/src/gleam/uri.gleam @@ -0,0 +1,300 @@ +//// Utilities for working with URIs +//// +//// This module provides functions for working with URIs (for example, parsing +//// URIs or encoding query strings). The functions in this module are implemented +//// according to [RFC 3986](https://tools.ietf.org/html/rfc3986). +//// +//// Query encoding (Form encoding) is defined in the w3c specification. +//// https://www.w3.org/TR/html52/sec-forms.html#urlencoded-form-data + +import gleam/list +import gleam/result +import gleam/option.{None, Option, Some} +import gleam/string +import gleam/dynamic.{Dynamic} +import gleam/map.{Map} +import gleam/function +import gleam/pair + +/// Type representing holding the parsed components of an URI. +/// All components of a URI are optional, except the path. +/// +pub type Uri { + Uri( + scheme: Option(String), + userinfo: Option(String), + host: Option(String), + port: Option(Int), + path: String, + query: Option(String), + fragment: Option(String), + ) +} + +pub external fn erl_parse(String) -> Dynamic = + "uri_string" "parse" + +type UriKey { + Scheme + Userinfo + Host + Port + Path + Query + Fragment +} + +/// Parses a compliant URI string into the `Uri` Type. +/// If the string is not a valid URI string then an error is returned. +/// +/// The opposite operation is `uri.to_string` +/// +pub fn parse(string: String) -> Result(Uri, Nil) { + try uri_map = + dynamic.map(erl_parse(string)) + |> result.nil_error + let get = fn(k: UriKey, decode_type: dynamic.Decoder(t)) -> Option(t) { + uri_map + |> map.get(dynamic.from(k)) + |> result.then(function.compose(decode_type, result.nil_error)) + |> option.from_result + } + + let uri = + Uri( + scheme: get(Scheme, dynamic.string), + userinfo: get(Userinfo, dynamic.string), + host: get(Host, dynamic.string), + port: get(Port, dynamic.int), + path: option.unwrap(get(Path, dynamic.string), ""), + query: get(Query, dynamic.string), + fragment: get(Fragment, dynamic.string), + ) + Ok(uri) +} + +external fn erl_parse_query(String) -> Dynamic = + "uri_string" "dissect_query" + +/// Parses an urlencoded query string into a list of key value pairs. +/// Returns an error for invalid encoding. +/// +/// The opposite operation is `uri.query_to_string`. +/// +pub fn parse_query(query: String) -> Result(List(tuple(String, String)), Nil) { + let bool_value = fn(x) { result.map(dynamic.bool(x), fn(_) { "" }) } + let query_param = dynamic.typed_tuple2( + _, + dynamic.string, + dynamic.any(_, of: [dynamic.string, bool_value]), + ) + + query + |> erl_parse_query + |> dynamic.typed_list(of: query_param) + |> result.nil_error +} + +type Encoding { + Utf8 +} + +type ErlQueryToStringOption { + Encoding(Encoding) +} + +external fn erl_query_to_string( + List(tuple(String, String)), + List(ErlQueryToStringOption), +) -> Dynamic = + "uri_string" "compose_query" + +/// Encode a list of key value pairs as a URI query string. +/// +/// The opposite operation is `uri.parse_query`. +/// +pub fn query_to_string(query: List(tuple(String, String))) -> String { + query + |> erl_query_to_string([Encoding(Utf8)]) + |> dynamic.string + |> result.unwrap("") +} + +/// Encode a string into a percent encoded representation. +/// Note that this encodes space as +. +/// +/// ## Example +/// +/// percent_encode("100% great") +/// > "100%25+great" +/// +pub fn percent_encode(value: String) -> String { + query_to_string([tuple("k", value)]) + |> string.replace(each: "k=", with: "") +} + +/// Decode a percent encoded string. +/// +/// ## Example +/// +/// percent_decode("100%25+great") +/// > Ok("100% great") +/// +pub fn percent_decode(value: String) -> Result(String, Nil) { + string.concat(["k=", value]) + |> parse_query + |> result.then(list.head) + |> result.map(pair.second) +} + +fn do_remove_dot_segments( + input: List(String), + accumulator: List(String), +) -> List(String) { + case input { + [] -> list.reverse(accumulator) + [segment, ..rest] -> { + let accumulator = case segment, accumulator { + "", accumulator -> accumulator + ".", accumulator -> accumulator + "..", [] -> [] + "..", [_, ..accumulator] -> accumulator + segment, accumulator -> [segment, ..accumulator] + } + do_remove_dot_segments(rest, accumulator) + } + } +} + +fn remove_dot_segments(input: List(String)) -> List(String) { + do_remove_dot_segments(input, []) +} + +/// Split the path section of a URI into it's constituent segments. +/// +/// Removes empty segments and resolves dot-segments as specified in +/// [section 5.2](https://www.ietf.org/rfc/rfc3986.html#section-5.2) of the RFC. +/// +pub fn path_segments(path: String) -> List(String) { + remove_dot_segments(string.split(path, "/")) +} + +external fn erl_to_string(Map(UriKey, Dynamic)) -> Dynamic = + "uri_string" "recompose" + +/// Encode a `Uri` value as a URI string. +/// +/// The opposite operation is `uri.parse`. +/// +pub fn to_string(uri: Uri) -> String { + let field = fn(key: UriKey, value: Option(anything)) -> Result( + tuple(UriKey, Dynamic), + Nil, + ) { + case value { + Some(v) -> Ok(tuple(key, dynamic.from(v))) + None -> Error(Nil) + } + } + + [ + field(Scheme, uri.scheme), + field(Userinfo, uri.userinfo), + field(Host, uri.host), + field(Port, uri.port), + field(Path, option.Some(uri.path)), + field(Query, uri.query), + field(Fragment, uri.fragment), + ] + |> list.filter_map(fn(x) { x }) + |> map.from_list + |> erl_to_string + |> dynamic.string + |> result.unwrap("") +} + +/// Fetch the origin of a uri +/// +/// Return the origin of a uri as defined in +/// https://tools.ietf.org/html/rfc6454 +/// +/// The supported uri schemes are `http` and `https` +/// Urls without a scheme will return Error +pub fn origin(uri: Uri) -> Result(String, Nil) { + let Uri(scheme: scheme, host: host, port: port, ..) = uri + case scheme { + Some("https") | Some("http") -> { + let origin = Uri(scheme, None, host, port, "", None, None) + Ok(to_string(origin)) + } + _ -> Error(Nil) + } +} + +fn drop_last(elements: List(a)) -> List(a) { + list.take(from: elements, up_to: list.length(elements) - 1) +} + +fn join_segments(segments: List(String)) -> String { + string.join(["", ..segments], "/") +} + +/// Resolve a uri with respect to the given base uri +/// +/// The base uri must be an absolute uri or this function will return an error. +/// The algorithm for merging uris is described in [RFC 3986](https://tools.ietf.org/html/rfc3986#section-5.2) +pub fn merge(base: Uri, relative: Uri) -> Result(Uri, Nil) { + case base { + Uri(scheme: Some(_), host: Some(_), ..) -> + case relative { + Uri(host: Some(_), ..) -> { + let path = + string.split(relative.path, "/") + |> remove_dot_segments() + |> join_segments() + let resolved = + Uri( + option.or(relative.scheme, base.scheme), + None, + relative.host, + relative.port, + path, + relative.query, + relative.fragment, + ) + Ok(resolved) + } + Uri(scheme: None, host: None, ..) -> { + let tuple(new_path, new_query) = case relative.path { + "" -> tuple(base.path, option.or(relative.query, base.query)) + _ -> { + let path_segments = case string.starts_with(relative.path, "/") { + True -> string.split(relative.path, "/") + False -> + string.split(base.path, "/") + |> drop_last() + |> list.append(string.split(relative.path, "/")) + } + let path = + path_segments + |> remove_dot_segments() + |> join_segments() + tuple(path, relative.query) + } + } + let resolved = + Uri( + base.scheme, + None, + base.host, + base.port, + new_path, + new_query, + relative.fragment, + ) + Ok(resolved) + } + } + _ -> Error(Nil) + } +} diff --git a/gleam_providers/deps/gleam_stdlib/src/gleam_stdlib.app.src b/gleam_providers/deps/gleam_stdlib/src/gleam_stdlib.app.src new file mode 100644 index 0000000..4a37f52 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/src/gleam_stdlib.app.src @@ -0,0 +1,10 @@ +{application,gleam_stdlib, + [{description,"A standard library for the Gleam programming language"}, + {vsn,"0.13.0"}, + {registered,[]}, + {applications,[kernel,stdlib]}, + {env,[]}, + {modules,[]}, + {licenses,["Apache 2.0"]}, + {links,[{"GitHub","https://github.com/gleam-lang/stdlib"}]}, + {include_files,["gleam.toml","gen"]}]}. diff --git a/gleam_providers/deps/gleam_stdlib/src/gleam_stdlib.erl b/gleam_providers/deps/gleam_stdlib/src/gleam_stdlib.erl new file mode 100644 index 0000000..d94abc1 --- /dev/null +++ b/gleam_providers/deps/gleam_stdlib/src/gleam_stdlib.erl @@ -0,0 +1,212 @@ +-module(gleam_stdlib). +-include_lib("eunit/include/eunit.hrl"). + +-export([should_equal/2, should_not_equal/2, should_be_ok/1, should_be_error/1, + atom_from_string/1, atom_create_from_string/1, atom_to_string/1, + map_get/2, iodata_append/2, iodata_prepend/2, identity/1, + decode_int/1, decode_bool/1, decode_float/1, + decode_thunk/1, decode_atom/1, decode_list/1, decode_field/2, + decode_element/2, parse_int/1, parse_float/1, compare_strings/2, + string_pop_grapheme/1, string_starts_with/2, string_ends_with/2, + string_pad/4, decode_tuple2/1, decode_map/1, bit_string_int_to_u32/1, + bit_string_int_from_u32/1, bit_string_append/2, bit_string_part_/3, + decode_bit_string/1, compile_regex/2, regex_match/2, regex_split/2, + regex_scan/2, base_decode64/1, wrap_list/1, rescue/1]). + +should_equal(Actual, Expected) -> ?assertEqual(Expected, Actual). +should_not_equal(Actual, Expected) -> ?assertNotEqual(Expected, Actual). +should_be_ok(A) -> ?assertMatch({ok, _}, A). +should_be_error(A) -> ?assertMatch({error, _}, A). + +map_get(Map, Key) -> + case maps:find(Key, Map) of + error -> {error, nil}; + OkFound -> OkFound + end. + +atom_create_from_string(S) -> + binary_to_atom(S, utf8). + +atom_to_string(S) -> + atom_to_binary(S, utf8). + +atom_from_string(S) -> + try {ok, binary_to_existing_atom(S, utf8)} + catch error:badarg -> {error, atom_not_loaded} + end. + +iodata_append(Iodata, String) -> [Iodata, String]. +iodata_prepend(Iodata, String) -> [String, Iodata]. + +identity(X) -> X. + +decode_error_msg(Type, Data) -> + {error, iolist_to_binary(io_lib:format("Expected ~s, got ~s", [Type, classify(Data)]))}. + +classify(X) when is_atom(X) -> "an atom"; +classify(X) when is_binary(X) -> "a binary"; +classify(X) when is_integer(X) -> "an int"; +classify(X) when is_float(X) -> "a float"; +classify(X) when is_list(X) -> "a list"; +classify(X) when is_boolean(X) -> "a bool"; +classify(X) when is_function(X, 0) -> "a zero arity function"; +classify(X) when is_tuple(X) -> ["a ", integer_to_list(tuple_size(X)), " element tuple"]; +classify(_) -> "some other type". + +decode_tuple2({_, _} = T) -> {ok, T}; +decode_tuple2(Data) -> decode_error_msg("a 2 element tuple", Data). + +decode_map(Data) when is_map(Data) -> {ok, Data}; +decode_map(Data) -> decode_error_msg("a map", Data). + +decode_atom(Data) when is_atom(Data) -> {ok, Data}; +decode_atom(Data) -> decode_error_msg("an atom", Data). + +decode_bit_string(Data) when is_bitstring(Data) -> {ok, Data}; +decode_bit_string(Data) -> decode_error_msg("a bit_string", Data). + +decode_int(Data) when is_integer(Data) -> {ok, Data}; +decode_int(Data) -> decode_error_msg("an int", Data). + +decode_float(Data) when is_float(Data) -> {ok, Data}; +decode_float(Data) -> decode_error_msg("a float", Data). + +decode_bool(Data) when is_boolean(Data) -> {ok, Data}; +decode_bool(Data) -> decode_error_msg("a bool", Data). + +decode_thunk(Data) when is_function(Data, 0) -> {ok, Data}; +decode_thunk(Data) -> decode_error_msg("a zero arity function", Data). + +decode_list(Data) when is_list(Data) -> {ok, Data}; +decode_list(Data) -> decode_error_msg("a list", Data). + +decode_field(Data, Key) -> + case Data of + #{Key := Value} -> + {ok, Value}; + + _ -> + decode_error_msg(io_lib:format("a map with key `~p`", [Key]), Data) + end. + +decode_element(Data, Position) when is_tuple(Data) -> + case catch element(Position + 1, Data) of + {'EXIT', _Reason} -> + decode_error_msg(["a tuple of at least ", integer_to_list(Position + 1), " size"], Data); + + Value -> + {ok, Value} + end; +decode_element(Data, _Position) -> decode_error_msg("a tuple", Data). + +parse_int(String) -> + case catch binary_to_integer(String) of + Int when is_integer(Int) -> {ok, Int}; + _ -> {error, nil} + end. + +parse_float(String) -> + case catch binary_to_float(String) of + Float when is_float(Float) -> {ok, Float}; + _ -> {error, nil} + end. + +compare_strings(Lhs, Rhs) -> + if + Lhs == Rhs -> eq; + Lhs < Rhs -> lt; + true -> gt + end. + +string_starts_with(_, <<>>) -> true; +string_starts_with(String, Prefix) when byte_size(Prefix) > byte_size(String) -> false; +string_starts_with(String, Prefix) -> + PrefixSize = byte_size(Prefix), + Prefix == binary_part(String, 0, PrefixSize). + +string_ends_with(_, <<>>) -> true; +string_ends_with(String, Suffix) when byte_size(Suffix) > byte_size(String) -> false; +string_ends_with(String, Suffix) -> + SuffixSize = byte_size(Suffix), + Suffix == binary_part(String, byte_size(String) - SuffixSize, SuffixSize). + +string_pad(String, Length, Dir, PadString) -> + unicode:characters_to_binary(string:pad(String, Length, Dir, PadString)). + +string_pop_grapheme(String) -> + case string:next_grapheme(String) of + [ Next | Rest ] -> + {ok, {unicode:characters_to_binary([Next]), unicode:characters_to_binary(Rest)}}; + _ -> {error, nil} + end. + +bit_string_append(First, Second) -> + <>. + +bit_string_part_(Bin, Pos, Len) -> + try {ok, binary:part(Bin, Pos, Len)} + catch error:badarg -> {error, nil} + end. + +bit_string_int_to_u32(I) when 0 =< I, I < 4294967296 -> + {ok, <>}; +bit_string_int_to_u32(_) -> + {error, nil}. + +bit_string_int_from_u32(<>) -> + {ok, I}; +bit_string_int_from_u32(_) -> + {error, nil}. + +compile_regex(String, Options) -> + {options, Caseless, Multiline} = Options, + OptionsList = [ + unicode, + Caseless andalso caseless, + Multiline andalso multiline + ], + FilteredOptions = [Option || Option <- OptionsList, Option /= false], + case re:compile(String, FilteredOptions) of + {ok, MP} -> {ok, MP}; + {error, {Str, Pos}} -> + {error, {compile_error, unicode:characters_to_binary(Str), Pos}} + end. + +regex_match(Regex, String) -> + re:run(String, Regex) /= nomatch. + +regex_split(Regex, String) -> + re:split(String, Regex). + +regex_submatches(String, {S, L}) -> + SubMatch = string:slice(String, S, L), + case string:is_empty(SubMatch) of + true -> none; + false -> {some, SubMatch} + end. + +regex_matches(String, [{S, L} | Submatches]) -> + {match, binary:part(String, S, L), S, + lists:map(fun(X) -> regex_submatches(String, X) end, Submatches)}. + +regex_scan(Regex, String) -> + case re:run(String, Regex, [global]) of + {match, Captured} -> lists:map(fun(X) -> regex_matches(String, X) end, Captured); + nomatch -> [] + end. + +base_decode64(S) -> + try {ok, base64:decode(S)} + catch error:badarith -> {error, nil} + end. + +wrap_list(X) when is_list(X) -> X; +wrap_list(X) -> [X]. + +rescue(F) -> + try {ok, F()} + catch + throw:X -> {error, {thrown, X}}; + error:X -> {error, {errored, X}}; + exit:X -> {error, {exited, X}} + end. diff --git a/gleam_providers/deps/mix_gleam/.fetch b/gleam_providers/deps/mix_gleam/.fetch new file mode 100644 index 0000000..e69de29 diff --git a/gleam_providers/deps/mix_gleam/.formatter.exs b/gleam_providers/deps/mix_gleam/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/gleam_providers/deps/mix_gleam/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/gleam_providers/deps/mix_gleam/.hex b/gleam_providers/deps/mix_gleam/.hex new file mode 100644 index 0000000..812dfc3 Binary files /dev/null and b/gleam_providers/deps/mix_gleam/.hex differ diff --git a/gleam_providers/deps/mix_gleam/README.md b/gleam_providers/deps/mix_gleam/README.md new file mode 100644 index 0000000..373b742 --- /dev/null +++ b/gleam_providers/deps/mix_gleam/README.md @@ -0,0 +1,49 @@ +# MixGleam + +A mix plugin for compiling Gleam code! + +## Installation + +Install the Gleam compiler onto your machine. [Installation instructions can +be found here here](https://gleam.run/getting-started/installing-gleam.html). + +Add `mix_gleam` to your mix project deps: + +```elixir +# in mix.exs +def deps do + [ + {:mix_gleam, "~> 0.1.0"}, + ] +end +``` + +Configure your mix project to add the Gleam compiler and to compile Erlang +that Gleam generates: + +```elixir +# in mix.exs +def project do + [ + # ... + erlc_paths: ["src", "gen"], + compilers: [:gleam | Mix.compilers()], # Gleam must go first + ] +end +``` + +Create a `gleam.toml` file containing the name of your OTP application: + +```toml +# In a new gleam.toml file +name = "my_cool_project" +``` + +Make a `src` directory for your Gleam code to live in: + +``` +mkdir src +``` + +And add `gen` to your `.gitignore` so that the generated Erlang is not +included in your project. diff --git a/gleam_providers/deps/mix_gleam/hex_metadata.config b/gleam_providers/deps/mix_gleam/hex_metadata.config new file mode 100644 index 0000000..6a106bb --- /dev/null +++ b/gleam_providers/deps/mix_gleam/hex_metadata.config @@ -0,0 +1,13 @@ +{<<"app">>,<<"mix_gleam">>}. +{<<"build_tools">>,[<<"mix">>]}. +{<<"description">>,<<"Compile Gleam code with mix">>}. +{<<"elixir">>,<<"~> 1.9">>}. +{<<"files">>, + [<<"lib">>,<<"lib/mix">>,<<"lib/mix/tasks">>,<<"lib/mix/tasks/compiler">>, + <<"lib/mix/tasks/compiler/gleam.ex">>,<<"lib/mix/compiler">>, + <<".formatter.exs">>,<<"mix.exs">>,<<"README.md">>]}. +{<<"licenses">>,[<<"Apache-2.0">>]}. +{<<"links">>,[{<<"GitHub">>,<<"https://github.com/gleam-lang/mix_gleam">>}]}. +{<<"name">>,<<"mix_gleam">>}. +{<<"requirements">>,[]}. +{<<"version">>,<<"0.1.0">>}. diff --git a/gleam_providers/deps/mix_gleam/lib/mix/tasks/compiler/gleam.ex b/gleam_providers/deps/mix_gleam/lib/mix/tasks/compiler/gleam.ex new file mode 100644 index 0000000..bbaad3a --- /dev/null +++ b/gleam_providers/deps/mix_gleam/lib/mix/tasks/compiler/gleam.ex @@ -0,0 +1,10 @@ +defmodule Mix.Tasks.Compile.Gleam do + use Mix.Task.Compiler + + def run(_args) do + case Mix.shell().cmd("gleam build .") do + 0 -> {:ok, []} + status -> exit(status) + end + end +end diff --git a/gleam_providers/deps/mix_gleam/mix.exs b/gleam_providers/deps/mix_gleam/mix.exs new file mode 100644 index 0000000..1f6781c --- /dev/null +++ b/gleam_providers/deps/mix_gleam/mix.exs @@ -0,0 +1,32 @@ +defmodule MixGleam.MixProject do + use Mix.Project + + def project do + [ + app: :mix_gleam, + version: "0.1.0", + elixir: "~> 1.9", + start_permanent: Mix.env() == :prod, + name: "mix_gleam", + description: "Compile Gleam code with mix", + deps: deps(), + package: [ + maintainers: ["Louis Pilfold"], + licenses: ["Apache-2.0"], + links: %{"GitHub" => "https://github.com/gleam-lang/mix_gleam"} + ] + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [ + {:ex_doc, ">= 0.0.0", only: :dev, runtime: false} + ] + end +end diff --git a/gleam_providers/gen/src/gleam@providers@csv.erl b/gleam_providers/gen/src/gleam@providers@csv.erl new file mode 100644 index 0000000..079f50c --- /dev/null +++ b/gleam_providers/gen/src/gleam@providers@csv.erl @@ -0,0 +1,149 @@ +-module(gleam@providers@csv). +-compile(no_auto_import). + +-export([extension/0, provide/1]). + +extension() -> + <<".schema.env"/utf8>>. + +render(Specs) -> + gleam@string:replace( + gleam@string:replace( + gleam@string:replace( + <<" +import gleam/map +import gleam/option.{Option} +import gleam/os +import gleam/result + +pub type Env{ + Env( + RECORD_FIELDS + ) +} + +pub fn from_env() { + let raw = os.get_env() + cast_env(raw) +} + +pub fn cast_env(raw) { + ENV_CHECKS + Ok(Env(RECORD_KEYS)) +} +"/utf8>>, + <<"RECORD_FIELDS"/utf8>>, + record_fields(Specs) + ), + <<"ENV_CHECKS"/utf8>>, + env_checks(Specs) + ), + <<"RECORD_KEYS"/utf8>>, + record_keys(Specs) + ). + +record_fields(Specs) -> + gleam@string:join( + gleam@list:map(Specs, fun record_field/1), + <<",\r\n"/utf8>> + ). + +record_field(Spec) -> + {Key, Spec@1} = Spec, + case Spec@1 of + required -> + gleam@string:concat( + [gleam@string:lowercase(Key), <<": String"/utf8>>] + ); + + {fallback, _} -> + gleam@string:concat( + [gleam@string:lowercase(Key), <<": String"/utf8>>] + ); + + optional -> + gleam@string:concat( + [gleam@string:lowercase(Key), <<": Option(String)"/utf8>>] + ) + end. + +env_checks(Specs) -> + gleam@string:join(gleam@list:map(Specs, fun env_check/1), <<"\r\n"/utf8>>). + +env_check(Spec) -> + case Spec of + {Key, required} -> + gleam@string:replace( + gleam@string:replace( + <<"try RECORD_KEY = map.get(raw, \"ENV_KEY\")"/utf8>>, + <<"RECORD_KEY"/utf8>>, + gleam@string:lowercase(Key) + ), + <<"ENV_KEY"/utf8>>, + Key + ); + + {Key@1, optional} -> + gleam@string:replace( + gleam@string:replace( + <<"let RECORD_KEY = map.get(raw, \"ENV_KEY\") |> option.from_result"/utf8>>, + <<"RECORD_KEY"/utf8>>, + gleam@string:lowercase(Key@1) + ), + <<"ENV_KEY"/utf8>>, + Key@1 + ); + + {Key@2, {fallback, Fallback}} -> + gleam@string:replace( + gleam@string:replace( + gleam@string:replace( + <<"let RECORD_KEY = map.get(raw, \"ENV_KEY\") |> result.unwrap(\"FALLBACK\")"/utf8>>, + <<"RECORD_KEY"/utf8>>, + gleam@string:lowercase(Key@2) + ), + <<"ENV_KEY"/utf8>>, + Key@2 + ), + <<"FALLBACK"/utf8>>, + Fallback + ) + end. + +record_keys(Specs) -> + gleam@string:join( + gleam@list:map( + Specs, + fun(Spec) -> gleam@string:lowercase(erlang:element(1, Spec)) end + ), + <<", "/utf8>> + ). + +provide(Raw) -> + render( + gleam@list:map( + gleam@list:filter( + gleam@list:map( + gleam@string:split(Raw, <<"\n"/utf8>>), + fun gleam@string:trim/1 + ), + fun non_empty/1 + ), + fun parse_spec/1 + ) + ). + +non_empty(Line) -> + <<""/utf8>> /= Line. + +parse_spec(Line) -> + case gleam@string:split_once(Line, <<"?"/utf8>>) of + {ok, {Key, <<""/utf8>>}} -> + {Key, optional}; + + {ok, {Key@1, Fallback}} -> + {Key@1, {fallback, Fallback}}; + + {error, nil} -> + {Line, required} + end. diff --git a/mix.exs b/mix.exs index 943133d..6e8e01e 100644 --- a/mix.exs +++ b/mix.exs @@ -8,7 +8,7 @@ defmodule PlumMail.MixProject do elixir: "~> 1.10", start_permanent: Mix.env() == :prod, erlc_paths: ["src", "gen"], - compilers: [:gleam | Mix.compilers()], + compilers: [:gleam_providers, :gleam | Mix.compilers()], deps: deps() ] end @@ -24,16 +24,18 @@ defmodule PlumMail.MixProject do defp deps do [ {:mix_gleam, "~> 0.1.0"}, - {:gleam_stdlib, "~> 0.12.0", override: true}, - {:gleam_cowboy, "~> 0.1.2"}, - {:gleam_http, "~> 1.6"}, - {:gleam_httpc, "~> 0.1.1"}, + {:gleam_stdlib, "~> 0.13.0", override: true}, + {:gleam_cowboy, "~> 0.2.2"}, + {:gleam_http, "~> 1.7"}, + {:gleam_httpc, "~> 1.0"}, {:gleam_json, "~> 0.1.0"}, {:floki, github: "midas-framework/floki", tag: "4bae91f3129fbf517aae084695db5671eb115931", manager: :mix, - override: true} + override: true}, + {:gleam_providers, path: "./gleam_providers"} + ] end end diff --git a/mix.lock b/mix.lock index e295e05..3c4f740 100644 --- a/mix.lock +++ b/mix.lock @@ -2,12 +2,12 @@ "cowboy": {:hex, :cowboy, "2.8.0", "f3dc62e35797ecd9ac1b50db74611193c29815401e53bac9a5c0577bd7bc667d", [:rebar3], [{:cowlib, "~> 2.9.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "4643e4fba74ac96d4d152c75803de6fad0b3fa5df354c71afdd6cbeeb15fac8a"}, "cowlib": {:hex, :cowlib, "2.9.1", "61a6c7c50cf07fdd24b2f45b89500bb93b6686579b069a89f88cb211e1125c78", [:rebar3], [], "hexpm", "e4175dc240a70d996156160891e1c62238ede1729e45740bdd38064dad476170"}, "floki": {:git, "https://github.com/midas-framework/floki.git", "4bae91f3129fbf517aae084695db5671eb115931", [tag: "4bae91f3129fbf517aae084695db5671eb115931"]}, - "gleam_cowboy": {:hex, :gleam_cowboy, "0.1.2", "63ed10783989868ac8a5276feaa00de90e171a0c909b0ef955dce1d99633848b", [:rebar3], [{:cowboy, "~> 2.8", [hex: :cowboy, repo: "hexpm", optional: false]}, {:gleam_http, "~> 1.3", [hex: :gleam_http, repo: "hexpm", optional: false]}, {:gleam_otp, "~> 0.0", [hex: :gleam_otp, repo: "hexpm", optional: false]}, {:gleam_stdlib, "~> 0.10", [hex: :gleam_stdlib, repo: "hexpm", optional: false]}], "hexpm", "a6c5f7fc11e9e2e88a20b16db495d7f55f9aa68d5b9a88995ebd42c745225b3f"}, - "gleam_http": {:hex, :gleam_http, "1.6.0", "7d37b0287ea47a47987da5fd7cadb75db9c96c3192d7c28c2182cd793496784f", [:rebar3], [{:gleam_stdlib, "~> 0.12", [hex: :gleam_stdlib, repo: "hexpm", optional: false]}], "hexpm", "1dd7e0f0103de27b1f5f985c0b146446c9d2726ef3b3525e8765d1e02fbe3926"}, - "gleam_httpc": {:hex, :gleam_httpc, "0.1.1", "4f043d1cd5cabc0a698dc38458ef590a4295df67ad87d20ea8d6d15f66cc8124", [:rebar3], [{:gleam_http, "~> 1.3", [hex: :gleam_http, repo: "hexpm", optional: false]}, {:gleam_stdlib, "~> 0.10.1", [hex: :gleam_stdlib, repo: "hexpm", optional: false]}], "hexpm", "429afba1594e94c76096cffd75e0b2bd8d7ad9e8cf13a9120bceb4cd4e801fa2"}, + "gleam_cowboy": {:hex, :gleam_cowboy, "0.2.2", "31ba888576631fe382fddef9625444c43815eab50496186e818a9f5d3baaa303", [:rebar3], [{:cowboy, "~> 2.8", [hex: :cowboy, repo: "hexpm", optional: false]}, {:gleam_http, "~> 1.6", [hex: :gleam_http, repo: "hexpm", optional: false]}, {:gleam_otp, "~> 0.1", [hex: :gleam_otp, repo: "hexpm", optional: false]}, {:gleam_stdlib, "~> 0.12", [hex: :gleam_stdlib, repo: "hexpm", optional: false]}], "hexpm", "995e18ac5a3676de0a972885abc1de1a1429dae1209ef514a8ae0b190587dc2a"}, + "gleam_http": {:hex, :gleam_http, "1.7.0", "ea3dac4ffcad0641986a43d5cf065c43cfa97149f41618833e8dae3b9acc01d3", [:rebar3], [{:gleam_stdlib, "~> 0.12", [hex: :gleam_stdlib, repo: "hexpm", optional: false]}], "hexpm", "093cb3cde4f21157e0d1eae37c7156eaae0d5e32a65d67adcf914cbeb2ab0e17"}, + "gleam_httpc": {:hex, :gleam_httpc, "1.0.0", "46918bcea0d66d536ad57a9eb51eae86205f2c965edabf45a799ca103e855b9c", [:rebar3], [{:gleam_http, "~> 1.3", [hex: :gleam_http, repo: "hexpm", optional: false]}, {:gleam_stdlib, "~> 0.12", [hex: :gleam_stdlib, repo: "hexpm", optional: false]}], "hexpm", "f4b27e6c18e73e4e3cc62f0c049c5b8c8a7d624989ce79820af4e6c517ee4e83"}, "gleam_json": {:hex, :gleam_json, "0.1.0", "8740eb85e079f38a15f2627d53c306491d90c43c423aae56a4abc371319cc346", [:rebar3], [{:gleam_stdlib, "0.10.1", [hex: :gleam_stdlib, repo: "hexpm", optional: false]}, {:jsone, "1.5.2", [hex: :jsone, repo: "hexpm", optional: false]}], "hexpm", "8c6f144bc4f2ddcb0347a84c4dee134bd771a8a7c95c135a31d373d5ea95652b"}, - "gleam_otp": {:hex, :gleam_otp, "0.1.3", "1501f9f838b254b854b7d0894113790a444096eccabc2f7010f56c643aeb7d96", [:rebar3], [{:gleam_stdlib, "~> 0.11", [hex: :gleam_stdlib, repo: "hexpm", optional: false]}], "hexpm", "4c914e4c73e311d546e4bc17812a3a6acdf49c68750ce2ccdf98f93da742ccb6"}, - "gleam_stdlib": {:hex, :gleam_stdlib, "0.12.0", "722b00f9a08e44d9582922b4ef6e890ca60e2caebefe81192f3cc8d337f2f0cf", [:rebar3], [], "hexpm", "c3435f25629ed5f2c0d53a1bc2fdf89acdffa75f036cb27396c1ef70396f644a"}, + "gleam_otp": {:hex, :gleam_otp, "0.1.4", "68adb5a9ede026804af8cb7d3826859094529cd7b6da4ac5fc6bec7b53a62a80", [:rebar3], [{:gleam_stdlib, "~> 0.11", [hex: :gleam_stdlib, repo: "hexpm", optional: false]}], "hexpm", "c36b2709e1703ce8582cdc4058e332a9bf8a2a4517e1813f0c761355c83e924b"}, + "gleam_stdlib": {:hex, :gleam_stdlib, "0.13.0", "604a40e0fbe6c688651a5ad5e892913ed314ab626d18d361a7fd8bf367907551", [:rebar3], [], "hexpm", "35a005f4daca2775687e46f4e20cec799563a698a16f8bcc0cf281522ad717f1"}, "html_entities": {:hex, :html_entities, "0.5.1", "1c9715058b42c35a2ab65edc5b36d0ea66dd083767bef6e3edb57870ef556549", [:mix], [], "hexpm", "30efab070904eb897ff05cd52fa61c1025d7f8ef3a9ca250bc4e6513d16c32de"}, "jsone": {:hex, :jsone, "1.5.2", "87adea283c9cf24767b4deed44602989a5331156df5d60a2660e9c9114d54046", [:rebar3], [], "hexpm", "170c171ce7f6dd70c858065154a3305b8564833c6dcca17e10b676ca31ea976f"}, "mix_gleam": {:hex, :mix_gleam, "0.1.0", "a0cee5d30de865124a32ca6cd53b64c3e2ac57f12adf6e47b88fb673f47c716e", [:mix], [], "hexpm", "9ff518e6aab444c7f2e74038f9383020ef89810cf1f4402911f33b202ffd72e7"}, diff --git a/src/glance/config.schema.env b/src/glance/config.schema.env new file mode 100644 index 0000000..0a05959 --- /dev/null +++ b/src/glance/config.schema.env @@ -0,0 +1,3 @@ +FOO? +BAR? +BAZ?hello world diff --git a/src/glance/strategy/fallback.gleam b/src/glance/strategy/fallback.gleam index 5d1c40a..1430bc7 100644 --- a/src/glance/strategy/fallback.gleam +++ b/src/glance/strategy/fallback.gleam @@ -4,7 +4,7 @@ import gleam/list import gleam/option.{Some} import gleam/result.{unwrap} import gleam/uri.{Uri} -import gleam/http.{Response} +import gleam/http import gleam/httpc import floki import glance/preview.{Image, Page} diff --git a/src/glance/web/router.gleam b/src/glance/web/router.gleam index 386db80..9057c59 100644 --- a/src/glance/web/router.gleam +++ b/src/glance/web/router.gleam @@ -3,12 +3,14 @@ import gleam/bit_string.{BitString} import gleam/io import gleam/list import gleam/option.{Some} +import gleam/string import gleam/uri import gleam/http.{Request, Response} import gleam/httpc import gleam/json import floki import glance +import glance/config.{Env} pub fn set_resp_json(response, data) { let body = @@ -23,6 +25,11 @@ pub fn set_resp_json(response, data) { } pub fn handle(request: Request(BitString), config: Nil) -> Response(BitBuilder) { + let Ok(Env(foo, bar, baz)) = config.from_env() + io.debug(foo) + io.debug(bar) + io.debug(baz) + string.concat([option.unwrap(foo, ""), option.unwrap(bar, ""), baz]) case request.method { http.Options -> http.response(200)