From a6272848f91d484c1c78949142ddc77056a026ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20M=C3=A5rdbrink?= Date: Sun, 30 Nov 2025 15:44:22 +0100 Subject: [PATCH] Initial commit --- Makefile | 12 + README.md | 12 + .../gleam_community@colour.cache | Bin 0 -> 57501 bytes .../gleam_community@colour.cache_inline | Bin 0 -> 8 bytes .../gleam_community@colour.cache_meta | Bin 0 -> 5305 bytes ...gleam_community@colour@accessibility.cache | Bin 0 -> 7219 bytes ...ommunity@colour@accessibility.cache_inline | Bin 0 -> 8 bytes ..._community@colour@accessibility.cache_meta | Bin 0 -> 876 bytes .../gleam_community_colour/gleam.mjs | 1 + .../gleam_community/colour.mjs | 1444 ++++++++ .../gleam_community/colour/accessibility.mjs | 146 + .../gleam_community@colour.erl | 1199 ++++++ .../gleam_community@colour@accessibility.erl | 203 ++ .../_gleam_artefacts/gleam@json.cache | Bin 0 -> 18027 bytes .../_gleam_artefacts/gleam@json.cache_inline | Bin 0 -> 8 bytes .../_gleam_artefacts/gleam@json.cache_meta | Bin 0 -> 1554 bytes build/dev/javascript/gleam_json/gleam.mjs | 1 + .../dev/javascript/gleam_json/gleam/json.mjs | 325 ++ .../dev/javascript/gleam_json/gleam@json.erl | 304 ++ .../javascript/gleam_json/gleam_json_ffi.erl | 66 + .../javascript/gleam_json/gleam_json_ffi.mjs | 201 ++ .../_gleam_artefacts/gleam@bit_array.cache | Bin 0 -> 14783 bytes .../gleam@bit_array.cache_inline | Bin 0 -> 8 bytes .../gleam@bit_array.cache_meta | Bin 0 -> 1253 bytes .../_gleam_artefacts/gleam@bool.cache | Bin 0 -> 8858 bytes .../_gleam_artefacts/gleam@bool.cache_inline | Bin 0 -> 1449 bytes .../_gleam_artefacts/gleam@bool.cache_meta | Bin 0 -> 1317 bytes .../_gleam_artefacts/gleam@bytes_tree.cache | Bin 0 -> 13269 bytes .../gleam@bytes_tree.cache_inline | Bin 0 -> 8 bytes .../gleam@bytes_tree.cache_meta | Bin 0 -> 903 bytes .../_gleam_artefacts/gleam@dict.cache | Bin 0 -> 24410 bytes .../_gleam_artefacts/gleam@dict.cache_inline | Bin 0 -> 8 bytes .../_gleam_artefacts/gleam@dict.cache_meta | Bin 0 -> 2273 bytes .../_gleam_artefacts/gleam@dynamic.cache | Bin 0 -> 7137 bytes .../gleam@dynamic.cache_inline | Bin 0 -> 8 bytes .../_gleam_artefacts/gleam@dynamic.cache_meta | Bin 0 -> 479 bytes .../gleam@dynamic@decode.cache | Bin 0 -> 58909 bytes .../gleam@dynamic@decode.cache_inline | Bin 0 -> 8 bytes .../gleam@dynamic@decode.cache_meta | Bin 0 -> 4482 bytes .../_gleam_artefacts/gleam@float.cache | Bin 0 -> 23692 bytes .../_gleam_artefacts/gleam@float.cache_inline | Bin 0 -> 8 bytes .../_gleam_artefacts/gleam@float.cache_meta | Bin 0 -> 2724 bytes .../_gleam_artefacts/gleam@function.cache | Bin 0 -> 1104 bytes .../gleam@function.cache_inline | Bin 0 -> 8 bytes .../gleam@function.cache_meta | Bin 0 -> 113 bytes .../_gleam_artefacts/gleam@int.cache | Bin 0 -> 29916 bytes .../_gleam_artefacts/gleam@int.cache_inline | Bin 0 -> 8 bytes .../_gleam_artefacts/gleam@int.cache_meta | Bin 0 -> 3419 bytes .../_gleam_artefacts/gleam@io.cache | Bin 0 -> 2697 bytes .../_gleam_artefacts/gleam@io.cache_inline | Bin 0 -> 8 bytes .../_gleam_artefacts/gleam@io.cache_meta | Bin 0 -> 289 bytes .../_gleam_artefacts/gleam@list.cache | Bin 0 -> 88736 bytes .../_gleam_artefacts/gleam@list.cache_inline | Bin 0 -> 8 bytes .../_gleam_artefacts/gleam@list.cache_meta | Bin 0 -> 9862 bytes .../_gleam_artefacts/gleam@option.cache | Bin 0 -> 13699 bytes .../gleam@option.cache_inline | Bin 0 -> 8 bytes .../_gleam_artefacts/gleam@option.cache_meta | Bin 0 -> 1485 bytes .../_gleam_artefacts/gleam@order.cache | Bin 0 -> 5915 bytes .../_gleam_artefacts/gleam@order.cache_inline | Bin 0 -> 8 bytes .../_gleam_artefacts/gleam@order.cache_meta | Bin 0 -> 677 bytes .../_gleam_artefacts/gleam@pair.cache | Bin 0 -> 2776 bytes .../_gleam_artefacts/gleam@pair.cache_inline | Bin 0 -> 8 bytes .../_gleam_artefacts/gleam@pair.cache_meta | Bin 0 -> 403 bytes .../_gleam_artefacts/gleam@result.cache | Bin 0 -> 15524 bytes .../gleam@result.cache_inline | Bin 0 -> 3168 bytes .../_gleam_artefacts/gleam@result.cache_meta | Bin 0 -> 1891 bytes .../_gleam_artefacts/gleam@set.cache | Bin 0 -> 16946 bytes .../_gleam_artefacts/gleam@set.cache_inline | Bin 0 -> 8 bytes .../_gleam_artefacts/gleam@set.cache_meta | Bin 0 -> 1761 bytes .../_gleam_artefacts/gleam@string.cache | Bin 0 -> 38365 bytes .../gleam@string.cache_inline | Bin 0 -> 8 bytes .../_gleam_artefacts/gleam@string.cache_meta | Bin 0 -> 4057 bytes .../_gleam_artefacts/gleam@string_tree.cache | Bin 0 -> 14554 bytes .../gleam@string_tree.cache_inline | Bin 0 -> 8 bytes .../gleam@string_tree.cache_meta | Bin 0 -> 911 bytes .../_gleam_artefacts/gleam@uri.cache | Bin 0 -> 32735 bytes .../_gleam_artefacts/gleam@uri.cache_inline | Bin 0 -> 8 bytes .../_gleam_artefacts/gleam@uri.cache_meta | Bin 0 -> 3273 bytes build/dev/javascript/gleam_stdlib/dict.mjs | 993 +++++ build/dev/javascript/gleam_stdlib/gleam.mjs | 1 + .../gleam_stdlib/gleam/bit_array.mjs | 285 ++ .../javascript/gleam_stdlib/gleam/bool.mjs | 313 ++ .../gleam_stdlib/gleam/bytes_tree.mjs | 225 ++ .../javascript/gleam_stdlib/gleam/dict.mjs | 534 +++ .../javascript/gleam_stdlib/gleam/dynamic.mjs | 35 + .../gleam_stdlib/gleam/dynamic/decode.mjs | 947 +++++ .../javascript/gleam_stdlib/gleam/float.mjs | 553 +++ .../gleam_stdlib/gleam/function.mjs | 17 + .../dev/javascript/gleam_stdlib/gleam/int.mjs | 816 +++++ .../dev/javascript/gleam_stdlib/gleam/io.mjs | 8 + .../javascript/gleam_stdlib/gleam/list.mjs | 3209 +++++++++++++++++ .../javascript/gleam_stdlib/gleam/option.mjs | 419 +++ .../javascript/gleam_stdlib/gleam/order.mjs | 178 + .../javascript/gleam_stdlib/gleam/pair.mjs | 102 + .../javascript/gleam_stdlib/gleam/result.mjs | 494 +++ .../dev/javascript/gleam_stdlib/gleam/set.mjs | 412 +++ .../javascript/gleam_stdlib/gleam/string.mjs | 723 ++++ .../gleam_stdlib/gleam/string_tree.mjs | 133 + .../dev/javascript/gleam_stdlib/gleam/uri.mjs | 1147 ++++++ .../gleam_stdlib/gleam@bit_array.erl | 347 ++ .../javascript/gleam_stdlib/gleam@bool.erl | 352 ++ .../gleam_stdlib/gleam@bytes_tree.erl | 211 ++ .../javascript/gleam_stdlib/gleam@dict.erl | 561 +++ .../javascript/gleam_stdlib/gleam@dynamic.erl | 106 + .../gleam_stdlib/gleam@dynamic@decode.erl | 1088 ++++++ .../javascript/gleam_stdlib/gleam@float.erl | 744 ++++ .../gleam_stdlib/gleam@function.erl | 30 + .../dev/javascript/gleam_stdlib/gleam@int.erl | 986 +++++ .../dev/javascript/gleam_stdlib/gleam@io.erl | 80 + .../javascript/gleam_stdlib/gleam@list.erl | 2873 +++++++++++++++ .../javascript/gleam_stdlib/gleam@option.erl | 413 +++ .../javascript/gleam_stdlib/gleam@order.erl | 200 + .../javascript/gleam_stdlib/gleam@pair.erl | 110 + .../javascript/gleam_stdlib/gleam@result.erl | 550 +++ .../dev/javascript/gleam_stdlib/gleam@set.erl | 429 +++ .../javascript/gleam_stdlib/gleam@string.erl | 1012 ++++++ .../gleam_stdlib/gleam@string_tree.erl | 207 ++ .../dev/javascript/gleam_stdlib/gleam@uri.erl | 1044 ++++++ .../javascript/gleam_stdlib/gleam_stdlib.erl | 534 +++ .../javascript/gleam_stdlib/gleam_stdlib.mjs | 1048 ++++++ .../gleam@time@calendar.cache | Bin 0 -> 21304 bytes .../gleam@time@calendar.cache_inline | Bin 0 -> 8 bytes .../gleam@time@calendar.cache_meta | Bin 0 -> 1524 bytes .../gleam@time@duration.cache | Bin 0 -> 15819 bytes .../gleam@time@duration.cache_inline | Bin 0 -> 8 bytes .../gleam@time@duration.cache_meta | Bin 0 -> 1347 bytes .../gleam@time@timestamp.cache | Bin 0 -> 46587 bytes .../gleam@time@timestamp.cache_inline | Bin 0 -> 8 bytes .../gleam@time@timestamp.cache_meta | Bin 0 -> 3911 bytes build/dev/javascript/gleam_time/gleam.mjs | 1 + .../gleam_time/gleam/time/calendar.mjs | 391 ++ .../gleam_time/gleam/time/duration.mjs | 382 ++ .../gleam_time/gleam/time/timestamp.mjs | 1215 +++++++ .../gleam_time/gleam@time@calendar.erl | 468 +++ .../gleam_time/gleam@time@duration.erl | 381 ++ .../gleam_time/gleam@time@timestamp.erl | 1188 ++++++ .../javascript/gleam_time/gleam_time_ffi.erl | 12 + .../javascript/gleam_time/gleam_time_ffi.mjs | 11 + build/dev/javascript/gleam_version | 1 + .../gleeunit/_gleam_artefacts/gleeunit.cache | Bin 0 -> 8472 bytes .../_gleam_artefacts/gleeunit.cache_inline | Bin 0 -> 8 bytes .../_gleam_artefacts/gleeunit.cache_meta | Bin 0 -> 479 bytes .../gleeunit@internal@gleam_panic.cache | Bin 0 -> 14778 bytes ...gleeunit@internal@gleam_panic.cache_inline | Bin 0 -> 8 bytes .../gleeunit@internal@gleam_panic.cache_meta | Bin 0 -> 278 bytes .../gleeunit@internal@reporting.cache | Bin 0 -> 14281 bytes .../gleeunit@internal@reporting.cache_inline | Bin 0 -> 8 bytes .../gleeunit@internal@reporting.cache_meta | Bin 0 -> 1277 bytes .../_gleam_artefacts/gleeunit@should.cache | Bin 0 -> 3853 bytes .../gleeunit@should.cache_inline | Bin 0 -> 8 bytes .../gleeunit@should.cache_meta | Bin 0 -> 397 bytes build/dev/javascript/gleeunit/gleam.mjs | 1 + build/dev/javascript/gleeunit/gleeunit.erl | 89 + build/dev/javascript/gleeunit/gleeunit.mjs | 62 + .../gleeunit/internal/gleam_panic.mjs | 180 + .../internal/gleeunit_gleam_panic_ffi.erl | 49 + .../internal/gleeunit_gleam_panic_ffi.mjs | 91 + .../gleeunit/gleeunit/internal/reporting.mjs | 256 ++ .../javascript/gleeunit/gleeunit/should.mjs | 135 + .../gleeunit@internal@gleam_panic.erl | 56 + .../gleeunit/gleeunit@internal@reporting.erl | 343 ++ .../javascript/gleeunit/gleeunit@should.erl | 153 + .../dev/javascript/gleeunit/gleeunit_ffi.erl | 21 + .../dev/javascript/gleeunit/gleeunit_ffi.mjs | 100 + .../javascript/gleeunit/gleeunit_progress.erl | 72 + .../paint/_gleam_artefacts/paint.cache | Bin 0 -> 18173 bytes .../paint/_gleam_artefacts/paint.cache_inline | Bin 0 -> 8 bytes .../paint/_gleam_artefacts/paint.cache_meta | Bin 0 -> 969 bytes .../paint/_gleam_artefacts/paint@canvas.cache | Bin 0 -> 20428 bytes .../paint@canvas.cache_inline | Bin 0 -> 8 bytes .../_gleam_artefacts/paint@canvas.cache_meta | Bin 0 -> 2142 bytes .../paint/_gleam_artefacts/paint@encode.cache | Bin 0 -> 10664 bytes .../paint@encode.cache_inline | Bin 0 -> 8 bytes .../_gleam_artefacts/paint@encode.cache_meta | Bin 0 -> 1250 bytes .../paint/_gleam_artefacts/paint@event.cache | Bin 0 -> 9101 bytes .../_gleam_artefacts/paint@event.cache_inline | Bin 0 -> 8 bytes .../_gleam_artefacts/paint@event.cache_meta | Bin 0 -> 253 bytes .../paint@internal@impl_canvas.cache | Bin 0 -> 16937 bytes .../paint@internal@impl_canvas.cache_inline | Bin 0 -> 8 bytes .../paint@internal@impl_canvas.cache_meta | Bin 0 -> 525 bytes .../paint@internal@types.cache | Bin 0 -> 14729 bytes .../paint@internal@types.cache_inline | Bin 0 -> 8 bytes .../paint@internal@types.cache_meta | Bin 0 -> 283 bytes build/dev/javascript/paint/gleam.mjs | 1 + .../javascript/paint/impl_canvas_bindings.mjs | 271 ++ build/dev/javascript/paint/numbers_ffi.mjs | 3 + build/dev/javascript/paint/paint.mjs | 246 ++ build/dev/javascript/paint/paint/canvas.mjs | 655 ++++ build/dev/javascript/paint/paint/encode.mjs | 506 +++ build/dev/javascript/paint/paint/event.mjs | 163 + .../paint/paint/internal/impl_canvas.mjs | 65 + .../javascript/paint/paint/internal/types.mjs | 217 ++ build/dev/javascript/prelude.mjs | 1575 ++++++++ .../stellar_prune/_gleam_artefacts/game.cache | Bin 0 -> 14636 bytes .../_gleam_artefacts/game.cache_inline | Bin 0 -> 8 bytes .../_gleam_artefacts/game.cache_meta | Bin 0 -> 1262 bytes .../_gleam_artefacts/game.cache_warnings | Bin 0 -> 8 bytes .../stellar_prune/_gleam_artefacts/hud.cache | Bin 0 -> 9498 bytes .../_gleam_artefacts/hud.cache_inline | Bin 0 -> 8 bytes .../_gleam_artefacts/hud.cache_meta | Bin 0 -> 491 bytes .../_gleam_artefacts/hud.cache_warnings | Bin 0 -> 8 bytes .../_gleam_artefacts/math@math.cache | Bin 0 -> 2990 bytes .../_gleam_artefacts/math@math.cache_inline | Bin 0 -> 8 bytes .../_gleam_artefacts/math@math.cache_meta | Bin 0 -> 165 bytes .../_gleam_artefacts/math@math.cache_warnings | Bin 0 -> 8 bytes .../_gleam_artefacts/physics.cache | Bin 0 -> 5550 bytes .../_gleam_artefacts/physics.cache_inline | Bin 0 -> 8 bytes .../_gleam_artefacts/physics.cache_meta | Bin 0 -> 149 bytes .../_gleam_artefacts/physics.cache_warnings | Bin 0 -> 8 bytes .../_gleam_artefacts/rendering.cache | Bin 0 -> 4103 bytes .../_gleam_artefacts/rendering.cache_inline | Bin 0 -> 8 bytes .../_gleam_artefacts/rendering.cache_meta | Bin 0 -> 643 bytes .../_gleam_artefacts/rendering.cache_warnings | Bin 0 -> 8 bytes .../_gleam_artefacts/target.cache | Bin 0 -> 10406 bytes .../_gleam_artefacts/target.cache_inline | Bin 0 -> 8 bytes .../_gleam_artefacts/target.cache_meta | Bin 0 -> 679 bytes .../_gleam_artefacts/target.cache_warnings | Bin 0 -> 8 bytes .../_gleam_artefacts/types.cache | Bin 0 -> 5139 bytes .../_gleam_artefacts/types.cache_inline | Bin 0 -> 8 bytes .../_gleam_artefacts/types.cache_meta | Bin 0 -> 193 bytes .../_gleam_artefacts/types.cache_warnings | Bin 0 -> 8 bytes .../javascript/stellar_prune/browser_ffi.mjs | 40 + build/dev/javascript/stellar_prune/game.mjs | 325 ++ build/dev/javascript/stellar_prune/gleam.mjs | 1 + build/dev/javascript/stellar_prune/hud.mjs | 153 + .../javascript/stellar_prune/math/math.mjs | 3 + .../stellar_prune/math/math_ffi.mjs | 35 + .../dev/javascript/stellar_prune/physics.mjs | 63 + .../javascript/stellar_prune/rendering.mjs | 155 + build/dev/javascript/stellar_prune/target.mjs | 194 + build/dev/javascript/stellar_prune/types.mjs | 66 + build/gleam-dev-erlang.lock | 0 build/gleam-dev-javascript.lock | 0 build/gleam-lsp-erlang.lock | 0 build/gleam-lsp-javascript.lock | 0 build/gleam-prod-erlang.lock | 0 build/gleam-prod-javascript.lock | 0 build/packages/gleam.lock | 0 build/packages/gleam_community_colour/LICENCE | 190 + .../packages/gleam_community_colour/README.md | 36 + .../gleam_community_colour/gleam.toml | 13 + .../include/gleam_community@colour_Hsla.hrl | 1 + .../include/gleam_community@colour_Rgba.hrl | 1 + .../src/gleam_community/colour.gleam | 1214 +++++++ .../colour/accessibility.gleam | 173 + .../src/gleam_community@colour.erl | 1199 ++++++ .../gleam_community@colour@accessibility.erl | 203 ++ .../src/gleam_community_colour.app.src | 10 + build/packages/gleam_json/LICENCE | 191 + build/packages/gleam_json/README.md | 47 + build/packages/gleam_json/gleam.toml | 18 + .../packages/gleam_json/src/gleam/json.gleam | 316 ++ build/packages/gleam_json/src/gleam@json.erl | 304 ++ .../gleam_json/src/gleam_json.app.src | 9 + .../gleam_json/src/gleam_json_ffi.erl | 66 + .../gleam_json/src/gleam_json_ffi.mjs | 201 ++ build/packages/gleam_stdlib/LICENCE | 191 + build/packages/gleam_stdlib/README.md | 34 + build/packages/gleam_stdlib/gleam.toml | 14 + .../gleam@dynamic@decode_DecodeError.hrl | 5 + .../include/gleam@dynamic@decode_Decoder.hrl | 4 + .../gleam_stdlib/include/gleam@set_Set.hrl | 1 + .../gleam_stdlib/include/gleam@uri_Uri.hrl | 9 + build/packages/gleam_stdlib/src/dict.mjs | 993 +++++ .../gleam_stdlib/src/gleam/bit_array.gleam | 280 ++ .../gleam_stdlib/src/gleam/bool.gleam | 316 ++ .../gleam_stdlib/src/gleam/bytes_tree.gleam | 190 + .../gleam_stdlib/src/gleam/dict.gleam | 548 +++ .../gleam_stdlib/src/gleam/dynamic.gleam | 100 + .../src/gleam/dynamic/decode.gleam | 1061 ++++++ .../gleam_stdlib/src/gleam/float.gleam | 661 ++++ .../gleam_stdlib/src/gleam/function.gleam | 15 + .../packages/gleam_stdlib/src/gleam/int.gleam | 828 +++++ .../packages/gleam_stdlib/src/gleam/io.gleam | 59 + .../gleam_stdlib/src/gleam/list.gleam | 2426 +++++++++++++ .../gleam_stdlib/src/gleam/option.gleam | 358 ++ .../gleam_stdlib/src/gleam/order.gleam | 156 + .../gleam_stdlib/src/gleam/pair.gleam | 85 + .../gleam_stdlib/src/gleam/result.gleam | 453 +++ .../packages/gleam_stdlib/src/gleam/set.gleam | 407 +++ .../gleam_stdlib/src/gleam/string.gleam | 900 +++++ .../gleam_stdlib/src/gleam/string_tree.gleam | 208 ++ .../packages/gleam_stdlib/src/gleam/uri.gleam | 770 ++++ .../gleam_stdlib/src/gleam@bit_array.erl | 347 ++ .../packages/gleam_stdlib/src/gleam@bool.erl | 352 ++ .../gleam_stdlib/src/gleam@bytes_tree.erl | 211 ++ .../packages/gleam_stdlib/src/gleam@dict.erl | 561 +++ .../gleam_stdlib/src/gleam@dynamic.erl | 106 + .../gleam_stdlib/src/gleam@dynamic@decode.erl | 1088 ++++++ .../packages/gleam_stdlib/src/gleam@float.erl | 744 ++++ .../gleam_stdlib/src/gleam@function.erl | 30 + build/packages/gleam_stdlib/src/gleam@int.erl | 986 +++++ build/packages/gleam_stdlib/src/gleam@io.erl | 80 + .../packages/gleam_stdlib/src/gleam@list.erl | 2873 +++++++++++++++ .../gleam_stdlib/src/gleam@option.erl | 413 +++ .../packages/gleam_stdlib/src/gleam@order.erl | 200 + .../packages/gleam_stdlib/src/gleam@pair.erl | 110 + .../gleam_stdlib/src/gleam@result.erl | 550 +++ build/packages/gleam_stdlib/src/gleam@set.erl | 429 +++ .../gleam_stdlib/src/gleam@string.erl | 1012 ++++++ .../gleam_stdlib/src/gleam@string_tree.erl | 207 ++ build/packages/gleam_stdlib/src/gleam@uri.erl | 1044 ++++++ .../gleam_stdlib/src/gleam_stdlib.app.src | 31 + .../gleam_stdlib/src/gleam_stdlib.erl | 534 +++ .../gleam_stdlib/src/gleam_stdlib.mjs | 1048 ++++++ build/packages/gleam_time/README.md | 101 + build/packages/gleam_time/gleam.toml | 19 + .../include/gleam@time@calendar_Date.hrl | 5 + .../include/gleam@time@calendar_TimeOfDay.hrl | 6 + .../include/gleam@time@duration_Duration.hrl | 1 + .../gleam@time@timestamp_Timestamp.hrl | 1 + .../gleam_time/src/gleam/time/calendar.gleam | 346 ++ .../gleam_time/src/gleam/time/duration.gleam | 297 ++ .../gleam_time/src/gleam/time/timestamp.gleam | 899 +++++ .../gleam_time/src/gleam@time@calendar.erl | 468 +++ .../gleam_time/src/gleam@time@duration.erl | 381 ++ .../gleam_time/src/gleam@time@timestamp.erl | 1188 ++++++ .../gleam_time/src/gleam_time.app.src | 12 + .../gleam_time/src/gleam_time_ffi.erl | 12 + .../gleam_time/src/gleam_time_ffi.mjs | 11 + build/packages/gleeunit/LICENCE | 191 + build/packages/gleeunit/README.md | 44 + build/packages/gleeunit/gleam.toml | 16 + .../gleeunit@internal@gleam_panic_Assert.hrl | 6 + ...nternal@gleam_panic_AssertedExpression.hrl | 5 + ...it@internal@gleam_panic_BinaryOperator.hrl | 5 + ...eeunit@internal@gleam_panic_Expression.hrl | 1 + ...unit@internal@gleam_panic_FunctionCall.hrl | 3 + ...eeunit@internal@gleam_panic_GleamPanic.hrl | 8 + ...leeunit@internal@gleam_panic_LetAssert.hrl | 7 + .../gleeunit@internal@gleam_panic_Literal.hrl | 1 + ...t@internal@gleam_panic_OtherExpression.hrl | 3 + .../gleeunit@internal@reporting_State.hrl | 1 + build/packages/gleeunit/src/gleeunit.app.src | 16 + build/packages/gleeunit/src/gleeunit.erl | 89 + build/packages/gleeunit/src/gleeunit.gleam | 86 + .../src/gleeunit/internal/gleam_panic.gleam | 49 + .../internal/gleeunit_gleam_panic_ffi.erl | 49 + .../internal/gleeunit_gleam_panic_ffi.mjs | 91 + .../src/gleeunit/internal/reporting.gleam | 240 ++ .../gleeunit/src/gleeunit/should.gleam | 72 + .../src/gleeunit@internal@gleam_panic.erl | 56 + .../src/gleeunit@internal@reporting.erl | 343 ++ .../packages/gleeunit/src/gleeunit@should.erl | 153 + build/packages/gleeunit/src/gleeunit_ffi.erl | 21 + build/packages/gleeunit/src/gleeunit_ffi.mjs | 100 + .../gleeunit/src/gleeunit_progress.erl | 72 + build/packages/packages.toml | 7 + build/packages/paint/LICENSE.txt | 21 + build/packages/paint/README.md | 34 + build/packages/paint/gleam.toml | 23 + .../paint/src/impl_canvas_bindings.mjs | 271 ++ build/packages/paint/src/numbers_ffi.mjs | 3 + build/packages/paint/src/paint.gleam | 201 ++ build/packages/paint/src/paint/canvas.gleam | 461 +++ build/packages/paint/src/paint/encode.gleam | 260 ++ build/packages/paint/src/paint/event.gleam | 50 + .../src/paint/internal/impl_canvas.gleam | 118 + .../paint/src/paint/internal/types.gleam | 48 + dev_server.py | 10 + gleam.toml | 22 + index.html | 59 + manifest.toml | 17 + res/diamond.svg | 6 + res/grain.png | Bin 0 -> 511323 bytes res/heart.svg | 3 + res/lucy.svg | 7 + res/no-heart.svg | 4 + res/rocket.svg | 5 + shell.nix | 11 + src/browser_ffi.mjs | 40 + src/game.gleam | 263 ++ src/hud.gleam | 73 + src/math/math.gleam | 28 + src/math/math_ffi.mjs | 35 + src/physics.gleam | 24 + src/rendering.gleam | 107 + src/target.gleam | 126 + src/types.gleam | 19 + 379 files changed, 74829 insertions(+) create mode 100644 Makefile create mode 100644 README.md create mode 100644 build/dev/javascript/gleam_community_colour/_gleam_artefacts/gleam_community@colour.cache create mode 100644 build/dev/javascript/gleam_community_colour/_gleam_artefacts/gleam_community@colour.cache_inline create mode 100644 build/dev/javascript/gleam_community_colour/_gleam_artefacts/gleam_community@colour.cache_meta create mode 100644 build/dev/javascript/gleam_community_colour/_gleam_artefacts/gleam_community@colour@accessibility.cache create mode 100644 build/dev/javascript/gleam_community_colour/_gleam_artefacts/gleam_community@colour@accessibility.cache_inline create mode 100644 build/dev/javascript/gleam_community_colour/_gleam_artefacts/gleam_community@colour@accessibility.cache_meta create mode 100644 build/dev/javascript/gleam_community_colour/gleam.mjs create mode 100644 build/dev/javascript/gleam_community_colour/gleam_community/colour.mjs create mode 100644 build/dev/javascript/gleam_community_colour/gleam_community/colour/accessibility.mjs create mode 100644 build/dev/javascript/gleam_community_colour/gleam_community@colour.erl create mode 100644 build/dev/javascript/gleam_community_colour/gleam_community@colour@accessibility.erl create mode 100644 build/dev/javascript/gleam_json/_gleam_artefacts/gleam@json.cache create mode 100644 build/dev/javascript/gleam_json/_gleam_artefacts/gleam@json.cache_inline create mode 100644 build/dev/javascript/gleam_json/_gleam_artefacts/gleam@json.cache_meta create mode 100644 build/dev/javascript/gleam_json/gleam.mjs create mode 100644 build/dev/javascript/gleam_json/gleam/json.mjs create mode 100644 build/dev/javascript/gleam_json/gleam@json.erl create mode 100644 build/dev/javascript/gleam_json/gleam_json_ffi.erl create mode 100644 build/dev/javascript/gleam_json/gleam_json_ffi.mjs create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bit_array.cache create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bit_array.cache_inline create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bit_array.cache_meta create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bool.cache create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bool.cache_inline create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bool.cache_meta create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bytes_tree.cache create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bytes_tree.cache_inline create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bytes_tree.cache_meta create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@dict.cache create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@dict.cache_inline create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@dict.cache_meta create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@dynamic.cache create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@dynamic.cache_inline create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@dynamic.cache_meta create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@dynamic@decode.cache create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@dynamic@decode.cache_inline create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@dynamic@decode.cache_meta create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@float.cache create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@float.cache_inline create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@float.cache_meta create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@function.cache create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@function.cache_inline create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@function.cache_meta create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@int.cache create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@int.cache_inline create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@int.cache_meta create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@io.cache create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@io.cache_inline create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@io.cache_meta create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@list.cache create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@list.cache_inline create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@list.cache_meta create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@option.cache create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@option.cache_inline create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@option.cache_meta create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@order.cache create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@order.cache_inline create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@order.cache_meta create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@pair.cache create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@pair.cache_inline create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@pair.cache_meta create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@result.cache create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@result.cache_inline create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@result.cache_meta create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@set.cache create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@set.cache_inline create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@set.cache_meta create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@string.cache create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@string.cache_inline create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@string.cache_meta create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@string_tree.cache create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@string_tree.cache_inline create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@string_tree.cache_meta create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@uri.cache create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@uri.cache_inline create mode 100644 build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@uri.cache_meta create mode 100644 build/dev/javascript/gleam_stdlib/dict.mjs create mode 100644 build/dev/javascript/gleam_stdlib/gleam.mjs create mode 100644 build/dev/javascript/gleam_stdlib/gleam/bit_array.mjs create mode 100644 build/dev/javascript/gleam_stdlib/gleam/bool.mjs create mode 100644 build/dev/javascript/gleam_stdlib/gleam/bytes_tree.mjs create mode 100644 build/dev/javascript/gleam_stdlib/gleam/dict.mjs create mode 100644 build/dev/javascript/gleam_stdlib/gleam/dynamic.mjs create mode 100644 build/dev/javascript/gleam_stdlib/gleam/dynamic/decode.mjs create mode 100644 build/dev/javascript/gleam_stdlib/gleam/float.mjs create mode 100644 build/dev/javascript/gleam_stdlib/gleam/function.mjs create mode 100644 build/dev/javascript/gleam_stdlib/gleam/int.mjs create mode 100644 build/dev/javascript/gleam_stdlib/gleam/io.mjs create mode 100644 build/dev/javascript/gleam_stdlib/gleam/list.mjs create mode 100644 build/dev/javascript/gleam_stdlib/gleam/option.mjs create mode 100644 build/dev/javascript/gleam_stdlib/gleam/order.mjs create mode 100644 build/dev/javascript/gleam_stdlib/gleam/pair.mjs create mode 100644 build/dev/javascript/gleam_stdlib/gleam/result.mjs create mode 100644 build/dev/javascript/gleam_stdlib/gleam/set.mjs create mode 100644 build/dev/javascript/gleam_stdlib/gleam/string.mjs create mode 100644 build/dev/javascript/gleam_stdlib/gleam/string_tree.mjs create mode 100644 build/dev/javascript/gleam_stdlib/gleam/uri.mjs create mode 100644 build/dev/javascript/gleam_stdlib/gleam@bit_array.erl create mode 100644 build/dev/javascript/gleam_stdlib/gleam@bool.erl create mode 100644 build/dev/javascript/gleam_stdlib/gleam@bytes_tree.erl create mode 100644 build/dev/javascript/gleam_stdlib/gleam@dict.erl create mode 100644 build/dev/javascript/gleam_stdlib/gleam@dynamic.erl create mode 100644 build/dev/javascript/gleam_stdlib/gleam@dynamic@decode.erl create mode 100644 build/dev/javascript/gleam_stdlib/gleam@float.erl create mode 100644 build/dev/javascript/gleam_stdlib/gleam@function.erl create mode 100644 build/dev/javascript/gleam_stdlib/gleam@int.erl create mode 100644 build/dev/javascript/gleam_stdlib/gleam@io.erl create mode 100644 build/dev/javascript/gleam_stdlib/gleam@list.erl create mode 100644 build/dev/javascript/gleam_stdlib/gleam@option.erl create mode 100644 build/dev/javascript/gleam_stdlib/gleam@order.erl create mode 100644 build/dev/javascript/gleam_stdlib/gleam@pair.erl create mode 100644 build/dev/javascript/gleam_stdlib/gleam@result.erl create mode 100644 build/dev/javascript/gleam_stdlib/gleam@set.erl create mode 100644 build/dev/javascript/gleam_stdlib/gleam@string.erl create mode 100644 build/dev/javascript/gleam_stdlib/gleam@string_tree.erl create mode 100644 build/dev/javascript/gleam_stdlib/gleam@uri.erl create mode 100644 build/dev/javascript/gleam_stdlib/gleam_stdlib.erl create mode 100644 build/dev/javascript/gleam_stdlib/gleam_stdlib.mjs create mode 100644 build/dev/javascript/gleam_time/_gleam_artefacts/gleam@time@calendar.cache create mode 100644 build/dev/javascript/gleam_time/_gleam_artefacts/gleam@time@calendar.cache_inline create mode 100644 build/dev/javascript/gleam_time/_gleam_artefacts/gleam@time@calendar.cache_meta create mode 100644 build/dev/javascript/gleam_time/_gleam_artefacts/gleam@time@duration.cache create mode 100644 build/dev/javascript/gleam_time/_gleam_artefacts/gleam@time@duration.cache_inline create mode 100644 build/dev/javascript/gleam_time/_gleam_artefacts/gleam@time@duration.cache_meta create mode 100644 build/dev/javascript/gleam_time/_gleam_artefacts/gleam@time@timestamp.cache create mode 100644 build/dev/javascript/gleam_time/_gleam_artefacts/gleam@time@timestamp.cache_inline create mode 100644 build/dev/javascript/gleam_time/_gleam_artefacts/gleam@time@timestamp.cache_meta create mode 100644 build/dev/javascript/gleam_time/gleam.mjs create mode 100644 build/dev/javascript/gleam_time/gleam/time/calendar.mjs create mode 100644 build/dev/javascript/gleam_time/gleam/time/duration.mjs create mode 100644 build/dev/javascript/gleam_time/gleam/time/timestamp.mjs create mode 100644 build/dev/javascript/gleam_time/gleam@time@calendar.erl create mode 100644 build/dev/javascript/gleam_time/gleam@time@duration.erl create mode 100644 build/dev/javascript/gleam_time/gleam@time@timestamp.erl create mode 100644 build/dev/javascript/gleam_time/gleam_time_ffi.erl create mode 100644 build/dev/javascript/gleam_time/gleam_time_ffi.mjs create mode 100644 build/dev/javascript/gleam_version create mode 100644 build/dev/javascript/gleeunit/_gleam_artefacts/gleeunit.cache create mode 100644 build/dev/javascript/gleeunit/_gleam_artefacts/gleeunit.cache_inline create mode 100644 build/dev/javascript/gleeunit/_gleam_artefacts/gleeunit.cache_meta create mode 100644 build/dev/javascript/gleeunit/_gleam_artefacts/gleeunit@internal@gleam_panic.cache create mode 100644 build/dev/javascript/gleeunit/_gleam_artefacts/gleeunit@internal@gleam_panic.cache_inline create mode 100644 build/dev/javascript/gleeunit/_gleam_artefacts/gleeunit@internal@gleam_panic.cache_meta create mode 100644 build/dev/javascript/gleeunit/_gleam_artefacts/gleeunit@internal@reporting.cache create mode 100644 build/dev/javascript/gleeunit/_gleam_artefacts/gleeunit@internal@reporting.cache_inline create mode 100644 build/dev/javascript/gleeunit/_gleam_artefacts/gleeunit@internal@reporting.cache_meta create mode 100644 build/dev/javascript/gleeunit/_gleam_artefacts/gleeunit@should.cache create mode 100644 build/dev/javascript/gleeunit/_gleam_artefacts/gleeunit@should.cache_inline create mode 100644 build/dev/javascript/gleeunit/_gleam_artefacts/gleeunit@should.cache_meta create mode 100644 build/dev/javascript/gleeunit/gleam.mjs create mode 100644 build/dev/javascript/gleeunit/gleeunit.erl create mode 100644 build/dev/javascript/gleeunit/gleeunit.mjs create mode 100644 build/dev/javascript/gleeunit/gleeunit/internal/gleam_panic.mjs create mode 100644 build/dev/javascript/gleeunit/gleeunit/internal/gleeunit_gleam_panic_ffi.erl create mode 100644 build/dev/javascript/gleeunit/gleeunit/internal/gleeunit_gleam_panic_ffi.mjs create mode 100644 build/dev/javascript/gleeunit/gleeunit/internal/reporting.mjs create mode 100644 build/dev/javascript/gleeunit/gleeunit/should.mjs create mode 100644 build/dev/javascript/gleeunit/gleeunit@internal@gleam_panic.erl create mode 100644 build/dev/javascript/gleeunit/gleeunit@internal@reporting.erl create mode 100644 build/dev/javascript/gleeunit/gleeunit@should.erl create mode 100644 build/dev/javascript/gleeunit/gleeunit_ffi.erl create mode 100644 build/dev/javascript/gleeunit/gleeunit_ffi.mjs create mode 100644 build/dev/javascript/gleeunit/gleeunit_progress.erl create mode 100644 build/dev/javascript/paint/_gleam_artefacts/paint.cache create mode 100644 build/dev/javascript/paint/_gleam_artefacts/paint.cache_inline create mode 100644 build/dev/javascript/paint/_gleam_artefacts/paint.cache_meta create mode 100644 build/dev/javascript/paint/_gleam_artefacts/paint@canvas.cache create mode 100644 build/dev/javascript/paint/_gleam_artefacts/paint@canvas.cache_inline create mode 100644 build/dev/javascript/paint/_gleam_artefacts/paint@canvas.cache_meta create mode 100644 build/dev/javascript/paint/_gleam_artefacts/paint@encode.cache create mode 100644 build/dev/javascript/paint/_gleam_artefacts/paint@encode.cache_inline create mode 100644 build/dev/javascript/paint/_gleam_artefacts/paint@encode.cache_meta create mode 100644 build/dev/javascript/paint/_gleam_artefacts/paint@event.cache create mode 100644 build/dev/javascript/paint/_gleam_artefacts/paint@event.cache_inline create mode 100644 build/dev/javascript/paint/_gleam_artefacts/paint@event.cache_meta create mode 100644 build/dev/javascript/paint/_gleam_artefacts/paint@internal@impl_canvas.cache create mode 100644 build/dev/javascript/paint/_gleam_artefacts/paint@internal@impl_canvas.cache_inline create mode 100644 build/dev/javascript/paint/_gleam_artefacts/paint@internal@impl_canvas.cache_meta create mode 100644 build/dev/javascript/paint/_gleam_artefacts/paint@internal@types.cache create mode 100644 build/dev/javascript/paint/_gleam_artefacts/paint@internal@types.cache_inline create mode 100644 build/dev/javascript/paint/_gleam_artefacts/paint@internal@types.cache_meta create mode 100644 build/dev/javascript/paint/gleam.mjs create mode 100644 build/dev/javascript/paint/impl_canvas_bindings.mjs create mode 100644 build/dev/javascript/paint/numbers_ffi.mjs create mode 100644 build/dev/javascript/paint/paint.mjs create mode 100644 build/dev/javascript/paint/paint/canvas.mjs create mode 100644 build/dev/javascript/paint/paint/encode.mjs create mode 100644 build/dev/javascript/paint/paint/event.mjs create mode 100644 build/dev/javascript/paint/paint/internal/impl_canvas.mjs create mode 100644 build/dev/javascript/paint/paint/internal/types.mjs create mode 100644 build/dev/javascript/prelude.mjs create mode 100644 build/dev/javascript/stellar_prune/_gleam_artefacts/game.cache create mode 100644 build/dev/javascript/stellar_prune/_gleam_artefacts/game.cache_inline create mode 100644 build/dev/javascript/stellar_prune/_gleam_artefacts/game.cache_meta create mode 100644 build/dev/javascript/stellar_prune/_gleam_artefacts/game.cache_warnings create mode 100644 build/dev/javascript/stellar_prune/_gleam_artefacts/hud.cache create mode 100644 build/dev/javascript/stellar_prune/_gleam_artefacts/hud.cache_inline create mode 100644 build/dev/javascript/stellar_prune/_gleam_artefacts/hud.cache_meta create mode 100644 build/dev/javascript/stellar_prune/_gleam_artefacts/hud.cache_warnings create mode 100644 build/dev/javascript/stellar_prune/_gleam_artefacts/math@math.cache create mode 100644 build/dev/javascript/stellar_prune/_gleam_artefacts/math@math.cache_inline create mode 100644 build/dev/javascript/stellar_prune/_gleam_artefacts/math@math.cache_meta create mode 100644 build/dev/javascript/stellar_prune/_gleam_artefacts/math@math.cache_warnings create mode 100644 build/dev/javascript/stellar_prune/_gleam_artefacts/physics.cache create mode 100644 build/dev/javascript/stellar_prune/_gleam_artefacts/physics.cache_inline create mode 100644 build/dev/javascript/stellar_prune/_gleam_artefacts/physics.cache_meta create mode 100644 build/dev/javascript/stellar_prune/_gleam_artefacts/physics.cache_warnings create mode 100644 build/dev/javascript/stellar_prune/_gleam_artefacts/rendering.cache create mode 100644 build/dev/javascript/stellar_prune/_gleam_artefacts/rendering.cache_inline create mode 100644 build/dev/javascript/stellar_prune/_gleam_artefacts/rendering.cache_meta create mode 100644 build/dev/javascript/stellar_prune/_gleam_artefacts/rendering.cache_warnings create mode 100644 build/dev/javascript/stellar_prune/_gleam_artefacts/target.cache create mode 100644 build/dev/javascript/stellar_prune/_gleam_artefacts/target.cache_inline create mode 100644 build/dev/javascript/stellar_prune/_gleam_artefacts/target.cache_meta create mode 100644 build/dev/javascript/stellar_prune/_gleam_artefacts/target.cache_warnings create mode 100644 build/dev/javascript/stellar_prune/_gleam_artefacts/types.cache create mode 100644 build/dev/javascript/stellar_prune/_gleam_artefacts/types.cache_inline create mode 100644 build/dev/javascript/stellar_prune/_gleam_artefacts/types.cache_meta create mode 100644 build/dev/javascript/stellar_prune/_gleam_artefacts/types.cache_warnings create mode 100644 build/dev/javascript/stellar_prune/browser_ffi.mjs create mode 100644 build/dev/javascript/stellar_prune/game.mjs create mode 100644 build/dev/javascript/stellar_prune/gleam.mjs create mode 100644 build/dev/javascript/stellar_prune/hud.mjs create mode 100644 build/dev/javascript/stellar_prune/math/math.mjs create mode 100644 build/dev/javascript/stellar_prune/math/math_ffi.mjs create mode 100644 build/dev/javascript/stellar_prune/physics.mjs create mode 100644 build/dev/javascript/stellar_prune/rendering.mjs create mode 100644 build/dev/javascript/stellar_prune/target.mjs create mode 100644 build/dev/javascript/stellar_prune/types.mjs create mode 100644 build/gleam-dev-erlang.lock create mode 100644 build/gleam-dev-javascript.lock create mode 100644 build/gleam-lsp-erlang.lock create mode 100644 build/gleam-lsp-javascript.lock create mode 100644 build/gleam-prod-erlang.lock create mode 100644 build/gleam-prod-javascript.lock create mode 100644 build/packages/gleam.lock create mode 100644 build/packages/gleam_community_colour/LICENCE create mode 100644 build/packages/gleam_community_colour/README.md create mode 100644 build/packages/gleam_community_colour/gleam.toml create mode 100644 build/packages/gleam_community_colour/include/gleam_community@colour_Hsla.hrl create mode 100644 build/packages/gleam_community_colour/include/gleam_community@colour_Rgba.hrl create mode 100644 build/packages/gleam_community_colour/src/gleam_community/colour.gleam create mode 100644 build/packages/gleam_community_colour/src/gleam_community/colour/accessibility.gleam create mode 100644 build/packages/gleam_community_colour/src/gleam_community@colour.erl create mode 100644 build/packages/gleam_community_colour/src/gleam_community@colour@accessibility.erl create mode 100644 build/packages/gleam_community_colour/src/gleam_community_colour.app.src create mode 100644 build/packages/gleam_json/LICENCE create mode 100644 build/packages/gleam_json/README.md create mode 100644 build/packages/gleam_json/gleam.toml create mode 100644 build/packages/gleam_json/src/gleam/json.gleam create mode 100644 build/packages/gleam_json/src/gleam@json.erl create mode 100644 build/packages/gleam_json/src/gleam_json.app.src create mode 100644 build/packages/gleam_json/src/gleam_json_ffi.erl create mode 100644 build/packages/gleam_json/src/gleam_json_ffi.mjs create mode 100644 build/packages/gleam_stdlib/LICENCE create mode 100644 build/packages/gleam_stdlib/README.md create mode 100644 build/packages/gleam_stdlib/gleam.toml create mode 100644 build/packages/gleam_stdlib/include/gleam@dynamic@decode_DecodeError.hrl create mode 100644 build/packages/gleam_stdlib/include/gleam@dynamic@decode_Decoder.hrl create mode 100644 build/packages/gleam_stdlib/include/gleam@set_Set.hrl create mode 100644 build/packages/gleam_stdlib/include/gleam@uri_Uri.hrl create mode 100644 build/packages/gleam_stdlib/src/dict.mjs create mode 100644 build/packages/gleam_stdlib/src/gleam/bit_array.gleam create mode 100644 build/packages/gleam_stdlib/src/gleam/bool.gleam create mode 100644 build/packages/gleam_stdlib/src/gleam/bytes_tree.gleam create mode 100644 build/packages/gleam_stdlib/src/gleam/dict.gleam create mode 100644 build/packages/gleam_stdlib/src/gleam/dynamic.gleam create mode 100644 build/packages/gleam_stdlib/src/gleam/dynamic/decode.gleam create mode 100644 build/packages/gleam_stdlib/src/gleam/float.gleam create mode 100644 build/packages/gleam_stdlib/src/gleam/function.gleam create mode 100644 build/packages/gleam_stdlib/src/gleam/int.gleam create mode 100644 build/packages/gleam_stdlib/src/gleam/io.gleam create mode 100644 build/packages/gleam_stdlib/src/gleam/list.gleam create mode 100644 build/packages/gleam_stdlib/src/gleam/option.gleam create mode 100644 build/packages/gleam_stdlib/src/gleam/order.gleam create mode 100644 build/packages/gleam_stdlib/src/gleam/pair.gleam create mode 100644 build/packages/gleam_stdlib/src/gleam/result.gleam create mode 100644 build/packages/gleam_stdlib/src/gleam/set.gleam create mode 100644 build/packages/gleam_stdlib/src/gleam/string.gleam create mode 100644 build/packages/gleam_stdlib/src/gleam/string_tree.gleam create mode 100644 build/packages/gleam_stdlib/src/gleam/uri.gleam create mode 100644 build/packages/gleam_stdlib/src/gleam@bit_array.erl create mode 100644 build/packages/gleam_stdlib/src/gleam@bool.erl create mode 100644 build/packages/gleam_stdlib/src/gleam@bytes_tree.erl create mode 100644 build/packages/gleam_stdlib/src/gleam@dict.erl create mode 100644 build/packages/gleam_stdlib/src/gleam@dynamic.erl create mode 100644 build/packages/gleam_stdlib/src/gleam@dynamic@decode.erl create mode 100644 build/packages/gleam_stdlib/src/gleam@float.erl create mode 100644 build/packages/gleam_stdlib/src/gleam@function.erl create mode 100644 build/packages/gleam_stdlib/src/gleam@int.erl create mode 100644 build/packages/gleam_stdlib/src/gleam@io.erl create mode 100644 build/packages/gleam_stdlib/src/gleam@list.erl create mode 100644 build/packages/gleam_stdlib/src/gleam@option.erl create mode 100644 build/packages/gleam_stdlib/src/gleam@order.erl create mode 100644 build/packages/gleam_stdlib/src/gleam@pair.erl create mode 100644 build/packages/gleam_stdlib/src/gleam@result.erl create mode 100644 build/packages/gleam_stdlib/src/gleam@set.erl create mode 100644 build/packages/gleam_stdlib/src/gleam@string.erl create mode 100644 build/packages/gleam_stdlib/src/gleam@string_tree.erl create mode 100644 build/packages/gleam_stdlib/src/gleam@uri.erl create mode 100644 build/packages/gleam_stdlib/src/gleam_stdlib.app.src create mode 100644 build/packages/gleam_stdlib/src/gleam_stdlib.erl create mode 100644 build/packages/gleam_stdlib/src/gleam_stdlib.mjs create mode 100644 build/packages/gleam_time/README.md create mode 100644 build/packages/gleam_time/gleam.toml create mode 100644 build/packages/gleam_time/include/gleam@time@calendar_Date.hrl create mode 100644 build/packages/gleam_time/include/gleam@time@calendar_TimeOfDay.hrl create mode 100644 build/packages/gleam_time/include/gleam@time@duration_Duration.hrl create mode 100644 build/packages/gleam_time/include/gleam@time@timestamp_Timestamp.hrl create mode 100644 build/packages/gleam_time/src/gleam/time/calendar.gleam create mode 100644 build/packages/gleam_time/src/gleam/time/duration.gleam create mode 100644 build/packages/gleam_time/src/gleam/time/timestamp.gleam create mode 100644 build/packages/gleam_time/src/gleam@time@calendar.erl create mode 100644 build/packages/gleam_time/src/gleam@time@duration.erl create mode 100644 build/packages/gleam_time/src/gleam@time@timestamp.erl create mode 100644 build/packages/gleam_time/src/gleam_time.app.src create mode 100644 build/packages/gleam_time/src/gleam_time_ffi.erl create mode 100644 build/packages/gleam_time/src/gleam_time_ffi.mjs create mode 100644 build/packages/gleeunit/LICENCE create mode 100644 build/packages/gleeunit/README.md create mode 100644 build/packages/gleeunit/gleam.toml create mode 100644 build/packages/gleeunit/include/gleeunit@internal@gleam_panic_Assert.hrl create mode 100644 build/packages/gleeunit/include/gleeunit@internal@gleam_panic_AssertedExpression.hrl create mode 100644 build/packages/gleeunit/include/gleeunit@internal@gleam_panic_BinaryOperator.hrl create mode 100644 build/packages/gleeunit/include/gleeunit@internal@gleam_panic_Expression.hrl create mode 100644 build/packages/gleeunit/include/gleeunit@internal@gleam_panic_FunctionCall.hrl create mode 100644 build/packages/gleeunit/include/gleeunit@internal@gleam_panic_GleamPanic.hrl create mode 100644 build/packages/gleeunit/include/gleeunit@internal@gleam_panic_LetAssert.hrl create mode 100644 build/packages/gleeunit/include/gleeunit@internal@gleam_panic_Literal.hrl create mode 100644 build/packages/gleeunit/include/gleeunit@internal@gleam_panic_OtherExpression.hrl create mode 100644 build/packages/gleeunit/include/gleeunit@internal@reporting_State.hrl create mode 100644 build/packages/gleeunit/src/gleeunit.app.src create mode 100644 build/packages/gleeunit/src/gleeunit.erl create mode 100644 build/packages/gleeunit/src/gleeunit.gleam create mode 100644 build/packages/gleeunit/src/gleeunit/internal/gleam_panic.gleam create mode 100644 build/packages/gleeunit/src/gleeunit/internal/gleeunit_gleam_panic_ffi.erl create mode 100644 build/packages/gleeunit/src/gleeunit/internal/gleeunit_gleam_panic_ffi.mjs create mode 100644 build/packages/gleeunit/src/gleeunit/internal/reporting.gleam create mode 100644 build/packages/gleeunit/src/gleeunit/should.gleam create mode 100644 build/packages/gleeunit/src/gleeunit@internal@gleam_panic.erl create mode 100644 build/packages/gleeunit/src/gleeunit@internal@reporting.erl create mode 100644 build/packages/gleeunit/src/gleeunit@should.erl create mode 100644 build/packages/gleeunit/src/gleeunit_ffi.erl create mode 100644 build/packages/gleeunit/src/gleeunit_ffi.mjs create mode 100644 build/packages/gleeunit/src/gleeunit_progress.erl create mode 100644 build/packages/packages.toml create mode 100644 build/packages/paint/LICENSE.txt create mode 100644 build/packages/paint/README.md create mode 100644 build/packages/paint/gleam.toml create mode 100644 build/packages/paint/src/impl_canvas_bindings.mjs create mode 100644 build/packages/paint/src/numbers_ffi.mjs create mode 100644 build/packages/paint/src/paint.gleam create mode 100644 build/packages/paint/src/paint/canvas.gleam create mode 100644 build/packages/paint/src/paint/encode.gleam create mode 100644 build/packages/paint/src/paint/event.gleam create mode 100644 build/packages/paint/src/paint/internal/impl_canvas.gleam create mode 100644 build/packages/paint/src/paint/internal/types.gleam create mode 100644 dev_server.py create mode 100644 gleam.toml create mode 100644 index.html create mode 100644 manifest.toml create mode 100644 res/diamond.svg create mode 100644 res/grain.png create mode 100644 res/heart.svg create mode 100644 res/lucy.svg create mode 100644 res/no-heart.svg create mode 100644 res/rocket.svg create mode 100644 shell.nix create mode 100644 src/browser_ffi.mjs create mode 100644 src/game.gleam create mode 100644 src/hud.gleam create mode 100644 src/math/math.gleam create mode 100644 src/math/math_ffi.mjs create mode 100644 src/physics.gleam create mode 100644 src/rendering.gleam create mode 100644 src/target.gleam create mode 100644 src/types.gleam diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2b02193 --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +.PHONY: watch serve play + +watch: + watchexec -e gleam gleam build + +serve: + python3 dev_server.py + +play: + gleam build + python3 -m http.server 3000 + diff --git a/README.md b/README.md new file mode 100644 index 0000000..e63a703 --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# Stellar prune +A clicking game where you prune stars and diamonds from the cosmos. Built with Gleam for the gleam game jam of 2025. + +## How to Play +- Click on stars and diamonds as jump up +- Avoid the volatile rockets! + +### Running the Game +```bash +make play +``` +And then play the game on: `http://localhost:3000/`! diff --git a/build/dev/javascript/gleam_community_colour/_gleam_artefacts/gleam_community@colour.cache b/build/dev/javascript/gleam_community_colour/_gleam_artefacts/gleam_community@colour.cache new file mode 100644 index 0000000000000000000000000000000000000000..1c43dda1f4027ad9d115aa8be94f3335d5f57a90 GIT binary patch literal 57501 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 G{0IQFuAxf+ literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_community_colour/_gleam_artefacts/gleam_community@colour.cache_inline b/build/dev/javascript/gleam_community_colour/_gleam_artefacts/gleam_community@colour.cache_inline new file mode 100644 index 0000000000000000000000000000000000000000..1b1cb4d44c57c2d7a5122870fa6ac3e62ff7e94e GIT binary patch literal 8 JcmZR80ssIA00961 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_community_colour/_gleam_artefacts/gleam_community@colour.cache_meta b/build/dev/javascript/gleam_community_colour/_gleam_artefacts/gleam_community@colour.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..963c8c5c31d2da6b5395770be8dcb95f6336bed9 GIT binary patch literal 5305 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 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_community_colour/_gleam_artefacts/gleam_community@colour@accessibility.cache b/build/dev/javascript/gleam_community_colour/_gleam_artefacts/gleam_community@colour@accessibility.cache new file mode 100644 index 0000000000000000000000000000000000000000..5a927a8e56a5b0c9ea30b81dfab4050358860ee4 GIT binary patch literal 7219 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 Wh|!o)kWCQcXJ~&7$#`8+V^ILsWf3F* literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_community_colour/_gleam_artefacts/gleam_community@colour@accessibility.cache_inline b/build/dev/javascript/gleam_community_colour/_gleam_artefacts/gleam_community@colour@accessibility.cache_inline new file mode 100644 index 0000000000000000000000000000000000000000..1b1cb4d44c57c2d7a5122870fa6ac3e62ff7e94e GIT binary patch literal 8 JcmZR80ssIA00961 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_community_colour/_gleam_artefacts/gleam_community@colour@accessibility.cache_meta b/build/dev/javascript/gleam_community_colour/_gleam_artefacts/gleam_community@colour@accessibility.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..b367370b45434ce2f0ce1afc6f750bba0928f7c0 GIT binary patch literal 876 zcmbQrl99mx1&qv41~-&W&q+T1MJgw=xa1-`1H&VB1_m)? znfT=V+}zT<%#up|?!#=yWJ&%nUo#K6Fi z&%nU2fPsNwDFXuoNX=#j28Q1Z3=C?F3=DaU3=GAL3=B<-3=Ah385qtpGB8|aWMFu~ z$iVQHk%2*iiGe|uiGiVziGkq=69a=7GXp~^GXuj0W(J0L%nS^lnHd;7Sr{1VSQr?Z zSr{0uvM?|xvobK0ure?lU}azsW@BJTV`E?_Wn*BdVq;+FVq;+FXJcTPz{bGvkd1-C zgq?xGoSlIolbwMfhn;~Tmz{xOFFOMRC~QEX0P-V02Lpou2LnR_2LnSQ2LnSN2Lr=h z4hDwT91ILO2#7A^*c9b60y$G8|6o^dfSyy9YD_|C<^z{SnLpwG>~V9L$FV9U+G;KR+p5WvmA zP{z%`aG#rjL5zoiL79hvA&!TEp_7M!VHytu!$KYghNV0V3>SGA7!-IJ7<_pd82oq{ z81i`;7$)#CFl^*yVA#aV!0?Bcf#Dx71H*riJRbwYQa%QTm3#~g+xZw6uJbW4+~#9o zc)`cO@SBf;!HJ)N!GoWHA%LHOA)cRsA(@|np_8A1;S)atgQfrjgP8yWL!kfz!yEwy zhLr*g3|j;k7L&J zi7+ty7hz!F7iD135M^L+7G+@Y6=h&36=h&pDaycbSCoO_o+twYhZq9`uNVV^ych$6 gA{4WL5-$S-!#)uRgPC!H0E|`317is=L0C+T0G_^p0{{R3 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_community_colour/gleam.mjs b/build/dev/javascript/gleam_community_colour/gleam.mjs new file mode 100644 index 0000000..197cbbc --- /dev/null +++ b/build/dev/javascript/gleam_community_colour/gleam.mjs @@ -0,0 +1 @@ +export * from "../prelude.mjs"; diff --git a/build/dev/javascript/gleam_community_colour/gleam_community/colour.mjs b/build/dev/javascript/gleam_community_colour/gleam_community/colour.mjs new file mode 100644 index 0000000..164bd50 --- /dev/null +++ b/build/dev/javascript/gleam_community_colour/gleam_community/colour.mjs @@ -0,0 +1,1444 @@ +import * as $json from "../../gleam_json/gleam/json.mjs"; +import * as $decode from "../../gleam_stdlib/gleam/dynamic/decode.mjs"; +import * as $float from "../../gleam_stdlib/gleam/float.mjs"; +import * as $int from "../../gleam_stdlib/gleam/int.mjs"; +import * as $list from "../../gleam_stdlib/gleam/list.mjs"; +import * as $result from "../../gleam_stdlib/gleam/result.mjs"; +import * as $string from "../../gleam_stdlib/gleam/string.mjs"; +import { Ok, Error, toList, CustomType as $CustomType, makeError, divideFloat } from "../gleam.mjs"; + +const FILEPATH = "src/gleam_community/colour.gleam"; + +class Rgba extends $CustomType { + constructor(r, g, b, a) { + super(); + this.r = r; + this.g = g; + this.b = b; + this.a = a; + } +} + +class Hsla extends $CustomType { + constructor(h, s, l, a) { + super(); + this.h = h; + this.s = s; + this.l = l; + this.a = a; + } +} + +function valid_colour_value(c) { + let $ = (c > 1.0) || (c < 0.0); + if ($) { + return new Error(undefined); + } else { + return new Ok(c); + } +} + +function hue_to_rgb(hue, m1, m2) { + let _block; + if (hue < 0.0) { + _block = hue + 1.0; + } else if (hue > 1.0) { + _block = hue - 1.0; + } else { + _block = hue; + } + let h = _block; + let h_t_6 = h * 6.0; + let h_t_2 = h * 2.0; + let h_t_3 = h * 3.0; + if (h_t_6 < 1.0) { + return m1 + (((m2 - m1) * h) * 6.0); + } else if (h_t_2 < 1.0) { + return m2; + } else if (h_t_3 < 2.0) { + return m1 + (((m2 - m1) * ((2.0 / 3.0) - h)) * 6.0); + } else { + return m1; + } +} + +function hex_string_to_int(hex_string) { + let _block; + if (hex_string.startsWith("#")) { + let hex_number = hex_string.slice(1); + _block = hex_number; + } else if (hex_string.startsWith("0x")) { + let hex_number = hex_string.slice(2); + _block = hex_number; + } else { + _block = hex_string; + } + let hex = _block; + let _pipe = hex; + let _pipe$1 = $string.lowercase(_pipe); + let _pipe$2 = $string.to_graphemes(_pipe$1); + let _pipe$3 = $list.reverse(_pipe$2); + return $list.index_fold( + _pipe$3, + new Ok(0), + (total, char, index) => { + if (total instanceof Ok) { + let v = total[0]; + return $result.try$( + (() => { + if (char === "a") { + return new Ok(10); + } else if (char === "b") { + return new Ok(11); + } else if (char === "c") { + return new Ok(12); + } else if (char === "d") { + return new Ok(13); + } else if (char === "e") { + return new Ok(14); + } else if (char === "f") { + return new Ok(15); + } else { + return $int.parse(char); + } + })(), + (num) => { + return $result.try$( + $int.power(16, $int.to_float(index)), + (base) => { + return new Ok(v + $float.round($int.to_float(num) * base)); + }, + ); + }, + ); + } else { + return total; + } + }, + ); +} + +function hsla_to_rgba(h, s, l, a) { + let _block; + let $ = l <= 0.5; + if ($) { + _block = l * (s + 1.0); + } else { + _block = (l + s) - (l * s); + } + let m2 = _block; + let m1 = (l * 2.0) - m2; + let r = hue_to_rgb(h + (1.0 / 3.0), m1, m2); + let g = hue_to_rgb(h, m1, m2); + let b = hue_to_rgb(h - (1.0 / 3.0), m1, m2); + return [r, g, b, a]; +} + +function rgba_to_hsla(r, g, b, a) { + let min_colour = $float.min(r, $float.min(g, b)); + let max_colour = $float.max(r, $float.max(g, b)); + let _block; + let $ = true; + if (max_colour === r) { + _block = $float.divide(g - b, max_colour - min_colour); + } else if (max_colour === g) { + let _pipe = $float.divide(b - r, max_colour - min_colour); + _block = $result.try$(_pipe, (d) => { return new Ok(2.0 + d); }); + } else { + let _pipe = $float.divide(r - g, max_colour - min_colour); + _block = $result.try$(_pipe, (d) => { return new Ok(4.0 + d); }); + } + let h1 = _block; + let _block$1; + if (h1 instanceof Ok) { + let v = h1[0]; + _block$1 = new Ok(v * (1.0 / 6.0)); + } else { + _block$1 = h1; + } + let h2 = _block$1; + let _block$2; + if (h2 instanceof Ok) { + let v = h2[0]; + if (v < 0.0) { + _block$2 = v + 1.0; + } else { + let v$1 = h2[0]; + _block$2 = v$1; + } + } else { + _block$2 = 0.0; + } + let h3 = _block$2; + let l = (min_colour + max_colour) / 2.0; + let _block$3; + let $1 = true; + if (min_colour === max_colour) { + _block$3 = 0.0; + } else if (l < 0.5) { + _block$3 = divideFloat((max_colour - min_colour), (max_colour + min_colour)); + } else { + _block$3 = divideFloat( + (max_colour - min_colour), + ((2.0 - max_colour) - min_colour) + ); + } + let s = _block$3; + return [h3, s, l, a]; +} + +/** + * Returns a `Result(Colour)` created from the given 8 bit RGB values. + * + * Returns `Error(Nil)` if the supplied RGB values are greater than 255 or less than 0. + * + *
+ * Example: + * + * ```gleam + * fn example() { + * assert Ok(red) = from_rgb255(255, 0, 0) + * } + * ``` + *
+ * + * + */ +export function from_rgb255(red, green, blue) { + return $result.try$( + (() => { + let _pipe = red; + let _pipe$1 = $int.to_float(_pipe); + let _pipe$2 = $float.divide(_pipe$1, 255.0); + return $result.try$(_pipe$2, valid_colour_value); + })(), + (r) => { + return $result.try$( + (() => { + let _pipe = green; + let _pipe$1 = $int.to_float(_pipe); + let _pipe$2 = $float.divide(_pipe$1, 255.0); + return $result.try$(_pipe$2, valid_colour_value); + })(), + (g) => { + return $result.try$( + (() => { + let _pipe = blue; + let _pipe$1 = $int.to_float(_pipe); + let _pipe$2 = $float.divide(_pipe$1, 255.0); + return $result.try$(_pipe$2, valid_colour_value); + })(), + (b) => { return new Ok(new Rgba(r, g, b, 1.0)); }, + ); + }, + ); + }, + ); +} + +/** + * Returns `Result(Colour)` created from the given RGB values. + * + * If the supplied RGB values are greater than 1.0 or less than 0.0 returns `Error(Nil)` + * + *
+ * Example: + * + * ```gleam + * fn example() { + * assert Ok(red) = from_rgb(1.0, 0.0, 0.0) + * } + * ``` + *
+ * + * + */ +export function from_rgb(red, green, blue) { + return $result.try$( + valid_colour_value(red), + (r) => { + return $result.try$( + valid_colour_value(green), + (g) => { + return $result.try$( + valid_colour_value(blue), + (b) => { return new Ok(new Rgba(r, g, b, 1.0)); }, + ); + }, + ); + }, + ); +} + +/** + * Returns `Result(Colour)` created from the given RGBA values. + * + * Returns `Error(Nil)` if the supplied RGBA values are greater than 1.0 or less than 0.0. + * + *
+ * Example: + * + * ```gleam + * fn example() { + * assert Ok(red_half_opacity) = from_rbga(1.0, 0.0, 0.0, 0.5) + * } + * ``` + *
+ * + * + */ +export function from_rgba(red, green, blue, alpha) { + return $result.try$( + valid_colour_value(red), + (r) => { + return $result.try$( + valid_colour_value(green), + (g) => { + return $result.try$( + valid_colour_value(blue), + (b) => { + return $result.try$( + valid_colour_value(alpha), + (a) => { return new Ok(new Rgba(r, g, b, a)); }, + ); + }, + ); + }, + ); + }, + ); +} + +/** + * Returns `Result(Colour)` created from the given HSLA values. + * + * Returns `Error(Nil)`f the supplied HSLA values are greater than 1.0 or less than 0.0. + * + *
+ * Example: + * + * ```gleam + * fn example() { + * assert Ok(red_half_opacity) = from_hsla(0.0, 1.0, 0.5, 0.5) + * } + * ``` + *
+ * + * + */ +export function from_hsla(hue, saturation, lightness, alpha) { + return $result.try$( + valid_colour_value(hue), + (h) => { + return $result.try$( + valid_colour_value(saturation), + (s) => { + return $result.try$( + valid_colour_value(lightness), + (l) => { + return $result.try$( + valid_colour_value(alpha), + (a) => { return new Ok(new Hsla(h, s, l, a)); }, + ); + }, + ); + }, + ); + }, + ); +} + +/** + * Returns `Result(Colour)` created from the given HSL values. + * + * Returns `Error(Nil)` if the supplied HSL values are greater than 1.0 or less than 0.0. + * + *
+ * Example: + * + * ```gleam + * fn example() { + * assert Ok(red) = from_hsla(0.0, 1.0, 0.5) + * } + * ``` + *
+ * + * + */ +export function from_hsl(hue, saturation, lightness) { + return from_hsla(hue, saturation, lightness, 1.0); +} + +/** + * Returns a `Result(Colour)` created from the given hex `Int`. + * + * Returns `Error(Nil)` if the supplied hex `Int is greater than 0xffffff or less than 0x0. + * + *
+ * Example: + * + * ```gleam + * fn example() { + * assert Ok(red) = from_rgb_hex(0xff0000) + * } + * ``` + *
+ * + * + */ +export function from_rgb_hex(hex) { + let $ = (hex > 0xffffff) || (hex < 0); + if ($) { + return new Error(undefined); + } else { + let _block; + let _pipe = $int.bitwise_shift_right(hex, 16); + _block = $int.bitwise_and(_pipe, 0xff); + let r = _block; + let _block$1; + let _pipe$1 = $int.bitwise_shift_right(hex, 8); + _block$1 = $int.bitwise_and(_pipe$1, 0xff); + let g = _block$1; + let b = $int.bitwise_and(hex, 0xff); + return from_rgb255(r, g, b); + } +} + +/** + * Returns a `Result(Colour)` created from the given RGB hex `String`. + * + * Returns `Error(Nil)` if the supplied hex `String` is invalid, or greater than `"#ffffff" or less than `"#0"` + * + *
+ * Example: + * + * ```gleam + * fn example() { + * assert Ok(red) = from_rgb_hex_string("#ff0000") + * } + * ``` + *
+ * + * + */ +export function from_rgb_hex_string(hex_string) { + return $result.try$( + hex_string_to_int(hex_string), + (hex_int) => { return from_rgb_hex(hex_int); }, + ); +} + +/** + * Returns a `Result(Colour)` created from the given hex `Int`. + * + * Returns `Error(Nil)` if the supplied hex `Int is greater than 0xffffffff or less than 0x0. + * + *
+ * Example: + * + * ```gleam + * fn example() { + * assert Ok(red_half_opacity) = from_rgba_hex(0xff00007f) + * } + * ``` + *
+ * + * + */ +export function from_rgba_hex(hex) { + let $ = (hex > 0xffffffff) || (hex < 0); + if ($) { + return new Error(undefined); + } else { + let _block; + let _pipe = $int.bitwise_shift_right(hex, 24); + let _pipe$1 = $int.bitwise_and(_pipe, 0xff); + let _pipe$2 = $int.to_float(_pipe$1); + _block = $float.divide(_pipe$2, 255.0); + let $1 = _block; + let r; + if ($1 instanceof Ok) { + r = $1[0]; + } else { + throw makeError( + "let_assert", + FILEPATH, + "gleam_community/colour", + 590, + "from_rgba_hex", + "Pattern match failed, no pattern matched the value.", + { + value: $1, + start: 17111, + end: 17260, + pattern_start: 17122, + pattern_end: 17127 + } + ) + } + let _block$1; + let _pipe$3 = $int.bitwise_shift_right(hex, 16); + let _pipe$4 = $int.bitwise_and(_pipe$3, 0xff); + let _pipe$5 = $int.to_float(_pipe$4); + _block$1 = $float.divide(_pipe$5, 255.0); + let $2 = _block$1; + let g; + if ($2 instanceof Ok) { + g = $2[0]; + } else { + throw makeError( + "let_assert", + FILEPATH, + "gleam_community/colour", + 596, + "from_rgba_hex", + "Pattern match failed, no pattern matched the value.", + { + value: $2, + start: 17332, + end: 17481, + pattern_start: 17343, + pattern_end: 17348 + } + ) + } + let _block$2; + let _pipe$6 = $int.bitwise_shift_right(hex, 8); + let _pipe$7 = $int.bitwise_and(_pipe$6, 0xff); + let _pipe$8 = $int.to_float(_pipe$7); + _block$2 = $float.divide(_pipe$8, 255.0); + let $3 = _block$2; + let b; + if ($3 instanceof Ok) { + b = $3[0]; + } else { + throw makeError( + "let_assert", + FILEPATH, + "gleam_community/colour", + 602, + "from_rgba_hex", + "Pattern match failed, no pattern matched the value.", + { + value: $3, + start: 17553, + end: 17701, + pattern_start: 17564, + pattern_end: 17569 + } + ) + } + let _block$3; + let _pipe$9 = $int.bitwise_and(hex, 0xff); + let _pipe$10 = $int.to_float(_pipe$9); + _block$3 = $float.divide(_pipe$10, 255.0); + let $4 = _block$3; + let a; + if ($4 instanceof Ok) { + a = $4[0]; + } else { + throw makeError( + "let_assert", + FILEPATH, + "gleam_community/colour", + 608, + "from_rgba_hex", + "Pattern match failed, no pattern matched the value.", + { + value: $4, + start: 17773, + end: 17883, + pattern_start: 17784, + pattern_end: 17789 + } + ) + } + return from_rgba(r, g, b, a); + } +} + +/** + * Returns a `Result(Colour)` created from the given RGBA hex `String`. + * + * Returns `Error(Nil)` if the supplied hex `String` is invalid, or greater than `"#ffffffff" or less than `"#0"` + * + *
+ * Example: + * + * ```gleam + * fn example() { + * assert Ok(red_half_opacity) = from_rgba_hex_string("#ff00007f") + * } + * ``` + *
+ * + * + */ +export function from_rgba_hex_string(hex_string) { + return $result.try$( + hex_string_to_int(hex_string), + (hex_int) => { return from_rgba_hex(hex_int); }, + ); +} + +/** + * Returns `#(Float, Float, Float, Float)` representing the given `Colour`'s + * R, G, B, and A values respectively. + * + *
+ * Example: + * + * ```gleam + * fn example() { + * assert Ok(red) = from_rgb255(255, 0, 0) + * let #(r, g, b, a) = to_rgba(red) + * } + * ``` + *
+ * + * + */ +export function to_rgba(colour) { + if (colour instanceof Rgba) { + let r = colour.r; + let g = colour.g; + let b = colour.b; + let a = colour.a; + return [r, g, b, a]; + } else { + let h = colour.h; + let s = colour.s; + let l = colour.l; + let a = colour.a; + return hsla_to_rgba(h, s, l, a); + } +} + +/** + * Returns `#(Float, Float, Float, Float)` representing the given `Colour`'s + * H, S, L, and A values respectively. + * + *
+ * Example: + * + * ```gleam + * fn example() { + * assert Ok(red) = from_rgb255(255, 0, 0) + * let #(h, s, l, a) = to_hsla(red) + * } + * ``` + *
+ * + * + */ +export function to_hsla(colour) { + if (colour instanceof Rgba) { + let r = colour.r; + let g = colour.g; + let b = colour.b; + let a = colour.a; + return rgba_to_hsla(r, g, b, a); + } else { + let h = colour.h; + let s = colour.s; + let l = colour.l; + let a = colour.a; + return [h, s, l, a]; + } +} + +/** + * Returns an rgba formatted CSS `String` created from the given `Colour`. + * + *
+ * Example: + * + * ```gleam + * fn example() { + * assert Ok(red) = from_rgb255(255, 0, 0) + * let css_red = to_css_rgba_string(red) + * } + * ``` + *
+ * + * + */ +export function to_css_rgba_string(colour) { + let $ = to_rgba(colour); + let r; + let g; + let b; + let a; + r = $[0]; + g = $[1]; + b = $[2]; + a = $[3]; + let percent = (x) => { + let _block; + let _pipe = x; + let _pipe$1 = $float.multiply(_pipe, 10_000.0); + let _pipe$2 = $float.round(_pipe$1); + let _pipe$3 = $int.to_float(_pipe$2); + _block = $float.divide(_pipe$3, 100.0); + let $1 = _block; + let p; + if ($1 instanceof Ok) { + p = $1[0]; + } else { + throw makeError( + "let_assert", + FILEPATH, + "gleam_community/colour", + 706, + "to_css_rgba_string", + "Pattern match failed, no pattern matched the value.", + { + value: $1, + start: 20510, + end: 20646, + pattern_start: 20521, + pattern_end: 20526 + } + ) + } + return p; + }; + let round_to = (x) => { + let _block; + let _pipe = x; + let _pipe$1 = $float.multiply(_pipe, 1000.0); + let _pipe$2 = $float.round(_pipe$1); + let _pipe$3 = $int.to_float(_pipe$2); + _block = $float.divide(_pipe$3, 1000.0); + let $1 = _block; + let r$1; + if ($1 instanceof Ok) { + r$1 = $1[0]; + } else { + throw makeError( + "let_assert", + FILEPATH, + "gleam_community/colour", + 718, + "to_css_rgba_string", + "Pattern match failed, no pattern matched the value.", + { + value: $1, + start: 20768, + end: 20903, + pattern_start: 20779, + pattern_end: 20784 + } + ) + } + return r$1; + }; + return $string.join( + toList([ + "rgba(", + $float.to_string(percent(r)) + "%,", + $float.to_string(percent(g)) + "%,", + $float.to_string(percent(b)) + "%,", + $float.to_string(round_to(a)), + ")", + ]), + "", + ); +} + +/** + * Returns an hex `Int` created from the given `Colour`. + * + *
+ * Example: + * + * ```gleam + * fn example() { + * assert Ok(red) = from_rgba(1.0, 0.0, 0.0, 1.0) + * let red_hex_int = to_rgba_hex(red) + * } + * ``` + *
+ * + * + */ +export function to_rgba_hex(colour) { + let $ = to_rgba(colour); + let r; + let g; + let b; + let a; + r = $[0]; + g = $[1]; + b = $[2]; + a = $[3]; + let _block; + let _pipe = r * 255.0; + let _pipe$1 = $float.round(_pipe); + _block = $int.bitwise_shift_left(_pipe$1, 24); + let red$1 = _block; + let _block$1; + let _pipe$2 = g * 255.0; + let _pipe$3 = $float.round(_pipe$2); + _block$1 = $int.bitwise_shift_left(_pipe$3, 16); + let green$1 = _block$1; + let _block$2; + let _pipe$4 = b * 255.0; + let _pipe$5 = $float.round(_pipe$4); + _block$2 = $int.bitwise_shift_left(_pipe$5, 8); + let blue$1 = _block$2; + let _block$3; + let _pipe$6 = a * 255.0; + _block$3 = $float.round(_pipe$6); + let alpha = _block$3; + return ((red$1 + green$1) + blue$1) + alpha; +} + +/** + * Returns an rgba hex formatted `String` created from the given `Colour`. + * + *
+ * Example: + * + * ```gleam + * fn example() { + * assert Ok(red) = from_rgba(1.0, 0.0, 0.0, 1.0) + * let red_hex = to_rgba_hex_string(red) + * } + * ``` + *
+ * + * + */ +export function to_rgba_hex_string(colour) { + let _block; + let _pipe = to_rgba_hex(colour); + _block = $int.to_base16(_pipe); + let hex_string = _block; + let $ = $string.length(hex_string); + if ($ === 8) { + return hex_string; + } else { + let l = $; + return $string.repeat("0", 8 - l) + hex_string; + } +} + +/** + * Returns a rgb hex `Int` created from the given `Colour`. + * + *
+ * Example: + * + * ```gleam + * fn example() { + * assert Ok(red) = from_rgba(255, 0, 0) + * let red_hex_int = to_rgb_hex(red) + * } + * ``` + *
+ * + * + */ +export function to_rgb_hex(colour) { + let $ = to_rgba(colour); + let r; + let g; + let b; + r = $[0]; + g = $[1]; + b = $[2]; + let _block; + let _pipe = r * 255.0; + let _pipe$1 = $float.round(_pipe); + _block = $int.bitwise_shift_left(_pipe$1, 16); + let red$1 = _block; + let _block$1; + let _pipe$2 = g * 255.0; + let _pipe$3 = $float.round(_pipe$2); + _block$1 = $int.bitwise_shift_left(_pipe$3, 8); + let green$1 = _block$1; + let _block$2; + let _pipe$4 = b * 255.0; + _block$2 = $float.round(_pipe$4); + let blue$1 = _block$2; + return (red$1 + green$1) + blue$1; +} + +/** + * Returns an rgb hex formatted `String` created from the given `Colour`. + * + *
+ * Example: + * + * ```gleam + * fn example() { + * assert Ok(red) = from_rgba(255, 0, 0) + * let red_hex = to_rgb_hex_string(red) + * } + * ``` + *
+ * + * + */ +export function to_rgb_hex_string(colour) { + let _block; + let _pipe = to_rgb_hex(colour); + _block = $int.to_base16(_pipe); + let hex_string = _block; + let $ = $string.length(hex_string); + if ($ === 6) { + return hex_string; + } else { + let l = $; + return $string.repeat("0", 6 - l) + hex_string; + } +} + +function encode_rgba(r, g, b, a) { + return $json.object( + toList([ + ["r", $json.float(r)], + ["g", $json.float(g)], + ["b", $json.float(b)], + ["a", $json.float(a)], + ]), + ); +} + +function encode_hsla(h, s, l, a) { + return $json.object( + toList([ + ["h", $json.float(h)], + ["s", $json.float(s)], + ["l", $json.float(l)], + ["a", $json.float(a)], + ]), + ); +} + +/** + * Encodes a `Colour` value as a Gleam [`Json`](https://hexdocs.pm/gleam_json/gleam/json.html#Json) + * value. You'll need this if you want to send a `Colour` value over the network + * in a HTTP request or response, for example. + * + * + */ +export function encode(colour) { + if (colour instanceof Rgba) { + let r = colour.r; + let g = colour.g; + let b = colour.b; + let a = colour.a; + return encode_rgba(r, g, b, a); + } else { + let h = colour.h; + let s = colour.s; + let l = colour.l; + let a = colour.a; + return encode_hsla(h, s, l, a); + } +} + +function rgba_decoder() { + return $decode.field( + "r", + $decode.float, + (r) => { + return $decode.field( + "g", + $decode.float, + (g) => { + return $decode.field( + "b", + $decode.float, + (b) => { + return $decode.field( + "a", + $decode.float, + (a) => { return $decode.success(new Rgba(r, g, b, a)); }, + ); + }, + ); + }, + ); + }, + ); +} + +function hsla_decoder() { + return $decode.field( + "h", + $decode.float, + (h) => { + return $decode.field( + "s", + $decode.float, + (s) => { + return $decode.field( + "l", + $decode.float, + (l) => { + return $decode.field( + "a", + $decode.float, + (a) => { return $decode.success(new Hsla(h, s, l, a)); }, + ); + }, + ); + }, + ); + }, + ); +} + +/** + * Attempt to decode some [`Dynamic`](https://hexdocs.pm/gleam_stdlib/gleam/dynamic.html#Dynamic) + * value into a `Colour`. Most often you'll use this to decode some JSON. + * + * + */ +export function decoder() { + return $decode.one_of(rgba_decoder(), toList([hsla_decoder()])); +} + +/** + * A `Colour` reprsenting the colour RGBA(239, 41, 41, 1.0) + */ +export const light_red = /* @__PURE__ */ new Rgba( + 0.9372549019607843, + 0.1607843137254902, + 0.1607843137254902, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(204, 0, 0, 1.0) + */ +export const red = /* @__PURE__ */ new Rgba(0.8, 0.0, 0.0, 1.0); + +/** + * A `Colour` reprsenting the colour RGBA(164, 0, 0, 1.0) + */ +export const dark_red = /* @__PURE__ */ new Rgba( + 0.6431372549019608, + 0.0, + 0.0, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(252, 175, 62, 1.0) + */ +export const light_orange = /* @__PURE__ */ new Rgba( + 0.9882352941176471, + 0.6862745098039216, + 0.24313725490196078, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(245, 121, 0, 1.0) + */ +export const orange = /* @__PURE__ */ new Rgba( + 0.9607843137254902, + 0.4745098039215686, + 0.0, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(206, 92, 0, 1.0) + */ +export const dark_orange = /* @__PURE__ */ new Rgba( + 0.807843137254902, + 0.3607843137254902, + 0.0, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(255, 233, 79, 1.0) + */ +export const light_yellow = /* @__PURE__ */ new Rgba( + 1.0, + 0.9137254901960784, + 0.30980392156862746, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(237, 212, 0, 1.0) + */ +export const yellow = /* @__PURE__ */ new Rgba( + 0.9294117647058824, + 0.8313725490196079, + 0.0, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(196, 160, 0, 1.0) + */ +export const dark_yellow = /* @__PURE__ */ new Rgba( + 0.7686274509803922, + 0.6274509803921569, + 0.0, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(138, 226, 52, 1.0) + */ +export const light_green = /* @__PURE__ */ new Rgba( + 0.5411764705882353, + 0.8862745098039215, + 0.20392156862745098, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(115, 210, 22, 1.0) + */ +export const green = /* @__PURE__ */ new Rgba( + 0.45098039215686275, + 0.8235294117647058, + 0.08627450980392157, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(78, 154, 6, 1.0) + */ +export const dark_green = /* @__PURE__ */ new Rgba( + 0.3058823529411765, + 0.6039215686274509, + 0.023529411764705882, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(114, 159, 207, 1.0) + */ +export const light_blue = /* @__PURE__ */ new Rgba( + 0.4470588235294118, + 0.6235294117647059, + 0.8117647058823529, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(52, 101, 164, 1.0) + */ +export const blue = /* @__PURE__ */ new Rgba( + 0.20392156862745098, + 0.396078431372549, + 0.6431372549019608, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(32, 74, 135, 1.0) + */ +export const dark_blue = /* @__PURE__ */ new Rgba( + 0.12549019607843137, + 0.2901960784313726, + 0.5294117647058824, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(173, 127, 168, 1.0) + */ +export const light_purple = /* @__PURE__ */ new Rgba( + 0.6784313725490196, + 0.4980392156862745, + 0.6588235294117647, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(117, 80, 123, 1.0) + */ +export const purple = /* @__PURE__ */ new Rgba( + 0.4588235294117647, + 0.3137254901960784, + 0.4823529411764706, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(92, 53, 102, 1.0) + */ +export const dark_purple = /* @__PURE__ */ new Rgba( + 0.3607843137254902, + 0.20784313725490197, + 0.4, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(233, 185, 110, 1.0) + */ +export const light_brown = /* @__PURE__ */ new Rgba( + 0.9137254901960784, + 0.7254901960784313, + 0.43137254901960786, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(193, 125, 17, 1.0) + */ +export const brown = /* @__PURE__ */ new Rgba( + 0.7568627450980392, + 0.49019607843137253, + 0.06666666666666667, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(143, 89, 2, 1.0) + */ +export const dark_brown = /* @__PURE__ */ new Rgba( + 0.5607843137254902, + 0.34901960784313724, + 0.00784313725490196, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(0, 0, 0, 1.0) + */ +export const black = /* @__PURE__ */ new Rgba(0.0, 0.0, 0.0, 1.0); + +/** + * A `Colour` reprsenting the colour RGBA(255, 255, 255, 1.0) + */ +export const white = /* @__PURE__ */ new Rgba(1.0, 1.0, 1.0, 1.0); + +/** + * A `Colour` reprsenting the colour RGBA(238, 238, 236, 1.0) + */ +export const light_grey = /* @__PURE__ */ new Rgba( + 0.9333333333333333, + 0.9333333333333333, + 0.9254901960784314, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(211, 215, 207, 1.0) + */ +export const grey = /* @__PURE__ */ new Rgba( + 0.8274509803921568, + 0.8431372549019608, + 0.8117647058823529, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(186, 189, 182, 1.0) + */ +export const dark_grey = /* @__PURE__ */ new Rgba( + 0.7294117647058823, + 0.7411764705882353, + 0.7137254901960784, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(238, 238, 236, 1.0) + */ +export const light_gray = /* @__PURE__ */ new Rgba( + 0.9333333333333333, + 0.9333333333333333, + 0.9254901960784314, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(211, 215, 207, 1.0) + */ +export const gray = /* @__PURE__ */ new Rgba( + 0.8274509803921568, + 0.8431372549019608, + 0.8117647058823529, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(186, 189, 182, 1.0) + */ +export const dark_gray = /* @__PURE__ */ new Rgba( + 0.7294117647058823, + 0.7411764705882353, + 0.7137254901960784, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(136, 138, 133, 1.0) + */ +export const light_charcoal = /* @__PURE__ */ new Rgba( + 0.5333333333333333, + 0.5411764705882353, + 0.5215686274509804, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(85, 87, 83, 1.0) + */ +export const charcoal = /* @__PURE__ */ new Rgba( + 0.3333333333333333, + 0.3411764705882353, + 0.3254901960784314, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(46, 52, 54, 1.0) + */ +export const dark_charcoal = /* @__PURE__ */ new Rgba( + 0.1803921568627451, + 0.20392156862745098, + 0.21176470588235294, + 1.0, +); + +/** + * A `Colour` reprsenting the colour RGBA(255, 175, 243, 1.0) + */ +export const pink = /* @__PURE__ */ new Rgba( + 1.0, + 0.6862745098039216, + 0.9529411764705882, + 1.0, +); diff --git a/build/dev/javascript/gleam_community_colour/gleam_community/colour/accessibility.mjs b/build/dev/javascript/gleam_community_colour/gleam_community/colour/accessibility.mjs new file mode 100644 index 0000000..0c56e21 --- /dev/null +++ b/build/dev/javascript/gleam_community_colour/gleam_community/colour/accessibility.mjs @@ -0,0 +1,146 @@ +import * as $float from "../../../gleam_stdlib/gleam/float.mjs"; +import * as $list from "../../../gleam_stdlib/gleam/list.mjs"; +import { Ok, makeError, divideFloat } from "../../gleam.mjs"; +import * as $colour from "../../gleam_community/colour.mjs"; + +const FILEPATH = "src/gleam_community/colour/accessibility.gleam"; + +function intensity(colour_value) { + let $ = true; + if (colour_value <= 0.03928) { + return colour_value / 12.92; + } else { + let $1 = $float.power((colour_value + 0.055) / 1.055, 2.4); + let i; + if ($1 instanceof Ok) { + i = $1[0]; + } else { + throw makeError( + "let_assert", + FILEPATH, + "gleam_community/colour/accessibility", + 62, + "intensity", + "Pattern match failed, no pattern matched the value.", + { + value: $1, + start: 2399, + end: 2470, + pattern_start: 2410, + pattern_end: 2415 + } + ) + } + return i; + } +} + +/** + * Returns the relative brightness of the given `Colour` as a `Float` between + * 0.0, and 1.0 with 0.0 being the darkest possible colour and 1.0 being the lightest. + * + *
+ * Example: + * + * ```gleam + * fn example() { + * luminance(colour.white) // 1.0 + * } + * ``` + *
+ * + * + */ +export function luminance(colour) { + let $ = $colour.to_rgba(colour); + let r; + let g; + let b; + r = $[0]; + g = $[1]; + b = $[2]; + let r_intensity = intensity(r); + let g_intensity = intensity(g); + let b_intensity = intensity(b); + return ((0.2126 * r_intensity) + (0.7152 * g_intensity)) + (0.0722 * b_intensity); +} + +/** + * Returns the contrast between two `Colour` values as a `Float` between 1.0, + * and 21.0 with 1.0 being no contrast and, 21.0 being the highest possible contrast. + * + *
+ * Example: + * + * ```gleam + * fn example() { + * contrast_ratio(between: colour.white, and: colour.black) // 21.0 + * } + * ``` + *
+ * + * + */ +export function contrast_ratio(colour_a, colour_b) { + let luminance_a = luminance(colour_a) + 0.05; + let luminance_b = luminance(colour_b) + 0.05; + let $ = luminance_a > luminance_b; + if ($) { + return divideFloat(luminance_a, luminance_b); + } else { + return divideFloat(luminance_b, luminance_a); + } +} + +/** + * Returns the `Colour` with the highest contrast between the base `Colour`, + * and and the other provided `Colour` values. + * + *
+ * Example: + * + * ```gleam + * fn example() { + * maximum_contrast( + * colour.yellow, + * [colour.white, colour.dark_blue, colour.green], + * ) + * } + * ``` + *
+ * + * + */ +export function maximum_contrast(base, colours) { + let _pipe = colours; + let _pipe$1 = $list.sort( + _pipe, + (colour_a, colour_b) => { + let contrast_a = contrast_ratio(base, colour_a); + let contrast_b = contrast_ratio(base, colour_b); + return $float.compare(contrast_b, contrast_a); + }, + ); + return $list.first(_pipe$1); +} diff --git a/build/dev/javascript/gleam_community_colour/gleam_community@colour.erl b/build/dev/javascript/gleam_community_colour/gleam_community@colour.erl new file mode 100644 index 0000000..faee070 --- /dev/null +++ b/build/dev/javascript/gleam_community_colour/gleam_community@colour.erl @@ -0,0 +1,1199 @@ +-module(gleam_community@colour). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). +-define(FILEPATH, "src/gleam_community/colour.gleam"). +-export([from_rgb255/3, from_rgb/3, from_rgba/4, from_hsla/4, from_hsl/3, from_rgb_hex/1, from_rgb_hex_string/1, from_rgba_hex/1, from_rgba_hex_string/1, to_rgba/1, to_hsla/1, to_css_rgba_string/1, to_rgba_hex/1, to_rgba_hex_string/1, to_rgb_hex/1, to_rgb_hex_string/1, encode/1, decoder/0]). +-export_type([colour/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC( + "\n" + " - **Types**\n" + " - [`Colour`](#Colour)\n" + " - [`Color`](#Color)\n" + " - **Constructors**\n" + " - [`from_rgb255`](#from_rgb255)\n" + " - [`from_rgb`](#from_rgb)\n" + " - [`from_rgba`](#from_rgba)\n" + " - [`from_hsl`](#from_hsl)\n" + " - [`from_hsla`](#from_hsla)\n" + " - [`from_rgb_hex`](#from_rgb_hex)\n" + " - [`from_rgba_hex`](#from_rgba_hex)\n" + " - [`from_rgb_hex_string`](#from_rgb_hex_string)\n" + " - [`from_rgba_hex_string`](#from_rgba_hex_string)\n" + " - **Conversions**\n" + " - [`to_rgba`](#to_rgba)\n" + " - [`to_hsla`](#hsla)\n" + " - [`to_css_rgba_string`](#to_css_rgba_string)\n" + " - [`to_rgba_hex_string`](#to_rgba_hex_string)\n" + " - [`to_rgb_hex_string`](#to_rgb_hex_string)\n" + " - [`to_rgba_hex`](#to_rgba_hex)\n" + " - [`to_rgb_hex`](#to_rgb_hex)\n" + " - **JSON**\n" + " - [`encode`](#encode)\n" + " - [`decoder`](#decoder)\n" + " - **Colours**\n" + " - [`light_red`](#light_red)\n" + " - [`red`](#red)\n" + " - [`dark_red`](#dark_red)\n" + " - [`light_orange`](#light_orange)\n" + " - [`orange`](#orange)\n" + " - [`dark_orange`](#dark_orange)\n" + " - [`light_yellow`](#light_yellow)\n" + " - [`yellow`](#yellow)\n" + " - [`dark_yellow`](#dark_yellow)\n" + " - [`light_green`](#light_green)\n" + " - [`green`](#green)\n" + " - [`dark_green`](#dark_green)\n" + " - [`light_blue`](#light_blue)\n" + " - [`blue`](#blue)\n" + " - [`dark_blue`](#dark_blue)\n" + " - [`light_purple`](#light_purple)\n" + " - [`purple`](#purple)\n" + " - [`dark_purple`](#dark_purple)\n" + " - [`light_brown`](#light_brown)\n" + " - [`brown`](#brown)\n" + " - [`dark_brown`](#dark_brown)\n" + " - [`black`](#black)\n" + " - [`white`](#white)\n" + " - [`light_grey`](#light_grey)\n" + " - [`grey`](#grey)\n" + " - [`dark_grey`](#dark_grey)\n" + " - [`light_gray`](#light_gray)\n" + " - [`gray`](#gray)\n" + " - [`dark_gray`](#dark_gray)\n" + " - [`light_charcoal`](#light_charcoal)\n" + " - [`charcoal`](#charcoal)\n" + " - [`dark_charcoal`](#dark_charcoal)\n" + " - [`pink`](#pink)\n" + "\n" + " ---\n" + "\n" + " This package was heavily inspired by the `elm-color` module.\n" + " The original source code can be found\n" + " here.\n" + "\n" + "
\n" + " The license of that package is produced below:\n" + "\n" + "\n" + " > MIT License\n" + "\n" + " > Copyright 2018 Aaron VonderHaar\n" + "\n" + " > Redistribution and use in source and binary forms, with or without modification,\n" + " are permitted provided that the following conditions are met:\n" + "\n" + " 1. Redistributions of source code must retain the above copyright notice,\n" + " this list of conditions and the following disclaimer.\n" + "\n" + " 2. Redistributions in binary form must reproduce the above copyright notice,\n" + " this list of conditions and the following disclaimer in the documentation\n" + " and/or other materials provided with the distribution.\n" + "\n" + " 3. Neither the name of the copyright holder nor the names of its contributors\n" + " may be used to endorse or promote products derived from this software without\n" + " specific prior written permission.\n" + "\n" + " > THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n" + " ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n" + " OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL\n" + " THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n" + " EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n" + " OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" + " THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n" + " ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" + "\n" + " > The above copyright notice and this permission notice shall be included in all\n" + " copies or substantial portions of the Software.\n" + "
\n" + "\n" +). + +-opaque colour() :: {rgba, float(), float(), float(), float()} | + {hsla, float(), float(), float(), float()}. + +-file("src/gleam_community/colour.gleam", 155). +-spec valid_colour_value(float()) -> {ok, float()} | {error, nil}. +valid_colour_value(C) -> + case (C > 1.0) orelse (C < +0.0) of + true -> + {error, nil}; + + false -> + {ok, C} + end. + +-file("src/gleam_community/colour.gleam", 162). +-spec hue_to_rgb(float(), float(), float()) -> float(). +hue_to_rgb(Hue, M1, M2) -> + H = case Hue of + _ when Hue < +0.0 -> + Hue + 1.0; + + _ when Hue > 1.0 -> + Hue - 1.0; + + _ -> + Hue + end, + H_t_6 = H * 6.0, + H_t_2 = H * 2.0, + H_t_3 = H * 3.0, + case H of + _ when H_t_6 < 1.0 -> + M1 + (((M2 - M1) * H) * 6.0); + + _ when H_t_2 < 1.0 -> + M2; + + _ when H_t_3 < 2.0 -> + M1 + (((M2 - M1) * ((2.0 / 3.0) - H)) * 6.0); + + _ -> + M1 + end. + +-file("src/gleam_community/colour.gleam", 181). +-spec hex_string_to_int(binary()) -> {ok, integer()} | {error, nil}. +hex_string_to_int(Hex_string) -> + Hex = case Hex_string of + <<"#"/utf8, Hex_number/binary>> -> + Hex_number; + + <<"0x"/utf8, Hex_number@1/binary>> -> + Hex_number@1; + + _ -> + Hex_string + end, + _pipe = Hex, + _pipe@1 = string:lowercase(_pipe), + _pipe@2 = gleam@string:to_graphemes(_pipe@1), + _pipe@3 = lists:reverse(_pipe@2), + gleam@list:index_fold( + _pipe@3, + {ok, 0}, + fun(Total, Char, Index) -> case Total of + {error, nil} -> + {error, nil}; + + {ok, V} -> + gleam@result:'try'(case Char of + <<"a"/utf8>> -> + {ok, 10}; + + <<"b"/utf8>> -> + {ok, 11}; + + <<"c"/utf8>> -> + {ok, 12}; + + <<"d"/utf8>> -> + {ok, 13}; + + <<"e"/utf8>> -> + {ok, 14}; + + <<"f"/utf8>> -> + {ok, 15}; + + _ -> + gleam_stdlib:parse_int(Char) + end, fun(Num) -> + gleam@result:'try'( + gleam@int:power(16, erlang:float(Index)), + fun(Base) -> + {ok, + V + erlang:round( + erlang:float(Num) * Base + )} + end + ) + end) + end end + ). + +-file("src/gleam_community/colour.gleam", 212). +-spec hsla_to_rgba(float(), float(), float(), float()) -> {float(), + float(), + float(), + float()}. +hsla_to_rgba(H, S, L, A) -> + M2 = case L =< 0.5 of + true -> + L * (S + 1.0); + + false -> + (L + S) - (L * S) + end, + M1 = (L * 2.0) - M2, + R = hue_to_rgb(H + (1.0 / 3.0), M1, M2), + G = hue_to_rgb(H, M1, M2), + B = hue_to_rgb(H - (1.0 / 3.0), M1, M2), + {R, G, B, A}. + +-file("src/gleam_community/colour.gleam", 232). +-spec rgba_to_hsla(float(), float(), float(), float()) -> {float(), + float(), + float(), + float()}. +rgba_to_hsla(R, G, B, A) -> + Min_colour = gleam@float:min(R, gleam@float:min(G, B)), + Max_colour = gleam@float:max(R, gleam@float:max(G, B)), + H1 = case true of + _ when Max_colour =:= R -> + gleam@float:divide(G - B, Max_colour - Min_colour); + + _ when Max_colour =:= G -> + _pipe = gleam@float:divide(B - R, Max_colour - Min_colour), + gleam@result:'try'(_pipe, fun(D) -> {ok, 2.0 + D} end); + + _ -> + _pipe@1 = gleam@float:divide(R - G, Max_colour - Min_colour), + gleam@result:'try'(_pipe@1, fun(D@1) -> {ok, 4.0 + D@1} end) + end, + H2 = case H1 of + {ok, V} -> + {ok, V * (1.0 / 6.0)}; + + _ -> + H1 + end, + H3 = case H2 of + {ok, V@1} when V@1 < +0.0 -> + V@1 + 1.0; + + {ok, V@2} -> + V@2; + + _ -> + +0.0 + end, + L = (Min_colour + Max_colour) / 2.0, + S = case true of + _ when Min_colour =:= Max_colour -> + +0.0; + + _ when L < 0.5 -> + case (Max_colour + Min_colour) of + +0.0 -> +0.0; + -0.0 -> -0.0; + Gleam@denominator -> (Max_colour - Min_colour) / Gleam@denominator + end; + + _ -> + case ((2.0 - Max_colour) - Min_colour) of + +0.0 -> +0.0; + -0.0 -> -0.0; + Gleam@denominator@1 -> (Max_colour - Min_colour) / Gleam@denominator@1 + end + end, + {H3, S, L, A}. + +-file("src/gleam_community/colour.gleam", 300). +?DOC( + " Returns a `Result(Colour)` created from the given 8 bit RGB values.\n" + "\n" + " Returns `Error(Nil)` if the supplied RGB values are greater than 255 or less than 0.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red) = from_rgb255(255, 0, 0)\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec from_rgb255(integer(), integer(), integer()) -> {ok, colour()} | + {error, nil}. +from_rgb255(Red, Green, Blue) -> + gleam@result:'try'( + begin + _pipe = Red, + _pipe@1 = erlang:float(_pipe), + _pipe@2 = gleam@float:divide(_pipe@1, 255.0), + gleam@result:'try'(_pipe@2, fun valid_colour_value/1) + end, + fun(R) -> + gleam@result:'try'( + begin + _pipe@3 = Green, + _pipe@4 = erlang:float(_pipe@3), + _pipe@5 = gleam@float:divide(_pipe@4, 255.0), + gleam@result:'try'(_pipe@5, fun valid_colour_value/1) + end, + fun(G) -> + gleam@result:'try'( + begin + _pipe@6 = Blue, + _pipe@7 = erlang:float(_pipe@6), + _pipe@8 = gleam@float:divide(_pipe@7, 255.0), + gleam@result:'try'( + _pipe@8, + fun valid_colour_value/1 + ) + end, + fun(B) -> {ok, {rgba, R, G, B, 1.0}} end + ) + end + ) + end + ). + +-file("src/gleam_community/colour.gleam", 348). +?DOC( + " Returns `Result(Colour)` created from the given RGB values.\n" + "\n" + " If the supplied RGB values are greater than 1.0 or less than 0.0 returns `Error(Nil)`\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red) = from_rgb(1.0, 0.0, 0.0)\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec from_rgb(float(), float(), float()) -> {ok, colour()} | {error, nil}. +from_rgb(Red, Green, Blue) -> + gleam@result:'try'( + valid_colour_value(Red), + fun(R) -> + gleam@result:'try'( + valid_colour_value(Green), + fun(G) -> + gleam@result:'try'( + valid_colour_value(Blue), + fun(B) -> {ok, {rgba, R, G, B, 1.0}} end + ) + end + ) + end + ). + +-file("src/gleam_community/colour.gleam", 383). +?DOC( + " Returns `Result(Colour)` created from the given RGBA values.\n" + "\n" + " Returns `Error(Nil)` if the supplied RGBA values are greater than 1.0 or less than 0.0.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red_half_opacity) = from_rbga(1.0, 0.0, 0.0, 0.5)\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec from_rgba(float(), float(), float(), float()) -> {ok, colour()} | + {error, nil}. +from_rgba(Red, Green, Blue, Alpha) -> + gleam@result:'try'( + valid_colour_value(Red), + fun(R) -> + gleam@result:'try'( + valid_colour_value(Green), + fun(G) -> + gleam@result:'try'( + valid_colour_value(Blue), + fun(B) -> + gleam@result:'try'( + valid_colour_value(Alpha), + fun(A) -> {ok, {rgba, R, G, B, A}} end + ) + end + ) + end + ) + end + ). + +-file("src/gleam_community/colour.gleam", 420). +?DOC( + " Returns `Result(Colour)` created from the given HSLA values.\n" + "\n" + " Returns `Error(Nil)`f the supplied HSLA values are greater than 1.0 or less than 0.0.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red_half_opacity) = from_hsla(0.0, 1.0, 0.5, 0.5)\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec from_hsla(float(), float(), float(), float()) -> {ok, colour()} | + {error, nil}. +from_hsla(Hue, Saturation, Lightness, Alpha) -> + gleam@result:'try'( + valid_colour_value(Hue), + fun(H) -> + gleam@result:'try'( + valid_colour_value(Saturation), + fun(S) -> + gleam@result:'try'( + valid_colour_value(Lightness), + fun(L) -> + gleam@result:'try'( + valid_colour_value(Alpha), + fun(A) -> {ok, {hsla, H, S, L, A}} end + ) + end + ) + end + ) + end + ). + +-file("src/gleam_community/colour.gleam", 457). +?DOC( + " Returns `Result(Colour)` created from the given HSL values.\n" + "\n" + " Returns `Error(Nil)` if the supplied HSL values are greater than 1.0 or less than 0.0.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red) = from_hsla(0.0, 1.0, 0.5)\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec from_hsl(float(), float(), float()) -> {ok, colour()} | {error, nil}. +from_hsl(Hue, Saturation, Lightness) -> + from_hsla(Hue, Saturation, Lightness, 1.0). + +-file("src/gleam_community/colour.gleam", 488). +?DOC( + " Returns a `Result(Colour)` created from the given hex `Int`.\n" + "\n" + " Returns `Error(Nil)` if the supplied hex `Int is greater than 0xffffff or less than 0x0.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red) = from_rgb_hex(0xff0000)\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec from_rgb_hex(integer()) -> {ok, colour()} | {error, nil}. +from_rgb_hex(Hex) -> + case (Hex > 16#ffffff) orelse (Hex < 0) of + true -> + {error, nil}; + + false -> + R = begin + _pipe = erlang:'bsr'(Hex, 16), + erlang:'band'(_pipe, 16#ff) + end, + G = begin + _pipe@1 = erlang:'bsr'(Hex, 8), + erlang:'band'(_pipe@1, 16#ff) + end, + B = erlang:'band'(Hex, 16#ff), + from_rgb255(R, G, B) + end. + +-file("src/gleam_community/colour.gleam", 527). +?DOC( + " Returns a `Result(Colour)` created from the given RGB hex `String`.\n" + "\n" + " Returns `Error(Nil)` if the supplied hex `String` is invalid, or greater than `\"#ffffff\" or less than `\"#0\"`\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red) = from_rgb_hex_string(\"#ff0000\")\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec from_rgb_hex_string(binary()) -> {ok, colour()} | {error, nil}. +from_rgb_hex_string(Hex_string) -> + gleam@result:'try'( + hex_string_to_int(Hex_string), + fun(Hex_int) -> from_rgb_hex(Hex_int) end + ). + +-file("src/gleam_community/colour.gleam", 585). +?DOC( + " Returns a `Result(Colour)` created from the given hex `Int`.\n" + "\n" + " Returns `Error(Nil)` if the supplied hex `Int is greater than 0xffffffff or less than 0x0.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red_half_opacity) = from_rgba_hex(0xff00007f)\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec from_rgba_hex(integer()) -> {ok, colour()} | {error, nil}. +from_rgba_hex(Hex) -> + case (Hex > 16#ffffffff) orelse (Hex < 0) of + true -> + {error, nil}; + + false -> + R@1 = case begin + _pipe = erlang:'bsr'(Hex, 24), + _pipe@1 = erlang:'band'(_pipe, 16#ff), + _pipe@2 = erlang:float(_pipe@1), + gleam@float:divide(_pipe@2, 255.0) + end of + {ok, R} -> R; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Pattern match failed, no pattern matched the value."/utf8>>, + file => <>, + module => <<"gleam_community/colour"/utf8>>, + function => <<"from_rgba_hex"/utf8>>, + line => 590, + value => _assert_fail, + start => 17111, + 'end' => 17260, + pattern_start => 17122, + pattern_end => 17127}) + end, + G@1 = case begin + _pipe@3 = erlang:'bsr'(Hex, 16), + _pipe@4 = erlang:'band'(_pipe@3, 16#ff), + _pipe@5 = erlang:float(_pipe@4), + gleam@float:divide(_pipe@5, 255.0) + end of + {ok, G} -> G; + _assert_fail@1 -> + erlang:error(#{gleam_error => let_assert, + message => <<"Pattern match failed, no pattern matched the value."/utf8>>, + file => <>, + module => <<"gleam_community/colour"/utf8>>, + function => <<"from_rgba_hex"/utf8>>, + line => 596, + value => _assert_fail@1, + start => 17332, + 'end' => 17481, + pattern_start => 17343, + pattern_end => 17348}) + end, + B@1 = case begin + _pipe@6 = erlang:'bsr'(Hex, 8), + _pipe@7 = erlang:'band'(_pipe@6, 16#ff), + _pipe@8 = erlang:float(_pipe@7), + gleam@float:divide(_pipe@8, 255.0) + end of + {ok, B} -> B; + _assert_fail@2 -> + erlang:error(#{gleam_error => let_assert, + message => <<"Pattern match failed, no pattern matched the value."/utf8>>, + file => <>, + module => <<"gleam_community/colour"/utf8>>, + function => <<"from_rgba_hex"/utf8>>, + line => 602, + value => _assert_fail@2, + start => 17553, + 'end' => 17701, + pattern_start => 17564, + pattern_end => 17569}) + end, + A@1 = case begin + _pipe@9 = erlang:'band'(Hex, 16#ff), + _pipe@10 = erlang:float(_pipe@9), + gleam@float:divide(_pipe@10, 255.0) + end of + {ok, A} -> A; + _assert_fail@3 -> + erlang:error(#{gleam_error => let_assert, + message => <<"Pattern match failed, no pattern matched the value."/utf8>>, + file => <>, + module => <<"gleam_community/colour"/utf8>>, + function => <<"from_rgba_hex"/utf8>>, + line => 608, + value => _assert_fail@3, + start => 17773, + 'end' => 17883, + pattern_start => 17784, + pattern_end => 17789}) + end, + from_rgba(R@1, G@1, B@1, A@1) + end. + +-file("src/gleam_community/colour.gleam", 556). +?DOC( + " Returns a `Result(Colour)` created from the given RGBA hex `String`.\n" + "\n" + " Returns `Error(Nil)` if the supplied hex `String` is invalid, or greater than `\"#ffffffff\" or less than `\"#0\"`\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red_half_opacity) = from_rgba_hex_string(\"#ff00007f\")\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec from_rgba_hex_string(binary()) -> {ok, colour()} | {error, nil}. +from_rgba_hex_string(Hex_string) -> + gleam@result:'try'( + hex_string_to_int(Hex_string), + fun(Hex_int) -> from_rgba_hex(Hex_int) end + ). + +-file("src/gleam_community/colour.gleam", 642). +?DOC( + " Returns `#(Float, Float, Float, Float)` representing the given `Colour`'s\n" + " R, G, B, and A values respectively.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red) = from_rgb255(255, 0, 0)\n" + " let #(r, g, b, a) = to_rgba(red)\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec to_rgba(colour()) -> {float(), float(), float(), float()}. +to_rgba(Colour) -> + case Colour of + {rgba, R, G, B, A} -> + {R, G, B, A}; + + {hsla, H, S, L, A@1} -> + hsla_to_rgba(H, S, L, A@1) + end. + +-file("src/gleam_community/colour.gleam", 672). +?DOC( + " Returns `#(Float, Float, Float, Float)` representing the given `Colour`'s\n" + " H, S, L, and A values respectively.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red) = from_rgb255(255, 0, 0)\n" + " let #(h, s, l, a) = to_hsla(red)\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec to_hsla(colour()) -> {float(), float(), float(), float()}. +to_hsla(Colour) -> + case Colour of + {hsla, H, S, L, A} -> + {H, S, L, A}; + + {rgba, R, G, B, A@1} -> + rgba_to_hsla(R, G, B, A@1) + end. + +-file("src/gleam_community/colour.gleam", 701). +?DOC( + " Returns an rgba formatted CSS `String` created from the given `Colour`.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red) = from_rgb255(255, 0, 0)\n" + " let css_red = to_css_rgba_string(red)\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec to_css_rgba_string(colour()) -> binary(). +to_css_rgba_string(Colour) -> + {R, G, B, A} = to_rgba(Colour), + Percent = fun(X) -> + P@1 = case begin + _pipe = X, + _pipe@1 = gleam@float:multiply(_pipe, 10000.0), + _pipe@2 = erlang:round(_pipe@1), + _pipe@3 = erlang:float(_pipe@2), + gleam@float:divide(_pipe@3, 100.0) + end of + {ok, P} -> P; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Pattern match failed, no pattern matched the value."/utf8>>, + file => <>, + module => <<"gleam_community/colour"/utf8>>, + function => <<"to_css_rgba_string"/utf8>>, + line => 706, + value => _assert_fail, + start => 20510, + 'end' => 20646, + pattern_start => 20521, + pattern_end => 20526}) + end, + P@1 + end, + Round_to = fun(X@1) -> + R@2 = case begin + _pipe@4 = X@1, + _pipe@5 = gleam@float:multiply(_pipe@4, 1000.0), + _pipe@6 = erlang:round(_pipe@5), + _pipe@7 = erlang:float(_pipe@6), + gleam@float:divide(_pipe@7, 1000.0) + end of + {ok, R@1} -> R@1; + _assert_fail@1 -> + erlang:error(#{gleam_error => let_assert, + message => <<"Pattern match failed, no pattern matched the value."/utf8>>, + file => <>, + module => <<"gleam_community/colour"/utf8>>, + function => <<"to_css_rgba_string"/utf8>>, + line => 718, + value => _assert_fail@1, + start => 20768, + 'end' => 20903, + pattern_start => 20779, + pattern_end => 20784}) + end, + R@2 + end, + gleam@string:join( + [<<"rgba("/utf8>>, + <<(gleam_stdlib:float_to_string(Percent(R)))/binary, "%,"/utf8>>, + <<(gleam_stdlib:float_to_string(Percent(G)))/binary, "%,"/utf8>>, + <<(gleam_stdlib:float_to_string(Percent(B)))/binary, "%,"/utf8>>, + gleam_stdlib:float_to_string(Round_to(A)), + <<")"/utf8>>], + <<""/utf8>> + ). + +-file("src/gleam_community/colour.gleam", 829). +?DOC( + " Returns an hex `Int` created from the given `Colour`.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red) = from_rgba(1.0, 0.0, 0.0, 1.0)\n" + " let red_hex_int = to_rgba_hex(red)\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec to_rgba_hex(colour()) -> integer(). +to_rgba_hex(Colour) -> + {R, G, B, A} = to_rgba(Colour), + Red = begin + _pipe = R * 255.0, + _pipe@1 = erlang:round(_pipe), + erlang:'bsl'(_pipe@1, 24) + end, + Green = begin + _pipe@2 = G * 255.0, + _pipe@3 = erlang:round(_pipe@2), + erlang:'bsl'(_pipe@3, 16) + end, + Blue = begin + _pipe@4 = B * 255.0, + _pipe@5 = erlang:round(_pipe@4), + erlang:'bsl'(_pipe@5, 8) + end, + Alpha = begin + _pipe@6 = A * 255.0, + erlang:round(_pipe@6) + end, + ((Red + Green) + Blue) + Alpha. + +-file("src/gleam_community/colour.gleam", 763). +?DOC( + " Returns an rgba hex formatted `String` created from the given `Colour`.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red) = from_rgba(1.0, 0.0, 0.0, 1.0)\n" + " let red_hex = to_rgba_hex_string(red)\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec to_rgba_hex_string(colour()) -> binary(). +to_rgba_hex_string(Colour) -> + Hex_string = begin + _pipe = to_rgba_hex(Colour), + gleam@int:to_base16(_pipe) + end, + case string:length(Hex_string) of + 8 -> + Hex_string; + + L -> + <<(gleam@string:repeat(<<"0"/utf8>>, 8 - L))/binary, + Hex_string/binary>> + end. + +-file("src/gleam_community/colour.gleam", 876). +?DOC( + " Returns a rgb hex `Int` created from the given `Colour`.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red) = from_rgba(255, 0, 0)\n" + " let red_hex_int = to_rgb_hex(red)\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec to_rgb_hex(colour()) -> integer(). +to_rgb_hex(Colour) -> + {R, G, B, _} = to_rgba(Colour), + Red = begin + _pipe = R * 255.0, + _pipe@1 = erlang:round(_pipe), + erlang:'bsl'(_pipe@1, 16) + end, + Green = begin + _pipe@2 = G * 255.0, + _pipe@3 = erlang:round(_pipe@2), + erlang:'bsl'(_pipe@3, 8) + end, + Blue = begin + _pipe@4 = B * 255.0, + erlang:round(_pipe@4) + end, + (Red + Green) + Blue. + +-file("src/gleam_community/colour.gleam", 796). +?DOC( + " Returns an rgb hex formatted `String` created from the given `Colour`.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red) = from_rgba(255, 0, 0)\n" + " let red_hex = to_rgb_hex_string(red)\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec to_rgb_hex_string(colour()) -> binary(). +to_rgb_hex_string(Colour) -> + Hex_string = begin + _pipe = to_rgb_hex(Colour), + gleam@int:to_base16(_pipe) + end, + case string:length(Hex_string) of + 6 -> + Hex_string; + + L -> + <<(gleam@string:repeat(<<"0"/utf8>>, 6 - L))/binary, + Hex_string/binary>> + end. + +-file("src/gleam_community/colour.gleam", 918). +-spec encode_rgba(float(), float(), float(), float()) -> gleam@json:json(). +encode_rgba(R, G, B, A) -> + gleam@json:object( + [{<<"r"/utf8>>, gleam@json:float(R)}, + {<<"g"/utf8>>, gleam@json:float(G)}, + {<<"b"/utf8>>, gleam@json:float(B)}, + {<<"a"/utf8>>, gleam@json:float(A)}] + ). + +-file("src/gleam_community/colour.gleam", 927). +-spec encode_hsla(float(), float(), float(), float()) -> gleam@json:json(). +encode_hsla(H, S, L, A) -> + gleam@json:object( + [{<<"h"/utf8>>, gleam@json:float(H)}, + {<<"s"/utf8>>, gleam@json:float(S)}, + {<<"l"/utf8>>, gleam@json:float(L)}, + {<<"a"/utf8>>, gleam@json:float(A)}] + ). + +-file("src/gleam_community/colour.gleam", 911). +?DOC( + " Encodes a `Colour` value as a Gleam [`Json`](https://hexdocs.pm/gleam_json/gleam/json.html#Json)\n" + " value. You'll need this if you want to send a `Colour` value over the network\n" + " in a HTTP request or response, for example.\n" + "\n" + " \n" +). +-spec encode(colour()) -> gleam@json:json(). +encode(Colour) -> + case Colour of + {rgba, R, G, B, A} -> + encode_rgba(R, G, B, A); + + {hsla, H, S, L, A@1} -> + encode_hsla(H, S, L, A@1) + end. + +-file("src/gleam_community/colour.gleam", 952). +-spec rgba_decoder() -> gleam@dynamic@decode:decoder(colour()). +rgba_decoder() -> + gleam@dynamic@decode:field( + <<"r"/utf8>>, + {decoder, fun gleam@dynamic@decode:decode_float/1}, + fun(R) -> + gleam@dynamic@decode:field( + <<"g"/utf8>>, + {decoder, fun gleam@dynamic@decode:decode_float/1}, + fun(G) -> + gleam@dynamic@decode:field( + <<"b"/utf8>>, + {decoder, fun gleam@dynamic@decode:decode_float/1}, + fun(B) -> + gleam@dynamic@decode:field( + <<"a"/utf8>>, + {decoder, + fun gleam@dynamic@decode:decode_float/1}, + fun(A) -> + gleam@dynamic@decode:success( + {rgba, R, G, B, A} + ) + end + ) + end + ) + end + ) + end + ). + +-file("src/gleam_community/colour.gleam", 961). +-spec hsla_decoder() -> gleam@dynamic@decode:decoder(colour()). +hsla_decoder() -> + gleam@dynamic@decode:field( + <<"h"/utf8>>, + {decoder, fun gleam@dynamic@decode:decode_float/1}, + fun(H) -> + gleam@dynamic@decode:field( + <<"s"/utf8>>, + {decoder, fun gleam@dynamic@decode:decode_float/1}, + fun(S) -> + gleam@dynamic@decode:field( + <<"l"/utf8>>, + {decoder, fun gleam@dynamic@decode:decode_float/1}, + fun(L) -> + gleam@dynamic@decode:field( + <<"a"/utf8>>, + {decoder, + fun gleam@dynamic@decode:decode_float/1}, + fun(A) -> + gleam@dynamic@decode:success( + {hsla, H, S, L, A} + ) + end + ) + end + ) + end + ) + end + ). + +-file("src/gleam_community/colour.gleam", 948). +?DOC( + " Attempt to decode some [`Dynamic`](https://hexdocs.pm/gleam_stdlib/gleam/dynamic.html#Dynamic)\n" + " value into a `Colour`. Most often you'll use this to decode some JSON.\n" + "\n" + " \n" +). +-spec decoder() -> gleam@dynamic@decode:decoder(colour()). +decoder() -> + gleam@dynamic@decode:one_of(rgba_decoder(), [hsla_decoder()]). diff --git a/build/dev/javascript/gleam_community_colour/gleam_community@colour@accessibility.erl b/build/dev/javascript/gleam_community_colour/gleam_community@colour@accessibility.erl new file mode 100644 index 0000000..35c1d67 --- /dev/null +++ b/build/dev/javascript/gleam_community_colour/gleam_community@colour@accessibility.erl @@ -0,0 +1,203 @@ +-module(gleam_community@colour@accessibility). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). +-define(FILEPATH, "src/gleam_community/colour/accessibility.gleam"). +-export([luminance/1, contrast_ratio/2, maximum_contrast/2]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC( + " \n" + " - **Accessibility**\n" + " - [`luminance`](#luminance)\n" + " - [`contrast_ratio`](#contrast_ratio)\n" + " - [`maximum_contrast`](#maximum_contrast)\n" + "\n" + " ---\n" + "\n" + " This package was heavily inspired by the `elm-color-extra` module.\n" + " The original source code can be found\n" + " here.\n" + "\n" + "
\n" + " The license of that package is produced below:\n" + " \n" + " \n" + " > MIT License\n" + "\n" + " > Copyright (c) 2016 Andreas Köberle\n" + "\n" + " > Permission is hereby granted, free of charge, to any person obtaining a copy\n" + " of this software and associated documentation files (the \"Software\"), to deal\n" + " in the Software without restriction, including without limitation the rights\n" + " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n" + " copies of the Software, and to permit persons to whom the Software is\n" + " furnished to do so, subject to the following conditions:\n" + "\n" + " > The above copyright notice and this permission notice shall be included in all\n" + " copies or substantial portions of the Software.\n" + "\n" + " > THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n" + " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n" + " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n" + " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n" + " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n" + " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n" + " SOFTWARE.\n" + "\n" + "
\n" + "\n" +). + +-file("src/gleam_community/colour/accessibility.gleam", 56). +-spec intensity(float()) -> float(). +intensity(Colour_value) -> + case true of + _ when Colour_value =< 0.03928 -> + Colour_value / 12.92; + + _ -> + I@1 = case gleam@float:power((Colour_value + 0.055) / 1.055, 2.4) of + {ok, I} -> I; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Pattern match failed, no pattern matched the value."/utf8>>, + file => <>, + module => <<"gleam_community/colour/accessibility"/utf8>>, + function => <<"intensity"/utf8>>, + line => 62, + value => _assert_fail, + start => 2399, + 'end' => 2470, + pattern_start => 2410, + pattern_end => 2415}) + end, + I@1 + end. + +-file("src/gleam_community/colour/accessibility.gleam", 92). +?DOC( + " Returns the relative brightness of the given `Colour` as a `Float` between\n" + " 0.0, and 1.0 with 0.0 being the darkest possible colour and 1.0 being the lightest.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " luminance(colour.white) // 1.0\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec luminance(gleam_community@colour:colour()) -> float(). +luminance(Colour) -> + {R, G, B, _} = gleam_community@colour:to_rgba(Colour), + R_intensity = intensity(R), + G_intensity = intensity(G), + B_intensity = intensity(B), + ((0.2126 * R_intensity) + (0.7152 * G_intensity)) + (0.0722 * B_intensity). + +-file("src/gleam_community/colour/accessibility.gleam", 125). +?DOC( + " Returns the contrast between two `Colour` values as a `Float` between 1.0,\n" + " and 21.0 with 1.0 being no contrast and, 21.0 being the highest possible contrast.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " contrast_ratio(between: colour.white, and: colour.black) // 21.0\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec contrast_ratio( + gleam_community@colour:colour(), + gleam_community@colour:colour() +) -> float(). +contrast_ratio(Colour_a, Colour_b) -> + Luminance_a = luminance(Colour_a) + 0.05, + Luminance_b = luminance(Colour_b) + 0.05, + case Luminance_a > Luminance_b of + true -> + case Luminance_b of + +0.0 -> +0.0; + -0.0 -> -0.0; + Gleam@denominator -> Luminance_a / Gleam@denominator + end; + + false -> + case Luminance_a of + +0.0 -> +0.0; + -0.0 -> -0.0; + Gleam@denominator@1 -> Luminance_b / Gleam@denominator@1 + end + end. + +-file("src/gleam_community/colour/accessibility.gleam", 161). +?DOC( + " Returns the `Colour` with the highest contrast between the base `Colour`,\n" + " and and the other provided `Colour` values.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " maximum_contrast(\n" + " colour.yellow,\n" + " [colour.white, colour.dark_blue, colour.green],\n" + " )\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec maximum_contrast( + gleam_community@colour:colour(), + list(gleam_community@colour:colour()) +) -> {ok, gleam_community@colour:colour()} | {error, nil}. +maximum_contrast(Base, Colours) -> + _pipe = Colours, + _pipe@1 = gleam@list:sort( + _pipe, + fun(Colour_a, Colour_b) -> + Contrast_a = contrast_ratio(Base, Colour_a), + Contrast_b = contrast_ratio(Base, Colour_b), + gleam@float:compare(Contrast_b, Contrast_a) + end + ), + gleam@list:first(_pipe@1). diff --git a/build/dev/javascript/gleam_json/_gleam_artefacts/gleam@json.cache b/build/dev/javascript/gleam_json/_gleam_artefacts/gleam@json.cache new file mode 100644 index 0000000000000000000000000000000000000000..a74dd80e3f3cf39c985b61a32ae1999cf41e886b GIT binary patch literal 18027 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 U=FQ9u48~lnf}o~D4o5Ku0L()w3IG5A literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_json/_gleam_artefacts/gleam@json.cache_inline b/build/dev/javascript/gleam_json/_gleam_artefacts/gleam@json.cache_inline new file mode 100644 index 0000000000000000000000000000000000000000..1b1cb4d44c57c2d7a5122870fa6ac3e62ff7e94e GIT binary patch literal 8 JcmZR80ssIA00961 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_json/_gleam_artefacts/gleam@json.cache_meta b/build/dev/javascript/gleam_json/_gleam_artefacts/gleam@json.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..00dd8197b7222766439b5ca90ee6c6f49f62c337 GIT binary patch literal 1554 zcmYelot(h{1&ka}20xTe&q+;RFK%gCL5&;*z4wy!7~zqSVxr3=9mP85kIh zTV$246+h0gWrV6^U|<0G2jpy!vp^02Sp+f=q~{9*1H)Ga1_owE1_l;J1_o(H1_mQW z1_p0N28Ikq28PLu3=C5l85pK9GB7-4WMB|wVqlPDVqnl@Vqnl^VqmahVqnN)Vqj=s zVqoZCVqoZDVqlob#K3TziGkrc69dD0CI*JjOdtyw7!;Tp7*d%T7;>2z7z&vg80wiB z7^W~YFzjGvV7SK2!0?xuf#E+h0|NsK1A_ny1A{CJ1A`F@149@K149W51H)Vv28Nw1 z3=F$j7#Q}jFfd$TVPLq+!ocu}g@Hkgm4U&8m4P9fm4RUbD+9weRtAP0tPBi0Ss55U zu`)2|u`w{{voSE#vN14hVq;*~#>T*KjE#Ze6dMD>bv6bD5q1U!HFgFDJ$42LBX$M` zdv*qfGIj=rY3vLP^Vk^}7O^uhtY>FnxWUfAAjiSLpvJ+#pvA$!V9UY45W>O0P{F~# zFpYzO;UWhE!#55F1{qEU202a!26;{f247AFhAEs34BI&w818d2FmQ7*Fz|CRFbHxn zFbHumFj#RhFxYW1Fm!V1_ozt z28JAN28Kp%28L#C28I@H28LbS3=F@y85rbv7#QSv7#Mtb7#IS07#QaAFfcsfVPN>p z!@%&Lhk=2gmw`c;mw`cnmw_RGmw}<2mw}<4mw};~mw{m?F9X9bUIqp&J_ZJDJ_d#~ zJ_d#?J_d#=J_d$5J_d$vJ_d%hd<+cx_!tp%yz>p@$z>qG;z%WUWf#Iwm1H*Gc28I`c3=C{S z3=CXC3=C323=DEY3=G;r3=Hl<3=Dxn3=EM%3=FYC3=B;|3=C6*7#OArF)&OQVqiET z#K7=hh=D;}n1MkVFre^ z!VC;Mg&7!j3o|h65oTa`A3^zm>7;cI(FbIh;Fo=mUFsO+!FldP}Fqn%mFa(G(FqDWfFia6+V3;Guz;IfO zf#I?k1H*MO1_pj{1_l*z1_m{81_pI;28K{^28KRy28Pq(3=C()85ll_GcbG=XJBBJ zU|`^sU|K$3yspCkhVgA@Y;qZ9*! zo)iN^niK=WJShf-`BDrFuca6mM5P%RB&8V`G^H6Bbfp;>tfUzj`lJ~c)=D!lY?WqU m*eT7xa7>zk;g&Q5!)Iv*1|As(1_>Dkh71`7hAb$Cm3{!JuHx + new UnexpectedEndOfInput(); +export const DecodeError$isUnexpectedEndOfInput = (value) => + value instanceof UnexpectedEndOfInput; + +export class UnexpectedByte extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} +export const DecodeError$UnexpectedByte = ($0) => new UnexpectedByte($0); +export const DecodeError$isUnexpectedByte = (value) => + value instanceof UnexpectedByte; +export const DecodeError$UnexpectedByte$0 = (value) => value[0]; + +export class UnexpectedSequence extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} +export const DecodeError$UnexpectedSequence = ($0) => + new UnexpectedSequence($0); +export const DecodeError$isUnexpectedSequence = (value) => + value instanceof UnexpectedSequence; +export const DecodeError$UnexpectedSequence$0 = (value) => value[0]; + +export class UnableToDecode extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} +export const DecodeError$UnableToDecode = ($0) => new UnableToDecode($0); +export const DecodeError$isUnableToDecode = (value) => + value instanceof UnableToDecode; +export const DecodeError$UnableToDecode$0 = (value) => value[0]; + +function do_parse(json, decoder) { + return $result.try$( + decode_string(json), + (dynamic_value) => { + let _pipe = $decode.run(dynamic_value, decoder); + return $result.map_error( + _pipe, + (var0) => { return new UnableToDecode(var0); }, + ); + }, + ); +} + +/** + * Decode a JSON string into dynamically typed data which can be decoded into + * typed data with the `gleam/dynamic` module. + * + * ## Examples + * + * ```gleam + * > parse("[1,2,3]", decode.list(of: decode.int)) + * Ok([1, 2, 3]) + * ``` + * + * ```gleam + * > parse("[", decode.list(of: decode.int)) + * Error(UnexpectedEndOfInput) + * ``` + * + * ```gleam + * > parse("1", decode.string) + * Error(UnableToDecode([decode.DecodeError("String", "Int", [])])) + * ``` + */ +export function parse(json, decoder) { + return do_parse(json, decoder); +} + +function decode_to_dynamic(json) { + let $ = $bit_array.to_string(json); + if ($ instanceof Ok) { + let string$1 = $[0]; + return decode_string(string$1); + } else { + return new Error(new UnexpectedByte("")); + } +} + +/** + * Decode a JSON bit string into dynamically typed data which can be decoded + * into typed data with the `gleam/dynamic` module. + * + * ## Examples + * + * ```gleam + * > parse_bits(<<"[1,2,3]">>, decode.list(of: decode.int)) + * Ok([1, 2, 3]) + * ``` + * + * ```gleam + * > parse_bits(<<"[">>, decode.list(of: decode.int)) + * Error(UnexpectedEndOfInput) + * ``` + * + * ```gleam + * > parse_bits(<<"1">>, decode.string) + * Error(UnableToDecode([decode.DecodeError("String", "Int", [])])), + * ``` + */ +export function parse_bits(json, decoder) { + return $result.try$( + decode_to_dynamic(json), + (dynamic_value) => { + let _pipe = $decode.run(dynamic_value, decoder); + return $result.map_error( + _pipe, + (var0) => { return new UnableToDecode(var0); }, + ); + }, + ); +} + +/** + * Convert a JSON value into a string. + * + * Where possible prefer the `to_string_tree` function as it is faster than + * this function, and BEAM VM IO is optimised for sending `StringTree` data. + * + * ## Examples + * + * ```gleam + * > to_string(array([1, 2, 3], of: int)) + * "[1,2,3]" + * ``` + */ +export function to_string(json) { + return do_to_string(json); +} + +/** + * Encode a string into JSON, using normal JSON escaping. + * + * ## Examples + * + * ```gleam + * > to_string(string("Hello!")) + * "\"Hello!\"" + * ``` + */ +export function string(input) { + return do_string(input); +} + +/** + * Encode a bool into JSON. + * + * ## Examples + * + * ```gleam + * > to_string(bool(False)) + * "false" + * ``` + */ +export function bool(input) { + return do_bool(input); +} + +/** + * Encode an int into JSON. + * + * ## Examples + * + * ```gleam + * > to_string(int(50)) + * "50" + * ``` + */ +export function int(input) { + return do_int(input); +} + +/** + * Encode a float into JSON. + * + * ## Examples + * + * ```gleam + * > to_string(float(4.7)) + * "4.7" + * ``` + */ +export function float(input) { + return do_float(input); +} + +/** + * The JSON value null. + * + * ## Examples + * + * ```gleam + * > to_string(null()) + * "null" + * ``` + */ +export function null$() { + return do_null(); +} + +/** + * Encode an optional value into JSON, using null if it is the `None` variant. + * + * ## Examples + * + * ```gleam + * > to_string(nullable(Some(50), of: int)) + * "50" + * ``` + * + * ```gleam + * > to_string(nullable(None, of: int)) + * "null" + * ``` + */ +export function nullable(input, inner_type) { + if (input instanceof Some) { + let value = input[0]; + return inner_type(value); + } else { + return null$(); + } +} + +/** + * Encode a list of key-value pairs into a JSON object. + * + * ## Examples + * + * ```gleam + * > to_string(object([ + * #("game", string("Pac-Man")), + * #("score", int(3333360)), + * ])) + * "{\"game\":\"Pac-Mac\",\"score\":3333360}" + * ``` + */ +export function object(entries) { + return do_object(entries); +} + +/** + * Encode a list of JSON values into a JSON array. + * + * ## Examples + * + * ```gleam + * > to_string(preprocessed_array([int(1), float(2.0), string("3")])) + * "[1, 2.0, \"3\"]" + * ``` + */ +export function preprocessed_array(from) { + return do_preprocessed_array(from); +} + +/** + * Encode a list into a JSON array. + * + * ## Examples + * + * ```gleam + * > to_string(array([1, 2, 3], of: int)) + * "[1, 2, 3]" + * ``` + */ +export function array(entries, inner_type) { + let _pipe = entries; + let _pipe$1 = $list.map(_pipe, inner_type); + return preprocessed_array(_pipe$1); +} + +/** + * Encode a Dict into a JSON object using the supplied functions to encode + * the keys and the values respectively. + * + * ## Examples + * + * ```gleam + * > to_string(dict(dict.from_list([ #(3, 3.0), #(4, 4.0)]), int.to_string, float) + * "{\"3\": 3.0, \"4\": 4.0}" + * ``` + */ +export function dict(dict, keys, values) { + return object( + $dict.fold( + dict, + toList([]), + (acc, k, v) => { return listPrepend([keys(k), values(v)], acc); }, + ), + ); +} diff --git a/build/dev/javascript/gleam_json/gleam@json.erl b/build/dev/javascript/gleam_json/gleam@json.erl new file mode 100644 index 0000000..8a33e49 --- /dev/null +++ b/build/dev/javascript/gleam_json/gleam@json.erl @@ -0,0 +1,304 @@ +-module(gleam@json). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/json.gleam"). +-export([parse_bits/2, parse/2, to_string/1, to_string_tree/1, string/1, bool/1, int/1, float/1, null/0, nullable/2, object/1, preprocessed_array/1, array/2, dict/3]). +-export_type([json/0, decode_error/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +-type json() :: any(). + +-type decode_error() :: unexpected_end_of_input | + {unexpected_byte, binary()} | + {unexpected_sequence, binary()} | + {unable_to_decode, list(gleam@dynamic@decode:decode_error())}. + +-file("src/gleam/json.gleam", 88). +?DOC( + " Decode a JSON bit string into dynamically typed data which can be decoded\n" + " into typed data with the `gleam/dynamic` module.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " > parse_bits(<<\"[1,2,3]\">>, decode.list(of: decode.int))\n" + " Ok([1, 2, 3])\n" + " ```\n" + "\n" + " ```gleam\n" + " > parse_bits(<<\"[\">>, decode.list(of: decode.int))\n" + " Error(UnexpectedEndOfInput)\n" + " ```\n" + "\n" + " ```gleam\n" + " > parse_bits(<<\"1\">>, decode.string)\n" + " Error(UnableToDecode([decode.DecodeError(\"String\", \"Int\", [])])),\n" + " ```\n" +). +-spec parse_bits(bitstring(), gleam@dynamic@decode:decoder(DNO)) -> {ok, DNO} | + {error, decode_error()}. +parse_bits(Json, Decoder) -> + gleam@result:'try'( + gleam_json_ffi:decode(Json), + fun(Dynamic_value) -> + _pipe = gleam@dynamic@decode:run(Dynamic_value, Decoder), + gleam@result:map_error( + _pipe, + fun(Field@0) -> {unable_to_decode, Field@0} end + ) + end + ). + +-file("src/gleam/json.gleam", 47). +-spec do_parse(binary(), gleam@dynamic@decode:decoder(DNI)) -> {ok, DNI} | + {error, decode_error()}. +do_parse(Json, Decoder) -> + Bits = gleam_stdlib:identity(Json), + parse_bits(Bits, Decoder). + +-file("src/gleam/json.gleam", 39). +?DOC( + " Decode a JSON string into dynamically typed data which can be decoded into\n" + " typed data with the `gleam/dynamic` module.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " > parse(\"[1,2,3]\", decode.list(of: decode.int))\n" + " Ok([1, 2, 3])\n" + " ```\n" + "\n" + " ```gleam\n" + " > parse(\"[\", decode.list(of: decode.int))\n" + " Error(UnexpectedEndOfInput)\n" + " ```\n" + "\n" + " ```gleam\n" + " > parse(\"1\", decode.string)\n" + " Error(UnableToDecode([decode.DecodeError(\"String\", \"Int\", [])]))\n" + " ```\n" +). +-spec parse(binary(), gleam@dynamic@decode:decoder(DNE)) -> {ok, DNE} | + {error, decode_error()}. +parse(Json, Decoder) -> + do_parse(Json, Decoder). + +-file("src/gleam/json.gleam", 117). +?DOC( + " Convert a JSON value into a string.\n" + "\n" + " Where possible prefer the `to_string_tree` function as it is faster than\n" + " this function, and BEAM VM IO is optimised for sending `StringTree` data.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " > to_string(array([1, 2, 3], of: int))\n" + " \"[1,2,3]\"\n" + " ```\n" +). +-spec to_string(json()) -> binary(). +to_string(Json) -> + gleam_json_ffi:json_to_string(Json). + +-file("src/gleam/json.gleam", 140). +?DOC( + " Convert a JSON value into a string tree.\n" + "\n" + " Where possible prefer this function to the `to_string` function as it is\n" + " slower than this function, and BEAM VM IO is optimised for sending\n" + " `StringTree` data.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " > to_string_tree(array([1, 2, 3], of: int))\n" + " string_tree.from_string(\"[1,2,3]\")\n" + " ```\n" +). +-spec to_string_tree(json()) -> gleam@string_tree:string_tree(). +to_string_tree(Json) -> + gleam_json_ffi:json_to_iodata(Json). + +-file("src/gleam/json.gleam", 151). +?DOC( + " Encode a string into JSON, using normal JSON escaping.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " > to_string(string(\"Hello!\"))\n" + " \"\\\"Hello!\\\"\"\n" + " ```\n" +). +-spec string(binary()) -> json(). +string(Input) -> + gleam_json_ffi:string(Input). + +-file("src/gleam/json.gleam", 168). +?DOC( + " Encode a bool into JSON.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " > to_string(bool(False))\n" + " \"false\"\n" + " ```\n" +). +-spec bool(boolean()) -> json(). +bool(Input) -> + gleam_json_ffi:bool(Input). + +-file("src/gleam/json.gleam", 185). +?DOC( + " Encode an int into JSON.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " > to_string(int(50))\n" + " \"50\"\n" + " ```\n" +). +-spec int(integer()) -> json(). +int(Input) -> + gleam_json_ffi:int(Input). + +-file("src/gleam/json.gleam", 202). +?DOC( + " Encode a float into JSON.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " > to_string(float(4.7))\n" + " \"4.7\"\n" + " ```\n" +). +-spec float(float()) -> json(). +float(Input) -> + gleam_json_ffi:float(Input). + +-file("src/gleam/json.gleam", 219). +?DOC( + " The JSON value null.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " > to_string(null())\n" + " \"null\"\n" + " ```\n" +). +-spec null() -> json(). +null() -> + gleam_json_ffi:null(). + +-file("src/gleam/json.gleam", 241). +?DOC( + " Encode an optional value into JSON, using null if it is the `None` variant.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " > to_string(nullable(Some(50), of: int))\n" + " \"50\"\n" + " ```\n" + "\n" + " ```gleam\n" + " > to_string(nullable(None, of: int))\n" + " \"null\"\n" + " ```\n" +). +-spec nullable(gleam@option:option(DNU), fun((DNU) -> json())) -> json(). +nullable(Input, Inner_type) -> + case Input of + {some, Value} -> + Inner_type(Value); + + none -> + null() + end. + +-file("src/gleam/json.gleam", 260). +?DOC( + " Encode a list of key-value pairs into a JSON object.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " > to_string(object([\n" + " #(\"game\", string(\"Pac-Man\")),\n" + " #(\"score\", int(3333360)),\n" + " ]))\n" + " \"{\\\"game\\\":\\\"Pac-Mac\\\",\\\"score\\\":3333360}\"\n" + " ```\n" +). +-spec object(list({binary(), json()})) -> json(). +object(Entries) -> + gleam_json_ffi:object(Entries). + +-file("src/gleam/json.gleam", 292). +?DOC( + " Encode a list of JSON values into a JSON array.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " > to_string(preprocessed_array([int(1), float(2.0), string(\"3\")]))\n" + " \"[1, 2.0, \\\"3\\\"]\"\n" + " ```\n" +). +-spec preprocessed_array(list(json())) -> json(). +preprocessed_array(From) -> + gleam_json_ffi:array(From). + +-file("src/gleam/json.gleam", 277). +?DOC( + " Encode a list into a JSON array.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " > to_string(array([1, 2, 3], of: int))\n" + " \"[1, 2, 3]\"\n" + " ```\n" +). +-spec array(list(DNY), fun((DNY) -> json())) -> json(). +array(Entries, Inner_type) -> + _pipe = Entries, + _pipe@1 = gleam@list:map(_pipe, Inner_type), + preprocessed_array(_pipe@1). + +-file("src/gleam/json.gleam", 310). +?DOC( + " Encode a Dict into a JSON object using the supplied functions to encode\n" + " the keys and the values respectively.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " > to_string(dict(dict.from_list([ #(3, 3.0), #(4, 4.0)]), int.to_string, float)\n" + " \"{\\\"3\\\": 3.0, \\\"4\\\": 4.0}\"\n" + " ```\n" +). +-spec dict( + gleam@dict:dict(DOC, DOD), + fun((DOC) -> binary()), + fun((DOD) -> json()) +) -> json(). +dict(Dict, Keys, Values) -> + object( + gleam@dict:fold( + Dict, + [], + fun(Acc, K, V) -> [{Keys(K), Values(V)} | Acc] end + ) + ). diff --git a/build/dev/javascript/gleam_json/gleam_json_ffi.erl b/build/dev/javascript/gleam_json/gleam_json_ffi.erl new file mode 100644 index 0000000..06a26a0 --- /dev/null +++ b/build/dev/javascript/gleam_json/gleam_json_ffi.erl @@ -0,0 +1,66 @@ +-module(gleam_json_ffi). + +-export([ + decode/1, json_to_iodata/1, json_to_string/1, int/1, float/1, string/1, + bool/1, null/0, array/1, object/1 +]). + +-if(?OTP_RELEASE < 27). +-define(bad_version, + error({erlang_otp_27_required, << "Insufficient Erlang/OTP version. + +`gleam_json` uses the Erlang `json` module introduced in Erlang/OTP 27. +You are using Erlang/OTP "/utf8, (integer_to_binary(?OTP_RELEASE))/binary, " +Please upgrade your Erlang install or downgrade to `gleam_json` v1.0.1. +"/utf8>>})). + +decode(_) -> ?bad_version. +json_to_iodata(_) -> ?bad_version. +json_to_string(_) -> ?bad_version. +int(_) -> ?bad_version. +float(_) -> ?bad_version. +string(_) -> ?bad_version. +bool(_) -> ?bad_version. +array(_) -> ?bad_version. +object(_) -> ?bad_version. +null() -> ?bad_version. +-else. + +decode(Json) -> + try + {ok, json:decode(Json)} + catch + error:unexpected_end -> {error, unexpected_end_of_input}; + error:{invalid_byte, Byte} -> {error, {unexpected_byte, hex(Byte)}}; + error:{unexpected_sequence, Byte} -> {error, {unexpected_sequence, Byte}} + end. + +hex(I) -> + H = list_to_binary(integer_to_list(I, 16)), + <<"0x"/utf8, H/binary>>. + +json_to_iodata(Json) -> + Json. + +json_to_string(Json) when is_binary(Json) -> + Json; +json_to_string(Json) when is_list(Json) -> + list_to_binary(Json). + +null() -> <<"null">>. +bool(true) -> <<"true">>; +bool(false) -> <<"false">>. +int(X) -> json:encode_integer(X). +float(X) -> json:encode_float(X). +string(X) -> json:encode_binary(X). + +array([]) -> <<"[]">>; +array([First | Rest]) -> [$[, First | array_loop(Rest)]. +array_loop([]) -> "]"; +array_loop([Elem | Rest]) -> [$,, Elem | array_loop(Rest)]. + +object(List) -> encode_object([[$,, string(Key), $: | Value] || {Key, Value} <- List]). +encode_object([]) -> <<"{}">>; +encode_object([[_Comma | Entry] | Rest]) -> ["{", Entry, Rest, "}"]. + +-endif. diff --git a/build/dev/javascript/gleam_json/gleam_json_ffi.mjs b/build/dev/javascript/gleam_json/gleam_json_ffi.mjs new file mode 100644 index 0000000..1d8d3ff --- /dev/null +++ b/build/dev/javascript/gleam_json/gleam_json_ffi.mjs @@ -0,0 +1,201 @@ +import { + Result$Ok, + Result$Error, + List$isNonEmpty, + List$NonEmpty$first, + List$NonEmpty$rest, +} from "./gleam.mjs"; +import { + DecodeError$UnexpectedByte, + DecodeError$UnexpectedEndOfInput, +} from "./gleam/json.mjs"; + +export function json_to_string(json) { + return JSON.stringify(json); +} + +export function object(entries) { + return Object.fromEntries(entries); +} + +export function identity(x) { + return x; +} + +export function array(list) { + const array = []; + while (List$isNonEmpty(list)) { + array.push(List$NonEmpty$first(list)); + list = List$NonEmpty$rest(list); + } + return array; +} + +export function do_null() { + return null; +} + +export function decode(string) { + try { + const result = JSON.parse(string); + return Result$Ok(result); + } catch (err) { + return Result$Error(getJsonDecodeError(err, string)); + } +} + +export function getJsonDecodeError(stdErr, json) { + if (isUnexpectedEndOfInput(stdErr)) return DecodeError$UnexpectedEndOfInput(); + return toUnexpectedByteError(stdErr, json); +} + +/** + * Matches unexpected end of input messages in: + * - Chromium (edge, chrome, node) + * - Spidermonkey (firefox) + * - JavascriptCore (safari) + * + * Note that Spidermonkey and JavascriptCore will both incorrectly report some + * UnexpectedByte errors as UnexpectedEndOfInput errors. For example: + * + * @example + * // in JavascriptCore + * JSON.parse('{"a"]: "b"}) + * // => JSON Parse error: Expected ':' before value + * + * JSON.parse('{"a"') + * // => JSON Parse error: Expected ':' before value + * + * // in Chromium (correct) + * JSON.parse('{"a"]: "b"}) + * // => Unexpected token ] in JSON at position 4 + * + * JSON.parse('{"a"') + * // => Unexpected end of JSON input + */ +function isUnexpectedEndOfInput(err) { + const unexpectedEndOfInputRegex = + /((unexpected (end|eof))|(end of data)|(unterminated string)|(json( parse error|\.parse)\: expected '(\:|\}|\])'))/i; + return unexpectedEndOfInputRegex.test(err.message); +} + +/** + * Converts a SyntaxError to an UnexpectedByte error based on the JS runtime. + * + * For Chromium, the unexpected byte and position are reported by the runtime. + * + * For JavascriptCore, only the unexpected byte is reported by the runtime, so + * there is no way to know which position that character is in unless we then + * parse the string again ourselves. So instead, the position is reported as 0. + * + * For Spidermonkey, the position is reported by the runtime as a line and column number + * and the unexpected byte is found using those coordinates. + */ +function toUnexpectedByteError(err, json) { + let converters = [ + v8UnexpectedByteError, + oldV8UnexpectedByteError, + jsCoreUnexpectedByteError, + spidermonkeyUnexpectedByteError, + ]; + + for (let converter of converters) { + let result = converter(err, json); + if (result) return result; + } + + return DecodeError$UnexpectedByte(""); +} + +/** + * Matches unexpected byte messages in: + * - V8 (edge, chrome, node) + * + * Matches the character but not the position as this is no longer reported by + * V8. Boo! + */ +function v8UnexpectedByteError(err) { + const regex = /unexpected token '(.)', ".+" is not valid JSON/i; + const match = regex.exec(err.message); + if (!match) return null; + const byte = toHex(match[1]); + return DecodeError$UnexpectedByte(byte); +} + +/** + * Matches unexpected byte messages in: + * - V8 (edge, chrome, node) + * + * No longer works in current versions of V8. + * + * Matches the character and its position. + */ +function oldV8UnexpectedByteError(err) { + const regex = /unexpected token (.) in JSON at position (\d+)/i; + const match = regex.exec(err.message); + if (!match) return null; + const byte = toHex(match[1]); + return DecodeError$UnexpectedByte(byte); +} + +/** + * Matches unexpected byte messages in: + * - Spidermonkey (firefox) + * + * Matches the position in a 2d grid only and not the character. + */ +function spidermonkeyUnexpectedByteError(err, json) { + const regex = + /(unexpected character|expected .*) at line (\d+) column (\d+)/i; + const match = regex.exec(err.message); + if (!match) return null; + const line = Number(match[2]); + const column = Number(match[3]); + const position = getPositionFromMultiline(line, column, json); + const byte = toHex(json[position]); + return DecodeError$UnexpectedByte(byte); +} + +/** + * Matches unexpected byte messages in: + * - JavascriptCore (safari) + * + * JavascriptCore only reports what the character is and not its position. + */ +function jsCoreUnexpectedByteError(err) { + const regex = /unexpected (identifier|token) "(.)"/i; + const match = regex.exec(err.message); + if (!match) return null; + const byte = toHex(match[2]); + return DecodeError$UnexpectedByte(byte); +} + +function toHex(char) { + return "0x" + char.charCodeAt(0).toString(16).toUpperCase(); +} + +/** + * Gets the position of a character in a flattened (i.e. single line) string + * from a line and column number. Note that the position is 0-indexed and + * the line and column numbers are 1-indexed. + * + * @param {number} line + * @param {number} column + * @param {string} string + */ +function getPositionFromMultiline(line, column, string) { + if (line === 1) return column - 1; + + let currentLn = 1; + let position = 0; + string.split("").find((char, idx) => { + if (char === "\n") currentLn += 1; + if (currentLn === line) { + position = idx + column; + return true; + } + return false; + }); + + return position; +} diff --git a/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bit_array.cache b/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bit_array.cache new file mode 100644 index 0000000000000000000000000000000000000000..8c670b6729e63e5dc30192281d78c662a06b72b2 GIT binary patch literal 14783 zcmY#nVlm>7W)5KFVPFu{Z4%66H_R+&6s|I26jaR;?lNK&<}zh8RD8uGJj;?%_?IQ4 z@Fq(}VJ<7i|LHlYiMjeonM@_|iA6<;l?-5k_~H`&l$^|@|N5cDsYS*58C<35`MLT9 zsYS*4d5JkGsb%`bC8;?%iAC`RMWuPE`bnjkIVt)DiOJcC>8Zu~Fhf9Q=oc3yLj?4b zGNHEUrSpUAbzpLgVG`UL5z&KsCngr~9wInQ8a5oTpia^_%X z5iaB~axUOt5su|9a!%l85w_)>%Y1SsIMOyEGVujkFnsy|fvHh4dMPwe%VPm*mG6mlS2@F)*eF zN-(hn>o78M268Ym3NmsrFflSRFbIme2Zk`QF)#xda6m1A7=5?SoQ_OLIzK$^tpS zW|~?Fni>h(vI`n=2^ug4nlLglFbWzf2^ty+TCxl3atZ1%1{yFj+6RMNnhw(pvV)P` zFEa;OfSHw%QBW}`Z~};G5}d$jsK~@D*uxm;!^jvIz{n`*fZ}I(!r_HRDT9J@eqLE> zQAx2vqC!$;i2^t|DU{?ZBr1RdDqW9@OCcz=q_ilnSRpY_A+@L|zepi7O`#+s6-h&8 zu|j5ESz=CRib80Jo34dIN@7W(9v2q_$Zw2{4orfqiv)QW{nyijBr3-E;u26Y(__!g zD*g{kVT_gWkO*SW%u8orG*0E5!^tT3KeIT#v?R?Uo`F9nKfeGJZ-HA7(FU>tj46sb zEpXJaL!(ZFk&)jiKOZHi8NorF73cy9Y8OU9MJ_=H#y|x|#y}NDMnMrqG>5_in-Lb+ z3?O&18oO|0b1=%;C+FuSCzgP{^9A7@cq*I#N>i{@2yvb})OjI{jQl>C#fWr8YFcF_ zAzefKZ54Qdkx@{#N$>)rp==VX;2Fli0!Bt~x=Ue%dIj!bF_?!HoQqNuOHx6JD=)Pi zDZM0BDrDtn=4IxkE992ulw=mlmae9zzI1ha0VlzpkkBY3`RpmEhfP!jDZe}jDZo1 zjDjYN$S#D0g?>^dCp=b>%7e_jlGOCnB8Bpd%;XG(%wll4kXM?Ulv<>apQZrvDX3@w z3&YA6{z^SWa9ato2HB?;_%Ke9>1O`x1 zf-{go!2vJnA*DA`5rCxtW+pk~Bn3`^g!2?eL&a5$f)f}6Ef^UCLl_wabr?~d2@h#@ zct|6a&X9670a7U>D3nwdq$*_QflKNHSe3+|fE2P>f}CCuBM}KXGbJ^zB(tQF0Vx4* zBAkFBp(zipj~Ez@RTv!^8Rg`Qb25{`2{?EKW;J>|@C>*B;{jKBpdTTz$h51Bp7NW7|Sjg$R!v64iW}N!9XR!KqJ9Oc0pe*K_74(hO5G5#7Nla0?g=D z@&ue!vX`Kxk)R{HpedK22{?ur7zIt0(Cca3PQk2~nFSS%0$+d&O<@&AMnl;sc41Im zF@cdWa1A4)U<)H803lJNpOh&Fk9F6IlA^?9aGhLSnxtEtnp~2Zp9jiMNR>3Gm<-8C zRRG6=LV0FRjzV!sVo`}gVu?a}W?5>ULP36UCRk%)UJ9t>EXm9(O;so@09EhB1*yrI zX_=`hTnagbciP@=)O#Gna&A`B%Si;C?yqfJd z8>om*EKW5uiBDya&r8lv0jKi74;Zy{dmyBihPLalCL%4O6A?y}1D<~I7fOt*j7)-x zMS*7^1op-H4Ht`+kq+hsl^I;`6b}wmYA8Rkf>0ek)M;QfLKE-4k%On_RBmS~C~9zjYpsQ!c}nIc?C230)+qwyCW z4PHb7C}k|li3i&at*ZxjBA67ofss*AwMlRTqoL{~Ho-OEZV0%U)&Nfg$bN!^pMFxN zG@|qYM>8lP!0VL+{7FFp9Ce{VJ_?C>DGF(sIjMPxxv5+V#ffRD3W+%d8Hq`$C7|+Z z0MZK=BNrnh1FP{hehC3axq4_{LXe43*o%=7-u3&Rk_yV^W+n^_48loFjKZ^67=^u< z7=^8vK%F~^dxntqJ5n~r$m@&&jGW+xnG?7TCfg(!!DuMEh!Ip>nlOT^BtZ>EnET<; zhG;5;q!yPHgYrvpYN|qcMruh$Y7t7OF0)txsfWf6ZYeOZ8vC=CvNKA8I=~E|!jh4J zA%KYu=3#OgNRW0EtQSnbDi;=jUV#;mg0KSIMJs`H(QF`HG^BWjhapP;3{qlb33B2p zT2dI~!D$7eaFHM{fuaSbo`KQ$5Z?_xMma&oB3N+@lXGAJHBqsTMhvAW04Lz0z!*sQ z$1oZy?qU>-04Lr+Pz30}%OIG~;gNty$S&aE1(iS8D;`)`1TCHH6DuLDUItd<0^W7J zpa9PLUyzsr8cRrG;IAx6Erx_|;0}xmoPxm~q6dZ`9q6pU1&~l#z-Xvw#3VQe(%(ve z^tVu*3l9`Tmn$GK1so#qAsTRz3#u)P6^io}N-`2lpw%hX8do0Ntw!qN?t<8XRB=K_ z`4}tXA&#++2lcCp;mLd#t{N1FMg~UX%gmpdLDAtV(0%u$7>K7B)l% zYwUoUDWGAol+@(R+{7GEXPNL!N@>I!Jq;}8kw5f*}(@;l;E<)Mmh>$IVCQTBfwFsVPj)x zV4$O5YGsV72P#^eS(U0`tZ8Sb1R8KjfD8??8V8D2iGq@d7kCg5q=18M-5FdC{pViGI> z4pxC$s zPV=DL%LE?y&de)`Ps~dJr7(D`h#-xufJTqOnEa7$Mn-1P97j6?3v4a|V~hjqTm<^a z3g*}gxaz+I?rCU(CL#nixj^#}8jRq%2tgS}e4d3z0`{iZC5Sv`WrZc&;92Anfh>YW zF9V~oja;f6C~mCm6AKDb^HLz0Bk%`C%b`7R3V3`AU)LXFJOJnT5iSW5#~Yah6|KMp zm~4~a5k^DVS*(HwAj1HlCUFKM)Kie))KAJ3ho^ZW2I@=7!98=x6cOH0dPvY|l;m4s z8+8W_)EAegrDay2NrQ*u;nfXjPzG&?AFKdeeSn9K7?>FuL0uLmK}K~)4kkffDM21a zL0&6C9!BPpdSV6JsRG*xmTaZ`;E%O5Z3|zDt35q5Os~J1D-GjSk43Oa-C-5MmY?I&#MnhR9cEKZzfgOzC-o9WB zBg~udh?Ye}G$a5NP-9aeu~?xvwXifbFFCas)MNp-{qQvzpaBW0_U-I+6l`pakg9#3 z5-!kO4LGPZY;2Sqos_IVQ+N=SN{)^&EU9RS_2wJnjlS}64y$>#s*a! zuG_T`oGKuV3Ph?9cVuA_WYrR6VdMuHz{qI)NK#G;ln}E1Lz_I%NkwQjM;hD#7qpOW zFfpTi7*o2qax}JSU6RK6@XtU(+SyQF!J{9gSY==}ULyJ!-0DjD4<0fujxT3m&n(G+ zh63hnB;5)-BF0I;gPlp>)UVhixQ5YCaS@B)3dX<;M#jJ#Mn=IHMpRG113?&(2QpHV zvx`AZZSWuos1=r$SyWttR9QpD(u*M@TOb9+smb}Ek?y=ye7OP^oS7vVpq!zjV5Fm9 ztfOE8O2d%UiDZdnNQs+*9x(AeudYB)KUcL$Fo97}l}j)NJYx=;ZxobZ#O7jnoS`IK zNY##iT8qV4i_43Pkqb7+VrUi*8)|^0?qSl-EdsZ+6hSR5@E}VMWRL|qtq2-8LGn2~ zIB^cL7@DDt?nB$Jup9v&WP!&4w5EdBP2ipdlARER1ct|u)H5&|_X}?l1|?$1@R*?) zeD(zrbI6N6D4!-LKJlVVtYl%ESV3|sJgBivtRVUVu;2wnlmbTJgGv~>50&$$fJcNO z@rG;s6UnC#g@hv*HigE(XuMJAnh+?0*&Uc*Q*ugyB8*IemRf-pj7);1R)HmqOrSMR z3=D#-@`8-ajx5ZAoJxYc?1HRZ5OD`)@X$F(fjLM42Qz3!PZB)-BWffFX7GbbRt5$K zW_h>7oZ?gv3%U}EfkBYdT#!+ih0zhDQA$u!9<-*$(@4-)UeJ|`**}|s!I6a-)Hwnb zRH|HzObi_IuAoIv7!r((4D11nOw1MvhOB2egiV>)1#ARu{R9(B1q0Y+nHCUqub4W>LM<07WnOvd+_SeT7jnSGdz1DJc5l`k{17&Ee1uo&C0 z_^=oUu*9+$&u2NrVtk6_9E-6yt0AkgF{=ryaXf1>tMO{q?X1SEY@BSyMr>wm#@TGE z*^GCxU1BqS#`cTNSd?9i-PoJmpWV2Ey_em%pM3(m@dEZ!?8Z0PU$PrZacFWF>vHIG z7#nc7a2V%tv~w8GLjIAl4E6**HmjpuW2<21g@`IXc7C+BZY z<3F5QT*jeXVO+)&xTbI!FX39jWxSQ^EtjzXw*@nr6k+{&l8d5l?jLU@ep zd762QxAA=8F*fEk;Wf_TE#NiY$orbt_z$l+pRqNcAD?jmUm%}x3*R9=<4=5_`HUU- zgZYia`Iqw>U*f;UZ!9gKEMROe5G!C@El?|9Tqkf&!1$iPBLQP|K|?`ff5BWq<0iot zLE~1zy@JLE1qFqSZG;?zjEjXf3K>5XdL(2lB`haw94Oo`YXDSOu(Y(av5d62v~i|%qqOlB=`+&C>@u!0#!)h>WsGmiu*({I%67{dFOgj%YrH{r zldSP(*-x^@HgctM#$|H54+*Gt~5F7T(3jB*>X1$QvXmm;@~oi%S^zi!$@lpa zAVwzPEJh~bU5re^ml&CZA2Bis8!<5ndoeKyM=>!8cQG*uFJfX6-o?Zue2SS#_z^Rc z@GoX2VJ{XY;UpF&;U*R);Y}<|!lzi6gm1Ah31_h~30JW)33stF3IAec5*A`(64qj4 z5{_bH5-wt65^iE+65hndBz%gEN%#>Pldusxldu;%lW-P0lkhBdCgD}=Ov1a^nS{AG zn1q!$n1ro3n1okxFbN;xU=qFr660hNw&G+G4&r1IUc|{Hyo-}b_!cLVuof4SuoD-P za1la$EBcpM<%mNv>sqhulpbjr&<;l;vm&~-8TNtsAy7(vz!2{MAaxX@tV#I~Ichk7gM5>2eu&Ej6b&B$mh$tur^Lwgq5 z(pD_SFdA2}x3M!a8@qD^<8rkVZ2B6j{$-qNIT@La!`NflaX14uD2M9pt1P!!K%oj= zBB~EARzO_HN@5TnR`EU%bwd0ih$@q)m%!n8FXS}_SOS3A_$c35K1N1kTRvAl9A==7-$R$B zpoYd>{&)O>j9Jix2P<<08J+5jQVVhtlT!s5-SKBdZ-F2I1_t9yfdUX^E9fG~z+fCL zm?S93Xare%4q|{?R?t=)W}ag-)=@B2U}Q9Y$oUMElvi@B!<8t|=W0>CbBp;IGdAy7 z*%zf2q$ZXisb*j>R_4>h6?93cYb~)xfHtEUBO{~ndEQ&RL`48}0tLwpjLgO_1%3&D z0tlQOK@4!~5sQ6{#;-Wvg92wR$0FQ;lZi5-gD1czOU##GWHw$dx>=Nw(YRi;TNH;M z;Dex8-SkQ7rxeH+k{={-=!bOAkQT>bF^AdsHxCCdBcri~fT;iubFhw8U^PpZ%a#jN z{&HUB#98&f$WqwyimBcOQf=IG@>QmzE*upqmG(f9-BXIwsj&ygeRU^b2rjTgn9{vm_x zNU9kajCuHkaHW4p2$`9{heNRVgxR>9w*?eyMS}Inxm^p~p~92)1 z<8em?X2Dn^!B{Q9SaHGJB*8)>!9sDtMkB#SEy2cc!HY(M7p(*@UKd`-Xe7LlQAv0q zW4bUSlaVkZlb10o({o1Qr_4sePnng3pE9QlyRsMwyRujbyRtxc>40}-z?%S!#%sAZ za-$|X$S6Nd2?K+%q?DW#q*Mj>YaKotIw;CG z1$YG*8I2doFO!ED2VDl?Xu>3z!3Y|7qY}x;Xl%vk04gUZa83h7w;xvsD0k}c8iCx= z$2$c?UF5qCs>_4~BtWiW5#$nNWHi1d^gsx-#8!BtFe9UJ325q{(fE?+4N!q~SM;$c z#NnW9<0!!-Xut>x3A`jDqp<+HFgqio@eIz{Ah)dIT??}PE#C)_?Fxd*AZw2cpAv=` zjXWstSivOtfl=@UOl2UzCzs?w~0$KLKQJEFxW6KFo28&=?AGRWnf?^V_;xd!@$6> zfq{YH9s>h|3L^u9A0q>UKO+Of6h;Py8H@}J2N)R`n3)(D)R`C1%3j>1<3j>1>3j;#{ z3j;$e3j@P^76yhxEDQ{%SQr@2u`n=*vobIkvNA9jvobK4ure^jvobIwvobKOW@TX5 z&dR{R%ErLJ$;QB7#Kyp2#>T*q&Bnm6nvH>BCmRF9B{l|zXKV}%zt|WUMA;b_#Ml`a zyxAET{Mi{8D%cqqdf6Em`q>#6Ca^OwEMR9~IK|GuaD$zJ;Uzl*gA@k?gC++9gDwXH zgFXiXg8>Hvg9`@(LmmeMLpuip!%PkahV>i_44XL^7`AdSFl^&sV7SA>BfkBp& zfkBazfgzQXfnh!;1H(2>28O$w3=Cg685n+YGBEt+WMKHi$-tn+#lR5C#lR58#lSFu zi-BPZ7X!l*E(V4bTnr3bxfmGUaxpLna5FGSa5FH-a5FFjaWgPX=4N0x$<4rU3Kai5 z3=Awh3=APW3=H)=3=GXY3=G?N7#P0rFfbVNGBB9%GBD)uGB6bIGB9l9Wng&C%fRr5 zmw~~YkAcCOkAcCDkAWe8kAWeOkAb0ukAdM39|OZDJ_d%*d<+Z@{0t1i{0t1?{0t1s z`5724@iQ=7<7Z%y7GPjd7GPko7hqtB6<}bf7GPkg6<}bf6JTICC&0jPPk@2pkpKgO zx*!9Cp&$c;zaRrct{?+LlOO{_iy#9-s~`izUO@(igMthUfEKkP~KL2oz>u=oe;SI3vu!a8{UsK~{u;K~aQ(Azy@nVUY*} z!!i*DhHWAY47)@a7>VDJ@XU28OSq3=F(t3=Di?3=Dx{3=C7m7#OaLF)&DpGcd%8GcYU=XJB|N z&cN_QoPpt=I0J*M1OtPC1OtPU1OtPY1OtP&1Or2_1Ovln2?mCp5)2HxBp4XZOE558 zmSAAGD#5^TO@e`eLz01kN0Na-Mv{R+L6U(%SCWCjU6O$zM3R9aLXv@@Ns@t~Uy^}g zvLpk;Nl6BVYmy8M_aqq@9!WAVC`vIf=u0s$R7x>0%#&hZI3UHq@JNb*L0FoBL0X!D zK}MQ^!CabwAyb-xp;4NFVT&{a!x?D?26h<+23HvdhA0^ZhSf3*3^!#M7}#YQ7(8Ve z7`kN{7?#K~FszYfVAvqbz_3Y{fnl>O1H&g-1_m2B28L2O28J>@28La73=I3^7#PmU zF)&<`V_>*1$H2fT&%hud&%mG{&%mG}&%lr+&%m%!o`K<_=2L+yj1<9(#)I`{er~g?8NlcVtufY@x>(|GxUp#k|6^6N%{FX zdSE#Q2PQ`iCPCjI#{d@Lt&CaDI~ZAnCo&s3Phe&d4rMQLj$mgIHs#cEHsNFu{>r7~ z{Dq4}_$g16^9vpp;iY_5&P(`Mgm-3$jZnls2CJD1w=&&PGK}uRALpJz!+G-$QW3{ z$S9bC;yOqu!-8BPD7B=tD6d#pAyEPB{&a<+)Pkba;?%s7#FEVXJcax;g_4X^h4jp_ z)I0@nAaZdjC@U+tRwU*Yh{1M za8PrAgW6O}(9}rKmR-=0OVEHZ(1ekZfl<&e`yUu_e>`vtIPQ7CaUZHB7-}RK%PttmB^baM7{bWN$i%<~ z&X9)efd(Lz{OI`(l=;A_m>Aet8JPtYje>KS7#Rg+qlDKmF&fG~QW9Rl#2DIsxR!9JQH$g`sza%5I zs64YcRiOx+ITaGQ6jD;t5=(PR6v`5FN>f2mAD&r~p^&JMP+FXtppaSt%Cg0v>{^nM zS&XnKIWbS6I5W32C$S_IlAIEYaw>J9<`yKDl%y8raVezbm*%A?WacRp=jWzE>{lo# z%1sLSB9eNL(Q`uOzc56(0P03Xa7Ji3&xj z#h|FG%r8{{#YbXsab|iRSUosIK&7TaqC#0>QD$ONPAaN@b5ctbDimxLs<{*t6iD;D z0;uo>d%qSI)_(cmWRO^*P@0zm3XRM>P&}i@B-oP*36NBh00~V{3QYyMH?br&g$opn zc?zKL2m1pa1t3-MbPNeTP_U+yWfp_{1_`Kqg@V+gwEUu6E>LKv<`rj_r79%n=N6Pg ztDL<2B8B42lvLf+w6xS@NSGIaQa$d92NX_uiMgo?whBs0T-ZViA`6m6q!Xp^)STq} z+*Cy+O)do`kJOx;d>sWP1sgjBkZN#B1=U_~Rj_bGRCvhtdMD;4W+zhJcBr%9rY9tT z>qKS-W=6&UMn*H)6CHeCb^B~pgplniU1#SV?!h%~E4P~>~1vfATPGDpNw|iR{ z!EVPM>oVY22ld+$;BnFNcOw&P9Itm~X zZaCOwTnY*N@b=Lg4na;vN%lb-~mR)z$1)|f?F6tPRAaL z8sJz=%u7LuL>+~s(h^WD4Xc}q6_WBnEu*6J(%jU%5^yuwvqT+IrNJ9usfDGPWr;bU zDjbymGg1{2)YK9b@(WUn5=-)n^tcp4P})(M#h@xBtuzPJNXp1e&qys&$S+DsErQqe z#h~_iL1sZJ*hXCaqr|)vaL))WB0;f=8SNm&;3!5@4Hm;x4i;9Zu~Pu)1DS}HPV7Pb zG-!S|U=Cqsl>DEQSXCLHUYf|j3~5dW%P?YTPu~Ewk^^zJr$L?DK=wdzj}_do4rLDv z0cpX}um<(!0+`qsLEU0j2nlXwM+G`S%J&9FK_*7Q8pc2wM#ew|Mn*vaMiiH#qz^_W z9Q7cm287gxsP!Pc0)$qG#_?j;#TX^+^HS3jOHx6d3Sli~Mk@5(wS+G*F$yy=GYUUq zVif+w#3(Gp4CzuDG75?PR|qQ21GNsrlZosefT9$&cK|jZk4qsj2h@yDECIKp&`kq1 z*+Yv{L1`D%0#HaREduo~P#Rpt8Tq9-DGEuc3Mr{MiIu4-3Pq{uiA5fD??yyKY;`@ z^9oBdiz*dt71HuFG!?29lsxjw6><|R70Oc;5>rx&Ku)X7FSS=vsD-!pVClIi1==?N zHRBX)Q5$_wlkv9rFxy-SC?#@cZULxM051LYGxJKgxD-H+hFAw~fr7Z{MX9MJpq>Vj zC(zoM#5x|y8Q&D*HHhe>S@oP>1deXB`W6w9;OK~A3{M~NA5 z9DpMQr6>m}2dP7EiW5{0(gcr3B-PN|-oUBGBBa@(|R-gqVlVGV; zUurBSBwzL02wj|7->Z zM;7Kl4kk81RW3nQE=DFsCI)tS*P^2QB1|b}2KE3(CT1;hL)JH(!nI870>Of%Rffk! z8I7|T=QA2FV0_GI{G3sa$vBQFiOIN#sf@|EnQ1zc@iL}$Ovam-_A?n@VS2)3{D$cR zlks0B5oTi*W*uf@1LkmM;{@g;X5(b$rOd`FnIAA4E3l}r7@M$Iuo%0tM6ejAv*fcF z7qc|57*AnY%woKnWj%}W9+pci#t&IuvKYT*5oR@(VU=SwmS=TgHTGbg!fHH&bp@;O z8rGex#-~_su^K;Oea341nU$5zScFZ6%~*lWh0WN9&5zC4pRJJ1xRkAx&A5|o8k_MN zw%u&ThuMy^8DC+0$!7eMjgj4$m0gJ4Se@OR-PoSpncX;mJ%!!4guRB{xPg5+yYW`` z?d--o*l)8N-)Dc%uKbyu!&sa{jl)=v!-&J!o+FgQIE5pJ!?=K>mcw{5$082nRUGR$ zjCXTf;4pr`@q)wn4TlJ)u@a{Wr?Dz$0H<*XX9}lr24^X!@f6M_oW^T7H*gy7<-E;l z{EG7vr|~yVW-enzE(0!O3oaWjV^6L$F5@z;IxgcTu1#FVySesq8SmqI#bx}C>mQdf z6E{D%u|BsQx3L?y7q@XZcNw>F8+RYK@g(l~+{XL3&v6@HWDiYJE0IF@G$kMRti6+Fgkcy{s_pXIs7WBiQg6_4?E9v)s}IbJnhV=dlbUgJdG zGG60O-dx?@EU*Nb>TBE<}2qjp2|0q&v-H45kBMdeAoGmZ}Tzo8?*8Y z@f$ny`|%rx@kj9+ui*d5Z_FbgAYd#jpeJD+ zuR>hH#!AAr!p4rm&cenn!V`pzHw$kUHf9wO7crI=aS<{05Qz{mjuFWeF>VyuCSv?v zgjLj7UQ}JwSX}Zf#B^il%3MKg* zpgyre0%-aqK~KRgzeph!GAF2`P?QLAmjYN}Mq(ML4VP>0Mf;n z@LwSaOiq<~Oel%JDor4XE)k)NLvl%JED099L_ky@0hkbo=!8e>*S zE(JLf>^@N7mSnIdmVgJ`lTsB@Gr{gj2rem2NzDVb42u*JLLhEQ&|_c_WMp?>a$pwZ zWHV${XB3WQGIum!7VM1@oERiHRa|gsl;BDw!Ik2IJEH{mS_$qI7d#s!cri-ws<`0O zD8ZLmf^WqI|3(QjG8PH5GKvd}G8zesGHMBnGMWp!G8#d#xo|0ClyD`ZlyD`ZxNs+9 zlyEPjk#H}gxbR%YDB*>STEYt%#f3LAMhS0aR1)6GD2`fx!W!)ijOmUnOoAMY0*s8z z#)iCByeR5G6)%!}J9`&9iafN%jif}KO_vQt2})-hNnx5;ff%Yn*w7{ecrq4AX(!iI zE*wfBdKlDs*y6{IpY5){=k6fI{IZe=tQZe_F*ZehW+!GwMq_toZx9s#8a`q){>Ac-g^|&inU$TDkIQxYF-H=8(gfUv}G)yUyjW8uJXM>c1 z#6TG4*pQ;qRJeC?QWJAQk{}FH3^NnSjUXv51_lO@G>8qtF#T?cImOrw1GxaC17;wS ze?SJn{0(A*3M1kT_WwW+;e; z84Pm@I1Eri2iXdc4rD$$8)gb_4}pRNmf~Qci4;9BA*29?2_Yp6kRc$ea}uj63u~ zF)=XgXJTNu!oIz#z=Zz#zlQ zz#zxUz#z}cz~I8lz~I5kz%YfCfnf$K1H%ed28K1P3=BJ285mBnGBDg?Wng&3%E0i9 zm4V?iD+2>78v}y~8v}z38v}y^8v}z28v}z68v}zM8v}zs8v{cj8v{cr8v{ct8v{cp z8w0~MHU@?@Yzz#$*%%lOvoSCnXJcTv!p6Yxl8u4kCmRC;BRc~FD?0;&5IX~dIy(b{ zIXeS`Jv#$~GdlxA06POi3OfTs2|EKr4Lbuv13Lr5a&`uWt?Ud8+u0cycCa%r+-7HB zxX;eO@SdH4;WIk}0|N&GgE$8RgBk||gB}M1gAoS>Bf#C%Q1H&5*1_lvM1_mWg1_l*Q z1_o7528IAm28Iw$28I+)28Iky28L2j28Jn|3=B&+85q`ZGB9l5WMJ6K$-r=%lY!wC zCj-MLP6mc=oD2-iTnr3~Tnr2bTnr2rTnr30Tnr4JTnr3pTnr3lTnr3#Tnr3NTnr4G zxEL6Ab1^XNer^T^eQpK@J8lLBH*N+7FKz~g zaBc>MGHwQjHf{!nK5hnvN!$z!^SK!q_Hi>Xoa1I-xW>)EaEqIP;W;-011k>$g9r}; zgA5M?g8~l&Lm&?WLlh4KLktfCLo5#i!xSC{h8a8z3@dmT7}oGGFzn=EU^vUez;KU; zf#Des1H&sG28QoE3=BNH3=DF-3=C?#3=CSl3=F}%3=E093=Czw3=EyT3=F-z3=I9e z3=9)^85r*JGBAALWnlQi%fR5m$G}j`$G}j|$G|X^kAYz(9|OZ;J_d#(d<+cd`4||k z^D!{o=3`)BR)B%wlmG*Rh#&)ln;-*&mmmX!pCAK6oFD^3 zk{|;^z90ibqaXvr4nYQnbAk*Ej|CYRUJEiXycc9(_#?=`AR@%T;4Q?!kSN5!P%Xs3 z&@9Bj&@RNlFja_wVZ9Io!wDeq1zz|bhdz_3k( zf#JOf0|Tol1B1LM1B1FK1B13G1A~Pq1B0(914E!F14FVX1H(j728MH@3=Ho?85rcm z7#PgN7#M=Z7#O0(7#QNk7#NDg7#MoQ7#P-yF)&;dV_>)?#=!7ijDdkuoPj|_oPj|@ MoPj|Liecp;0H3<9g#Z8m literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bytes_tree.cache b/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bytes_tree.cache new file mode 100644 index 0000000000000000000000000000000000000000..e7446ce83176b2b4ed94233be680c2529c5ff8f9 GIT binary patch literal 13269 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 F3jp3Gk_!L; literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bytes_tree.cache_inline b/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bytes_tree.cache_inline new file mode 100644 index 0000000000000000000000000000000000000000..1b1cb4d44c57c2d7a5122870fa6ac3e62ff7e94e GIT binary patch literal 8 JcmZR80ssIA00961 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bytes_tree.cache_meta b/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@bytes_tree.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..b00ed3338f56deed1bd8698003758ebf8df5b566 GIT binary patch literal 903 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 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@dynamic.cache b/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@dynamic.cache new file mode 100644 index 0000000000000000000000000000000000000000..bbe4a320d77b4b6a94f000fa42127c24c64c9fa6 GIT binary patch literal 7137 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 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@float.cache_inline b/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@float.cache_inline new file mode 100644 index 0000000000000000000000000000000000000000..1b1cb4d44c57c2d7a5122870fa6ac3e62ff7e94e GIT binary patch literal 8 JcmZR80ssIA00961 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@float.cache_meta b/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@float.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..604414bb9ff7d49e8007dc12be5b85abcc9e8382 GIT binary patch literal 2724 zcmZ3jurQqg3K$uo3~nf$o|BrGtDj$#l3EnV%D_;-%D`|zaMGP8n}2Sf#spQvz`&rv zz`&r(z`)?ez`)?oz`!t{fq~&I0|SE{BLhPyBLhPeBLhPTBLhPvBLl;JMg|6cCI*H$ zCI*IVCI*HACI*H*ObiU)nHU)UFflNQGBYqpF*7h^Ff%ahU}j)oXJKH7WMN>K!NR~W zmxY1h4GRN)gCG|JgD4jRgEAKb zLq8V-!#OSnhD%%w46NJ?45Hi&3|ia_4CdSn4Bp%f3@O|U40YTL3~k&D3|-s|3_aWo z487b83>QIac^DWZc^DXico-P=^Drg9p408k+7*-1~Fl-iJVAw9ez;Hx>f#HV$0|SR31A~Ad z1A~Yl1B1381B1CB1A~Vk14FtX14FSO1H)WF28JDi3=F#j85nj8GBErQWMKFs$iToO z#K0gR#K0gc#K530#K2%H#K7Px#K7Pw#K2H2#K6!j#K167h=E~&5Cg*!AqIwJLJSPc zg%}tf3o$S}7h+)eF2um_TZn;yLzsa+IVutEhF2mC48KGe82*VcFmQ`9Fi43qFldM}Fc^q3Fqnuk zFocUTF!YKtFsv12U|1*0!0<+tf#HKF0|SE?0|Sc~1B0L#1A~GX1A~qj1A~be1A~Pa z1B0s=14D=y14Dus14D)w14E7&1H(iy28NAd3=Bua7#Qw|F)%QSGcd4TRv5TnSzkf6xGkfg}KkgUkS z(4xq|uuGAF;jtnE!xKdY25BV*26-h027M(424f`#1_vbuhEOF2hIAzchI}OkhGHcK zh6W`DhUrQS3~Q7a7`7-eFzirbV0f#Xet1>X;s4_5gsxmOlP-S3Ppvu6oM3sTz ziYf!cM^y%f&#DXzUsM?wOw|||+|(Etn$#E=rl>J6%ur)sn5D+RFk6j*;jtP6!xuFM zh97DS4EE{_47KVE42|jx3=`EE7^bQ-Ff3AMU|6rtz;Ifff#I?`1H*N728KWC3=Gy9 z3=AO}3=E+f3=Fe17#QYjFfhE;U|^8YWMGidWMI(IWMDARWMHt>WMBx_WMIhDWMC-N zWMC-OWMF90WMG)9$-uB)lYwEoCIiE6O$LS&nhXq&H5nMbX)-YU(_~;^(qdrX*J5DM z)?#3=(_&z7(_&!o(qdqkqQ$_lREvROw-y7#FD(X!e_9L-4B89~jM@wg#@Y-F=GqJl z?%E6t-r5Wd5!wt4x!Md2_1X*!?b-|s-P#NcGqf2P)@m~_9MWcBIHk?Na88?nfm?@x zK}?5%K}Uyy!9jFzD(sFc|7GFxcrbFoftbFhuAwFf{5iF!bv(Fih8FV3@7Tz_3D> zfnkp>1H&m@28K(z3=G#m4GBF422MQ&25vnD1|B^I1_M0?1`|C7hHO0shShou4D0n6 z820NiFdWunV7Q>i!0=p;f#Itj1H)fE1_nlb1_nNT1_l*<1_lj%28J+w28L9928LXH z28Kd?28KF)28QYS3=B*485q{;Gcat_XJB}s&%p3fpMl}EJ_ExWeFg?00|o{$0|o|P z0|tg(0|thP1`G@f4Hy`f8Za_;4+abjY=#UBT!st` zmWB)r?uHBu{)P+;!G;VB35E;|HHHifU4{${lMERcrWrCYEH-3d*l) z!!1Jw22LXe1|cH`1`#6$22mphhGZiKhWSPe42z8z7&aR*Fl;wsU^rsLz;NA&f#IbQ z1H(rn28ORj3=B-h3=Epa3=G!B3=Gc33=HnZ3=AR03=HYU3=B2K3=A#C3=AE{3=C6^ z85mX?GcfEkW?(pG%)oHUn1SK1F$05?2?Il-2?N7&69$GiCJYRMrVI=!rVI?$rVI>W zrVI>GrVI=*rVI?RrVI>qrVI?5Oc@x?n=&w5FlAtnHDh4VF=JpbFk@hFFk@hFF=Jo| zG-F^$Gh<+=Fk@h7Fk@h7F=JquXvV;>%#4BIm>C1ZIWq=^OJ)oV_stj>zM3&Gh?_Go a$eS}TD4R1dxR^6ARG2d`%!6WB3ljh{EuIYk literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@function.cache b/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@function.cache new file mode 100644 index 0000000000000000000000000000000000000000..466438342bfdd97a6c00ac69c583c60c8c92a48c GIT binary patch literal 1104 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 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@int.cache_inline b/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@int.cache_inline new file mode 100644 index 0000000000000000000000000000000000000000..1b1cb4d44c57c2d7a5122870fa6ac3e62ff7e94e GIT binary patch literal 8 JcmZR80ssIA00961 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@int.cache_meta b/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@int.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..87308288f7d793cd31810581b01ed45406ec23a8 GIT binary patch literal 3419 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 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@io.cache b/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@io.cache new file mode 100644 index 0000000000000000000000000000000000000000..fb831f1c97ecd3a5ca63a15030e9c25af49518b8 GIT binary patch literal 2697 zcmXq*%M`%K!@wY@>m``VE|}^r%*)O$sG23r$!;F(z`@95sJMwq_y7l!@KFwS;e#A( z&IdS{|EK4qCg$pA<})xbK-lrcB@Fy2IhjfS^+StOi;DF#I7`#>bM*^Si;DB}5_3{g z%k+y&Qgd<=i{cB4O7l|nlS(slQuGTFld}`kQ;YS%CdL<+fXvV@E=q<7=x65Z$%CaF zm>dO|1Z9&P6QYvgN5aPK~ZL2NqlM%1A9?^QJ@49 zTi^yp&Oi=EMnOg{1|~*E1_nV<_rL%qHU{QE7A7_ZMnO?4K~WEXO3`E%?LEfbQdU}v} zW`xGP9(!(9F={|DFdFY>y352UCl5AcAfnYM@B|~HpsbhR2}VQNPppDR7y~O983QLU zG79D}LYxKhFj{nqVU12`T!G>`CJl9`vTkf@NCS`N;4CHV>^ z8L0}Xc`4Yk8(Jj6vL-mP@a8&<$SQ)k8y;Ec;SP_sBJ9!jKRG|II6o&97EcU}#u2PF ztc-H?;GCC}HxLo$6u1YH_x3Ov%AR5o+`$-_!3Zu^1!EXt4ug1{s0c&LeN>4ta9rWd zSm1ocz<^XlIzikFk1Uv1;E{x@aD+z^drp2j1EVo3i!uwNoCA|150jv%QlJPUlc1$m zpamn7V5wDL2_qAzrea_aWR(|WWOig>7UWbCFbj%m z2}*)1L{TFMgFmFGG?jtDfmz-yF{c>JvJXlvF3l-nU=ZXq7i1J>VRQs(loFJb2i2sW zMuNWbg05W5{@Dx+jx5Z9987G2s$7DqT#QT%9P+N9`V2#Yk&%I&xrNn`^$DY3CA)y3 zplO|8(?r3kiv)Kb7rc7c@F5SVJV|3TPG_9XXgr(o6{GPx#=nflj7*$N#%fF^OvX-3 zUQEV*O!Z90yP2Lc89!q(XEwHG_F^{nV~%DvE@5tBHlDydgV}fv^GRl7Ru*j*V;zAP1uW zBO|l%A*QoT$O<9lCXxbAMn6Vm1?V{%NohQ54lA-!Sb9QI!owoW0*)smaD&EOFw{se z)Jiba9nz2iB?wR(OMy{Pf>98IWMni>VoYUZWHf%r^n{6#(O8{Di-nQVxPi5q6=DFi c>4e{maK>1W8P}NZfXt9#Q39D!%393|02%+KVgLXD literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@io.cache_inline b/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@io.cache_inline new file mode 100644 index 0000000000000000000000000000000000000000..1b1cb4d44c57c2d7a5122870fa6ac3e62ff7e94e GIT binary patch literal 8 JcmZR80ssIA00961 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@io.cache_meta b/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@io.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..b6b04350eee83b04238efb73438189a9e3a25d74 GIT binary patch literal 289 zcmZ4dzC42g3K*d@L*Swho{Sm#?`&WK3=9m03=9mW3=9l)3=9lS3=9kt85kI*GB7YK zVqjp{$-uyHoPmMiDgy(5L2v(-|2UW-~G{ykcZv zc*n@V@RyN+fsu)Ufs=`WL5+!l!Gwu{!HJ21!HbE3!HVK);4!&4>(hG$F+ z4Cc%X4A#sH3|`C(41UZE4AIOC3?P$KFT6PC31{tN z5mq|HBHXl(McC*N%m4J8)WlrA!wxacWVqeg;=*dVa2c zL26NPeqLfuN@|&YaY<@UPGV7fK~ZU5s(w;wW=@KJL1J=tVtQ(^KG?|k;u4S<`o%@b z5CQ$1%;FL~upEOU2a_PLlOqqiU}u$M4|{!Zeo;wgUV5Mo6PsWzyP#lHpadhMpdgoE z0;6Ce7sOdmH!?6VGBPj;iaH63MhU923yNk5N-ze>FfzgnK~W9izcnOB<1VDDcPT#{b^cEm(?Bu6Y^6kLkt2o^!nDnZF8K}~l-$t*z` z#y|%~Mhuf6mP6eQ;UKvrkcEkjk%@r=O0yXsbi3`wz~KCZkxBR{^CIUn%q+sZ>{`w| z>@32f97fI}94x}NTwcx&Tr9#@d9<9b@URHK<<%12#LOuCl2=Q35*wp%7B{1C6E~yq zBR)pqUwn+hS9z6$PYE#!XGt&$H%TxG&yr;n-UPx*jKZgs7=#OA<`NT(V5UQnF0KNs3IuRfmq~b4 zE|c)3VkY5N#Z1CNRZPN0RZN0e%z{nK!mC)Ag?F(q3p;T#3kPvB3o{8Y3kwM_3m+0? z7JekkEUYEVEbJu9Ec{E6Sy)JkS=dRPSvW|YSy)P&S=dOMS(wRySy;+|S-8rSS-8uT zS@@JCv+yHJX5mY=%)*~+nT4yIn1v@fF$)K|FbgNSFbfxXG7EQkG7CEeFbf9-FbgMz zFbfxjFbiLbWEOrE$t-*+j#>Cs9JBDPL}uYviOj;c(wK!`r7;WN%3~J(l*cR_RLLxy zRmm*uRL?9NRnIKk)WR%0tA$yZtD9L^she5&)&yqZPZOAhXH8`m-ZYh2SZEHju+|)A z;YACWg%2%Y7M``7S$NZOW?`ju%)(adn1vs0W)}XnnOQh%JF{@rc4lF&eaymA`ld?d z(=TS>S^t=Ycl~1)e#FWm{EL-Ecoi3m@F6Z1;Vym_;YIu`!k_*#3kwOe2s25r2n$KD z2=9_*5xykLBD_nHMfes7tFs7yQfCpKq|G9{NSj6YkuHnyFI^U4DH9f9EfW^uSr#n9 zn=Dv_n`~KxXW6m{CpohSS2?o?|MFxJ=JH|@77AbyHVR-777Aez)(T+}UKPnAd?=Dd zcvdWn@TyoAVXs6M;iNC{7Zl_Y6krTA z!J>+hiII_k6GJmtn4MT*60G7zcT@ouN0DG2Co3bXpyHxH4@O2o)hIy^MnP3BK^MkA z6-LHD4Ms*m2}bO(1}VLvML9UiSdC>?uUgG0%@3|KB$(I&4880#zEJOpHv7f{I#!I*g2h28^JpNS2F%8SEHTw=x;ObNlKhUzC`ao(fKT z8jKk3Y!B=KyOV>7je$|nR7ud(NYIvD(2z^e0GtdM7zGWL1PzS@E!hQixde3>0}U7% z**){%2`B)h5nX^8J?;W<#GMj2*|9^Dod~AU%&d$|f{I3gTNoJyWupWiFdE7hu?yZ| z44lBo7&wKIQLu#(;z1Oz|5tD>N=+#ifvtkO0mwTwnuI zH4JnV3^chE^z{{V?G&O7V!uCb1St_es*ESG|OLP7!;7b7DBGb3XFBO@b|Afr0Ci6te-!zjqBCCJ0b zT$0bo$Pe-`1B3C;PQxxn$^TWE1@Srg`2`FN4B(VufUQg?iC!w60uJfK(>4Al}0 zH4=<<7YyVQ3}6f_0hKa>g<67zMuL^@g1KCRIpBCr4Ss2(sWi%rbBMSpN%(F-thTkba zKL=KtgEJ$4c~N2ke^F|2343B*C0IyM)C!!@+MyY(fRWKYD7CmW2cC^V4usi(nJN7; zQLJDNVB`enOD$0SBPbgsEW^lXD0_)ZSb~u;@B|}c;2B0n!99%Na6t*S{|auIc`3yT zC0ZG&3Tc@|#U%=f{ngGhMK@cH@1lOXX{2~p%%pA=Gh0HXCynKb?(&P+;J;e%X`K5U& zAU(J;IVe6gqM>;;R!1Q%PoqLpp<1Cr!A`+Qp%$K_{j)WUH8Jx(KJ6w*+7T|rWd_9V zc#z#7kK)nG$i>KnQ3!Ym3NQ)^N(l-u{?E@#jW5YaEMWkpT;|L?1_t9#-abBzk`64O z5+HC3M%92)g)y=sQXhd<0xJVMD=0&0fy-yvD8VU=hO$Ylf)f}63mCzrhF}UKIFX_y z4QLFCAVqj)NouYFC_NXXra)2%C~e@2I*?)wLwF2D8^&^RflbheR!UUTQBX<(5y?ui z2qkC|2o2G($f}}aV<81LI~TabWi{q8)i#9%U~mRzF3Ug}nR{h1|sKRE0!^)%da{M6Cfrv41v1E4Us;ZYQJ`mq3aTRJBGRE=VyWsMcmR zRuTyjVU!eP^!lG!91ji=1_lNPNS?faB~OAY3X)P3J19j#+V^- zR19Jl+yZW81KgV2@_s6^ue3qZ7jiz`@JNywahNRf&#s)S1+GdDLiB{Q)kHK!6+Ucya$)O2VHYO4Wx~Zk-60t+22OCP(}b`8 z1sh0X0u7{rf)(5i*U!mhE-nGJWEohE@2#|1#V8G`LDO;)OX71ALA_dNSv&ztK@=Ul;l`uh-?BKqYSKu5_D>_Q>1f!wiAy&a7jDZ!5jDauXVhBVBOvRZV7jzWckwt``-0;0zRQU}VCItr#b3T8SA=8z^e z7Z<1&LC<`k(F@2J27i88Y7qkiKgc(XjKJzKPQDj9$X7UqrC!K zrZW^}XAIr~iF*O?cm~=4>lA2rd}4dx1yEhZ$i&FUz%KYxOYo2fCwK#cK->s` z;)YpJF$mltRs?s56}8Yh#K_@|5;6Z3+#!RM!uh$W3dInjs3bK-p*Xd$G&L_dwOBzT zvADQ2H#J3}Bwry3qzEjZnWvDDl3A1r9x_SLRLCshQUJ#)sC-DxRY=UqQ7Fkzhcwnp z^GY&v6v|W8i&7PGQqxKlKv_*8tF*WTR!S7-LrvvUNGwiH%}W8bAM%S*Qu&MYxIm*1 ztj5nP0xLnOCktFYm89myGw_3k8KDWN1F1AdY8kf&b3hUh))ILFI02yzD6YX$?Gc@3 z1hG$J2ohaH3xXn)Nl-BhUuFXpjffCHiBf3Abr!Mkws83314iY#1ML9g8D!AZaCg# zloMo3`d^-zmy%zO5}85R;u2JHxCH&AKqX`2}|@~XJlYC-m{i%9Vido z`k#^?Uz|~zmd2Em8lO}dUyztt6rY)wl3Kx>S`5un$lX$;^2Z%g-D4kAnSv#3QL1TB ziZ=oe>myB@F)}fVFtB2#XI9tCQGU5+0LB=SQ;1*zHWC{QkBM3r((OC6j-9-UL zQ20C5gJ!u?^HQKeEr3+i!$$xnfLc{Bw_>)tnHWW}w~R!IE}2E4C9@N#i2<81%tD(m zggF((gZ~u*ic-@uD^g{O6%z9lzyr_l7P6iKc)Th(F;5{erx;wvrsU_PDwGz3>OfE* zIwdtFGdZy&HHAx|xH7LKu|gp;uec;NF$HB(FekM{A+;j27_{Ok9@OMkuvLJyh(W{7 zpeYAEJrotO@L5LabcBwA2}mh4xHU@htx%2BQ7FmCEVfcW9s`F@I)Yl;{KU){GBEIi zjA3Lnj<%R$0qWvv@q^mg(CCf8jNaqHB6te3642Zvqc{UAxZM}n9tchg*yi+*E_G#Ti(^T}CZf^nQTM2fqQ&JA#{6EsS7yqQz}cYD#G`>U1z-$OO_80!3v` zszO;}PHCz_Ql&z2VonYyc43pmu$~i_LVlh?Y9eArNk<_)AEXd8!wP9SmEnMN*F${DR5>rw@b5r1Em_lk{X=Yhs4rmH2KaEQvAu%OI1BMNB6bv;9PdWiCmzGEP}NHBNHRcW_BzSEzE+7 zc!xb;!OsK>e#~JHv*?AL&iRm^Ycm)GxI>1Ge0i}ZO|1uatfKE0++>!X(g#eps^Az#Eer$ zVzEL}YHFTBVnIPpCZvZ2&itU_AGBhmBqKGmNC6xLpn;N*j8q5*vZ$n3A+ZFscB42S zWG?!QcQHr`RC^SsCgkOWP@Yl0NH7?_dWgpx2An2aB|y>Ji9|Dp0RZf7?~L5u$9>$VNOsbMZ_XRIgr1Z1Qo3UH-M*X1UE1m z%5t#@t^tpY22NmP6f9tb`Ul0&{}sIQGxLfS^2PI@W25SfF)9$zUXzDc3n0}@jPjr&AGN9t!CKWaLd<7kR0O4Y5_LeAZUrlV z=Is=tgkwO}#UozO{9WK5M#dnJ@CQcZkU&cdjtXUoMVX0ac_pBj2Q|_ZK+9tkazPC@ zg|x)X98jczigS=0s0vAgEj}vF$S=)FfljR{BtRBYX(T2mm*$q{B$nhC=_sUv)*yje zY_NGe$bzNR6lnW0vsfX)KO0$(W`Y7N+7!w&K_hcTkP0g=wW0*3D-T?YfZ|<`3y;Z= z1q@I(pcz|Sl3xHG=7+cl;%Tr_kbiL{Qdmj^cRmzq?BIriYLv7*kVhcZ4wr(0LULkp zszRoMje@a4HHfdE0GiqZt(1g#SwUMNQxhTqp1A|bK%5L(v;~r=32Q{S}z>8giO|VruSMXLkkiMil z?!}YJ@bFazSGp>oP{wSyqAi4C1UE$CeatPOQGl>A%;h@U}~M`;0Q-X#wf%f+z8<)Da$4iOcl7N-`K#xb}#6Hii*6_+$7NIx->+BLt8ryiV@TX zET|h>k`HgI!kZDGgqs3cx|f#<7R5AOhfAS2Um>#ubF>LO*aR6Gg*T2eixu(`OESwq zi;prv+b2MayHis@BWfVWmt_`}fRb2lVsb`i9%SuiX0bv>W_m_WC6_`zC_!fyL)L!c zN>q>-L|*!504kRf(1uz;(X0`Tw2aRXk18Dn@WM>U@<1ald(cWw&?;X@bK6Rg)yh7# zC?_#59U4CjjF9~njIgem4k+gRy@KwjEsQ~ zAZ4Q96-E?ape0Ml%ygnYqz$?r_!?sXSBh6SH1<Z)a9rpR9lScALWCK#8*u?-Fb1@1 zK!^>rYoLaaF|YwV7z6eTif{ib1ZU=E<|G!0gDN2K6iI@PLQ-joLS<%Z4rqT+Mk;6k zwImhPz5>-7NWE*&kSe%UU0jlw4w~-3xmOPqh8m!iV^#{V4lHPlv?xE;KnGkR zSb=u;Bqk?A`mYL!$;qJpDN_G4+R(sIN5Rm*7(|#s$6}DT+mP4^O*?S<04NVu{Vyp> z%quR)FJ`cZ^g$u68xp@t2HWCLqF2f2Vp$hJf@bQj71aeT7GM%otiqSM z(1Q~t$wAlLl%GC!8Wi|l|G_mwaX~6$Q86U+;UfS!NcjzUSUB(vxHpP*u|^5Fp4MYv z7Yrn77*r2?FI$i3>RS(*^YHe^vG@^NplnL`od61yUC>2nDXEOG1zz^xg%q#_6gX%9 z-awKj)}Yw{T~@ia9UOqH!lH~?!lH~u!m^C+!h(!k!UBwp!3EeBRT7=t^cgroeM?3r zMtyJrrVlE>$Y0pUh%w4Sf=j5I@by6npIK0GS6~ig-+B(Cp=uSAUyb&Y@u#KldN6N4-biy%gVu%?9pfz|tXlF>EEQw-dVl)KjLqlk+ zBn2)3PwEOTVKh|KViQ~dZW;u(Fft03Fv1qhL0pd!IpAqsap>q4xH$mnM<7k^mXzl! zfVi3IrTL}B@R$R|6RyCAE-Xu`j5h+sfRT=|jtNqbgQrJ9%Py2PKocK4(f6zcdYEgVHV9y~A&Y;51+Z1O zj7*Fs@T6k`N;E`H%9(r`Bi3t`=;9Lmb5CmF^dr($E z4icu|)5+qCQW>G6udq#7AF#LZpo?EXO$nTHC`1iYn_~`BGoqA`P~)QnUE2jsvji;| zK|5?27_qG-plr3XIRoapYmC+nBNL-JDD0U)tCB82O6ot1hKiRs1b;9Fu3=;ZPqi;# z{0|FHCRlhP1^#~pC(v1N#R^KG?3kICS(2HUqW~WORwyloi~_=ksug_nxL{2Zg+zsv zOz=L1#G*=Yw+u8Wgmzp@fs9{?5{pVe4rQ>< z1J6-H>kO=|aPZE66_6qU*UkX&eAz^I@D5{E!HZgg7mWmOx(lA=5~0V`*MCIKJRL${E72E0abrIz4IBf*XCf=jssmw?Y&;B?HECf6QZ^aA?F3Cb}hI2_2oI)}8`OP|Ji>P_awUh#7RqmlreWkgq6aK_+HF zDQ3_SUs&P@JyJpEC@~-r0*uD_yFTrLtaDF-j~g(s7ZpQ$JD}TbKS24xY@G&rcm0oow}THY{;6|}qosiO?> zFIr-O&Vh>}^~H*FGQmq2AnHMb0yqa_Axhw#EgjQXl*wOc`y8}F9=;<2)XWCAuuY*S z@1V@^Aq-S7iq%mtL3Eg*sR6Ctg~Ts-o(|OhHa1F zk&zKJ#)&w2>6Fj}&M6$wXet0%|OBaVbQD58%>KfGiPH3NOmfO9!oP_RrQZ zG62oS`DbgG8-qISHFgT%ScY}+Kue@SE6t#5)u8Lt{IfOUH9^bKl!8l>lT(X}(@Jxc zK*|)LRzPLl5;JpBQ@}FNrD`>H;0CN-Nj`XA54@BDGZYk*HI$%wbQGddT^p;ZgHVKI ztq$0C=Ej;j3dr6u)`X5QKodE58WG$K2Oa!@wAu$6L=ay`8|r|EIKa6M+RFt6fkLK& zu0p1QzJjrWmICBl3|u||FCGW2PK7QW2X%f8W5FXMMmh@7rl5W?D5ODW8-b2CMV^yH z&w57UrQ)C_u2Vf|&a*hR1b5~;9{2_KsQV7^sJk6FQ`uvyP0{AaVJa~ySYn2>V45LI zPn`mPKxVvtFdE9P;uieE7zkR#af6Xj@B|~wTj;q7ypcOm7m{VLPM2pwDrAL3Q2z(K zP7{(nz{fR!@*J$Sl&Vmk2%26mN=;0uR7iwOyF)rqAl0Cp2tLmtvjj9yQj}Vd zlbD>EqN4y_=vdhYQ4F4%$jt;Tuue^}0dCCc%r<}lf$`MS0>>%zyY$s?g2*Nil zjT8VV=^i?M@by5}K}bmDCKec>hLjG@B54i&kRn_p2@*LX?2J9+T;L(+3=T6FFzJe| z4rXF>CAwH~B_=?T!U7+EfPgGg11q*dKE4`MRf1+MkSbv;8j#MT1}}EQB%tP?b-N&wA4n^6L0jt? z805k3U}Q9Q>?!L3ts>9*pPZkYl$n=U!oZ)Ip9iazY1!F!r*&uB9oqfM0-v9%7$tav z(NJ*_yWkb@ct7}nuO3D;f1{-aNT*vt7BX85T9*h8BTxu|Dj0aDEHe*W{DUeqP*sGh zxdsbkkbY2K9DEu%>gWi#Ck~qGGK6ec2Y1J@b-=Nj1==Bmvtth#tucWa4p9p>AH)Qw zAMorpzP`hUBl<@{**oe#Xk%7wPxFD}jmwVz7C$GCzLLV^PHzztBNDHC*jRZb3Q z&Pf5>rpZms%`d9d106HR3_B$o_p&|E00;a~1n5Z##*6Ja96;riQ$6%#CupA)%XSy{ zK*)9%7VxM(T2q3esdq2T0dpJ`kQb-|ViZ(#0-wbWt$>&~Ko!soM)0JbU=JhAD`;sE zvQIWq9^Ov{l`5b)dgNpX5(6b4aHAI<(@48vb8=uK#-O%&W^yWM>K@db^hzvC3{Eb} zEGSVZNi0fFErHDg5;7$}4`B#&9XHGX+!r=LV@o3%K7S7$V?;jM8&WJ8gUg=;P#VFr z%l5OS68O-rsQ<~JJsI&i`A93MdyobXQReD(Ak8(b>*Fxy>M>`0ur+DD;e(3apq7gk z_<(=BQ*c<$b0XN@VFaJIf_F_bayXzQBxnQoC%Xd&DC6vcS9FkDaS}6&?2AF|UD&A= zSFnd(doY0mJ2AI5;@E@lgRL!#HbmqDF4lc8Tdc$k5y3P=PVn3XTA~M8kmy@3!588;uz?`28DSrY>5G(DFk{mne9w7#1&>3ospp*gHSdWxA zFi)le_2L!r#xt_r*j)MX7Uylypq`ODI7L957mR%77JNLN!lR&l;Yr_@xTMc4C`wF9 z2d`=R0v^@_ElM?%_2L%hVFcg21RC5EJi-X^8jA1#D?m4A>43^MM57$3_J+@{fyzt; z*g3*^>2UFaBIxl^m<1}dqYW!p;f*}d!g)$}ZsBeumgbefj(JW5Erv=|uv0KlKpf+W zWy2LX(n0&JbifPhvFXCH0Sl%JazYhoCu&}aUS@H8eo6{_KF0_=#?6I&P}xY3g;Bm7 z)G7cionXJj4k~Jr;QPB7*fVorv$V+L4%AADe()l&=%=V7|ikvTbqHA ziO~;Rc?K2+?_f>}7J=kyVHeQp2tvHV4vgS+9^h8$6GqfPLQB-apbhfH3dYc0D5!f; z4m$V@G@=Afgy7>XU^P5sB@C=*3a`hZ3u(9%62Ke%!N;G2k~n1W8qwH;s)LNKx(1;#84E2H^PD}$Cc_~o7XdQ zAXCoprGlWDO2iE?Hnz}-63AXbW#nEIXimfwG(Q8L1VQRg6K}RDQZ7T7tpi#d3~~qf znhKEV$Qw}LyS^Z&m1_wKF#d;?GLX~C`9VR!z+fCd>BJ;ZO`^r0lUNMvG+}MQl77#G zKd8<`T``FBo*{owX2C3y(Kg~UgLllLY{aiZ--r+L2U^mFbeW4~z{wOO4^O3_R1CjN z2oxls^Z-?kb`J^UTz!xlP*((=cu-rr80W9yU-1oc3-Y#A$V4FI&mMJv);U%JfwuY%DeD@z{Ex7C9!o#4FPl-J-2XV8sil zq<|MOuyuP7t)O^^-&cca4x%&zZyJD3l_AUmO{rnKgaN7#bSMdBw=m@7z^Z4gw=jUt zTLC8~lm!!sAYCS)d*{GaG#NTUJtss-Xope|g8c@y6m+^2`hrJD*#y3e3bky4+@!E7 z@N*z20Y^b=C6s;@2exr=(9Tro!O^(-RiH~0l)%^1u|uz?D*&&Q%heLhH4-d!7tG`W z?WF0!emYAa=I|gRww_oZRo5#9Vz1N#86fk{c$?^uY6cQ?C^-eX`z^nJS3juuPx=qp zLyvMuF=)aLzKRX0D?zojO+lF9NqoZ`?=itBzQN}4IMzN^Q0lU>hpZomc1{YAdYsg1 z5eCCc$Y2snNCF%7gFz_(Jf(01+(iLjh8e^G+IF#kkrBMZW(p(Je<)!9zp6%o=)(~~ zBVgd_6RFjemS4mL*`xuMffSUmvauL?92V@R8BoQFtDyyn0{E2?psSWp3nb(#_CQT4 z*rr=>=>n}E2^wOifatD7Lx*jLVYas>U_EFJy4e7=lflTu z7>0eeE)+Ux;S>xyd;)Y(&Jj>`+$Ab}fRQm6G+Gs0!N|xcoWaNl3nH}S7Lu3^Iu0)s z(ZB~!IKx``iLf)%62J>wASY`UgJ$we^16N~fnU{>bA@1KL)j97^So0A9KYKUkk=qdPtjupjdP9QlLbT&mnQ9fvGT55^{ z_{37sNujCm?3b7aZODT6G~mjD(4>vrcY)RG{@EI!TU(5fPYwlLatGS}25LKCx$hFY zY2aHcAt7UcY#2-koSRV^C~$L-hH60@_CfcABDZ}ZGgSB-0x5W zKqY#61u|m^A|a#u+Y@ zBpk{ZC7jC0E*#331v>E%yttE*i7}jk9ji{z#WvU)F5&Pgt#ELO5)LYZsJx^ooPnLB z3tBMTN%UrdaLl<8Y~v;*n8(S=$R?;56nqCdjB*DwjIv5n_y!|mFlgyv@E%Y(xQ3At zIVGT#h0r5%Qo$n>pfauqdLdkKPA2H;7SP4iCD7p$&|x(BpyS>u@vKP#mwma3>7dh- zD)UPfz(?tT&T0qSUH%hRM1i`h+~R%AQb|rScEU?Lcf2e$Vwq08s=M2o(CP94qA>H3oZk76%x{m^2;;x z(i1=hI_M04#8I7yrIN{|MMa=P*dd;RtcL>W%i~heMZQM|WCp030Lg*YOu=v1fz=-v z2IzpIJsGsx3KW8=nV>UfLB|v!2a^Ki#P6I+q!nvmiwlZU%fRji*$LugB5iSoq?^3d zRM3$rpwT(hWt=c4f?Wi%8?p*CIkBh+bd)u0-Bv#2IzZ5FIEMCi;)#UMm*Cz#hm0zGNJvnu#$2R4QVQ zRe55bb`DV=iFGl67RF)#@R{N95MQAc8a|+D@w9yHoD}GCy;KFz95*NgVGVA`MHEG; z$*GxT&;y6T2EtMyG@@WlDNw0_t1*Cf|1lS6KQ!1Z@FBt)N=o3?Ipm0(lGI!s$N}e? z(8FCp2joE&2h`N(xHIXS*sHSS2bLS|uqM zStTpM$_d2rmC=Tvqp}Qi6pTTIR}ILPl++4{vkY*#3U-7K=;~-Kh+C1m0v4dD3&_G? z_$m_;R|4=kRXBkcBn_4(ga;yDTe?ffdW05ln)L zUf@j!ic!L#vm+IQ5NAh1wjF?vyM_i1N+`k)r`17py&&xnlp+N*FbdKRD>Oh&0m%H1 z4$^tGu>H-Tju>RuHJ*`Da9bO+wkH+RiOB>V#|PPaUCgBbI*b~0!VKt~KTy>GK8G4q z!9pAd>2*1yaO8#YUIR&&jybNoei$TrGVTaLG&fi zMi`J6KVs=h{8|#e6jYJ~A+~xm!cKjo#cX9fJXOboQZ*zG;~ki1L|z^NO0QbrtFFkI zt&GP$uo(|3BbY$9Bd&m)JaB~3P%(*3@Bm|A3nOD-2P30k1tW6!prz~p$R>G-{5((- z7CK4*UZ?~<&QTw95g4da1j!V0fzBa?O;dvIYXY6V3mT5j&x6M`zN!*s6Iy0o31|-* z>KqNI@`P<0&(p}v!bP`rZ{OQdDyrGN#XM(TieH(;D<%cTHX zmy9n7M8|@n80IOB#Jo!6#ni~NAV~AS(FS;RVV|#wHpHt5msOw*P~>VP&Y9pfx|mz1 zl>}M9_oOp28kY*J5C9cyUWf~N;fEo}AhnL51F6S@BM3I#lVDAE=+1hKfdWP*#zc6l z2c#Or$GCzQB9V-FH*XT6L?qCvNoIgd#drmRZXi*N5`4pGsCbJ_@C9RF4%E$yAO@rOca8<<%eD81*v`w9s&zO?}UK|KJv*!X*GY>vH1#VPB4?@DV zJ&%!zF%_D23XcbZ&TwSK(zM2woKoODL68AVj47ZbLR3#M6-!eZ;tuekBY5xrg0zRz z@|j=(0G{zcIpJuz6Q46Edu@VlXG|<+gkH!SFAu8kV4Ekfo)iO`Hyz;f8q?tCHKu`E z%xR#2rRKmR(Sv+xR30=;15KVV3o32`Z|+cy5}d zD<_~sha%vCJx~({F<=C$)QZ95N?@Zv+jp}PlR?8*;OGbS#`8e~dYO6YIjLMoV@hDN zbQCfYbIL$Xf5`1+kVT82D`iR$^9W#-AX`BVbHph~NJE2QM}XGUgQojgjqPez) zK!s0yXBRAo(1m2gQ{?ptJ5=(8cp!p4^)yv zD?eN-V8AU2P8+$J=3)H1RHHfsy>TK*?l`Lp^ zn}u%&E~vAQ2vL+=2_0Wj)9nS%=_UP#T*rpGFb4TPJfy&f?l;ES!6JGcZw~e_%K?QM zq#H({MVbR@kunju!W_kC=z#!QL%721kr^lnTG;q?vOpLjpl!@6E!(2fMS}_ys0-C{-jS>cx+p<*x;Bp(( zZ4Fid&ESbJGD5wK5-k5AQ(bAL4te0C2^A8PONteWAt%>^ifd3g139)n1$M$sNn$qW zQWFK(;Wou3i6yC^niX;zG`PwxE=epZf!-1Xs$3y!Y@ppz(3l%Y5LCHxf!9Ysc8a8e z7f68G&X5_g;?ktz)WT9wm5;2mv?vdH$qw$vLQ;E6NTKU>2PX{9ZE#sI$J8Z@XU$O|GN1vz|`!2NyN`$3t>NFE%$(7Z&Y zrPg_%l7YyKl1FStVH8v}3IvV8$wmpTU^JB7#RfV>5HSV^@d#QDaR+r+OEM(UZ*Bo~ zF~OrP;KUAVMuHNx4(QMe&^#mf_Nn|lyf-+2!;aV`(@@0*pzbK7TK3P@0G$?tzP1{w z5HxUyqSg>~O|^kgtPm&)v;OC1=EWD}7iTi?gKjp0#ARRr>QWCTqPAHv1!^#|2r4iN zIz|bKatV4c3fg8dFdHAfb@dh_qag1g2B!Z;dPYX32Ij`h7N!=a2IfWvh9;@H#s(G) z;KU5+2co$MPM{Xcht^_I z;J%Y0_{;>wRji;bs}+pk+g$~77*TwQmKa@%@(YR;N<=fzQX^>VDtxaMC=G&d&nwT! zOwItOQTX;N_!uy}8C8k| z?qG|EtlX6Xw;-0oCZU}|4hkW(L;_u$C~U*T%gPA(o;cqY#SPAf~p&ka=qNd3JuVV0Oa_fROA^X zLu};~bYeakbWLND61Xmen**6>*TZ6z68Mg&1ongkE=Ce6ETPL;mqDp23w+^MacW6C zN(UqYTL%O@{06-p3hVIO4e$YNh2Ydv#K4YzzV#1m3-6IvT7` zHQ)g{yxj@t31!gmK}lZFb@ssv_JfZb)8Yp$T7qWL8JI&L?G$IxVt5uU1_yF6#aR?m zo)ajli=joe7OcT=gwarT6WXL5*z+jC^Isu2AKdhnECSu2nVXoClUiH?YDeaP4gx4H zQAkPz9TQ$!kPn$I1|ORR8mKBRNKMX61Fc$00v&UQ-mpc)3(Jkc zmjIaRfSQHi)_{%z=x{`Ac@$}o0j%51X?2=#3lHL5p)=FAZUxi7Dlj-(Ncgj_>`Yi4bY(w`QSnrR6T zD(eEsvI1Q7IVh5l*F}TU2{^LB>y0XO6e>Ymn?XyYD;1FHbWCy^CBi)L$(Z1DiE%6mmhGN83I#>^pi@^f^Yb7#=Ykst;5%lKdp%sBLJn#dyB^|Z zQ6oWCCwWkUFRq8i66|JCcxw>UxfJ9D-5Ubk#mfx7(bklwfd>@5TKu3f2WYW^rGH7a z?OPQL7)Qht+p@2K#-SGIo@LOE5&=+Y(ZmHlUEu&DBdoXp`2-`K_}ip zbpj})!Cg7%4iU&S1}M~_Ll#J5EYJ!A6w=TV1f(0PP62et0{EPul8i)1DT2i;@a_$e zuC&se9B=|r$Sg)|ctD*LYSPB;`nk?b%N~Agq zrWG`N2_2wDOJLY;O^!F3ZUjnWLH`}0*MBiErbCiiU<5|@*GW(`N>G(uP&7+Wf-z8r zk&&2%|BQ?QOl;s$Y9{a^AOl85K@&#M!Q_fu49v*(q9hCkCgX=$ud)OgAx8)oz%MZk zL29Q$E7o>=7b=1FI59C+VjEpXYhG4@Yse~4!G$vP0-hAI4@w0sW|s#y{}~uTW5VF2 zQ>N_HN$*s-q?bb=3eg3f)A4H5vI z`v6|010U`LA5dI?J_)7(opE%4q;9MS6oZ!0KsrW{tOs6X1*&~Zz_|#zZU^_BzMwJ4 z%wl+T4q5#V>2HB6ZRpVr(45Hy%FWX@|S0t zLS8;p7-SPzCwSl#WDvL@P=KF{3>lyV4;zBlu0vdczl8v5{}<#JfQ~!{*XE#u#+5ao z*ZzVo=}lCEXF>mL4amXS2nCv0HZBn~G6~H{LmdUgt;4Zo842ztLdMC^$4JZrFU}}UOUp@(X97(x$Cu`n7s0Myf-TQjgVehL6*6EfDB2zjJ^BjkAjB5% zZtRU(;2Tcdp*NgdAk--Yt!6Uh);LCcC|vv|Q*(tluNgxn1Ngc0UW z6wmxua4t$sEJ-akLEcpi9`}V70+3P!t-=N$>kQ4^kh7d2yM91-zZZb+WlDu?8Up2H z)an#eN#nZ1J{jy3Z~+3^z6otgrGQ-kTIk0G+Jb|lK1Q_#bVM_a@2`it4tZD`wm})W z-vC#JbcKDiA*>=tT3m=w2-@@nFM2@dYJ!zRh8YoO+Mzc%nT@r$gSbH%J?ei^VqQAv zI$Wef?U0vlfD$kmgQlKYAj6B$sb?j@P$R)ucELa{!2rg<5OB^8#JDYufl<&`OVHOy zFw|Yp6P%Aa7%?vzLO-dF5hD|W7WiNe5TfnLVHHF>doch@CMU`yX2QqdA%`4FMuCnA zg?3Hr0_FNHj5S-uu%(g0UCb8VT0A3l?$-7BB`*0msioEy0P@I<~$Rdq=MpKF&}JZa;yX zCrbVpGGdfd{fcJCjC_lLy~8 zOvG{W_8Exdou9bN86k?Xok57cTmid1#5PvyKovSO zfn^{l0f>_H!Q~CB@yevn;A@4W{)0M~beK@AhfgThBl<(sm{6<-g$I+MBFco~A@m7F zWM86$$A1OLTv3S_T_+S_?m?MQG=dBWq~V-X1P@<>n-8D^Y%%A3K;uV(yr7vy@OexB zL7QHR(<(C|=ffFyZ+f~3lEENHzoTX_9D{_%1Hl(AKzGYFFt7`nVr*w)U=%de5;Qat zv~(BL3&Afl^#Y(NLxI33sQFmI*i~R zxCMNK1J+ZWiRrdCf-Ar#s-70rh`rWr0)+^8LB|~M=sai{vMduD>M~?V8v!LKU`akv zTmgA79?M7oxEBuTEhCNOgIevl#<##N5>TfZlvKf2^?>HVVb@vd+9^PHMuA4j!O0y< zKN)HUV%3H+_#8C&sfb*l;-A&HICf1eC^ajwS7pM^@4jhR~U2um4t57!2Mn?bS8A!sEI>ppWRwD1Ckd?2vW`F zyH?Breyq*yR?K9Lqc745O2f>cy`3(Qy`3(Mf{I*% z4vc{cjEsRQjEsUJjL60PUbwps4U~Ri#ZWgG&gmqAgiLnh^kP+7?1dU>0)Rv4OBQbC61`j2RwllDR#+5-D zwFFIRe|LBr<|ZbrqiJo>SYCziymRc4hb_W|?l#^9og<$0zaX_Jx3nY?^+FgdQ(WW> zleJ?eVr+#wXmo(6!L)YJLUcw!#Vqh1AH^ua8b(9KUCe?NjDZo1jDazXjDkLlXg)&m zJ^YkDUhr{cps{M0|3R0vfCj=Ld-3s=n#ejJb4f@g6?i%bRPKS!1Byj1r&x^>R!&|C zDvgxb^HR$pZJ@v@h#4Me6A1NGd7{FlgMl5C^_2osAPtceMnl;oCcy;orX291eG^8I zo!G;I13I=y+;ZW()N&0(aSk3SgKkx2HRd-_GXV|1MEy_BfZWfGG{m(8Tj2q|pZf)* z^uTpLH@J}5=pJ|h)G}mbV(emI7sNc=3!_U3zBCV8o3|6*73u^x0=lrxOEEEaVrg#> zol-lgyydMEl=4|Yy(~P1JaV9;L^X5+#*3|a+aT!=apGbKQY{EgfOx0!h&gbv8y>*j z-~jGH^f(DDdFla`a-jAY_+D+e*MP`n4v)e_K@ z3~mG=jfv;yLGL?+x0|4?F39v6SULEZKTtCZv6LS?*_H&lqZ@L3VhU(|0r>0=0xPO> z6AR!cZ-N)8!q%v2DWD&_X`!QFXavqh;7d`E`;?G1PKebejKF6kNfG>}j<4 z4}k=SN^z<}5ontqs4CP zG&PYAx-$p)K1dzNCXtei(qe_c$KisaPP6oulF#N=!R&~5ji<@S(m9y(l*8B_3D zJBVcp8qkGy7Mda_V)D}zz@=-k z0#dP(0KQlw0kQfGR5pO36ncg!=y-XEHU;?JCGbgt=o-O0mNG$0^omPT6H~Yp5*5J5 zn<878D9c_z zQ+(J~y)ZFO0N1P&K@DtF9XL1hO#qpWIh}_-Iu1E+20R|32)S@r@ew!Z!eONG5aggi z%Q$YCpmlyFdKqZjLqJ>ZP^PRw=U^drhd@@0AXarj&hO+>NI*Sk7(5P=2j4gXD%U}) zBtXYoCBV-k2Ja++W?<+!y|A?>;8iQdrO6qvJPTUfng&|Df-l*_5`so_G$;i_3RcV` zZ$UT6V;N3E(~YSQePIZAfEm2TiAw>Y8PtwIIinX<8@LUQ+MW1eUy^x9s5?y~CJ)#@6Vbgjw)Y^Ol}TSQ_h$OpKGD z?MWlZDht^tVFSEh$59=P`iv2tf%LG+PO|?-|rkgszAL zA0+}xKCn_hvlx62JZJz8G$^N#nwMXio&jn*f{I;qonSuHP;eUsbny-s=;%^V>c)2Q z4y2_DR|`@K?z(~8fUD#NN3%w>8Kh7Jt)T|pBhRG(8T5k=@FK}#NypfXKos=K5W_+F z!vbVHkp`H+%W&>{iQ^>tGi4OMTk2u=VGe+QN@G76?3 zF0F;%Ux#t4JEWLX5Q0oNDu7mwfi_xE>-ySwkb%XJK@LzA3y&?x&@bpNQs{s#bZ8bf z7z{b4h}HP*F*WF|dXT#W;&U08;ivs$+w@LxbvgxJolXH&c&L>l&T|!}f@5VGW|ZO> zbe#%{Kk%&Q8PLc}l;8_SL&aGff=?Jhn{@)$Fft0xVf>HmXSBiv5^=?fME6)hBmLmA z0;$hR>}?VL*&6YXt>w7N2bfEdIX>~#Dbj;o_j$|?&luST}*#ypj zB$FA8hKfRhlSv;_Ukx?*%@qcC>!ar!q#20+jfr1FQv`@?_NiE7tEP<`dhmU5! z(*~p&1yT&!pM@*3VfK~5x|X0)8`iG`v5i1|Od|s$g0?UEAl}0)WAT{9T z3Ml$O$6KVHoOTivNlx|9)vS=F1(lXv&LBE}&VUBI6L{=EHcD^@qoHgUyWke^sw{A9 zbTGo)h!*CM1qq3=;NH6qXqRb9er^urxcFl5xH0IULsE;8FB0-gLNCs$W zbSZRXp|m))SRo-TCqJ=74|Kg~elGZMhm_Rh%oOl7dyqAmu<;65J?KTS(*{H?$2O_UtnE_sQG*wA(s*&JacEO2Upn7!*mUW3J zb3Tkrj5ER2z$^xK?2G=RAY%vcvw?ZQi+Gi`uEpt1vLei&>$xa$QjBT&*VidL{blzC#JwPKww2Dult zU19=W0s(SAc-cL^dS-JJZ#1X~Yl2LgmSO zp2&co6GhC5d?v=(@aD{HP;&;--o&?V3+rt1Y*1c;=p`@$KO0nVG7Bo=Jv|Y&dWs1a zFyP)QAKe z?%vPC?A~LH>*8o5%>$)4CP76l(0Rt74H6olF8U*G(E00uplw`t7#Ri6FoJ!8;#c^z zj~;B47Ic6(Xha9pb4E0UAcLo+1)v@{LI%G1kP9{z1iD@=H@_@3MFHNPN-N5T93%+R z2WnP~;i?$YPevSTrH+b_6#Xz=KVgBdVa?jgWJi3j;x)A>Y~r}IIno6xdJXcr0+C@5(hTAIF> zvXlnp=Ojp-l$KeieYt&!kXcfplhf-4vU z&wvF_Y6+e+61?axc$7=<2qSpo62_1o(KF!-7&tLBGcqwQz*5ALVkSz<4Z2zf_3}N; zlL6OYs~0GD3b?q&dvzwbzJx^@di;XAO-Ikzodw0O7x-Kl(4a43(gEw5$}6C?5J(G^ zkh~7rY`YNKq6W0hwhO^^;3803Mr~)~oI_Xy$}_~yAq0CsXW&Y}GjIZ+g8{*b2|P6i zT4IUpTa;k=58EZ{2=0V|`b0*$umZOPv_Z942Rt?onh%4`G=RlP*d>eEflE!!&r5+7 z+R&n&I@?@PyMnoim7r1E(&E&#(i{cQOh_hZ2p};T+_eV{)GN3a&!k({5Ko1X`ADy}*JwCfd=pg@a)iP=eu?TjSCW}t4JHN-Od zOV|uNdu6#mT~tJ)9b;|-OYjtypo{33 z7#EXwxYA;9Hd}(Fz(Xm-7(o`WGBPnP!N`yl?0#PY%CV>|e_}?d;cmv#5oQK0z{9f) z2^7)A+~T>Q;x6jHODg2d1JDi$NCX8^>C6LG_@M`ZOibV*IJ8p_kgY~_ zB$M&mtnXQZjF4nj+cVKd0f*tYzwlI`kFjO40st;r#S?578FhI|P0QE@GtP*4lcNAd~RCNkeVPq1_ z)C$aCWD;De6u5+uN$_M<;2B0H;Yvm;(CxHvS1~XMvYHDrGCOiG3yKB_%Cdt_U6hOx zRAd*Fo`CxP_hK+kq24J z!Ys(CB*@Dy$jSu~cVNbdDh38YPIEy6|@OpQ#&yO|C#8J}jlz+`-p={=J%6SFY0u`#m=vvE3eHnZ_= z=3C6h_n38AjBQvPSd0T$LRgFwSqfN;Ygk%Xj5}DSvKTLA*~ntNlVvZ9@hO(OEXFTc zKC&2pWsznzc43WXHI8MCV>O=0I-k|}JnMZ{J5ZCuJ-&TU-5 zy_VbfH#awrF+Yzfk8v~4WFF%sJfC=szw!LwG5*P;#cQm`8_sJS&6~|@oX=asYuw8_ zhu3%s?+RYyt-Qy1jj!|G=QV!J`+?V(l~0tEPFSV2%x&^S{tSJ1dtuu;&s zPjHT)@kYU&g2sCVPYD{|6nrUY{88|$pfQt>h>)?0kdBbCflz{wah_1KknsYcB|^r_ zgq8~#{}$pDHs%$M7B-F-&KEW=7H$wW?iF4nY`jW%ov`t4;giC~w}c-F8$T2NENsjo zA|_%iC!!=`94?Y7Vq7IMO~iPX$Q%*lxgt+Qj9-X|iyBLdYKt1{i`s}92Z@G>8aIp1 z5j9>ddQ#N*ndm!F%{kq8($UwC~nLtAueI8A>k-t952x?VSH4AThdrq(n`|ULDEIi z*iF)1(s;7uK}qAIl2;{-Z%RIsG!~Fjlrq+o(v>o{l1h;>E|jX2GOm?6DP{ad>YbGF zdnp@fV=w7MY2zyC1=7a*rLRaE-;rjLG3Jorkum0z;g>Ovmg$l)?vt4(W4uUay^Qf? znFlh)FJ#`x82^;fmNj;g^^!IAlbtJTyifLktnoqFtFp$=WLe~lMdXy_j1A>H?8J}viE&iJ?7e>r1Dc_w+|Ao*H(<3{<3^2Sr;7s(qRlfNx*{8;|Eys?Uc zor1B0f}?_Qhr%=k9o5gO#*AvJYQ}bI8EVE0)t0LnuU1>DX1q@ApPI3yx~;l# zxB6stq0XscET|!>VH~T`pkdsk(X3%SO=G!+@qUf78pd}u-e?&A z(h$@%*3t~sG;Y=G)->+doTzC$N%OI$@o&xln#TTGTe#s)gBI>x>_fjY)zHxP4K@|YUynVOp#Tbf##8mF0N znHq05J!oot)by&U@lDfbrpEtFh0Khl%;e0Boz3dajF*}%H#1&g_SDSyiy5oAv7@=G zxv{&shq>`2^QGp-E6ul=8{aU$V{ZJ_oX5ggz{0}9*xw@D!Z_Mux`puyi&YlJt1WI? z7~i!}voy}K%(FDEvuv_7?zdcLX?)1?l%?@G%g>g^s#f+^#*S7_R>sX%ldOzaTAi>m z=Cc;EHWslKwKmSS&bKzMw{EsJo?yMt+W5BhV{7B*)+RQ_el`I%#(_2qY>dy_T(&W0 zvsJe>*0v3@H4d{)wl%J_?XxvrYrDhN_?YbtTVoMB2|HscJ83)PEW4R@#&hki*%^Pg z`)y~eWUpp#Twp)n-gvS7HhbeU_7Cli9Jn2fB^|UKjB_0d9gK?|N*s(&JDhhgzVGnZ z!T5s%i=(ljW0s?Fp<}0`@kGa|j>d}|H#-`Cb!2if=5pe5GFEglcQW>IDsVEcc3R?O zyv%92lksk+Gfu`goPIbNvpEYo8_PTEIvcwuTw09O@eDYMkO)>1y2Q+UjaN&2^2d@j=(uuEvaR{BFjo zZZ>Ylv2Oit#+%%Bxf$}|ZG4AwP;bVNr=ZBAxFN?1+ zn=iYsafI(=U*qY%XMK$?`abkEe(L+l*I3L?&CgiR&&bc%-Y?eAxX7={&$!O7+s}Bh z-wr?H1Aa&RjIa8=^fTu1SMoPD@OSk$j`1(?H=gW&)Zh55|3!b}%l=pVja34Q1B`bC z>pX@(41{4QdH8?g^R@WIQQoa**-kAj4qe^5E)VVaBOpnPJ9dVQpc?lf!0*8P5+}6J~rc>}r_t z-LQvY#_z(I!;Qtm<-?7Y!wtfXJ;GDNjdQ~b!;LqGpAR>_7=9_-SUJKx!ninMPlWN+ zh?^0{wAFsOX$%G471%iZMPLb1}yFVa(GQ<4-XHvBo;FCb7mAv97Vk!LjMF z#`&?uvBv9TPsbXcjXf7@d^h%MtTAhxc$~3OoMW7EcHFc$|{!cV!P7+8mHcj$LG7d?KNHT6p zT9#zIB57rk@wX(FWMk=Mqh#aAB*0hjbA0dOE&(H{4v?sF~vW{I5;Ia#W+2s zB*nNhr8mWRU&_4{<7X+aQj85#b5f0WrT$Ab_Dh?YX1q7;beb_&x_G*=TDp3=aasD| zbmQ;o{29hE87nf3KV@iS8V6_AWE#)TT%2jVB=cRSv2~VTmhqIV!&$~Bv(9E2pU=9G zWqd#Db(ZmutY2Bizq35EjYF~{vW+XUYqE`7vgc+S-^_lLZ7iFknq#b)QBhbF1c68NaMDt2R!oF0VG;UH!V+_+#~_YGckCzZ&D(n&ukg z_L|Nb^daXj8aZ26#I^(Bx&+3c? z>SODT=hiQ-H(p-9vfg-A{jYlCfAx|L#-SPH8qSYo5|Dh z<`!d?7K0Y!(3V9l#s^ziT8(*H1zL@TT7_GU6I$1_8b4}%)@p3h*3xEtq3wH{@$WXl zc4Oss({|(F_T+ZsqV|q<<3sK5+Kq)f5<83=I*xQ0pXs>JVSK6Ma)+^Kr(~zGW~Xka zu~ny6r*TAQLZ@*`XJMytW9Ph1<7J(zI*qq?p6E1w+xfNA_-Cg{mvK;6NSASF*UT>C z-CZ}kj77TryN!dpL%WT`x+irTPwQURZM>>`d$;k$?t9(F&$?fA8-MQ>>@ha&vF$N- z?5XTAp4v0L$9P81^&aEbJ->U5O?oYQjjejEdyR8?3wn(Y^#1HM{@csjXDrw!*Jo_m z7v5(a-`eV_5>z5{*6xBGtf88h~?_8a^4=ky!r_2>5+@9jU^Z~U?U zU%#=y1d|EIl@l5#7`IMnpJ3cE;l>2xmlNJjFt(YPKhd~&V)I1f_K8y_8XuqdY@+eI ziJvAK|DR|y$vAjY^d#f>NyU?lJ11?JWPEzk=Sjw2Cw-e_%sp9Vva!Krhsnm#lQSn9 zw@;oo*?7z3cax1pr^rq*R-B?d#aLxZ;S}R_Q#MU8{yT+ns&U}djH$*sQ){LgH%#rF zYP@~w@u|k=r(T|F{9r2AG-Ii0O4E$hrWsE&j-6IC&A4h>-8AF=X&a{*pO|)UnlbBi z)#=90)1#*wmrk#qZrm`vX}WRq^fS|qIcM?nL#s+OJ;V@G@do{@J!>!GZ|+YOU^Q$Wo$XiZkDmbEXP^K zeY0-PGUlBvINLa8cEW7qg4rdrjT>jLnr(b)_NCdz*JeMSZOk)AXO6MS9E&-|u5tX*JyXu*XA#$Oj0E;O!PxOSoO#)X>~8gE(1w8)rkk?SI3 z&qZO2jI$QyEi#_6Xwf3$)r+<)GTyuB=py5ji~cM!=2|SV*w}Ef!(!u*#Zilm;}$0@ zHcni;VX^U_#mY;J)t96#G0s|2wZyn?N%s=tnM*b-F}}9s&JyEyOTH~J{{BqgHWyW8Zu`f54SZ=l4ICOc#a^t4u&C88w?N>&&jSaD*7 z@tGCpRv4dO@q2~w{}uczjfGb#tTeV<8M4wiW@W-k<7q3GtTf)d^7=~S+bi#`G`_d; z(@JCJRU)g5wN}}$GWK1SvdXw>)znqSi&x!RrTk*mYGdBjqN|N1S4*!pmRW7G+Sqn= z%xdHO)lI97XRKbk+W7G5PpgeN)&#FH4qp?w#yDzC;~L}EHB;9Z&s?)?jq$-XZ`T-q zUGsB|G22?1wZ@uj4c8i*t}R$=+_1KPt?|~iJJ%ZTUb|x>tyd$`W{>AJ7$jDN0UTW_qm-hI8X|N7wd#tG|7*Bdvj?^Y<9q8L ztT%qR-hP9z^M>FJ#^D=MHW*Lduw;YrnhhH^7$4qneS`784ZItTr8jD9G`8OuyV1CF zKFSXuM3&AeNTwYL~=F*e^~xy9IOOTrf8g=(*>0@8Lw|>H z@s5={jMwh?wZr(|4!)hnLObPm8vE@m+-Y37vv#L(*UtSrjbH5iu+#X*k`)WIBs9@KI7_r z_4|x__8r=1e0ksPea83q>FqbR*&n#yxOsp3e&f#lUHgr1?7y?$_|5(g`;GtZcQ{}i zbs*`0aoU061IC>P<{dEJd*Ivw<4*^^95DWRfcc=Y$U%*R#!d%g4jShktUhQw>)_^t z#&-^i95R+VBzMSI;gI4X_u;pQ6$HT6NjY|$U9X6hRc->*+Glwr6HokQD@?m4{Bm75<<&P*IF*Z2jf5f=x zNYxSJx+C33jMpA{a>V$}kq<|VjgGn;H4Z(Rdepf7XxCBWoF{FHIWsh(5Db51QdWxV#(%~Qrqr@2lW^PLVjZCrA??6h(D>6538 zub=*O+W7Bj<1@zYXIjn}Z#c8#jPbrRht3!uIdk-k@zpbL&KUncqj1*P@vQ$@ z&l-O@Yk1DM@Lc&hW9jo+=Z*EwTc0=AV3oaP1yRhei@!bn@7mbxJs$4Wyz36h$IOAgDMdR}qzg{%{ zb@9(dx@;_TS?;p2$>s3N z#<`cPFB?y|eCo3Cxyu(W8(+Nq`LZ$N75*#6YFFH^7*}1{cExz#l|xsIk6bx=#aQra z#Z}{jSC3vb{&@B4Rb$p`oY#!SuGwERF1l8A&A9H`?rX+>uKm4cEPp-gy79K_hp!u7 zx_lk(z*E7A97W)3@W96&g0>@r zuc}K<1Ez%=@p9l30?cP*TZQ0J{7xPr+S2Cp9rw zkAdO8f?sMmXfB*RH#M)MSRpwv4>V_;n34jz%PucP0eTuVmc>%g4O7gSdH+G%Ws6G^ z^EgVtcfRW=lxJk-q$(t)q<|K&gSKgdjRA4si>^|^h8LD*7Nr&|K<}B)WX?kdg|yYcEBixH7LKu|fefJ6}+gnwD7s zwiI;ZFZeD2D^L(Yms&9}=Kfd6Ni9*xOD$)K2d&Cdu!Y@+r>B=%ky#9y*au6*GVp^o zy@E75=A|ekD&(>!mLz9@%to>>J+%a+1H8x!vfl$9q4}_TIjk5M|AUMIt-S<00DR|a zP9_6?afw3pe+31FXpmNx64>Q}ItqGvMXALlu?o6&3Lq(n40~!`2}pH3NSIX#9DD_d zC7`Va3b`P+r51x$AV7}n2m39vSRqwDx1gj_iCLi*>K#Ew&_pG(Ag7w4>3m*cLBsEY zyh(zBNuUWq$tdXjYJL#|drE2%l$Qot@03^qT1}$r~li%bO%D$ZIa_$Qvc>%4;R;$}27$ z${Qsd$!jGX$ty0L$r~k{%bO)!$g3_qkvB?sDzB69R9Q zPQrWn#f8uECkbEVcM`tHFE0F$KT7y1e~|D?esSTS{87Sx`MZQ!1>A*Q1)_vK1+;`c z1;m9T1)_vw1)_u#1>A)f3M2_H6_65MD$p*hDi|fKDQG3EDX1>&DQE=3#-4)H8HH~O zMG4;(N)mo3WG*Zy93?C&tRyTdEH11n93`wNtR<`|tS;;+oFwckY$fa~tS+1@Y$Tj3 ztR$Q({9M>qBuY3?BuY3`L|iykBuY3_BuY3}L|nL5BucnZq)E6{#9Vl*NR;qS5hvlD zBI3d)MWTeyiX;hN6mb_86ipHq6*UqT6%`j&6^#}&M5p+EK2yT*e&6oV(P+@;!(n~;z`1a;_kwU;!(n> z;z7ch;^x9L#iNAhihBt!6c-oXC>|xeRoqE+$ z;hB4O~SoW=E8fWqJ$4hWeFdZG8g_T6(#&rDoOaSl(;ajbd<25w3D!)w79UMbd<2F zbd<2Bw7RgTbds>Iw3M)~bi43b=_uig(ni7;rNxCGN=FGlmDUn|Dy=TeDibBlDHA2k zE2A!~D-$JbC=(@YDq}93DibA~DHA1}E2A#lD-$I=QN~Mns*JkuR+%8-oibL!J7vU$ zPs&6IpOuLcz9=Iu{7@!J_^C{k@JktS;h!>5!hdCwgc)Vcg$-q+giU3wgiU3|gcM;h##Agc+6H zg&mcngk6=bgk6=zg+rC2gd>%sgkzP>g?p8wgeNL{2~SlP7hb9yCA?DEOL(oax$s@( zDB*|7LBda!-GxbWxYc<`4_i9E7AJoheKB{Rh{8ckb_@}0q@Lx@FXl)0n;n>qt zOB`941UVQ%%|_!IiFygBd`>0`J6X*_92}Vqk zk3Qktk&iF@zQD+A9DE`B0wbfb!3B>C zs769pc;_<0uJpiV&_%fia*WKzM`SO{GBO&^mt7|dx$cJ9Kie^aNpJ(`Af$mM85xbI z3C$2a)xruU&jK(W;SLre`8h_CJ1fnF}q}>=9jZ@vyLDUYP zT|SJA#x?$R{)~*qo&Mb*>R9xNXhufk!0Ex$85xcFX9>;%MbX@2a~TEY#v85xb2UtW2ck#Gmnv!c=Y)7peR9E^5&?+BpAUc z7{Evg$;fOBTB69vX#BX~Z2=>r@q?mQMU0HbGfNhOPC?jEvI|71lIc<~jK)8!nQIssjVENz%0zW5c&$3*pj2FeR%~2j%*brK)98>9Bct&g zqoqbDMu2zffYuOdfQud+BqO8oW`pepjLgR32GRzMjK;a^3fD0*8lPW(c|9lr+&qC6 zpH-O!@u0Iv85nS8?8=D72u5b(tKoOU85xbYhVKnWaTxeQreZ{q%gAi}a{h<;P&JSu z**-I`IJKyRpz|3Rj8{mklVD&lekAb%MD0l4m(0Lm{OsPFdkhT5^0!oPLCuAhPnn<< z-q20!NQouqWZ6j+h4SDs9HGLfixss@MmygUvv^*;YWpfiX5;jg#Vb)v{trG=sw6X? zfiVwZzVV-Btjkf_=$qnZcFWYE*laV5IRx8~hqWHx?yPJ#L5GM$uTGmILZC=V5m}v+*xWMk_`}V>26D8%9Rs zEhl%QMmp$#X-vy1!mB~)_~`l*>rpKG53B1K*fUWIr?g3hu&NB)ovw%8TnzIv1B3CB zuGOf?$-X=@FD1Vmri78%*k-NMT1ce>UU=)6!X$WtQSbocU?V|g561!yMn>be0v`oH zRgd%Z+n{RApWHi3D?cO#rY#;^ai+(j@ml;Q|&5!A|@U%n93%G}Vh z3DnBW?q-&grl}H?Q_7Fj$&q2B~}eqMn+@3btdZ|SqHLI zeK6;piyYTLdFQae2~gfSE`0`+cM1$jL3u~XSRIsiPWhY%<(<-?Dp01W3u*>YqM=ft zOp_a01fu$qCxi0N{~nfJMn>bW{l7qY=iB7ppuCefJq?t1fNtj_4MtEsTuDO54@9Gcp=$?=jfJ$Y@+}uN0Jb{BMMy7WMx@StqfC zKz+f{E8NS-Y&^SX87Na%_q3t5$v~IZBjp##%QC2y51M|z;?U|=x*c>eo&1_ooB+fKJ3DFd`c!O?Vn=V5At-jH%$*KuaEmM!U(Cp8 zT)MOZ)Zq5n?+-MffiEWMrC8@pmz-r}G&VoybdHhP_{5pZXFy$#Gb(4GCPGtzd|FO^ei6)41_t9f zHy5HcW1;164g+Hjek%`Fp8_@1Luz7c7@3V_Sjy_C+ zGZ+OYFw&D`WHhc2ss$zIe324Ra-J?R2h`KtEVl#H)6~^7(qm*aw$*b2QPbV#fV!CJ zJ~^NiZ{=?fO7XM(7l5cY(H}txKWlnED8*~c(gmgXzPXb@DgM#Y=b)tiZs}(b<+k4& zl;V31PXwj-*2`UJDLy4X9(>9U=;Vu}O7QV$@!%SmIkgxq?=vzRYg}}?$jE5ScR>Sf zH~?~78>|Gw+lJ0eE&=7%g~@A@8JUgOByCS(WHfF`>Q6$6zyF{M&_M|R)C|Be?6FXJ zl`w}EU#y<`)9%N)RzHs0sTDu{sG&3h9z93y%1-e8!vIY&A(G8?aQ+UW!u$Z_g%LUG=I)E&3DJjpRnWF8~4G3#7GP`USb&igs2ra(G^ z@ktEqmGMSMxqIsR1+e}nWQ?RfBe6I>JGBy~hJnG@DcLg_)ilsCBCvbyFylUVWBEo# zX5;M}4sL*IgEj753&|To<}BteW?(R0Q?d@!xUVd)1~u+?x9kNq?yEX#K#lvH%sf!z z{%F^6P~(2`m8qzWdwK8)-!M-xG8?;Z^?`LgAeAjY=tOT!&m25_^e`i{vCrXv!%)va zl>Y}MoA}ba5(ef>EW_yBUZP%%%*HaFYM!XtkWcr)6`6D5mc=nL8()mQ9m~jQyfJon zEUFn`tBW$z8JIIlFkS5C5a0kxQ1)BwQMG}ZO`w)9s1Cpw_usYj&{9TbW5%VNOQE{J zqYSX7FSO2JU@$hlWsNcVm6BQ!pO(+SoP*rw6?Kxwl9WN?Gz`qC@OBg(ie@u|9t zAgZfjVgn8#f4lz?s18$@pgsZA^_pA*>N_(|;h4h6XxzTA zZy_V2@ulTAmoqXNKU)40MD?tj4C+F6ZJr1kaFW}i3hFz%ZTAKBo%d`%45E&1KLeu7 zcH4qRp(1w2gQ&#a86YZSPd=!7-FmPG)bM(F^aH4N+I9ROsOb}ZHVM?v$vImLq8^@p zahj3QIDA#yDwIqi4_-ZlR8pD?*$APi_z&&o6f-8L=7H9F#mj?_62?`WCD&KfGcp@H z)`o$?uD5zoHHvZkpyn--eI-g2XpL9cd4i0vSpr-(S;lz8FftokMtgvwRVZ398pWpn z(2Ks~858qT;z6Ty?5TMvjv7p$8515 zK`HG?^LNnL+T!+Y?Tn1ZXXf4q)z7~d@PT69?nK}TMn>Zut4^av0qh{HT+n&S=mk8p zaj0xOX#Q}{%w@1*6WT-lpIT9npO>0f!oZ%Hh+Zi%Fc>G?%Dx4fg1u3RHs}Iv9e~zs z<>PEe2ds-;$H;8Ff9)yI;Le=2%hy5!7iN1-etKe2W=RGEV=kJb85oRpZkyg_U@$Ja zS&g;NoRZ22IV>bE-5%WYD#jJZ0gY*mpo*$N9F)!EYfNjP&Vbb&`RVbX@frr^{9JsU zaAxB#xBlH?WHerMbInagM&q1YWguSFjTW@PfuwdM2Qn7pGndg=y~Cn|k(on@c1$RW@i~=LGv5SHa zsO(i!&{Kf=2paUo&_${YghEKASQpfPIahwYoRQhMJg*^-kfy4+u=qeJV_*igv$z7-d zs0X3e9BY@BA41)8#zSSPa%MLDcA!JoSyNxzk5WH$aJ^;ZfsF(7pf zHhBgK8}Pg#wuTsJWrz3-aRvtC*-7(~7#NH>?sB7r8hCcJxFD5*y{H(uJtmr@!F%2y{!ERk*U`)jf zKGD;1p!#s>$#tmHckru(7$LXbAtWOqmmNMvL--kNYUfsxU;HDM}R#!Sx71z$VCzz@1g6Iazd z`RIIb!FrVY7$dXs_an?lQ5^&dN>C~Q&iI;|IVL<#Ax#)MKt;*&DWI-K`0`ZL#UcMeP65q1Fn})x#^s^E zx}189%*L9!rl4*XyRNVuF2f!1t98b(hbmv z|Mh9Nrh!%$%(**hYLV+7ipI>QRgn| zgY%0(n@X@p8ME=#tUI7VG3&=i(XC@2IS zMBG3j*d*Hl>hFA${R5)z>O9r~twGfJ2BIQ#<3Ne9TDK8I-FAKG3X*gE2BPeITtFfJ z&yNKZ@`C>2AnJ3}&nQseJGvJX0cWRO28H~-IY&StA3Qe_6!KrtB)6L4*0- zwG%*HXP*YpEvd%6&67Y~N}u*XP{$(Rawur!)$xmGE<)mzAGCwac?t`Yum&fiumUIJ zC_Neuqv>EYAB>g*qvgP8IWSrdjFtna9tds^qB!QRaNse2k37-2(lf<(AGu?x5DIiHHTL_3AF- z4WeAdJwfBMx#EQ&YMb~j5cORAHHi8r&IDTeG*N0QXzXpj)L{_CBFzDs?^2W20#QcN zW*{m}HVf1So-8{ZMBSBr2%@;<_(4-*l5(;j>WBOv&{(IGf*fd&HdrAXL=`ENfv7r# zCJ@!2)B;*1G(l+!h}x>OQ;CsL`HvE4CXG*72t=7ETYxAhWj7G@S>?M5BcrjCsvBs1 z*(B9zAnLN}brAJJ^$m#9(J%mwP6ca(gQ$5Li$K&BjT<29k;XF+wO{8jsKx$W=QoJ5 z*L4Q9*mHFYK~$4&8;DXj&<3qgbu@4VQDsI|paIPZMpHo4Nu#qM>b}uq5cS&VJ&2Mw zQ8oeXbTid31?^okivi8?Wt!!Js8X{^5Vg}5K%1N_CW0tF zDBB#bm<<8c3G2Q36p~HhLhcz_tXm zFyyfPaeL5eVJ9QddXB?R$3fI$*X5udiK(ZhCnKXVmk%GPWAec731~&lzQ99)jEu%d zLr#V;G8#99w}JKrOb?$8q85iQ2T_m1pM$8+;om_NV+1Q`@=`cL97LUqx&-RbJdJt@ zqJBmF15ts|p`h+fa&$U~DvmA(QGL;qKva8dH)vVG?AZArYDw%05cMqfRV*kG#DZo( z`Qn5?lw6z=h*FQ!22%;zps^vx1XmDsCgB2zdXex3M14v40itq}3P9c2iliD4wJ+%q zh= zQS6o6pnecjB^!wHs`LXy9h+htep#@v>Wt6(?erfj{{K|?KvPyqe}<0k+Qz4 z8ASDVO$1T-J;k6k$On6lf~d1S7eSP4zanVX*Q(zRL^=1ngQ({Ib`UkYe?Ev>*}oP< zZR+0!q8cW(Ok!j--actJh&nLo2#6A%EDoA2HlJ(_qTDBYgQ$SXAs}kev}K@he#5aqSp4@6b1tOG63?OxdrqS{t= zfv8_A|A8p(Rs5iFGlf+uAWC|bJcu$`WdWk}R~dt-pw(fZg}uJ313^^U>MRgdxw;la z^{t)+qB>Xif~ZBSmw~9cs~3VO&$YgwsmIi{nII}*Z3>9Gy7nfBy0G>Nho7Zm#QRg;X+Q7(Y%)F5uv^ss|rnQ?G8IA94dIX~MHXDHk z>7q8rfvAklIUp)*YZPd+Lgm(45LL3Z0!01a$_(1iV7tu`Lfo-UAnMw#TOjJgleRAnM1y zKl?z#kq251Fftk+I&cg`aUbLd&F`BZv<6X$2U9^*_~B^K5C!*9e$Xha`cZ8VrF2vc zMDZRM1T6uOJT42O3Xhk9sQTm0AZp_AsUWK9WZOy53bd1rAgcf5WDsS1+8i{RuXoxA zM7f^!1W^vBT|m^7(=$L+|LMsfYR%~lAZq#P)gWr$=|doD=jpv5>d~2Jpc#U@XC8v6 zZ)bjiD6z9rpmhj>XGKAj`dMudWqQ^UL>Zhl0Z~iNuQ(6d=5u}vh$^~J2HJqzb)gSL zO}j7)M1@|A1TDkOy;umMv@h#}21BM_n|+Ou(Rkyvtsv^#jbAqy8I9N7*#ufpdgIO= z5M_MV9JCy;@^0;2$gqjLTTXsriK7e?Xn(T-c$YIFl9AE4@$}G1zLAzI%TwDnf+<9@|Md&ywWJ-TJ?QP}$3KQvl>1)0>tc>eQWcpa2NF8wCo0?7R8U0ATmbD{)-HB<#V+DD1$< zI7(9%4xssFR!2~CvYE9FM0KRdIt0iu$f(m)N0*G}(16r(eXGid!&mnf(%Qs`0!QMz3QAS$aX z7etkIRe-3Lu67VLscR~TO7F=6)l=Jhc7Z5^eq&H2)Zae|L`AHP1{Ing*M0#}s=G8m zrO>=x3qjQCUF$%U)KOVbp|bMY8c_ZcyeR@oSMoQNK$O)@TM%{N&S6k0JAdaAh;q5> z4oYm%cjG`*!QEnLngibt;^@L8*uf~+z({Knv{0A*6e#qyIP^fl?#$;7YPT;DSOIFc zZuYIsXa%Y&NQ#y;ktM%uH$SD@C}`@qj2>TSqJP$TVI$S)AJYSKDTJ*_oa4^$1H%>u z1_owE1_ob728I|$28KjN28P*;3=9hx85q_vGBDg?WMFvA$iVP|k%2*%iGjh0iGjg^ ziGd-IiGiV!iGg7^69dB`CI*JnObiScm>3v9-T=856xW~-Epv%I*V8g<|;K0Jb5WvE~5W>R1kjTQoP{6{#P{YE&(89vN(80pM zFqMUYVJQm(!$uYchMg=740~A^7*4S;Fx+KfV0g*G!0?fUf#E9)1A{ax1A_}I14A?` z14Aq;14A4u1H(jC28Q{p3=HR485r)fGB7-5Wng&9%E0i9m4U&Gje)_6je((yje%h$ z8w0~yHU@^hYzzzs*%%nku`w{bWn*CY$;QCI$j-pP%Fe(b#LmEA#LmEA$Iigu#?HXt z#m>MG&d$J4!Op{h7c|Wh6pYOhEy&FhEgsDh9)ishAu7!hCVI^hS^*U3~RU; z7eg=j}eg=kEeg=jteg=j%eg=lA z{0t0p`571%@-r~3<7Z$v#m~TSi=Tnv5kCXNGkyk!&-@GwLIMm7N&*ZFS^^9VdIAg# z)&dL+Q34DMnF0(9g#ru=r2-5Ls|6Ssb_y^soD^VS_$0us22n8v23s)(h5|7LhDtF8hDI?4 zhE_2KhDl-!3@gMK7}kg}Fgy@rVE7`&z@R41z@R72z+fcKz)&I1z%WOgfnlCF1H*iA z28MOw3=I3l85pjLGcbGH3=AG}3=D~K z3=HjZ3=FH~7#I%AF)*B#V_^6y$H4Ggj)CF790LQRJOcxhJOe|JJOe|mJOe|cJOjf- zc?O25@(c`%LxF)|ngRpE zS_KA%w+ajl9~BrFJ}WRVd{JOvuux=Ruu)`SXjWukXjf!ln6AjcFk6vWDs4_5gsWLFkR%Kw=t;)b~T$O?0jw%DgXH^CU zMl}WoRW$|%J2eJ|3^fLZg=!28%hebdR;w{EtW{%RSf|Fo@K23_!BU-p!B(AtpFs#yMVA!tB!0bz`zh~ zz`&4Wz`#&xz`)RLz`!uafPvwt0RzKX0|thR1`G_B4Hy`%7%(uX7&0*E88R>!88R?5 z7&0*Q88R?TGGt)bX~@8^*N}nXtRVx#MMDOLdxi`QKMWZdIE@$>1dSLNM2#32l#Cb{ zY>XHff{hp$qKz0B;*A&>W*IRsY&K$G*lNVUu+4~p;j0k?1FJCugPbt~gSIgPgRL1H*k|28K7r3=H3l85o#N z7#O%s7#R3X7#K877#Q?S7#Ivp7#Ivq7#QMB7#NaG7#NC87#PY;7#Lbi7#Jp)FfhzB zVPIHf!oaY{gn{9P2?N6&69$IYCJYRJOc)qgOc@wBOc@w>Oc@yTOc@x=O&J(0O&J)h zOc@x`Oc@xmOc@xqn=&vQG-Y5oYRbTH)s%tZrYQr%GgAhJf2Ir!LS_sMQf3Sca%Kz+ z&Sne@^=1qVOU)PcxuML@WqUQfz_OW!O@(7!PT6B!QGsJ!NZ(^VUjrm z!%}kwhLz?F4BN~Z7;cy|Fx)X`VEAgzz`$d{z#w43z+hp)z~FDezz}Z1zz}W0z%bo{ zfnkLO1H&o{28PuZ3=B6d7#QwaFfgcDGB9LWGBD&>GBDIxGB7k*GBEU8GBB*OWMDXC z$-r>Rl7ZozB?H4}O9lp2D+UI8D+UHfD+UH9D+Y#UD+Y#1RtyX)tr!?iSTQj0Su-#Q zSu-$*STit)S~DB|KpO^z1vU%}=WQ4mF556Lu-P&&sM|6yXxlO{1lck$gxNAMB-=7DRN68y z^w}~nthHre*kQ}SaLks0;f5^(gNPjigM=LegOnWugR~t3LzW!_!%RB{hPieO4A<-! z7{1#vF#NV-U{JDWU{JGXU?{L>V3=>uz_8e!fnl3H1H&1628M_B3=9ko3=G^33=EPE z3=G;13=Fvr3=D-13=G8%3=Aa>3=F3o7#PkwFfiPAU|@Lcz`*dqfq{X=k%7U`k%1x0 zk%6Jmk%6Jpk%3{NBLl-!M+SyPjtmT&9T^zDIx;XYIWaJBIWaKsIWaIOIx#SqJ25c$ zI5993I599(J25aUabjRt=ET6T+=+oo*x}5;u*aE!fzgG5AoSqB}f}RWvqMi&4 z8lDUccAg9j!JZ5ZO`Z%4y`BsVvppFY)_O899PwmecRhVT%_7!wxS7hId{J44=Fh7?`{n7}&fS7=*nU7%aRQ7(Bcg7y`T* z7(%=m7!ti17}~rU7-oAjFf8_FU|8$0#QHEW6!|bPbowwbtnguAIOM~?@WY3Jfx(x7fyI}Bfz6kJf!&vZA;On| zVX`j+!*pK;hO@p53>SSF7#{jEFg*2TVEE+Az#!(wz@X;Gz@X>Hz+mLZz+mskz!2-l zz)GcYLmGcXwV zGcdUNGcd&XGcc6+GcZi{XJ9z$&%kijpMl|`KLf*Me+Gst{tOH%0SpYq0SpYg0vH(f z1u!t23t(Wl6u`i6KY)SZPXGf0cOV0Ua3BMNcpw9VN+1J+YajzdNFW14Odtb8LLdV} zZXg3gUmyd+yg&wqWq}L~s{$Dqz6CNcummwM2nR7RXazAacmy#p!<=9ShMmC-3@3vb7%m1g zFkB60V0aYFz`z{Bz#tUDz#tXEz#tdGz@Qz%z~B?Yzz`F{z>pHcz>pEbz)%{(z%VU@ zfnjL~1H;-728N9x3=Drm7#O%i85pEP85oR085jaW85mMS85p`l85kyqGB8XJWnh>Y z%D^xyl!4)HC`1Ovm(2nL4R z5ey7>A{ZFtBN-T!BN-TSBN-S9BN-SPBN-T4BN-SbMKUm~jAUTg70JMGD3XEUSR@0( zr}JI2Ogga6O8F;T=d{Gy{WiGy{WaGy{WKGy{WIGy_9aGy_9UGy_9ZGy}tu zXa*s}mR))+aD9Y)oKa*p$G)@GpUZfhUoHK{=6u z!77n~p*E3$p)rwxp*fL(p(T-lVOJsp!@fiYhS!M<4DS;e82%?RFfb=EFbE_uFqkGW zF!&@fFoYyAFhnFVFf=7GFf2=AU|5mFz_2ojf#F*c0|QGk1A}xj1A|dA14Cpo14COf z1H<%W28Kt;3=FT585rIrGcbHeW?=Z3%)sE7!oc94!oU!m!oZN6!oZN8!oX0H!obj( z!obj*!oaXEg@NH-3IoHl6b6P@DGUsTsSFG`sSFIeQW+Tjr7|%1r7?#7|v%g zFkHxDV7Q;f!0JO+l6JO+lQJO+lzc?=92^B5S8<}onb&SPMBmdC*GCy#+aFrR@TGM|B= zJfDGKW8hI2&>3{u4m4AI353^BzF46BP782%J9 zF!+=(Fcg+BFdQghV0c=>z#vh|z~EfUz~EoXz>r$Xz_6{9fq|urfkCy5fkCZ|fuX#N zf#FaY0|P@j1A{_214BeP14Cyy1H*=L28ILW3=Aw43=BdQ3=DD=3=A3-3=9Sp3=Ad} z3=C!!3=HNK3=FLm3=HQg7#JQ`Ffcr?U|{%K!N4F`$-tmq$-v-R$-od&$-q!l$-vN5 z$-vND$-r>Dl7ZoEB?H5|N(KhIDh7t!Dh7tRRSXRCsu&nvRxvP`RWmRoRx>b^S2Hl| zu4Z6(UCqGov6_M5Q#AtvXAJ{`Ukw98Z4CoMa}5JSdkq6aXAJ{GR}BNhwHgKn###mj zt6ByIyIKZ@h*}1Ql3E6aNwo|ND{2`S*3>dE?5|~DxK_)+aJ`m+L7|R;A*GIiVSOC~ z!_ztjhG%sQ3niv=~niv?$niv?4H8C&R@2F+`+&g+R4Bm*~!45*~!45+sVLS)ycr%)ycpR(aFG&(8<7%(#gP3*vY`q z*vY^!uakjcStkR-s!j%m?VSt^CpsAz-gYuDeC=dl_}R(8VA93F5Y)xM5YolK5ZcAS zFtdw+VRsh;!_6)R29a(C2LEmbhTv`nhR|*XhOll1hDqHF4AZ(97?yQ2Fs$lkVA$Tx zz;Lmff#F^^1H-d!28LJN3=H4985jh67#K`@7#M7O7#JLT7#J#h7#ODZFfdH-VPKfi z!@zL8hk@aB4+F#R9tH-JUIqq>UIqrMUIqs1UIvDoUIvDOUIvB(y$lRLdl?x1_A)T= z_AxLB_AxNX^)WD5_AxMo_c1WU_c1Ue_c1UO^f54W_c1Um?_*$C-^akPxsQS2Kpz9c z?LG#EpM4ArjQtD@to;lOe*Fv#IsFU_dHoCw`TYzGd;1v}&h|4feC%gn_}93A$}4AL-8aA zhR#V03>zjfFr1#m!0>qz1H;!z3=H2UF)(mXW?+z+%)nqUnSsG!G6O^OWCn)J$qWqb zlNlK1O=e)&GMRzl-DCy^(J2fJvQros6sIsSC{JNvP?^HOP&kEwVcir4hD}o#82(OS zU|^iezz{fg(Omi6+gy%9aXw79{FqzB1;5V0np=>S#L(5zShM99281~I& zVE71@hZZm}Tv))s@O1$LgW*C3hT4S;3~LuMFl=1N zz_58C1H+bu3=B+*7#P?VF)+9;VqoxG#J~`?h=Cz%5d%ZsA_j&jix?OdEn;9;y@-Kf z%OVDby^9zajxJ(gIJt;{;m;xl2Cl^n3=)eO7z`IPFgPq`UbQ2?Img5(b8>B@7HzOBfjHmM}1MFJWMqxrBjX!x9FD zYfBgy?kr(oc(;Us;oA}hhF?n<7`T@*Feol%U~pN=z!0^Rfgxil14GqP28M}C85mYB zWnj3ql!4*| z%v{F6Fn1XP!>MHq3@?{4FnnCb!0>e$0|WbV1_p`c3=CGw85lyBGcYtPXJBYr&cM*T zoPlA-at4ND%NZCxFK1xjTEW1exq^YAWCa7mffWo4Csr^poLRxZaBc+y!}%2q48K<} zF#KP^z`(zffkAjB1B1d!1_sNO3=APF85m+#GB6~pWMG)Kl7V5#N(P3_D;XHBuVi4j zy^?|9?n(xRdn*|jKCNV6U|z+*AhL>qL2DHQgZ(N72H#Z-3@NJ^7^+q=Fic&=z_54~ z1H-LV3=A(;F)%Q!W?lo#%czJwW}Ez4zFfl__UgVfnyB=L+}~~hVV5E43TRX7^2oNFf^`VU}#;#z%X?U z1H;TU3=GTGFfbfk!@%%%4Fkj1H4F?t*Dx@!tz}@4SlhfGu47>Mx{iV2=Q;)kw)G4Qit8B|+}ATO_^)SR2wu;?kg%SC zp>#b1L(_T&hOYGt41Mbv7!I#zV7RxQf#Jb=28M_085rz0Ffcf8U|4k76u0MEes5nTNoIuwlFXx zY++znxP^gX=@tftty>rvc5Y!{IJSj>fn_TLgUMC~2Ai!63=Ufv7+SV6FihIYz%YL+ z1H;3u3=B`VGB7;f%E0hqD+9y#tqcqt+ZY%mw=pp2Y-3<>+s42UvyFkFa2o?d{Wb=M z8QT~bHg02JxU`LdL25e#gYI?)hScp0470W~Ff7^5z_4aJ1H-263=B`VGcdf|&cN_` zI|IX;?FW-AheT#L4GF#gWpaD zhQgf;43#?>7;1MiFm&x?VA#Ktf#JnY28Iti85q9oWMHt}#lVoWi-93;7Xw57E(V5{ zT?`CUcQG(5-o?PMVHX3#sa*^V4|XvyeBH&sz`UD*fp<3pgT`(K2It)j47Ix%7+QBT zFwEP{z;JRm1H=8@3=GP97#K|UFfcUiVPNRk!@$tDhk;?z9tMUfdl(p|?qOhfxQBs3 zdM^V*%3cPBje8jww(ezMVA#jNAh3^tL1Z5TgUUVz2913T45s@S7~=LZFcj}&V5r{5 zz)-)BfuUy~1H++x3=Eg|F)-ZT$G~uZ9|MEleg+1c{R|9&`xzLT_cJiG?`L4>+|R(! zwV#3E#(oBdJNp?J-t1>!_^_XW;qQJ128RO-3{eLd7?KV!Fr*z|U?@Joz|eVsfnnYO z28O){7#PkSU|{%kfPvx50S1Pz2N)Qb4>B-_9Ase7ILN@@bdZ4|<{$$@?m-5I>Vpgn zvko#aY(B`qa0jI35CenMAqEDyLktWGhZq2$V9%5h!J;cD!a)^PU;}8SG`a=v1 zn-4KC96rRraQqMh!<9n}3=a=6Fnl}2!0_)70|V1x1_u7a3=Eow85k@NGcY(FW?*nV z%)n4`n1P|`FayK%!wd}T4l^*EIn2Ot;V=WkrNayimk%>Aa35h{;6K8^Ab*5`LHP&+ zgTWC72LB@r3`Iv67^;piFw`AkVCX)=z_9iR1H+Re3=D6MFfe>L!oXm3l!3wJC<8<2 zQ3i(8qYMo7M;REpjxsPTKgz)H;3xyb$D<4khQ}BfOph@zm>*+cusFuRP3=D6NF)(NzXJGI>&cNV*oPi=O(OlTR=(%sj!sF!uxl!~7Er3=2*$Fx)%A!0_k<1H-2i z3=H2+FfcHmWMD8k$-oeKl7S)dBm+b0Nd|_QCm9$PoMd2Ff0BV=&q)S`eJ2?h_Mc>6 z_;Qkg;m1h^2F+6p4B@937&=ZdF!Y>aV3>1?fnmWZ28Ok#7#MDzVqjo8&A`BQnt_4u zGy_AxX$FRp(+mt{rx_T^Pctx_Jk7vx{WJr^r_&4!e@`VZ#{) zh8<@Z81|iEU^sM!f#Jv*28N?&7#OaeVPJT3hJoS#83qQ0vkVN5XBim$&oVHyo@HP- ze3pUX%UK2n!*dJ_h36O;%Fi({RG(vDs6EHPPF2A7Kr3>g<07#c4!Fr2^0 z!0`1V1H-S23=DrRGBEtT$iU!piGd;D5(7iXB?gA%OAHL@mlzmIE-^67yTrh-{t^Sj z_Dc*5yDu>?oVdim@Z%B#1J`8+2BFIg404wl7)&lRFoa)bV9349z)*defnmaB28L6Y z85qu8W?;B*nStTrWd?@Nml+rsuP`w1UtwTSyTZWWeuaUd>IwtHwkr$_`>rrB9J<25 zaO4UD!_g}Y41!k~7%HwZFdV$fz;N^`1H;Fw3=ChdGBB`SV_@LC#=szUje)`b8UsVo zH3o*NYYYr^*BBUfUt?hSbB%%F?==Pn`Rfb}S=SjDwq0jnIDDOf;nH;mhRfF(7?f@> zFnHf!V2HZGz%cs;1H;7|3=G$AFfiP{!N73$1_Q&r8w?EZZZI(XzrnyDb(4WX!8aKg+HNv1?6}Fmu;(TN!`_<=4A*ZmFucCWz`$~gfx-P214HjE28M07 z7#Q~4VqiFMi-FE(61cy9^8$?lLg2-eX`;xyQhudXIr2=N3ZpReB$iu)OsGB92DQ=^&2=cFd)>gN|Q@Rwxf=Rvsf#U%{p>US%RwWAX0)c(1wu_Y6O~62p8@d28KWuCN@SU1`a6AW?aJD#{6F)0pftP z1cjp1f}+&o)Vz{ng+zt2#GKMpg_4ZK5{2BvN`<6U1*oJ#evv|6eugmYJ81>YU{K(wr2KgZxT!a�DLHfBA3i6AKGm~;s6*BV_it}?*74l0m zQi~LF67$kaL9wF8#igLEtN`_Gfc2r?Js(WF#`^vtr*^r&f@!71HvH6iO1a!4Uz@W{EkFR9dW% zSj?pWi&%xkqV&>SP{P*%rEZX};*$I#khYSH)Lc-K0{crLC9xz?p}3@|G`XbIz9_X= zkBiIEhe>d%mg5u_;ZQ~^=MY8~VMFF5XA@=?VOG{8XAV{t;h$_y&Og{#glBRlInUu_ z5uV7s$aw}gi||n%Dd!_REW%Q3jKW%MjKYgJ8HG1-G72;CGYU)bGYTsSGYT6CGYVHp zF$#A{F$yn|Wfa~d%P8EWz$iRPfl+vs5~J`gB}U;*>Wspt)ER}nbQp!BbQp!33>bwc z88GsLvRnrHmDMTCUC>Y0rbh0OKaWOJ7uo|C{{wK{S{XeZJKR3Q8 zwU~juG^Yd{KN8sDX99BkSb^iG9U4C&jEwe>+zXE(4p242$i%?P$i&FZz)5L^#mK|} ziGifR8K8*B5}d(ksMy3RIE68=gb|!V1Tz>>y@C-D&XDp)6jZapib+VZQIfBah$xT1 zV)()w7GkA2B^v(O8itziPzDFPp(ZGpK@|!jnvk`)f{I@aB~S$w4=#$85C(u^52~91 z97)1F9z2Ya|8o+nD&tG@${E;;5(_|)6oeT^?SXs1am2yI#=t0OY9(lzCFtr7A}tsL zV;C91IW3YsFao5Cr1kY?>Sm1Ca0S&j$tBXr%?3~?A;*M{LMkXfB$hx+VTF{`w8YY!5=5a5sl*f@ z%_>ku2Wf2Ji#=FS79|#FK;uhCAuUfsQ=wYHK%o{Mmxj=IK+0^$T0t>|Ri6PwA0rnd zGXpatV*n!~Ba3L|6S4Ms-66O6FH!bnO%;MQ2N zF1SsSmRVF>0x1?jO`OaUaH;@Bbb=0~c?WKpKoeFfB>90FJ9&sy1*-fL!Huk>#N=#< zVFZ&@ei24;0@_^cL;viWL z;(`)4xCUWnU^Si~bwi3#@;@k#rWVKNFtF$6gA1qN2}ns9RE~kMplExr07`n+5;Qdu zv~?FW zY|s>&6zBpe0$msd6}bc*7y}g;83R=q83jccQ3D1o*)o8_gw12 zN-S_-0cFa-I~X09_P`l<6B#2DBPRnZX{|0!26l)xa6YmMoCAsD9gK#ur&tBIfSbL6 zHH?gcIgC)}VZ<}El_dtL=D-ED0%j`<60@L27H9+j(!_$Og|x3=g9LftZa1#32qc6M zHJAZBSg|yvpo&2OuA_iZ32i)aflEh3o=0yzV&{98i~y0aQ%l z&-suRv3p)nZW41duv6U0;fB^JN`W81F)b{^$Y?0*#4Rkr$QXEr zkrAB94={rKj1keH1u2Oo;Bga3rc}sC%}G%x&n(Hv%mZg~=ty0D8gc_$A*oU!88pNN z9$!Ok1#v0l=P6{CfZ9Et;GrUT#}(9~N`wvT6(xekv_OM|dC=~v4%j~UxJo8yc&P+z zO==2wsHY?&6=|HTxFoR>l01tObMYpy+{6Nn{Ib*{D@gYi)Y}Dl&6Sfc$~Q(VG<+(;x`7Q23#s|aWOD5AvNkDRS~EXf>cHPAafWQ`9Y3kU@+zu3=jmR z(xCq(`Jf(lX$}Kp38VxGw!oHJ2^Jx|q!uAO(0ay5P?lhCod=wdd7+6gC~yg+VY7tM zP|=A^Z~?el3~XU!6f9vx_7Fx=g!Z!~icwM;Xjl(43YZ8VaV6N#hJ{)QqD(>QHN(a> zzYFP6?Y)Fe<-i`Do5*EcRu9BT2GWTfUnBX0$k$O~3LY1(46 zGAJl(_~2y?9~DX&erS@QVE%xSADT$Cf_E@NimEH1qH2=3@C8Q3;35h6IKrz0P%z+X7l1MjA_u4CX@JXW zaM=tlurd0_*xGA&3{lolN>tKOsL;eP2r2^}pi4+V9g)K@FdkHcfJzFa3IxQ376-@$ zOGauQwDpMJHwf)mM}v$6Ss3LL3kq^785xaJ#MX&{3L2;S%;I>^*dU~=3A};P)P%NI zK+O*-4BrTViUUZOmmNBuE5gXg@06dP0~^f^01YUD2f&>Id%)RUu!qr5_7aO=2YB#1 zFo%&*FoqH4aE#3EoRONGU92F4GzkVzIMB8PC^aNNS}ypK5;SOFsR`b|3@IuFH7U@d z7^WH&4REDyi8;kkwTzH)@E(q>9FUNQ_QIhd{{a#5pt=BzK{FKvpw=QR^dTV!o2kGt zSRjZ9V}dFn!Hp>3ujQ1nUT?fNs!e@kkv^Z z)FCd0DP>?b-pzKOjZs>VQHviG)=)3*L3k0`WOffUfq4%!PfTu`7BN#K$^;rI#+WGr zIRf1SOvc^Jlb8h=mDm$=av&}YuE4Bn2V$;B2sBrO+F!3AX^;SvQ;A9|LJXYXtfmxr z2a*ErFdE9f;uO5W7`TIxF>ntfqu?4wPynHY-hTz>{M@9>yi_&N{01oYQRbQyz%^H* zLUCqZdJb~+rQn$co>ETDNd?b-f+jjaJ$$eUiAAtEC{V4H2QBqM7C^fb8TmQjDQ>uq z{4_`|F4p4$o19mv05utu(;=htC}u%~@MZeMoE)V2El|SIFhZKy0tq=X1A3cwAz zK{TW(jD1K`7*zJqCFKf(QZAFAq8Gj)QKW!HOTeJ=i-FZRf^{}H4xH+tjTLD4yC8)> zysUyuc(H)$C-jnvv@twT_CyUNQrFZlieMHIWS9Xf{#QUU{|ZJ!*-31IOBg{NFYxSh z1tTnA(8BM(f-k7CTC5gUYj_`LkQR7i@TdCN!?lye|u zHWgY%Acteb7!fTaP;d~}G6LBEi86457gEL{HFv;)3SBk;4M}h-79R45RunYk`#E=T zfW}P?s<;2y&VWGIBF8IC3z9mUBqTgIckk zT7tgpg05WP#ts96BMUQV4iz;2pvnbm*ML{5FfceUgB!XG41%oYf{e^CBf!xuY9uHL zV(^241*B9S9F`y!yI*DwNRK>74-2y(r;;EqyC5qUMBITHx*7uNHepsVaj?l!f>6g9 z3Hr(lx^gl5XJa^xQH+6I9y}<4DJ2dX@MmKFE@Q}Q!!3M~iCrK}u&GV((htKwl8nZ( zjH!&q+ZoxJjKi3gG8x}vdc|*b6av7TXE-b8y9dla2vO9PvkaU!M%&y_z?Fo zZsW_`Ke&zAdH8vZg?YSrjH7u9c#KnNDeqQZ<72$Hc#ZGzKHxQe$m`5!?9La?XB^F!!DrmdHUFy<4t^5_>3R$z2Gx`!)L&6?7$z$Z=BAb&2OB`pT}=}g#SFh@nwE30b_RoZvo?I zfp`Jq9D!K^#%lz&2pI1WI4WTLLEyiDF}onQpmDHZvY>Ij-~>VADT31kji(Eq6f{07 zcvsN)q2N0~V@@GyA!B7Bbs=LDAs->*XrW{w<8+}CA>%fosY1qcg%%1K-x7KyWc*u* zN7z_ESV-7dSU6MIc(?F#VdK}rjv~e}A_*eO8$?8nSwuxdjU_}?MUDML6GV+OL~}%q zD@B)y8XpinA!>X^^roosZ&5BWV<9myF=J&hPch>Nu>>*W6tRtB#s|f&iy6NVdn0E2 zPVBvyv4gmaxN(Shgt&34c%8WM1o0W-#&g71iW~12zbI~eQ~a*D@+)x(V<8DC31c}4 zZ3$yL31120P>Dzh@r7*GhLu8?To>AZ>g?`i!(Oql}Da}pzbK6@fOYm`1(g`oP7=XspL$!ehwzpV2XdS@36;;9o1jzv9BYj9J2hj9$W` zjON0wj9J2-j9S8;jN-zPj9J35j9S96jL(H_nX-f(nWTgrnZ$(ynX-gKnUsV>nZ$)t znT()VUAUFWNVt_rO1PCtU3e)|mheg@CE=A!;=((bvV`|CX&LWjQfCyt%bX?rkU2>B zDYLlnSLQ6?pUhUmKbggaIa#uVd0CQ#1zE&}6#~>&$FgJzC$cyRC$fkO z7qVmtm$Ensm$HZpx3Xjjcd{f2_p-PPpJmAszR2Pve33<5_#sP{@KY8i;ioL-!lJBM z!jh~`!ji1w!kVmE!n&+U!iKEk=m7|x-zo;J%Hd!XU}R)AzASxH8ch*$H^7mHNl*eD zOGqRmqw!x>RyIav<7n1oRz^l+Csr?3G(*4xph)&)i{*=Zz!7>o_PUw>mBct(Av6EtqjK-@Z)=4lj8o!hJB*n;Ryj^Cu44P+{^NWytn;?-S zfu;a7e}$w%NKjM|l8zzu9%eGu;?@T_wS=dV2cnSKKO0k(1h*7ORXk5354wekIXOrc z3a|=e#1W{K<>gRF-x;cGcp=?$@HN|8uEk=QcxY|y1<1Nn9!MKBt@mX)x2nmP}lw;sr}6N zj}KifbT|uj?F+8#*3CYTosrp?gI$`Pk&bgdu)}XGKMN%upqsk-5 z_}$TlNw5N3AklziWHgpyl>@ool+_YMWwGXgs0P*+5H*=~I*2;Mb^+wm8*Fz#lqS0_ zDBf+^9YK^QyDx}(Ed3mmtiMbD22mz57NDT_lJNskVKPxNhK$=89dnojZ!ij8U>wXO zBeQWWb2F%{mS?!0kMp84XA*)$#ox8$J_I`f(nQh z-Y!r9!OqVMDj<{vH9;kVyPz+K+AnkzRF3Zu+6SV}30(nE6=HRu0%E$@ToAQXY&D3= sk|+Qb7qt@2AgWtpB8XZhwFXq;ACx){qRvZQ1yL`g-+{v1QpR2e0Dc^tCjbBd literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@option.cache_inline b/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@option.cache_inline new file mode 100644 index 0000000000000000000000000000000000000000..1b1cb4d44c57c2d7a5122870fa6ac3e62ff7e94e GIT binary patch literal 8 JcmZR80ssIA00961 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@option.cache_meta b/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@option.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..8656c26a6732c744d9a78b34c76394e55653d6eb GIT binary patch literal 1485 zcmX?Lp`F111&mOd;q%=y(iLw$97u3wem>C!fm>C#Km>C#4nHd-+ zGcz#EVrF2N!_2@imzja#8Z!ffF$)7j3JU{61`7j21q%a14GRN9Ckq3^QWge=JuD0i zM_3pbPOvaATxDTkc*nxP@Rx;wft8hkfs>Vi!GV>5A(WMYA)J+gA%c~GVIC_3!x~lw zhLfxe3=dct7-ZNO7!=qT7?ju;7?jx<7z)`K7^bo@FzjYyV0gsFz`(@Lz`(-Jz`)AR zz!1#Nz>v(&z)-@@z;KA2f#DcC1H%b+28NUD3=DtR85kHi7#IXN7#Ku27#I{e7#Per z7#LhR7#Msx7#IRM7#QL>7#IpT7#JEj7#KP@7#Mmu7#MbNFfg3tU|=}S!N72agMr~c z2Ll5$Cj)~pCj)~xCj)~DCj)~eCj)~gCj&zuCj&z$Cj&zgCj&zXCj&z>Cj&z_Cj&!2 zCj-N7P6mcEoD2--I2jnub22b6axpNlaxpN7axpMSaxpNdaWOD>b1^VPb1^U^b1^Wa zb1^WKa4|4U<6>ag!o|R_hl_#X02c$pPc8-qPHqMUIc^39V{Qfpb8ZF(OKt`RD{cmc z9Bu}N0&WI|25tt17H$THiQEhfE4UdLc5yQ>9O7nRIL6JuaG9Hd;RiPZ13M1`13wP~ zgD?*RgEtQYLo^QqLjey1Ln{vhLnjXdLpKitLk|xF!$TehhNnCX3}1N|7=H3FFtG75 zFv#*UFlh2JFc|VOFqrZ(FgWotFof|kFeLFZFl6yEFy!$vFf8R|VA#sbz;KM0f#DV} z1H(OD28IW`3=9u>85o@T7#Q677#PC&7#O1Y7#K467#Mo_7#L>qF)%FTV_;ay$H1_O zkAdL|9|OY!J_d#td<+b4_!t-r_!$@+_!$@i`573}`573p`572;`574U_!$_E@G~%+ z=VxHJ%+J7}CBVSoF2KOxEx^DKEx^DKFTlW%Bf!8gOMro4jQ|6~76Ar^9RdstM+F!d zJ_s-{{1;$gU>9Uy;1*|f&Fh!7oVVWQV!*oFghLeH}3}*!y z814!(Fgz4wV0b6Uz`!ZQz#uKez@RL|z@RR~z+fW8z~Cdqzz{9Oz>qA&z>qG)z)&K@ zz|bbdz%W&afnlx?1H(cg28LTg3=FS?7#MyFF);85GcX7UGcX7VGcX7XGcaTdGcfEH zW?*U;)h%zwT6lGxeEy}>aCC0!YB*wrXCdR;^ zEXKg#DaODMA;!RvAjZIuBF4b5QH+7%pcn(gbuk8p7h((yZ^RfF-ia|Vycc6&a1du; za1m!<2oYysh!AIBNEK&bs1s*km>|x;FhiVyVU9Qh!%A@mhW+9U3>U>27;cI)Fx(Yq zV0b0Yz`!8Ez#t^Sz#t{Tz#u2Vz@RO`z+flAz~C#vzz{0Izz`|Hz%X5cfnk{h1H*0! z28MGI3=Eef7#OZdFfd$|U|_J9WMFWXWMBxEWMBxFWMD{Qj^33%B literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@order.cache b/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@order.cache new file mode 100644 index 0000000000000000000000000000000000000000..ad64dbc455d155e0b9e23d18e9cd296f2200f899 GIT binary patch literal 5915 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 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@order.cache_inline b/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@order.cache_inline new file mode 100644 index 0000000000000000000000000000000000000000..1b1cb4d44c57c2d7a5122870fa6ac3e62ff7e94e GIT binary patch literal 8 JcmZR80ssIA00961 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@order.cache_meta b/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@order.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..4404517ca4be50751de9a68d9658ab34e27040a1 GIT binary patch literal 677 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 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@result.cache_inline b/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@result.cache_inline new file mode 100644 index 0000000000000000000000000000000000000000..3d06590585e32906712f8c5273a50463af96e4b8 GIT binary patch literal 3168 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 X9H7MoXuA}8W@ZwU0C~@Z$$|+0|LW{@ literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@set.cache_inline b/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@set.cache_inline new file mode 100644 index 0000000000000000000000000000000000000000..1b1cb4d44c57c2d7a5122870fa6ac3e62ff7e94e GIT binary patch literal 8 JcmZR80ssIA00961 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@set.cache_meta b/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@set.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..e5bd75e58b0eb4da8415e982f4799788bdd6d70d GIT binary patch literal 1761 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 VqHWXM}5vD*(91sxtrp literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@string.cache_inline b/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@string.cache_inline new file mode 100644 index 0000000000000000000000000000000000000000..1b1cb4d44c57c2d7a5122870fa6ac3e62ff7e94e GIT binary patch literal 8 JcmZR80ssIA00961 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@string.cache_meta b/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@string.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..09ee85c7a6bdebe1a73eed8f2ae2ab57dd95a16b GIT binary patch literal 4057 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 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@string_tree.cache b/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@string_tree.cache new file mode 100644 index 0000000000000000000000000000000000000000..93d3cc108ad346f287a237b00ebf3854763118c2 GIT binary patch literal 14554 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 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@string_tree.cache_inline b/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@string_tree.cache_inline new file mode 100644 index 0000000000000000000000000000000000000000..1b1cb4d44c57c2d7a5122870fa6ac3e62ff7e94e GIT binary patch literal 8 JcmZR80ssIA00961 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@string_tree.cache_meta b/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@string_tree.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..abfcf6a5e95e84863043c38a0ea00a48f1cb0d89 GIT binary patch literal 911 zcmZ>AiAZOF0!BtCg9}Qh=cFd)>gQw@mw*HW85kJ;FfDzl{qG&~MUVg#3o$S-2s1D+ z#4|83tYu(e_{PA%;KInjFo}_Y;W{G&!)-Vw?;NQk)D7rko56mYfU>>6{D<^Eeq8E^{(4T;XJ3_|M6} zz|6(KAjiePpv1+%5WvO2(863NSE;2rw{c3NSEO z3otPF3NSGE3otMQ2rw{o2{1716<}c4C&0jPUVwq&ssIDSbpZy38v+ar(t->O@`4Nu zp@Iwy^8^_f_6agD91vt+I4H=#@Jx__L0^c0!9a+Cp+bm(p+<;-VS^9@!+RkHhW|ng z3}V6z40^&044%Ra43WYN3{k=i42y*s7&ZtqFl-TKU|<$uUVhjvB#TXd&iZL*p5@TR^Eylp`ON@bmNt}U!O`L&2Pn>}vN}PeAM4W-4 J42l_`004g+d!_&Y literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@uri.cache b/build/dev/javascript/gleam_stdlib/_gleam_artefacts/gleam@uri.cache new file mode 100644 index 0000000000000000000000000000000000000000..f51ee88b699a73add287667f1ba0dd898f806f30 GIT binary patch literal 32735 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 2))) | 0; +} + +/** + * standard string hash popularised by java + * @param {string} s + * @returns {number} + */ +function hashString(s) { + let hash = 0; + const len = s.length; + for (let i = 0; i < len; i++) { + hash = (Math.imul(31, hash) + s.charCodeAt(i)) | 0; + } + return hash; +} + +/** + * hash a number by converting to two integers and do some jumbling + * @param {number} n + * @returns {number} + */ +function hashNumber(n) { + tempDataView.setFloat64(0, n); + const i = tempDataView.getInt32(0); + const j = tempDataView.getInt32(4); + return Math.imul(0x45d9f3b, (i >> 16) ^ i) ^ j; +} + +/** + * hash a BigInt by converting it to a string and hashing that + * @param {BigInt} n + * @returns {number} + */ +function hashBigInt(n) { + return hashString(n.toString()); +} + +/** + * hash any js object + * @param {any} o + * @returns {number} + */ +function hashObject(o) { + const proto = Object.getPrototypeOf(o); + if (proto !== null && typeof proto.hashCode === "function") { + try { + const code = o.hashCode(o); + if (typeof code === "number") { + return code; + } + } catch {} + } + if (o instanceof Promise || o instanceof WeakSet || o instanceof WeakMap) { + return hashByReference(o); + } + if (o instanceof Date) { + return hashNumber(o.getTime()); + } + let h = 0; + if (o instanceof ArrayBuffer) { + o = new Uint8Array(o); + } + if (Array.isArray(o) || o instanceof Uint8Array) { + for (let i = 0; i < o.length; i++) { + h = (Math.imul(31, h) + getHash(o[i])) | 0; + } + } else if (o instanceof Set) { + o.forEach((v) => { + h = (h + getHash(v)) | 0; + }); + } else if (o instanceof Map) { + o.forEach((v, k) => { + h = (h + hashMerge(getHash(v), getHash(k))) | 0; + }); + } else { + const keys = Object.keys(o); + for (let i = 0; i < keys.length; i++) { + const k = keys[i]; + const v = o[k]; + h = (h + hashMerge(getHash(v), hashString(k))) | 0; + } + } + return h; +} + +/** + * hash any js value + * @param {any} u + * @returns {number} + */ +export function getHash(u) { + if (u === null) return 0x42108422; + if (u === undefined) return 0x42108423; + if (u === true) return 0x42108421; + if (u === false) return 0x42108420; + switch (typeof u) { + case "number": + return hashNumber(u); + case "string": + return hashString(u); + case "bigint": + return hashBigInt(u); + case "object": + return hashObject(u); + case "symbol": + return hashByReference(u); + case "function": + return hashByReference(u); + default: + return 0; // should be unreachable + } +} + +/** + * @template K,V + * @typedef {ArrayNode | IndexNode | CollisionNode} Node + */ +/** + * @template K,V + * @typedef {{ type: typeof ENTRY, k: K, v: V }} Entry + */ +/** + * @template K,V + * @typedef {{ type: typeof ARRAY_NODE, size: number, array: (undefined | Entry | Node)[] }} ArrayNode + */ +/** + * @template K,V + * @typedef {{ type: typeof INDEX_NODE, bitmap: number, array: (Entry | Node)[] }} IndexNode + */ +/** + * @template K,V + * @typedef {{ type: typeof COLLISION_NODE, hash: number, array: Entry[] }} CollisionNode + */ +/** + * @typedef {{ val: boolean }} Flag + */ +const SHIFT = 5; // number of bits you need to shift by to get the next bucket +const BUCKET_SIZE = Math.pow(2, SHIFT); +const MASK = BUCKET_SIZE - 1; // used to zero out all bits not in the bucket +const MAX_INDEX_NODE = BUCKET_SIZE / 2; // when does index node grow into array node +const MIN_ARRAY_NODE = BUCKET_SIZE / 4; // when does array node shrink to index node +const ENTRY = 0; +const ARRAY_NODE = 1; +const INDEX_NODE = 2; +const COLLISION_NODE = 3; + +/** @type {IndexNode} */ +const EMPTY = { + type: INDEX_NODE, + bitmap: 0, + array: [], +}; +/** + * Mask the hash to get only the bucket corresponding to shift + * @param {number} hash + * @param {number} shift + * @returns {number} + */ +function mask(hash, shift) { + return (hash >>> shift) & MASK; +} + +/** + * Set only the Nth bit where N is the masked hash + * @param {number} hash + * @param {number} shift + * @returns {number} + */ +function bitpos(hash, shift) { + return 1 << mask(hash, shift); +} + +/** + * Count the number of 1 bits in a number + * @param {number} x + * @returns {number} + */ +function bitcount(x) { + x -= (x >> 1) & 0x55555555; + x = (x & 0x33333333) + ((x >> 2) & 0x33333333); + x = (x + (x >> 4)) & 0x0f0f0f0f; + x += x >> 8; + x += x >> 16; + return x & 0x7f; +} + +/** + * Calculate the array index of an item in a bitmap index node + * @param {number} bitmap + * @param {number} bit + * @returns {number} + */ +function index(bitmap, bit) { + return bitcount(bitmap & (bit - 1)); +} + +/** + * Efficiently copy an array and set one value at an index + * @template T + * @param {T[]} arr + * @param {number} at + * @param {T} val + * @returns {T[]} + */ +function cloneAndSet(arr, at, val) { + const len = arr.length; + const out = new Array(len); + for (let i = 0; i < len; ++i) { + out[i] = arr[i]; + } + out[at] = val; + return out; +} + +/** + * Efficiently copy an array and insert one value at an index + * @template T + * @param {T[]} arr + * @param {number} at + * @param {T} val + * @returns {T[]} + */ +function spliceIn(arr, at, val) { + const len = arr.length; + const out = new Array(len + 1); + let i = 0; + let g = 0; + while (i < at) { + out[g++] = arr[i++]; + } + out[g++] = val; + while (i < len) { + out[g++] = arr[i++]; + } + return out; +} + +/** + * Efficiently copy an array and remove one value at an index + * @template T + * @param {T[]} arr + * @param {number} at + * @returns {T[]} + */ +function spliceOut(arr, at) { + const len = arr.length; + const out = new Array(len - 1); + let i = 0; + let g = 0; + while (i < at) { + out[g++] = arr[i++]; + } + ++i; + while (i < len) { + out[g++] = arr[i++]; + } + return out; +} + +/** + * Create a new node containing two entries + * @template K,V + * @param {number} shift + * @param {K} key1 + * @param {V} val1 + * @param {number} key2hash + * @param {K} key2 + * @param {V} val2 + * @returns {Node} + */ +function createNode(shift, key1, val1, key2hash, key2, val2) { + const key1hash = getHash(key1); + if (key1hash === key2hash) { + return { + type: COLLISION_NODE, + hash: key1hash, + array: [ + { type: ENTRY, k: key1, v: val1 }, + { type: ENTRY, k: key2, v: val2 }, + ], + }; + } + const addedLeaf = { val: false }; + return assoc( + assocIndex(EMPTY, shift, key1hash, key1, val1, addedLeaf), + shift, + key2hash, + key2, + val2, + addedLeaf, + ); +} + +/** + * @template T,K,V + * @callback AssocFunction + * @param {T} root + * @param {number} shift + * @param {number} hash + * @param {K} key + * @param {V} val + * @param {Flag} addedLeaf + * @returns {Node} + */ +/** + * Associate a node with a new entry, creating a new node + * @template T,K,V + * @type {AssocFunction,K,V>} + */ +function assoc(root, shift, hash, key, val, addedLeaf) { + switch (root.type) { + case ARRAY_NODE: + return assocArray(root, shift, hash, key, val, addedLeaf); + case INDEX_NODE: + return assocIndex(root, shift, hash, key, val, addedLeaf); + case COLLISION_NODE: + return assocCollision(root, shift, hash, key, val, addedLeaf); + } +} +/** + * @template T,K,V + * @type {AssocFunction,K,V>} + */ +function assocArray(root, shift, hash, key, val, addedLeaf) { + const idx = mask(hash, shift); + const node = root.array[idx]; + // if the corresponding index is empty set the index to a newly created node + if (node === undefined) { + addedLeaf.val = true; + return { + type: ARRAY_NODE, + size: root.size + 1, + array: cloneAndSet(root.array, idx, { type: ENTRY, k: key, v: val }), + }; + } + if (node.type === ENTRY) { + // if keys are equal replace the entry + if (isEqual(key, node.k)) { + if (val === node.v) { + return root; + } + return { + type: ARRAY_NODE, + size: root.size, + array: cloneAndSet(root.array, idx, { + type: ENTRY, + k: key, + v: val, + }), + }; + } + // otherwise upgrade the entry to a node and insert + addedLeaf.val = true; + return { + type: ARRAY_NODE, + size: root.size, + array: cloneAndSet( + root.array, + idx, + createNode(shift + SHIFT, node.k, node.v, hash, key, val), + ), + }; + } + // otherwise call assoc on the child node + const n = assoc(node, shift + SHIFT, hash, key, val, addedLeaf); + // if the child node hasn't changed just return the old root + if (n === node) { + return root; + } + // otherwise set the index to the new node + return { + type: ARRAY_NODE, + size: root.size, + array: cloneAndSet(root.array, idx, n), + }; +} +/** + * @template T,K,V + * @type {AssocFunction,K,V>} + */ +function assocIndex(root, shift, hash, key, val, addedLeaf) { + const bit = bitpos(hash, shift); + const idx = index(root.bitmap, bit); + // if there is already a item at this hash index.. + if ((root.bitmap & bit) !== 0) { + // if there is a node at the index (not an entry), call assoc on the child node + const node = root.array[idx]; + if (node.type !== ENTRY) { + const n = assoc(node, shift + SHIFT, hash, key, val, addedLeaf); + if (n === node) { + return root; + } + return { + type: INDEX_NODE, + bitmap: root.bitmap, + array: cloneAndSet(root.array, idx, n), + }; + } + // otherwise there is an entry at the index + // if the keys are equal replace the entry with the updated value + const nodeKey = node.k; + if (isEqual(key, nodeKey)) { + if (val === node.v) { + return root; + } + return { + type: INDEX_NODE, + bitmap: root.bitmap, + array: cloneAndSet(root.array, idx, { + type: ENTRY, + k: key, + v: val, + }), + }; + } + // if the keys are not equal, replace the entry with a new child node + addedLeaf.val = true; + return { + type: INDEX_NODE, + bitmap: root.bitmap, + array: cloneAndSet( + root.array, + idx, + createNode(shift + SHIFT, nodeKey, node.v, hash, key, val), + ), + }; + } else { + // else there is currently no item at the hash index + const n = root.array.length; + // if the number of nodes is at the maximum, expand this node into an array node + if (n >= MAX_INDEX_NODE) { + // create a 32 length array for the new array node (one for each bit in the hash) + const nodes = new Array(32); + // create and insert a node for the new entry + const jdx = mask(hash, shift); + nodes[jdx] = assocIndex(EMPTY, shift + SHIFT, hash, key, val, addedLeaf); + let j = 0; + let bitmap = root.bitmap; + // place each item in the index node into the correct spot in the array node + // loop through all 32 bits / array positions + for (let i = 0; i < 32; i++) { + if ((bitmap & 1) !== 0) { + const node = root.array[j++]; + nodes[i] = node; + } + // shift the bitmap to process the next bit + bitmap = bitmap >>> 1; + } + return { + type: ARRAY_NODE, + size: n + 1, + array: nodes, + }; + } else { + // else there is still space in this index node + // simply insert a new entry at the hash index + const newArray = spliceIn(root.array, idx, { + type: ENTRY, + k: key, + v: val, + }); + addedLeaf.val = true; + return { + type: INDEX_NODE, + bitmap: root.bitmap | bit, + array: newArray, + }; + } + } +} +/** + * @template T,K,V + * @type {AssocFunction,K,V>} + */ +function assocCollision(root, shift, hash, key, val, addedLeaf) { + // if there is a hash collision + if (hash === root.hash) { + const idx = collisionIndexOf(root, key); + // if this key already exists replace the entry with the new value + if (idx !== -1) { + const entry = root.array[idx]; + if (entry.v === val) { + return root; + } + return { + type: COLLISION_NODE, + hash: hash, + array: cloneAndSet(root.array, idx, { type: ENTRY, k: key, v: val }), + }; + } + // otherwise insert the entry at the end of the array + const size = root.array.length; + addedLeaf.val = true; + return { + type: COLLISION_NODE, + hash: hash, + array: cloneAndSet(root.array, size, { type: ENTRY, k: key, v: val }), + }; + } + // if there is no hash collision, upgrade to an index node + return assoc( + { + type: INDEX_NODE, + bitmap: bitpos(root.hash, shift), + array: [root], + }, + shift, + hash, + key, + val, + addedLeaf, + ); +} +/** + * Find the index of a key in the collision node's array + * @template K,V + * @param {CollisionNode} root + * @param {K} key + * @returns {number} + */ +function collisionIndexOf(root, key) { + const size = root.array.length; + for (let i = 0; i < size; i++) { + if (isEqual(key, root.array[i].k)) { + return i; + } + } + return -1; +} +/** + * @template T,K,V + * @callback FindFunction + * @param {T} root + * @param {number} shift + * @param {number} hash + * @param {K} key + * @returns {undefined | Entry} + */ +/** + * Return the found entry or undefined if not present in the root + * @template K,V + * @type {FindFunction,K,V>} + */ +function find(root, shift, hash, key) { + switch (root.type) { + case ARRAY_NODE: + return findArray(root, shift, hash, key); + case INDEX_NODE: + return findIndex(root, shift, hash, key); + case COLLISION_NODE: + return findCollision(root, key); + } +} +/** + * @template K,V + * @type {FindFunction,K,V>} + */ +function findArray(root, shift, hash, key) { + const idx = mask(hash, shift); + const node = root.array[idx]; + if (node === undefined) { + return undefined; + } + if (node.type !== ENTRY) { + return find(node, shift + SHIFT, hash, key); + } + if (isEqual(key, node.k)) { + return node; + } + return undefined; +} +/** + * @template K,V + * @type {FindFunction,K,V>} + */ +function findIndex(root, shift, hash, key) { + const bit = bitpos(hash, shift); + if ((root.bitmap & bit) === 0) { + return undefined; + } + const idx = index(root.bitmap, bit); + const node = root.array[idx]; + if (node.type !== ENTRY) { + return find(node, shift + SHIFT, hash, key); + } + if (isEqual(key, node.k)) { + return node; + } + return undefined; +} +/** + * @template K,V + * @param {CollisionNode} root + * @param {K} key + * @returns {undefined | Entry} + */ +function findCollision(root, key) { + const idx = collisionIndexOf(root, key); + if (idx < 0) { + return undefined; + } + return root.array[idx]; +} +/** + * @template T,K,V + * @callback WithoutFunction + * @param {T} root + * @param {number} shift + * @param {number} hash + * @param {K} key + * @returns {undefined | Node} + */ +/** + * Remove an entry from the root, returning the updated root. + * Returns undefined if the node should be removed from the parent. + * @template K,V + * @type {WithoutFunction,K,V>} + * */ +function without(root, shift, hash, key) { + switch (root.type) { + case ARRAY_NODE: + return withoutArray(root, shift, hash, key); + case INDEX_NODE: + return withoutIndex(root, shift, hash, key); + case COLLISION_NODE: + return withoutCollision(root, key); + } +} +/** + * @template K,V + * @type {WithoutFunction,K,V>} + */ +function withoutArray(root, shift, hash, key) { + const idx = mask(hash, shift); + const node = root.array[idx]; + if (node === undefined) { + return root; // already empty + } + let n = undefined; + // if node is an entry and the keys are not equal there is nothing to remove + // if node is not an entry do a recursive call + if (node.type === ENTRY) { + if (!isEqual(node.k, key)) { + return root; // no changes + } + } else { + n = without(node, shift + SHIFT, hash, key); + if (n === node) { + return root; // no changes + } + } + // if the recursive call returned undefined the node should be removed + if (n === undefined) { + // if the number of child nodes is at the minimum, pack into an index node + if (root.size <= MIN_ARRAY_NODE) { + const arr = root.array; + const out = new Array(root.size - 1); + let i = 0; + let j = 0; + let bitmap = 0; + while (i < idx) { + const nv = arr[i]; + if (nv !== undefined) { + out[j] = nv; + bitmap |= 1 << i; + ++j; + } + ++i; + } + ++i; // skip copying the removed node + while (i < arr.length) { + const nv = arr[i]; + if (nv !== undefined) { + out[j] = nv; + bitmap |= 1 << i; + ++j; + } + ++i; + } + return { + type: INDEX_NODE, + bitmap: bitmap, + array: out, + }; + } + return { + type: ARRAY_NODE, + size: root.size - 1, + array: cloneAndSet(root.array, idx, n), + }; + } + return { + type: ARRAY_NODE, + size: root.size, + array: cloneAndSet(root.array, idx, n), + }; +} +/** + * @template K,V + * @type {WithoutFunction,K,V>} + */ +function withoutIndex(root, shift, hash, key) { + const bit = bitpos(hash, shift); + if ((root.bitmap & bit) === 0) { + return root; // already empty + } + const idx = index(root.bitmap, bit); + const node = root.array[idx]; + // if the item is not an entry + if (node.type !== ENTRY) { + const n = without(node, shift + SHIFT, hash, key); + if (n === node) { + return root; // no changes + } + // if not undefined, the child node still has items, so update it + if (n !== undefined) { + return { + type: INDEX_NODE, + bitmap: root.bitmap, + array: cloneAndSet(root.array, idx, n), + }; + } + // otherwise the child node should be removed + // if it was the only child node, remove this node from the parent + if (root.bitmap === bit) { + return undefined; + } + // otherwise just remove the child node + return { + type: INDEX_NODE, + bitmap: root.bitmap ^ bit, + array: spliceOut(root.array, idx), + }; + } + // otherwise the item is an entry, remove it if the key matches + if (isEqual(key, node.k)) { + if (root.bitmap === bit) { + return undefined; + } + return { + type: INDEX_NODE, + bitmap: root.bitmap ^ bit, + array: spliceOut(root.array, idx), + }; + } + return root; +} +/** + * @template K,V + * @param {CollisionNode} root + * @param {K} key + * @returns {undefined | Node} + */ +function withoutCollision(root, key) { + const idx = collisionIndexOf(root, key); + // if the key not found, no changes + if (idx < 0) { + return root; + } + // otherwise the entry was found, remove it + // if it was the only entry in this node, remove the whole node + if (root.array.length === 1) { + return undefined; + } + // otherwise just remove the entry + return { + type: COLLISION_NODE, + hash: root.hash, + array: spliceOut(root.array, idx), + }; +} +/** + * @template K,V + * @param {undefined | Node} root + * @param {(value:V,key:K)=>void} fn + * @returns {void} + */ +function forEach(root, fn) { + if (root === undefined) { + return; + } + const items = root.array; + const size = items.length; + for (let i = 0; i < size; i++) { + const item = items[i]; + if (item === undefined) { + continue; + } + if (item.type === ENTRY) { + fn(item.v, item.k); + continue; + } + forEach(item, fn); + } +} + +/** + * Extra wrapper to keep track of Dict size and clean up the API + * @template K,V + */ +export default class Dict { + /** + * @template V + * @param {Record} o + * @returns {Dict} + */ + static fromObject(o) { + const keys = Object.keys(o); + /** @type Dict */ + let m = Dict.new(); + for (let i = 0; i < keys.length; i++) { + const k = keys[i]; + m = m.set(k, o[k]); + } + return m; + } + + /** + * @template K,V + * @param {Map} o + * @returns {Dict} + */ + static fromMap(o) { + /** @type Dict */ + let m = Dict.new(); + o.forEach((v, k) => { + m = m.set(k, v); + }); + return m; + } + + static new() { + return new Dict(undefined, 0); + } + + /** + * @param {undefined | Node} root + * @param {number} size + */ + constructor(root, size) { + this.root = root; + this.size = size; + } + /** + * @template NotFound + * @param {K} key + * @param {NotFound} notFound + * @returns {NotFound | V} + */ + get(key, notFound) { + if (this.root === undefined) { + return notFound; + } + const found = find(this.root, 0, getHash(key), key); + if (found === undefined) { + return notFound; + } + return found.v; + } + /** + * @param {K} key + * @param {V} val + * @returns {Dict} + */ + set(key, val) { + const addedLeaf = { val: false }; + const root = this.root === undefined ? EMPTY : this.root; + const newRoot = assoc(root, 0, getHash(key), key, val, addedLeaf); + if (newRoot === this.root) { + return this; + } + return new Dict(newRoot, addedLeaf.val ? this.size + 1 : this.size); + } + /** + * @param {K} key + * @returns {Dict} + */ + delete(key) { + if (this.root === undefined) { + return this; + } + const newRoot = without(this.root, 0, getHash(key), key); + if (newRoot === this.root) { + return this; + } + if (newRoot === undefined) { + return Dict.new(); + } + return new Dict(newRoot, this.size - 1); + } + /** + * @param {K} key + * @returns {boolean} + */ + has(key) { + if (this.root === undefined) { + return false; + } + return find(this.root, 0, getHash(key), key) !== undefined; + } + /** + * @returns {[K,V][]} + */ + entries() { + if (this.root === undefined) { + return []; + } + /** @type [K,V][] */ + const result = []; + this.forEach((v, k) => result.push([k, v])); + return result; + } + /** + * + * @param {(val:V,key:K)=>void} fn + */ + forEach(fn) { + forEach(this.root, fn); + } + hashCode() { + let h = 0; + this.forEach((v, k) => { + h = (h + hashMerge(getHash(v), getHash(k))) | 0; + }); + return h; + } + /** + * @param {unknown} o + * @returns {boolean} + */ + equals(o) { + if (!(o instanceof Dict) || this.size !== o.size) { + return false; + } + + try { + this.forEach((v, k) => { + if (!isEqual(o.get(k, !v), v)) { + throw unequalDictSymbol; + } + }); + return true; + } catch (e) { + if (e === unequalDictSymbol) { + return false; + } + + throw e; + } + } +} + +// This is thrown internally in Dict.equals() so that it returns false as soon +// as a non-matching key is found +const unequalDictSymbol = /* @__PURE__ */ Symbol(); diff --git a/build/dev/javascript/gleam_stdlib/gleam.mjs b/build/dev/javascript/gleam_stdlib/gleam.mjs new file mode 100644 index 0000000..197cbbc --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam.mjs @@ -0,0 +1 @@ +export * from "../prelude.mjs"; diff --git a/build/dev/javascript/gleam_stdlib/gleam/bit_array.mjs b/build/dev/javascript/gleam_stdlib/gleam/bit_array.mjs new file mode 100644 index 0000000..e6c1a3a --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam/bit_array.mjs @@ -0,0 +1,285 @@ +import { Ok, toList, bitArraySlice, bitArraySliceToInt } from "../gleam.mjs"; +import * as $int from "../gleam/int.mjs"; +import * as $order from "../gleam/order.mjs"; +import * as $string from "../gleam/string.mjs"; +import { + bit_array_from_string as from_string, + bit_array_bit_size as bit_size, + bit_array_byte_size as byte_size, + bit_array_pad_to_bytes as pad_to_bytes, + bit_array_slice as slice, + bit_array_concat as concat, + base64_encode, + base64_decode as decode64, + base16_encode, + base16_decode, + bit_array_to_int_and_size, + bit_array_starts_with as starts_with, + bit_array_to_string as to_string, +} from "../gleam_stdlib.mjs"; + +export { + base16_decode, + base16_encode, + base64_encode, + bit_size, + byte_size, + concat, + from_string, + pad_to_bytes, + slice, + starts_with, + to_string, +}; + +/** + * Creates a new bit array by joining two bit arrays. + * + * ## Examples + * + * ```gleam + * append(to: from_string("butter"), suffix: from_string("fly")) + * // -> from_string("butterfly") + * ``` + */ +export function append(first, second) { + return concat(toList([first, second])); +} + +/** + * Decodes a base 64 encoded string into a `BitArray`. + */ +export function base64_decode(encoded) { + let _block; + let $ = byte_size(from_string(encoded)) % 4; + if ($ === 0) { + _block = encoded; + } else { + let n = $; + _block = $string.append(encoded, $string.repeat("=", 4 - n)); + } + let padded = _block; + return decode64(padded); +} + +/** + * Encodes a `BitArray` into a base 64 encoded string with URL and filename + * safe alphabet. + * + * If the bit array does not contain a whole number of bytes then it is padded + * with zero bits prior to being encoded. + */ +export function base64_url_encode(input, padding) { + let _pipe = input; + let _pipe$1 = base64_encode(_pipe, padding); + let _pipe$2 = $string.replace(_pipe$1, "+", "-"); + return $string.replace(_pipe$2, "/", "_"); +} + +/** + * Decodes a base 64 encoded string with URL and filename safe alphabet into a + * `BitArray`. + */ +export function base64_url_decode(encoded) { + let _pipe = encoded; + let _pipe$1 = $string.replace(_pipe, "-", "+"); + let _pipe$2 = $string.replace(_pipe$1, "_", "/"); + return base64_decode(_pipe$2); +} + +function inspect_loop(loop$input, loop$accumulator) { + while (true) { + let input = loop$input; + let accumulator = loop$accumulator; + if (input.bitSize === 0) { + return accumulator; + } else if (input.bitSize === 1) { + let x = bitArraySliceToInt(input, 0, 1, true, false); + return (accumulator + $int.to_string(x)) + ":size(1)"; + } else if (input.bitSize === 2) { + let x = bitArraySliceToInt(input, 0, 2, true, false); + return (accumulator + $int.to_string(x)) + ":size(2)"; + } else if (input.bitSize === 3) { + let x = bitArraySliceToInt(input, 0, 3, true, false); + return (accumulator + $int.to_string(x)) + ":size(3)"; + } else if (input.bitSize === 4) { + let x = bitArraySliceToInt(input, 0, 4, true, false); + return (accumulator + $int.to_string(x)) + ":size(4)"; + } else if (input.bitSize === 5) { + let x = bitArraySliceToInt(input, 0, 5, true, false); + return (accumulator + $int.to_string(x)) + ":size(5)"; + } else if (input.bitSize === 6) { + let x = bitArraySliceToInt(input, 0, 6, true, false); + return (accumulator + $int.to_string(x)) + ":size(6)"; + } else if (input.bitSize === 7) { + let x = bitArraySliceToInt(input, 0, 7, true, false); + return (accumulator + $int.to_string(x)) + ":size(7)"; + } else if (input.bitSize >= 8) { + let x = input.byteAt(0); + let rest = bitArraySlice(input, 8); + let _block; + if (rest.bitSize === 0) { + _block = ""; + } else { + _block = ", "; + } + let suffix = _block; + let accumulator$1 = (accumulator + $int.to_string(x)) + suffix; + loop$input = rest; + loop$accumulator = accumulator$1; + } else { + return accumulator; + } + } +} + +/** + * Converts a bit array to a string containing the decimal value of each byte. + * + * Use this over `string.inspect` when you have a bit array you want printed + * in the array syntax even if it is valid UTF-8. + * + * ## Examples + * + * ```gleam + * inspect(<<0, 20, 0x20, 255>>) + * // -> "<<0, 20, 32, 255>>" + * + * inspect(<<100, 5:3>>) + * // -> "<<100, 5:size(3)>>" + * ``` + */ +export function inspect(input) { + return inspect_loop(input, "<<") + ">>"; +} + +/** + * Compare two bit arrays as sequences of bytes. + * + * ## Examples + * + * ```gleam + * compare(<<1>>, <<2>>) + * // -> Lt + * + * compare(<<"AB":utf8>>, <<"AA":utf8>>) + * // -> Gt + * + * compare(<<1, 2:size(2)>>, with: <<1, 2:size(2)>>) + * // -> Eq + * ``` + */ +export function compare(loop$a, loop$b) { + while (true) { + let a = loop$a; + let b = loop$b; + if (a.bitSize >= 8) { + if (b.bitSize >= 8) { + let first_byte = a.byteAt(0); + let first_rest = bitArraySlice(a, 8); + let second_byte = b.byteAt(0); + let second_rest = bitArraySlice(b, 8); + let f = first_byte; + let s = second_byte; + if (f > s) { + return new $order.Gt(); + } else { + let f$1 = first_byte; + let s$1 = second_byte; + if (f$1 < s$1) { + return new $order.Lt(); + } else { + loop$a = first_rest; + loop$b = second_rest; + } + } + } else if (b.bitSize === 0) { + return new $order.Gt(); + } else { + let first = a; + let second = b; + let $ = bit_array_to_int_and_size(first); + let $1 = bit_array_to_int_and_size(second); + let a$1 = $[0]; + let b$1 = $1[0]; + if (a$1 > b$1) { + return new $order.Gt(); + } else { + let a$2 = $[0]; + let b$2 = $1[0]; + if (a$2 < b$2) { + return new $order.Lt(); + } else { + let size_a = $[1]; + let size_b = $1[1]; + if (size_a > size_b) { + return new $order.Gt(); + } else { + let size_a$1 = $[1]; + let size_b$1 = $1[1]; + if (size_a$1 < size_b$1) { + return new $order.Lt(); + } else { + return new $order.Eq(); + } + } + } + } + } + } else if (b.bitSize === 0) { + if (a.bitSize === 0) { + return new $order.Eq(); + } else { + return new $order.Gt(); + } + } else if (a.bitSize === 0) { + return new $order.Lt(); + } else { + let first = a; + let second = b; + let $ = bit_array_to_int_and_size(first); + let $1 = bit_array_to_int_and_size(second); + let a$1 = $[0]; + let b$1 = $1[0]; + if (a$1 > b$1) { + return new $order.Gt(); + } else { + let a$2 = $[0]; + let b$2 = $1[0]; + if (a$2 < b$2) { + return new $order.Lt(); + } else { + let size_a = $[1]; + let size_b = $1[1]; + if (size_a > size_b) { + return new $order.Gt(); + } else { + let size_a$1 = $[1]; + let size_b$1 = $1[1]; + if (size_a$1 < size_b$1) { + return new $order.Lt(); + } else { + return new $order.Eq(); + } + } + } + } + } + } +} + +/** + * Tests to see whether a bit array is valid UTF-8. + */ +export function is_utf8(bits) { + return is_utf8_loop(bits); +} + +function is_utf8_loop(bits) { + let $ = to_string(bits); + if ($ instanceof Ok) { + return true; + } else { + return false; + } +} diff --git a/build/dev/javascript/gleam_stdlib/gleam/bool.mjs b/build/dev/javascript/gleam_stdlib/gleam/bool.mjs new file mode 100644 index 0000000..1a761a2 --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam/bool.mjs @@ -0,0 +1,313 @@ +/** + * Returns the and of two bools, but it evaluates both arguments. + * + * It's the function equivalent of the `&&` operator. + * This function is useful in higher order functions or pipes. + * + * ## Examples + * + * ```gleam + * and(True, True) + * // -> True + * ``` + * + * ```gleam + * and(False, True) + * // -> False + * ``` + * + * ```gleam + * False |> and(True) + * // -> False + * ``` + */ +export function and(a, b) { + return a && b; +} + +/** + * Returns the or of two bools, but it evaluates both arguments. + * + * It's the function equivalent of the `||` operator. + * This function is useful in higher order functions or pipes. + * + * ## Examples + * + * ```gleam + * or(True, True) + * // -> True + * ``` + * + * ```gleam + * or(False, True) + * // -> True + * ``` + * + * ```gleam + * False |> or(True) + * // -> True + * ``` + */ +export function or(a, b) { + return a || b; +} + +/** + * Returns the opposite bool value. + * + * This is the same as the `!` or `not` operators in some other languages. + * + * ## Examples + * + * ```gleam + * negate(True) + * // -> False + * ``` + * + * ```gleam + * negate(False) + * // -> True + * ``` + */ +export function negate(bool) { + return !bool; +} + +/** + * Returns the nor of two bools. + * + * ## Examples + * + * ```gleam + * nor(False, False) + * // -> True + * ``` + * + * ```gleam + * nor(False, True) + * // -> False + * ``` + * + * ```gleam + * nor(True, False) + * // -> False + * ``` + * + * ```gleam + * nor(True, True) + * // -> False + * ``` + */ +export function nor(a, b) { + return !(a || b); +} + +/** + * Returns the nand of two bools. + * + * ## Examples + * + * ```gleam + * nand(False, False) + * // -> True + * ``` + * + * ```gleam + * nand(False, True) + * // -> True + * ``` + * + * ```gleam + * nand(True, False) + * // -> True + * ``` + * + * ```gleam + * nand(True, True) + * // -> False + * ``` + */ +export function nand(a, b) { + return !(a && b); +} + +/** + * Returns the exclusive or of two bools. + * + * ## Examples + * + * ```gleam + * exclusive_or(False, False) + * // -> False + * ``` + * + * ```gleam + * exclusive_or(False, True) + * // -> True + * ``` + * + * ```gleam + * exclusive_or(True, False) + * // -> True + * ``` + * + * ```gleam + * exclusive_or(True, True) + * // -> False + * ``` + */ +export function exclusive_or(a, b) { + return a !== b; +} + +/** + * Returns the exclusive nor of two bools. + * + * ## Examples + * + * ```gleam + * exclusive_nor(False, False) + * // -> True + * ``` + * + * ```gleam + * exclusive_nor(False, True) + * // -> False + * ``` + * + * ```gleam + * exclusive_nor(True, False) + * // -> False + * ``` + * + * ```gleam + * exclusive_nor(True, True) + * // -> True + * ``` + */ +export function exclusive_nor(a, b) { + return a === b; +} + +/** + * Returns a string representation of the given bool. + * + * ## Examples + * + * ```gleam + * to_string(True) + * // -> "True" + * ``` + * + * ```gleam + * to_string(False) + * // -> "False" + * ``` + */ +export function to_string(bool) { + if (bool) { + return "True"; + } else { + return "False"; + } +} + +/** + * Run a callback function if the given bool is `False`, otherwise return a + * default value. + * + * With a `use` expression this function can simulate the early-return pattern + * found in some other programming languages. + * + * In a procedural language: + * + * ```js + * if (predicate) return value; + * // ... + * ``` + * + * In Gleam with a `use` expression: + * + * ```gleam + * use <- guard(when: predicate, return: value) + * // ... + * ``` + * + * Like everything in Gleam `use` is an expression, so it short circuits the + * current block, not the entire function. As a result you can assign the value + * to a variable: + * + * ```gleam + * let x = { + * use <- guard(when: predicate, return: value) + * // ... + * } + * ``` + * + * Note that unlike in procedural languages the `return` value is evaluated + * even when the predicate is `False`, so it is advisable not to perform + * expensive computation nor side-effects there. + * + * + * ## Examples + * + * ```gleam + * let name = "" + * use <- guard(when: name == "", return: "Welcome!") + * "Hello, " <> name + * // -> "Welcome!" + * ``` + * + * ```gleam + * let name = "Kamaka" + * use <- guard(when: name == "", return: "Welcome!") + * "Hello, " <> name + * // -> "Hello, Kamaka" + * ``` + */ +export function guard(requirement, consequence, alternative) { + if (requirement) { + return consequence; + } else { + return alternative(); + } +} + +/** + * Runs a callback function if the given bool is `True`, otherwise runs an + * alternative callback function. + * + * Useful when further computation should be delayed regardless of the given + * bool's value. + * + * See [`guard`](#guard) for more info. + * + * ## Examples + * + * ```gleam + * let name = "Kamaka" + * let inquiry = fn() { "How may we address you?" } + * use <- lazy_guard(when: name == "", return: inquiry) + * "Hello, " <> name + * // -> "Hello, Kamaka" + * ``` + * + * ```gleam + * import gleam/int + * + * let name = "" + * let greeting = fn() { "Hello, " <> name } + * use <- lazy_guard(when: name == "", otherwise: greeting) + * let number = int.random(99) + * let name = "User " <> int.to_string(number) + * "Welcome, " <> name + * // -> "Welcome, User 54" + * ``` + */ +export function lazy_guard(requirement, consequence, alternative) { + if (requirement) { + return consequence(); + } else { + return alternative(); + } +} diff --git a/build/dev/javascript/gleam_stdlib/gleam/bytes_tree.mjs b/build/dev/javascript/gleam_stdlib/gleam/bytes_tree.mjs new file mode 100644 index 0000000..517efd0 --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam/bytes_tree.mjs @@ -0,0 +1,225 @@ +import { + toList, + Empty as $Empty, + prepend as listPrepend, + CustomType as $CustomType, +} from "../gleam.mjs"; +import * as $bit_array from "../gleam/bit_array.mjs"; +import * as $list from "../gleam/list.mjs"; +import * as $string_tree from "../gleam/string_tree.mjs"; + +class Bytes extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} + +class Text extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} + +class Many extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} + +/** + * Appends a bytes tree onto the end of another. + * + * Runs in constant time. + */ +export function append_tree(first, second) { + if (second instanceof Bytes) { + return new Many(toList([first, second])); + } else if (second instanceof Text) { + return new Many(toList([first, second])); + } else { + let trees = second[0]; + return new Many(listPrepend(first, trees)); + } +} + +/** + * Prepends a bytes tree onto the start of another. + * + * Runs in constant time. + */ +export function prepend_tree(second, first) { + return append_tree(first, second); +} + +/** + * Joins a list of bytes trees into a single one. + * + * Runs in constant time. + */ +export function concat(trees) { + return new Many(trees); +} + +/** + * Create an empty `BytesTree`. Useful as the start of a pipe chaining many + * trees together. + */ +export function new$() { + return concat(toList([])); +} + +/** + * Creates a new bytes tree from a string. + * + * Runs in constant time when running on Erlang. + * Runs in linear time otherwise. + */ +export function from_string(string) { + return new Text($string_tree.from_string(string)); +} + +/** + * Prepends a string onto the start of a bytes tree. + * + * Runs in constant time when running on Erlang. + * Runs in linear time with the length of the string otherwise. + */ +export function prepend_string(second, first) { + return append_tree(from_string(first), second); +} + +/** + * Appends a string onto the end of a bytes tree. + * + * Runs in constant time when running on Erlang. + * Runs in linear time with the length of the string otherwise. + */ +export function append_string(first, second) { + return append_tree(first, from_string(second)); +} + +/** + * Creates a new bytes tree from a string tree. + * + * Runs in constant time when running on Erlang. + * Runs in linear time otherwise. + */ +export function from_string_tree(tree) { + return new Text(tree); +} + +function wrap_list(bits) { + return new Bytes(bits); +} + +/** + * Creates a new bytes tree from a bit array. + * + * Runs in constant time. + */ +export function from_bit_array(bits) { + let _pipe = bits; + let _pipe$1 = $bit_array.pad_to_bytes(_pipe); + return wrap_list(_pipe$1); +} + +/** + * Prepends a bit array to the start of a bytes tree. + * + * Runs in constant time. + */ +export function prepend(second, first) { + return append_tree(from_bit_array(first), second); +} + +/** + * Appends a bit array to the end of a bytes tree. + * + * Runs in constant time. + */ +export function append(first, second) { + return append_tree(first, from_bit_array(second)); +} + +/** + * Joins a list of bit arrays into a single bytes tree. + * + * Runs in constant time. + */ +export function concat_bit_arrays(bits) { + let _pipe = bits; + let _pipe$1 = $list.map(_pipe, from_bit_array); + return concat(_pipe$1); +} + +function to_list(loop$stack, loop$acc) { + while (true) { + let stack = loop$stack; + let acc = loop$acc; + if (stack instanceof $Empty) { + return acc; + } else { + let $ = stack.head; + if ($ instanceof $Empty) { + let remaining_stack = stack.tail; + loop$stack = remaining_stack; + loop$acc = acc; + } else { + let $1 = $.head; + if ($1 instanceof Bytes) { + let remaining_stack = stack.tail; + let rest = $.tail; + let bits = $1[0]; + loop$stack = listPrepend(rest, remaining_stack); + loop$acc = listPrepend(bits, acc); + } else if ($1 instanceof Text) { + let remaining_stack = stack.tail; + let rest = $.tail; + let tree = $1[0]; + let bits = $bit_array.from_string($string_tree.to_string(tree)); + loop$stack = listPrepend(rest, remaining_stack); + loop$acc = listPrepend(bits, acc); + } else { + let remaining_stack = stack.tail; + let rest = $.tail; + let trees = $1[0]; + loop$stack = listPrepend(trees, listPrepend(rest, remaining_stack)); + loop$acc = acc; + } + } + } + } +} + +/** + * Turns a bytes tree into a bit array. + * + * Runs in linear time. + * + * When running on Erlang this function is implemented natively by the + * virtual machine and is highly optimised. + */ +export function to_bit_array(tree) { + let _pipe = toList([toList([tree])]); + let _pipe$1 = to_list(_pipe, toList([])); + let _pipe$2 = $list.reverse(_pipe$1); + return $bit_array.concat(_pipe$2); +} + +/** + * Returns the size of the bytes tree's content in bytes. + * + * Runs in linear time. + */ +export function byte_size(tree) { + let _pipe = toList([toList([tree])]); + let _pipe$1 = to_list(_pipe, toList([])); + return $list.fold( + _pipe$1, + 0, + (acc, bits) => { return $bit_array.byte_size(bits) + acc; }, + ); +} diff --git a/build/dev/javascript/gleam_stdlib/gleam/dict.mjs b/build/dev/javascript/gleam_stdlib/gleam/dict.mjs new file mode 100644 index 0000000..aa206af --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam/dict.mjs @@ -0,0 +1,534 @@ +import { Ok, Error, toList, Empty as $Empty, prepend as listPrepend, isEqual } from "../gleam.mjs"; +import * as $option from "../gleam/option.mjs"; +import { + map_size as size, + map_to_list as to_list, + new_map as new$, + map_get as get, + map_insert as do_insert, + map_remove as do_delete, +} from "../gleam_stdlib.mjs"; + +export { get, new$, size, to_list }; + +/** + * Determines whether or not the dict is empty. + * + * ## Examples + * + * ```gleam + * new() |> is_empty + * // -> True + * ``` + * + * ```gleam + * new() |> insert("b", 1) |> is_empty + * // -> False + * ``` + */ +export function is_empty(dict) { + return size(dict) === 0; +} + +function do_has_key(key, dict) { + return !isEqual(get(dict, key), new Error(undefined)); +} + +/** + * Determines whether or not a value present in the dict for a given key. + * + * ## Examples + * + * ```gleam + * new() |> insert("a", 0) |> has_key("a") + * // -> True + * ``` + * + * ```gleam + * new() |> insert("a", 0) |> has_key("b") + * // -> False + * ``` + */ +export function has_key(dict, key) { + return do_has_key(key, dict); +} + +/** + * Inserts a value into the dict with the given key. + * + * If the dict already has a value for the given key then the value is + * replaced with the new value. + * + * ## Examples + * + * ```gleam + * new() |> insert("a", 0) + * // -> from_list([#("a", 0)]) + * ``` + * + * ```gleam + * new() |> insert("a", 0) |> insert("a", 5) + * // -> from_list([#("a", 5)]) + * ``` + */ +export function insert(dict, key, value) { + return do_insert(key, value, dict); +} + +function from_list_loop(loop$list, loop$initial) { + while (true) { + let list = loop$list; + let initial = loop$initial; + if (list instanceof $Empty) { + return initial; + } else { + let rest = list.tail; + let key = list.head[0]; + let value = list.head[1]; + loop$list = rest; + loop$initial = insert(initial, key, value); + } + } +} + +/** + * Converts a list of 2-element tuples `#(key, value)` to a dict. + * + * If two tuples have the same key the last one in the list will be the one + * that is present in the dict. + */ +export function from_list(list) { + return from_list_loop(list, new$()); +} + +function reverse_and_concat(loop$remaining, loop$accumulator) { + while (true) { + let remaining = loop$remaining; + let accumulator = loop$accumulator; + if (remaining instanceof $Empty) { + return accumulator; + } else { + let first = remaining.head; + let rest = remaining.tail; + loop$remaining = rest; + loop$accumulator = listPrepend(first, accumulator); + } + } +} + +function do_keys_loop(loop$list, loop$acc) { + while (true) { + let list = loop$list; + let acc = loop$acc; + if (list instanceof $Empty) { + return reverse_and_concat(acc, toList([])); + } else { + let rest = list.tail; + let key = list.head[0]; + loop$list = rest; + loop$acc = listPrepend(key, acc); + } + } +} + +/** + * Gets a list of all keys in a given dict. + * + * Dicts 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 + * + * ```gleam + * from_list([#("a", 0), #("b", 1)]) |> keys + * // -> ["a", "b"] + * ``` + */ +export function keys(dict) { + return do_keys_loop(to_list(dict), toList([])); +} + +function do_values_loop(loop$list, loop$acc) { + while (true) { + let list = loop$list; + let acc = loop$acc; + if (list instanceof $Empty) { + return reverse_and_concat(acc, toList([])); + } else { + let rest = list.tail; + let value = list.head[1]; + loop$list = rest; + loop$acc = listPrepend(value, acc); + } + } +} + +/** + * Gets a list of all values in a given dict. + * + * Dicts 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 + * + * ```gleam + * from_list([#("a", 0), #("b", 1)]) |> values + * // -> [0, 1] + * ``` + */ +export function values(dict) { + let list_of_pairs = to_list(dict); + return do_values_loop(list_of_pairs, toList([])); +} + +function do_take_loop(loop$dict, loop$desired_keys, loop$acc) { + while (true) { + let dict = loop$dict; + let desired_keys = loop$desired_keys; + let acc = loop$acc; + let insert$1 = (taken, key) => { + let $ = get(dict, key); + if ($ instanceof Ok) { + let value = $[0]; + return insert(taken, key, value); + } else { + return taken; + } + }; + if (desired_keys instanceof $Empty) { + return acc; + } else { + let first = desired_keys.head; + let rest = desired_keys.tail; + loop$dict = dict; + loop$desired_keys = rest; + loop$acc = insert$1(acc, first); + } + } +} + +function do_take(desired_keys, dict) { + return do_take_loop(dict, desired_keys, new$()); +} + +/** + * Creates a new dict from a given dict, only including any entries for which the + * keys are in a given list. + * + * ## Examples + * + * ```gleam + * from_list([#("a", 0), #("b", 1)]) + * |> take(["b"]) + * // -> from_list([#("b", 1)]) + * ``` + * + * ```gleam + * from_list([#("a", 0), #("b", 1)]) + * |> take(["a", "b", "c"]) + * // -> from_list([#("a", 0), #("b", 1)]) + * ``` + */ +export function take(dict, desired_keys) { + return do_take(desired_keys, dict); +} + +function insert_pair(dict, pair) { + return insert(dict, pair[0], pair[1]); +} + +function fold_inserts(loop$new_entries, loop$dict) { + while (true) { + let new_entries = loop$new_entries; + let dict = loop$dict; + if (new_entries instanceof $Empty) { + return dict; + } else { + let first = new_entries.head; + let rest = new_entries.tail; + loop$new_entries = rest; + loop$dict = insert_pair(dict, first); + } + } +} + +/** + * Creates a new dict from a pair of given dicts by combining their entries. + * + * If there are entries with the same keys in both dicts the entry from the + * second dict takes precedence. + * + * ## Examples + * + * ```gleam + * let a = from_list([#("a", 0), #("b", 1)]) + * let b = from_list([#("b", 2), #("c", 3)]) + * merge(a, b) + * // -> from_list([#("a", 0), #("b", 2), #("c", 3)]) + * ``` + */ +export function merge(dict, new_entries) { + let _pipe = new_entries; + let _pipe$1 = to_list(_pipe); + return fold_inserts(_pipe$1, dict); +} + +/** + * Creates a new dict from a given dict with all the same entries except for the + * one with a given key, if it exists. + * + * ## Examples + * + * ```gleam + * from_list([#("a", 0), #("b", 1)]) |> delete("a") + * // -> from_list([#("b", 1)]) + * ``` + * + * ```gleam + * from_list([#("a", 0), #("b", 1)]) |> delete("c") + * // -> from_list([#("a", 0), #("b", 1)]) + * ``` + */ +export function delete$(dict, key) { + return do_delete(key, dict); +} + +/** + * Creates a new dict from a given dict with all the same entries except any with + * keys found in a given list. + * + * ## Examples + * + * ```gleam + * from_list([#("a", 0), #("b", 1)]) |> drop(["a"]) + * // -> from_list([#("b", 1)]) + * ``` + * + * ```gleam + * from_list([#("a", 0), #("b", 1)]) |> drop(["c"]) + * // -> from_list([#("a", 0), #("b", 1)]) + * ``` + * + * ```gleam + * from_list([#("a", 0), #("b", 1)]) |> drop(["a", "b", "c"]) + * // -> from_list([]) + * ``` + */ +export function drop(loop$dict, loop$disallowed_keys) { + while (true) { + let dict = loop$dict; + let disallowed_keys = loop$disallowed_keys; + if (disallowed_keys instanceof $Empty) { + return dict; + } else { + let first = disallowed_keys.head; + let rest = disallowed_keys.tail; + loop$dict = delete$(dict, first); + loop$disallowed_keys = rest; + } + } +} + +/** + * Creates a new dict with one entry inserted or updated using a given function. + * + * If there was not an entry in the dict for the given key then the function + * gets `None` as its argument, otherwise it gets `Some(value)`. + * + * ## Example + * + * ```gleam + * let dict = from_list([#("a", 0)]) + * let increment = fn(x) { + * case x { + * Some(i) -> i + 1 + * None -> 0 + * } + * } + * + * upsert(dict, "a", increment) + * // -> from_list([#("a", 1)]) + * + * upsert(dict, "b", increment) + * // -> from_list([#("a", 0), #("b", 0)]) + * ``` + */ +export function upsert(dict, key, fun) { + let $ = get(dict, key); + if ($ instanceof Ok) { + let value = $[0]; + return insert(dict, key, fun(new $option.Some(value))); + } else { + return insert(dict, key, fun(new $option.None())); + } +} + +function fold_loop(loop$list, loop$initial, loop$fun) { + while (true) { + let list = loop$list; + let initial = loop$initial; + let fun = loop$fun; + if (list instanceof $Empty) { + return initial; + } else { + let rest = list.tail; + let k = list.head[0]; + let v = list.head[1]; + loop$list = rest; + loop$initial = fun(initial, k, v); + loop$fun = fun; + } + } +} + +/** + * Combines all entries into a single value by calling a given function on each + * one. + * + * Dicts 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 + * + * ```gleam + * let dict = from_list([#("a", 1), #("b", 3), #("c", 9)]) + * fold(dict, 0, fn(accumulator, key, value) { accumulator + value }) + * // -> 13 + * ``` + * + * ```gleam + * import gleam/string + * + * let dict = from_list([#("a", 1), #("b", 3), #("c", 9)]) + * fold(dict, "", fn(accumulator, key, value) { + * string.append(accumulator, key) + * }) + * // -> "abc" + * ``` + */ +export function fold(dict, initial, fun) { + return fold_loop(to_list(dict), initial, fun); +} + +function do_map_values(f, dict) { + let f$1 = (dict, k, v) => { return insert(dict, k, f(k, v)); }; + return fold(dict, new$(), f$1); +} + +/** + * Updates all values in a given dict by calling a given function on each key + * and value. + * + * ## Examples + * + * ```gleam + * from_list([#(3, 3), #(2, 4)]) + * |> map_values(fn(key, value) { key * value }) + * // -> from_list([#(3, 9), #(2, 8)]) + * ``` + */ +export function map_values(dict, fun) { + return do_map_values(fun, dict); +} + +function do_filter(f, dict) { + let insert$1 = (dict, k, v) => { + let $ = f(k, v); + if ($) { + return insert(dict, k, v); + } else { + return dict; + } + }; + return fold(dict, new$(), insert$1); +} + +/** + * Creates a new dict from a given dict, minus any entries that a given function + * returns `False` for. + * + * ## Examples + * + * ```gleam + * from_list([#("a", 0), #("b", 1)]) + * |> filter(fn(key, value) { value != 0 }) + * // -> from_list([#("b", 1)]) + * ``` + * + * ```gleam + * from_list([#("a", 0), #("b", 1)]) + * |> filter(fn(key, value) { True }) + * // -> from_list([#("a", 0), #("b", 1)]) + * ``` + */ +export function filter(dict, predicate) { + return do_filter(predicate, dict); +} + +/** + * Calls a function for each key and value in a dict, discarding the return + * value. + * + * Useful for producing a side effect for every item of a dict. + * + * ```gleam + * import gleam/io + * + * let dict = from_list([#("a", "apple"), #("b", "banana"), #("c", "cherry")]) + * + * each(dict, fn(k, v) { + * io.println(key <> " => " <> value) + * }) + * // -> Nil + * // a => apple + * // b => banana + * // c => cherry + * ``` + * + * The order of elements in the iteration is an implementation detail that + * should not be relied upon. + */ +export function each(dict, fun) { + return fold( + dict, + undefined, + (nil, k, v) => { + fun(k, v); + return nil; + }, + ); +} + +/** + * Creates a new dict from a pair of given dicts by combining their entries. + * + * If there are entries with the same keys in both dicts the given function is + * used to determine the new value to use in the resulting dict. + * + * ## Examples + * + * ```gleam + * let a = from_list([#("a", 0), #("b", 1)]) + * let b = from_list([#("a", 2), #("c", 3)]) + * combine(a, b, fn(one, other) { one + other }) + * // -> from_list([#("a", 2), #("b", 1), #("c", 3)]) + * ``` + */ +export function combine(dict, other, fun) { + return fold( + dict, + other, + (acc, key, value) => { + let $ = get(acc, key); + if ($ instanceof Ok) { + let other_value = $[0]; + return insert(acc, key, fun(value, other_value)); + } else { + return insert(acc, key, value); + } + }, + ); +} diff --git a/build/dev/javascript/gleam_stdlib/gleam/dynamic.mjs b/build/dev/javascript/gleam_stdlib/gleam/dynamic.mjs new file mode 100644 index 0000000..3c67bbb --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam/dynamic.mjs @@ -0,0 +1,35 @@ +import * as $dict from "../gleam/dict.mjs"; +import { + classify_dynamic as classify, + identity as bool, + identity as string, + identity as float, + identity as int, + identity as bit_array, + identity as list, + list_to_array as array, + identity as cast, +} from "../gleam_stdlib.mjs"; + +export { array, bit_array, bool, classify, float, int, list, string }; + +/** + * Create a dynamic value made an unordered series of keys and values, where + * the keys are unique. + * + * On Erlang this will be a map, on JavaScript this will be a Gleam dict + * object. + */ +export function properties(entries) { + return cast($dict.from_list(entries)); +} + +/** + * A dynamic value representing nothing. + * + * On Erlang this will be the atom `nil`, on JavaScript this will be + * `undefined`. + */ +export function nil() { + return cast(undefined); +} diff --git a/build/dev/javascript/gleam_stdlib/gleam/dynamic/decode.mjs b/build/dev/javascript/gleam_stdlib/gleam/dynamic/decode.mjs new file mode 100644 index 0000000..25c8902 --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam/dynamic/decode.mjs @@ -0,0 +1,947 @@ +import { + Ok, + Error, + toList, + Empty as $Empty, + prepend as listPrepend, + CustomType as $CustomType, + isEqual, +} from "../../gleam.mjs"; +import * as $bit_array from "../../gleam/bit_array.mjs"; +import * as $dict from "../../gleam/dict.mjs"; +import * as $dynamic from "../../gleam/dynamic.mjs"; +import * as $int from "../../gleam/int.mjs"; +import * as $list from "../../gleam/list.mjs"; +import * as $option from "../../gleam/option.mjs"; +import { None, Some } from "../../gleam/option.mjs"; +import { + index as bare_index, + int as dynamic_int, + float as dynamic_float, + bit_array as dynamic_bit_array, + list as decode_list, + dict as decode_dict, + identity as cast, + is_null, + string as dynamic_string, +} from "../../gleam_stdlib.mjs"; + +export class DecodeError extends $CustomType { + constructor(expected, found, path) { + super(); + this.expected = expected; + this.found = found; + this.path = path; + } +} +export const DecodeError$DecodeError = (expected, found, path) => + new DecodeError(expected, found, path); +export const DecodeError$isDecodeError = (value) => + value instanceof DecodeError; +export const DecodeError$DecodeError$expected = (value) => value.expected; +export const DecodeError$DecodeError$0 = (value) => value.expected; +export const DecodeError$DecodeError$found = (value) => value.found; +export const DecodeError$DecodeError$1 = (value) => value.found; +export const DecodeError$DecodeError$path = (value) => value.path; +export const DecodeError$DecodeError$2 = (value) => value.path; + +class Decoder extends $CustomType { + constructor(function$) { + super(); + this.function = function$; + } +} + +/** + * Run a decoder on a `Dynamic` value, decoding the value if it is of the + * desired type, or returning errors. + * + * # Examples + * + * ```gleam + * let decoder = { + * use name <- decode.field("email", decode.string) + * use email <- decode.field("password", decode.string) + * decode.success(SignUp(name: name, email: email)) + * } + * + * decode.run(data, decoder) + * ``` + */ +export function run(data, decoder) { + let $ = decoder.function(data); + let maybe_invalid_data; + let errors; + maybe_invalid_data = $[0]; + errors = $[1]; + if (errors instanceof $Empty) { + return new Ok(maybe_invalid_data); + } else { + return new Error(errors); + } +} + +/** + * Finalise a decoder having successfully extracted a value. + * + * # Examples + * + * ```gleam + * let data = dynamic.properties([ + * #(dynamic.string("email"), dynamic.string("lucy@example.com")), + * #(dynamic.string("name"), dynamic.string("Lucy")), + * ])) + * + * let decoder = { + * use name <- decode.field("name", string) + * use email <- decode.field("email", string) + * decode.success(SignUp(name: name, email: email)) + * } + * + * let result = decode.run(data, decoder) + * assert result == Ok(SignUp(name: "Lucy", email: "lucy@example.com")) + * ``` + */ +export function success(data) { + return new Decoder((_) => { return [data, toList([])]; }); +} + +function decode_dynamic(data) { + return [data, toList([])]; +} + +/** + * Apply a transformation function to any value decoded by the decoder. + * + * # Examples + * + * ```gleam + * let decoder = decode.int |> decode.map(int.to_string) + * let result = decode.run(dynamic.int(1000), decoder) + * assert result == Ok("1000") + * ``` + */ +export function map(decoder, transformer) { + return new Decoder( + (d) => { + let $ = decoder.function(d); + let data; + let errors; + data = $[0]; + errors = $[1]; + return [transformer(data), errors]; + }, + ); +} + +/** + * Apply a transformation function to any errors returned by the decoder. + */ +export function map_errors(decoder, transformer) { + return new Decoder( + (d) => { + let $ = decoder.function(d); + let data; + let errors; + data = $[0]; + errors = $[1]; + return [data, transformer(errors)]; + }, + ); +} + +/** + * Create a new decoder based upon the value of a previous decoder. + * + * This may be useful to run one previous decoder to use in further decoding. + */ +export function then$(decoder, next) { + return new Decoder( + (dynamic_data) => { + let $ = decoder.function(dynamic_data); + let data; + let errors; + data = $[0]; + errors = $[1]; + let decoder$1 = next(data); + let $1 = decoder$1.function(dynamic_data); + let layer; + let data$1; + layer = $1; + data$1 = $1[0]; + if (errors instanceof $Empty) { + return layer; + } else { + return [data$1, errors]; + } + }, + ); +} + +function run_decoders(loop$data, loop$failure, loop$decoders) { + while (true) { + let data = loop$data; + let failure = loop$failure; + let decoders = loop$decoders; + if (decoders instanceof $Empty) { + return failure; + } else { + let decoder = decoders.head; + let decoders$1 = decoders.tail; + let $ = decoder.function(data); + let layer; + let errors; + layer = $; + errors = $[1]; + if (errors instanceof $Empty) { + return layer; + } else { + loop$data = data; + loop$failure = failure; + loop$decoders = decoders$1; + } + } + } +} + +/** + * Create a new decoder from several other decoders. Each of the inner + * decoders is run in turn, and the value from the first to succeed is used. + * + * If no decoder succeeds then the errors from the first decoder is used. + * If you wish for different errors then you may wish to use the + * `collapse_errors` or `map_errors` functions. + * + * # Examples + * + * ```gleam + * let decoder = decode.one_of(decode.string, or: [ + * decode.int |> decode.map(int.to_string), + * decode.float |> decode.map(float.to_string), + * ]) + * decode.run(dynamic.int(1000), decoder) + * // -> Ok("1000") + * ``` + */ +export function one_of(first, alternatives) { + return new Decoder( + (dynamic_data) => { + let $ = first.function(dynamic_data); + let layer; + let errors; + layer = $; + errors = $[1]; + if (errors instanceof $Empty) { + return layer; + } else { + return run_decoders(dynamic_data, layer, alternatives); + } + }, + ); +} + +/** + * Create a decoder that can refer to itself, useful for decoding deeply + * nested data. + * + * Attempting to create a recursive decoder without this function could result + * in an infinite loop. If you are using `field` or other `use`able functions + * then you may not need to use this function. + * + * ```gleam + * type Nested { + * Nested(List(Nested)) + * Value(String) + * } + * + * fn nested_decoder() -> decode.Decoder(Nested) { + * use <- decode.recursive + * decode.one_of(decode.string |> decode.map(Value), [ + * decode.list(nested_decoder()) |> decode.map(Nested), + * ]) + * } + * ``` + */ +export function recursive(inner) { + return new Decoder( + (data) => { + let decoder = inner(); + return decoder.function(data); + }, + ); +} + +/** + * A decoder that decodes nullable values of a type decoded by with a given + * decoder. + * + * This function can handle common representations of null on all runtimes, such as + * `nil`, `null`, and `undefined` on Erlang, and `undefined` and `null` on + * JavaScript. + * + * # Examples + * + * ```gleam + * let result = decode.run(dynamic.int(100), decode.optional(decode.int)) + * assert result == Ok(option.Some(100)) + * ``` + * + * ```gleam + * let result = decode.run(dynamic.nil(), decode.optional(decode.int)) + * assert result == Ok(option.None) + * ``` + */ +export function optional(inner) { + return new Decoder( + (data) => { + let $ = is_null(data); + if ($) { + return [new $option.None(), toList([])]; + } else { + let $1 = inner.function(data); + let data$1; + let errors; + data$1 = $1[0]; + errors = $1[1]; + return [new $option.Some(data$1), errors]; + } + }, + ); +} + +/** + * A decoder that decodes `Dynamic` values. This decoder never returns an error. + * + * # Examples + * + * ```gleam + * let result = decode.run(dynamic.float(3.14), decode.dynamic) + * assert result == Ok(dynamic.float(3.14)) + * ``` + */ +export const dynamic = /* @__PURE__ */ new Decoder(decode_dynamic); + +/** + * Construct a decode error for some unexpected dynamic data. + */ +export function decode_error(expected, found) { + return toList([ + new DecodeError(expected, $dynamic.classify(found), toList([])), + ]); +} + +function run_dynamic_function(data, name, f) { + let $ = f(data); + if ($ instanceof Ok) { + let data$1 = $[0]; + return [data$1, toList([])]; + } else { + let zero = $[0]; + return [ + zero, + toList([new DecodeError(name, $dynamic.classify(data), toList([]))]), + ]; + } +} + +function decode_bool(data) { + let $ = isEqual(cast(true), data); + if ($) { + return [true, toList([])]; + } else { + let $1 = isEqual(cast(false), data); + if ($1) { + return [false, toList([])]; + } else { + return [false, decode_error("Bool", data)]; + } + } +} + +function decode_int(data) { + return run_dynamic_function(data, "Int", dynamic_int); +} + +function decode_float(data) { + return run_dynamic_function(data, "Float", dynamic_float); +} + +function decode_bit_array(data) { + return run_dynamic_function(data, "BitArray", dynamic_bit_array); +} + +/** + * Replace all errors produced by a decoder with one single error for a named + * expected type. + * + * This function may be useful if you wish to simplify errors before + * presenting them to a user, particularly when using the `one_of` function. + * + * # Examples + * + * ```gleam + * let decoder = decode.string |> decode.collapse_errors("MyThing") + * let result = decode.run(dynamic.int(1000), decoder) + * assert result == Error([DecodeError("MyThing", "Int", [])]) + * ``` + */ +export function collapse_errors(decoder, name) { + return new Decoder( + (dynamic_data) => { + let $ = decoder.function(dynamic_data); + let layer; + let data; + let errors; + layer = $; + data = $[0]; + errors = $[1]; + if (errors instanceof $Empty) { + return layer; + } else { + return [data, decode_error(name, dynamic_data)]; + } + }, + ); +} + +/** + * Define a decoder that always fails. The parameter for this function is the + * name of the type that has failed to decode. + */ +export function failure(zero, expected) { + return new Decoder((d) => { return [zero, decode_error(expected, d)]; }); +} + +/** + * Create a decoder for a new data type from a decoding function. + * + * This function is used for new primitive types. For example, you might + * define a decoder for Erlang's pid type. + * + * A default "zero" value is also required to make a decoder. When this + * decoder is used as part of a larger decoder this zero value used as + * a placeholder so that the rest of the decoder can continue to run and + * collect all decoding errors. + * + * If you were to make a decoder for the `String` type (rather than using the + * build-in `string` decoder) you would define it like so: + * + * ```gleam + * pub fn string_decoder() -> decode.Decoder(String) { + * let default = "" + * decode.new_primitive_decoder("String", fn(data) { + * case dynamic.string(data) { + * Ok(x) -> Ok(x) + * Error(_) -> Error(default) + * } + * }) + * } + * ``` + */ +export function new_primitive_decoder(name, decoding_function) { + return new Decoder( + (d) => { + let $ = decoding_function(d); + if ($ instanceof Ok) { + let t = $[0]; + return [t, toList([])]; + } else { + let zero = $[0]; + return [ + zero, + toList([new DecodeError(name, $dynamic.classify(d), toList([]))]), + ]; + } + }, + ); +} + +/** + * A decoder that decodes `Bool` values. + * + * # Examples + * + * ```gleam + * let result = decode.run(dynamic.bool(True), decode.bool) + * assert result == Ok(True) + * ``` + */ +export const bool = /* @__PURE__ */ new Decoder(decode_bool); + +/** + * A decoder that decodes `Int` values. + * + * # Examples + * + * ```gleam + * let result = decode.run(dynamic.int(147), decode.int) + * assert result == Ok(147) + * ``` + */ +export const int = /* @__PURE__ */ new Decoder(decode_int); + +/** + * A decoder that decodes `Float` values. + * + * # Examples + * + * ```gleam + * let result = decode.run(dynamic.float(3.14), decode.float) + * assert result == Ok(3.14) + * ``` + */ +export const float = /* @__PURE__ */ new Decoder(decode_float); + +/** + * A decoder that decodes `BitArray` values. This decoder never returns an error. + * + * # Examples + * + * ```gleam + * let result = decode.run(dynamic.bit_array(<<5, 7>>), decode.bit_array) + * assert result == Ok(<<5, 7>>) + * ``` + */ +export const bit_array = /* @__PURE__ */ new Decoder(decode_bit_array); + +function decode_string(data) { + return run_dynamic_function(data, "String", dynamic_string); +} + +/** + * A decoder that decodes `String` values. + * + * # Examples + * + * ```gleam + * let result = decode.run(dynamic.string("Hello!"), decode.string) + * assert result == Ok("Hello!") + * ``` + */ +export const string = /* @__PURE__ */ new Decoder(decode_string); + +function fold_dict(acc, key, value, key_decoder, value_decoder) { + let $ = key_decoder(key); + let $1 = $[1]; + if ($1 instanceof $Empty) { + let key$1 = $[0]; + let $2 = value_decoder(value); + let $3 = $2[1]; + if ($3 instanceof $Empty) { + let value$1 = $2[0]; + let dict$1 = $dict.insert(acc[0], key$1, value$1); + return [dict$1, acc[1]]; + } else { + let errors = $3; + return push_path([$dict.new$(), errors], toList(["values"])); + } + } else { + let errors = $1; + return push_path([$dict.new$(), errors], toList(["keys"])); + } +} + +/** + * A decoder that decodes dicts where all keys and vales are decoded with + * given decoders. + * + * # Examples + * + * ```gleam + * let values = dynamic.properties([ + * #(dynamic.string("one"), dynamic.int(1)), + * #(dynamic.string("two"), dynamic.int(2)), + * ]) + * + * let result = + * decode.run(values, decode.dict(decode.string, decode.int)) + * assert result == Ok(values) + * ``` + */ +export function dict(key, value) { + return new Decoder( + (data) => { + let $ = decode_dict(data); + if ($ instanceof Ok) { + let dict$1 = $[0]; + return $dict.fold( + dict$1, + [$dict.new$(), toList([])], + (a, k, v) => { + let $1 = a[1]; + if ($1 instanceof $Empty) { + return fold_dict(a, k, v, key.function, value.function); + } else { + return a; + } + }, + ); + } else { + return [$dict.new$(), decode_error("Dict", data)]; + } + }, + ); +} + +/** + * A decoder that decodes lists where all elements are decoded with a given + * decoder. + * + * # Examples + * + * ```gleam + * let result = + * [1, 2, 3] + * |> list.map(dynamic.int) + * |> dynamic.list + * |> decode.run(decode.list(of: decode.int)) + * assert result == Ok([1, 2, 3]) + * ``` + */ +export function list(inner) { + return new Decoder( + (data) => { + return decode_list( + data, + inner.function, + (p, k) => { return push_path(p, toList([k])); }, + 0, + toList([]), + ); + }, + ); +} + +function push_path(layer, path) { + let decoder = one_of( + string, + toList([ + (() => { + let _pipe = int; + return map(_pipe, $int.to_string); + })(), + ]), + ); + let path$1 = $list.map( + path, + (key) => { + let key$1 = cast(key); + let $ = run(key$1, decoder); + if ($ instanceof Ok) { + let key$2 = $[0]; + return key$2; + } else { + return ("<" + $dynamic.classify(key$1)) + ">"; + } + }, + ); + let errors = $list.map( + layer[1], + (error) => { + return new DecodeError( + error.expected, + error.found, + $list.append(path$1, error.path), + ); + }, + ); + return [layer[0], errors]; +} + +function index( + loop$path, + loop$position, + loop$inner, + loop$data, + loop$handle_miss +) { + while (true) { + let path = loop$path; + let position = loop$position; + let inner = loop$inner; + let data = loop$data; + let handle_miss = loop$handle_miss; + if (path instanceof $Empty) { + let _pipe = data; + let _pipe$1 = inner(_pipe); + return push_path(_pipe$1, $list.reverse(position)); + } else { + let key = path.head; + let path$1 = path.tail; + let $ = bare_index(data, key); + if ($ instanceof Ok) { + let $1 = $[0]; + if ($1 instanceof Some) { + let data$1 = $1[0]; + loop$path = path$1; + loop$position = listPrepend(key, position); + loop$inner = inner; + loop$data = data$1; + loop$handle_miss = handle_miss; + } else { + return handle_miss(data, listPrepend(key, position)); + } + } else { + let kind = $[0]; + let $1 = inner(data); + let default$; + default$ = $1[0]; + let _pipe = [ + default$, + toList([new DecodeError(kind, $dynamic.classify(data), toList([]))]), + ]; + return push_path(_pipe, $list.reverse(position)); + } + } + } +} + +/** + * The same as [`field`](#field), except taking a path to the value rather + * than a field name. + * + * This function will index into dictionaries with any key type, and if the key is + * an int then it'll also index into Erlang tuples and JavaScript arrays, and + * the first eight elements of Gleam lists. + * + * # Examples + * + * ```gleam + * let data = dynamic.properties([ + * #(dynamic.string("data"), dynamic.properties([ + * #(dynamic.string("email"), dynamic.string("lucy@example.com")), + * #(dynamic.string("name"), dynamic.string("Lucy")), + * ]) + * ])) + * + * let decoder = { + * use name <- decode.subfield(["data", "name"], decode.string) + * use email <- decode.subfield(["data", "email"], decode.string) + * decode.success(SignUp(name: name, email: email)) + * } + * let result = decode.run(data, decoder) + * assert result == Ok(SignUp(name: "Lucy", email: "lucy@example.com")) + * ``` + */ +export function subfield(field_path, field_decoder, next) { + return new Decoder( + (data) => { + let $ = index( + field_path, + toList([]), + field_decoder.function, + data, + (data, position) => { + let $1 = field_decoder.function(data); + let default$; + default$ = $1[0]; + let _pipe = [ + default$, + toList([new DecodeError("Field", "Nothing", toList([]))]), + ]; + return push_path(_pipe, $list.reverse(position)); + }, + ); + let out; + let errors1; + out = $[0]; + errors1 = $[1]; + let $1 = next(out).function(data); + let out$1; + let errors2; + out$1 = $1[0]; + errors2 = $1[1]; + return [out$1, $list.append(errors1, errors2)]; + }, + ); +} + +/** + * A decoder that decodes a value that is nested within other values. For + * example, decoding a value that is within some deeply nested JSON objects. + * + * This function will index into dictionaries with any key type, and if the key is + * an int then it'll also index into Erlang tuples and JavaScript arrays, and + * the first eight elements of Gleam lists. + * + * # Examples + * + * ```gleam + * let decoder = decode.at(["one", "two"], decode.int) + * + * let data = dynamic.properties([ + * #(dynamic.string("one"), dynamic.properties([ + * #(dynamic.string("two"), dynamic.int(1000)), + * ])), + * ])) + * + * + * decode.run(data, decoder) + * // -> Ok(1000) + * ``` + * + * ```gleam + * dynamic.nil() + * |> decode.run(decode.optional(decode.int)) + * // -> Ok(option.None) + * ``` + */ +export function at(path, inner) { + return new Decoder( + (data) => { + return index( + path, + toList([]), + inner.function, + data, + (data, position) => { + let $ = inner.function(data); + let default$; + default$ = $[0]; + let _pipe = [ + default$, + toList([new DecodeError("Field", "Nothing", toList([]))]), + ]; + return push_path(_pipe, $list.reverse(position)); + }, + ); + }, + ); +} + +/** + * Run a decoder on a field of a `Dynamic` value, decoding the value if it is + * of the desired type, or returning errors. An error is returned if there is + * no field for the specified key. + * + * This function will index into dictionaries with any key type, and if the key is + * an int then it'll also index into Erlang tuples and JavaScript arrays, and + * the first eight elements of Gleam lists. + * + * # Examples + * + * ```gleam + * let data = dynamic.properties([ + * #(dynamic.string("email"), dynamic.string("lucy@example.com")), + * #(dynamic.string("name"), dynamic.string("Lucy")), + * ])) + * + * let decoder = { + * use name <- decode.field("name", string) + * use email <- decode.field("email", string) + * decode.success(SignUp(name: name, email: email)) + * } + * + * let result = decode.run(data, decoder) + * assert result == Ok(SignUp(name: "Lucy", email: "lucy@example.com")) + * ``` + * + * If you wish to decode a value that is more deeply nested within the dynamic + * data, see [`subfield`](#subfield) and [`at`](#at). + * + * If you wish to return a default in the event that a field is not present, + * see [`optional_field`](#optional_field) and / [`optionally_at`](#optionally_at). + */ +export function field(field_name, field_decoder, next) { + return subfield(toList([field_name]), field_decoder, next); +} + +/** + * Run a decoder on a field of a `Dynamic` value, decoding the value if it is + * of the desired type, or returning errors. The given default value is + * returned if there is no field for the specified key. + * + * This function will index into dictionaries with any key type, and if the key is + * an int then it'll also index into Erlang tuples and JavaScript arrays, and + * the first eight elements of Gleam lists. + * + * # Examples + * + * ```gleam + * let data = dynamic.properties([ + * #(dynamic.string("name"), dynamic.string("Lucy")), + * ])) + * + * let decoder = { + * use name <- decode.field("name", string) + * use email <- decode.optional_field("email", "n/a", string) + * decode.success(SignUp(name: name, email: email)) + * } + * + * let result = decode.run(data, decoder) + * assert result == Ok(SignUp(name: "Lucy", email: "n/a")) + * ``` + */ +export function optional_field(key, default$, field_decoder, next) { + return new Decoder( + (data) => { + let _block; + let _block$1; + let $1 = bare_index(data, key); + if ($1 instanceof Ok) { + let $2 = $1[0]; + if ($2 instanceof Some) { + let data$1 = $2[0]; + _block$1 = field_decoder.function(data$1); + } else { + _block$1 = [default$, toList([])]; + } + } else { + let kind = $1[0]; + _block$1 = [ + default$, + toList([new DecodeError(kind, $dynamic.classify(data), toList([]))]), + ]; + } + let _pipe = _block$1; + _block = push_path(_pipe, toList([key])); + let $ = _block; + let out; + let errors1; + out = $[0]; + errors1 = $[1]; + let $2 = next(out).function(data); + let out$1; + let errors2; + out$1 = $2[0]; + errors2 = $2[1]; + return [out$1, $list.append(errors1, errors2)]; + }, + ); +} + +/** + * A decoder that decodes a value that is nested within other values. For + * example, decoding a value that is within some deeply nested JSON objects. + * + * This function will index into dictionaries with any key type, and if the key is + * an int then it'll also index into Erlang tuples and JavaScript arrays, and + * the first eight elements of Gleam lists. + * + * # Examples + * + * ```gleam + * let decoder = decode.optionally_at(["one", "two"], 100, decode.int) + * + * let data = dynamic.properties([ + * #(dynamic.string("one"), dynamic.properties([])), + * ])) + * + * + * decode.run(data, decoder) + * // -> Ok(100) + * ``` + */ +export function optionally_at(path, default$, inner) { + return new Decoder( + (data) => { + return index( + path, + toList([]), + inner.function, + data, + (_, _1) => { return [default$, toList([])]; }, + ); + }, + ); +} diff --git a/build/dev/javascript/gleam_stdlib/gleam/float.mjs b/build/dev/javascript/gleam_stdlib/gleam/float.mjs new file mode 100644 index 0000000..4f3aaa6 --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam/float.mjs @@ -0,0 +1,553 @@ +import { Ok, Error, Empty as $Empty, divideFloat } from "../gleam.mjs"; +import * as $order from "../gleam/order.mjs"; +import { + parse_float as parse, + float_to_string as to_string, + ceiling, + floor, + round as js_round, + truncate, + identity as do_to_float, + power as do_power, + random_uniform as random, + log as do_log, + exp as exponential, +} from "../gleam_stdlib.mjs"; + +export { ceiling, exponential, floor, parse, random, to_string, truncate }; + +/** + * Compares two `Float`s, returning an `Order`: + * `Lt` for lower than, `Eq` for equals, or `Gt` for greater than. + * + * ## Examples + * + * ```gleam + * compare(2.0, 2.3) + * // -> Lt + * ``` + * + * To handle + * [Floating Point Imprecision](https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems) + * you may use [`loosely_compare`](#loosely_compare) instead. + */ +export function compare(a, b) { + let $ = a === b; + if ($) { + return new $order.Eq(); + } else { + let $1 = a < b; + if ($1) { + return new $order.Lt(); + } else { + return new $order.Gt(); + } + } +} + +/** + * Compares two `Float`s, returning the smaller of the two. + * + * ## Examples + * + * ```gleam + * min(2.0, 2.3) + * // -> 2.0 + * ``` + */ +export function min(a, b) { + let $ = a < b; + if ($) { + return a; + } else { + return b; + } +} + +/** + * Compares two `Float`s, returning the larger of the two. + * + * ## Examples + * + * ```gleam + * max(2.0, 2.3) + * // -> 2.3 + * ``` + */ +export function max(a, b) { + let $ = a > b; + if ($) { + return a; + } else { + return b; + } +} + +/** + * Restricts a `Float` between a lower and upper bound. + * + * ## Examples + * + * ```gleam + * clamp(1.2, min: 1.4, max: 1.6) + * // -> 1.4 + * ``` + */ +export function clamp(x, min_bound, max_bound) { + let _pipe = x; + let _pipe$1 = min(_pipe, max_bound); + return max(_pipe$1, min_bound); +} + +/** + * Returns the absolute value of the input as a `Float`. + * + * ## Examples + * + * ```gleam + * absolute_value(-12.5) + * // -> 12.5 + * ``` + * + * ```gleam + * absolute_value(10.2) + * // -> 10.2 + * ``` + */ +export function absolute_value(x) { + let $ = x >= 0.0; + if ($) { + return x; + } else { + return 0.0 - x; + } +} + +/** + * Compares two `Float`s within a tolerance, returning an `Order`: + * `Lt` for lower than, `Eq` for equals, or `Gt` for greater than. + * + * This function allows Float comparison while handling + * [Floating Point Imprecision](https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems). + * + * Notice: For `Float`s the tolerance won't be exact: + * `5.3 - 5.0` is not exactly `0.3`. + * + * ## Examples + * + * ```gleam + * loosely_compare(5.0, with: 5.3, tolerating: 0.5) + * // -> Eq + * ``` + * + * If you want to check only for equality you may use + * [`loosely_equals`](#loosely_equals) instead. + */ +export function loosely_compare(a, b, tolerance) { + let difference = absolute_value(a - b); + let $ = difference <= tolerance; + if ($) { + return new $order.Eq(); + } else { + return compare(a, b); + } +} + +/** + * Checks for equality of two `Float`s within a tolerance, + * returning an `Bool`. + * + * This function allows Float comparison while handling + * [Floating Point Imprecision](https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems). + * + * Notice: For `Float`s the tolerance won't be exact: + * `5.3 - 5.0` is not exactly `0.3`. + * + * ## Examples + * + * ```gleam + * loosely_equals(5.0, with: 5.3, tolerating: 0.5) + * // -> True + * ``` + * + * ```gleam + * loosely_equals(5.0, with: 5.1, tolerating: 0.1) + * // -> False + * ``` + */ +export function loosely_equals(a, b, tolerance) { + let difference = absolute_value(a - b); + return difference <= tolerance; +} + +/** + * Returns the results of the base being raised to the power of the + * exponent, as a `Float`. + * + * ## Examples + * + * ```gleam + * power(2.0, -1.0) + * // -> Ok(0.5) + * ``` + * + * ```gleam + * power(2.0, 2.0) + * // -> Ok(4.0) + * ``` + * + * ```gleam + * power(8.0, 1.5) + * // -> Ok(22.627416997969522) + * ``` + * + * ```gleam + * 4.0 |> power(of: 2.0) + * // -> Ok(16.0) + * ``` + * + * ```gleam + * power(-1.0, 0.5) + * // -> Error(Nil) + * ``` + */ +export function power(base, exponent) { + let fractional = (ceiling(exponent) - exponent) > 0.0; + let $ = ((base < 0.0) && fractional) || ((base === 0.0) && (exponent < 0.0)); + if ($) { + return new Error(undefined); + } else { + return new Ok(do_power(base, exponent)); + } +} + +/** + * Returns the square root of the input as a `Float`. + * + * ## Examples + * + * ```gleam + * square_root(4.0) + * // -> Ok(2.0) + * ``` + * + * ```gleam + * square_root(-16.0) + * // -> Error(Nil) + * ``` + */ +export function square_root(x) { + return power(x, 0.5); +} + +/** + * Returns the negative of the value provided. + * + * ## Examples + * + * ```gleam + * negate(1.0) + * // -> -1.0 + * ``` + */ +export function negate(x) { + return -1.0 * x; +} + +/** + * Rounds the value to the nearest whole number as an `Int`. + * + * ## Examples + * + * ```gleam + * round(2.3) + * // -> 2 + * ``` + * + * ```gleam + * round(2.5) + * // -> 3 + * ``` + */ +export function round(x) { + let $ = x >= 0.0; + if ($) { + return js_round(x); + } else { + return 0 - js_round(negate(x)); + } +} + +/** + * Converts the value to a given precision as a `Float`. + * The precision is the number of allowed decimal places. + * Negative precisions are allowed and force rounding + * to the nearest tenth, hundredth, thousandth etc. + * + * ## Examples + * + * ```gleam + * to_precision(2.43434348473, precision: 2) + * // -> 2.43 + * ``` + * + * ```gleam + * to_precision(547890.453444, precision: -3) + * // -> 548000.0 + * ``` + */ +export function to_precision(x, precision) { + let $ = precision <= 0; + if ($) { + let factor = do_power(10.0, do_to_float(- precision)); + return do_to_float(round(divideFloat(x, factor))) * factor; + } else { + let factor = do_power(10.0, do_to_float(precision)); + return divideFloat(do_to_float(round(x * factor)), factor); + } +} + +function sum_loop(loop$numbers, loop$initial) { + while (true) { + let numbers = loop$numbers; + let initial = loop$initial; + if (numbers instanceof $Empty) { + return initial; + } else { + let first = numbers.head; + let rest = numbers.tail; + loop$numbers = rest; + loop$initial = first + initial; + } + } +} + +/** + * Sums a list of `Float`s. + * + * ## Example + * + * ```gleam + * sum([1.0, 2.2, 3.3]) + * // -> 6.5 + * ``` + */ +export function sum(numbers) { + return sum_loop(numbers, 0.0); +} + +function product_loop(loop$numbers, loop$initial) { + while (true) { + let numbers = loop$numbers; + let initial = loop$initial; + if (numbers instanceof $Empty) { + return initial; + } else { + let first = numbers.head; + let rest = numbers.tail; + loop$numbers = rest; + loop$initial = first * initial; + } + } +} + +/** + * Multiplies a list of `Float`s and returns the product. + * + * ## Example + * + * ```gleam + * product([2.5, 3.2, 4.2]) + * // -> 33.6 + * ``` + */ +export function product(numbers) { + return product_loop(numbers, 1.0); +} + +/** + * Computes the modulo of an float division of inputs as a `Result`. + * + * Returns division of the inputs as a `Result`: If the given divisor equals + * `0`, this function returns an `Error`. + * + * ## Examples + * + * ```gleam + * modulo(13.3, by: 3.3) + * // -> Ok(0.1) + * ``` + * + * ```gleam + * modulo(-13.3, by: 3.3) + * // -> Ok(3.2) + * ``` + * + * ```gleam + * modulo(13.3, by: -3.3) + * // -> Ok(-3.2) + * ``` + * + * ```gleam + * modulo(-13.3, by: -3.3) + * // -> Ok(-0.1) + * ``` + */ +export function modulo(dividend, divisor) { + if (divisor === 0.0) { + return new Error(undefined); + } else { + return new Ok(dividend - (floor(divideFloat(dividend, divisor)) * divisor)); + } +} + +/** + * Returns division of the inputs as a `Result`. + * + * ## Examples + * + * ```gleam + * divide(0.0, 1.0) + * // -> Ok(0.0) + * ``` + * + * ```gleam + * divide(1.0, 0.0) + * // -> Error(Nil) + * ``` + */ +export function divide(a, b) { + if (b === 0.0) { + return new Error(undefined); + } else { + let b$1 = b; + return new Ok(divideFloat(a, b$1)); + } +} + +/** + * Adds two floats together. + * + * It's the function equivalent of the `+.` operator. + * This function is useful in higher order functions or pipes. + * + * ## Examples + * + * ```gleam + * add(1.0, 2.0) + * // -> 3.0 + * ``` + * + * ```gleam + * import gleam/list + * + * list.fold([1.0, 2.0, 3.0], 0.0, add) + * // -> 6.0 + * ``` + * + * ```gleam + * 3.0 |> add(2.0) + * // -> 5.0 + * ``` + */ +export function add(a, b) { + return a + b; +} + +/** + * Multiplies two floats together. + * + * It's the function equivalent of the `*.` operator. + * This function is useful in higher order functions or pipes. + * + * ## Examples + * + * ```gleam + * multiply(2.0, 4.0) + * // -> 8.0 + * ``` + * + * ```gleam + * import gleam/list + * + * list.fold([2.0, 3.0, 4.0], 1.0, multiply) + * // -> 24.0 + * ``` + * + * ```gleam + * 3.0 |> multiply(2.0) + * // -> 6.0 + * ``` + */ +export function multiply(a, b) { + return a * b; +} + +/** + * Subtracts one float from another. + * + * It's the function equivalent of the `-.` operator. + * This function is useful in higher order functions or pipes. + * + * ## Examples + * + * ```gleam + * subtract(3.0, 1.0) + * // -> 2.0 + * ``` + * + * ```gleam + * import gleam/list + * + * list.fold([1.0, 2.0, 3.0], 10.0, subtract) + * // -> 4.0 + * ``` + * + * ```gleam + * 3.0 |> subtract(_, 2.0) + * // -> 1.0 + * ``` + * + * ```gleam + * 3.0 |> subtract(2.0, _) + * // -> -1.0 + * ``` + */ +export function subtract(a, b) { + return a - b; +} + +/** + * Returns the natural logarithm (base e) of the given as a `Result`. If the + * input is less than or equal to 0, returns `Error(Nil)`. + * + * ## Examples + * + * ```gleam + * logarithm(1.0) + * // -> Ok(0.0) + * ``` + * + * ```gleam + * logarithm(2.718281828459045) // e + * // -> Ok(1.0) + * ``` + * + * ```gleam + * logarithm(0.0) + * // -> Error(Nil) + * ``` + * + * ```gleam + * logarithm(-1.0) + * // -> Error(Nil) + * ``` + */ +export function logarithm(x) { + let $ = x <= 0.0; + if ($) { + return new Error(undefined); + } else { + return new Ok(do_log(x)); + } +} diff --git a/build/dev/javascript/gleam_stdlib/gleam/function.mjs b/build/dev/javascript/gleam_stdlib/gleam/function.mjs new file mode 100644 index 0000000..2292fbc --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam/function.mjs @@ -0,0 +1,17 @@ +/** + * Takes a single argument and always returns its input value. + */ +export function identity(x) { + return x; +} + +/** + * Takes an argument and a single function, calls that function with that + * argument and returns that argument instead of the function return value. + * + * Useful for running synchronous side effects in a pipeline. + */ +export function tap(arg, effect) { + effect(arg); + return arg; +} diff --git a/build/dev/javascript/gleam_stdlib/gleam/int.mjs b/build/dev/javascript/gleam_stdlib/gleam/int.mjs new file mode 100644 index 0000000..5824eb6 --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam/int.mjs @@ -0,0 +1,816 @@ +import { + Ok, + Error, + toList, + Empty as $Empty, + prepend as listPrepend, + remainderInt, + divideInt, +} from "../gleam.mjs"; +import * as $float from "../gleam/float.mjs"; +import * as $order from "../gleam/order.mjs"; +import { + parse_int as parse, + int_from_base_string as do_base_parse, + to_string, + int_to_base_string as do_to_base_string, + identity as to_float, + bitwise_and, + bitwise_not, + bitwise_or, + bitwise_exclusive_or, + bitwise_shift_left, + bitwise_shift_right, +} from "../gleam_stdlib.mjs"; + +export { + bitwise_and, + bitwise_exclusive_or, + bitwise_not, + bitwise_or, + bitwise_shift_left, + bitwise_shift_right, + parse, + to_float, + to_string, +}; + +/** + * Returns the absolute value of the input. + * + * ## Examples + * + * ```gleam + * absolute_value(-12) + * // -> 12 + * ``` + * + * ```gleam + * absolute_value(10) + * // -> 10 + * ``` + */ +export function absolute_value(x) { + let $ = x >= 0; + if ($) { + return x; + } else { + return x * -1; + } +} + +/** + * Parses a given string as an int in a given base if possible. + * Supports only bases 2 to 36, for values outside of which this function returns an `Error(Nil)`. + * + * ## Examples + * + * ```gleam + * base_parse("10", 2) + * // -> Ok(2) + * ``` + * + * ```gleam + * base_parse("30", 16) + * // -> Ok(48) + * ``` + * + * ```gleam + * base_parse("1C", 36) + * // -> Ok(48) + * ``` + * + * ```gleam + * base_parse("48", 1) + * // -> Error(Nil) + * ``` + * + * ```gleam + * base_parse("48", 37) + * // -> Error(Nil) + * ``` + */ +export function base_parse(string, base) { + let $ = (base >= 2) && (base <= 36); + if ($) { + return do_base_parse(string, base); + } else { + return new Error(undefined); + } +} + +/** + * Prints a given int to a string using the base number provided. + * Supports only bases 2 to 36, for values outside of which this function returns an `Error(Nil)`. + * For common bases (2, 8, 16, 36), use the `to_baseN` functions. + * + * ## Examples + * + * ```gleam + * to_base_string(2, 2) + * // -> Ok("10") + * ``` + * + * ```gleam + * to_base_string(48, 16) + * // -> Ok("30") + * ``` + * + * ```gleam + * to_base_string(48, 36) + * // -> Ok("1C") + * ``` + * + * ```gleam + * to_base_string(48, 1) + * // -> Error(Nil) + * ``` + * + * ```gleam + * to_base_string(48, 37) + * // -> Error(Nil) + * ``` + */ +export function to_base_string(x, base) { + let $ = (base >= 2) && (base <= 36); + if ($) { + return new Ok(do_to_base_string(x, base)); + } else { + return new Error(undefined); + } +} + +/** + * Prints a given int to a string using base-2. + * + * ## Examples + * + * ```gleam + * to_base2(2) + * // -> "10" + * ``` + */ +export function to_base2(x) { + return do_to_base_string(x, 2); +} + +/** + * Prints a given int to a string using base-8. + * + * ## Examples + * + * ```gleam + * to_base8(15) + * // -> "17" + * ``` + */ +export function to_base8(x) { + return do_to_base_string(x, 8); +} + +/** + * Prints a given int to a string using base-16. + * + * ## Examples + * + * ```gleam + * to_base16(48) + * // -> "30" + * ``` + */ +export function to_base16(x) { + return do_to_base_string(x, 16); +} + +/** + * Prints a given int to a string using base-36. + * + * ## Examples + * + * ```gleam + * to_base36(48) + * // -> "1C" + * ``` + */ +export function to_base36(x) { + return do_to_base_string(x, 36); +} + +/** + * Returns the results of the base being raised to the power of the + * exponent, as a `Float`. + * + * ## Examples + * + * ```gleam + * power(2, -1.0) + * // -> Ok(0.5) + * ``` + * + * ```gleam + * power(2, 2.0) + * // -> Ok(4.0) + * ``` + * + * ```gleam + * power(8, 1.5) + * // -> Ok(22.627416997969522) + * ``` + * + * ```gleam + * 4 |> power(of: 2.0) + * // -> Ok(16.0) + * ``` + * + * ```gleam + * power(-1, 0.5) + * // -> Error(Nil) + * ``` + */ +export function power(base, exponent) { + let _pipe = base; + let _pipe$1 = to_float(_pipe); + return $float.power(_pipe$1, exponent); +} + +/** + * Returns the square root of the input as a `Float`. + * + * ## Examples + * + * ```gleam + * square_root(4) + * // -> Ok(2.0) + * ``` + * + * ```gleam + * square_root(-16) + * // -> Error(Nil) + * ``` + */ +export function square_root(x) { + let _pipe = x; + let _pipe$1 = to_float(_pipe); + return $float.square_root(_pipe$1); +} + +/** + * Compares two ints, returning an order. + * + * ## Examples + * + * ```gleam + * compare(2, 3) + * // -> Lt + * ``` + * + * ```gleam + * compare(4, 3) + * // -> Gt + * ``` + * + * ```gleam + * compare(3, 3) + * // -> Eq + * ``` + */ +export function compare(a, b) { + let $ = a === b; + if ($) { + return new $order.Eq(); + } else { + let $1 = a < b; + if ($1) { + return new $order.Lt(); + } else { + return new $order.Gt(); + } + } +} + +/** + * Compares two ints, returning the smaller of the two. + * + * ## Examples + * + * ```gleam + * min(2, 3) + * // -> 2 + * ``` + */ +export function min(a, b) { + let $ = a < b; + if ($) { + return a; + } else { + return b; + } +} + +/** + * Compares two ints, returning the larger of the two. + * + * ## Examples + * + * ```gleam + * max(2, 3) + * // -> 3 + * ``` + */ +export function max(a, b) { + let $ = a > b; + if ($) { + return a; + } else { + return b; + } +} + +/** + * Restricts an int between a lower and upper bound. + * + * ## Examples + * + * ```gleam + * clamp(40, min: 50, max: 60) + * // -> 50 + * ``` + */ +export function clamp(x, min_bound, max_bound) { + let _pipe = x; + let _pipe$1 = min(_pipe, max_bound); + return max(_pipe$1, min_bound); +} + +/** + * Returns whether the value provided is even. + * + * ## Examples + * + * ```gleam + * is_even(2) + * // -> True + * ``` + * + * ```gleam + * is_even(3) + * // -> False + * ``` + */ +export function is_even(x) { + return (x % 2) === 0; +} + +/** + * Returns whether the value provided is odd. + * + * ## Examples + * + * ```gleam + * is_odd(3) + * // -> True + * ``` + * + * ```gleam + * is_odd(2) + * // -> False + * ``` + */ +export function is_odd(x) { + return (x % 2) !== 0; +} + +/** + * Returns the negative of the value provided. + * + * ## Examples + * + * ```gleam + * negate(1) + * // -> -1 + * ``` + */ +export function negate(x) { + return -1 * x; +} + +function sum_loop(loop$numbers, loop$initial) { + while (true) { + let numbers = loop$numbers; + let initial = loop$initial; + if (numbers instanceof $Empty) { + return initial; + } else { + let first = numbers.head; + let rest = numbers.tail; + loop$numbers = rest; + loop$initial = first + initial; + } + } +} + +/** + * Sums a list of ints. + * + * ## Example + * + * ```gleam + * sum([1, 2, 3]) + * // -> 6 + * ``` + */ +export function sum(numbers) { + return sum_loop(numbers, 0); +} + +function product_loop(loop$numbers, loop$initial) { + while (true) { + let numbers = loop$numbers; + let initial = loop$initial; + if (numbers instanceof $Empty) { + return initial; + } else { + let first = numbers.head; + let rest = numbers.tail; + loop$numbers = rest; + loop$initial = first * initial; + } + } +} + +/** + * Multiplies a list of ints and returns the product. + * + * ## Example + * + * ```gleam + * product([2, 3, 4]) + * // -> 24 + * ``` + */ +export function product(numbers) { + return product_loop(numbers, 1); +} + +function digits_loop(loop$x, loop$base, loop$acc) { + while (true) { + let x = loop$x; + let base = loop$base; + let acc = loop$acc; + let $ = absolute_value(x) < base; + if ($) { + return listPrepend(x, acc); + } else { + loop$x = divideInt(x, base); + loop$base = base; + loop$acc = listPrepend(remainderInt(x, base), acc); + } + } +} + +export function digits(x, base) { + let $ = base < 2; + if ($) { + return new Error(undefined); + } else { + return new Ok(digits_loop(x, base, toList([]))); + } +} + +function undigits_loop(loop$numbers, loop$base, loop$acc) { + while (true) { + let numbers = loop$numbers; + let base = loop$base; + let acc = loop$acc; + if (numbers instanceof $Empty) { + return new Ok(acc); + } else { + let digit = numbers.head; + if (digit >= base) { + return new Error(undefined); + } else { + let digit$1 = numbers.head; + let rest = numbers.tail; + loop$numbers = rest; + loop$base = base; + loop$acc = acc * base + digit$1; + } + } + } +} + +export function undigits(numbers, base) { + let $ = base < 2; + if ($) { + return new Error(undefined); + } else { + return undigits_loop(numbers, base, 0); + } +} + +/** + * Generates a random int between zero and the given maximum. + * + * The lower number is inclusive, the upper number is exclusive. + * + * ## Examples + * + * ```gleam + * random(10) + * // -> 4 + * ``` + * + * ```gleam + * random(1) + * // -> 0 + * ``` + * + * ```gleam + * random(-1) + * // -> -1 + * ``` + */ +export function random(max) { + let _pipe = ($float.random() * to_float(max)); + let _pipe$1 = $float.floor(_pipe); + return $float.round(_pipe$1); +} + +/** + * Performs a truncated integer division. + * + * Returns division of the inputs as a `Result`: If the given divisor equals + * `0`, this function returns an `Error`. + * + * ## Examples + * + * ```gleam + * divide(0, 1) + * // -> Ok(0) + * ``` + * + * ```gleam + * divide(1, 0) + * // -> Error(Nil) + * ``` + * + * ```gleam + * divide(5, 2) + * // -> Ok(2) + * ``` + * + * ```gleam + * divide(-99, 2) + * // -> Ok(-49) + * ``` + */ +export function divide(dividend, divisor) { + if (divisor === 0) { + return new Error(undefined); + } else { + let divisor$1 = divisor; + return new Ok(divideInt(dividend, divisor$1)); + } +} + +/** + * Computes the remainder of an integer division of inputs as a `Result`. + * + * Returns division of the inputs as a `Result`: If the given divisor equals + * `0`, this function returns an `Error`. + * + * Most the time you will want to use the `%` operator instead of this + * function. + * + * ## Examples + * + * ```gleam + * remainder(3, 2) + * // -> Ok(1) + * ``` + * + * ```gleam + * remainder(1, 0) + * // -> Error(Nil) + * ``` + * + * ```gleam + * remainder(10, -1) + * // -> Ok(0) + * ``` + * + * ```gleam + * remainder(13, by: 3) + * // -> Ok(1) + * ``` + * + * ```gleam + * remainder(-13, by: 3) + * // -> Ok(-1) + * ``` + * + * ```gleam + * remainder(13, by: -3) + * // -> Ok(1) + * ``` + * + * ```gleam + * remainder(-13, by: -3) + * // -> Ok(-1) + * ``` + */ +export function remainder(dividend, divisor) { + if (divisor === 0) { + return new Error(undefined); + } else { + let divisor$1 = divisor; + return new Ok(remainderInt(dividend, divisor$1)); + } +} + +/** + * Computes the modulo of an integer division of inputs as a `Result`. + * + * Returns division of the inputs as a `Result`: If the given divisor equals + * `0`, this function returns an `Error`. + * + * Most the time you will want to use the `%` operator instead of this + * function. + * + * ## Examples + * + * ```gleam + * modulo(3, 2) + * // -> Ok(1) + * ``` + * + * ```gleam + * modulo(1, 0) + * // -> Error(Nil) + * ``` + * + * ```gleam + * modulo(10, -1) + * // -> Ok(0) + * ``` + * + * ```gleam + * modulo(13, by: 3) + * // -> Ok(1) + * ``` + * + * ```gleam + * modulo(-13, by: 3) + * // -> Ok(2) + * ``` + */ +export function modulo(dividend, divisor) { + if (divisor === 0) { + return new Error(undefined); + } else { + let remainder$1 = remainderInt(dividend, divisor); + let $ = remainder$1 * divisor < 0; + if ($) { + return new Ok(remainder$1 + divisor); + } else { + return new Ok(remainder$1); + } + } +} + +/** + * Performs a *floored* integer division, which means that the result will + * always be rounded towards negative infinity. + * + * If you want to perform truncated integer division (rounding towards zero), + * use `int.divide()` or the `/` operator instead. + * + * Returns division of the inputs as a `Result`: If the given divisor equals + * `0`, this function returns an `Error`. + * + * ## Examples + * + * ```gleam + * floor_divide(1, 0) + * // -> Error(Nil) + * ``` + * + * ```gleam + * floor_divide(5, 2) + * // -> Ok(2) + * ``` + * + * ```gleam + * floor_divide(6, -4) + * // -> Ok(-2) + * ``` + * + * ```gleam + * floor_divide(-99, 2) + * // -> Ok(-50) + * ``` + */ +export function floor_divide(dividend, divisor) { + if (divisor === 0) { + return new Error(undefined); + } else { + let divisor$1 = divisor; + let $ = (dividend * divisor$1 < 0) && ((remainderInt(dividend, divisor$1)) !== 0); + if ($) { + return new Ok((divideInt(dividend, divisor$1)) - 1); + } else { + return new Ok(divideInt(dividend, divisor$1)); + } + } +} + +/** + * Adds two integers together. + * + * It's the function equivalent of the `+` operator. + * This function is useful in higher order functions or pipes. + * + * ## Examples + * + * ```gleam + * add(1, 2) + * // -> 3 + * ``` + * + * ```gleam + * import gleam/list + * list.fold([1, 2, 3], 0, add) + * // -> 6 + * ``` + * + * ```gleam + * 3 |> add(2) + * // -> 5 + * ``` + */ +export function add(a, b) { + return a + b; +} + +/** + * Multiplies two integers together. + * + * It's the function equivalent of the `*` operator. + * This function is useful in higher order functions or pipes. + * + * ## Examples + * + * ```gleam + * multiply(2, 4) + * // -> 8 + * ``` + * + * ```gleam + * import gleam/list + * + * list.fold([2, 3, 4], 1, multiply) + * // -> 24 + * ``` + * + * ```gleam + * 3 |> multiply(2) + * // -> 6 + * ``` + */ +export function multiply(a, b) { + return a * b; +} + +/** + * Subtracts one int from another. + * + * It's the function equivalent of the `-` operator. + * This function is useful in higher order functions or pipes. + * + * ## Examples + * + * ```gleam + * subtract(3, 1) + * // -> 2 + * ``` + * + * ```gleam + * import gleam/list + * + * list.fold([1, 2, 3], 10, subtract) + * // -> 4 + * ``` + * + * ```gleam + * 3 |> subtract(2) + * // -> 1 + * ``` + * + * ```gleam + * 3 |> subtract(2, _) + * // -> -1 + * ``` + */ +export function subtract(a, b) { + return a - b; +} diff --git a/build/dev/javascript/gleam_stdlib/gleam/io.mjs b/build/dev/javascript/gleam_stdlib/gleam/io.mjs new file mode 100644 index 0000000..ddab435 --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam/io.mjs @@ -0,0 +1,8 @@ +import { + print, + print_error, + console_log as println, + console_error as println_error, +} from "../gleam_stdlib.mjs"; + +export { print, print_error, println, println_error }; diff --git a/build/dev/javascript/gleam_stdlib/gleam/list.mjs b/build/dev/javascript/gleam_stdlib/gleam/list.mjs new file mode 100644 index 0000000..2ea158e --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam/list.mjs @@ -0,0 +1,3209 @@ +import { + Ok, + Error, + toList, + Empty as $Empty, + prepend as listPrepend, + CustomType as $CustomType, + makeError, + divideFloat, + isEqual, +} from "../gleam.mjs"; +import * as $dict from "../gleam/dict.mjs"; +import * as $float from "../gleam/float.mjs"; +import * as $int from "../gleam/int.mjs"; +import * as $order from "../gleam/order.mjs"; + +const FILEPATH = "src/gleam/list.gleam"; + +export class Continue extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} +export const ContinueOrStop$Continue = ($0) => new Continue($0); +export const ContinueOrStop$isContinue = (value) => value instanceof Continue; +export const ContinueOrStop$Continue$0 = (value) => value[0]; + +export class Stop extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} +export const ContinueOrStop$Stop = ($0) => new Stop($0); +export const ContinueOrStop$isStop = (value) => value instanceof Stop; +export const ContinueOrStop$Stop$0 = (value) => value[0]; + +class Ascending extends $CustomType {} + +class Descending extends $CustomType {} + +function length_loop(loop$list, loop$count) { + while (true) { + let list = loop$list; + let count = loop$count; + if (list instanceof $Empty) { + return count; + } else { + let list$1 = list.tail; + loop$list = list$1; + loop$count = count + 1; + } + } +} + +/** + * Counts 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 + * + * ```gleam + * length([]) + * // -> 0 + * ``` + * + * ```gleam + * length([1]) + * // -> 1 + * ``` + * + * ```gleam + * length([1, 2]) + * // -> 2 + * ``` + */ +export function length(list) { + return length_loop(list, 0); +} + +function count_loop(loop$list, loop$predicate, loop$acc) { + while (true) { + let list = loop$list; + let predicate = loop$predicate; + let acc = loop$acc; + if (list instanceof $Empty) { + return acc; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = predicate(first$1); + if ($) { + loop$list = rest$1; + loop$predicate = predicate; + loop$acc = acc + 1; + } else { + loop$list = rest$1; + loop$predicate = predicate; + loop$acc = acc; + } + } + } +} + +/** + * Counts the number of elements in a given list satisfying a given predicate. + * + * This function has to traverse the list to determine the number of elements, + * so it runs in linear time. + * + * ## Examples + * + * ```gleam + * count([], fn(a) { a > 0 }) + * // -> 0 + * ``` + * + * ```gleam + * count([1], fn(a) { a > 0 }) + * // -> 1 + * ``` + * + * ```gleam + * count([1, 2, 3], int.is_odd) + * // -> 2 + * ``` + */ +export function count(list, predicate) { + return count_loop(list, predicate, 0); +} + +/** + * Reverses a list and prepends it to another list. + * This function runs in linear time, proportional to the length of the list + * to prepend. + * + * @ignore + */ +function reverse_and_prepend(loop$prefix, loop$suffix) { + while (true) { + let prefix = loop$prefix; + let suffix = loop$suffix; + if (prefix instanceof $Empty) { + return suffix; + } else { + let first$1 = prefix.head; + let rest$1 = prefix.tail; + loop$prefix = rest$1; + loop$suffix = listPrepend(first$1, suffix); + } + } +} + +/** + * Creates 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 + * + * ```gleam + * reverse([]) + * // -> [] + * ``` + * + * ```gleam + * reverse([1]) + * // -> [1] + * ``` + * + * ```gleam + * reverse([1, 2]) + * // -> [2, 1] + * ``` + */ +export function reverse(list) { + return reverse_and_prepend(list, toList([])); +} + +/** + * Determines whether or not the list is empty. + * + * This function runs in constant time. + * + * ## Examples + * + * ```gleam + * is_empty([]) + * // -> True + * ``` + * + * ```gleam + * is_empty([1]) + * // -> False + * ``` + * + * ```gleam + * is_empty([1, 1]) + * // -> False + * ``` + */ +export function is_empty(list) { + return isEqual(list, toList([])); +} + +/** + * Determines 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 + * + * ```gleam + * [] |> contains(any: 0) + * // -> False + * ``` + * + * ```gleam + * [0] |> contains(any: 0) + * // -> True + * ``` + * + * ```gleam + * [1] |> contains(any: 0) + * // -> False + * ``` + * + * ```gleam + * [1, 1] |> contains(any: 0) + * // -> False + * ``` + * + * ```gleam + * [1, 0] |> contains(any: 0) + * // -> True + * ``` + */ +export function contains(loop$list, loop$elem) { + while (true) { + let list = loop$list; + let elem = loop$elem; + if (list instanceof $Empty) { + return false; + } else { + let first$1 = list.head; + if (isEqual(first$1, elem)) { + return true; + } else { + let rest$1 = list.tail; + loop$list = rest$1; + loop$elem = elem; + } + } + } +} + +/** + * Gets the first element from the start of the list, if there is one. + * + * ## Examples + * + * ```gleam + * first([]) + * // -> Error(Nil) + * ``` + * + * ```gleam + * first([0]) + * // -> Ok(0) + * ``` + * + * ```gleam + * first([1, 2]) + * // -> Ok(1) + * ``` + */ +export function first(list) { + if (list instanceof $Empty) { + return new Error(undefined); + } else { + let first$1 = list.head; + return new Ok(first$1); + } +} + +/** + * Returns 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 + * + * ```gleam + * rest([]) + * // -> Error(Nil) + * ``` + * + * ```gleam + * rest([0]) + * // -> Ok([]) + * ``` + * + * ```gleam + * rest([1, 2]) + * // -> Ok([2]) + * ``` + */ +export function rest(list) { + if (list instanceof $Empty) { + return new Error(undefined); + } else { + let rest$1 = list.tail; + return new Ok(rest$1); + } +} + +function group_loop(loop$list, loop$to_key, loop$groups) { + while (true) { + let list = loop$list; + let to_key = loop$to_key; + let groups = loop$groups; + if (list instanceof $Empty) { + return groups; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let key = to_key(first$1); + let _block; + let $ = $dict.get(groups, key); + if ($ instanceof Ok) { + let existing = $[0]; + _block = $dict.insert(groups, key, listPrepend(first$1, existing)); + } else { + _block = $dict.insert(groups, key, toList([first$1])); + } + let groups$1 = _block; + loop$list = rest$1; + loop$to_key = to_key; + loop$groups = groups$1; + } + } +} + +/** + * Groups the elements from the given list by the given key function. + * + * Does not preserve the initial value order. + * + * ## Examples + * + * ```gleam + * import gleam/dict + * + * [Ok(3), Error("Wrong"), Ok(200), Ok(73)] + * |> group(by: fn(i) { + * case i { + * Ok(_) -> "Successful" + * Error(_) -> "Failed" + * } + * }) + * |> dict.to_list + * // -> [ + * // #("Failed", [Error("Wrong")]), + * // #("Successful", [Ok(73), Ok(200), Ok(3)]) + * // ] + * ``` + * + * ```gleam + * import gleam/dict + * + * group([1,2,3,4,5], by: fn(i) { i - i / 3 * 3 }) + * |> dict.to_list + * // -> [#(0, [3]), #(1, [4, 1]), #(2, [5, 2])] + * ``` + */ +export function group(list, key) { + return group_loop(list, key, $dict.new$()); +} + +function filter_loop(loop$list, loop$fun, loop$acc) { + while (true) { + let list = loop$list; + let fun = loop$fun; + let acc = loop$acc; + if (list instanceof $Empty) { + return reverse(acc); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let _block; + let $ = fun(first$1); + if ($) { + _block = listPrepend(first$1, acc); + } else { + _block = acc; + } + let new_acc = _block; + loop$list = rest$1; + loop$fun = fun; + loop$acc = new_acc; + } + } +} + +/** + * Returns a new list containing only the elements from the first list for + * which the given functions returns `True`. + * + * ## Examples + * + * ```gleam + * filter([2, 4, 6, 1], fn(x) { x > 2 }) + * // -> [4, 6] + * ``` + * + * ```gleam + * filter([2, 4, 6, 1], fn(x) { x > 6 }) + * // -> [] + * ``` + */ +export function filter(list, predicate) { + return filter_loop(list, predicate, toList([])); +} + +function filter_map_loop(loop$list, loop$fun, loop$acc) { + while (true) { + let list = loop$list; + let fun = loop$fun; + let acc = loop$acc; + if (list instanceof $Empty) { + return reverse(acc); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let _block; + let $ = fun(first$1); + if ($ instanceof Ok) { + let first$2 = $[0]; + _block = listPrepend(first$2, acc); + } else { + _block = acc; + } + let new_acc = _block; + loop$list = rest$1; + loop$fun = fun; + loop$acc = new_acc; + } + } +} + +/** + * Returns a new list containing only the elements from the first list for + * which the given functions returns `Ok(_)`. + * + * ## Examples + * + * ```gleam + * filter_map([2, 4, 6, 1], Error) + * // -> [] + * ``` + * + * ```gleam + * filter_map([2, 4, 6, 1], fn(x) { Ok(x + 1) }) + * // -> [3, 5, 7, 2] + * ``` + */ +export function filter_map(list, fun) { + return filter_map_loop(list, fun, toList([])); +} + +function map_loop(loop$list, loop$fun, loop$acc) { + while (true) { + let list = loop$list; + let fun = loop$fun; + let acc = loop$acc; + if (list instanceof $Empty) { + return reverse(acc); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + loop$list = rest$1; + loop$fun = fun; + loop$acc = listPrepend(fun(first$1), acc); + } + } +} + +/** + * Returns a new list containing only the elements of the first list after the + * function has been applied to each one. + * + * ## Examples + * + * ```gleam + * map([2, 4, 6], fn(x) { x * 2 }) + * // -> [4, 8, 12] + * ``` + */ +export function map(list, fun) { + return map_loop(list, fun, toList([])); +} + +function map2_loop(loop$list1, loop$list2, loop$fun, loop$acc) { + while (true) { + let list1 = loop$list1; + let list2 = loop$list2; + let fun = loop$fun; + let acc = loop$acc; + if (list1 instanceof $Empty) { + return reverse(acc); + } else if (list2 instanceof $Empty) { + return reverse(acc); + } else { + let a = list1.head; + let as_ = list1.tail; + let b = list2.head; + let bs = list2.tail; + loop$list1 = as_; + loop$list2 = bs; + loop$fun = fun; + loop$acc = listPrepend(fun(a, b), acc); + } + } +} + +/** + * Combines two lists into a single list using the given function. + * + * If a list is longer than the other the extra elements are dropped. + * + * ## Examples + * + * ```gleam + * map2([1, 2, 3], [4, 5, 6], fn(x, y) { x + y }) + * // -> [5, 7, 9] + * ``` + * + * ```gleam + * map2([1, 2], ["a", "b", "c"], fn(i, x) { #(i, x) }) + * // -> [#(1, "a"), #(2, "b")] + * ``` + */ +export function map2(list1, list2, fun) { + return map2_loop(list1, list2, fun, toList([])); +} + +function map_fold_loop(loop$list, loop$fun, loop$acc, loop$list_acc) { + while (true) { + let list = loop$list; + let fun = loop$fun; + let acc = loop$acc; + let list_acc = loop$list_acc; + if (list instanceof $Empty) { + return [acc, reverse(list_acc)]; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = fun(acc, first$1); + let acc$1; + let first$2; + acc$1 = $[0]; + first$2 = $[1]; + loop$list = rest$1; + loop$fun = fun; + loop$acc = acc$1; + loop$list_acc = listPrepend(first$2, list_acc); + } + } +} + +/** + * Similar to `map` but also lets you pass around an accumulated value. + * + * ## Examples + * + * ```gleam + * map_fold( + * over: [1, 2, 3], + * from: 100, + * with: fn(memo, i) { #(memo + i, i * 2) } + * ) + * // -> #(106, [2, 4, 6]) + * ``` + */ +export function map_fold(list, initial, fun) { + return map_fold_loop(list, fun, initial, toList([])); +} + +function index_map_loop(loop$list, loop$fun, loop$index, loop$acc) { + while (true) { + let list = loop$list; + let fun = loop$fun; + let index = loop$index; + let acc = loop$acc; + if (list instanceof $Empty) { + return reverse(acc); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let acc$1 = listPrepend(fun(first$1, index), acc); + loop$list = rest$1; + loop$fun = fun; + loop$index = index + 1; + loop$acc = acc$1; + } + } +} + +/** + * 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 + * + * ```gleam + * index_map(["a", "b"], fn(x, i) { #(i, x) }) + * // -> [#(0, "a"), #(1, "b")] + * ``` + */ +export function index_map(list, fun) { + return index_map_loop(list, fun, 0, toList([])); +} + +function try_map_loop(loop$list, loop$fun, loop$acc) { + while (true) { + let list = loop$list; + let fun = loop$fun; + let acc = loop$acc; + if (list instanceof $Empty) { + return new Ok(reverse(acc)); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = fun(first$1); + if ($ instanceof Ok) { + let first$2 = $[0]; + loop$list = rest$1; + loop$fun = fun; + loop$acc = listPrepend(first$2, acc); + } else { + return $; + } + } + } +} + +/** + * Takes a function that returns a `Result` and applies it to each element in a + * given list in turn. + * + * 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 + * + * ```gleam + * try_map([1, 2, 3], fn(x) { Ok(x + 2) }) + * // -> Ok([3, 4, 5]) + * ``` + * + * ```gleam + * try_map([1, 2, 3], fn(_) { Error(0) }) + * // -> Error(0) + * ``` + * + * ```gleam + * try_map([[1], [2, 3]], first) + * // -> Ok([1, 2]) + * ``` + * + * ```gleam + * try_map([[1], [], [2]], first) + * // -> Error(Nil) + * ``` + */ +export function try_map(list, fun) { + return try_map_loop(list, fun, toList([])); +} + +/** + * 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 + * + * ```gleam + * drop([1, 2, 3, 4], 2) + * // -> [3, 4] + * ``` + * + * ```gleam + * drop([1, 2, 3, 4], 9) + * // -> [] + * ``` + */ +export function drop(loop$list, loop$n) { + while (true) { + let list = loop$list; + let n = loop$n; + let $ = n <= 0; + if ($) { + return list; + } else { + if (list instanceof $Empty) { + return list; + } else { + let rest$1 = list.tail; + loop$list = rest$1; + loop$n = n - 1; + } + } + } +} + +function take_loop(loop$list, loop$n, loop$acc) { + while (true) { + let list = loop$list; + let n = loop$n; + let acc = loop$acc; + let $ = n <= 0; + if ($) { + return reverse(acc); + } else { + if (list instanceof $Empty) { + return reverse(acc); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + loop$list = rest$1; + loop$n = n - 1; + loop$acc = listPrepend(first$1, 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. + * + * ## Examples + * + * ```gleam + * take([1, 2, 3, 4], 2) + * // -> [1, 2] + * ``` + * + * ```gleam + * take([1, 2, 3, 4], 9) + * // -> [1, 2, 3, 4] + * ``` + */ +export function take(list, n) { + return take_loop(list, n, toList([])); +} + +/** + * Returns a new empty list. + * + * ## Examples + * + * ```gleam + * new() + * // -> [] + * ``` + */ +export function new$() { + return toList([]); +} + +/** + * Returns the given item wrapped in a list. + * + * ## Examples + * + * ```gleam + * wrap(1) + * // -> [1] + * + * wrap(["a", "b", "c"]) + * // -> [["a", "b", "c"]] + * + * wrap([[]]) + * // -> [[[]]] + * ``` + */ +export function wrap(item) { + return toList([item]); +} + +function append_loop(loop$first, loop$second) { + while (true) { + let first = loop$first; + let second = loop$second; + if (first instanceof $Empty) { + return second; + } else { + let first$1 = first.head; + let rest$1 = first.tail; + loop$first = rest$1; + loop$second = listPrepend(first$1, second); + } + } +} + +/** + * Joins one list onto the end of another. + * + * This function runs in linear time, and it traverses and copies the first + * list. + * + * ## Examples + * + * ```gleam + * append([1, 2], [3]) + * // -> [1, 2, 3] + * ``` + */ +export function append(first, second) { + return append_loop(reverse(first), second); +} + +/** + * Prefixes an item to a list. This can also be done using the dedicated + * syntax instead + * + * ```gleam + * let existing_list = [2, 3, 4] + * + * [1, ..existing_list] + * // -> [1, 2, 3, 4] + * + * prepend(to: existing_list, this: 1) + * // -> [1, 2, 3, 4] + * ``` + */ +export function prepend(list, item) { + return listPrepend(item, list); +} + +function flatten_loop(loop$lists, loop$acc) { + while (true) { + let lists = loop$lists; + let acc = loop$acc; + if (lists instanceof $Empty) { + return reverse(acc); + } else { + let list = lists.head; + let further_lists = lists.tail; + loop$lists = further_lists; + loop$acc = reverse_and_prepend(list, acc); + } + } +} + +/** + * Joins a list of lists into a single list. + * + * This function traverses all elements twice on the JavaScript target. + * This function traverses all elements once on the Erlang target. + * + * ## Examples + * + * ```gleam + * flatten([[1], [2, 3], []]) + * // -> [1, 2, 3] + * ``` + */ +export function flatten(lists) { + return flatten_loop(lists, toList([])); +} + +/** + * Maps the list with the given function into a list of lists, and then flattens it. + * + * ## Examples + * + * ```gleam + * flat_map([2, 4, 6], fn(x) { [x, x + 1] }) + * // -> [2, 3, 4, 5, 6, 7] + * ``` + */ +export function flat_map(list, fun) { + return flatten(map(list, fun)); +} + +/** + * Reduces 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(add(add(0, 1), 2), 3)`. + * + * This function runs in linear time. + */ +export function fold(loop$list, loop$initial, loop$fun) { + while (true) { + let list = loop$list; + let initial = loop$initial; + let fun = loop$fun; + if (list instanceof $Empty) { + return initial; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + loop$list = rest$1; + loop$initial = fun(initial, first$1); + loop$fun = fun; + } + } +} + +/** + * Reduces 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(add(add(0, 3), 2), 1)`. + * + * 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. + */ +export function fold_right(list, initial, fun) { + if (list instanceof $Empty) { + return initial; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + return fun(fold_right(rest$1, initial, fun), first$1); + } +} + +function index_fold_loop(loop$over, loop$acc, loop$with, loop$index) { + while (true) { + let over = loop$over; + let acc = loop$acc; + let with$ = loop$with; + let index = loop$index; + if (over instanceof $Empty) { + return acc; + } else { + let first$1 = over.head; + let rest$1 = over.tail; + loop$over = rest$1; + loop$acc = with$(acc, first$1, index); + loop$with = with$; + loop$index = index + 1; + } + } +} + +/** + * Like fold but the folding function also receives the index of the current element. + * + * ## Examples + * + * ```gleam + * ["a", "b", "c"] + * |> index_fold("", fn(acc, item, index) { + * acc <> int.to_string(index) <> ":" <> item <> " " + * }) + * // -> "0:a 1:b 2:c" + * ``` + * + * ```gleam + * [10, 20, 30] + * |> index_fold(0, fn(acc, item, index) { acc + item * index }) + * // -> 80 + * ``` + */ +export function index_fold(list, initial, fun) { + return index_fold_loop(list, 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 + * + * ```gleam + * [1, 2, 3, 4] + * |> try_fold(0, fn(acc, i) { + * case i < 3 { + * True -> Ok(acc + i) + * False -> Error(Nil) + * } + * }) + * // -> Error(Nil) + * ``` + */ +export function try_fold(loop$list, loop$initial, loop$fun) { + while (true) { + let list = loop$list; + let initial = loop$initial; + let fun = loop$fun; + if (list instanceof $Empty) { + return new Ok(initial); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = fun(initial, first$1); + if ($ instanceof Ok) { + let result = $[0]; + loop$list = rest$1; + loop$initial = result; + loop$fun = fun; + } else { + return $; + } + } + } +} + +/** + * A variant of fold that allows to stop folding earlier. + * + * The folding function should return `ContinueOrStop(accumulator)`. + * If the returned value is `Continue(accumulator)` fold_until will try the next value in the list. + * If the returned value is `Stop(accumulator)` fold_until will stop and return that accumulator. + * + * ## Examples + * + * ```gleam + * [1, 2, 3, 4] + * |> fold_until(0, fn(acc, i) { + * case i < 3 { + * True -> Continue(acc + i) + * False -> Stop(acc) + * } + * }) + * // -> 3 + * ``` + */ +export function fold_until(loop$list, loop$initial, loop$fun) { + while (true) { + let list = loop$list; + let initial = loop$initial; + let fun = loop$fun; + if (list instanceof $Empty) { + return initial; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = fun(initial, first$1); + if ($ instanceof Continue) { + let next_accumulator = $[0]; + loop$list = rest$1; + loop$initial = next_accumulator; + loop$fun = fun; + } else { + let b = $[0]; + return b; + } + } + } +} + +/** + * Finds the first element in a given list for which the given function returns + * `True`. + * + * Returns `Error(Nil)` if no such element is found. + * + * ## Examples + * + * ```gleam + * find([1, 2, 3], fn(x) { x > 2 }) + * // -> Ok(3) + * ``` + * + * ```gleam + * find([1, 2, 3], fn(x) { x > 4 }) + * // -> Error(Nil) + * ``` + * + * ```gleam + * find([], fn(_) { True }) + * // -> Error(Nil) + * ``` + */ +export function find(loop$list, loop$is_desired) { + while (true) { + let list = loop$list; + let is_desired = loop$is_desired; + if (list instanceof $Empty) { + return new Error(undefined); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = is_desired(first$1); + if ($) { + return new Ok(first$1); + } else { + loop$list = rest$1; + loop$is_desired = is_desired; + } + } + } +} + +/** + * Finds the first element in a given list for which the given function returns + * `Ok(new_value)`, then returns the wrapped `new_value`. + * + * Returns `Error(Nil)` if no such element is found. + * + * ## Examples + * + * ```gleam + * find_map([[], [2], [3]], first) + * // -> Ok(2) + * ``` + * + * ```gleam + * find_map([[], []], first) + * // -> Error(Nil) + * ``` + * + * ```gleam + * find_map([], first) + * // -> Error(Nil) + * ``` + */ +export function find_map(loop$list, loop$fun) { + while (true) { + let list = loop$list; + let fun = loop$fun; + if (list instanceof $Empty) { + return new Error(undefined); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = fun(first$1); + if ($ instanceof Ok) { + return $; + } else { + loop$list = rest$1; + loop$fun = 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 + * + * ```gleam + * all([], fn(x) { x > 3 }) + * // -> True + * ``` + * + * ```gleam + * all([4, 5], fn(x) { x > 3 }) + * // -> True + * ``` + * + * ```gleam + * all([4, 3], fn(x) { x > 3 }) + * // -> False + * ``` + */ +export function all(loop$list, loop$predicate) { + while (true) { + let list = loop$list; + let predicate = loop$predicate; + if (list instanceof $Empty) { + return true; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = predicate(first$1); + if ($) { + loop$list = rest$1; + loop$predicate = predicate; + } else { + return $; + } + } + } +} + +/** + * 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 + * + * ```gleam + * any([], fn(x) { x > 3 }) + * // -> False + * ``` + * + * ```gleam + * any([4, 5], fn(x) { x > 3 }) + * // -> True + * ``` + * + * ```gleam + * any([4, 3], fn(x) { x > 4 }) + * // -> False + * ``` + * + * ```gleam + * any([3, 4], fn(x) { x > 3 }) + * // -> True + * ``` + */ +export function any(loop$list, loop$predicate) { + while (true) { + let list = loop$list; + let predicate = loop$predicate; + if (list instanceof $Empty) { + return false; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = predicate(first$1); + if ($) { + return $; + } else { + loop$list = rest$1; + loop$predicate = predicate; + } + } + } +} + +function zip_loop(loop$one, loop$other, loop$acc) { + while (true) { + let one = loop$one; + let other = loop$other; + let acc = loop$acc; + if (one instanceof $Empty) { + return reverse(acc); + } else if (other instanceof $Empty) { + return reverse(acc); + } else { + let first_one = one.head; + let rest_one = one.tail; + let first_other = other.head; + let rest_other = other.tail; + loop$one = rest_one; + loop$other = rest_other; + loop$acc = listPrepend([first_one, first_other], acc); + } + } +} + +/** + * Takes two lists and returns a single list of 2-element tuples. + * + * If one of the lists is longer than the other, the remaining elements from + * the longer list are not used. + * + * ## Examples + * + * ```gleam + * zip([], []) + * // -> [] + * ``` + * + * ```gleam + * zip([1, 2], [3]) + * // -> [#(1, 3)] + * ``` + * + * ```gleam + * zip([1], [3, 4]) + * // -> [#(1, 3)] + * ``` + * + * ```gleam + * zip([1, 2], [3, 4]) + * // -> [#(1, 3), #(2, 4)] + * ``` + */ +export function zip(list, other) { + return zip_loop(list, other, toList([])); +} + +function strict_zip_loop(loop$one, loop$other, loop$acc) { + while (true) { + let one = loop$one; + let other = loop$other; + let acc = loop$acc; + if (one instanceof $Empty) { + if (other instanceof $Empty) { + return new Ok(reverse(acc)); + } else { + return new Error(undefined); + } + } else if (other instanceof $Empty) { + return new Error(undefined); + } else { + let first_one = one.head; + let rest_one = one.tail; + let first_other = other.head; + let rest_other = other.tail; + loop$one = rest_one; + loop$other = rest_other; + loop$acc = listPrepend([first_one, first_other], acc); + } + } +} + +/** + * Takes two lists and returns a single list of 2-element tuples. + * + * If one of the lists is longer than the other, an `Error` is returned. + * + * ## Examples + * + * ```gleam + * strict_zip([], []) + * // -> Ok([]) + * ``` + * + * ```gleam + * strict_zip([1, 2], [3]) + * // -> Error(Nil) + * ``` + * + * ```gleam + * strict_zip([1], [3, 4]) + * // -> Error(Nil) + * ``` + * + * ```gleam + * strict_zip([1, 2], [3, 4]) + * // -> Ok([#(1, 3), #(2, 4)]) + * ``` + */ +export function strict_zip(list, other) { + return strict_zip_loop(list, other, toList([])); +} + +function unzip_loop(loop$input, loop$one, loop$other) { + while (true) { + let input = loop$input; + let one = loop$one; + let other = loop$other; + if (input instanceof $Empty) { + return [reverse(one), reverse(other)]; + } else { + let rest$1 = input.tail; + let first_one = input.head[0]; + let first_other = input.head[1]; + loop$input = rest$1; + loop$one = listPrepend(first_one, one); + loop$other = listPrepend(first_other, other); + } + } +} + +/** + * Takes a single list of 2-element tuples and returns two lists. + * + * ## Examples + * + * ```gleam + * unzip([#(1, 2), #(3, 4)]) + * // -> #([1, 3], [2, 4]) + * ``` + * + * ```gleam + * unzip([]) + * // -> #([], []) + * ``` + */ +export function unzip(input) { + return unzip_loop(input, toList([]), toList([])); +} + +function intersperse_loop(loop$list, loop$separator, loop$acc) { + while (true) { + let list = loop$list; + let separator = loop$separator; + let acc = loop$acc; + if (list instanceof $Empty) { + return reverse(acc); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + loop$list = rest$1; + loop$separator = separator; + loop$acc = listPrepend(first$1, listPrepend(separator, acc)); + } + } +} + +/** + * Inserts a given value between each existing element in a given list. + * + * This function runs in linear time and copies the list. + * + * ## Examples + * + * ```gleam + * intersperse([1, 1, 1], 2) + * // -> [1, 2, 1, 2, 1] + * ``` + * + * ```gleam + * intersperse([], 2) + * // -> [] + * ``` + */ +export function intersperse(list, elem) { + if (list instanceof $Empty) { + return list; + } else { + let $ = list.tail; + if ($ instanceof $Empty) { + return list; + } else { + let first$1 = list.head; + let rest$1 = $; + return intersperse_loop(rest$1, elem, toList([first$1])); + } + } +} + +function unique_loop(loop$list, loop$seen, loop$acc) { + while (true) { + let list = loop$list; + let seen = loop$seen; + let acc = loop$acc; + if (list instanceof $Empty) { + return reverse(acc); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = $dict.has_key(seen, first$1); + if ($) { + loop$list = rest$1; + loop$seen = seen; + loop$acc = acc; + } else { + loop$list = rest$1; + loop$seen = $dict.insert(seen, first$1, undefined); + loop$acc = listPrepend(first$1, acc); + } + } + } +} + +/** + * Removes any duplicate elements from a given list. + * + * This function returns in loglinear time. + * + * ## Examples + * + * ```gleam + * unique([1, 1, 1, 4, 7, 3, 3, 4]) + * // -> [1, 4, 7, 3] + * ``` + */ +export function unique(list) { + return unique_loop(list, $dict.new$(), toList([])); +} + +/** + * Given a list it returns slices of it that are locally sorted in ascending + * order. + * + * Imagine you have this list: + * + * ``` + * [1, 2, 3, 2, 1, 0] + * ^^^^^^^ ^^^^^^^ This is a slice in descending order + * | + * | This is a slice that is sorted in ascending order + * ``` + * + * So the produced result will contain these two slices, each one sorted in + * ascending order: `[[1, 2, 3], [0, 1, 2]]`. + * + * - `growing` is an accumulator with the current slice being grown + * - `direction` is the growing direction of the slice being grown, it could + * either be ascending or strictly descending + * - `prev` is the previous element that needs to be added to the growing slice + * it is carried around to check whether we have to keep growing the current + * slice or not + * - `acc` is the accumulator containing the slices sorted in ascending order + * + * @ignore + */ +function sequences( + loop$list, + loop$compare, + loop$growing, + loop$direction, + loop$prev, + loop$acc +) { + while (true) { + let list = loop$list; + let compare = loop$compare; + let growing = loop$growing; + let direction = loop$direction; + let prev = loop$prev; + let acc = loop$acc; + let growing$1 = listPrepend(prev, growing); + if (list instanceof $Empty) { + if (direction instanceof Ascending) { + return listPrepend(reverse(growing$1), acc); + } else { + return listPrepend(growing$1, acc); + } + } else { + let new$1 = list.head; + let rest$1 = list.tail; + let $ = compare(prev, new$1); + if (direction instanceof Ascending) { + if ($ instanceof $order.Lt) { + loop$list = rest$1; + loop$compare = compare; + loop$growing = growing$1; + loop$direction = direction; + loop$prev = new$1; + loop$acc = acc; + } else if ($ instanceof $order.Eq) { + loop$list = rest$1; + loop$compare = compare; + loop$growing = growing$1; + loop$direction = direction; + loop$prev = new$1; + loop$acc = acc; + } else { + let _block; + if (direction instanceof Ascending) { + _block = listPrepend(reverse(growing$1), acc); + } else { + _block = listPrepend(growing$1, acc); + } + let acc$1 = _block; + if (rest$1 instanceof $Empty) { + return listPrepend(toList([new$1]), acc$1); + } else { + let next = rest$1.head; + let rest$2 = rest$1.tail; + let _block$1; + let $1 = compare(new$1, next); + if ($1 instanceof $order.Lt) { + _block$1 = new Ascending(); + } else if ($1 instanceof $order.Eq) { + _block$1 = new Ascending(); + } else { + _block$1 = new Descending(); + } + let direction$1 = _block$1; + loop$list = rest$2; + loop$compare = compare; + loop$growing = toList([new$1]); + loop$direction = direction$1; + loop$prev = next; + loop$acc = acc$1; + } + } + } else if ($ instanceof $order.Lt) { + let _block; + if (direction instanceof Ascending) { + _block = listPrepend(reverse(growing$1), acc); + } else { + _block = listPrepend(growing$1, acc); + } + let acc$1 = _block; + if (rest$1 instanceof $Empty) { + return listPrepend(toList([new$1]), acc$1); + } else { + let next = rest$1.head; + let rest$2 = rest$1.tail; + let _block$1; + let $1 = compare(new$1, next); + if ($1 instanceof $order.Lt) { + _block$1 = new Ascending(); + } else if ($1 instanceof $order.Eq) { + _block$1 = new Ascending(); + } else { + _block$1 = new Descending(); + } + let direction$1 = _block$1; + loop$list = rest$2; + loop$compare = compare; + loop$growing = toList([new$1]); + loop$direction = direction$1; + loop$prev = next; + loop$acc = acc$1; + } + } else if ($ instanceof $order.Eq) { + let _block; + if (direction instanceof Ascending) { + _block = listPrepend(reverse(growing$1), acc); + } else { + _block = listPrepend(growing$1, acc); + } + let acc$1 = _block; + if (rest$1 instanceof $Empty) { + return listPrepend(toList([new$1]), acc$1); + } else { + let next = rest$1.head; + let rest$2 = rest$1.tail; + let _block$1; + let $1 = compare(new$1, next); + if ($1 instanceof $order.Lt) { + _block$1 = new Ascending(); + } else if ($1 instanceof $order.Eq) { + _block$1 = new Ascending(); + } else { + _block$1 = new Descending(); + } + let direction$1 = _block$1; + loop$list = rest$2; + loop$compare = compare; + loop$growing = toList([new$1]); + loop$direction = direction$1; + loop$prev = next; + loop$acc = acc$1; + } + } else { + loop$list = rest$1; + loop$compare = compare; + loop$growing = growing$1; + loop$direction = direction; + loop$prev = new$1; + loop$acc = acc; + } + } + } +} + +/** + * Merges two lists sorted in ascending order into a single list sorted in + * descending order according to the given comparator function. + * + * This reversing of the sort order is not avoidable if we want to implement + * merge as a tail recursive function. We could reverse the accumulator before + * returning it but that would end up being less efficient; so the merging + * algorithm has to play around this. + * + * @ignore + */ +function merge_ascendings(loop$list1, loop$list2, loop$compare, loop$acc) { + while (true) { + let list1 = loop$list1; + let list2 = loop$list2; + let compare = loop$compare; + let acc = loop$acc; + if (list1 instanceof $Empty) { + let list = list2; + return reverse_and_prepend(list, acc); + } else if (list2 instanceof $Empty) { + let list = list1; + return reverse_and_prepend(list, acc); + } else { + let first1 = list1.head; + let rest1 = list1.tail; + let first2 = list2.head; + let rest2 = list2.tail; + let $ = compare(first1, first2); + if ($ instanceof $order.Lt) { + loop$list1 = rest1; + loop$list2 = list2; + loop$compare = compare; + loop$acc = listPrepend(first1, acc); + } else if ($ instanceof $order.Eq) { + loop$list1 = list1; + loop$list2 = rest2; + loop$compare = compare; + loop$acc = listPrepend(first2, acc); + } else { + loop$list1 = list1; + loop$list2 = rest2; + loop$compare = compare; + loop$acc = listPrepend(first2, acc); + } + } + } +} + +/** + * Given a list of ascending lists, it merges adjacent pairs into a single + * descending list, halving their number. + * It returns a list of the remaining descending lists. + * + * @ignore + */ +function merge_ascending_pairs(loop$sequences, loop$compare, loop$acc) { + while (true) { + let sequences = loop$sequences; + let compare = loop$compare; + let acc = loop$acc; + if (sequences instanceof $Empty) { + return reverse(acc); + } else { + let $ = sequences.tail; + if ($ instanceof $Empty) { + let sequence = sequences.head; + return reverse(listPrepend(reverse(sequence), acc)); + } else { + let ascending1 = sequences.head; + let ascending2 = $.head; + let rest$1 = $.tail; + let descending = merge_ascendings( + ascending1, + ascending2, + compare, + toList([]), + ); + loop$sequences = rest$1; + loop$compare = compare; + loop$acc = listPrepend(descending, acc); + } + } + } +} + +/** + * This is exactly the same as merge_ascendings but mirrored: it merges two + * lists sorted in descending order into a single list sorted in ascending + * order according to the given comparator function. + * + * This reversing of the sort order is not avoidable if we want to implement + * merge as a tail recursive function. We could reverse the accumulator before + * returning it but that would end up being less efficient; so the merging + * algorithm has to play around this. + * + * @ignore + */ +function merge_descendings(loop$list1, loop$list2, loop$compare, loop$acc) { + while (true) { + let list1 = loop$list1; + let list2 = loop$list2; + let compare = loop$compare; + let acc = loop$acc; + if (list1 instanceof $Empty) { + let list = list2; + return reverse_and_prepend(list, acc); + } else if (list2 instanceof $Empty) { + let list = list1; + return reverse_and_prepend(list, acc); + } else { + let first1 = list1.head; + let rest1 = list1.tail; + let first2 = list2.head; + let rest2 = list2.tail; + let $ = compare(first1, first2); + if ($ instanceof $order.Lt) { + loop$list1 = list1; + loop$list2 = rest2; + loop$compare = compare; + loop$acc = listPrepend(first2, acc); + } else if ($ instanceof $order.Eq) { + loop$list1 = rest1; + loop$list2 = list2; + loop$compare = compare; + loop$acc = listPrepend(first1, acc); + } else { + loop$list1 = rest1; + loop$list2 = list2; + loop$compare = compare; + loop$acc = listPrepend(first1, acc); + } + } + } +} + +/** + * This is the same as merge_ascending_pairs but flipped for descending lists. + * + * @ignore + */ +function merge_descending_pairs(loop$sequences, loop$compare, loop$acc) { + while (true) { + let sequences = loop$sequences; + let compare = loop$compare; + let acc = loop$acc; + if (sequences instanceof $Empty) { + return reverse(acc); + } else { + let $ = sequences.tail; + if ($ instanceof $Empty) { + let sequence = sequences.head; + return reverse(listPrepend(reverse(sequence), acc)); + } else { + let descending1 = sequences.head; + let descending2 = $.head; + let rest$1 = $.tail; + let ascending = merge_descendings( + descending1, + descending2, + compare, + toList([]), + ); + loop$sequences = rest$1; + loop$compare = compare; + loop$acc = listPrepend(ascending, acc); + } + } + } +} + +/** + * Given some some sorted sequences (assumed to be sorted in `direction`) it + * merges them all together until we're left with just a list sorted in + * ascending order. + * + * @ignore + */ +function merge_all(loop$sequences, loop$direction, loop$compare) { + while (true) { + let sequences = loop$sequences; + let direction = loop$direction; + let compare = loop$compare; + if (sequences instanceof $Empty) { + return sequences; + } else if (direction instanceof Ascending) { + let $ = sequences.tail; + if ($ instanceof $Empty) { + let sequence = sequences.head; + return sequence; + } else { + let sequences$1 = merge_ascending_pairs(sequences, compare, toList([])); + loop$sequences = sequences$1; + loop$direction = new Descending(); + loop$compare = compare; + } + } else { + let $ = sequences.tail; + if ($ instanceof $Empty) { + let sequence = sequences.head; + return reverse(sequence); + } else { + let sequences$1 = merge_descending_pairs(sequences, compare, toList([])); + loop$sequences = sequences$1; + loop$direction = new Ascending(); + loop$compare = compare; + } + } + } +} + +/** + * Sorts from smallest to largest based upon the ordering specified by a given + * function. + * + * ## Examples + * + * ```gleam + * import gleam/int + * + * sort([4, 3, 6, 5, 4, 1, 2], by: int.compare) + * // -> [1, 2, 3, 4, 4, 5, 6] + * ``` + */ +export function sort(list, compare) { + if (list instanceof $Empty) { + return list; + } else { + let $ = list.tail; + if ($ instanceof $Empty) { + return list; + } else { + let x = list.head; + let y = $.head; + let rest$1 = $.tail; + let _block; + let $1 = compare(x, y); + if ($1 instanceof $order.Lt) { + _block = new Ascending(); + } else if ($1 instanceof $order.Eq) { + _block = new Ascending(); + } else { + _block = new Descending(); + } + let direction = _block; + let sequences$1 = sequences( + rest$1, + compare, + toList([x]), + direction, + y, + toList([]), + ); + return merge_all(sequences$1, new Ascending(), compare); + } + } +} + +function range_loop(loop$start, loop$stop, loop$acc) { + while (true) { + let start = loop$start; + let stop = loop$stop; + let acc = loop$acc; + let $ = $int.compare(start, stop); + if ($ instanceof $order.Lt) { + loop$start = start; + loop$stop = stop - 1; + loop$acc = listPrepend(stop, acc); + } else if ($ instanceof $order.Eq) { + return listPrepend(stop, acc); + } else { + loop$start = start; + loop$stop = stop + 1; + loop$acc = listPrepend(stop, acc); + } + } +} + +/** + * Creates a list of ints ranging from a given start and finish. + * + * ## Examples + * + * ```gleam + * range(0, 0) + * // -> [0] + * ``` + * + * ```gleam + * range(0, 5) + * // -> [0, 1, 2, 3, 4, 5] + * ``` + * + * ```gleam + * range(1, -5) + * // -> [1, 0, -1, -2, -3, -4, -5] + * ``` + */ +export function range(start, stop) { + return range_loop(start, stop, toList([])); +} + +function repeat_loop(loop$item, loop$times, loop$acc) { + while (true) { + let item = loop$item; + let times = loop$times; + let acc = loop$acc; + let $ = times <= 0; + if ($) { + return acc; + } else { + loop$item = item; + loop$times = times - 1; + loop$acc = listPrepend(item, acc); + } + } +} + +/** + * Builds a list of a given value a given number of times. + * + * ## Examples + * + * ```gleam + * repeat("a", times: 0) + * // -> [] + * ``` + * + * ```gleam + * repeat("a", times: 5) + * // -> ["a", "a", "a", "a", "a"] + * ``` + */ +export function repeat(a, times) { + return repeat_loop(a, times, toList([])); +} + +function split_loop(loop$list, loop$n, loop$taken) { + while (true) { + let list = loop$list; + let n = loop$n; + let taken = loop$taken; + let $ = n <= 0; + if ($) { + return [reverse(taken), list]; + } else { + if (list instanceof $Empty) { + return [reverse(taken), toList([])]; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + loop$list = rest$1; + loop$n = n - 1; + loop$taken = listPrepend(first$1, taken); + } + } + } +} + +/** + * Splits 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 + * + * ```gleam + * split([6, 7, 8, 9], 0) + * // -> #([], [6, 7, 8, 9]) + * ``` + * + * ```gleam + * split([6, 7, 8, 9], 2) + * // -> #([6, 7], [8, 9]) + * ``` + * + * ```gleam + * split([6, 7, 8, 9], 4) + * // -> #([6, 7, 8, 9], []) + * ``` + */ +export function split(list, index) { + return split_loop(list, index, toList([])); +} + +function split_while_loop(loop$list, loop$f, loop$acc) { + while (true) { + let list = loop$list; + let f = loop$f; + let acc = loop$acc; + if (list instanceof $Empty) { + return [reverse(acc), toList([])]; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = f(first$1); + if ($) { + loop$list = rest$1; + loop$f = f; + loop$acc = listPrepend(first$1, acc); + } else { + return [reverse(acc), list]; + } + } + } +} + +/** + * Splits 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 + * + * ```gleam + * split_while([1, 2, 3, 4, 5], fn(x) { x <= 3 }) + * // -> #([1, 2, 3], [4, 5]) + * ``` + * + * ```gleam + * split_while([1, 2, 3, 4, 5], fn(x) { x <= 5 }) + * // -> #([1, 2, 3, 4, 5], []) + * ``` + */ +export function split_while(list, predicate) { + return split_while_loop(list, predicate, toList([])); +} + +/** + * Given a list of 2-element tuples, finds the first tuple that has a given + * key as the first element and returns 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 + * + * ```gleam + * key_find([#("a", 0), #("b", 1)], "a") + * // -> Ok(0) + * ``` + * + * ```gleam + * key_find([#("a", 0), #("b", 1)], "b") + * // -> Ok(1) + * ``` + * + * ```gleam + * key_find([#("a", 0), #("b", 1)], "c") + * // -> Error(Nil) + * ``` + */ +export function key_find(keyword_list, desired_key) { + return find_map( + keyword_list, + (keyword) => { + let key; + let value; + key = keyword[0]; + value = keyword[1]; + let $ = isEqual(key, desired_key); + if ($) { + return new Ok(value); + } else { + return new Error(undefined); + } + }, + ); +} + +/** + * Given a list of 2-element tuples, finds all tuples that have a given + * key as the first element and returns the second element. + * + * This function may be useful for interacting with Erlang code where lists of + * tuples are common. + * + * ## Examples + * + * ```gleam + * key_filter([#("a", 0), #("b", 1), #("a", 2)], "a") + * // -> [0, 2] + * ``` + * + * ```gleam + * key_filter([#("a", 0), #("b", 1)], "c") + * // -> [] + * ``` + */ +export function key_filter(keyword_list, desired_key) { + return filter_map( + keyword_list, + (keyword) => { + let key; + let value; + key = keyword[0]; + value = keyword[1]; + let $ = isEqual(key, desired_key); + if ($) { + return new Ok(value); + } else { + return new Error(undefined); + } + }, + ); +} + +function key_pop_loop(loop$list, loop$key, loop$checked) { + while (true) { + let list = loop$list; + let key = loop$key; + let checked = loop$checked; + if (list instanceof $Empty) { + return new Error(undefined); + } else { + let k = list.head[0]; + if (isEqual(k, key)) { + let rest$1 = list.tail; + let v = list.head[1]; + return new Ok([v, reverse_and_prepend(checked, rest$1)]); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + loop$list = rest$1; + loop$key = key; + loop$checked = listPrepend(first$1, checked); + } + } + } +} + +/** + * Given a list of 2-element tuples, finds 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 + * + * ```gleam + * key_pop([#("a", 0), #("b", 1)], "a") + * // -> Ok(#(0, [#("b", 1)])) + * ``` + * + * ```gleam + * key_pop([#("a", 0), #("b", 1)], "b") + * // -> Ok(#(1, [#("a", 0)])) + * ``` + * + * ```gleam + * key_pop([#("a", 0), #("b", 1)], "c") + * // -> Error(Nil) + * ``` + */ +export function key_pop(list, key) { + return key_pop_loop(list, key, toList([])); +} + +function key_set_loop(loop$list, loop$key, loop$value, loop$inspected) { + while (true) { + let list = loop$list; + let key = loop$key; + let value = loop$value; + let inspected = loop$inspected; + if (list instanceof $Empty) { + return reverse(listPrepend([key, value], inspected)); + } else { + let k = list.head[0]; + if (isEqual(k, key)) { + let rest$1 = list.tail; + return reverse_and_prepend(inspected, listPrepend([k, value], rest$1)); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + loop$list = rest$1; + loop$key = key; + loop$value = value; + loop$inspected = listPrepend(first$1, inspected); + } + } + } +} + +/** + * Given a list of 2-element tuples, inserts 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 + * + * ```gleam + * key_set([#(5, 0), #(4, 1)], 4, 100) + * // -> [#(5, 0), #(4, 100)] + * ``` + * + * ```gleam + * key_set([#(5, 0), #(4, 1)], 1, 100) + * // -> [#(5, 0), #(4, 1), #(1, 100)] + * ``` + */ +export function key_set(list, key, value) { + return key_set_loop(list, key, value, toList([])); +} + +/** + * Calls a function for each element in a list, discarding the return value. + * + * Useful for calling a side effect for every item of a list. + * + * ```gleam + * import gleam/io + * + * each(["1", "2", "3"], io.println) + * // -> Nil + * // 1 + * // 2 + * // 3 + * ``` + */ +export function each(loop$list, loop$f) { + while (true) { + let list = loop$list; + let f = loop$f; + if (list instanceof $Empty) { + return undefined; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + f(first$1); + loop$list = rest$1; + loop$f = f; + } + } +} + +/** + * Calls a `Result` returning function for each element in a list, discarding + * the return value. If the function returns `Error` then the iteration is + * stopped and the error is returned. + * + * Useful for calling a side effect for every item of a list. + * + * ## Examples + * + * ```gleam + * try_each( + * over: [1, 2, 3], + * with: function_that_might_fail, + * ) + * // -> Ok(Nil) + * ``` + */ +export function try_each(loop$list, loop$fun) { + while (true) { + let list = loop$list; + let fun = loop$fun; + if (list instanceof $Empty) { + return new Ok(undefined); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = fun(first$1); + if ($ instanceof Ok) { + loop$list = rest$1; + loop$fun = fun; + } else { + return $; + } + } + } +} + +function partition_loop(loop$list, loop$categorise, loop$trues, loop$falses) { + while (true) { + let list = loop$list; + let categorise = loop$categorise; + let trues = loop$trues; + let falses = loop$falses; + if (list instanceof $Empty) { + return [reverse(trues), reverse(falses)]; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = categorise(first$1); + if ($) { + loop$list = rest$1; + loop$categorise = categorise; + loop$trues = listPrepend(first$1, trues); + loop$falses = falses; + } else { + loop$list = rest$1; + loop$categorise = categorise; + loop$trues = trues; + loop$falses = listPrepend(first$1, falses); + } + } + } +} + +/** + * Partitions a list into a tuple/pair of lists + * by a given categorisation function. + * + * ## Examples + * + * ```gleam + * import gleam/int + * + * [1, 2, 3, 4, 5] |> partition(int.is_odd) + * // -> #([1, 3, 5], [2, 4]) + * ``` + */ +export function partition(list, categorise) { + return partition_loop(list, categorise, toList([]), toList([])); +} + +function window_loop(loop$acc, loop$list, loop$n) { + while (true) { + let acc = loop$acc; + let list = loop$list; + let n = loop$n; + let window$1 = take(list, n); + let $ = length(window$1) === n; + if ($) { + loop$acc = listPrepend(window$1, acc); + loop$list = drop(list, 1); + loop$n = n; + } else { + return reverse(acc); + } + } +} + +/** + * Returns a list of sliding windows. + * + * ## Examples + * + * ```gleam + * window([1,2,3,4,5], 3) + * // -> [[1, 2, 3], [2, 3, 4], [3, 4, 5]] + * ``` + * + * ```gleam + * window([1, 2], 4) + * // -> [] + * ``` + */ +export function window(list, n) { + let $ = n <= 0; + if ($) { + return toList([]); + } else { + return window_loop(toList([]), list, n); + } +} + +/** + * Returns a list of tuples containing two contiguous elements. + * + * ## Examples + * + * ```gleam + * window_by_2([1,2,3,4]) + * // -> [#(1, 2), #(2, 3), #(3, 4)] + * ``` + * + * ```gleam + * window_by_2([1]) + * // -> [] + * ``` + */ +export function window_by_2(list) { + return zip(list, drop(list, 1)); +} + +/** + * Drops the first elements in a given list for which the predicate function returns `True`. + * + * ## Examples + * + * ```gleam + * drop_while([1, 2, 3, 4], fn (x) { x < 3 }) + * // -> [3, 4] + * ``` + */ +export function drop_while(loop$list, loop$predicate) { + while (true) { + let list = loop$list; + let predicate = loop$predicate; + if (list instanceof $Empty) { + return list; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = predicate(first$1); + if ($) { + loop$list = rest$1; + loop$predicate = predicate; + } else { + return listPrepend(first$1, rest$1); + } + } + } +} + +function take_while_loop(loop$list, loop$predicate, loop$acc) { + while (true) { + let list = loop$list; + let predicate = loop$predicate; + let acc = loop$acc; + if (list instanceof $Empty) { + return reverse(acc); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = predicate(first$1); + if ($) { + loop$list = rest$1; + loop$predicate = predicate; + loop$acc = listPrepend(first$1, acc); + } else { + return reverse(acc); + } + } + } +} + +/** + * Takes the first elements in a given list for which the predicate function returns `True`. + * + * ## Examples + * + * ```gleam + * take_while([1, 2, 3, 2, 4], fn (x) { x < 3 }) + * // -> [1, 2] + * ``` + */ +export function take_while(list, predicate) { + return take_while_loop(list, predicate, toList([])); +} + +function chunk_loop( + loop$list, + loop$f, + loop$previous_key, + loop$current_chunk, + loop$acc +) { + while (true) { + let list = loop$list; + let f = loop$f; + let previous_key = loop$previous_key; + let current_chunk = loop$current_chunk; + let acc = loop$acc; + if (list instanceof $Empty) { + return reverse(listPrepend(reverse(current_chunk), acc)); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let key = f(first$1); + let $ = isEqual(key, previous_key); + if ($) { + loop$list = rest$1; + loop$f = f; + loop$previous_key = key; + loop$current_chunk = listPrepend(first$1, current_chunk); + loop$acc = acc; + } else { + let new_acc = listPrepend(reverse(current_chunk), acc); + loop$list = rest$1; + loop$f = f; + loop$previous_key = key; + loop$current_chunk = toList([first$1]); + loop$acc = new_acc; + } + } + } +} + +/** + * Returns a list of chunks in which + * the return value of calling `f` on each element is the same. + * + * ## Examples + * + * ```gleam + * [1, 2, 2, 3, 4, 4, 6, 7, 7] |> chunk(by: fn(n) { n % 2 }) + * // -> [[1], [2, 2], [3], [4, 4, 6], [7, 7]] + * ``` + */ +export function chunk(list, f) { + if (list instanceof $Empty) { + return list; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + return chunk_loop(rest$1, f, f(first$1), toList([first$1]), toList([])); + } +} + +function sized_chunk_loop( + loop$list, + loop$count, + loop$left, + loop$current_chunk, + loop$acc +) { + while (true) { + let list = loop$list; + let count = loop$count; + let left = loop$left; + let current_chunk = loop$current_chunk; + let acc = loop$acc; + if (list instanceof $Empty) { + if (current_chunk instanceof $Empty) { + return reverse(acc); + } else { + let remaining = current_chunk; + return reverse(listPrepend(reverse(remaining), acc)); + } + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let chunk$1 = listPrepend(first$1, current_chunk); + let $ = left > 1; + if ($) { + loop$list = rest$1; + loop$count = count; + loop$left = left - 1; + loop$current_chunk = chunk$1; + loop$acc = acc; + } else { + loop$list = rest$1; + loop$count = count; + loop$left = count; + loop$current_chunk = toList([]); + loop$acc = listPrepend(reverse(chunk$1), acc); + } + } + } +} + +/** + * Returns a list of chunks containing `count` elements each. + * + * If the last chunk does not have `count` elements, it is instead + * a partial chunk, with less than `count` elements. + * + * For any `count` less than 1 this function behaves as if it was set to 1. + * + * ## Examples + * + * ```gleam + * [1, 2, 3, 4, 5, 6] |> sized_chunk(into: 2) + * // -> [[1, 2], [3, 4], [5, 6]] + * ``` + * + * ```gleam + * [1, 2, 3, 4, 5, 6, 7, 8] |> sized_chunk(into: 3) + * // -> [[1, 2, 3], [4, 5, 6], [7, 8]] + * ``` + */ +export function sized_chunk(list, count) { + return sized_chunk_loop(list, count, count, toList([]), toList([])); +} + +/** + * This function acts similar to fold, but does not take an initial state. + * Instead, it starts from the first element in the list + * and combines it with each subsequent element in turn using the given + * function. The function is called as `fun(accumulator, current_element)`. + * + * Returns `Ok` to indicate a successful run, and `Error` if called on an + * empty list. + * + * ## Examples + * + * ```gleam + * [] |> reduce(fn(acc, x) { acc + x }) + * // -> Error(Nil) + * ``` + * + * ```gleam + * [1, 2, 3, 4, 5] |> reduce(fn(acc, x) { acc + x }) + * // -> Ok(15) + * ``` + */ +export function reduce(list, fun) { + if (list instanceof $Empty) { + return new Error(undefined); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + return new Ok(fold(rest$1, first$1, fun)); + } +} + +function scan_loop(loop$list, loop$accumulator, loop$accumulated, loop$fun) { + while (true) { + let list = loop$list; + let accumulator = loop$accumulator; + let accumulated = loop$accumulated; + let fun = loop$fun; + if (list instanceof $Empty) { + return reverse(accumulated); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let next = fun(accumulator, first$1); + loop$list = rest$1; + loop$accumulator = next; + loop$accumulated = listPrepend(next, accumulated); + loop$fun = fun; + } + } +} + +/** + * Similar to `fold`, but yields the state of the accumulator at each stage. + * + * ## Examples + * + * ```gleam + * scan(over: [1, 2, 3], from: 100, with: fn(acc, i) { acc + i }) + * // -> [101, 103, 106] + * ``` + */ +export function scan(list, initial, fun) { + return scan_loop(list, initial, toList([]), fun); +} + +/** + * Returns the last element in the given list. + * + * Returns `Error(Nil)` if the list is empty. + * + * This function runs in linear time. + * + * ## Examples + * + * ```gleam + * last([]) + * // -> Error(Nil) + * ``` + * + * ```gleam + * last([1, 2, 3, 4, 5]) + * // -> Ok(5) + * ``` + */ +export function last(loop$list) { + while (true) { + let list = loop$list; + if (list instanceof $Empty) { + return new Error(undefined); + } else { + let $ = list.tail; + if ($ instanceof $Empty) { + let last$1 = list.head; + return new Ok(last$1); + } else { + let rest$1 = $; + loop$list = rest$1; + } + } + } +} + +/** + * Return unique combinations of elements in the list. + * + * ## Examples + * + * ```gleam + * combinations([1, 2, 3], 2) + * // -> [[1, 2], [1, 3], [2, 3]] + * ``` + * + * ```gleam + * combinations([1, 2, 3, 4], 3) + * // -> [[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]] + * ``` + */ +export function combinations(items, n) { + if (n === 0) { + return toList([toList([])]); + } else if (items instanceof $Empty) { + return items; + } else { + let first$1 = items.head; + let rest$1 = items.tail; + let _pipe = rest$1; + let _pipe$1 = combinations(_pipe, n - 1); + let _pipe$2 = map( + _pipe$1, + (combination) => { return listPrepend(first$1, combination); }, + ); + let _pipe$3 = reverse(_pipe$2); + return fold( + _pipe$3, + combinations(rest$1, n), + (acc, c) => { return listPrepend(c, acc); }, + ); + } +} + +function combination_pairs_loop(loop$items, loop$acc) { + while (true) { + let items = loop$items; + let acc = loop$acc; + if (items instanceof $Empty) { + return reverse(acc); + } else { + let first$1 = items.head; + let rest$1 = items.tail; + let first_combinations = map( + rest$1, + (other) => { return [first$1, other]; }, + ); + let acc$1 = reverse_and_prepend(first_combinations, acc); + loop$items = rest$1; + loop$acc = acc$1; + } + } +} + +/** + * Return unique pair combinations of elements in the list. + * + * ## Examples + * + * ```gleam + * combination_pairs([1, 2, 3]) + * // -> [#(1, 2), #(1, 3), #(2, 3)] + * ``` + */ +export function combination_pairs(items) { + return combination_pairs_loop(items, toList([])); +} + +function take_firsts(loop$rows, loop$column, loop$remaining_rows) { + while (true) { + let rows = loop$rows; + let column = loop$column; + let remaining_rows = loop$remaining_rows; + if (rows instanceof $Empty) { + return [reverse(column), reverse(remaining_rows)]; + } else { + let $ = rows.head; + if ($ instanceof $Empty) { + let rest$1 = rows.tail; + loop$rows = rest$1; + loop$column = column; + loop$remaining_rows = remaining_rows; + } else { + let rest_rows = rows.tail; + let first$1 = $.head; + let remaining_row = $.tail; + let remaining_rows$1 = listPrepend(remaining_row, remaining_rows); + loop$rows = rest_rows; + loop$column = listPrepend(first$1, column); + loop$remaining_rows = remaining_rows$1; + } + } + } +} + +function transpose_loop(loop$rows, loop$columns) { + while (true) { + let rows = loop$rows; + let columns = loop$columns; + if (rows instanceof $Empty) { + return reverse(columns); + } else { + let $ = take_firsts(rows, toList([]), toList([])); + let column; + let rest$1; + column = $[0]; + rest$1 = $[1]; + if (column instanceof $Empty) { + loop$rows = rest$1; + loop$columns = columns; + } else { + loop$rows = rest$1; + loop$columns = listPrepend(column, columns); + } + } + } +} + +/** + * Transpose rows and columns of the list of lists. + * + * Notice: This function is not tail recursive, + * and thus may exceed stack size if called, + * with large lists (on the JavaScript target). + * + * ## Examples + * + * ```gleam + * transpose([[1, 2, 3], [101, 102, 103]]) + * // -> [[1, 101], [2, 102], [3, 103]] + * ``` + */ +export function transpose(list_of_lists) { + return transpose_loop(list_of_lists, toList([])); +} + +/** + * Make a list alternating the elements from the given lists + * + * ## Examples + * + * ```gleam + * interleave([[1, 2], [101, 102], [201, 202]]) + * // -> [1, 101, 201, 2, 102, 202] + * ``` + */ +export function interleave(list) { + let _pipe = list; + let _pipe$1 = transpose(_pipe); + return flatten(_pipe$1); +} + +function shuffle_pair_unwrap_loop(loop$list, loop$acc) { + while (true) { + let list = loop$list; + let acc = loop$acc; + if (list instanceof $Empty) { + return acc; + } else { + let elem_pair = list.head; + let enumerable = list.tail; + loop$list = enumerable; + loop$acc = listPrepend(elem_pair[1], acc); + } + } +} + +function do_shuffle_by_pair_indexes(list_of_pairs) { + return sort( + list_of_pairs, + (a_pair, b_pair) => { return $float.compare(a_pair[0], b_pair[0]); }, + ); +} + +/** + * Takes a list, randomly sorts all items and returns the shuffled list. + * + * This function uses `float.random` to decide the order of the elements. + * + * ## Example + * + * ```gleam + * range(1, 10) |> shuffle() + * // -> [1, 6, 9, 10, 3, 8, 4, 2, 7, 5] + * ``` + */ +export function shuffle(list) { + let _pipe = list; + let _pipe$1 = fold( + _pipe, + toList([]), + (acc, a) => { return listPrepend([$float.random(), a], acc); }, + ); + let _pipe$2 = do_shuffle_by_pair_indexes(_pipe$1); + return shuffle_pair_unwrap_loop(_pipe$2, toList([])); +} + +function max_loop(loop$list, loop$compare, loop$max) { + while (true) { + let list = loop$list; + let compare = loop$compare; + let max = loop$max; + if (list instanceof $Empty) { + return max; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let $ = compare(first$1, max); + if ($ instanceof $order.Lt) { + loop$list = rest$1; + loop$compare = compare; + loop$max = max; + } else if ($ instanceof $order.Eq) { + loop$list = rest$1; + loop$compare = compare; + loop$max = max; + } else { + loop$list = rest$1; + loop$compare = compare; + loop$max = first$1; + } + } + } +} + +/** + * Takes a list and a comparator, and returns the maximum element in the list + * + * + * ## Example + * + * ```gleam + * range(1, 10) |> list.max(int.compare) + * // -> Ok(10) + * ``` + * + * ```gleam + * ["a", "c", "b"] |> list.max(string.compare) + * // -> Ok("c") + * ``` + */ +export function max(list, compare) { + if (list instanceof $Empty) { + return new Error(undefined); + } else { + let first$1 = list.head; + let rest$1 = list.tail; + return new Ok(max_loop(rest$1, compare, first$1)); + } +} + +function build_reservoir_loop(loop$list, loop$size, loop$reservoir) { + while (true) { + let list = loop$list; + let size = loop$size; + let reservoir = loop$reservoir; + let reservoir_size = $dict.size(reservoir); + let $ = reservoir_size >= size; + if ($) { + return [reservoir, list]; + } else { + if (list instanceof $Empty) { + return [reservoir, toList([])]; + } else { + let first$1 = list.head; + let rest$1 = list.tail; + let reservoir$1 = $dict.insert(reservoir, reservoir_size, first$1); + loop$list = rest$1; + loop$size = size; + loop$reservoir = reservoir$1; + } + } + } +} + +/** + * Builds the initial reservoir used by Algorithm L. + * This is a dictionary with keys ranging from `0` up to `n - 1` where each + * value is the corresponding element at that position in `list`. + * + * This also returns the remaining elements of `list` that didn't end up in + * the reservoir. + * + * @ignore + */ +function build_reservoir(list, n) { + return build_reservoir_loop(list, n, $dict.new$()); +} + +const min_positive = 2.2250738585072014e-308; + +function log_random() { + let $ = $float.logarithm($float.random() + min_positive); + let random; + if ($ instanceof Ok) { + random = $[0]; + } else { + throw makeError( + "let_assert", + FILEPATH, + "gleam/list", + 2391, + "log_random", + "Pattern match failed, no pattern matched the value.", + { + value: $, + start: 56078, + end: 56149, + pattern_start: 56089, + pattern_end: 56099 + } + ) + } + return random; +} + +function sample_loop(loop$list, loop$reservoir, loop$n, loop$w) { + while (true) { + let list = loop$list; + let reservoir = loop$reservoir; + let n = loop$n; + let w = loop$w; + let _block; + { + let $ = $float.logarithm(1.0 - w); + let log; + if ($ instanceof Ok) { + log = $[0]; + } else { + throw makeError( + "let_assert", + FILEPATH, + "gleam/list", + 2374, + "sample_loop", + "Pattern match failed, no pattern matched the value.", + { + value: $, + start: 55639, + end: 55685, + pattern_start: 55650, + pattern_end: 55657 + } + ) + } + _block = $float.round($float.floor(divideFloat(log_random(), log))); + } + let skip = _block; + let $ = drop(list, skip); + if ($ instanceof $Empty) { + return reservoir; + } else { + let first$1 = $.head; + let rest$1 = $.tail; + let reservoir$1 = $dict.insert(reservoir, $int.random(n), first$1); + let w$1 = w * $float.exponential( + divideFloat(log_random(), $int.to_float(n)), + ); + loop$list = rest$1; + loop$reservoir = reservoir$1; + loop$n = n; + loop$w = w$1; + } + } +} + +/** + * Returns a random sample of up to n elements from a list using reservoir + * sampling via [Algorithm L](https://en.wikipedia.org/wiki/Reservoir_sampling#Optimal:_Algorithm_L). + * Returns an empty list if the sample size is less than or equal to 0. + * + * Order is not random, only selection is. + * + * ## Examples + * + * ```gleam + * reservoir_sample([1, 2, 3, 4, 5], 3) + * // -> [2, 4, 5] // A random sample of 3 items + * ``` + */ +export function sample(list, n) { + let $ = build_reservoir(list, n); + let reservoir; + let rest$1; + reservoir = $[0]; + rest$1 = $[1]; + let $1 = $dict.is_empty(reservoir); + if ($1) { + return toList([]); + } else { + let w = $float.exponential(divideFloat(log_random(), $int.to_float(n))); + return $dict.values(sample_loop(rest$1, reservoir, n, w)); + } +} + +function permutation_zip(list, rest, acc) { + if (list instanceof $Empty) { + return reverse(acc); + } else { + let head = list.head; + let tail = list.tail; + return permutation_prepend( + head, + permutations(reverse_and_prepend(rest, tail)), + tail, + listPrepend(head, rest), + acc, + ); + } +} + +function permutation_prepend( + loop$el, + loop$permutations, + loop$list_1, + loop$list_2, + loop$acc +) { + while (true) { + let el = loop$el; + let permutations = loop$permutations; + let list_1 = loop$list_1; + let list_2 = loop$list_2; + let acc = loop$acc; + if (permutations instanceof $Empty) { + return permutation_zip(list_1, list_2, acc); + } else { + let head = permutations.head; + let tail = permutations.tail; + loop$el = el; + loop$permutations = tail; + loop$list_1 = list_1; + loop$list_2 = list_2; + loop$acc = listPrepend(listPrepend(el, head), acc); + } + } +} + +/** + * Returns all the permutations of a list. + * + * ## Examples + * + * ```gleam + * permutations([1, 2]) + * // -> [[1, 2], [2, 1]] + * ``` + */ +export function permutations(list) { + if (list instanceof $Empty) { + return toList([toList([])]); + } else { + let l = list; + return permutation_zip(l, toList([]), toList([])); + } +} diff --git a/build/dev/javascript/gleam_stdlib/gleam/option.mjs b/build/dev/javascript/gleam_stdlib/gleam/option.mjs new file mode 100644 index 0000000..5e820a3 --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam/option.mjs @@ -0,0 +1,419 @@ +import { + Ok, + Error, + toList, + Empty as $Empty, + prepend as listPrepend, + CustomType as $CustomType, + isEqual, +} from "../gleam.mjs"; + +export class Some extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} +export const Option$Some = ($0) => new Some($0); +export const Option$isSome = (value) => value instanceof Some; +export const Option$Some$0 = (value) => value[0]; + +export class None extends $CustomType {} +export const Option$None = () => new None(); +export const Option$isNone = (value) => value instanceof None; + +function reverse_and_prepend(loop$prefix, loop$suffix) { + while (true) { + let prefix = loop$prefix; + let suffix = loop$suffix; + if (prefix instanceof $Empty) { + return suffix; + } else { + let first = prefix.head; + let rest = prefix.tail; + loop$prefix = rest; + loop$suffix = listPrepend(first, suffix); + } + } +} + +function reverse(list) { + return reverse_and_prepend(list, toList([])); +} + +function all_loop(loop$list, loop$acc) { + while (true) { + let list = loop$list; + let acc = loop$acc; + if (list instanceof $Empty) { + return new Some(reverse(acc)); + } else { + let $ = list.head; + if ($ instanceof Some) { + let rest = list.tail; + let first = $[0]; + loop$list = rest; + loop$acc = listPrepend(first, acc); + } else { + return new None(); + } + } + } +} + +/** + * Combines a list of `Option`s into a single `Option`. + * If all elements in the list are `Some` then returns a `Some` holding the list of values. + * If any element is `None` then returns`None`. + * + * ## Examples + * + * ```gleam + * all([Some(1), Some(2)]) + * // -> Some([1, 2]) + * ``` + * + * ```gleam + * all([Some(1), None]) + * // -> None + * ``` + */ +export function all(list) { + return all_loop(list, toList([])); +} + +/** + * Checks whether the `Option` is a `Some` value. + * + * ## Examples + * + * ```gleam + * is_some(Some(1)) + * // -> True + * ``` + * + * ```gleam + * is_some(None) + * // -> False + * ``` + */ +export function is_some(option) { + return !isEqual(option, new None()); +} + +/** + * Checks whether the `Option` is a `None` value. + * + * ## Examples + * + * ```gleam + * is_none(Some(1)) + * // -> False + * ``` + * + * ```gleam + * is_none(None) + * // -> True + * ``` + */ +export function is_none(option) { + return isEqual(option, new None()); +} + +/** + * Converts an `Option` type to a `Result` type. + * + * ## Examples + * + * ```gleam + * to_result(Some(1), "some_error") + * // -> Ok(1) + * ``` + * + * ```gleam + * to_result(None, "some_error") + * // -> Error("some_error") + * ``` + */ +export function to_result(option, e) { + if (option instanceof Some) { + let a = option[0]; + return new Ok(a); + } else { + return new Error(e); + } +} + +/** + * Converts a `Result` type to an `Option` type. + * + * ## Examples + * + * ```gleam + * from_result(Ok(1)) + * // -> Some(1) + * ``` + * + * ```gleam + * from_result(Error("some_error")) + * // -> None + * ``` + */ +export function from_result(result) { + if (result instanceof Ok) { + let a = result[0]; + return new Some(a); + } else { + return new None(); + } +} + +/** + * Extracts the value from an `Option`, returning a default value if there is none. + * + * ## Examples + * + * ```gleam + * unwrap(Some(1), 0) + * // -> 1 + * ``` + * + * ```gleam + * unwrap(None, 0) + * // -> 0 + * ``` + */ +export function unwrap(option, default$) { + if (option instanceof Some) { + let x = option[0]; + return x; + } else { + return default$; + } +} + +/** + * Extracts the value from an `Option`, evaluating the default function if the option is `None`. + * + * ## Examples + * + * ```gleam + * lazy_unwrap(Some(1), fn() { 0 }) + * // -> 1 + * ``` + * + * ```gleam + * lazy_unwrap(None, fn() { 0 }) + * // -> 0 + * ``` + */ +export function lazy_unwrap(option, default$) { + if (option instanceof Some) { + let x = option[0]; + return x; + } else { + return default$(); + } +} + +/** + * Updates 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 + * + * ```gleam + * map(over: Some(1), with: fn(x) { x + 1 }) + * // -> Some(2) + * ``` + * + * ```gleam + * map(over: None, with: fn(x) { x + 1 }) + * // -> None + * ``` + */ +export function map(option, fun) { + if (option instanceof Some) { + let x = option[0]; + return new Some(fun(x)); + } else { + return option; + } +} + +/** + * Merges a nested `Option` into a single layer. + * + * ## Examples + * + * ```gleam + * flatten(Some(Some(1))) + * // -> Some(1) + * ``` + * + * ```gleam + * flatten(Some(None)) + * // -> None + * ``` + * + * ```gleam + * flatten(None) + * // -> None + * ``` + */ +export function flatten(option) { + if (option instanceof Some) { + let x = option[0]; + return x; + } else { + return option; + } +} + +/** + * Updates 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 `Option`. + * + * ## Examples + * + * ```gleam + * then(Some(1), fn(x) { Some(x + 1) }) + * // -> Some(2) + * ``` + * + * ```gleam + * then(Some(1), fn(x) { Some(#("a", x)) }) + * // -> Some(#("a", 1)) + * ``` + * + * ```gleam + * then(Some(1), fn(_) { None }) + * // -> None + * ``` + * + * ```gleam + * then(None, fn(x) { Some(x + 1) }) + * // -> None + * ``` + */ +export function then$(option, fun) { + if (option instanceof Some) { + let x = option[0]; + return fun(x); + } else { + return option; + } +} + +/** + * Returns the first value if it is `Some`, otherwise returns the second value. + * + * ## Examples + * + * ```gleam + * or(Some(1), Some(2)) + * // -> Some(1) + * ``` + * + * ```gleam + * or(Some(1), None) + * // -> Some(1) + * ``` + * + * ```gleam + * or(None, Some(2)) + * // -> Some(2) + * ``` + * + * ```gleam + * or(None, None) + * // -> None + * ``` + */ +export function or(first, second) { + if (first instanceof Some) { + return first; + } else { + return second; + } +} + +/** + * Returns the first value if it is `Some`, otherwise evaluates the given function for a fallback value. + * + * ## Examples + * + * ```gleam + * lazy_or(Some(1), fn() { Some(2) }) + * // -> Some(1) + * ``` + * + * ```gleam + * lazy_or(Some(1), fn() { None }) + * // -> Some(1) + * ``` + * + * ```gleam + * lazy_or(None, fn() { Some(2) }) + * // -> Some(2) + * ``` + * + * ```gleam + * lazy_or(None, fn() { None }) + * // -> None + * ``` + */ +export function lazy_or(first, second) { + if (first instanceof Some) { + return first; + } else { + return second(); + } +} + +function values_loop(loop$list, loop$acc) { + while (true) { + let list = loop$list; + let acc = loop$acc; + if (list instanceof $Empty) { + return reverse(acc); + } else { + let $ = list.head; + if ($ instanceof Some) { + let rest = list.tail; + let first = $[0]; + loop$list = rest; + loop$acc = listPrepend(first, acc); + } else { + let rest = list.tail; + loop$list = rest; + loop$acc = acc; + } + } + } +} + +/** + * Given a list of `Option`s, + * returns only the values inside `Some`. + * + * ## Examples + * + * ```gleam + * values([Some(1), None, Some(3)]) + * // -> [1, 3] + * ``` + */ +export function values(options) { + return values_loop(options, toList([])); +} diff --git a/build/dev/javascript/gleam_stdlib/gleam/order.mjs b/build/dev/javascript/gleam_stdlib/gleam/order.mjs new file mode 100644 index 0000000..617938c --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam/order.mjs @@ -0,0 +1,178 @@ +import { CustomType as $CustomType, isEqual } from "../gleam.mjs"; + +export class Lt extends $CustomType {} +export const Order$Lt = () => new Lt(); +export const Order$isLt = (value) => value instanceof Lt; + +export class Eq extends $CustomType {} +export const Order$Eq = () => new Eq(); +export const Order$isEq = (value) => value instanceof Eq; + +export class Gt extends $CustomType {} +export const Order$Gt = () => new Gt(); +export const Order$isGt = (value) => value instanceof Gt; + +/** + * Inverts an order, so less-than becomes greater-than and greater-than + * becomes less-than. + * + * ## Examples + * + * ```gleam + * negate(Lt) + * // -> Gt + * ``` + * + * ```gleam + * negate(Eq) + * // -> Eq + * ``` + * + * ```gleam + * negate(Gt) + * // -> Lt + * ``` + */ +export function negate(order) { + if (order instanceof Lt) { + return new Gt(); + } else if (order instanceof Eq) { + return order; + } else { + return new Lt(); + } +} + +/** + * Produces a numeric representation of the order. + * + * ## Examples + * + * ```gleam + * to_int(Lt) + * // -> -1 + * ``` + * + * ```gleam + * to_int(Eq) + * // -> 0 + * ``` + * + * ```gleam + * to_int(Gt) + * // -> 1 + * ``` + */ +export function to_int(order) { + if (order instanceof Lt) { + return -1; + } else if (order instanceof Eq) { + return 0; + } else { + return 1; + } +} + +/** + * Compares two `Order` values to one another, producing a new `Order`. + * + * ## Examples + * + * ```gleam + * compare(Eq, with: Lt) + * // -> Gt + * ``` + */ +export function compare(a, b) { + let x = a; + let y = b; + if (isEqual(x, y)) { + return new Eq(); + } else if (a instanceof Lt) { + return new Lt(); + } else if (a instanceof Eq && b instanceof Gt) { + return new Lt(); + } else { + return new Gt(); + } +} + +/** + * Inverts an ordering function, so less-than becomes greater-than and greater-than + * becomes less-than. + * + * ## Examples + * + * ```gleam + * import gleam/int + * import gleam/list + * + * list.sort([1, 5, 4], by: reverse(int.compare)) + * // -> [5, 4, 1] + * ``` + */ +export function reverse(orderer) { + return (a, b) => { return orderer(b, a); }; +} + +/** + * Return a fallback `Order` in case the first argument is `Eq`. + * + * ## Examples + * + * ```gleam + * import gleam/int + * + * break_tie(in: int.compare(1, 1), with: Lt) + * // -> Lt + * ``` + * + * ```gleam + * import gleam/int + * + * break_tie(in: int.compare(1, 0), with: Eq) + * // -> Gt + * ``` + */ +export function break_tie(order, other) { + if (order instanceof Lt) { + return order; + } else if (order instanceof Eq) { + return other; + } else { + return order; + } +} + +/** + * Invokes a fallback function returning an `Order` in case the first argument + * is `Eq`. + * + * This can be useful when the fallback comparison might be expensive and it + * needs to be delayed until strictly necessary. + * + * ## Examples + * + * ```gleam + * import gleam/int + * + * lazy_break_tie(in: int.compare(1, 1), with: fn() { Lt }) + * // -> Lt + * ``` + * + * ```gleam + * import gleam/int + * + * lazy_break_tie(in: int.compare(1, 0), with: fn() { Eq }) + * // -> Gt + * ``` + */ +export function lazy_break_tie(order, comparison) { + if (order instanceof Lt) { + return order; + } else if (order instanceof Eq) { + return comparison(); + } else { + return order; + } +} diff --git a/build/dev/javascript/gleam_stdlib/gleam/pair.mjs b/build/dev/javascript/gleam_stdlib/gleam/pair.mjs new file mode 100644 index 0000000..13f0cd5 --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam/pair.mjs @@ -0,0 +1,102 @@ +/** + * Returns the first element in a pair. + * + * ## Examples + * + * ```gleam + * first(#(1, 2)) + * // -> 1 + * ``` + */ +export function first(pair) { + let a; + a = pair[0]; + return a; +} + +/** + * Returns the second element in a pair. + * + * ## Examples + * + * ```gleam + * second(#(1, 2)) + * // -> 2 + * ``` + */ +export function second(pair) { + let a; + a = pair[1]; + return a; +} + +/** + * Returns a new pair with the elements swapped. + * + * ## Examples + * + * ```gleam + * swap(#(1, 2)) + * // -> #(2, 1) + * ``` + */ +export function swap(pair) { + let a; + let b; + a = pair[0]; + b = pair[1]; + return [b, a]; +} + +/** + * Returns a new pair with the first element having had `with` applied to + * it. + * + * ## Examples + * + * ```gleam + * #(1, 2) |> map_first(fn(n) { n * 2 }) + * // -> #(2, 2) + * ``` + */ +export function map_first(pair, fun) { + let a; + let b; + a = pair[0]; + b = pair[1]; + return [fun(a), b]; +} + +/** + * Returns a new pair with the second element having had `with` applied to + * it. + * + * ## Examples + * + * ```gleam + * #(1, 2) |> map_second(fn(n) { n * 2 }) + * // -> #(1, 4) + * ``` + */ +export function map_second(pair, fun) { + let a; + let b; + a = pair[0]; + b = pair[1]; + return [a, fun(b)]; +} + +/** + * Returns a new pair with the given elements. This can also be done using the dedicated + * syntax instead: `new(1, 2) == #(1, 2)`. + * + * ## Examples + * + * ```gleam + * new(1, 2) + * // -> #(1, 2) + * ``` + */ +export function new$(first, second) { + return [first, second]; +} diff --git a/build/dev/javascript/gleam_stdlib/gleam/result.mjs b/build/dev/javascript/gleam_stdlib/gleam/result.mjs new file mode 100644 index 0000000..527de4f --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam/result.mjs @@ -0,0 +1,494 @@ +import { Ok, Error, toList, Empty as $Empty, prepend as listPrepend } from "../gleam.mjs"; +import * as $list from "../gleam/list.mjs"; + +/** + * Checks whether the result is an `Ok` value. + * + * ## Examples + * + * ```gleam + * is_ok(Ok(1)) + * // -> True + * ``` + * + * ```gleam + * is_ok(Error(Nil)) + * // -> False + * ``` + */ +export function is_ok(result) { + if (result instanceof Ok) { + return true; + } else { + return false; + } +} + +/** + * Checks whether the result is an `Error` value. + * + * ## Examples + * + * ```gleam + * is_error(Ok(1)) + * // -> False + * ``` + * + * ```gleam + * is_error(Error(Nil)) + * // -> True + * ``` + */ +export function is_error(result) { + if (result instanceof Ok) { + return false; + } else { + return true; + } +} + +/** + * Updates 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 + * + * ```gleam + * map(over: Ok(1), with: fn(x) { x + 1 }) + * // -> Ok(2) + * ``` + * + * ```gleam + * map(over: Error(1), with: fn(x) { x + 1 }) + * // -> Error(1) + * ``` + */ +export function map(result, fun) { + if (result instanceof Ok) { + let x = result[0]; + return new Ok(fun(x)); + } else { + return result; + } +} + +/** + * Updates 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 + * + * ```gleam + * map_error(over: Error(1), with: fn(x) { x + 1 }) + * // -> Error(2) + * ``` + * + * ```gleam + * map_error(over: Ok(1), with: fn(x) { x + 1 }) + * // -> Ok(1) + * ``` + */ +export function map_error(result, fun) { + if (result instanceof Ok) { + return result; + } else { + let error = result[0]; + return new Error(fun(error)); + } +} + +/** + * Merges a nested `Result` into a single layer. + * + * ## Examples + * + * ```gleam + * flatten(Ok(Ok(1))) + * // -> Ok(1) + * ``` + * + * ```gleam + * flatten(Ok(Error(""))) + * // -> Error("") + * ``` + * + * ```gleam + * flatten(Error(Nil)) + * // -> Error(Nil) + * ``` + */ +export function flatten(result) { + if (result instanceof Ok) { + let x = result[0]; + return x; + } else { + return result; + } +} + +/** + * "Updates" an `Ok` result by passing its value to a function that yields a result, + * and returning the yielded result. (This may "replace" the `Ok` with an `Error`.) + * + * If the input is an `Error` rather than an `Ok`, the function is not called and + * the original `Error` is returned. + * + * This function is the equivalent of calling `map` followed by `flatten`, and + * it is useful for chaining together multiple functions that may fail. + * + * ## Examples + * + * ```gleam + * try(Ok(1), fn(x) { Ok(x + 1) }) + * // -> Ok(2) + * ``` + * + * ```gleam + * try(Ok(1), fn(x) { Ok(#("a", x)) }) + * // -> Ok(#("a", 1)) + * ``` + * + * ```gleam + * try(Ok(1), fn(_) { Error("Oh no") }) + * // -> Error("Oh no") + * ``` + * + * ```gleam + * try(Error(Nil), fn(x) { Ok(x + 1) }) + * // -> Error(Nil) + * ``` + */ +export function try$(result, fun) { + if (result instanceof Ok) { + let x = result[0]; + return fun(x); + } else { + return result; + } +} + +export function then$(result, fun) { + return try$(result, fun); +} + +/** + * Extracts the `Ok` value from a result, returning a default value if the result + * is an `Error`. + * + * ## Examples + * + * ```gleam + * unwrap(Ok(1), 0) + * // -> 1 + * ``` + * + * ```gleam + * unwrap(Error(""), 0) + * // -> 0 + * ``` + */ +export function unwrap(result, default$) { + if (result instanceof Ok) { + let v = result[0]; + return v; + } else { + return default$; + } +} + +/** + * Extracts the `Ok` value from a result, evaluating the default function if the result + * is an `Error`. + * + * ## Examples + * + * ```gleam + * lazy_unwrap(Ok(1), fn() { 0 }) + * // -> 1 + * ``` + * + * ```gleam + * lazy_unwrap(Error(""), fn() { 0 }) + * // -> 0 + * ``` + */ +export function lazy_unwrap(result, default$) { + if (result instanceof Ok) { + let v = result[0]; + return v; + } else { + return default$(); + } +} + +/** + * Extracts the `Error` value from a result, returning a default value if the result + * is an `Ok`. + * + * ## Examples + * + * ```gleam + * unwrap_error(Error(1), 0) + * // -> 1 + * ``` + * + * ```gleam + * unwrap_error(Ok(""), 0) + * // -> 0 + * ``` + */ +export function unwrap_error(result, default$) { + if (result instanceof Ok) { + return default$; + } else { + let e = result[0]; + return e; + } +} + +export function unwrap_both(result) { + if (result instanceof Ok) { + let a = result[0]; + return a; + } else { + let a = result[0]; + return a; + } +} + +/** + * Returns the first value if it is `Ok`, otherwise returns the second value. + * + * ## Examples + * + * ```gleam + * or(Ok(1), Ok(2)) + * // -> Ok(1) + * ``` + * + * ```gleam + * or(Ok(1), Error("Error 2")) + * // -> Ok(1) + * ``` + * + * ```gleam + * or(Error("Error 1"), Ok(2)) + * // -> Ok(2) + * ``` + * + * ```gleam + * or(Error("Error 1"), Error("Error 2")) + * // -> Error("Error 2") + * ``` + */ +export function or(first, second) { + if (first instanceof Ok) { + return first; + } else { + return second; + } +} + +/** + * Returns the first value if it is `Ok`, otherwise evaluates the given function for a fallback value. + * + * If you need access to the initial error value, use `result.try_recover`. + * + * ## Examples + * + * ```gleam + * lazy_or(Ok(1), fn() { Ok(2) }) + * // -> Ok(1) + * ``` + * + * ```gleam + * lazy_or(Ok(1), fn() { Error("Error 2") }) + * // -> Ok(1) + * ``` + * + * ```gleam + * lazy_or(Error("Error 1"), fn() { Ok(2) }) + * // -> Ok(2) + * ``` + * + * ```gleam + * lazy_or(Error("Error 1"), fn() { Error("Error 2") }) + * // -> Error("Error 2") + * ``` + */ +export function lazy_or(first, second) { + if (first instanceof Ok) { + return first; + } else { + return second(); + } +} + +/** + * Combines 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 + * + * ```gleam + * all([Ok(1), Ok(2)]) + * // -> Ok([1, 2]) + * ``` + * + * ```gleam + * all([Ok(1), Error("e")]) + * // -> Error("e") + * ``` + */ +export function all(results) { + return $list.try_map(results, (result) => { return result; }); +} + +function partition_loop(loop$results, loop$oks, loop$errors) { + while (true) { + let results = loop$results; + let oks = loop$oks; + let errors = loop$errors; + if (results instanceof $Empty) { + return [oks, errors]; + } else { + let $ = results.head; + if ($ instanceof Ok) { + let rest = results.tail; + let a = $[0]; + loop$results = rest; + loop$oks = listPrepend(a, oks); + loop$errors = errors; + } else { + let rest = results.tail; + let e = $[0]; + loop$results = rest; + loop$oks = oks; + loop$errors = listPrepend(e, errors); + } + } + } +} + +/** + * Given a list of results, returns a pair where the first element is a list + * of all the values inside `Ok` and the second element is a list with all the + * values inside `Error`. The values in both lists appear in reverse order with + * respect to their position in the original list of results. + * + * ## Examples + * + * ```gleam + * partition([Ok(1), Error("a"), Error("b"), Ok(2)]) + * // -> #([2, 1], ["b", "a"]) + * ``` + */ +export function partition(results) { + return partition_loop(results, toList([]), toList([])); +} + +/** + * Replace the value within a result + * + * ## Examples + * + * ```gleam + * replace(Ok(1), Nil) + * // -> Ok(Nil) + * ``` + * + * ```gleam + * replace(Error(1), Nil) + * // -> Error(1) + * ``` + */ +export function replace(result, value) { + if (result instanceof Ok) { + return new Ok(value); + } else { + return result; + } +} + +/** + * Replace the error within a result + * + * ## Examples + * + * ```gleam + * replace_error(Error(1), Nil) + * // -> Error(Nil) + * ``` + * + * ```gleam + * replace_error(Ok(1), Nil) + * // -> Ok(1) + * ``` + */ +export function replace_error(result, error) { + if (result instanceof Ok) { + return result; + } else { + return new Error(error); + } +} + +/** + * Given a list of results, returns only the values inside `Ok`. + * + * ## Examples + * + * ```gleam + * values([Ok(1), Error("a"), Ok(3)]) + * // -> [1, 3] + * ``` + */ +export function values(results) { + return $list.filter_map(results, (result) => { return result; }); +} + +/** + * Updates a value held within the `Error` 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 `Ok` rather than `Error` the function is not called and the + * result stays the same. + * + * This function is useful for chaining together computations that may fail + * and trying to recover from possible errors. + * + * If you do not need access to the initial error value, use `result.lazy_or`. + * + * ## Examples + * + * ```gleam + * Ok(1) |> try_recover(with: fn(_) { Error("failed to recover") }) + * // -> Ok(1) + * ``` + * + * ```gleam + * Error(1) |> try_recover(with: fn(error) { Ok(error + 1) }) + * // -> Ok(2) + * ``` + * + * ```gleam + * Error(1) |> try_recover(with: fn(error) { Error("failed to recover") }) + * // -> Error("failed to recover") + * ``` + */ +export function try_recover(result, fun) { + if (result instanceof Ok) { + return result; + } else { + let error = result[0]; + return fun(error); + } +} diff --git a/build/dev/javascript/gleam_stdlib/gleam/set.mjs b/build/dev/javascript/gleam_stdlib/gleam/set.mjs new file mode 100644 index 0000000..dc1ae52 --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam/set.mjs @@ -0,0 +1,412 @@ +import { CustomType as $CustomType, isEqual } from "../gleam.mjs"; +import * as $dict from "../gleam/dict.mjs"; +import * as $list from "../gleam/list.mjs"; +import * as $result from "../gleam/result.mjs"; + +class Set extends $CustomType { + constructor(dict) { + super(); + this.dict = dict; + } +} + +/** + * Creates a new empty set. + */ +export function new$() { + return new Set($dict.new$()); +} + +/** + * Gets the number of members in a set. + * + * This function runs in constant time. + * + * ## Examples + * + * ```gleam + * new() + * |> insert(1) + * |> insert(2) + * |> size + * // -> 2 + * ``` + */ +export function size(set) { + return $dict.size(set.dict); +} + +/** + * Determines whether or not the set is empty. + * + * ## Examples + * + * ```gleam + * new() |> is_empty + * // -> True + * ``` + * + * ```gleam + * new() |> insert(1) |> is_empty + * // -> False + * ``` + */ +export function is_empty(set) { + return isEqual(set, new$()); +} + +/** + * Checks whether a set contains a given member. + * + * This function runs in logarithmic time. + * + * ## Examples + * + * ```gleam + * new() + * |> insert(2) + * |> contains(2) + * // -> True + * ``` + * + * ```gleam + * new() + * |> insert(2) + * |> contains(1) + * // -> False + * ``` + */ +export function contains(set, member) { + let _pipe = set.dict; + let _pipe$1 = $dict.get(_pipe, member); + return $result.is_ok(_pipe$1); +} + +/** + * Removes a 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 + * + * ```gleam + * new() + * |> insert(2) + * |> delete(2) + * |> contains(1) + * // -> False + * ``` + */ +export function delete$(set, member) { + return new Set($dict.delete$(set.dict, member)); +} + +/** + * Converts 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 + * + * ```gleam + * new() |> insert(2) |> to_list + * // -> [2] + * ``` + */ +export function to_list(set) { + return $dict.keys(set.dict); +} + +/** + * Combines 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 + * + * ```gleam + * from_list([1, 3, 9]) + * |> fold(0, fn(accumulator, member) { accumulator + member }) + * // -> 13 + * ``` + */ +export function fold(set, initial, reducer) { + return $dict.fold(set.dict, initial, (a, k, _) => { return reducer(a, k); }); +} + +/** + * Creates a new set from an existing set, minus any members that a given + * function returns `False` for. + * + * This function runs in loglinear time. + * + * ## Examples + * + * ```gleam + * import gleam/int + * + * from_list([1, 4, 6, 3, 675, 44, 67]) + * |> filter(keeping: int.is_even) + * |> to_list + * // -> [4, 6, 44] + * ``` + */ +export function filter(set, predicate) { + return new Set($dict.filter(set.dict, (m, _) => { return predicate(m); })); +} + +/** + * Creates a new set from a given set with all the same entries except any + * entry found on the given list. + * + * ## Examples + * + * ```gleam + * from_list([1, 2, 3, 4]) + * |> drop([1, 3]) + * |> to_list + * // -> [2, 4] + * ``` + */ +export function drop(set, disallowed) { + return $list.fold(disallowed, set, delete$); +} + +/** + * Creates a new set from a given set, only including any members which are in + * a given list. + * + * This function runs in loglinear time. + * + * ## Examples + * + * ```gleam + * from_list([1, 2, 3]) + * |> take([1, 3, 5]) + * |> to_list + * // -> [1, 3] + * ``` + */ +export function take(set, desired) { + return new Set($dict.take(set.dict, desired)); +} + +function order(first, second) { + let $ = $dict.size(first.dict) > $dict.size(second.dict); + if ($) { + return [first, second]; + } else { + return [second, first]; + } +} + +/** + * Creates a new set that contains members that are present in both given sets. + * + * This function runs in loglinear time. + * + * ## Examples + * + * ```gleam + * intersection(from_list([1, 2]), from_list([2, 3])) |> to_list + * // -> [2] + * ``` + */ +export function intersection(first, second) { + let $ = order(first, second); + let larger; + let smaller; + larger = $[0]; + smaller = $[1]; + return take(larger, to_list(smaller)); +} + +/** + * Creates a new set that contains members that are present in the first set + * but not the second. + * + * ## Examples + * + * ```gleam + * difference(from_list([1, 2]), from_list([2, 3, 4])) |> to_list + * // -> [1] + * ``` + */ +export function difference(first, second) { + return drop(first, to_list(second)); +} + +/** + * Determines if a set is fully contained by another. + * + * ## Examples + * + * ```gleam + * is_subset(from_list([1]), from_list([1, 2])) + * // -> True + * ``` + * + * ```gleam + * is_subset(from_list([1, 2, 3]), from_list([3, 4, 5])) + * // -> False + * ``` + */ +export function is_subset(first, second) { + return isEqual(intersection(first, second), first); +} + +/** + * Determines if two sets contain no common members + * + * ## Examples + * + * ```gleam + * is_disjoint(from_list([1, 2, 3]), from_list([4, 5, 6])) + * // -> True + * ``` + * + * ```gleam + * is_disjoint(from_list([1, 2, 3]), from_list([3, 4, 5])) + * // -> False + * ``` + */ +export function is_disjoint(first, second) { + return isEqual(intersection(first, second), new$()); +} + +/** + * Calls a function for each member in a set, discarding the return + * value. + * + * Useful for producing a side effect for every item of a set. + * + * ```gleam + * let set = from_list(["apple", "banana", "cherry"]) + * + * each(set, io.println) + * // -> Nil + * // apple + * // banana + * // cherry + * ``` + * + * The order of elements in the iteration is an implementation detail that + * should not be relied upon. + */ +export function each(set, fun) { + return fold( + set, + undefined, + (nil, member) => { + fun(member); + return nil; + }, + ); +} + +const token = undefined; + +/** + * Inserts an member into the set. + * + * This function runs in logarithmic time. + * + * ## Examples + * + * ```gleam + * new() + * |> insert(1) + * |> insert(2) + * |> size + * // -> 2 + * ``` + */ +export function insert(set, member) { + return new Set($dict.insert(set.dict, member, token)); +} + +/** + * Creates a new set of the members in a given list. + * + * This function runs in loglinear time. + * + * ## Examples + * + * ```gleam + * import gleam/int + * import gleam/list + * + * [1, 1, 2, 4, 3, 2] |> from_list |> to_list |> list.sort(by: int.compare) + * // -> [1, 2, 3, 4] + * ``` + */ +export function from_list(members) { + let dict = $list.fold( + members, + $dict.new$(), + (m, k) => { return $dict.insert(m, k, token); }, + ); + return new Set(dict); +} + +/** + * Creates a new set from a given set with the result of applying the given + * function to each member. + * + * ## Examples + * + * ```gleam + * from_list([1, 2, 3, 4]) + * |> map(with: fn(x) { x * 2 }) + * |> to_list + * // -> [2, 4, 6, 8] + * ``` + */ +export function map(set, fun) { + return fold( + set, + new$(), + (acc, member) => { return insert(acc, fun(member)); }, + ); +} + +/** + * Creates a new set that contains all members of both given sets. + * + * This function runs in loglinear time. + * + * ## Examples + * + * ```gleam + * union(from_list([1, 2]), from_list([2, 3])) |> to_list + * // -> [1, 2, 3] + * ``` + */ +export function union(first, second) { + let $ = order(first, second); + let larger; + let smaller; + larger = $[0]; + smaller = $[1]; + return fold(smaller, larger, insert); +} + +/** + * Creates a new set that contains members that are present in either set, but + * not both. + * + * ```gleam + * symmetric_difference(from_list([1, 2, 3]), from_list([3, 4])) |> to_list + * // -> [1, 2, 4] + * ``` + */ +export function symmetric_difference(first, second) { + return difference(union(first, second), intersection(first, second)); +} diff --git a/build/dev/javascript/gleam_stdlib/gleam/string.mjs b/build/dev/javascript/gleam_stdlib/gleam/string.mjs new file mode 100644 index 0000000..d15468e --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam/string.mjs @@ -0,0 +1,723 @@ +import { + Ok, + Error, + Empty as $Empty, + prepend as listPrepend, + CustomType as $CustomType, + remainderInt, + divideInt, +} from "../gleam.mjs"; +import * as $list from "../gleam/list.mjs"; +import * as $option from "../gleam/option.mjs"; +import { None, Some } from "../gleam/option.mjs"; +import * as $order from "../gleam/order.mjs"; +import * as $string_tree from "../gleam/string_tree.mjs"; +import { + string_length as length, + lowercase, + uppercase, + less_than, + string_grapheme_slice as grapheme_slice, + string_byte_slice as unsafe_byte_slice, + crop_string as crop, + contains_string as contains, + starts_with, + ends_with, + split_once, + trim_start, + trim_end, + pop_grapheme, + graphemes as to_graphemes, + codepoint as unsafe_int_to_utf_codepoint, + string_to_codepoint_integer_list, + utf_codepoint_list_to_string as from_utf_codepoints, + utf_codepoint_to_int, + inspect as do_inspect, + byte_size, +} from "../gleam_stdlib.mjs"; + +export { + byte_size, + contains, + crop, + ends_with, + from_utf_codepoints, + length, + lowercase, + pop_grapheme, + split_once, + starts_with, + to_graphemes, + trim_end, + trim_start, + uppercase, + utf_codepoint_to_int, +}; + +class Leading extends $CustomType {} + +class Trailing extends $CustomType {} + +/** + * Determines if a `String` is empty. + * + * ## Examples + * + * ```gleam + * is_empty("") + * // -> True + * ``` + * + * ```gleam + * is_empty("the world") + * // -> False + * ``` + */ +export function is_empty(str) { + return str === ""; +} + +/** + * Reverses a `String`. + * + * This function has to iterate across the whole `String` so it runs in linear + * time. Avoid using this in a loop. + * + * ## Examples + * + * ```gleam + * reverse("stressed") + * // -> "desserts" + * ``` + */ +export function reverse(string) { + let _pipe = string; + let _pipe$1 = $string_tree.from_string(_pipe); + let _pipe$2 = $string_tree.reverse(_pipe$1); + return $string_tree.to_string(_pipe$2); +} + +/** + * Creates a new `String` by replacing all occurrences of a given substring. + * + * ## Examples + * + * ```gleam + * replace("www.example.com", each: ".", with: "-") + * // -> "www-example-com" + * ``` + * + * ```gleam + * replace("a,b,c,d,e", each: ",", with: "/") + * // -> "a/b/c/d/e" + * ``` + */ +export function replace(string, pattern, substitute) { + let _pipe = string; + let _pipe$1 = $string_tree.from_string(_pipe); + let _pipe$2 = $string_tree.replace(_pipe$1, pattern, substitute); + return $string_tree.to_string(_pipe$2); +} + +/** + * Compares two `String`s to see which is "larger" by comparing their graphemes. + * + * This does not compare the size or length of the given `String`s. + * + * ## Examples + * + * ```gleam + * compare("Anthony", "Anthony") + * // -> order.Eq + * ``` + * + * ```gleam + * compare("A", "B") + * // -> order.Lt + * ``` + */ +export function compare(a, b) { + let $ = a === b; + if ($) { + return new $order.Eq(); + } else { + let $1 = less_than(a, b); + if ($1) { + return new $order.Lt(); + } else { + return new $order.Gt(); + } + } +} + +/** + * Takes a substring given a start grapheme index and a length. Negative indexes + * are taken starting from the *end* of the list. + * + * This function runs in linear time with the size of the index and the + * length. Negative indexes are linear with the size of the input string in + * addition to the other costs. + * + * ## Examples + * + * ```gleam + * slice(from: "gleam", at_index: 1, length: 2) + * // -> "le" + * ``` + * + * ```gleam + * slice(from: "gleam", at_index: 1, length: 10) + * // -> "leam" + * ``` + * + * ```gleam + * slice(from: "gleam", at_index: 10, length: 3) + * // -> "" + * ``` + * + * ```gleam + * slice(from: "gleam", at_index: -2, length: 2) + * // -> "am" + * ``` + * + * ```gleam + * slice(from: "gleam", at_index: -12, length: 2) + * // -> "" + * ``` + */ +export function slice(string, idx, len) { + let $ = len <= 0; + if ($) { + return ""; + } else { + let $1 = idx < 0; + if ($1) { + let translated_idx = length(string) + idx; + let $2 = translated_idx < 0; + if ($2) { + return ""; + } else { + return grapheme_slice(string, translated_idx, len); + } + } else { + return grapheme_slice(string, idx, len); + } + } +} + +/** + * Drops *n* graphemes from the end of a `String`. + * + * This function traverses the full string, so it runs in linear time with the + * size of the string. Avoid using this in a loop. + * + * ## Examples + * + * ```gleam + * drop_end(from: "Cigarette Smoking Man", up_to: 2) + * // -> "Cigarette Smoking M" + * ``` + */ +export function drop_end(string, num_graphemes) { + let $ = num_graphemes <= 0; + if ($) { + return string; + } else { + return slice(string, 0, length(string) - num_graphemes); + } +} + +/** + * Creates a new `String` by joining two `String`s together. + * + * This function typically copies both `String`s and runs in linear time, but + * the exact behaviour will depend on how the runtime you are using optimises + * your code. Benchmark and profile your code if you need to understand its + * performance better. + * + * If you are joining together large string and want to avoid copying any data + * you may want to investigate using the [`string_tree`](../gleam/string_tree.html) + * module. + * + * ## Examples + * + * ```gleam + * append(to: "butter", suffix: "fly") + * // -> "butterfly" + * ``` + */ +export function append(first, second) { + return first + second; +} + +function concat_loop(loop$strings, loop$accumulator) { + while (true) { + let strings = loop$strings; + let accumulator = loop$accumulator; + if (strings instanceof $Empty) { + return accumulator; + } else { + let string = strings.head; + let strings$1 = strings.tail; + loop$strings = strings$1; + loop$accumulator = accumulator + string; + } + } +} + +/** + * Creates a new `String` by joining many `String`s together. + * + * This function copies all the `String`s and runs in linear time. + * + * ## Examples + * + * ```gleam + * concat(["never", "the", "less"]) + * // -> "nevertheless" + * ``` + */ +export function concat(strings) { + return concat_loop(strings, ""); +} + +function repeat_loop(loop$times, loop$doubling_acc, loop$acc) { + while (true) { + let times = loop$times; + let doubling_acc = loop$doubling_acc; + let acc = loop$acc; + let _block; + let $ = times % 2; + if ($ === 0) { + _block = acc; + } else { + _block = acc + doubling_acc; + } + let acc$1 = _block; + let times$1 = globalThis.Math.trunc(times / 2); + let $1 = times$1 <= 0; + if ($1) { + return acc$1; + } else { + loop$times = times$1; + loop$doubling_acc = doubling_acc + doubling_acc; + loop$acc = acc$1; + } + } +} + +/** + * Creates a new `String` by repeating a `String` a given number of times. + * + * This function runs in loglinear time. + * + * ## Examples + * + * ```gleam + * repeat("ha", times: 3) + * // -> "hahaha" + * ``` + */ +export function repeat(string, times) { + let $ = times <= 0; + if ($) { + return ""; + } else { + return repeat_loop(times, string, ""); + } +} + +function join_loop(loop$strings, loop$separator, loop$accumulator) { + while (true) { + let strings = loop$strings; + let separator = loop$separator; + let accumulator = loop$accumulator; + if (strings instanceof $Empty) { + return accumulator; + } else { + let string = strings.head; + let strings$1 = strings.tail; + loop$strings = strings$1; + loop$separator = separator; + loop$accumulator = (accumulator + separator) + string; + } + } +} + +/** + * Joins many `String`s together with a given separator. + * + * This function runs in linear time. + * + * ## Examples + * + * ```gleam + * join(["home","evan","Desktop"], with: "/") + * // -> "home/evan/Desktop" + * ``` + */ +export function join(strings, separator) { + if (strings instanceof $Empty) { + return ""; + } else { + let first$1 = strings.head; + let rest = strings.tail; + return join_loop(rest, separator, first$1); + } +} + +function padding(size, pad_string) { + let pad_string_length = length(pad_string); + let num_pads = divideInt(size, pad_string_length); + let extra = remainderInt(size, pad_string_length); + return repeat(pad_string, num_pads) + slice(pad_string, 0, extra); +} + +/** + * Pads the start of a `String` until it has a given length. + * + * ## Examples + * + * ```gleam + * pad_start("121", to: 5, with: ".") + * // -> "..121" + * ``` + * + * ```gleam + * pad_start("121", to: 3, with: ".") + * // -> "121" + * ``` + * + * ```gleam + * pad_start("121", to: 2, with: ".") + * // -> "121" + * ``` + */ +export function pad_start(string, desired_length, pad_string) { + let current_length = length(string); + let to_pad_length = desired_length - current_length; + let $ = to_pad_length <= 0; + if ($) { + return string; + } else { + return padding(to_pad_length, pad_string) + string; + } +} + +/** + * Pads the end of a `String` until it has a given length. + * + * ## Examples + * + * ```gleam + * pad_end("123", to: 5, with: ".") + * // -> "123.." + * ``` + * + * ```gleam + * pad_end("123", to: 3, with: ".") + * // -> "123" + * ``` + * + * ```gleam + * pad_end("123", to: 2, with: ".") + * // -> "123" + * ``` + */ +export function pad_end(string, desired_length, pad_string) { + let current_length = length(string); + let to_pad_length = desired_length - current_length; + let $ = to_pad_length <= 0; + if ($) { + return string; + } else { + return string + padding(to_pad_length, pad_string); + } +} + +/** + * Removes whitespace on both sides of a `String`. + * + * Whitespace in this function is the set of nonbreakable whitespace + * codepoints, defined as Pattern_White_Space in [Unicode Standard Annex #31][1]. + * + * [1]: https://unicode.org/reports/tr31/ + * + * ## Examples + * + * ```gleam + * trim(" hats \n") + * // -> "hats" + * ``` + */ +export function trim(string) { + let _pipe = string; + let _pipe$1 = trim_start(_pipe); + return trim_end(_pipe$1); +} + +function to_graphemes_loop(loop$string, loop$acc) { + while (true) { + let string = loop$string; + let acc = loop$acc; + let $ = pop_grapheme(string); + if ($ instanceof Ok) { + let grapheme = $[0][0]; + let rest = $[0][1]; + loop$string = rest; + loop$acc = listPrepend(grapheme, acc); + } else { + return acc; + } + } +} + +/** + * Creates a list of `String`s by splitting a given string on a given substring. + * + * ## Examples + * + * ```gleam + * split("home/gleam/desktop/", on: "/") + * // -> ["home", "gleam", "desktop", ""] + * ``` + */ +export function split(x, substring) { + if (substring === "") { + return to_graphemes(x); + } else { + let _pipe = x; + let _pipe$1 = $string_tree.from_string(_pipe); + let _pipe$2 = $string_tree.split(_pipe$1, substring); + return $list.map(_pipe$2, $string_tree.to_string); + } +} + +function do_to_utf_codepoints(string) { + let _pipe = string; + let _pipe$1 = string_to_codepoint_integer_list(_pipe); + return $list.map(_pipe$1, unsafe_int_to_utf_codepoint); +} + +/** + * Converts a `String` to a `List` of `UtfCodepoint`. + * + * See and + * for an + * explanation on code points. + * + * ## Examples + * + * ```gleam + * "a" |> to_utf_codepoints + * // -> [UtfCodepoint(97)] + * ``` + * + * ```gleam + * // Semantically the same as: + * // ["🏳", "️", "‍", "🌈"] or: + * // [waving_white_flag, variant_selector_16, zero_width_joiner, rainbow] + * "🏳️‍🌈" |> to_utf_codepoints + * // -> [ + * // UtfCodepoint(127987), + * // UtfCodepoint(65039), + * // UtfCodepoint(8205), + * // UtfCodepoint(127752), + * // ] + * ``` + */ +export function to_utf_codepoints(string) { + return do_to_utf_codepoints(string); +} + +/** + * Converts an integer to a `UtfCodepoint`. + * + * Returns an `Error` if the integer does not represent a valid UTF codepoint. + */ +export function utf_codepoint(value) { + let i = value; + if (i > 1_114_111) { + return new Error(undefined); + } else { + let i$1 = value; + if ((i$1 >= 55_296) && (i$1 <= 57_343)) { + return new Error(undefined); + } else { + let i$2 = value; + if (i$2 < 0) { + return new Error(undefined); + } else { + let i$3 = value; + return new Ok(unsafe_int_to_utf_codepoint(i$3)); + } + } + } +} + +/** + * Converts a `String` into `Option(String)` where an empty `String` becomes + * `None`. + * + * ## Examples + * + * ```gleam + * to_option("") + * // -> None + * ``` + * + * ```gleam + * to_option("hats") + * // -> Some("hats") + * ``` + */ +export function to_option(string) { + if (string === "") { + return new None(); + } else { + return new Some(string); + } +} + +/** + * Returns the first grapheme cluster in a given `String` and wraps it in a + * `Result(String, Nil)`. If the `String` is empty, it returns `Error(Nil)`. + * Otherwise, it returns `Ok(String)`. + * + * ## Examples + * + * ```gleam + * first("") + * // -> Error(Nil) + * ``` + * + * ```gleam + * first("icecream") + * // -> Ok("i") + * ``` + */ +export function first(string) { + let $ = pop_grapheme(string); + if ($ instanceof Ok) { + let first$1 = $[0][0]; + return new Ok(first$1); + } else { + return $; + } +} + +/** + * Returns the last grapheme cluster in a given `String` and wraps it in a + * `Result(String, Nil)`. If the `String` is empty, it returns `Error(Nil)`. + * Otherwise, it returns `Ok(String)`. + * + * This function traverses the full string, so it runs in linear time with the + * length of the string. Avoid using this in a loop. + * + * ## Examples + * + * ```gleam + * last("") + * // -> Error(Nil) + * ``` + * + * ```gleam + * last("icecream") + * // -> Ok("m") + * ``` + */ +export function last(string) { + let $ = pop_grapheme(string); + if ($ instanceof Ok) { + let $1 = $[0][1]; + if ($1 === "") { + let first$1 = $[0][0]; + return new Ok(first$1); + } else { + let rest = $1; + return new Ok(slice(rest, -1, 1)); + } + } else { + return $; + } +} + +/** + * Creates a new `String` with the first grapheme in the input `String` + * converted to uppercase and the remaining graphemes to lowercase. + * + * ## Examples + * + * ```gleam + * capitalise("mamouna") + * // -> "Mamouna" + * ``` + */ +export function capitalise(string) { + let $ = pop_grapheme(string); + if ($ instanceof Ok) { + let first$1 = $[0][0]; + let rest = $[0][1]; + return append(uppercase(first$1), lowercase(rest)); + } else { + return ""; + } +} + +/** + * Returns a `String` representation of a term in Gleam syntax. + * + * This may be occasionally useful for quick-and-dirty printing of values in + * scripts. For error reporting and other uses prefer constructing strings by + * pattern matching on the values. + * + * ## Limitations + * + * The output format of this function is not stable and could change at any + * time. The output is not suitable for parsing. + * + * This function works using runtime reflection, so the output may not be + * perfectly accurate for data structures where the runtime structure doesn't + * hold enough information to determine the original syntax. For example, + * tuples with an Erlang atom in the first position will be mistaken for Gleam + * records. + * + * ## Security and safety + * + * There is no limit to how large the strings that this function can produce. + * Be careful not to call this function with large data structures or you + * could use very large amounts of memory, potentially causing runtime + * problems. + */ +export function inspect(term) { + let _pipe = term; + let _pipe$1 = do_inspect(_pipe); + return $string_tree.to_string(_pipe$1); +} + +/** + * Drops *n* graphemes from the start of a `String`. + * + * This function runs in linear time with the number of graphemes to drop. + * + * ## Examples + * + * ```gleam + * drop_start(from: "The Lone Gunmen", up_to: 2) + * // -> "e Lone Gunmen" + * ``` + */ +export function drop_start(string, num_graphemes) { + let $ = num_graphemes <= 0; + if ($) { + return string; + } else { + let prefix = grapheme_slice(string, 0, num_graphemes); + let prefix_size = byte_size(prefix); + return unsafe_byte_slice( + string, + prefix_size, + byte_size(string) - prefix_size, + ); + } +} diff --git a/build/dev/javascript/gleam_stdlib/gleam/string_tree.mjs b/build/dev/javascript/gleam_stdlib/gleam/string_tree.mjs new file mode 100644 index 0000000..a8b24ce --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam/string_tree.mjs @@ -0,0 +1,133 @@ +import { toList, CustomType as $CustomType, isEqual } from "../gleam.mjs"; +import * as $list from "../gleam/list.mjs"; +import { + add as append_tree, + concat as from_strings, + concat, + identity as from_string, + identity as to_string, + length as byte_size, + lowercase, + uppercase, + graphemes as do_to_graphemes, + split, + string_replace as replace, +} from "../gleam_stdlib.mjs"; + +export { + append_tree, + byte_size, + concat, + from_string, + from_strings, + lowercase, + replace, + split, + to_string, + uppercase, +}; + +class All extends $CustomType {} + +/** + * Prepends some `StringTree` onto the start of another. + * + * Runs in constant time. + */ +export function prepend_tree(tree, prefix) { + return append_tree(prefix, tree); +} + +/** + * Create an empty `StringTree`. Useful as the start of a pipe chaining many + * trees together. + */ +export function new$() { + return from_strings(toList([])); +} + +/** + * Prepends a `String` onto the start of some `StringTree`. + * + * Runs in constant time. + */ +export function prepend(tree, prefix) { + return append_tree(from_string(prefix), tree); +} + +/** + * Appends a `String` onto the end of some `StringTree`. + * + * Runs in constant time. + */ +export function append(tree, second) { + return append_tree(tree, from_string(second)); +} + +/** + * Joins the given trees into a new tree separated with the given string. + */ +export function join(trees, sep) { + let _pipe = trees; + let _pipe$1 = $list.intersperse(_pipe, from_string(sep)); + return concat(_pipe$1); +} + +/** + * Converts a `StringTree` to a new one with the contents reversed. + */ +export function reverse(tree) { + let _pipe = tree; + let _pipe$1 = to_string(_pipe); + let _pipe$2 = do_to_graphemes(_pipe$1); + let _pipe$3 = $list.reverse(_pipe$2); + return from_strings(_pipe$3); +} + +/** + * Compares two string trees to determine if they have the same textual + * content. + * + * Comparing two string trees 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 + * + * ```gleam + * from_strings(["a", "b"]) == from_string("ab") + * // -> False + * ``` + * + * ```gleam + * is_equal(from_strings(["a", "b"]), from_string("ab")) + * // -> True + * ``` + */ +export function is_equal(a, b) { + return isEqual(a, b); +} + +/** + * Inspects a `StringTree` to determine if it is equivalent to an empty string. + * + * ## Examples + * + * ```gleam + * from_string("ok") |> is_empty + * // -> False + * ``` + * + * ```gleam + * from_string("") |> is_empty + * // -> True + * ``` + * + * ```gleam + * from_strings([]) |> is_empty + * // -> True + * ``` + */ +export function is_empty(tree) { + return isEqual(from_string(""), tree); +} diff --git a/build/dev/javascript/gleam_stdlib/gleam/uri.mjs b/build/dev/javascript/gleam_stdlib/gleam/uri.mjs new file mode 100644 index 0000000..01f4bba --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam/uri.mjs @@ -0,0 +1,1147 @@ +import { + Ok, + Error, + toList, + Empty as $Empty, + prepend as listPrepend, + CustomType as $CustomType, + isEqual, +} from "../gleam.mjs"; +import * as $int from "../gleam/int.mjs"; +import * as $list from "../gleam/list.mjs"; +import * as $option from "../gleam/option.mjs"; +import { None, Some } from "../gleam/option.mjs"; +import * as $string from "../gleam/string.mjs"; +import * as $string_tree from "../gleam/string_tree.mjs"; +import { + pop_codeunit, + string_codeunit_slice as codeunit_slice, + parse_query, + percent_encode, + percent_decode, +} from "../gleam_stdlib.mjs"; + +export { parse_query, percent_decode, percent_encode }; + +export class Uri extends $CustomType { + constructor(scheme, userinfo, host, port, path, query, fragment) { + super(); + this.scheme = scheme; + this.userinfo = userinfo; + this.host = host; + this.port = port; + this.path = path; + this.query = query; + this.fragment = fragment; + } +} +export const Uri$Uri = (scheme, userinfo, host, port, path, query, fragment) => + new Uri(scheme, userinfo, host, port, path, query, fragment); +export const Uri$isUri = (value) => value instanceof Uri; +export const Uri$Uri$scheme = (value) => value.scheme; +export const Uri$Uri$0 = (value) => value.scheme; +export const Uri$Uri$userinfo = (value) => value.userinfo; +export const Uri$Uri$1 = (value) => value.userinfo; +export const Uri$Uri$host = (value) => value.host; +export const Uri$Uri$2 = (value) => value.host; +export const Uri$Uri$port = (value) => value.port; +export const Uri$Uri$3 = (value) => value.port; +export const Uri$Uri$path = (value) => value.path; +export const Uri$Uri$4 = (value) => value.path; +export const Uri$Uri$query = (value) => value.query; +export const Uri$Uri$5 = (value) => value.query; +export const Uri$Uri$fragment = (value) => value.fragment; +export const Uri$Uri$6 = (value) => value.fragment; + +function is_valid_host_within_brackets_char(char) { + return (((((48 >= char) && (char <= 57)) || ((65 >= char) && (char <= 90))) || ((97 >= char) && (char <= 122))) || (char === 58)) || (char === 46); +} + +function parse_fragment(rest, pieces) { + return new Ok( + new Uri( + pieces.scheme, + pieces.userinfo, + pieces.host, + pieces.port, + pieces.path, + pieces.query, + new Some(rest), + ), + ); +} + +function parse_query_with_question_mark_loop( + loop$original, + loop$uri_string, + loop$pieces, + loop$size +) { + while (true) { + let original = loop$original; + let uri_string = loop$uri_string; + let pieces = loop$pieces; + let size = loop$size; + if (uri_string.startsWith("#")) { + if (size === 0) { + let rest = uri_string.slice(1); + return parse_fragment(rest, pieces); + } else { + let rest = uri_string.slice(1); + let query = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + pieces.host, + pieces.port, + pieces.path, + new Some(query), + pieces.fragment, + ); + return parse_fragment(rest, pieces$1); + } + } else if (uri_string === "") { + return new Ok( + new Uri( + pieces.scheme, + pieces.userinfo, + pieces.host, + pieces.port, + pieces.path, + new Some(original), + pieces.fragment, + ), + ); + } else { + let $ = pop_codeunit(uri_string); + let rest; + rest = $[1]; + loop$original = original; + loop$uri_string = rest; + loop$pieces = pieces; + loop$size = size + 1; + } + } +} + +function parse_query_with_question_mark(uri_string, pieces) { + return parse_query_with_question_mark_loop(uri_string, uri_string, pieces, 0); +} + +function parse_path_loop(loop$original, loop$uri_string, loop$pieces, loop$size) { + while (true) { + let original = loop$original; + let uri_string = loop$uri_string; + let pieces = loop$pieces; + let size = loop$size; + if (uri_string.startsWith("?")) { + let rest = uri_string.slice(1); + let path = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + pieces.host, + pieces.port, + path, + pieces.query, + pieces.fragment, + ); + return parse_query_with_question_mark(rest, pieces$1); + } else if (uri_string.startsWith("#")) { + let rest = uri_string.slice(1); + let path = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + pieces.host, + pieces.port, + path, + pieces.query, + pieces.fragment, + ); + return parse_fragment(rest, pieces$1); + } else if (uri_string === "") { + return new Ok( + new Uri( + pieces.scheme, + pieces.userinfo, + pieces.host, + pieces.port, + original, + pieces.query, + pieces.fragment, + ), + ); + } else { + let $ = pop_codeunit(uri_string); + let rest; + rest = $[1]; + loop$original = original; + loop$uri_string = rest; + loop$pieces = pieces; + loop$size = size + 1; + } + } +} + +function parse_path(uri_string, pieces) { + return parse_path_loop(uri_string, uri_string, pieces, 0); +} + +function parse_port_loop(loop$uri_string, loop$pieces, loop$port) { + while (true) { + let uri_string = loop$uri_string; + let pieces = loop$pieces; + let port = loop$port; + if (uri_string.startsWith("0")) { + let rest = uri_string.slice(1); + loop$uri_string = rest; + loop$pieces = pieces; + loop$port = port * 10; + } else if (uri_string.startsWith("1")) { + let rest = uri_string.slice(1); + loop$uri_string = rest; + loop$pieces = pieces; + loop$port = port * 10 + 1; + } else if (uri_string.startsWith("2")) { + let rest = uri_string.slice(1); + loop$uri_string = rest; + loop$pieces = pieces; + loop$port = port * 10 + 2; + } else if (uri_string.startsWith("3")) { + let rest = uri_string.slice(1); + loop$uri_string = rest; + loop$pieces = pieces; + loop$port = port * 10 + 3; + } else if (uri_string.startsWith("4")) { + let rest = uri_string.slice(1); + loop$uri_string = rest; + loop$pieces = pieces; + loop$port = port * 10 + 4; + } else if (uri_string.startsWith("5")) { + let rest = uri_string.slice(1); + loop$uri_string = rest; + loop$pieces = pieces; + loop$port = port * 10 + 5; + } else if (uri_string.startsWith("6")) { + let rest = uri_string.slice(1); + loop$uri_string = rest; + loop$pieces = pieces; + loop$port = port * 10 + 6; + } else if (uri_string.startsWith("7")) { + let rest = uri_string.slice(1); + loop$uri_string = rest; + loop$pieces = pieces; + loop$port = port * 10 + 7; + } else if (uri_string.startsWith("8")) { + let rest = uri_string.slice(1); + loop$uri_string = rest; + loop$pieces = pieces; + loop$port = port * 10 + 8; + } else if (uri_string.startsWith("9")) { + let rest = uri_string.slice(1); + loop$uri_string = rest; + loop$pieces = pieces; + loop$port = port * 10 + 9; + } else if (uri_string.startsWith("?")) { + let rest = uri_string.slice(1); + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + pieces.host, + new Some(port), + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_query_with_question_mark(rest, pieces$1); + } else if (uri_string.startsWith("#")) { + let rest = uri_string.slice(1); + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + pieces.host, + new Some(port), + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_fragment(rest, pieces$1); + } else if (uri_string.startsWith("/")) { + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + pieces.host, + new Some(port), + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_path(uri_string, pieces$1); + } else if (uri_string === "") { + return new Ok( + new Uri( + pieces.scheme, + pieces.userinfo, + pieces.host, + new Some(port), + pieces.path, + pieces.query, + pieces.fragment, + ), + ); + } else { + return new Error(undefined); + } + } +} + +function parse_port(uri_string, pieces) { + if (uri_string.startsWith(":0")) { + let rest = uri_string.slice(2); + return parse_port_loop(rest, pieces, 0); + } else if (uri_string.startsWith(":1")) { + let rest = uri_string.slice(2); + return parse_port_loop(rest, pieces, 1); + } else if (uri_string.startsWith(":2")) { + let rest = uri_string.slice(2); + return parse_port_loop(rest, pieces, 2); + } else if (uri_string.startsWith(":3")) { + let rest = uri_string.slice(2); + return parse_port_loop(rest, pieces, 3); + } else if (uri_string.startsWith(":4")) { + let rest = uri_string.slice(2); + return parse_port_loop(rest, pieces, 4); + } else if (uri_string.startsWith(":5")) { + let rest = uri_string.slice(2); + return parse_port_loop(rest, pieces, 5); + } else if (uri_string.startsWith(":6")) { + let rest = uri_string.slice(2); + return parse_port_loop(rest, pieces, 6); + } else if (uri_string.startsWith(":7")) { + let rest = uri_string.slice(2); + return parse_port_loop(rest, pieces, 7); + } else if (uri_string.startsWith(":8")) { + let rest = uri_string.slice(2); + return parse_port_loop(rest, pieces, 8); + } else if (uri_string.startsWith(":9")) { + let rest = uri_string.slice(2); + return parse_port_loop(rest, pieces, 9); + } else if (uri_string === ":") { + return new Ok(pieces); + } else if (uri_string === "") { + return new Ok(pieces); + } else if (uri_string.startsWith("?")) { + let rest = uri_string.slice(1); + return parse_query_with_question_mark(rest, pieces); + } else if (uri_string.startsWith(":?")) { + let rest = uri_string.slice(2); + return parse_query_with_question_mark(rest, pieces); + } else if (uri_string.startsWith("#")) { + let rest = uri_string.slice(1); + return parse_fragment(rest, pieces); + } else if (uri_string.startsWith(":#")) { + let rest = uri_string.slice(2); + return parse_fragment(rest, pieces); + } else if (uri_string.startsWith("/")) { + return parse_path(uri_string, pieces); + } else if (uri_string.startsWith(":")) { + let rest = uri_string.slice(1); + if (rest.startsWith("/")) { + return parse_path(rest, pieces); + } else { + return new Error(undefined); + } + } else { + return new Error(undefined); + } +} + +function parse_host_outside_of_brackets_loop( + loop$original, + loop$uri_string, + loop$pieces, + loop$size +) { + while (true) { + let original = loop$original; + let uri_string = loop$uri_string; + let pieces = loop$pieces; + let size = loop$size; + if (uri_string === "") { + return new Ok( + new Uri( + pieces.scheme, + pieces.userinfo, + new Some(original), + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ), + ); + } else if (uri_string.startsWith(":")) { + let host = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + new Some(host), + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_port(uri_string, pieces$1); + } else if (uri_string.startsWith("/")) { + let host = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + new Some(host), + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_path(uri_string, pieces$1); + } else if (uri_string.startsWith("?")) { + let rest = uri_string.slice(1); + let host = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + new Some(host), + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_query_with_question_mark(rest, pieces$1); + } else if (uri_string.startsWith("#")) { + let rest = uri_string.slice(1); + let host = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + new Some(host), + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_fragment(rest, pieces$1); + } else { + let $ = pop_codeunit(uri_string); + let rest; + rest = $[1]; + loop$original = original; + loop$uri_string = rest; + loop$pieces = pieces; + loop$size = size + 1; + } + } +} + +function parse_host_within_brackets_loop( + loop$original, + loop$uri_string, + loop$pieces, + loop$size +) { + while (true) { + let original = loop$original; + let uri_string = loop$uri_string; + let pieces = loop$pieces; + let size = loop$size; + if (uri_string === "") { + return new Ok( + new Uri( + pieces.scheme, + pieces.userinfo, + new Some(uri_string), + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ), + ); + } else if (uri_string.startsWith("]")) { + if (size === 0) { + let rest = uri_string.slice(1); + return parse_port(rest, pieces); + } else { + let rest = uri_string.slice(1); + let host = codeunit_slice(original, 0, size + 1); + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + new Some(host), + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_port(rest, pieces$1); + } + } else if (uri_string.startsWith("/")) { + if (size === 0) { + return parse_path(uri_string, pieces); + } else { + let host = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + new Some(host), + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_path(uri_string, pieces$1); + } + } else if (uri_string.startsWith("?")) { + if (size === 0) { + let rest = uri_string.slice(1); + return parse_query_with_question_mark(rest, pieces); + } else { + let rest = uri_string.slice(1); + let host = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + new Some(host), + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_query_with_question_mark(rest, pieces$1); + } + } else if (uri_string.startsWith("#")) { + if (size === 0) { + let rest = uri_string.slice(1); + return parse_fragment(rest, pieces); + } else { + let rest = uri_string.slice(1); + let host = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + new Some(host), + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_fragment(rest, pieces$1); + } + } else { + let $ = pop_codeunit(uri_string); + let char; + let rest; + char = $[0]; + rest = $[1]; + let $1 = is_valid_host_within_brackets_char(char); + if ($1) { + loop$original = original; + loop$uri_string = rest; + loop$pieces = pieces; + loop$size = size + 1; + } else { + return parse_host_outside_of_brackets_loop( + original, + original, + pieces, + 0, + ); + } + } + } +} + +function parse_host_within_brackets(uri_string, pieces) { + return parse_host_within_brackets_loop(uri_string, uri_string, pieces, 0); +} + +function parse_host_outside_of_brackets(uri_string, pieces) { + return parse_host_outside_of_brackets_loop(uri_string, uri_string, pieces, 0); +} + +function parse_host(uri_string, pieces) { + if (uri_string.startsWith("[")) { + return parse_host_within_brackets(uri_string, pieces); + } else if (uri_string.startsWith(":")) { + let pieces$1 = new Uri( + pieces.scheme, + pieces.userinfo, + new Some(""), + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_port(uri_string, pieces$1); + } else if (uri_string === "") { + return new Ok( + new Uri( + pieces.scheme, + pieces.userinfo, + new Some(""), + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ), + ); + } else { + return parse_host_outside_of_brackets(uri_string, pieces); + } +} + +function parse_userinfo_loop( + loop$original, + loop$uri_string, + loop$pieces, + loop$size +) { + while (true) { + let original = loop$original; + let uri_string = loop$uri_string; + let pieces = loop$pieces; + let size = loop$size; + if (uri_string.startsWith("@")) { + if (size === 0) { + let rest = uri_string.slice(1); + return parse_host(rest, pieces); + } else { + let rest = uri_string.slice(1); + let userinfo = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + pieces.scheme, + new Some(userinfo), + pieces.host, + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_host(rest, pieces$1); + } + } else if (uri_string === "") { + return parse_host(original, pieces); + } else if (uri_string.startsWith("/")) { + return parse_host(original, pieces); + } else if (uri_string.startsWith("?")) { + return parse_host(original, pieces); + } else if (uri_string.startsWith("#")) { + return parse_host(original, pieces); + } else { + let $ = pop_codeunit(uri_string); + let rest; + rest = $[1]; + loop$original = original; + loop$uri_string = rest; + loop$pieces = pieces; + loop$size = size + 1; + } + } +} + +function parse_authority_pieces(string, pieces) { + return parse_userinfo_loop(string, string, pieces, 0); +} + +function parse_authority_with_slashes(uri_string, pieces) { + if (uri_string === "//") { + return new Ok( + new Uri( + pieces.scheme, + pieces.userinfo, + new Some(""), + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ), + ); + } else if (uri_string.startsWith("//")) { + let rest = uri_string.slice(2); + return parse_authority_pieces(rest, pieces); + } else { + return parse_path(uri_string, pieces); + } +} + +function parse_scheme_loop( + loop$original, + loop$uri_string, + loop$pieces, + loop$size +) { + while (true) { + let original = loop$original; + let uri_string = loop$uri_string; + let pieces = loop$pieces; + let size = loop$size; + if (uri_string.startsWith("/")) { + if (size === 0) { + return parse_authority_with_slashes(uri_string, pieces); + } else { + let scheme = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + new Some($string.lowercase(scheme)), + pieces.userinfo, + pieces.host, + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_authority_with_slashes(uri_string, pieces$1); + } + } else if (uri_string.startsWith("?")) { + if (size === 0) { + let rest = uri_string.slice(1); + return parse_query_with_question_mark(rest, pieces); + } else { + let rest = uri_string.slice(1); + let scheme = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + new Some($string.lowercase(scheme)), + pieces.userinfo, + pieces.host, + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_query_with_question_mark(rest, pieces$1); + } + } else if (uri_string.startsWith("#")) { + if (size === 0) { + let rest = uri_string.slice(1); + return parse_fragment(rest, pieces); + } else { + let rest = uri_string.slice(1); + let scheme = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + new Some($string.lowercase(scheme)), + pieces.userinfo, + pieces.host, + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_fragment(rest, pieces$1); + } + } else if (uri_string.startsWith(":")) { + if (size === 0) { + return new Error(undefined); + } else { + let rest = uri_string.slice(1); + let scheme = codeunit_slice(original, 0, size); + let pieces$1 = new Uri( + new Some($string.lowercase(scheme)), + pieces.userinfo, + pieces.host, + pieces.port, + pieces.path, + pieces.query, + pieces.fragment, + ); + return parse_authority_with_slashes(rest, pieces$1); + } + } else if (uri_string === "") { + return new Ok( + new Uri( + pieces.scheme, + pieces.userinfo, + pieces.host, + pieces.port, + original, + pieces.query, + pieces.fragment, + ), + ); + } else { + let $ = pop_codeunit(uri_string); + let rest; + rest = $[1]; + loop$original = original; + loop$uri_string = rest; + loop$pieces = pieces; + loop$size = size + 1; + } + } +} + +function query_pair(pair) { + return $string_tree.from_strings( + toList([percent_encode(pair[0]), "=", percent_encode(pair[1])]), + ); +} + +/** + * Encodes a list of key value pairs as a URI query string. + * + * The opposite operation is `uri.parse_query`. + * + * ## Examples + * + * ```gleam + * query_to_string([#("a", "1"), #("b", "2")]) + * // -> "a=1&b=2" + * ``` + */ +export function query_to_string(query) { + let _pipe = query; + let _pipe$1 = $list.map(_pipe, query_pair); + let _pipe$2 = $list.intersperse(_pipe$1, $string_tree.from_string("&")); + let _pipe$3 = $string_tree.concat(_pipe$2); + return $string_tree.to_string(_pipe$3); +} + +function remove_dot_segments_loop(loop$input, loop$accumulator) { + while (true) { + let input = loop$input; + let accumulator = loop$accumulator; + if (input instanceof $Empty) { + return $list.reverse(accumulator); + } else { + let segment = input.head; + let rest = input.tail; + let _block; + if (segment === "") { + _block = accumulator; + } else if (segment === ".") { + _block = accumulator; + } else if (segment === "..") { + if (accumulator instanceof $Empty) { + _block = accumulator; + } else { + let accumulator$1 = accumulator.tail; + _block = accumulator$1; + } + } else { + let segment$1 = segment; + let accumulator$1 = accumulator; + _block = listPrepend(segment$1, accumulator$1); + } + let accumulator$1 = _block; + loop$input = rest; + loop$accumulator = accumulator$1; + } + } +} + +function remove_dot_segments(input) { + return remove_dot_segments_loop(input, toList([])); +} + +/** + * Splits 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. + * + * ## Examples + * + * ```gleam + * path_segments("/users/1") + * // -> ["users" ,"1"] + * ``` + */ +export function path_segments(path) { + return remove_dot_segments($string.split(path, "/")); +} + +/** + * Encodes a `Uri` value as a URI string. + * + * The opposite operation is `uri.parse`. + * + * ## Examples + * + * ```gleam + * let uri = Uri(..empty, scheme: Some("https"), host: Some("example.com")) + * to_string(uri) + * // -> "https://example.com" + * ``` + */ +export function to_string(uri) { + let _block; + let $ = uri.fragment; + if ($ instanceof Some) { + let fragment = $[0]; + _block = toList(["#", fragment]); + } else { + _block = toList([]); + } + let parts = _block; + let _block$1; + let $1 = uri.query; + if ($1 instanceof Some) { + let query = $1[0]; + _block$1 = listPrepend("?", listPrepend(query, parts)); + } else { + _block$1 = parts; + } + let parts$1 = _block$1; + let parts$2 = listPrepend(uri.path, parts$1); + let _block$2; + let $2 = uri.host; + let $3 = $string.starts_with(uri.path, "/"); + if ($2 instanceof Some && !$3) { + let host = $2[0]; + if (host !== "") { + _block$2 = listPrepend("/", parts$2); + } else { + _block$2 = parts$2; + } + } else { + _block$2 = parts$2; + } + let parts$3 = _block$2; + let _block$3; + let $4 = uri.host; + let $5 = uri.port; + if ($4 instanceof Some && $5 instanceof Some) { + let port = $5[0]; + _block$3 = listPrepend(":", listPrepend($int.to_string(port), parts$3)); + } else { + _block$3 = parts$3; + } + let parts$4 = _block$3; + let _block$4; + let $6 = uri.scheme; + let $7 = uri.userinfo; + let $8 = uri.host; + if ($6 instanceof Some) { + if ($7 instanceof Some) { + if ($8 instanceof Some) { + let s = $6[0]; + let u = $7[0]; + let h = $8[0]; + _block$4 = listPrepend( + s, + listPrepend( + "://", + listPrepend(u, listPrepend("@", listPrepend(h, parts$4))), + ), + ); + } else { + let s = $6[0]; + _block$4 = listPrepend(s, listPrepend(":", parts$4)); + } + } else if ($8 instanceof Some) { + let s = $6[0]; + let h = $8[0]; + _block$4 = listPrepend(s, listPrepend("://", listPrepend(h, parts$4))); + } else { + let s = $6[0]; + _block$4 = listPrepend(s, listPrepend(":", parts$4)); + } + } else if ($7 instanceof None && $8 instanceof Some) { + let h = $8[0]; + _block$4 = listPrepend("//", listPrepend(h, parts$4)); + } else { + _block$4 = parts$4; + } + let parts$5 = _block$4; + return $string.concat(parts$5); +} + +/** + * Fetches the origin of a URI. + * + * Returns the origin of a uri as defined in + * [RFC 6454](https://tools.ietf.org/html/rfc6454) + * + * The supported URI schemes are `http` and `https`. + * URLs without a scheme will return `Error`. + * + * ## Examples + * + * ```gleam + * let assert Ok(uri) = parse("https://example.com/path?foo#bar") + * origin(uri) + * // -> Ok("https://example.com") + * ``` + */ +export function origin(uri) { + let scheme; + let host; + let port; + scheme = uri.scheme; + host = uri.host; + port = uri.port; + if (host instanceof Some && scheme instanceof Some) { + let $ = scheme[0]; + if ($ === "https" && isEqual(port, new Some(443))) { + let h = host[0]; + return new Ok($string.concat(toList(["https://", h]))); + } else if ($ === "http" && isEqual(port, new Some(80))) { + let h = host[0]; + return new Ok($string.concat(toList(["http://", h]))); + } else { + let s = $; + if ((s === "http") || (s === "https")) { + let h = host[0]; + if (port instanceof Some) { + let p = port[0]; + return new Ok( + $string.concat(toList([s, "://", h, ":", $int.to_string(p)])), + ); + } else { + return new Ok($string.concat(toList([s, "://", h]))); + } + } else { + return new Error(undefined); + } + } + } else { + return new Error(undefined); + } +} + +function drop_last(elements) { + return $list.take(elements, $list.length(elements) - 1); +} + +function join_segments(segments) { + return $string.join(listPrepend("", segments), "/"); +} + +/** + * Resolves 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). + */ +export function merge(base, relative) { + let $ = base.scheme; + if ($ instanceof Some) { + let $1 = base.host; + if ($1 instanceof Some) { + let $2 = relative.host; + if ($2 instanceof Some) { + let _block; + let _pipe = relative.path; + let _pipe$1 = $string.split(_pipe, "/"); + let _pipe$2 = remove_dot_segments(_pipe$1); + _block = join_segments(_pipe$2); + let path = _block; + let resolved = new Uri( + $option.or(relative.scheme, base.scheme), + new None(), + relative.host, + $option.or(relative.port, base.port), + path, + relative.query, + relative.fragment, + ); + return new Ok(resolved); + } else { + let _block; + let $4 = relative.path; + if ($4 === "") { + _block = [base.path, $option.or(relative.query, base.query)]; + } else { + let _block$1; + let $5 = $string.starts_with(relative.path, "/"); + if ($5) { + _block$1 = $string.split(relative.path, "/"); + } else { + let _pipe = base.path; + let _pipe$1 = $string.split(_pipe, "/"); + let _pipe$2 = drop_last(_pipe$1); + _block$1 = $list.append(_pipe$2, $string.split(relative.path, "/")); + } + let path_segments$1 = _block$1; + let _block$2; + let _pipe = path_segments$1; + let _pipe$1 = remove_dot_segments(_pipe); + _block$2 = join_segments(_pipe$1); + let path = _block$2; + _block = [path, relative.query]; + } + let $3 = _block; + let new_path; + let new_query; + new_path = $3[0]; + new_query = $3[1]; + let resolved = new Uri( + base.scheme, + new None(), + base.host, + base.port, + new_path, + new_query, + relative.fragment, + ); + return new Ok(resolved); + } + } else { + return new Error(undefined); + } + } else { + return new Error(undefined); + } +} + +/** + * Constant representing an empty URI, equivalent to "". + * + * ## Examples + * + * ```gleam + * let uri = Uri(..empty, scheme: Some("https"), host: Some("example.com")) + * // -> Uri( + * // scheme: Some("https"), + * // userinfo: None, + * // host: Some("example.com"), + * // port: None, + * // path: "", + * // query: None, + * // fragment: None, + * // ) + * ``` + */ +export const empty = /* @__PURE__ */ new Uri( + /* @__PURE__ */ new None(), + /* @__PURE__ */ new None(), + /* @__PURE__ */ new None(), + /* @__PURE__ */ new None(), + "", + /* @__PURE__ */ new None(), + /* @__PURE__ */ new None(), +); + +/** + * 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`. + * + * ## Examples + * + * ```gleam + * parse("https://example.com:1234/a/b?query=true#fragment") + * // -> Ok( + * // Uri( + * // scheme: Some("https"), + * // userinfo: None, + * // host: Some("example.com"), + * // port: Some(1234), + * // path: "/a/b", + * // query: Some("query=true"), + * // fragment: Some("fragment") + * // ) + * // ) + * ``` + */ +export function parse(uri_string) { + return parse_scheme_loop(uri_string, uri_string, empty, 0); +} diff --git a/build/dev/javascript/gleam_stdlib/gleam@bit_array.erl b/build/dev/javascript/gleam_stdlib/gleam@bit_array.erl new file mode 100644 index 0000000..7df56ce --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam@bit_array.erl @@ -0,0 +1,347 @@ +-module(gleam@bit_array). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/bit_array.gleam"). +-export([from_string/1, bit_size/1, byte_size/1, pad_to_bytes/1, slice/3, is_utf8/1, to_string/1, concat/1, append/2, base64_encode/2, base64_decode/1, base64_url_encode/2, base64_url_decode/1, base16_encode/1, base16_decode/1, inspect/1, compare/2, starts_with/2]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC(" BitArrays are a sequence of binary data of any length.\n"). + +-file("src/gleam/bit_array.gleam", 11). +?DOC(" Converts a UTF-8 `String` type into a `BitArray`.\n"). +-spec from_string(binary()) -> bitstring(). +from_string(X) -> + gleam_stdlib:identity(X). + +-file("src/gleam/bit_array.gleam", 17). +?DOC(" Returns an integer which is the number of bits in the bit array.\n"). +-spec bit_size(bitstring()) -> integer(). +bit_size(X) -> + erlang:bit_size(X). + +-file("src/gleam/bit_array.gleam", 23). +?DOC(" Returns an integer which is the number of bytes in the bit array.\n"). +-spec byte_size(bitstring()) -> integer(). +byte_size(X) -> + erlang:byte_size(X). + +-file("src/gleam/bit_array.gleam", 29). +?DOC(" Pads a bit array with zeros so that it is a whole number of bytes.\n"). +-spec pad_to_bytes(bitstring()) -> bitstring(). +pad_to_bytes(X) -> + gleam_stdlib:bit_array_pad_to_bytes(X). + +-file("src/gleam/bit_array.gleam", 54). +?DOC( + " Extracts a sub-section of a bit array.\n" + "\n" + " The slice will start at given position and continue up to specified\n" + " length.\n" + " A negative length can be used to extract bytes at the end of a bit array.\n" + "\n" + " This function runs in constant time.\n" +). +-spec slice(bitstring(), integer(), integer()) -> {ok, bitstring()} | + {error, nil}. +slice(String, Position, Length) -> + gleam_stdlib:bit_array_slice(String, Position, Length). + +-file("src/gleam/bit_array.gleam", 67). +-spec is_utf8_loop(bitstring()) -> boolean(). +is_utf8_loop(Bits) -> + case Bits of + <<>> -> + true; + + <<_/utf8, Rest/binary>> -> + is_utf8_loop(Rest); + + _ -> + false + end. + +-file("src/gleam/bit_array.gleam", 62). +?DOC(" Tests to see whether a bit array is valid UTF-8.\n"). +-spec is_utf8(bitstring()) -> boolean(). +is_utf8(Bits) -> + is_utf8_loop(Bits). + +-file("src/gleam/bit_array.gleam", 88). +?DOC( + " Converts a bit array to a string.\n" + "\n" + " Returns an error if the bit array is invalid UTF-8 data.\n" +). +-spec to_string(bitstring()) -> {ok, binary()} | {error, nil}. +to_string(Bits) -> + case is_utf8(Bits) of + true -> + {ok, gleam_stdlib:identity(Bits)}; + + false -> + {error, nil} + end. + +-file("src/gleam/bit_array.gleam", 109). +?DOC( + " Creates a new bit array by joining multiple binaries.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " concat([from_string(\"butter\"), from_string(\"fly\")])\n" + " // -> from_string(\"butterfly\")\n" + " ```\n" +). +-spec concat(list(bitstring())) -> bitstring(). +concat(Bit_arrays) -> + gleam_stdlib:bit_array_concat(Bit_arrays). + +-file("src/gleam/bit_array.gleam", 40). +?DOC( + " Creates a new bit array by joining two bit arrays.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " append(to: from_string(\"butter\"), suffix: from_string(\"fly\"))\n" + " // -> from_string(\"butterfly\")\n" + " ```\n" +). +-spec append(bitstring(), bitstring()) -> bitstring(). +append(First, Second) -> + gleam_stdlib:bit_array_concat([First, Second]). + +-file("src/gleam/bit_array.gleam", 118). +?DOC( + " Encodes a BitArray into a base 64 encoded string.\n" + "\n" + " If the bit array does not contain a whole number of bytes then it is padded\n" + " with zero bits prior to being encoded.\n" +). +-spec base64_encode(bitstring(), boolean()) -> binary(). +base64_encode(Input, Padding) -> + gleam_stdlib:base64_encode(Input, Padding). + +-file("src/gleam/bit_array.gleam", 122). +?DOC(" Decodes a base 64 encoded string into a `BitArray`.\n"). +-spec base64_decode(binary()) -> {ok, bitstring()} | {error, nil}. +base64_decode(Encoded) -> + Padded = case erlang:byte_size(gleam_stdlib:identity(Encoded)) rem 4 of + 0 -> + Encoded; + + N -> + gleam@string:append( + Encoded, + gleam@string:repeat(<<"="/utf8>>, 4 - N) + ) + end, + gleam_stdlib:base64_decode(Padded). + +-file("src/gleam/bit_array.gleam", 140). +?DOC( + " Encodes a `BitArray` into a base 64 encoded string with URL and filename\n" + " safe alphabet.\n" + "\n" + " If the bit array does not contain a whole number of bytes then it is padded\n" + " with zero bits prior to being encoded.\n" +). +-spec base64_url_encode(bitstring(), boolean()) -> binary(). +base64_url_encode(Input, Padding) -> + _pipe = Input, + _pipe@1 = gleam_stdlib:base64_encode(_pipe, Padding), + _pipe@2 = gleam@string:replace(_pipe@1, <<"+"/utf8>>, <<"-"/utf8>>), + gleam@string:replace(_pipe@2, <<"/"/utf8>>, <<"_"/utf8>>). + +-file("src/gleam/bit_array.gleam", 150). +?DOC( + " Decodes a base 64 encoded string with URL and filename safe alphabet into a\n" + " `BitArray`.\n" +). +-spec base64_url_decode(binary()) -> {ok, bitstring()} | {error, nil}. +base64_url_decode(Encoded) -> + _pipe = Encoded, + _pipe@1 = gleam@string:replace(_pipe, <<"-"/utf8>>, <<"+"/utf8>>), + _pipe@2 = gleam@string:replace(_pipe@1, <<"_"/utf8>>, <<"/"/utf8>>), + base64_decode(_pipe@2). + +-file("src/gleam/bit_array.gleam", 164). +?DOC( + " Encodes a `BitArray` into a base 16 encoded string.\n" + "\n" + " If the bit array does not contain a whole number of bytes then it is padded\n" + " with zero bits prior to being encoded.\n" +). +-spec base16_encode(bitstring()) -> binary(). +base16_encode(Input) -> + gleam_stdlib:base16_encode(Input). + +-file("src/gleam/bit_array.gleam", 170). +?DOC(" Decodes a base 16 encoded string into a `BitArray`.\n"). +-spec base16_decode(binary()) -> {ok, bitstring()} | {error, nil}. +base16_decode(Input) -> + gleam_stdlib:base16_decode(Input). + +-file("src/gleam/bit_array.gleam", 191). +-spec inspect_loop(bitstring(), binary()) -> binary(). +inspect_loop(Input, Accumulator) -> + case Input of + <<>> -> + Accumulator; + + <> -> + <<<>/binary, + ":size(1)"/utf8>>; + + <> -> + <<<>/binary, + ":size(2)"/utf8>>; + + <> -> + <<<>/binary, + ":size(3)"/utf8>>; + + <> -> + <<<>/binary, + ":size(4)"/utf8>>; + + <> -> + <<<>/binary, + ":size(5)"/utf8>>; + + <> -> + <<<>/binary, + ":size(6)"/utf8>>; + + <> -> + <<<>/binary, + ":size(7)"/utf8>>; + + <> -> + Suffix = case Rest of + <<>> -> + <<""/utf8>>; + + _ -> + <<", "/utf8>> + end, + Accumulator@1 = <<<>/binary, + Suffix/binary>>, + inspect_loop(Rest, Accumulator@1); + + _ -> + Accumulator + end. + +-file("src/gleam/bit_array.gleam", 187). +?DOC( + " Converts a bit array to a string containing the decimal value of each byte.\n" + "\n" + " Use this over `string.inspect` when you have a bit array you want printed\n" + " in the array syntax even if it is valid UTF-8.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " inspect(<<0, 20, 0x20, 255>>)\n" + " // -> \"<<0, 20, 32, 255>>\"\n" + "\n" + " inspect(<<100, 5:3>>)\n" + " // -> \"<<100, 5:size(3)>>\"\n" + " ```\n" +). +-spec inspect(bitstring()) -> binary(). +inspect(Input) -> + <<(inspect_loop(Input, <<"<<"/utf8>>))/binary, ">>"/utf8>>. + +-file("src/gleam/bit_array.gleam", 232). +?DOC( + " Compare two bit arrays as sequences of bytes.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " compare(<<1>>, <<2>>)\n" + " // -> Lt\n" + "\n" + " compare(<<\"AB\":utf8>>, <<\"AA\":utf8>>)\n" + " // -> Gt\n" + "\n" + " compare(<<1, 2:size(2)>>, with: <<1, 2:size(2)>>)\n" + " // -> Eq\n" + " ```\n" +). +-spec compare(bitstring(), bitstring()) -> gleam@order:order(). +compare(A, B) -> + case {A, B} of + {<>, + <>} -> + case {First_byte, Second_byte} of + {F, S} when F > S -> + gt; + + {F@1, S@1} when F@1 < S@1 -> + lt; + + {_, _} -> + compare(First_rest, Second_rest) + end; + + {<<>>, <<>>} -> + eq; + + {_, <<>>} -> + gt; + + {<<>>, _} -> + lt; + + {First, Second} -> + case {gleam_stdlib:bit_array_to_int_and_size(First), + gleam_stdlib:bit_array_to_int_and_size(Second)} of + {{A@1, _}, {B@1, _}} when A@1 > B@1 -> + gt; + + {{A@2, _}, {B@2, _}} when A@2 < B@2 -> + lt; + + {{_, Size_a}, {_, Size_b}} when Size_a > Size_b -> + gt; + + {{_, Size_a@1}, {_, Size_b@1}} when Size_a@1 < Size_b@1 -> + lt; + + {_, _} -> + eq + end + end. + +-file("src/gleam/bit_array.gleam", 273). +?DOC( + " Checks whether the first `BitArray` starts with the second one.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " starts_with(<<1, 2, 3, 4>>, <<1, 2>>)\n" + " // -> True\n" + " ```\n" +). +-spec starts_with(bitstring(), bitstring()) -> boolean(). +starts_with(Bits, Prefix) -> + Prefix_size = erlang:bit_size(Prefix), + case Bits of + <> when Pref =:= Prefix -> + true; + + _ -> + false + end. diff --git a/build/dev/javascript/gleam_stdlib/gleam@bool.erl b/build/dev/javascript/gleam_stdlib/gleam@bool.erl new file mode 100644 index 0000000..01307b3 --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam@bool.erl @@ -0,0 +1,352 @@ +-module(gleam@bool). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/bool.gleam"). +-export(['and'/2, 'or'/2, negate/1, nor/2, nand/2, exclusive_or/2, exclusive_nor/2, to_string/1, guard/3, lazy_guard/3]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC( + " A type with two possible values, `True` and `False`. Used to indicate whether\n" + " things are... true or false!\n" + "\n" + " Often is it clearer and offers more type safety to define a custom type\n" + " than to use `Bool`. For example, rather than having a `is_teacher: Bool`\n" + " field consider having a `role: SchoolRole` field where `SchoolRole` is a custom\n" + " type that can be either `Student` or `Teacher`.\n" +). + +-file("src/gleam/bool.gleam", 31). +?DOC( + " Returns the and of two bools, but it evaluates both arguments.\n" + "\n" + " It's the function equivalent of the `&&` operator.\n" + " This function is useful in higher order functions or pipes.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " and(True, True)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " and(False, True)\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " False |> and(True)\n" + " // -> False\n" + " ```\n" +). +-spec 'and'(boolean(), boolean()) -> boolean(). +'and'(A, B) -> + A andalso B. + +-file("src/gleam/bool.gleam", 57). +?DOC( + " Returns the or of two bools, but it evaluates both arguments.\n" + "\n" + " It's the function equivalent of the `||` operator.\n" + " This function is useful in higher order functions or pipes.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " or(True, True)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " or(False, True)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " False |> or(True)\n" + " // -> True\n" + " ```\n" +). +-spec 'or'(boolean(), boolean()) -> boolean(). +'or'(A, B) -> + A orelse B. + +-file("src/gleam/bool.gleam", 77). +?DOC( + " Returns the opposite bool value.\n" + "\n" + " This is the same as the `!` or `not` operators in some other languages.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " negate(True)\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " negate(False)\n" + " // -> True\n" + " ```\n" +). +-spec negate(boolean()) -> boolean(). +negate(Bool) -> + not Bool. + +-file("src/gleam/bool.gleam", 105). +?DOC( + " Returns the nor of two bools.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " nor(False, False)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " nor(False, True)\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " nor(True, False)\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " nor(True, True)\n" + " // -> False\n" + " ```\n" +). +-spec nor(boolean(), boolean()) -> boolean(). +nor(A, B) -> + not (A orelse B). + +-file("src/gleam/bool.gleam", 133). +?DOC( + " Returns the nand of two bools.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " nand(False, False)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " nand(False, True)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " nand(True, False)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " nand(True, True)\n" + " // -> False\n" + " ```\n" +). +-spec nand(boolean(), boolean()) -> boolean(). +nand(A, B) -> + not (A andalso B). + +-file("src/gleam/bool.gleam", 161). +?DOC( + " Returns the exclusive or of two bools.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " exclusive_or(False, False)\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " exclusive_or(False, True)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " exclusive_or(True, False)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " exclusive_or(True, True)\n" + " // -> False\n" + " ```\n" +). +-spec exclusive_or(boolean(), boolean()) -> boolean(). +exclusive_or(A, B) -> + A /= B. + +-file("src/gleam/bool.gleam", 189). +?DOC( + " Returns the exclusive nor of two bools.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " exclusive_nor(False, False)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " exclusive_nor(False, True)\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " exclusive_nor(True, False)\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " exclusive_nor(True, True)\n" + " // -> True\n" + " ```\n" +). +-spec exclusive_nor(boolean(), boolean()) -> boolean(). +exclusive_nor(A, B) -> + A =:= B. + +-file("src/gleam/bool.gleam", 207). +?DOC( + " Returns a string representation of the given bool.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " to_string(True)\n" + " // -> \"True\"\n" + " ```\n" + "\n" + " ```gleam\n" + " to_string(False)\n" + " // -> \"False\"\n" + " ```\n" +). +-spec to_string(boolean()) -> binary(). +to_string(Bool) -> + case Bool of + false -> + <<"False"/utf8>>; + + true -> + <<"True"/utf8>> + end. + +-file("src/gleam/bool.gleam", 266). +?DOC( + " Run a callback function if the given bool is `False`, otherwise return a\n" + " default value.\n" + "\n" + " With a `use` expression this function can simulate the early-return pattern\n" + " found in some other programming languages.\n" + "\n" + " In a procedural language:\n" + "\n" + " ```js\n" + " if (predicate) return value;\n" + " // ...\n" + " ```\n" + "\n" + " In Gleam with a `use` expression:\n" + "\n" + " ```gleam\n" + " use <- guard(when: predicate, return: value)\n" + " // ...\n" + " ```\n" + "\n" + " Like everything in Gleam `use` is an expression, so it short circuits the\n" + " current block, not the entire function. As a result you can assign the value\n" + " to a variable:\n" + "\n" + " ```gleam\n" + " let x = {\n" + " use <- guard(when: predicate, return: value)\n" + " // ...\n" + " }\n" + " ```\n" + "\n" + " Note that unlike in procedural languages the `return` value is evaluated\n" + " even when the predicate is `False`, so it is advisable not to perform\n" + " expensive computation nor side-effects there.\n" + "\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let name = \"\"\n" + " use <- guard(when: name == \"\", return: \"Welcome!\")\n" + " \"Hello, \" <> name\n" + " // -> \"Welcome!\"\n" + " ```\n" + "\n" + " ```gleam\n" + " let name = \"Kamaka\"\n" + " use <- guard(when: name == \"\", return: \"Welcome!\")\n" + " \"Hello, \" <> name\n" + " // -> \"Hello, Kamaka\"\n" + " ```\n" +). +-spec guard(boolean(), BSY, fun(() -> BSY)) -> BSY. +guard(Requirement, Consequence, Alternative) -> + case Requirement of + true -> + Consequence; + + false -> + Alternative() + end. + +-file("src/gleam/bool.gleam", 307). +?DOC( + " Runs a callback function if the given bool is `True`, otherwise runs an\n" + " alternative callback function.\n" + "\n" + " Useful when further computation should be delayed regardless of the given\n" + " bool's value.\n" + "\n" + " See [`guard`](#guard) for more info.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let name = \"Kamaka\"\n" + " let inquiry = fn() { \"How may we address you?\" }\n" + " use <- lazy_guard(when: name == \"\", return: inquiry)\n" + " \"Hello, \" <> name\n" + " // -> \"Hello, Kamaka\"\n" + " ```\n" + "\n" + " ```gleam\n" + " import gleam/int\n" + "\n" + " let name = \"\"\n" + " let greeting = fn() { \"Hello, \" <> name }\n" + " use <- lazy_guard(when: name == \"\", otherwise: greeting)\n" + " let number = int.random(99)\n" + " let name = \"User \" <> int.to_string(number)\n" + " \"Welcome, \" <> name\n" + " // -> \"Welcome, User 54\"\n" + " ```\n" +). +-spec lazy_guard(boolean(), fun(() -> BSZ), fun(() -> BSZ)) -> BSZ. +lazy_guard(Requirement, Consequence, Alternative) -> + case Requirement of + true -> + Consequence(); + + false -> + Alternative() + end. diff --git a/build/dev/javascript/gleam_stdlib/gleam@bytes_tree.erl b/build/dev/javascript/gleam_stdlib/gleam@bytes_tree.erl new file mode 100644 index 0000000..a96eaa2 --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam@bytes_tree.erl @@ -0,0 +1,211 @@ +-module(gleam@bytes_tree). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/bytes_tree.gleam"). +-export([append_tree/2, prepend_tree/2, concat/1, new/0, from_string/1, prepend_string/2, append_string/2, from_string_tree/1, from_bit_array/1, prepend/2, append/2, concat_bit_arrays/1, to_bit_array/1, byte_size/1]). +-export_type([bytes_tree/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC( + " `BytesTree` is a type used for efficiently building binary content to be\n" + " written to a file or a socket. Internally it is represented as tree so to\n" + " append or prepend to a bytes tree is a constant time operation that\n" + " allocates a new node in the tree without copying any of the content. When\n" + " writing to an output stream the tree is traversed and the content is sent\n" + " directly rather than copying it into a single buffer beforehand.\n" + "\n" + " If we append one bit array to another the bit arrays must be copied to a\n" + " new location in memory so that they can sit together. This behaviour\n" + " enables efficient reading of the data but copying can be expensive,\n" + " especially if we want to join many bit arrays together.\n" + "\n" + " BytesTree is different in that it can be joined together in constant\n" + " time using minimal memory, and then can be efficiently converted to a\n" + " bit array using the `to_bit_array` function.\n" + "\n" + " Byte trees are always byte aligned, so that a number of bits that is not\n" + " divisible by 8 will be padded with 0s.\n" + "\n" + " On Erlang this type is compatible with Erlang's iolists.\n" +). + +-opaque bytes_tree() :: {bytes, bitstring()} | + {text, gleam@string_tree:string_tree()} | + {many, list(bytes_tree())}. + +-file("src/gleam/bytes_tree.gleam", 68). +?DOC( + " Appends a bytes tree onto the end of another.\n" + "\n" + " Runs in constant time.\n" +). +-spec append_tree(bytes_tree(), bytes_tree()) -> bytes_tree(). +append_tree(First, Second) -> + gleam_stdlib:iodata_append(First, Second). + +-file("src/gleam/bytes_tree.gleam", 59). +?DOC( + " Prepends a bytes tree onto the start of another.\n" + "\n" + " Runs in constant time.\n" +). +-spec prepend_tree(bytes_tree(), bytes_tree()) -> bytes_tree(). +prepend_tree(Second, First) -> + gleam_stdlib:iodata_append(First, Second). + +-file("src/gleam/bytes_tree.gleam", 98). +?DOC( + " Joins a list of bytes trees into a single one.\n" + "\n" + " Runs in constant time.\n" +). +-spec concat(list(bytes_tree())) -> bytes_tree(). +concat(Trees) -> + gleam_stdlib:identity(Trees). + +-file("src/gleam/bytes_tree.gleam", 35). +?DOC( + " Create an empty `BytesTree`. Useful as the start of a pipe chaining many\n" + " trees together.\n" +). +-spec new() -> bytes_tree(). +new() -> + gleam_stdlib:identity([]). + +-file("src/gleam/bytes_tree.gleam", 118). +?DOC( + " Creates a new bytes tree from a string.\n" + "\n" + " Runs in constant time when running on Erlang.\n" + " Runs in linear time otherwise.\n" +). +-spec from_string(binary()) -> bytes_tree(). +from_string(String) -> + gleam_stdlib:wrap_list(String). + +-file("src/gleam/bytes_tree.gleam", 80). +?DOC( + " Prepends a string onto the start of a bytes tree.\n" + "\n" + " Runs in constant time when running on Erlang.\n" + " Runs in linear time with the length of the string otherwise.\n" +). +-spec prepend_string(bytes_tree(), binary()) -> bytes_tree(). +prepend_string(Second, First) -> + gleam_stdlib:iodata_append(gleam_stdlib:wrap_list(First), Second). + +-file("src/gleam/bytes_tree.gleam", 89). +?DOC( + " Appends a string onto the end of a bytes tree.\n" + "\n" + " Runs in constant time when running on Erlang.\n" + " Runs in linear time with the length of the string otherwise.\n" +). +-spec append_string(bytes_tree(), binary()) -> bytes_tree(). +append_string(First, Second) -> + gleam_stdlib:iodata_append(First, gleam_stdlib:wrap_list(Second)). + +-file("src/gleam/bytes_tree.gleam", 128). +?DOC( + " Creates a new bytes tree from a string tree.\n" + "\n" + " Runs in constant time when running on Erlang.\n" + " Runs in linear time otherwise.\n" +). +-spec from_string_tree(gleam@string_tree:string_tree()) -> bytes_tree(). +from_string_tree(Tree) -> + gleam_stdlib:wrap_list(Tree). + +-file("src/gleam/bytes_tree.gleam", 136). +?DOC( + " Creates a new bytes tree from a bit array.\n" + "\n" + " Runs in constant time.\n" +). +-spec from_bit_array(bitstring()) -> bytes_tree(). +from_bit_array(Bits) -> + _pipe = Bits, + _pipe@1 = gleam_stdlib:bit_array_pad_to_bytes(_pipe), + gleam_stdlib:wrap_list(_pipe@1). + +-file("src/gleam/bytes_tree.gleam", 43). +?DOC( + " Prepends a bit array to the start of a bytes tree.\n" + "\n" + " Runs in constant time.\n" +). +-spec prepend(bytes_tree(), bitstring()) -> bytes_tree(). +prepend(Second, First) -> + gleam_stdlib:iodata_append(from_bit_array(First), Second). + +-file("src/gleam/bytes_tree.gleam", 51). +?DOC( + " Appends a bit array to the end of a bytes tree.\n" + "\n" + " Runs in constant time.\n" +). +-spec append(bytes_tree(), bitstring()) -> bytes_tree(). +append(First, Second) -> + gleam_stdlib:iodata_append(First, from_bit_array(Second)). + +-file("src/gleam/bytes_tree.gleam", 106). +?DOC( + " Joins a list of bit arrays into a single bytes tree.\n" + "\n" + " Runs in constant time.\n" +). +-spec concat_bit_arrays(list(bitstring())) -> bytes_tree(). +concat_bit_arrays(Bits) -> + _pipe = Bits, + _pipe@1 = gleam@list:map(_pipe, fun from_bit_array/1), + gleam_stdlib:identity(_pipe@1). + +-file("src/gleam/bytes_tree.gleam", 162). +-spec to_list(list(list(bytes_tree())), list(bitstring())) -> list(bitstring()). +to_list(Stack, Acc) -> + case Stack of + [] -> + Acc; + + [[] | Remaining_stack] -> + to_list(Remaining_stack, Acc); + + [[{bytes, Bits} | Rest] | Remaining_stack@1] -> + to_list([Rest | Remaining_stack@1], [Bits | Acc]); + + [[{text, Tree} | Rest@1] | Remaining_stack@2] -> + Bits@1 = gleam_stdlib:identity(unicode:characters_to_binary(Tree)), + to_list([Rest@1 | Remaining_stack@2], [Bits@1 | Acc]); + + [[{many, Trees} | Rest@2] | Remaining_stack@3] -> + to_list([Trees, Rest@2 | Remaining_stack@3], Acc) + end. + +-file("src/gleam/bytes_tree.gleam", 155). +?DOC( + " Turns a bytes tree into a bit array.\n" + "\n" + " Runs in linear time.\n" + "\n" + " When running on Erlang this function is implemented natively by the\n" + " virtual machine and is highly optimised.\n" +). +-spec to_bit_array(bytes_tree()) -> bitstring(). +to_bit_array(Tree) -> + erlang:list_to_bitstring(Tree). + +-file("src/gleam/bytes_tree.gleam", 186). +?DOC( + " Returns the size of the bytes tree's content in bytes.\n" + "\n" + " Runs in linear time.\n" +). +-spec byte_size(bytes_tree()) -> integer(). +byte_size(Tree) -> + erlang:iolist_size(Tree). diff --git a/build/dev/javascript/gleam_stdlib/gleam@dict.erl b/build/dev/javascript/gleam_stdlib/gleam@dict.erl new file mode 100644 index 0000000..d496afb --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam@dict.erl @@ -0,0 +1,561 @@ +-module(gleam@dict). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/dict.gleam"). +-export([size/1, is_empty/1, to_list/1, new/0, get/2, has_key/2, insert/3, from_list/1, keys/1, values/1, take/2, merge/2, delete/2, drop/2, upsert/3, fold/3, map_values/2, filter/2, each/2, combine/3]). +-export_type([dict/2]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +-type dict(KG, KH) :: any() | {gleam_phantom, KG, KH}. + +-file("src/gleam/dict.gleam", 36). +?DOC( + " Determines the number of key-value pairs in the dict.\n" + " This function runs in constant time and does not need to iterate the dict.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " new() |> size\n" + " // -> 0\n" + " ```\n" + "\n" + " ```gleam\n" + " new() |> insert(\"key\", \"value\") |> size\n" + " // -> 1\n" + " ```\n" +). +-spec size(dict(any(), any())) -> integer(). +size(Dict) -> + maps:size(Dict). + +-file("src/gleam/dict.gleam", 52). +?DOC( + " Determines whether or not the dict is empty.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " new() |> is_empty\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " new() |> insert(\"b\", 1) |> is_empty\n" + " // -> False\n" + " ```\n" +). +-spec is_empty(dict(any(), any())) -> boolean(). +is_empty(Dict) -> + maps:size(Dict) =:= 0. + +-file("src/gleam/dict.gleam", 80). +?DOC( + " Converts the dict to a list of 2-element tuples `#(key, value)`, one for\n" + " each key-value pair in the dict.\n" + "\n" + " The tuples in the list have no specific order.\n" + "\n" + " ## Examples\n" + "\n" + " Calling `to_list` on an empty `dict` returns an empty list.\n" + "\n" + " ```gleam\n" + " new() |> to_list\n" + " // -> []\n" + " ```\n" + "\n" + " The ordering of elements in the resulting list is an implementation detail\n" + " that should not be relied upon.\n" + "\n" + " ```gleam\n" + " new() |> insert(\"b\", 1) |> insert(\"a\", 0) |> insert(\"c\", 2) |> to_list\n" + " // -> [#(\"a\", 0), #(\"b\", 1), #(\"c\", 2)]\n" + " ```\n" +). +-spec to_list(dict(KQ, KR)) -> list({KQ, KR}). +to_list(Dict) -> + maps:to_list(Dict). + +-file("src/gleam/dict.gleam", 129). +?DOC(" Creates a fresh dict that contains no values.\n"). +-spec new() -> dict(any(), any()). +new() -> + maps:new(). + +-file("src/gleam/dict.gleam", 150). +?DOC( + " Fetches a value from a dict for a given key.\n" + "\n" + " The dict may not have a value for the key, so the value is wrapped in a\n" + " `Result`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " new() |> insert(\"a\", 0) |> get(\"a\")\n" + " // -> Ok(0)\n" + " ```\n" + "\n" + " ```gleam\n" + " new() |> insert(\"a\", 0) |> get(\"b\")\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec get(dict(LT, LU), LT) -> {ok, LU} | {error, nil}. +get(From, Get) -> + gleam_stdlib:map_get(From, Get). + +-file("src/gleam/dict.gleam", 116). +?DOC( + " Determines whether or not a value present in the dict for a given key.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " new() |> insert(\"a\", 0) |> has_key(\"a\")\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " new() |> insert(\"a\", 0) |> has_key(\"b\")\n" + " // -> False\n" + " ```\n" +). +-spec has_key(dict(LH, any()), LH) -> boolean(). +has_key(Dict, Key) -> + maps:is_key(Key, Dict). + +-file("src/gleam/dict.gleam", 169). +?DOC( + " Inserts a value into the dict with the given key.\n" + "\n" + " If the dict already has a value for the given key then the value is\n" + " replaced with the new value.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " new() |> insert(\"a\", 0)\n" + " // -> from_list([#(\"a\", 0)])\n" + " ```\n" + "\n" + " ```gleam\n" + " new() |> insert(\"a\", 0) |> insert(\"a\", 5)\n" + " // -> from_list([#(\"a\", 5)])\n" + " ```\n" +). +-spec insert(dict(LZ, MA), LZ, MA) -> dict(LZ, MA). +insert(Dict, Key, Value) -> + maps:put(Key, Value, Dict). + +-file("src/gleam/dict.gleam", 92). +-spec from_list_loop(list({LA, LB}), dict(LA, LB)) -> dict(LA, LB). +from_list_loop(List, Initial) -> + case List of + [] -> + Initial; + + [{Key, Value} | Rest] -> + from_list_loop(Rest, insert(Initial, Key, Value)) + end. + +-file("src/gleam/dict.gleam", 88). +?DOC( + " Converts a list of 2-element tuples `#(key, value)` to a dict.\n" + "\n" + " If two tuples have the same key the last one in the list will be the one\n" + " that is present in the dict.\n" +). +-spec from_list(list({KV, KW})) -> dict(KV, KW). +from_list(List) -> + maps:from_list(List). + +-file("src/gleam/dict.gleam", 223). +-spec reverse_and_concat(list(NJ), list(NJ)) -> list(NJ). +reverse_and_concat(Remaining, Accumulator) -> + case Remaining of + [] -> + Accumulator; + + [First | Rest] -> + reverse_and_concat(Rest, [First | Accumulator]) + end. + +-file("src/gleam/dict.gleam", 216). +-spec do_keys_loop(list({NE, any()}), list(NE)) -> list(NE). +do_keys_loop(List, Acc) -> + case List of + [] -> + reverse_and_concat(Acc, []); + + [{Key, _} | Rest] -> + do_keys_loop(Rest, [Key | Acc]) + end. + +-file("src/gleam/dict.gleam", 212). +?DOC( + " Gets a list of all keys in a given dict.\n" + "\n" + " Dicts are not ordered so the keys are not returned in any specific order. Do\n" + " not write code that relies on the order keys are returned by this function\n" + " as it may change in later versions of Gleam or Erlang.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " from_list([#(\"a\", 0), #(\"b\", 1)]) |> keys\n" + " // -> [\"a\", \"b\"]\n" + " ```\n" +). +-spec keys(dict(MZ, any())) -> list(MZ). +keys(Dict) -> + maps:keys(Dict). + +-file("src/gleam/dict.gleam", 249). +-spec do_values_loop(list({any(), NT}), list(NT)) -> list(NT). +do_values_loop(List, Acc) -> + case List of + [] -> + reverse_and_concat(Acc, []); + + [{_, Value} | Rest] -> + do_values_loop(Rest, [Value | Acc]) + end. + +-file("src/gleam/dict.gleam", 244). +?DOC( + " Gets a list of all values in a given dict.\n" + "\n" + " Dicts are not ordered so the values are not returned in any specific order. Do\n" + " not write code that relies on the order values are returned by this function\n" + " as it may change in later versions of Gleam or Erlang.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " from_list([#(\"a\", 0), #(\"b\", 1)]) |> values\n" + " // -> [0, 1]\n" + " ```\n" +). +-spec values(dict(any(), NO)) -> list(NO). +values(Dict) -> + maps:values(Dict). + +-file("src/gleam/dict.gleam", 318). +-spec do_take_loop(dict(OX, OY), list(OX), dict(OX, OY)) -> dict(OX, OY). +do_take_loop(Dict, Desired_keys, Acc) -> + Insert = fun(Taken, Key) -> case gleam_stdlib:map_get(Dict, Key) of + {ok, Value} -> + insert(Taken, Key, Value); + + {error, _} -> + Taken + end end, + case Desired_keys of + [] -> + Acc; + + [First | Rest] -> + do_take_loop(Dict, Rest, Insert(Acc, First)) + end. + +-file("src/gleam/dict.gleam", 309). +?DOC( + " Creates a new dict from a given dict, only including any entries for which the\n" + " keys are in a given list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " from_list([#(\"a\", 0), #(\"b\", 1)])\n" + " |> take([\"b\"])\n" + " // -> from_list([#(\"b\", 1)])\n" + " ```\n" + "\n" + " ```gleam\n" + " from_list([#(\"a\", 0), #(\"b\", 1)])\n" + " |> take([\"a\", \"b\", \"c\"])\n" + " // -> from_list([#(\"a\", 0), #(\"b\", 1)])\n" + " ```\n" +). +-spec take(dict(OJ, OK), list(OJ)) -> dict(OJ, OK). +take(Dict, Desired_keys) -> + maps:with(Desired_keys, Dict). + +-file("src/gleam/dict.gleam", 363). +-spec insert_pair(dict(PV, PW), {PV, PW}) -> dict(PV, PW). +insert_pair(Dict, Pair) -> + insert(Dict, erlang:element(1, Pair), erlang:element(2, Pair)). + +-file("src/gleam/dict.gleam", 356). +-spec fold_inserts(list({PO, PP}), dict(PO, PP)) -> dict(PO, PP). +fold_inserts(New_entries, Dict) -> + case New_entries of + [] -> + Dict; + + [First | Rest] -> + fold_inserts(Rest, insert_pair(Dict, First)) + end. + +-file("src/gleam/dict.gleam", 350). +?DOC( + " Creates a new dict from a pair of given dicts by combining their entries.\n" + "\n" + " If there are entries with the same keys in both dicts the entry from the\n" + " second dict takes precedence.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let a = from_list([#(\"a\", 0), #(\"b\", 1)])\n" + " let b = from_list([#(\"b\", 2), #(\"c\", 3)])\n" + " merge(a, b)\n" + " // -> from_list([#(\"a\", 0), #(\"b\", 2), #(\"c\", 3)])\n" + " ```\n" +). +-spec merge(dict(PG, PH), dict(PG, PH)) -> dict(PG, PH). +merge(Dict, New_entries) -> + maps:merge(Dict, New_entries). + +-file("src/gleam/dict.gleam", 382). +?DOC( + " Creates a new dict from a given dict with all the same entries except for the\n" + " one with a given key, if it exists.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " from_list([#(\"a\", 0), #(\"b\", 1)]) |> delete(\"a\")\n" + " // -> from_list([#(\"b\", 1)])\n" + " ```\n" + "\n" + " ```gleam\n" + " from_list([#(\"a\", 0), #(\"b\", 1)]) |> delete(\"c\")\n" + " // -> from_list([#(\"a\", 0), #(\"b\", 1)])\n" + " ```\n" +). +-spec delete(dict(QB, QC), QB) -> dict(QB, QC). +delete(Dict, Key) -> + maps:remove(Key, Dict). + +-file("src/gleam/dict.gleam", 410). +?DOC( + " Creates a new dict from a given dict with all the same entries except any with\n" + " keys found in a given list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " from_list([#(\"a\", 0), #(\"b\", 1)]) |> drop([\"a\"])\n" + " // -> from_list([#(\"b\", 1)])\n" + " ```\n" + "\n" + " ```gleam\n" + " from_list([#(\"a\", 0), #(\"b\", 1)]) |> drop([\"c\"])\n" + " // -> from_list([#(\"a\", 0), #(\"b\", 1)])\n" + " ```\n" + "\n" + " ```gleam\n" + " from_list([#(\"a\", 0), #(\"b\", 1)]) |> drop([\"a\", \"b\", \"c\"])\n" + " // -> from_list([])\n" + " ```\n" +). +-spec drop(dict(QN, QO), list(QN)) -> dict(QN, QO). +drop(Dict, Disallowed_keys) -> + case Disallowed_keys of + [] -> + Dict; + + [First | Rest] -> + drop(delete(Dict, First), Rest) + end. + +-file("src/gleam/dict.gleam", 440). +?DOC( + " Creates a new dict with one entry inserted or updated using a given function.\n" + "\n" + " If there was not an entry in the dict for the given key then the function\n" + " gets `None` as its argument, otherwise it gets `Some(value)`.\n" + "\n" + " ## Example\n" + "\n" + " ```gleam\n" + " let dict = from_list([#(\"a\", 0)])\n" + " let increment = fn(x) {\n" + " case x {\n" + " Some(i) -> i + 1\n" + " None -> 0\n" + " }\n" + " }\n" + "\n" + " upsert(dict, \"a\", increment)\n" + " // -> from_list([#(\"a\", 1)])\n" + "\n" + " upsert(dict, \"b\", increment)\n" + " // -> from_list([#(\"a\", 0), #(\"b\", 0)])\n" + " ```\n" +). +-spec upsert(dict(QU, QV), QU, fun((gleam@option:option(QV)) -> QV)) -> dict(QU, QV). +upsert(Dict, Key, Fun) -> + case gleam_stdlib:map_get(Dict, Key) of + {ok, Value} -> + insert(Dict, Key, Fun({some, Value})); + + {error, _} -> + insert(Dict, Key, Fun(none)) + end. + +-file("src/gleam/dict.gleam", 484). +-spec fold_loop(list({RG, RH}), RJ, fun((RJ, RG, RH) -> RJ)) -> RJ. +fold_loop(List, Initial, Fun) -> + case List of + [] -> + Initial; + + [{K, V} | Rest] -> + fold_loop(Rest, Fun(Initial, K, V), Fun) + end. + +-file("src/gleam/dict.gleam", 476). +?DOC( + " Combines all entries into a single value by calling a given function on each\n" + " one.\n" + "\n" + " Dicts are not ordered so the values are not returned in any specific order. Do\n" + " not write code that relies on the order entries are used by this function\n" + " as it may change in later versions of Gleam or Erlang.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " let dict = from_list([#(\"a\", 1), #(\"b\", 3), #(\"c\", 9)])\n" + " fold(dict, 0, fn(accumulator, key, value) { accumulator + value })\n" + " // -> 13\n" + " ```\n" + "\n" + " ```gleam\n" + " import gleam/string\n" + "\n" + " let dict = from_list([#(\"a\", 1), #(\"b\", 3), #(\"c\", 9)])\n" + " fold(dict, \"\", fn(accumulator, key, value) {\n" + " string.append(accumulator, key)\n" + " })\n" + " // -> \"abc\"\n" + " ```\n" +). +-spec fold(dict(RB, RC), RF, fun((RF, RB, RC) -> RF)) -> RF. +fold(Dict, Initial, Fun) -> + fold_loop(maps:to_list(Dict), Initial, Fun). + +-file("src/gleam/dict.gleam", 188). +?DOC( + " Updates all values in a given dict by calling a given function on each key\n" + " and value.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " from_list([#(3, 3), #(2, 4)])\n" + " |> map_values(fn(key, value) { key * value })\n" + " // -> from_list([#(3, 9), #(2, 8)])\n" + " ```\n" +). +-spec map_values(dict(ML, MM), fun((ML, MM) -> MP)) -> dict(ML, MP). +map_values(Dict, Fun) -> + maps:map(Fun, Dict). + +-file("src/gleam/dict.gleam", 273). +?DOC( + " Creates a new dict from a given dict, minus any entries that a given function\n" + " returns `False` for.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " from_list([#(\"a\", 0), #(\"b\", 1)])\n" + " |> filter(fn(key, value) { value != 0 })\n" + " // -> from_list([#(\"b\", 1)])\n" + " ```\n" + "\n" + " ```gleam\n" + " from_list([#(\"a\", 0), #(\"b\", 1)])\n" + " |> filter(fn(key, value) { True })\n" + " // -> from_list([#(\"a\", 0), #(\"b\", 1)])\n" + " ```\n" +). +-spec filter(dict(NX, NY), fun((NX, NY) -> boolean())) -> dict(NX, NY). +filter(Dict, Predicate) -> + maps:filter(Predicate, Dict). + +-file("src/gleam/dict.gleam", 517). +?DOC( + " Calls a function for each key and value in a dict, discarding the return\n" + " value.\n" + "\n" + " Useful for producing a side effect for every item of a dict.\n" + "\n" + " ```gleam\n" + " import gleam/io\n" + "\n" + " let dict = from_list([#(\"a\", \"apple\"), #(\"b\", \"banana\"), #(\"c\", \"cherry\")])\n" + "\n" + " each(dict, fn(k, v) {\n" + " io.println(key <> \" => \" <> value)\n" + " })\n" + " // -> Nil\n" + " // a => apple\n" + " // b => banana\n" + " // c => cherry\n" + " ```\n" + "\n" + " The order of elements in the iteration is an implementation detail that\n" + " should not be relied upon.\n" +). +-spec each(dict(RK, RL), fun((RK, RL) -> any())) -> nil. +each(Dict, Fun) -> + fold( + Dict, + nil, + fun(Nil, K, V) -> + Fun(K, V), + Nil + end + ). + +-file("src/gleam/dict.gleam", 538). +?DOC( + " Creates a new dict from a pair of given dicts by combining their entries.\n" + "\n" + " If there are entries with the same keys in both dicts the given function is\n" + " used to determine the new value to use in the resulting dict.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let a = from_list([#(\"a\", 0), #(\"b\", 1)])\n" + " let b = from_list([#(\"a\", 2), #(\"c\", 3)])\n" + " combine(a, b, fn(one, other) { one + other })\n" + " // -> from_list([#(\"a\", 2), #(\"b\", 1), #(\"c\", 3)])\n" + " ```\n" +). +-spec combine(dict(RP, RQ), dict(RP, RQ), fun((RQ, RQ) -> RQ)) -> dict(RP, RQ). +combine(Dict, Other, Fun) -> + fold( + Dict, + Other, + fun(Acc, Key, Value) -> case gleam_stdlib:map_get(Acc, Key) of + {ok, Other_value} -> + insert(Acc, Key, Fun(Value, Other_value)); + + {error, _} -> + insert(Acc, Key, Value) + end end + ). diff --git a/build/dev/javascript/gleam_stdlib/gleam@dynamic.erl b/build/dev/javascript/gleam_stdlib/gleam@dynamic.erl new file mode 100644 index 0000000..f057ca2 --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam@dynamic.erl @@ -0,0 +1,106 @@ +-module(gleam@dynamic). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/dynamic.gleam"). +-export([classify/1, bool/1, string/1, float/1, int/1, bit_array/1, list/1, array/1, properties/1, nil/0]). +-export_type([dynamic_/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +-type dynamic_() :: any(). + +-file("src/gleam/dynamic.gleam", 30). +?DOC( + " Return a string indicating the type of the dynamic value.\n" + "\n" + " This function may be useful for constructing error messages or logs. If you\n" + " want to turn dynamic data into well typed data then you want the\n" + " `gleam/dynamic/decode` module.\n" + "\n" + " ```gleam\n" + " classify(string(\"Hello\"))\n" + " // -> \"String\"\n" + " ```\n" +). +-spec classify(dynamic_()) -> binary(). +classify(Data) -> + gleam_stdlib:classify_dynamic(Data). + +-file("src/gleam/dynamic.gleam", 36). +?DOC(" Create a dynamic value from a bool.\n"). +-spec bool(boolean()) -> dynamic_(). +bool(A) -> + gleam_stdlib:identity(A). + +-file("src/gleam/dynamic.gleam", 44). +?DOC( + " Create a dynamic value from a string.\n" + "\n" + " On Erlang this will be a binary string rather than a character list.\n" +). +-spec string(binary()) -> dynamic_(). +string(A) -> + gleam_stdlib:identity(A). + +-file("src/gleam/dynamic.gleam", 50). +?DOC(" Create a dynamic value from a float.\n"). +-spec float(float()) -> dynamic_(). +float(A) -> + gleam_stdlib:identity(A). + +-file("src/gleam/dynamic.gleam", 56). +?DOC(" Create a dynamic value from an int.\n"). +-spec int(integer()) -> dynamic_(). +int(A) -> + gleam_stdlib:identity(A). + +-file("src/gleam/dynamic.gleam", 62). +?DOC(" Create a dynamic value from a bit array.\n"). +-spec bit_array(bitstring()) -> dynamic_(). +bit_array(A) -> + gleam_stdlib:identity(A). + +-file("src/gleam/dynamic.gleam", 68). +?DOC(" Create a dynamic value from a list.\n"). +-spec list(list(dynamic_())) -> dynamic_(). +list(A) -> + gleam_stdlib:identity(A). + +-file("src/gleam/dynamic.gleam", 77). +?DOC( + " Create a dynamic value from a list, converting it to a sequential runtime\n" + " format rather than the regular list format.\n" + "\n" + " On Erlang this will be a tuple, on JavaScript this will be an array.\n" +). +-spec array(list(dynamic_())) -> dynamic_(). +array(A) -> + erlang:list_to_tuple(A). + +-file("src/gleam/dynamic.gleam", 85). +?DOC( + " Create a dynamic value made an unordered series of keys and values, where\n" + " the keys are unique.\n" + "\n" + " On Erlang this will be a map, on JavaScript this will be a Gleam dict\n" + " object.\n" +). +-spec properties(list({dynamic_(), dynamic_()})) -> dynamic_(). +properties(Entries) -> + gleam_stdlib:identity(maps:from_list(Entries)). + +-file("src/gleam/dynamic.gleam", 94). +?DOC( + " A dynamic value representing nothing.\n" + "\n" + " On Erlang this will be the atom `nil`, on JavaScript this will be\n" + " `undefined`.\n" +). +-spec nil() -> dynamic_(). +nil() -> + gleam_stdlib:identity(nil). diff --git a/build/dev/javascript/gleam_stdlib/gleam@dynamic@decode.erl b/build/dev/javascript/gleam_stdlib/gleam@dynamic@decode.erl new file mode 100644 index 0000000..bf4b951 --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam@dynamic@decode.erl @@ -0,0 +1,1088 @@ +-module(gleam@dynamic@decode). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/dynamic/decode.gleam"). +-export([run/2, success/1, decode_dynamic/1, map/2, map_errors/2, then/2, one_of/2, recursive/1, optional/1, decode_error/2, decode_bool/1, decode_int/1, decode_float/1, decode_bit_array/1, collapse_errors/2, failure/2, new_primitive_decoder/2, decode_string/1, dict/2, list/1, subfield/3, at/2, field/3, optional_field/4, optionally_at/3]). +-export_type([decode_error/0, decoder/1]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC( + " The `Dynamic` type is used to represent dynamically typed data. That is, data\n" + " that we don't know the precise type of yet, so we need to introspect the data to\n" + " see if it is of the desired type before we can use it. Typically data like this\n" + " would come from user input or from untyped languages such as Erlang or JavaScript.\n" + "\n" + " This module provides the `Decoder` type and associated functions, which provides\n" + " a type-safe and composable way to convert dynamic data into some desired type,\n" + " or into errors if the data doesn't have the desired structure.\n" + "\n" + " The `Decoder` type is generic and has 1 type parameter, which is the type that\n" + " it attempts to decode. A `Decoder(String)` can be used to decode strings, and a\n" + " `Decoder(Option(Int))` can be used to decode `Option(Int)`s\n" + "\n" + " Decoders work using _runtime reflection_ and the data structures of the target\n" + " platform. Differences between Erlang and JavaScript data structures may impact\n" + " your decoders, so it is important to test your decoders on all supported\n" + " platforms.\n" + "\n" + " The decoding technique used by this module was inspired by Juraj Petráš'\n" + " [Toy](https://github.com/Hackder/toy), Go's `encoding/json`, and Elm's\n" + " `Json.Decode`. Thank you to them!\n" + "\n" + " # Examples\n" + "\n" + " Dynamic data may come from various sources and so many different syntaxes could\n" + " be used to describe or construct them. In these examples a pseudocode\n" + " syntax is used to describe the data.\n" + "\n" + " ## Simple types\n" + "\n" + " This module defines decoders for simple data types such as [`string`](#string),\n" + " [`int`](#int), [`float`](#float), [`bit_array`](#bit_array), and [`bool`](#bool).\n" + "\n" + " ```gleam\n" + " // Data:\n" + " // \"Hello, Joe!\"\n" + "\n" + " let result = decode.run(data, decode.string)\n" + " assert result == Ok(\"Hello, Joe!\")\n" + " ```\n" + "\n" + " ## Lists\n" + "\n" + " The [`list`](#list) decoder decodes `List`s. To use it you must construct it by\n" + " passing in another decoder into the `list` function, which is the decoder that\n" + " is to be used for the elements of the list, type checking both the list and its\n" + " elements.\n" + "\n" + " ```gleam\n" + " // Data:\n" + " // [1, 2, 3, 4]\n" + "\n" + " let result = decode.run(data, decode.list(decode.int))\n" + " assert result == Ok([1, 2, 3, 4])\n" + " ```\n" + "\n" + " On Erlang this decoder can decode from lists, and on JavaScript it can\n" + " decode from lists as well as JavaScript arrays.\n" + "\n" + " ## Options\n" + "\n" + " The [`optional`](#optional) decoder is used to decode values that may or may not\n" + " be present. In other environment these might be called \"nullable\" values.\n" + "\n" + " Like the `list` decoder, the `optional` decoder takes another decoder,\n" + " which is used to decode the value if it is present.\n" + "\n" + " ```gleam\n" + " // Data:\n" + " // 12.45\n" + "\n" + " let result = decode.run(data, decode.optional(decode.float))\n" + " assert result == Ok(option.Some(12.45))\n" + " ```\n" + " ```gleam\n" + " // Data:\n" + " // null\n" + "\n" + " let result = decode.run(data, decode.optional(decode.int))\n" + " assert result == Ok(option.None)\n" + " ```\n" + "\n" + " This decoder knows how to handle multiple different runtime representations of\n" + " absent values, including `Nil`, `None`, `null`, and `undefined`.\n" + "\n" + " ## Dicts\n" + "\n" + " The [`dict`](#dict) decoder decodes `Dicts` and contains two other decoders, one\n" + " for the keys, one for the values.\n" + "\n" + " ```gleam\n" + " // Data:\n" + " // { \"Lucy\" -> 10, \"Nubi\" -> 20 }\n" + "\n" + " let result = decode.run(data, decode.dict(decode.string, decode.int))\n" + " assert result == Ok(dict.from_list([\n" + " #(\"Lucy\", 10),\n" + " #(\"Nubi\", 20),\n" + " ]))\n" + " ```\n" + "\n" + " ## Indexing objects\n" + "\n" + " The [`at`](#at) decoder can be used to decode a value that is nested within\n" + " key-value containers such as Gleam dicts, Erlang maps, or JavaScript objects.\n" + "\n" + " ```gleam\n" + " // Data:\n" + " // { \"one\" -> { \"two\" -> 123 } }\n" + "\n" + " let result = decode.run(data, decode.at([\"one\", \"two\"], decode.int))\n" + " assert result == Ok(123)\n" + " ```\n" + "\n" + " ## Indexing arrays\n" + "\n" + " If you use ints as keys then the [`at`](#at) decoder can be used to index into\n" + " array-like containers such as Gleam or Erlang tuples, or JavaScript arrays.\n" + "\n" + " ```gleam\n" + " // Data:\n" + " // [\"one\", \"two\", \"three\"]\n" + "\n" + " let result = decode.run(data, decode.at([1], decode.string))\n" + " assert result == Ok(\"two\")\n" + " ```\n" + "\n" + " ## Records\n" + "\n" + " Decoding records from dynamic data is more complex and requires combining a\n" + " decoder for each field and a special constructor that builds your records with\n" + " the decoded field values.\n" + "\n" + " ```gleam\n" + " // Data:\n" + " // {\n" + " // \"score\" -> 180,\n" + " // \"name\" -> \"Mel Smith\",\n" + " // \"is-admin\" -> false,\n" + " // \"enrolled\" -> true,\n" + " // \"colour\" -> \"Red\",\n" + " // }\n" + "\n" + " let decoder = {\n" + " use name <- decode.field(\"name\", decode.string)\n" + " use score <- decode.field(\"score\", decode.int)\n" + " use colour <- decode.field(\"colour\", decode.string)\n" + " use enrolled <- decode.field(\"enrolled\", decode.bool)\n" + " decode.success(Player(name:, score:, colour:, enrolled:))\n" + " }\n" + "\n" + " let result = decode.run(data, decoder)\n" + " assert result == Ok(Player(\"Mel Smith\", 180, \"Red\", True))\n" + " ```\n" + "\n" + " ## Enum variants\n" + "\n" + " Imagine you have a custom type where all the variants do not contain any values.\n" + "\n" + " ```gleam\n" + " pub type PocketMonsterType {\n" + " Fire\n" + " Water\n" + " Grass\n" + " Electric\n" + " }\n" + " ```\n" + "\n" + " You might choose to encode these variants as strings, `\"fire\"` for `Fire`,\n" + " `\"water\"` for `Water`, and so on. To decode them you'll need to decode the dynamic\n" + " data as a string, but then you'll need to decode it further still as not all\n" + " strings are valid values for the enum. This can be done with the `then`\n" + " function, which enables running a second decoder after the first one\n" + " succeeds.\n" + "\n" + " ```gleam\n" + " let decoder = {\n" + " use decoded_string <- decode.then(decode.string)\n" + " case decoded_string {\n" + " // Return succeeding decoders for valid strings\n" + " \"fire\" -> decode.success(Fire)\n" + " \"water\" -> decode.success(Water)\n" + " \"grass\" -> decode.success(Grass)\n" + " \"electric\" -> decode.success(Electric)\n" + " // Return a failing decoder for any other strings\n" + " _ -> decode.failure(Fire, \"PocketMonsterType\")\n" + " }\n" + " }\n" + "\n" + " let result = decode.run(dynamic.string(\"water\"), decoder)\n" + " assert result == Ok(Water)\n" + "\n" + " let result = decode.run(dynamic.string(\"wobble\"), decoder)\n" + " assert result == Error([DecodeError(\"PocketMonsterType\", \"String\", [])])\n" + " ```\n" + "\n" + " ## Record variants\n" + "\n" + " Decoding type variants that contain other values is done by combining the\n" + " techniques from the \"enum variants\" and \"records\" examples. Imagine you have\n" + " this custom type that you want to decode:\n" + "\n" + " ```gleam\n" + " pub type PocketMonsterPerson {\n" + " Trainer(name: String, badge_count: Int)\n" + " GymLeader(name: String, speciality: PocketMonsterType)\n" + " }\n" + " ```\n" + " And you would like to be able to decode these from dynamic data like this:\n" + " ```erlang\n" + " {\n" + " \"type\" -> \"trainer\",\n" + " \"name\" -> \"Ash\",\n" + " \"badge-count\" -> 1,\n" + " }\n" + " ```\n" + " ```erlang\n" + " {\n" + " \"type\" -> \"gym-leader\",\n" + " \"name\" -> \"Misty\",\n" + " \"speciality\" -> \"water\",\n" + " }\n" + " ```\n" + "\n" + " Notice how both documents have a `\"type\"` field, which is used to indicate which\n" + " variant the data is for.\n" + "\n" + " First, define decoders for each of the variants:\n" + "\n" + " ```gleam\n" + " let trainer_decoder = {\n" + " use name <- decode.field(\"name\", decode.string)\n" + " use badge_count <- decode.field(\"badge-count\", decode.int)\n" + " decode.success(Trainer(name, badge_count))\n" + " }\n" + "\n" + " let gym_leader_decoder = {\n" + " use name <- decode.field(\"name\", decode.string)\n" + " use speciality <- decode.field(\"speciality\", pocket_monster_type_decoder)\n" + " decode.success(GymLeader(name, speciality))\n" + " }\n" + " ```\n" + "\n" + " A third decoder can be used to extract and decode the `\"type\"` field, and the\n" + " expression can evaluate to whichever decoder is suitable for the document.\n" + "\n" + " ```gleam\n" + " // Data:\n" + " // {\n" + " // \"type\" -> \"gym-leader\",\n" + " // \"name\" -> \"Misty\",\n" + " // \"speciality\" -> \"water\",\n" + " // }\n" + "\n" + " let decoder = {\n" + " use tag <- decode.field(\"type\", decode.string)\n" + " case tag {\n" + " \"gym-leader\" -> gym_leader_decoder\n" + " _ -> trainer_decoder\n" + " }\n" + " }\n" + "\n" + " let result = decode.run(data, decoder)\n" + " assert result == Ok(GymLeader(\"Misty\", Water))\n" + " ```\n" +). + +-type decode_error() :: {decode_error, binary(), binary(), list(binary())}. + +-opaque decoder(BUW) :: {decoder, + fun((gleam@dynamic:dynamic_()) -> {BUW, list(decode_error())})}. + +-file("src/gleam/dynamic/decode.gleam", 356). +?DOC( + " Run a decoder on a `Dynamic` value, decoding the value if it is of the\n" + " desired type, or returning errors.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " let decoder = {\n" + " use name <- decode.field(\"email\", decode.string)\n" + " use email <- decode.field(\"password\", decode.string)\n" + " decode.success(SignUp(name: name, email: email))\n" + " }\n" + "\n" + " decode.run(data, decoder)\n" + " ```\n" +). +-spec run(gleam@dynamic:dynamic_(), decoder(BVE)) -> {ok, BVE} | + {error, list(decode_error())}. +run(Data, Decoder) -> + {Maybe_invalid_data, Errors} = (erlang:element(2, Decoder))(Data), + case Errors of + [] -> + {ok, Maybe_invalid_data}; + + [_ | _] -> + {error, Errors} + end. + +-file("src/gleam/dynamic/decode.gleam", 479). +?DOC( + " Finalise a decoder having successfully extracted a value.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " let data = dynamic.properties([\n" + " #(dynamic.string(\"email\"), dynamic.string(\"lucy@example.com\")),\n" + " #(dynamic.string(\"name\"), dynamic.string(\"Lucy\")),\n" + " ]))\n" + "\n" + " let decoder = {\n" + " use name <- decode.field(\"name\", string)\n" + " use email <- decode.field(\"email\", string)\n" + " decode.success(SignUp(name: name, email: email))\n" + " }\n" + "\n" + " let result = decode.run(data, decoder)\n" + " assert result == Ok(SignUp(name: \"Lucy\", email: \"lucy@example.com\"))\n" + " ```\n" +). +-spec success(BWF) -> decoder(BWF). +success(Data) -> + {decoder, fun(_) -> {Data, []} end}. + +-file("src/gleam/dynamic/decode.gleam", 718). +-spec decode_dynamic(gleam@dynamic:dynamic_()) -> {gleam@dynamic:dynamic_(), + list(decode_error())}. +decode_dynamic(Data) -> + {Data, []}. + +-file("src/gleam/dynamic/decode.gleam", 875). +?DOC( + " Apply a transformation function to any value decoded by the decoder.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " let decoder = decode.int |> decode.map(int.to_string)\n" + " let result = decode.run(dynamic.int(1000), decoder)\n" + " assert result == Ok(\"1000\")\n" + " ```\n" +). +-spec map(decoder(BZC), fun((BZC) -> BZE)) -> decoder(BZE). +map(Decoder, Transformer) -> + {decoder, + fun(D) -> + {Data, Errors} = (erlang:element(2, Decoder))(D), + {Transformer(Data), Errors} + end}. + +-file("src/gleam/dynamic/decode.gleam", 884). +?DOC(" Apply a transformation function to any errors returned by the decoder.\n"). +-spec map_errors( + decoder(BZG), + fun((list(decode_error())) -> list(decode_error())) +) -> decoder(BZG). +map_errors(Decoder, Transformer) -> + {decoder, + fun(D) -> + {Data, Errors} = (erlang:element(2, Decoder))(D), + {Data, Transformer(Errors)} + end}. + +-file("src/gleam/dynamic/decode.gleam", 922). +?DOC( + " Create a new decoder based upon the value of a previous decoder.\n" + "\n" + " This may be useful to run one previous decoder to use in further decoding.\n" +). +-spec then(decoder(BZO), fun((BZO) -> decoder(BZQ))) -> decoder(BZQ). +then(Decoder, Next) -> + {decoder, + fun(Dynamic_data) -> + {Data, Errors} = (erlang:element(2, Decoder))(Dynamic_data), + Decoder@1 = Next(Data), + {Data@1, _} = Layer = (erlang:element(2, Decoder@1))(Dynamic_data), + case Errors of + [] -> + Layer; + + [_ | _] -> + {Data@1, Errors} + end + end}. + +-file("src/gleam/dynamic/decode.gleam", 965). +-spec run_decoders( + gleam@dynamic:dynamic_(), + {BZY, list(decode_error())}, + list(decoder(BZY)) +) -> {BZY, list(decode_error())}. +run_decoders(Data, Failure, Decoders) -> + case Decoders of + [] -> + Failure; + + [Decoder | Decoders@1] -> + {_, Errors} = Layer = (erlang:element(2, Decoder))(Data), + case Errors of + [] -> + Layer; + + [_ | _] -> + run_decoders(Data, Failure, Decoders@1) + end + end. + +-file("src/gleam/dynamic/decode.gleam", 952). +?DOC( + " Create a new decoder from several other decoders. Each of the inner\n" + " decoders is run in turn, and the value from the first to succeed is used.\n" + "\n" + " If no decoder succeeds then the errors from the first decoder is used.\n" + " If you wish for different errors then you may wish to use the\n" + " `collapse_errors` or `map_errors` functions.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " let decoder = decode.one_of(decode.string, or: [\n" + " decode.int |> decode.map(int.to_string),\n" + " decode.float |> decode.map(float.to_string),\n" + " ])\n" + " decode.run(dynamic.int(1000), decoder)\n" + " // -> Ok(\"1000\")\n" + " ```\n" +). +-spec one_of(decoder(BZT), list(decoder(BZT))) -> decoder(BZT). +one_of(First, Alternatives) -> + {decoder, + fun(Dynamic_data) -> + {_, Errors} = Layer = (erlang:element(2, First))(Dynamic_data), + case Errors of + [] -> + Layer; + + [_ | _] -> + run_decoders(Dynamic_data, Layer, Alternatives) + end + end}. + +-file("src/gleam/dynamic/decode.gleam", 1048). +?DOC( + " Create a decoder that can refer to itself, useful for decoding deeply\n" + " nested data.\n" + "\n" + " Attempting to create a recursive decoder without this function could result\n" + " in an infinite loop. If you are using `field` or other `use`able functions\n" + " then you may not need to use this function.\n" + "\n" + " ```gleam\n" + " type Nested {\n" + " Nested(List(Nested))\n" + " Value(String)\n" + " }\n" + "\n" + " fn nested_decoder() -> decode.Decoder(Nested) {\n" + " use <- decode.recursive\n" + " decode.one_of(decode.string |> decode.map(Value), [\n" + " decode.list(nested_decoder()) |> decode.map(Nested),\n" + " ])\n" + " }\n" + " ```\n" +). +-spec recursive(fun(() -> decoder(CAJ))) -> decoder(CAJ). +recursive(Inner) -> + {decoder, + fun(Data) -> + Decoder = Inner(), + (erlang:element(2, Decoder))(Data) + end}. + +-file("src/gleam/dynamic/decode.gleam", 853). +?DOC( + " A decoder that decodes nullable values of a type decoded by with a given\n" + " decoder.\n" + "\n" + " This function can handle common representations of null on all runtimes, such as\n" + " `nil`, `null`, and `undefined` on Erlang, and `undefined` and `null` on\n" + " JavaScript.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " let result = decode.run(dynamic.int(100), decode.optional(decode.int))\n" + " assert result == Ok(option.Some(100))\n" + " ```\n" + "\n" + " ```gleam\n" + " let result = decode.run(dynamic.nil(), decode.optional(decode.int))\n" + " assert result == Ok(option.None)\n" + " ```\n" +). +-spec optional(decoder(BYY)) -> decoder(gleam@option:option(BYY)). +optional(Inner) -> + {decoder, fun(Data) -> case gleam_stdlib:is_null(Data) of + true -> + {none, []}; + + false -> + {Data@1, Errors} = (erlang:element(2, Inner))(Data), + {{some, Data@1}, Errors} + end end}. + +-file("src/gleam/dynamic/decode.gleam", 485). +?DOC(" Construct a decode error for some unexpected dynamic data.\n"). +-spec decode_error(binary(), gleam@dynamic:dynamic_()) -> list(decode_error()). +decode_error(Expected, Found) -> + [{decode_error, Expected, gleam_stdlib:classify_dynamic(Found), []}]. + +-file("src/gleam/dynamic/decode.gleam", 609). +-spec run_dynamic_function( + gleam@dynamic:dynamic_(), + binary(), + fun((gleam@dynamic:dynamic_()) -> {ok, BWZ} | {error, BWZ}) +) -> {BWZ, list(decode_error())}. +run_dynamic_function(Data, Name, F) -> + case F(Data) of + {ok, Data@1} -> + {Data@1, []}; + + {error, Zero} -> + {Zero, + [{decode_error, Name, gleam_stdlib:classify_dynamic(Data), []}]} + end. + +-file("src/gleam/dynamic/decode.gleam", 658). +-spec decode_bool(gleam@dynamic:dynamic_()) -> {boolean(), list(decode_error())}. +decode_bool(Data) -> + case gleam_stdlib:identity(true) =:= Data of + true -> + {true, []}; + + false -> + case gleam_stdlib:identity(false) =:= Data of + true -> + {false, []}; + + false -> + {false, decode_error(<<"Bool"/utf8>>, Data)} + end + end. + +-file("src/gleam/dynamic/decode.gleam", 680). +-spec decode_int(gleam@dynamic:dynamic_()) -> {integer(), list(decode_error())}. +decode_int(Data) -> + run_dynamic_function(Data, <<"Int"/utf8>>, fun gleam_stdlib:int/1). + +-file("src/gleam/dynamic/decode.gleam", 699). +-spec decode_float(gleam@dynamic:dynamic_()) -> {float(), list(decode_error())}. +decode_float(Data) -> + run_dynamic_function(Data, <<"Float"/utf8>>, fun gleam_stdlib:float/1). + +-file("src/gleam/dynamic/decode.gleam", 733). +-spec decode_bit_array(gleam@dynamic:dynamic_()) -> {bitstring(), + list(decode_error())}. +decode_bit_array(Data) -> + run_dynamic_function( + Data, + <<"BitArray"/utf8>>, + fun gleam_stdlib:bit_array/1 + ). + +-file("src/gleam/dynamic/decode.gleam", 908). +?DOC( + " Replace all errors produced by a decoder with one single error for a named\n" + " expected type.\n" + "\n" + " This function may be useful if you wish to simplify errors before\n" + " presenting them to a user, particularly when using the `one_of` function.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " let decoder = decode.string |> decode.collapse_errors(\"MyThing\")\n" + " let result = decode.run(dynamic.int(1000), decoder)\n" + " assert result == Error([DecodeError(\"MyThing\", \"Int\", [])])\n" + " ```\n" +). +-spec collapse_errors(decoder(BZL), binary()) -> decoder(BZL). +collapse_errors(Decoder, Name) -> + {decoder, + fun(Dynamic_data) -> + {Data, Errors} = Layer = (erlang:element(2, Decoder))(Dynamic_data), + case Errors of + [] -> + Layer; + + [_ | _] -> + {Data, decode_error(Name, Dynamic_data)} + end + end}. + +-file("src/gleam/dynamic/decode.gleam", 986). +?DOC( + " Define a decoder that always fails. The parameter for this function is the\n" + " name of the type that has failed to decode.\n" +). +-spec failure(CAD, binary()) -> decoder(CAD). +failure(Zero, Expected) -> + {decoder, fun(D) -> {Zero, decode_error(Expected, D)} end}. + +-file("src/gleam/dynamic/decode.gleam", 1015). +?DOC( + " Create a decoder for a new data type from a decoding function.\n" + "\n" + " This function is used for new primitive types. For example, you might\n" + " define a decoder for Erlang's pid type.\n" + "\n" + " A default \"zero\" value is also required to make a decoder. When this\n" + " decoder is used as part of a larger decoder this zero value used as\n" + " a placeholder so that the rest of the decoder can continue to run and\n" + " collect all decoding errors.\n" + "\n" + " If you were to make a decoder for the `String` type (rather than using the\n" + " build-in `string` decoder) you would define it like so:\n" + "\n" + " ```gleam\n" + " pub fn string_decoder() -> decode.Decoder(String) {\n" + " let default = \"\"\n" + " decode.new_primitive_decoder(\"String\", fn(data) {\n" + " case dynamic.string(data) {\n" + " Ok(x) -> Ok(x)\n" + " Error(_) -> Error(default)\n" + " }\n" + " })\n" + " }\n" + " ```\n" +). +-spec new_primitive_decoder( + binary(), + fun((gleam@dynamic:dynamic_()) -> {ok, CAF} | {error, CAF}) +) -> decoder(CAF). +new_primitive_decoder(Name, Decoding_function) -> + {decoder, fun(D) -> case Decoding_function(D) of + {ok, T} -> + {T, []}; + + {error, Zero} -> + {Zero, + [{decode_error, + Name, + gleam_stdlib:classify_dynamic(D), + []}]} + end end}. + +-file("src/gleam/dynamic/decode.gleam", 636). +-spec dynamic_string(gleam@dynamic:dynamic_()) -> {ok, binary()} | + {error, binary()}. +dynamic_string(Data) -> + case gleam_stdlib:bit_array(Data) of + {ok, Data@1} -> + case gleam@bit_array:to_string(Data@1) of + {ok, String} -> + {ok, String}; + + {error, _} -> + {error, <<""/utf8>>} + end; + + {error, _} -> + {error, <<""/utf8>>} + end. + +-file("src/gleam/dynamic/decode.gleam", 631). +-spec decode_string(gleam@dynamic:dynamic_()) -> {binary(), + list(decode_error())}. +decode_string(Data) -> + run_dynamic_function(Data, <<"String"/utf8>>, fun dynamic_string/1). + +-file("src/gleam/dynamic/decode.gleam", 807). +-spec fold_dict( + {gleam@dict:dict(BYK, BYL), list(decode_error())}, + gleam@dynamic:dynamic_(), + gleam@dynamic:dynamic_(), + fun((gleam@dynamic:dynamic_()) -> {BYK, list(decode_error())}), + fun((gleam@dynamic:dynamic_()) -> {BYL, list(decode_error())}) +) -> {gleam@dict:dict(BYK, BYL), list(decode_error())}. +fold_dict(Acc, Key, Value, Key_decoder, Value_decoder) -> + case Key_decoder(Key) of + {Key@1, []} -> + case Value_decoder(Value) of + {Value@1, []} -> + Dict = gleam@dict:insert( + erlang:element(1, Acc), + Key@1, + Value@1 + ), + {Dict, erlang:element(2, Acc)}; + + {_, Errors} -> + push_path({maps:new(), Errors}, [<<"values"/utf8>>]) + end; + + {_, Errors@1} -> + push_path({maps:new(), Errors@1}, [<<"keys"/utf8>>]) + end. + +-file("src/gleam/dynamic/decode.gleam", 787). +?DOC( + " A decoder that decodes dicts where all keys and vales are decoded with\n" + " given decoders.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " let values = dynamic.properties([\n" + " #(dynamic.string(\"one\"), dynamic.int(1)),\n" + " #(dynamic.string(\"two\"), dynamic.int(2)),\n" + " ])\n" + "\n" + " let result =\n" + " decode.run(values, decode.dict(decode.string, decode.int))\n" + " assert result == Ok(values)\n" + " ```\n" +). +-spec dict(decoder(BYD), decoder(BYF)) -> decoder(gleam@dict:dict(BYD, BYF)). +dict(Key, Value) -> + {decoder, fun(Data) -> case gleam_stdlib:dict(Data) of + {error, _} -> + {maps:new(), decode_error(<<"Dict"/utf8>>, Data)}; + + {ok, Dict} -> + gleam@dict:fold( + Dict, + {maps:new(), []}, + fun(A, K, V) -> case erlang:element(2, A) of + [] -> + fold_dict( + A, + K, + V, + erlang:element(2, Key), + erlang:element(2, Value) + ); + + [_ | _] -> + A + end end + ) + end end}. + +-file("src/gleam/dynamic/decode.gleam", 755). +?DOC( + " A decoder that decodes lists where all elements are decoded with a given\n" + " decoder.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " let result =\n" + " [1, 2, 3]\n" + " |> list.map(dynamic.int)\n" + " |> dynamic.list\n" + " |> decode.run(decode.list(of: decode.int))\n" + " assert result == Ok([1, 2, 3])\n" + " ```\n" +). +-spec list(decoder(BXR)) -> decoder(list(BXR)). +list(Inner) -> + {decoder, + fun(Data) -> + gleam_stdlib:list( + Data, + erlang:element(2, Inner), + fun(P, K) -> push_path(P, [K]) end, + 0, + [] + ) + end}. + +-file("src/gleam/dynamic/decode.gleam", 439). +-spec push_path({BWA, list(decode_error())}, list(any())) -> {BWA, + list(decode_error())}. +push_path(Layer, Path) -> + Decoder = one_of( + {decoder, fun decode_string/1}, + [begin + _pipe = {decoder, fun decode_int/1}, + map(_pipe, fun erlang:integer_to_binary/1) + end] + ), + Path@1 = gleam@list:map( + Path, + fun(Key) -> + Key@1 = gleam_stdlib:identity(Key), + case run(Key@1, Decoder) of + {ok, Key@2} -> + Key@2; + + {error, _} -> + <<<<"<"/utf8, + (gleam_stdlib:classify_dynamic(Key@1))/binary>>/binary, + ">"/utf8>> + end + end + ), + Errors = gleam@list:map( + erlang:element(2, Layer), + fun(Error) -> + {decode_error, + erlang:element(2, Error), + erlang:element(3, Error), + lists:append(Path@1, erlang:element(4, Error))} + end + ), + {erlang:element(1, Layer), Errors}. + +-file("src/gleam/dynamic/decode.gleam", 403). +-spec index( + list(BVO), + list(BVO), + fun((gleam@dynamic:dynamic_()) -> {BVR, list(decode_error())}), + gleam@dynamic:dynamic_(), + fun((gleam@dynamic:dynamic_(), list(BVO)) -> {BVR, list(decode_error())}) +) -> {BVR, list(decode_error())}. +index(Path, Position, Inner, Data, Handle_miss) -> + case Path of + [] -> + _pipe = Data, + _pipe@1 = Inner(_pipe), + push_path(_pipe@1, lists:reverse(Position)); + + [Key | Path@1] -> + case gleam_stdlib:index(Data, Key) of + {ok, {some, Data@1}} -> + index(Path@1, [Key | Position], Inner, Data@1, Handle_miss); + + {ok, none} -> + Handle_miss(Data, [Key | Position]); + + {error, Kind} -> + {Default, _} = Inner(Data), + _pipe@2 = {Default, + [{decode_error, + Kind, + gleam_stdlib:classify_dynamic(Data), + []}]}, + push_path(_pipe@2, lists:reverse(Position)) + end + end. + +-file("src/gleam/dynamic/decode.gleam", 324). +?DOC( + " The same as [`field`](#field), except taking a path to the value rather\n" + " than a field name.\n" + "\n" + " This function will index into dictionaries with any key type, and if the key is\n" + " an int then it'll also index into Erlang tuples and JavaScript arrays, and\n" + " the first eight elements of Gleam lists.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " let data = dynamic.properties([\n" + " #(dynamic.string(\"data\"), dynamic.properties([\n" + " #(dynamic.string(\"email\"), dynamic.string(\"lucy@example.com\")),\n" + " #(dynamic.string(\"name\"), dynamic.string(\"Lucy\")),\n" + " ])\n" + " ]))\n" + "\n" + " let decoder = {\n" + " use name <- decode.subfield([\"data\", \"name\"], decode.string)\n" + " use email <- decode.subfield([\"data\", \"email\"], decode.string)\n" + " decode.success(SignUp(name: name, email: email))\n" + " }\n" + " let result = decode.run(data, decoder)\n" + " assert result == Ok(SignUp(name: \"Lucy\", email: \"lucy@example.com\"))\n" + " ```\n" +). +-spec subfield(list(any()), decoder(BUZ), fun((BUZ) -> decoder(BVB))) -> decoder(BVB). +subfield(Field_path, Field_decoder, Next) -> + {decoder, + fun(Data) -> + {Out, Errors1} = index( + Field_path, + [], + erlang:element(2, Field_decoder), + Data, + fun(Data@1, Position) -> + {Default, _} = (erlang:element(2, Field_decoder))(Data@1), + _pipe = {Default, + [{decode_error, + <<"Field"/utf8>>, + <<"Nothing"/utf8>>, + []}]}, + push_path(_pipe, lists:reverse(Position)) + end + ), + {Out@1, Errors2} = (erlang:element(2, Next(Out)))(Data), + {Out@1, lists:append(Errors1, Errors2)} + end}. + +-file("src/gleam/dynamic/decode.gleam", 393). +?DOC( + " A decoder that decodes a value that is nested within other values. For\n" + " example, decoding a value that is within some deeply nested JSON objects.\n" + "\n" + " This function will index into dictionaries with any key type, and if the key is\n" + " an int then it'll also index into Erlang tuples and JavaScript arrays, and\n" + " the first eight elements of Gleam lists.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " let decoder = decode.at([\"one\", \"two\"], decode.int)\n" + "\n" + " let data = dynamic.properties([\n" + " #(dynamic.string(\"one\"), dynamic.properties([\n" + " #(dynamic.string(\"two\"), dynamic.int(1000)),\n" + " ])),\n" + " ]))\n" + "\n" + "\n" + " decode.run(data, decoder)\n" + " // -> Ok(1000)\n" + " ```\n" + "\n" + " ```gleam\n" + " dynamic.nil()\n" + " |> decode.run(decode.optional(decode.int))\n" + " // -> Ok(option.None)\n" + " ```\n" +). +-spec at(list(any()), decoder(BVL)) -> decoder(BVL). +at(Path, Inner) -> + {decoder, + fun(Data) -> + index( + Path, + [], + erlang:element(2, Inner), + Data, + fun(Data@1, Position) -> + {Default, _} = (erlang:element(2, Inner))(Data@1), + _pipe = {Default, + [{decode_error, + <<"Field"/utf8>>, + <<"Nothing"/utf8>>, + []}]}, + push_path(_pipe, lists:reverse(Position)) + end + ) + end}. + +-file("src/gleam/dynamic/decode.gleam", 524). +?DOC( + " Run a decoder on a field of a `Dynamic` value, decoding the value if it is\n" + " of the desired type, or returning errors. An error is returned if there is\n" + " no field for the specified key.\n" + "\n" + " This function will index into dictionaries with any key type, and if the key is\n" + " an int then it'll also index into Erlang tuples and JavaScript arrays, and\n" + " the first eight elements of Gleam lists.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " let data = dynamic.properties([\n" + " #(dynamic.string(\"email\"), dynamic.string(\"lucy@example.com\")),\n" + " #(dynamic.string(\"name\"), dynamic.string(\"Lucy\")),\n" + " ]))\n" + "\n" + " let decoder = {\n" + " use name <- decode.field(\"name\", string)\n" + " use email <- decode.field(\"email\", string)\n" + " decode.success(SignUp(name: name, email: email))\n" + " }\n" + "\n" + " let result = decode.run(data, decoder)\n" + " assert result == Ok(SignUp(name: \"Lucy\", email: \"lucy@example.com\"))\n" + " ```\n" + "\n" + " If you wish to decode a value that is more deeply nested within the dynamic\n" + " data, see [`subfield`](#subfield) and [`at`](#at).\n" + "\n" + " If you wish to return a default in the event that a field is not present,\n" + " see [`optional_field`](#optional_field) and / [`optionally_at`](#optionally_at).\n" +). +-spec field(any(), decoder(BWJ), fun((BWJ) -> decoder(BWL))) -> decoder(BWL). +field(Field_name, Field_decoder, Next) -> + subfield([Field_name], Field_decoder, Next). + +-file("src/gleam/dynamic/decode.gleam", 557). +?DOC( + " Run a decoder on a field of a `Dynamic` value, decoding the value if it is\n" + " of the desired type, or returning errors. The given default value is\n" + " returned if there is no field for the specified key.\n" + "\n" + " This function will index into dictionaries with any key type, and if the key is\n" + " an int then it'll also index into Erlang tuples and JavaScript arrays, and\n" + " the first eight elements of Gleam lists.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " let data = dynamic.properties([\n" + " #(dynamic.string(\"name\"), dynamic.string(\"Lucy\")),\n" + " ]))\n" + "\n" + " let decoder = {\n" + " use name <- decode.field(\"name\", string)\n" + " use email <- decode.optional_field(\"email\", \"n/a\", string)\n" + " decode.success(SignUp(name: name, email: email))\n" + " }\n" + "\n" + " let result = decode.run(data, decoder)\n" + " assert result == Ok(SignUp(name: \"Lucy\", email: \"n/a\"))\n" + " ```\n" +). +-spec optional_field(any(), BWP, decoder(BWP), fun((BWP) -> decoder(BWR))) -> decoder(BWR). +optional_field(Key, Default, Field_decoder, Next) -> + {decoder, + fun(Data) -> + {Out, Errors1} = begin + _pipe = case gleam_stdlib:index(Data, Key) of + {ok, {some, Data@1}} -> + (erlang:element(2, Field_decoder))(Data@1); + + {ok, none} -> + {Default, []}; + + {error, Kind} -> + {Default, + [{decode_error, + Kind, + gleam_stdlib:classify_dynamic(Data), + []}]} + end, + push_path(_pipe, [Key]) + end, + {Out@1, Errors2} = (erlang:element(2, Next(Out)))(Data), + {Out@1, lists:append(Errors1, Errors2)} + end}. + +-file("src/gleam/dynamic/decode.gleam", 599). +?DOC( + " A decoder that decodes a value that is nested within other values. For\n" + " example, decoding a value that is within some deeply nested JSON objects.\n" + "\n" + " This function will index into dictionaries with any key type, and if the key is\n" + " an int then it'll also index into Erlang tuples and JavaScript arrays, and\n" + " the first eight elements of Gleam lists.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " let decoder = decode.optionally_at([\"one\", \"two\"], 100, decode.int)\n" + "\n" + " let data = dynamic.properties([\n" + " #(dynamic.string(\"one\"), dynamic.properties([])),\n" + " ]))\n" + "\n" + "\n" + " decode.run(data, decoder)\n" + " // -> Ok(100)\n" + " ```\n" +). +-spec optionally_at(list(any()), BWW, decoder(BWW)) -> decoder(BWW). +optionally_at(Path, Default, Inner) -> + {decoder, + fun(Data) -> + index( + Path, + [], + erlang:element(2, Inner), + Data, + fun(_, _) -> {Default, []} end + ) + end}. diff --git a/build/dev/javascript/gleam_stdlib/gleam@float.erl b/build/dev/javascript/gleam_stdlib/gleam@float.erl new file mode 100644 index 0000000..6b55b47 --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam@float.erl @@ -0,0 +1,744 @@ +-module(gleam@float). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/float.gleam"). +-export([parse/1, to_string/1, compare/2, min/2, max/2, clamp/3, ceiling/1, floor/1, truncate/1, absolute_value/1, loosely_compare/3, loosely_equals/3, power/2, square_root/1, negate/1, round/1, to_precision/2, sum/1, product/1, random/0, modulo/2, divide/2, add/2, multiply/2, subtract/2, logarithm/1, exponential/1]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC( + " Functions for working with floats.\n" + "\n" + " ## Float representation\n" + "\n" + " Floats are represented as 64 bit floating point numbers on both the Erlang\n" + " and JavaScript runtimes. The floating point behaviour is native to their\n" + " respective runtimes, so their exact behaviour will be slightly different on\n" + " the two runtimes.\n" + "\n" + " ### Infinity and NaN\n" + "\n" + " Under the JavaScript runtime, exceeding the maximum (or minimum)\n" + " representable value for a floating point value will result in Infinity (or\n" + " -Infinity). Should you try to divide two infinities you will get NaN as a\n" + " result.\n" + "\n" + " When running on BEAM, exceeding the maximum (or minimum) representable\n" + " value for a floating point value will raise an error.\n" + "\n" + " ## Division by zero\n" + "\n" + " Gleam runs on the Erlang virtual machine, which does not follow the IEEE\n" + " 754 standard for floating point arithmetic and does not have an `Infinity`\n" + " value. In Erlang division by zero results in a crash, however Gleam does\n" + " not have partial functions and operators in core so instead division by zero\n" + " returns zero, a behaviour taken from Pony, Coq, and Lean.\n" + "\n" + " This may seem unexpected at first, but it is no less mathematically valid\n" + " than crashing or returning a special value. Division by zero is undefined\n" + " in mathematics.\n" +). + +-file("src/gleam/float.gleam", 51). +?DOC( + " Attempts to parse a string as a `Float`, returning `Error(Nil)` if it was\n" + " not possible.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " parse(\"2.3\")\n" + " // -> Ok(2.3)\n" + " ```\n" + "\n" + " ```gleam\n" + " parse(\"ABC\")\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec parse(binary()) -> {ok, float()} | {error, nil}. +parse(String) -> + gleam_stdlib:parse_float(String). + +-file("src/gleam/float.gleam", 64). +?DOC( + " Returns the string representation of the provided `Float`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " to_string(2.3)\n" + " // -> \"2.3\"\n" + " ```\n" +). +-spec to_string(float()) -> binary(). +to_string(X) -> + gleam_stdlib:float_to_string(X). + +-file("src/gleam/float.gleam", 95). +?DOC( + " Compares two `Float`s, returning an `Order`:\n" + " `Lt` for lower than, `Eq` for equals, or `Gt` for greater than.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " compare(2.0, 2.3)\n" + " // -> Lt\n" + " ```\n" + "\n" + " To handle\n" + " [Floating Point Imprecision](https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems)\n" + " you may use [`loosely_compare`](#loosely_compare) instead.\n" +). +-spec compare(float(), float()) -> gleam@order:order(). +compare(A, B) -> + case A =:= B of + true -> + eq; + + false -> + case A < B of + true -> + lt; + + false -> + gt + end + end. + +-file("src/gleam/float.gleam", 176). +?DOC( + " Compares two `Float`s, returning the smaller of the two.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " min(2.0, 2.3)\n" + " // -> 2.0\n" + " ```\n" +). +-spec min(float(), float()) -> float(). +min(A, B) -> + case A < B of + true -> + A; + + false -> + B + end. + +-file("src/gleam/float.gleam", 192). +?DOC( + " Compares two `Float`s, returning the larger of the two.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " max(2.0, 2.3)\n" + " // -> 2.3\n" + " ```\n" +). +-spec max(float(), float()) -> float(). +max(A, B) -> + case A > B of + true -> + A; + + false -> + B + end. + +-file("src/gleam/float.gleam", 75). +?DOC( + " Restricts a `Float` between a lower and upper bound.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " clamp(1.2, min: 1.4, max: 1.6)\n" + " // -> 1.4\n" + " ```\n" +). +-spec clamp(float(), float(), float()) -> float(). +clamp(X, Min_bound, Max_bound) -> + _pipe = X, + _pipe@1 = min(_pipe, Max_bound), + max(_pipe@1, Min_bound). + +-file("src/gleam/float.gleam", 210). +?DOC( + " Rounds the value to the next highest whole number as a `Float`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " ceiling(2.3)\n" + " // -> 3.0\n" + " ```\n" +). +-spec ceiling(float()) -> float(). +ceiling(X) -> + math:ceil(X). + +-file("src/gleam/float.gleam", 223). +?DOC( + " Rounds the value to the next lowest whole number as a `Float`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " floor(2.3)\n" + " // -> 2.0\n" + " ```\n" +). +-spec floor(float()) -> float(). +floor(X) -> + math:floor(X). + +-file("src/gleam/float.gleam", 261). +?DOC( + " Returns the value as an `Int`, truncating all decimal digits.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " truncate(2.4343434847383438)\n" + " // -> 2\n" + " ```\n" +). +-spec truncate(float()) -> integer(). +truncate(X) -> + erlang:trunc(X). + +-file("src/gleam/float.gleam", 311). +?DOC( + " Returns the absolute value of the input as a `Float`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " absolute_value(-12.5)\n" + " // -> 12.5\n" + " ```\n" + "\n" + " ```gleam\n" + " absolute_value(10.2)\n" + " // -> 10.2\n" + " ```\n" +). +-spec absolute_value(float()) -> float(). +absolute_value(X) -> + case X >= +0.0 of + true -> + X; + + false -> + +0.0 - X + end. + +-file("src/gleam/float.gleam", 125). +?DOC( + " Compares two `Float`s within a tolerance, returning an `Order`:\n" + " `Lt` for lower than, `Eq` for equals, or `Gt` for greater than.\n" + "\n" + " This function allows Float comparison while handling\n" + " [Floating Point Imprecision](https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems).\n" + "\n" + " Notice: For `Float`s the tolerance won't be exact:\n" + " `5.3 - 5.0` is not exactly `0.3`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " loosely_compare(5.0, with: 5.3, tolerating: 0.5)\n" + " // -> Eq\n" + " ```\n" + "\n" + " If you want to check only for equality you may use\n" + " [`loosely_equals`](#loosely_equals) instead.\n" +). +-spec loosely_compare(float(), float(), float()) -> gleam@order:order(). +loosely_compare(A, B, Tolerance) -> + Difference = absolute_value(A - B), + case Difference =< Tolerance of + true -> + eq; + + false -> + compare(A, B) + end. + +-file("src/gleam/float.gleam", 158). +?DOC( + " Checks for equality of two `Float`s within a tolerance,\n" + " returning an `Bool`.\n" + "\n" + " This function allows Float comparison while handling\n" + " [Floating Point Imprecision](https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems).\n" + "\n" + " Notice: For `Float`s the tolerance won't be exact:\n" + " `5.3 - 5.0` is not exactly `0.3`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " loosely_equals(5.0, with: 5.3, tolerating: 0.5)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " loosely_equals(5.0, with: 5.1, tolerating: 0.1)\n" + " // -> False\n" + " ```\n" +). +-spec loosely_equals(float(), float(), float()) -> boolean(). +loosely_equals(A, B, Tolerance) -> + Difference = absolute_value(A - B), + Difference =< Tolerance. + +-file("src/gleam/float.gleam", 348). +?DOC( + " Returns the results of the base being raised to the power of the\n" + " exponent, as a `Float`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " power(2.0, -1.0)\n" + " // -> Ok(0.5)\n" + " ```\n" + "\n" + " ```gleam\n" + " power(2.0, 2.0)\n" + " // -> Ok(4.0)\n" + " ```\n" + "\n" + " ```gleam\n" + " power(8.0, 1.5)\n" + " // -> Ok(22.627416997969522)\n" + " ```\n" + "\n" + " ```gleam\n" + " 4.0 |> power(of: 2.0)\n" + " // -> Ok(16.0)\n" + " ```\n" + "\n" + " ```gleam\n" + " power(-1.0, 0.5)\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec power(float(), float()) -> {ok, float()} | {error, nil}. +power(Base, Exponent) -> + Fractional = (math:ceil(Exponent) - Exponent) > +0.0, + case ((Base < +0.0) andalso Fractional) orelse ((Base =:= +0.0) andalso (Exponent + < +0.0)) of + true -> + {error, nil}; + + false -> + {ok, math:pow(Base, Exponent)} + end. + +-file("src/gleam/float.gleam", 380). +?DOC( + " Returns the square root of the input as a `Float`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " square_root(4.0)\n" + " // -> Ok(2.0)\n" + " ```\n" + "\n" + " ```gleam\n" + " square_root(-16.0)\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec square_root(float()) -> {ok, float()} | {error, nil}. +square_root(X) -> + power(X, 0.5). + +-file("src/gleam/float.gleam", 393). +?DOC( + " Returns the negative of the value provided.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " negate(1.0)\n" + " // -> -1.0\n" + " ```\n" +). +-spec negate(float()) -> float(). +negate(X) -> + -1.0 * X. + +-file("src/gleam/float.gleam", 240). +?DOC( + " Rounds the value to the nearest whole number as an `Int`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " round(2.3)\n" + " // -> 2\n" + " ```\n" + "\n" + " ```gleam\n" + " round(2.5)\n" + " // -> 3\n" + " ```\n" +). +-spec round(float()) -> integer(). +round(X) -> + erlang:round(X). + +-file("src/gleam/float.gleam", 280). +?DOC( + " Converts the value to a given precision as a `Float`.\n" + " The precision is the number of allowed decimal places.\n" + " Negative precisions are allowed and force rounding\n" + " to the nearest tenth, hundredth, thousandth etc.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " to_precision(2.43434348473, precision: 2)\n" + " // -> 2.43\n" + " ```\n" + "\n" + " ```gleam\n" + " to_precision(547890.453444, precision: -3)\n" + " // -> 548000.0\n" + " ```\n" +). +-spec to_precision(float(), integer()) -> float(). +to_precision(X, Precision) -> + case Precision =< 0 of + true -> + Factor = math:pow(10.0, erlang:float(- Precision)), + erlang:float(erlang:round(case Factor of + +0.0 -> +0.0; + -0.0 -> -0.0; + Gleam@denominator -> X / Gleam@denominator + end)) * Factor; + + false -> + Factor@1 = math:pow(10.0, erlang:float(Precision)), + case Factor@1 of + +0.0 -> +0.0; + -0.0 -> -0.0; + Gleam@denominator@1 -> erlang:float(erlang:round(X * Factor@1)) + / Gleam@denominator@1 + end + end. + +-file("src/gleam/float.gleam", 410). +-spec sum_loop(list(float()), float()) -> float(). +sum_loop(Numbers, Initial) -> + case Numbers of + [First | Rest] -> + sum_loop(Rest, First + Initial); + + [] -> + Initial + end. + +-file("src/gleam/float.gleam", 406). +?DOC( + " Sums a list of `Float`s.\n" + "\n" + " ## Example\n" + "\n" + " ```gleam\n" + " sum([1.0, 2.2, 3.3])\n" + " // -> 6.5\n" + " ```\n" +). +-spec sum(list(float())) -> float(). +sum(Numbers) -> + sum_loop(Numbers, +0.0). + +-file("src/gleam/float.gleam", 430). +-spec product_loop(list(float()), float()) -> float(). +product_loop(Numbers, Initial) -> + case Numbers of + [First | Rest] -> + product_loop(Rest, First * Initial); + + [] -> + Initial + end. + +-file("src/gleam/float.gleam", 426). +?DOC( + " Multiplies a list of `Float`s and returns the product.\n" + "\n" + " ## Example\n" + "\n" + " ```gleam\n" + " product([2.5, 3.2, 4.2])\n" + " // -> 33.6\n" + " ```\n" +). +-spec product(list(float())) -> float(). +product(Numbers) -> + product_loop(Numbers, 1.0). + +-file("src/gleam/float.gleam", 452). +?DOC( + " Generates a random float between the given zero (inclusive) and one\n" + " (exclusive).\n" + "\n" + " On Erlang this updates the random state in the process dictionary.\n" + " See: \n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " random()\n" + " // -> 0.646355926896028\n" + " ```\n" +). +-spec random() -> float(). +random() -> + rand:uniform(). + +-file("src/gleam/float.gleam", 481). +?DOC( + " Computes the modulo of an float division of inputs as a `Result`.\n" + "\n" + " Returns division of the inputs as a `Result`: If the given divisor equals\n" + " `0`, this function returns an `Error`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " modulo(13.3, by: 3.3)\n" + " // -> Ok(0.1)\n" + " ```\n" + "\n" + " ```gleam\n" + " modulo(-13.3, by: 3.3)\n" + " // -> Ok(3.2)\n" + " ```\n" + "\n" + " ```gleam\n" + " modulo(13.3, by: -3.3)\n" + " // -> Ok(-3.2)\n" + " ```\n" + "\n" + " ```gleam\n" + " modulo(-13.3, by: -3.3)\n" + " // -> Ok(-0.1)\n" + " ```\n" +). +-spec modulo(float(), float()) -> {ok, float()} | {error, nil}. +modulo(Dividend, Divisor) -> + case Divisor of + +0.0 -> + {error, nil}; + + _ -> + {ok, Dividend - (math:floor(case Divisor of + +0.0 -> +0.0; + -0.0 -> -0.0; + Gleam@denominator -> Dividend / Gleam@denominator + end) * Divisor)} + end. + +-file("src/gleam/float.gleam", 502). +?DOC( + " Returns division of the inputs as a `Result`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " divide(0.0, 1.0)\n" + " // -> Ok(0.0)\n" + " ```\n" + "\n" + " ```gleam\n" + " divide(1.0, 0.0)\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec divide(float(), float()) -> {ok, float()} | {error, nil}. +divide(A, B) -> + case B of + +0.0 -> + {error, nil}; + + B@1 -> + {ok, case B@1 of + +0.0 -> +0.0; + -0.0 -> -0.0; + Gleam@denominator -> A / Gleam@denominator + end} + end. + +-file("src/gleam/float.gleam", 533). +?DOC( + " Adds two floats together.\n" + "\n" + " It's the function equivalent of the `+.` operator.\n" + " This function is useful in higher order functions or pipes.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " add(1.0, 2.0)\n" + " // -> 3.0\n" + " ```\n" + "\n" + " ```gleam\n" + " import gleam/list\n" + "\n" + " list.fold([1.0, 2.0, 3.0], 0.0, add)\n" + " // -> 6.0\n" + " ```\n" + "\n" + " ```gleam\n" + " 3.0 |> add(2.0)\n" + " // -> 5.0\n" + " ```\n" +). +-spec add(float(), float()) -> float(). +add(A, B) -> + A + B. + +-file("src/gleam/float.gleam", 561). +?DOC( + " Multiplies two floats together.\n" + "\n" + " It's the function equivalent of the `*.` operator.\n" + " This function is useful in higher order functions or pipes.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " multiply(2.0, 4.0)\n" + " // -> 8.0\n" + " ```\n" + "\n" + " ```gleam\n" + " import gleam/list\n" + "\n" + " list.fold([2.0, 3.0, 4.0], 1.0, multiply)\n" + " // -> 24.0\n" + " ```\n" + "\n" + " ```gleam\n" + " 3.0 |> multiply(2.0)\n" + " // -> 6.0\n" + " ```\n" +). +-spec multiply(float(), float()) -> float(). +multiply(A, B) -> + A * B. + +-file("src/gleam/float.gleam", 594). +?DOC( + " Subtracts one float from another.\n" + "\n" + " It's the function equivalent of the `-.` operator.\n" + " This function is useful in higher order functions or pipes.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " subtract(3.0, 1.0)\n" + " // -> 2.0\n" + " ```\n" + "\n" + " ```gleam\n" + " import gleam/list\n" + "\n" + " list.fold([1.0, 2.0, 3.0], 10.0, subtract)\n" + " // -> 4.0\n" + " ```\n" + "\n" + " ```gleam\n" + " 3.0 |> subtract(_, 2.0)\n" + " // -> 1.0\n" + " ```\n" + "\n" + " ```gleam\n" + " 3.0 |> subtract(2.0, _)\n" + " // -> -1.0\n" + " ```\n" +). +-spec subtract(float(), float()) -> float(). +subtract(A, B) -> + A - B. + +-file("src/gleam/float.gleam", 623). +?DOC( + " Returns the natural logarithm (base e) of the given as a `Result`. If the\n" + " input is less than or equal to 0, returns `Error(Nil)`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " logarithm(1.0)\n" + " // -> Ok(0.0)\n" + " ```\n" + "\n" + " ```gleam\n" + " logarithm(2.718281828459045) // e\n" + " // -> Ok(1.0)\n" + " ```\n" + "\n" + " ```gleam\n" + " logarithm(0.0)\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " logarithm(-1.0)\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec logarithm(float()) -> {ok, float()} | {error, nil}. +logarithm(X) -> + case X =< +0.0 of + true -> + {error, nil}; + + false -> + {ok, math:log(X)} + end. + +-file("src/gleam/float.gleam", 661). +?DOC( + " Returns e (Euler's number) raised to the power of the given exponent, as\n" + " a `Float`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " exponential(0.0)\n" + " // -> Ok(1.0)\n" + " ```\n" + "\n" + " ```gleam\n" + " exponential(1.0)\n" + " // -> Ok(2.718281828459045)\n" + " ```\n" + "\n" + " ```gleam\n" + " exponential(-1.0)\n" + " // -> Ok(0.36787944117144233)\n" + " ```\n" +). +-spec exponential(float()) -> float(). +exponential(X) -> + math:exp(X). diff --git a/build/dev/javascript/gleam_stdlib/gleam@function.erl b/build/dev/javascript/gleam_stdlib/gleam@function.erl new file mode 100644 index 0000000..a6dec81 --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam@function.erl @@ -0,0 +1,30 @@ +-module(gleam@function). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/function.gleam"). +-export([identity/1, tap/2]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +-file("src/gleam/function.gleam", 3). +?DOC(" Takes a single argument and always returns its input value.\n"). +-spec identity(CLA) -> CLA. +identity(X) -> + X. + +-file("src/gleam/function.gleam", 12). +?DOC( + " Takes an argument and a single function, calls that function with that\n" + " argument and returns that argument instead of the function return value.\n" + "\n" + " Useful for running synchronous side effects in a pipeline.\n" +). +-spec tap(CLB, fun((CLB) -> any())) -> CLB. +tap(Arg, Effect) -> + Effect(Arg), + Arg. diff --git a/build/dev/javascript/gleam_stdlib/gleam@int.erl b/build/dev/javascript/gleam_stdlib/gleam@int.erl new file mode 100644 index 0000000..5e298da --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam@int.erl @@ -0,0 +1,986 @@ +-module(gleam@int). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/int.gleam"). +-export([absolute_value/1, parse/1, base_parse/2, to_string/1, to_base_string/2, to_base2/1, to_base8/1, to_base16/1, to_base36/1, to_float/1, power/2, square_root/1, compare/2, min/2, max/2, clamp/3, is_even/1, is_odd/1, negate/1, sum/1, product/1, digits/2, undigits/2, random/1, divide/2, remainder/2, modulo/2, floor_divide/2, add/2, multiply/2, subtract/2, bitwise_and/2, bitwise_not/1, bitwise_or/2, bitwise_exclusive_or/2, bitwise_shift_left/2, bitwise_shift_right/2]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC( + " Functions for working with integers.\n" + "\n" + " ## Division by zero\n" + "\n" + " In Erlang division by zero results in a crash, however Gleam does not have\n" + " partial functions and operators in core so instead division by zero returns\n" + " zero, a behaviour taken from Pony, Coq, and Lean.\n" + "\n" + " This may seem unexpected at first, but it is no less mathematically valid\n" + " than crashing or returning a special value. Division by zero is undefined\n" + " in mathematics.\n" +). + +-file("src/gleam/int.gleam", 30). +?DOC( + " Returns the absolute value of the input.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " absolute_value(-12)\n" + " // -> 12\n" + " ```\n" + "\n" + " ```gleam\n" + " absolute_value(10)\n" + " // -> 10\n" + " ```\n" +). +-spec absolute_value(integer()) -> integer(). +absolute_value(X) -> + case X >= 0 of + true -> + X; + + false -> + X * -1 + end. + +-file("src/gleam/int.gleam", 109). +?DOC( + " Parses a given string as an int if possible.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " parse(\"2\")\n" + " // -> Ok(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " parse(\"ABC\")\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec parse(binary()) -> {ok, integer()} | {error, nil}. +parse(String) -> + gleam_stdlib:parse_int(String). + +-file("src/gleam/int.gleam", 141). +?DOC( + " Parses a given string as an int in a given base if possible.\n" + " Supports only bases 2 to 36, for values outside of which this function returns an `Error(Nil)`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " base_parse(\"10\", 2)\n" + " // -> Ok(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " base_parse(\"30\", 16)\n" + " // -> Ok(48)\n" + " ```\n" + "\n" + " ```gleam\n" + " base_parse(\"1C\", 36)\n" + " // -> Ok(48)\n" + " ```\n" + "\n" + " ```gleam\n" + " base_parse(\"48\", 1)\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " base_parse(\"48\", 37)\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec base_parse(binary(), integer()) -> {ok, integer()} | {error, nil}. +base_parse(String, Base) -> + case (Base >= 2) andalso (Base =< 36) of + true -> + gleam_stdlib:int_from_base_string(String, Base); + + false -> + {error, nil} + end. + +-file("src/gleam/int.gleam", 163). +?DOC( + " Prints a given int to a string.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " to_string(2)\n" + " // -> \"2\"\n" + " ```\n" +). +-spec to_string(integer()) -> binary(). +to_string(X) -> + erlang:integer_to_binary(X). + +-file("src/gleam/int.gleam", 196). +?DOC( + " Prints a given int to a string using the base number provided.\n" + " Supports only bases 2 to 36, for values outside of which this function returns an `Error(Nil)`.\n" + " For common bases (2, 8, 16, 36), use the `to_baseN` functions.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " to_base_string(2, 2)\n" + " // -> Ok(\"10\")\n" + " ```\n" + "\n" + " ```gleam\n" + " to_base_string(48, 16)\n" + " // -> Ok(\"30\")\n" + " ```\n" + "\n" + " ```gleam\n" + " to_base_string(48, 36)\n" + " // -> Ok(\"1C\")\n" + " ```\n" + "\n" + " ```gleam\n" + " to_base_string(48, 1)\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " to_base_string(48, 37)\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec to_base_string(integer(), integer()) -> {ok, binary()} | {error, nil}. +to_base_string(X, Base) -> + case (Base >= 2) andalso (Base =< 36) of + true -> + {ok, erlang:integer_to_binary(X, Base)}; + + false -> + {error, nil} + end. + +-file("src/gleam/int.gleam", 216). +?DOC( + " Prints a given int to a string using base-2.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " to_base2(2)\n" + " // -> \"10\"\n" + " ```\n" +). +-spec to_base2(integer()) -> binary(). +to_base2(X) -> + erlang:integer_to_binary(X, 2). + +-file("src/gleam/int.gleam", 229). +?DOC( + " Prints a given int to a string using base-8.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " to_base8(15)\n" + " // -> \"17\"\n" + " ```\n" +). +-spec to_base8(integer()) -> binary(). +to_base8(X) -> + erlang:integer_to_binary(X, 8). + +-file("src/gleam/int.gleam", 242). +?DOC( + " Prints a given int to a string using base-16.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " to_base16(48)\n" + " // -> \"30\"\n" + " ```\n" +). +-spec to_base16(integer()) -> binary(). +to_base16(X) -> + erlang:integer_to_binary(X, 16). + +-file("src/gleam/int.gleam", 255). +?DOC( + " Prints a given int to a string using base-36.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " to_base36(48)\n" + " // -> \"1C\"\n" + " ```\n" +). +-spec to_base36(integer()) -> binary(). +to_base36(X) -> + erlang:integer_to_binary(X, 36). + +-file("src/gleam/int.gleam", 280). +?DOC( + " Takes an int and returns its value as a float.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " to_float(5)\n" + " // -> 5.0\n" + " ```\n" + "\n" + " ```gleam\n" + " to_float(0)\n" + " // -> 0.0\n" + " ```\n" + "\n" + " ```gleam\n" + " to_float(-3)\n" + " // -> -3.0\n" + " ```\n" +). +-spec to_float(integer()) -> float(). +to_float(X) -> + erlang:float(X). + +-file("src/gleam/int.gleam", 67). +?DOC( + " Returns the results of the base being raised to the power of the\n" + " exponent, as a `Float`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " power(2, -1.0)\n" + " // -> Ok(0.5)\n" + " ```\n" + "\n" + " ```gleam\n" + " power(2, 2.0)\n" + " // -> Ok(4.0)\n" + " ```\n" + "\n" + " ```gleam\n" + " power(8, 1.5)\n" + " // -> Ok(22.627416997969522)\n" + " ```\n" + "\n" + " ```gleam\n" + " 4 |> power(of: 2.0)\n" + " // -> Ok(16.0)\n" + " ```\n" + "\n" + " ```gleam\n" + " power(-1, 0.5)\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec power(integer(), float()) -> {ok, float()} | {error, nil}. +power(Base, Exponent) -> + _pipe = Base, + _pipe@1 = erlang:float(_pipe), + gleam@float:power(_pipe@1, Exponent). + +-file("src/gleam/int.gleam", 87). +?DOC( + " Returns the square root of the input as a `Float`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " square_root(4)\n" + " // -> Ok(2.0)\n" + " ```\n" + "\n" + " ```gleam\n" + " square_root(-16)\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec square_root(integer()) -> {ok, float()} | {error, nil}. +square_root(X) -> + _pipe = X, + _pipe@1 = erlang:float(_pipe), + gleam@float:square_root(_pipe@1). + +-file("src/gleam/int.gleam", 316). +?DOC( + " Compares two ints, returning an order.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " compare(2, 3)\n" + " // -> Lt\n" + " ```\n" + "\n" + " ```gleam\n" + " compare(4, 3)\n" + " // -> Gt\n" + " ```\n" + "\n" + " ```gleam\n" + " compare(3, 3)\n" + " // -> Eq\n" + " ```\n" +). +-spec compare(integer(), integer()) -> gleam@order:order(). +compare(A, B) -> + case A =:= B of + true -> + eq; + + false -> + case A < B of + true -> + lt; + + false -> + gt + end + end. + +-file("src/gleam/int.gleam", 336). +?DOC( + " Compares two ints, returning the smaller of the two.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " min(2, 3)\n" + " // -> 2\n" + " ```\n" +). +-spec min(integer(), integer()) -> integer(). +min(A, B) -> + case A < B of + true -> + A; + + false -> + B + end. + +-file("src/gleam/int.gleam", 352). +?DOC( + " Compares two ints, returning the larger of the two.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " max(2, 3)\n" + " // -> 3\n" + " ```\n" +). +-spec max(integer(), integer()) -> integer(). +max(A, B) -> + case A > B of + true -> + A; + + false -> + B + end. + +-file("src/gleam/int.gleam", 291). +?DOC( + " Restricts an int between a lower and upper bound.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " clamp(40, min: 50, max: 60)\n" + " // -> 50\n" + " ```\n" +). +-spec clamp(integer(), integer(), integer()) -> integer(). +clamp(X, Min_bound, Max_bound) -> + _pipe = X, + _pipe@1 = min(_pipe, Max_bound), + max(_pipe@1, Min_bound). + +-file("src/gleam/int.gleam", 373). +?DOC( + " Returns whether the value provided is even.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " is_even(2)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " is_even(3)\n" + " // -> False\n" + " ```\n" +). +-spec is_even(integer()) -> boolean(). +is_even(X) -> + (X rem 2) =:= 0. + +-file("src/gleam/int.gleam", 391). +?DOC( + " Returns whether the value provided is odd.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " is_odd(3)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " is_odd(2)\n" + " // -> False\n" + " ```\n" +). +-spec is_odd(integer()) -> boolean(). +is_odd(X) -> + (X rem 2) /= 0. + +-file("src/gleam/int.gleam", 404). +?DOC( + " Returns the negative of the value provided.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " negate(1)\n" + " // -> -1\n" + " ```\n" +). +-spec negate(integer()) -> integer(). +negate(X) -> + -1 * X. + +-file("src/gleam/int.gleam", 421). +-spec sum_loop(list(integer()), integer()) -> integer(). +sum_loop(Numbers, Initial) -> + case Numbers of + [First | Rest] -> + sum_loop(Rest, First + Initial); + + [] -> + Initial + end. + +-file("src/gleam/int.gleam", 417). +?DOC( + " Sums a list of ints.\n" + "\n" + " ## Example\n" + "\n" + " ```gleam\n" + " sum([1, 2, 3])\n" + " // -> 6\n" + " ```\n" +). +-spec sum(list(integer())) -> integer(). +sum(Numbers) -> + sum_loop(Numbers, 0). + +-file("src/gleam/int.gleam", 441). +-spec product_loop(list(integer()), integer()) -> integer(). +product_loop(Numbers, Initial) -> + case Numbers of + [First | Rest] -> + product_loop(Rest, First * Initial); + + [] -> + Initial + end. + +-file("src/gleam/int.gleam", 437). +?DOC( + " Multiplies a list of ints and returns the product.\n" + "\n" + " ## Example\n" + "\n" + " ```gleam\n" + " product([2, 3, 4])\n" + " // -> 24\n" + " ```\n" +). +-spec product(list(integer())) -> integer(). +product(Numbers) -> + product_loop(Numbers, 1). + +-file("src/gleam/int.gleam", 456). +-spec digits_loop(integer(), integer(), list(integer())) -> list(integer()). +digits_loop(X, Base, Acc) -> + case absolute_value(X) < Base of + true -> + [X | Acc]; + + false -> + digits_loop(case Base of + 0 -> 0; + Gleam@denominator -> X div Gleam@denominator + end, Base, [case Base of + 0 -> 0; + Gleam@denominator@1 -> X rem Gleam@denominator@1 + end | Acc]) + end. + +-file("src/gleam/int.gleam", 449). +-spec digits(integer(), integer()) -> {ok, list(integer())} | {error, nil}. +digits(X, Base) -> + case Base < 2 of + true -> + {error, nil}; + + false -> + {ok, digits_loop(X, Base, [])} + end. + +-file("src/gleam/int.gleam", 471). +-spec undigits_loop(list(integer()), integer(), integer()) -> {ok, integer()} | + {error, nil}. +undigits_loop(Numbers, Base, Acc) -> + case Numbers of + [] -> + {ok, Acc}; + + [Digit | _] when Digit >= Base -> + {error, nil}; + + [Digit@1 | Rest] -> + undigits_loop(Rest, Base, (Acc * Base) + Digit@1) + end. + +-file("src/gleam/int.gleam", 464). +-spec undigits(list(integer()), integer()) -> {ok, integer()} | {error, nil}. +undigits(Numbers, Base) -> + case Base < 2 of + true -> + {error, nil}; + + false -> + undigits_loop(Numbers, Base, 0) + end. + +-file("src/gleam/int.gleam", 500). +?DOC( + " Generates a random int between zero and the given maximum.\n" + "\n" + " The lower number is inclusive, the upper number is exclusive.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " random(10)\n" + " // -> 4\n" + " ```\n" + "\n" + " ```gleam\n" + " random(1)\n" + " // -> 0\n" + " ```\n" + "\n" + " ```gleam\n" + " random(-1)\n" + " // -> -1\n" + " ```\n" +). +-spec random(integer()) -> integer(). +random(Max) -> + _pipe = (rand:uniform() * erlang:float(Max)), + _pipe@1 = math:floor(_pipe), + erlang:round(_pipe@1). + +-file("src/gleam/int.gleam", 533). +?DOC( + " Performs a truncated integer division.\n" + "\n" + " Returns division of the inputs as a `Result`: If the given divisor equals\n" + " `0`, this function returns an `Error`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " divide(0, 1)\n" + " // -> Ok(0)\n" + " ```\n" + "\n" + " ```gleam\n" + " divide(1, 0)\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " divide(5, 2)\n" + " // -> Ok(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " divide(-99, 2)\n" + " // -> Ok(-49)\n" + " ```\n" +). +-spec divide(integer(), integer()) -> {ok, integer()} | {error, nil}. +divide(Dividend, Divisor) -> + case Divisor of + 0 -> + {error, nil}; + + Divisor@1 -> + {ok, case Divisor@1 of + 0 -> 0; + Gleam@denominator -> Dividend div Gleam@denominator + end} + end. + +-file("src/gleam/int.gleam", 585). +?DOC( + " Computes the remainder of an integer division of inputs as a `Result`.\n" + "\n" + " Returns division of the inputs as a `Result`: If the given divisor equals\n" + " `0`, this function returns an `Error`.\n" + "\n" + " Most the time you will want to use the `%` operator instead of this\n" + " function.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " remainder(3, 2)\n" + " // -> Ok(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " remainder(1, 0)\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " remainder(10, -1)\n" + " // -> Ok(0)\n" + " ```\n" + "\n" + " ```gleam\n" + " remainder(13, by: 3)\n" + " // -> Ok(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " remainder(-13, by: 3)\n" + " // -> Ok(-1)\n" + " ```\n" + "\n" + " ```gleam\n" + " remainder(13, by: -3)\n" + " // -> Ok(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " remainder(-13, by: -3)\n" + " // -> Ok(-1)\n" + " ```\n" +). +-spec remainder(integer(), integer()) -> {ok, integer()} | {error, nil}. +remainder(Dividend, Divisor) -> + case Divisor of + 0 -> + {error, nil}; + + Divisor@1 -> + {ok, case Divisor@1 of + 0 -> 0; + Gleam@denominator -> Dividend rem Gleam@denominator + end} + end. + +-file("src/gleam/int.gleam", 627). +?DOC( + " Computes the modulo of an integer division of inputs as a `Result`.\n" + "\n" + " Returns division of the inputs as a `Result`: If the given divisor equals\n" + " `0`, this function returns an `Error`.\n" + "\n" + " Most the time you will want to use the `%` operator instead of this\n" + " function.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " modulo(3, 2)\n" + " // -> Ok(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " modulo(1, 0)\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " modulo(10, -1)\n" + " // -> Ok(0)\n" + " ```\n" + "\n" + " ```gleam\n" + " modulo(13, by: 3)\n" + " // -> Ok(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " modulo(-13, by: 3)\n" + " // -> Ok(2)\n" + " ```\n" +). +-spec modulo(integer(), integer()) -> {ok, integer()} | {error, nil}. +modulo(Dividend, Divisor) -> + case Divisor of + 0 -> + {error, nil}; + + _ -> + Remainder = case Divisor of + 0 -> 0; + Gleam@denominator -> Dividend rem Gleam@denominator + end, + case (Remainder * Divisor) < 0 of + true -> + {ok, Remainder + Divisor}; + + false -> + {ok, Remainder} + end + end. + +-file("src/gleam/int.gleam", 671). +?DOC( + " Performs a *floored* integer division, which means that the result will\n" + " always be rounded towards negative infinity.\n" + "\n" + " If you want to perform truncated integer division (rounding towards zero),\n" + " use `int.divide()` or the `/` operator instead.\n" + "\n" + " Returns division of the inputs as a `Result`: If the given divisor equals\n" + " `0`, this function returns an `Error`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " floor_divide(1, 0)\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " floor_divide(5, 2)\n" + " // -> Ok(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " floor_divide(6, -4)\n" + " // -> Ok(-2)\n" + " ```\n" + "\n" + " ```gleam\n" + " floor_divide(-99, 2)\n" + " // -> Ok(-50)\n" + " ```\n" +). +-spec floor_divide(integer(), integer()) -> {ok, integer()} | {error, nil}. +floor_divide(Dividend, Divisor) -> + case Divisor of + 0 -> + {error, nil}; + + Divisor@1 -> + case ((Dividend * Divisor@1) < 0) andalso ((case Divisor@1 of + 0 -> 0; + Gleam@denominator -> Dividend rem Gleam@denominator + end) /= 0) of + true -> + {ok, (case Divisor@1 of + 0 -> 0; + Gleam@denominator@1 -> Dividend div Gleam@denominator@1 + end) - 1}; + + false -> + {ok, case Divisor@1 of + 0 -> 0; + Gleam@denominator@2 -> Dividend div Gleam@denominator@2 + end} + end + end. + +-file("src/gleam/int.gleam", 705). +?DOC( + " Adds two integers together.\n" + "\n" + " It's the function equivalent of the `+` operator.\n" + " This function is useful in higher order functions or pipes.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " add(1, 2)\n" + " // -> 3\n" + " ```\n" + "\n" + " ```gleam\n" + " import gleam/list\n" + " list.fold([1, 2, 3], 0, add)\n" + " // -> 6\n" + " ```\n" + "\n" + " ```gleam\n" + " 3 |> add(2)\n" + " // -> 5\n" + " ```\n" +). +-spec add(integer(), integer()) -> integer(). +add(A, B) -> + A + B. + +-file("src/gleam/int.gleam", 733). +?DOC( + " Multiplies two integers together.\n" + "\n" + " It's the function equivalent of the `*` operator.\n" + " This function is useful in higher order functions or pipes.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " multiply(2, 4)\n" + " // -> 8\n" + " ```\n" + "\n" + " ```gleam\n" + " import gleam/list\n" + "\n" + " list.fold([2, 3, 4], 1, multiply)\n" + " // -> 24\n" + " ```\n" + "\n" + " ```gleam\n" + " 3 |> multiply(2)\n" + " // -> 6\n" + " ```\n" +). +-spec multiply(integer(), integer()) -> integer(). +multiply(A, B) -> + A * B. + +-file("src/gleam/int.gleam", 766). +?DOC( + " Subtracts one int from another.\n" + "\n" + " It's the function equivalent of the `-` operator.\n" + " This function is useful in higher order functions or pipes.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " subtract(3, 1)\n" + " // -> 2\n" + " ```\n" + "\n" + " ```gleam\n" + " import gleam/list\n" + "\n" + " list.fold([1, 2, 3], 10, subtract)\n" + " // -> 4\n" + " ```\n" + "\n" + " ```gleam\n" + " 3 |> subtract(2)\n" + " // -> 1\n" + " ```\n" + "\n" + " ```gleam\n" + " 3 |> subtract(2, _)\n" + " // -> -1\n" + " ```\n" +). +-spec subtract(integer(), integer()) -> integer(). +subtract(A, B) -> + A - B. + +-file("src/gleam/int.gleam", 778). +?DOC( + " Calculates the bitwise AND of its arguments.\n" + "\n" + " The exact behaviour of this function depends on the target platform.\n" + " On Erlang it is equivalent to bitwise operations on ints, on JavaScript it\n" + " is equivalent to bitwise operations on big-ints.\n" +). +-spec bitwise_and(integer(), integer()) -> integer(). +bitwise_and(X, Y) -> + erlang:'band'(X, Y). + +-file("src/gleam/int.gleam", 788). +?DOC( + " Calculates the bitwise NOT of its argument.\n" + "\n" + " The exact behaviour of this function depends on the target platform.\n" + " On Erlang it is equivalent to bitwise operations on ints, on JavaScript it\n" + " is equivalent to bitwise operations on big-ints.\n" +). +-spec bitwise_not(integer()) -> integer(). +bitwise_not(X) -> + erlang:'bnot'(X). + +-file("src/gleam/int.gleam", 798). +?DOC( + " Calculates the bitwise OR of its arguments.\n" + "\n" + " The exact behaviour of this function depends on the target platform.\n" + " On Erlang it is equivalent to bitwise operations on ints, on JavaScript it\n" + " is equivalent to bitwise operations on big-ints.\n" +). +-spec bitwise_or(integer(), integer()) -> integer(). +bitwise_or(X, Y) -> + erlang:'bor'(X, Y). + +-file("src/gleam/int.gleam", 808). +?DOC( + " Calculates the bitwise XOR of its arguments.\n" + "\n" + " The exact behaviour of this function depends on the target platform.\n" + " On Erlang it is equivalent to bitwise operations on ints, on JavaScript it\n" + " is equivalent to bitwise operations on big-ints.\n" +). +-spec bitwise_exclusive_or(integer(), integer()) -> integer(). +bitwise_exclusive_or(X, Y) -> + erlang:'bxor'(X, Y). + +-file("src/gleam/int.gleam", 818). +?DOC( + " Calculates the result of an arithmetic left bitshift.\n" + "\n" + " The exact behaviour of this function depends on the target platform.\n" + " On Erlang it is equivalent to bitwise operations on ints, on JavaScript it\n" + " is equivalent to bitwise operations on big-ints.\n" +). +-spec bitwise_shift_left(integer(), integer()) -> integer(). +bitwise_shift_left(X, Y) -> + erlang:'bsl'(X, Y). + +-file("src/gleam/int.gleam", 828). +?DOC( + " Calculates the result of an arithmetic right bitshift.\n" + "\n" + " The exact behaviour of this function depends on the target platform.\n" + " On Erlang it is equivalent to bitwise operations on ints, on JavaScript it\n" + " is equivalent to bitwise operations on big-ints.\n" +). +-spec bitwise_shift_right(integer(), integer()) -> integer(). +bitwise_shift_right(X, Y) -> + erlang:'bsr'(X, Y). diff --git a/build/dev/javascript/gleam_stdlib/gleam@io.erl b/build/dev/javascript/gleam_stdlib/gleam@io.erl new file mode 100644 index 0000000..e60295e --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam@io.erl @@ -0,0 +1,80 @@ +-module(gleam@io). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/io.gleam"). +-export([print/1, print_error/1, println/1, println_error/1]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +-file("src/gleam/io.gleam", 15). +?DOC( + " Writes a string to standard output (stdout).\n" + "\n" + " If you want your output to be printed on its own line see `println`.\n" + "\n" + " ## Example\n" + "\n" + " ```gleam\n" + " io.print(\"Hi mum\")\n" + " // -> Nil\n" + " // Hi mum\n" + " ```\n" +). +-spec print(binary()) -> nil. +print(String) -> + gleam_stdlib:print(String). + +-file("src/gleam/io.gleam", 31). +?DOC( + " Writes a string to standard error (stderr).\n" + "\n" + " If you want your output to be printed on its own line see `println_error`.\n" + "\n" + " ## Example\n" + "\n" + " ```\n" + " io.print_error(\"Hi pop\")\n" + " // -> Nil\n" + " // Hi pop\n" + " ```\n" +). +-spec print_error(binary()) -> nil. +print_error(String) -> + gleam_stdlib:print_error(String). + +-file("src/gleam/io.gleam", 45). +?DOC( + " Writes a string to standard output (stdout), appending a newline to the end.\n" + "\n" + " ## Example\n" + "\n" + " ```gleam\n" + " io.println(\"Hi mum\")\n" + " // -> Nil\n" + " // Hi mum\n" + " ```\n" +). +-spec println(binary()) -> nil. +println(String) -> + gleam_stdlib:println(String). + +-file("src/gleam/io.gleam", 59). +?DOC( + " Writes a string to standard error (stderr), appending a newline to the end.\n" + "\n" + " ## Example\n" + "\n" + " ```gleam\n" + " io.println_error(\"Hi pop\")\n" + " // -> Nil\n" + " // Hi pop\n" + " ```\n" +). +-spec println_error(binary()) -> nil. +println_error(String) -> + gleam_stdlib:println_error(String). diff --git a/build/dev/javascript/gleam_stdlib/gleam@list.erl b/build/dev/javascript/gleam_stdlib/gleam@list.erl new file mode 100644 index 0000000..0c96ecb --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam@list.erl @@ -0,0 +1,2873 @@ +-module(gleam@list). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/list.gleam"). +-export([length/1, count/2, reverse/1, is_empty/1, contains/2, first/1, rest/1, group/2, filter/2, filter_map/2, map/2, map2/3, map_fold/3, index_map/2, try_map/2, drop/2, take/2, new/0, wrap/1, append/2, prepend/2, flatten/1, flat_map/2, fold/3, fold_right/3, index_fold/3, try_fold/3, fold_until/3, find/2, find_map/2, all/2, any/2, zip/2, strict_zip/2, unzip/1, intersperse/2, unique/1, sort/2, range/2, repeat/2, split/2, split_while/2, key_find/2, key_filter/2, key_pop/2, key_set/3, each/2, try_each/2, partition/2, window/2, window_by_2/1, drop_while/2, take_while/2, chunk/2, sized_chunk/2, reduce/2, scan/3, last/1, combinations/2, combination_pairs/1, transpose/1, interleave/1, shuffle/1, max/2, sample/2, permutations/1]). +-export_type([continue_or_stop/1, sorting/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC( + " Lists are an ordered sequence of elements and are one of the most common\n" + " data types in Gleam.\n" + "\n" + " New elements can be added and removed from the front of a list in\n" + " constant time, while adding and removing from the end requires traversing\n" + " and copying the whole list, so keep this in mind when designing your\n" + " programs.\n" + "\n" + " There is a dedicated syntax for prefixing to a list:\n" + "\n" + " ```gleam\n" + " let new_list = [1, 2, ..existing_list]\n" + " ```\n" + "\n" + " And a matching syntax for getting the first elements of a list:\n" + "\n" + " ```gleam\n" + " case list {\n" + " [first_element, ..rest] -> first_element\n" + " _ -> \"this pattern matches when the list is empty\"\n" + " }\n" + " ```\n" + "\n" +). + +-type continue_or_stop(XG) :: {continue, XG} | {stop, XG}. + +-type sorting() :: ascending | descending. + +-file("src/gleam/list.gleam", 60). +-spec length_loop(list(any()), integer()) -> integer(). +length_loop(List, Count) -> + case List of + [_ | List@1] -> + length_loop(List@1, Count + 1); + + [] -> + Count + end. + +-file("src/gleam/list.gleam", 56). +?DOC( + " Counts the number of elements in a given list.\n" + "\n" + " This function has to traverse the list to determine the number of elements,\n" + " so it runs in linear time.\n" + "\n" + " This function is natively implemented by the virtual machine and is highly\n" + " optimised.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " length([])\n" + " // -> 0\n" + " ```\n" + "\n" + " ```gleam\n" + " length([1])\n" + " // -> 1\n" + " ```\n" + "\n" + " ```gleam\n" + " length([1, 2])\n" + " // -> 2\n" + " ```\n" +). +-spec length(list(any())) -> integer(). +length(List) -> + erlang:length(List). + +-file("src/gleam/list.gleam", 93). +-spec count_loop(list(XN), fun((XN) -> boolean()), integer()) -> integer(). +count_loop(List, Predicate, Acc) -> + case List of + [] -> + Acc; + + [First | Rest] -> + case Predicate(First) of + true -> + count_loop(Rest, Predicate, Acc + 1); + + false -> + count_loop(Rest, Predicate, Acc) + end + end. + +-file("src/gleam/list.gleam", 89). +?DOC( + " Counts the number of elements in a given list satisfying a given predicate.\n" + "\n" + " This function has to traverse the list to determine the number of elements,\n" + " so it runs in linear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " count([], fn(a) { a > 0 })\n" + " // -> 0\n" + " ```\n" + "\n" + " ```gleam\n" + " count([1], fn(a) { a > 0 })\n" + " // -> 1\n" + " ```\n" + "\n" + " ```gleam\n" + " count([1, 2, 3], int.is_odd)\n" + " // -> 2\n" + " ```\n" +). +-spec count(list(XL), fun((XL) -> boolean())) -> integer(). +count(List, Predicate) -> + count_loop(List, Predicate, 0). + +-file("src/gleam/list.gleam", 131). +?DOC( + " Creates a new list from a given list containing the same elements but in the\n" + " opposite order.\n" + "\n" + " This function has to traverse the list to create the new reversed list, so\n" + " it runs in linear time.\n" + "\n" + " This function is natively implemented by the virtual machine and is highly\n" + " optimised.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " reverse([])\n" + " // -> []\n" + " ```\n" + "\n" + " ```gleam\n" + " reverse([1])\n" + " // -> [1]\n" + " ```\n" + "\n" + " ```gleam\n" + " reverse([1, 2])\n" + " // -> [2, 1]\n" + " ```\n" +). +-spec reverse(list(XP)) -> list(XP). +reverse(List) -> + lists:reverse(List). + +-file("src/gleam/list.gleam", 168). +?DOC( + " Determines whether or not the list is empty.\n" + "\n" + " This function runs in constant time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " is_empty([])\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " is_empty([1])\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " is_empty([1, 1])\n" + " // -> False\n" + " ```\n" +). +-spec is_empty(list(any())) -> boolean(). +is_empty(List) -> + List =:= []. + +-file("src/gleam/list.gleam", 204). +?DOC( + " Determines whether or not a given element exists within a given list.\n" + "\n" + " This function traverses the list to find the element, so it runs in linear\n" + " time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " [] |> contains(any: 0)\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " [0] |> contains(any: 0)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " [1] |> contains(any: 0)\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " [1, 1] |> contains(any: 0)\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " [1, 0] |> contains(any: 0)\n" + " // -> True\n" + " ```\n" +). +-spec contains(list(XY), XY) -> boolean(). +contains(List, Elem) -> + case List of + [] -> + false; + + [First | _] when First =:= Elem -> + true; + + [_ | Rest] -> + contains(Rest, Elem) + end. + +-file("src/gleam/list.gleam", 231). +?DOC( + " Gets the first element from the start of the list, if there is one.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " first([])\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " first([0])\n" + " // -> Ok(0)\n" + " ```\n" + "\n" + " ```gleam\n" + " first([1, 2])\n" + " // -> Ok(1)\n" + " ```\n" +). +-spec first(list(YA)) -> {ok, YA} | {error, nil}. +first(List) -> + case List of + [] -> + {error, nil}; + + [First | _] -> + {ok, First} + end. + +-file("src/gleam/list.gleam", 260). +?DOC( + " Returns the list minus the first element. If the list is empty, `Error(Nil)` is\n" + " returned.\n" + "\n" + " This function runs in constant time and does not make a copy of the list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " rest([])\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " rest([0])\n" + " // -> Ok([])\n" + " ```\n" + "\n" + " ```gleam\n" + " rest([1, 2])\n" + " // -> Ok([2])\n" + " ```\n" +). +-spec rest(list(YE)) -> {ok, list(YE)} | {error, nil}. +rest(List) -> + case List of + [] -> + {error, nil}; + + [_ | Rest] -> + {ok, Rest} + end. + +-file("src/gleam/list.gleam", 302). +-spec group_loop(list(YP), fun((YP) -> YR), gleam@dict:dict(YR, list(YP))) -> gleam@dict:dict(YR, list(YP)). +group_loop(List, To_key, Groups) -> + case List of + [] -> + Groups; + + [First | Rest] -> + Key = To_key(First), + Groups@1 = case gleam_stdlib:map_get(Groups, Key) of + {error, _} -> + gleam@dict:insert(Groups, Key, [First]); + + {ok, Existing} -> + gleam@dict:insert(Groups, Key, [First | Existing]) + end, + group_loop(Rest, To_key, Groups@1) + end. + +-file("src/gleam/list.gleam", 298). +?DOC( + " Groups the elements from the given list by the given key function.\n" + "\n" + " Does not preserve the initial value order.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " import gleam/dict\n" + "\n" + " [Ok(3), Error(\"Wrong\"), Ok(200), Ok(73)]\n" + " |> group(by: fn(i) {\n" + " case i {\n" + " Ok(_) -> \"Successful\"\n" + " Error(_) -> \"Failed\"\n" + " }\n" + " })\n" + " |> dict.to_list\n" + " // -> [\n" + " // #(\"Failed\", [Error(\"Wrong\")]),\n" + " // #(\"Successful\", [Ok(73), Ok(200), Ok(3)])\n" + " // ]\n" + " ```\n" + "\n" + " ```gleam\n" + " import gleam/dict\n" + "\n" + " group([1,2,3,4,5], by: fn(i) { i - i / 3 * 3 })\n" + " |> dict.to_list\n" + " // -> [#(0, [3]), #(1, [4, 1]), #(2, [5, 2])]\n" + " ```\n" +). +-spec group(list(YJ), fun((YJ) -> YL)) -> gleam@dict:dict(YL, list(YJ)). +group(List, Key) -> + group_loop(List, Key, maps:new()). + +-file("src/gleam/list.gleam", 339). +-spec filter_loop(list(AAB), fun((AAB) -> boolean()), list(AAB)) -> list(AAB). +filter_loop(List, Fun, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [First | Rest] -> + New_acc = case Fun(First) of + true -> + [First | Acc]; + + false -> + Acc + end, + filter_loop(Rest, Fun, New_acc) + end. + +-file("src/gleam/list.gleam", 335). +?DOC( + " Returns a new list containing only the elements from the first list for\n" + " which the given functions returns `True`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " filter([2, 4, 6, 1], fn(x) { x > 2 })\n" + " // -> [4, 6]\n" + " ```\n" + "\n" + " ```gleam\n" + " filter([2, 4, 6, 1], fn(x) { x > 6 })\n" + " // -> []\n" + " ```\n" +). +-spec filter(list(YY), fun((YY) -> boolean())) -> list(YY). +filter(List, Predicate) -> + filter_loop(List, Predicate, []). + +-file("src/gleam/list.gleam", 371). +-spec filter_map_loop( + list(AAM), + fun((AAM) -> {ok, AAO} | {error, any()}), + list(AAO) +) -> list(AAO). +filter_map_loop(List, Fun, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [First | Rest] -> + New_acc = case Fun(First) of + {ok, First@1} -> + [First@1 | Acc]; + + {error, _} -> + Acc + end, + filter_map_loop(Rest, Fun, New_acc) + end. + +-file("src/gleam/list.gleam", 367). +?DOC( + " Returns a new list containing only the elements from the first list for\n" + " which the given functions returns `Ok(_)`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " filter_map([2, 4, 6, 1], Error)\n" + " // -> []\n" + " ```\n" + "\n" + " ```gleam\n" + " filter_map([2, 4, 6, 1], fn(x) { Ok(x + 1) })\n" + " // -> [3, 5, 7, 2]\n" + " ```\n" +). +-spec filter_map(list(AAF), fun((AAF) -> {ok, AAH} | {error, any()})) -> list(AAH). +filter_map(List, Fun) -> + filter_map_loop(List, Fun, []). + +-file("src/gleam/list.gleam", 402). +-spec map_loop(list(AAY), fun((AAY) -> ABA), list(ABA)) -> list(ABA). +map_loop(List, Fun, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [First | Rest] -> + map_loop(Rest, Fun, [Fun(First) | Acc]) + end. + +-file("src/gleam/list.gleam", 398). +?DOC( + " Returns a new list containing only the elements of the first list after the\n" + " function has been applied to each one.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " map([2, 4, 6], fn(x) { x * 2 })\n" + " // -> [4, 8, 12]\n" + " ```\n" +). +-spec map(list(AAU), fun((AAU) -> AAW)) -> list(AAW). +map(List, Fun) -> + map_loop(List, Fun, []). + +-file("src/gleam/list.gleam", 429). +-spec map2_loop(list(ABJ), list(ABL), fun((ABJ, ABL) -> ABN), list(ABN)) -> list(ABN). +map2_loop(List1, List2, Fun, Acc) -> + case {List1, List2} of + {[], _} -> + lists:reverse(Acc); + + {_, []} -> + lists:reverse(Acc); + + {[A | As_], [B | Bs]} -> + map2_loop(As_, Bs, Fun, [Fun(A, B) | Acc]) + end. + +-file("src/gleam/list.gleam", 425). +?DOC( + " Combines two lists into a single list using the given function.\n" + "\n" + " If a list is longer than the other the extra elements are dropped.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " map2([1, 2, 3], [4, 5, 6], fn(x, y) { x + y })\n" + " // -> [5, 7, 9]\n" + " ```\n" + "\n" + " ```gleam\n" + " map2([1, 2], [\"a\", \"b\", \"c\"], fn(i, x) { #(i, x) })\n" + " // -> [#(1, \"a\"), #(2, \"b\")]\n" + " ```\n" +). +-spec map2(list(ABD), list(ABF), fun((ABD, ABF) -> ABH)) -> list(ABH). +map2(List1, List2, Fun) -> + map2_loop(List1, List2, Fun, []). + +-file("src/gleam/list.gleam", 462). +-spec map_fold_loop(list(ABV), fun((ABX, ABV) -> {ABX, ABY}), ABX, list(ABY)) -> {ABX, + list(ABY)}. +map_fold_loop(List, Fun, Acc, List_acc) -> + case List of + [] -> + {Acc, lists:reverse(List_acc)}; + + [First | Rest] -> + {Acc@1, First@1} = Fun(Acc, First), + map_fold_loop(Rest, Fun, Acc@1, [First@1 | List_acc]) + end. + +-file("src/gleam/list.gleam", 454). +?DOC( + " Similar to `map` but also lets you pass around an accumulated value.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " map_fold(\n" + " over: [1, 2, 3],\n" + " from: 100,\n" + " with: fn(memo, i) { #(memo + i, i * 2) }\n" + " )\n" + " // -> #(106, [2, 4, 6])\n" + " ```\n" +). +-spec map_fold(list(ABQ), ABS, fun((ABS, ABQ) -> {ABS, ABT})) -> {ABS, + list(ABT)}. +map_fold(List, Initial, Fun) -> + map_fold_loop(List, Fun, Initial, []). + +-file("src/gleam/list.gleam", 494). +-spec index_map_loop( + list(ACF), + fun((ACF, integer()) -> ACH), + integer(), + list(ACH) +) -> list(ACH). +index_map_loop(List, Fun, Index, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [First | Rest] -> + Acc@1 = [Fun(First, Index) | Acc], + index_map_loop(Rest, Fun, Index + 1, Acc@1) + end. + +-file("src/gleam/list.gleam", 490). +?DOC( + " Returns a new list containing only the elements of the first list after the\n" + " function has been applied to each one and their index.\n" + "\n" + " The index starts at 0, so the first element is 0, the second is 1, and so\n" + " on.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " index_map([\"a\", \"b\"], fn(x, i) { #(i, x) })\n" + " // -> [#(0, \"a\"), #(1, \"b\")]\n" + " ```\n" +). +-spec index_map(list(ACB), fun((ACB, integer()) -> ACD)) -> list(ACD). +index_map(List, Fun) -> + index_map_loop(List, Fun, 0, []). + +-file("src/gleam/list.gleam", 548). +-spec try_map_loop(list(ACT), fun((ACT) -> {ok, ACV} | {error, ACW}), list(ACV)) -> {ok, + list(ACV)} | + {error, ACW}. +try_map_loop(List, Fun, Acc) -> + case List of + [] -> + {ok, lists:reverse(Acc)}; + + [First | Rest] -> + case Fun(First) of + {ok, First@1} -> + try_map_loop(Rest, Fun, [First@1 | Acc]); + + {error, Error} -> + {error, Error} + end + end. + +-file("src/gleam/list.gleam", 541). +?DOC( + " Takes a function that returns a `Result` and applies it to each element in a\n" + " given list in turn.\n" + "\n" + " If the function returns `Ok(new_value)` for all elements in the list then a\n" + " list of the new values is returned.\n" + "\n" + " If the function returns `Error(reason)` for any of the elements then it is\n" + " returned immediately. None of the elements in the list are processed after\n" + " one returns an `Error`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " try_map([1, 2, 3], fn(x) { Ok(x + 2) })\n" + " // -> Ok([3, 4, 5])\n" + " ```\n" + "\n" + " ```gleam\n" + " try_map([1, 2, 3], fn(_) { Error(0) })\n" + " // -> Error(0)\n" + " ```\n" + "\n" + " ```gleam\n" + " try_map([[1], [2, 3]], first)\n" + " // -> Ok([1, 2])\n" + " ```\n" + "\n" + " ```gleam\n" + " try_map([[1], [], [2]], first)\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec try_map(list(ACK), fun((ACK) -> {ok, ACM} | {error, ACN})) -> {ok, + list(ACM)} | + {error, ACN}. +try_map(List, Fun) -> + try_map_loop(List, Fun, []). + +-file("src/gleam/list.gleam", 583). +?DOC( + " Returns a list that is the given list with up to the given number of\n" + " elements removed from the front of the list.\n" + "\n" + " If the element has less than the number of elements an empty list is\n" + " returned.\n" + "\n" + " This function runs in linear time but does not copy the list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " drop([1, 2, 3, 4], 2)\n" + " // -> [3, 4]\n" + " ```\n" + "\n" + " ```gleam\n" + " drop([1, 2, 3, 4], 9)\n" + " // -> []\n" + " ```\n" +). +-spec drop(list(ADD), integer()) -> list(ADD). +drop(List, N) -> + case N =< 0 of + true -> + List; + + false -> + case List of + [] -> + []; + + [_ | Rest] -> + drop(Rest, N - 1) + end + end. + +-file("src/gleam/list.gleam", 618). +-spec take_loop(list(ADJ), integer(), list(ADJ)) -> list(ADJ). +take_loop(List, N, Acc) -> + case N =< 0 of + true -> + lists:reverse(Acc); + + false -> + case List of + [] -> + lists:reverse(Acc); + + [First | Rest] -> + take_loop(Rest, N - 1, [First | Acc]) + end + end. + +-file("src/gleam/list.gleam", 614). +?DOC( + " Returns a list containing the first given number of elements from the given\n" + " list.\n" + "\n" + " If the element has less than the number of elements then the full list is\n" + " returned.\n" + "\n" + " This function runs in linear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " take([1, 2, 3, 4], 2)\n" + " // -> [1, 2]\n" + " ```\n" + "\n" + " ```gleam\n" + " take([1, 2, 3, 4], 9)\n" + " // -> [1, 2, 3, 4]\n" + " ```\n" +). +-spec take(list(ADG), integer()) -> list(ADG). +take(List, N) -> + take_loop(List, N, []). + +-file("src/gleam/list.gleam", 638). +?DOC( + " Returns a new empty list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " new()\n" + " // -> []\n" + " ```\n" +). +-spec new() -> list(any()). +new() -> + []. + +-file("src/gleam/list.gleam", 658). +?DOC( + " Returns the given item wrapped in a list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " wrap(1)\n" + " // -> [1]\n" + "\n" + " wrap([\"a\", \"b\", \"c\"])\n" + " // -> [[\"a\", \"b\", \"c\"]]\n" + "\n" + " wrap([[]])\n" + " // -> [[[]]]\n" + " ```\n" +). +-spec wrap(ADP) -> list(ADP). +wrap(Item) -> + [Item]. + +-file("src/gleam/list.gleam", 679). +-spec append_loop(list(ADV), list(ADV)) -> list(ADV). +append_loop(First, Second) -> + case First of + [] -> + Second; + + [First@1 | Rest] -> + append_loop(Rest, [First@1 | Second]) + end. + +-file("src/gleam/list.gleam", 675). +?DOC( + " Joins one list onto the end of another.\n" + "\n" + " This function runs in linear time, and it traverses and copies the first\n" + " list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " append([1, 2], [3])\n" + " // -> [1, 2, 3]\n" + " ```\n" +). +-spec append(list(ADR), list(ADR)) -> list(ADR). +append(First, Second) -> + lists:append(First, Second). + +-file("src/gleam/list.gleam", 699). +?DOC( + " Prefixes an item to a list. This can also be done using the dedicated\n" + " syntax instead\n" + "\n" + " ```gleam\n" + " let existing_list = [2, 3, 4]\n" + "\n" + " [1, ..existing_list]\n" + " // -> [1, 2, 3, 4]\n" + "\n" + " prepend(to: existing_list, this: 1)\n" + " // -> [1, 2, 3, 4]\n" + " ```\n" +). +-spec prepend(list(ADZ), ADZ) -> list(ADZ). +prepend(List, Item) -> + [Item | List]. + +-file("src/gleam/list.gleam", 720). +-spec flatten_loop(list(list(AEG)), list(AEG)) -> list(AEG). +flatten_loop(Lists, Acc) -> + case Lists of + [] -> + lists:reverse(Acc); + + [List | Further_lists] -> + flatten_loop(Further_lists, lists:reverse(List, Acc)) + end. + +-file("src/gleam/list.gleam", 716). +?DOC( + " Joins a list of lists into a single list.\n" + "\n" + " This function traverses all elements twice on the JavaScript target.\n" + " This function traverses all elements once on the Erlang target.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " flatten([[1], [2, 3], []])\n" + " // -> [1, 2, 3]\n" + " ```\n" +). +-spec flatten(list(list(AEC))) -> list(AEC). +flatten(Lists) -> + lists:append(Lists). + +-file("src/gleam/list.gleam", 737). +?DOC( + " Maps the list with the given function into a list of lists, and then flattens it.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " flat_map([2, 4, 6], fn(x) { [x, x + 1] })\n" + " // -> [2, 3, 4, 5, 6, 7]\n" + " ```\n" +). +-spec flat_map(list(AEL), fun((AEL) -> list(AEN))) -> list(AEN). +flat_map(List, Fun) -> + lists:append(map(List, Fun)). + +-file("src/gleam/list.gleam", 749). +?DOC( + " Reduces a list of elements into a single value by calling a given function\n" + " on each element, going from left to right.\n" + "\n" + " `fold([1, 2, 3], 0, add)` is the equivalent of\n" + " `add(add(add(0, 1), 2), 3)`.\n" + "\n" + " This function runs in linear time.\n" +). +-spec fold(list(AEQ), AES, fun((AES, AEQ) -> AES)) -> AES. +fold(List, Initial, Fun) -> + case List of + [] -> + Initial; + + [First | Rest] -> + fold(Rest, Fun(Initial, First), Fun) + end. + +-file("src/gleam/list.gleam", 771). +?DOC( + " Reduces a list of elements into a single value by calling a given function\n" + " on each element, going from right to left.\n" + "\n" + " `fold_right([1, 2, 3], 0, add)` is the equivalent of\n" + " `add(add(add(0, 3), 2), 1)`.\n" + "\n" + " This function runs in linear time.\n" + "\n" + " Unlike `fold` this function is not tail recursive. Where possible use\n" + " `fold` instead as it will use less memory.\n" +). +-spec fold_right(list(AET), AEV, fun((AEV, AET) -> AEV)) -> AEV. +fold_right(List, Initial, Fun) -> + case List of + [] -> + Initial; + + [First | Rest] -> + Fun(fold_right(Rest, Initial, Fun), First) + end. + +-file("src/gleam/list.gleam", 808). +-spec index_fold_loop( + list(AEZ), + AFB, + fun((AFB, AEZ, integer()) -> AFB), + integer() +) -> AFB. +index_fold_loop(Over, Acc, With, Index) -> + case Over of + [] -> + Acc; + + [First | Rest] -> + index_fold_loop(Rest, With(Acc, First, Index), With, Index + 1) + end. + +-file("src/gleam/list.gleam", 800). +?DOC( + " Like fold but the folding function also receives the index of the current element.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " [\"a\", \"b\", \"c\"]\n" + " |> index_fold(\"\", fn(acc, item, index) {\n" + " acc <> int.to_string(index) <> \":\" <> item <> \" \"\n" + " })\n" + " // -> \"0:a 1:b 2:c\"\n" + " ```\n" + "\n" + " ```gleam\n" + " [10, 20, 30]\n" + " |> index_fold(0, fn(acc, item, index) { acc + item * index })\n" + " // -> 80\n" + " ```\n" +). +-spec index_fold(list(AEW), AEY, fun((AEY, AEW, integer()) -> AEY)) -> AEY. +index_fold(List, Initial, Fun) -> + index_fold_loop(List, Initial, Fun, 0). + +-file("src/gleam/list.gleam", 840). +?DOC( + " A variant of fold that might fail.\n" + "\n" + " The folding function should return `Result(accumulator, error)`.\n" + " If the returned value is `Ok(accumulator)` try_fold will try the next value in the list.\n" + " If the returned value is `Error(error)` try_fold will stop and return that error.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " [1, 2, 3, 4]\n" + " |> try_fold(0, fn(acc, i) {\n" + " case i < 3 {\n" + " True -> Ok(acc + i)\n" + " False -> Error(Nil)\n" + " }\n" + " })\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec try_fold(list(AFC), AFE, fun((AFE, AFC) -> {ok, AFE} | {error, AFF})) -> {ok, + AFE} | + {error, AFF}. +try_fold(List, Initial, Fun) -> + case List of + [] -> + {ok, Initial}; + + [First | Rest] -> + case Fun(Initial, First) of + {ok, Result} -> + try_fold(Rest, Result, Fun); + + {error, _} = Error -> + Error + end + end. + +-file("src/gleam/list.gleam", 879). +?DOC( + " A variant of fold that allows to stop folding earlier.\n" + "\n" + " The folding function should return `ContinueOrStop(accumulator)`.\n" + " If the returned value is `Continue(accumulator)` fold_until will try the next value in the list.\n" + " If the returned value is `Stop(accumulator)` fold_until will stop and return that accumulator.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " [1, 2, 3, 4]\n" + " |> fold_until(0, fn(acc, i) {\n" + " case i < 3 {\n" + " True -> Continue(acc + i)\n" + " False -> Stop(acc)\n" + " }\n" + " })\n" + " // -> 3\n" + " ```\n" +). +-spec fold_until(list(AFK), AFM, fun((AFM, AFK) -> continue_or_stop(AFM))) -> AFM. +fold_until(List, Initial, Fun) -> + case List of + [] -> + Initial; + + [First | Rest] -> + case Fun(Initial, First) of + {continue, Next_accumulator} -> + fold_until(Rest, Next_accumulator, Fun); + + {stop, B} -> + B + end + end. + +-file("src/gleam/list.gleam", 916). +?DOC( + " Finds the first element in a given list for which the given function returns\n" + " `True`.\n" + "\n" + " Returns `Error(Nil)` if no such element is found.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " find([1, 2, 3], fn(x) { x > 2 })\n" + " // -> Ok(3)\n" + " ```\n" + "\n" + " ```gleam\n" + " find([1, 2, 3], fn(x) { x > 4 })\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " find([], fn(_) { True })\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec find(list(AFO), fun((AFO) -> boolean())) -> {ok, AFO} | {error, nil}. +find(List, Is_desired) -> + case List of + [] -> + {error, nil}; + + [First | Rest] -> + case Is_desired(First) of + true -> + {ok, First}; + + false -> + find(Rest, Is_desired) + end + end. + +-file("src/gleam/list.gleam", 952). +?DOC( + " Finds the first element in a given list for which the given function returns\n" + " `Ok(new_value)`, then returns the wrapped `new_value`.\n" + "\n" + " Returns `Error(Nil)` if no such element is found.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " find_map([[], [2], [3]], first)\n" + " // -> Ok(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " find_map([[], []], first)\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " find_map([], first)\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec find_map(list(AFS), fun((AFS) -> {ok, AFU} | {error, any()})) -> {ok, AFU} | + {error, nil}. +find_map(List, Fun) -> + case List of + [] -> + {error, nil}; + + [First | Rest] -> + case Fun(First) of + {ok, First@1} -> + {ok, First@1}; + + {error, _} -> + find_map(Rest, Fun) + end + end. + +-file("src/gleam/list.gleam", 987). +?DOC( + " Returns `True` if the given function returns `True` for all the elements in\n" + " the given list. If the function returns `False` for any of the elements it\n" + " immediately returns `False` without checking the rest of the list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " all([], fn(x) { x > 3 })\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " all([4, 5], fn(x) { x > 3 })\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " all([4, 3], fn(x) { x > 3 })\n" + " // -> False\n" + " ```\n" +). +-spec all(list(AGA), fun((AGA) -> boolean())) -> boolean(). +all(List, Predicate) -> + case List of + [] -> + true; + + [First | Rest] -> + case Predicate(First) of + true -> + all(Rest, Predicate); + + false -> + false + end + end. + +-file("src/gleam/list.gleam", 1024). +?DOC( + " Returns `True` if the given function returns `True` for any the elements in\n" + " the given list. If the function returns `True` for any of the elements it\n" + " immediately returns `True` without checking the rest of the list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " any([], fn(x) { x > 3 })\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " any([4, 5], fn(x) { x > 3 })\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " any([4, 3], fn(x) { x > 4 })\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " any([3, 4], fn(x) { x > 3 })\n" + " // -> True\n" + " ```\n" +). +-spec any(list(AGC), fun((AGC) -> boolean())) -> boolean(). +any(List, Predicate) -> + case List of + [] -> + false; + + [First | Rest] -> + case Predicate(First) of + true -> + true; + + false -> + any(Rest, Predicate) + end + end. + +-file("src/gleam/list.gleam", 1066). +-spec zip_loop(list(AGJ), list(AGL), list({AGJ, AGL})) -> list({AGJ, AGL}). +zip_loop(One, Other, Acc) -> + case {One, Other} of + {[First_one | Rest_one], [First_other | Rest_other]} -> + zip_loop(Rest_one, Rest_other, [{First_one, First_other} | Acc]); + + {_, _} -> + lists:reverse(Acc) + end. + +-file("src/gleam/list.gleam", 1062). +?DOC( + " Takes two lists and returns a single list of 2-element tuples.\n" + "\n" + " If one of the lists is longer than the other, the remaining elements from\n" + " the longer list are not used.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " zip([], [])\n" + " // -> []\n" + " ```\n" + "\n" + " ```gleam\n" + " zip([1, 2], [3])\n" + " // -> [#(1, 3)]\n" + " ```\n" + "\n" + " ```gleam\n" + " zip([1], [3, 4])\n" + " // -> [#(1, 3)]\n" + " ```\n" + "\n" + " ```gleam\n" + " zip([1, 2], [3, 4])\n" + " // -> [#(1, 3), #(2, 4)]\n" + " ```\n" +). +-spec zip(list(AGE), list(AGG)) -> list({AGE, AGG}). +zip(List, Other) -> + zip_loop(List, Other, []). + +-file("src/gleam/list.gleam", 1107). +-spec strict_zip_loop(list(AGW), list(AGY), list({AGW, AGY})) -> {ok, + list({AGW, AGY})} | + {error, nil}. +strict_zip_loop(One, Other, Acc) -> + case {One, Other} of + {[], []} -> + {ok, lists:reverse(Acc)}; + + {[], _} -> + {error, nil}; + + {_, []} -> + {error, nil}; + + {[First_one | Rest_one], [First_other | Rest_other]} -> + strict_zip_loop( + Rest_one, + Rest_other, + [{First_one, First_other} | Acc] + ) + end. + +-file("src/gleam/list.gleam", 1100). +?DOC( + " Takes two lists and returns a single list of 2-element tuples.\n" + "\n" + " If one of the lists is longer than the other, an `Error` is returned.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " strict_zip([], [])\n" + " // -> Ok([])\n" + " ```\n" + "\n" + " ```gleam\n" + " strict_zip([1, 2], [3])\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " strict_zip([1], [3, 4])\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " strict_zip([1, 2], [3, 4])\n" + " // -> Ok([#(1, 3), #(2, 4)])\n" + " ```\n" +). +-spec strict_zip(list(AGP), list(AGR)) -> {ok, list({AGP, AGR})} | {error, nil}. +strict_zip(List, Other) -> + strict_zip_loop(List, Other, []). + +-file("src/gleam/list.gleam", 1138). +-spec unzip_loop(list({AHJ, AHK}), list(AHJ), list(AHK)) -> {list(AHJ), + list(AHK)}. +unzip_loop(Input, One, Other) -> + case Input of + [] -> + {lists:reverse(One), lists:reverse(Other)}; + + [{First_one, First_other} | Rest] -> + unzip_loop(Rest, [First_one | One], [First_other | Other]) + end. + +-file("src/gleam/list.gleam", 1134). +?DOC( + " Takes a single list of 2-element tuples and returns two lists.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " unzip([#(1, 2), #(3, 4)])\n" + " // -> #([1, 3], [2, 4])\n" + " ```\n" + "\n" + " ```gleam\n" + " unzip([])\n" + " // -> #([], [])\n" + " ```\n" +). +-spec unzip(list({AHE, AHF})) -> {list(AHE), list(AHF)}. +unzip(Input) -> + unzip_loop(Input, [], []). + +-file("src/gleam/list.gleam", 1173). +-spec intersperse_loop(list(AHT), AHT, list(AHT)) -> list(AHT). +intersperse_loop(List, Separator, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [First | Rest] -> + intersperse_loop(Rest, Separator, [First, Separator | Acc]) + end. + +-file("src/gleam/list.gleam", 1166). +?DOC( + " Inserts a given value between each existing element in a given list.\n" + "\n" + " This function runs in linear time and copies the list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " intersperse([1, 1, 1], 2)\n" + " // -> [1, 2, 1, 2, 1]\n" + " ```\n" + "\n" + " ```gleam\n" + " intersperse([], 2)\n" + " // -> []\n" + " ```\n" +). +-spec intersperse(list(AHQ), AHQ) -> list(AHQ). +intersperse(List, Elem) -> + case List of + [] -> + List; + + [_] -> + List; + + [First | Rest] -> + intersperse_loop(Rest, Elem, [First]) + end. + +-file("src/gleam/list.gleam", 1196). +-spec unique_loop(list(AIA), gleam@dict:dict(AIA, nil), list(AIA)) -> list(AIA). +unique_loop(List, Seen, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [First | Rest] -> + case gleam@dict:has_key(Seen, First) of + true -> + unique_loop(Rest, Seen, Acc); + + false -> + unique_loop( + Rest, + gleam@dict:insert(Seen, First, nil), + [First | Acc] + ) + end + end. + +-file("src/gleam/list.gleam", 1192). +?DOC( + " Removes any duplicate elements from a given list.\n" + "\n" + " This function returns in loglinear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " unique([1, 1, 1, 4, 7, 3, 3, 4])\n" + " // -> [1, 4, 7, 3]\n" + " ```\n" +). +-spec unique(list(AHX)) -> list(AHX). +unique(List) -> + unique_loop(List, maps:new(), []). + +-file("src/gleam/list.gleam", 1282). +?DOC( + " Given a list it returns slices of it that are locally sorted in ascending\n" + " order.\n" + "\n" + " Imagine you have this list:\n" + "\n" + " ```\n" + " [1, 2, 3, 2, 1, 0]\n" + " ^^^^^^^ ^^^^^^^ This is a slice in descending order\n" + " |\n" + " | This is a slice that is sorted in ascending order\n" + " ```\n" + "\n" + " So the produced result will contain these two slices, each one sorted in\n" + " ascending order: `[[1, 2, 3], [0, 1, 2]]`.\n" + "\n" + " - `growing` is an accumulator with the current slice being grown\n" + " - `direction` is the growing direction of the slice being grown, it could\n" + " either be ascending or strictly descending\n" + " - `prev` is the previous element that needs to be added to the growing slice\n" + " it is carried around to check whether we have to keep growing the current\n" + " slice or not\n" + " - `acc` is the accumulator containing the slices sorted in ascending order\n" +). +-spec sequences( + list(AIJ), + fun((AIJ, AIJ) -> gleam@order:order()), + list(AIJ), + sorting(), + AIJ, + list(list(AIJ)) +) -> list(list(AIJ)). +sequences(List, Compare, Growing, Direction, Prev, Acc) -> + Growing@1 = [Prev | Growing], + case List of + [] -> + case Direction of + ascending -> + [lists:reverse(Growing@1) | Acc]; + + descending -> + [Growing@1 | Acc] + end; + + [New | Rest] -> + case {Compare(Prev, New), Direction} of + {gt, descending} -> + sequences(Rest, Compare, Growing@1, Direction, New, Acc); + + {lt, ascending} -> + sequences(Rest, Compare, Growing@1, Direction, New, Acc); + + {eq, ascending} -> + sequences(Rest, Compare, Growing@1, Direction, New, Acc); + + {gt, ascending} -> + Acc@1 = case Direction of + ascending -> + [lists:reverse(Growing@1) | Acc]; + + descending -> + [Growing@1 | Acc] + end, + case Rest of + [] -> + [[New] | Acc@1]; + + [Next | Rest@1] -> + Direction@1 = case Compare(New, Next) of + lt -> + ascending; + + eq -> + ascending; + + gt -> + descending + end, + sequences( + Rest@1, + Compare, + [New], + Direction@1, + Next, + Acc@1 + ) + end; + + {lt, descending} -> + Acc@1 = case Direction of + ascending -> + [lists:reverse(Growing@1) | Acc]; + + descending -> + [Growing@1 | Acc] + end, + case Rest of + [] -> + [[New] | Acc@1]; + + [Next | Rest@1] -> + Direction@1 = case Compare(New, Next) of + lt -> + ascending; + + eq -> + ascending; + + gt -> + descending + end, + sequences( + Rest@1, + Compare, + [New], + Direction@1, + Next, + Acc@1 + ) + end; + + {eq, descending} -> + Acc@1 = case Direction of + ascending -> + [lists:reverse(Growing@1) | Acc]; + + descending -> + [Growing@1 | Acc] + end, + case Rest of + [] -> + [[New] | Acc@1]; + + [Next | Rest@1] -> + Direction@1 = case Compare(New, Next) of + lt -> + ascending; + + eq -> + ascending; + + gt -> + descending + end, + sequences( + Rest@1, + Compare, + [New], + Direction@1, + Next, + Acc@1 + ) + end + end + end. + +-file("src/gleam/list.gleam", 1430). +?DOC( + " Merges two lists sorted in ascending order into a single list sorted in\n" + " descending order according to the given comparator function.\n" + "\n" + " This reversing of the sort order is not avoidable if we want to implement\n" + " merge as a tail recursive function. We could reverse the accumulator before\n" + " returning it but that would end up being less efficient; so the merging\n" + " algorithm has to play around this.\n" +). +-spec merge_ascendings( + list(AJG), + list(AJG), + fun((AJG, AJG) -> gleam@order:order()), + list(AJG) +) -> list(AJG). +merge_ascendings(List1, List2, Compare, Acc) -> + case {List1, List2} of + {[], List} -> + lists:reverse(List, Acc); + + {List, []} -> + lists:reverse(List, Acc); + + {[First1 | Rest1], [First2 | Rest2]} -> + case Compare(First1, First2) of + lt -> + merge_ascendings(Rest1, List2, Compare, [First1 | Acc]); + + gt -> + merge_ascendings(List1, Rest2, Compare, [First2 | Acc]); + + eq -> + merge_ascendings(List1, Rest2, Compare, [First2 | Acc]) + end + end. + +-file("src/gleam/list.gleam", 1383). +?DOC( + " Given a list of ascending lists, it merges adjacent pairs into a single\n" + " descending list, halving their number.\n" + " It returns a list of the remaining descending lists.\n" +). +-spec merge_ascending_pairs( + list(list(AIU)), + fun((AIU, AIU) -> gleam@order:order()), + list(list(AIU)) +) -> list(list(AIU)). +merge_ascending_pairs(Sequences, Compare, Acc) -> + case Sequences of + [] -> + lists:reverse(Acc); + + [Sequence] -> + lists:reverse([lists:reverse(Sequence) | Acc]); + + [Ascending1, Ascending2 | Rest] -> + Descending = merge_ascendings(Ascending1, Ascending2, Compare, []), + merge_ascending_pairs(Rest, Compare, [Descending | Acc]) + end. + +-file("src/gleam/list.gleam", 1457). +?DOC( + " This is exactly the same as merge_ascendings but mirrored: it merges two\n" + " lists sorted in descending order into a single list sorted in ascending\n" + " order according to the given comparator function.\n" + "\n" + " This reversing of the sort order is not avoidable if we want to implement\n" + " merge as a tail recursive function. We could reverse the accumulator before\n" + " returning it but that would end up being less efficient; so the merging\n" + " algorithm has to play around this.\n" +). +-spec merge_descendings( + list(AJL), + list(AJL), + fun((AJL, AJL) -> gleam@order:order()), + list(AJL) +) -> list(AJL). +merge_descendings(List1, List2, Compare, Acc) -> + case {List1, List2} of + {[], List} -> + lists:reverse(List, Acc); + + {List, []} -> + lists:reverse(List, Acc); + + {[First1 | Rest1], [First2 | Rest2]} -> + case Compare(First1, First2) of + lt -> + merge_descendings(List1, Rest2, Compare, [First2 | Acc]); + + gt -> + merge_descendings(Rest1, List2, Compare, [First1 | Acc]); + + eq -> + merge_descendings(Rest1, List2, Compare, [First1 | Acc]) + end + end. + +-file("src/gleam/list.gleam", 1405). +?DOC(" This is the same as merge_ascending_pairs but flipped for descending lists.\n"). +-spec merge_descending_pairs( + list(list(AJA)), + fun((AJA, AJA) -> gleam@order:order()), + list(list(AJA)) +) -> list(list(AJA)). +merge_descending_pairs(Sequences, Compare, Acc) -> + case Sequences of + [] -> + lists:reverse(Acc); + + [Sequence] -> + lists:reverse([lists:reverse(Sequence) | Acc]); + + [Descending1, Descending2 | Rest] -> + Ascending = merge_descendings(Descending1, Descending2, Compare, []), + merge_descending_pairs(Rest, Compare, [Ascending | Acc]) + end. + +-file("src/gleam/list.gleam", 1349). +?DOC( + " Given some some sorted sequences (assumed to be sorted in `direction`) it\n" + " merges them all together until we're left with just a list sorted in\n" + " ascending order.\n" +). +-spec merge_all( + list(list(AIQ)), + sorting(), + fun((AIQ, AIQ) -> gleam@order:order()) +) -> list(AIQ). +merge_all(Sequences, Direction, Compare) -> + case {Sequences, Direction} of + {[], _} -> + []; + + {[Sequence], ascending} -> + Sequence; + + {[Sequence@1], descending} -> + lists:reverse(Sequence@1); + + {_, ascending} -> + Sequences@1 = merge_ascending_pairs(Sequences, Compare, []), + merge_all(Sequences@1, descending, Compare); + + {_, descending} -> + Sequences@2 = merge_descending_pairs(Sequences, Compare, []), + merge_all(Sequences@2, ascending, Compare) + end. + +-file("src/gleam/list.gleam", 1220). +?DOC( + " Sorts from smallest to largest based upon the ordering specified by a given\n" + " function.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " import gleam/int\n" + "\n" + " sort([4, 3, 6, 5, 4, 1, 2], by: int.compare)\n" + " // -> [1, 2, 3, 4, 4, 5, 6]\n" + " ```\n" +). +-spec sort(list(AIG), fun((AIG, AIG) -> gleam@order:order())) -> list(AIG). +sort(List, Compare) -> + case List of + [] -> + []; + + [X] -> + [X]; + + [X@1, Y | Rest] -> + Direction = case Compare(X@1, Y) of + lt -> + ascending; + + eq -> + ascending; + + gt -> + descending + end, + Sequences = sequences(Rest, Compare, [X@1], Direction, Y, []), + merge_all(Sequences, ascending, Compare) + end. + +-file("src/gleam/list.gleam", 1497). +-spec range_loop(integer(), integer(), list(integer())) -> list(integer()). +range_loop(Start, Stop, Acc) -> + case gleam@int:compare(Start, Stop) of + eq -> + [Stop | Acc]; + + gt -> + range_loop(Start, Stop + 1, [Stop | Acc]); + + lt -> + range_loop(Start, Stop - 1, [Stop | Acc]) + end. + +-file("src/gleam/list.gleam", 1493). +?DOC( + " Creates a list of ints ranging from a given start and finish.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " range(0, 0)\n" + " // -> [0]\n" + " ```\n" + "\n" + " ```gleam\n" + " range(0, 5)\n" + " // -> [0, 1, 2, 3, 4, 5]\n" + " ```\n" + "\n" + " ```gleam\n" + " range(1, -5)\n" + " // -> [1, 0, -1, -2, -3, -4, -5]\n" + " ```\n" +). +-spec range(integer(), integer()) -> list(integer()). +range(Start, Stop) -> + range_loop(Start, Stop, []). + +-file("src/gleam/list.gleam", 1523). +-spec repeat_loop(AJV, integer(), list(AJV)) -> list(AJV). +repeat_loop(Item, Times, Acc) -> + case Times =< 0 of + true -> + Acc; + + false -> + repeat_loop(Item, Times - 1, [Item | Acc]) + end. + +-file("src/gleam/list.gleam", 1519). +?DOC( + " Builds a list of a given value a given number of times.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " repeat(\"a\", times: 0)\n" + " // -> []\n" + " ```\n" + "\n" + " ```gleam\n" + " repeat(\"a\", times: 5)\n" + " // -> [\"a\", \"a\", \"a\", \"a\", \"a\"]\n" + " ```\n" +). +-spec repeat(AJT, integer()) -> list(AJT). +repeat(A, Times) -> + repeat_loop(A, Times, []). + +-file("src/gleam/list.gleam", 1556). +-spec split_loop(list(AKC), integer(), list(AKC)) -> {list(AKC), list(AKC)}. +split_loop(List, N, Taken) -> + case N =< 0 of + true -> + {lists:reverse(Taken), List}; + + false -> + case List of + [] -> + {lists:reverse(Taken), []}; + + [First | Rest] -> + split_loop(Rest, N - 1, [First | Taken]) + end + end. + +-file("src/gleam/list.gleam", 1552). +?DOC( + " Splits a list in two before the given index.\n" + "\n" + " If the list is not long enough to have the given index the before list will\n" + " be the input list, and the after list will be empty.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " split([6, 7, 8, 9], 0)\n" + " // -> #([], [6, 7, 8, 9])\n" + " ```\n" + "\n" + " ```gleam\n" + " split([6, 7, 8, 9], 2)\n" + " // -> #([6, 7], [8, 9])\n" + " ```\n" + "\n" + " ```gleam\n" + " split([6, 7, 8, 9], 4)\n" + " // -> #([6, 7, 8, 9], [])\n" + " ```\n" +). +-spec split(list(AJY), integer()) -> {list(AJY), list(AJY)}. +split(List, Index) -> + split_loop(List, Index, []). + +-file("src/gleam/list.gleam", 1592). +-spec split_while_loop(list(AKL), fun((AKL) -> boolean()), list(AKL)) -> {list(AKL), + list(AKL)}. +split_while_loop(List, F, Acc) -> + case List of + [] -> + {lists:reverse(Acc), []}; + + [First | Rest] -> + case F(First) of + true -> + split_while_loop(Rest, F, [First | Acc]); + + false -> + {lists:reverse(Acc), List} + end + end. + +-file("src/gleam/list.gleam", 1585). +?DOC( + " Splits a list in two before the first element that a given function returns\n" + " `False` for.\n" + "\n" + " If the function returns `True` for all elements the first list will be the\n" + " input list, and the second list will be empty.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " split_while([1, 2, 3, 4, 5], fn(x) { x <= 3 })\n" + " // -> #([1, 2, 3], [4, 5])\n" + " ```\n" + "\n" + " ```gleam\n" + " split_while([1, 2, 3, 4, 5], fn(x) { x <= 5 })\n" + " // -> #([1, 2, 3, 4, 5], [])\n" + " ```\n" +). +-spec split_while(list(AKH), fun((AKH) -> boolean())) -> {list(AKH), list(AKH)}. +split_while(List, Predicate) -> + split_while_loop(List, Predicate, []). + +-file("src/gleam/list.gleam", 1632). +?DOC( + " Given a list of 2-element tuples, finds the first tuple that has a given\n" + " key as the first element and returns the second element.\n" + "\n" + " If no tuple is found with the given key then `Error(Nil)` is returned.\n" + "\n" + " This function may be useful for interacting with Erlang code where lists of\n" + " tuples are common.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " key_find([#(\"a\", 0), #(\"b\", 1)], \"a\")\n" + " // -> Ok(0)\n" + " ```\n" + "\n" + " ```gleam\n" + " key_find([#(\"a\", 0), #(\"b\", 1)], \"b\")\n" + " // -> Ok(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " key_find([#(\"a\", 0), #(\"b\", 1)], \"c\")\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec key_find(list({AKQ, AKR}), AKQ) -> {ok, AKR} | {error, nil}. +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 + ). + +-file("src/gleam/list.gleam", 1663). +?DOC( + " Given a list of 2-element tuples, finds all tuples that have a given\n" + " key as the first element and returns the second element.\n" + "\n" + " This function may be useful for interacting with Erlang code where lists of\n" + " tuples are common.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " key_filter([#(\"a\", 0), #(\"b\", 1), #(\"a\", 2)], \"a\")\n" + " // -> [0, 2]\n" + " ```\n" + "\n" + " ```gleam\n" + " key_filter([#(\"a\", 0), #(\"b\", 1)], \"c\")\n" + " // -> []\n" + " ```\n" +). +-spec key_filter(list({AKV, AKW}), AKV) -> list(AKW). +key_filter(Keyword_list, Desired_key) -> + filter_map( + Keyword_list, + fun(Keyword) -> + {Key, Value} = Keyword, + case Key =:= Desired_key of + true -> + {ok, Value}; + + false -> + {error, nil} + end + end + ). + +-file("src/gleam/list.gleam", 1703). +-spec key_pop_loop(list({ALF, ALG}), ALF, list({ALF, ALG})) -> {ok, + {ALG, list({ALF, ALG})}} | + {error, nil}. +key_pop_loop(List, Key, Checked) -> + case List of + [] -> + {error, nil}; + + [{K, V} | Rest] when K =:= Key -> + {ok, {V, lists:reverse(Checked, Rest)}}; + + [First | Rest@1] -> + key_pop_loop(Rest@1, Key, [First | Checked]) + end. + +-file("src/gleam/list.gleam", 1699). +?DOC( + " Given a list of 2-element tuples, finds the first tuple that has a given\n" + " key as the first element. This function will return the second element\n" + " of the found tuple and list with tuple removed.\n" + "\n" + " If no tuple is found with the given key then `Error(Nil)` is returned.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " key_pop([#(\"a\", 0), #(\"b\", 1)], \"a\")\n" + " // -> Ok(#(0, [#(\"b\", 1)]))\n" + " ```\n" + "\n" + " ```gleam\n" + " key_pop([#(\"a\", 0), #(\"b\", 1)], \"b\")\n" + " // -> Ok(#(1, [#(\"a\", 0)]))\n" + " ```\n" + "\n" + " ```gleam\n" + " key_pop([#(\"a\", 0), #(\"b\", 1)], \"c\")\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec key_pop(list({AKZ, ALA}), AKZ) -> {ok, {ALA, list({AKZ, ALA})}} | + {error, nil}. +key_pop(List, Key) -> + key_pop_loop(List, Key, []). + +-file("src/gleam/list.gleam", 1737). +-spec key_set_loop(list({ALQ, ALR}), ALQ, ALR, list({ALQ, ALR})) -> list({ALQ, + ALR}). +key_set_loop(List, Key, Value, Inspected) -> + case List of + [{K, _} | Rest] when K =:= Key -> + lists:reverse(Inspected, [{K, Value} | Rest]); + + [First | Rest@1] -> + key_set_loop(Rest@1, Key, Value, [First | Inspected]); + + [] -> + lists:reverse([{Key, Value} | Inspected]) + end. + +-file("src/gleam/list.gleam", 1733). +?DOC( + " Given a list of 2-element tuples, inserts a key and value into the list.\n" + "\n" + " If there was already a tuple with the key then it is replaced, otherwise it\n" + " is added to the end of the list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " key_set([#(5, 0), #(4, 1)], 4, 100)\n" + " // -> [#(5, 0), #(4, 100)]\n" + " ```\n" + "\n" + " ```gleam\n" + " key_set([#(5, 0), #(4, 1)], 1, 100)\n" + " // -> [#(5, 0), #(4, 1), #(1, 100)]\n" + " ```\n" +). +-spec key_set(list({ALM, ALN}), ALM, ALN) -> list({ALM, ALN}). +key_set(List, Key, Value) -> + key_set_loop(List, Key, Value, []). + +-file("src/gleam/list.gleam", 1765). +?DOC( + " Calls a function for each element in a list, discarding the return value.\n" + "\n" + " Useful for calling a side effect for every item of a list.\n" + "\n" + " ```gleam\n" + " import gleam/io\n" + "\n" + " each([\"1\", \"2\", \"3\"], io.println)\n" + " // -> Nil\n" + " // 1\n" + " // 2\n" + " // 3\n" + " ```\n" +). +-spec each(list(ALV), fun((ALV) -> any())) -> nil. +each(List, F) -> + case List of + [] -> + nil; + + [First | Rest] -> + F(First), + each(Rest, F) + end. + +-file("src/gleam/list.gleam", 1791). +?DOC( + " Calls a `Result` returning function for each element in a list, discarding\n" + " the return value. If the function returns `Error` then the iteration is\n" + " stopped and the error is returned.\n" + "\n" + " Useful for calling a side effect for every item of a list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " try_each(\n" + " over: [1, 2, 3],\n" + " with: function_that_might_fail,\n" + " )\n" + " // -> Ok(Nil)\n" + " ```\n" +). +-spec try_each(list(ALY), fun((ALY) -> {ok, any()} | {error, AMB})) -> {ok, nil} | + {error, AMB}. +try_each(List, Fun) -> + case List of + [] -> + {ok, nil}; + + [First | Rest] -> + case Fun(First) of + {ok, _} -> + try_each(Rest, Fun); + + {error, E} -> + {error, E} + end + end. + +-file("src/gleam/list.gleam", 1824). +-spec partition_loop(list(BFR), fun((BFR) -> boolean()), list(BFR), list(BFR)) -> {list(BFR), + list(BFR)}. +partition_loop(List, Categorise, Trues, Falses) -> + case List of + [] -> + {lists:reverse(Trues), lists:reverse(Falses)}; + + [First | Rest] -> + case Categorise(First) of + true -> + partition_loop(Rest, Categorise, [First | Trues], Falses); + + false -> + partition_loop(Rest, Categorise, Trues, [First | Falses]) + end + end. + +-file("src/gleam/list.gleam", 1817). +?DOC( + " Partitions a list into a tuple/pair of lists\n" + " by a given categorisation function.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " import gleam/int\n" + "\n" + " [1, 2, 3, 4, 5] |> partition(int.is_odd)\n" + " // -> #([1, 3, 5], [2, 4])\n" + " ```\n" +). +-spec partition(list(AMG), fun((AMG) -> boolean())) -> {list(AMG), list(AMG)}. +partition(List, Categorise) -> + partition_loop(List, Categorise, [], []). + +-file("src/gleam/list.gleam", 1904). +-spec window_loop(list(list(ANN)), list(ANN), integer()) -> list(list(ANN)). +window_loop(Acc, List, N) -> + Window = take(List, N), + case erlang:length(Window) =:= N of + true -> + window_loop([Window | Acc], drop(List, 1), N); + + false -> + lists:reverse(Acc) + end. + +-file("src/gleam/list.gleam", 1897). +?DOC( + " Returns a list of sliding windows.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " window([1,2,3,4,5], 3)\n" + " // -> [[1, 2, 3], [2, 3, 4], [3, 4, 5]]\n" + " ```\n" + "\n" + " ```gleam\n" + " window([1, 2], 4)\n" + " // -> []\n" + " ```\n" +). +-spec window(list(ANJ), integer()) -> list(list(ANJ)). +window(List, N) -> + case N =< 0 of + true -> + []; + + false -> + window_loop([], List, N) + end. + +-file("src/gleam/list.gleam", 1927). +?DOC( + " Returns a list of tuples containing two contiguous elements.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " window_by_2([1,2,3,4])\n" + " // -> [#(1, 2), #(2, 3), #(3, 4)]\n" + " ```\n" + "\n" + " ```gleam\n" + " window_by_2([1])\n" + " // -> []\n" + " ```\n" +). +-spec window_by_2(list(ANT)) -> list({ANT, ANT}). +window_by_2(List) -> + zip(List, drop(List, 1)). + +-file("src/gleam/list.gleam", 1940). +?DOC( + " Drops the first elements in a given list for which the predicate function returns `True`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " drop_while([1, 2, 3, 4], fn (x) { x < 3 })\n" + " // -> [3, 4]\n" + " ```\n" +). +-spec drop_while(list(ANW), fun((ANW) -> boolean())) -> list(ANW). +drop_while(List, Predicate) -> + case List of + [] -> + []; + + [First | Rest] -> + case Predicate(First) of + true -> + drop_while(Rest, Predicate); + + false -> + [First | Rest] + end + end. + +-file("src/gleam/list.gleam", 1970). +-spec take_while_loop(list(AOC), fun((AOC) -> boolean()), list(AOC)) -> list(AOC). +take_while_loop(List, Predicate, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [First | Rest] -> + case Predicate(First) of + true -> + take_while_loop(Rest, Predicate, [First | Acc]); + + false -> + lists:reverse(Acc) + end + end. + +-file("src/gleam/list.gleam", 1963). +?DOC( + " Takes the first elements in a given list for which the predicate function returns `True`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " take_while([1, 2, 3, 2, 4], fn (x) { x < 3 })\n" + " // -> [1, 2]\n" + " ```\n" +). +-spec take_while(list(ANZ), fun((ANZ) -> boolean())) -> list(ANZ). +take_while(List, Predicate) -> + take_while_loop(List, Predicate, []). + +-file("src/gleam/list.gleam", 2002). +-spec chunk_loop(list(AOL), fun((AOL) -> AON), AON, list(AOL), list(list(AOL))) -> list(list(AOL)). +chunk_loop(List, F, Previous_key, Current_chunk, Acc) -> + case List of + [First | Rest] -> + Key = F(First), + case Key =:= Previous_key of + true -> + chunk_loop(Rest, F, Key, [First | Current_chunk], Acc); + + false -> + New_acc = [lists:reverse(Current_chunk) | Acc], + chunk_loop(Rest, F, Key, [First], New_acc) + end; + + [] -> + lists:reverse([lists:reverse(Current_chunk) | Acc]) + end. + +-file("src/gleam/list.gleam", 1995). +?DOC( + " Returns a list of chunks in which\n" + " the return value of calling `f` on each element is the same.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " [1, 2, 2, 3, 4, 4, 6, 7, 7] |> chunk(by: fn(n) { n % 2 })\n" + " // -> [[1], [2, 2], [3], [4, 4, 6], [7, 7]]\n" + " ```\n" +). +-spec chunk(list(AOG), fun((AOG) -> any())) -> list(list(AOG)). +chunk(List, F) -> + case List of + [] -> + []; + + [First | Rest] -> + chunk_loop(Rest, F, F(First), [First], []) + end. + +-file("src/gleam/list.gleam", 2047). +-spec sized_chunk_loop( + list(AOX), + integer(), + integer(), + list(AOX), + list(list(AOX)) +) -> list(list(AOX)). +sized_chunk_loop(List, Count, Left, Current_chunk, Acc) -> + case List of + [] -> + case Current_chunk of + [] -> + lists:reverse(Acc); + + Remaining -> + lists:reverse([lists:reverse(Remaining) | Acc]) + end; + + [First | Rest] -> + Chunk = [First | Current_chunk], + case Left > 1 of + true -> + sized_chunk_loop(Rest, Count, Left - 1, Chunk, Acc); + + false -> + sized_chunk_loop( + Rest, + Count, + Count, + [], + [lists:reverse(Chunk) | Acc] + ) + end + end. + +-file("src/gleam/list.gleam", 2043). +?DOC( + " Returns a list of chunks containing `count` elements each.\n" + "\n" + " If the last chunk does not have `count` elements, it is instead\n" + " a partial chunk, with less than `count` elements.\n" + "\n" + " For any `count` less than 1 this function behaves as if it was set to 1.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " [1, 2, 3, 4, 5, 6] |> sized_chunk(into: 2)\n" + " // -> [[1, 2], [3, 4], [5, 6]]\n" + " ```\n" + "\n" + " ```gleam\n" + " [1, 2, 3, 4, 5, 6, 7, 8] |> sized_chunk(into: 3)\n" + " // -> [[1, 2, 3], [4, 5, 6], [7, 8]]\n" + " ```\n" +). +-spec sized_chunk(list(AOT), integer()) -> list(list(AOT)). +sized_chunk(List, Count) -> + sized_chunk_loop(List, Count, Count, [], []). + +-file("src/gleam/list.gleam", 2091). +?DOC( + " This function acts similar to fold, but does not take an initial state.\n" + " Instead, it starts from the first element in the list\n" + " and combines it with each subsequent element in turn using the given\n" + " function. The function is called as `fun(accumulator, current_element)`.\n" + "\n" + " Returns `Ok` to indicate a successful run, and `Error` if called on an\n" + " empty list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " [] |> reduce(fn(acc, x) { acc + x })\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " [1, 2, 3, 4, 5] |> reduce(fn(acc, x) { acc + x })\n" + " // -> Ok(15)\n" + " ```\n" +). +-spec reduce(list(APE), fun((APE, APE) -> APE)) -> {ok, APE} | {error, nil}. +reduce(List, Fun) -> + case List of + [] -> + {error, nil}; + + [First | Rest] -> + {ok, fold(Rest, First, Fun)} + end. + +-file("src/gleam/list.gleam", 2115). +-spec scan_loop(list(APM), APO, list(APO), fun((APO, APM) -> APO)) -> list(APO). +scan_loop(List, Accumulator, Accumulated, Fun) -> + case List of + [] -> + lists:reverse(Accumulated); + + [First | Rest] -> + Next = Fun(Accumulator, First), + scan_loop(Rest, Next, [Next | Accumulated], Fun) + end. + +-file("src/gleam/list.gleam", 2107). +?DOC( + " Similar to `fold`, but yields the state of the accumulator at each stage.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " scan(over: [1, 2, 3], from: 100, with: fn(acc, i) { acc + i })\n" + " // -> [101, 103, 106]\n" + " ```\n" +). +-spec scan(list(API), APK, fun((APK, API) -> APK)) -> list(APK). +scan(List, Initial, Fun) -> + scan_loop(List, Initial, [], Fun). + +-file("src/gleam/list.gleam", 2148). +?DOC( + " Returns the last element in the given list.\n" + "\n" + " Returns `Error(Nil)` if the list is empty.\n" + "\n" + " This function runs in linear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " last([])\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " last([1, 2, 3, 4, 5])\n" + " // -> Ok(5)\n" + " ```\n" +). +-spec last(list(APR)) -> {ok, APR} | {error, nil}. +last(List) -> + case List of + [] -> + {error, nil}; + + [Last] -> + {ok, Last}; + + [_ | Rest] -> + last(Rest) + end. + +-file("src/gleam/list.gleam", 2170). +?DOC( + " Return unique combinations of elements in the list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " combinations([1, 2, 3], 2)\n" + " // -> [[1, 2], [1, 3], [2, 3]]\n" + " ```\n" + "\n" + " ```gleam\n" + " combinations([1, 2, 3, 4], 3)\n" + " // -> [[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]\n" + " ```\n" +). +-spec combinations(list(APV), integer()) -> list(list(APV)). +combinations(Items, N) -> + case {N, Items} of + {0, _} -> + [[]]; + + {_, []} -> + []; + + {_, [First | Rest]} -> + _pipe = Rest, + _pipe@1 = combinations(_pipe, N - 1), + _pipe@2 = map( + _pipe@1, + fun(Combination) -> [First | Combination] end + ), + _pipe@3 = lists:reverse(_pipe@2), + fold(_pipe@3, combinations(Rest, N), fun(Acc, C) -> [C | Acc] end) + end. + +-file("src/gleam/list.gleam", 2196). +-spec combination_pairs_loop(list(AQC), list({AQC, AQC})) -> list({AQC, AQC}). +combination_pairs_loop(Items, Acc) -> + case Items of + [] -> + lists:reverse(Acc); + + [First | Rest] -> + First_combinations = map(Rest, fun(Other) -> {First, Other} end), + Acc@1 = lists:reverse(First_combinations, Acc), + combination_pairs_loop(Rest, Acc@1) + end. + +-file("src/gleam/list.gleam", 2192). +?DOC( + " Return unique pair combinations of elements in the list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " combination_pairs([1, 2, 3])\n" + " // -> [#(1, 2), #(1, 3), #(2, 3)]\n" + " ```\n" +). +-spec combination_pairs(list(APZ)) -> list({APZ, APZ}). +combination_pairs(Items) -> + combination_pairs_loop(Items, []). + +-file("src/gleam/list.gleam", 2252). +-spec take_firsts(list(list(AQW)), list(AQW), list(list(AQW))) -> {list(AQW), + list(list(AQW))}. +take_firsts(Rows, Column, Remaining_rows) -> + case Rows of + [] -> + {lists:reverse(Column), lists:reverse(Remaining_rows)}; + + [[] | Rest] -> + take_firsts(Rest, Column, Remaining_rows); + + [[First | Remaining_row] | Rest_rows] -> + Remaining_rows@1 = [Remaining_row | Remaining_rows], + take_firsts(Rest_rows, [First | Column], Remaining_rows@1) + end. + +-file("src/gleam/list.gleam", 2239). +-spec transpose_loop(list(list(AQP)), list(list(AQP))) -> list(list(AQP)). +transpose_loop(Rows, Columns) -> + case Rows of + [] -> + lists:reverse(Columns); + + _ -> + {Column, Rest} = take_firsts(Rows, [], []), + case Column of + [_ | _] -> + transpose_loop(Rest, [Column | Columns]); + + [] -> + transpose_loop(Rest, Columns) + end + end. + +-file("src/gleam/list.gleam", 2235). +?DOC( + " Transpose rows and columns of the list of lists.\n" + "\n" + " Notice: This function is not tail recursive,\n" + " and thus may exceed stack size if called,\n" + " with large lists (on the JavaScript target).\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " transpose([[1, 2, 3], [101, 102, 103]])\n" + " // -> [[1, 101], [2, 102], [3, 103]]\n" + " ```\n" +). +-spec transpose(list(list(AQK))) -> list(list(AQK)). +transpose(List_of_lists) -> + transpose_loop(List_of_lists, []). + +-file("src/gleam/list.gleam", 2216). +?DOC( + " Make a list alternating the elements from the given lists\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " interleave([[1, 2], [101, 102], [201, 202]])\n" + " // -> [1, 101, 201, 2, 102, 202]\n" + " ```\n" +). +-spec interleave(list(list(AQG))) -> list(AQG). +interleave(List) -> + _pipe = List, + _pipe@1 = transpose(_pipe), + lists:append(_pipe@1). + +-file("src/gleam/list.gleam", 2285). +-spec shuffle_pair_unwrap_loop(list({float(), ARI}), list(ARI)) -> list(ARI). +shuffle_pair_unwrap_loop(List, Acc) -> + case List of + [] -> + Acc; + + [Elem_pair | Enumerable] -> + shuffle_pair_unwrap_loop( + Enumerable, + [erlang:element(2, Elem_pair) | Acc] + ) + end. + +-file("src/gleam/list.gleam", 2293). +-spec do_shuffle_by_pair_indexes(list({float(), ARM})) -> list({float(), ARM}). +do_shuffle_by_pair_indexes(List_of_pairs) -> + sort( + List_of_pairs, + fun(A_pair, B_pair) -> + gleam@float:compare( + erlang:element(1, A_pair), + erlang:element(1, B_pair) + ) + end + ). + +-file("src/gleam/list.gleam", 2278). +?DOC( + " Takes a list, randomly sorts all items and returns the shuffled list.\n" + "\n" + " This function uses `float.random` to decide the order of the elements.\n" + "\n" + " ## Example\n" + "\n" + " ```gleam\n" + " range(1, 10) |> shuffle()\n" + " // -> [1, 6, 9, 10, 3, 8, 4, 2, 7, 5]\n" + " ```\n" +). +-spec shuffle(list(ARF)) -> list(ARF). +shuffle(List) -> + _pipe = List, + _pipe@1 = fold(_pipe, [], fun(Acc, A) -> [{rand:uniform(), A} | Acc] end), + _pipe@2 = do_shuffle_by_pair_indexes(_pipe@1), + shuffle_pair_unwrap_loop(_pipe@2, []). + +-file("src/gleam/list.gleam", 2325). +-spec max_loop(list(ARW), fun((ARW, ARW) -> gleam@order:order()), ARW) -> ARW. +max_loop(List, Compare, Max) -> + case List of + [] -> + Max; + + [First | Rest] -> + case Compare(First, Max) of + gt -> + max_loop(Rest, Compare, First); + + lt -> + max_loop(Rest, Compare, Max); + + eq -> + max_loop(Rest, Compare, Max) + end + end. + +-file("src/gleam/list.gleam", 2315). +?DOC( + " Takes a list and a comparator, and returns the maximum element in the list\n" + "\n" + "\n" + " ## Example\n" + "\n" + " ```gleam\n" + " range(1, 10) |> list.max(int.compare)\n" + " // -> Ok(10)\n" + " ```\n" + "\n" + " ```gleam\n" + " [\"a\", \"c\", \"b\"] |> list.max(string.compare)\n" + " // -> Ok(\"c\")\n" + " ```\n" +). +-spec max(list(ARP), fun((ARP, ARP) -> gleam@order:order())) -> {ok, ARP} | + {error, nil}. +max(List, Compare) -> + case List of + [] -> + {error, nil}; + + [First | Rest] -> + {ok, max_loop(Rest, Compare, First)} + end. + +-file("src/gleam/list.gleam", 2406). +-spec build_reservoir_loop( + list(ASL), + integer(), + gleam@dict:dict(integer(), ASL) +) -> {gleam@dict:dict(integer(), ASL), list(ASL)}. +build_reservoir_loop(List, Size, Reservoir) -> + Reservoir_size = maps:size(Reservoir), + case Reservoir_size >= Size of + true -> + {Reservoir, List}; + + false -> + case List of + [] -> + {Reservoir, []}; + + [First | Rest] -> + Reservoir@1 = gleam@dict:insert( + Reservoir, + Reservoir_size, + First + ), + build_reservoir_loop(Rest, Size, Reservoir@1) + end + end. + +-file("src/gleam/list.gleam", 2402). +?DOC( + " Builds the initial reservoir used by Algorithm L.\n" + " This is a dictionary with keys ranging from `0` up to `n - 1` where each\n" + " value is the corresponding element at that position in `list`.\n" + "\n" + " This also returns the remaining elements of `list` that didn't end up in\n" + " the reservoir.\n" +). +-spec build_reservoir(list(ASG), integer()) -> {gleam@dict:dict(integer(), ASG), + list(ASG)}. +build_reservoir(List, N) -> + build_reservoir_loop(List, N, maps:new()). + +-file("src/gleam/list.gleam", 2390). +-spec log_random() -> float(). +log_random() -> + Random@1 = case gleam@float:logarithm( + rand:uniform() + 2.2250738585072014e-308 + ) of + {ok, Random} -> Random; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Pattern match failed, no pattern matched the value."/utf8>>, + file => <>, + module => <<"gleam/list"/utf8>>, + function => <<"log_random"/utf8>>, + line => 2391, + value => _assert_fail, + start => 56078, + 'end' => 56149, + pattern_start => 56089, + pattern_end => 56099}) + end, + Random@1. + +-file("src/gleam/list.gleam", 2367). +-spec sample_loop( + list(ASA), + gleam@dict:dict(integer(), ASA), + integer(), + float() +) -> gleam@dict:dict(integer(), ASA). +sample_loop(List, Reservoir, N, W) -> + Skip = begin + Log@1 = case gleam@float:logarithm(1.0 - W) of + {ok, Log} -> Log; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Pattern match failed, no pattern matched the value."/utf8>>, + file => <>, + module => <<"gleam/list"/utf8>>, + function => <<"sample_loop"/utf8>>, + line => 2374, + value => _assert_fail, + start => 55639, + 'end' => 55685, + pattern_start => 55650, + pattern_end => 55657}) + end, + erlang:round(math:floor(case Log@1 of + +0.0 -> +0.0; + -0.0 -> -0.0; + Gleam@denominator -> log_random() / Gleam@denominator + end)) + end, + case drop(List, Skip) of + [] -> + Reservoir; + + [First | Rest] -> + Reservoir@1 = gleam@dict:insert( + Reservoir, + gleam@int:random(N), + First + ), + W@1 = W * math:exp(case erlang:float(N) of + +0.0 -> +0.0; + -0.0 -> -0.0; + Gleam@denominator@1 -> log_random() / Gleam@denominator@1 + end), + sample_loop(Rest, Reservoir@1, N, W@1) + end. + +-file("src/gleam/list.gleam", 2349). +?DOC( + " Returns a random sample of up to n elements from a list using reservoir\n" + " sampling via [Algorithm L](https://en.wikipedia.org/wiki/Reservoir_sampling#Optimal:_Algorithm_L).\n" + " Returns an empty list if the sample size is less than or equal to 0.\n" + "\n" + " Order is not random, only selection is.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " reservoir_sample([1, 2, 3, 4, 5], 3)\n" + " // -> [2, 4, 5] // A random sample of 3 items\n" + " ```\n" +). +-spec sample(list(ARX), integer()) -> list(ARX). +sample(List, N) -> + {Reservoir, Rest} = build_reservoir(List, N), + case gleam@dict:is_empty(Reservoir) of + true -> + []; + + false -> + W = math:exp(case erlang:float(N) of + +0.0 -> +0.0; + -0.0 -> -0.0; + Gleam@denominator -> log_random() / Gleam@denominator + end), + maps:values(sample_loop(Rest, Reservoir, N, W)) + end. + +-file("src/gleam/list.gleam", 1851). +-spec permutation_zip(list(AMT), list(AMT), list(list(AMT))) -> list(list(AMT)). +permutation_zip(List, Rest, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [Head | Tail] -> + permutation_prepend( + Head, + permutations(lists:reverse(Rest, Tail)), + Tail, + [Head | Rest], + Acc + ) + end. + +-file("src/gleam/list.gleam", 1869). +-spec permutation_prepend( + ANA, + list(list(ANA)), + list(ANA), + list(ANA), + list(list(ANA)) +) -> list(list(ANA)). +permutation_prepend(El, Permutations, List_1, List_2, Acc) -> + case Permutations of + [] -> + permutation_zip(List_1, List_2, Acc); + + [Head | Tail] -> + permutation_prepend(El, Tail, List_1, List_2, [[El | Head] | Acc]) + end. + +-file("src/gleam/list.gleam", 1844). +?DOC( + " Returns all the permutations of a list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " permutations([1, 2])\n" + " // -> [[1, 2], [2, 1]]\n" + " ```\n" +). +-spec permutations(list(AMP)) -> list(list(AMP)). +permutations(List) -> + case List of + [] -> + [[]]; + + L -> + permutation_zip(L, [], []) + end. diff --git a/build/dev/javascript/gleam_stdlib/gleam@option.erl b/build/dev/javascript/gleam_stdlib/gleam@option.erl new file mode 100644 index 0000000..8e86a8e --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam@option.erl @@ -0,0 +1,413 @@ +-module(gleam@option). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/option.gleam"). +-export([all/1, is_some/1, is_none/1, to_result/2, from_result/1, unwrap/2, lazy_unwrap/2, map/2, flatten/1, then/2, 'or'/2, lazy_or/2, values/1]). +-export_type([option/1]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +-type option(FG) :: {some, FG} | none. + +-file("src/gleam/option.gleam", 59). +-spec reverse_and_prepend(list(FV), list(FV)) -> list(FV). +reverse_and_prepend(Prefix, Suffix) -> + case Prefix of + [] -> + Suffix; + + [First | Rest] -> + reverse_and_prepend(Rest, [First | Suffix]) + end. + +-file("src/gleam/option.gleam", 44). +-spec all_loop(list(option(FM)), list(FM)) -> option(list(FM)). +all_loop(List, Acc) -> + case List of + [] -> + {some, lists:reverse(Acc)}; + + [none | _] -> + none; + + [{some, First} | Rest] -> + all_loop(Rest, [First | Acc]) + end. + +-file("src/gleam/option.gleam", 40). +?DOC( + " Combines a list of `Option`s into a single `Option`.\n" + " If all elements in the list are `Some` then returns a `Some` holding the list of values.\n" + " If any element is `None` then returns`None`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " all([Some(1), Some(2)])\n" + " // -> Some([1, 2])\n" + " ```\n" + "\n" + " ```gleam\n" + " all([Some(1), None])\n" + " // -> None\n" + " ```\n" +). +-spec all(list(option(FH))) -> option(list(FH)). +all(List) -> + all_loop(List, []). + +-file("src/gleam/option.gleam", 80). +?DOC( + " Checks whether the `Option` is a `Some` value.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " is_some(Some(1))\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " is_some(None)\n" + " // -> False\n" + " ```\n" +). +-spec is_some(option(any())) -> boolean(). +is_some(Option) -> + Option /= none. + +-file("src/gleam/option.gleam", 98). +?DOC( + " Checks whether the `Option` is a `None` value.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " is_none(Some(1))\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " is_none(None)\n" + " // -> True\n" + " ```\n" +). +-spec is_none(option(any())) -> boolean(). +is_none(Option) -> + Option =:= none. + +-file("src/gleam/option.gleam", 116). +?DOC( + " Converts an `Option` type to a `Result` type.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " to_result(Some(1), \"some_error\")\n" + " // -> Ok(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " to_result(None, \"some_error\")\n" + " // -> Error(\"some_error\")\n" + " ```\n" +). +-spec to_result(option(GD), GG) -> {ok, GD} | {error, GG}. +to_result(Option, E) -> + case Option of + {some, A} -> + {ok, A}; + + none -> + {error, E} + end. + +-file("src/gleam/option.gleam", 137). +?DOC( + " Converts a `Result` type to an `Option` type.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " from_result(Ok(1))\n" + " // -> Some(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " from_result(Error(\"some_error\"))\n" + " // -> None\n" + " ```\n" +). +-spec from_result({ok, GJ} | {error, any()}) -> option(GJ). +from_result(Result) -> + case Result of + {ok, A} -> + {some, A}; + + {error, _} -> + none + end. + +-file("src/gleam/option.gleam", 158). +?DOC( + " Extracts the value from an `Option`, returning a default value if there is none.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " unwrap(Some(1), 0)\n" + " // -> 1\n" + " ```\n" + "\n" + " ```gleam\n" + " unwrap(None, 0)\n" + " // -> 0\n" + " ```\n" +). +-spec unwrap(option(GO), GO) -> GO. +unwrap(Option, Default) -> + case Option of + {some, X} -> + X; + + none -> + Default + end. + +-file("src/gleam/option.gleam", 179). +?DOC( + " Extracts the value from an `Option`, evaluating the default function if the option is `None`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " lazy_unwrap(Some(1), fn() { 0 })\n" + " // -> 1\n" + " ```\n" + "\n" + " ```gleam\n" + " lazy_unwrap(None, fn() { 0 })\n" + " // -> 0\n" + " ```\n" +). +-spec lazy_unwrap(option(GQ), fun(() -> GQ)) -> GQ. +lazy_unwrap(Option, Default) -> + case Option of + {some, X} -> + X; + + none -> + Default() + end. + +-file("src/gleam/option.gleam", 204). +?DOC( + " Updates a value held within the `Some` of an `Option` by calling a given function\n" + " on it.\n" + "\n" + " If the `Option` is a `None` rather than `Some`, the function is not called and the\n" + " `Option` stays the same.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " map(over: Some(1), with: fn(x) { x + 1 })\n" + " // -> Some(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " map(over: None, with: fn(x) { x + 1 })\n" + " // -> None\n" + " ```\n" +). +-spec map(option(GS), fun((GS) -> GU)) -> option(GU). +map(Option, Fun) -> + case Option of + {some, X} -> + {some, Fun(X)}; + + none -> + none + end. + +-file("src/gleam/option.gleam", 230). +?DOC( + " Merges a nested `Option` into a single layer.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " flatten(Some(Some(1)))\n" + " // -> Some(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " flatten(Some(None))\n" + " // -> None\n" + " ```\n" + "\n" + " ```gleam\n" + " flatten(None)\n" + " // -> None\n" + " ```\n" +). +-spec flatten(option(option(GW))) -> option(GW). +flatten(Option) -> + case Option of + {some, X} -> + X; + + none -> + none + end. + +-file("src/gleam/option.gleam", 269). +?DOC( + " Updates a value held within the `Some` of an `Option` by calling a given function\n" + " on it, where the given function also returns an `Option`. The two options are\n" + " then merged together into one `Option`.\n" + "\n" + " If the `Option` is a `None` rather than `Some` the function is not called and the\n" + " option stays the same.\n" + "\n" + " This function is the equivalent of calling `map` followed by `flatten`, and\n" + " it is useful for chaining together multiple functions that return `Option`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " then(Some(1), fn(x) { Some(x + 1) })\n" + " // -> Some(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " then(Some(1), fn(x) { Some(#(\"a\", x)) })\n" + " // -> Some(#(\"a\", 1))\n" + " ```\n" + "\n" + " ```gleam\n" + " then(Some(1), fn(_) { None })\n" + " // -> None\n" + " ```\n" + "\n" + " ```gleam\n" + " then(None, fn(x) { Some(x + 1) })\n" + " // -> None\n" + " ```\n" +). +-spec then(option(HA), fun((HA) -> option(HC))) -> option(HC). +then(Option, Fun) -> + case Option of + {some, X} -> + Fun(X); + + none -> + none + end. + +-file("src/gleam/option.gleam", 300). +?DOC( + " Returns the first value if it is `Some`, otherwise returns the second value.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " or(Some(1), Some(2))\n" + " // -> Some(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " or(Some(1), None)\n" + " // -> Some(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " or(None, Some(2))\n" + " // -> Some(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " or(None, None)\n" + " // -> None\n" + " ```\n" +). +-spec 'or'(option(HF), option(HF)) -> option(HF). +'or'(First, Second) -> + case First of + {some, _} -> + First; + + none -> + Second + end. + +-file("src/gleam/option.gleam", 331). +?DOC( + " Returns the first value if it is `Some`, otherwise evaluates the given function for a fallback value.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " lazy_or(Some(1), fn() { Some(2) })\n" + " // -> Some(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " lazy_or(Some(1), fn() { None })\n" + " // -> Some(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " lazy_or(None, fn() { Some(2) })\n" + " // -> Some(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " lazy_or(None, fn() { None })\n" + " // -> None\n" + " ```\n" +). +-spec lazy_or(option(HJ), fun(() -> option(HJ))) -> option(HJ). +lazy_or(First, Second) -> + case First of + {some, _} -> + First; + + none -> + Second() + end. + +-file("src/gleam/option.gleam", 352). +-spec values_loop(list(option(HR)), list(HR)) -> list(HR). +values_loop(List, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [none | Rest] -> + values_loop(Rest, Acc); + + [{some, First} | Rest@1] -> + values_loop(Rest@1, [First | Acc]) + end. + +-file("src/gleam/option.gleam", 348). +?DOC( + " Given a list of `Option`s,\n" + " returns only the values inside `Some`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " values([Some(1), None, Some(3)])\n" + " // -> [1, 3]\n" + " ```\n" +). +-spec values(list(option(HN))) -> list(HN). +values(Options) -> + values_loop(Options, []). diff --git a/build/dev/javascript/gleam_stdlib/gleam@order.erl b/build/dev/javascript/gleam_stdlib/gleam@order.erl new file mode 100644 index 0000000..ec2bb84 --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam@order.erl @@ -0,0 +1,200 @@ +-module(gleam@order). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/order.gleam"). +-export([negate/1, to_int/1, compare/2, reverse/1, break_tie/2, lazy_break_tie/2]). +-export_type([order/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +-type order() :: lt | eq | gt. + +-file("src/gleam/order.gleam", 35). +?DOC( + " Inverts an order, so less-than becomes greater-than and greater-than\n" + " becomes less-than.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " negate(Lt)\n" + " // -> Gt\n" + " ```\n" + "\n" + " ```gleam\n" + " negate(Eq)\n" + " // -> Eq\n" + " ```\n" + "\n" + " ```gleam\n" + " negate(Gt)\n" + " // -> Lt\n" + " ```\n" +). +-spec negate(order()) -> order(). +negate(Order) -> + case Order of + lt -> + gt; + + eq -> + eq; + + gt -> + lt + end. + +-file("src/gleam/order.gleam", 62). +?DOC( + " Produces a numeric representation of the order.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " to_int(Lt)\n" + " // -> -1\n" + " ```\n" + "\n" + " ```gleam\n" + " to_int(Eq)\n" + " // -> 0\n" + " ```\n" + "\n" + " ```gleam\n" + " to_int(Gt)\n" + " // -> 1\n" + " ```\n" +). +-spec to_int(order()) -> integer(). +to_int(Order) -> + case Order of + lt -> + -1; + + eq -> + 0; + + gt -> + 1 + end. + +-file("src/gleam/order.gleam", 79). +?DOC( + " Compares two `Order` values to one another, producing a new `Order`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " compare(Eq, with: Lt)\n" + " // -> Gt\n" + " ```\n" +). +-spec compare(order(), order()) -> order(). +compare(A, B) -> + case {A, B} of + {X, Y} when X =:= Y -> + eq; + + {lt, _} -> + lt; + + {eq, gt} -> + lt; + + {_, _} -> + gt + end. + +-file("src/gleam/order.gleam", 100). +?DOC( + " Inverts an ordering function, so less-than becomes greater-than and greater-than\n" + " becomes less-than.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " import gleam/int\n" + " import gleam/list\n" + "\n" + " list.sort([1, 5, 4], by: reverse(int.compare))\n" + " // -> [5, 4, 1]\n" + " ```\n" +). +-spec reverse(fun((I, I) -> order())) -> fun((I, I) -> order()). +reverse(Orderer) -> + fun(A, B) -> Orderer(B, A) end. + +-file("src/gleam/order.gleam", 122). +?DOC( + " Return a fallback `Order` in case the first argument is `Eq`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " import gleam/int\n" + "\n" + " break_tie(in: int.compare(1, 1), with: Lt)\n" + " // -> Lt\n" + " ```\n" + "\n" + " ```gleam\n" + " import gleam/int\n" + "\n" + " break_tie(in: int.compare(1, 0), with: Eq)\n" + " // -> Gt\n" + " ```\n" +). +-spec break_tie(order(), order()) -> order(). +break_tie(Order, Other) -> + case Order of + lt -> + Order; + + gt -> + Order; + + eq -> + Other + end. + +-file("src/gleam/order.gleam", 151). +?DOC( + " Invokes a fallback function returning an `Order` in case the first argument\n" + " is `Eq`.\n" + "\n" + " This can be useful when the fallback comparison might be expensive and it\n" + " needs to be delayed until strictly necessary.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " import gleam/int\n" + "\n" + " lazy_break_tie(in: int.compare(1, 1), with: fn() { Lt })\n" + " // -> Lt\n" + " ```\n" + "\n" + " ```gleam\n" + " import gleam/int\n" + "\n" + " lazy_break_tie(in: int.compare(1, 0), with: fn() { Eq })\n" + " // -> Gt\n" + " ```\n" +). +-spec lazy_break_tie(order(), fun(() -> order())) -> order(). +lazy_break_tie(Order, Comparison) -> + case Order of + lt -> + Order; + + gt -> + Order; + + eq -> + Comparison() + end. diff --git a/build/dev/javascript/gleam_stdlib/gleam@pair.erl b/build/dev/javascript/gleam_stdlib/gleam@pair.erl new file mode 100644 index 0000000..cb18264 --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam@pair.erl @@ -0,0 +1,110 @@ +-module(gleam@pair). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/pair.gleam"). +-export([first/1, second/1, swap/1, map_first/2, map_second/2, new/2]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +-file("src/gleam/pair.gleam", 10). +?DOC( + " Returns the first element in a pair.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " first(#(1, 2))\n" + " // -> 1\n" + " ```\n" +). +-spec first({CLF, any()}) -> CLF. +first(Pair) -> + {A, _} = Pair, + A. + +-file("src/gleam/pair.gleam", 24). +?DOC( + " Returns the second element in a pair.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " second(#(1, 2))\n" + " // -> 2\n" + " ```\n" +). +-spec second({any(), CLI}) -> CLI. +second(Pair) -> + {_, A} = Pair, + A. + +-file("src/gleam/pair.gleam", 38). +?DOC( + " Returns a new pair with the elements swapped.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " swap(#(1, 2))\n" + " // -> #(2, 1)\n" + " ```\n" +). +-spec swap({CLJ, CLK}) -> {CLK, CLJ}. +swap(Pair) -> + {A, B} = Pair, + {B, A}. + +-file("src/gleam/pair.gleam", 53). +?DOC( + " Returns a new pair with the first element having had `with` applied to\n" + " it.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " #(1, 2) |> map_first(fn(n) { n * 2 })\n" + " // -> #(2, 2)\n" + " ```\n" +). +-spec map_first({CLL, CLM}, fun((CLL) -> CLN)) -> {CLN, CLM}. +map_first(Pair, Fun) -> + {A, B} = Pair, + {Fun(A), B}. + +-file("src/gleam/pair.gleam", 68). +?DOC( + " Returns a new pair with the second element having had `with` applied to\n" + " it.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " #(1, 2) |> map_second(fn(n) { n * 2 })\n" + " // -> #(1, 4)\n" + " ```\n" +). +-spec map_second({CLO, CLP}, fun((CLP) -> CLQ)) -> {CLO, CLQ}. +map_second(Pair, Fun) -> + {A, B} = Pair, + {A, Fun(B)}. + +-file("src/gleam/pair.gleam", 83). +?DOC( + " Returns a new pair with the given elements. This can also be done using the dedicated\n" + " syntax instead: `new(1, 2) == #(1, 2)`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " new(1, 2)\n" + " // -> #(1, 2)\n" + " ```\n" +). +-spec new(CLR, CLS) -> {CLR, CLS}. +new(First, Second) -> + {First, Second}. diff --git a/build/dev/javascript/gleam_stdlib/gleam@result.erl b/build/dev/javascript/gleam_stdlib/gleam@result.erl new file mode 100644 index 0000000..9d89ff7 --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam@result.erl @@ -0,0 +1,550 @@ +-module(gleam@result). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/result.gleam"). +-export([is_ok/1, is_error/1, map/2, map_error/2, flatten/1, 'try'/2, then/2, unwrap/2, lazy_unwrap/2, unwrap_error/2, unwrap_both/1, 'or'/2, lazy_or/2, all/1, partition/1, replace/2, replace_error/2, values/1, try_recover/2]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC( + " Result represents the result of something that may succeed or not.\n" + " `Ok` means it was successful, `Error` means it was not successful.\n" +). + +-file("src/gleam/result.gleam", 20). +?DOC( + " Checks whether the result is an `Ok` value.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " is_ok(Ok(1))\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " is_ok(Error(Nil))\n" + " // -> False\n" + " ```\n" +). +-spec is_ok({ok, any()} | {error, any()}) -> boolean(). +is_ok(Result) -> + case Result of + {error, _} -> + false; + + {ok, _} -> + true + end. + +-file("src/gleam/result.gleam", 41). +?DOC( + " Checks whether the result is an `Error` value.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " is_error(Ok(1))\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " is_error(Error(Nil))\n" + " // -> True\n" + " ```\n" +). +-spec is_error({ok, any()} | {error, any()}) -> boolean(). +is_error(Result) -> + case Result of + {ok, _} -> + false; + + {error, _} -> + true + end. + +-file("src/gleam/result.gleam", 66). +?DOC( + " Updates a value held within the `Ok` of a result by calling a given function\n" + " on it.\n" + "\n" + " If the result is an `Error` rather than `Ok` the function is not called and the\n" + " result stays the same.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " map(over: Ok(1), with: fn(x) { x + 1 })\n" + " // -> Ok(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " map(over: Error(1), with: fn(x) { x + 1 })\n" + " // -> Error(1)\n" + " ```\n" +). +-spec map({ok, CMC} | {error, CMD}, fun((CMC) -> CMG)) -> {ok, CMG} | + {error, CMD}. +map(Result, Fun) -> + case Result of + {ok, X} -> + {ok, Fun(X)}; + + {error, E} -> + {error, E} + end. + +-file("src/gleam/result.gleam", 91). +?DOC( + " Updates a value held within the `Error` of a result by calling a given function\n" + " on it.\n" + "\n" + " If the result is `Ok` rather than `Error` the function is not called and the\n" + " result stays the same.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " map_error(over: Error(1), with: fn(x) { x + 1 })\n" + " // -> Error(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " map_error(over: Ok(1), with: fn(x) { x + 1 })\n" + " // -> Ok(1)\n" + " ```\n" +). +-spec map_error({ok, CMJ} | {error, CMK}, fun((CMK) -> CMN)) -> {ok, CMJ} | + {error, CMN}. +map_error(Result, Fun) -> + case Result of + {ok, X} -> + {ok, X}; + + {error, Error} -> + {error, Fun(Error)} + end. + +-file("src/gleam/result.gleam", 120). +?DOC( + " Merges a nested `Result` into a single layer.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " flatten(Ok(Ok(1)))\n" + " // -> Ok(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " flatten(Ok(Error(\"\")))\n" + " // -> Error(\"\")\n" + " ```\n" + "\n" + " ```gleam\n" + " flatten(Error(Nil))\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec flatten({ok, {ok, CMQ} | {error, CMR}} | {error, CMR}) -> {ok, CMQ} | + {error, CMR}. +flatten(Result) -> + case Result of + {ok, X} -> + X; + + {error, Error} -> + {error, Error} + end. + +-file("src/gleam/result.gleam", 158). +?DOC( + " \"Updates\" an `Ok` result by passing its value to a function that yields a result,\n" + " and returning the yielded result. (This may \"replace\" the `Ok` with an `Error`.)\n" + "\n" + " If the input is an `Error` rather than an `Ok`, the function is not called and\n" + " the original `Error` is returned.\n" + "\n" + " This function is the equivalent of calling `map` followed by `flatten`, and\n" + " it is useful for chaining together multiple functions that may fail.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " try(Ok(1), fn(x) { Ok(x + 1) })\n" + " // -> Ok(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " try(Ok(1), fn(x) { Ok(#(\"a\", x)) })\n" + " // -> Ok(#(\"a\", 1))\n" + " ```\n" + "\n" + " ```gleam\n" + " try(Ok(1), fn(_) { Error(\"Oh no\") })\n" + " // -> Error(\"Oh no\")\n" + " ```\n" + "\n" + " ```gleam\n" + " try(Error(Nil), fn(x) { Ok(x + 1) })\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec 'try'({ok, CMY} | {error, CMZ}, fun((CMY) -> {ok, CNC} | {error, CMZ})) -> {ok, + CNC} | + {error, CMZ}. +'try'(Result, Fun) -> + case Result of + {ok, X} -> + Fun(X); + + {error, E} -> + {error, E} + end. + +-file("src/gleam/result.gleam", 169). +-spec then({ok, CNH} | {error, CNI}, fun((CNH) -> {ok, CNL} | {error, CNI})) -> {ok, + CNL} | + {error, CNI}. +then(Result, Fun) -> + 'try'(Result, Fun). + +-file("src/gleam/result.gleam", 191). +?DOC( + " Extracts the `Ok` value from a result, returning a default value if the result\n" + " is an `Error`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " unwrap(Ok(1), 0)\n" + " // -> 1\n" + " ```\n" + "\n" + " ```gleam\n" + " unwrap(Error(\"\"), 0)\n" + " // -> 0\n" + " ```\n" +). +-spec unwrap({ok, CNQ} | {error, any()}, CNQ) -> CNQ. +unwrap(Result, Default) -> + case Result of + {ok, V} -> + V; + + {error, _} -> + Default + end. + +-file("src/gleam/result.gleam", 213). +?DOC( + " Extracts the `Ok` value from a result, evaluating the default function if the result\n" + " is an `Error`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " lazy_unwrap(Ok(1), fn() { 0 })\n" + " // -> 1\n" + " ```\n" + "\n" + " ```gleam\n" + " lazy_unwrap(Error(\"\"), fn() { 0 })\n" + " // -> 0\n" + " ```\n" +). +-spec lazy_unwrap({ok, CNU} | {error, any()}, fun(() -> CNU)) -> CNU. +lazy_unwrap(Result, Default) -> + case Result of + {ok, V} -> + V; + + {error, _} -> + Default() + end. + +-file("src/gleam/result.gleam", 235). +?DOC( + " Extracts the `Error` value from a result, returning a default value if the result\n" + " is an `Ok`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " unwrap_error(Error(1), 0)\n" + " // -> 1\n" + " ```\n" + "\n" + " ```gleam\n" + " unwrap_error(Ok(\"\"), 0)\n" + " // -> 0\n" + " ```\n" +). +-spec unwrap_error({ok, any()} | {error, CNZ}, CNZ) -> CNZ. +unwrap_error(Result, Default) -> + case Result of + {ok, _} -> + Default; + + {error, E} -> + E + end. + +-file("src/gleam/result.gleam", 243). +-spec unwrap_both({ok, COC} | {error, COC}) -> COC. +unwrap_both(Result) -> + case Result of + {ok, A} -> + A; + + {error, A@1} -> + A@1 + end. + +-file("src/gleam/result.gleam", 274). +?DOC( + " Returns the first value if it is `Ok`, otherwise returns the second value.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " or(Ok(1), Ok(2))\n" + " // -> Ok(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " or(Ok(1), Error(\"Error 2\"))\n" + " // -> Ok(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " or(Error(\"Error 1\"), Ok(2))\n" + " // -> Ok(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " or(Error(\"Error 1\"), Error(\"Error 2\"))\n" + " // -> Error(\"Error 2\")\n" + " ```\n" +). +-spec 'or'({ok, COF} | {error, COG}, {ok, COF} | {error, COG}) -> {ok, COF} | + {error, COG}. +'or'(First, Second) -> + case First of + {ok, _} -> + First; + + {error, _} -> + Second + end. + +-file("src/gleam/result.gleam", 307). +?DOC( + " Returns the first value if it is `Ok`, otherwise evaluates the given function for a fallback value.\n" + "\n" + " If you need access to the initial error value, use `result.try_recover`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " lazy_or(Ok(1), fn() { Ok(2) })\n" + " // -> Ok(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " lazy_or(Ok(1), fn() { Error(\"Error 2\") })\n" + " // -> Ok(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " lazy_or(Error(\"Error 1\"), fn() { Ok(2) })\n" + " // -> Ok(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " lazy_or(Error(\"Error 1\"), fn() { Error(\"Error 2\") })\n" + " // -> Error(\"Error 2\")\n" + " ```\n" +). +-spec lazy_or({ok, CON} | {error, COO}, fun(() -> {ok, CON} | {error, COO})) -> {ok, + CON} | + {error, COO}. +lazy_or(First, Second) -> + case First of + {ok, _} -> + First; + + {error, _} -> + Second() + end. + +-file("src/gleam/result.gleam", 333). +?DOC( + " Combines a list of results into a single result.\n" + " If all elements in the list are `Ok` then returns an `Ok` holding the list of values.\n" + " If any element is `Error` then returns the first error.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " all([Ok(1), Ok(2)])\n" + " // -> Ok([1, 2])\n" + " ```\n" + "\n" + " ```gleam\n" + " all([Ok(1), Error(\"e\")])\n" + " // -> Error(\"e\")\n" + " ```\n" +). +-spec all(list({ok, COV} | {error, COW})) -> {ok, list(COV)} | {error, COW}. +all(Results) -> + gleam@list:try_map(Results, fun(Result) -> Result end). + +-file("src/gleam/result.gleam", 353). +-spec partition_loop(list({ok, CPK} | {error, CPL}), list(CPK), list(CPL)) -> {list(CPK), + list(CPL)}. +partition_loop(Results, Oks, Errors) -> + case Results of + [] -> + {Oks, Errors}; + + [{ok, A} | Rest] -> + partition_loop(Rest, [A | Oks], Errors); + + [{error, E} | Rest@1] -> + partition_loop(Rest@1, Oks, [E | Errors]) + end. + +-file("src/gleam/result.gleam", 349). +?DOC( + " Given a list of results, returns a pair where the first element is a list\n" + " of all the values inside `Ok` and the second element is a list with all the\n" + " values inside `Error`. The values in both lists appear in reverse order with\n" + " respect to their position in the original list of results.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " partition([Ok(1), Error(\"a\"), Error(\"b\"), Ok(2)])\n" + " // -> #([2, 1], [\"b\", \"a\"])\n" + " ```\n" +). +-spec partition(list({ok, CPD} | {error, CPE})) -> {list(CPD), list(CPE)}. +partition(Results) -> + partition_loop(Results, [], []). + +-file("src/gleam/result.gleam", 375). +?DOC( + " Replace the value within a result\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " replace(Ok(1), Nil)\n" + " // -> Ok(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " replace(Error(1), Nil)\n" + " // -> Error(1)\n" + " ```\n" +). +-spec replace({ok, any()} | {error, CPT}, CPW) -> {ok, CPW} | {error, CPT}. +replace(Result, Value) -> + case Result of + {ok, _} -> + {ok, Value}; + + {error, Error} -> + {error, Error} + end. + +-file("src/gleam/result.gleam", 396). +?DOC( + " Replace the error within a result\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " replace_error(Error(1), Nil)\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " replace_error(Ok(1), Nil)\n" + " // -> Ok(1)\n" + " ```\n" +). +-spec replace_error({ok, CPZ} | {error, any()}, CQD) -> {ok, CPZ} | {error, CQD}. +replace_error(Result, Error) -> + case Result of + {ok, X} -> + {ok, X}; + + {error, _} -> + {error, Error} + end. + +-file("src/gleam/result.gleam", 412). +?DOC( + " Given a list of results, returns only the values inside `Ok`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " values([Ok(1), Error(\"a\"), Ok(3)])\n" + " // -> [1, 3]\n" + " ```\n" +). +-spec values(list({ok, CQG} | {error, any()})) -> list(CQG). +values(Results) -> + gleam@list:filter_map(Results, fun(Result) -> Result end). + +-file("src/gleam/result.gleam", 445). +?DOC( + " Updates a value held within the `Error` of a result by calling a given function\n" + " on it, where the given function also returns a result. The two results are\n" + " then merged together into one result.\n" + "\n" + " If the result is an `Ok` rather than `Error` the function is not called and the\n" + " result stays the same.\n" + "\n" + " This function is useful for chaining together computations that may fail\n" + " and trying to recover from possible errors.\n" + "\n" + " If you do not need access to the initial error value, use `result.lazy_or`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " Ok(1) |> try_recover(with: fn(_) { Error(\"failed to recover\") })\n" + " // -> Ok(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " Error(1) |> try_recover(with: fn(error) { Ok(error + 1) })\n" + " // -> Ok(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " Error(1) |> try_recover(with: fn(error) { Error(\"failed to recover\") })\n" + " // -> Error(\"failed to recover\")\n" + " ```\n" +). +-spec try_recover( + {ok, CQM} | {error, CQN}, + fun((CQN) -> {ok, CQM} | {error, CQQ}) +) -> {ok, CQM} | {error, CQQ}. +try_recover(Result, Fun) -> + case Result of + {ok, Value} -> + {ok, Value}; + + {error, Error} -> + Fun(Error) + end. diff --git a/build/dev/javascript/gleam_stdlib/gleam@set.erl b/build/dev/javascript/gleam_stdlib/gleam@set.erl new file mode 100644 index 0000000..bb3c417 --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam@set.erl @@ -0,0 +1,429 @@ +-module(gleam@set). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/set.gleam"). +-export([new/0, size/1, is_empty/1, contains/2, delete/2, to_list/1, fold/3, filter/2, drop/2, take/2, intersection/2, difference/2, is_subset/2, is_disjoint/2, each/2, insert/2, from_list/1, map/2, union/2, symmetric_difference/2]). +-export_type([set/1]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +-opaque set(CVL) :: {set, gleam@dict:dict(CVL, list(nil))}. + +-file("src/gleam/set.gleam", 32). +?DOC(" Creates a new empty set.\n"). +-spec new() -> set(any()). +new() -> + {set, maps:new()}. + +-file("src/gleam/set.gleam", 50). +?DOC( + " Gets the number of members in a set.\n" + "\n" + " This function runs in constant time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " new()\n" + " |> insert(1)\n" + " |> insert(2)\n" + " |> size\n" + " // -> 2\n" + " ```\n" +). +-spec size(set(any())) -> integer(). +size(Set) -> + maps:size(erlang:element(2, Set)). + +-file("src/gleam/set.gleam", 68). +?DOC( + " Determines whether or not the set is empty.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " new() |> is_empty\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " new() |> insert(1) |> is_empty\n" + " // -> False\n" + " ```\n" +). +-spec is_empty(set(any())) -> boolean(). +is_empty(Set) -> + Set =:= new(). + +-file("src/gleam/set.gleam", 110). +?DOC( + " Checks whether a set contains a given member.\n" + "\n" + " This function runs in logarithmic time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " new()\n" + " |> insert(2)\n" + " |> contains(2)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " new()\n" + " |> insert(2)\n" + " |> contains(1)\n" + " // -> False\n" + " ```\n" +). +-spec contains(set(CVW), CVW) -> boolean(). +contains(Set, Member) -> + _pipe = erlang:element(2, Set), + _pipe@1 = gleam_stdlib:map_get(_pipe, Member), + gleam@result:is_ok(_pipe@1). + +-file("src/gleam/set.gleam", 131). +?DOC( + " Removes a member from a set. If the set does not contain the member then\n" + " the set is returned unchanged.\n" + "\n" + " This function runs in logarithmic time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " new()\n" + " |> insert(2)\n" + " |> delete(2)\n" + " |> contains(1)\n" + " // -> False\n" + " ```\n" +). +-spec delete(set(CVY), CVY) -> set(CVY). +delete(Set, Member) -> + {set, gleam@dict:delete(erlang:element(2, Set), Member)}. + +-file("src/gleam/set.gleam", 149). +?DOC( + " Converts the set into a list of the contained members.\n" + "\n" + " The list has no specific ordering, any unintentional ordering may change in\n" + " future versions of Gleam or Erlang.\n" + "\n" + " This function runs in linear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " new() |> insert(2) |> to_list\n" + " // -> [2]\n" + " ```\n" +). +-spec to_list(set(CWB)) -> list(CWB). +to_list(Set) -> + maps:keys(erlang:element(2, Set)). + +-file("src/gleam/set.gleam", 190). +?DOC( + " Combines all entries into a single value by calling a given function on each\n" + " one.\n" + "\n" + " Sets are not ordered so the values are not returned in any specific order.\n" + " Do not write code that relies on the order entries are used by this\n" + " function as it may change in later versions of Gleam or Erlang.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " from_list([1, 3, 9])\n" + " |> fold(0, fn(accumulator, member) { accumulator + member })\n" + " // -> 13\n" + " ```\n" +). +-spec fold(set(CWH), CWJ, fun((CWJ, CWH) -> CWJ)) -> CWJ. +fold(Set, Initial, Reducer) -> + gleam@dict:fold( + erlang:element(2, Set), + Initial, + fun(A, K, _) -> Reducer(A, K) end + ). + +-file("src/gleam/set.gleam", 214). +?DOC( + " Creates a new set from an existing set, minus any members that a given\n" + " function returns `False` for.\n" + "\n" + " This function runs in loglinear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " import gleam/int\n" + "\n" + " from_list([1, 4, 6, 3, 675, 44, 67])\n" + " |> filter(keeping: int.is_even)\n" + " |> to_list\n" + " // -> [4, 6, 44]\n" + " ```\n" +). +-spec filter(set(CWK), fun((CWK) -> boolean())) -> set(CWK). +filter(Set, Predicate) -> + {set, + gleam@dict:filter(erlang:element(2, Set), fun(M, _) -> Predicate(M) end)}. + +-file("src/gleam/set.gleam", 249). +?DOC( + " Creates a new set from a given set with all the same entries except any\n" + " entry found on the given list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " from_list([1, 2, 3, 4])\n" + " |> drop([1, 3])\n" + " |> to_list\n" + " // -> [2, 4]\n" + " ```\n" +). +-spec drop(set(CWR), list(CWR)) -> set(CWR). +drop(Set, Disallowed) -> + gleam@list:fold(Disallowed, Set, fun delete/2). + +-file("src/gleam/set.gleam", 267). +?DOC( + " Creates a new set from a given set, only including any members which are in\n" + " a given list.\n" + "\n" + " This function runs in loglinear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " from_list([1, 2, 3])\n" + " |> take([1, 3, 5])\n" + " |> to_list\n" + " // -> [1, 3]\n" + " ```\n" +). +-spec take(set(CWV), list(CWV)) -> set(CWV). +take(Set, Desired) -> + {set, gleam@dict:take(erlang:element(2, Set), Desired)}. + +-file("src/gleam/set.gleam", 287). +-spec order(set(CXD), set(CXD)) -> {set(CXD), set(CXD)}. +order(First, Second) -> + case maps:size(erlang:element(2, First)) > maps:size( + erlang:element(2, Second) + ) of + true -> + {First, Second}; + + false -> + {Second, First} + end. + +-file("src/gleam/set.gleam", 305). +?DOC( + " Creates a new set that contains members that are present in both given sets.\n" + "\n" + " This function runs in loglinear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " intersection(from_list([1, 2]), from_list([2, 3])) |> to_list\n" + " // -> [2]\n" + " ```\n" +). +-spec intersection(set(CXI), set(CXI)) -> set(CXI). +intersection(First, Second) -> + {Larger, Smaller} = order(First, Second), + take(Larger, to_list(Smaller)). + +-file("src/gleam/set.gleam", 323). +?DOC( + " Creates a new set that contains members that are present in the first set\n" + " but not the second.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " difference(from_list([1, 2]), from_list([2, 3, 4])) |> to_list\n" + " // -> [1]\n" + " ```\n" +). +-spec difference(set(CXM), set(CXM)) -> set(CXM). +difference(First, Second) -> + drop(First, to_list(Second)). + +-file("src/gleam/set.gleam", 344). +?DOC( + " Determines if a set is fully contained by another.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " is_subset(from_list([1]), from_list([1, 2]))\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " is_subset(from_list([1, 2, 3]), from_list([3, 4, 5]))\n" + " // -> False\n" + " ```\n" +). +-spec is_subset(set(CXQ), set(CXQ)) -> boolean(). +is_subset(First, Second) -> + intersection(First, Second) =:= First. + +-file("src/gleam/set.gleam", 362). +?DOC( + " Determines if two sets contain no common members\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " is_disjoint(from_list([1, 2, 3]), from_list([4, 5, 6]))\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " is_disjoint(from_list([1, 2, 3]), from_list([3, 4, 5]))\n" + " // -> False\n" + " ```\n" +). +-spec is_disjoint(set(CXT), set(CXT)) -> boolean(). +is_disjoint(First, Second) -> + intersection(First, Second) =:= new(). + +-file("src/gleam/set.gleam", 402). +?DOC( + " Calls a function for each member in a set, discarding the return\n" + " value.\n" + "\n" + " Useful for producing a side effect for every item of a set.\n" + "\n" + " ```gleam\n" + " let set = from_list([\"apple\", \"banana\", \"cherry\"])\n" + "\n" + " each(set, io.println)\n" + " // -> Nil\n" + " // apple\n" + " // banana\n" + " // cherry\n" + " ```\n" + "\n" + " The order of elements in the iteration is an implementation detail that\n" + " should not be relied upon.\n" +). +-spec each(set(CYA), fun((CYA) -> any())) -> nil. +each(Set, Fun) -> + fold( + Set, + nil, + fun(Nil, Member) -> + Fun(Member), + Nil + end + ). + +-file("src/gleam/set.gleam", 86). +?DOC( + " Inserts an member into the set.\n" + "\n" + " This function runs in logarithmic time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " new()\n" + " |> insert(1)\n" + " |> insert(2)\n" + " |> size\n" + " // -> 2\n" + " ```\n" +). +-spec insert(set(CVT), CVT) -> set(CVT). +insert(Set, Member) -> + {set, gleam@dict:insert(erlang:element(2, Set), Member, [])}. + +-file("src/gleam/set.gleam", 167). +?DOC( + " Creates a new set of the members in a given list.\n" + "\n" + " This function runs in loglinear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " import gleam/int\n" + " import gleam/list\n" + "\n" + " [1, 1, 2, 4, 3, 2] |> from_list |> to_list |> list.sort(by: int.compare)\n" + " // -> [1, 2, 3, 4]\n" + " ```\n" +). +-spec from_list(list(CWE)) -> set(CWE). +from_list(Members) -> + Dict = gleam@list:fold( + Members, + maps:new(), + fun(M, K) -> gleam@dict:insert(M, K, []) end + ), + {set, Dict}. + +-file("src/gleam/set.gleam", 232). +?DOC( + " Creates a new set from a given set with the result of applying the given\n" + " function to each member.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " from_list([1, 2, 3, 4])\n" + " |> map(with: fn(x) { x * 2 })\n" + " |> to_list\n" + " // -> [2, 4, 6, 8]\n" + " ```\n" +). +-spec map(set(CWN), fun((CWN) -> CWP)) -> set(CWP). +map(Set, Fun) -> + fold(Set, new(), fun(Acc, Member) -> insert(Acc, Fun(Member)) end). + +-file("src/gleam/set.gleam", 282). +?DOC( + " Creates a new set that contains all members of both given sets.\n" + "\n" + " This function runs in loglinear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " union(from_list([1, 2]), from_list([2, 3])) |> to_list\n" + " // -> [1, 2, 3]\n" + " ```\n" +). +-spec union(set(CWZ), set(CWZ)) -> set(CWZ). +union(First, Second) -> + {Larger, Smaller} = order(First, Second), + fold(Smaller, Larger, fun insert/2). + +-file("src/gleam/set.gleam", 374). +?DOC( + " Creates a new set that contains members that are present in either set, but\n" + " not both.\n" + "\n" + " ```gleam\n" + " symmetric_difference(from_list([1, 2, 3]), from_list([3, 4])) |> to_list\n" + " // -> [1, 2, 4]\n" + " ```\n" +). +-spec symmetric_difference(set(CXW), set(CXW)) -> set(CXW). +symmetric_difference(First, Second) -> + difference(union(First, Second), intersection(First, Second)). diff --git a/build/dev/javascript/gleam_stdlib/gleam@string.erl b/build/dev/javascript/gleam_stdlib/gleam@string.erl new file mode 100644 index 0000000..63006b8 --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam@string.erl @@ -0,0 +1,1012 @@ +-module(gleam@string). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/string.gleam"). +-export([is_empty/1, length/1, reverse/1, replace/3, lowercase/1, uppercase/1, compare/2, slice/3, crop/2, drop_end/2, contains/2, starts_with/2, ends_with/2, split_once/2, append/2, concat/1, repeat/2, join/2, pad_start/3, pad_end/3, trim_start/1, trim_end/1, trim/1, pop_grapheme/1, to_graphemes/1, split/2, to_utf_codepoints/1, from_utf_codepoints/1, utf_codepoint/1, utf_codepoint_to_int/1, to_option/1, first/1, last/1, capitalise/1, inspect/1, byte_size/1, drop_start/2]). +-export_type([direction/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC( + " Strings in Gleam are UTF-8 binaries. They can be written in your code as\n" + " text surrounded by `\"double quotes\"`.\n" +). + +-type direction() :: leading | trailing. + +-file("src/gleam/string.gleam", 23). +?DOC( + " Determines if a `String` is empty.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " is_empty(\"\")\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " is_empty(\"the world\")\n" + " // -> False\n" + " ```\n" +). +-spec is_empty(binary()) -> boolean(). +is_empty(Str) -> + Str =:= <<""/utf8>>. + +-file("src/gleam/string.gleam", 51). +?DOC( + " Gets the number of grapheme clusters in a given `String`.\n" + "\n" + " This function has to iterate across the whole string to count the number of\n" + " graphemes, so it runs in linear time. Avoid using this in a loop.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " length(\"Gleam\")\n" + " // -> 5\n" + " ```\n" + "\n" + " ```gleam\n" + " length(\"ß↑e̊\")\n" + " // -> 3\n" + " ```\n" + "\n" + " ```gleam\n" + " length(\"\")\n" + " // -> 0\n" + " ```\n" +). +-spec length(binary()) -> integer(). +length(String) -> + string:length(String). + +-file("src/gleam/string.gleam", 65). +?DOC( + " Reverses a `String`.\n" + "\n" + " This function has to iterate across the whole `String` so it runs in linear\n" + " time. Avoid using this in a loop.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " reverse(\"stressed\")\n" + " // -> \"desserts\"\n" + " ```\n" +). +-spec reverse(binary()) -> binary(). +reverse(String) -> + _pipe = String, + _pipe@1 = gleam_stdlib:identity(_pipe), + _pipe@2 = string:reverse(_pipe@1), + unicode:characters_to_binary(_pipe@2). + +-file("src/gleam/string.gleam", 86). +?DOC( + " Creates a new `String` by replacing all occurrences of a given substring.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " replace(\"www.example.com\", each: \".\", with: \"-\")\n" + " // -> \"www-example-com\"\n" + " ```\n" + "\n" + " ```gleam\n" + " replace(\"a,b,c,d,e\", each: \",\", with: \"/\")\n" + " // -> \"a/b/c/d/e\"\n" + " ```\n" +). +-spec replace(binary(), binary(), binary()) -> binary(). +replace(String, Pattern, Substitute) -> + _pipe = String, + _pipe@1 = gleam_stdlib:identity(_pipe), + _pipe@2 = gleam_stdlib:string_replace(_pipe@1, Pattern, Substitute), + unicode:characters_to_binary(_pipe@2). + +-file("src/gleam/string.gleam", 111). +?DOC( + " Creates a new `String` with all the graphemes in the input `String` converted to\n" + " lowercase.\n" + "\n" + " Useful for case-insensitive comparisons.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " lowercase(\"X-FILES\")\n" + " // -> \"x-files\"\n" + " ```\n" +). +-spec lowercase(binary()) -> binary(). +lowercase(String) -> + string:lowercase(String). + +-file("src/gleam/string.gleam", 127). +?DOC( + " Creates a new `String` with all the graphemes in the input `String` converted to\n" + " uppercase.\n" + "\n" + " Useful for case-insensitive comparisons and VIRTUAL YELLING.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " uppercase(\"skinner\")\n" + " // -> \"SKINNER\"\n" + " ```\n" +). +-spec uppercase(binary()) -> binary(). +uppercase(String) -> + string:uppercase(String). + +-file("src/gleam/string.gleam", 145). +?DOC( + " Compares two `String`s to see which is \"larger\" by comparing their graphemes.\n" + "\n" + " This does not compare the size or length of the given `String`s.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " compare(\"Anthony\", \"Anthony\")\n" + " // -> order.Eq\n" + " ```\n" + "\n" + " ```gleam\n" + " compare(\"A\", \"B\")\n" + " // -> order.Lt\n" + " ```\n" +). +-spec compare(binary(), binary()) -> gleam@order:order(). +compare(A, B) -> + case A =:= B of + true -> + eq; + + _ -> + case gleam_stdlib:less_than(A, B) of + true -> + lt; + + false -> + gt + end + end. + +-file("src/gleam/string.gleam", 194). +?DOC( + " Takes a substring given a start grapheme index and a length. Negative indexes\n" + " are taken starting from the *end* of the list.\n" + "\n" + " This function runs in linear time with the size of the index and the\n" + " length. Negative indexes are linear with the size of the input string in\n" + " addition to the other costs.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " slice(from: \"gleam\", at_index: 1, length: 2)\n" + " // -> \"le\"\n" + " ```\n" + "\n" + " ```gleam\n" + " slice(from: \"gleam\", at_index: 1, length: 10)\n" + " // -> \"leam\"\n" + " ```\n" + "\n" + " ```gleam\n" + " slice(from: \"gleam\", at_index: 10, length: 3)\n" + " // -> \"\"\n" + " ```\n" + "\n" + " ```gleam\n" + " slice(from: \"gleam\", at_index: -2, length: 2)\n" + " // -> \"am\"\n" + " ```\n" + "\n" + " ```gleam\n" + " slice(from: \"gleam\", at_index: -12, length: 2)\n" + " // -> \"\"\n" + " ```\n" +). +-spec slice(binary(), integer(), integer()) -> binary(). +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 -> + gleam_stdlib:slice(String, Translated_idx, Len) + end; + + false -> + gleam_stdlib:slice(String, Idx, Len) + end + end. + +-file("src/gleam/string.gleam", 232). +?DOC( + " Drops contents of the first `String` that occur before the second `String`.\n" + " If the `from` string does not contain the `before` string, `from` is\n" + " returned unchanged.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " crop(from: \"The Lone Gunmen\", before: \"Lone\")\n" + " // -> \"Lone Gunmen\"\n" + " ```\n" +). +-spec crop(binary(), binary()) -> binary(). +crop(String, Substring) -> + gleam_stdlib:crop_string(String, Substring). + +-file("src/gleam/string.gleam", 268). +?DOC( + " Drops *n* graphemes from the end of a `String`.\n" + "\n" + " This function traverses the full string, so it runs in linear time with the\n" + " size of the string. Avoid using this in a loop.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " drop_end(from: \"Cigarette Smoking Man\", up_to: 2)\n" + " // -> \"Cigarette Smoking M\"\n" + " ```\n" +). +-spec drop_end(binary(), integer()) -> binary(). +drop_end(String, Num_graphemes) -> + case Num_graphemes =< 0 of + true -> + String; + + false -> + slice(String, 0, string:length(String) - Num_graphemes) + end. + +-file("src/gleam/string.gleam", 296). +?DOC( + " Checks if the first `String` contains the second.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " contains(does: \"theory\", contain: \"ory\")\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " contains(does: \"theory\", contain: \"the\")\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " contains(does: \"theory\", contain: \"THE\")\n" + " // -> False\n" + " ```\n" +). +-spec contains(binary(), binary()) -> boolean(). +contains(Haystack, Needle) -> + gleam_stdlib:contains_string(Haystack, Needle). + +-file("src/gleam/string.gleam", 309). +?DOC( + " Checks whether the first `String` starts with the second one.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " starts_with(\"theory\", \"ory\")\n" + " // -> False\n" + " ```\n" +). +-spec starts_with(binary(), binary()) -> boolean(). +starts_with(String, Prefix) -> + gleam_stdlib:string_starts_with(String, Prefix). + +-file("src/gleam/string.gleam", 322). +?DOC( + " Checks whether the first `String` ends with the second one.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " ends_with(\"theory\", \"ory\")\n" + " // -> True\n" + " ```\n" +). +-spec ends_with(binary(), binary()) -> boolean(). +ends_with(String, Suffix) -> + gleam_stdlib:string_ends_with(String, Suffix). + +-file("src/gleam/string.gleam", 361). +?DOC( + " Splits a `String` a single time on the given substring.\n" + "\n" + " Returns an `Error` if substring not present.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " split_once(\"home/gleam/desktop/\", on: \"/\")\n" + " // -> Ok(#(\"home\", \"gleam/desktop/\"))\n" + " ```\n" + "\n" + " ```gleam\n" + " split_once(\"home/gleam/desktop/\", on: \"?\")\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec split_once(binary(), binary()) -> {ok, {binary(), binary()}} | + {error, nil}. +split_once(String, Substring) -> + case string:split(String, Substring) of + [First, Rest] -> + {ok, {First, Rest}}; + + _ -> + {error, nil} + end. + +-file("src/gleam/string.gleam", 392). +?DOC( + " Creates a new `String` by joining two `String`s together.\n" + "\n" + " This function typically copies both `String`s and runs in linear time, but\n" + " the exact behaviour will depend on how the runtime you are using optimises\n" + " your code. Benchmark and profile your code if you need to understand its\n" + " performance better.\n" + "\n" + " If you are joining together large string and want to avoid copying any data\n" + " you may want to investigate using the [`string_tree`](../gleam/string_tree.html)\n" + " module.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " append(to: \"butter\", suffix: \"fly\")\n" + " // -> \"butterfly\"\n" + " ```\n" +). +-spec append(binary(), binary()) -> binary(). +append(First, Second) -> + <>. + +-file("src/gleam/string.gleam", 412). +-spec concat_loop(list(binary()), binary()) -> binary(). +concat_loop(Strings, Accumulator) -> + case Strings of + [String | Strings@1] -> + concat_loop(Strings@1, <>); + + [] -> + Accumulator + end. + +-file("src/gleam/string.gleam", 408). +?DOC( + " Creates a new `String` by joining many `String`s together.\n" + "\n" + " This function copies all the `String`s and runs in linear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " concat([\"never\", \"the\", \"less\"])\n" + " // -> \"nevertheless\"\n" + " ```\n" +). +-spec concat(list(binary())) -> binary(). +concat(Strings) -> + erlang:list_to_binary(Strings). + +-file("src/gleam/string.gleam", 437). +-spec repeat_loop(integer(), binary(), binary()) -> binary(). +repeat_loop(Times, Doubling_acc, Acc) -> + Acc@1 = case Times rem 2 of + 0 -> + Acc; + + _ -> + <> + end, + Times@1 = Times div 2, + case Times@1 =< 0 of + true -> + Acc@1; + + false -> + repeat_loop( + Times@1, + <>, + Acc@1 + ) + end. + +-file("src/gleam/string.gleam", 430). +?DOC( + " Creates a new `String` by repeating a `String` a given number of times.\n" + "\n" + " This function runs in loglinear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " repeat(\"ha\", times: 3)\n" + " // -> \"hahaha\"\n" + " ```\n" +). +-spec repeat(binary(), integer()) -> binary(). +repeat(String, Times) -> + case Times =< 0 of + true -> + <<""/utf8>>; + + false -> + repeat_loop(Times, String, <<""/utf8>>) + end. + +-file("src/gleam/string.gleam", 467). +-spec join_loop(list(binary()), binary(), binary()) -> binary(). +join_loop(Strings, Separator, Accumulator) -> + case Strings of + [] -> + Accumulator; + + [String | Strings@1] -> + join_loop( + Strings@1, + Separator, + <<<>/binary, + String/binary>> + ) + end. + +-file("src/gleam/string.gleam", 460). +?DOC( + " Joins many `String`s together with a given separator.\n" + "\n" + " This function runs in linear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " join([\"home\",\"evan\",\"Desktop\"], with: \"/\")\n" + " // -> \"home/evan/Desktop\"\n" + " ```\n" +). +-spec join(list(binary()), binary()) -> binary(). +join(Strings, Separator) -> + case Strings of + [] -> + <<""/utf8>>; + + [First | Rest] -> + join_loop(Rest, Separator, First) + end. + +-file("src/gleam/string.gleam", 545). +-spec padding(integer(), binary()) -> binary(). +padding(Size, Pad_string) -> + Pad_string_length = string:length(Pad_string), + Num_pads = case Pad_string_length of + 0 -> 0; + Gleam@denominator -> Size div Gleam@denominator + end, + Extra = case Pad_string_length of + 0 -> 0; + Gleam@denominator@1 -> Size rem Gleam@denominator@1 + end, + <<(repeat(Pad_string, Num_pads))/binary, + (slice(Pad_string, 0, Extra))/binary>>. + +-file("src/gleam/string.gleam", 498). +?DOC( + " Pads the start of a `String` until it has a given length.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " pad_start(\"121\", to: 5, with: \".\")\n" + " // -> \"..121\"\n" + " ```\n" + "\n" + " ```gleam\n" + " pad_start(\"121\", to: 3, with: \".\")\n" + " // -> \"121\"\n" + " ```\n" + "\n" + " ```gleam\n" + " pad_start(\"121\", to: 2, with: \".\")\n" + " // -> \"121\"\n" + " ```\n" +). +-spec pad_start(binary(), integer(), binary()) -> binary(). +pad_start(String, Desired_length, Pad_string) -> + Current_length = string:length(String), + To_pad_length = Desired_length - Current_length, + case To_pad_length =< 0 of + true -> + String; + + false -> + <<(padding(To_pad_length, Pad_string))/binary, String/binary>> + end. + +-file("src/gleam/string.gleam", 531). +?DOC( + " Pads the end of a `String` until it has a given length.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " pad_end(\"123\", to: 5, with: \".\")\n" + " // -> \"123..\"\n" + " ```\n" + "\n" + " ```gleam\n" + " pad_end(\"123\", to: 3, with: \".\")\n" + " // -> \"123\"\n" + " ```\n" + "\n" + " ```gleam\n" + " pad_end(\"123\", to: 2, with: \".\")\n" + " // -> \"123\"\n" + " ```\n" +). +-spec pad_end(binary(), integer(), binary()) -> binary(). +pad_end(String, Desired_length, Pad_string) -> + Current_length = string:length(String), + To_pad_length = Desired_length - Current_length, + case To_pad_length =< 0 of + true -> + String; + + false -> + <> + end. + +-file("src/gleam/string.gleam", 589). +?DOC( + " Removes whitespace at the start of a `String`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " trim_start(\" hats \\n\")\n" + " // -> \"hats \\n\"\n" + " ```\n" +). +-spec trim_start(binary()) -> binary(). +trim_start(String) -> + string:trim(String, leading). + +-file("src/gleam/string.gleam", 603). +?DOC( + " Removes whitespace at the end of a `String`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " trim_end(\" hats \\n\")\n" + " // -> \" hats\"\n" + " ```\n" +). +-spec trim_end(binary()) -> binary(). +trim_end(String) -> + string:trim(String, trailing). + +-file("src/gleam/string.gleam", 567). +?DOC( + " Removes whitespace on both sides of a `String`.\n" + "\n" + " Whitespace in this function is the set of nonbreakable whitespace\n" + " codepoints, defined as Pattern_White_Space in [Unicode Standard Annex #31][1].\n" + "\n" + " [1]: https://unicode.org/reports/tr31/\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " trim(\" hats \\n\")\n" + " // -> \"hats\"\n" + " ```\n" +). +-spec trim(binary()) -> binary(). +trim(String) -> + _pipe = String, + _pipe@1 = trim_start(_pipe), + trim_end(_pipe@1). + +-file("src/gleam/string.gleam", 630). +?DOC( + " Splits a non-empty `String` into its first element (head) and rest (tail).\n" + " This lets you pattern match on `String`s exactly as you would with lists.\n" + "\n" + " ## Performance\n" + "\n" + " There is a notable overhead to using this function, so you may not want to\n" + " use it in a tight loop. If you wish to efficiently parse a string you may\n" + " want to use alternatives such as the [splitter package](https://hex.pm/packages/splitter).\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " pop_grapheme(\"gleam\")\n" + " // -> Ok(#(\"g\", \"leam\"))\n" + " ```\n" + "\n" + " ```gleam\n" + " pop_grapheme(\"\")\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec pop_grapheme(binary()) -> {ok, {binary(), binary()}} | {error, nil}. +pop_grapheme(String) -> + gleam_stdlib:string_pop_grapheme(String). + +-file("src/gleam/string.gleam", 647). +-spec to_graphemes_loop(binary(), list(binary())) -> list(binary()). +to_graphemes_loop(String, Acc) -> + case gleam_stdlib:string_pop_grapheme(String) of + {ok, {Grapheme, Rest}} -> + to_graphemes_loop(Rest, [Grapheme | Acc]); + + {error, _} -> + Acc + end. + +-file("src/gleam/string.gleam", 641). +?DOC( + " Converts a `String` to a list of\n" + " [graphemes](https://en.wikipedia.org/wiki/Grapheme).\n" + "\n" + " ```gleam\n" + " to_graphemes(\"abc\")\n" + " // -> [\"a\", \"b\", \"c\"]\n" + " ```\n" +). +-spec to_graphemes(binary()) -> list(binary()). +to_graphemes(String) -> + _pipe = String, + _pipe@1 = to_graphemes_loop(_pipe, []), + lists:reverse(_pipe@1). + +-file("src/gleam/string.gleam", 333). +?DOC( + " Creates a list of `String`s by splitting a given string on a given substring.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " split(\"home/gleam/desktop/\", on: \"/\")\n" + " // -> [\"home\", \"gleam\", \"desktop\", \"\"]\n" + " ```\n" +). +-spec split(binary(), binary()) -> list(binary()). +split(X, Substring) -> + case Substring of + <<""/utf8>> -> + to_graphemes(X); + + _ -> + _pipe = X, + _pipe@1 = gleam_stdlib:identity(_pipe), + _pipe@2 = gleam@string_tree:split(_pipe@1, Substring), + gleam@list:map(_pipe@2, fun unicode:characters_to_binary/1) + end. + +-file("src/gleam/string.gleam", 694). +-spec to_utf_codepoints_loop(bitstring(), list(integer())) -> list(integer()). +to_utf_codepoints_loop(Bit_array, Acc) -> + case Bit_array of + <> -> + to_utf_codepoints_loop(Rest, [First | Acc]); + + _ -> + lists:reverse(Acc) + end. + +-file("src/gleam/string.gleam", 689). +-spec do_to_utf_codepoints(binary()) -> list(integer()). +do_to_utf_codepoints(String) -> + to_utf_codepoints_loop(<>, []). + +-file("src/gleam/string.gleam", 684). +?DOC( + " Converts a `String` to a `List` of `UtfCodepoint`.\n" + "\n" + " See and\n" + " for an\n" + " explanation on code points.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " \"a\" |> to_utf_codepoints\n" + " // -> [UtfCodepoint(97)]\n" + " ```\n" + "\n" + " ```gleam\n" + " // Semantically the same as:\n" + " // [\"🏳\", \"️\", \"‍\", \"🌈\"] or:\n" + " // [waving_white_flag, variant_selector_16, zero_width_joiner, rainbow]\n" + " \"🏳️‍🌈\" |> to_utf_codepoints\n" + " // -> [\n" + " // UtfCodepoint(127987),\n" + " // UtfCodepoint(65039),\n" + " // UtfCodepoint(8205),\n" + " // UtfCodepoint(127752),\n" + " // ]\n" + " ```\n" +). +-spec to_utf_codepoints(binary()) -> list(integer()). +to_utf_codepoints(String) -> + do_to_utf_codepoints(String). + +-file("src/gleam/string.gleam", 734). +?DOC( + " Converts a `List` of `UtfCodepoint`s to a `String`.\n" + "\n" + " See and\n" + " for an\n" + " explanation on code points.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let assert Ok(a) = utf_codepoint(97)\n" + " let assert Ok(b) = utf_codepoint(98)\n" + " let assert Ok(c) = utf_codepoint(99)\n" + " from_utf_codepoints([a, b, c])\n" + " // -> \"abc\"\n" + " ```\n" +). +-spec from_utf_codepoints(list(integer())) -> binary(). +from_utf_codepoints(Utf_codepoints) -> + gleam_stdlib:utf_codepoint_list_to_string(Utf_codepoints). + +-file("src/gleam/string.gleam", 740). +?DOC( + " Converts an integer to a `UtfCodepoint`.\n" + "\n" + " Returns an `Error` if the integer does not represent a valid UTF codepoint.\n" +). +-spec utf_codepoint(integer()) -> {ok, integer()} | {error, nil}. +utf_codepoint(Value) -> + case Value of + I when I > 1114111 -> + {error, nil}; + + I@1 when (I@1 >= 55296) andalso (I@1 =< 57343) -> + {error, nil}; + + I@2 when I@2 < 0 -> + {error, nil}; + + I@3 -> + {ok, gleam_stdlib:identity(I@3)} + end. + +-file("src/gleam/string.gleam", 761). +?DOC( + " Converts an UtfCodepoint to its ordinal code point value.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let assert [utf_codepoint, ..] = to_utf_codepoints(\"💜\")\n" + " utf_codepoint_to_int(utf_codepoint)\n" + " // -> 128156\n" + " ```\n" +). +-spec utf_codepoint_to_int(integer()) -> integer(). +utf_codepoint_to_int(Cp) -> + gleam_stdlib:identity(Cp). + +-file("src/gleam/string.gleam", 778). +?DOC( + " Converts a `String` into `Option(String)` where an empty `String` becomes\n" + " `None`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " to_option(\"\")\n" + " // -> None\n" + " ```\n" + "\n" + " ```gleam\n" + " to_option(\"hats\")\n" + " // -> Some(\"hats\")\n" + " ```\n" +). +-spec to_option(binary()) -> gleam@option:option(binary()). +to_option(String) -> + case String of + <<""/utf8>> -> + none; + + _ -> + {some, String} + end. + +-file("src/gleam/string.gleam", 801). +?DOC( + " Returns the first grapheme cluster in a given `String` and wraps it in a\n" + " `Result(String, Nil)`. If the `String` is empty, it returns `Error(Nil)`.\n" + " Otherwise, it returns `Ok(String)`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " first(\"\")\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " first(\"icecream\")\n" + " // -> Ok(\"i\")\n" + " ```\n" +). +-spec first(binary()) -> {ok, binary()} | {error, nil}. +first(String) -> + case gleam_stdlib:string_pop_grapheme(String) of + {ok, {First, _}} -> + {ok, First}; + + {error, E} -> + {error, E} + end. + +-file("src/gleam/string.gleam", 827). +?DOC( + " Returns the last grapheme cluster in a given `String` and wraps it in a\n" + " `Result(String, Nil)`. If the `String` is empty, it returns `Error(Nil)`.\n" + " Otherwise, it returns `Ok(String)`.\n" + "\n" + " This function traverses the full string, so it runs in linear time with the\n" + " length of the string. Avoid using this in a loop.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " last(\"\")\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " last(\"icecream\")\n" + " // -> Ok(\"m\")\n" + " ```\n" +). +-spec last(binary()) -> {ok, binary()} | {error, nil}. +last(String) -> + case gleam_stdlib:string_pop_grapheme(String) of + {ok, {First, <<""/utf8>>}} -> + {ok, First}; + + {ok, {_, Rest}} -> + {ok, slice(Rest, -1, 1)}; + + {error, E} -> + {error, E} + end. + +-file("src/gleam/string.gleam", 845). +?DOC( + " Creates a new `String` with the first grapheme in the input `String`\n" + " converted to uppercase and the remaining graphemes to lowercase.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " capitalise(\"mamouna\")\n" + " // -> \"Mamouna\"\n" + " ```\n" +). +-spec capitalise(binary()) -> binary(). +capitalise(String) -> + case gleam_stdlib:string_pop_grapheme(String) of + {ok, {First, Rest}} -> + append(string:uppercase(First), string:lowercase(Rest)); + + {error, _} -> + <<""/utf8>> + end. + +-file("src/gleam/string.gleam", 876). +?DOC( + " Returns a `String` representation of a term in Gleam syntax.\n" + "\n" + " This may be occasionally useful for quick-and-dirty printing of values in\n" + " scripts. For error reporting and other uses prefer constructing strings by\n" + " pattern matching on the values.\n" + "\n" + " ## Limitations\n" + "\n" + " The output format of this function is not stable and could change at any\n" + " time. The output is not suitable for parsing.\n" + "\n" + " This function works using runtime reflection, so the output may not be\n" + " perfectly accurate for data structures where the runtime structure doesn't\n" + " hold enough information to determine the original syntax. For example,\n" + " tuples with an Erlang atom in the first position will be mistaken for Gleam\n" + " records.\n" + "\n" + " ## Security and safety\n" + "\n" + " There is no limit to how large the strings that this function can produce.\n" + " Be careful not to call this function with large data structures or you\n" + " could use very large amounts of memory, potentially causing runtime\n" + " problems.\n" +). +-spec inspect(any()) -> binary(). +inspect(Term) -> + _pipe = Term, + _pipe@1 = gleam_stdlib:inspect(_pipe), + unicode:characters_to_binary(_pipe@1). + +-file("src/gleam/string.gleam", 900). +?DOC( + " Returns the number of bytes in a `String`.\n" + "\n" + " This function runs in constant time on Erlang and in linear time on\n" + " JavaScript.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " byte_size(\"🏳️‍⚧️🏳️‍🌈👩🏾‍❤️‍👨🏻\")\n" + " // -> 58\n" + " ```\n" +). +-spec byte_size(binary()) -> integer(). +byte_size(String) -> + erlang:byte_size(String). + +-file("src/gleam/string.gleam", 245). +?DOC( + " Drops *n* graphemes from the start of a `String`.\n" + "\n" + " This function runs in linear time with the number of graphemes to drop.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " drop_start(from: \"The Lone Gunmen\", up_to: 2)\n" + " // -> \"e Lone Gunmen\"\n" + " ```\n" +). +-spec drop_start(binary(), integer()) -> binary(). +drop_start(String, Num_graphemes) -> + case Num_graphemes =< 0 of + true -> + String; + + false -> + Prefix = gleam_stdlib:slice(String, 0, Num_graphemes), + Prefix_size = erlang:byte_size(Prefix), + binary:part( + String, + Prefix_size, + erlang:byte_size(String) - Prefix_size + ) + end. diff --git a/build/dev/javascript/gleam_stdlib/gleam@string_tree.erl b/build/dev/javascript/gleam_stdlib/gleam@string_tree.erl new file mode 100644 index 0000000..0d72b4d --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam@string_tree.erl @@ -0,0 +1,207 @@ +-module(gleam@string_tree). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/string_tree.gleam"). +-export([append_tree/2, prepend_tree/2, from_strings/1, new/0, concat/1, from_string/1, prepend/2, append/2, to_string/1, byte_size/1, join/2, lowercase/1, uppercase/1, reverse/1, split/2, replace/3, is_equal/2, is_empty/1]). +-export_type([string_tree/0, direction/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +-type string_tree() :: any(). + +-type direction() :: all. + +-file("src/gleam/string_tree.gleam", 61). +?DOC( + " Appends some `StringTree` onto the end of another.\n" + "\n" + " Runs in constant time.\n" +). +-spec append_tree(string_tree(), string_tree()) -> string_tree(). +append_tree(Tree, Suffix) -> + gleam_stdlib:iodata_append(Tree, Suffix). + +-file("src/gleam/string_tree.gleam", 48). +?DOC( + " Prepends some `StringTree` onto the start of another.\n" + "\n" + " Runs in constant time.\n" +). +-spec prepend_tree(string_tree(), string_tree()) -> string_tree(). +prepend_tree(Tree, Prefix) -> + gleam_stdlib:iodata_append(Prefix, Tree). + +-file("src/gleam/string_tree.gleam", 69). +?DOC( + " Converts a list of strings into a `StringTree`.\n" + "\n" + " Runs in constant time.\n" +). +-spec from_strings(list(binary())) -> string_tree(). +from_strings(Strings) -> + gleam_stdlib:identity(Strings). + +-file("src/gleam/string_tree.gleam", 24). +?DOC( + " Create an empty `StringTree`. Useful as the start of a pipe chaining many\n" + " trees together.\n" +). +-spec new() -> string_tree(). +new() -> + gleam_stdlib:identity([]). + +-file("src/gleam/string_tree.gleam", 77). +?DOC( + " Joins a list of trees into a single tree.\n" + "\n" + " Runs in constant time.\n" +). +-spec concat(list(string_tree())) -> string_tree(). +concat(Trees) -> + gleam_stdlib:identity(Trees). + +-file("src/gleam/string_tree.gleam", 85). +?DOC( + " Converts a string into a `StringTree`.\n" + "\n" + " Runs in constant time.\n" +). +-spec from_string(binary()) -> string_tree(). +from_string(String) -> + gleam_stdlib:identity(String). + +-file("src/gleam/string_tree.gleam", 32). +?DOC( + " Prepends a `String` onto the start of some `StringTree`.\n" + "\n" + " Runs in constant time.\n" +). +-spec prepend(string_tree(), binary()) -> string_tree(). +prepend(Tree, Prefix) -> + gleam_stdlib:iodata_append(gleam_stdlib:identity(Prefix), Tree). + +-file("src/gleam/string_tree.gleam", 40). +?DOC( + " Appends a `String` onto the end of some `StringTree`.\n" + "\n" + " Runs in constant time.\n" +). +-spec append(string_tree(), binary()) -> string_tree(). +append(Tree, Second) -> + gleam_stdlib:iodata_append(Tree, gleam_stdlib:identity(Second)). + +-file("src/gleam/string_tree.gleam", 94). +?DOC( + " Turns a `StringTree` into a `String`\n" + "\n" + " This function is implemented natively by the virtual machine and is highly\n" + " optimised.\n" +). +-spec to_string(string_tree()) -> binary(). +to_string(Tree) -> + unicode:characters_to_binary(Tree). + +-file("src/gleam/string_tree.gleam", 100). +?DOC(" Returns the size of the `StringTree` in bytes.\n"). +-spec byte_size(string_tree()) -> integer(). +byte_size(Tree) -> + erlang:iolist_size(Tree). + +-file("src/gleam/string_tree.gleam", 104). +?DOC(" Joins the given trees into a new tree separated with the given string.\n"). +-spec join(list(string_tree()), binary()) -> string_tree(). +join(Trees, Sep) -> + _pipe = Trees, + _pipe@1 = gleam@list:intersperse(_pipe, gleam_stdlib:identity(Sep)), + gleam_stdlib:identity(_pipe@1). + +-file("src/gleam/string_tree.gleam", 115). +?DOC( + " Converts a `StringTree` to a new one where the contents have been\n" + " lowercased.\n" +). +-spec lowercase(string_tree()) -> string_tree(). +lowercase(Tree) -> + string:lowercase(Tree). + +-file("src/gleam/string_tree.gleam", 122). +?DOC( + " Converts a `StringTree` to a new one where the contents have been\n" + " uppercased.\n" +). +-spec uppercase(string_tree()) -> string_tree(). +uppercase(Tree) -> + string:uppercase(Tree). + +-file("src/gleam/string_tree.gleam", 127). +?DOC(" Converts a `StringTree` to a new one with the contents reversed.\n"). +-spec reverse(string_tree()) -> string_tree(). +reverse(Tree) -> + string:reverse(Tree). + +-file("src/gleam/string_tree.gleam", 145). +?DOC(" Splits a `StringTree` on a given pattern into a list of trees.\n"). +-spec split(string_tree(), binary()) -> list(string_tree()). +split(Tree, Pattern) -> + string:split(Tree, Pattern, all). + +-file("src/gleam/string_tree.gleam", 156). +?DOC(" Replaces all instances of a pattern with a given string substitute.\n"). +-spec replace(string_tree(), binary(), binary()) -> string_tree(). +replace(Tree, Pattern, Substitute) -> + gleam_stdlib:string_replace(Tree, Pattern, Substitute). + +-file("src/gleam/string_tree.gleam", 182). +?DOC( + " Compares two string trees to determine if they have the same textual\n" + " content.\n" + "\n" + " Comparing two string trees using the `==` operator may return `False` even\n" + " if they have the same content as they may have been build in different ways,\n" + " so using this function is often preferred.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " from_strings([\"a\", \"b\"]) == from_string(\"ab\")\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " is_equal(from_strings([\"a\", \"b\"]), from_string(\"ab\"))\n" + " // -> True\n" + " ```\n" +). +-spec is_equal(string_tree(), string_tree()) -> boolean(). +is_equal(A, B) -> + string:equal(A, B). + +-file("src/gleam/string_tree.gleam", 206). +?DOC( + " Inspects a `StringTree` to determine if it is equivalent to an empty string.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " from_string(\"ok\") |> is_empty\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " from_string(\"\") |> is_empty\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " from_strings([]) |> is_empty\n" + " // -> True\n" + " ```\n" +). +-spec is_empty(string_tree()) -> boolean(). +is_empty(Tree) -> + string:is_empty(Tree). diff --git a/build/dev/javascript/gleam_stdlib/gleam@uri.erl b/build/dev/javascript/gleam_stdlib/gleam@uri.erl new file mode 100644 index 0000000..0819463 --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam@uri.erl @@ -0,0 +1,1044 @@ +-module(gleam@uri). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/uri.gleam"). +-export([parse_query/1, percent_encode/1, query_to_string/1, percent_decode/1, path_segments/1, to_string/1, origin/1, merge/2, parse/1]). +-export_type([uri/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC( + " Utilities for working with URIs\n" + "\n" + " This module provides functions for working with URIs (for example, parsing\n" + " URIs or encoding query strings). The functions in this module are implemented\n" + " according to [RFC 3986](https://tools.ietf.org/html/rfc3986).\n" + "\n" + " Query encoding (Form encoding) is defined in the\n" + " [W3C specification](https://www.w3.org/TR/html52/sec-forms.html#urlencoded-form-data).\n" +). + +-type uri() :: {uri, + gleam@option:option(binary()), + gleam@option:option(binary()), + gleam@option:option(binary()), + gleam@option:option(integer()), + binary(), + gleam@option:option(binary()), + gleam@option:option(binary())}. + +-file("src/gleam/uri.gleam", 289). +-spec is_valid_host_within_brackets_char(integer()) -> boolean(). +is_valid_host_within_brackets_char(Char) -> + (((((48 >= Char) andalso (Char =< 57)) orelse ((65 >= Char) andalso (Char =< 90))) + orelse ((97 >= Char) andalso (Char =< 122))) + orelse (Char =:= 58)) + orelse (Char =:= 46). + +-file("src/gleam/uri.gleam", 506). +-spec parse_fragment(binary(), uri()) -> {ok, uri()} | {error, nil}. +parse_fragment(Rest, Pieces) -> + {ok, + {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + {some, Rest}}}. + +-file("src/gleam/uri.gleam", 478). +-spec parse_query_with_question_mark_loop(binary(), binary(), uri(), integer()) -> {ok, + uri()} | + {error, nil}. +parse_query_with_question_mark_loop(Original, Uri_string, Pieces, Size) -> + case Uri_string of + <<"#"/utf8, Rest/binary>> when Size =:= 0 -> + parse_fragment(Rest, Pieces); + + <<"#"/utf8, Rest@1/binary>> -> + Query = binary:part(Original, 0, Size), + Pieces@1 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + erlang:element(6, Pieces), + {some, Query}, + erlang:element(8, Pieces)}, + parse_fragment(Rest@1, Pieces@1); + + <<""/utf8>> -> + {ok, + {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + erlang:element(6, Pieces), + {some, Original}, + erlang:element(8, Pieces)}}; + + _ -> + {_, Rest@2} = gleam_stdlib:string_pop_codeunit(Uri_string), + parse_query_with_question_mark_loop( + Original, + Rest@2, + Pieces, + Size + 1 + ) + end. + +-file("src/gleam/uri.gleam", 471). +-spec parse_query_with_question_mark(binary(), uri()) -> {ok, uri()} | + {error, nil}. +parse_query_with_question_mark(Uri_string, Pieces) -> + parse_query_with_question_mark_loop(Uri_string, Uri_string, Pieces, 0). + +-file("src/gleam/uri.gleam", 437). +-spec parse_path_loop(binary(), binary(), uri(), integer()) -> {ok, uri()} | + {error, nil}. +parse_path_loop(Original, Uri_string, Pieces, Size) -> + case Uri_string of + <<"?"/utf8, Rest/binary>> -> + Path = binary:part(Original, 0, Size), + Pieces@1 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + Path, + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_query_with_question_mark(Rest, Pieces@1); + + <<"#"/utf8, Rest@1/binary>> -> + Path@1 = binary:part(Original, 0, Size), + Pieces@2 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + Path@1, + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_fragment(Rest@1, Pieces@2); + + <<""/utf8>> -> + {ok, + {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + Original, + erlang:element(7, Pieces), + erlang:element(8, Pieces)}}; + + _ -> + {_, Rest@2} = gleam_stdlib:string_pop_codeunit(Uri_string), + parse_path_loop(Original, Rest@2, Pieces, Size + 1) + end. + +-file("src/gleam/uri.gleam", 433). +-spec parse_path(binary(), uri()) -> {ok, uri()} | {error, nil}. +parse_path(Uri_string, Pieces) -> + parse_path_loop(Uri_string, Uri_string, Pieces, 0). + +-file("src/gleam/uri.gleam", 388). +-spec parse_port_loop(binary(), uri(), integer()) -> {ok, uri()} | {error, nil}. +parse_port_loop(Uri_string, Pieces, Port) -> + case Uri_string of + <<"0"/utf8, Rest/binary>> -> + parse_port_loop(Rest, Pieces, Port * 10); + + <<"1"/utf8, Rest@1/binary>> -> + parse_port_loop(Rest@1, Pieces, (Port * 10) + 1); + + <<"2"/utf8, Rest@2/binary>> -> + parse_port_loop(Rest@2, Pieces, (Port * 10) + 2); + + <<"3"/utf8, Rest@3/binary>> -> + parse_port_loop(Rest@3, Pieces, (Port * 10) + 3); + + <<"4"/utf8, Rest@4/binary>> -> + parse_port_loop(Rest@4, Pieces, (Port * 10) + 4); + + <<"5"/utf8, Rest@5/binary>> -> + parse_port_loop(Rest@5, Pieces, (Port * 10) + 5); + + <<"6"/utf8, Rest@6/binary>> -> + parse_port_loop(Rest@6, Pieces, (Port * 10) + 6); + + <<"7"/utf8, Rest@7/binary>> -> + parse_port_loop(Rest@7, Pieces, (Port * 10) + 7); + + <<"8"/utf8, Rest@8/binary>> -> + parse_port_loop(Rest@8, Pieces, (Port * 10) + 8); + + <<"9"/utf8, Rest@9/binary>> -> + parse_port_loop(Rest@9, Pieces, (Port * 10) + 9); + + <<"?"/utf8, Rest@10/binary>> -> + Pieces@1 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + {some, Port}, + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_query_with_question_mark(Rest@10, Pieces@1); + + <<"#"/utf8, Rest@11/binary>> -> + Pieces@2 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + {some, Port}, + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_fragment(Rest@11, Pieces@2); + + <<"/"/utf8, _/binary>> -> + Pieces@3 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + {some, Port}, + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_path(Uri_string, Pieces@3); + + <<""/utf8>> -> + {ok, + {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + {some, Port}, + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}}; + + _ -> + {error, nil} + end. + +-file("src/gleam/uri.gleam", 353). +-spec parse_port(binary(), uri()) -> {ok, uri()} | {error, nil}. +parse_port(Uri_string, Pieces) -> + case Uri_string of + <<":0"/utf8, Rest/binary>> -> + parse_port_loop(Rest, Pieces, 0); + + <<":1"/utf8, Rest@1/binary>> -> + parse_port_loop(Rest@1, Pieces, 1); + + <<":2"/utf8, Rest@2/binary>> -> + parse_port_loop(Rest@2, Pieces, 2); + + <<":3"/utf8, Rest@3/binary>> -> + parse_port_loop(Rest@3, Pieces, 3); + + <<":4"/utf8, Rest@4/binary>> -> + parse_port_loop(Rest@4, Pieces, 4); + + <<":5"/utf8, Rest@5/binary>> -> + parse_port_loop(Rest@5, Pieces, 5); + + <<":6"/utf8, Rest@6/binary>> -> + parse_port_loop(Rest@6, Pieces, 6); + + <<":7"/utf8, Rest@7/binary>> -> + parse_port_loop(Rest@7, Pieces, 7); + + <<":8"/utf8, Rest@8/binary>> -> + parse_port_loop(Rest@8, Pieces, 8); + + <<":9"/utf8, Rest@9/binary>> -> + parse_port_loop(Rest@9, Pieces, 9); + + <<":"/utf8>> -> + {ok, Pieces}; + + <<""/utf8>> -> + {ok, Pieces}; + + <<"?"/utf8, Rest@10/binary>> -> + parse_query_with_question_mark(Rest@10, Pieces); + + <<":?"/utf8, Rest@10/binary>> -> + parse_query_with_question_mark(Rest@10, Pieces); + + <<"#"/utf8, Rest@11/binary>> -> + parse_fragment(Rest@11, Pieces); + + <<":#"/utf8, Rest@11/binary>> -> + parse_fragment(Rest@11, Pieces); + + <<"/"/utf8, _/binary>> -> + parse_path(Uri_string, Pieces); + + <<":"/utf8, Rest@12/binary>> -> + case Rest@12 of + <<"/"/utf8, _/binary>> -> + parse_path(Rest@12, Pieces); + + _ -> + {error, nil} + end; + + _ -> + {error, nil} + end. + +-file("src/gleam/uri.gleam", 309). +-spec parse_host_outside_of_brackets_loop(binary(), binary(), uri(), integer()) -> {ok, + uri()} | + {error, nil}. +parse_host_outside_of_brackets_loop(Original, Uri_string, Pieces, Size) -> + case Uri_string of + <<""/utf8>> -> + {ok, + {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Original}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}}; + + <<":"/utf8, _/binary>> -> + Host = binary:part(Original, 0, Size), + Pieces@1 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Host}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_port(Uri_string, Pieces@1); + + <<"/"/utf8, _/binary>> -> + Host@1 = binary:part(Original, 0, Size), + Pieces@2 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Host@1}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_path(Uri_string, Pieces@2); + + <<"?"/utf8, Rest/binary>> -> + Host@2 = binary:part(Original, 0, Size), + Pieces@3 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Host@2}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_query_with_question_mark(Rest, Pieces@3); + + <<"#"/utf8, Rest@1/binary>> -> + Host@3 = binary:part(Original, 0, Size), + Pieces@4 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Host@3}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_fragment(Rest@1, Pieces@4); + + _ -> + {_, Rest@2} = gleam_stdlib:string_pop_codeunit(Uri_string), + parse_host_outside_of_brackets_loop( + Original, + Rest@2, + Pieces, + Size + 1 + ) + end. + +-file("src/gleam/uri.gleam", 229). +-spec parse_host_within_brackets_loop(binary(), binary(), uri(), integer()) -> {ok, + uri()} | + {error, nil}. +parse_host_within_brackets_loop(Original, Uri_string, Pieces, Size) -> + case Uri_string of + <<""/utf8>> -> + {ok, + {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Uri_string}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}}; + + <<"]"/utf8, Rest/binary>> when Size =:= 0 -> + parse_port(Rest, Pieces); + + <<"]"/utf8, Rest@1/binary>> -> + Host = binary:part(Original, 0, Size + 1), + Pieces@1 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Host}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_port(Rest@1, Pieces@1); + + <<"/"/utf8, _/binary>> when Size =:= 0 -> + parse_path(Uri_string, Pieces); + + <<"/"/utf8, _/binary>> -> + Host@1 = binary:part(Original, 0, Size), + Pieces@2 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Host@1}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_path(Uri_string, Pieces@2); + + <<"?"/utf8, Rest@2/binary>> when Size =:= 0 -> + parse_query_with_question_mark(Rest@2, Pieces); + + <<"?"/utf8, Rest@3/binary>> -> + Host@2 = binary:part(Original, 0, Size), + Pieces@3 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Host@2}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_query_with_question_mark(Rest@3, Pieces@3); + + <<"#"/utf8, Rest@4/binary>> when Size =:= 0 -> + parse_fragment(Rest@4, Pieces); + + <<"#"/utf8, Rest@5/binary>> -> + Host@3 = binary:part(Original, 0, Size), + Pieces@4 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Host@3}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_fragment(Rest@5, Pieces@4); + + _ -> + {Char, Rest@6} = gleam_stdlib:string_pop_codeunit(Uri_string), + case is_valid_host_within_brackets_char(Char) of + true -> + parse_host_within_brackets_loop( + Original, + Rest@6, + Pieces, + Size + 1 + ); + + false -> + parse_host_outside_of_brackets_loop( + Original, + Original, + Pieces, + 0 + ) + end + end. + +-file("src/gleam/uri.gleam", 222). +-spec parse_host_within_brackets(binary(), uri()) -> {ok, uri()} | {error, nil}. +parse_host_within_brackets(Uri_string, Pieces) -> + parse_host_within_brackets_loop(Uri_string, Uri_string, Pieces, 0). + +-file("src/gleam/uri.gleam", 302). +-spec parse_host_outside_of_brackets(binary(), uri()) -> {ok, uri()} | + {error, nil}. +parse_host_outside_of_brackets(Uri_string, Pieces) -> + parse_host_outside_of_brackets_loop(Uri_string, Uri_string, Pieces, 0). + +-file("src/gleam/uri.gleam", 199). +-spec parse_host(binary(), uri()) -> {ok, uri()} | {error, nil}. +parse_host(Uri_string, Pieces) -> + case Uri_string of + <<"["/utf8, _/binary>> -> + parse_host_within_brackets(Uri_string, Pieces); + + <<":"/utf8, _/binary>> -> + Pieces@1 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, <<""/utf8>>}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_port(Uri_string, Pieces@1); + + <<""/utf8>> -> + {ok, + {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, <<""/utf8>>}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}}; + + _ -> + parse_host_outside_of_brackets(Uri_string, Pieces) + end. + +-file("src/gleam/uri.gleam", 167). +-spec parse_userinfo_loop(binary(), binary(), uri(), integer()) -> {ok, uri()} | + {error, nil}. +parse_userinfo_loop(Original, Uri_string, Pieces, Size) -> + case Uri_string of + <<"@"/utf8, Rest/binary>> when Size =:= 0 -> + parse_host(Rest, Pieces); + + <<"@"/utf8, Rest@1/binary>> -> + Userinfo = binary:part(Original, 0, Size), + Pieces@1 = {uri, + erlang:element(2, Pieces), + {some, Userinfo}, + erlang:element(4, Pieces), + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_host(Rest@1, Pieces@1); + + <<""/utf8>> -> + parse_host(Original, Pieces); + + <<"/"/utf8, _/binary>> -> + parse_host(Original, Pieces); + + <<"?"/utf8, _/binary>> -> + parse_host(Original, Pieces); + + <<"#"/utf8, _/binary>> -> + parse_host(Original, Pieces); + + _ -> + {_, Rest@2} = gleam_stdlib:string_pop_codeunit(Uri_string), + parse_userinfo_loop(Original, Rest@2, Pieces, Size + 1) + end. + +-file("src/gleam/uri.gleam", 163). +-spec parse_authority_pieces(binary(), uri()) -> {ok, uri()} | {error, nil}. +parse_authority_pieces(String, Pieces) -> + parse_userinfo_loop(String, String, Pieces, 0). + +-file("src/gleam/uri.gleam", 150). +-spec parse_authority_with_slashes(binary(), uri()) -> {ok, uri()} | + {error, nil}. +parse_authority_with_slashes(Uri_string, Pieces) -> + case Uri_string of + <<"//"/utf8>> -> + {ok, + {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, <<""/utf8>>}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}}; + + <<"//"/utf8, Rest/binary>> -> + parse_authority_pieces(Rest, Pieces); + + _ -> + parse_path(Uri_string, Pieces) + end. + +-file("src/gleam/uri.gleam", 91). +-spec parse_scheme_loop(binary(), binary(), uri(), integer()) -> {ok, uri()} | + {error, nil}. +parse_scheme_loop(Original, Uri_string, Pieces, Size) -> + case Uri_string of + <<"/"/utf8, _/binary>> when Size =:= 0 -> + parse_authority_with_slashes(Uri_string, Pieces); + + <<"/"/utf8, _/binary>> -> + Scheme = binary:part(Original, 0, Size), + Pieces@1 = {uri, + {some, string:lowercase(Scheme)}, + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_authority_with_slashes(Uri_string, Pieces@1); + + <<"?"/utf8, Rest/binary>> when Size =:= 0 -> + parse_query_with_question_mark(Rest, Pieces); + + <<"?"/utf8, Rest@1/binary>> -> + Scheme@1 = binary:part(Original, 0, Size), + Pieces@2 = {uri, + {some, string:lowercase(Scheme@1)}, + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_query_with_question_mark(Rest@1, Pieces@2); + + <<"#"/utf8, Rest@2/binary>> when Size =:= 0 -> + parse_fragment(Rest@2, Pieces); + + <<"#"/utf8, Rest@3/binary>> -> + Scheme@2 = binary:part(Original, 0, Size), + Pieces@3 = {uri, + {some, string:lowercase(Scheme@2)}, + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_fragment(Rest@3, Pieces@3); + + <<":"/utf8, _/binary>> when Size =:= 0 -> + {error, nil}; + + <<":"/utf8, Rest@4/binary>> -> + Scheme@3 = binary:part(Original, 0, Size), + Pieces@4 = {uri, + {some, string:lowercase(Scheme@3)}, + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_authority_with_slashes(Rest@4, Pieces@4); + + <<""/utf8>> -> + {ok, + {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + Original, + erlang:element(7, Pieces), + erlang:element(8, Pieces)}}; + + _ -> + {_, Rest@5} = gleam_stdlib:string_pop_codeunit(Uri_string), + parse_scheme_loop(Original, Rest@5, Pieces, Size + 1) + end. + +-file("src/gleam/uri.gleam", 537). +?DOC( + " Parses an urlencoded query string into a list of key value pairs.\n" + " Returns an error for invalid encoding.\n" + "\n" + " The opposite operation is `uri.query_to_string`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " parse_query(\"a=1&b=2\")\n" + " // -> Ok([#(\"a\", \"1\"), #(\"b\", \"2\")])\n" + " ```\n" +). +-spec parse_query(binary()) -> {ok, list({binary(), binary()})} | {error, nil}. +parse_query(Query) -> + gleam_stdlib:parse_query(Query). + +-file("src/gleam/uri.gleam", 573). +?DOC( + " Encodes a string into a percent encoded representation.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " percent_encode(\"100% great\")\n" + " // -> \"100%25%20great\"\n" + " ```\n" +). +-spec percent_encode(binary()) -> binary(). +percent_encode(Value) -> + gleam_stdlib:percent_encode(Value). + +-file("src/gleam/uri.gleam", 558). +-spec query_pair({binary(), binary()}) -> gleam@string_tree:string_tree(). +query_pair(Pair) -> + gleam_stdlib:identity( + [gleam_stdlib:percent_encode(erlang:element(1, Pair)), + <<"="/utf8>>, + gleam_stdlib:percent_encode(erlang:element(2, Pair))] + ). + +-file("src/gleam/uri.gleam", 550). +?DOC( + " Encodes a list of key value pairs as a URI query string.\n" + "\n" + " The opposite operation is `uri.parse_query`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " query_to_string([#(\"a\", \"1\"), #(\"b\", \"2\")])\n" + " // -> \"a=1&b=2\"\n" + " ```\n" +). +-spec query_to_string(list({binary(), binary()})) -> binary(). +query_to_string(Query) -> + _pipe = Query, + _pipe@1 = gleam@list:map(_pipe, fun query_pair/1), + _pipe@2 = gleam@list:intersperse( + _pipe@1, + gleam_stdlib:identity(<<"&"/utf8>>) + ), + _pipe@3 = gleam_stdlib:identity(_pipe@2), + unicode:characters_to_binary(_pipe@3). + +-file("src/gleam/uri.gleam", 586). +?DOC( + " Decodes a percent encoded string.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " percent_decode(\"100%25%20great+fun\")\n" + " // -> Ok(\"100% great+fun\")\n" + " ```\n" +). +-spec percent_decode(binary()) -> {ok, binary()} | {error, nil}. +percent_decode(Value) -> + gleam_stdlib:percent_decode(Value). + +-file("src/gleam/uri.gleam", 608). +-spec remove_dot_segments_loop(list(binary()), list(binary())) -> list(binary()). +remove_dot_segments_loop(Input, Accumulator) -> + case Input of + [] -> + lists: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, + remove_dot_segments_loop(Rest, Accumulator@5) + end. + +-file("src/gleam/uri.gleam", 604). +-spec remove_dot_segments(list(binary())) -> list(binary()). +remove_dot_segments(Input) -> + remove_dot_segments_loop(Input, []). + +-file("src/gleam/uri.gleam", 600). +?DOC( + " Splits the path section of a URI into it's constituent segments.\n" + "\n" + " Removes empty segments and resolves dot-segments as specified in\n" + " [section 5.2](https://www.ietf.org/rfc/rfc3986.html#section-5.2) of the RFC.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " path_segments(\"/users/1\")\n" + " // -> [\"users\" ,\"1\"]\n" + " ```\n" +). +-spec path_segments(binary()) -> list(binary()). +path_segments(Path) -> + remove_dot_segments(gleam@string:split(Path, <<"/"/utf8>>)). + +-file("src/gleam/uri.gleam", 639). +?DOC( + " Encodes a `Uri` value as a URI string.\n" + "\n" + " The opposite operation is `uri.parse`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let uri = Uri(..empty, scheme: Some(\"https\"), host: Some(\"example.com\"))\n" + " to_string(uri)\n" + " // -> \"https://example.com\"\n" + " ```\n" +). +-spec to_string(uri()) -> binary(). +to_string(Uri) -> + Parts = case erlang:element(8, Uri) of + {some, Fragment} -> + [<<"#"/utf8>>, Fragment]; + + none -> + [] + end, + Parts@1 = case erlang:element(7, Uri) of + {some, Query} -> + [<<"?"/utf8>>, Query | Parts]; + + none -> + Parts + end, + Parts@2 = [erlang:element(6, Uri) | Parts@1], + Parts@3 = case {erlang:element(4, Uri), + gleam_stdlib:string_starts_with(erlang:element(6, Uri), <<"/"/utf8>>)} of + {{some, Host}, false} when Host =/= <<""/utf8>> -> + [<<"/"/utf8>> | Parts@2]; + + {_, _} -> + Parts@2 + end, + Parts@4 = case {erlang:element(4, Uri), erlang:element(5, Uri)} of + {{some, _}, {some, Port}} -> + [<<":"/utf8>>, erlang:integer_to_binary(Port) | Parts@3]; + + {_, _} -> + Parts@3 + end, + Parts@5 = case {erlang:element(2, Uri), + erlang:element(3, Uri), + erlang:element(4, Uri)} of + {{some, S}, {some, U}, {some, H}} -> + [S, <<"://"/utf8>>, U, <<"@"/utf8>>, H | Parts@4]; + + {{some, S@1}, none, {some, H@1}} -> + [S@1, <<"://"/utf8>>, H@1 | Parts@4]; + + {{some, S@2}, {some, _}, none} -> + [S@2, <<":"/utf8>> | Parts@4]; + + {{some, S@2}, none, none} -> + [S@2, <<":"/utf8>> | Parts@4]; + + {none, none, {some, H@2}} -> + [<<"//"/utf8>>, H@2 | Parts@4]; + + {_, _, _} -> + Parts@4 + end, + erlang:list_to_binary(Parts@5). + +-file("src/gleam/uri.gleam", 683). +?DOC( + " Fetches the origin of a URI.\n" + "\n" + " Returns the origin of a uri as defined in\n" + " [RFC 6454](https://tools.ietf.org/html/rfc6454)\n" + "\n" + " The supported URI schemes are `http` and `https`.\n" + " URLs without a scheme will return `Error`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let assert Ok(uri) = parse(\"https://example.com/path?foo#bar\")\n" + " origin(uri)\n" + " // -> Ok(\"https://example.com\")\n" + " ```\n" +). +-spec origin(uri()) -> {ok, binary()} | {error, nil}. +origin(Uri) -> + {uri, Scheme, _, Host, Port, _, _, _} = Uri, + case {Host, Scheme} of + {{some, H}, {some, <<"https"/utf8>>}} when Port =:= {some, 443} -> + {ok, erlang:list_to_binary([<<"https://"/utf8>>, H])}; + + {{some, H@1}, {some, <<"http"/utf8>>}} when Port =:= {some, 80} -> + {ok, erlang:list_to_binary([<<"http://"/utf8>>, H@1])}; + + {{some, H@2}, {some, S}} when (S =:= <<"http"/utf8>>) orelse (S =:= <<"https"/utf8>>) -> + case Port of + {some, P} -> + {ok, + erlang:list_to_binary( + [S, + <<"://"/utf8>>, + H@2, + <<":"/utf8>>, + erlang:integer_to_binary(P)] + )}; + + none -> + {ok, erlang:list_to_binary([S, <<"://"/utf8>>, H@2])} + end; + + {_, _} -> + {error, nil} + end. + +-file("src/gleam/uri.gleam", 764). +-spec drop_last(list(DDO)) -> list(DDO). +drop_last(Elements) -> + gleam@list:take(Elements, erlang:length(Elements) - 1). + +-file("src/gleam/uri.gleam", 768). +-spec join_segments(list(binary())) -> binary(). +join_segments(Segments) -> + gleam@string:join([<<""/utf8>> | Segments], <<"/"/utf8>>). + +-file("src/gleam/uri.gleam", 706). +?DOC( + " Resolves a URI with respect to the given base URI.\n" + "\n" + " The base URI must be an absolute URI or this function will return an error.\n" + " The algorithm for merging uris is described in\n" + " [RFC 3986](https://tools.ietf.org/html/rfc3986#section-5.2).\n" +). +-spec merge(uri(), uri()) -> {ok, uri()} | {error, nil}. +merge(Base, Relative) -> + case Base of + {uri, {some, _}, _, {some, _}, _, _, _, _} -> + case Relative of + {uri, _, _, {some, _}, _, _, _, _} -> + Path = begin + _pipe = erlang:element(6, Relative), + _pipe@1 = gleam@string:split(_pipe, <<"/"/utf8>>), + _pipe@2 = remove_dot_segments(_pipe@1), + join_segments(_pipe@2) + end, + Resolved = {uri, + gleam@option:'or'( + erlang:element(2, Relative), + erlang:element(2, Base) + ), + none, + erlang:element(4, Relative), + gleam@option:'or'( + erlang:element(5, Relative), + erlang:element(5, Base) + ), + Path, + erlang:element(7, Relative), + erlang:element(8, Relative)}, + {ok, Resolved}; + + _ -> + {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_stdlib:string_starts_with( + erlang:element(6, Relative), + <<"/"/utf8>> + ) of + true -> + gleam@string:split( + erlang:element(6, Relative), + <<"/"/utf8>> + ); + + false -> + _pipe@3 = erlang:element(6, Base), + _pipe@4 = gleam@string:split( + _pipe@3, + <<"/"/utf8>> + ), + _pipe@5 = drop_last(_pipe@4), + lists:append( + _pipe@5, + gleam@string:split( + erlang:element(6, Relative), + <<"/"/utf8>> + ) + ) + end, + Path@1 = begin + _pipe@6 = Path_segments, + _pipe@7 = remove_dot_segments(_pipe@6), + join_segments(_pipe@7) + end, + {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. + +-file("src/gleam/uri.gleam", 81). +?DOC( + " Parses a compliant URI string into the `Uri` Type.\n" + " If the string is not a valid URI string then an error is returned.\n" + "\n" + " The opposite operation is `uri.to_string`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " parse(\"https://example.com:1234/a/b?query=true#fragment\")\n" + " // -> Ok(\n" + " // Uri(\n" + " // scheme: Some(\"https\"),\n" + " // userinfo: None,\n" + " // host: Some(\"example.com\"),\n" + " // port: Some(1234),\n" + " // path: \"/a/b\",\n" + " // query: Some(\"query=true\"),\n" + " // fragment: Some(\"fragment\")\n" + " // )\n" + " // )\n" + " ```\n" +). +-spec parse(binary()) -> {ok, uri()} | {error, nil}. +parse(Uri_string) -> + gleam_stdlib:uri_parse(Uri_string). diff --git a/build/dev/javascript/gleam_stdlib/gleam_stdlib.erl b/build/dev/javascript/gleam_stdlib/gleam_stdlib.erl new file mode 100644 index 0000000..2c416f4 --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam_stdlib.erl @@ -0,0 +1,534 @@ +-module(gleam_stdlib). + +-export([ + map_get/2, iodata_append/2, identity/1, parse_int/1, parse_float/1, + less_than/2, string_pop_grapheme/1, string_pop_codeunit/1, + string_starts_with/2, wrap_list/1, string_ends_with/2, string_pad/4, + uri_parse/1, bit_array_slice/3, percent_encode/1, percent_decode/1, + base64_decode/1, parse_query/1, bit_array_concat/1, + base64_encode/2, tuple_get/2, classify_dynamic/1, print/1, + println/1, print_error/1, println_error/1, inspect/1, float_to_string/1, + int_from_base_string/2, utf_codepoint_list_to_string/1, contains_string/2, + crop_string/2, base16_encode/1, base16_decode/1, string_replace/3, slice/3, + bit_array_to_int_and_size/1, bit_array_pad_to_bytes/1, index/2, list/5, + dict/1, int/1, float/1, bit_array/1, is_null/1 +]). + +%% Taken from OTP's uri_string module +-define(DEC2HEX(X), + if ((X) >= 0) andalso ((X) =< 9) -> (X) + $0; + ((X) >= 10) andalso ((X) =< 15) -> (X) + $A - 10 + end). + +%% Taken from OTP's uri_string module +-define(HEX2DEC(X), + if ((X) >= $0) andalso ((X) =< $9) -> (X) - $0; + ((X) >= $A) andalso ((X) =< $F) -> (X) - $A + 10; + ((X) >= $a) andalso ((X) =< $f) -> (X) - $a + 10 + end). + +-define(is_lowercase_char(X), + (X > 96 andalso X < 123)). +-define(is_underscore_char(X), + (X == 95)). +-define(is_digit_char(X), + (X > 47 andalso X < 58)). +-define(is_ascii_character(X), + (erlang:is_integer(X) andalso X >= 32 andalso X =< 126)). + +uppercase(X) -> X - 32. + +map_get(Map, Key) -> + case maps:find(Key, Map) of + error -> {error, nil}; + OkFound -> OkFound + end. + +iodata_append(Iodata, String) -> [Iodata, String]. + +identity(X) -> X. + +classify_dynamic(nil) -> <<"Nil">>; +classify_dynamic(null) -> <<"Nil">>; +classify_dynamic(undefined) -> <<"Nil">>; +classify_dynamic(X) when is_boolean(X) -> <<"Bool">>; +classify_dynamic(X) when is_atom(X) -> <<"Atom">>; +classify_dynamic(X) when is_binary(X) -> <<"String">>; +classify_dynamic(X) when is_bitstring(X) -> <<"BitArray">>; +classify_dynamic(X) when is_integer(X) -> <<"Int">>; +classify_dynamic(X) when is_float(X) -> <<"Float">>; +classify_dynamic(X) when is_list(X) -> <<"List">>; +classify_dynamic(X) when is_map(X) -> <<"Dict">>; +classify_dynamic(X) when is_tuple(X) -> <<"Array">>; +classify_dynamic(X) when is_reference(X) -> <<"Reference">>; +classify_dynamic(X) when is_pid(X) -> <<"Pid">>; +classify_dynamic(X) when is_port(X) -> <<"Port">>; +classify_dynamic(X) when + is_function(X, 0) orelse is_function(X, 1) orelse is_function(X, 2) orelse + is_function(X, 3) orelse is_function(X, 4) orelse is_function(X, 5) orelse + is_function(X, 6) orelse is_function(X, 7) orelse is_function(X, 8) orelse + is_function(X, 9) orelse is_function(X, 10) orelse is_function(X, 11) orelse + is_function(X, 12) -> <<"Function">>; +classify_dynamic(_) -> <<"Unknown">>. + +tuple_get(_tup, Index) when Index < 0 -> {error, nil}; +tuple_get(Data, Index) when Index >= tuple_size(Data) -> {error, nil}; +tuple_get(Data, Index) -> {ok, element(Index + 1, Data)}. + +int_from_base_string(String, Base) -> + case catch binary_to_integer(String, Base) of + Int when is_integer(Int) -> {ok, Int}; + _ -> {error, nil} + end. + +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. + +less_than(Lhs, Rhs) -> + Lhs < Rhs. + +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) -> + Chars = string:pad(String, Length, Dir, binary_to_list(PadString)), + case unicode:characters_to_binary(Chars) of + Bin when is_binary(Bin) -> Bin; + Error -> erlang:error({gleam_error, {string_invalid_utf8, Error}}) + end. + +string_pop_grapheme(String) -> + case string:next_grapheme(String) of + [ Next | Rest ] when is_binary(Rest) -> + {ok, {unicode:characters_to_binary([Next]), Rest}}; + + [ Next | Rest ] -> + {ok, {unicode:characters_to_binary([Next]), unicode:characters_to_binary(Rest)}}; + + _ -> {error, nil} + end. + +string_pop_codeunit(<>) -> {Cp, Rest}; +string_pop_codeunit(Binary) -> {0, Binary}. + +bit_array_pad_to_bytes(Bin) -> + case erlang:bit_size(Bin) rem 8 of + 0 -> Bin; + TrailingBits -> + PaddingBits = 8 - TrailingBits, + <> + end. + +bit_array_concat(BitArrays) -> + list_to_bitstring(BitArrays). + +-if(?OTP_RELEASE >= 26). +base64_encode(Bin, Padding) -> + PaddedBin = bit_array_pad_to_bytes(Bin), + base64:encode(PaddedBin, #{padding => Padding}). +-else. +base64_encode(_Bin, _Padding) -> + erlang:error(<<"Erlang OTP/26 or higher is required to use base64:encode">>). +-endif. + +bit_array_slice(Bin, Pos, Len) -> + try {ok, binary:part(Bin, Pos, Len)} + catch error:badarg -> {error, nil} + end. + +base64_decode(S) -> + try {ok, base64:decode(S)} + catch error:_ -> {error, nil} + end. + +wrap_list(X) when is_list(X) -> X; +wrap_list(X) -> [X]. + +parse_query(Query) -> + case uri_string:dissect_query(Query) of + {error, _, _} -> {error, nil}; + Pairs -> + Pairs1 = lists:map(fun + ({K, true}) -> {K, <<"">>}; + (Pair) -> Pair + end, Pairs), + {ok, Pairs1} + end. + +percent_encode(B) -> percent_encode(B, <<>>). +percent_encode(<<>>, Acc) -> + Acc; +percent_encode(<>, Acc) -> + case percent_ok(H) of + true -> + percent_encode(T, <>); + false -> + <> = <>, + percent_encode(T, <>) + end. + +percent_decode(Cs) -> percent_decode(Cs, <<>>). +percent_decode(<<$%, C0, C1, Cs/binary>>, Acc) -> + case is_hex_digit(C0) andalso is_hex_digit(C1) of + true -> + B = ?HEX2DEC(C0)*16+?HEX2DEC(C1), + percent_decode(Cs, <>); + false -> + {error, nil} + end; +percent_decode(<>, Acc) -> + percent_decode(Cs, <>); +percent_decode(<<>>, Acc) -> + check_utf8(Acc). + +percent_ok($!) -> true; +percent_ok($$) -> true; +percent_ok($') -> true; +percent_ok($() -> true; +percent_ok($)) -> true; +percent_ok($*) -> true; +percent_ok($+) -> true; +percent_ok($-) -> true; +percent_ok($.) -> true; +percent_ok($_) -> true; +percent_ok($~) -> true; +percent_ok(C) when $0 =< C, C =< $9 -> true; +percent_ok(C) when $A =< C, C =< $Z -> true; +percent_ok(C) when $a =< C, C =< $z -> true; +percent_ok(_) -> false. + +is_hex_digit(C) -> + ($0 =< C andalso C =< $9) orelse ($a =< C andalso C =< $f) orelse ($A =< C andalso C =< $F). + +check_utf8(Cs) -> + case unicode:characters_to_list(Cs) of + {incomplete, _, _} -> {error, nil}; + {error, _, _} -> {error, nil}; + _ -> {ok, Cs} + end. + +uri_parse(String) -> + case uri_string:parse(String) of + {error, _, _} -> {error, nil}; + Uri -> + Port = + try maps:get(port, Uri) of + undefined -> none; + Value -> {some, Value} + catch _:_ -> none + end, + {ok, {uri, + maps_get_optional(Uri, scheme), + maps_get_optional(Uri, userinfo), + maps_get_optional(Uri, host), + Port, + maps_get_or(Uri, path, <<>>), + maps_get_optional(Uri, query), + maps_get_optional(Uri, fragment) + }} + end. + +maps_get_optional(Map, Key) -> + try {some, maps:get(Key, Map)} + catch _:_ -> none + end. + +maps_get_or(Map, Key, Default) -> + try maps:get(Key, Map) + catch _:_ -> Default + end. + +print(String) -> + io:put_chars(String), + nil. + +println(String) -> + io:put_chars([String, $\n]), + nil. + +print_error(String) -> + io:put_chars(standard_error, String), + nil. + +println_error(String) -> + io:put_chars(standard_error, [String, $\n]), + nil. + +inspect(true) -> + "True"; +inspect(false) -> + "False"; +inspect(nil) -> + "Nil"; +inspect(Data) when is_map(Data) -> + Fields = [ + [<<"#(">>, inspect(Key), <<", ">>, inspect(Value), <<")">>] + || {Key, Value} <- maps:to_list(Data) + ], + ["dict.from_list([", lists:join(", ", Fields), "])"]; +inspect(Atom) when is_atom(Atom) -> + erlang:element(2, inspect_atom(Atom)); +inspect(Any) when is_integer(Any) -> + erlang:integer_to_list(Any); +inspect(Any) when is_float(Any) -> + io_lib_format:fwrite_g(Any); +inspect(Binary) when is_binary(Binary) -> + case inspect_maybe_utf8_string(Binary, <<>>) of + {ok, InspectedUtf8String} -> InspectedUtf8String; + {error, not_a_utf8_string} -> + Segments = [erlang:integer_to_list(X) || <> <= Binary], + ["<<", lists:join(", ", Segments), ">>"] + end; +inspect(Bits) when is_bitstring(Bits) -> + inspect_bit_array(Bits); +inspect(List) when is_list(List) -> + case inspect_list(List, true) of + {charlist, _} -> ["charlist.from_string(\"", list_to_binary(List), "\")"]; + {proper, Elements} -> ["[", Elements, "]"]; + {improper, Elements} -> ["//erl([", Elements, "])"] + end; +inspect(Any) when is_tuple(Any) % Record constructors + andalso is_atom(element(1, Any)) + andalso element(1, Any) =/= false + andalso element(1, Any) =/= true + andalso element(1, Any) =/= nil +-> + [Atom | ArgsList] = erlang:tuple_to_list(Any), + InspectedArgs = lists:map(fun inspect/1, ArgsList), + case inspect_atom(Atom) of + {gleam_atom, GleamAtom} -> + Args = lists:join(<<", ">>, InspectedArgs), + [GleamAtom, "(", Args, ")"]; + {erlang_atom, ErlangAtom} -> + Args = lists:join(<<", ">>, [ErlangAtom | InspectedArgs]), + ["#(", Args, ")"] + end; +inspect(Tuple) when is_tuple(Tuple) -> + Elements = lists:map(fun inspect/1, erlang:tuple_to_list(Tuple)), + ["#(", lists:join(", ", Elements), ")"]; +inspect(Any) when is_function(Any) -> + {arity, Arity} = erlang:fun_info(Any, arity), + ArgsAsciiCodes = lists:seq($a, $a + Arity - 1), + Args = lists:join(<<", ">>, + lists:map(fun(Arg) -> <> end, ArgsAsciiCodes) + ), + ["//fn(", Args, ") { ... }"]; +inspect(Any) -> + ["//erl(", io_lib:format("~p", [Any]), ")"]. + +inspect_atom(Atom) -> + Binary = erlang:atom_to_binary(Atom), + case inspect_maybe_gleam_atom(Binary, none, <<>>) of + {ok, Inspected} -> {gleam_atom, Inspected}; + {error, _} -> {erlang_atom, ["atom.create(\"", Binary, "\")"]} + end. + +inspect_maybe_gleam_atom(<<>>, none, _) -> + {error, nil}; +inspect_maybe_gleam_atom(<>, none, _) when ?is_digit_char(First) -> + {error, nil}; +inspect_maybe_gleam_atom(<<"_", _Rest/binary>>, none, _) -> + {error, nil}; +inspect_maybe_gleam_atom(<<"_">>, _PrevChar, _Acc) -> + {error, nil}; +inspect_maybe_gleam_atom(<<"_", _Rest/binary>>, $_, _Acc) -> + {error, nil}; +inspect_maybe_gleam_atom(<>, _PrevChar, _Acc) + when not (?is_lowercase_char(First) orelse ?is_underscore_char(First) orelse ?is_digit_char(First)) -> + {error, nil}; +inspect_maybe_gleam_atom(<>, none, Acc) -> + inspect_maybe_gleam_atom(Rest, First, <>); +inspect_maybe_gleam_atom(<<"_", Rest/binary>>, _PrevChar, Acc) -> + inspect_maybe_gleam_atom(Rest, $_, Acc); +inspect_maybe_gleam_atom(<>, $_, Acc) -> + inspect_maybe_gleam_atom(Rest, First, <>); +inspect_maybe_gleam_atom(<>, _PrevChar, Acc) -> + inspect_maybe_gleam_atom(Rest, First, <>); +inspect_maybe_gleam_atom(<<>>, _PrevChar, Acc) -> + {ok, Acc}; +inspect_maybe_gleam_atom(A, B, C) -> + erlang:display({A, B, C}), + throw({gleam_error, A, B, C}). + +inspect_list([], _) -> + {proper, []}; +inspect_list([First], true) when ?is_ascii_character(First) -> + {charlist, nil}; +inspect_list([First], _) -> + {proper, [inspect(First)]}; +inspect_list([First | Rest], ValidCharlist) when is_list(Rest) -> + StillValidCharlist = ValidCharlist andalso ?is_ascii_character(First), + {Kind, Inspected} = inspect_list(Rest, StillValidCharlist), + {Kind, [inspect(First), <<", ">> | Inspected]}; +inspect_list([First | ImproperTail], _) -> + {improper, [inspect(First), <<" | ">>, inspect(ImproperTail)]}. + +inspect_bit_array(Bits) -> + Text = inspect_bit_array(Bits, <<"<<">>), + <>">>. + +inspect_bit_array(<<>>, Acc) -> + Acc; +inspect_bit_array(<>, Acc) -> + inspect_bit_array(Rest, append_segment(Acc, erlang:integer_to_binary(X))); +inspect_bit_array(Rest, Acc) -> + Size = bit_size(Rest), + <> = Rest, + X1 = erlang:integer_to_binary(X), + Size1 = erlang:integer_to_binary(Size), + Segment = <>, + inspect_bit_array(<<>>, append_segment(Acc, Segment)). + +bit_array_to_int_and_size(A) -> + Size = bit_size(A), + <> = A, + {A1, Size}. + +append_segment(<<"<<">>, Segment) -> + <<"<<", Segment/binary>>; +append_segment(Acc, Segment) -> + <>. + + +inspect_maybe_utf8_string(Binary, Acc) -> + case Binary of + <<>> -> {ok, <<$", Acc/binary, $">>}; + <> -> + Escaped = case First of + $" -> <<$\\, $">>; + $\\ -> <<$\\, $\\>>; + $\r -> <<$\\, $r>>; + $\n -> <<$\\, $n>>; + $\t -> <<$\\, $t>>; + $\f -> <<$\\, $f>>; + X when X > 126, X < 160 -> convert_to_u(X); + X when X < 32 -> convert_to_u(X); + Other -> <> + end, + inspect_maybe_utf8_string(Rest, <>); + _ -> {error, not_a_utf8_string} + end. + +convert_to_u(Code) -> + list_to_binary(io_lib:format("\\u{~4.16.0B}", [Code])). + +float_to_string(Float) when is_float(Float) -> + erlang:iolist_to_binary(io_lib_format:fwrite_g(Float)). + +utf_codepoint_list_to_string(List) -> + case unicode:characters_to_binary(List) of + {error, _} -> erlang:error({gleam_error, {string_invalid_utf8, List}}); + Binary -> Binary + end. + +crop_string(String, Prefix) -> + case string:find(String, Prefix) of + nomatch -> String; + New -> New + end. + +contains_string(String, Substring) -> + is_bitstring(string:find(String, Substring)). + +base16_encode(Bin) -> + PaddedBin = bit_array_pad_to_bytes(Bin), + binary:encode_hex(PaddedBin). + +base16_decode(String) -> + try + {ok, binary:decode_hex(String)} + catch + _:_ -> {error, nil} + end. + +string_replace(String, Pattern, Replacement) -> + string:replace(String, Pattern, Replacement, all). + +slice(String, Index, Length) -> + case string:slice(String, Index, Length) of + X when is_binary(X) -> X; + X when is_list(X) -> unicode:characters_to_binary(X) + end. + +index([X | _], 0) -> + {ok, {some, X}}; +index([_, X | _], 1) -> + {ok, {some, X}}; +index([_, _, X | _], 2) -> + {ok, {some, X}}; +index([_, _, _, X | _], 3) -> + {ok, {some, X}}; +index([_, _, _, _, X | _], 4) -> + {ok, {some, X}}; +index([_, _, _, _, _, X | _], 5) -> + {ok, {some, X}}; +index([_, _, _, _, _, _, X | _], 6) -> + {ok, {some, X}}; +index([_, _, _, _, _, _, _, X | _], 7) -> + {ok, {some, X}}; +index(Tuple, Index) when is_tuple(Tuple) andalso is_integer(Index) -> + {ok, try + {some, element(Index + 1, Tuple)} + catch _:_ -> + none + end}; +index(Map, Key) when is_map(Map) -> + {ok, try + {some, maps:get(Key, Map)} + catch _:_ -> + none + end}; +index(_, Index) when is_integer(Index) -> + {error, <<"Indexable">>}; +index(_, _) -> + {error, <<"Dict">>}. + +list(T, A, B, C, D) when is_tuple(T) -> + list(tuple_to_list(T), A, B, C, D); +list([], _, _, _, Acc) -> + {lists:reverse(Acc), []}; +list([X | Xs], Decode, PushPath, Index, Acc) -> + {Out, Errors} = Decode(X), + case Errors of + [] -> list(Xs, Decode, PushPath, Index + 1, [Out | Acc]); + _ -> PushPath({[], Errors}, integer_to_binary(Index)) + end; +list(Unexpected, _, _, _, []) -> + Found = gleam@dynamic:classify(Unexpected), + Error = {decode_error, <<"List"/utf8>>, Found, []}, + {[], [Error]}; +list(_, _, _, _, Acc) -> + {lists:reverse(Acc), []}. + +dict(#{} = Data) -> {ok, Data}; +dict(_) -> {error, nil}. + +int(I) when is_integer(I) -> {ok, I}; +int(_) -> {error, 0}. + +float(F) when is_float(F) -> {ok, F}; +float(_) -> {error, 0.0}. + +bit_array(B) when is_bitstring(B) -> {ok, B}; +bit_array(_) -> {error, <<>>}. + +is_null(X) -> + X =:= undefined orelse X =:= null orelse X =:= nil. diff --git a/build/dev/javascript/gleam_stdlib/gleam_stdlib.mjs b/build/dev/javascript/gleam_stdlib/gleam_stdlib.mjs new file mode 100644 index 0000000..ebac45f --- /dev/null +++ b/build/dev/javascript/gleam_stdlib/gleam_stdlib.mjs @@ -0,0 +1,1048 @@ +import { + BitArray, + Error, + List, + Ok, + Result, + UtfCodepoint, + stringBits, + toBitArray, + bitArraySlice, + NonEmpty, + Empty, + CustomType, +} from "./gleam.mjs"; +import { Some, None } from "./gleam/option.mjs"; +import Dict from "./dict.mjs"; +import { classify } from "./gleam/dynamic.mjs"; +import { DecodeError } from "./gleam/dynamic/decode.mjs"; + +const Nil = undefined; +const NOT_FOUND = {}; + +export function identity(x) { + return x; +} + +export function parse_int(value) { + if (/^[-+]?(\d+)$/.test(value)) { + return new Ok(parseInt(value)); + } else { + return new Error(Nil); + } +} + +export function parse_float(value) { + if (/^[-+]?(\d+)\.(\d+)([eE][-+]?\d+)?$/.test(value)) { + return new Ok(parseFloat(value)); + } else { + return new Error(Nil); + } +} + +export function to_string(term) { + return term.toString(); +} + +export function int_to_base_string(int, base) { + return int.toString(base).toUpperCase(); +} + +const int_base_patterns = { + 2: /[^0-1]/, + 3: /[^0-2]/, + 4: /[^0-3]/, + 5: /[^0-4]/, + 6: /[^0-5]/, + 7: /[^0-6]/, + 8: /[^0-7]/, + 9: /[^0-8]/, + 10: /[^0-9]/, + 11: /[^0-9a]/, + 12: /[^0-9a-b]/, + 13: /[^0-9a-c]/, + 14: /[^0-9a-d]/, + 15: /[^0-9a-e]/, + 16: /[^0-9a-f]/, + 17: /[^0-9a-g]/, + 18: /[^0-9a-h]/, + 19: /[^0-9a-i]/, + 20: /[^0-9a-j]/, + 21: /[^0-9a-k]/, + 22: /[^0-9a-l]/, + 23: /[^0-9a-m]/, + 24: /[^0-9a-n]/, + 25: /[^0-9a-o]/, + 26: /[^0-9a-p]/, + 27: /[^0-9a-q]/, + 28: /[^0-9a-r]/, + 29: /[^0-9a-s]/, + 30: /[^0-9a-t]/, + 31: /[^0-9a-u]/, + 32: /[^0-9a-v]/, + 33: /[^0-9a-w]/, + 34: /[^0-9a-x]/, + 35: /[^0-9a-y]/, + 36: /[^0-9a-z]/, +}; + +export function int_from_base_string(string, base) { + if (int_base_patterns[base].test(string.replace(/^-/, "").toLowerCase())) { + return new Error(Nil); + } + + const result = parseInt(string, base); + + if (isNaN(result)) { + return new Error(Nil); + } + + return new Ok(result); +} + +export function string_replace(string, target, substitute) { + return string.replaceAll(target, substitute); +} + +export function string_reverse(string) { + return [...string].reverse().join(""); +} + +export function string_length(string) { + if (string === "") { + return 0; + } + const iterator = graphemes_iterator(string); + if (iterator) { + let i = 0; + for (const _ of iterator) { + i++; + } + return i; + } else { + return string.match(/./gsu).length; + } +} + +export function graphemes(string) { + const iterator = graphemes_iterator(string); + if (iterator) { + return List.fromArray(Array.from(iterator).map((item) => item.segment)); + } else { + return List.fromArray(string.match(/./gsu)); + } +} + +let segmenter = undefined; + +function graphemes_iterator(string) { + if (globalThis.Intl && Intl.Segmenter) { + segmenter ||= new Intl.Segmenter(); + return segmenter.segment(string)[Symbol.iterator](); + } +} + +export function pop_grapheme(string) { + let first; + const iterator = graphemes_iterator(string); + if (iterator) { + first = iterator.next().value?.segment; + } else { + first = string.match(/./su)?.[0]; + } + if (first) { + return new Ok([first, string.slice(first.length)]); + } else { + return new Error(Nil); + } +} + +export function pop_codeunit(str) { + return [str.charCodeAt(0) | 0, str.slice(1)]; +} + +export function lowercase(string) { + return string.toLowerCase(); +} + +export function uppercase(string) { + return string.toUpperCase(); +} + +export function less_than(a, b) { + return a < b; +} + +export function add(a, b) { + return a + b; +} + +export function split(xs, pattern) { + return List.fromArray(xs.split(pattern)); +} + +export function concat(xs) { + let result = ""; + for (const x of xs) { + result = result + x; + } + return result; +} + +export function length(data) { + return data.length; +} + +export function string_byte_slice(string, index, length) { + return string.slice(index, index + length); +} + +export function string_grapheme_slice(string, idx, len) { + if (len <= 0 || idx >= string.length) { + return ""; + } + + const iterator = graphemes_iterator(string); + if (iterator) { + while (idx-- > 0) { + iterator.next(); + } + + let result = ""; + + while (len-- > 0) { + const v = iterator.next().value; + if (v === undefined) { + break; + } + + result += v.segment; + } + + return result; + } else { + return string + .match(/./gsu) + .slice(idx, idx + len) + .join(""); + } +} + +export function string_codeunit_slice(str, from, length) { + return str.slice(from, from + length); +} +export function crop_string(string, substring) { + return string.substring(string.indexOf(substring)); +} + +export function contains_string(haystack, needle) { + return haystack.indexOf(needle) >= 0; +} + +export function starts_with(haystack, needle) { + return haystack.startsWith(needle); +} + +export function ends_with(haystack, needle) { + return haystack.endsWith(needle); +} + +export function split_once(haystack, needle) { + const index = haystack.indexOf(needle); + if (index >= 0) { + const before = haystack.slice(0, index); + const after = haystack.slice(index + needle.length); + return new Ok([before, after]); + } else { + return new Error(Nil); + } +} + +const unicode_whitespaces = [ + "\u0020", // Space + "\u0009", // Horizontal tab + "\u000A", // Line feed + "\u000B", // Vertical tab + "\u000C", // Form feed + "\u000D", // Carriage return + "\u0085", // Next line + "\u2028", // Line separator + "\u2029", // Paragraph separator +].join(""); + +const trim_start_regex = /* @__PURE__ */ new RegExp( + `^[${unicode_whitespaces}]*`, +); +const trim_end_regex = /* @__PURE__ */ new RegExp(`[${unicode_whitespaces}]*$`); + +export function trim_start(string) { + return string.replace(trim_start_regex, ""); +} + +export function trim_end(string) { + return string.replace(trim_end_regex, ""); +} + +export function bit_array_from_string(string) { + return toBitArray([stringBits(string)]); +} + +export function bit_array_bit_size(bit_array) { + return bit_array.bitSize; +} + +export function bit_array_byte_size(bit_array) { + return bit_array.byteSize; +} + +export function bit_array_pad_to_bytes(bit_array) { + const trailingBitsCount = bit_array.bitSize % 8; + + // If the bit array is a whole number of bytes it can be returned unchanged + if (trailingBitsCount === 0) { + return bit_array; + } + + const finalByte = bit_array.byteAt(bit_array.byteSize - 1); + + // The required final byte has its unused trailing bits set to zero + const unusedBitsCount = 8 - trailingBitsCount; + const correctFinalByte = (finalByte >> unusedBitsCount) << unusedBitsCount; + + // If the unused bits in the final byte are already set to zero then the + // existing buffer can be re-used, avoiding a copy + if (finalByte === correctFinalByte) { + return new BitArray( + bit_array.rawBuffer, + bit_array.byteSize * 8, + bit_array.bitOffset, + ); + } + + // Copy the bit array into a new aligned buffer and set the correct final byte + const buffer = new Uint8Array(bit_array.byteSize); + for (let i = 0; i < buffer.length - 1; i++) { + buffer[i] = bit_array.byteAt(i); + } + buffer[buffer.length - 1] = correctFinalByte; + + return new BitArray(buffer); +} + +export function bit_array_concat(bit_arrays) { + return toBitArray(bit_arrays.toArray()); +} + +export function console_log(term) { + console.log(term); +} + +export function console_error(term) { + console.error(term); +} + +export function crash(message) { + throw new globalThis.Error(message); +} + +export function bit_array_to_string(bit_array) { + // If the bit array isn't a whole number of bytes then return an error + if (bit_array.bitSize % 8 !== 0) { + return new Error(Nil); + } + + try { + const decoder = new TextDecoder("utf-8", { fatal: true }); + + if (bit_array.bitOffset === 0) { + return new Ok(decoder.decode(bit_array.rawBuffer)); + } else { + // The input data isn't aligned, so copy it into a new aligned buffer so + // that TextDecoder can be used + const buffer = new Uint8Array(bit_array.byteSize); + for (let i = 0; i < buffer.length; i++) { + buffer[i] = bit_array.byteAt(i); + } + return new Ok(decoder.decode(buffer)); + } + } catch { + return new Error(Nil); + } +} + +export function print(string) { + if (typeof process === "object" && process.stdout?.write) { + process.stdout.write(string); // We can write without a trailing newline + } else if (typeof Deno === "object") { + Deno.stdout.writeSync(new TextEncoder().encode(string)); // We can write without a trailing newline + } else { + console.log(string); // We're in a browser. Newlines are mandated + } +} + +export function print_error(string) { + if (typeof process === "object" && process.stderr?.write) { + process.stderr.write(string); // We can write without a trailing newline + } else if (typeof Deno === "object") { + Deno.stderr.writeSync(new TextEncoder().encode(string)); // We can write without a trailing newline + } else { + console.error(string); // We're in a browser. Newlines are mandated + } +} + +export function print_debug(string) { + if (typeof process === "object" && process.stderr?.write) { + process.stderr.write(string + "\n"); // If we're in Node.js, use `stderr` + } else if (typeof Deno === "object") { + Deno.stderr.writeSync(new TextEncoder().encode(string + "\n")); // If we're in Deno, use `stderr` + } else { + console.log(string); // Otherwise, use `console.log` (so that it doesn't look like an error) + } +} + +export function ceiling(float) { + return Math.ceil(float); +} + +export function floor(float) { + return Math.floor(float); +} + +export function round(float) { + return Math.round(float); +} + +export function truncate(float) { + return Math.trunc(float); +} + +export function power(base, exponent) { + // It is checked in Gleam that: + // - The base is non-negative and that the exponent is not fractional. + // - The base is non-zero and the exponent is non-negative (otherwise + // the result will essentially be division by zero). + // It can thus be assumed that valid input is passed to the Math.pow + // function and a NaN or Infinity value will not be produced. + return Math.pow(base, exponent); +} + +export function random_uniform() { + const random_uniform_result = Math.random(); + // With round-to-nearest-even behavior, the ranges claimed for the functions below + // (excluding the one for Math.random() itself) aren't exact. + // If extremely large bounds are chosen (2^53 or higher), + // it's possible in extremely rare cases to calculate the usually-excluded upper bound. + // Note that as numbers in JavaScript are IEEE 754 floating point numbers + // See: + // Because of this, we just loop 'until' we get a valid result where 0.0 <= x < 1.0: + if (random_uniform_result === 1.0) { + return random_uniform(); + } + return random_uniform_result; +} + +export function bit_array_slice(bits, position, length) { + const start = Math.min(position, position + length); + const end = Math.max(position, position + length); + + if (start < 0 || end * 8 > bits.bitSize) { + return new Error(Nil); + } + + return new Ok(bitArraySlice(bits, start * 8, end * 8)); +} + +export function codepoint(int) { + return new UtfCodepoint(int); +} + +export function string_to_codepoint_integer_list(string) { + return List.fromArray(Array.from(string).map((item) => item.codePointAt(0))); +} + +export function utf_codepoint_list_to_string(utf_codepoint_integer_list) { + return utf_codepoint_integer_list + .toArray() + .map((x) => String.fromCodePoint(x.value)) + .join(""); +} + +export function utf_codepoint_to_int(utf_codepoint) { + return utf_codepoint.value; +} + +export function new_map() { + return Dict.new(); +} + +export function map_size(map) { + return map.size; +} + +export function map_to_list(map) { + return List.fromArray(map.entries()); +} + +export function map_remove(key, map) { + return map.delete(key); +} + +export function map_get(map, key) { + const value = map.get(key, NOT_FOUND); + if (value === NOT_FOUND) { + return new Error(Nil); + } + return new Ok(value); +} + +export function map_insert(key, value, map) { + return map.set(key, value); +} + +function unsafe_percent_decode(string) { + return decodeURIComponent(string || ""); +} + +function unsafe_percent_decode_query(string) { + return decodeURIComponent((string || "").replace("+", " ")); +} + +export function percent_decode(string) { + try { + return new Ok(unsafe_percent_decode(string)); + } catch { + return new Error(Nil); + } +} + +export function percent_encode(string) { + return encodeURIComponent(string).replace("%2B", "+"); +} + +export function parse_query(query) { + try { + const pairs = []; + for (const section of query.split("&")) { + const [key, value] = section.split("="); + if (!key) continue; + + const decodedKey = unsafe_percent_decode_query(key); + const decodedValue = unsafe_percent_decode_query(value); + pairs.push([decodedKey, decodedValue]); + } + return new Ok(List.fromArray(pairs)); + } catch { + return new Error(Nil); + } +} + +const b64EncodeLookup = [ + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 43, 47, +]; + +let b64TextDecoder; + +// Implementation based on https://github.com/mitschabaude/fast-base64/blob/main/js.js +export function base64_encode(bit_array, padding) { + b64TextDecoder ??= new TextDecoder(); + + bit_array = bit_array_pad_to_bytes(bit_array); + + const m = bit_array.byteSize; + const k = m % 3; + const n = Math.floor(m / 3) * 4 + (k && k + 1); + const N = Math.ceil(m / 3) * 4; + const encoded = new Uint8Array(N); + + for (let i = 0, j = 0; j < m; i += 4, j += 3) { + const y = + (bit_array.byteAt(j) << 16) + + (bit_array.byteAt(j + 1) << 8) + + (bit_array.byteAt(j + 2) | 0); + + encoded[i] = b64EncodeLookup[y >> 18]; + encoded[i + 1] = b64EncodeLookup[(y >> 12) & 0x3f]; + encoded[i + 2] = b64EncodeLookup[(y >> 6) & 0x3f]; + encoded[i + 3] = b64EncodeLookup[y & 0x3f]; + } + + let base64 = b64TextDecoder.decode(new Uint8Array(encoded.buffer, 0, n)); + + if (padding) { + if (k === 1) { + base64 += "=="; + } else if (k === 2) { + base64 += "="; + } + } + + return base64; +} + +// From https://developer.mozilla.org/en-US/docs/Glossary/Base64 +export function base64_decode(sBase64) { + try { + const binString = atob(sBase64); + const length = binString.length; + const array = new Uint8Array(length); + for (let i = 0; i < length; i++) { + array[i] = binString.charCodeAt(i); + } + return new Ok(new BitArray(array)); + } catch { + return new Error(Nil); + } +} + +export function classify_dynamic(data) { + if (typeof data === "string") { + return "String"; + } else if (typeof data === "boolean") { + return "Bool"; + } else if (data instanceof Result) { + return "Result"; + } else if (data instanceof List) { + return "List"; + } else if (data instanceof BitArray) { + return "BitArray"; + } else if (data instanceof Dict) { + return "Dict"; + } else if (Number.isInteger(data)) { + return "Int"; + } else if (Array.isArray(data)) { + return `Array`; + } else if (typeof data === "number") { + return "Float"; + } else if (data === null) { + return "Nil"; + } else if (data === undefined) { + return "Nil"; + } else { + const type = typeof data; + return type.charAt(0).toUpperCase() + type.slice(1); + } +} + +export function byte_size(string) { + return new TextEncoder().encode(string).length; +} + +// In JavaScript bitwise operations convert numbers to a sequence of 32 bits +// while Erlang uses arbitrary precision. +// To get around this problem and get consistent results use BigInt and then +// downcast the value back to a Number value. + +export function bitwise_and(x, y) { + return Number(BigInt(x) & BigInt(y)); +} + +export function bitwise_not(x) { + return Number(~BigInt(x)); +} + +export function bitwise_or(x, y) { + return Number(BigInt(x) | BigInt(y)); +} + +export function bitwise_exclusive_or(x, y) { + return Number(BigInt(x) ^ BigInt(y)); +} + +export function bitwise_shift_left(x, y) { + return Number(BigInt(x) << BigInt(y)); +} + +export function bitwise_shift_right(x, y) { + return Number(BigInt(x) >> BigInt(y)); +} + +export function inspect(v) { + return new Inspector().inspect(v); +} + +export function float_to_string(float) { + const string = float.toString().replace("+", ""); + if (string.indexOf(".") >= 0) { + return string; + } else { + const index = string.indexOf("e"); + if (index >= 0) { + return string.slice(0, index) + ".0" + string.slice(index); + } else { + return string + ".0"; + } + } +} + +class Inspector { + #references = new Set(); + + inspect(v) { + const t = typeof v; + if (v === true) return "True"; + if (v === false) return "False"; + if (v === null) return "//js(null)"; + if (v === undefined) return "Nil"; + if (t === "string") return this.#string(v); + if (t === "bigint" || Number.isInteger(v)) return v.toString(); + if (t === "number") return float_to_string(v); + if (v instanceof UtfCodepoint) return this.#utfCodepoint(v); + if (v instanceof BitArray) return this.#bit_array(v); + if (v instanceof RegExp) return `//js(${v})`; + if (v instanceof Date) return `//js(Date("${v.toISOString()}"))`; + if (v instanceof globalThis.Error) return `//js(${v.toString()})`; + if (v instanceof Function) { + const args = []; + for (const i of Array(v.length).keys()) + args.push(String.fromCharCode(i + 97)); + return `//fn(${args.join(", ")}) { ... }`; + } + + if (this.#references.size === this.#references.add(v).size) { + return "//js(circular reference)"; + } + + let printed; + if (Array.isArray(v)) { + printed = `#(${v.map((v) => this.inspect(v)).join(", ")})`; + } else if (v instanceof List) { + printed = this.#list(v); + } else if (v instanceof CustomType) { + printed = this.#customType(v); + } else if (v instanceof Dict) { + printed = this.#dict(v); + } else if (v instanceof Set) { + return `//js(Set(${[...v].map((v) => this.inspect(v)).join(", ")}))`; + } else { + printed = this.#object(v); + } + this.#references.delete(v); + return printed; + } + + #object(v) { + const name = Object.getPrototypeOf(v)?.constructor?.name || "Object"; + const props = []; + for (const k of Object.keys(v)) { + props.push(`${this.inspect(k)}: ${this.inspect(v[k])}`); + } + const body = props.length ? " " + props.join(", ") + " " : ""; + const head = name === "Object" ? "" : name + " "; + return `//js(${head}{${body}})`; + } + + #dict(map) { + let body = "dict.from_list(["; + let first = true; + map.forEach((value, key) => { + if (!first) body = body + ", "; + body = body + "#(" + this.inspect(key) + ", " + this.inspect(value) + ")"; + first = false; + }); + return body + "])"; + } + + #customType(record) { + const props = Object.keys(record) + .map((label) => { + const value = this.inspect(record[label]); + return isNaN(parseInt(label)) ? `${label}: ${value}` : value; + }) + .join(", "); + return props + ? `${record.constructor.name}(${props})` + : record.constructor.name; + } + + #list(list) { + if (list instanceof Empty) { + return "[]"; + } + + let char_out = 'charlist.from_string("'; + let list_out = "["; + + let current = list; + while (current instanceof NonEmpty) { + let element = current.head; + current = current.tail; + + if (list_out !== "[") { + list_out += ", "; + } + list_out += this.inspect(element); + + if (char_out) { + if (Number.isInteger(element) && element >= 32 && element <= 126) { + char_out += String.fromCharCode(element); + } else { + char_out = null; + } + } + } + + if (char_out) { + return char_out + '")'; + } else { + return list_out + "]"; + } + } + + #string(str) { + let new_str = '"'; + for (let i = 0; i < str.length; i++) { + const char = str[i]; + switch (char) { + case "\n": + new_str += "\\n"; + break; + case "\r": + new_str += "\\r"; + break; + case "\t": + new_str += "\\t"; + break; + case "\f": + new_str += "\\f"; + break; + case "\\": + new_str += "\\\\"; + break; + case '"': + new_str += '\\"'; + break; + default: + if (char < " " || (char > "~" && char < "\u{00A0}")) { + new_str += + "\\u{" + + char.charCodeAt(0).toString(16).toUpperCase().padStart(4, "0") + + "}"; + } else { + new_str += char; + } + } + } + new_str += '"'; + return new_str; + } + + #utfCodepoint(codepoint) { + return `//utfcodepoint(${String.fromCodePoint(codepoint.value)})`; + } + + #bit_array(bits) { + if (bits.bitSize === 0) { + return "<<>>"; + } + + let acc = "<<"; + + for (let i = 0; i < bits.byteSize - 1; i++) { + acc += bits.byteAt(i).toString(); + acc += ", "; + } + + if (bits.byteSize * 8 === bits.bitSize) { + acc += bits.byteAt(bits.byteSize - 1).toString(); + } else { + const trailingBitsCount = bits.bitSize % 8; + acc += bits.byteAt(bits.byteSize - 1) >> (8 - trailingBitsCount); + acc += `:size(${trailingBitsCount})`; + } + + acc += ">>"; + return acc; + } +} + +export function base16_encode(bit_array) { + const trailingBitsCount = bit_array.bitSize % 8; + + let result = ""; + + for (let i = 0; i < bit_array.byteSize; i++) { + let byte = bit_array.byteAt(i); + + if (i === bit_array.byteSize - 1 && trailingBitsCount !== 0) { + const unusedBitsCount = 8 - trailingBitsCount; + byte = (byte >> unusedBitsCount) << unusedBitsCount; + } + + result += byte.toString(16).padStart(2, "0").toUpperCase(); + } + + return result; +} + +export function base16_decode(string) { + const bytes = new Uint8Array(string.length / 2); + for (let i = 0; i < string.length; i += 2) { + const a = parseInt(string[i], 16); + const b = parseInt(string[i + 1], 16); + if (isNaN(a) || isNaN(b)) return new Error(Nil); + bytes[i / 2] = a * 16 + b; + } + return new Ok(new BitArray(bytes)); +} + +export function bit_array_to_int_and_size(bits) { + const trailingBitsCount = bits.bitSize % 8; + const unusedBitsCount = trailingBitsCount === 0 ? 0 : 8 - trailingBitsCount; + + return [bits.byteAt(0) >> unusedBitsCount, bits.bitSize]; +} + +export function bit_array_starts_with(bits, prefix) { + if (prefix.bitSize > bits.bitSize) { + return false; + } + + // Check any whole bytes + const byteCount = Math.trunc(prefix.bitSize / 8); + for (let i = 0; i < byteCount; i++) { + if (bits.byteAt(i) !== prefix.byteAt(i)) { + return false; + } + } + + // Check any trailing bits at the end of the prefix + if (prefix.bitSize % 8 !== 0) { + const unusedBitsCount = 8 - (prefix.bitSize % 8); + if ( + bits.byteAt(byteCount) >> unusedBitsCount !== + prefix.byteAt(byteCount) >> unusedBitsCount + ) { + return false; + } + } + + return true; +} + +export function log(x) { + // It is checked in Gleam that: + // - The input is strictly positive (x > 0) + // - This ensures that Math.log will never return NaN or -Infinity + // The function can thus safely pass the input to Math.log + // and a valid finite float will always be produced. + return Math.log(x); +} + +export function exp(x) { + return Math.exp(x); +} + +export function list_to_array(list) { + let current = list; + let array = []; + while (current instanceof NonEmpty) { + array.push(current.head); + current = current.tail; + } + return array; +} + +export function index(data, key) { + // Dictionaries and dictionary-like objects can be indexed + if (data instanceof Dict || data instanceof WeakMap || data instanceof Map) { + const token = {}; + const entry = data.get(key, token); + if (entry === token) return new Ok(new None()); + return new Ok(new Some(entry)); + } + + const key_is_int = Number.isInteger(key); + + // Only elements 0-7 of lists can be indexed, negative indices are not allowed + if (key_is_int && key >= 0 && key < 8 && data instanceof List) { + let i = 0; + for (const value of data) { + if (i === key) return new Ok(new Some(value)); + i++; + } + return new Error("Indexable"); + } + + // Arrays and objects can be indexed + if ( + (key_is_int && Array.isArray(data)) || + (data && typeof data === "object") || + (data && Object.getPrototypeOf(data) === Object.prototype) + ) { + if (key in data) return new Ok(new Some(data[key])); + return new Ok(new None()); + } + + return new Error(key_is_int ? "Indexable" : "Dict"); +} + +export function list(data, decode, pushPath, index, emptyList) { + if (!(data instanceof List || Array.isArray(data))) { + const error = new DecodeError("List", classify(data), emptyList); + return [emptyList, List.fromArray([error])]; + } + + const decoded = []; + + for (const element of data) { + const layer = decode(element); + const [out, errors] = layer; + + if (errors instanceof NonEmpty) { + const [_, errors] = pushPath(layer, index.toString()); + return [emptyList, errors]; + } + decoded.push(out); + index++; + } + + return [List.fromArray(decoded), emptyList]; +} + +export function dict(data) { + if (data instanceof Dict) { + return new Ok(data); + } + if (data instanceof Map || data instanceof WeakMap) { + return new Ok(Dict.fromMap(data)); + } + if (data == null) { + return new Error("Dict"); + } + if (typeof data !== "object") { + return new Error("Dict"); + } + const proto = Object.getPrototypeOf(data); + if (proto === Object.prototype || proto === null) { + return new Ok(Dict.fromObject(data)); + } + return new Error("Dict"); +} + +export function bit_array(data) { + if (data instanceof BitArray) return new Ok(data); + if (data instanceof Uint8Array) return new Ok(new BitArray(data)); + return new Error(new BitArray(new Uint8Array())); +} + +export function float(data) { + if (typeof data === "number") return new Ok(data); + return new Error(0.0); +} + +export function int(data) { + if (Number.isInteger(data)) return new Ok(data); + return new Error(0); +} + +export function string(data) { + if (typeof data === "string") return new Ok(data); + return new Error(""); +} + +export function is_null(data) { + return data === null || data === undefined; +} diff --git a/build/dev/javascript/gleam_time/_gleam_artefacts/gleam@time@calendar.cache b/build/dev/javascript/gleam_time/_gleam_artefacts/gleam@time@calendar.cache new file mode 100644 index 0000000000000000000000000000000000000000..b3da925345c563e0958a2d10015535fb17ae5816 GIT binary patch literal 21304 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 YqI87yK>=zdYzv}1guOvjlyIyt06O%*tN;K2 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_time/_gleam_artefacts/gleam@time@calendar.cache_inline b/build/dev/javascript/gleam_time/_gleam_artefacts/gleam@time@calendar.cache_inline new file mode 100644 index 0000000000000000000000000000000000000000..1b1cb4d44c57c2d7a5122870fa6ac3e62ff7e94e GIT binary patch literal 8 JcmZR80ssIA00961 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleam_time/_gleam_artefacts/gleam@time@calendar.cache_meta b/build/dev/javascript/gleam_time/_gleam_artefacts/gleam@time@calendar.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..e65ddb65f29754b87dd5eaa78b87eeded909f888 GIT binary patch literal 1524 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
+//// The license of that package is produced below: +//// +//// +//// > MIT License +//// +//// > Copyright 2018 Aaron VonderHaar +//// +//// > Redistribution and use in source and binary forms, with or without modification, +//// are permitted provided that the following conditions are met: +//// +//// 1. Redistributions of source code must retain the above copyright notice, +//// this list of conditions and the following disclaimer. +//// +//// 2. Redistributions in binary form must reproduce the above copyright notice, +//// this list of conditions and the following disclaimer in the documentation +//// and/or other materials provided with the distribution. +//// +//// 3. Neither the name of the copyright holder nor the names of its contributors +//// may be used to endorse or promote products derived from this software without +//// specific prior written permission. +//// +//// > THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +//// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +//// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL +//// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +//// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +//// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +//// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +//// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//// +//// > The above copyright notice and this permission notice shall be included in all +//// copies or substantial portions of the Software. +////
+//// + +// Just in case we decide in the future to no longer include the above reference +// and license, this package was initially a port of the `elm-color` module: +// +// https://github.com/avh4/elm-color/ +// + +// IMPORTS -------------------------------------------------------------------- + +import gleam/dynamic/decode +import gleam/float +import gleam/int +import gleam/json.{type Json} +import gleam/list +import gleam/result +import gleam/string + +// TYPES ---------------------------------------------------------------------- + +/// A representation of a colour that can be converted to RGBA or HSLA format. +/// +/// +///
+/// +pub opaque type Colour { + Rgba(r: Float, g: Float, b: Float, a: Float) + Hsla(h: Float, s: Float, l: Float, a: Float) +} + +/// Type alias for `Colour` +/// +/// +///
+/// +pub type Color = + Colour + +// UTILITY -------------------------------------------------------------------- + +fn valid_colour_value(c: Float) -> Result(Float, Nil) { + case c >. 1.0 || c <. 0.0 { + True -> Error(Nil) + False -> Ok(c) + } +} + +fn hue_to_rgb(hue: Float, m1: Float, m2: Float) -> Float { + let h = case hue { + _ if hue <. 0.0 -> hue +. 1.0 + _ if hue >. 1.0 -> hue -. 1.0 + _ -> hue + } + + let h_t_6 = h *. 6.0 + let h_t_2 = h *. 2.0 + let h_t_3 = h *. 3.0 + + case h { + _ if h_t_6 <. 1.0 -> m1 +. { m2 -. m1 } *. h *. 6.0 + _ if h_t_2 <. 1.0 -> m2 + _ if h_t_3 <. 2.0 -> m1 +. { m2 -. m1 } *. { 2.0 /. 3.0 -. h } *. 6.0 + _ -> m1 + } +} + +fn hex_string_to_int(hex_string: String) -> Result(Int, Nil) { + let hex = case hex_string { + "#" <> hex_number -> hex_number + "0x" <> hex_number -> hex_number + _ -> hex_string + } + + hex + |> string.lowercase() + |> string.to_graphemes() + |> list.reverse() + |> list.index_fold(Ok(0), fn(total, char, index) { + case total { + Error(Nil) -> Error(Nil) + Ok(v) -> { + use num <- result.try(case char { + "a" -> Ok(10) + "b" -> Ok(11) + "c" -> Ok(12) + "d" -> Ok(13) + "e" -> Ok(14) + "f" -> Ok(15) + _ -> int.parse(char) + }) + use base <- result.try(int.power(16, int.to_float(index))) + Ok(v + float.round(int.to_float(num) *. base)) + } + } + }) +} + +fn hsla_to_rgba( + h: Float, + s: Float, + l: Float, + a: Float, +) -> #(Float, Float, Float, Float) { + let m2 = case l <=. 0.5 { + True -> l *. { s +. 1.0 } + False -> l +. s -. l *. s + } + + let m1 = l *. 2.0 -. m2 + + let r = hue_to_rgb(h +. 1.0 /. 3.0, m1, m2) + let g = hue_to_rgb(h, m1, m2) + let b = hue_to_rgb(h -. 1.0 /. 3.0, m1, m2) + + #(r, g, b, a) +} + +fn rgba_to_hsla( + r: Float, + g: Float, + b: Float, + a: Float, +) -> #(Float, Float, Float, Float) { + let min_colour = float.min(r, float.min(g, b)) + + let max_colour = float.max(r, float.max(g, b)) + + let h1 = case True { + _ if max_colour == r -> float.divide(g -. b, max_colour -. min_colour) + _ if max_colour == g -> + float.divide(b -. r, max_colour -. min_colour) + |> result.try(fn(d) { Ok(2.0 +. d) }) + _ -> + float.divide(r -. g, max_colour -. min_colour) + |> result.try(fn(d) { Ok(4.0 +. d) }) + } + + let h2 = case h1 { + Ok(v) -> Ok(v *. { 1.0 /. 6.0 }) + _ -> h1 + } + + let h3 = case h2 { + Ok(v) if v <. 0.0 -> v +. 1.0 + Ok(v) -> v + _ -> 0.0 + } + + let l = { min_colour +. max_colour } /. 2.0 + + let s = case True { + _ if min_colour == max_colour -> 0.0 + _ if l <. 0.5 -> + { max_colour -. min_colour } /. { max_colour +. min_colour } + _ -> { max_colour -. min_colour } /. { 2.0 -. max_colour -. min_colour } + } + + #(h3, s, l, a) +} + +// CONSTRUCTORS --------------------------------------------------------------- + +/// Returns a `Result(Colour)` created from the given 8 bit RGB values. +/// +/// Returns `Error(Nil)` if the supplied RGB values are greater than 255 or less than 0. +/// +///
+/// Example: +/// +/// ```gleam +/// fn example() { +/// assert Ok(red) = from_rgb255(255, 0, 0) +/// } +/// ``` +///
+/// +/// +/// +pub fn from_rgb255(r red: Int, g green: Int, b blue: Int) -> Result(Colour, Nil) { + use r <- result.try( + red + |> int.to_float() + |> float.divide(255.0) + |> result.try(valid_colour_value), + ) + + use g <- result.try( + green + |> int.to_float() + |> float.divide(255.0) + |> result.try(valid_colour_value), + ) + + use b <- result.try( + blue + |> int.to_float() + |> float.divide(255.0) + |> result.try(valid_colour_value), + ) + + Ok(Rgba(r: r, g: g, b: b, a: 1.0)) +} + +/// Returns `Result(Colour)` created from the given RGB values. +/// +/// If the supplied RGB values are greater than 1.0 or less than 0.0 returns `Error(Nil)` +/// +///
+/// Example: +/// +/// ```gleam +/// fn example() { +/// assert Ok(red) = from_rgb(1.0, 0.0, 0.0) +/// } +/// ``` +///
+/// +/// +/// +pub fn from_rgb( + r red: Float, + g green: Float, + b blue: Float, +) -> Result(Colour, Nil) { + use r <- result.try(valid_colour_value(red)) + use g <- result.try(valid_colour_value(green)) + use b <- result.try(valid_colour_value(blue)) + + Ok(Rgba(r: r, g: g, b: b, a: 1.0)) +} + +/// Returns `Result(Colour)` created from the given RGBA values. +/// +/// Returns `Error(Nil)` if the supplied RGBA values are greater than 1.0 or less than 0.0. +/// +///
+/// Example: +/// +/// ```gleam +/// fn example() { +/// assert Ok(red_half_opacity) = from_rbga(1.0, 0.0, 0.0, 0.5) +/// } +/// ``` +///
+/// +/// +/// +pub fn from_rgba( + r red: Float, + g green: Float, + b blue: Float, + a alpha: Float, +) -> Result(Colour, Nil) { + use r <- result.try(valid_colour_value(red)) + use g <- result.try(valid_colour_value(green)) + use b <- result.try(valid_colour_value(blue)) + use a <- result.try(valid_colour_value(alpha)) + + Ok(Rgba(r: r, g: g, b: b, a: a)) +} + +/// Returns `Result(Colour)` created from the given HSLA values. +/// +/// Returns `Error(Nil)`f the supplied HSLA values are greater than 1.0 or less than 0.0. +/// +///
+/// Example: +/// +/// ```gleam +/// fn example() { +/// assert Ok(red_half_opacity) = from_hsla(0.0, 1.0, 0.5, 0.5) +/// } +/// ``` +///
+/// +/// +/// +pub fn from_hsla( + h hue: Float, + s saturation: Float, + l lightness: Float, + a alpha: Float, +) -> Result(Colour, Nil) { + use h <- result.try(valid_colour_value(hue)) + use s <- result.try(valid_colour_value(saturation)) + use l <- result.try(valid_colour_value(lightness)) + use a <- result.try(valid_colour_value(alpha)) + + Ok(Hsla(h: h, s: s, l: l, a: a)) +} + +/// Returns `Result(Colour)` created from the given HSL values. +/// +/// Returns `Error(Nil)` if the supplied HSL values are greater than 1.0 or less than 0.0. +/// +///
+/// Example: +/// +/// ```gleam +/// fn example() { +/// assert Ok(red) = from_hsla(0.0, 1.0, 0.5) +/// } +/// ``` +///
+/// +/// +/// +pub fn from_hsl( + h hue: Float, + s saturation: Float, + l lightness: Float, +) -> Result(Colour, Nil) { + from_hsla(hue, saturation, lightness, 1.0) +} + +/// Returns a `Result(Colour)` created from the given hex `Int`. +/// +/// Returns `Error(Nil)` if the supplied hex `Int is greater than 0xffffff or less than 0x0. +/// +///
+/// Example: +/// +/// ```gleam +/// fn example() { +/// assert Ok(red) = from_rgb_hex(0xff0000) +/// } +/// ``` +///
+/// +/// +/// +pub fn from_rgb_hex(hex: Int) -> Result(Colour, Nil) { + case hex > 0xffffff || hex < 0 { + True -> Error(Nil) + False -> { + let r = + int.bitwise_shift_right(hex, 16) + |> int.bitwise_and(0xff) + let g = + int.bitwise_shift_right(hex, 8) + |> int.bitwise_and(0xff) + let b = int.bitwise_and(hex, 0xff) + from_rgb255(r, g, b) + } + } +} + +/// Returns a `Result(Colour)` created from the given RGB hex `String`. +/// +/// Returns `Error(Nil)` if the supplied hex `String` is invalid, or greater than `"#ffffff" or less than `"#0"` +/// +///
+/// Example: +/// +/// ```gleam +/// fn example() { +/// assert Ok(red) = from_rgb_hex_string("#ff0000") +/// } +/// ``` +///
+/// +/// +/// +pub fn from_rgb_hex_string(hex_string: String) -> Result(Colour, Nil) { + use hex_int <- result.try(hex_string_to_int(hex_string)) + + from_rgb_hex(hex_int) +} + +/// Returns a `Result(Colour)` created from the given RGBA hex `String`. +/// +/// Returns `Error(Nil)` if the supplied hex `String` is invalid, or greater than `"#ffffffff" or less than `"#0"` +/// +///
+/// Example: +/// +/// ```gleam +/// fn example() { +/// assert Ok(red_half_opacity) = from_rgba_hex_string("#ff00007f") +/// } +/// ``` +///
+/// +/// +/// +pub fn from_rgba_hex_string(hex_string: String) -> Result(Colour, Nil) { + use hex_int <- result.try(hex_string_to_int(hex_string)) + + from_rgba_hex(hex_int) +} + +/// Returns a `Result(Colour)` created from the given hex `Int`. +/// +/// Returns `Error(Nil)` if the supplied hex `Int is greater than 0xffffffff or less than 0x0. +/// +///
+/// Example: +/// +/// ```gleam +/// fn example() { +/// assert Ok(red_half_opacity) = from_rgba_hex(0xff00007f) +/// } +/// ``` +///
+/// +/// +/// +pub fn from_rgba_hex(hex: Int) -> Result(Colour, Nil) { + case hex > 0xffffffff || hex < 0 { + True -> Error(Nil) + False -> { + // This won't fail because we are always dividing by 255.0 + let assert Ok(r) = + int.bitwise_shift_right(hex, 24) + |> int.bitwise_and(0xff) + |> int.to_float() + |> float.divide(255.0) + // This won't fail because we are always dividing by 255.0 + let assert Ok(g) = + int.bitwise_shift_right(hex, 16) + |> int.bitwise_and(0xff) + |> int.to_float() + |> float.divide(255.0) + // This won't fail because we are always dividing by 255.0 + let assert Ok(b) = + int.bitwise_shift_right(hex, 8) + |> int.bitwise_and(0xff) + |> int.to_float() + |> float.divide(255.0) + // This won't fail because we are always dividing by 255.0 + let assert Ok(a) = + int.bitwise_and(hex, 0xff) + |> int.to_float() + |> float.divide(255.0) + from_rgba(r, g, b, a) + } + } +} + +// CONVERSIONS ---------------------------------------------------------------- + +/// Returns `#(Float, Float, Float, Float)` representing the given `Colour`'s +/// R, G, B, and A values respectively. +/// +///
+/// Example: +/// +/// ```gleam +/// fn example() { +/// assert Ok(red) = from_rgb255(255, 0, 0) +/// let #(r, g, b, a) = to_rgba(red) +/// } +/// ``` +///
+/// +/// +/// +pub fn to_rgba(colour: Colour) -> #(Float, Float, Float, Float) { + case colour { + Rgba(r, g, b, a) -> #(r, g, b, a) + Hsla(h, s, l, a) -> hsla_to_rgba(h, s, l, a) + } +} + +/// Returns `#(Float, Float, Float, Float)` representing the given `Colour`'s +/// H, S, L, and A values respectively. +/// +///
+/// Example: +/// +/// ```gleam +/// fn example() { +/// assert Ok(red) = from_rgb255(255, 0, 0) +/// let #(h, s, l, a) = to_hsla(red) +/// } +/// ``` +///
+/// +/// +/// +pub fn to_hsla(colour: Colour) -> #(Float, Float, Float, Float) { + case colour { + Hsla(h, s, l, a) -> #(h, s, l, a) + Rgba(r, g, b, a) -> rgba_to_hsla(r, g, b, a) + } +} + +/// Returns an rgba formatted CSS `String` created from the given `Colour`. +/// +///
+/// Example: +/// +/// ```gleam +/// fn example() { +/// assert Ok(red) = from_rgb255(255, 0, 0) +/// let css_red = to_css_rgba_string(red) +/// } +/// ``` +///
+/// +/// +/// +pub fn to_css_rgba_string(colour: Colour) -> String { + let #(r, g, b, a) = to_rgba(colour) + + let percent = fn(x: Float) -> Float { + // This won't fail because we are always dividing by 100.0 + let assert Ok(p) = + x + |> float.multiply(10_000.0) + |> float.round() + |> int.to_float() + |> float.divide(100.0) + + p + } + + let round_to = fn(x: Float) -> Float { + // This won't fail because we are always dividing by 1000.0 + let assert Ok(r) = + x + |> float.multiply(1000.0) + |> float.round() + |> int.to_float() + |> float.divide(1000.0) + + r + } + + string.join( + [ + "rgba(", + float.to_string(percent(r)) <> "%,", + float.to_string(percent(g)) <> "%,", + float.to_string(percent(b)) <> "%,", + float.to_string(round_to(a)), + ")", + ], + "", + ) +} + +/// Returns an rgba hex formatted `String` created from the given `Colour`. +/// +///
+/// Example: +/// +/// ```gleam +/// fn example() { +/// assert Ok(red) = from_rgba(1.0, 0.0, 0.0, 1.0) +/// let red_hex = to_rgba_hex_string(red) +/// } +/// ``` +///
+/// +/// +/// +pub fn to_rgba_hex_string(colour: Colour) -> String { + let hex_string = + to_rgba_hex(colour) + |> int.to_base16() + + case string.length(hex_string) { + 8 -> hex_string + l -> string.repeat("0", 8 - l) <> hex_string + } +} + +/// Returns an rgb hex formatted `String` created from the given `Colour`. +/// +///
+/// Example: +/// +/// ```gleam +/// fn example() { +/// assert Ok(red) = from_rgba(255, 0, 0) +/// let red_hex = to_rgb_hex_string(red) +/// } +/// ``` +///
+/// +/// +/// +pub fn to_rgb_hex_string(colour: Colour) -> String { + let hex_string = + to_rgb_hex(colour) + |> int.to_base16() + + case string.length(hex_string) { + 6 -> hex_string + l -> string.repeat("0", 6 - l) <> hex_string + } +} + +/// Returns an hex `Int` created from the given `Colour`. +/// +///
+/// Example: +/// +/// ```gleam +/// fn example() { +/// assert Ok(red) = from_rgba(1.0, 0.0, 0.0, 1.0) +/// let red_hex_int = to_rgba_hex(red) +/// } +/// ``` +///
+/// +/// +/// +pub fn to_rgba_hex(colour: Colour) -> Int { + let #(r, g, b, a) = to_rgba(colour) + + let red = + r *. 255.0 + |> float.round() + |> int.bitwise_shift_left(24) + + let green = + g *. 255.0 + |> float.round() + |> int.bitwise_shift_left(16) + + let blue = + b *. 255.0 + |> float.round() + |> int.bitwise_shift_left(8) + + let alpha = + a *. 255.0 + |> float.round() + + red + green + blue + alpha +} + +/// Returns a rgb hex `Int` created from the given `Colour`. +/// +///
+/// Example: +/// +/// ```gleam +/// fn example() { +/// assert Ok(red) = from_rgba(255, 0, 0) +/// let red_hex_int = to_rgb_hex(red) +/// } +/// ``` +///
+/// +/// +/// +pub fn to_rgb_hex(colour: Colour) -> Int { + let #(r, g, b, _) = to_rgba(colour) + + let red = + r *. 255.0 + |> float.round() + |> int.bitwise_shift_left(16) + + let green = + g *. 255.0 + |> float.round() + |> int.bitwise_shift_left(8) + + let blue = + b *. 255.0 + |> float.round() + + red + green + blue +} + +// JSON ------------------------------------------------------------------------ + +/// Encodes a `Colour` value as a Gleam [`Json`](https://hexdocs.pm/gleam_json/gleam/json.html#Json) +/// value. You'll need this if you want to send a `Colour` value over the network +/// in a HTTP request or response, for example. +/// +/// +/// +pub fn encode(colour: Colour) -> Json { + case colour { + Rgba(r, g, b, a) -> encode_rgba(r, g, b, a) + Hsla(h, s, l, a) -> encode_hsla(h, s, l, a) + } +} + +fn encode_rgba(r: Float, g: Float, b: Float, a: Float) -> Json { + json.object([ + #("r", json.float(r)), + #("g", json.float(g)), + #("b", json.float(b)), + #("a", json.float(a)), + ]) +} + +fn encode_hsla(h: Float, s: Float, l: Float, a: Float) -> Json { + json.object([ + #("h", json.float(h)), + #("s", json.float(s)), + #("l", json.float(l)), + #("a", json.float(a)), + ]) +} + +/// Attempt to decode some [`Dynamic`](https://hexdocs.pm/gleam_stdlib/gleam/dynamic.html#Dynamic) +/// value into a `Colour`. Most often you'll use this to decode some JSON. +/// +/// +/// +pub fn decoder() -> decode.Decoder(Colour) { + decode.one_of(rgba_decoder(), or: [hsla_decoder()]) +} + +fn rgba_decoder() -> decode.Decoder(Colour) { + use r <- decode.field("r", decode.float) + use g <- decode.field("g", decode.float) + use b <- decode.field("b", decode.float) + use a <- decode.field("a", decode.float) + + decode.success(Rgba(r, g, b, a)) +} + +fn hsla_decoder() -> decode.Decoder(Colour) { + use h <- decode.field("h", decode.float) + use s <- decode.field("s", decode.float) + use l <- decode.field("l", decode.float) + use a <- decode.field("a", decode.float) + + decode.success(Hsla(h, s, l, a)) +} + +// COLOURS --------------------------------------------------------------------- + +/// A `Colour` reprsenting the colour RGBA(239, 41, 41, 1.0) +pub const light_red = Rgba( + r: 0.9372549019607843, + g: 0.1607843137254902, + b: 0.1607843137254902, + a: 1.0, +) + +/// A `Colour` reprsenting the colour RGBA(204, 0, 0, 1.0) +pub const red = Rgba(r: 0.8, g: 0.0, b: 0.0, a: 1.0) + +/// A `Colour` reprsenting the colour RGBA(164, 0, 0, 1.0) +pub const dark_red = Rgba(r: 0.6431372549019608, g: 0.0, b: 0.0, a: 1.0) + +/// A `Colour` reprsenting the colour RGBA(252, 175, 62, 1.0) +pub const light_orange = Rgba( + r: 0.9882352941176471, + g: 0.6862745098039216, + b: 0.24313725490196078, + a: 1.0, +) + +/// A `Colour` reprsenting the colour RGBA(245, 121, 0, 1.0) +pub const orange = Rgba( + r: 0.9607843137254902, + g: 0.4745098039215686, + b: 0.0, + a: 1.0, +) + +/// A `Colour` reprsenting the colour RGBA(206, 92, 0, 1.0) +pub const dark_orange = Rgba( + r: 0.807843137254902, + g: 0.3607843137254902, + b: 0.0, + a: 1.0, +) + +/// A `Colour` reprsenting the colour RGBA(255, 233, 79, 1.0) +pub const light_yellow = Rgba( + r: 1.0, + g: 0.9137254901960784, + b: 0.30980392156862746, + a: 1.0, +) + +/// A `Colour` reprsenting the colour RGBA(237, 212, 0, 1.0) +pub const yellow = Rgba( + r: 0.9294117647058824, + g: 0.8313725490196079, + b: 0.0, + a: 1.0, +) + +/// A `Colour` reprsenting the colour RGBA(196, 160, 0, 1.0) +pub const dark_yellow = Rgba( + r: 0.7686274509803922, + g: 0.6274509803921569, + b: 0.0, + a: 1.0, +) + +/// A `Colour` reprsenting the colour RGBA(138, 226, 52, 1.0) +pub const light_green = Rgba( + r: 0.5411764705882353, + g: 0.8862745098039215, + b: 0.20392156862745098, + a: 1.0, +) + +/// A `Colour` reprsenting the colour RGBA(115, 210, 22, 1.0) +pub const green = Rgba( + r: 0.45098039215686275, + g: 0.8235294117647058, + b: 0.08627450980392157, + a: 1.0, +) + +/// A `Colour` reprsenting the colour RGBA(78, 154, 6, 1.0) +pub const dark_green = Rgba( + r: 0.3058823529411765, + g: 0.6039215686274509, + b: 0.023529411764705882, + a: 1.0, +) + +/// A `Colour` reprsenting the colour RGBA(114, 159, 207, 1.0) +pub const light_blue = Rgba( + r: 0.4470588235294118, + g: 0.6235294117647059, + b: 0.8117647058823529, + a: 1.0, +) + +/// A `Colour` reprsenting the colour RGBA(52, 101, 164, 1.0) +pub const blue = Rgba( + r: 0.20392156862745098, + g: 0.396078431372549, + b: 0.6431372549019608, + a: 1.0, +) + +/// A `Colour` reprsenting the colour RGBA(32, 74, 135, 1.0) +pub const dark_blue = Rgba( + r: 0.12549019607843137, + g: 0.2901960784313726, + b: 0.5294117647058824, + a: 1.0, +) + +/// A `Colour` reprsenting the colour RGBA(173, 127, 168, 1.0) +pub const light_purple = Rgba( + r: 0.6784313725490196, + g: 0.4980392156862745, + b: 0.6588235294117647, + a: 1.0, +) + +/// A `Colour` reprsenting the colour RGBA(117, 80, 123, 1.0) +pub const purple = Rgba( + r: 0.4588235294117647, + g: 0.3137254901960784, + b: 0.4823529411764706, + a: 1.0, +) + +/// A `Colour` reprsenting the colour RGBA(92, 53, 102, 1.0) +pub const dark_purple = Rgba( + r: 0.3607843137254902, + g: 0.20784313725490197, + b: 0.4, + a: 1.0, +) + +/// A `Colour` reprsenting the colour RGBA(233, 185, 110, 1.0) +pub const light_brown = Rgba( + r: 0.9137254901960784, + g: 0.7254901960784313, + b: 0.43137254901960786, + a: 1.0, +) + +/// A `Colour` reprsenting the colour RGBA(193, 125, 17, 1.0) +pub const brown = Rgba( + r: 0.7568627450980392, + g: 0.49019607843137253, + b: 0.06666666666666667, + a: 1.0, +) + +/// A `Colour` reprsenting the colour RGBA(143, 89, 2, 1.0) +pub const dark_brown = Rgba( + r: 0.5607843137254902, + g: 0.34901960784313724, + b: 0.00784313725490196, + a: 1.0, +) + +/// A `Colour` reprsenting the colour RGBA(0, 0, 0, 1.0) +pub const black = Rgba(r: 0.0, g: 0.0, b: 0.0, a: 1.0) + +/// A `Colour` reprsenting the colour RGBA(255, 255, 255, 1.0) +pub const white = Rgba(r: 1.0, g: 1.0, b: 1.0, a: 1.0) + +/// A `Colour` reprsenting the colour RGBA(238, 238, 236, 1.0) +pub const light_grey = Rgba( + r: 0.9333333333333333, + g: 0.9333333333333333, + b: 0.9254901960784314, + a: 1.0, +) + +/// A `Colour` reprsenting the colour RGBA(211, 215, 207, 1.0) +pub const grey = Rgba( + r: 0.8274509803921568, + g: 0.8431372549019608, + b: 0.8117647058823529, + a: 1.0, +) + +/// A `Colour` reprsenting the colour RGBA(186, 189, 182, 1.0) +pub const dark_grey = Rgba( + r: 0.7294117647058823, + g: 0.7411764705882353, + b: 0.7137254901960784, + a: 1.0, +) + +/// A `Colour` reprsenting the colour RGBA(238, 238, 236, 1.0) +pub const light_gray = Rgba( + r: 0.9333333333333333, + g: 0.9333333333333333, + b: 0.9254901960784314, + a: 1.0, +) + +/// A `Colour` reprsenting the colour RGBA(211, 215, 207, 1.0) +pub const gray = Rgba( + r: 0.8274509803921568, + g: 0.8431372549019608, + b: 0.8117647058823529, + a: 1.0, +) + +/// A `Colour` reprsenting the colour RGBA(186, 189, 182, 1.0) +pub const dark_gray = Rgba( + r: 0.7294117647058823, + g: 0.7411764705882353, + b: 0.7137254901960784, + a: 1.0, +) + +/// A `Colour` reprsenting the colour RGBA(136, 138, 133, 1.0) +pub const light_charcoal = Rgba( + r: 0.5333333333333333, + g: 0.5411764705882353, + b: 0.5215686274509804, + a: 1.0, +) + +/// A `Colour` reprsenting the colour RGBA(85, 87, 83, 1.0) +pub const charcoal = Rgba( + r: 0.3333333333333333, + g: 0.3411764705882353, + b: 0.3254901960784314, + a: 1.0, +) + +/// A `Colour` reprsenting the colour RGBA(46, 52, 54, 1.0) +pub const dark_charcoal = Rgba( + r: 0.1803921568627451, + g: 0.20392156862745098, + b: 0.21176470588235294, + a: 1.0, +) + +/// A `Colour` reprsenting the colour RGBA(255, 175, 243, 1.0) +pub const pink = Rgba( + r: 1.0, + g: 0.6862745098039216, + b: 0.9529411764705882, + a: 1.0, +) diff --git a/build/packages/gleam_community_colour/src/gleam_community/colour/accessibility.gleam b/build/packages/gleam_community_colour/src/gleam_community/colour/accessibility.gleam new file mode 100644 index 0000000..54f75e4 --- /dev/null +++ b/build/packages/gleam_community_colour/src/gleam_community/colour/accessibility.gleam @@ -0,0 +1,173 @@ +//// +//// - **Accessibility** +//// - [`luminance`](#luminance) +//// - [`contrast_ratio`](#contrast_ratio) +//// - [`maximum_contrast`](#maximum_contrast) +//// +//// --- +//// +//// This package was heavily inspired by the `elm-color-extra` module. +//// The original source code can be found +//// here. +//// +////
+//// The license of that package is produced below: +//// +//// +//// > MIT License +//// +//// > Copyright (c) 2016 Andreas Köberle +//// +//// > Permission is hereby granted, free of charge, to any person obtaining a copy +//// of this software and associated documentation files (the "Software"), to deal +//// in the Software without restriction, including without limitation the rights +//// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +//// copies of the Software, and to permit persons to whom the Software is +//// furnished to do so, subject to the following conditions: +//// +//// > The above copyright notice and this permission notice shall be included in all +//// copies or substantial portions of the Software. +//// +//// > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +//// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//// SOFTWARE. +//// +////
+//// + +// Just in case we decide in the future to no longer include the above reference +// and license, this package was initially a port of the `elm-color-extra` module: +// +// https://github.com/noahzgordon/elm-color-extra +// + +// IMPORTS -------------------------------------------------------------------- + +import gleam/float +import gleam/list +import gleam_community/colour.{type Colour} + +// UTILITIES ------------------------------------------------------------------ + +fn intensity(colour_value: Float) -> Float { + // Calculation taken from https://www.w3.org/TR/WCAG20/#relativeluminancedef + case True { + _ if colour_value <=. 0.03928 -> colour_value /. 12.92 + _ -> { + // Is this guaranteed to be `OK`? + let assert Ok(i) = float.power({ colour_value +. 0.055 } /. 1.055, 2.4) + i + } + } +} + +// ACCESSIBILITY -------------------------------------------------------------- + +/// Returns the relative brightness of the given `Colour` as a `Float` between +/// 0.0, and 1.0 with 0.0 being the darkest possible colour and 1.0 being the lightest. +/// +///
+/// Example: +/// +/// ```gleam +/// fn example() { +/// luminance(colour.white) // 1.0 +/// } +/// ``` +///
+/// +/// +/// +pub fn luminance(colour: Colour) -> Float { + // Calculation taken from https://www.w3.org/TR/WCAG20/#relativeluminancedef + let #(r, g, b, _) = colour.to_rgba(colour) + + let r_intensity = intensity(r) + let g_intensity = intensity(g) + let b_intensity = intensity(b) + + 0.2126 *. r_intensity +. 0.7152 *. g_intensity +. 0.0722 *. b_intensity +} + +/// Returns the contrast between two `Colour` values as a `Float` between 1.0, +/// and 21.0 with 1.0 being no contrast and, 21.0 being the highest possible contrast. +/// +///
+/// Example: +/// +/// ```gleam +/// fn example() { +/// contrast_ratio(between: colour.white, and: colour.black) // 21.0 +/// } +/// ``` +///
+/// +/// +/// +pub fn contrast_ratio(between colour_a: Colour, and colour_b: Colour) -> Float { + // Calculation taken from https://www.w3.org/TR/WCAG20/#contrast-ratiodef + let luminance_a = luminance(colour_a) +. 0.05 + let luminance_b = luminance(colour_b) +. 0.05 + + case luminance_a >. luminance_b { + True -> luminance_a /. luminance_b + False -> luminance_b /. luminance_a + } +} + +/// Returns the `Colour` with the highest contrast between the base `Colour`, +/// and and the other provided `Colour` values. +/// +///
+/// Example: +/// +/// ```gleam +/// fn example() { +/// maximum_contrast( +/// colour.yellow, +/// [colour.white, colour.dark_blue, colour.green], +/// ) +/// } +/// ``` +///
+/// +/// +/// +pub fn maximum_contrast( + base: Colour, + colours: List(Colour), +) -> Result(Colour, Nil) { + colours + |> list.sort(fn(colour_a, colour_b) { + let contrast_a = contrast_ratio(base, colour_a) + let contrast_b = contrast_ratio(base, colour_b) + + float.compare(contrast_b, contrast_a) + }) + |> list.first() +} diff --git a/build/packages/gleam_community_colour/src/gleam_community@colour.erl b/build/packages/gleam_community_colour/src/gleam_community@colour.erl new file mode 100644 index 0000000..faee070 --- /dev/null +++ b/build/packages/gleam_community_colour/src/gleam_community@colour.erl @@ -0,0 +1,1199 @@ +-module(gleam_community@colour). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). +-define(FILEPATH, "src/gleam_community/colour.gleam"). +-export([from_rgb255/3, from_rgb/3, from_rgba/4, from_hsla/4, from_hsl/3, from_rgb_hex/1, from_rgb_hex_string/1, from_rgba_hex/1, from_rgba_hex_string/1, to_rgba/1, to_hsla/1, to_css_rgba_string/1, to_rgba_hex/1, to_rgba_hex_string/1, to_rgb_hex/1, to_rgb_hex_string/1, encode/1, decoder/0]). +-export_type([colour/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC( + "\n" + " - **Types**\n" + " - [`Colour`](#Colour)\n" + " - [`Color`](#Color)\n" + " - **Constructors**\n" + " - [`from_rgb255`](#from_rgb255)\n" + " - [`from_rgb`](#from_rgb)\n" + " - [`from_rgba`](#from_rgba)\n" + " - [`from_hsl`](#from_hsl)\n" + " - [`from_hsla`](#from_hsla)\n" + " - [`from_rgb_hex`](#from_rgb_hex)\n" + " - [`from_rgba_hex`](#from_rgba_hex)\n" + " - [`from_rgb_hex_string`](#from_rgb_hex_string)\n" + " - [`from_rgba_hex_string`](#from_rgba_hex_string)\n" + " - **Conversions**\n" + " - [`to_rgba`](#to_rgba)\n" + " - [`to_hsla`](#hsla)\n" + " - [`to_css_rgba_string`](#to_css_rgba_string)\n" + " - [`to_rgba_hex_string`](#to_rgba_hex_string)\n" + " - [`to_rgb_hex_string`](#to_rgb_hex_string)\n" + " - [`to_rgba_hex`](#to_rgba_hex)\n" + " - [`to_rgb_hex`](#to_rgb_hex)\n" + " - **JSON**\n" + " - [`encode`](#encode)\n" + " - [`decoder`](#decoder)\n" + " - **Colours**\n" + " - [`light_red`](#light_red)\n" + " - [`red`](#red)\n" + " - [`dark_red`](#dark_red)\n" + " - [`light_orange`](#light_orange)\n" + " - [`orange`](#orange)\n" + " - [`dark_orange`](#dark_orange)\n" + " - [`light_yellow`](#light_yellow)\n" + " - [`yellow`](#yellow)\n" + " - [`dark_yellow`](#dark_yellow)\n" + " - [`light_green`](#light_green)\n" + " - [`green`](#green)\n" + " - [`dark_green`](#dark_green)\n" + " - [`light_blue`](#light_blue)\n" + " - [`blue`](#blue)\n" + " - [`dark_blue`](#dark_blue)\n" + " - [`light_purple`](#light_purple)\n" + " - [`purple`](#purple)\n" + " - [`dark_purple`](#dark_purple)\n" + " - [`light_brown`](#light_brown)\n" + " - [`brown`](#brown)\n" + " - [`dark_brown`](#dark_brown)\n" + " - [`black`](#black)\n" + " - [`white`](#white)\n" + " - [`light_grey`](#light_grey)\n" + " - [`grey`](#grey)\n" + " - [`dark_grey`](#dark_grey)\n" + " - [`light_gray`](#light_gray)\n" + " - [`gray`](#gray)\n" + " - [`dark_gray`](#dark_gray)\n" + " - [`light_charcoal`](#light_charcoal)\n" + " - [`charcoal`](#charcoal)\n" + " - [`dark_charcoal`](#dark_charcoal)\n" + " - [`pink`](#pink)\n" + "\n" + " ---\n" + "\n" + " This package was heavily inspired by the `elm-color` module.\n" + " The original source code can be found\n" + " here.\n" + "\n" + "
\n" + " The license of that package is produced below:\n" + "\n" + "\n" + " > MIT License\n" + "\n" + " > Copyright 2018 Aaron VonderHaar\n" + "\n" + " > Redistribution and use in source and binary forms, with or without modification,\n" + " are permitted provided that the following conditions are met:\n" + "\n" + " 1. Redistributions of source code must retain the above copyright notice,\n" + " this list of conditions and the following disclaimer.\n" + "\n" + " 2. Redistributions in binary form must reproduce the above copyright notice,\n" + " this list of conditions and the following disclaimer in the documentation\n" + " and/or other materials provided with the distribution.\n" + "\n" + " 3. Neither the name of the copyright holder nor the names of its contributors\n" + " may be used to endorse or promote products derived from this software without\n" + " specific prior written permission.\n" + "\n" + " > THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\n" + " ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n" + " OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL\n" + " THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n" + " EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n" + " OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" + " THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n" + " ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" + "\n" + " > The above copyright notice and this permission notice shall be included in all\n" + " copies or substantial portions of the Software.\n" + "
\n" + "\n" +). + +-opaque colour() :: {rgba, float(), float(), float(), float()} | + {hsla, float(), float(), float(), float()}. + +-file("src/gleam_community/colour.gleam", 155). +-spec valid_colour_value(float()) -> {ok, float()} | {error, nil}. +valid_colour_value(C) -> + case (C > 1.0) orelse (C < +0.0) of + true -> + {error, nil}; + + false -> + {ok, C} + end. + +-file("src/gleam_community/colour.gleam", 162). +-spec hue_to_rgb(float(), float(), float()) -> float(). +hue_to_rgb(Hue, M1, M2) -> + H = case Hue of + _ when Hue < +0.0 -> + Hue + 1.0; + + _ when Hue > 1.0 -> + Hue - 1.0; + + _ -> + Hue + end, + H_t_6 = H * 6.0, + H_t_2 = H * 2.0, + H_t_3 = H * 3.0, + case H of + _ when H_t_6 < 1.0 -> + M1 + (((M2 - M1) * H) * 6.0); + + _ when H_t_2 < 1.0 -> + M2; + + _ when H_t_3 < 2.0 -> + M1 + (((M2 - M1) * ((2.0 / 3.0) - H)) * 6.0); + + _ -> + M1 + end. + +-file("src/gleam_community/colour.gleam", 181). +-spec hex_string_to_int(binary()) -> {ok, integer()} | {error, nil}. +hex_string_to_int(Hex_string) -> + Hex = case Hex_string of + <<"#"/utf8, Hex_number/binary>> -> + Hex_number; + + <<"0x"/utf8, Hex_number@1/binary>> -> + Hex_number@1; + + _ -> + Hex_string + end, + _pipe = Hex, + _pipe@1 = string:lowercase(_pipe), + _pipe@2 = gleam@string:to_graphemes(_pipe@1), + _pipe@3 = lists:reverse(_pipe@2), + gleam@list:index_fold( + _pipe@3, + {ok, 0}, + fun(Total, Char, Index) -> case Total of + {error, nil} -> + {error, nil}; + + {ok, V} -> + gleam@result:'try'(case Char of + <<"a"/utf8>> -> + {ok, 10}; + + <<"b"/utf8>> -> + {ok, 11}; + + <<"c"/utf8>> -> + {ok, 12}; + + <<"d"/utf8>> -> + {ok, 13}; + + <<"e"/utf8>> -> + {ok, 14}; + + <<"f"/utf8>> -> + {ok, 15}; + + _ -> + gleam_stdlib:parse_int(Char) + end, fun(Num) -> + gleam@result:'try'( + gleam@int:power(16, erlang:float(Index)), + fun(Base) -> + {ok, + V + erlang:round( + erlang:float(Num) * Base + )} + end + ) + end) + end end + ). + +-file("src/gleam_community/colour.gleam", 212). +-spec hsla_to_rgba(float(), float(), float(), float()) -> {float(), + float(), + float(), + float()}. +hsla_to_rgba(H, S, L, A) -> + M2 = case L =< 0.5 of + true -> + L * (S + 1.0); + + false -> + (L + S) - (L * S) + end, + M1 = (L * 2.0) - M2, + R = hue_to_rgb(H + (1.0 / 3.0), M1, M2), + G = hue_to_rgb(H, M1, M2), + B = hue_to_rgb(H - (1.0 / 3.0), M1, M2), + {R, G, B, A}. + +-file("src/gleam_community/colour.gleam", 232). +-spec rgba_to_hsla(float(), float(), float(), float()) -> {float(), + float(), + float(), + float()}. +rgba_to_hsla(R, G, B, A) -> + Min_colour = gleam@float:min(R, gleam@float:min(G, B)), + Max_colour = gleam@float:max(R, gleam@float:max(G, B)), + H1 = case true of + _ when Max_colour =:= R -> + gleam@float:divide(G - B, Max_colour - Min_colour); + + _ when Max_colour =:= G -> + _pipe = gleam@float:divide(B - R, Max_colour - Min_colour), + gleam@result:'try'(_pipe, fun(D) -> {ok, 2.0 + D} end); + + _ -> + _pipe@1 = gleam@float:divide(R - G, Max_colour - Min_colour), + gleam@result:'try'(_pipe@1, fun(D@1) -> {ok, 4.0 + D@1} end) + end, + H2 = case H1 of + {ok, V} -> + {ok, V * (1.0 / 6.0)}; + + _ -> + H1 + end, + H3 = case H2 of + {ok, V@1} when V@1 < +0.0 -> + V@1 + 1.0; + + {ok, V@2} -> + V@2; + + _ -> + +0.0 + end, + L = (Min_colour + Max_colour) / 2.0, + S = case true of + _ when Min_colour =:= Max_colour -> + +0.0; + + _ when L < 0.5 -> + case (Max_colour + Min_colour) of + +0.0 -> +0.0; + -0.0 -> -0.0; + Gleam@denominator -> (Max_colour - Min_colour) / Gleam@denominator + end; + + _ -> + case ((2.0 - Max_colour) - Min_colour) of + +0.0 -> +0.0; + -0.0 -> -0.0; + Gleam@denominator@1 -> (Max_colour - Min_colour) / Gleam@denominator@1 + end + end, + {H3, S, L, A}. + +-file("src/gleam_community/colour.gleam", 300). +?DOC( + " Returns a `Result(Colour)` created from the given 8 bit RGB values.\n" + "\n" + " Returns `Error(Nil)` if the supplied RGB values are greater than 255 or less than 0.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red) = from_rgb255(255, 0, 0)\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec from_rgb255(integer(), integer(), integer()) -> {ok, colour()} | + {error, nil}. +from_rgb255(Red, Green, Blue) -> + gleam@result:'try'( + begin + _pipe = Red, + _pipe@1 = erlang:float(_pipe), + _pipe@2 = gleam@float:divide(_pipe@1, 255.0), + gleam@result:'try'(_pipe@2, fun valid_colour_value/1) + end, + fun(R) -> + gleam@result:'try'( + begin + _pipe@3 = Green, + _pipe@4 = erlang:float(_pipe@3), + _pipe@5 = gleam@float:divide(_pipe@4, 255.0), + gleam@result:'try'(_pipe@5, fun valid_colour_value/1) + end, + fun(G) -> + gleam@result:'try'( + begin + _pipe@6 = Blue, + _pipe@7 = erlang:float(_pipe@6), + _pipe@8 = gleam@float:divide(_pipe@7, 255.0), + gleam@result:'try'( + _pipe@8, + fun valid_colour_value/1 + ) + end, + fun(B) -> {ok, {rgba, R, G, B, 1.0}} end + ) + end + ) + end + ). + +-file("src/gleam_community/colour.gleam", 348). +?DOC( + " Returns `Result(Colour)` created from the given RGB values.\n" + "\n" + " If the supplied RGB values are greater than 1.0 or less than 0.0 returns `Error(Nil)`\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red) = from_rgb(1.0, 0.0, 0.0)\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec from_rgb(float(), float(), float()) -> {ok, colour()} | {error, nil}. +from_rgb(Red, Green, Blue) -> + gleam@result:'try'( + valid_colour_value(Red), + fun(R) -> + gleam@result:'try'( + valid_colour_value(Green), + fun(G) -> + gleam@result:'try'( + valid_colour_value(Blue), + fun(B) -> {ok, {rgba, R, G, B, 1.0}} end + ) + end + ) + end + ). + +-file("src/gleam_community/colour.gleam", 383). +?DOC( + " Returns `Result(Colour)` created from the given RGBA values.\n" + "\n" + " Returns `Error(Nil)` if the supplied RGBA values are greater than 1.0 or less than 0.0.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red_half_opacity) = from_rbga(1.0, 0.0, 0.0, 0.5)\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec from_rgba(float(), float(), float(), float()) -> {ok, colour()} | + {error, nil}. +from_rgba(Red, Green, Blue, Alpha) -> + gleam@result:'try'( + valid_colour_value(Red), + fun(R) -> + gleam@result:'try'( + valid_colour_value(Green), + fun(G) -> + gleam@result:'try'( + valid_colour_value(Blue), + fun(B) -> + gleam@result:'try'( + valid_colour_value(Alpha), + fun(A) -> {ok, {rgba, R, G, B, A}} end + ) + end + ) + end + ) + end + ). + +-file("src/gleam_community/colour.gleam", 420). +?DOC( + " Returns `Result(Colour)` created from the given HSLA values.\n" + "\n" + " Returns `Error(Nil)`f the supplied HSLA values are greater than 1.0 or less than 0.0.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red_half_opacity) = from_hsla(0.0, 1.0, 0.5, 0.5)\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec from_hsla(float(), float(), float(), float()) -> {ok, colour()} | + {error, nil}. +from_hsla(Hue, Saturation, Lightness, Alpha) -> + gleam@result:'try'( + valid_colour_value(Hue), + fun(H) -> + gleam@result:'try'( + valid_colour_value(Saturation), + fun(S) -> + gleam@result:'try'( + valid_colour_value(Lightness), + fun(L) -> + gleam@result:'try'( + valid_colour_value(Alpha), + fun(A) -> {ok, {hsla, H, S, L, A}} end + ) + end + ) + end + ) + end + ). + +-file("src/gleam_community/colour.gleam", 457). +?DOC( + " Returns `Result(Colour)` created from the given HSL values.\n" + "\n" + " Returns `Error(Nil)` if the supplied HSL values are greater than 1.0 or less than 0.0.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red) = from_hsla(0.0, 1.0, 0.5)\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec from_hsl(float(), float(), float()) -> {ok, colour()} | {error, nil}. +from_hsl(Hue, Saturation, Lightness) -> + from_hsla(Hue, Saturation, Lightness, 1.0). + +-file("src/gleam_community/colour.gleam", 488). +?DOC( + " Returns a `Result(Colour)` created from the given hex `Int`.\n" + "\n" + " Returns `Error(Nil)` if the supplied hex `Int is greater than 0xffffff or less than 0x0.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red) = from_rgb_hex(0xff0000)\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec from_rgb_hex(integer()) -> {ok, colour()} | {error, nil}. +from_rgb_hex(Hex) -> + case (Hex > 16#ffffff) orelse (Hex < 0) of + true -> + {error, nil}; + + false -> + R = begin + _pipe = erlang:'bsr'(Hex, 16), + erlang:'band'(_pipe, 16#ff) + end, + G = begin + _pipe@1 = erlang:'bsr'(Hex, 8), + erlang:'band'(_pipe@1, 16#ff) + end, + B = erlang:'band'(Hex, 16#ff), + from_rgb255(R, G, B) + end. + +-file("src/gleam_community/colour.gleam", 527). +?DOC( + " Returns a `Result(Colour)` created from the given RGB hex `String`.\n" + "\n" + " Returns `Error(Nil)` if the supplied hex `String` is invalid, or greater than `\"#ffffff\" or less than `\"#0\"`\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red) = from_rgb_hex_string(\"#ff0000\")\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec from_rgb_hex_string(binary()) -> {ok, colour()} | {error, nil}. +from_rgb_hex_string(Hex_string) -> + gleam@result:'try'( + hex_string_to_int(Hex_string), + fun(Hex_int) -> from_rgb_hex(Hex_int) end + ). + +-file("src/gleam_community/colour.gleam", 585). +?DOC( + " Returns a `Result(Colour)` created from the given hex `Int`.\n" + "\n" + " Returns `Error(Nil)` if the supplied hex `Int is greater than 0xffffffff or less than 0x0.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red_half_opacity) = from_rgba_hex(0xff00007f)\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec from_rgba_hex(integer()) -> {ok, colour()} | {error, nil}. +from_rgba_hex(Hex) -> + case (Hex > 16#ffffffff) orelse (Hex < 0) of + true -> + {error, nil}; + + false -> + R@1 = case begin + _pipe = erlang:'bsr'(Hex, 24), + _pipe@1 = erlang:'band'(_pipe, 16#ff), + _pipe@2 = erlang:float(_pipe@1), + gleam@float:divide(_pipe@2, 255.0) + end of + {ok, R} -> R; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Pattern match failed, no pattern matched the value."/utf8>>, + file => <>, + module => <<"gleam_community/colour"/utf8>>, + function => <<"from_rgba_hex"/utf8>>, + line => 590, + value => _assert_fail, + start => 17111, + 'end' => 17260, + pattern_start => 17122, + pattern_end => 17127}) + end, + G@1 = case begin + _pipe@3 = erlang:'bsr'(Hex, 16), + _pipe@4 = erlang:'band'(_pipe@3, 16#ff), + _pipe@5 = erlang:float(_pipe@4), + gleam@float:divide(_pipe@5, 255.0) + end of + {ok, G} -> G; + _assert_fail@1 -> + erlang:error(#{gleam_error => let_assert, + message => <<"Pattern match failed, no pattern matched the value."/utf8>>, + file => <>, + module => <<"gleam_community/colour"/utf8>>, + function => <<"from_rgba_hex"/utf8>>, + line => 596, + value => _assert_fail@1, + start => 17332, + 'end' => 17481, + pattern_start => 17343, + pattern_end => 17348}) + end, + B@1 = case begin + _pipe@6 = erlang:'bsr'(Hex, 8), + _pipe@7 = erlang:'band'(_pipe@6, 16#ff), + _pipe@8 = erlang:float(_pipe@7), + gleam@float:divide(_pipe@8, 255.0) + end of + {ok, B} -> B; + _assert_fail@2 -> + erlang:error(#{gleam_error => let_assert, + message => <<"Pattern match failed, no pattern matched the value."/utf8>>, + file => <>, + module => <<"gleam_community/colour"/utf8>>, + function => <<"from_rgba_hex"/utf8>>, + line => 602, + value => _assert_fail@2, + start => 17553, + 'end' => 17701, + pattern_start => 17564, + pattern_end => 17569}) + end, + A@1 = case begin + _pipe@9 = erlang:'band'(Hex, 16#ff), + _pipe@10 = erlang:float(_pipe@9), + gleam@float:divide(_pipe@10, 255.0) + end of + {ok, A} -> A; + _assert_fail@3 -> + erlang:error(#{gleam_error => let_assert, + message => <<"Pattern match failed, no pattern matched the value."/utf8>>, + file => <>, + module => <<"gleam_community/colour"/utf8>>, + function => <<"from_rgba_hex"/utf8>>, + line => 608, + value => _assert_fail@3, + start => 17773, + 'end' => 17883, + pattern_start => 17784, + pattern_end => 17789}) + end, + from_rgba(R@1, G@1, B@1, A@1) + end. + +-file("src/gleam_community/colour.gleam", 556). +?DOC( + " Returns a `Result(Colour)` created from the given RGBA hex `String`.\n" + "\n" + " Returns `Error(Nil)` if the supplied hex `String` is invalid, or greater than `\"#ffffffff\" or less than `\"#0\"`\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red_half_opacity) = from_rgba_hex_string(\"#ff00007f\")\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec from_rgba_hex_string(binary()) -> {ok, colour()} | {error, nil}. +from_rgba_hex_string(Hex_string) -> + gleam@result:'try'( + hex_string_to_int(Hex_string), + fun(Hex_int) -> from_rgba_hex(Hex_int) end + ). + +-file("src/gleam_community/colour.gleam", 642). +?DOC( + " Returns `#(Float, Float, Float, Float)` representing the given `Colour`'s\n" + " R, G, B, and A values respectively.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red) = from_rgb255(255, 0, 0)\n" + " let #(r, g, b, a) = to_rgba(red)\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec to_rgba(colour()) -> {float(), float(), float(), float()}. +to_rgba(Colour) -> + case Colour of + {rgba, R, G, B, A} -> + {R, G, B, A}; + + {hsla, H, S, L, A@1} -> + hsla_to_rgba(H, S, L, A@1) + end. + +-file("src/gleam_community/colour.gleam", 672). +?DOC( + " Returns `#(Float, Float, Float, Float)` representing the given `Colour`'s\n" + " H, S, L, and A values respectively.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red) = from_rgb255(255, 0, 0)\n" + " let #(h, s, l, a) = to_hsla(red)\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec to_hsla(colour()) -> {float(), float(), float(), float()}. +to_hsla(Colour) -> + case Colour of + {hsla, H, S, L, A} -> + {H, S, L, A}; + + {rgba, R, G, B, A@1} -> + rgba_to_hsla(R, G, B, A@1) + end. + +-file("src/gleam_community/colour.gleam", 701). +?DOC( + " Returns an rgba formatted CSS `String` created from the given `Colour`.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red) = from_rgb255(255, 0, 0)\n" + " let css_red = to_css_rgba_string(red)\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec to_css_rgba_string(colour()) -> binary(). +to_css_rgba_string(Colour) -> + {R, G, B, A} = to_rgba(Colour), + Percent = fun(X) -> + P@1 = case begin + _pipe = X, + _pipe@1 = gleam@float:multiply(_pipe, 10000.0), + _pipe@2 = erlang:round(_pipe@1), + _pipe@3 = erlang:float(_pipe@2), + gleam@float:divide(_pipe@3, 100.0) + end of + {ok, P} -> P; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Pattern match failed, no pattern matched the value."/utf8>>, + file => <>, + module => <<"gleam_community/colour"/utf8>>, + function => <<"to_css_rgba_string"/utf8>>, + line => 706, + value => _assert_fail, + start => 20510, + 'end' => 20646, + pattern_start => 20521, + pattern_end => 20526}) + end, + P@1 + end, + Round_to = fun(X@1) -> + R@2 = case begin + _pipe@4 = X@1, + _pipe@5 = gleam@float:multiply(_pipe@4, 1000.0), + _pipe@6 = erlang:round(_pipe@5), + _pipe@7 = erlang:float(_pipe@6), + gleam@float:divide(_pipe@7, 1000.0) + end of + {ok, R@1} -> R@1; + _assert_fail@1 -> + erlang:error(#{gleam_error => let_assert, + message => <<"Pattern match failed, no pattern matched the value."/utf8>>, + file => <>, + module => <<"gleam_community/colour"/utf8>>, + function => <<"to_css_rgba_string"/utf8>>, + line => 718, + value => _assert_fail@1, + start => 20768, + 'end' => 20903, + pattern_start => 20779, + pattern_end => 20784}) + end, + R@2 + end, + gleam@string:join( + [<<"rgba("/utf8>>, + <<(gleam_stdlib:float_to_string(Percent(R)))/binary, "%,"/utf8>>, + <<(gleam_stdlib:float_to_string(Percent(G)))/binary, "%,"/utf8>>, + <<(gleam_stdlib:float_to_string(Percent(B)))/binary, "%,"/utf8>>, + gleam_stdlib:float_to_string(Round_to(A)), + <<")"/utf8>>], + <<""/utf8>> + ). + +-file("src/gleam_community/colour.gleam", 829). +?DOC( + " Returns an hex `Int` created from the given `Colour`.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red) = from_rgba(1.0, 0.0, 0.0, 1.0)\n" + " let red_hex_int = to_rgba_hex(red)\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec to_rgba_hex(colour()) -> integer(). +to_rgba_hex(Colour) -> + {R, G, B, A} = to_rgba(Colour), + Red = begin + _pipe = R * 255.0, + _pipe@1 = erlang:round(_pipe), + erlang:'bsl'(_pipe@1, 24) + end, + Green = begin + _pipe@2 = G * 255.0, + _pipe@3 = erlang:round(_pipe@2), + erlang:'bsl'(_pipe@3, 16) + end, + Blue = begin + _pipe@4 = B * 255.0, + _pipe@5 = erlang:round(_pipe@4), + erlang:'bsl'(_pipe@5, 8) + end, + Alpha = begin + _pipe@6 = A * 255.0, + erlang:round(_pipe@6) + end, + ((Red + Green) + Blue) + Alpha. + +-file("src/gleam_community/colour.gleam", 763). +?DOC( + " Returns an rgba hex formatted `String` created from the given `Colour`.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red) = from_rgba(1.0, 0.0, 0.0, 1.0)\n" + " let red_hex = to_rgba_hex_string(red)\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec to_rgba_hex_string(colour()) -> binary(). +to_rgba_hex_string(Colour) -> + Hex_string = begin + _pipe = to_rgba_hex(Colour), + gleam@int:to_base16(_pipe) + end, + case string:length(Hex_string) of + 8 -> + Hex_string; + + L -> + <<(gleam@string:repeat(<<"0"/utf8>>, 8 - L))/binary, + Hex_string/binary>> + end. + +-file("src/gleam_community/colour.gleam", 876). +?DOC( + " Returns a rgb hex `Int` created from the given `Colour`.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red) = from_rgba(255, 0, 0)\n" + " let red_hex_int = to_rgb_hex(red)\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec to_rgb_hex(colour()) -> integer(). +to_rgb_hex(Colour) -> + {R, G, B, _} = to_rgba(Colour), + Red = begin + _pipe = R * 255.0, + _pipe@1 = erlang:round(_pipe), + erlang:'bsl'(_pipe@1, 16) + end, + Green = begin + _pipe@2 = G * 255.0, + _pipe@3 = erlang:round(_pipe@2), + erlang:'bsl'(_pipe@3, 8) + end, + Blue = begin + _pipe@4 = B * 255.0, + erlang:round(_pipe@4) + end, + (Red + Green) + Blue. + +-file("src/gleam_community/colour.gleam", 796). +?DOC( + " Returns an rgb hex formatted `String` created from the given `Colour`.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " assert Ok(red) = from_rgba(255, 0, 0)\n" + " let red_hex = to_rgb_hex_string(red)\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec to_rgb_hex_string(colour()) -> binary(). +to_rgb_hex_string(Colour) -> + Hex_string = begin + _pipe = to_rgb_hex(Colour), + gleam@int:to_base16(_pipe) + end, + case string:length(Hex_string) of + 6 -> + Hex_string; + + L -> + <<(gleam@string:repeat(<<"0"/utf8>>, 6 - L))/binary, + Hex_string/binary>> + end. + +-file("src/gleam_community/colour.gleam", 918). +-spec encode_rgba(float(), float(), float(), float()) -> gleam@json:json(). +encode_rgba(R, G, B, A) -> + gleam@json:object( + [{<<"r"/utf8>>, gleam@json:float(R)}, + {<<"g"/utf8>>, gleam@json:float(G)}, + {<<"b"/utf8>>, gleam@json:float(B)}, + {<<"a"/utf8>>, gleam@json:float(A)}] + ). + +-file("src/gleam_community/colour.gleam", 927). +-spec encode_hsla(float(), float(), float(), float()) -> gleam@json:json(). +encode_hsla(H, S, L, A) -> + gleam@json:object( + [{<<"h"/utf8>>, gleam@json:float(H)}, + {<<"s"/utf8>>, gleam@json:float(S)}, + {<<"l"/utf8>>, gleam@json:float(L)}, + {<<"a"/utf8>>, gleam@json:float(A)}] + ). + +-file("src/gleam_community/colour.gleam", 911). +?DOC( + " Encodes a `Colour` value as a Gleam [`Json`](https://hexdocs.pm/gleam_json/gleam/json.html#Json)\n" + " value. You'll need this if you want to send a `Colour` value over the network\n" + " in a HTTP request or response, for example.\n" + "\n" + " \n" +). +-spec encode(colour()) -> gleam@json:json(). +encode(Colour) -> + case Colour of + {rgba, R, G, B, A} -> + encode_rgba(R, G, B, A); + + {hsla, H, S, L, A@1} -> + encode_hsla(H, S, L, A@1) + end. + +-file("src/gleam_community/colour.gleam", 952). +-spec rgba_decoder() -> gleam@dynamic@decode:decoder(colour()). +rgba_decoder() -> + gleam@dynamic@decode:field( + <<"r"/utf8>>, + {decoder, fun gleam@dynamic@decode:decode_float/1}, + fun(R) -> + gleam@dynamic@decode:field( + <<"g"/utf8>>, + {decoder, fun gleam@dynamic@decode:decode_float/1}, + fun(G) -> + gleam@dynamic@decode:field( + <<"b"/utf8>>, + {decoder, fun gleam@dynamic@decode:decode_float/1}, + fun(B) -> + gleam@dynamic@decode:field( + <<"a"/utf8>>, + {decoder, + fun gleam@dynamic@decode:decode_float/1}, + fun(A) -> + gleam@dynamic@decode:success( + {rgba, R, G, B, A} + ) + end + ) + end + ) + end + ) + end + ). + +-file("src/gleam_community/colour.gleam", 961). +-spec hsla_decoder() -> gleam@dynamic@decode:decoder(colour()). +hsla_decoder() -> + gleam@dynamic@decode:field( + <<"h"/utf8>>, + {decoder, fun gleam@dynamic@decode:decode_float/1}, + fun(H) -> + gleam@dynamic@decode:field( + <<"s"/utf8>>, + {decoder, fun gleam@dynamic@decode:decode_float/1}, + fun(S) -> + gleam@dynamic@decode:field( + <<"l"/utf8>>, + {decoder, fun gleam@dynamic@decode:decode_float/1}, + fun(L) -> + gleam@dynamic@decode:field( + <<"a"/utf8>>, + {decoder, + fun gleam@dynamic@decode:decode_float/1}, + fun(A) -> + gleam@dynamic@decode:success( + {hsla, H, S, L, A} + ) + end + ) + end + ) + end + ) + end + ). + +-file("src/gleam_community/colour.gleam", 948). +?DOC( + " Attempt to decode some [`Dynamic`](https://hexdocs.pm/gleam_stdlib/gleam/dynamic.html#Dynamic)\n" + " value into a `Colour`. Most often you'll use this to decode some JSON.\n" + "\n" + " \n" +). +-spec decoder() -> gleam@dynamic@decode:decoder(colour()). +decoder() -> + gleam@dynamic@decode:one_of(rgba_decoder(), [hsla_decoder()]). diff --git a/build/packages/gleam_community_colour/src/gleam_community@colour@accessibility.erl b/build/packages/gleam_community_colour/src/gleam_community@colour@accessibility.erl new file mode 100644 index 0000000..35c1d67 --- /dev/null +++ b/build/packages/gleam_community_colour/src/gleam_community@colour@accessibility.erl @@ -0,0 +1,203 @@ +-module(gleam_community@colour@accessibility). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). +-define(FILEPATH, "src/gleam_community/colour/accessibility.gleam"). +-export([luminance/1, contrast_ratio/2, maximum_contrast/2]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC( + " \n" + " - **Accessibility**\n" + " - [`luminance`](#luminance)\n" + " - [`contrast_ratio`](#contrast_ratio)\n" + " - [`maximum_contrast`](#maximum_contrast)\n" + "\n" + " ---\n" + "\n" + " This package was heavily inspired by the `elm-color-extra` module.\n" + " The original source code can be found\n" + " here.\n" + "\n" + "
\n" + " The license of that package is produced below:\n" + " \n" + " \n" + " > MIT License\n" + "\n" + " > Copyright (c) 2016 Andreas Köberle\n" + "\n" + " > Permission is hereby granted, free of charge, to any person obtaining a copy\n" + " of this software and associated documentation files (the \"Software\"), to deal\n" + " in the Software without restriction, including without limitation the rights\n" + " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n" + " copies of the Software, and to permit persons to whom the Software is\n" + " furnished to do so, subject to the following conditions:\n" + "\n" + " > The above copyright notice and this permission notice shall be included in all\n" + " copies or substantial portions of the Software.\n" + "\n" + " > THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n" + " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n" + " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n" + " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n" + " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n" + " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n" + " SOFTWARE.\n" + "\n" + "
\n" + "\n" +). + +-file("src/gleam_community/colour/accessibility.gleam", 56). +-spec intensity(float()) -> float(). +intensity(Colour_value) -> + case true of + _ when Colour_value =< 0.03928 -> + Colour_value / 12.92; + + _ -> + I@1 = case gleam@float:power((Colour_value + 0.055) / 1.055, 2.4) of + {ok, I} -> I; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Pattern match failed, no pattern matched the value."/utf8>>, + file => <>, + module => <<"gleam_community/colour/accessibility"/utf8>>, + function => <<"intensity"/utf8>>, + line => 62, + value => _assert_fail, + start => 2399, + 'end' => 2470, + pattern_start => 2410, + pattern_end => 2415}) + end, + I@1 + end. + +-file("src/gleam_community/colour/accessibility.gleam", 92). +?DOC( + " Returns the relative brightness of the given `Colour` as a `Float` between\n" + " 0.0, and 1.0 with 0.0 being the darkest possible colour and 1.0 being the lightest.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " luminance(colour.white) // 1.0\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec luminance(gleam_community@colour:colour()) -> float(). +luminance(Colour) -> + {R, G, B, _} = gleam_community@colour:to_rgba(Colour), + R_intensity = intensity(R), + G_intensity = intensity(G), + B_intensity = intensity(B), + ((0.2126 * R_intensity) + (0.7152 * G_intensity)) + (0.0722 * B_intensity). + +-file("src/gleam_community/colour/accessibility.gleam", 125). +?DOC( + " Returns the contrast between two `Colour` values as a `Float` between 1.0,\n" + " and 21.0 with 1.0 being no contrast and, 21.0 being the highest possible contrast.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " contrast_ratio(between: colour.white, and: colour.black) // 21.0\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec contrast_ratio( + gleam_community@colour:colour(), + gleam_community@colour:colour() +) -> float(). +contrast_ratio(Colour_a, Colour_b) -> + Luminance_a = luminance(Colour_a) + 0.05, + Luminance_b = luminance(Colour_b) + 0.05, + case Luminance_a > Luminance_b of + true -> + case Luminance_b of + +0.0 -> +0.0; + -0.0 -> -0.0; + Gleam@denominator -> Luminance_a / Gleam@denominator + end; + + false -> + case Luminance_a of + +0.0 -> +0.0; + -0.0 -> -0.0; + Gleam@denominator@1 -> Luminance_b / Gleam@denominator@1 + end + end. + +-file("src/gleam_community/colour/accessibility.gleam", 161). +?DOC( + " Returns the `Colour` with the highest contrast between the base `Colour`,\n" + " and and the other provided `Colour` values.\n" + "\n" + "
\n" + " Example:\n" + "\n" + " ```gleam\n" + " fn example() {\n" + " maximum_contrast(\n" + " colour.yellow,\n" + " [colour.white, colour.dark_blue, colour.green],\n" + " )\n" + " }\n" + " ```\n" + "
\n" + "\n" + " \n" +). +-spec maximum_contrast( + gleam_community@colour:colour(), + list(gleam_community@colour:colour()) +) -> {ok, gleam_community@colour:colour()} | {error, nil}. +maximum_contrast(Base, Colours) -> + _pipe = Colours, + _pipe@1 = gleam@list:sort( + _pipe, + fun(Colour_a, Colour_b) -> + Contrast_a = contrast_ratio(Base, Colour_a), + Contrast_b = contrast_ratio(Base, Colour_b), + gleam@float:compare(Contrast_b, Contrast_a) + end + ), + gleam@list:first(_pipe@1). diff --git a/build/packages/gleam_community_colour/src/gleam_community_colour.app.src b/build/packages/gleam_community_colour/src/gleam_community_colour.app.src new file mode 100644 index 0000000..0d22534 --- /dev/null +++ b/build/packages/gleam_community_colour/src/gleam_community_colour.app.src @@ -0,0 +1,10 @@ +{application, gleam_community_colour, [ + {vsn, "2.0.2"}, + {applications, [gleam_json, + gleam_stdlib]}, + {description, "Colour types, conversions, and other utilities"}, + {modules, [gleam_community@colour, + gleam_community@colour@accessibility, + gleam_community_colour@@main]}, + {registered, []} +]}. diff --git a/build/packages/gleam_json/LICENCE b/build/packages/gleam_json/LICENCE new file mode 100644 index 0000000..3d89615 --- /dev/null +++ b/build/packages/gleam_json/LICENCE @@ -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 2021 - present, 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/build/packages/gleam_json/README.md b/build/packages/gleam_json/README.md new file mode 100644 index 0000000..1a06b52 --- /dev/null +++ b/build/packages/gleam_json/README.md @@ -0,0 +1,47 @@ +# json 🐑 + +Work with JSON in Gleam! + +## Installation + +```shell +gleam add gleam_json@3 +``` + +## Encoding + +```gleam +import myapp.{type Cat} +import gleam/json + +pub fn cat_to_json(cat: Cat) -> String { + json.object([ + #("name", json.string(cat.name)), + #("lives", json.int(cat.lives)), + #("flaws", json.null()), + #("nicknames", json.array(cat.nicknames, of: json.string)), + ]) + |> json.to_string +} +``` + +## Parsing + +JSON is parsed into a `Dynamic` value which can be decoded using the +`gleam/dynamic/decode` module from the Gleam standard library. + +```gleam +import myapp.{Cat} +import gleam/json +import gleam/dynamic/decode + +pub fn cat_from_json(json_string: String) -> Result(Cat, json.DecodeError) { + let cat_decoder = { + use name <- decode.field("name", decode.string) + use lives <- decode.field("lives", decode.int) + use nicknames <- decode.field("nicknames", decode.list(decode.string)) + decode.success(Cat(name:, lives:, nicknames:)) + } + json.parse(from: json_string, using: cat_decoder) +} +``` diff --git a/build/packages/gleam_json/gleam.toml b/build/packages/gleam_json/gleam.toml new file mode 100644 index 0000000..f7e7f8d --- /dev/null +++ b/build/packages/gleam_json/gleam.toml @@ -0,0 +1,18 @@ +name = "gleam_json" +version = "3.1.0" +gleam = ">= 1.13.0" + +licences = ["Apache-2.0"] +description = "Work with JSON in Gleam" + +repository = { type = "github", user = "gleam-lang", repo = "json" } +links = [ + { title = "Website", href = "https://gleam.run" }, + { title = "Sponsor", href = "https://github.com/sponsors/lpil" }, +] + +[dependencies] +gleam_stdlib = ">= 0.51.0 and < 2.0.0" + +[dev-dependencies] +gleeunit = ">= 1.2.0 and < 2.0.0" diff --git a/build/packages/gleam_json/src/gleam/json.gleam b/build/packages/gleam_json/src/gleam/json.gleam new file mode 100644 index 0000000..e3a3cae --- /dev/null +++ b/build/packages/gleam_json/src/gleam/json.gleam @@ -0,0 +1,316 @@ +import gleam/bit_array +import gleam/dict.{type Dict} +import gleam/dynamic.{type Dynamic} +import gleam/dynamic/decode +import gleam/list +import gleam/option.{type Option, None, Some} +import gleam/result +import gleam/string_tree.{type StringTree} + +pub type Json + +pub type DecodeError { + UnexpectedEndOfInput + UnexpectedByte(String) + UnexpectedSequence(String) + UnableToDecode(List(decode.DecodeError)) +} + +/// Decode a JSON string into dynamically typed data which can be decoded into +/// typed data with the `gleam/dynamic` module. +/// +/// ## Examples +/// +/// ```gleam +/// > parse("[1,2,3]", decode.list(of: decode.int)) +/// Ok([1, 2, 3]) +/// ``` +/// +/// ```gleam +/// > parse("[", decode.list(of: decode.int)) +/// Error(UnexpectedEndOfInput) +/// ``` +/// +/// ```gleam +/// > parse("1", decode.string) +/// Error(UnableToDecode([decode.DecodeError("String", "Int", [])])) +/// ``` +/// +pub fn parse( + from json: String, + using decoder: decode.Decoder(t), +) -> Result(t, DecodeError) { + do_parse(from: json, using: decoder) +} + +@target(erlang) +fn do_parse( + from json: String, + using decoder: decode.Decoder(t), +) -> Result(t, DecodeError) { + let bits = bit_array.from_string(json) + parse_bits(bits, decoder) +} + +@target(javascript) +fn do_parse( + from json: String, + using decoder: decode.Decoder(t), +) -> Result(t, DecodeError) { + use dynamic_value <- result.try(decode_string(json)) + decode.run(dynamic_value, decoder) + |> result.map_error(UnableToDecode) +} + +@external(javascript, "../gleam_json_ffi.mjs", "decode") +fn decode_string(a: String) -> Result(Dynamic, DecodeError) + +/// Decode a JSON bit string into dynamically typed data which can be decoded +/// into typed data with the `gleam/dynamic` module. +/// +/// ## Examples +/// +/// ```gleam +/// > parse_bits(<<"[1,2,3]">>, decode.list(of: decode.int)) +/// Ok([1, 2, 3]) +/// ``` +/// +/// ```gleam +/// > parse_bits(<<"[">>, decode.list(of: decode.int)) +/// Error(UnexpectedEndOfInput) +/// ``` +/// +/// ```gleam +/// > parse_bits(<<"1">>, decode.string) +/// Error(UnableToDecode([decode.DecodeError("String", "Int", [])])), +/// ``` +/// +pub fn parse_bits( + from json: BitArray, + using decoder: decode.Decoder(t), +) -> Result(t, DecodeError) { + use dynamic_value <- result.try(decode_to_dynamic(json)) + decode.run(dynamic_value, decoder) + |> result.map_error(UnableToDecode) +} + +@external(erlang, "gleam_json_ffi", "decode") +fn decode_to_dynamic(json: BitArray) -> Result(Dynamic, DecodeError) { + case bit_array.to_string(json) { + Ok(string) -> decode_string(string) + Error(Nil) -> Error(UnexpectedByte("")) + } +} + +/// Convert a JSON value into a string. +/// +/// Where possible prefer the `to_string_tree` function as it is faster than +/// this function, and BEAM VM IO is optimised for sending `StringTree` data. +/// +/// ## Examples +/// +/// ```gleam +/// > to_string(array([1, 2, 3], of: int)) +/// "[1,2,3]" +/// ``` +/// +pub fn to_string(json: Json) -> String { + do_to_string(json) +} + +@external(erlang, "gleam_json_ffi", "json_to_string") +@external(javascript, "../gleam_json_ffi.mjs", "json_to_string") +fn do_to_string(a: Json) -> String + +/// Convert a JSON value into a string tree. +/// +/// Where possible prefer this function to the `to_string` function as it is +/// slower than this function, and BEAM VM IO is optimised for sending +/// `StringTree` data. +/// +/// ## Examples +/// +/// ```gleam +/// > to_string_tree(array([1, 2, 3], of: int)) +/// string_tree.from_string("[1,2,3]") +/// ``` +/// +@external(erlang, "gleam_json_ffi", "json_to_iodata") +@external(javascript, "../gleam_json_ffi.mjs", "json_to_string") +pub fn to_string_tree(json: Json) -> StringTree + +/// Encode a string into JSON, using normal JSON escaping. +/// +/// ## Examples +/// +/// ```gleam +/// > to_string(string("Hello!")) +/// "\"Hello!\"" +/// ``` +/// +pub fn string(input: String) -> Json { + do_string(input) +} + +@external(erlang, "gleam_json_ffi", "string") +@external(javascript, "../gleam_json_ffi.mjs", "identity") +fn do_string(a: String) -> Json + +/// Encode a bool into JSON. +/// +/// ## Examples +/// +/// ```gleam +/// > to_string(bool(False)) +/// "false" +/// ``` +/// +pub fn bool(input: Bool) -> Json { + do_bool(input) +} + +@external(erlang, "gleam_json_ffi", "bool") +@external(javascript, "../gleam_json_ffi.mjs", "identity") +fn do_bool(a: Bool) -> Json + +/// Encode an int into JSON. +/// +/// ## Examples +/// +/// ```gleam +/// > to_string(int(50)) +/// "50" +/// ``` +/// +pub fn int(input: Int) -> Json { + do_int(input) +} + +@external(erlang, "gleam_json_ffi", "int") +@external(javascript, "../gleam_json_ffi.mjs", "identity") +fn do_int(a: Int) -> Json + +/// Encode a float into JSON. +/// +/// ## Examples +/// +/// ```gleam +/// > to_string(float(4.7)) +/// "4.7" +/// ``` +/// +pub fn float(input: Float) -> Json { + do_float(input) +} + +@external(erlang, "gleam_json_ffi", "float") +@external(javascript, "../gleam_json_ffi.mjs", "identity") +fn do_float(input input: Float) -> Json + +/// The JSON value null. +/// +/// ## Examples +/// +/// ```gleam +/// > to_string(null()) +/// "null" +/// ``` +/// +pub fn null() -> Json { + do_null() +} + +@external(erlang, "gleam_json_ffi", "null") +@external(javascript, "../gleam_json_ffi.mjs", "do_null") +fn do_null() -> Json + +/// Encode an optional value into JSON, using null if it is the `None` variant. +/// +/// ## Examples +/// +/// ```gleam +/// > to_string(nullable(Some(50), of: int)) +/// "50" +/// ``` +/// +/// ```gleam +/// > to_string(nullable(None, of: int)) +/// "null" +/// ``` +/// +pub fn nullable(from input: Option(a), of inner_type: fn(a) -> Json) -> Json { + case input { + Some(value) -> inner_type(value) + None -> null() + } +} + +/// Encode a list of key-value pairs into a JSON object. +/// +/// ## Examples +/// +/// ```gleam +/// > to_string(object([ +/// #("game", string("Pac-Man")), +/// #("score", int(3333360)), +/// ])) +/// "{\"game\":\"Pac-Mac\",\"score\":3333360}" +/// ``` +/// +pub fn object(entries: List(#(String, Json))) -> Json { + do_object(entries) +} + +@external(erlang, "gleam_json_ffi", "object") +@external(javascript, "../gleam_json_ffi.mjs", "object") +fn do_object(entries entries: List(#(String, Json))) -> Json + +/// Encode a list into a JSON array. +/// +/// ## Examples +/// +/// ```gleam +/// > to_string(array([1, 2, 3], of: int)) +/// "[1, 2, 3]" +/// ``` +/// +pub fn array(from entries: List(a), of inner_type: fn(a) -> Json) -> Json { + entries + |> list.map(inner_type) + |> preprocessed_array +} + +/// Encode a list of JSON values into a JSON array. +/// +/// ## Examples +/// +/// ```gleam +/// > to_string(preprocessed_array([int(1), float(2.0), string("3")])) +/// "[1, 2.0, \"3\"]" +/// ``` +/// +pub fn preprocessed_array(from: List(Json)) -> Json { + do_preprocessed_array(from) +} + +@external(erlang, "gleam_json_ffi", "array") +@external(javascript, "../gleam_json_ffi.mjs", "array") +fn do_preprocessed_array(from from: List(Json)) -> Json + +/// Encode a Dict into a JSON object using the supplied functions to encode +/// the keys and the values respectively. +/// +/// ## Examples +/// +/// ```gleam +/// > to_string(dict(dict.from_list([ #(3, 3.0), #(4, 4.0)]), int.to_string, float) +/// "{\"3\": 3.0, \"4\": 4.0}" +/// ``` +/// +pub fn dict( + dict: Dict(k, v), + keys: fn(k) -> String, + values: fn(v) -> Json, +) -> Json { + object(dict.fold(dict, [], fn(acc, k, v) { [#(keys(k), values(v)), ..acc] })) +} diff --git a/build/packages/gleam_json/src/gleam@json.erl b/build/packages/gleam_json/src/gleam@json.erl new file mode 100644 index 0000000..8a33e49 --- /dev/null +++ b/build/packages/gleam_json/src/gleam@json.erl @@ -0,0 +1,304 @@ +-module(gleam@json). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/json.gleam"). +-export([parse_bits/2, parse/2, to_string/1, to_string_tree/1, string/1, bool/1, int/1, float/1, null/0, nullable/2, object/1, preprocessed_array/1, array/2, dict/3]). +-export_type([json/0, decode_error/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +-type json() :: any(). + +-type decode_error() :: unexpected_end_of_input | + {unexpected_byte, binary()} | + {unexpected_sequence, binary()} | + {unable_to_decode, list(gleam@dynamic@decode:decode_error())}. + +-file("src/gleam/json.gleam", 88). +?DOC( + " Decode a JSON bit string into dynamically typed data which can be decoded\n" + " into typed data with the `gleam/dynamic` module.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " > parse_bits(<<\"[1,2,3]\">>, decode.list(of: decode.int))\n" + " Ok([1, 2, 3])\n" + " ```\n" + "\n" + " ```gleam\n" + " > parse_bits(<<\"[\">>, decode.list(of: decode.int))\n" + " Error(UnexpectedEndOfInput)\n" + " ```\n" + "\n" + " ```gleam\n" + " > parse_bits(<<\"1\">>, decode.string)\n" + " Error(UnableToDecode([decode.DecodeError(\"String\", \"Int\", [])])),\n" + " ```\n" +). +-spec parse_bits(bitstring(), gleam@dynamic@decode:decoder(DNO)) -> {ok, DNO} | + {error, decode_error()}. +parse_bits(Json, Decoder) -> + gleam@result:'try'( + gleam_json_ffi:decode(Json), + fun(Dynamic_value) -> + _pipe = gleam@dynamic@decode:run(Dynamic_value, Decoder), + gleam@result:map_error( + _pipe, + fun(Field@0) -> {unable_to_decode, Field@0} end + ) + end + ). + +-file("src/gleam/json.gleam", 47). +-spec do_parse(binary(), gleam@dynamic@decode:decoder(DNI)) -> {ok, DNI} | + {error, decode_error()}. +do_parse(Json, Decoder) -> + Bits = gleam_stdlib:identity(Json), + parse_bits(Bits, Decoder). + +-file("src/gleam/json.gleam", 39). +?DOC( + " Decode a JSON string into dynamically typed data which can be decoded into\n" + " typed data with the `gleam/dynamic` module.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " > parse(\"[1,2,3]\", decode.list(of: decode.int))\n" + " Ok([1, 2, 3])\n" + " ```\n" + "\n" + " ```gleam\n" + " > parse(\"[\", decode.list(of: decode.int))\n" + " Error(UnexpectedEndOfInput)\n" + " ```\n" + "\n" + " ```gleam\n" + " > parse(\"1\", decode.string)\n" + " Error(UnableToDecode([decode.DecodeError(\"String\", \"Int\", [])]))\n" + " ```\n" +). +-spec parse(binary(), gleam@dynamic@decode:decoder(DNE)) -> {ok, DNE} | + {error, decode_error()}. +parse(Json, Decoder) -> + do_parse(Json, Decoder). + +-file("src/gleam/json.gleam", 117). +?DOC( + " Convert a JSON value into a string.\n" + "\n" + " Where possible prefer the `to_string_tree` function as it is faster than\n" + " this function, and BEAM VM IO is optimised for sending `StringTree` data.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " > to_string(array([1, 2, 3], of: int))\n" + " \"[1,2,3]\"\n" + " ```\n" +). +-spec to_string(json()) -> binary(). +to_string(Json) -> + gleam_json_ffi:json_to_string(Json). + +-file("src/gleam/json.gleam", 140). +?DOC( + " Convert a JSON value into a string tree.\n" + "\n" + " Where possible prefer this function to the `to_string` function as it is\n" + " slower than this function, and BEAM VM IO is optimised for sending\n" + " `StringTree` data.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " > to_string_tree(array([1, 2, 3], of: int))\n" + " string_tree.from_string(\"[1,2,3]\")\n" + " ```\n" +). +-spec to_string_tree(json()) -> gleam@string_tree:string_tree(). +to_string_tree(Json) -> + gleam_json_ffi:json_to_iodata(Json). + +-file("src/gleam/json.gleam", 151). +?DOC( + " Encode a string into JSON, using normal JSON escaping.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " > to_string(string(\"Hello!\"))\n" + " \"\\\"Hello!\\\"\"\n" + " ```\n" +). +-spec string(binary()) -> json(). +string(Input) -> + gleam_json_ffi:string(Input). + +-file("src/gleam/json.gleam", 168). +?DOC( + " Encode a bool into JSON.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " > to_string(bool(False))\n" + " \"false\"\n" + " ```\n" +). +-spec bool(boolean()) -> json(). +bool(Input) -> + gleam_json_ffi:bool(Input). + +-file("src/gleam/json.gleam", 185). +?DOC( + " Encode an int into JSON.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " > to_string(int(50))\n" + " \"50\"\n" + " ```\n" +). +-spec int(integer()) -> json(). +int(Input) -> + gleam_json_ffi:int(Input). + +-file("src/gleam/json.gleam", 202). +?DOC( + " Encode a float into JSON.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " > to_string(float(4.7))\n" + " \"4.7\"\n" + " ```\n" +). +-spec float(float()) -> json(). +float(Input) -> + gleam_json_ffi:float(Input). + +-file("src/gleam/json.gleam", 219). +?DOC( + " The JSON value null.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " > to_string(null())\n" + " \"null\"\n" + " ```\n" +). +-spec null() -> json(). +null() -> + gleam_json_ffi:null(). + +-file("src/gleam/json.gleam", 241). +?DOC( + " Encode an optional value into JSON, using null if it is the `None` variant.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " > to_string(nullable(Some(50), of: int))\n" + " \"50\"\n" + " ```\n" + "\n" + " ```gleam\n" + " > to_string(nullable(None, of: int))\n" + " \"null\"\n" + " ```\n" +). +-spec nullable(gleam@option:option(DNU), fun((DNU) -> json())) -> json(). +nullable(Input, Inner_type) -> + case Input of + {some, Value} -> + Inner_type(Value); + + none -> + null() + end. + +-file("src/gleam/json.gleam", 260). +?DOC( + " Encode a list of key-value pairs into a JSON object.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " > to_string(object([\n" + " #(\"game\", string(\"Pac-Man\")),\n" + " #(\"score\", int(3333360)),\n" + " ]))\n" + " \"{\\\"game\\\":\\\"Pac-Mac\\\",\\\"score\\\":3333360}\"\n" + " ```\n" +). +-spec object(list({binary(), json()})) -> json(). +object(Entries) -> + gleam_json_ffi:object(Entries). + +-file("src/gleam/json.gleam", 292). +?DOC( + " Encode a list of JSON values into a JSON array.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " > to_string(preprocessed_array([int(1), float(2.0), string(\"3\")]))\n" + " \"[1, 2.0, \\\"3\\\"]\"\n" + " ```\n" +). +-spec preprocessed_array(list(json())) -> json(). +preprocessed_array(From) -> + gleam_json_ffi:array(From). + +-file("src/gleam/json.gleam", 277). +?DOC( + " Encode a list into a JSON array.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " > to_string(array([1, 2, 3], of: int))\n" + " \"[1, 2, 3]\"\n" + " ```\n" +). +-spec array(list(DNY), fun((DNY) -> json())) -> json(). +array(Entries, Inner_type) -> + _pipe = Entries, + _pipe@1 = gleam@list:map(_pipe, Inner_type), + preprocessed_array(_pipe@1). + +-file("src/gleam/json.gleam", 310). +?DOC( + " Encode a Dict into a JSON object using the supplied functions to encode\n" + " the keys and the values respectively.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " > to_string(dict(dict.from_list([ #(3, 3.0), #(4, 4.0)]), int.to_string, float)\n" + " \"{\\\"3\\\": 3.0, \\\"4\\\": 4.0}\"\n" + " ```\n" +). +-spec dict( + gleam@dict:dict(DOC, DOD), + fun((DOC) -> binary()), + fun((DOD) -> json()) +) -> json(). +dict(Dict, Keys, Values) -> + object( + gleam@dict:fold( + Dict, + [], + fun(Acc, K, V) -> [{Keys(K), Values(V)} | Acc] end + ) + ). diff --git a/build/packages/gleam_json/src/gleam_json.app.src b/build/packages/gleam_json/src/gleam_json.app.src new file mode 100644 index 0000000..3bad27e --- /dev/null +++ b/build/packages/gleam_json/src/gleam_json.app.src @@ -0,0 +1,9 @@ +{application, gleam_json, [ + {vsn, "3.1.0"}, + {applications, [gleam_stdlib]}, + {description, "Work with JSON in Gleam"}, + {modules, [gleam@json, + gleam_json@@main, + gleam_json_ffi]}, + {registered, []} +]}. diff --git a/build/packages/gleam_json/src/gleam_json_ffi.erl b/build/packages/gleam_json/src/gleam_json_ffi.erl new file mode 100644 index 0000000..06a26a0 --- /dev/null +++ b/build/packages/gleam_json/src/gleam_json_ffi.erl @@ -0,0 +1,66 @@ +-module(gleam_json_ffi). + +-export([ + decode/1, json_to_iodata/1, json_to_string/1, int/1, float/1, string/1, + bool/1, null/0, array/1, object/1 +]). + +-if(?OTP_RELEASE < 27). +-define(bad_version, + error({erlang_otp_27_required, << "Insufficient Erlang/OTP version. + +`gleam_json` uses the Erlang `json` module introduced in Erlang/OTP 27. +You are using Erlang/OTP "/utf8, (integer_to_binary(?OTP_RELEASE))/binary, " +Please upgrade your Erlang install or downgrade to `gleam_json` v1.0.1. +"/utf8>>})). + +decode(_) -> ?bad_version. +json_to_iodata(_) -> ?bad_version. +json_to_string(_) -> ?bad_version. +int(_) -> ?bad_version. +float(_) -> ?bad_version. +string(_) -> ?bad_version. +bool(_) -> ?bad_version. +array(_) -> ?bad_version. +object(_) -> ?bad_version. +null() -> ?bad_version. +-else. + +decode(Json) -> + try + {ok, json:decode(Json)} + catch + error:unexpected_end -> {error, unexpected_end_of_input}; + error:{invalid_byte, Byte} -> {error, {unexpected_byte, hex(Byte)}}; + error:{unexpected_sequence, Byte} -> {error, {unexpected_sequence, Byte}} + end. + +hex(I) -> + H = list_to_binary(integer_to_list(I, 16)), + <<"0x"/utf8, H/binary>>. + +json_to_iodata(Json) -> + Json. + +json_to_string(Json) when is_binary(Json) -> + Json; +json_to_string(Json) when is_list(Json) -> + list_to_binary(Json). + +null() -> <<"null">>. +bool(true) -> <<"true">>; +bool(false) -> <<"false">>. +int(X) -> json:encode_integer(X). +float(X) -> json:encode_float(X). +string(X) -> json:encode_binary(X). + +array([]) -> <<"[]">>; +array([First | Rest]) -> [$[, First | array_loop(Rest)]. +array_loop([]) -> "]"; +array_loop([Elem | Rest]) -> [$,, Elem | array_loop(Rest)]. + +object(List) -> encode_object([[$,, string(Key), $: | Value] || {Key, Value} <- List]). +encode_object([]) -> <<"{}">>; +encode_object([[_Comma | Entry] | Rest]) -> ["{", Entry, Rest, "}"]. + +-endif. diff --git a/build/packages/gleam_json/src/gleam_json_ffi.mjs b/build/packages/gleam_json/src/gleam_json_ffi.mjs new file mode 100644 index 0000000..1d8d3ff --- /dev/null +++ b/build/packages/gleam_json/src/gleam_json_ffi.mjs @@ -0,0 +1,201 @@ +import { + Result$Ok, + Result$Error, + List$isNonEmpty, + List$NonEmpty$first, + List$NonEmpty$rest, +} from "./gleam.mjs"; +import { + DecodeError$UnexpectedByte, + DecodeError$UnexpectedEndOfInput, +} from "./gleam/json.mjs"; + +export function json_to_string(json) { + return JSON.stringify(json); +} + +export function object(entries) { + return Object.fromEntries(entries); +} + +export function identity(x) { + return x; +} + +export function array(list) { + const array = []; + while (List$isNonEmpty(list)) { + array.push(List$NonEmpty$first(list)); + list = List$NonEmpty$rest(list); + } + return array; +} + +export function do_null() { + return null; +} + +export function decode(string) { + try { + const result = JSON.parse(string); + return Result$Ok(result); + } catch (err) { + return Result$Error(getJsonDecodeError(err, string)); + } +} + +export function getJsonDecodeError(stdErr, json) { + if (isUnexpectedEndOfInput(stdErr)) return DecodeError$UnexpectedEndOfInput(); + return toUnexpectedByteError(stdErr, json); +} + +/** + * Matches unexpected end of input messages in: + * - Chromium (edge, chrome, node) + * - Spidermonkey (firefox) + * - JavascriptCore (safari) + * + * Note that Spidermonkey and JavascriptCore will both incorrectly report some + * UnexpectedByte errors as UnexpectedEndOfInput errors. For example: + * + * @example + * // in JavascriptCore + * JSON.parse('{"a"]: "b"}) + * // => JSON Parse error: Expected ':' before value + * + * JSON.parse('{"a"') + * // => JSON Parse error: Expected ':' before value + * + * // in Chromium (correct) + * JSON.parse('{"a"]: "b"}) + * // => Unexpected token ] in JSON at position 4 + * + * JSON.parse('{"a"') + * // => Unexpected end of JSON input + */ +function isUnexpectedEndOfInput(err) { + const unexpectedEndOfInputRegex = + /((unexpected (end|eof))|(end of data)|(unterminated string)|(json( parse error|\.parse)\: expected '(\:|\}|\])'))/i; + return unexpectedEndOfInputRegex.test(err.message); +} + +/** + * Converts a SyntaxError to an UnexpectedByte error based on the JS runtime. + * + * For Chromium, the unexpected byte and position are reported by the runtime. + * + * For JavascriptCore, only the unexpected byte is reported by the runtime, so + * there is no way to know which position that character is in unless we then + * parse the string again ourselves. So instead, the position is reported as 0. + * + * For Spidermonkey, the position is reported by the runtime as a line and column number + * and the unexpected byte is found using those coordinates. + */ +function toUnexpectedByteError(err, json) { + let converters = [ + v8UnexpectedByteError, + oldV8UnexpectedByteError, + jsCoreUnexpectedByteError, + spidermonkeyUnexpectedByteError, + ]; + + for (let converter of converters) { + let result = converter(err, json); + if (result) return result; + } + + return DecodeError$UnexpectedByte(""); +} + +/** + * Matches unexpected byte messages in: + * - V8 (edge, chrome, node) + * + * Matches the character but not the position as this is no longer reported by + * V8. Boo! + */ +function v8UnexpectedByteError(err) { + const regex = /unexpected token '(.)', ".+" is not valid JSON/i; + const match = regex.exec(err.message); + if (!match) return null; + const byte = toHex(match[1]); + return DecodeError$UnexpectedByte(byte); +} + +/** + * Matches unexpected byte messages in: + * - V8 (edge, chrome, node) + * + * No longer works in current versions of V8. + * + * Matches the character and its position. + */ +function oldV8UnexpectedByteError(err) { + const regex = /unexpected token (.) in JSON at position (\d+)/i; + const match = regex.exec(err.message); + if (!match) return null; + const byte = toHex(match[1]); + return DecodeError$UnexpectedByte(byte); +} + +/** + * Matches unexpected byte messages in: + * - Spidermonkey (firefox) + * + * Matches the position in a 2d grid only and not the character. + */ +function spidermonkeyUnexpectedByteError(err, json) { + const regex = + /(unexpected character|expected .*) at line (\d+) column (\d+)/i; + const match = regex.exec(err.message); + if (!match) return null; + const line = Number(match[2]); + const column = Number(match[3]); + const position = getPositionFromMultiline(line, column, json); + const byte = toHex(json[position]); + return DecodeError$UnexpectedByte(byte); +} + +/** + * Matches unexpected byte messages in: + * - JavascriptCore (safari) + * + * JavascriptCore only reports what the character is and not its position. + */ +function jsCoreUnexpectedByteError(err) { + const regex = /unexpected (identifier|token) "(.)"/i; + const match = regex.exec(err.message); + if (!match) return null; + const byte = toHex(match[2]); + return DecodeError$UnexpectedByte(byte); +} + +function toHex(char) { + return "0x" + char.charCodeAt(0).toString(16).toUpperCase(); +} + +/** + * Gets the position of a character in a flattened (i.e. single line) string + * from a line and column number. Note that the position is 0-indexed and + * the line and column numbers are 1-indexed. + * + * @param {number} line + * @param {number} column + * @param {string} string + */ +function getPositionFromMultiline(line, column, string) { + if (line === 1) return column - 1; + + let currentLn = 1; + let position = 0; + string.split("").find((char, idx) => { + if (char === "\n") currentLn += 1; + if (currentLn === line) { + position = idx + column; + return true; + } + return false; + }); + + return position; +} diff --git a/build/packages/gleam_stdlib/LICENCE b/build/packages/gleam_stdlib/LICENCE new file mode 100644 index 0000000..c1dabd0 --- /dev/null +++ b/build/packages/gleam_stdlib/LICENCE @@ -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/build/packages/gleam_stdlib/README.md b/build/packages/gleam_stdlib/README.md new file mode 100644 index 0000000..5e1ed4c --- /dev/null +++ b/build/packages/gleam_stdlib/README.md @@ -0,0 +1,34 @@ +# stdlib + +[![Package Version](https://img.shields.io/hexpm/v/gleam_stdlib)](https://hex.pm/packages/gleam_stdlib) +[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/gleam_stdlib/) +[![Discord chat](https://img.shields.io/discord/768594524158427167?color=blue)](https://discord.gg/Fm8Pwmy) + +Gleam's standard library! +Documentation available on [HexDocs](https://hexdocs.pm/gleam_stdlib/). + +## Installation + +Add `gleam_stdlib` to your Gleam project. + +```sh +gleam add gleam_stdlib +``` +```gleam +import gleam/io + +pub fn greet(name: String) -> Nil { + io.println("Hello " <> name <> "!") +} +``` + +## Targets + +Gleam's standard library supports both targets: Erlang and JavaScript. + +### Compatibility + +This library is compatible with all versions of Erlang/OTP 26 and higher, +as well as all NodeJS, Deno, Bun, and major browsers that are currently +supported by their maintainers. If you have a compatibility issue with +any platform open an issue and we'll see what we can do to help. diff --git a/build/packages/gleam_stdlib/gleam.toml b/build/packages/gleam_stdlib/gleam.toml new file mode 100644 index 0000000..27c3f0e --- /dev/null +++ b/build/packages/gleam_stdlib/gleam.toml @@ -0,0 +1,14 @@ +name = "gleam_stdlib" +version = "0.65.0" +gleam = ">= 1.11.0" +licences = ["Apache-2.0"] +description = "A standard library for the Gleam programming language" + +repository = { type = "github", user = "gleam-lang", repo = "stdlib" } +links = [ + { title = "Website", href = "https://gleam.run" }, + { title = "Sponsor", href = "https://github.com/sponsors/lpil" }, +] + +[javascript.deno] +allow_read = ["./"] diff --git a/build/packages/gleam_stdlib/include/gleam@dynamic@decode_DecodeError.hrl b/build/packages/gleam_stdlib/include/gleam@dynamic@decode_DecodeError.hrl new file mode 100644 index 0000000..b1135f2 --- /dev/null +++ b/build/packages/gleam_stdlib/include/gleam@dynamic@decode_DecodeError.hrl @@ -0,0 +1,5 @@ +-record(decode_error, { + expected :: binary(), + found :: binary(), + path :: list(binary()) +}). diff --git a/build/packages/gleam_stdlib/include/gleam@dynamic@decode_Decoder.hrl b/build/packages/gleam_stdlib/include/gleam@dynamic@decode_Decoder.hrl new file mode 100644 index 0000000..a2fcfc6 --- /dev/null +++ b/build/packages/gleam_stdlib/include/gleam@dynamic@decode_Decoder.hrl @@ -0,0 +1,4 @@ +-record(decoder, { + function :: fun((gleam@dynamic:dynamic_()) -> {any(), + list(gleam@dynamic@decode:decode_error())}) +}). diff --git a/build/packages/gleam_stdlib/include/gleam@set_Set.hrl b/build/packages/gleam_stdlib/include/gleam@set_Set.hrl new file mode 100644 index 0000000..51fb778 --- /dev/null +++ b/build/packages/gleam_stdlib/include/gleam@set_Set.hrl @@ -0,0 +1 @@ +-record(set, {dict :: gleam@dict:dict(any(), list(nil))}). diff --git a/build/packages/gleam_stdlib/include/gleam@uri_Uri.hrl b/build/packages/gleam_stdlib/include/gleam@uri_Uri.hrl new file mode 100644 index 0000000..50150f4 --- /dev/null +++ b/build/packages/gleam_stdlib/include/gleam@uri_Uri.hrl @@ -0,0 +1,9 @@ +-record(uri, { + scheme :: gleam@option:option(binary()), + userinfo :: gleam@option:option(binary()), + host :: gleam@option:option(binary()), + port :: gleam@option:option(integer()), + path :: binary(), + 'query' :: gleam@option:option(binary()), + fragment :: gleam@option:option(binary()) +}). diff --git a/build/packages/gleam_stdlib/src/dict.mjs b/build/packages/gleam_stdlib/src/dict.mjs new file mode 100644 index 0000000..f39cd54 --- /dev/null +++ b/build/packages/gleam_stdlib/src/dict.mjs @@ -0,0 +1,993 @@ +/** + * This file uses jsdoc to annotate types. + * These types can be checked using the typescript compiler with "checkjs" option. + */ + +import { isEqual } from "./gleam.mjs"; + +const referenceMap = /* @__PURE__ */ new WeakMap(); +const tempDataView = /* @__PURE__ */ new DataView( + /* @__PURE__ */ new ArrayBuffer(8), +); +let referenceUID = 0; +/** + * hash the object by reference using a weak map and incrementing uid + * @param {any} o + * @returns {number} + */ +function hashByReference(o) { + const known = referenceMap.get(o); + if (known !== undefined) { + return known; + } + const hash = referenceUID++; + if (referenceUID === 0x7fffffff) { + referenceUID = 0; + } + referenceMap.set(o, hash); + return hash; +} + +/** + * merge two hashes in an order sensitive way + * @param {number} a + * @param {number} b + * @returns {number} + */ +function hashMerge(a, b) { + return (a ^ (b + 0x9e3779b9 + (a << 6) + (a >> 2))) | 0; +} + +/** + * standard string hash popularised by java + * @param {string} s + * @returns {number} + */ +function hashString(s) { + let hash = 0; + const len = s.length; + for (let i = 0; i < len; i++) { + hash = (Math.imul(31, hash) + s.charCodeAt(i)) | 0; + } + return hash; +} + +/** + * hash a number by converting to two integers and do some jumbling + * @param {number} n + * @returns {number} + */ +function hashNumber(n) { + tempDataView.setFloat64(0, n); + const i = tempDataView.getInt32(0); + const j = tempDataView.getInt32(4); + return Math.imul(0x45d9f3b, (i >> 16) ^ i) ^ j; +} + +/** + * hash a BigInt by converting it to a string and hashing that + * @param {BigInt} n + * @returns {number} + */ +function hashBigInt(n) { + return hashString(n.toString()); +} + +/** + * hash any js object + * @param {any} o + * @returns {number} + */ +function hashObject(o) { + const proto = Object.getPrototypeOf(o); + if (proto !== null && typeof proto.hashCode === "function") { + try { + const code = o.hashCode(o); + if (typeof code === "number") { + return code; + } + } catch {} + } + if (o instanceof Promise || o instanceof WeakSet || o instanceof WeakMap) { + return hashByReference(o); + } + if (o instanceof Date) { + return hashNumber(o.getTime()); + } + let h = 0; + if (o instanceof ArrayBuffer) { + o = new Uint8Array(o); + } + if (Array.isArray(o) || o instanceof Uint8Array) { + for (let i = 0; i < o.length; i++) { + h = (Math.imul(31, h) + getHash(o[i])) | 0; + } + } else if (o instanceof Set) { + o.forEach((v) => { + h = (h + getHash(v)) | 0; + }); + } else if (o instanceof Map) { + o.forEach((v, k) => { + h = (h + hashMerge(getHash(v), getHash(k))) | 0; + }); + } else { + const keys = Object.keys(o); + for (let i = 0; i < keys.length; i++) { + const k = keys[i]; + const v = o[k]; + h = (h + hashMerge(getHash(v), hashString(k))) | 0; + } + } + return h; +} + +/** + * hash any js value + * @param {any} u + * @returns {number} + */ +export function getHash(u) { + if (u === null) return 0x42108422; + if (u === undefined) return 0x42108423; + if (u === true) return 0x42108421; + if (u === false) return 0x42108420; + switch (typeof u) { + case "number": + return hashNumber(u); + case "string": + return hashString(u); + case "bigint": + return hashBigInt(u); + case "object": + return hashObject(u); + case "symbol": + return hashByReference(u); + case "function": + return hashByReference(u); + default: + return 0; // should be unreachable + } +} + +/** + * @template K,V + * @typedef {ArrayNode | IndexNode | CollisionNode} Node + */ +/** + * @template K,V + * @typedef {{ type: typeof ENTRY, k: K, v: V }} Entry + */ +/** + * @template K,V + * @typedef {{ type: typeof ARRAY_NODE, size: number, array: (undefined | Entry | Node)[] }} ArrayNode + */ +/** + * @template K,V + * @typedef {{ type: typeof INDEX_NODE, bitmap: number, array: (Entry | Node)[] }} IndexNode + */ +/** + * @template K,V + * @typedef {{ type: typeof COLLISION_NODE, hash: number, array: Entry[] }} CollisionNode + */ +/** + * @typedef {{ val: boolean }} Flag + */ +const SHIFT = 5; // number of bits you need to shift by to get the next bucket +const BUCKET_SIZE = Math.pow(2, SHIFT); +const MASK = BUCKET_SIZE - 1; // used to zero out all bits not in the bucket +const MAX_INDEX_NODE = BUCKET_SIZE / 2; // when does index node grow into array node +const MIN_ARRAY_NODE = BUCKET_SIZE / 4; // when does array node shrink to index node +const ENTRY = 0; +const ARRAY_NODE = 1; +const INDEX_NODE = 2; +const COLLISION_NODE = 3; + +/** @type {IndexNode} */ +const EMPTY = { + type: INDEX_NODE, + bitmap: 0, + array: [], +}; +/** + * Mask the hash to get only the bucket corresponding to shift + * @param {number} hash + * @param {number} shift + * @returns {number} + */ +function mask(hash, shift) { + return (hash >>> shift) & MASK; +} + +/** + * Set only the Nth bit where N is the masked hash + * @param {number} hash + * @param {number} shift + * @returns {number} + */ +function bitpos(hash, shift) { + return 1 << mask(hash, shift); +} + +/** + * Count the number of 1 bits in a number + * @param {number} x + * @returns {number} + */ +function bitcount(x) { + x -= (x >> 1) & 0x55555555; + x = (x & 0x33333333) + ((x >> 2) & 0x33333333); + x = (x + (x >> 4)) & 0x0f0f0f0f; + x += x >> 8; + x += x >> 16; + return x & 0x7f; +} + +/** + * Calculate the array index of an item in a bitmap index node + * @param {number} bitmap + * @param {number} bit + * @returns {number} + */ +function index(bitmap, bit) { + return bitcount(bitmap & (bit - 1)); +} + +/** + * Efficiently copy an array and set one value at an index + * @template T + * @param {T[]} arr + * @param {number} at + * @param {T} val + * @returns {T[]} + */ +function cloneAndSet(arr, at, val) { + const len = arr.length; + const out = new Array(len); + for (let i = 0; i < len; ++i) { + out[i] = arr[i]; + } + out[at] = val; + return out; +} + +/** + * Efficiently copy an array and insert one value at an index + * @template T + * @param {T[]} arr + * @param {number} at + * @param {T} val + * @returns {T[]} + */ +function spliceIn(arr, at, val) { + const len = arr.length; + const out = new Array(len + 1); + let i = 0; + let g = 0; + while (i < at) { + out[g++] = arr[i++]; + } + out[g++] = val; + while (i < len) { + out[g++] = arr[i++]; + } + return out; +} + +/** + * Efficiently copy an array and remove one value at an index + * @template T + * @param {T[]} arr + * @param {number} at + * @returns {T[]} + */ +function spliceOut(arr, at) { + const len = arr.length; + const out = new Array(len - 1); + let i = 0; + let g = 0; + while (i < at) { + out[g++] = arr[i++]; + } + ++i; + while (i < len) { + out[g++] = arr[i++]; + } + return out; +} + +/** + * Create a new node containing two entries + * @template K,V + * @param {number} shift + * @param {K} key1 + * @param {V} val1 + * @param {number} key2hash + * @param {K} key2 + * @param {V} val2 + * @returns {Node} + */ +function createNode(shift, key1, val1, key2hash, key2, val2) { + const key1hash = getHash(key1); + if (key1hash === key2hash) { + return { + type: COLLISION_NODE, + hash: key1hash, + array: [ + { type: ENTRY, k: key1, v: val1 }, + { type: ENTRY, k: key2, v: val2 }, + ], + }; + } + const addedLeaf = { val: false }; + return assoc( + assocIndex(EMPTY, shift, key1hash, key1, val1, addedLeaf), + shift, + key2hash, + key2, + val2, + addedLeaf, + ); +} + +/** + * @template T,K,V + * @callback AssocFunction + * @param {T} root + * @param {number} shift + * @param {number} hash + * @param {K} key + * @param {V} val + * @param {Flag} addedLeaf + * @returns {Node} + */ +/** + * Associate a node with a new entry, creating a new node + * @template T,K,V + * @type {AssocFunction,K,V>} + */ +function assoc(root, shift, hash, key, val, addedLeaf) { + switch (root.type) { + case ARRAY_NODE: + return assocArray(root, shift, hash, key, val, addedLeaf); + case INDEX_NODE: + return assocIndex(root, shift, hash, key, val, addedLeaf); + case COLLISION_NODE: + return assocCollision(root, shift, hash, key, val, addedLeaf); + } +} +/** + * @template T,K,V + * @type {AssocFunction,K,V>} + */ +function assocArray(root, shift, hash, key, val, addedLeaf) { + const idx = mask(hash, shift); + const node = root.array[idx]; + // if the corresponding index is empty set the index to a newly created node + if (node === undefined) { + addedLeaf.val = true; + return { + type: ARRAY_NODE, + size: root.size + 1, + array: cloneAndSet(root.array, idx, { type: ENTRY, k: key, v: val }), + }; + } + if (node.type === ENTRY) { + // if keys are equal replace the entry + if (isEqual(key, node.k)) { + if (val === node.v) { + return root; + } + return { + type: ARRAY_NODE, + size: root.size, + array: cloneAndSet(root.array, idx, { + type: ENTRY, + k: key, + v: val, + }), + }; + } + // otherwise upgrade the entry to a node and insert + addedLeaf.val = true; + return { + type: ARRAY_NODE, + size: root.size, + array: cloneAndSet( + root.array, + idx, + createNode(shift + SHIFT, node.k, node.v, hash, key, val), + ), + }; + } + // otherwise call assoc on the child node + const n = assoc(node, shift + SHIFT, hash, key, val, addedLeaf); + // if the child node hasn't changed just return the old root + if (n === node) { + return root; + } + // otherwise set the index to the new node + return { + type: ARRAY_NODE, + size: root.size, + array: cloneAndSet(root.array, idx, n), + }; +} +/** + * @template T,K,V + * @type {AssocFunction,K,V>} + */ +function assocIndex(root, shift, hash, key, val, addedLeaf) { + const bit = bitpos(hash, shift); + const idx = index(root.bitmap, bit); + // if there is already a item at this hash index.. + if ((root.bitmap & bit) !== 0) { + // if there is a node at the index (not an entry), call assoc on the child node + const node = root.array[idx]; + if (node.type !== ENTRY) { + const n = assoc(node, shift + SHIFT, hash, key, val, addedLeaf); + if (n === node) { + return root; + } + return { + type: INDEX_NODE, + bitmap: root.bitmap, + array: cloneAndSet(root.array, idx, n), + }; + } + // otherwise there is an entry at the index + // if the keys are equal replace the entry with the updated value + const nodeKey = node.k; + if (isEqual(key, nodeKey)) { + if (val === node.v) { + return root; + } + return { + type: INDEX_NODE, + bitmap: root.bitmap, + array: cloneAndSet(root.array, idx, { + type: ENTRY, + k: key, + v: val, + }), + }; + } + // if the keys are not equal, replace the entry with a new child node + addedLeaf.val = true; + return { + type: INDEX_NODE, + bitmap: root.bitmap, + array: cloneAndSet( + root.array, + idx, + createNode(shift + SHIFT, nodeKey, node.v, hash, key, val), + ), + }; + } else { + // else there is currently no item at the hash index + const n = root.array.length; + // if the number of nodes is at the maximum, expand this node into an array node + if (n >= MAX_INDEX_NODE) { + // create a 32 length array for the new array node (one for each bit in the hash) + const nodes = new Array(32); + // create and insert a node for the new entry + const jdx = mask(hash, shift); + nodes[jdx] = assocIndex(EMPTY, shift + SHIFT, hash, key, val, addedLeaf); + let j = 0; + let bitmap = root.bitmap; + // place each item in the index node into the correct spot in the array node + // loop through all 32 bits / array positions + for (let i = 0; i < 32; i++) { + if ((bitmap & 1) !== 0) { + const node = root.array[j++]; + nodes[i] = node; + } + // shift the bitmap to process the next bit + bitmap = bitmap >>> 1; + } + return { + type: ARRAY_NODE, + size: n + 1, + array: nodes, + }; + } else { + // else there is still space in this index node + // simply insert a new entry at the hash index + const newArray = spliceIn(root.array, idx, { + type: ENTRY, + k: key, + v: val, + }); + addedLeaf.val = true; + return { + type: INDEX_NODE, + bitmap: root.bitmap | bit, + array: newArray, + }; + } + } +} +/** + * @template T,K,V + * @type {AssocFunction,K,V>} + */ +function assocCollision(root, shift, hash, key, val, addedLeaf) { + // if there is a hash collision + if (hash === root.hash) { + const idx = collisionIndexOf(root, key); + // if this key already exists replace the entry with the new value + if (idx !== -1) { + const entry = root.array[idx]; + if (entry.v === val) { + return root; + } + return { + type: COLLISION_NODE, + hash: hash, + array: cloneAndSet(root.array, idx, { type: ENTRY, k: key, v: val }), + }; + } + // otherwise insert the entry at the end of the array + const size = root.array.length; + addedLeaf.val = true; + return { + type: COLLISION_NODE, + hash: hash, + array: cloneAndSet(root.array, size, { type: ENTRY, k: key, v: val }), + }; + } + // if there is no hash collision, upgrade to an index node + return assoc( + { + type: INDEX_NODE, + bitmap: bitpos(root.hash, shift), + array: [root], + }, + shift, + hash, + key, + val, + addedLeaf, + ); +} +/** + * Find the index of a key in the collision node's array + * @template K,V + * @param {CollisionNode} root + * @param {K} key + * @returns {number} + */ +function collisionIndexOf(root, key) { + const size = root.array.length; + for (let i = 0; i < size; i++) { + if (isEqual(key, root.array[i].k)) { + return i; + } + } + return -1; +} +/** + * @template T,K,V + * @callback FindFunction + * @param {T} root + * @param {number} shift + * @param {number} hash + * @param {K} key + * @returns {undefined | Entry} + */ +/** + * Return the found entry or undefined if not present in the root + * @template K,V + * @type {FindFunction,K,V>} + */ +function find(root, shift, hash, key) { + switch (root.type) { + case ARRAY_NODE: + return findArray(root, shift, hash, key); + case INDEX_NODE: + return findIndex(root, shift, hash, key); + case COLLISION_NODE: + return findCollision(root, key); + } +} +/** + * @template K,V + * @type {FindFunction,K,V>} + */ +function findArray(root, shift, hash, key) { + const idx = mask(hash, shift); + const node = root.array[idx]; + if (node === undefined) { + return undefined; + } + if (node.type !== ENTRY) { + return find(node, shift + SHIFT, hash, key); + } + if (isEqual(key, node.k)) { + return node; + } + return undefined; +} +/** + * @template K,V + * @type {FindFunction,K,V>} + */ +function findIndex(root, shift, hash, key) { + const bit = bitpos(hash, shift); + if ((root.bitmap & bit) === 0) { + return undefined; + } + const idx = index(root.bitmap, bit); + const node = root.array[idx]; + if (node.type !== ENTRY) { + return find(node, shift + SHIFT, hash, key); + } + if (isEqual(key, node.k)) { + return node; + } + return undefined; +} +/** + * @template K,V + * @param {CollisionNode} root + * @param {K} key + * @returns {undefined | Entry} + */ +function findCollision(root, key) { + const idx = collisionIndexOf(root, key); + if (idx < 0) { + return undefined; + } + return root.array[idx]; +} +/** + * @template T,K,V + * @callback WithoutFunction + * @param {T} root + * @param {number} shift + * @param {number} hash + * @param {K} key + * @returns {undefined | Node} + */ +/** + * Remove an entry from the root, returning the updated root. + * Returns undefined if the node should be removed from the parent. + * @template K,V + * @type {WithoutFunction,K,V>} + * */ +function without(root, shift, hash, key) { + switch (root.type) { + case ARRAY_NODE: + return withoutArray(root, shift, hash, key); + case INDEX_NODE: + return withoutIndex(root, shift, hash, key); + case COLLISION_NODE: + return withoutCollision(root, key); + } +} +/** + * @template K,V + * @type {WithoutFunction,K,V>} + */ +function withoutArray(root, shift, hash, key) { + const idx = mask(hash, shift); + const node = root.array[idx]; + if (node === undefined) { + return root; // already empty + } + let n = undefined; + // if node is an entry and the keys are not equal there is nothing to remove + // if node is not an entry do a recursive call + if (node.type === ENTRY) { + if (!isEqual(node.k, key)) { + return root; // no changes + } + } else { + n = without(node, shift + SHIFT, hash, key); + if (n === node) { + return root; // no changes + } + } + // if the recursive call returned undefined the node should be removed + if (n === undefined) { + // if the number of child nodes is at the minimum, pack into an index node + if (root.size <= MIN_ARRAY_NODE) { + const arr = root.array; + const out = new Array(root.size - 1); + let i = 0; + let j = 0; + let bitmap = 0; + while (i < idx) { + const nv = arr[i]; + if (nv !== undefined) { + out[j] = nv; + bitmap |= 1 << i; + ++j; + } + ++i; + } + ++i; // skip copying the removed node + while (i < arr.length) { + const nv = arr[i]; + if (nv !== undefined) { + out[j] = nv; + bitmap |= 1 << i; + ++j; + } + ++i; + } + return { + type: INDEX_NODE, + bitmap: bitmap, + array: out, + }; + } + return { + type: ARRAY_NODE, + size: root.size - 1, + array: cloneAndSet(root.array, idx, n), + }; + } + return { + type: ARRAY_NODE, + size: root.size, + array: cloneAndSet(root.array, idx, n), + }; +} +/** + * @template K,V + * @type {WithoutFunction,K,V>} + */ +function withoutIndex(root, shift, hash, key) { + const bit = bitpos(hash, shift); + if ((root.bitmap & bit) === 0) { + return root; // already empty + } + const idx = index(root.bitmap, bit); + const node = root.array[idx]; + // if the item is not an entry + if (node.type !== ENTRY) { + const n = without(node, shift + SHIFT, hash, key); + if (n === node) { + return root; // no changes + } + // if not undefined, the child node still has items, so update it + if (n !== undefined) { + return { + type: INDEX_NODE, + bitmap: root.bitmap, + array: cloneAndSet(root.array, idx, n), + }; + } + // otherwise the child node should be removed + // if it was the only child node, remove this node from the parent + if (root.bitmap === bit) { + return undefined; + } + // otherwise just remove the child node + return { + type: INDEX_NODE, + bitmap: root.bitmap ^ bit, + array: spliceOut(root.array, idx), + }; + } + // otherwise the item is an entry, remove it if the key matches + if (isEqual(key, node.k)) { + if (root.bitmap === bit) { + return undefined; + } + return { + type: INDEX_NODE, + bitmap: root.bitmap ^ bit, + array: spliceOut(root.array, idx), + }; + } + return root; +} +/** + * @template K,V + * @param {CollisionNode} root + * @param {K} key + * @returns {undefined | Node} + */ +function withoutCollision(root, key) { + const idx = collisionIndexOf(root, key); + // if the key not found, no changes + if (idx < 0) { + return root; + } + // otherwise the entry was found, remove it + // if it was the only entry in this node, remove the whole node + if (root.array.length === 1) { + return undefined; + } + // otherwise just remove the entry + return { + type: COLLISION_NODE, + hash: root.hash, + array: spliceOut(root.array, idx), + }; +} +/** + * @template K,V + * @param {undefined | Node} root + * @param {(value:V,key:K)=>void} fn + * @returns {void} + */ +function forEach(root, fn) { + if (root === undefined) { + return; + } + const items = root.array; + const size = items.length; + for (let i = 0; i < size; i++) { + const item = items[i]; + if (item === undefined) { + continue; + } + if (item.type === ENTRY) { + fn(item.v, item.k); + continue; + } + forEach(item, fn); + } +} + +/** + * Extra wrapper to keep track of Dict size and clean up the API + * @template K,V + */ +export default class Dict { + /** + * @template V + * @param {Record} o + * @returns {Dict} + */ + static fromObject(o) { + const keys = Object.keys(o); + /** @type Dict */ + let m = Dict.new(); + for (let i = 0; i < keys.length; i++) { + const k = keys[i]; + m = m.set(k, o[k]); + } + return m; + } + + /** + * @template K,V + * @param {Map} o + * @returns {Dict} + */ + static fromMap(o) { + /** @type Dict */ + let m = Dict.new(); + o.forEach((v, k) => { + m = m.set(k, v); + }); + return m; + } + + static new() { + return new Dict(undefined, 0); + } + + /** + * @param {undefined | Node} root + * @param {number} size + */ + constructor(root, size) { + this.root = root; + this.size = size; + } + /** + * @template NotFound + * @param {K} key + * @param {NotFound} notFound + * @returns {NotFound | V} + */ + get(key, notFound) { + if (this.root === undefined) { + return notFound; + } + const found = find(this.root, 0, getHash(key), key); + if (found === undefined) { + return notFound; + } + return found.v; + } + /** + * @param {K} key + * @param {V} val + * @returns {Dict} + */ + set(key, val) { + const addedLeaf = { val: false }; + const root = this.root === undefined ? EMPTY : this.root; + const newRoot = assoc(root, 0, getHash(key), key, val, addedLeaf); + if (newRoot === this.root) { + return this; + } + return new Dict(newRoot, addedLeaf.val ? this.size + 1 : this.size); + } + /** + * @param {K} key + * @returns {Dict} + */ + delete(key) { + if (this.root === undefined) { + return this; + } + const newRoot = without(this.root, 0, getHash(key), key); + if (newRoot === this.root) { + return this; + } + if (newRoot === undefined) { + return Dict.new(); + } + return new Dict(newRoot, this.size - 1); + } + /** + * @param {K} key + * @returns {boolean} + */ + has(key) { + if (this.root === undefined) { + return false; + } + return find(this.root, 0, getHash(key), key) !== undefined; + } + /** + * @returns {[K,V][]} + */ + entries() { + if (this.root === undefined) { + return []; + } + /** @type [K,V][] */ + const result = []; + this.forEach((v, k) => result.push([k, v])); + return result; + } + /** + * + * @param {(val:V,key:K)=>void} fn + */ + forEach(fn) { + forEach(this.root, fn); + } + hashCode() { + let h = 0; + this.forEach((v, k) => { + h = (h + hashMerge(getHash(v), getHash(k))) | 0; + }); + return h; + } + /** + * @param {unknown} o + * @returns {boolean} + */ + equals(o) { + if (!(o instanceof Dict) || this.size !== o.size) { + return false; + } + + try { + this.forEach((v, k) => { + if (!isEqual(o.get(k, !v), v)) { + throw unequalDictSymbol; + } + }); + return true; + } catch (e) { + if (e === unequalDictSymbol) { + return false; + } + + throw e; + } + } +} + +// This is thrown internally in Dict.equals() so that it returns false as soon +// as a non-matching key is found +const unequalDictSymbol = /* @__PURE__ */ Symbol(); diff --git a/build/packages/gleam_stdlib/src/gleam/bit_array.gleam b/build/packages/gleam_stdlib/src/gleam/bit_array.gleam new file mode 100644 index 0000000..544b74a --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam/bit_array.gleam @@ -0,0 +1,280 @@ +//// BitArrays are a sequence of binary data of any length. + +import gleam/int +import gleam/order +import gleam/string + +/// Converts a UTF-8 `String` type into a `BitArray`. +/// +@external(erlang, "gleam_stdlib", "identity") +@external(javascript, "../gleam_stdlib.mjs", "bit_array_from_string") +pub fn from_string(x: String) -> BitArray + +/// Returns an integer which is the number of bits in the bit array. +/// +@external(erlang, "erlang", "bit_size") +@external(javascript, "../gleam_stdlib.mjs", "bit_array_bit_size") +pub fn bit_size(x: BitArray) -> Int + +/// Returns an integer which is the number of bytes in the bit array. +/// +@external(erlang, "erlang", "byte_size") +@external(javascript, "../gleam_stdlib.mjs", "bit_array_byte_size") +pub fn byte_size(x: BitArray) -> Int + +/// Pads a bit array with zeros so that it is a whole number of bytes. +/// +@external(erlang, "gleam_stdlib", "bit_array_pad_to_bytes") +@external(javascript, "../gleam_stdlib.mjs", "bit_array_pad_to_bytes") +pub fn pad_to_bytes(x: BitArray) -> BitArray + +/// Creates a new bit array by joining two bit arrays. +/// +/// ## Examples +/// +/// ```gleam +/// append(to: from_string("butter"), suffix: from_string("fly")) +/// // -> from_string("butterfly") +/// ``` +/// +pub fn append(to first: BitArray, suffix second: BitArray) -> BitArray { + concat([first, second]) +} + +/// Extracts a sub-section of a bit array. +/// +/// The slice 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 array. +/// +/// This function runs in constant time. +/// +@external(erlang, "gleam_stdlib", "bit_array_slice") +@external(javascript, "../gleam_stdlib.mjs", "bit_array_slice") +pub fn slice( + from string: BitArray, + at position: Int, + take length: Int, +) -> Result(BitArray, Nil) + +/// Tests to see whether a bit array is valid UTF-8. +/// +pub fn is_utf8(bits: BitArray) -> Bool { + is_utf8_loop(bits) +} + +@target(erlang) +fn is_utf8_loop(bits: BitArray) -> Bool { + case bits { + <<>> -> True + <<_:utf8, rest:bytes>> -> is_utf8_loop(rest) + _ -> False + } +} + +@target(javascript) +fn is_utf8_loop(bits: BitArray) -> Bool { + case to_string(bits) { + Ok(_) -> True + Error(_) -> False + } +} + +/// Converts a bit array to a string. +/// +/// Returns an error if the bit array is invalid UTF-8 data. +/// +@external(javascript, "../gleam_stdlib.mjs", "bit_array_to_string") +pub fn to_string(bits: BitArray) -> Result(String, Nil) { + case is_utf8(bits) { + True -> Ok(unsafe_to_string(bits)) + False -> Error(Nil) + } +} + +@external(erlang, "gleam_stdlib", "identity") +fn unsafe_to_string(a: BitArray) -> String + +/// Creates a new bit array by joining multiple binaries. +/// +/// ## Examples +/// +/// ```gleam +/// concat([from_string("butter"), from_string("fly")]) +/// // -> from_string("butterfly") +/// ``` +/// +@external(erlang, "gleam_stdlib", "bit_array_concat") +@external(javascript, "../gleam_stdlib.mjs", "bit_array_concat") +pub fn concat(bit_arrays: List(BitArray)) -> BitArray + +/// Encodes a BitArray into a base 64 encoded string. +/// +/// If the bit array does not contain a whole number of bytes then it is padded +/// with zero bits prior to being encoded. +/// +@external(erlang, "gleam_stdlib", "base64_encode") +@external(javascript, "../gleam_stdlib.mjs", "base64_encode") +pub fn base64_encode(input: BitArray, padding: Bool) -> String + +/// Decodes a base 64 encoded string into a `BitArray`. +/// +pub fn base64_decode(encoded: String) -> Result(BitArray, Nil) { + let padded = case byte_size(from_string(encoded)) % 4 { + 0 -> encoded + n -> string.append(encoded, string.repeat("=", 4 - n)) + } + decode64(padded) +} + +@external(erlang, "gleam_stdlib", "base64_decode") +@external(javascript, "../gleam_stdlib.mjs", "base64_decode") +fn decode64(a: String) -> Result(BitArray, Nil) + +/// Encodes a `BitArray` into a base 64 encoded string with URL and filename +/// safe alphabet. +/// +/// If the bit array does not contain a whole number of bytes then it is padded +/// with zero bits prior to being encoded. +/// +pub fn base64_url_encode(input: BitArray, padding: Bool) -> String { + input + |> base64_encode(padding) + |> string.replace("+", "-") + |> string.replace("/", "_") +} + +/// Decodes a base 64 encoded string with URL and filename safe alphabet into a +/// `BitArray`. +/// +pub fn base64_url_decode(encoded: String) -> Result(BitArray, Nil) { + encoded + |> string.replace("-", "+") + |> string.replace("_", "/") + |> base64_decode() +} + +/// Encodes a `BitArray` into a base 16 encoded string. +/// +/// If the bit array does not contain a whole number of bytes then it is padded +/// with zero bits prior to being encoded. +/// +@external(erlang, "gleam_stdlib", "base16_encode") +@external(javascript, "../gleam_stdlib.mjs", "base16_encode") +pub fn base16_encode(input: BitArray) -> String + +/// Decodes a base 16 encoded string into a `BitArray`. +/// +@external(erlang, "gleam_stdlib", "base16_decode") +@external(javascript, "../gleam_stdlib.mjs", "base16_decode") +pub fn base16_decode(input: String) -> Result(BitArray, Nil) + +/// Converts a bit array to a string containing the decimal value of each byte. +/// +/// Use this over `string.inspect` when you have a bit array you want printed +/// in the array syntax even if it is valid UTF-8. +/// +/// ## Examples +/// +/// ```gleam +/// inspect(<<0, 20, 0x20, 255>>) +/// // -> "<<0, 20, 32, 255>>" +/// +/// inspect(<<100, 5:3>>) +/// // -> "<<100, 5:size(3)>>" +/// ``` +/// +pub fn inspect(input: BitArray) -> String { + inspect_loop(input, "<<") <> ">>" +} + +fn inspect_loop(input: BitArray, accumulator: String) -> String { + case input { + <<>> -> accumulator + + <> -> accumulator <> int.to_string(x) <> ":size(1)" + <> -> accumulator <> int.to_string(x) <> ":size(2)" + <> -> accumulator <> int.to_string(x) <> ":size(3)" + <> -> accumulator <> int.to_string(x) <> ":size(4)" + <> -> accumulator <> int.to_string(x) <> ":size(5)" + <> -> accumulator <> int.to_string(x) <> ":size(6)" + <> -> accumulator <> int.to_string(x) <> ":size(7)" + + <> -> { + let suffix = case rest { + <<>> -> "" + _ -> ", " + } + + let accumulator = accumulator <> int.to_string(x) <> suffix + inspect_loop(rest, accumulator) + } + + _ -> accumulator + } +} + +/// Compare two bit arrays as sequences of bytes. +/// +/// ## Examples +/// +/// ```gleam +/// compare(<<1>>, <<2>>) +/// // -> Lt +/// +/// compare(<<"AB":utf8>>, <<"AA":utf8>>) +/// // -> Gt +/// +/// compare(<<1, 2:size(2)>>, with: <<1, 2:size(2)>>) +/// // -> Eq +/// ``` +/// +pub fn compare(a: BitArray, with b: BitArray) -> order.Order { + case a, b { + <>, <> -> + case first_byte, second_byte { + f, s if f > s -> order.Gt + f, s if f < s -> order.Lt + _, _ -> compare(first_rest, second_rest) + } + + <<>>, <<>> -> order.Eq + // First has more items, example: "AB" > "A": + _, <<>> -> order.Gt + // Second has more items, example: "A" < "AB": + <<>>, _ -> order.Lt + // This happens when there's unusually sized elements. + // Handle these special cases via custom erlang function. + first, second -> + case bit_array_to_int_and_size(first), bit_array_to_int_and_size(second) { + #(a, _), #(b, _) if a > b -> order.Gt + #(a, _), #(b, _) if a < b -> order.Lt + #(_, size_a), #(_, size_b) if size_a > size_b -> order.Gt + #(_, size_a), #(_, size_b) if size_a < size_b -> order.Lt + _, _ -> order.Eq + } + } +} + +@external(erlang, "gleam_stdlib", "bit_array_to_int_and_size") +@external(javascript, "../gleam_stdlib.mjs", "bit_array_to_int_and_size") +fn bit_array_to_int_and_size(a: BitArray) -> #(Int, Int) + +/// Checks whether the first `BitArray` starts with the second one. +/// +/// ## Examples +/// +/// ```gleam +/// starts_with(<<1, 2, 3, 4>>, <<1, 2>>) +/// // -> True +/// ``` +/// +@external(javascript, "../gleam_stdlib.mjs", "bit_array_starts_with") +pub fn starts_with(bits: BitArray, prefix: BitArray) -> Bool { + let prefix_size = bit_size(prefix) + + case bits { + <> if pref == prefix -> True + _ -> False + } +} diff --git a/build/packages/gleam_stdlib/src/gleam/bool.gleam b/build/packages/gleam_stdlib/src/gleam/bool.gleam new file mode 100644 index 0000000..26a6ac4 --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam/bool.gleam @@ -0,0 +1,316 @@ +//// 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`. + +/// Returns the and of two bools, but it evaluates both arguments. +/// +/// It's the function equivalent of the `&&` operator. +/// This function is useful in higher order functions or pipes. +/// +/// ## Examples +/// +/// ```gleam +/// and(True, True) +/// // -> True +/// ``` +/// +/// ```gleam +/// and(False, True) +/// // -> False +/// ``` +/// +/// ```gleam +/// False |> and(True) +/// // -> False +/// ``` +/// +pub fn and(a: Bool, b: Bool) -> Bool { + a && b +} + +/// Returns the or of two bools, but it evaluates both arguments. +/// +/// It's the function equivalent of the `||` operator. +/// This function is useful in higher order functions or pipes. +/// +/// ## Examples +/// +/// ```gleam +/// or(True, True) +/// // -> True +/// ``` +/// +/// ```gleam +/// or(False, True) +/// // -> True +/// ``` +/// +/// ```gleam +/// False |> or(True) +/// // -> True +/// ``` +/// +pub fn or(a: Bool, b: Bool) -> Bool { + a || b +} + +/// Returns the opposite bool value. +/// +/// This is the same as the `!` or `not` operators in some other languages. +/// +/// ## Examples +/// +/// ```gleam +/// negate(True) +/// // -> False +/// ``` +/// +/// ```gleam +/// negate(False) +/// // -> True +/// ``` +/// +pub fn negate(bool: Bool) -> Bool { + !bool +} + +/// Returns the nor of two bools. +/// +/// ## Examples +/// +/// ```gleam +/// nor(False, False) +/// // -> True +/// ``` +/// +/// ```gleam +/// nor(False, True) +/// // -> False +/// ``` +/// +/// ```gleam +/// nor(True, False) +/// // -> False +/// ``` +/// +/// ```gleam +/// nor(True, True) +/// // -> False +/// ``` +/// +pub fn nor(a: Bool, b: Bool) -> Bool { + !{ a || b } +} + +/// Returns the nand of two bools. +/// +/// ## Examples +/// +/// ```gleam +/// nand(False, False) +/// // -> True +/// ``` +/// +/// ```gleam +/// nand(False, True) +/// // -> True +/// ``` +/// +/// ```gleam +/// nand(True, False) +/// // -> True +/// ``` +/// +/// ```gleam +/// nand(True, True) +/// // -> False +/// ``` +/// +pub fn nand(a: Bool, b: Bool) -> Bool { + !{ a && b } +} + +/// Returns the exclusive or of two bools. +/// +/// ## Examples +/// +/// ```gleam +/// exclusive_or(False, False) +/// // -> False +/// ``` +/// +/// ```gleam +/// exclusive_or(False, True) +/// // -> True +/// ``` +/// +/// ```gleam +/// exclusive_or(True, False) +/// // -> True +/// ``` +/// +/// ```gleam +/// exclusive_or(True, True) +/// // -> False +/// ``` +/// +pub fn exclusive_or(a: Bool, b: Bool) -> Bool { + a != b +} + +/// Returns the exclusive nor of two bools. +/// +/// ## Examples +/// +/// ```gleam +/// exclusive_nor(False, False) +/// // -> True +/// ``` +/// +/// ```gleam +/// exclusive_nor(False, True) +/// // -> False +/// ``` +/// +/// ```gleam +/// exclusive_nor(True, False) +/// // -> False +/// ``` +/// +/// ```gleam +/// exclusive_nor(True, True) +/// // -> True +/// ``` +/// +pub fn exclusive_nor(a: Bool, b: Bool) -> Bool { + a == b +} + +/// Returns a string representation of the given bool. +/// +/// ## Examples +/// +/// ```gleam +/// to_string(True) +/// // -> "True" +/// ``` +/// +/// ```gleam +/// to_string(False) +/// // -> "False" +/// ``` +/// +pub fn to_string(bool: Bool) -> String { + case bool { + False -> "False" + True -> "True" + } +} + +/// Run a callback function if the given bool is `False`, otherwise return a +/// default value. +/// +/// With a `use` expression this function can simulate the early-return pattern +/// found in some other programming languages. +/// +/// In a procedural language: +/// +/// ```js +/// if (predicate) return value; +/// // ... +/// ``` +/// +/// In Gleam with a `use` expression: +/// +/// ```gleam +/// use <- guard(when: predicate, return: value) +/// // ... +/// ``` +/// +/// Like everything in Gleam `use` is an expression, so it short circuits the +/// current block, not the entire function. As a result you can assign the value +/// to a variable: +/// +/// ```gleam +/// let x = { +/// use <- guard(when: predicate, return: value) +/// // ... +/// } +/// ``` +/// +/// Note that unlike in procedural languages the `return` value is evaluated +/// even when the predicate is `False`, so it is advisable not to perform +/// expensive computation nor side-effects there. +/// +/// +/// ## Examples +/// +/// ```gleam +/// let name = "" +/// use <- guard(when: name == "", return: "Welcome!") +/// "Hello, " <> name +/// // -> "Welcome!" +/// ``` +/// +/// ```gleam +/// let name = "Kamaka" +/// use <- guard(when: name == "", return: "Welcome!") +/// "Hello, " <> name +/// // -> "Hello, Kamaka" +/// ``` +/// +pub fn guard( + when requirement: Bool, + return consequence: a, + otherwise alternative: fn() -> a, +) -> a { + case requirement { + True -> consequence + False -> alternative() + } +} + +/// Runs a callback function if the given bool is `True`, otherwise runs an +/// alternative callback function. +/// +/// Useful when further computation should be delayed regardless of the given +/// bool's value. +/// +/// See [`guard`](#guard) for more info. +/// +/// ## Examples +/// +/// ```gleam +/// let name = "Kamaka" +/// let inquiry = fn() { "How may we address you?" } +/// use <- lazy_guard(when: name == "", return: inquiry) +/// "Hello, " <> name +/// // -> "Hello, Kamaka" +/// ``` +/// +/// ```gleam +/// import gleam/int +/// +/// let name = "" +/// let greeting = fn() { "Hello, " <> name } +/// use <- lazy_guard(when: name == "", otherwise: greeting) +/// let number = int.random(99) +/// let name = "User " <> int.to_string(number) +/// "Welcome, " <> name +/// // -> "Welcome, User 54" +/// ``` +/// +pub fn lazy_guard( + when requirement: Bool, + return consequence: fn() -> a, + otherwise alternative: fn() -> a, +) -> a { + case requirement { + True -> consequence() + False -> alternative() + } +} diff --git a/build/packages/gleam_stdlib/src/gleam/bytes_tree.gleam b/build/packages/gleam_stdlib/src/gleam/bytes_tree.gleam new file mode 100644 index 0000000..832fbee --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam/bytes_tree.gleam @@ -0,0 +1,190 @@ +//// `BytesTree` is a type used for efficiently building binary content to be +//// written to a file or a socket. Internally it is represented as tree so to +//// append or prepend to a bytes tree is a constant time operation that +//// allocates a new node in the tree without copying any of the content. When +//// writing to an output stream the tree is traversed and the content is sent +//// directly rather than copying it into a single buffer beforehand. +//// +//// If we append one bit array to another the bit arrays must be copied to a +//// new location in memory so that they can sit together. This behaviour +//// enables efficient reading of the data but copying can be expensive, +//// especially if we want to join many bit arrays together. +//// +//// BytesTree is different in that it can be joined together in constant +//// time using minimal memory, and then can be efficiently converted to a +//// bit array using the `to_bit_array` function. +//// +//// Byte trees are always byte aligned, so that a number of bits that is not +//// divisible by 8 will be padded with 0s. +//// +//// On Erlang this type is compatible with Erlang's iolists. + +import gleam/bit_array +import gleam/list +import gleam/string_tree.{type StringTree} + +pub opaque type BytesTree { + Bytes(BitArray) + Text(StringTree) + Many(List(BytesTree)) +} + +/// Create an empty `BytesTree`. Useful as the start of a pipe chaining many +/// trees together. +/// +pub fn new() -> BytesTree { + concat([]) +} + +/// Prepends a bit array to the start of a bytes tree. +/// +/// Runs in constant time. +/// +pub fn prepend(to second: BytesTree, prefix first: BitArray) -> BytesTree { + append_tree(from_bit_array(first), second) +} + +/// Appends a bit array to the end of a bytes tree. +/// +/// Runs in constant time. +/// +pub fn append(to first: BytesTree, suffix second: BitArray) -> BytesTree { + append_tree(first, from_bit_array(second)) +} + +/// Prepends a bytes tree onto the start of another. +/// +/// Runs in constant time. +/// +pub fn prepend_tree(to second: BytesTree, prefix first: BytesTree) -> BytesTree { + append_tree(first, second) +} + +/// Appends a bytes tree onto the end of another. +/// +/// Runs in constant time. +/// +@external(erlang, "gleam_stdlib", "iodata_append") +pub fn append_tree(to first: BytesTree, suffix second: BytesTree) -> BytesTree { + case second { + Many(trees) -> Many([first, ..trees]) + Text(_) | Bytes(_) -> Many([first, second]) + } +} + +/// Prepends a string onto the start of a bytes tree. +/// +/// Runs in constant time when running on Erlang. +/// Runs in linear time with the length of the string otherwise. +/// +pub fn prepend_string(to second: BytesTree, prefix first: String) -> BytesTree { + append_tree(from_string(first), second) +} + +/// Appends a string onto the end of a bytes tree. +/// +/// Runs in constant time when running on Erlang. +/// Runs in linear time with the length of the string otherwise. +/// +pub fn append_string(to first: BytesTree, suffix second: String) -> BytesTree { + append_tree(first, from_string(second)) +} + +/// Joins a list of bytes trees into a single one. +/// +/// Runs in constant time. +/// +@external(erlang, "gleam_stdlib", "identity") +pub fn concat(trees: List(BytesTree)) -> BytesTree { + Many(trees) +} + +/// Joins a list of bit arrays into a single bytes tree. +/// +/// Runs in constant time. +/// +pub fn concat_bit_arrays(bits: List(BitArray)) -> BytesTree { + bits + |> list.map(from_bit_array) + |> concat() +} + +/// Creates a new bytes tree from a string. +/// +/// Runs in constant time when running on Erlang. +/// Runs in linear time otherwise. +/// +@external(erlang, "gleam_stdlib", "wrap_list") +pub fn from_string(string: String) -> BytesTree { + Text(string_tree.from_string(string)) +} + +/// Creates a new bytes tree from a string tree. +/// +/// Runs in constant time when running on Erlang. +/// Runs in linear time otherwise. +/// +@external(erlang, "gleam_stdlib", "wrap_list") +pub fn from_string_tree(tree: string_tree.StringTree) -> BytesTree { + Text(tree) +} + +/// Creates a new bytes tree from a bit array. +/// +/// Runs in constant time. +/// +pub fn from_bit_array(bits: BitArray) -> BytesTree { + bits + |> bit_array.pad_to_bytes + |> wrap_list +} + +@external(erlang, "gleam_stdlib", "wrap_list") +fn wrap_list(bits: BitArray) -> BytesTree { + Bytes(bits) +} + +/// Turns a bytes tree into a bit array. +/// +/// Runs in linear time. +/// +/// When running on Erlang this function is implemented natively by the +/// virtual machine and is highly optimised. +/// +@external(erlang, "erlang", "list_to_bitstring") +pub fn to_bit_array(tree: BytesTree) -> BitArray { + [[tree]] + |> to_list([]) + |> list.reverse + |> bit_array.concat +} + +fn to_list(stack: List(List(BytesTree)), acc: List(BitArray)) -> List(BitArray) { + case stack { + [] -> acc + + [[], ..remaining_stack] -> to_list(remaining_stack, acc) + + [[Bytes(bits), ..rest], ..remaining_stack] -> + to_list([rest, ..remaining_stack], [bits, ..acc]) + + [[Text(tree), ..rest], ..remaining_stack] -> { + let bits = bit_array.from_string(string_tree.to_string(tree)) + to_list([rest, ..remaining_stack], [bits, ..acc]) + } + + [[Many(trees), ..rest], ..remaining_stack] -> + to_list([trees, rest, ..remaining_stack], acc) + } +} + +/// Returns the size of the bytes tree's content in bytes. +/// +/// Runs in linear time. +/// +@external(erlang, "erlang", "iolist_size") +pub fn byte_size(tree: BytesTree) -> Int { + [[tree]] + |> to_list([]) + |> list.fold(0, fn(acc, bits) { bit_array.byte_size(bits) + acc }) +} diff --git a/build/packages/gleam_stdlib/src/gleam/dict.gleam b/build/packages/gleam_stdlib/src/gleam/dict.gleam new file mode 100644 index 0000000..2942727 --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam/dict.gleam @@ -0,0 +1,548 @@ +import gleam/option.{type Option} + +/// A dictionary of keys and values. +/// +/// Any type can be used for the keys and values of a dict, 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 dict once. +/// +/// Dicts 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 type Dict(key, value) + +/// Determines the number of key-value pairs in the dict. +/// This function runs in constant time and does not need to iterate the dict. +/// +/// ## Examples +/// +/// ```gleam +/// new() |> size +/// // -> 0 +/// ``` +/// +/// ```gleam +/// new() |> insert("key", "value") |> size +/// // -> 1 +/// ``` +/// +@external(erlang, "maps", "size") +@external(javascript, "../gleam_stdlib.mjs", "map_size") +pub fn size(dict: Dict(k, v)) -> Int + +/// Determines whether or not the dict is empty. +/// +/// ## Examples +/// +/// ```gleam +/// new() |> is_empty +/// // -> True +/// ``` +/// +/// ```gleam +/// new() |> insert("b", 1) |> is_empty +/// // -> False +/// ``` +/// +pub fn is_empty(dict: Dict(k, v)) -> Bool { + size(dict) == 0 +} + +/// Converts the dict to a list of 2-element tuples `#(key, value)`, one for +/// each key-value pair in the dict. +/// +/// The tuples in the list have no specific order. +/// +/// ## Examples +/// +/// Calling `to_list` on an empty `dict` returns an empty list. +/// +/// ```gleam +/// new() |> to_list +/// // -> [] +/// ``` +/// +/// The ordering of elements in the resulting list is an implementation detail +/// that should not be relied upon. +/// +/// ```gleam +/// new() |> insert("b", 1) |> insert("a", 0) |> insert("c", 2) |> to_list +/// // -> [#("a", 0), #("b", 1), #("c", 2)] +/// ``` +/// +@external(erlang, "maps", "to_list") +@external(javascript, "../gleam_stdlib.mjs", "map_to_list") +pub fn to_list(dict: Dict(k, v)) -> List(#(k, v)) + +/// Converts a list of 2-element tuples `#(key, value)` to a dict. +/// +/// If two tuples have the same key the last one in the list will be the one +/// that is present in the dict. +/// +@external(erlang, "maps", "from_list") +pub fn from_list(list: List(#(k, v))) -> Dict(k, v) { + from_list_loop(list, new()) +} + +fn from_list_loop( + over list: List(#(k, v)), + from initial: Dict(k, v), +) -> Dict(k, v) { + case list { + [] -> initial + [#(key, value), ..rest] -> from_list_loop(rest, insert(initial, key, value)) + } +} + +/// Determines whether or not a value present in the dict for a given key. +/// +/// ## Examples +/// +/// ```gleam +/// new() |> insert("a", 0) |> has_key("a") +/// // -> True +/// ``` +/// +/// ```gleam +/// new() |> insert("a", 0) |> has_key("b") +/// // -> False +/// ``` +/// +pub fn has_key(dict: Dict(k, v), key: k) -> Bool { + do_has_key(key, dict) +} + +@external(erlang, "maps", "is_key") +fn do_has_key(key: k, dict: Dict(k, v)) -> Bool { + get(dict, key) != Error(Nil) +} + +/// Creates a fresh dict that contains no values. +/// +@external(erlang, "maps", "new") +@external(javascript, "../gleam_stdlib.mjs", "new_map") +pub fn new() -> Dict(k, v) + +/// Fetches a value from a dict for a given key. +/// +/// The dict may not have a value for the key, so the value is wrapped in a +/// `Result`. +/// +/// ## Examples +/// +/// ```gleam +/// new() |> insert("a", 0) |> get("a") +/// // -> Ok(0) +/// ``` +/// +/// ```gleam +/// new() |> insert("a", 0) |> get("b") +/// // -> Error(Nil) +/// ``` +/// +@external(erlang, "gleam_stdlib", "map_get") +@external(javascript, "../gleam_stdlib.mjs", "map_get") +pub fn get(from: Dict(k, v), get: k) -> Result(v, Nil) + +/// Inserts a value into the dict with the given key. +/// +/// If the dict already has a value for the given key then the value is +/// replaced with the new value. +/// +/// ## Examples +/// +/// ```gleam +/// new() |> insert("a", 0) +/// // -> from_list([#("a", 0)]) +/// ``` +/// +/// ```gleam +/// new() |> insert("a", 0) |> insert("a", 5) +/// // -> from_list([#("a", 5)]) +/// ``` +/// +pub fn insert(into dict: Dict(k, v), for key: k, insert value: v) -> Dict(k, v) { + do_insert(key, value, dict) +} + +@external(erlang, "maps", "put") +@external(javascript, "../gleam_stdlib.mjs", "map_insert") +fn do_insert(key: k, value: v, dict: Dict(k, v)) -> Dict(k, v) + +/// Updates all values in a given dict by calling a given function on each key +/// and value. +/// +/// ## Examples +/// +/// ```gleam +/// from_list([#(3, 3), #(2, 4)]) +/// |> map_values(fn(key, value) { key * value }) +/// // -> from_list([#(3, 9), #(2, 8)]) +/// ``` +/// +pub fn map_values(in dict: Dict(k, v), with fun: fn(k, v) -> a) -> Dict(k, a) { + do_map_values(fun, dict) +} + +@external(erlang, "maps", "map") +fn do_map_values(f: fn(k, v) -> a, dict: Dict(k, v)) -> Dict(k, a) { + let f = fn(dict, k, v) { insert(dict, k, f(k, v)) } + fold(dict, from: new(), with: f) +} + +/// Gets a list of all keys in a given dict. +/// +/// Dicts 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 +/// +/// ```gleam +/// from_list([#("a", 0), #("b", 1)]) |> keys +/// // -> ["a", "b"] +/// ``` +/// +@external(erlang, "maps", "keys") +pub fn keys(dict: Dict(k, v)) -> List(k) { + do_keys_loop(to_list(dict), []) +} + +fn do_keys_loop(list: List(#(k, v)), acc: List(k)) -> List(k) { + case list { + [] -> reverse_and_concat(acc, []) + [#(key, _value), ..rest] -> do_keys_loop(rest, [key, ..acc]) + } +} + +fn reverse_and_concat(remaining: List(a), accumulator: List(a)) -> List(a) { + case remaining { + [] -> accumulator + [first, ..rest] -> reverse_and_concat(rest, [first, ..accumulator]) + } +} + +/// Gets a list of all values in a given dict. +/// +/// Dicts 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 +/// +/// ```gleam +/// from_list([#("a", 0), #("b", 1)]) |> values +/// // -> [0, 1] +/// ``` +/// +@external(erlang, "maps", "values") +pub fn values(dict: Dict(k, v)) -> List(v) { + let list_of_pairs = to_list(dict) + do_values_loop(list_of_pairs, []) +} + +fn do_values_loop(list: List(#(k, v)), acc: List(v)) -> List(v) { + case list { + [] -> reverse_and_concat(acc, []) + [#(_key, value), ..rest] -> do_values_loop(rest, [value, ..acc]) + } +} + +/// Creates a new dict from a given dict, minus any entries that a given function +/// returns `False` for. +/// +/// ## Examples +/// +/// ```gleam +/// from_list([#("a", 0), #("b", 1)]) +/// |> filter(fn(key, value) { value != 0 }) +/// // -> from_list([#("b", 1)]) +/// ``` +/// +/// ```gleam +/// from_list([#("a", 0), #("b", 1)]) +/// |> filter(fn(key, value) { True }) +/// // -> from_list([#("a", 0), #("b", 1)]) +/// ``` +/// +pub fn filter( + in dict: Dict(k, v), + keeping predicate: fn(k, v) -> Bool, +) -> Dict(k, v) { + do_filter(predicate, dict) +} + +@external(erlang, "maps", "filter") +fn do_filter(f: fn(k, v) -> Bool, dict: Dict(k, v)) -> Dict(k, v) { + let insert = fn(dict, k, v) { + case f(k, v) { + True -> insert(dict, k, v) + False -> dict + } + } + + fold(dict, from: new(), with: insert) +} + +/// Creates a new dict from a given dict, only including any entries for which the +/// keys are in a given list. +/// +/// ## Examples +/// +/// ```gleam +/// from_list([#("a", 0), #("b", 1)]) +/// |> take(["b"]) +/// // -> from_list([#("b", 1)]) +/// ``` +/// +/// ```gleam +/// from_list([#("a", 0), #("b", 1)]) +/// |> take(["a", "b", "c"]) +/// // -> from_list([#("a", 0), #("b", 1)]) +/// ``` +/// +pub fn take(from dict: Dict(k, v), keeping desired_keys: List(k)) -> Dict(k, v) { + do_take(desired_keys, dict) +} + +@external(erlang, "maps", "with") +fn do_take(desired_keys: List(k), dict: Dict(k, v)) -> Dict(k, v) { + do_take_loop(dict, desired_keys, new()) +} + +fn do_take_loop( + dict: Dict(k, v), + desired_keys: List(k), + acc: Dict(k, v), +) -> Dict(k, v) { + let insert = fn(taken, key) { + case get(dict, key) { + Ok(value) -> insert(taken, key, value) + Error(_) -> taken + } + } + case desired_keys { + [] -> acc + [first, ..rest] -> do_take_loop(dict, rest, insert(acc, first)) + } +} + +/// Creates a new dict from a pair of given dicts by combining their entries. +/// +/// If there are entries with the same keys in both dicts the entry from the +/// second dict takes precedence. +/// +/// ## Examples +/// +/// ```gleam +/// let a = from_list([#("a", 0), #("b", 1)]) +/// let b = from_list([#("b", 2), #("c", 3)]) +/// merge(a, b) +/// // -> from_list([#("a", 0), #("b", 2), #("c", 3)]) +/// ``` +/// +@external(erlang, "maps", "merge") +pub fn merge(into dict: Dict(k, v), from new_entries: Dict(k, v)) -> Dict(k, v) { + new_entries + |> to_list + |> fold_inserts(dict) +} + +fn fold_inserts(new_entries: List(#(k, v)), dict: Dict(k, v)) -> Dict(k, v) { + case new_entries { + [] -> dict + [first, ..rest] -> fold_inserts(rest, insert_pair(dict, first)) + } +} + +fn insert_pair(dict: Dict(k, v), pair: #(k, v)) -> Dict(k, v) { + insert(dict, pair.0, pair.1) +} + +/// Creates a new dict from a given dict with all the same entries except for the +/// one with a given key, if it exists. +/// +/// ## Examples +/// +/// ```gleam +/// from_list([#("a", 0), #("b", 1)]) |> delete("a") +/// // -> from_list([#("b", 1)]) +/// ``` +/// +/// ```gleam +/// from_list([#("a", 0), #("b", 1)]) |> delete("c") +/// // -> from_list([#("a", 0), #("b", 1)]) +/// ``` +/// +pub fn delete(from dict: Dict(k, v), delete key: k) -> Dict(k, v) { + do_delete(key, dict) +} + +@external(erlang, "maps", "remove") +@external(javascript, "../gleam_stdlib.mjs", "map_remove") +fn do_delete(a: k, b: Dict(k, v)) -> Dict(k, v) + +/// Creates a new dict from a given dict with all the same entries except any with +/// keys found in a given list. +/// +/// ## Examples +/// +/// ```gleam +/// from_list([#("a", 0), #("b", 1)]) |> drop(["a"]) +/// // -> from_list([#("b", 1)]) +/// ``` +/// +/// ```gleam +/// from_list([#("a", 0), #("b", 1)]) |> drop(["c"]) +/// // -> from_list([#("a", 0), #("b", 1)]) +/// ``` +/// +/// ```gleam +/// from_list([#("a", 0), #("b", 1)]) |> drop(["a", "b", "c"]) +/// // -> from_list([]) +/// ``` +/// +pub fn drop(from dict: Dict(k, v), drop disallowed_keys: List(k)) -> Dict(k, v) { + case disallowed_keys { + [] -> dict + [first, ..rest] -> drop(delete(dict, first), rest) + } +} + +/// Creates a new dict with one entry inserted or updated using a given function. +/// +/// If there was not an entry in the dict for the given key then the function +/// gets `None` as its argument, otherwise it gets `Some(value)`. +/// +/// ## Example +/// +/// ```gleam +/// let dict = from_list([#("a", 0)]) +/// let increment = fn(x) { +/// case x { +/// Some(i) -> i + 1 +/// None -> 0 +/// } +/// } +/// +/// upsert(dict, "a", increment) +/// // -> from_list([#("a", 1)]) +/// +/// upsert(dict, "b", increment) +/// // -> from_list([#("a", 0), #("b", 0)]) +/// ``` +/// +pub fn upsert( + in dict: Dict(k, v), + update key: k, + with fun: fn(Option(v)) -> v, +) -> Dict(k, v) { + case get(dict, key) { + Ok(value) -> insert(dict, key, fun(option.Some(value))) + Error(_) -> insert(dict, key, fun(option.None)) + } +} + +/// Combines all entries into a single value by calling a given function on each +/// one. +/// +/// Dicts 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 +/// +/// ```gleam +/// let dict = from_list([#("a", 1), #("b", 3), #("c", 9)]) +/// fold(dict, 0, fn(accumulator, key, value) { accumulator + value }) +/// // -> 13 +/// ``` +/// +/// ```gleam +/// import gleam/string +/// +/// let dict = from_list([#("a", 1), #("b", 3), #("c", 9)]) +/// fold(dict, "", fn(accumulator, key, value) { +/// string.append(accumulator, key) +/// }) +/// // -> "abc" +/// ``` +/// +pub fn fold( + over dict: Dict(k, v), + from initial: acc, + with fun: fn(acc, k, v) -> acc, +) -> acc { + fold_loop(to_list(dict), initial, fun) +} + +fn fold_loop( + list: List(#(k, v)), + initial: acc, + fun: fn(acc, k, v) -> acc, +) -> acc { + case list { + [] -> initial + [#(k, v), ..rest] -> fold_loop(rest, fun(initial, k, v), fun) + } +} + +/// Calls a function for each key and value in a dict, discarding the return +/// value. +/// +/// Useful for producing a side effect for every item of a dict. +/// +/// ```gleam +/// import gleam/io +/// +/// let dict = from_list([#("a", "apple"), #("b", "banana"), #("c", "cherry")]) +/// +/// each(dict, fn(k, v) { +/// io.println(key <> " => " <> value) +/// }) +/// // -> Nil +/// // a => apple +/// // b => banana +/// // c => cherry +/// ``` +/// +/// The order of elements in the iteration is an implementation detail that +/// should not be relied upon. +/// +pub fn each(dict: Dict(k, v), fun: fn(k, v) -> a) -> Nil { + fold(dict, Nil, fn(nil, k, v) { + fun(k, v) + nil + }) +} + +/// Creates a new dict from a pair of given dicts by combining their entries. +/// +/// If there are entries with the same keys in both dicts the given function is +/// used to determine the new value to use in the resulting dict. +/// +/// ## Examples +/// +/// ```gleam +/// let a = from_list([#("a", 0), #("b", 1)]) +/// let b = from_list([#("a", 2), #("c", 3)]) +/// combine(a, b, fn(one, other) { one + other }) +/// // -> from_list([#("a", 2), #("b", 1), #("c", 3)]) +/// ``` +/// +pub fn combine( + dict: Dict(k, v), + other: Dict(k, v), + with fun: fn(v, v) -> v, +) -> Dict(k, v) { + use acc, key, value <- fold(over: dict, from: other) + case get(acc, key) { + Ok(other_value) -> insert(acc, key, fun(value, other_value)) + Error(_) -> insert(acc, key, value) + } +} diff --git a/build/packages/gleam_stdlib/src/gleam/dynamic.gleam b/build/packages/gleam_stdlib/src/gleam/dynamic.gleam new file mode 100644 index 0000000..6dfcbe6 --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam/dynamic.gleam @@ -0,0 +1,100 @@ +import gleam/dict + +/// `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. +/// +/// This module contains code for forming dynamic data, and the +/// `gleam/dynamic/decode` module contains code for turning dynamic data back +/// into Gleam data with known types. You will likely mostly use the other +/// module in your projects. +/// +/// The exact runtime representation of dynamic values will depend on the +/// compilation target used. +/// +pub type Dynamic + +/// Return a string indicating the type of the dynamic value. +/// +/// This function may be useful for constructing error messages or logs. If you +/// want to turn dynamic data into well typed data then you want the +/// `gleam/dynamic/decode` module. +/// +/// ```gleam +/// classify(string("Hello")) +/// // -> "String" +/// ``` +/// +@external(erlang, "gleam_stdlib", "classify_dynamic") +@external(javascript, "../gleam_stdlib.mjs", "classify_dynamic") +pub fn classify(data: Dynamic) -> String + +/// Create a dynamic value from a bool. +/// +@external(erlang, "gleam_stdlib", "identity") +@external(javascript, "../gleam_stdlib.mjs", "identity") +pub fn bool(a: Bool) -> Dynamic + +/// Create a dynamic value from a string. +/// +/// On Erlang this will be a binary string rather than a character list. +/// +@external(erlang, "gleam_stdlib", "identity") +@external(javascript, "../gleam_stdlib.mjs", "identity") +pub fn string(a: String) -> Dynamic + +/// Create a dynamic value from a float. +/// +@external(erlang, "gleam_stdlib", "identity") +@external(javascript, "../gleam_stdlib.mjs", "identity") +pub fn float(a: Float) -> Dynamic + +/// Create a dynamic value from an int. +/// +@external(erlang, "gleam_stdlib", "identity") +@external(javascript, "../gleam_stdlib.mjs", "identity") +pub fn int(a: Int) -> Dynamic + +/// Create a dynamic value from a bit array. +/// +@external(erlang, "gleam_stdlib", "identity") +@external(javascript, "../gleam_stdlib.mjs", "identity") +pub fn bit_array(a: BitArray) -> Dynamic + +/// Create a dynamic value from a list. +/// +@external(erlang, "gleam_stdlib", "identity") +@external(javascript, "../gleam_stdlib.mjs", "identity") +pub fn list(a: List(Dynamic)) -> Dynamic + +/// Create a dynamic value from a list, converting it to a sequential runtime +/// format rather than the regular list format. +/// +/// On Erlang this will be a tuple, on JavaScript this will be an array. +/// +@external(erlang, "erlang", "list_to_tuple") +@external(javascript, "../gleam_stdlib.mjs", "list_to_array") +pub fn array(a: List(Dynamic)) -> Dynamic + +/// Create a dynamic value made an unordered series of keys and values, where +/// the keys are unique. +/// +/// On Erlang this will be a map, on JavaScript this will be a Gleam dict +/// object. +/// +pub fn properties(entries: List(#(Dynamic, Dynamic))) -> Dynamic { + cast(dict.from_list(entries)) +} + +/// A dynamic value representing nothing. +/// +/// On Erlang this will be the atom `nil`, on JavaScript this will be +/// `undefined`. +/// +pub fn nil() -> Dynamic { + cast(Nil) +} + +@external(erlang, "gleam_stdlib", "identity") +@external(javascript, "../gleam_stdlib.mjs", "identity") +fn cast(a: anything) -> Dynamic diff --git a/build/packages/gleam_stdlib/src/gleam/dynamic/decode.gleam b/build/packages/gleam_stdlib/src/gleam/dynamic/decode.gleam new file mode 100644 index 0000000..03ee2ac --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam/dynamic/decode.gleam @@ -0,0 +1,1061 @@ +//// The `Dynamic` type is used to represent dynamically typed data. That is, data +//// that we don't know the precise type of yet, so we need to introspect the data to +//// see if it is of the desired type before we can use it. Typically data like this +//// would come from user input or from untyped languages such as Erlang or JavaScript. +//// +//// This module provides the `Decoder` type and associated functions, which provides +//// a type-safe and composable way to convert dynamic data into some desired type, +//// or into errors if the data doesn't have the desired structure. +//// +//// The `Decoder` type is generic and has 1 type parameter, which is the type that +//// it attempts to decode. A `Decoder(String)` can be used to decode strings, and a +//// `Decoder(Option(Int))` can be used to decode `Option(Int)`s +//// +//// Decoders work using _runtime reflection_ and the data structures of the target +//// platform. Differences between Erlang and JavaScript data structures may impact +//// your decoders, so it is important to test your decoders on all supported +//// platforms. +//// +//// The decoding technique used by this module was inspired by Juraj Petráš' +//// [Toy](https://github.com/Hackder/toy), Go's `encoding/json`, and Elm's +//// `Json.Decode`. Thank you to them! +//// +//// # Examples +//// +//// Dynamic data may come from various sources and so many different syntaxes could +//// be used to describe or construct them. In these examples a pseudocode +//// syntax is used to describe the data. +//// +//// ## Simple types +//// +//// This module defines decoders for simple data types such as [`string`](#string), +//// [`int`](#int), [`float`](#float), [`bit_array`](#bit_array), and [`bool`](#bool). +//// +//// ```gleam +//// // Data: +//// // "Hello, Joe!" +//// +//// let result = decode.run(data, decode.string) +//// assert result == Ok("Hello, Joe!") +//// ``` +//// +//// ## Lists +//// +//// The [`list`](#list) decoder decodes `List`s. To use it you must construct it by +//// passing in another decoder into the `list` function, which is the decoder that +//// is to be used for the elements of the list, type checking both the list and its +//// elements. +//// +//// ```gleam +//// // Data: +//// // [1, 2, 3, 4] +//// +//// let result = decode.run(data, decode.list(decode.int)) +//// assert result == Ok([1, 2, 3, 4]) +//// ``` +//// +//// On Erlang this decoder can decode from lists, and on JavaScript it can +//// decode from lists as well as JavaScript arrays. +//// +//// ## Options +//// +//// The [`optional`](#optional) decoder is used to decode values that may or may not +//// be present. In other environment these might be called "nullable" values. +//// +//// Like the `list` decoder, the `optional` decoder takes another decoder, +//// which is used to decode the value if it is present. +//// +//// ```gleam +//// // Data: +//// // 12.45 +//// +//// let result = decode.run(data, decode.optional(decode.float)) +//// assert result == Ok(option.Some(12.45)) +//// ``` +//// ```gleam +//// // Data: +//// // null +//// +//// let result = decode.run(data, decode.optional(decode.int)) +//// assert result == Ok(option.None) +//// ``` +//// +//// This decoder knows how to handle multiple different runtime representations of +//// absent values, including `Nil`, `None`, `null`, and `undefined`. +//// +//// ## Dicts +//// +//// The [`dict`](#dict) decoder decodes `Dicts` and contains two other decoders, one +//// for the keys, one for the values. +//// +//// ```gleam +//// // Data: +//// // { "Lucy" -> 10, "Nubi" -> 20 } +//// +//// let result = decode.run(data, decode.dict(decode.string, decode.int)) +//// assert result == Ok(dict.from_list([ +//// #("Lucy", 10), +//// #("Nubi", 20), +//// ])) +//// ``` +//// +//// ## Indexing objects +//// +//// The [`at`](#at) decoder can be used to decode a value that is nested within +//// key-value containers such as Gleam dicts, Erlang maps, or JavaScript objects. +//// +//// ```gleam +//// // Data: +//// // { "one" -> { "two" -> 123 } } +//// +//// let result = decode.run(data, decode.at(["one", "two"], decode.int)) +//// assert result == Ok(123) +//// ``` +//// +//// ## Indexing arrays +//// +//// If you use ints as keys then the [`at`](#at) decoder can be used to index into +//// array-like containers such as Gleam or Erlang tuples, or JavaScript arrays. +//// +//// ```gleam +//// // Data: +//// // ["one", "two", "three"] +//// +//// let result = decode.run(data, decode.at([1], decode.string)) +//// assert result == Ok("two") +//// ``` +//// +//// ## Records +//// +//// Decoding records from dynamic data is more complex and requires combining a +//// decoder for each field and a special constructor that builds your records with +//// the decoded field values. +//// +//// ```gleam +//// // Data: +//// // { +//// // "score" -> 180, +//// // "name" -> "Mel Smith", +//// // "is-admin" -> false, +//// // "enrolled" -> true, +//// // "colour" -> "Red", +//// // } +//// +//// let decoder = { +//// use name <- decode.field("name", decode.string) +//// use score <- decode.field("score", decode.int) +//// use colour <- decode.field("colour", decode.string) +//// use enrolled <- decode.field("enrolled", decode.bool) +//// decode.success(Player(name:, score:, colour:, enrolled:)) +//// } +//// +//// let result = decode.run(data, decoder) +//// assert result == Ok(Player("Mel Smith", 180, "Red", True)) +//// ``` +//// +//// ## Enum variants +//// +//// Imagine you have a custom type where all the variants do not contain any values. +//// +//// ```gleam +//// pub type PocketMonsterType { +//// Fire +//// Water +//// Grass +//// Electric +//// } +//// ``` +//// +//// You might choose to encode these variants as strings, `"fire"` for `Fire`, +//// `"water"` for `Water`, and so on. To decode them you'll need to decode the dynamic +//// data as a string, but then you'll need to decode it further still as not all +//// strings are valid values for the enum. This can be done with the `then` +//// function, which enables running a second decoder after the first one +//// succeeds. +//// +//// ```gleam +//// let decoder = { +//// use decoded_string <- decode.then(decode.string) +//// case decoded_string { +//// // Return succeeding decoders for valid strings +//// "fire" -> decode.success(Fire) +//// "water" -> decode.success(Water) +//// "grass" -> decode.success(Grass) +//// "electric" -> decode.success(Electric) +//// // Return a failing decoder for any other strings +//// _ -> decode.failure(Fire, "PocketMonsterType") +//// } +//// } +//// +//// let result = decode.run(dynamic.string("water"), decoder) +//// assert result == Ok(Water) +//// +//// let result = decode.run(dynamic.string("wobble"), decoder) +//// assert result == Error([DecodeError("PocketMonsterType", "String", [])]) +//// ``` +//// +//// ## Record variants +//// +//// Decoding type variants that contain other values is done by combining the +//// techniques from the "enum variants" and "records" examples. Imagine you have +//// this custom type that you want to decode: +//// +//// ```gleam +//// pub type PocketMonsterPerson { +//// Trainer(name: String, badge_count: Int) +//// GymLeader(name: String, speciality: PocketMonsterType) +//// } +//// ``` +//// And you would like to be able to decode these from dynamic data like this: +//// ```erlang +//// { +//// "type" -> "trainer", +//// "name" -> "Ash", +//// "badge-count" -> 1, +//// } +//// ``` +//// ```erlang +//// { +//// "type" -> "gym-leader", +//// "name" -> "Misty", +//// "speciality" -> "water", +//// } +//// ``` +//// +//// Notice how both documents have a `"type"` field, which is used to indicate which +//// variant the data is for. +//// +//// First, define decoders for each of the variants: +//// +//// ```gleam +//// let trainer_decoder = { +//// use name <- decode.field("name", decode.string) +//// use badge_count <- decode.field("badge-count", decode.int) +//// decode.success(Trainer(name, badge_count)) +//// } +//// +//// let gym_leader_decoder = { +//// use name <- decode.field("name", decode.string) +//// use speciality <- decode.field("speciality", pocket_monster_type_decoder) +//// decode.success(GymLeader(name, speciality)) +//// } +//// ``` +//// +//// A third decoder can be used to extract and decode the `"type"` field, and the +//// expression can evaluate to whichever decoder is suitable for the document. +//// +//// ```gleam +//// // Data: +//// // { +//// // "type" -> "gym-leader", +//// // "name" -> "Misty", +//// // "speciality" -> "water", +//// // } +//// +//// let decoder = { +//// use tag <- decode.field("type", decode.string) +//// case tag { +//// "gym-leader" -> gym_leader_decoder +//// _ -> trainer_decoder +//// } +//// } +//// +//// let result = decode.run(data, decoder) +//// assert result == Ok(GymLeader("Misty", Water)) +//// ``` + +import gleam/bit_array +import gleam/dict.{type Dict} +import gleam/dynamic +import gleam/int +import gleam/list +import gleam/option.{type Option, None, Some} + +/// `Dynamic` data is data that we don't know the type of yet, originating from +/// external untyped systems. +/// +/// You should never be converting your well typed data to dynamic data. +/// +pub type Dynamic = + dynamic.Dynamic + +/// Error returned when unexpected data is encountered +/// +pub type DecodeError { + DecodeError(expected: String, found: String, path: List(String)) +} + +/// A decoder is a value that can be used to turn dynamically typed `Dynamic` +/// data into typed data using the `run` function. +/// +/// Several smaller decoders can be combined to make larger decoders using +/// functions such as `list` and `field`. +/// +pub opaque type Decoder(t) { + Decoder(function: fn(Dynamic) -> #(t, List(DecodeError))) +} + +/// The same as [`field`](#field), except taking a path to the value rather +/// than a field name. +/// +/// This function will index into dictionaries with any key type, and if the key is +/// an int then it'll also index into Erlang tuples and JavaScript arrays, and +/// the first eight elements of Gleam lists. +/// +/// # Examples +/// +/// ```gleam +/// let data = dynamic.properties([ +/// #(dynamic.string("data"), dynamic.properties([ +/// #(dynamic.string("email"), dynamic.string("lucy@example.com")), +/// #(dynamic.string("name"), dynamic.string("Lucy")), +/// ]) +/// ])) +/// +/// let decoder = { +/// use name <- decode.subfield(["data", "name"], decode.string) +/// use email <- decode.subfield(["data", "email"], decode.string) +/// decode.success(SignUp(name: name, email: email)) +/// } +/// let result = decode.run(data, decoder) +/// assert result == Ok(SignUp(name: "Lucy", email: "lucy@example.com")) +/// ``` +/// +pub fn subfield( + field_path: List(name), + field_decoder: Decoder(t), + next: fn(t) -> Decoder(final), +) -> Decoder(final) { + Decoder(function: fn(data) { + let #(out, errors1) = + index(field_path, [], field_decoder.function, data, fn(data, position) { + let #(default, _) = field_decoder.function(data) + #(default, [DecodeError("Field", "Nothing", [])]) + |> push_path(list.reverse(position)) + }) + let #(out, errors2) = next(out).function(data) + #(out, list.append(errors1, errors2)) + }) +} + +/// Run a decoder on a `Dynamic` value, decoding the value if it is of the +/// desired type, or returning errors. +/// +/// # Examples +/// +/// ```gleam +/// let decoder = { +/// use name <- decode.field("email", decode.string) +/// use email <- decode.field("password", decode.string) +/// decode.success(SignUp(name: name, email: email)) +/// } +/// +/// decode.run(data, decoder) +/// ``` +/// +pub fn run(data: Dynamic, decoder: Decoder(t)) -> Result(t, List(DecodeError)) { + let #(maybe_invalid_data, errors) = decoder.function(data) + case errors { + [] -> Ok(maybe_invalid_data) + [_, ..] -> Error(errors) + } +} + +/// A decoder that decodes a value that is nested within other values. For +/// example, decoding a value that is within some deeply nested JSON objects. +/// +/// This function will index into dictionaries with any key type, and if the key is +/// an int then it'll also index into Erlang tuples and JavaScript arrays, and +/// the first eight elements of Gleam lists. +/// +/// # Examples +/// +/// ```gleam +/// let decoder = decode.at(["one", "two"], decode.int) +/// +/// let data = dynamic.properties([ +/// #(dynamic.string("one"), dynamic.properties([ +/// #(dynamic.string("two"), dynamic.int(1000)), +/// ])), +/// ])) +/// +/// +/// decode.run(data, decoder) +/// // -> Ok(1000) +/// ``` +/// +/// ```gleam +/// dynamic.nil() +/// |> decode.run(decode.optional(decode.int)) +/// // -> Ok(option.None) +/// ``` +/// +pub fn at(path: List(segment), inner: Decoder(a)) -> Decoder(a) { + Decoder(function: fn(data) { + index(path, [], inner.function, data, fn(data, position) { + let #(default, _) = inner.function(data) + #(default, [DecodeError("Field", "Nothing", [])]) + |> push_path(list.reverse(position)) + }) + }) +} + +fn index( + path: List(a), + position: List(a), + inner: fn(Dynamic) -> #(b, List(DecodeError)), + data: Dynamic, + handle_miss: fn(Dynamic, List(a)) -> #(b, List(DecodeError)), +) -> #(b, List(DecodeError)) { + case path { + [] -> { + data + |> inner + |> push_path(list.reverse(position)) + } + + [key, ..path] -> { + case bare_index(data, key) { + Ok(Some(data)) -> { + index(path, [key, ..position], inner, data, handle_miss) + } + Ok(None) -> { + handle_miss(data, [key, ..position]) + } + Error(kind) -> { + let #(default, _) = inner(data) + #(default, [DecodeError(kind, dynamic.classify(data), [])]) + |> push_path(list.reverse(position)) + } + } + } + } +} + +@external(erlang, "gleam_stdlib", "index") +@external(javascript, "../../gleam_stdlib.mjs", "index") +fn bare_index(data: Dynamic, key: anything) -> Result(Option(Dynamic), String) + +fn push_path( + layer: #(t, List(DecodeError)), + path: List(key), +) -> #(t, List(DecodeError)) { + let decoder = one_of(string, [int |> map(int.to_string)]) + let path = + list.map(path, fn(key) { + let key = cast(key) + case run(key, decoder) { + Ok(key) -> key + Error(_) -> "<" <> dynamic.classify(key) <> ">" + } + }) + let errors = + list.map(layer.1, fn(error) { + DecodeError(..error, path: list.append(path, error.path)) + }) + #(layer.0, errors) +} + +/// Finalise a decoder having successfully extracted a value. +/// +/// # Examples +/// +/// ```gleam +/// let data = dynamic.properties([ +/// #(dynamic.string("email"), dynamic.string("lucy@example.com")), +/// #(dynamic.string("name"), dynamic.string("Lucy")), +/// ])) +/// +/// let decoder = { +/// use name <- decode.field("name", string) +/// use email <- decode.field("email", string) +/// decode.success(SignUp(name: name, email: email)) +/// } +/// +/// let result = decode.run(data, decoder) +/// assert result == Ok(SignUp(name: "Lucy", email: "lucy@example.com")) +/// ``` +/// +pub fn success(data: t) -> Decoder(t) { + Decoder(function: fn(_) { #(data, []) }) +} + +/// Construct a decode error for some unexpected dynamic data. +/// +pub fn decode_error( + expected expected: String, + found found: Dynamic, +) -> List(DecodeError) { + [DecodeError(expected: expected, found: dynamic.classify(found), path: [])] +} + +/// Run a decoder on a field of a `Dynamic` value, decoding the value if it is +/// of the desired type, or returning errors. An error is returned if there is +/// no field for the specified key. +/// +/// This function will index into dictionaries with any key type, and if the key is +/// an int then it'll also index into Erlang tuples and JavaScript arrays, and +/// the first eight elements of Gleam lists. +/// +/// # Examples +/// +/// ```gleam +/// let data = dynamic.properties([ +/// #(dynamic.string("email"), dynamic.string("lucy@example.com")), +/// #(dynamic.string("name"), dynamic.string("Lucy")), +/// ])) +/// +/// let decoder = { +/// use name <- decode.field("name", string) +/// use email <- decode.field("email", string) +/// decode.success(SignUp(name: name, email: email)) +/// } +/// +/// let result = decode.run(data, decoder) +/// assert result == Ok(SignUp(name: "Lucy", email: "lucy@example.com")) +/// ``` +/// +/// If you wish to decode a value that is more deeply nested within the dynamic +/// data, see [`subfield`](#subfield) and [`at`](#at). +/// +/// If you wish to return a default in the event that a field is not present, +/// see [`optional_field`](#optional_field) and / [`optionally_at`](#optionally_at). +/// +pub fn field( + field_name: name, + field_decoder: Decoder(t), + next: fn(t) -> Decoder(final), +) -> Decoder(final) { + subfield([field_name], field_decoder, next) +} + +/// Run a decoder on a field of a `Dynamic` value, decoding the value if it is +/// of the desired type, or returning errors. The given default value is +/// returned if there is no field for the specified key. +/// +/// This function will index into dictionaries with any key type, and if the key is +/// an int then it'll also index into Erlang tuples and JavaScript arrays, and +/// the first eight elements of Gleam lists. +/// +/// # Examples +/// +/// ```gleam +/// let data = dynamic.properties([ +/// #(dynamic.string("name"), dynamic.string("Lucy")), +/// ])) +/// +/// let decoder = { +/// use name <- decode.field("name", string) +/// use email <- decode.optional_field("email", "n/a", string) +/// decode.success(SignUp(name: name, email: email)) +/// } +/// +/// let result = decode.run(data, decoder) +/// assert result == Ok(SignUp(name: "Lucy", email: "n/a")) +/// ``` +/// +pub fn optional_field( + key: name, + default: t, + field_decoder: Decoder(t), + next: fn(t) -> Decoder(final), +) -> Decoder(final) { + Decoder(function: fn(data) { + let #(out, errors1) = + case bare_index(data, key) { + Ok(Some(data)) -> field_decoder.function(data) + Ok(None) -> #(default, []) + Error(kind) -> #(default, [ + DecodeError(kind, dynamic.classify(data), []), + ]) + } + |> push_path([key]) + let #(out, errors2) = next(out).function(data) + #(out, list.append(errors1, errors2)) + }) +} + +/// A decoder that decodes a value that is nested within other values. For +/// example, decoding a value that is within some deeply nested JSON objects. +/// +/// This function will index into dictionaries with any key type, and if the key is +/// an int then it'll also index into Erlang tuples and JavaScript arrays, and +/// the first eight elements of Gleam lists. +/// +/// # Examples +/// +/// ```gleam +/// let decoder = decode.optionally_at(["one", "two"], 100, decode.int) +/// +/// let data = dynamic.properties([ +/// #(dynamic.string("one"), dynamic.properties([])), +/// ])) +/// +/// +/// decode.run(data, decoder) +/// // -> Ok(100) +/// ``` +/// +pub fn optionally_at( + path: List(segment), + default: a, + inner: Decoder(a), +) -> Decoder(a) { + Decoder(function: fn(data) { + index(path, [], inner.function, data, fn(_, _) { #(default, []) }) + }) +} + +fn run_dynamic_function( + data: Dynamic, + name: String, + f: fn(Dynamic) -> Result(t, t), +) -> #(t, List(DecodeError)) { + case f(data) { + Ok(data) -> #(data, []) + Error(zero) -> #(zero, [DecodeError(name, dynamic.classify(data), [])]) + } +} + +/// A decoder that decodes `String` values. +/// +/// # Examples +/// +/// ```gleam +/// let result = decode.run(dynamic.string("Hello!"), decode.string) +/// assert result == Ok("Hello!") +/// ``` +/// +pub const string: Decoder(String) = Decoder(decode_string) + +fn decode_string(data: Dynamic) -> #(String, List(DecodeError)) { + run_dynamic_function(data, "String", dynamic_string) +} + +@external(javascript, "../../gleam_stdlib.mjs", "string") +fn dynamic_string(from data: Dynamic) -> Result(String, String) { + case dynamic_bit_array(data) { + Ok(data) -> + case bit_array.to_string(data) { + Ok(string) -> Ok(string) + Error(_) -> Error("") + } + Error(_) -> Error("") + } +} + +/// A decoder that decodes `Bool` values. +/// +/// # Examples +/// +/// ```gleam +/// let result = decode.run(dynamic.bool(True), decode.bool) +/// assert result == Ok(True) +/// ``` +/// +pub const bool: Decoder(Bool) = Decoder(decode_bool) + +fn decode_bool(data: Dynamic) -> #(Bool, List(DecodeError)) { + case cast(True) == data { + True -> #(True, []) + False -> + case cast(False) == data { + True -> #(False, []) + False -> #(False, decode_error("Bool", data)) + } + } +} + +/// A decoder that decodes `Int` values. +/// +/// # Examples +/// +/// ```gleam +/// let result = decode.run(dynamic.int(147), decode.int) +/// assert result == Ok(147) +/// ``` +/// +pub const int: Decoder(Int) = Decoder(decode_int) + +fn decode_int(data: Dynamic) -> #(Int, List(DecodeError)) { + run_dynamic_function(data, "Int", dynamic_int) +} + +@external(erlang, "gleam_stdlib", "int") +@external(javascript, "../../gleam_stdlib.mjs", "int") +fn dynamic_int(data: Dynamic) -> Result(Int, Int) + +/// A decoder that decodes `Float` values. +/// +/// # Examples +/// +/// ```gleam +/// let result = decode.run(dynamic.float(3.14), decode.float) +/// assert result == Ok(3.14) +/// ``` +/// +pub const float: Decoder(Float) = Decoder(decode_float) + +fn decode_float(data: Dynamic) -> #(Float, List(DecodeError)) { + run_dynamic_function(data, "Float", dynamic_float) +} + +@external(erlang, "gleam_stdlib", "float") +@external(javascript, "../../gleam_stdlib.mjs", "float") +fn dynamic_float(data: Dynamic) -> Result(Float, Float) + +/// A decoder that decodes `Dynamic` values. This decoder never returns an error. +/// +/// # Examples +/// +/// ```gleam +/// let result = decode.run(dynamic.float(3.14), decode.dynamic) +/// assert result == Ok(dynamic.float(3.14)) +/// ``` +/// +pub const dynamic: Decoder(Dynamic) = Decoder(decode_dynamic) + +fn decode_dynamic(data: Dynamic) -> #(Dynamic, List(DecodeError)) { + #(data, []) +} + +/// A decoder that decodes `BitArray` values. This decoder never returns an error. +/// +/// # Examples +/// +/// ```gleam +/// let result = decode.run(dynamic.bit_array(<<5, 7>>), decode.bit_array) +/// assert result == Ok(<<5, 7>>) +/// ``` +/// +pub const bit_array: Decoder(BitArray) = Decoder(decode_bit_array) + +fn decode_bit_array(data: Dynamic) -> #(BitArray, List(DecodeError)) { + run_dynamic_function(data, "BitArray", dynamic_bit_array) +} + +@external(erlang, "gleam_stdlib", "bit_array") +@external(javascript, "../../gleam_stdlib.mjs", "bit_array") +fn dynamic_bit_array(data: Dynamic) -> Result(BitArray, BitArray) + +/// A decoder that decodes lists where all elements are decoded with a given +/// decoder. +/// +/// # Examples +/// +/// ```gleam +/// let result = +/// [1, 2, 3] +/// |> list.map(dynamic.int) +/// |> dynamic.list +/// |> decode.run(decode.list(of: decode.int)) +/// assert result == Ok([1, 2, 3]) +/// ``` +/// +pub fn list(of inner: Decoder(a)) -> Decoder(List(a)) { + Decoder(fn(data) { + decode_list(data, inner.function, fn(p, k) { push_path(p, [k]) }, 0, []) + }) +} + +@external(erlang, "gleam_stdlib", "list") +@external(javascript, "../../gleam_stdlib.mjs", "list") +fn decode_list( + data: Dynamic, + item: fn(Dynamic) -> #(t, List(DecodeError)), + push_path: fn(#(t, List(DecodeError)), key) -> #(t, List(DecodeError)), + index: Int, + acc: List(t), +) -> #(List(t), List(DecodeError)) + +/// A decoder that decodes dicts where all keys and vales are decoded with +/// given decoders. +/// +/// # Examples +/// +/// ```gleam +/// let values = dynamic.properties([ +/// #(dynamic.string("one"), dynamic.int(1)), +/// #(dynamic.string("two"), dynamic.int(2)), +/// ]) +/// +/// let result = +/// decode.run(values, decode.dict(decode.string, decode.int)) +/// assert result == Ok(values) +/// ``` +/// +pub fn dict( + key: Decoder(key), + value: Decoder(value), +) -> Decoder(Dict(key, value)) { + Decoder(fn(data) { + case decode_dict(data) { + Error(_) -> #(dict.new(), decode_error("Dict", data)) + Ok(dict) -> + dict.fold(dict, #(dict.new(), []), fn(a, k, v) { + // If there are any errors from previous key-value pairs then we + // don't need to run the decoders, instead return the existing acc. + case a.1 { + [] -> fold_dict(a, k, v, key.function, value.function) + [_, ..] -> a + } + }) + } + }) +} + +fn fold_dict( + acc: #(Dict(k, v), List(DecodeError)), + key: Dynamic, + value: Dynamic, + key_decoder: fn(Dynamic) -> #(k, List(DecodeError)), + value_decoder: fn(Dynamic) -> #(v, List(DecodeError)), +) -> #(Dict(k, v), List(DecodeError)) { + // First we decode the key. + case key_decoder(key) { + #(key, []) -> + // Then we decode the value. + case value_decoder(value) { + #(value, []) -> { + // It worked! Insert the new key-value pair so we can move onto the next. + let dict = dict.insert(acc.0, key, value) + #(dict, acc.1) + } + #(_, errors) -> push_path(#(dict.new(), errors), ["values"]) + } + #(_, errors) -> push_path(#(dict.new(), errors), ["keys"]) + } +} + +@external(erlang, "gleam_stdlib", "dict") +@external(javascript, "../../gleam_stdlib.mjs", "dict") +fn decode_dict(data: Dynamic) -> Result(Dict(Dynamic, Dynamic), Nil) + +/// A decoder that decodes nullable values of a type decoded by with a given +/// decoder. +/// +/// This function can handle common representations of null on all runtimes, such as +/// `nil`, `null`, and `undefined` on Erlang, and `undefined` and `null` on +/// JavaScript. +/// +/// # Examples +/// +/// ```gleam +/// let result = decode.run(dynamic.int(100), decode.optional(decode.int)) +/// assert result == Ok(option.Some(100)) +/// ``` +/// +/// ```gleam +/// let result = decode.run(dynamic.nil(), decode.optional(decode.int)) +/// assert result == Ok(option.None) +/// ``` +/// +pub fn optional(inner: Decoder(a)) -> Decoder(Option(a)) { + Decoder(function: fn(data) { + case is_null(data) { + True -> #(option.None, []) + False -> { + let #(data, errors) = inner.function(data) + #(option.Some(data), errors) + } + } + }) +} + +/// Apply a transformation function to any value decoded by the decoder. +/// +/// # Examples +/// +/// ```gleam +/// let decoder = decode.int |> decode.map(int.to_string) +/// let result = decode.run(dynamic.int(1000), decoder) +/// assert result == Ok("1000") +/// ``` +/// +pub fn map(decoder: Decoder(a), transformer: fn(a) -> b) -> Decoder(b) { + Decoder(function: fn(d) { + let #(data, errors) = decoder.function(d) + #(transformer(data), errors) + }) +} + +/// Apply a transformation function to any errors returned by the decoder. +/// +pub fn map_errors( + decoder: Decoder(a), + transformer: fn(List(DecodeError)) -> List(DecodeError), +) -> Decoder(a) { + Decoder(function: fn(d) { + let #(data, errors) = decoder.function(d) + #(data, transformer(errors)) + }) +} + +/// Replace all errors produced by a decoder with one single error for a named +/// expected type. +/// +/// This function may be useful if you wish to simplify errors before +/// presenting them to a user, particularly when using the `one_of` function. +/// +/// # Examples +/// +/// ```gleam +/// let decoder = decode.string |> decode.collapse_errors("MyThing") +/// let result = decode.run(dynamic.int(1000), decoder) +/// assert result == Error([DecodeError("MyThing", "Int", [])]) +/// ``` +/// +pub fn collapse_errors(decoder: Decoder(a), name: String) -> Decoder(a) { + Decoder(function: fn(dynamic_data) { + let #(data, errors) as layer = decoder.function(dynamic_data) + case errors { + [] -> layer + [_, ..] -> #(data, decode_error(name, dynamic_data)) + } + }) +} + +/// Create a new decoder based upon the value of a previous decoder. +/// +/// This may be useful to run one previous decoder to use in further decoding. +/// +pub fn then(decoder: Decoder(a), next: fn(a) -> Decoder(b)) -> Decoder(b) { + Decoder(function: fn(dynamic_data) { + let #(data, errors) = decoder.function(dynamic_data) + let decoder = next(data) + let #(data, _) as layer = decoder.function(dynamic_data) + case errors { + [] -> layer + [_, ..] -> #(data, errors) + } + }) +} + +/// Create a new decoder from several other decoders. Each of the inner +/// decoders is run in turn, and the value from the first to succeed is used. +/// +/// If no decoder succeeds then the errors from the first decoder is used. +/// If you wish for different errors then you may wish to use the +/// `collapse_errors` or `map_errors` functions. +/// +/// # Examples +/// +/// ```gleam +/// let decoder = decode.one_of(decode.string, or: [ +/// decode.int |> decode.map(int.to_string), +/// decode.float |> decode.map(float.to_string), +/// ]) +/// decode.run(dynamic.int(1000), decoder) +/// // -> Ok("1000") +/// ``` +/// +pub fn one_of( + first: Decoder(a), + or alternatives: List(Decoder(a)), +) -> Decoder(a) { + Decoder(function: fn(dynamic_data) { + let #(_, errors) as layer = first.function(dynamic_data) + case errors { + [] -> layer + [_, ..] -> run_decoders(dynamic_data, layer, alternatives) + } + }) +} + +fn run_decoders( + data: Dynamic, + failure: #(a, List(DecodeError)), + decoders: List(Decoder(a)), +) -> #(a, List(DecodeError)) { + case decoders { + [] -> failure + + [decoder, ..decoders] -> { + let #(_, errors) as layer = decoder.function(data) + case errors { + [] -> layer + [_, ..] -> run_decoders(data, failure, decoders) + } + } + } +} + +/// Define a decoder that always fails. The parameter for this function is the +/// name of the type that has failed to decode. +/// +pub fn failure(zero: a, expected: String) -> Decoder(a) { + Decoder(function: fn(d) { #(zero, decode_error(expected, d)) }) +} + +/// Create a decoder for a new data type from a decoding function. +/// +/// This function is used for new primitive types. For example, you might +/// define a decoder for Erlang's pid type. +/// +/// A default "zero" value is also required to make a decoder. When this +/// decoder is used as part of a larger decoder this zero value used as +/// a placeholder so that the rest of the decoder can continue to run and +/// collect all decoding errors. +/// +/// If you were to make a decoder for the `String` type (rather than using the +/// build-in `string` decoder) you would define it like so: +/// +/// ```gleam +/// pub fn string_decoder() -> decode.Decoder(String) { +/// let default = "" +/// decode.new_primitive_decoder("String", fn(data) { +/// case dynamic.string(data) { +/// Ok(x) -> Ok(x) +/// Error(_) -> Error(default) +/// } +/// }) +/// } +/// ``` +/// +pub fn new_primitive_decoder( + name: String, + decoding_function: fn(Dynamic) -> Result(t, t), +) -> Decoder(t) { + Decoder(function: fn(d) { + case decoding_function(d) { + Ok(t) -> #(t, []) + Error(zero) -> #(zero, [DecodeError(name, dynamic.classify(d), [])]) + } + }) +} + +/// Create a decoder that can refer to itself, useful for decoding deeply +/// nested data. +/// +/// Attempting to create a recursive decoder without this function could result +/// in an infinite loop. If you are using `field` or other `use`able functions +/// then you may not need to use this function. +/// +/// ```gleam +/// type Nested { +/// Nested(List(Nested)) +/// Value(String) +/// } +/// +/// fn nested_decoder() -> decode.Decoder(Nested) { +/// use <- decode.recursive +/// decode.one_of(decode.string |> decode.map(Value), [ +/// decode.list(nested_decoder()) |> decode.map(Nested), +/// ]) +/// } +/// ``` +/// +pub fn recursive(inner: fn() -> Decoder(a)) -> Decoder(a) { + Decoder(function: fn(data) { + let decoder = inner() + decoder.function(data) + }) +} + +@external(erlang, "gleam_stdlib", "identity") +@external(javascript, "../../gleam_stdlib.mjs", "identity") +fn cast(a: anything) -> Dynamic + +@external(erlang, "gleam_stdlib", "is_null") +@external(javascript, "../../gleam_stdlib.mjs", "is_null") +fn is_null(a: Dynamic) -> Bool diff --git a/build/packages/gleam_stdlib/src/gleam/float.gleam b/build/packages/gleam_stdlib/src/gleam/float.gleam new file mode 100644 index 0000000..83bfa6e --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam/float.gleam @@ -0,0 +1,661 @@ +//// Functions for working with floats. +//// +//// ## Float representation +//// +//// Floats are represented as 64 bit floating point numbers on both the Erlang +//// and JavaScript runtimes. The floating point behaviour is native to their +//// respective runtimes, so their exact behaviour will be slightly different on +//// the two runtimes. +//// +//// ### Infinity and NaN +//// +//// Under the JavaScript runtime, exceeding the maximum (or minimum) +//// representable value for a floating point value will result in Infinity (or +//// -Infinity). Should you try to divide two infinities you will get NaN as a +//// result. +//// +//// When running on BEAM, exceeding the maximum (or minimum) representable +//// value for a floating point value will raise an error. +//// +//// ## Division by zero +//// +//// Gleam runs on the Erlang virtual machine, which does not follow the IEEE +//// 754 standard for floating point arithmetic and does not have an `Infinity` +//// value. In Erlang division by zero results in a crash, however Gleam does +//// not have partial functions and operators in core so instead division by zero +//// returns zero, a behaviour taken from Pony, Coq, and Lean. +//// +//// This may seem unexpected at first, but it is no less mathematically valid +//// than crashing or returning a special value. Division by zero is undefined +//// in mathematics. + +import gleam/order.{type Order} + +/// Attempts to parse a string as a `Float`, returning `Error(Nil)` if it was +/// not possible. +/// +/// ## Examples +/// +/// ```gleam +/// parse("2.3") +/// // -> Ok(2.3) +/// ``` +/// +/// ```gleam +/// parse("ABC") +/// // -> Error(Nil) +/// ``` +/// +@external(erlang, "gleam_stdlib", "parse_float") +@external(javascript, "../gleam_stdlib.mjs", "parse_float") +pub fn parse(string: String) -> Result(Float, Nil) + +/// Returns the string representation of the provided `Float`. +/// +/// ## Examples +/// +/// ```gleam +/// to_string(2.3) +/// // -> "2.3" +/// ``` +/// +@external(erlang, "gleam_stdlib", "float_to_string") +@external(javascript, "../gleam_stdlib.mjs", "float_to_string") +pub fn to_string(x: Float) -> String + +/// Restricts a `Float` between a lower and upper bound. +/// +/// ## Examples +/// +/// ```gleam +/// clamp(1.2, min: 1.4, max: 1.6) +/// // -> 1.4 +/// ``` +/// +pub fn clamp(x: Float, min min_bound: Float, max max_bound: Float) -> Float { + x + |> min(max_bound) + |> max(min_bound) +} + +/// Compares two `Float`s, returning an `Order`: +/// `Lt` for lower than, `Eq` for equals, or `Gt` for greater than. +/// +/// ## Examples +/// +/// ```gleam +/// compare(2.0, 2.3) +/// // -> Lt +/// ``` +/// +/// To handle +/// [Floating Point Imprecision](https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems) +/// you may use [`loosely_compare`](#loosely_compare) instead. +/// +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 `Float`s within a tolerance, returning an `Order`: +/// `Lt` for lower than, `Eq` for equals, or `Gt` for greater than. +/// +/// This function allows Float comparison while handling +/// [Floating Point Imprecision](https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems). +/// +/// Notice: For `Float`s the tolerance won't be exact: +/// `5.3 - 5.0` is not exactly `0.3`. +/// +/// ## Examples +/// +/// ```gleam +/// loosely_compare(5.0, with: 5.3, tolerating: 0.5) +/// // -> Eq +/// ``` +/// +/// If you want to check only for equality you may use +/// [`loosely_equals`](#loosely_equals) instead. +/// +pub fn loosely_compare( + a: Float, + with b: Float, + tolerating tolerance: Float, +) -> Order { + let difference = absolute_value(a -. b) + case difference <=. tolerance { + True -> order.Eq + False -> compare(a, b) + } +} + +/// Checks for equality of two `Float`s within a tolerance, +/// returning an `Bool`. +/// +/// This function allows Float comparison while handling +/// [Floating Point Imprecision](https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems). +/// +/// Notice: For `Float`s the tolerance won't be exact: +/// `5.3 - 5.0` is not exactly `0.3`. +/// +/// ## Examples +/// +/// ```gleam +/// loosely_equals(5.0, with: 5.3, tolerating: 0.5) +/// // -> True +/// ``` +/// +/// ```gleam +/// loosely_equals(5.0, with: 5.1, tolerating: 0.1) +/// // -> False +/// ``` +/// +pub fn loosely_equals( + a: Float, + with b: Float, + tolerating tolerance: Float, +) -> Bool { + let difference = absolute_value(a -. b) + difference <=. tolerance +} + +/// Compares two `Float`s, returning the smaller of the two. +/// +/// ## Examples +/// +/// ```gleam +/// min(2.0, 2.3) +/// // -> 2.0 +/// ``` +/// +pub fn min(a: Float, b: Float) -> Float { + case a <. b { + True -> a + False -> b + } +} + +/// Compares two `Float`s, returning the larger of the two. +/// +/// ## Examples +/// +/// ```gleam +/// 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 +/// +/// ```gleam +/// ceiling(2.3) +/// // -> 3.0 +/// ``` +/// +@external(erlang, "math", "ceil") +@external(javascript, "../gleam_stdlib.mjs", "ceiling") +pub fn ceiling(x: Float) -> Float + +/// Rounds the value to the next lowest whole number as a `Float`. +/// +/// ## Examples +/// +/// ```gleam +/// floor(2.3) +/// // -> 2.0 +/// ``` +/// +@external(erlang, "math", "floor") +@external(javascript, "../gleam_stdlib.mjs", "floor") +pub fn floor(x: Float) -> Float + +/// Rounds the value to the nearest whole number as an `Int`. +/// +/// ## Examples +/// +/// ```gleam +/// round(2.3) +/// // -> 2 +/// ``` +/// +/// ```gleam +/// round(2.5) +/// // -> 3 +/// ``` +/// +@external(erlang, "erlang", "round") +pub fn round(x: Float) -> Int { + case x >=. 0.0 { + True -> js_round(x) + False -> 0 - js_round(negate(x)) + } +} + +@external(javascript, "../gleam_stdlib.mjs", "round") +fn js_round(a: Float) -> Int + +/// Returns the value as an `Int`, truncating all decimal digits. +/// +/// ## Examples +/// +/// ```gleam +/// truncate(2.4343434847383438) +/// // -> 2 +/// ``` +/// +@external(erlang, "erlang", "trunc") +@external(javascript, "../gleam_stdlib.mjs", "truncate") +pub fn truncate(x: Float) -> Int + +/// Converts the value to a given precision as a `Float`. +/// The precision is the number of allowed decimal places. +/// Negative precisions are allowed and force rounding +/// to the nearest tenth, hundredth, thousandth etc. +/// +/// ## Examples +/// +/// ```gleam +/// to_precision(2.43434348473, precision: 2) +/// // -> 2.43 +/// ``` +/// +/// ```gleam +/// to_precision(547890.453444, precision: -3) +/// // -> 548000.0 +/// ``` +/// +pub fn to_precision(x: Float, precision: Int) -> Float { + case precision <= 0 { + True -> { + let factor = do_power(10.0, do_to_float(-precision)) + do_to_float(round(x /. factor)) *. factor + } + False -> { + let factor = do_power(10.0, do_to_float(precision)) + do_to_float(round(x *. factor)) /. factor + } + } +} + +@external(erlang, "erlang", "float") +@external(javascript, "../gleam_stdlib.mjs", "identity") +fn do_to_float(a: Int) -> Float + +/// Returns the absolute value of the input as a `Float`. +/// +/// ## Examples +/// +/// ```gleam +/// absolute_value(-12.5) +/// // -> 12.5 +/// ``` +/// +/// ```gleam +/// absolute_value(10.2) +/// // -> 10.2 +/// ``` +/// +pub fn absolute_value(x: Float) -> Float { + case x >=. 0.0 { + True -> x + False -> 0.0 -. x + } +} + +/// Returns the results of the base being raised to the power of the +/// exponent, as a `Float`. +/// +/// ## Examples +/// +/// ```gleam +/// power(2.0, -1.0) +/// // -> Ok(0.5) +/// ``` +/// +/// ```gleam +/// power(2.0, 2.0) +/// // -> Ok(4.0) +/// ``` +/// +/// ```gleam +/// power(8.0, 1.5) +/// // -> Ok(22.627416997969522) +/// ``` +/// +/// ```gleam +/// 4.0 |> power(of: 2.0) +/// // -> Ok(16.0) +/// ``` +/// +/// ```gleam +/// power(-1.0, 0.5) +/// // -> Error(Nil) +/// ``` +/// +pub fn power(base: Float, of exponent: Float) -> Result(Float, Nil) { + let fractional: Bool = ceiling(exponent) -. exponent >. 0.0 + // In the following check: + // 1. If the base is negative and the exponent is fractional then + // return an error as it will otherwise be an imaginary number + // 2. If the base is 0 and the exponent is negative then the expression + // is equivalent to the exponent divided by 0 and an error should be + // returned + case base <. 0.0 && fractional || base == 0.0 && exponent <. 0.0 { + True -> Error(Nil) + False -> Ok(do_power(base, exponent)) + } +} + +@external(erlang, "math", "pow") +@external(javascript, "../gleam_stdlib.mjs", "power") +fn do_power(a: Float, b: Float) -> Float + +/// Returns the square root of the input as a `Float`. +/// +/// ## Examples +/// +/// ```gleam +/// square_root(4.0) +/// // -> Ok(2.0) +/// ``` +/// +/// ```gleam +/// square_root(-16.0) +/// // -> Error(Nil) +/// ``` +/// +pub fn square_root(x: Float) -> Result(Float, Nil) { + power(x, 0.5) +} + +/// Returns the negative of the value provided. +/// +/// ## Examples +/// +/// ```gleam +/// negate(1.0) +/// // -> -1.0 +/// ``` +/// +pub fn negate(x: Float) -> Float { + -1.0 *. x +} + +/// Sums a list of `Float`s. +/// +/// ## Example +/// +/// ```gleam +/// sum([1.0, 2.2, 3.3]) +/// // -> 6.5 +/// ``` +/// +pub fn sum(numbers: List(Float)) -> Float { + sum_loop(numbers, 0.0) +} + +fn sum_loop(numbers: List(Float), initial: Float) -> Float { + case numbers { + [first, ..rest] -> sum_loop(rest, first +. initial) + [] -> initial + } +} + +/// Multiplies a list of `Float`s and returns the product. +/// +/// ## Example +/// +/// ```gleam +/// product([2.5, 3.2, 4.2]) +/// // -> 33.6 +/// ``` +/// +pub fn product(numbers: List(Float)) -> Float { + product_loop(numbers, 1.0) +} + +fn product_loop(numbers: List(Float), initial: Float) -> Float { + case numbers { + [first, ..rest] -> product_loop(rest, first *. initial) + [] -> initial + } +} + +/// Generates a random float between the given zero (inclusive) and one +/// (exclusive). +/// +/// On Erlang this updates the random state in the process dictionary. +/// See: +/// +/// ## Examples +/// +/// ```gleam +/// random() +/// // -> 0.646355926896028 +/// ``` +/// +@external(erlang, "rand", "uniform") +@external(javascript, "../gleam_stdlib.mjs", "random_uniform") +pub fn random() -> Float + +/// Computes the modulo of an float division of inputs as a `Result`. +/// +/// Returns division of the inputs as a `Result`: If the given divisor equals +/// `0`, this function returns an `Error`. +/// +/// ## Examples +/// +/// ```gleam +/// modulo(13.3, by: 3.3) +/// // -> Ok(0.1) +/// ``` +/// +/// ```gleam +/// modulo(-13.3, by: 3.3) +/// // -> Ok(3.2) +/// ``` +/// +/// ```gleam +/// modulo(13.3, by: -3.3) +/// // -> Ok(-3.2) +/// ``` +/// +/// ```gleam +/// modulo(-13.3, by: -3.3) +/// // -> Ok(-0.1) +/// ``` +/// +pub fn modulo(dividend: Float, by divisor: Float) -> Result(Float, Nil) { + case divisor { + 0.0 -> Error(Nil) + _ -> Ok(dividend -. floor(dividend /. divisor) *. divisor) + } +} + +/// Returns division of the inputs as a `Result`. +/// +/// ## Examples +/// +/// ```gleam +/// divide(0.0, 1.0) +/// // -> Ok(0.0) +/// ``` +/// +/// ```gleam +/// divide(1.0, 0.0) +/// // -> Error(Nil) +/// ``` +/// +pub fn divide(a: Float, by b: Float) -> Result(Float, Nil) { + case b { + 0.0 -> Error(Nil) + b -> Ok(a /. b) + } +} + +/// Adds two floats together. +/// +/// It's the function equivalent of the `+.` operator. +/// This function is useful in higher order functions or pipes. +/// +/// ## Examples +/// +/// ```gleam +/// add(1.0, 2.0) +/// // -> 3.0 +/// ``` +/// +/// ```gleam +/// import gleam/list +/// +/// list.fold([1.0, 2.0, 3.0], 0.0, add) +/// // -> 6.0 +/// ``` +/// +/// ```gleam +/// 3.0 |> add(2.0) +/// // -> 5.0 +/// ``` +/// +pub fn add(a: Float, b: Float) -> Float { + a +. b +} + +/// Multiplies two floats together. +/// +/// It's the function equivalent of the `*.` operator. +/// This function is useful in higher order functions or pipes. +/// +/// ## Examples +/// +/// ```gleam +/// multiply(2.0, 4.0) +/// // -> 8.0 +/// ``` +/// +/// ```gleam +/// import gleam/list +/// +/// list.fold([2.0, 3.0, 4.0], 1.0, multiply) +/// // -> 24.0 +/// ``` +/// +/// ```gleam +/// 3.0 |> multiply(2.0) +/// // -> 6.0 +/// ``` +/// +pub fn multiply(a: Float, b: Float) -> Float { + a *. b +} + +/// Subtracts one float from another. +/// +/// It's the function equivalent of the `-.` operator. +/// This function is useful in higher order functions or pipes. +/// +/// ## Examples +/// +/// ```gleam +/// subtract(3.0, 1.0) +/// // -> 2.0 +/// ``` +/// +/// ```gleam +/// import gleam/list +/// +/// list.fold([1.0, 2.0, 3.0], 10.0, subtract) +/// // -> 4.0 +/// ``` +/// +/// ```gleam +/// 3.0 |> subtract(_, 2.0) +/// // -> 1.0 +/// ``` +/// +/// ```gleam +/// 3.0 |> subtract(2.0, _) +/// // -> -1.0 +/// ``` +/// +pub fn subtract(a: Float, b: Float) -> Float { + a -. b +} + +/// Returns the natural logarithm (base e) of the given as a `Result`. If the +/// input is less than or equal to 0, returns `Error(Nil)`. +/// +/// ## Examples +/// +/// ```gleam +/// logarithm(1.0) +/// // -> Ok(0.0) +/// ``` +/// +/// ```gleam +/// logarithm(2.718281828459045) // e +/// // -> Ok(1.0) +/// ``` +/// +/// ```gleam +/// logarithm(0.0) +/// // -> Error(Nil) +/// ``` +/// +/// ```gleam +/// logarithm(-1.0) +/// // -> Error(Nil) +/// ``` +/// +pub fn logarithm(x: Float) -> Result(Float, Nil) { + // In the following check: + // 1. If x is negative then return an error as the natural logarithm + // of a negative number is undefined (would be a complex number) + // 2. If x is 0 then return an error as the natural logarithm of 0 + // approaches negative infinity + case x <=. 0.0 { + True -> Error(Nil) + False -> Ok(do_log(x)) + } +} + +@external(erlang, "math", "log") +@external(javascript, "../gleam_stdlib.mjs", "log") +fn do_log(x: Float) -> Float + +/// Returns e (Euler's number) raised to the power of the given exponent, as +/// a `Float`. +/// +/// ## Examples +/// +/// ```gleam +/// exponential(0.0) +/// // -> Ok(1.0) +/// ``` +/// +/// ```gleam +/// exponential(1.0) +/// // -> Ok(2.718281828459045) +/// ``` +/// +/// ```gleam +/// exponential(-1.0) +/// // -> Ok(0.36787944117144233) +/// ``` +/// +@external(erlang, "math", "exp") +@external(javascript, "../gleam_stdlib.mjs", "exp") +pub fn exponential(x: Float) -> Float diff --git a/build/packages/gleam_stdlib/src/gleam/function.gleam b/build/packages/gleam_stdlib/src/gleam/function.gleam new file mode 100644 index 0000000..6ae3a62 --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam/function.gleam @@ -0,0 +1,15 @@ +/// Takes a single argument and always returns its input value. +/// +pub fn identity(x: a) -> a { + x +} + +/// Takes an argument and a single function, calls that function with that +/// argument and returns that argument instead of the function return value. +/// +/// Useful for running synchronous side effects in a pipeline. +/// +pub fn tap(arg: a, effect: fn(a) -> b) -> a { + effect(arg) + arg +} diff --git a/build/packages/gleam_stdlib/src/gleam/int.gleam b/build/packages/gleam_stdlib/src/gleam/int.gleam new file mode 100644 index 0000000..fa8aaef --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam/int.gleam @@ -0,0 +1,828 @@ +//// Functions for working with integers. +//// +//// ## Division by zero +//// +//// In Erlang division by zero results in a crash, however Gleam does not have +//// partial functions and operators in core so instead division by zero returns +//// zero, a behaviour taken from Pony, Coq, and Lean. +//// +//// This may seem unexpected at first, but it is no less mathematically valid +//// than crashing or returning a special value. Division by zero is undefined +//// in mathematics. + +import gleam/float +import gleam/order.{type Order} + +/// Returns the absolute value of the input. +/// +/// ## Examples +/// +/// ```gleam +/// absolute_value(-12) +/// // -> 12 +/// ``` +/// +/// ```gleam +/// absolute_value(10) +/// // -> 10 +/// ``` +/// +pub fn absolute_value(x: Int) -> Int { + case x >= 0 { + True -> x + False -> x * -1 + } +} + +/// Returns the results of the base being raised to the power of the +/// exponent, as a `Float`. +/// +/// ## Examples +/// +/// ```gleam +/// power(2, -1.0) +/// // -> Ok(0.5) +/// ``` +/// +/// ```gleam +/// power(2, 2.0) +/// // -> Ok(4.0) +/// ``` +/// +/// ```gleam +/// power(8, 1.5) +/// // -> Ok(22.627416997969522) +/// ``` +/// +/// ```gleam +/// 4 |> power(of: 2.0) +/// // -> Ok(16.0) +/// ``` +/// +/// ```gleam +/// power(-1, 0.5) +/// // -> Error(Nil) +/// ``` +/// +pub fn power(base: Int, of exponent: Float) -> Result(Float, Nil) { + base + |> to_float + |> float.power(exponent) +} + +/// Returns the square root of the input as a `Float`. +/// +/// ## Examples +/// +/// ```gleam +/// square_root(4) +/// // -> Ok(2.0) +/// ``` +/// +/// ```gleam +/// square_root(-16) +/// // -> Error(Nil) +/// ``` +/// +pub fn square_root(x: Int) -> Result(Float, Nil) { + x + |> to_float + |> float.square_root() +} + +/// Parses a given string as an int if possible. +/// +/// ## Examples +/// +/// ```gleam +/// parse("2") +/// // -> Ok(2) +/// ``` +/// +/// ```gleam +/// parse("ABC") +/// // -> Error(Nil) +/// ``` +/// +@external(erlang, "gleam_stdlib", "parse_int") +@external(javascript, "../gleam_stdlib.mjs", "parse_int") +pub fn parse(string: String) -> Result(Int, Nil) + +/// Parses a given string as an int in a given base if possible. +/// Supports only bases 2 to 36, for values outside of which this function returns an `Error(Nil)`. +/// +/// ## Examples +/// +/// ```gleam +/// base_parse("10", 2) +/// // -> Ok(2) +/// ``` +/// +/// ```gleam +/// base_parse("30", 16) +/// // -> Ok(48) +/// ``` +/// +/// ```gleam +/// base_parse("1C", 36) +/// // -> Ok(48) +/// ``` +/// +/// ```gleam +/// base_parse("48", 1) +/// // -> Error(Nil) +/// ``` +/// +/// ```gleam +/// base_parse("48", 37) +/// // -> Error(Nil) +/// ``` +/// +pub fn base_parse(string: String, base: Int) -> Result(Int, Nil) { + case base >= 2 && base <= 36 { + True -> do_base_parse(string, base) + False -> Error(Nil) + } +} + +@external(erlang, "gleam_stdlib", "int_from_base_string") +@external(javascript, "../gleam_stdlib.mjs", "int_from_base_string") +fn do_base_parse(a: String, b: Int) -> Result(Int, Nil) + +/// Prints a given int to a string. +/// +/// ## Examples +/// +/// ```gleam +/// to_string(2) +/// // -> "2" +/// ``` +/// +@external(erlang, "erlang", "integer_to_binary") +@external(javascript, "../gleam_stdlib.mjs", "to_string") +pub fn to_string(x: Int) -> String + +/// Prints a given int to a string using the base number provided. +/// Supports only bases 2 to 36, for values outside of which this function returns an `Error(Nil)`. +/// For common bases (2, 8, 16, 36), use the `to_baseN` functions. +/// +/// ## Examples +/// +/// ```gleam +/// to_base_string(2, 2) +/// // -> Ok("10") +/// ``` +/// +/// ```gleam +/// to_base_string(48, 16) +/// // -> Ok("30") +/// ``` +/// +/// ```gleam +/// to_base_string(48, 36) +/// // -> Ok("1C") +/// ``` +/// +/// ```gleam +/// to_base_string(48, 1) +/// // -> Error(Nil) +/// ``` +/// +/// ```gleam +/// to_base_string(48, 37) +/// // -> Error(Nil) +/// ``` +/// +pub fn to_base_string(x: Int, base: Int) -> Result(String, Nil) { + case base >= 2 && base <= 36 { + True -> Ok(do_to_base_string(x, base)) + False -> Error(Nil) + } +} + +@external(erlang, "erlang", "integer_to_binary") +@external(javascript, "../gleam_stdlib.mjs", "int_to_base_string") +fn do_to_base_string(a: Int, b: Int) -> String + +/// Prints a given int to a string using base-2. +/// +/// ## Examples +/// +/// ```gleam +/// to_base2(2) +/// // -> "10" +/// ``` +/// +pub fn to_base2(x: Int) -> String { + do_to_base_string(x, 2) +} + +/// Prints a given int to a string using base-8. +/// +/// ## Examples +/// +/// ```gleam +/// to_base8(15) +/// // -> "17" +/// ``` +/// +pub fn to_base8(x: Int) -> String { + do_to_base_string(x, 8) +} + +/// Prints a given int to a string using base-16. +/// +/// ## Examples +/// +/// ```gleam +/// to_base16(48) +/// // -> "30" +/// ``` +/// +pub fn to_base16(x: Int) -> String { + do_to_base_string(x, 16) +} + +/// Prints a given int to a string using base-36. +/// +/// ## Examples +/// +/// ```gleam +/// to_base36(48) +/// // -> "1C" +/// ``` +/// +pub fn to_base36(x: Int) -> String { + do_to_base_string(x, 36) +} + +/// Takes an int and returns its value as a float. +/// +/// ## Examples +/// +/// ```gleam +/// to_float(5) +/// // -> 5.0 +/// ``` +/// +/// ```gleam +/// to_float(0) +/// // -> 0.0 +/// ``` +/// +/// ```gleam +/// to_float(-3) +/// // -> -3.0 +/// ``` +/// +@external(erlang, "erlang", "float") +@external(javascript, "../gleam_stdlib.mjs", "identity") +pub fn to_float(x: Int) -> Float + +/// Restricts an int between a lower and upper bound. +/// +/// ## Examples +/// +/// ```gleam +/// clamp(40, min: 50, max: 60) +/// // -> 50 +/// ``` +/// +pub fn clamp(x: Int, min min_bound: Int, max max_bound: Int) -> Int { + x + |> min(max_bound) + |> max(min_bound) +} + +/// Compares two ints, returning an order. +/// +/// ## Examples +/// +/// ```gleam +/// compare(2, 3) +/// // -> Lt +/// ``` +/// +/// ```gleam +/// compare(4, 3) +/// // -> Gt +/// ``` +/// +/// ```gleam +/// 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 ints, returning the smaller of the two. +/// +/// ## Examples +/// +/// ```gleam +/// min(2, 3) +/// // -> 2 +/// ``` +/// +pub fn min(a: Int, b: Int) -> Int { + case a < b { + True -> a + False -> b + } +} + +/// Compares two ints, returning the larger of the two. +/// +/// ## Examples +/// +/// ```gleam +/// 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 +/// +/// ```gleam +/// is_even(2) +/// // -> True +/// ``` +/// +/// ```gleam +/// is_even(3) +/// // -> False +/// ``` +/// +pub fn is_even(x: Int) -> Bool { + x % 2 == 0 +} + +/// Returns whether the value provided is odd. +/// +/// ## Examples +/// +/// ```gleam +/// is_odd(3) +/// // -> True +/// ``` +/// +/// ```gleam +/// is_odd(2) +/// // -> False +/// ``` +/// +pub fn is_odd(x: Int) -> Bool { + x % 2 != 0 +} + +/// Returns the negative of the value provided. +/// +/// ## Examples +/// +/// ```gleam +/// negate(1) +/// // -> -1 +/// ``` +/// +pub fn negate(x: Int) -> Int { + -1 * x +} + +/// Sums a list of ints. +/// +/// ## Example +/// +/// ```gleam +/// sum([1, 2, 3]) +/// // -> 6 +/// ``` +/// +pub fn sum(numbers: List(Int)) -> Int { + sum_loop(numbers, 0) +} + +fn sum_loop(numbers: List(Int), initial: Int) -> Int { + case numbers { + [first, ..rest] -> sum_loop(rest, first + initial) + [] -> initial + } +} + +/// Multiplies a list of ints and returns the product. +/// +/// ## Example +/// +/// ```gleam +/// product([2, 3, 4]) +/// // -> 24 +/// ``` +/// +pub fn product(numbers: List(Int)) -> Int { + product_loop(numbers, 1) +} + +fn product_loop(numbers: List(Int), initial: Int) -> Int { + case numbers { + [first, ..rest] -> product_loop(rest, first * initial) + [] -> initial + } +} + +@deprecated("Vendor this function into your codebase") +pub fn digits(x: Int, base: Int) -> Result(List(Int), Nil) { + case base < 2 { + True -> Error(Nil) + False -> Ok(digits_loop(x, base, [])) + } +} + +fn digits_loop(x: Int, base: Int, acc: List(Int)) -> List(Int) { + case absolute_value(x) < base { + True -> [x, ..acc] + False -> digits_loop(x / base, base, [x % base, ..acc]) + } +} + +@deprecated("Vendor this function into your codebase") +pub fn undigits(numbers: List(Int), base: Int) -> Result(Int, Nil) { + case base < 2 { + True -> Error(Nil) + False -> undigits_loop(numbers, base, 0) + } +} + +fn undigits_loop(numbers: List(Int), base: Int, acc: Int) -> Result(Int, Nil) { + case numbers { + [] -> Ok(acc) + [digit, ..] if digit >= base -> Error(Nil) + [digit, ..rest] -> undigits_loop(rest, base, acc * base + digit) + } +} + +/// Generates a random int between zero and the given maximum. +/// +/// The lower number is inclusive, the upper number is exclusive. +/// +/// ## Examples +/// +/// ```gleam +/// random(10) +/// // -> 4 +/// ``` +/// +/// ```gleam +/// random(1) +/// // -> 0 +/// ``` +/// +/// ```gleam +/// random(-1) +/// // -> -1 +/// ``` +/// +pub fn random(max: Int) -> Int { + { float.random() *. to_float(max) } + |> float.floor + |> float.round +} + +/// Performs a truncated integer division. +/// +/// Returns division of the inputs as a `Result`: If the given divisor equals +/// `0`, this function returns an `Error`. +/// +/// ## Examples +/// +/// ```gleam +/// divide(0, 1) +/// // -> Ok(0) +/// ``` +/// +/// ```gleam +/// divide(1, 0) +/// // -> Error(Nil) +/// ``` +/// +/// ```gleam +/// divide(5, 2) +/// // -> Ok(2) +/// ``` +/// +/// ```gleam +/// divide(-99, 2) +/// // -> Ok(-49) +/// ``` +/// +pub fn divide(dividend: Int, by divisor: Int) -> Result(Int, Nil) { + case divisor { + 0 -> Error(Nil) + divisor -> Ok(dividend / divisor) + } +} + +/// Computes the remainder of an integer division of inputs as a `Result`. +/// +/// Returns division of the inputs as a `Result`: If the given divisor equals +/// `0`, this function returns an `Error`. +/// +/// Most the time you will want to use the `%` operator instead of this +/// function. +/// +/// ## Examples +/// +/// ```gleam +/// remainder(3, 2) +/// // -> Ok(1) +/// ``` +/// +/// ```gleam +/// remainder(1, 0) +/// // -> Error(Nil) +/// ``` +/// +/// ```gleam +/// remainder(10, -1) +/// // -> Ok(0) +/// ``` +/// +/// ```gleam +/// remainder(13, by: 3) +/// // -> Ok(1) +/// ``` +/// +/// ```gleam +/// remainder(-13, by: 3) +/// // -> Ok(-1) +/// ``` +/// +/// ```gleam +/// remainder(13, by: -3) +/// // -> Ok(1) +/// ``` +/// +/// ```gleam +/// remainder(-13, by: -3) +/// // -> Ok(-1) +/// ``` +/// +pub fn remainder(dividend: Int, by divisor: Int) -> Result(Int, Nil) { + case divisor { + 0 -> Error(Nil) + divisor -> Ok(dividend % divisor) + } +} + +/// Computes the modulo of an integer division of inputs as a `Result`. +/// +/// Returns division of the inputs as a `Result`: If the given divisor equals +/// `0`, this function returns an `Error`. +/// +/// Most the time you will want to use the `%` operator instead of this +/// function. +/// +/// ## Examples +/// +/// ```gleam +/// modulo(3, 2) +/// // -> Ok(1) +/// ``` +/// +/// ```gleam +/// modulo(1, 0) +/// // -> Error(Nil) +/// ``` +/// +/// ```gleam +/// modulo(10, -1) +/// // -> Ok(0) +/// ``` +/// +/// ```gleam +/// modulo(13, by: 3) +/// // -> Ok(1) +/// ``` +/// +/// ```gleam +/// modulo(-13, by: 3) +/// // -> Ok(2) +/// ``` +/// +pub fn modulo(dividend: Int, by divisor: Int) -> Result(Int, Nil) { + case divisor { + 0 -> Error(Nil) + _ -> { + let remainder = dividend % divisor + case remainder * divisor < 0 { + True -> Ok(remainder + divisor) + False -> Ok(remainder) + } + } + } +} + +/// Performs a *floored* integer division, which means that the result will +/// always be rounded towards negative infinity. +/// +/// If you want to perform truncated integer division (rounding towards zero), +/// use `int.divide()` or the `/` operator instead. +/// +/// Returns division of the inputs as a `Result`: If the given divisor equals +/// `0`, this function returns an `Error`. +/// +/// ## Examples +/// +/// ```gleam +/// floor_divide(1, 0) +/// // -> Error(Nil) +/// ``` +/// +/// ```gleam +/// floor_divide(5, 2) +/// // -> Ok(2) +/// ``` +/// +/// ```gleam +/// floor_divide(6, -4) +/// // -> Ok(-2) +/// ``` +/// +/// ```gleam +/// floor_divide(-99, 2) +/// // -> Ok(-50) +/// ``` +/// +pub fn floor_divide(dividend: Int, by divisor: Int) -> Result(Int, Nil) { + case divisor { + 0 -> Error(Nil) + divisor -> + case dividend * divisor < 0 && dividend % divisor != 0 { + True -> Ok(dividend / divisor - 1) + False -> Ok(dividend / divisor) + } + } +} + +/// Adds two integers together. +/// +/// It's the function equivalent of the `+` operator. +/// This function is useful in higher order functions or pipes. +/// +/// ## Examples +/// +/// ```gleam +/// add(1, 2) +/// // -> 3 +/// ``` +/// +/// ```gleam +/// import gleam/list +/// list.fold([1, 2, 3], 0, add) +/// // -> 6 +/// ``` +/// +/// ```gleam +/// 3 |> add(2) +/// // -> 5 +/// ``` +/// +pub fn add(a: Int, b: Int) -> Int { + a + b +} + +/// Multiplies two integers together. +/// +/// It's the function equivalent of the `*` operator. +/// This function is useful in higher order functions or pipes. +/// +/// ## Examples +/// +/// ```gleam +/// multiply(2, 4) +/// // -> 8 +/// ``` +/// +/// ```gleam +/// import gleam/list +/// +/// list.fold([2, 3, 4], 1, multiply) +/// // -> 24 +/// ``` +/// +/// ```gleam +/// 3 |> multiply(2) +/// // -> 6 +/// ``` +/// +pub fn multiply(a: Int, b: Int) -> Int { + a * b +} + +/// Subtracts one int from another. +/// +/// It's the function equivalent of the `-` operator. +/// This function is useful in higher order functions or pipes. +/// +/// ## Examples +/// +/// ```gleam +/// subtract(3, 1) +/// // -> 2 +/// ``` +/// +/// ```gleam +/// import gleam/list +/// +/// list.fold([1, 2, 3], 10, subtract) +/// // -> 4 +/// ``` +/// +/// ```gleam +/// 3 |> subtract(2) +/// // -> 1 +/// ``` +/// +/// ```gleam +/// 3 |> subtract(2, _) +/// // -> -1 +/// ``` +/// +pub fn subtract(a: Int, b: Int) -> Int { + a - b +} + +/// Calculates the bitwise AND of its arguments. +/// +/// The exact behaviour of this function depends on the target platform. +/// On Erlang it is equivalent to bitwise operations on ints, on JavaScript it +/// is equivalent to bitwise operations on big-ints. +/// +@external(erlang, "erlang", "band") +@external(javascript, "../gleam_stdlib.mjs", "bitwise_and") +pub fn bitwise_and(x: Int, y: Int) -> Int + +/// Calculates the bitwise NOT of its argument. +/// +/// The exact behaviour of this function depends on the target platform. +/// On Erlang it is equivalent to bitwise operations on ints, on JavaScript it +/// is equivalent to bitwise operations on big-ints. +/// +@external(erlang, "erlang", "bnot") +@external(javascript, "../gleam_stdlib.mjs", "bitwise_not") +pub fn bitwise_not(x: Int) -> Int + +/// Calculates the bitwise OR of its arguments. +/// +/// The exact behaviour of this function depends on the target platform. +/// On Erlang it is equivalent to bitwise operations on ints, on JavaScript it +/// is equivalent to bitwise operations on big-ints. +/// +@external(erlang, "erlang", "bor") +@external(javascript, "../gleam_stdlib.mjs", "bitwise_or") +pub fn bitwise_or(x: Int, y: Int) -> Int + +/// Calculates the bitwise XOR of its arguments. +/// +/// The exact behaviour of this function depends on the target platform. +/// On Erlang it is equivalent to bitwise operations on ints, on JavaScript it +/// is equivalent to bitwise operations on big-ints. +/// +@external(erlang, "erlang", "bxor") +@external(javascript, "../gleam_stdlib.mjs", "bitwise_exclusive_or") +pub fn bitwise_exclusive_or(x: Int, y: Int) -> Int + +/// Calculates the result of an arithmetic left bitshift. +/// +/// The exact behaviour of this function depends on the target platform. +/// On Erlang it is equivalent to bitwise operations on ints, on JavaScript it +/// is equivalent to bitwise operations on big-ints. +/// +@external(erlang, "erlang", "bsl") +@external(javascript, "../gleam_stdlib.mjs", "bitwise_shift_left") +pub fn bitwise_shift_left(x: Int, y: Int) -> Int + +/// Calculates the result of an arithmetic right bitshift. +/// +/// The exact behaviour of this function depends on the target platform. +/// On Erlang it is equivalent to bitwise operations on ints, on JavaScript it +/// is equivalent to bitwise operations on big-ints. +/// +@external(erlang, "erlang", "bsr") +@external(javascript, "../gleam_stdlib.mjs", "bitwise_shift_right") +pub fn bitwise_shift_right(x: Int, y: Int) -> Int diff --git a/build/packages/gleam_stdlib/src/gleam/io.gleam b/build/packages/gleam_stdlib/src/gleam/io.gleam new file mode 100644 index 0000000..67088eb --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam/io.gleam @@ -0,0 +1,59 @@ +/// Writes a string to standard output (stdout). +/// +/// If you want your output to be printed on its own line see `println`. +/// +/// ## Example +/// +/// ```gleam +/// io.print("Hi mum") +/// // -> Nil +/// // Hi mum +/// ``` +/// +@external(erlang, "gleam_stdlib", "print") +@external(javascript, "../gleam_stdlib.mjs", "print") +pub fn print(string: String) -> Nil + +/// Writes a string to standard error (stderr). +/// +/// If you want your output to be printed on its own line see `println_error`. +/// +/// ## Example +/// +/// ``` +/// io.print_error("Hi pop") +/// // -> Nil +/// // Hi pop +/// ``` +/// +@external(erlang, "gleam_stdlib", "print_error") +@external(javascript, "../gleam_stdlib.mjs", "print_error") +pub fn print_error(string: String) -> Nil + +/// Writes a string to standard output (stdout), appending a newline to the end. +/// +/// ## Example +/// +/// ```gleam +/// io.println("Hi mum") +/// // -> Nil +/// // Hi mum +/// ``` +/// +@external(erlang, "gleam_stdlib", "println") +@external(javascript, "../gleam_stdlib.mjs", "console_log") +pub fn println(string: String) -> Nil + +/// Writes a string to standard error (stderr), appending a newline to the end. +/// +/// ## Example +/// +/// ```gleam +/// io.println_error("Hi pop") +/// // -> Nil +/// // Hi pop +/// ``` +/// +@external(erlang, "gleam_stdlib", "println_error") +@external(javascript, "../gleam_stdlib.mjs", "console_error") +pub fn println_error(string: String) -> Nil diff --git a/build/packages/gleam_stdlib/src/gleam/list.gleam b/build/packages/gleam_stdlib/src/gleam/list.gleam new file mode 100644 index 0000000..6373efb --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam/list.gleam @@ -0,0 +1,2426 @@ +//// 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 +//// and copying the whole list, so keep this in mind when designing your +//// programs. +//// +//// There is a dedicated syntax for prefixing to a list: +//// +//// ```gleam +//// let new_list = [1, 2, ..existing_list] +//// ``` +//// +//// And a matching syntax for getting the first elements of a list: +//// +//// ```gleam +//// case list { +//// [first_element, ..rest] -> first_element +//// _ -> "this pattern matches when the list is empty" +//// } +//// ``` +//// + +import gleam/dict.{type Dict} +import gleam/float +import gleam/int +import gleam/order.{type Order} + +/// Counts 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 +/// +/// ```gleam +/// length([]) +/// // -> 0 +/// ``` +/// +/// ```gleam +/// length([1]) +/// // -> 1 +/// ``` +/// +/// ```gleam +/// length([1, 2]) +/// // -> 2 +/// ``` +/// +@external(erlang, "erlang", "length") +pub fn length(of list: List(a)) -> Int { + length_loop(list, 0) +} + +fn length_loop(list: List(a), count: Int) -> Int { + case list { + [_, ..list] -> length_loop(list, count + 1) + [] -> count + } +} + +/// Counts the number of elements in a given list satisfying a given predicate. +/// +/// This function has to traverse the list to determine the number of elements, +/// so it runs in linear time. +/// +/// ## Examples +/// +/// ```gleam +/// count([], fn(a) { a > 0 }) +/// // -> 0 +/// ``` +/// +/// ```gleam +/// count([1], fn(a) { a > 0 }) +/// // -> 1 +/// ``` +/// +/// ```gleam +/// count([1, 2, 3], int.is_odd) +/// // -> 2 +/// ``` +/// +pub fn count(list: List(a), where predicate: fn(a) -> Bool) -> Int { + count_loop(list, predicate, 0) +} + +fn count_loop(list: List(a), predicate: fn(a) -> Bool, acc: Int) -> Int { + case list { + [] -> acc + [first, ..rest] -> + case predicate(first) { + True -> count_loop(rest, predicate, acc + 1) + False -> count_loop(rest, predicate, acc) + } + } +} + +/// Creates 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 +/// +/// ```gleam +/// reverse([]) +/// // -> [] +/// ``` +/// +/// ```gleam +/// reverse([1]) +/// // -> [1] +/// ``` +/// +/// ```gleam +/// reverse([1, 2]) +/// // -> [2, 1] +/// ``` +/// +@external(erlang, "lists", "reverse") +pub fn reverse(list: List(a)) -> List(a) { + reverse_and_prepend(list, []) +} + +/// Reverses a list and prepends it to another list. +/// This function runs in linear time, proportional to the length of the list +/// to prepend. +/// +@external(erlang, "lists", "reverse") +fn reverse_and_prepend(list prefix: List(a), to suffix: List(a)) -> List(a) { + case prefix { + [] -> suffix + [first, ..rest] -> reverse_and_prepend(list: rest, to: [first, ..suffix]) + } +} + +/// Determines whether or not the list is empty. +/// +/// This function runs in constant time. +/// +/// ## Examples +/// +/// ```gleam +/// is_empty([]) +/// // -> True +/// ``` +/// +/// ```gleam +/// is_empty([1]) +/// // -> False +/// ``` +/// +/// ```gleam +/// is_empty([1, 1]) +/// // -> False +/// ``` +/// +pub fn is_empty(list: List(a)) -> Bool { + list == [] +} + +/// Determines 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 +/// +/// ```gleam +/// [] |> contains(any: 0) +/// // -> False +/// ``` +/// +/// ```gleam +/// [0] |> contains(any: 0) +/// // -> True +/// ``` +/// +/// ```gleam +/// [1] |> contains(any: 0) +/// // -> False +/// ``` +/// +/// ```gleam +/// [1, 1] |> contains(any: 0) +/// // -> False +/// ``` +/// +/// ```gleam +/// [1, 0] |> contains(any: 0) +/// // -> True +/// ``` +/// +pub fn contains(list: List(a), any elem: a) -> Bool { + case list { + [] -> False + [first, ..] if first == elem -> True + [_, ..rest] -> contains(rest, elem) + } +} + +/// Gets the first element from the start of the list, if there is one. +/// +/// ## Examples +/// +/// ```gleam +/// first([]) +/// // -> Error(Nil) +/// ``` +/// +/// ```gleam +/// first([0]) +/// // -> Ok(0) +/// ``` +/// +/// ```gleam +/// first([1, 2]) +/// // -> Ok(1) +/// ``` +/// +pub fn first(list: List(a)) -> Result(a, Nil) { + case list { + [] -> Error(Nil) + [first, ..] -> Ok(first) + } +} + +/// Returns 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 +/// +/// ```gleam +/// rest([]) +/// // -> Error(Nil) +/// ``` +/// +/// ```gleam +/// rest([0]) +/// // -> Ok([]) +/// ``` +/// +/// ```gleam +/// rest([1, 2]) +/// // -> Ok([2]) +/// ``` +/// +pub fn rest(list: List(a)) -> Result(List(a), Nil) { + case list { + [] -> Error(Nil) + [_, ..rest] -> Ok(rest) + } +} + +/// Groups the elements from the given list by the given key function. +/// +/// Does not preserve the initial value order. +/// +/// ## Examples +/// +/// ```gleam +/// import gleam/dict +/// +/// [Ok(3), Error("Wrong"), Ok(200), Ok(73)] +/// |> group(by: fn(i) { +/// case i { +/// Ok(_) -> "Successful" +/// Error(_) -> "Failed" +/// } +/// }) +/// |> dict.to_list +/// // -> [ +/// // #("Failed", [Error("Wrong")]), +/// // #("Successful", [Ok(73), Ok(200), Ok(3)]) +/// // ] +/// ``` +/// +/// ```gleam +/// import gleam/dict +/// +/// group([1,2,3,4,5], by: fn(i) { i - i / 3 * 3 }) +/// |> dict.to_list +/// // -> [#(0, [3]), #(1, [4, 1]), #(2, [5, 2])] +/// ``` +/// +pub fn group(list: List(v), by key: fn(v) -> k) -> Dict(k, List(v)) { + group_loop(list, key, dict.new()) +} + +fn group_loop( + list: List(v), + to_key: fn(v) -> k, + groups: Dict(k, List(v)), +) -> Dict(k, List(v)) { + case list { + [] -> groups + [first, ..rest] -> { + let key = to_key(first) + let groups = case dict.get(groups, key) { + Error(_) -> dict.insert(groups, key, [first]) + Ok(existing) -> dict.insert(groups, key, [first, ..existing]) + } + group_loop(rest, to_key, groups) + } + } +} + +/// Returns a new list containing only the elements from the first list for +/// which the given functions returns `True`. +/// +/// ## Examples +/// +/// ```gleam +/// filter([2, 4, 6, 1], fn(x) { x > 2 }) +/// // -> [4, 6] +/// ``` +/// +/// ```gleam +/// filter([2, 4, 6, 1], fn(x) { x > 6 }) +/// // -> [] +/// ``` +/// +pub fn filter(list: List(a), keeping predicate: fn(a) -> Bool) -> List(a) { + filter_loop(list, predicate, []) +} + +fn filter_loop(list: List(a), fun: fn(a) -> Bool, acc: List(a)) -> List(a) { + case list { + [] -> reverse(acc) + [first, ..rest] -> { + let new_acc = case fun(first) { + True -> [first, ..acc] + False -> acc + } + filter_loop(rest, fun, new_acc) + } + } +} + +/// Returns a new list containing only the elements from the first list for +/// which the given functions returns `Ok(_)`. +/// +/// ## Examples +/// +/// ```gleam +/// filter_map([2, 4, 6, 1], Error) +/// // -> [] +/// ``` +/// +/// ```gleam +/// filter_map([2, 4, 6, 1], fn(x) { Ok(x + 1) }) +/// // -> [3, 5, 7, 2] +/// ``` +/// +pub fn filter_map(list: List(a), with fun: fn(a) -> Result(b, e)) -> List(b) { + filter_map_loop(list, fun, []) +} + +fn filter_map_loop( + list: List(a), + fun: fn(a) -> Result(b, e), + acc: List(b), +) -> List(b) { + case list { + [] -> reverse(acc) + [first, ..rest] -> { + let new_acc = case fun(first) { + Ok(first) -> [first, ..acc] + Error(_) -> acc + } + filter_map_loop(rest, fun, new_acc) + } + } +} + +/// Returns a new list containing only the elements of the first list after the +/// function has been applied to each one. +/// +/// ## Examples +/// +/// ```gleam +/// map([2, 4, 6], fn(x) { x * 2 }) +/// // -> [4, 8, 12] +/// ``` +/// +pub fn map(list: List(a), with fun: fn(a) -> b) -> List(b) { + map_loop(list, fun, []) +} + +fn map_loop(list: List(a), fun: fn(a) -> b, acc: List(b)) -> List(b) { + case list { + [] -> reverse(acc) + [first, ..rest] -> map_loop(rest, fun, [fun(first), ..acc]) + } +} + +/// Combines two lists into a single list using the given function. +/// +/// If a list is longer than the other the extra elements are dropped. +/// +/// ## Examples +/// +/// ```gleam +/// map2([1, 2, 3], [4, 5, 6], fn(x, y) { x + y }) +/// // -> [5, 7, 9] +/// ``` +/// +/// ```gleam +/// map2([1, 2], ["a", "b", "c"], fn(i, x) { #(i, x) }) +/// // -> [#(1, "a"), #(2, "b")] +/// ``` +/// +pub fn map2(list1: List(a), list2: List(b), with fun: fn(a, b) -> c) -> List(c) { + map2_loop(list1, list2, fun, []) +} + +fn map2_loop( + list1: List(a), + list2: List(b), + fun: fn(a, b) -> c, + acc: List(c), +) -> List(c) { + case list1, list2 { + [], _ | _, [] -> reverse(acc) + [a, ..as_], [b, ..bs] -> map2_loop(as_, bs, fun, [fun(a, b), ..acc]) + } +} + +/// Similar to `map` but also lets you pass around an accumulated value. +/// +/// ## Examples +/// +/// ```gleam +/// map_fold( +/// over: [1, 2, 3], +/// from: 100, +/// with: fn(memo, i) { #(memo + i, i * 2) } +/// ) +/// // -> #(106, [2, 4, 6]) +/// ``` +/// +pub fn map_fold( + over list: List(a), + from initial: acc, + with fun: fn(acc, a) -> #(acc, b), +) -> #(acc, List(b)) { + map_fold_loop(list, fun, initial, []) +} + +fn map_fold_loop( + list: List(a), + fun: fn(acc, a) -> #(acc, b), + acc: acc, + list_acc: List(b), +) -> #(acc, List(b)) { + case list { + [] -> #(acc, reverse(list_acc)) + [first, ..rest] -> { + let #(acc, first) = fun(acc, first) + map_fold_loop(rest, fun, acc, [first, ..list_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 +/// +/// ```gleam +/// index_map(["a", "b"], fn(x, i) { #(i, x) }) +/// // -> [#(0, "a"), #(1, "b")] +/// ``` +/// +pub fn index_map(list: List(a), with fun: fn(a, Int) -> b) -> List(b) { + index_map_loop(list, fun, 0, []) +} + +fn index_map_loop( + list: List(a), + fun: fn(a, Int) -> b, + index: Int, + acc: List(b), +) -> List(b) { + case list { + [] -> reverse(acc) + [first, ..rest] -> { + let acc = [fun(first, index), ..acc] + index_map_loop(rest, fun, index + 1, acc) + } + } +} + +/// Takes a function that returns a `Result` and applies it to each element in a +/// given list in turn. +/// +/// 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 +/// +/// ```gleam +/// try_map([1, 2, 3], fn(x) { Ok(x + 2) }) +/// // -> Ok([3, 4, 5]) +/// ``` +/// +/// ```gleam +/// try_map([1, 2, 3], fn(_) { Error(0) }) +/// // -> Error(0) +/// ``` +/// +/// ```gleam +/// try_map([[1], [2, 3]], first) +/// // -> Ok([1, 2]) +/// ``` +/// +/// ```gleam +/// try_map([[1], [], [2]], first) +/// // -> Error(Nil) +/// ``` +/// +pub fn try_map( + over list: List(a), + with fun: fn(a) -> Result(b, e), +) -> Result(List(b), e) { + try_map_loop(list, fun, []) +} + +fn try_map_loop( + list: List(a), + fun: fn(a) -> Result(b, e), + acc: List(b), +) -> Result(List(b), e) { + case list { + [] -> Ok(reverse(acc)) + [first, ..rest] -> + case fun(first) { + Ok(first) -> try_map_loop(rest, fun, [first, ..acc]) + Error(error) -> Error(error) + } + } +} + +/// 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 +/// +/// ```gleam +/// drop([1, 2, 3, 4], 2) +/// // -> [3, 4] +/// ``` +/// +/// ```gleam +/// 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 { + [] -> [] + [_, ..rest] -> drop(rest, n - 1) + } + } +} + +/// 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. +/// +/// ## Examples +/// +/// ```gleam +/// take([1, 2, 3, 4], 2) +/// // -> [1, 2] +/// ``` +/// +/// ```gleam +/// take([1, 2, 3, 4], 9) +/// // -> [1, 2, 3, 4] +/// ``` +/// +pub fn take(from list: List(a), up_to n: Int) -> List(a) { + take_loop(list, n, []) +} + +fn take_loop(list: List(a), n: Int, acc: List(a)) -> List(a) { + case n <= 0 { + True -> reverse(acc) + False -> + case list { + [] -> reverse(acc) + [first, ..rest] -> take_loop(rest, n - 1, [first, ..acc]) + } + } +} + +/// Returns a new empty list. +/// +/// ## Examples +/// +/// ```gleam +/// new() +/// // -> [] +/// ``` +/// +pub fn new() -> List(a) { + [] +} + +/// Returns the given item wrapped in a list. +/// +/// ## Examples +/// +/// ```gleam +/// wrap(1) +/// // -> [1] +/// +/// wrap(["a", "b", "c"]) +/// // -> [["a", "b", "c"]] +/// +/// wrap([[]]) +/// // -> [[[]]] +/// ``` +/// +/// +pub fn wrap(item: a) -> List(a) { + [item] +} + +/// Joins one list onto the end of another. +/// +/// This function runs in linear time, and it traverses and copies the first +/// list. +/// +/// ## Examples +/// +/// ```gleam +/// append([1, 2], [3]) +/// // -> [1, 2, 3] +/// ``` +/// +@external(erlang, "lists", "append") +pub fn append(first: List(a), second: List(a)) -> List(a) { + append_loop(reverse(first), second) +} + +fn append_loop(first: List(a), second: List(a)) -> List(a) { + case first { + [] -> second + [first, ..rest] -> append_loop(rest, [first, ..second]) + } +} + +/// Prefixes an item to a list. This can also be done using the dedicated +/// syntax instead +/// +/// ```gleam +/// let existing_list = [2, 3, 4] +/// +/// [1, ..existing_list] +/// // -> [1, 2, 3, 4] +/// +/// prepend(to: existing_list, this: 1) +/// // -> [1, 2, 3, 4] +/// ``` +/// +pub fn prepend(to list: List(a), this item: a) -> List(a) { + [item, ..list] +} + +/// Joins a list of lists into a single list. +/// +/// This function traverses all elements twice on the JavaScript target. +/// This function traverses all elements once on the Erlang target. +/// +/// ## Examples +/// +/// ```gleam +/// flatten([[1], [2, 3], []]) +/// // -> [1, 2, 3] +/// ``` +/// +@external(erlang, "lists", "append") +pub fn flatten(lists: List(List(a))) -> List(a) { + flatten_loop(lists, []) +} + +fn flatten_loop(lists: List(List(a)), acc: List(a)) -> List(a) { + case lists { + [] -> reverse(acc) + [list, ..further_lists] -> + flatten_loop(further_lists, reverse_and_prepend(list, to: acc)) + } +} + +/// Maps the list with the given function into a list of lists, and then flattens it. +/// +/// ## Examples +/// +/// ```gleam +/// flat_map([2, 4, 6], fn(x) { [x, x + 1] }) +/// // -> [2, 3, 4, 5, 6, 7] +/// ``` +/// +pub fn flat_map(over list: List(a), with fun: fn(a) -> List(b)) -> List(b) { + flatten(map(list, fun)) +} + +/// Reduces 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(add(add(0, 1), 2), 3)`. +/// +/// This function runs in linear time. +/// +pub fn fold( + over list: List(a), + from initial: acc, + with fun: fn(acc, a) -> acc, +) -> acc { + case list { + [] -> initial + [first, ..rest] -> fold(rest, fun(initial, first), fun) + } +} + +/// Reduces 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(add(add(0, 3), 2), 1)`. +/// +/// 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( + over list: List(a), + from initial: acc, + with fun: fn(acc, a) -> acc, +) -> acc { + case list { + [] -> initial + [first, ..rest] -> fun(fold_right(rest, initial, fun), first) + } +} + +/// Like fold but the folding function also receives the index of the current element. +/// +/// ## Examples +/// +/// ```gleam +/// ["a", "b", "c"] +/// |> index_fold("", fn(acc, item, index) { +/// acc <> int.to_string(index) <> ":" <> item <> " " +/// }) +/// // -> "0:a 1:b 2:c" +/// ``` +/// +/// ```gleam +/// [10, 20, 30] +/// |> index_fold(0, fn(acc, item, index) { acc + item * index }) +/// // -> 80 +/// ``` +/// +pub fn index_fold( + over list: List(a), + from initial: acc, + with fun: fn(acc, a, Int) -> acc, +) -> acc { + index_fold_loop(list, initial, fun, 0) +} + +fn index_fold_loop( + over: List(a), + acc: acc, + with: fn(acc, a, Int) -> acc, + index: Int, +) -> acc { + case over { + [] -> acc + [first, ..rest] -> + index_fold_loop(rest, with(acc, first, index), with, index + 1) + } +} + +/// 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 +/// +/// ```gleam +/// [1, 2, 3, 4] +/// |> try_fold(0, fn(acc, i) { +/// case i < 3 { +/// True -> Ok(acc + i) +/// False -> Error(Nil) +/// } +/// }) +/// // -> Error(Nil) +/// ``` +/// +pub fn try_fold( + over list: List(a), + from initial: acc, + with fun: fn(acc, a) -> Result(acc, e), +) -> Result(acc, e) { + case list { + [] -> Ok(initial) + [first, ..rest] -> + case fun(initial, first) { + Ok(result) -> try_fold(rest, result, fun) + Error(_) as error -> error + } + } +} + +pub type ContinueOrStop(a) { + Continue(a) + Stop(a) +} + +/// A variant of fold that allows to stop folding earlier. +/// +/// The folding function should return `ContinueOrStop(accumulator)`. +/// If the returned value is `Continue(accumulator)` fold_until will try the next value in the list. +/// If the returned value is `Stop(accumulator)` fold_until will stop and return that accumulator. +/// +/// ## Examples +/// +/// ```gleam +/// [1, 2, 3, 4] +/// |> fold_until(0, fn(acc, i) { +/// case i < 3 { +/// True -> Continue(acc + i) +/// False -> Stop(acc) +/// } +/// }) +/// // -> 3 +/// ``` +/// +pub fn fold_until( + over list: List(a), + from initial: acc, + with fun: fn(acc, a) -> ContinueOrStop(acc), +) -> acc { + case list { + [] -> initial + [first, ..rest] -> + case fun(initial, first) { + Continue(next_accumulator) -> fold_until(rest, next_accumulator, fun) + Stop(b) -> b + } + } +} + +/// Finds the first element in a given list for which the given function returns +/// `True`. +/// +/// Returns `Error(Nil)` if no such element is found. +/// +/// ## Examples +/// +/// ```gleam +/// find([1, 2, 3], fn(x) { x > 2 }) +/// // -> Ok(3) +/// ``` +/// +/// ```gleam +/// find([1, 2, 3], fn(x) { x > 4 }) +/// // -> Error(Nil) +/// ``` +/// +/// ```gleam +/// find([], fn(_) { True }) +/// // -> Error(Nil) +/// ``` +/// +pub fn find( + in list: List(a), + one_that is_desired: fn(a) -> Bool, +) -> Result(a, Nil) { + case list { + [] -> Error(Nil) + [first, ..rest] -> + case is_desired(first) { + True -> Ok(first) + False -> find(in: rest, one_that: is_desired) + } + } +} + +/// Finds the first element in a given list for which the given function returns +/// `Ok(new_value)`, then returns the wrapped `new_value`. +/// +/// Returns `Error(Nil)` if no such element is found. +/// +/// ## Examples +/// +/// ```gleam +/// find_map([[], [2], [3]], first) +/// // -> Ok(2) +/// ``` +/// +/// ```gleam +/// find_map([[], []], first) +/// // -> Error(Nil) +/// ``` +/// +/// ```gleam +/// find_map([], first) +/// // -> Error(Nil) +/// ``` +/// +pub fn find_map( + in list: List(a), + with fun: fn(a) -> Result(b, c), +) -> Result(b, Nil) { + case list { + [] -> Error(Nil) + [first, ..rest] -> + case fun(first) { + Ok(first) -> Ok(first) + Error(_) -> 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 +/// +/// ```gleam +/// all([], fn(x) { x > 3 }) +/// // -> True +/// ``` +/// +/// ```gleam +/// all([4, 5], fn(x) { x > 3 }) +/// // -> True +/// ``` +/// +/// ```gleam +/// all([4, 3], fn(x) { x > 3 }) +/// // -> False +/// ``` +/// +pub fn all(in list: List(a), satisfying predicate: fn(a) -> Bool) -> Bool { + case list { + [] -> True + [first, ..rest] -> + case predicate(first) { + True -> all(rest, predicate) + False -> 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 +/// +/// ```gleam +/// any([], fn(x) { x > 3 }) +/// // -> False +/// ``` +/// +/// ```gleam +/// any([4, 5], fn(x) { x > 3 }) +/// // -> True +/// ``` +/// +/// ```gleam +/// any([4, 3], fn(x) { x > 4 }) +/// // -> False +/// ``` +/// +/// ```gleam +/// any([3, 4], fn(x) { x > 3 }) +/// // -> True +/// ``` +/// +pub fn any(in list: List(a), satisfying predicate: fn(a) -> Bool) -> Bool { + case list { + [] -> False + [first, ..rest] -> + case predicate(first) { + True -> True + False -> any(rest, predicate) + } + } +} + +/// Takes two lists and returns a single list of 2-element tuples. +/// +/// If one of the lists is longer than the other, the remaining elements from +/// the longer list are not used. +/// +/// ## Examples +/// +/// ```gleam +/// zip([], []) +/// // -> [] +/// ``` +/// +/// ```gleam +/// zip([1, 2], [3]) +/// // -> [#(1, 3)] +/// ``` +/// +/// ```gleam +/// zip([1], [3, 4]) +/// // -> [#(1, 3)] +/// ``` +/// +/// ```gleam +/// zip([1, 2], [3, 4]) +/// // -> [#(1, 3), #(2, 4)] +/// ``` +/// +pub fn zip(list: List(a), with other: List(b)) -> List(#(a, b)) { + zip_loop(list, other, []) +} + +fn zip_loop(one: List(a), other: List(b), acc: List(#(a, b))) -> List(#(a, b)) { + case one, other { + [first_one, ..rest_one], [first_other, ..rest_other] -> + zip_loop(rest_one, rest_other, [#(first_one, first_other), ..acc]) + _, _ -> reverse(acc) + } +} + +/// Takes two lists and returns a single list of 2-element tuples. +/// +/// If one of the lists is longer than the other, an `Error` is returned. +/// +/// ## Examples +/// +/// ```gleam +/// strict_zip([], []) +/// // -> Ok([]) +/// ``` +/// +/// ```gleam +/// strict_zip([1, 2], [3]) +/// // -> Error(Nil) +/// ``` +/// +/// ```gleam +/// strict_zip([1], [3, 4]) +/// // -> Error(Nil) +/// ``` +/// +/// ```gleam +/// strict_zip([1, 2], [3, 4]) +/// // -> Ok([#(1, 3), #(2, 4)]) +/// ``` +/// +pub fn strict_zip( + list: List(a), + with other: List(b), +) -> Result(List(#(a, b)), Nil) { + strict_zip_loop(list, other, []) +} + +fn strict_zip_loop( + one: List(a), + other: List(b), + acc: List(#(a, b)), +) -> Result(List(#(a, b)), Nil) { + case one, other { + [], [] -> Ok(reverse(acc)) + [], _ | _, [] -> Error(Nil) + [first_one, ..rest_one], [first_other, ..rest_other] -> + strict_zip_loop(rest_one, rest_other, [#(first_one, first_other), ..acc]) + } +} + +/// Takes a single list of 2-element tuples and returns two lists. +/// +/// ## Examples +/// +/// ```gleam +/// unzip([#(1, 2), #(3, 4)]) +/// // -> #([1, 3], [2, 4]) +/// ``` +/// +/// ```gleam +/// unzip([]) +/// // -> #([], []) +/// ``` +/// +pub fn unzip(input: List(#(a, b))) -> #(List(a), List(b)) { + unzip_loop(input, [], []) +} + +fn unzip_loop( + input: List(#(a, b)), + one: List(a), + other: List(b), +) -> #(List(a), List(b)) { + case input { + [] -> #(reverse(one), reverse(other)) + [#(first_one, first_other), ..rest] -> + unzip_loop(rest, [first_one, ..one], [first_other, ..other]) + } +} + +/// Inserts a given value between each existing element in a given list. +/// +/// This function runs in linear time and copies the list. +/// +/// ## Examples +/// +/// ```gleam +/// intersperse([1, 1, 1], 2) +/// // -> [1, 2, 1, 2, 1] +/// ``` +/// +/// ```gleam +/// intersperse([], 2) +/// // -> [] +/// ``` +/// +pub fn intersperse(list: List(a), with elem: a) -> List(a) { + case list { + [] | [_] -> list + [first, ..rest] -> intersperse_loop(rest, elem, [first]) + } +} + +fn intersperse_loop(list: List(a), separator: a, acc: List(a)) -> List(a) { + case list { + [] -> reverse(acc) + [first, ..rest] -> + intersperse_loop(rest, separator, [first, separator, ..acc]) + } +} + +/// Removes any duplicate elements from a given list. +/// +/// This function returns in loglinear time. +/// +/// ## Examples +/// +/// ```gleam +/// unique([1, 1, 1, 4, 7, 3, 3, 4]) +/// // -> [1, 4, 7, 3] +/// ``` +/// +pub fn unique(list: List(a)) -> List(a) { + unique_loop(list, dict.new(), []) +} + +fn unique_loop(list: List(a), seen: Dict(a, Nil), acc: List(a)) -> List(a) { + case list { + [] -> reverse(acc) + [first, ..rest] -> + case dict.has_key(seen, first) { + True -> unique_loop(rest, seen, acc) + False -> + unique_loop(rest, dict.insert(seen, first, Nil), [first, ..acc]) + } + } +} + +/// Sorts from smallest to largest based upon the ordering specified by a given +/// function. +/// +/// ## Examples +/// +/// ```gleam +/// import gleam/int +/// +/// 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) { + // This is a natural, tail recursive, stable merge sort: + // - natural: it is very efficient if you call it on a list that is already + // (pre)sorted because it works on slices of the original list. + // - tail recursive: the stack won't grow linearly with the size of the list. + // - stable: if two items are considered to be equal then their original + // relative order is preserved. + case list { + // If the list has zero/one item then it's already sorted. + [] -> [] + [x] -> [x] + + // Otherwise the algorithm works as follow: we split the list in sequences + // of already sorted values as they appear in the list and then we merge + // those together two by two using `merge_all`. + [x, y, ..rest] -> { + // We need to compare the first two items to properly call `sequences` + // with the correct initial values. If the second item is <= than the + // first, then we know we'll start by growing a descending sequence + // (and an ascending one in the opposite case). + let direction = case compare(x, y) { + order.Lt | order.Eq -> Ascending + order.Gt -> Descending + } + + // `sequences` produces sequences in ascending order so we call the + // `merge_all` function saying it to expect all sequences to be sorted + // that way. + let sequences = sequences(rest, compare, [x], direction, y, []) + merge_all(sequences, Ascending, compare) + } + } +} + +type Sorting { + Ascending + Descending +} + +/// Given a list it returns slices of it that are locally sorted in ascending +/// order. +/// +/// Imagine you have this list: +/// +/// ``` +/// [1, 2, 3, 2, 1, 0] +/// ^^^^^^^ ^^^^^^^ This is a slice in descending order +/// | +/// | This is a slice that is sorted in ascending order +/// ``` +/// +/// So the produced result will contain these two slices, each one sorted in +/// ascending order: `[[1, 2, 3], [0, 1, 2]]`. +/// +/// - `growing` is an accumulator with the current slice being grown +/// - `direction` is the growing direction of the slice being grown, it could +/// either be ascending or strictly descending +/// - `prev` is the previous element that needs to be added to the growing slice +/// it is carried around to check whether we have to keep growing the current +/// slice or not +/// - `acc` is the accumulator containing the slices sorted in ascending order +/// +fn sequences( + list: List(a), + compare: fn(a, a) -> Order, + growing: List(a), + direction: Sorting, + prev: a, + acc: List(List(a)), +) -> List(List(a)) { + // First of all we must not forget to add the previous element to the + // currently growing slice. + let growing = [prev, ..growing] + + case list { + [] -> + case direction { + // Notice how we have to reverse the accumulator we're growing: since + // we always add items to the head, `growing` is built in the opposite + // sorting order of what it actually is in the original list. + Ascending -> [reverse(growing), ..acc] + Descending -> [growing, ..acc] + } + + [new, ..rest] -> + case compare(prev, new), direction { + // In case the new element respects the ordering of the growing + // sequence, then we just keep growing it. + // Notice how a growing sequence is weakly growing (that is it can have + // consecutive equal items) while a decreasing sequence is strictly + // decreasing (no consecutive equal items), this is needed to make the + // algorithm stable! + order.Gt, Descending | order.Lt, Ascending | order.Eq, Ascending -> + sequences(rest, compare, growing, direction, new, acc) + + // We were growing an ascending (descending) sequence and the new item + // is smaller (bigger) than the previous one, this means we have to stop + // growing this sequence and start with a new one whose first item will + // be the one we just found. + order.Gt, Ascending | order.Lt, Descending | order.Eq, Descending -> { + let acc = case direction { + Ascending -> [reverse(growing), ..acc] + Descending -> [growing, ..acc] + } + case rest { + // The list is over so we just create a sequence containing the last + // item we saw and add it to the accumulator before returning it. + [] -> [[new], ..acc] + + // If the list is not over we have a peek at the next item to decide + // in which direction is growing the new sequence and make the + // recursive call with the appropriate arguments. + [next, ..rest] -> { + let direction = case compare(new, next) { + order.Lt | order.Eq -> Ascending + order.Gt -> Descending + } + sequences(rest, compare, [new], direction, next, acc) + } + } + } + } + } +} + +/// Given some some sorted sequences (assumed to be sorted in `direction`) it +/// merges them all together until we're left with just a list sorted in +/// ascending order. +/// +fn merge_all( + sequences: List(List(a)), + direction: Sorting, + compare: fn(a, a) -> Order, +) -> List(a) { + case sequences, direction { + [], _ -> [] + + // If we have a single list in ascending order then we're done. + [sequence], Ascending -> sequence + + // If we have a single list in descending order, we reverse it to make sure + // it's in ascending order and we're done. + [sequence], Descending -> reverse(sequence) + + // Merging together sequences that are in ascending (descending) order + // reverses their order, so the recursive call will assume to be merging + // lists sorted in the opposite order! + _, Ascending -> { + let sequences = merge_ascending_pairs(sequences, compare, []) + merge_all(sequences, Descending, compare) + } + + _, Descending -> { + let sequences = merge_descending_pairs(sequences, compare, []) + merge_all(sequences, Ascending, compare) + } + } +} + +/// Given a list of ascending lists, it merges adjacent pairs into a single +/// descending list, halving their number. +/// It returns a list of the remaining descending lists. +/// +fn merge_ascending_pairs( + sequences: List(List(a)), + compare: fn(a, a) -> Order, + acc: List(List(a)), +) { + case sequences { + [] -> reverse(acc) + + // Beware, if we have just one item left we must reverse it: we take + // ascending lists as input and have to return descending ones. + // If we returned it like it is it would be sorted in ascending order. + [sequence] -> reverse([reverse(sequence), ..acc]) + + [ascending1, ascending2, ..rest] -> { + let descending = merge_ascendings(ascending1, ascending2, compare, []) + merge_ascending_pairs(rest, compare, [descending, ..acc]) + } + } +} + +/// This is the same as merge_ascending_pairs but flipped for descending lists. +/// +fn merge_descending_pairs( + sequences: List(List(a)), + compare: fn(a, a) -> Order, + acc: List(List(a)), +) { + case sequences { + [] -> reverse(acc) + + [sequence] -> reverse([reverse(sequence), ..acc]) + + [descending1, descending2, ..rest] -> { + let ascending = merge_descendings(descending1, descending2, compare, []) + merge_descending_pairs(rest, compare, [ascending, ..acc]) + } + } +} + +/// Merges two lists sorted in ascending order into a single list sorted in +/// descending order according to the given comparator function. +/// +/// This reversing of the sort order is not avoidable if we want to implement +/// merge as a tail recursive function. We could reverse the accumulator before +/// returning it but that would end up being less efficient; so the merging +/// algorithm has to play around this. +/// +fn merge_ascendings( + list1: List(a), + list2: List(a), + compare: fn(a, a) -> Order, + acc: List(a), +) -> List(a) { + case list1, list2 { + [], list | list, [] -> reverse_and_prepend(list, acc) + + [first1, ..rest1], [first2, ..rest2] -> + case compare(first1, first2) { + order.Lt -> merge_ascendings(rest1, list2, compare, [first1, ..acc]) + order.Gt | order.Eq -> + merge_ascendings(list1, rest2, compare, [first2, ..acc]) + } + } +} + +/// This is exactly the same as merge_ascendings but mirrored: it merges two +/// lists sorted in descending order into a single list sorted in ascending +/// order according to the given comparator function. +/// +/// This reversing of the sort order is not avoidable if we want to implement +/// merge as a tail recursive function. We could reverse the accumulator before +/// returning it but that would end up being less efficient; so the merging +/// algorithm has to play around this. +/// +fn merge_descendings( + list1: List(a), + list2: List(a), + compare: fn(a, a) -> Order, + acc: List(a), +) -> List(a) { + case list1, list2 { + [], list | list, [] -> reverse_and_prepend(list, acc) + [first1, ..rest1], [first2, ..rest2] -> + case compare(first1, first2) { + order.Lt -> merge_descendings(list1, rest2, compare, [first2, ..acc]) + order.Gt | order.Eq -> + merge_descendings(rest1, list2, compare, [first1, ..acc]) + } + } +} + +/// Creates a list of ints ranging from a given start and finish. +/// +/// ## Examples +/// +/// ```gleam +/// range(0, 0) +/// // -> [0] +/// ``` +/// +/// ```gleam +/// range(0, 5) +/// // -> [0, 1, 2, 3, 4, 5] +/// ``` +/// +/// ```gleam +/// range(1, -5) +/// // -> [1, 0, -1, -2, -3, -4, -5] +/// ``` +/// +pub fn range(from start: Int, to stop: Int) -> List(Int) { + range_loop(start, stop, []) +} + +fn range_loop(start: Int, stop: Int, acc: List(Int)) -> List(Int) { + case int.compare(start, stop) { + order.Eq -> [stop, ..acc] + order.Gt -> range_loop(start, stop + 1, [stop, ..acc]) + order.Lt -> range_loop(start, stop - 1, [stop, ..acc]) + } +} + +/// Builds a list of a given value a given number of times. +/// +/// ## Examples +/// +/// ```gleam +/// repeat("a", times: 0) +/// // -> [] +/// ``` +/// +/// ```gleam +/// repeat("a", times: 5) +/// // -> ["a", "a", "a", "a", "a"] +/// ``` +/// +pub fn repeat(item a: a, times times: Int) -> List(a) { + repeat_loop(a, times, []) +} + +fn repeat_loop(item: a, times: Int, acc: List(a)) -> List(a) { + case times <= 0 { + True -> acc + False -> repeat_loop(item, times - 1, [item, ..acc]) + } +} + +/// Splits 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 +/// +/// ```gleam +/// split([6, 7, 8, 9], 0) +/// // -> #([], [6, 7, 8, 9]) +/// ``` +/// +/// ```gleam +/// split([6, 7, 8, 9], 2) +/// // -> #([6, 7], [8, 9]) +/// ``` +/// +/// ```gleam +/// split([6, 7, 8, 9], 4) +/// // -> #([6, 7, 8, 9], []) +/// ``` +/// +pub fn split(list list: List(a), at index: Int) -> #(List(a), List(a)) { + split_loop(list, index, []) +} + +fn split_loop(list: List(a), n: Int, taken: List(a)) -> #(List(a), List(a)) { + case n <= 0 { + True -> #(reverse(taken), list) + False -> + case list { + [] -> #(reverse(taken), []) + [first, ..rest] -> split_loop(rest, n - 1, [first, ..taken]) + } + } +} + +/// Splits 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 +/// +/// ```gleam +/// split_while([1, 2, 3, 4, 5], fn(x) { x <= 3 }) +/// // -> #([1, 2, 3], [4, 5]) +/// ``` +/// +/// ```gleam +/// split_while([1, 2, 3, 4, 5], fn(x) { x <= 5 }) +/// // -> #([1, 2, 3, 4, 5], []) +/// ``` +/// +pub fn split_while( + list list: List(a), + satisfying predicate: fn(a) -> Bool, +) -> #(List(a), List(a)) { + split_while_loop(list, predicate, []) +} + +fn split_while_loop( + list: List(a), + f: fn(a) -> Bool, + acc: List(a), +) -> #(List(a), List(a)) { + case list { + [] -> #(reverse(acc), []) + [first, ..rest] -> + case f(first) { + True -> split_while_loop(rest, f, [first, ..acc]) + False -> #(reverse(acc), list) + } + } +} + +/// Given a list of 2-element tuples, finds the first tuple that has a given +/// key as the first element and returns 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 +/// +/// ```gleam +/// key_find([#("a", 0), #("b", 1)], "a") +/// // -> Ok(0) +/// ``` +/// +/// ```gleam +/// key_find([#("a", 0), #("b", 1)], "b") +/// // -> Ok(1) +/// ``` +/// +/// ```gleam +/// key_find([#("a", 0), #("b", 1)], "c") +/// // -> Error(Nil) +/// ``` +/// +pub fn key_find( + in keyword_list: List(#(k, v)), + find desired_key: k, +) -> Result(v, Nil) { + find_map(keyword_list, fn(keyword) { + let #(key, value) = keyword + case key == desired_key { + True -> Ok(value) + False -> Error(Nil) + } + }) +} + +/// Given a list of 2-element tuples, finds all tuples that have a given +/// key as the first element and returns the second element. +/// +/// This function may be useful for interacting with Erlang code where lists of +/// tuples are common. +/// +/// ## Examples +/// +/// ```gleam +/// key_filter([#("a", 0), #("b", 1), #("a", 2)], "a") +/// // -> [0, 2] +/// ``` +/// +/// ```gleam +/// key_filter([#("a", 0), #("b", 1)], "c") +/// // -> [] +/// ``` +/// +pub fn key_filter( + in keyword_list: List(#(k, v)), + find desired_key: k, +) -> List(v) { + filter_map(keyword_list, fn(keyword) { + let #(key, value) = keyword + case key == desired_key { + True -> Ok(value) + False -> Error(Nil) + } + }) +} + +/// Given a list of 2-element tuples, finds 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 +/// +/// ```gleam +/// key_pop([#("a", 0), #("b", 1)], "a") +/// // -> Ok(#(0, [#("b", 1)])) +/// ``` +/// +/// ```gleam +/// key_pop([#("a", 0), #("b", 1)], "b") +/// // -> Ok(#(1, [#("a", 0)])) +/// ``` +/// +/// ```gleam +/// key_pop([#("a", 0), #("b", 1)], "c") +/// // -> Error(Nil) +/// ``` +/// +pub fn key_pop(list: List(#(k, v)), key: k) -> Result(#(v, List(#(k, v))), Nil) { + key_pop_loop(list, key, []) +} + +fn key_pop_loop( + list: List(#(k, v)), + key: k, + checked: List(#(k, v)), +) -> Result(#(v, List(#(k, v))), Nil) { + case list { + [] -> Error(Nil) + [#(k, v), ..rest] if k == key -> + Ok(#(v, reverse_and_prepend(checked, rest))) + [first, ..rest] -> key_pop_loop(rest, key, [first, ..checked]) + } +} + +/// Given a list of 2-element tuples, inserts 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 +/// +/// ```gleam +/// key_set([#(5, 0), #(4, 1)], 4, 100) +/// // -> [#(5, 0), #(4, 100)] +/// ``` +/// +/// ```gleam +/// key_set([#(5, 0), #(4, 1)], 1, 100) +/// // -> [#(5, 0), #(4, 1), #(1, 100)] +/// ``` +/// +pub fn key_set(list: List(#(k, v)), key: k, value: v) -> List(#(k, v)) { + key_set_loop(list, key, value, []) +} + +fn key_set_loop( + list: List(#(k, v)), + key: k, + value: v, + inspected: List(#(k, v)), +) -> List(#(k, v)) { + case list { + [#(k, _), ..rest] if k == key -> + reverse_and_prepend(inspected, [#(k, value), ..rest]) + [first, ..rest] -> key_set_loop(rest, key, value, [first, ..inspected]) + [] -> reverse([#(key, value), ..inspected]) + } +} + +/// Calls a function for each element in a list, discarding the return value. +/// +/// Useful for calling a side effect for every item of a list. +/// +/// ```gleam +/// import gleam/io +/// +/// each(["1", "2", "3"], io.println) +/// // -> Nil +/// // 1 +/// // 2 +/// // 3 +/// ``` +/// +pub fn each(list: List(a), f: fn(a) -> b) -> Nil { + case list { + [] -> Nil + [first, ..rest] -> { + f(first) + each(rest, f) + } + } +} + +/// Calls a `Result` returning function for each element in a list, discarding +/// the return value. If the function returns `Error` then the iteration is +/// stopped and the error is returned. +/// +/// Useful for calling a side effect for every item of a list. +/// +/// ## Examples +/// +/// ```gleam +/// try_each( +/// over: [1, 2, 3], +/// with: function_that_might_fail, +/// ) +/// // -> Ok(Nil) +/// ``` +/// +pub fn try_each( + over list: List(a), + with fun: fn(a) -> Result(b, e), +) -> Result(Nil, e) { + case list { + [] -> Ok(Nil) + [first, ..rest] -> + case fun(first) { + Ok(_) -> try_each(over: rest, with: fun) + Error(e) -> Error(e) + } + } +} + +/// Partitions a list into a tuple/pair of lists +/// by a given categorisation function. +/// +/// ## Examples +/// +/// ```gleam +/// import gleam/int +/// +/// [1, 2, 3, 4, 5] |> partition(int.is_odd) +/// // -> #([1, 3, 5], [2, 4]) +/// ``` +/// +pub fn partition( + list: List(a), + with categorise: fn(a) -> Bool, +) -> #(List(a), List(a)) { + partition_loop(list, categorise, [], []) +} + +fn partition_loop(list, categorise, trues, falses) { + case list { + [] -> #(reverse(trues), reverse(falses)) + [first, ..rest] -> + case categorise(first) { + True -> partition_loop(rest, categorise, [first, ..trues], falses) + False -> partition_loop(rest, categorise, trues, [first, ..falses]) + } + } +} + +/// Returns all the permutations of a list. +/// +/// ## Examples +/// +/// ```gleam +/// permutations([1, 2]) +/// // -> [[1, 2], [2, 1]] +/// ``` +/// +pub fn permutations(list: List(a)) -> List(List(a)) { + case list { + [] -> [[]] + l -> permutation_zip(l, [], []) + } +} + +fn permutation_zip( + list: List(a), + rest: List(a), + acc: List(List(a)), +) -> List(List(a)) { + case list { + [] -> reverse(acc) + [head, ..tail] -> + permutation_prepend( + head, + permutations(reverse_and_prepend(rest, tail)), + tail, + [head, ..rest], + acc, + ) + } +} + +fn permutation_prepend( + el: a, + permutations: List(List(a)), + list_1: List(a), + list_2: List(a), + acc: List(List(a)), +) -> List(List(a)) { + case permutations { + [] -> permutation_zip(list_1, list_2, acc) + [head, ..tail] -> + permutation_prepend(el, tail, list_1, list_2, [[el, ..head], ..acc]) + } +} + +/// Returns a list of sliding windows. +/// +/// ## Examples +/// +/// ```gleam +/// window([1,2,3,4,5], 3) +/// // -> [[1, 2, 3], [2, 3, 4], [3, 4, 5]] +/// ``` +/// +/// ```gleam +/// window([1, 2], 4) +/// // -> [] +/// ``` +/// +pub fn window(list: List(a), by n: Int) -> List(List(a)) { + case n <= 0 { + True -> [] + False -> window_loop([], list, n) + } +} + +fn window_loop(acc: List(List(a)), list: List(a), n: Int) -> List(List(a)) { + let window = take(list, n) + + case length(window) == n { + True -> window_loop([window, ..acc], drop(list, 1), n) + False -> reverse(acc) + } +} + +/// Returns a list of tuples containing two contiguous elements. +/// +/// ## Examples +/// +/// ```gleam +/// window_by_2([1,2,3,4]) +/// // -> [#(1, 2), #(2, 3), #(3, 4)] +/// ``` +/// +/// ```gleam +/// window_by_2([1]) +/// // -> [] +/// ``` +/// +pub fn window_by_2(list: List(a)) -> List(#(a, a)) { + zip(list, drop(list, 1)) +} + +/// Drops the first elements in a given list for which the predicate function returns `True`. +/// +/// ## Examples +/// +/// ```gleam +/// drop_while([1, 2, 3, 4], fn (x) { x < 3 }) +/// // -> [3, 4] +/// ``` +/// +pub fn drop_while( + in list: List(a), + satisfying predicate: fn(a) -> Bool, +) -> List(a) { + case list { + [] -> [] + [first, ..rest] -> + case predicate(first) { + True -> drop_while(rest, predicate) + False -> [first, ..rest] + } + } +} + +/// Takes the first elements in a given list for which the predicate function returns `True`. +/// +/// ## Examples +/// +/// ```gleam +/// take_while([1, 2, 3, 2, 4], fn (x) { x < 3 }) +/// // -> [1, 2] +/// ``` +/// +pub fn take_while( + in list: List(a), + satisfying predicate: fn(a) -> Bool, +) -> List(a) { + take_while_loop(list, predicate, []) +} + +fn take_while_loop( + list: List(a), + predicate: fn(a) -> Bool, + acc: List(a), +) -> List(a) { + case list { + [] -> reverse(acc) + [first, ..rest] -> + case predicate(first) { + True -> take_while_loop(rest, predicate, [first, ..acc]) + False -> reverse(acc) + } + } +} + +/// Returns a list of chunks in which +/// the return value of calling `f` on each element is the same. +/// +/// ## Examples +/// +/// ```gleam +/// [1, 2, 2, 3, 4, 4, 6, 7, 7] |> chunk(by: fn(n) { n % 2 }) +/// // -> [[1], [2, 2], [3], [4, 4, 6], [7, 7]] +/// ``` +/// +pub fn chunk(in list: List(a), by f: fn(a) -> k) -> List(List(a)) { + case list { + [] -> [] + [first, ..rest] -> chunk_loop(rest, f, f(first), [first], []) + } +} + +fn chunk_loop( + list: List(a), + f: fn(a) -> k, + previous_key: k, + current_chunk: List(a), + acc: List(List(a)), +) -> List(List(a)) { + case list { + [first, ..rest] -> { + let key = f(first) + case key == previous_key { + True -> chunk_loop(rest, f, key, [first, ..current_chunk], acc) + False -> { + let new_acc = [reverse(current_chunk), ..acc] + chunk_loop(rest, f, key, [first], new_acc) + } + } + } + [] -> reverse([reverse(current_chunk), ..acc]) + } +} + +/// Returns a list of chunks containing `count` elements each. +/// +/// If the last chunk does not have `count` elements, it is instead +/// a partial chunk, with less than `count` elements. +/// +/// For any `count` less than 1 this function behaves as if it was set to 1. +/// +/// ## Examples +/// +/// ```gleam +/// [1, 2, 3, 4, 5, 6] |> sized_chunk(into: 2) +/// // -> [[1, 2], [3, 4], [5, 6]] +/// ``` +/// +/// ```gleam +/// [1, 2, 3, 4, 5, 6, 7, 8] |> sized_chunk(into: 3) +/// // -> [[1, 2, 3], [4, 5, 6], [7, 8]] +/// ``` +/// +pub fn sized_chunk(in list: List(a), into count: Int) -> List(List(a)) { + sized_chunk_loop(list, count, count, [], []) +} + +fn sized_chunk_loop( + list: List(a), + count: Int, + left: Int, + current_chunk: List(a), + acc: List(List(a)), +) -> List(List(a)) { + case list { + [] -> + case current_chunk { + [] -> reverse(acc) + remaining -> reverse([reverse(remaining), ..acc]) + } + [first, ..rest] -> { + let chunk = [first, ..current_chunk] + case left > 1 { + True -> sized_chunk_loop(rest, count, left - 1, chunk, acc) + False -> + sized_chunk_loop(rest, count, count, [], [reverse(chunk), ..acc]) + } + } + } +} + +/// This function acts similar to fold, but does not take an initial state. +/// Instead, it starts from the first element in the list +/// and combines it with each subsequent element in turn using the given +/// function. The function is called as `fun(accumulator, current_element)`. +/// +/// Returns `Ok` to indicate a successful run, and `Error` if called on an +/// empty list. +/// +/// ## Examples +/// +/// ```gleam +/// [] |> reduce(fn(acc, x) { acc + x }) +/// // -> Error(Nil) +/// ``` +/// +/// ```gleam +/// [1, 2, 3, 4, 5] |> reduce(fn(acc, x) { acc + x }) +/// // -> Ok(15) +/// ``` +/// +pub fn reduce(over list: List(a), with fun: fn(a, a) -> a) -> Result(a, Nil) { + case list { + [] -> Error(Nil) + [first, ..rest] -> Ok(fold(rest, first, fun)) + } +} + +/// Similar to `fold`, but yields the state of the accumulator at each stage. +/// +/// ## Examples +/// +/// ```gleam +/// scan(over: [1, 2, 3], from: 100, with: fn(acc, i) { acc + i }) +/// // -> [101, 103, 106] +/// ``` +/// +pub fn scan( + over list: List(a), + from initial: acc, + with fun: fn(acc, a) -> acc, +) -> List(acc) { + scan_loop(list, initial, [], fun) +} + +fn scan_loop( + list: List(a), + accumulator: acc, + accumulated: List(acc), + fun: fn(acc, a) -> acc, +) -> List(acc) { + case list { + [] -> reverse(accumulated) + [first, ..rest] -> { + let next = fun(accumulator, first) + scan_loop(rest, next, [next, ..accumulated], fun) + } + } +} + +/// Returns the last element in the given list. +/// +/// Returns `Error(Nil)` if the list is empty. +/// +/// This function runs in linear time. +/// +/// ## Examples +/// +/// ```gleam +/// last([]) +/// // -> Error(Nil) +/// ``` +/// +/// ```gleam +/// last([1, 2, 3, 4, 5]) +/// // -> Ok(5) +/// ``` +/// +pub fn last(list: List(a)) -> Result(a, Nil) { + case list { + [] -> Error(Nil) + [last] -> Ok(last) + [_, ..rest] -> last(rest) + } +} + +/// Return unique combinations of elements in the list. +/// +/// ## Examples +/// +/// ```gleam +/// combinations([1, 2, 3], 2) +/// // -> [[1, 2], [1, 3], [2, 3]] +/// ``` +/// +/// ```gleam +/// combinations([1, 2, 3, 4], 3) +/// // -> [[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]] +/// ``` +/// +pub fn combinations(items: List(a), by n: Int) -> List(List(a)) { + case n, items { + 0, _ -> [[]] + _, [] -> [] + _, [first, ..rest] -> + rest + |> combinations(n - 1) + |> map(fn(combination) { [first, ..combination] }) + |> reverse + |> fold(combinations(rest, n), fn(acc, c) { [c, ..acc] }) + } +} + +/// Return unique pair combinations of elements in the list. +/// +/// ## Examples +/// +/// ```gleam +/// combination_pairs([1, 2, 3]) +/// // -> [#(1, 2), #(1, 3), #(2, 3)] +/// ``` +/// +pub fn combination_pairs(items: List(a)) -> List(#(a, a)) { + combination_pairs_loop(items, []) +} + +fn combination_pairs_loop(items: List(a), acc: List(#(a, a))) -> List(#(a, a)) { + case items { + [] -> reverse(acc) + [first, ..rest] -> { + let first_combinations = map(rest, with: fn(other) { #(first, other) }) + let acc = reverse_and_prepend(first_combinations, acc) + combination_pairs_loop(rest, acc) + } + } +} + +/// Make a list alternating the elements from the given lists +/// +/// ## Examples +/// +/// ```gleam +/// interleave([[1, 2], [101, 102], [201, 202]]) +/// // -> [1, 101, 201, 2, 102, 202] +/// ``` +/// +pub fn interleave(list: List(List(a))) -> List(a) { + list + |> transpose + |> flatten +} + +/// Transpose rows and columns of the list of lists. +/// +/// Notice: This function is not tail recursive, +/// and thus may exceed stack size if called, +/// with large lists (on the JavaScript target). +/// +/// ## Examples +/// +/// ```gleam +/// transpose([[1, 2, 3], [101, 102, 103]]) +/// // -> [[1, 101], [2, 102], [3, 103]] +/// ``` +/// +pub fn transpose(list_of_lists: List(List(a))) -> List(List(a)) { + transpose_loop(list_of_lists, []) +} + +fn transpose_loop(rows: List(List(a)), columns: List(List(a))) -> List(List(a)) { + case rows { + [] -> reverse(columns) + _ -> { + let #(column, rest) = take_firsts(rows, [], []) + case column { + [_, ..] -> transpose_loop(rest, [column, ..columns]) + [] -> transpose_loop(rest, columns) + } + } + } +} + +fn take_firsts( + rows: List(List(a)), + column: List(a), + remaining_rows: List(List(a)), +) -> #(List(a), List(List(a))) { + case rows { + [] -> #(reverse(column), reverse(remaining_rows)) + [[], ..rest] -> take_firsts(rest, column, remaining_rows) + [[first, ..remaining_row], ..rest_rows] -> { + let remaining_rows = [remaining_row, ..remaining_rows] + take_firsts(rest_rows, [first, ..column], remaining_rows) + } + } +} + +/// Takes a list, randomly sorts all items and returns the shuffled list. +/// +/// This function uses `float.random` to decide the order of the elements. +/// +/// ## Example +/// +/// ```gleam +/// range(1, 10) |> shuffle() +/// // -> [1, 6, 9, 10, 3, 8, 4, 2, 7, 5] +/// ``` +/// +pub fn shuffle(list: List(a)) -> List(a) { + list + |> fold(from: [], with: fn(acc, a) { [#(float.random(), a), ..acc] }) + |> do_shuffle_by_pair_indexes() + |> shuffle_pair_unwrap_loop([]) +} + +fn shuffle_pair_unwrap_loop(list: List(#(Float, a)), acc: List(a)) -> List(a) { + case list { + [] -> acc + [elem_pair, ..enumerable] -> + shuffle_pair_unwrap_loop(enumerable, [elem_pair.1, ..acc]) + } +} + +fn do_shuffle_by_pair_indexes( + list_of_pairs: List(#(Float, a)), +) -> List(#(Float, a)) { + sort(list_of_pairs, fn(a_pair: #(Float, a), b_pair: #(Float, a)) -> Order { + float.compare(a_pair.0, b_pair.0) + }) +} + +/// Takes a list and a comparator, and returns the maximum element in the list +/// +/// +/// ## Example +/// +/// ```gleam +/// range(1, 10) |> list.max(int.compare) +/// // -> Ok(10) +/// ``` +/// +/// ```gleam +/// ["a", "c", "b"] |> list.max(string.compare) +/// // -> Ok("c") +/// ``` +pub fn max( + over list: List(a), + with compare: fn(a, a) -> Order, +) -> Result(a, Nil) { + case list { + [] -> Error(Nil) + [first, ..rest] -> Ok(max_loop(rest, compare, first)) + } +} + +fn max_loop(list, compare, max) { + case list { + [] -> max + [first, ..rest] -> + case compare(first, max) { + order.Gt -> max_loop(rest, compare, first) + order.Lt | order.Eq -> max_loop(rest, compare, max) + } + } +} + +/// Returns a random sample of up to n elements from a list using reservoir +/// sampling via [Algorithm L](https://en.wikipedia.org/wiki/Reservoir_sampling#Optimal:_Algorithm_L). +/// Returns an empty list if the sample size is less than or equal to 0. +/// +/// Order is not random, only selection is. +/// +/// ## Examples +/// +/// ```gleam +/// reservoir_sample([1, 2, 3, 4, 5], 3) +/// // -> [2, 4, 5] // A random sample of 3 items +/// ``` +/// +pub fn sample(from list: List(a), up_to n: Int) -> List(a) { + let #(reservoir, rest) = build_reservoir(from: list, sized: n) + + case dict.is_empty(reservoir) { + // If the reservoire is empty that means we were asking to sample 0 or + // less items. That doesn't make much sense, so we just return an empty + // list. + True -> [] + + // Otherwise we keep looping over the remaining part of the list replacing + // random elements in the reservoir. + False -> { + let w = float.exponential(log_random() /. int.to_float(n)) + dict.values(sample_loop(rest, reservoir, n, w)) + } + } +} + +fn sample_loop( + list: List(a), + reservoir: Dict(Int, a), + n: Int, + w: Float, +) -> Dict(Int, a) { + let skip = { + let assert Ok(log) = float.logarithm(1.0 -. w) + float.round(float.floor(log_random() /. log)) + } + + case drop(list, skip) { + [] -> reservoir + [first, ..rest] -> { + let reservoir = dict.insert(reservoir, int.random(n), first) + let w = w *. float.exponential(log_random() /. int.to_float(n)) + sample_loop(rest, reservoir, n, w) + } + } +} + +const min_positive = 2.2250738585072014e-308 + +fn log_random() -> Float { + let assert Ok(random) = float.logarithm(float.random() +. min_positive) + random +} + +/// Builds the initial reservoir used by Algorithm L. +/// This is a dictionary with keys ranging from `0` up to `n - 1` where each +/// value is the corresponding element at that position in `list`. +/// +/// This also returns the remaining elements of `list` that didn't end up in +/// the reservoir. +/// +fn build_reservoir(from list: List(a), sized n: Int) -> #(Dict(Int, a), List(a)) { + build_reservoir_loop(list, n, dict.new()) +} + +fn build_reservoir_loop( + list: List(a), + size: Int, + reservoir: Dict(Int, a), +) -> #(Dict(Int, a), List(a)) { + let reservoir_size = dict.size(reservoir) + case reservoir_size >= size { + // The reservoir already has the size we wanted. + True -> #(reservoir, list) + + // Otherwise we add another element from the list to the reservoir + False -> + case list { + [] -> #(reservoir, []) + [first, ..rest] -> { + let reservoir = dict.insert(reservoir, reservoir_size, first) + build_reservoir_loop(rest, size, reservoir) + } + } + } +} diff --git a/build/packages/gleam_stdlib/src/gleam/option.gleam b/build/packages/gleam_stdlib/src/gleam/option.gleam new file mode 100644 index 0000000..af5d864 --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam/option.gleam @@ -0,0 +1,358 @@ +/// `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. +/// +/// ## `Option` and `Result` +/// +/// In other languages fallible functions may return either `Result` or +/// `Option` depending on whether there is more information to be given about the +/// failure. In Gleam all fallible functions return `Result`, and `Nil` is used +/// as the error if there is no extra detail to give. This consistency removes +/// the boilerplate that would otherwise be needed to convert between `Option` +/// and `Result` types, and makes APIs more predictable. +/// +/// The `Option` type should only be used for taking optional values as +/// function arguments, or for storing them in other data structures. +/// +pub type Option(a) { + Some(a) + None +} + +/// Combines a list of `Option`s into a single `Option`. +/// If all elements in the list are `Some` then returns a `Some` holding the list of values. +/// If any element is `None` then returns`None`. +/// +/// ## Examples +/// +/// ```gleam +/// all([Some(1), Some(2)]) +/// // -> Some([1, 2]) +/// ``` +/// +/// ```gleam +/// all([Some(1), None]) +/// // -> None +/// ``` +/// +pub fn all(list: List(Option(a))) -> Option(List(a)) { + all_loop(list, []) +} + +fn all_loop(list: List(Option(a)), acc: List(a)) -> Option(List(a)) { + case list { + [] -> Some(reverse(acc)) + [None, ..] -> None + [Some(first), ..rest] -> all_loop(rest, [first, ..acc]) + } +} + +// This is copied from the list module and not imported as importing it would +// result in a circular dependency! +@external(erlang, "lists", "reverse") +fn reverse(list: List(a)) -> List(a) { + reverse_and_prepend(list, []) +} + +fn reverse_and_prepend(list prefix: List(a), to suffix: List(a)) -> List(a) { + case prefix { + [] -> suffix + [first, ..rest] -> reverse_and_prepend(list: rest, to: [first, ..suffix]) + } +} + +/// Checks whether the `Option` is a `Some` value. +/// +/// ## Examples +/// +/// ```gleam +/// is_some(Some(1)) +/// // -> True +/// ``` +/// +/// ```gleam +/// is_some(None) +/// // -> False +/// ``` +/// +pub fn is_some(option: Option(a)) -> Bool { + option != None +} + +/// Checks whether the `Option` is a `None` value. +/// +/// ## Examples +/// +/// ```gleam +/// is_none(Some(1)) +/// // -> False +/// ``` +/// +/// ```gleam +/// is_none(None) +/// // -> True +/// ``` +/// +pub fn is_none(option: Option(a)) -> Bool { + option == None +} + +/// Converts an `Option` type to a `Result` type. +/// +/// ## Examples +/// +/// ```gleam +/// to_result(Some(1), "some_error") +/// // -> Ok(1) +/// ``` +/// +/// ```gleam +/// 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) + None -> Error(e) + } +} + +/// Converts a `Result` type to an `Option` type. +/// +/// ## Examples +/// +/// ```gleam +/// from_result(Ok(1)) +/// // -> Some(1) +/// ``` +/// +/// ```gleam +/// from_result(Error("some_error")) +/// // -> None +/// ``` +/// +pub fn from_result(result: Result(a, e)) -> Option(a) { + case result { + Ok(a) -> Some(a) + Error(_) -> None + } +} + +/// Extracts the value from an `Option`, returning a default value if there is none. +/// +/// ## Examples +/// +/// ```gleam +/// unwrap(Some(1), 0) +/// // -> 1 +/// ``` +/// +/// ```gleam +/// unwrap(None, 0) +/// // -> 0 +/// ``` +/// +pub fn unwrap(option: Option(a), or default: a) -> a { + case option { + Some(x) -> x + None -> default + } +} + +/// Extracts the value from an `Option`, evaluating the default function if the option is `None`. +/// +/// ## Examples +/// +/// ```gleam +/// lazy_unwrap(Some(1), fn() { 0 }) +/// // -> 1 +/// ``` +/// +/// ```gleam +/// lazy_unwrap(None, fn() { 0 }) +/// // -> 0 +/// ``` +/// +pub fn lazy_unwrap(option: Option(a), or default: fn() -> a) -> a { + case option { + Some(x) -> x + None -> default() + } +} + +/// Updates 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 +/// +/// ```gleam +/// map(over: Some(1), with: fn(x) { x + 1 }) +/// // -> Some(2) +/// ``` +/// +/// ```gleam +/// 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 + } +} + +/// Merges a nested `Option` into a single layer. +/// +/// ## Examples +/// +/// ```gleam +/// flatten(Some(Some(1))) +/// // -> Some(1) +/// ``` +/// +/// ```gleam +/// flatten(Some(None)) +/// // -> None +/// ``` +/// +/// ```gleam +/// flatten(None) +/// // -> None +/// ``` +/// +pub fn flatten(option: Option(Option(a))) -> Option(a) { + case option { + Some(x) -> x + None -> None + } +} + +/// Updates 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 `Option`. +/// +/// ## Examples +/// +/// ```gleam +/// then(Some(1), fn(x) { Some(x + 1) }) +/// // -> Some(2) +/// ``` +/// +/// ```gleam +/// then(Some(1), fn(x) { Some(#("a", x)) }) +/// // -> Some(#("a", 1)) +/// ``` +/// +/// ```gleam +/// then(Some(1), fn(_) { None }) +/// // -> None +/// ``` +/// +/// ```gleam +/// 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 + } +} + +/// Returns the first value if it is `Some`, otherwise returns the second value. +/// +/// ## Examples +/// +/// ```gleam +/// or(Some(1), Some(2)) +/// // -> Some(1) +/// ``` +/// +/// ```gleam +/// or(Some(1), None) +/// // -> Some(1) +/// ``` +/// +/// ```gleam +/// or(None, Some(2)) +/// // -> Some(2) +/// ``` +/// +/// ```gleam +/// or(None, None) +/// // -> None +/// ``` +/// +pub fn or(first: Option(a), second: Option(a)) -> Option(a) { + case first { + Some(_) -> first + None -> second + } +} + +/// Returns the first value if it is `Some`, otherwise evaluates the given function for a fallback value. +/// +/// ## Examples +/// +/// ```gleam +/// lazy_or(Some(1), fn() { Some(2) }) +/// // -> Some(1) +/// ``` +/// +/// ```gleam +/// lazy_or(Some(1), fn() { None }) +/// // -> Some(1) +/// ``` +/// +/// ```gleam +/// lazy_or(None, fn() { Some(2) }) +/// // -> Some(2) +/// ``` +/// +/// ```gleam +/// lazy_or(None, fn() { None }) +/// // -> None +/// ``` +/// +pub fn lazy_or(first: Option(a), second: fn() -> Option(a)) -> Option(a) { + case first { + Some(_) -> first + None -> second() + } +} + +/// Given a list of `Option`s, +/// returns only the values inside `Some`. +/// +/// ## Examples +/// +/// ```gleam +/// values([Some(1), None, Some(3)]) +/// // -> [1, 3] +/// ``` +/// +pub fn values(options: List(Option(a))) -> List(a) { + values_loop(options, []) +} + +fn values_loop(list: List(Option(a)), acc: List(a)) -> List(a) { + case list { + [] -> reverse(acc) + [None, ..rest] -> values_loop(rest, acc) + [Some(first), ..rest] -> values_loop(rest, [first, ..acc]) + } +} diff --git a/build/packages/gleam_stdlib/src/gleam/order.gleam b/build/packages/gleam_stdlib/src/gleam/order.gleam new file mode 100644 index 0000000..be8b599 --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam/order.gleam @@ -0,0 +1,156 @@ +/// 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 +/// +/// ```gleam +/// negate(Lt) +/// // -> Gt +/// ``` +/// +/// ```gleam +/// negate(Eq) +/// // -> Eq +/// ``` +/// +/// ```gleam +/// negate(Gt) +/// // -> Lt +/// ``` +/// +pub fn negate(order: Order) -> Order { + case order { + Lt -> Gt + Eq -> Eq + Gt -> Lt + } +} + +/// Produces a numeric representation of the order. +/// +/// ## Examples +/// +/// ```gleam +/// to_int(Lt) +/// // -> -1 +/// ``` +/// +/// ```gleam +/// to_int(Eq) +/// // -> 0 +/// ``` +/// +/// ```gleam +/// 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 +/// +/// ```gleam +/// 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 + } +} + +/// Inverts an ordering function, so less-than becomes greater-than and greater-than +/// becomes less-than. +/// +/// ## Examples +/// +/// ```gleam +/// import gleam/int +/// import gleam/list +/// +/// list.sort([1, 5, 4], by: reverse(int.compare)) +/// // -> [5, 4, 1] +/// ``` +/// +pub fn reverse(orderer: fn(a, a) -> Order) -> fn(a, a) -> Order { + fn(a, b) { orderer(b, a) } +} + +/// Return a fallback `Order` in case the first argument is `Eq`. +/// +/// ## Examples +/// +/// ```gleam +/// import gleam/int +/// +/// break_tie(in: int.compare(1, 1), with: Lt) +/// // -> Lt +/// ``` +/// +/// ```gleam +/// import gleam/int +/// +/// break_tie(in: int.compare(1, 0), with: Eq) +/// // -> Gt +/// ``` +/// +pub fn break_tie(in order: Order, with other: Order) -> Order { + case order { + Lt | Gt -> order + Eq -> other + } +} + +/// Invokes a fallback function returning an `Order` in case the first argument +/// is `Eq`. +/// +/// This can be useful when the fallback comparison might be expensive and it +/// needs to be delayed until strictly necessary. +/// +/// ## Examples +/// +/// ```gleam +/// import gleam/int +/// +/// lazy_break_tie(in: int.compare(1, 1), with: fn() { Lt }) +/// // -> Lt +/// ``` +/// +/// ```gleam +/// import gleam/int +/// +/// lazy_break_tie(in: int.compare(1, 0), with: fn() { Eq }) +/// // -> Gt +/// ``` +/// +pub fn lazy_break_tie(in order: Order, with comparison: fn() -> Order) -> Order { + case order { + Lt | Gt -> order + Eq -> comparison() + } +} diff --git a/build/packages/gleam_stdlib/src/gleam/pair.gleam b/build/packages/gleam_stdlib/src/gleam/pair.gleam new file mode 100644 index 0000000..566fc9c --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam/pair.gleam @@ -0,0 +1,85 @@ +/// Returns the first element in a pair. +/// +/// ## Examples +/// +/// ```gleam +/// first(#(1, 2)) +/// // -> 1 +/// ``` +/// +pub fn first(pair: #(a, b)) -> a { + let #(a, _) = pair + a +} + +/// Returns the second element in a pair. +/// +/// ## Examples +/// +/// ```gleam +/// second(#(1, 2)) +/// // -> 2 +/// ``` +/// +pub fn second(pair: #(a, b)) -> b { + let #(_, a) = pair + a +} + +/// Returns a new pair with the elements swapped. +/// +/// ## Examples +/// +/// ```gleam +/// swap(#(1, 2)) +/// // -> #(2, 1) +/// ``` +/// +pub fn swap(pair: #(a, b)) -> #(b, a) { + let #(a, b) = pair + #(b, a) +} + +/// Returns a new pair with the first element having had `with` applied to +/// it. +/// +/// ## Examples +/// +/// ```gleam +/// #(1, 2) |> map_first(fn(n) { n * 2 }) +/// // -> #(2, 2) +/// ``` +/// +pub fn map_first(of pair: #(a, b), with fun: fn(a) -> c) -> #(c, b) { + let #(a, b) = pair + #(fun(a), b) +} + +/// Returns a new pair with the second element having had `with` applied to +/// it. +/// +/// ## Examples +/// +/// ```gleam +/// #(1, 2) |> map_second(fn(n) { n * 2 }) +/// // -> #(1, 4) +/// ``` +/// +pub fn map_second(of pair: #(a, b), with fun: fn(b) -> c) -> #(a, c) { + let #(a, b) = pair + #(a, fun(b)) +} + +/// Returns a new pair with the given elements. This can also be done using the dedicated +/// syntax instead: `new(1, 2) == #(1, 2)`. +/// +/// ## Examples +/// +/// ```gleam +/// new(1, 2) +/// // -> #(1, 2) +/// ``` +/// +pub fn new(first: a, second: b) -> #(a, b) { + #(first, second) +} diff --git a/build/packages/gleam_stdlib/src/gleam/result.gleam b/build/packages/gleam_stdlib/src/gleam/result.gleam new file mode 100644 index 0000000..741754d --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam/result.gleam @@ -0,0 +1,453 @@ +//// Result represents the result of something that may succeed or not. +//// `Ok` means it was successful, `Error` means it was not successful. + +import gleam/list + +/// Checks whether the result is an `Ok` value. +/// +/// ## Examples +/// +/// ```gleam +/// is_ok(Ok(1)) +/// // -> True +/// ``` +/// +/// ```gleam +/// is_ok(Error(Nil)) +/// // -> False +/// ``` +/// +pub fn is_ok(result: Result(a, e)) -> Bool { + case result { + Error(_) -> False + Ok(_) -> True + } +} + +/// Checks whether the result is an `Error` value. +/// +/// ## Examples +/// +/// ```gleam +/// is_error(Ok(1)) +/// // -> False +/// ``` +/// +/// ```gleam +/// is_error(Error(Nil)) +/// // -> True +/// ``` +/// +pub fn is_error(result: Result(a, e)) -> Bool { + case result { + Ok(_) -> False + Error(_) -> True + } +} + +/// Updates 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 +/// +/// ```gleam +/// map(over: Ok(1), with: fn(x) { x + 1 }) +/// // -> Ok(2) +/// ``` +/// +/// ```gleam +/// 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) + } +} + +/// Updates 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 +/// +/// ```gleam +/// map_error(over: Error(1), with: fn(x) { x + 1 }) +/// // -> Error(2) +/// ``` +/// +/// ```gleam +/// 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)) + } +} + +/// Merges a nested `Result` into a single layer. +/// +/// ## Examples +/// +/// ```gleam +/// flatten(Ok(Ok(1))) +/// // -> Ok(1) +/// ``` +/// +/// ```gleam +/// flatten(Ok(Error(""))) +/// // -> Error("") +/// ``` +/// +/// ```gleam +/// 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) + } +} + +/// "Updates" an `Ok` result by passing its value to a function that yields a result, +/// and returning the yielded result. (This may "replace" the `Ok` with an `Error`.) +/// +/// If the input is an `Error` rather than an `Ok`, the function is not called and +/// the original `Error` is returned. +/// +/// This function is the equivalent of calling `map` followed by `flatten`, and +/// it is useful for chaining together multiple functions that may fail. +/// +/// ## Examples +/// +/// ```gleam +/// try(Ok(1), fn(x) { Ok(x + 1) }) +/// // -> Ok(2) +/// ``` +/// +/// ```gleam +/// try(Ok(1), fn(x) { Ok(#("a", x)) }) +/// // -> Ok(#("a", 1)) +/// ``` +/// +/// ```gleam +/// try(Ok(1), fn(_) { Error("Oh no") }) +/// // -> Error("Oh no") +/// ``` +/// +/// ```gleam +/// try(Error(Nil), fn(x) { Ok(x + 1) }) +/// // -> Error(Nil) +/// ``` +/// +pub fn try( + result: Result(a, e), + apply fun: fn(a) -> Result(b, e), +) -> Result(b, e) { + case result { + Ok(x) -> fun(x) + Error(e) -> Error(e) + } +} + +@deprecated("This function is an alias of result.try, use that instead") +pub fn then( + result: Result(a, e), + apply fun: fn(a) -> Result(b, e), +) -> Result(b, e) { + try(result, fun) +} + +/// Extracts the `Ok` value from a result, returning a default value if the result +/// is an `Error`. +/// +/// ## Examples +/// +/// ```gleam +/// unwrap(Ok(1), 0) +/// // -> 1 +/// ``` +/// +/// ```gleam +/// unwrap(Error(""), 0) +/// // -> 0 +/// ``` +/// +pub fn unwrap(result: Result(a, e), or default: a) -> a { + case result { + Ok(v) -> v + Error(_) -> default + } +} + +/// Extracts the `Ok` value from a result, evaluating the default function if the result +/// is an `Error`. +/// +/// ## Examples +/// +/// ```gleam +/// lazy_unwrap(Ok(1), fn() { 0 }) +/// // -> 1 +/// ``` +/// +/// ```gleam +/// lazy_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() + } +} + +/// Extracts the `Error` value from a result, returning a default value if the result +/// is an `Ok`. +/// +/// ## Examples +/// +/// ```gleam +/// unwrap_error(Error(1), 0) +/// // -> 1 +/// ``` +/// +/// ```gleam +/// unwrap_error(Ok(""), 0) +/// // -> 0 +/// ``` +/// +pub fn unwrap_error(result: Result(a, e), or default: e) -> e { + case result { + Ok(_) -> default + Error(e) -> e + } +} + +@deprecated("Use a case expression instead of this function") +pub fn unwrap_both(result: Result(a, a)) -> a { + case result { + Ok(a) -> a + Error(a) -> a + } +} + +/// Returns the first value if it is `Ok`, otherwise returns the second value. +/// +/// ## Examples +/// +/// ```gleam +/// or(Ok(1), Ok(2)) +/// // -> Ok(1) +/// ``` +/// +/// ```gleam +/// or(Ok(1), Error("Error 2")) +/// // -> Ok(1) +/// ``` +/// +/// ```gleam +/// or(Error("Error 1"), Ok(2)) +/// // -> Ok(2) +/// ``` +/// +/// ```gleam +/// 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 + } +} + +/// Returns the first value if it is `Ok`, otherwise evaluates the given function for a fallback value. +/// +/// If you need access to the initial error value, use `result.try_recover`. +/// +/// ## Examples +/// +/// ```gleam +/// lazy_or(Ok(1), fn() { Ok(2) }) +/// // -> Ok(1) +/// ``` +/// +/// ```gleam +/// lazy_or(Ok(1), fn() { Error("Error 2") }) +/// // -> Ok(1) +/// ``` +/// +/// ```gleam +/// lazy_or(Error("Error 1"), fn() { Ok(2) }) +/// // -> Ok(2) +/// ``` +/// +/// ```gleam +/// lazy_or(Error("Error 1"), fn() { 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() + } +} + +/// Combines 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 +/// +/// ```gleam +/// all([Ok(1), Ok(2)]) +/// // -> Ok([1, 2]) +/// ``` +/// +/// ```gleam +/// all([Ok(1), Error("e")]) +/// // -> Error("e") +/// ``` +/// +pub fn all(results: List(Result(a, e))) -> Result(List(a), e) { + list.try_map(results, fn(result) { result }) +} + +/// Given a list of results, returns a pair where the first element is a list +/// of all the values inside `Ok` and the second element is a list with all the +/// values inside `Error`. The values in both lists appear in reverse order with +/// respect to their position in the original list of results. +/// +/// ## Examples +/// +/// ```gleam +/// partition([Ok(1), Error("a"), Error("b"), Ok(2)]) +/// // -> #([2, 1], ["b", "a"]) +/// ``` +/// +pub fn partition(results: List(Result(a, e))) -> #(List(a), List(e)) { + partition_loop(results, [], []) +} + +fn partition_loop(results: List(Result(a, e)), oks: List(a), errors: List(e)) { + case results { + [] -> #(oks, errors) + [Ok(a), ..rest] -> partition_loop(rest, [a, ..oks], errors) + [Error(e), ..rest] -> partition_loop(rest, oks, [e, ..errors]) + } +} + +/// Replace the value within a result +/// +/// ## Examples +/// +/// ```gleam +/// replace(Ok(1), Nil) +/// // -> Ok(Nil) +/// ``` +/// +/// ```gleam +/// replace(Error(1), Nil) +/// // -> Error(1) +/// ``` +/// +pub fn replace(result: Result(a, e), value: b) -> Result(b, e) { + case result { + Ok(_) -> Ok(value) + Error(error) -> Error(error) + } +} + +/// Replace the error within a result +/// +/// ## Examples +/// +/// ```gleam +/// replace_error(Error(1), Nil) +/// // -> Error(Nil) +/// ``` +/// +/// ```gleam +/// replace_error(Ok(1), Nil) +/// // -> Ok(1) +/// ``` +/// +pub fn replace_error(result: Result(a, e), error: f) -> Result(a, f) { + case result { + Ok(x) -> Ok(x) + Error(_) -> Error(error) + } +} + +/// Given a list of results, returns only the values inside `Ok`. +/// +/// ## Examples +/// +/// ```gleam +/// values([Ok(1), Error("a"), Ok(3)]) +/// // -> [1, 3] +/// ``` +/// +pub fn values(results: List(Result(a, e))) -> List(a) { + list.filter_map(results, fn(result) { result }) +} + +/// Updates a value held within the `Error` 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 `Ok` rather than `Error` the function is not called and the +/// result stays the same. +/// +/// This function is useful for chaining together computations that may fail +/// and trying to recover from possible errors. +/// +/// If you do not need access to the initial error value, use `result.lazy_or`. +/// +/// ## Examples +/// +/// ```gleam +/// Ok(1) |> try_recover(with: fn(_) { Error("failed to recover") }) +/// // -> Ok(1) +/// ``` +/// +/// ```gleam +/// Error(1) |> try_recover(with: fn(error) { Ok(error + 1) }) +/// // -> Ok(2) +/// ``` +/// +/// ```gleam +/// Error(1) |> try_recover(with: fn(error) { Error("failed to recover") }) +/// // -> Error("failed to recover") +/// ``` +/// +pub fn try_recover( + result: Result(a, e), + with fun: fn(e) -> Result(a, f), +) -> Result(a, f) { + case result { + Ok(value) -> Ok(value) + Error(error) -> fun(error) + } +} diff --git a/build/packages/gleam_stdlib/src/gleam/set.gleam b/build/packages/gleam_stdlib/src/gleam/set.gleam new file mode 100644 index 0000000..6ae5e9e --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam/set.gleam @@ -0,0 +1,407 @@ +import gleam/dict.{type Dict} +import gleam/list +import gleam/result + +// A list is used as the dict value as an empty list has the smallest +// representation in Erlang's binary format +@target(erlang) +type Token = + List(Nil) + +@target(erlang) +const token = [] + +@target(javascript) +type Token = + Nil + +@target(javascript) +const token = Nil + +/// A set is a collection of unique members of the same type. +/// +/// It is implemented using the `gleam/dict` module, so inserts and lookups have +/// logarithmic time complexity. +/// +pub opaque type Set(member) { + Set(dict: Dict(member, Token)) +} + +/// Creates a new empty set. +/// +pub fn new() -> Set(member) { + Set(dict.new()) +} + +/// Gets the number of members in a set. +/// +/// This function runs in constant time. +/// +/// ## Examples +/// +/// ```gleam +/// new() +/// |> insert(1) +/// |> insert(2) +/// |> size +/// // -> 2 +/// ``` +/// +pub fn size(set: Set(member)) -> Int { + dict.size(set.dict) +} + +/// Determines whether or not the set is empty. +/// +/// ## Examples +/// +/// ```gleam +/// new() |> is_empty +/// // -> True +/// ``` +/// +/// ```gleam +/// new() |> insert(1) |> is_empty +/// // -> False +/// ``` +/// +pub fn is_empty(set: Set(member)) -> Bool { + set == new() +} + +/// Inserts an member into the set. +/// +/// This function runs in logarithmic time. +/// +/// ## Examples +/// +/// ```gleam +/// new() +/// |> insert(1) +/// |> insert(2) +/// |> size +/// // -> 2 +/// ``` +/// +pub fn insert(into set: Set(member), this member: member) -> Set(member) { + Set(dict: dict.insert(set.dict, member, token)) +} + +/// Checks whether a set contains a given member. +/// +/// This function runs in logarithmic time. +/// +/// ## Examples +/// +/// ```gleam +/// new() +/// |> insert(2) +/// |> contains(2) +/// // -> True +/// ``` +/// +/// ```gleam +/// new() +/// |> insert(2) +/// |> contains(1) +/// // -> False +/// ``` +/// +pub fn contains(in set: Set(member), this member: member) -> Bool { + set.dict + |> dict.get(member) + |> result.is_ok +} + +/// Removes a 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 +/// +/// ```gleam +/// new() +/// |> insert(2) +/// |> delete(2) +/// |> contains(1) +/// // -> False +/// ``` +/// +pub fn delete(from set: Set(member), this member: member) -> Set(member) { + Set(dict: dict.delete(set.dict, member)) +} + +/// Converts 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 +/// +/// ```gleam +/// new() |> insert(2) |> to_list +/// // -> [2] +/// ``` +/// +pub fn to_list(set: Set(member)) -> List(member) { + dict.keys(set.dict) +} + +/// Creates a new set of the members in a given list. +/// +/// This function runs in loglinear time. +/// +/// ## Examples +/// +/// ```gleam +/// import gleam/int +/// import gleam/list +/// +/// [1, 1, 2, 4, 3, 2] |> from_list |> to_list |> list.sort(by: int.compare) +/// // -> [1, 2, 3, 4] +/// ``` +/// +pub fn from_list(members: List(member)) -> Set(member) { + let dict = + list.fold(over: members, from: dict.new(), with: fn(m, k) { + dict.insert(m, k, token) + }) + Set(dict) +} + +/// Combines 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 +/// +/// ```gleam +/// from_list([1, 3, 9]) +/// |> fold(0, fn(accumulator, member) { accumulator + member }) +/// // -> 13 +/// ``` +/// +pub fn fold( + over set: Set(member), + from initial: acc, + with reducer: fn(acc, member) -> acc, +) -> acc { + dict.fold(over: set.dict, from: initial, with: fn(a, k, _) { reducer(a, k) }) +} + +/// Creates a new set from an existing set, minus any members that a given +/// function returns `False` for. +/// +/// This function runs in loglinear time. +/// +/// ## Examples +/// +/// ```gleam +/// import gleam/int +/// +/// from_list([1, 4, 6, 3, 675, 44, 67]) +/// |> filter(keeping: int.is_even) +/// |> to_list +/// // -> [4, 6, 44] +/// ``` +/// +pub fn filter( + in set: Set(member), + keeping predicate: fn(member) -> Bool, +) -> Set(member) { + Set(dict.filter(in: set.dict, keeping: fn(m, _) { predicate(m) })) +} + +/// Creates a new set from a given set with the result of applying the given +/// function to each member. +/// +/// ## Examples +/// +/// ```gleam +/// from_list([1, 2, 3, 4]) +/// |> map(with: fn(x) { x * 2 }) +/// |> to_list +/// // -> [2, 4, 6, 8] +/// ``` +pub fn map(set: Set(member), with fun: fn(member) -> mapped) -> Set(mapped) { + fold(over: set, from: new(), with: fn(acc, member) { + insert(acc, fun(member)) + }) +} + +/// Creates a new set from a given set with all the same entries except any +/// entry found on the given list. +/// +/// ## Examples +/// +/// ```gleam +/// from_list([1, 2, 3, 4]) +/// |> drop([1, 3]) +/// |> to_list +/// // -> [2, 4] +/// ``` +pub fn drop(from set: Set(member), drop disallowed: List(member)) -> Set(member) { + list.fold(over: disallowed, from: set, with: delete) +} + +/// Creates a new set from a given set, only including any members which are in +/// a given list. +/// +/// This function runs in loglinear time. +/// +/// ## Examples +/// +/// ```gleam +/// 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(dict.take(from: set.dict, keeping: desired)) +} + +/// Creates a new set that contains all members of both given sets. +/// +/// This function runs in loglinear time. +/// +/// ## Examples +/// +/// ```gleam +/// 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 #(larger, smaller) = order(first, second) + fold(over: smaller, from: larger, with: insert) +} + +fn order(first: Set(member), second: Set(member)) -> #(Set(member), Set(member)) { + case dict.size(first.dict) > dict.size(second.dict) { + True -> #(first, second) + False -> #(second, first) + } +} + +/// Creates a new set that contains members that are present in both given sets. +/// +/// This function runs in loglinear time. +/// +/// ## Examples +/// +/// ```gleam +/// 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 #(larger, smaller) = order(first, second) + take(from: larger, keeping: to_list(smaller)) +} + +/// Creates a new set that contains members that are present in the first set +/// but not the second. +/// +/// ## Examples +/// +/// ```gleam +/// difference(from_list([1, 2]), from_list([2, 3, 4])) |> to_list +/// // -> [1] +/// ``` +/// +pub fn difference( + from first: Set(member), + minus second: Set(member), +) -> Set(member) { + drop(from: first, drop: to_list(second)) +} + +/// Determines if a set is fully contained by another. +/// +/// ## Examples +/// +/// ```gleam +/// is_subset(from_list([1]), from_list([1, 2])) +/// // -> True +/// ``` +/// +/// ```gleam +/// is_subset(from_list([1, 2, 3]), from_list([3, 4, 5])) +/// // -> False +/// ``` +/// +pub fn is_subset(first: Set(member), of second: Set(member)) -> Bool { + intersection(of: first, and: second) == first +} + +/// Determines if two sets contain no common members +/// +/// ## Examples +/// +/// ```gleam +/// is_disjoint(from_list([1, 2, 3]), from_list([4, 5, 6])) +/// // -> True +/// ``` +/// +/// ```gleam +/// is_disjoint(from_list([1, 2, 3]), from_list([3, 4, 5])) +/// // -> False +/// ``` +/// +pub fn is_disjoint(first: Set(member), from second: Set(member)) -> Bool { + intersection(of: first, and: second) == new() +} + +/// Creates a new set that contains members that are present in either set, but +/// not both. +/// +/// ```gleam +/// symmetric_difference(from_list([1, 2, 3]), from_list([3, 4])) |> to_list +/// // -> [1, 2, 4] +/// ``` +/// +pub fn symmetric_difference( + of first: Set(member), + and second: Set(member), +) -> Set(member) { + difference( + from: union(of: first, and: second), + minus: intersection(of: first, and: second), + ) +} + +/// Calls a function for each member in a set, discarding the return +/// value. +/// +/// Useful for producing a side effect for every item of a set. +/// +/// ```gleam +/// let set = from_list(["apple", "banana", "cherry"]) +/// +/// each(set, io.println) +/// // -> Nil +/// // apple +/// // banana +/// // cherry +/// ``` +/// +/// The order of elements in the iteration is an implementation detail that +/// should not be relied upon. +/// +pub fn each(set: Set(member), fun: fn(member) -> a) -> Nil { + fold(set, Nil, fn(nil, member) { + fun(member) + nil + }) +} diff --git a/build/packages/gleam_stdlib/src/gleam/string.gleam b/build/packages/gleam_stdlib/src/gleam/string.gleam new file mode 100644 index 0000000..c5945b5 --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam/string.gleam @@ -0,0 +1,900 @@ +//// Strings in Gleam are UTF-8 binaries. They can be written in your code as +//// text surrounded by `"double quotes"`. + +import gleam/list +import gleam/option.{type Option, None, Some} +import gleam/order +import gleam/string_tree.{type StringTree} + +/// Determines if a `String` is empty. +/// +/// ## Examples +/// +/// ```gleam +/// is_empty("") +/// // -> True +/// ``` +/// +/// ```gleam +/// is_empty("the world") +/// // -> False +/// ``` +/// +pub fn is_empty(str: String) -> Bool { + str == "" +} + +/// Gets 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. Avoid using this in a loop. +/// +/// ## Examples +/// +/// ```gleam +/// length("Gleam") +/// // -> 5 +/// ``` +/// +/// ```gleam +/// length("ß↑e̊") +/// // -> 3 +/// ``` +/// +/// ```gleam +/// length("") +/// // -> 0 +/// ``` +/// +@external(erlang, "string", "length") +@external(javascript, "../gleam_stdlib.mjs", "string_length") +pub fn length(string: String) -> Int + +/// Reverses a `String`. +/// +/// This function has to iterate across the whole `String` so it runs in linear +/// time. Avoid using this in a loop. +/// +/// ## Examples +/// +/// ```gleam +/// reverse("stressed") +/// // -> "desserts" +/// ``` +/// +pub fn reverse(string: String) -> String { + string + |> string_tree.from_string + |> string_tree.reverse + |> string_tree.to_string +} + +/// Creates a new `String` by replacing all occurrences of a given substring. +/// +/// ## Examples +/// +/// ```gleam +/// replace("www.example.com", each: ".", with: "-") +/// // -> "www-example-com" +/// ``` +/// +/// ```gleam +/// 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_tree.from_string + |> string_tree.replace(each: pattern, with: substitute) + |> string_tree.to_string +} + +/// Creates a new `String` with all the graphemes in the input `String` converted to +/// lowercase. +/// +/// Useful for case-insensitive comparisons. +/// +/// ## Examples +/// +/// ```gleam +/// lowercase("X-FILES") +/// // -> "x-files" +/// ``` +/// +@external(erlang, "string", "lowercase") +@external(javascript, "../gleam_stdlib.mjs", "lowercase") +pub fn lowercase(string: String) -> String + +/// Creates a new `String` with all the graphemes in the input `String` converted to +/// uppercase. +/// +/// Useful for case-insensitive comparisons and VIRTUAL YELLING. +/// +/// ## Examples +/// +/// ```gleam +/// uppercase("skinner") +/// // -> "SKINNER" +/// ``` +/// +@external(erlang, "string", "uppercase") +@external(javascript, "../gleam_stdlib.mjs", "uppercase") +pub fn uppercase(string: String) -> String + +/// Compares two `String`s to see which is "larger" by comparing their graphemes. +/// +/// This does not compare the size or length of the given `String`s. +/// +/// ## Examples +/// +/// ```gleam +/// compare("Anthony", "Anthony") +/// // -> order.Eq +/// ``` +/// +/// ```gleam +/// compare("A", "B") +/// // -> order.Lt +/// ``` +/// +pub fn compare(a: String, b: String) -> order.Order { + case a == b { + True -> order.Eq + _ -> + case less_than(a, b) { + True -> order.Lt + False -> order.Gt + } + } +} + +@external(erlang, "gleam_stdlib", "less_than") +@external(javascript, "../gleam_stdlib.mjs", "less_than") +fn less_than(a: String, b: String) -> Bool + +/// Takes a substring given a start grapheme index and a length. Negative indexes +/// are taken starting from the *end* of the list. +/// +/// This function runs in linear time with the size of the index and the +/// length. Negative indexes are linear with the size of the input string in +/// addition to the other costs. +/// +/// ## Examples +/// +/// ```gleam +/// slice(from: "gleam", at_index: 1, length: 2) +/// // -> "le" +/// ``` +/// +/// ```gleam +/// slice(from: "gleam", at_index: 1, length: 10) +/// // -> "leam" +/// ``` +/// +/// ```gleam +/// slice(from: "gleam", at_index: 10, length: 3) +/// // -> "" +/// ``` +/// +/// ```gleam +/// slice(from: "gleam", at_index: -2, length: 2) +/// // -> "am" +/// ``` +/// +/// ```gleam +/// 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 -> grapheme_slice(string, translated_idx, len) + } + } + False -> grapheme_slice(string, idx, len) + } + } +} + +@external(erlang, "gleam_stdlib", "slice") +@external(javascript, "../gleam_stdlib.mjs", "string_grapheme_slice") +fn grapheme_slice(string: String, index: Int, length: Int) -> String + +@external(erlang, "binary", "part") +@external(javascript, "../gleam_stdlib.mjs", "string_byte_slice") +fn unsafe_byte_slice(string: String, index: Int, length: Int) -> String + +/// Drops contents of the first `String` that occur before the second `String`. +/// If the `from` string does not contain the `before` string, `from` is +/// returned unchanged. +/// +/// ## Examples +/// +/// ```gleam +/// crop(from: "The Lone Gunmen", before: "Lone") +/// // -> "Lone Gunmen" +/// ``` +/// +@external(erlang, "gleam_stdlib", "crop_string") +@external(javascript, "../gleam_stdlib.mjs", "crop_string") +pub fn crop(from string: String, before substring: String) -> String + +/// Drops *n* graphemes from the start of a `String`. +/// +/// This function runs in linear time with the number of graphemes to drop. +/// +/// ## Examples +/// +/// ```gleam +/// drop_start(from: "The Lone Gunmen", up_to: 2) +/// // -> "e Lone Gunmen" +/// ``` +/// +pub fn drop_start(from string: String, up_to num_graphemes: Int) -> String { + case num_graphemes <= 0 { + True -> string + False -> { + let prefix = grapheme_slice(string, 0, num_graphemes) + let prefix_size = byte_size(prefix) + unsafe_byte_slice(string, prefix_size, byte_size(string) - prefix_size) + } + } +} + +/// Drops *n* graphemes from the end of a `String`. +/// +/// This function traverses the full string, so it runs in linear time with the +/// size of the string. Avoid using this in a loop. +/// +/// ## Examples +/// +/// ```gleam +/// drop_end(from: "Cigarette Smoking Man", up_to: 2) +/// // -> "Cigarette Smoking M" +/// ``` +/// +pub fn drop_end(from string: String, up_to num_graphemes: Int) -> String { + case num_graphemes <= 0 { + True -> string + False -> slice(string, 0, length(string) - num_graphemes) + } +} + +/// Checks if the first `String` contains the second. +/// +/// ## Examples +/// +/// ```gleam +/// contains(does: "theory", contain: "ory") +/// // -> True +/// ``` +/// +/// ```gleam +/// contains(does: "theory", contain: "the") +/// // -> True +/// ``` +/// +/// ```gleam +/// contains(does: "theory", contain: "THE") +/// // -> False +/// ``` +/// +@external(erlang, "gleam_stdlib", "contains_string") +@external(javascript, "../gleam_stdlib.mjs", "contains_string") +pub fn contains(does haystack: String, contain needle: String) -> Bool + +/// Checks whether the first `String` starts with the second one. +/// +/// ## Examples +/// +/// ```gleam +/// starts_with("theory", "ory") +/// // -> False +/// ``` +/// +@external(erlang, "gleam_stdlib", "string_starts_with") +@external(javascript, "../gleam_stdlib.mjs", "starts_with") +pub fn starts_with(string: String, prefix: String) -> Bool + +/// Checks whether the first `String` ends with the second one. +/// +/// ## Examples +/// +/// ```gleam +/// ends_with("theory", "ory") +/// // -> True +/// ``` +/// +@external(erlang, "gleam_stdlib", "string_ends_with") +@external(javascript, "../gleam_stdlib.mjs", "ends_with") +pub fn ends_with(string: String, suffix: String) -> Bool + +/// Creates a list of `String`s by splitting a given string on a given substring. +/// +/// ## Examples +/// +/// ```gleam +/// split("home/gleam/desktop/", on: "/") +/// // -> ["home", "gleam", "desktop", ""] +/// ``` +/// +pub fn split(x: String, on substring: String) -> List(String) { + case substring { + "" -> to_graphemes(x) + _ -> + x + |> string_tree.from_string + |> string_tree.split(on: substring) + |> list.map(with: string_tree.to_string) + } +} + +/// Splits a `String` a single time on the given substring. +/// +/// Returns an `Error` if substring not present. +/// +/// ## Examples +/// +/// ```gleam +/// split_once("home/gleam/desktop/", on: "/") +/// // -> Ok(#("home", "gleam/desktop/")) +/// ``` +/// +/// ```gleam +/// split_once("home/gleam/desktop/", on: "?") +/// // -> Error(Nil) +/// ``` +/// +@external(javascript, "../gleam_stdlib.mjs", "split_once") +pub fn split_once( + string: String, + on substring: String, +) -> Result(#(String, String), Nil) { + case erl_split(string, substring) { + [first, rest] -> Ok(#(first, rest)) + _ -> Error(Nil) + } +} + +@external(erlang, "string", "split") +fn erl_split(a: String, b: String) -> List(String) + +/// Creates a new `String` by joining two `String`s together. +/// +/// This function typically copies both `String`s and runs in linear time, but +/// the exact behaviour will depend on how the runtime you are using optimises +/// your code. Benchmark and profile your code if you need to understand its +/// performance better. +/// +/// If you are joining together large string and want to avoid copying any data +/// you may want to investigate using the [`string_tree`](../gleam/string_tree.html) +/// module. +/// +/// ## Examples +/// +/// ```gleam +/// append(to: "butter", suffix: "fly") +/// // -> "butterfly" +/// ``` +/// +pub fn append(to first: String, suffix second: String) -> String { + first <> second +} + +/// Creates a new `String` by joining many `String`s together. +/// +/// This function copies all the `String`s and runs in linear time. +/// +/// ## Examples +/// +/// ```gleam +/// concat(["never", "the", "less"]) +/// // -> "nevertheless" +/// ``` +/// +@external(erlang, "erlang", "list_to_binary") +pub fn concat(strings: List(String)) -> String { + concat_loop(strings, "") +} + +fn concat_loop(strings: List(String), accumulator: String) -> String { + case strings { + [string, ..strings] -> concat_loop(strings, accumulator <> string) + [] -> accumulator + } +} + +/// Creates a new `String` by repeating a `String` a given number of times. +/// +/// This function runs in loglinear time. +/// +/// ## Examples +/// +/// ```gleam +/// repeat("ha", times: 3) +/// // -> "hahaha" +/// ``` +/// +pub fn repeat(string: String, times times: Int) -> String { + case times <= 0 { + True -> "" + False -> repeat_loop(times, string, "") + } +} + +fn repeat_loop(times: Int, doubling_acc: String, acc: String) -> String { + let acc = case times % 2 { + 0 -> acc + _ -> acc <> doubling_acc + } + let times = times / 2 + case times <= 0 { + True -> acc + False -> repeat_loop(times, doubling_acc <> doubling_acc, acc) + } +} + +/// Joins many `String`s together with a given separator. +/// +/// This function runs in linear time. +/// +/// ## Examples +/// +/// ```gleam +/// join(["home","evan","Desktop"], with: "/") +/// // -> "home/evan/Desktop" +/// ``` +/// +pub fn join(strings: List(String), with separator: String) -> String { + case strings { + [] -> "" + [first, ..rest] -> join_loop(rest, separator, first) + } +} + +fn join_loop( + strings: List(String), + separator: String, + accumulator: String, +) -> String { + case strings { + [] -> accumulator + [string, ..strings] -> + join_loop(strings, separator, accumulator <> separator <> string) + } +} + +/// Pads the start of a `String` until it has a given length. +/// +/// ## Examples +/// +/// ```gleam +/// pad_start("121", to: 5, with: ".") +/// // -> "..121" +/// ``` +/// +/// ```gleam +/// pad_start("121", to: 3, with: ".") +/// // -> "121" +/// ``` +/// +/// ```gleam +/// pad_start("121", to: 2, with: ".") +/// // -> "121" +/// ``` +/// +pub fn pad_start( + string: String, + to desired_length: Int, + with pad_string: String, +) -> String { + let current_length = length(string) + let to_pad_length = desired_length - current_length + + case to_pad_length <= 0 { + True -> string + False -> padding(to_pad_length, pad_string) <> string + } +} + +/// Pads the end of a `String` until it has a given length. +/// +/// ## Examples +/// +/// ```gleam +/// pad_end("123", to: 5, with: ".") +/// // -> "123.." +/// ``` +/// +/// ```gleam +/// pad_end("123", to: 3, with: ".") +/// // -> "123" +/// ``` +/// +/// ```gleam +/// pad_end("123", to: 2, with: ".") +/// // -> "123" +/// ``` +/// +pub fn pad_end( + string: String, + to desired_length: Int, + with pad_string: String, +) -> String { + let current_length = length(string) + let to_pad_length = desired_length - current_length + + case to_pad_length <= 0 { + True -> string + False -> string <> padding(to_pad_length, pad_string) + } +} + +fn padding(size: Int, pad_string: String) -> String { + let pad_string_length = length(pad_string) + let num_pads = size / pad_string_length + let extra = size % pad_string_length + + repeat(pad_string, num_pads) <> slice(pad_string, 0, extra) +} + +/// Removes whitespace on both sides of a `String`. +/// +/// Whitespace in this function is the set of nonbreakable whitespace +/// codepoints, defined as Pattern_White_Space in [Unicode Standard Annex #31][1]. +/// +/// [1]: https://unicode.org/reports/tr31/ +/// +/// ## Examples +/// +/// ```gleam +/// trim(" hats \n") +/// // -> "hats" +/// ``` +/// +pub fn trim(string: String) -> String { + string |> trim_start |> trim_end +} + +@external(erlang, "string", "trim") +fn erl_trim(a: String, b: Direction) -> String + +type Direction { + Leading + Trailing +} + +/// Removes whitespace at the start of a `String`. +/// +/// ## Examples +/// +/// ```gleam +/// trim_start(" hats \n") +/// // -> "hats \n" +/// ``` +/// +@external(javascript, "../gleam_stdlib.mjs", "trim_start") +pub fn trim_start(string: String) -> String { + erl_trim(string, Leading) +} + +/// Removes whitespace at the end of a `String`. +/// +/// ## Examples +/// +/// ```gleam +/// trim_end(" hats \n") +/// // -> " hats" +/// ``` +/// +@external(javascript, "../gleam_stdlib.mjs", "trim_end") +pub fn trim_end(string: String) -> String { + erl_trim(string, Trailing) +} + +/// Splits a non-empty `String` into its first element (head) and rest (tail). +/// This lets you pattern match on `String`s exactly as you would with lists. +/// +/// ## Performance +/// +/// There is a notable overhead to using this function, so you may not want to +/// use it in a tight loop. If you wish to efficiently parse a string you may +/// want to use alternatives such as the [splitter package](https://hex.pm/packages/splitter). +/// +/// ## Examples +/// +/// ```gleam +/// pop_grapheme("gleam") +/// // -> Ok(#("g", "leam")) +/// ``` +/// +/// ```gleam +/// pop_grapheme("") +/// // -> Error(Nil) +/// ``` +/// +@external(erlang, "gleam_stdlib", "string_pop_grapheme") +@external(javascript, "../gleam_stdlib.mjs", "pop_grapheme") +pub fn pop_grapheme(string: String) -> Result(#(String, String), Nil) + +/// Converts a `String` to a list of +/// [graphemes](https://en.wikipedia.org/wiki/Grapheme). +/// +/// ```gleam +/// to_graphemes("abc") +/// // -> ["a", "b", "c"] +/// ``` +/// +@external(javascript, "../gleam_stdlib.mjs", "graphemes") +pub fn to_graphemes(string: String) -> List(String) { + string + |> to_graphemes_loop([]) + |> list.reverse +} + +fn to_graphemes_loop(string: String, acc: List(String)) -> List(String) { + case pop_grapheme(string) { + Ok(#(grapheme, rest)) -> to_graphemes_loop(rest, [grapheme, ..acc]) + Error(_) -> acc + } +} + +@external(erlang, "gleam_stdlib", "identity") +@external(javascript, "../gleam_stdlib.mjs", "codepoint") +fn unsafe_int_to_utf_codepoint(a: Int) -> UtfCodepoint + +/// Converts a `String` to a `List` of `UtfCodepoint`. +/// +/// See and +/// for an +/// explanation on code points. +/// +/// ## Examples +/// +/// ```gleam +/// "a" |> to_utf_codepoints +/// // -> [UtfCodepoint(97)] +/// ``` +/// +/// ```gleam +/// // Semantically the same as: +/// // ["🏳", "️", "‍", "🌈"] or: +/// // [waving_white_flag, variant_selector_16, zero_width_joiner, rainbow] +/// "🏳️‍🌈" |> to_utf_codepoints +/// // -> [ +/// // UtfCodepoint(127987), +/// // UtfCodepoint(65039), +/// // UtfCodepoint(8205), +/// // UtfCodepoint(127752), +/// // ] +/// ``` +/// +pub fn to_utf_codepoints(string: String) -> List(UtfCodepoint) { + do_to_utf_codepoints(string) +} + +@target(erlang) +fn do_to_utf_codepoints(string: String) -> List(UtfCodepoint) { + to_utf_codepoints_loop(<>, []) +} + +@target(erlang) +fn to_utf_codepoints_loop( + bit_array: BitArray, + acc: List(UtfCodepoint), +) -> List(UtfCodepoint) { + case bit_array { + <> -> + to_utf_codepoints_loop(rest, [first, ..acc]) + _ -> list.reverse(acc) + } +} + +@target(javascript) +fn do_to_utf_codepoints(string: String) -> List(UtfCodepoint) { + string + |> string_to_codepoint_integer_list + |> list.map(unsafe_int_to_utf_codepoint) +} + +@target(javascript) +@external(javascript, "../gleam_stdlib.mjs", "string_to_codepoint_integer_list") +fn string_to_codepoint_integer_list(string: String) -> List(Int) + +/// Converts a `List` of `UtfCodepoint`s to a `String`. +/// +/// See and +/// for an +/// explanation on code points. +/// +/// ## Examples +/// +/// ```gleam +/// let assert Ok(a) = utf_codepoint(97) +/// let assert Ok(b) = utf_codepoint(98) +/// let assert Ok(c) = utf_codepoint(99) +/// from_utf_codepoints([a, b, c]) +/// // -> "abc" +/// ``` +/// +@external(erlang, "gleam_stdlib", "utf_codepoint_list_to_string") +@external(javascript, "../gleam_stdlib.mjs", "utf_codepoint_list_to_string") +pub fn from_utf_codepoints(utf_codepoints: List(UtfCodepoint)) -> String + +/// Converts 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 > 1_114_111 -> Error(Nil) + i if i >= 55_296 && i <= 57_343 -> Error(Nil) + i if i < 0 -> Error(Nil) + i -> Ok(unsafe_int_to_utf_codepoint(i)) + } +} + +/// Converts an UtfCodepoint to its ordinal code point value. +/// +/// ## Examples +/// +/// ```gleam +/// let assert [utf_codepoint, ..] = to_utf_codepoints("💜") +/// utf_codepoint_to_int(utf_codepoint) +/// // -> 128156 +/// ``` +/// +@external(erlang, "gleam_stdlib", "identity") +@external(javascript, "../gleam_stdlib.mjs", "utf_codepoint_to_int") +pub fn utf_codepoint_to_int(cp: UtfCodepoint) -> Int + +/// Converts a `String` into `Option(String)` where an empty `String` becomes +/// `None`. +/// +/// ## Examples +/// +/// ```gleam +/// to_option("") +/// // -> None +/// ``` +/// +/// ```gleam +/// to_option("hats") +/// // -> Some("hats") +/// ``` +/// +pub fn to_option(string: String) -> Option(String) { + case string { + "" -> None + _ -> Some(string) + } +} + +/// Returns the first grapheme cluster in a given `String` and wraps it in a +/// `Result(String, Nil)`. If the `String` is empty, it returns `Error(Nil)`. +/// Otherwise, it returns `Ok(String)`. +/// +/// ## Examples +/// +/// ```gleam +/// first("") +/// // -> Error(Nil) +/// ``` +/// +/// ```gleam +/// first("icecream") +/// // -> Ok("i") +/// ``` +/// +pub fn first(string: String) -> Result(String, Nil) { + case pop_grapheme(string) { + Ok(#(first, _)) -> Ok(first) + Error(e) -> Error(e) + } +} + +/// Returns the last grapheme cluster in a given `String` and wraps it in a +/// `Result(String, Nil)`. If the `String` is empty, it returns `Error(Nil)`. +/// Otherwise, it returns `Ok(String)`. +/// +/// This function traverses the full string, so it runs in linear time with the +/// length of the string. Avoid using this in a loop. +/// +/// ## Examples +/// +/// ```gleam +/// last("") +/// // -> Error(Nil) +/// ``` +/// +/// ```gleam +/// last("icecream") +/// // -> Ok("m") +/// ``` +/// +pub fn last(string: String) -> Result(String, Nil) { + case pop_grapheme(string) { + Ok(#(first, "")) -> Ok(first) + Ok(#(_, rest)) -> Ok(slice(rest, -1, 1)) + Error(e) -> Error(e) + } +} + +/// Creates a new `String` with the first grapheme in the input `String` +/// converted to uppercase and the remaining graphemes to lowercase. +/// +/// ## Examples +/// +/// ```gleam +/// capitalise("mamouna") +/// // -> "Mamouna" +/// ``` +/// +pub fn capitalise(string: String) -> String { + case pop_grapheme(string) { + Ok(#(first, rest)) -> append(to: uppercase(first), suffix: lowercase(rest)) + Error(_) -> "" + } +} + +/// Returns a `String` representation of a term in Gleam syntax. +/// +/// This may be occasionally useful for quick-and-dirty printing of values in +/// scripts. For error reporting and other uses prefer constructing strings by +/// pattern matching on the values. +/// +/// ## Limitations +/// +/// The output format of this function is not stable and could change at any +/// time. The output is not suitable for parsing. +/// +/// This function works using runtime reflection, so the output may not be +/// perfectly accurate for data structures where the runtime structure doesn't +/// hold enough information to determine the original syntax. For example, +/// tuples with an Erlang atom in the first position will be mistaken for Gleam +/// records. +/// +/// ## Security and safety +/// +/// There is no limit to how large the strings that this function can produce. +/// Be careful not to call this function with large data structures or you +/// could use very large amounts of memory, potentially causing runtime +/// problems. +/// +pub fn inspect(term: anything) -> String { + term + |> do_inspect + |> string_tree.to_string +} + +@external(erlang, "gleam_stdlib", "inspect") +@external(javascript, "../gleam_stdlib.mjs", "inspect") +fn do_inspect(term: anything) -> StringTree + +/// Returns the number of bytes in a `String`. +/// +/// This function runs in constant time on Erlang and in linear time on +/// JavaScript. +/// +/// ## Examples +/// +/// ```gleam +/// byte_size("🏳️‍⚧️🏳️‍🌈👩🏾‍❤️‍👨🏻") +/// // -> 58 +/// ``` +/// +@external(erlang, "erlang", "byte_size") +@external(javascript, "../gleam_stdlib.mjs", "byte_size") +pub fn byte_size(string: String) -> Int diff --git a/build/packages/gleam_stdlib/src/gleam/string_tree.gleam b/build/packages/gleam_stdlib/src/gleam/string_tree.gleam new file mode 100644 index 0000000..22937e2 --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam/string_tree.gleam @@ -0,0 +1,208 @@ +import gleam/list + +/// `StringTree` is a type used for efficiently building text content to be +/// written to a file or a socket. Internally it is represented as tree so to +/// append or prepend to a string tree is a constant time operation that +/// allocates a new node in the tree without copying any of the content. When +/// writing to an output stream the tree is traversed and the content is sent +/// directly rather than copying it into a single buffer beforehand. +/// +/// On Erlang this type is compatible with Erlang's iodata. On JavaScript this +/// type is compatible with normal strings. +/// +/// The BEAM virtual machine has an optimisation for appending strings, where it +/// will mutate the string buffer when safe to do so, so if you are looking to +/// build a string through appending many small strings then you may get better +/// performance by not using a string tree. Always benchmark your performance +/// sensitive code. +/// +pub type StringTree + +/// Create an empty `StringTree`. Useful as the start of a pipe chaining many +/// trees together. +/// +pub fn new() -> StringTree { + from_strings([]) +} + +/// Prepends a `String` onto the start of some `StringTree`. +/// +/// Runs in constant time. +/// +pub fn prepend(to tree: StringTree, prefix prefix: String) -> StringTree { + append_tree(from_string(prefix), tree) +} + +/// Appends a `String` onto the end of some `StringTree`. +/// +/// Runs in constant time. +/// +pub fn append(to tree: StringTree, suffix second: String) -> StringTree { + append_tree(tree, from_string(second)) +} + +/// Prepends some `StringTree` onto the start of another. +/// +/// Runs in constant time. +/// +pub fn prepend_tree( + to tree: StringTree, + prefix prefix: StringTree, +) -> StringTree { + append_tree(prefix, tree) +} + +/// Appends some `StringTree` onto the end of another. +/// +/// Runs in constant time. +/// +@external(erlang, "gleam_stdlib", "iodata_append") +@external(javascript, "../gleam_stdlib.mjs", "add") +pub fn append_tree(to tree: StringTree, suffix suffix: StringTree) -> StringTree + +/// Converts a list of strings into a `StringTree`. +/// +/// Runs in constant time. +/// +@external(erlang, "gleam_stdlib", "identity") +@external(javascript, "../gleam_stdlib.mjs", "concat") +pub fn from_strings(strings: List(String)) -> StringTree + +/// Joins a list of trees into a single tree. +/// +/// Runs in constant time. +/// +@external(erlang, "gleam_stdlib", "identity") +@external(javascript, "../gleam_stdlib.mjs", "concat") +pub fn concat(trees: List(StringTree)) -> StringTree + +/// Converts a string into a `StringTree`. +/// +/// Runs in constant time. +/// +@external(erlang, "gleam_stdlib", "identity") +@external(javascript, "../gleam_stdlib.mjs", "identity") +pub fn from_string(string: String) -> StringTree + +/// Turns a `StringTree` into a `String` +/// +/// This function is implemented natively by the virtual machine and is highly +/// optimised. +/// +@external(erlang, "unicode", "characters_to_binary") +@external(javascript, "../gleam_stdlib.mjs", "identity") +pub fn to_string(tree: StringTree) -> String + +/// Returns the size of the `StringTree` in bytes. +/// +@external(erlang, "erlang", "iolist_size") +@external(javascript, "../gleam_stdlib.mjs", "length") +pub fn byte_size(tree: StringTree) -> Int + +/// Joins the given trees into a new tree separated with the given string. +/// +pub fn join(trees: List(StringTree), with sep: String) -> StringTree { + trees + |> list.intersperse(from_string(sep)) + |> concat +} + +/// Converts a `StringTree` to a new one where the contents have been +/// lowercased. +/// +@external(erlang, "string", "lowercase") +@external(javascript, "../gleam_stdlib.mjs", "lowercase") +pub fn lowercase(tree: StringTree) -> StringTree + +/// Converts a `StringTree` to a new one where the contents have been +/// uppercased. +/// +@external(erlang, "string", "uppercase") +@external(javascript, "../gleam_stdlib.mjs", "uppercase") +pub fn uppercase(tree: StringTree) -> StringTree + +/// Converts a `StringTree` to a new one with the contents reversed. +/// +@external(erlang, "string", "reverse") +pub fn reverse(tree: StringTree) -> StringTree { + tree + |> to_string + |> do_to_graphemes + |> list.reverse + |> from_strings +} + +@external(javascript, "../gleam_stdlib.mjs", "graphemes") +fn do_to_graphemes(string: String) -> List(String) + +type Direction { + All +} + +/// Splits a `StringTree` on a given pattern into a list of trees. +/// +@external(javascript, "../gleam_stdlib.mjs", "split") +pub fn split(tree: StringTree, on pattern: String) -> List(StringTree) { + erl_split(tree, pattern, All) +} + +@external(erlang, "string", "split") +fn erl_split(a: StringTree, b: String, c: Direction) -> List(StringTree) + +/// Replaces all instances of a pattern with a given string substitute. +/// +@external(erlang, "gleam_stdlib", "string_replace") +@external(javascript, "../gleam_stdlib.mjs", "string_replace") +pub fn replace( + in tree: StringTree, + each pattern: String, + with substitute: String, +) -> StringTree + +/// Compares two string trees to determine if they have the same textual +/// content. +/// +/// Comparing two string trees 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 +/// +/// ```gleam +/// from_strings(["a", "b"]) == from_string("ab") +/// // -> False +/// ``` +/// +/// ```gleam +/// is_equal(from_strings(["a", "b"]), from_string("ab")) +/// // -> True +/// ``` +/// +@external(erlang, "string", "equal") +pub fn is_equal(a: StringTree, b: StringTree) -> Bool { + a == b +} + +/// Inspects a `StringTree` to determine if it is equivalent to an empty string. +/// +/// ## Examples +/// +/// ```gleam +/// from_string("ok") |> is_empty +/// // -> False +/// ``` +/// +/// ```gleam +/// from_string("") |> is_empty +/// // -> True +/// ``` +/// +/// ```gleam +/// from_strings([]) |> is_empty +/// // -> True +/// ``` +/// +@external(erlang, "string", "is_empty") +pub fn is_empty(tree: StringTree) -> Bool { + from_string("") == tree +} diff --git a/build/packages/gleam_stdlib/src/gleam/uri.gleam b/build/packages/gleam_stdlib/src/gleam/uri.gleam new file mode 100644 index 0000000..9413b99 --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam/uri.gleam @@ -0,0 +1,770 @@ +//// 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/int +import gleam/list +import gleam/option.{type Option, None, Some} +import gleam/string +import gleam/string_tree.{type StringTree} + +/// 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), + ) +} + +/// Constant representing an empty URI, equivalent to "". +/// +/// ## Examples +/// +/// ```gleam +/// let uri = Uri(..empty, scheme: Some("https"), host: Some("example.com")) +/// // -> Uri( +/// // scheme: Some("https"), +/// // userinfo: None, +/// // host: Some("example.com"), +/// // port: None, +/// // path: "", +/// // query: None, +/// // fragment: None, +/// // ) +/// ``` +/// +pub const empty = Uri( + scheme: None, + userinfo: None, + host: None, + port: None, + path: "", + query: None, + fragment: None, +) + +/// 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`. +/// +/// ## Examples +/// +/// ```gleam +/// parse("https://example.com:1234/a/b?query=true#fragment") +/// // -> Ok( +/// // Uri( +/// // scheme: Some("https"), +/// // userinfo: None, +/// // host: Some("example.com"), +/// // port: Some(1234), +/// // path: "/a/b", +/// // query: Some("query=true"), +/// // fragment: Some("fragment") +/// // ) +/// // ) +/// ``` +/// +@external(erlang, "gleam_stdlib", "uri_parse") +pub fn parse(uri_string: String) -> Result(Uri, Nil) { + // This parses a uri_string following the regex defined in + // https://tools.ietf.org/html/rfc3986#appendix-B + // + // TODO: This is not perfect and will be more permissive than its Erlang + // counterpart, ideally we want to replicate Erlang's implementation on the js + // target as well. + parse_scheme_loop(uri_string, uri_string, empty, 0) +} + +fn parse_scheme_loop( + original: String, + uri_string: String, + pieces: Uri, + size: Int, +) -> Result(Uri, Nil) { + case uri_string { + // `/` is not allowed to appear in a scheme so we know it's over and we can + // start parsing the authority with slashes. + "/" <> _ if size == 0 -> parse_authority_with_slashes(uri_string, pieces) + "/" <> _ -> { + let scheme = codeunit_slice(original, at_index: 0, length: size) + let pieces = Uri(..pieces, scheme: Some(string.lowercase(scheme))) + parse_authority_with_slashes(uri_string, pieces) + } + + // `?` is not allowed to appear in a schemem, in an authority, or in a path; + // so if we see it we know it marks the beginning of the query part. + "?" <> rest if size == 0 -> parse_query_with_question_mark(rest, pieces) + "?" <> rest -> { + let scheme = codeunit_slice(original, at_index: 0, length: size) + let pieces = Uri(..pieces, scheme: Some(string.lowercase(scheme))) + parse_query_with_question_mark(rest, pieces) + } + + // `#` is not allowed to appear in a scheme, in an authority, in a path or + // in a query; so if we see it we know it marks the beginning of the final + // fragment. + "#" <> rest if size == 0 -> parse_fragment(rest, pieces) + "#" <> rest -> { + let scheme = codeunit_slice(original, at_index: 0, length: size) + let pieces = Uri(..pieces, scheme: Some(string.lowercase(scheme))) + parse_fragment(rest, pieces) + } + + // A colon marks the end of a uri scheme, but if it is not preceded by any + // character then it's not a valid URI. + ":" <> _ if size == 0 -> Error(Nil) + ":" <> rest -> { + let scheme = codeunit_slice(original, at_index: 0, length: size) + let pieces = Uri(..pieces, scheme: Some(string.lowercase(scheme))) + parse_authority_with_slashes(rest, pieces) + } + + // If we could get to the end of the string and we've met no special + // chars whatsoever, that means the entire string is just a long path. + "" -> Ok(Uri(..pieces, path: original)) + + // In all other cases the first character is just a valid URI scheme + // character and we just keep munching characters until we reach the end of + // the uri scheme (or the end of the string and that would mean this is not + // a valid uri scheme since we found no `:`). + _ -> { + let #(_, rest) = pop_codeunit(uri_string) + parse_scheme_loop(original, rest, pieces, size + 1) + } + } +} + +fn parse_authority_with_slashes( + uri_string: String, + pieces: Uri, +) -> Result(Uri, Nil) { + case uri_string { + // To be a valid authority the string must start with a `//`, otherwise + // there's no authority and we just skip ahead to parsing the path. + "//" -> Ok(Uri(..pieces, host: Some(""))) + "//" <> rest -> parse_authority_pieces(rest, pieces) + _ -> parse_path(uri_string, pieces) + } +} + +fn parse_authority_pieces(string: String, pieces: Uri) -> Result(Uri, Nil) { + parse_userinfo_loop(string, string, pieces, 0) +} + +fn parse_userinfo_loop( + original: String, + uri_string: String, + pieces: Uri, + size: Int, +) -> Result(Uri, Nil) { + case uri_string { + // `@` marks the end of the userinfo and the start of the host part in the + // authority string. + "@" <> rest if size == 0 -> parse_host(rest, pieces) + "@" <> rest -> { + let userinfo = codeunit_slice(original, at_index: 0, length: size) + let pieces = Uri(..pieces, userinfo: Some(userinfo)) + parse_host(rest, pieces) + } + + // If we reach the end of the authority string without finding an `@` + // special character, then we know that the authority doesn't actually + // contain the userinfo part. + // The entire string we just went through was a host! So we parse it as + // such. + "" | "/" <> _ | "?" <> _ | "#" <> _ -> parse_host(original, pieces) + + // In all other cases we just keep munching characters increasing the size + // of the userinfo bit. + _ -> { + let #(_, rest) = pop_codeunit(uri_string) + parse_userinfo_loop(original, rest, pieces, size + 1) + } + } +} + +fn parse_host(uri_string: String, pieces: Uri) -> Result(Uri, Nil) { + // A host string can be in two formats: + // - \[[:.a-zA-Z0-9]*\] + // - [^:] + case uri_string { + // If we find an opening bracket we know it's the first format. + "[" <> _ -> parse_host_within_brackets(uri_string, pieces) + + // A `:` marks the beginning of the port part of the authority string. + ":" <> _ -> { + let pieces = Uri(..pieces, host: Some("")) + parse_port(uri_string, pieces) + } + + // If the string is empty then there's no need to keep going. The host is + // empty. + "" -> Ok(Uri(..pieces, host: Some(""))) + + // Otherwise it's the second format + _ -> parse_host_outside_of_brackets(uri_string, pieces) + } +} + +fn parse_host_within_brackets( + uri_string: String, + pieces: Uri, +) -> Result(Uri, Nil) { + parse_host_within_brackets_loop(uri_string, uri_string, pieces, 0) +} + +fn parse_host_within_brackets_loop( + original: String, + uri_string: String, + pieces: Uri, + size: Int, +) -> Result(Uri, Nil) { + case uri_string { + // If the string is over the entire string we were iterating through is the + // host part. + "" -> Ok(Uri(..pieces, host: Some(uri_string))) + + // A `]` marks the end of the host and the start of the port part. + "]" <> rest if size == 0 -> parse_port(rest, pieces) + "]" <> rest -> { + let host = codeunit_slice(original, at_index: 0, length: size + 1) + let pieces = Uri(..pieces, host: Some(host)) + parse_port(rest, pieces) + } + + // `/` marks the beginning of a path. + "/" <> _ if size == 0 -> parse_path(uri_string, pieces) + "/" <> _ -> { + let host = codeunit_slice(original, at_index: 0, length: size) + let pieces = Uri(..pieces, host: Some(host)) + parse_path(uri_string, pieces) + } + + // `?` marks the beginning of the query with question mark. + "?" <> rest if size == 0 -> parse_query_with_question_mark(rest, pieces) + "?" <> rest -> { + let host = codeunit_slice(original, at_index: 0, length: size) + let pieces = Uri(..pieces, host: Some(host)) + parse_query_with_question_mark(rest, pieces) + } + + // `#` marks the beginning of the fragment part. + "#" <> rest if size == 0 -> parse_fragment(rest, pieces) + "#" <> rest -> { + let host = codeunit_slice(original, at_index: 0, length: size) + let pieces = Uri(..pieces, host: Some(host)) + parse_fragment(rest, pieces) + } + + // In all other cases we just keep iterating. + _ -> { + let #(char, rest) = pop_codeunit(uri_string) + // Inside `[...]` there can only be some characters, if we find a special + // one then we know that we're actually parsing the other format for the + // host and we switch to that! + case is_valid_host_within_brackets_char(char) { + True -> + parse_host_within_brackets_loop(original, rest, pieces, size + 1) + + False -> + parse_host_outside_of_brackets_loop(original, original, pieces, 0) + } + } + } +} + +fn is_valid_host_within_brackets_char(char: Int) -> Bool { + // [0-9] + { 48 >= char && char <= 57 } + // [A-Z] + || { 65 >= char && char <= 90 } + // [a-z] + || { 97 >= char && char <= 122 } + // : + || char == 58 + // . + || char == 46 +} + +fn parse_host_outside_of_brackets( + uri_string: String, + pieces: Uri, +) -> Result(Uri, Nil) { + parse_host_outside_of_brackets_loop(uri_string, uri_string, pieces, 0) +} + +fn parse_host_outside_of_brackets_loop( + original: String, + uri_string: String, + pieces: Uri, + size: Int, +) -> Result(Uri, Nil) { + case uri_string { + "" -> Ok(Uri(..pieces, host: Some(original))) + + // `:` marks the beginning of the port. + ":" <> _ -> { + let host = codeunit_slice(original, at_index: 0, length: size) + let pieces = Uri(..pieces, host: Some(host)) + parse_port(uri_string, pieces) + } + + // `/` marks the beginning of a path. + "/" <> _ -> { + let host = codeunit_slice(original, at_index: 0, length: size) + let pieces = Uri(..pieces, host: Some(host)) + parse_path(uri_string, pieces) + } + + // `?` marks the beginning of the query with question mark. + "?" <> rest -> { + let host = codeunit_slice(original, at_index: 0, length: size) + let pieces = Uri(..pieces, host: Some(host)) + parse_query_with_question_mark(rest, pieces) + } + + // `#` marks the beginning of the fragment part. + "#" <> rest -> { + let host = codeunit_slice(original, at_index: 0, length: size) + let pieces = Uri(..pieces, host: Some(host)) + parse_fragment(rest, pieces) + } + + _ -> { + let #(_, rest) = pop_codeunit(uri_string) + parse_host_outside_of_brackets_loop(original, rest, pieces, size + 1) + } + } +} + +fn parse_port(uri_string: String, pieces: Uri) -> Result(Uri, Nil) { + case uri_string { + ":0" <> rest -> parse_port_loop(rest, pieces, 0) + ":1" <> rest -> parse_port_loop(rest, pieces, 1) + ":2" <> rest -> parse_port_loop(rest, pieces, 2) + ":3" <> rest -> parse_port_loop(rest, pieces, 3) + ":4" <> rest -> parse_port_loop(rest, pieces, 4) + ":5" <> rest -> parse_port_loop(rest, pieces, 5) + ":6" <> rest -> parse_port_loop(rest, pieces, 6) + ":7" <> rest -> parse_port_loop(rest, pieces, 7) + ":8" <> rest -> parse_port_loop(rest, pieces, 8) + ":9" <> rest -> parse_port_loop(rest, pieces, 9) + + // The port could be empty and be followed by any of the next delimiters. + // Like `:#`, `:?` or `:/` + ":" | "" -> Ok(pieces) + + // `?` marks the beginning of the query with question mark. + "?" <> rest | ":?" <> rest -> parse_query_with_question_mark(rest, pieces) + + // `#` marks the beginning of the fragment part. + "#" <> rest | ":#" <> rest -> parse_fragment(rest, pieces) + + // `/` marks the beginning of a path. + "/" <> _ -> parse_path(uri_string, pieces) + ":" <> rest -> + case rest { + "/" <> _ -> parse_path(rest, pieces) + _ -> Error(Nil) + } + + _ -> Error(Nil) + } +} + +fn parse_port_loop( + uri_string: String, + pieces: Uri, + port: Int, +) -> Result(Uri, Nil) { + case uri_string { + // As long as we find port numbers we keep accumulating those. + "0" <> rest -> parse_port_loop(rest, pieces, port * 10) + "1" <> rest -> parse_port_loop(rest, pieces, port * 10 + 1) + "2" <> rest -> parse_port_loop(rest, pieces, port * 10 + 2) + "3" <> rest -> parse_port_loop(rest, pieces, port * 10 + 3) + "4" <> rest -> parse_port_loop(rest, pieces, port * 10 + 4) + "5" <> rest -> parse_port_loop(rest, pieces, port * 10 + 5) + "6" <> rest -> parse_port_loop(rest, pieces, port * 10 + 6) + "7" <> rest -> parse_port_loop(rest, pieces, port * 10 + 7) + "8" <> rest -> parse_port_loop(rest, pieces, port * 10 + 8) + "9" <> rest -> parse_port_loop(rest, pieces, port * 10 + 9) + + // `?` marks the beginning of the query with question mark. + "?" <> rest -> { + let pieces = Uri(..pieces, port: Some(port)) + parse_query_with_question_mark(rest, pieces) + } + + // `#` marks the beginning of the fragment part. + "#" <> rest -> { + let pieces = Uri(..pieces, port: Some(port)) + parse_fragment(rest, pieces) + } + + // `/` marks the beginning of a path. + "/" <> _ -> { + let pieces = Uri(..pieces, port: Some(port)) + parse_path(uri_string, pieces) + } + + // The string (and so the port) is over, we return what we parsed so far. + "" -> Ok(Uri(..pieces, port: Some(port))) + + // In all other cases we've ran into some invalid character inside the port + // so the uri is invalid! + _ -> Error(Nil) + } +} + +fn parse_path(uri_string: String, pieces: Uri) -> Result(Uri, Nil) { + parse_path_loop(uri_string, uri_string, pieces, 0) +} + +fn parse_path_loop( + original: String, + uri_string: String, + pieces: Uri, + size: Int, +) -> Result(Uri, Nil) { + case uri_string { + // `?` marks the beginning of the query with question mark. + "?" <> rest -> { + let path = codeunit_slice(original, at_index: 0, length: size) + let pieces = Uri(..pieces, path: path) + parse_query_with_question_mark(rest, pieces) + } + + // `#` marks the beginning of the fragment part. + "#" <> rest -> { + let path = codeunit_slice(original, at_index: 0, length: size) + let pieces = Uri(..pieces, path: path) + parse_fragment(rest, pieces) + } + + // If the string is over that means the entirety of the string was the path + // and it has an empty query and fragment. + "" -> Ok(Uri(..pieces, path: original)) + + // In all other cases the character is allowed to be part of the path so we + // just keep munching until we reach to its end. + _ -> { + let #(_, rest) = pop_codeunit(uri_string) + parse_path_loop(original, rest, pieces, size + 1) + } + } +} + +fn parse_query_with_question_mark( + uri_string: String, + pieces: Uri, +) -> Result(Uri, Nil) { + parse_query_with_question_mark_loop(uri_string, uri_string, pieces, 0) +} + +fn parse_query_with_question_mark_loop( + original: String, + uri_string: String, + pieces: Uri, + size: Int, +) -> Result(Uri, Nil) { + case uri_string { + // `#` marks the beginning of the fragment part. + "#" <> rest if size == 0 -> parse_fragment(rest, pieces) + "#" <> rest -> { + let query = codeunit_slice(original, at_index: 0, length: size) + let pieces = Uri(..pieces, query: Some(query)) + parse_fragment(rest, pieces) + } + + // If the string is over that means the entirety of the string was the query + // and it has an empty fragment. + "" -> Ok(Uri(..pieces, query: Some(original))) + + // In all other cases the character is allowed to be part of the query so we + // just keep munching until we reach to its end. + _ -> { + let #(_, rest) = pop_codeunit(uri_string) + parse_query_with_question_mark_loop(original, rest, pieces, size + 1) + } + } +} + +fn parse_fragment(rest: String, pieces: Uri) -> Result(Uri, Nil) { + Ok(Uri(..pieces, fragment: Some(rest))) +} + +// WARN: this function returns invalid strings! +// We need to return a String anyways to have this as the representation on the +// JavaScript target. +// Alternatively, we could rewrite the entire code to use a single +// `fold_codeunits`-style loop and a state machine. +@external(erlang, "gleam_stdlib", "string_pop_codeunit") +@external(javascript, "../gleam_stdlib.mjs", "pop_codeunit") +fn pop_codeunit(str: String) -> #(Int, String) + +@external(erlang, "binary", "part") +@external(javascript, "../gleam_stdlib.mjs", "string_codeunit_slice") +fn codeunit_slice(str: String, at_index from: Int, length length: Int) -> String + +/// 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`. +/// +/// ## Examples +/// +/// ```gleam +/// parse_query("a=1&b=2") +/// // -> Ok([#("a", "1"), #("b", "2")]) +/// ``` +/// +@external(erlang, "gleam_stdlib", "parse_query") +@external(javascript, "../gleam_stdlib.mjs", "parse_query") +pub fn parse_query(query: String) -> Result(List(#(String, String)), Nil) + +/// Encodes a list of key value pairs as a URI query string. +/// +/// The opposite operation is `uri.parse_query`. +/// +/// ## Examples +/// +/// ```gleam +/// query_to_string([#("a", "1"), #("b", "2")]) +/// // -> "a=1&b=2" +/// ``` +/// +pub fn query_to_string(query: List(#(String, String))) -> String { + query + |> list.map(query_pair) + |> list.intersperse(string_tree.from_string("&")) + |> string_tree.concat + |> string_tree.to_string +} + +fn query_pair(pair: #(String, String)) -> StringTree { + string_tree.from_strings([percent_encode(pair.0), "=", percent_encode(pair.1)]) +} + +/// Encodes a string into a percent encoded representation. +/// +/// ## Examples +/// +/// ```gleam +/// percent_encode("100% great") +/// // -> "100%25%20great" +/// ``` +/// +@external(erlang, "gleam_stdlib", "percent_encode") +@external(javascript, "../gleam_stdlib.mjs", "percent_encode") +pub fn percent_encode(value: String) -> String + +/// Decodes a percent encoded string. +/// +/// ## Examples +/// +/// ```gleam +/// percent_decode("100%25%20great+fun") +/// // -> Ok("100% great+fun") +/// ``` +/// +@external(erlang, "gleam_stdlib", "percent_decode") +@external(javascript, "../gleam_stdlib.mjs", "percent_decode") +pub fn percent_decode(value: String) -> Result(String, Nil) + +/// Splits 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. +/// +/// ## Examples +/// +/// ```gleam +/// path_segments("/users/1") +/// // -> ["users" ,"1"] +/// ``` +/// +pub fn path_segments(path: String) -> List(String) { + remove_dot_segments(string.split(path, "/")) +} + +fn remove_dot_segments(input: List(String)) -> List(String) { + remove_dot_segments_loop(input, []) +} + +fn remove_dot_segments_loop( + 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] + } + remove_dot_segments_loop(rest, accumulator) + } + } +} + +/// Encodes a `Uri` value as a URI string. +/// +/// The opposite operation is `uri.parse`. +/// +/// ## Examples +/// +/// ```gleam +/// let uri = Uri(..empty, scheme: Some("https"), host: Some("example.com")) +/// to_string(uri) +/// // -> "https://example.com" +/// ``` +/// +pub fn to_string(uri: Uri) -> String { + let parts = case uri.fragment { + Some(fragment) -> ["#", fragment] + None -> [] + } + let parts = case uri.query { + Some(query) -> ["?", query, ..parts] + None -> parts + } + let parts = [uri.path, ..parts] + let parts = case uri.host, string.starts_with(uri.path, "/") { + Some(host), False if host != "" -> ["/", ..parts] + _, _ -> parts + } + let parts = case uri.host, uri.port { + Some(_), Some(port) -> [":", int.to_string(port), ..parts] + _, _ -> parts + } + let parts = case uri.scheme, uri.userinfo, uri.host { + Some(s), Some(u), Some(h) -> [s, "://", u, "@", h, ..parts] + Some(s), None, Some(h) -> [s, "://", h, ..parts] + Some(s), Some(_), None | Some(s), None, None -> [s, ":", ..parts] + None, None, Some(h) -> ["//", h, ..parts] + _, _, _ -> parts + } + string.concat(parts) +} + +/// Fetches the origin of a URI. +/// +/// Returns the origin of a uri as defined in +/// [RFC 6454](https://tools.ietf.org/html/rfc6454) +/// +/// The supported URI schemes are `http` and `https`. +/// URLs without a scheme will return `Error`. +/// +/// ## Examples +/// +/// ```gleam +/// let assert Ok(uri) = parse("https://example.com/path?foo#bar") +/// origin(uri) +/// // -> Ok("https://example.com") +/// ``` +/// +pub fn origin(uri: Uri) -> Result(String, Nil) { + let Uri(scheme: scheme, host: host, port: port, ..) = uri + case host, scheme { + Some(h), Some("https") if port == Some(443) -> + Ok(string.concat(["https://", h])) + Some(h), Some("http") if port == Some(80) -> + Ok(string.concat(["http://", h])) + Some(h), Some(s) if s == "http" || s == "https" -> { + case port { + Some(p) -> Ok(string.concat([s, "://", h, ":", int.to_string(p)])) + None -> Ok(string.concat([s, "://", h])) + } + } + _, _ -> Error(Nil) + } +} + +/// Resolves 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 = + relative.path + |> string.split("/") + |> remove_dot_segments() + |> join_segments() + let resolved = + Uri( + option.or(relative.scheme, base.scheme), + None, + relative.host, + option.or(relative.port, base.port), + path, + relative.query, + relative.fragment, + ) + Ok(resolved) + } + _ -> { + let #(new_path, new_query) = case relative.path { + "" -> #(base.path, option.or(relative.query, base.query)) + _ -> { + let path_segments = case string.starts_with(relative.path, "/") { + True -> string.split(relative.path, "/") + False -> + base.path + |> string.split("/") + |> drop_last() + |> list.append(string.split(relative.path, "/")) + } + let path = + path_segments + |> remove_dot_segments() + |> join_segments() + #(path, relative.query) + } + } + let resolved = + Uri( + base.scheme, + None, + base.host, + base.port, + new_path, + new_query, + relative.fragment, + ) + Ok(resolved) + } + } + _ -> 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], "/") +} diff --git a/build/packages/gleam_stdlib/src/gleam@bit_array.erl b/build/packages/gleam_stdlib/src/gleam@bit_array.erl new file mode 100644 index 0000000..7df56ce --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam@bit_array.erl @@ -0,0 +1,347 @@ +-module(gleam@bit_array). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/bit_array.gleam"). +-export([from_string/1, bit_size/1, byte_size/1, pad_to_bytes/1, slice/3, is_utf8/1, to_string/1, concat/1, append/2, base64_encode/2, base64_decode/1, base64_url_encode/2, base64_url_decode/1, base16_encode/1, base16_decode/1, inspect/1, compare/2, starts_with/2]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC(" BitArrays are a sequence of binary data of any length.\n"). + +-file("src/gleam/bit_array.gleam", 11). +?DOC(" Converts a UTF-8 `String` type into a `BitArray`.\n"). +-spec from_string(binary()) -> bitstring(). +from_string(X) -> + gleam_stdlib:identity(X). + +-file("src/gleam/bit_array.gleam", 17). +?DOC(" Returns an integer which is the number of bits in the bit array.\n"). +-spec bit_size(bitstring()) -> integer(). +bit_size(X) -> + erlang:bit_size(X). + +-file("src/gleam/bit_array.gleam", 23). +?DOC(" Returns an integer which is the number of bytes in the bit array.\n"). +-spec byte_size(bitstring()) -> integer(). +byte_size(X) -> + erlang:byte_size(X). + +-file("src/gleam/bit_array.gleam", 29). +?DOC(" Pads a bit array with zeros so that it is a whole number of bytes.\n"). +-spec pad_to_bytes(bitstring()) -> bitstring(). +pad_to_bytes(X) -> + gleam_stdlib:bit_array_pad_to_bytes(X). + +-file("src/gleam/bit_array.gleam", 54). +?DOC( + " Extracts a sub-section of a bit array.\n" + "\n" + " The slice will start at given position and continue up to specified\n" + " length.\n" + " A negative length can be used to extract bytes at the end of a bit array.\n" + "\n" + " This function runs in constant time.\n" +). +-spec slice(bitstring(), integer(), integer()) -> {ok, bitstring()} | + {error, nil}. +slice(String, Position, Length) -> + gleam_stdlib:bit_array_slice(String, Position, Length). + +-file("src/gleam/bit_array.gleam", 67). +-spec is_utf8_loop(bitstring()) -> boolean(). +is_utf8_loop(Bits) -> + case Bits of + <<>> -> + true; + + <<_/utf8, Rest/binary>> -> + is_utf8_loop(Rest); + + _ -> + false + end. + +-file("src/gleam/bit_array.gleam", 62). +?DOC(" Tests to see whether a bit array is valid UTF-8.\n"). +-spec is_utf8(bitstring()) -> boolean(). +is_utf8(Bits) -> + is_utf8_loop(Bits). + +-file("src/gleam/bit_array.gleam", 88). +?DOC( + " Converts a bit array to a string.\n" + "\n" + " Returns an error if the bit array is invalid UTF-8 data.\n" +). +-spec to_string(bitstring()) -> {ok, binary()} | {error, nil}. +to_string(Bits) -> + case is_utf8(Bits) of + true -> + {ok, gleam_stdlib:identity(Bits)}; + + false -> + {error, nil} + end. + +-file("src/gleam/bit_array.gleam", 109). +?DOC( + " Creates a new bit array by joining multiple binaries.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " concat([from_string(\"butter\"), from_string(\"fly\")])\n" + " // -> from_string(\"butterfly\")\n" + " ```\n" +). +-spec concat(list(bitstring())) -> bitstring(). +concat(Bit_arrays) -> + gleam_stdlib:bit_array_concat(Bit_arrays). + +-file("src/gleam/bit_array.gleam", 40). +?DOC( + " Creates a new bit array by joining two bit arrays.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " append(to: from_string(\"butter\"), suffix: from_string(\"fly\"))\n" + " // -> from_string(\"butterfly\")\n" + " ```\n" +). +-spec append(bitstring(), bitstring()) -> bitstring(). +append(First, Second) -> + gleam_stdlib:bit_array_concat([First, Second]). + +-file("src/gleam/bit_array.gleam", 118). +?DOC( + " Encodes a BitArray into a base 64 encoded string.\n" + "\n" + " If the bit array does not contain a whole number of bytes then it is padded\n" + " with zero bits prior to being encoded.\n" +). +-spec base64_encode(bitstring(), boolean()) -> binary(). +base64_encode(Input, Padding) -> + gleam_stdlib:base64_encode(Input, Padding). + +-file("src/gleam/bit_array.gleam", 122). +?DOC(" Decodes a base 64 encoded string into a `BitArray`.\n"). +-spec base64_decode(binary()) -> {ok, bitstring()} | {error, nil}. +base64_decode(Encoded) -> + Padded = case erlang:byte_size(gleam_stdlib:identity(Encoded)) rem 4 of + 0 -> + Encoded; + + N -> + gleam@string:append( + Encoded, + gleam@string:repeat(<<"="/utf8>>, 4 - N) + ) + end, + gleam_stdlib:base64_decode(Padded). + +-file("src/gleam/bit_array.gleam", 140). +?DOC( + " Encodes a `BitArray` into a base 64 encoded string with URL and filename\n" + " safe alphabet.\n" + "\n" + " If the bit array does not contain a whole number of bytes then it is padded\n" + " with zero bits prior to being encoded.\n" +). +-spec base64_url_encode(bitstring(), boolean()) -> binary(). +base64_url_encode(Input, Padding) -> + _pipe = Input, + _pipe@1 = gleam_stdlib:base64_encode(_pipe, Padding), + _pipe@2 = gleam@string:replace(_pipe@1, <<"+"/utf8>>, <<"-"/utf8>>), + gleam@string:replace(_pipe@2, <<"/"/utf8>>, <<"_"/utf8>>). + +-file("src/gleam/bit_array.gleam", 150). +?DOC( + " Decodes a base 64 encoded string with URL and filename safe alphabet into a\n" + " `BitArray`.\n" +). +-spec base64_url_decode(binary()) -> {ok, bitstring()} | {error, nil}. +base64_url_decode(Encoded) -> + _pipe = Encoded, + _pipe@1 = gleam@string:replace(_pipe, <<"-"/utf8>>, <<"+"/utf8>>), + _pipe@2 = gleam@string:replace(_pipe@1, <<"_"/utf8>>, <<"/"/utf8>>), + base64_decode(_pipe@2). + +-file("src/gleam/bit_array.gleam", 164). +?DOC( + " Encodes a `BitArray` into a base 16 encoded string.\n" + "\n" + " If the bit array does not contain a whole number of bytes then it is padded\n" + " with zero bits prior to being encoded.\n" +). +-spec base16_encode(bitstring()) -> binary(). +base16_encode(Input) -> + gleam_stdlib:base16_encode(Input). + +-file("src/gleam/bit_array.gleam", 170). +?DOC(" Decodes a base 16 encoded string into a `BitArray`.\n"). +-spec base16_decode(binary()) -> {ok, bitstring()} | {error, nil}. +base16_decode(Input) -> + gleam_stdlib:base16_decode(Input). + +-file("src/gleam/bit_array.gleam", 191). +-spec inspect_loop(bitstring(), binary()) -> binary(). +inspect_loop(Input, Accumulator) -> + case Input of + <<>> -> + Accumulator; + + <> -> + <<<>/binary, + ":size(1)"/utf8>>; + + <> -> + <<<>/binary, + ":size(2)"/utf8>>; + + <> -> + <<<>/binary, + ":size(3)"/utf8>>; + + <> -> + <<<>/binary, + ":size(4)"/utf8>>; + + <> -> + <<<>/binary, + ":size(5)"/utf8>>; + + <> -> + <<<>/binary, + ":size(6)"/utf8>>; + + <> -> + <<<>/binary, + ":size(7)"/utf8>>; + + <> -> + Suffix = case Rest of + <<>> -> + <<""/utf8>>; + + _ -> + <<", "/utf8>> + end, + Accumulator@1 = <<<>/binary, + Suffix/binary>>, + inspect_loop(Rest, Accumulator@1); + + _ -> + Accumulator + end. + +-file("src/gleam/bit_array.gleam", 187). +?DOC( + " Converts a bit array to a string containing the decimal value of each byte.\n" + "\n" + " Use this over `string.inspect` when you have a bit array you want printed\n" + " in the array syntax even if it is valid UTF-8.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " inspect(<<0, 20, 0x20, 255>>)\n" + " // -> \"<<0, 20, 32, 255>>\"\n" + "\n" + " inspect(<<100, 5:3>>)\n" + " // -> \"<<100, 5:size(3)>>\"\n" + " ```\n" +). +-spec inspect(bitstring()) -> binary(). +inspect(Input) -> + <<(inspect_loop(Input, <<"<<"/utf8>>))/binary, ">>"/utf8>>. + +-file("src/gleam/bit_array.gleam", 232). +?DOC( + " Compare two bit arrays as sequences of bytes.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " compare(<<1>>, <<2>>)\n" + " // -> Lt\n" + "\n" + " compare(<<\"AB\":utf8>>, <<\"AA\":utf8>>)\n" + " // -> Gt\n" + "\n" + " compare(<<1, 2:size(2)>>, with: <<1, 2:size(2)>>)\n" + " // -> Eq\n" + " ```\n" +). +-spec compare(bitstring(), bitstring()) -> gleam@order:order(). +compare(A, B) -> + case {A, B} of + {<>, + <>} -> + case {First_byte, Second_byte} of + {F, S} when F > S -> + gt; + + {F@1, S@1} when F@1 < S@1 -> + lt; + + {_, _} -> + compare(First_rest, Second_rest) + end; + + {<<>>, <<>>} -> + eq; + + {_, <<>>} -> + gt; + + {<<>>, _} -> + lt; + + {First, Second} -> + case {gleam_stdlib:bit_array_to_int_and_size(First), + gleam_stdlib:bit_array_to_int_and_size(Second)} of + {{A@1, _}, {B@1, _}} when A@1 > B@1 -> + gt; + + {{A@2, _}, {B@2, _}} when A@2 < B@2 -> + lt; + + {{_, Size_a}, {_, Size_b}} when Size_a > Size_b -> + gt; + + {{_, Size_a@1}, {_, Size_b@1}} when Size_a@1 < Size_b@1 -> + lt; + + {_, _} -> + eq + end + end. + +-file("src/gleam/bit_array.gleam", 273). +?DOC( + " Checks whether the first `BitArray` starts with the second one.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " starts_with(<<1, 2, 3, 4>>, <<1, 2>>)\n" + " // -> True\n" + " ```\n" +). +-spec starts_with(bitstring(), bitstring()) -> boolean(). +starts_with(Bits, Prefix) -> + Prefix_size = erlang:bit_size(Prefix), + case Bits of + <> when Pref =:= Prefix -> + true; + + _ -> + false + end. diff --git a/build/packages/gleam_stdlib/src/gleam@bool.erl b/build/packages/gleam_stdlib/src/gleam@bool.erl new file mode 100644 index 0000000..01307b3 --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam@bool.erl @@ -0,0 +1,352 @@ +-module(gleam@bool). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/bool.gleam"). +-export(['and'/2, 'or'/2, negate/1, nor/2, nand/2, exclusive_or/2, exclusive_nor/2, to_string/1, guard/3, lazy_guard/3]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC( + " A type with two possible values, `True` and `False`. Used to indicate whether\n" + " things are... true or false!\n" + "\n" + " Often is it clearer and offers more type safety to define a custom type\n" + " than to use `Bool`. For example, rather than having a `is_teacher: Bool`\n" + " field consider having a `role: SchoolRole` field where `SchoolRole` is a custom\n" + " type that can be either `Student` or `Teacher`.\n" +). + +-file("src/gleam/bool.gleam", 31). +?DOC( + " Returns the and of two bools, but it evaluates both arguments.\n" + "\n" + " It's the function equivalent of the `&&` operator.\n" + " This function is useful in higher order functions or pipes.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " and(True, True)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " and(False, True)\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " False |> and(True)\n" + " // -> False\n" + " ```\n" +). +-spec 'and'(boolean(), boolean()) -> boolean(). +'and'(A, B) -> + A andalso B. + +-file("src/gleam/bool.gleam", 57). +?DOC( + " Returns the or of two bools, but it evaluates both arguments.\n" + "\n" + " It's the function equivalent of the `||` operator.\n" + " This function is useful in higher order functions or pipes.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " or(True, True)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " or(False, True)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " False |> or(True)\n" + " // -> True\n" + " ```\n" +). +-spec 'or'(boolean(), boolean()) -> boolean(). +'or'(A, B) -> + A orelse B. + +-file("src/gleam/bool.gleam", 77). +?DOC( + " Returns the opposite bool value.\n" + "\n" + " This is the same as the `!` or `not` operators in some other languages.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " negate(True)\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " negate(False)\n" + " // -> True\n" + " ```\n" +). +-spec negate(boolean()) -> boolean(). +negate(Bool) -> + not Bool. + +-file("src/gleam/bool.gleam", 105). +?DOC( + " Returns the nor of two bools.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " nor(False, False)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " nor(False, True)\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " nor(True, False)\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " nor(True, True)\n" + " // -> False\n" + " ```\n" +). +-spec nor(boolean(), boolean()) -> boolean(). +nor(A, B) -> + not (A orelse B). + +-file("src/gleam/bool.gleam", 133). +?DOC( + " Returns the nand of two bools.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " nand(False, False)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " nand(False, True)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " nand(True, False)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " nand(True, True)\n" + " // -> False\n" + " ```\n" +). +-spec nand(boolean(), boolean()) -> boolean(). +nand(A, B) -> + not (A andalso B). + +-file("src/gleam/bool.gleam", 161). +?DOC( + " Returns the exclusive or of two bools.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " exclusive_or(False, False)\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " exclusive_or(False, True)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " exclusive_or(True, False)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " exclusive_or(True, True)\n" + " // -> False\n" + " ```\n" +). +-spec exclusive_or(boolean(), boolean()) -> boolean(). +exclusive_or(A, B) -> + A /= B. + +-file("src/gleam/bool.gleam", 189). +?DOC( + " Returns the exclusive nor of two bools.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " exclusive_nor(False, False)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " exclusive_nor(False, True)\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " exclusive_nor(True, False)\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " exclusive_nor(True, True)\n" + " // -> True\n" + " ```\n" +). +-spec exclusive_nor(boolean(), boolean()) -> boolean(). +exclusive_nor(A, B) -> + A =:= B. + +-file("src/gleam/bool.gleam", 207). +?DOC( + " Returns a string representation of the given bool.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " to_string(True)\n" + " // -> \"True\"\n" + " ```\n" + "\n" + " ```gleam\n" + " to_string(False)\n" + " // -> \"False\"\n" + " ```\n" +). +-spec to_string(boolean()) -> binary(). +to_string(Bool) -> + case Bool of + false -> + <<"False"/utf8>>; + + true -> + <<"True"/utf8>> + end. + +-file("src/gleam/bool.gleam", 266). +?DOC( + " Run a callback function if the given bool is `False`, otherwise return a\n" + " default value.\n" + "\n" + " With a `use` expression this function can simulate the early-return pattern\n" + " found in some other programming languages.\n" + "\n" + " In a procedural language:\n" + "\n" + " ```js\n" + " if (predicate) return value;\n" + " // ...\n" + " ```\n" + "\n" + " In Gleam with a `use` expression:\n" + "\n" + " ```gleam\n" + " use <- guard(when: predicate, return: value)\n" + " // ...\n" + " ```\n" + "\n" + " Like everything in Gleam `use` is an expression, so it short circuits the\n" + " current block, not the entire function. As a result you can assign the value\n" + " to a variable:\n" + "\n" + " ```gleam\n" + " let x = {\n" + " use <- guard(when: predicate, return: value)\n" + " // ...\n" + " }\n" + " ```\n" + "\n" + " Note that unlike in procedural languages the `return` value is evaluated\n" + " even when the predicate is `False`, so it is advisable not to perform\n" + " expensive computation nor side-effects there.\n" + "\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let name = \"\"\n" + " use <- guard(when: name == \"\", return: \"Welcome!\")\n" + " \"Hello, \" <> name\n" + " // -> \"Welcome!\"\n" + " ```\n" + "\n" + " ```gleam\n" + " let name = \"Kamaka\"\n" + " use <- guard(when: name == \"\", return: \"Welcome!\")\n" + " \"Hello, \" <> name\n" + " // -> \"Hello, Kamaka\"\n" + " ```\n" +). +-spec guard(boolean(), BSY, fun(() -> BSY)) -> BSY. +guard(Requirement, Consequence, Alternative) -> + case Requirement of + true -> + Consequence; + + false -> + Alternative() + end. + +-file("src/gleam/bool.gleam", 307). +?DOC( + " Runs a callback function if the given bool is `True`, otherwise runs an\n" + " alternative callback function.\n" + "\n" + " Useful when further computation should be delayed regardless of the given\n" + " bool's value.\n" + "\n" + " See [`guard`](#guard) for more info.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let name = \"Kamaka\"\n" + " let inquiry = fn() { \"How may we address you?\" }\n" + " use <- lazy_guard(when: name == \"\", return: inquiry)\n" + " \"Hello, \" <> name\n" + " // -> \"Hello, Kamaka\"\n" + " ```\n" + "\n" + " ```gleam\n" + " import gleam/int\n" + "\n" + " let name = \"\"\n" + " let greeting = fn() { \"Hello, \" <> name }\n" + " use <- lazy_guard(when: name == \"\", otherwise: greeting)\n" + " let number = int.random(99)\n" + " let name = \"User \" <> int.to_string(number)\n" + " \"Welcome, \" <> name\n" + " // -> \"Welcome, User 54\"\n" + " ```\n" +). +-spec lazy_guard(boolean(), fun(() -> BSZ), fun(() -> BSZ)) -> BSZ. +lazy_guard(Requirement, Consequence, Alternative) -> + case Requirement of + true -> + Consequence(); + + false -> + Alternative() + end. diff --git a/build/packages/gleam_stdlib/src/gleam@bytes_tree.erl b/build/packages/gleam_stdlib/src/gleam@bytes_tree.erl new file mode 100644 index 0000000..a96eaa2 --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam@bytes_tree.erl @@ -0,0 +1,211 @@ +-module(gleam@bytes_tree). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/bytes_tree.gleam"). +-export([append_tree/2, prepend_tree/2, concat/1, new/0, from_string/1, prepend_string/2, append_string/2, from_string_tree/1, from_bit_array/1, prepend/2, append/2, concat_bit_arrays/1, to_bit_array/1, byte_size/1]). +-export_type([bytes_tree/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC( + " `BytesTree` is a type used for efficiently building binary content to be\n" + " written to a file or a socket. Internally it is represented as tree so to\n" + " append or prepend to a bytes tree is a constant time operation that\n" + " allocates a new node in the tree without copying any of the content. When\n" + " writing to an output stream the tree is traversed and the content is sent\n" + " directly rather than copying it into a single buffer beforehand.\n" + "\n" + " If we append one bit array to another the bit arrays must be copied to a\n" + " new location in memory so that they can sit together. This behaviour\n" + " enables efficient reading of the data but copying can be expensive,\n" + " especially if we want to join many bit arrays together.\n" + "\n" + " BytesTree is different in that it can be joined together in constant\n" + " time using minimal memory, and then can be efficiently converted to a\n" + " bit array using the `to_bit_array` function.\n" + "\n" + " Byte trees are always byte aligned, so that a number of bits that is not\n" + " divisible by 8 will be padded with 0s.\n" + "\n" + " On Erlang this type is compatible with Erlang's iolists.\n" +). + +-opaque bytes_tree() :: {bytes, bitstring()} | + {text, gleam@string_tree:string_tree()} | + {many, list(bytes_tree())}. + +-file("src/gleam/bytes_tree.gleam", 68). +?DOC( + " Appends a bytes tree onto the end of another.\n" + "\n" + " Runs in constant time.\n" +). +-spec append_tree(bytes_tree(), bytes_tree()) -> bytes_tree(). +append_tree(First, Second) -> + gleam_stdlib:iodata_append(First, Second). + +-file("src/gleam/bytes_tree.gleam", 59). +?DOC( + " Prepends a bytes tree onto the start of another.\n" + "\n" + " Runs in constant time.\n" +). +-spec prepend_tree(bytes_tree(), bytes_tree()) -> bytes_tree(). +prepend_tree(Second, First) -> + gleam_stdlib:iodata_append(First, Second). + +-file("src/gleam/bytes_tree.gleam", 98). +?DOC( + " Joins a list of bytes trees into a single one.\n" + "\n" + " Runs in constant time.\n" +). +-spec concat(list(bytes_tree())) -> bytes_tree(). +concat(Trees) -> + gleam_stdlib:identity(Trees). + +-file("src/gleam/bytes_tree.gleam", 35). +?DOC( + " Create an empty `BytesTree`. Useful as the start of a pipe chaining many\n" + " trees together.\n" +). +-spec new() -> bytes_tree(). +new() -> + gleam_stdlib:identity([]). + +-file("src/gleam/bytes_tree.gleam", 118). +?DOC( + " Creates a new bytes tree from a string.\n" + "\n" + " Runs in constant time when running on Erlang.\n" + " Runs in linear time otherwise.\n" +). +-spec from_string(binary()) -> bytes_tree(). +from_string(String) -> + gleam_stdlib:wrap_list(String). + +-file("src/gleam/bytes_tree.gleam", 80). +?DOC( + " Prepends a string onto the start of a bytes tree.\n" + "\n" + " Runs in constant time when running on Erlang.\n" + " Runs in linear time with the length of the string otherwise.\n" +). +-spec prepend_string(bytes_tree(), binary()) -> bytes_tree(). +prepend_string(Second, First) -> + gleam_stdlib:iodata_append(gleam_stdlib:wrap_list(First), Second). + +-file("src/gleam/bytes_tree.gleam", 89). +?DOC( + " Appends a string onto the end of a bytes tree.\n" + "\n" + " Runs in constant time when running on Erlang.\n" + " Runs in linear time with the length of the string otherwise.\n" +). +-spec append_string(bytes_tree(), binary()) -> bytes_tree(). +append_string(First, Second) -> + gleam_stdlib:iodata_append(First, gleam_stdlib:wrap_list(Second)). + +-file("src/gleam/bytes_tree.gleam", 128). +?DOC( + " Creates a new bytes tree from a string tree.\n" + "\n" + " Runs in constant time when running on Erlang.\n" + " Runs in linear time otherwise.\n" +). +-spec from_string_tree(gleam@string_tree:string_tree()) -> bytes_tree(). +from_string_tree(Tree) -> + gleam_stdlib:wrap_list(Tree). + +-file("src/gleam/bytes_tree.gleam", 136). +?DOC( + " Creates a new bytes tree from a bit array.\n" + "\n" + " Runs in constant time.\n" +). +-spec from_bit_array(bitstring()) -> bytes_tree(). +from_bit_array(Bits) -> + _pipe = Bits, + _pipe@1 = gleam_stdlib:bit_array_pad_to_bytes(_pipe), + gleam_stdlib:wrap_list(_pipe@1). + +-file("src/gleam/bytes_tree.gleam", 43). +?DOC( + " Prepends a bit array to the start of a bytes tree.\n" + "\n" + " Runs in constant time.\n" +). +-spec prepend(bytes_tree(), bitstring()) -> bytes_tree(). +prepend(Second, First) -> + gleam_stdlib:iodata_append(from_bit_array(First), Second). + +-file("src/gleam/bytes_tree.gleam", 51). +?DOC( + " Appends a bit array to the end of a bytes tree.\n" + "\n" + " Runs in constant time.\n" +). +-spec append(bytes_tree(), bitstring()) -> bytes_tree(). +append(First, Second) -> + gleam_stdlib:iodata_append(First, from_bit_array(Second)). + +-file("src/gleam/bytes_tree.gleam", 106). +?DOC( + " Joins a list of bit arrays into a single bytes tree.\n" + "\n" + " Runs in constant time.\n" +). +-spec concat_bit_arrays(list(bitstring())) -> bytes_tree(). +concat_bit_arrays(Bits) -> + _pipe = Bits, + _pipe@1 = gleam@list:map(_pipe, fun from_bit_array/1), + gleam_stdlib:identity(_pipe@1). + +-file("src/gleam/bytes_tree.gleam", 162). +-spec to_list(list(list(bytes_tree())), list(bitstring())) -> list(bitstring()). +to_list(Stack, Acc) -> + case Stack of + [] -> + Acc; + + [[] | Remaining_stack] -> + to_list(Remaining_stack, Acc); + + [[{bytes, Bits} | Rest] | Remaining_stack@1] -> + to_list([Rest | Remaining_stack@1], [Bits | Acc]); + + [[{text, Tree} | Rest@1] | Remaining_stack@2] -> + Bits@1 = gleam_stdlib:identity(unicode:characters_to_binary(Tree)), + to_list([Rest@1 | Remaining_stack@2], [Bits@1 | Acc]); + + [[{many, Trees} | Rest@2] | Remaining_stack@3] -> + to_list([Trees, Rest@2 | Remaining_stack@3], Acc) + end. + +-file("src/gleam/bytes_tree.gleam", 155). +?DOC( + " Turns a bytes tree into a bit array.\n" + "\n" + " Runs in linear time.\n" + "\n" + " When running on Erlang this function is implemented natively by the\n" + " virtual machine and is highly optimised.\n" +). +-spec to_bit_array(bytes_tree()) -> bitstring(). +to_bit_array(Tree) -> + erlang:list_to_bitstring(Tree). + +-file("src/gleam/bytes_tree.gleam", 186). +?DOC( + " Returns the size of the bytes tree's content in bytes.\n" + "\n" + " Runs in linear time.\n" +). +-spec byte_size(bytes_tree()) -> integer(). +byte_size(Tree) -> + erlang:iolist_size(Tree). diff --git a/build/packages/gleam_stdlib/src/gleam@dict.erl b/build/packages/gleam_stdlib/src/gleam@dict.erl new file mode 100644 index 0000000..d496afb --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam@dict.erl @@ -0,0 +1,561 @@ +-module(gleam@dict). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/dict.gleam"). +-export([size/1, is_empty/1, to_list/1, new/0, get/2, has_key/2, insert/3, from_list/1, keys/1, values/1, take/2, merge/2, delete/2, drop/2, upsert/3, fold/3, map_values/2, filter/2, each/2, combine/3]). +-export_type([dict/2]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +-type dict(KG, KH) :: any() | {gleam_phantom, KG, KH}. + +-file("src/gleam/dict.gleam", 36). +?DOC( + " Determines the number of key-value pairs in the dict.\n" + " This function runs in constant time and does not need to iterate the dict.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " new() |> size\n" + " // -> 0\n" + " ```\n" + "\n" + " ```gleam\n" + " new() |> insert(\"key\", \"value\") |> size\n" + " // -> 1\n" + " ```\n" +). +-spec size(dict(any(), any())) -> integer(). +size(Dict) -> + maps:size(Dict). + +-file("src/gleam/dict.gleam", 52). +?DOC( + " Determines whether or not the dict is empty.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " new() |> is_empty\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " new() |> insert(\"b\", 1) |> is_empty\n" + " // -> False\n" + " ```\n" +). +-spec is_empty(dict(any(), any())) -> boolean(). +is_empty(Dict) -> + maps:size(Dict) =:= 0. + +-file("src/gleam/dict.gleam", 80). +?DOC( + " Converts the dict to a list of 2-element tuples `#(key, value)`, one for\n" + " each key-value pair in the dict.\n" + "\n" + " The tuples in the list have no specific order.\n" + "\n" + " ## Examples\n" + "\n" + " Calling `to_list` on an empty `dict` returns an empty list.\n" + "\n" + " ```gleam\n" + " new() |> to_list\n" + " // -> []\n" + " ```\n" + "\n" + " The ordering of elements in the resulting list is an implementation detail\n" + " that should not be relied upon.\n" + "\n" + " ```gleam\n" + " new() |> insert(\"b\", 1) |> insert(\"a\", 0) |> insert(\"c\", 2) |> to_list\n" + " // -> [#(\"a\", 0), #(\"b\", 1), #(\"c\", 2)]\n" + " ```\n" +). +-spec to_list(dict(KQ, KR)) -> list({KQ, KR}). +to_list(Dict) -> + maps:to_list(Dict). + +-file("src/gleam/dict.gleam", 129). +?DOC(" Creates a fresh dict that contains no values.\n"). +-spec new() -> dict(any(), any()). +new() -> + maps:new(). + +-file("src/gleam/dict.gleam", 150). +?DOC( + " Fetches a value from a dict for a given key.\n" + "\n" + " The dict may not have a value for the key, so the value is wrapped in a\n" + " `Result`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " new() |> insert(\"a\", 0) |> get(\"a\")\n" + " // -> Ok(0)\n" + " ```\n" + "\n" + " ```gleam\n" + " new() |> insert(\"a\", 0) |> get(\"b\")\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec get(dict(LT, LU), LT) -> {ok, LU} | {error, nil}. +get(From, Get) -> + gleam_stdlib:map_get(From, Get). + +-file("src/gleam/dict.gleam", 116). +?DOC( + " Determines whether or not a value present in the dict for a given key.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " new() |> insert(\"a\", 0) |> has_key(\"a\")\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " new() |> insert(\"a\", 0) |> has_key(\"b\")\n" + " // -> False\n" + " ```\n" +). +-spec has_key(dict(LH, any()), LH) -> boolean(). +has_key(Dict, Key) -> + maps:is_key(Key, Dict). + +-file("src/gleam/dict.gleam", 169). +?DOC( + " Inserts a value into the dict with the given key.\n" + "\n" + " If the dict already has a value for the given key then the value is\n" + " replaced with the new value.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " new() |> insert(\"a\", 0)\n" + " // -> from_list([#(\"a\", 0)])\n" + " ```\n" + "\n" + " ```gleam\n" + " new() |> insert(\"a\", 0) |> insert(\"a\", 5)\n" + " // -> from_list([#(\"a\", 5)])\n" + " ```\n" +). +-spec insert(dict(LZ, MA), LZ, MA) -> dict(LZ, MA). +insert(Dict, Key, Value) -> + maps:put(Key, Value, Dict). + +-file("src/gleam/dict.gleam", 92). +-spec from_list_loop(list({LA, LB}), dict(LA, LB)) -> dict(LA, LB). +from_list_loop(List, Initial) -> + case List of + [] -> + Initial; + + [{Key, Value} | Rest] -> + from_list_loop(Rest, insert(Initial, Key, Value)) + end. + +-file("src/gleam/dict.gleam", 88). +?DOC( + " Converts a list of 2-element tuples `#(key, value)` to a dict.\n" + "\n" + " If two tuples have the same key the last one in the list will be the one\n" + " that is present in the dict.\n" +). +-spec from_list(list({KV, KW})) -> dict(KV, KW). +from_list(List) -> + maps:from_list(List). + +-file("src/gleam/dict.gleam", 223). +-spec reverse_and_concat(list(NJ), list(NJ)) -> list(NJ). +reverse_and_concat(Remaining, Accumulator) -> + case Remaining of + [] -> + Accumulator; + + [First | Rest] -> + reverse_and_concat(Rest, [First | Accumulator]) + end. + +-file("src/gleam/dict.gleam", 216). +-spec do_keys_loop(list({NE, any()}), list(NE)) -> list(NE). +do_keys_loop(List, Acc) -> + case List of + [] -> + reverse_and_concat(Acc, []); + + [{Key, _} | Rest] -> + do_keys_loop(Rest, [Key | Acc]) + end. + +-file("src/gleam/dict.gleam", 212). +?DOC( + " Gets a list of all keys in a given dict.\n" + "\n" + " Dicts are not ordered so the keys are not returned in any specific order. Do\n" + " not write code that relies on the order keys are returned by this function\n" + " as it may change in later versions of Gleam or Erlang.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " from_list([#(\"a\", 0), #(\"b\", 1)]) |> keys\n" + " // -> [\"a\", \"b\"]\n" + " ```\n" +). +-spec keys(dict(MZ, any())) -> list(MZ). +keys(Dict) -> + maps:keys(Dict). + +-file("src/gleam/dict.gleam", 249). +-spec do_values_loop(list({any(), NT}), list(NT)) -> list(NT). +do_values_loop(List, Acc) -> + case List of + [] -> + reverse_and_concat(Acc, []); + + [{_, Value} | Rest] -> + do_values_loop(Rest, [Value | Acc]) + end. + +-file("src/gleam/dict.gleam", 244). +?DOC( + " Gets a list of all values in a given dict.\n" + "\n" + " Dicts are not ordered so the values are not returned in any specific order. Do\n" + " not write code that relies on the order values are returned by this function\n" + " as it may change in later versions of Gleam or Erlang.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " from_list([#(\"a\", 0), #(\"b\", 1)]) |> values\n" + " // -> [0, 1]\n" + " ```\n" +). +-spec values(dict(any(), NO)) -> list(NO). +values(Dict) -> + maps:values(Dict). + +-file("src/gleam/dict.gleam", 318). +-spec do_take_loop(dict(OX, OY), list(OX), dict(OX, OY)) -> dict(OX, OY). +do_take_loop(Dict, Desired_keys, Acc) -> + Insert = fun(Taken, Key) -> case gleam_stdlib:map_get(Dict, Key) of + {ok, Value} -> + insert(Taken, Key, Value); + + {error, _} -> + Taken + end end, + case Desired_keys of + [] -> + Acc; + + [First | Rest] -> + do_take_loop(Dict, Rest, Insert(Acc, First)) + end. + +-file("src/gleam/dict.gleam", 309). +?DOC( + " Creates a new dict from a given dict, only including any entries for which the\n" + " keys are in a given list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " from_list([#(\"a\", 0), #(\"b\", 1)])\n" + " |> take([\"b\"])\n" + " // -> from_list([#(\"b\", 1)])\n" + " ```\n" + "\n" + " ```gleam\n" + " from_list([#(\"a\", 0), #(\"b\", 1)])\n" + " |> take([\"a\", \"b\", \"c\"])\n" + " // -> from_list([#(\"a\", 0), #(\"b\", 1)])\n" + " ```\n" +). +-spec take(dict(OJ, OK), list(OJ)) -> dict(OJ, OK). +take(Dict, Desired_keys) -> + maps:with(Desired_keys, Dict). + +-file("src/gleam/dict.gleam", 363). +-spec insert_pair(dict(PV, PW), {PV, PW}) -> dict(PV, PW). +insert_pair(Dict, Pair) -> + insert(Dict, erlang:element(1, Pair), erlang:element(2, Pair)). + +-file("src/gleam/dict.gleam", 356). +-spec fold_inserts(list({PO, PP}), dict(PO, PP)) -> dict(PO, PP). +fold_inserts(New_entries, Dict) -> + case New_entries of + [] -> + Dict; + + [First | Rest] -> + fold_inserts(Rest, insert_pair(Dict, First)) + end. + +-file("src/gleam/dict.gleam", 350). +?DOC( + " Creates a new dict from a pair of given dicts by combining their entries.\n" + "\n" + " If there are entries with the same keys in both dicts the entry from the\n" + " second dict takes precedence.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let a = from_list([#(\"a\", 0), #(\"b\", 1)])\n" + " let b = from_list([#(\"b\", 2), #(\"c\", 3)])\n" + " merge(a, b)\n" + " // -> from_list([#(\"a\", 0), #(\"b\", 2), #(\"c\", 3)])\n" + " ```\n" +). +-spec merge(dict(PG, PH), dict(PG, PH)) -> dict(PG, PH). +merge(Dict, New_entries) -> + maps:merge(Dict, New_entries). + +-file("src/gleam/dict.gleam", 382). +?DOC( + " Creates a new dict from a given dict with all the same entries except for the\n" + " one with a given key, if it exists.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " from_list([#(\"a\", 0), #(\"b\", 1)]) |> delete(\"a\")\n" + " // -> from_list([#(\"b\", 1)])\n" + " ```\n" + "\n" + " ```gleam\n" + " from_list([#(\"a\", 0), #(\"b\", 1)]) |> delete(\"c\")\n" + " // -> from_list([#(\"a\", 0), #(\"b\", 1)])\n" + " ```\n" +). +-spec delete(dict(QB, QC), QB) -> dict(QB, QC). +delete(Dict, Key) -> + maps:remove(Key, Dict). + +-file("src/gleam/dict.gleam", 410). +?DOC( + " Creates a new dict from a given dict with all the same entries except any with\n" + " keys found in a given list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " from_list([#(\"a\", 0), #(\"b\", 1)]) |> drop([\"a\"])\n" + " // -> from_list([#(\"b\", 1)])\n" + " ```\n" + "\n" + " ```gleam\n" + " from_list([#(\"a\", 0), #(\"b\", 1)]) |> drop([\"c\"])\n" + " // -> from_list([#(\"a\", 0), #(\"b\", 1)])\n" + " ```\n" + "\n" + " ```gleam\n" + " from_list([#(\"a\", 0), #(\"b\", 1)]) |> drop([\"a\", \"b\", \"c\"])\n" + " // -> from_list([])\n" + " ```\n" +). +-spec drop(dict(QN, QO), list(QN)) -> dict(QN, QO). +drop(Dict, Disallowed_keys) -> + case Disallowed_keys of + [] -> + Dict; + + [First | Rest] -> + drop(delete(Dict, First), Rest) + end. + +-file("src/gleam/dict.gleam", 440). +?DOC( + " Creates a new dict with one entry inserted or updated using a given function.\n" + "\n" + " If there was not an entry in the dict for the given key then the function\n" + " gets `None` as its argument, otherwise it gets `Some(value)`.\n" + "\n" + " ## Example\n" + "\n" + " ```gleam\n" + " let dict = from_list([#(\"a\", 0)])\n" + " let increment = fn(x) {\n" + " case x {\n" + " Some(i) -> i + 1\n" + " None -> 0\n" + " }\n" + " }\n" + "\n" + " upsert(dict, \"a\", increment)\n" + " // -> from_list([#(\"a\", 1)])\n" + "\n" + " upsert(dict, \"b\", increment)\n" + " // -> from_list([#(\"a\", 0), #(\"b\", 0)])\n" + " ```\n" +). +-spec upsert(dict(QU, QV), QU, fun((gleam@option:option(QV)) -> QV)) -> dict(QU, QV). +upsert(Dict, Key, Fun) -> + case gleam_stdlib:map_get(Dict, Key) of + {ok, Value} -> + insert(Dict, Key, Fun({some, Value})); + + {error, _} -> + insert(Dict, Key, Fun(none)) + end. + +-file("src/gleam/dict.gleam", 484). +-spec fold_loop(list({RG, RH}), RJ, fun((RJ, RG, RH) -> RJ)) -> RJ. +fold_loop(List, Initial, Fun) -> + case List of + [] -> + Initial; + + [{K, V} | Rest] -> + fold_loop(Rest, Fun(Initial, K, V), Fun) + end. + +-file("src/gleam/dict.gleam", 476). +?DOC( + " Combines all entries into a single value by calling a given function on each\n" + " one.\n" + "\n" + " Dicts are not ordered so the values are not returned in any specific order. Do\n" + " not write code that relies on the order entries are used by this function\n" + " as it may change in later versions of Gleam or Erlang.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " let dict = from_list([#(\"a\", 1), #(\"b\", 3), #(\"c\", 9)])\n" + " fold(dict, 0, fn(accumulator, key, value) { accumulator + value })\n" + " // -> 13\n" + " ```\n" + "\n" + " ```gleam\n" + " import gleam/string\n" + "\n" + " let dict = from_list([#(\"a\", 1), #(\"b\", 3), #(\"c\", 9)])\n" + " fold(dict, \"\", fn(accumulator, key, value) {\n" + " string.append(accumulator, key)\n" + " })\n" + " // -> \"abc\"\n" + " ```\n" +). +-spec fold(dict(RB, RC), RF, fun((RF, RB, RC) -> RF)) -> RF. +fold(Dict, Initial, Fun) -> + fold_loop(maps:to_list(Dict), Initial, Fun). + +-file("src/gleam/dict.gleam", 188). +?DOC( + " Updates all values in a given dict by calling a given function on each key\n" + " and value.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " from_list([#(3, 3), #(2, 4)])\n" + " |> map_values(fn(key, value) { key * value })\n" + " // -> from_list([#(3, 9), #(2, 8)])\n" + " ```\n" +). +-spec map_values(dict(ML, MM), fun((ML, MM) -> MP)) -> dict(ML, MP). +map_values(Dict, Fun) -> + maps:map(Fun, Dict). + +-file("src/gleam/dict.gleam", 273). +?DOC( + " Creates a new dict from a given dict, minus any entries that a given function\n" + " returns `False` for.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " from_list([#(\"a\", 0), #(\"b\", 1)])\n" + " |> filter(fn(key, value) { value != 0 })\n" + " // -> from_list([#(\"b\", 1)])\n" + " ```\n" + "\n" + " ```gleam\n" + " from_list([#(\"a\", 0), #(\"b\", 1)])\n" + " |> filter(fn(key, value) { True })\n" + " // -> from_list([#(\"a\", 0), #(\"b\", 1)])\n" + " ```\n" +). +-spec filter(dict(NX, NY), fun((NX, NY) -> boolean())) -> dict(NX, NY). +filter(Dict, Predicate) -> + maps:filter(Predicate, Dict). + +-file("src/gleam/dict.gleam", 517). +?DOC( + " Calls a function for each key and value in a dict, discarding the return\n" + " value.\n" + "\n" + " Useful for producing a side effect for every item of a dict.\n" + "\n" + " ```gleam\n" + " import gleam/io\n" + "\n" + " let dict = from_list([#(\"a\", \"apple\"), #(\"b\", \"banana\"), #(\"c\", \"cherry\")])\n" + "\n" + " each(dict, fn(k, v) {\n" + " io.println(key <> \" => \" <> value)\n" + " })\n" + " // -> Nil\n" + " // a => apple\n" + " // b => banana\n" + " // c => cherry\n" + " ```\n" + "\n" + " The order of elements in the iteration is an implementation detail that\n" + " should not be relied upon.\n" +). +-spec each(dict(RK, RL), fun((RK, RL) -> any())) -> nil. +each(Dict, Fun) -> + fold( + Dict, + nil, + fun(Nil, K, V) -> + Fun(K, V), + Nil + end + ). + +-file("src/gleam/dict.gleam", 538). +?DOC( + " Creates a new dict from a pair of given dicts by combining their entries.\n" + "\n" + " If there are entries with the same keys in both dicts the given function is\n" + " used to determine the new value to use in the resulting dict.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let a = from_list([#(\"a\", 0), #(\"b\", 1)])\n" + " let b = from_list([#(\"a\", 2), #(\"c\", 3)])\n" + " combine(a, b, fn(one, other) { one + other })\n" + " // -> from_list([#(\"a\", 2), #(\"b\", 1), #(\"c\", 3)])\n" + " ```\n" +). +-spec combine(dict(RP, RQ), dict(RP, RQ), fun((RQ, RQ) -> RQ)) -> dict(RP, RQ). +combine(Dict, Other, Fun) -> + fold( + Dict, + Other, + fun(Acc, Key, Value) -> case gleam_stdlib:map_get(Acc, Key) of + {ok, Other_value} -> + insert(Acc, Key, Fun(Value, Other_value)); + + {error, _} -> + insert(Acc, Key, Value) + end end + ). diff --git a/build/packages/gleam_stdlib/src/gleam@dynamic.erl b/build/packages/gleam_stdlib/src/gleam@dynamic.erl new file mode 100644 index 0000000..f057ca2 --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam@dynamic.erl @@ -0,0 +1,106 @@ +-module(gleam@dynamic). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/dynamic.gleam"). +-export([classify/1, bool/1, string/1, float/1, int/1, bit_array/1, list/1, array/1, properties/1, nil/0]). +-export_type([dynamic_/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +-type dynamic_() :: any(). + +-file("src/gleam/dynamic.gleam", 30). +?DOC( + " Return a string indicating the type of the dynamic value.\n" + "\n" + " This function may be useful for constructing error messages or logs. If you\n" + " want to turn dynamic data into well typed data then you want the\n" + " `gleam/dynamic/decode` module.\n" + "\n" + " ```gleam\n" + " classify(string(\"Hello\"))\n" + " // -> \"String\"\n" + " ```\n" +). +-spec classify(dynamic_()) -> binary(). +classify(Data) -> + gleam_stdlib:classify_dynamic(Data). + +-file("src/gleam/dynamic.gleam", 36). +?DOC(" Create a dynamic value from a bool.\n"). +-spec bool(boolean()) -> dynamic_(). +bool(A) -> + gleam_stdlib:identity(A). + +-file("src/gleam/dynamic.gleam", 44). +?DOC( + " Create a dynamic value from a string.\n" + "\n" + " On Erlang this will be a binary string rather than a character list.\n" +). +-spec string(binary()) -> dynamic_(). +string(A) -> + gleam_stdlib:identity(A). + +-file("src/gleam/dynamic.gleam", 50). +?DOC(" Create a dynamic value from a float.\n"). +-spec float(float()) -> dynamic_(). +float(A) -> + gleam_stdlib:identity(A). + +-file("src/gleam/dynamic.gleam", 56). +?DOC(" Create a dynamic value from an int.\n"). +-spec int(integer()) -> dynamic_(). +int(A) -> + gleam_stdlib:identity(A). + +-file("src/gleam/dynamic.gleam", 62). +?DOC(" Create a dynamic value from a bit array.\n"). +-spec bit_array(bitstring()) -> dynamic_(). +bit_array(A) -> + gleam_stdlib:identity(A). + +-file("src/gleam/dynamic.gleam", 68). +?DOC(" Create a dynamic value from a list.\n"). +-spec list(list(dynamic_())) -> dynamic_(). +list(A) -> + gleam_stdlib:identity(A). + +-file("src/gleam/dynamic.gleam", 77). +?DOC( + " Create a dynamic value from a list, converting it to a sequential runtime\n" + " format rather than the regular list format.\n" + "\n" + " On Erlang this will be a tuple, on JavaScript this will be an array.\n" +). +-spec array(list(dynamic_())) -> dynamic_(). +array(A) -> + erlang:list_to_tuple(A). + +-file("src/gleam/dynamic.gleam", 85). +?DOC( + " Create a dynamic value made an unordered series of keys and values, where\n" + " the keys are unique.\n" + "\n" + " On Erlang this will be a map, on JavaScript this will be a Gleam dict\n" + " object.\n" +). +-spec properties(list({dynamic_(), dynamic_()})) -> dynamic_(). +properties(Entries) -> + gleam_stdlib:identity(maps:from_list(Entries)). + +-file("src/gleam/dynamic.gleam", 94). +?DOC( + " A dynamic value representing nothing.\n" + "\n" + " On Erlang this will be the atom `nil`, on JavaScript this will be\n" + " `undefined`.\n" +). +-spec nil() -> dynamic_(). +nil() -> + gleam_stdlib:identity(nil). diff --git a/build/packages/gleam_stdlib/src/gleam@dynamic@decode.erl b/build/packages/gleam_stdlib/src/gleam@dynamic@decode.erl new file mode 100644 index 0000000..bf4b951 --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam@dynamic@decode.erl @@ -0,0 +1,1088 @@ +-module(gleam@dynamic@decode). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/dynamic/decode.gleam"). +-export([run/2, success/1, decode_dynamic/1, map/2, map_errors/2, then/2, one_of/2, recursive/1, optional/1, decode_error/2, decode_bool/1, decode_int/1, decode_float/1, decode_bit_array/1, collapse_errors/2, failure/2, new_primitive_decoder/2, decode_string/1, dict/2, list/1, subfield/3, at/2, field/3, optional_field/4, optionally_at/3]). +-export_type([decode_error/0, decoder/1]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC( + " The `Dynamic` type is used to represent dynamically typed data. That is, data\n" + " that we don't know the precise type of yet, so we need to introspect the data to\n" + " see if it is of the desired type before we can use it. Typically data like this\n" + " would come from user input or from untyped languages such as Erlang or JavaScript.\n" + "\n" + " This module provides the `Decoder` type and associated functions, which provides\n" + " a type-safe and composable way to convert dynamic data into some desired type,\n" + " or into errors if the data doesn't have the desired structure.\n" + "\n" + " The `Decoder` type is generic and has 1 type parameter, which is the type that\n" + " it attempts to decode. A `Decoder(String)` can be used to decode strings, and a\n" + " `Decoder(Option(Int))` can be used to decode `Option(Int)`s\n" + "\n" + " Decoders work using _runtime reflection_ and the data structures of the target\n" + " platform. Differences between Erlang and JavaScript data structures may impact\n" + " your decoders, so it is important to test your decoders on all supported\n" + " platforms.\n" + "\n" + " The decoding technique used by this module was inspired by Juraj Petráš'\n" + " [Toy](https://github.com/Hackder/toy), Go's `encoding/json`, and Elm's\n" + " `Json.Decode`. Thank you to them!\n" + "\n" + " # Examples\n" + "\n" + " Dynamic data may come from various sources and so many different syntaxes could\n" + " be used to describe or construct them. In these examples a pseudocode\n" + " syntax is used to describe the data.\n" + "\n" + " ## Simple types\n" + "\n" + " This module defines decoders for simple data types such as [`string`](#string),\n" + " [`int`](#int), [`float`](#float), [`bit_array`](#bit_array), and [`bool`](#bool).\n" + "\n" + " ```gleam\n" + " // Data:\n" + " // \"Hello, Joe!\"\n" + "\n" + " let result = decode.run(data, decode.string)\n" + " assert result == Ok(\"Hello, Joe!\")\n" + " ```\n" + "\n" + " ## Lists\n" + "\n" + " The [`list`](#list) decoder decodes `List`s. To use it you must construct it by\n" + " passing in another decoder into the `list` function, which is the decoder that\n" + " is to be used for the elements of the list, type checking both the list and its\n" + " elements.\n" + "\n" + " ```gleam\n" + " // Data:\n" + " // [1, 2, 3, 4]\n" + "\n" + " let result = decode.run(data, decode.list(decode.int))\n" + " assert result == Ok([1, 2, 3, 4])\n" + " ```\n" + "\n" + " On Erlang this decoder can decode from lists, and on JavaScript it can\n" + " decode from lists as well as JavaScript arrays.\n" + "\n" + " ## Options\n" + "\n" + " The [`optional`](#optional) decoder is used to decode values that may or may not\n" + " be present. In other environment these might be called \"nullable\" values.\n" + "\n" + " Like the `list` decoder, the `optional` decoder takes another decoder,\n" + " which is used to decode the value if it is present.\n" + "\n" + " ```gleam\n" + " // Data:\n" + " // 12.45\n" + "\n" + " let result = decode.run(data, decode.optional(decode.float))\n" + " assert result == Ok(option.Some(12.45))\n" + " ```\n" + " ```gleam\n" + " // Data:\n" + " // null\n" + "\n" + " let result = decode.run(data, decode.optional(decode.int))\n" + " assert result == Ok(option.None)\n" + " ```\n" + "\n" + " This decoder knows how to handle multiple different runtime representations of\n" + " absent values, including `Nil`, `None`, `null`, and `undefined`.\n" + "\n" + " ## Dicts\n" + "\n" + " The [`dict`](#dict) decoder decodes `Dicts` and contains two other decoders, one\n" + " for the keys, one for the values.\n" + "\n" + " ```gleam\n" + " // Data:\n" + " // { \"Lucy\" -> 10, \"Nubi\" -> 20 }\n" + "\n" + " let result = decode.run(data, decode.dict(decode.string, decode.int))\n" + " assert result == Ok(dict.from_list([\n" + " #(\"Lucy\", 10),\n" + " #(\"Nubi\", 20),\n" + " ]))\n" + " ```\n" + "\n" + " ## Indexing objects\n" + "\n" + " The [`at`](#at) decoder can be used to decode a value that is nested within\n" + " key-value containers such as Gleam dicts, Erlang maps, or JavaScript objects.\n" + "\n" + " ```gleam\n" + " // Data:\n" + " // { \"one\" -> { \"two\" -> 123 } }\n" + "\n" + " let result = decode.run(data, decode.at([\"one\", \"two\"], decode.int))\n" + " assert result == Ok(123)\n" + " ```\n" + "\n" + " ## Indexing arrays\n" + "\n" + " If you use ints as keys then the [`at`](#at) decoder can be used to index into\n" + " array-like containers such as Gleam or Erlang tuples, or JavaScript arrays.\n" + "\n" + " ```gleam\n" + " // Data:\n" + " // [\"one\", \"two\", \"three\"]\n" + "\n" + " let result = decode.run(data, decode.at([1], decode.string))\n" + " assert result == Ok(\"two\")\n" + " ```\n" + "\n" + " ## Records\n" + "\n" + " Decoding records from dynamic data is more complex and requires combining a\n" + " decoder for each field and a special constructor that builds your records with\n" + " the decoded field values.\n" + "\n" + " ```gleam\n" + " // Data:\n" + " // {\n" + " // \"score\" -> 180,\n" + " // \"name\" -> \"Mel Smith\",\n" + " // \"is-admin\" -> false,\n" + " // \"enrolled\" -> true,\n" + " // \"colour\" -> \"Red\",\n" + " // }\n" + "\n" + " let decoder = {\n" + " use name <- decode.field(\"name\", decode.string)\n" + " use score <- decode.field(\"score\", decode.int)\n" + " use colour <- decode.field(\"colour\", decode.string)\n" + " use enrolled <- decode.field(\"enrolled\", decode.bool)\n" + " decode.success(Player(name:, score:, colour:, enrolled:))\n" + " }\n" + "\n" + " let result = decode.run(data, decoder)\n" + " assert result == Ok(Player(\"Mel Smith\", 180, \"Red\", True))\n" + " ```\n" + "\n" + " ## Enum variants\n" + "\n" + " Imagine you have a custom type where all the variants do not contain any values.\n" + "\n" + " ```gleam\n" + " pub type PocketMonsterType {\n" + " Fire\n" + " Water\n" + " Grass\n" + " Electric\n" + " }\n" + " ```\n" + "\n" + " You might choose to encode these variants as strings, `\"fire\"` for `Fire`,\n" + " `\"water\"` for `Water`, and so on. To decode them you'll need to decode the dynamic\n" + " data as a string, but then you'll need to decode it further still as not all\n" + " strings are valid values for the enum. This can be done with the `then`\n" + " function, which enables running a second decoder after the first one\n" + " succeeds.\n" + "\n" + " ```gleam\n" + " let decoder = {\n" + " use decoded_string <- decode.then(decode.string)\n" + " case decoded_string {\n" + " // Return succeeding decoders for valid strings\n" + " \"fire\" -> decode.success(Fire)\n" + " \"water\" -> decode.success(Water)\n" + " \"grass\" -> decode.success(Grass)\n" + " \"electric\" -> decode.success(Electric)\n" + " // Return a failing decoder for any other strings\n" + " _ -> decode.failure(Fire, \"PocketMonsterType\")\n" + " }\n" + " }\n" + "\n" + " let result = decode.run(dynamic.string(\"water\"), decoder)\n" + " assert result == Ok(Water)\n" + "\n" + " let result = decode.run(dynamic.string(\"wobble\"), decoder)\n" + " assert result == Error([DecodeError(\"PocketMonsterType\", \"String\", [])])\n" + " ```\n" + "\n" + " ## Record variants\n" + "\n" + " Decoding type variants that contain other values is done by combining the\n" + " techniques from the \"enum variants\" and \"records\" examples. Imagine you have\n" + " this custom type that you want to decode:\n" + "\n" + " ```gleam\n" + " pub type PocketMonsterPerson {\n" + " Trainer(name: String, badge_count: Int)\n" + " GymLeader(name: String, speciality: PocketMonsterType)\n" + " }\n" + " ```\n" + " And you would like to be able to decode these from dynamic data like this:\n" + " ```erlang\n" + " {\n" + " \"type\" -> \"trainer\",\n" + " \"name\" -> \"Ash\",\n" + " \"badge-count\" -> 1,\n" + " }\n" + " ```\n" + " ```erlang\n" + " {\n" + " \"type\" -> \"gym-leader\",\n" + " \"name\" -> \"Misty\",\n" + " \"speciality\" -> \"water\",\n" + " }\n" + " ```\n" + "\n" + " Notice how both documents have a `\"type\"` field, which is used to indicate which\n" + " variant the data is for.\n" + "\n" + " First, define decoders for each of the variants:\n" + "\n" + " ```gleam\n" + " let trainer_decoder = {\n" + " use name <- decode.field(\"name\", decode.string)\n" + " use badge_count <- decode.field(\"badge-count\", decode.int)\n" + " decode.success(Trainer(name, badge_count))\n" + " }\n" + "\n" + " let gym_leader_decoder = {\n" + " use name <- decode.field(\"name\", decode.string)\n" + " use speciality <- decode.field(\"speciality\", pocket_monster_type_decoder)\n" + " decode.success(GymLeader(name, speciality))\n" + " }\n" + " ```\n" + "\n" + " A third decoder can be used to extract and decode the `\"type\"` field, and the\n" + " expression can evaluate to whichever decoder is suitable for the document.\n" + "\n" + " ```gleam\n" + " // Data:\n" + " // {\n" + " // \"type\" -> \"gym-leader\",\n" + " // \"name\" -> \"Misty\",\n" + " // \"speciality\" -> \"water\",\n" + " // }\n" + "\n" + " let decoder = {\n" + " use tag <- decode.field(\"type\", decode.string)\n" + " case tag {\n" + " \"gym-leader\" -> gym_leader_decoder\n" + " _ -> trainer_decoder\n" + " }\n" + " }\n" + "\n" + " let result = decode.run(data, decoder)\n" + " assert result == Ok(GymLeader(\"Misty\", Water))\n" + " ```\n" +). + +-type decode_error() :: {decode_error, binary(), binary(), list(binary())}. + +-opaque decoder(BUW) :: {decoder, + fun((gleam@dynamic:dynamic_()) -> {BUW, list(decode_error())})}. + +-file("src/gleam/dynamic/decode.gleam", 356). +?DOC( + " Run a decoder on a `Dynamic` value, decoding the value if it is of the\n" + " desired type, or returning errors.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " let decoder = {\n" + " use name <- decode.field(\"email\", decode.string)\n" + " use email <- decode.field(\"password\", decode.string)\n" + " decode.success(SignUp(name: name, email: email))\n" + " }\n" + "\n" + " decode.run(data, decoder)\n" + " ```\n" +). +-spec run(gleam@dynamic:dynamic_(), decoder(BVE)) -> {ok, BVE} | + {error, list(decode_error())}. +run(Data, Decoder) -> + {Maybe_invalid_data, Errors} = (erlang:element(2, Decoder))(Data), + case Errors of + [] -> + {ok, Maybe_invalid_data}; + + [_ | _] -> + {error, Errors} + end. + +-file("src/gleam/dynamic/decode.gleam", 479). +?DOC( + " Finalise a decoder having successfully extracted a value.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " let data = dynamic.properties([\n" + " #(dynamic.string(\"email\"), dynamic.string(\"lucy@example.com\")),\n" + " #(dynamic.string(\"name\"), dynamic.string(\"Lucy\")),\n" + " ]))\n" + "\n" + " let decoder = {\n" + " use name <- decode.field(\"name\", string)\n" + " use email <- decode.field(\"email\", string)\n" + " decode.success(SignUp(name: name, email: email))\n" + " }\n" + "\n" + " let result = decode.run(data, decoder)\n" + " assert result == Ok(SignUp(name: \"Lucy\", email: \"lucy@example.com\"))\n" + " ```\n" +). +-spec success(BWF) -> decoder(BWF). +success(Data) -> + {decoder, fun(_) -> {Data, []} end}. + +-file("src/gleam/dynamic/decode.gleam", 718). +-spec decode_dynamic(gleam@dynamic:dynamic_()) -> {gleam@dynamic:dynamic_(), + list(decode_error())}. +decode_dynamic(Data) -> + {Data, []}. + +-file("src/gleam/dynamic/decode.gleam", 875). +?DOC( + " Apply a transformation function to any value decoded by the decoder.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " let decoder = decode.int |> decode.map(int.to_string)\n" + " let result = decode.run(dynamic.int(1000), decoder)\n" + " assert result == Ok(\"1000\")\n" + " ```\n" +). +-spec map(decoder(BZC), fun((BZC) -> BZE)) -> decoder(BZE). +map(Decoder, Transformer) -> + {decoder, + fun(D) -> + {Data, Errors} = (erlang:element(2, Decoder))(D), + {Transformer(Data), Errors} + end}. + +-file("src/gleam/dynamic/decode.gleam", 884). +?DOC(" Apply a transformation function to any errors returned by the decoder.\n"). +-spec map_errors( + decoder(BZG), + fun((list(decode_error())) -> list(decode_error())) +) -> decoder(BZG). +map_errors(Decoder, Transformer) -> + {decoder, + fun(D) -> + {Data, Errors} = (erlang:element(2, Decoder))(D), + {Data, Transformer(Errors)} + end}. + +-file("src/gleam/dynamic/decode.gleam", 922). +?DOC( + " Create a new decoder based upon the value of a previous decoder.\n" + "\n" + " This may be useful to run one previous decoder to use in further decoding.\n" +). +-spec then(decoder(BZO), fun((BZO) -> decoder(BZQ))) -> decoder(BZQ). +then(Decoder, Next) -> + {decoder, + fun(Dynamic_data) -> + {Data, Errors} = (erlang:element(2, Decoder))(Dynamic_data), + Decoder@1 = Next(Data), + {Data@1, _} = Layer = (erlang:element(2, Decoder@1))(Dynamic_data), + case Errors of + [] -> + Layer; + + [_ | _] -> + {Data@1, Errors} + end + end}. + +-file("src/gleam/dynamic/decode.gleam", 965). +-spec run_decoders( + gleam@dynamic:dynamic_(), + {BZY, list(decode_error())}, + list(decoder(BZY)) +) -> {BZY, list(decode_error())}. +run_decoders(Data, Failure, Decoders) -> + case Decoders of + [] -> + Failure; + + [Decoder | Decoders@1] -> + {_, Errors} = Layer = (erlang:element(2, Decoder))(Data), + case Errors of + [] -> + Layer; + + [_ | _] -> + run_decoders(Data, Failure, Decoders@1) + end + end. + +-file("src/gleam/dynamic/decode.gleam", 952). +?DOC( + " Create a new decoder from several other decoders. Each of the inner\n" + " decoders is run in turn, and the value from the first to succeed is used.\n" + "\n" + " If no decoder succeeds then the errors from the first decoder is used.\n" + " If you wish for different errors then you may wish to use the\n" + " `collapse_errors` or `map_errors` functions.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " let decoder = decode.one_of(decode.string, or: [\n" + " decode.int |> decode.map(int.to_string),\n" + " decode.float |> decode.map(float.to_string),\n" + " ])\n" + " decode.run(dynamic.int(1000), decoder)\n" + " // -> Ok(\"1000\")\n" + " ```\n" +). +-spec one_of(decoder(BZT), list(decoder(BZT))) -> decoder(BZT). +one_of(First, Alternatives) -> + {decoder, + fun(Dynamic_data) -> + {_, Errors} = Layer = (erlang:element(2, First))(Dynamic_data), + case Errors of + [] -> + Layer; + + [_ | _] -> + run_decoders(Dynamic_data, Layer, Alternatives) + end + end}. + +-file("src/gleam/dynamic/decode.gleam", 1048). +?DOC( + " Create a decoder that can refer to itself, useful for decoding deeply\n" + " nested data.\n" + "\n" + " Attempting to create a recursive decoder without this function could result\n" + " in an infinite loop. If you are using `field` or other `use`able functions\n" + " then you may not need to use this function.\n" + "\n" + " ```gleam\n" + " type Nested {\n" + " Nested(List(Nested))\n" + " Value(String)\n" + " }\n" + "\n" + " fn nested_decoder() -> decode.Decoder(Nested) {\n" + " use <- decode.recursive\n" + " decode.one_of(decode.string |> decode.map(Value), [\n" + " decode.list(nested_decoder()) |> decode.map(Nested),\n" + " ])\n" + " }\n" + " ```\n" +). +-spec recursive(fun(() -> decoder(CAJ))) -> decoder(CAJ). +recursive(Inner) -> + {decoder, + fun(Data) -> + Decoder = Inner(), + (erlang:element(2, Decoder))(Data) + end}. + +-file("src/gleam/dynamic/decode.gleam", 853). +?DOC( + " A decoder that decodes nullable values of a type decoded by with a given\n" + " decoder.\n" + "\n" + " This function can handle common representations of null on all runtimes, such as\n" + " `nil`, `null`, and `undefined` on Erlang, and `undefined` and `null` on\n" + " JavaScript.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " let result = decode.run(dynamic.int(100), decode.optional(decode.int))\n" + " assert result == Ok(option.Some(100))\n" + " ```\n" + "\n" + " ```gleam\n" + " let result = decode.run(dynamic.nil(), decode.optional(decode.int))\n" + " assert result == Ok(option.None)\n" + " ```\n" +). +-spec optional(decoder(BYY)) -> decoder(gleam@option:option(BYY)). +optional(Inner) -> + {decoder, fun(Data) -> case gleam_stdlib:is_null(Data) of + true -> + {none, []}; + + false -> + {Data@1, Errors} = (erlang:element(2, Inner))(Data), + {{some, Data@1}, Errors} + end end}. + +-file("src/gleam/dynamic/decode.gleam", 485). +?DOC(" Construct a decode error for some unexpected dynamic data.\n"). +-spec decode_error(binary(), gleam@dynamic:dynamic_()) -> list(decode_error()). +decode_error(Expected, Found) -> + [{decode_error, Expected, gleam_stdlib:classify_dynamic(Found), []}]. + +-file("src/gleam/dynamic/decode.gleam", 609). +-spec run_dynamic_function( + gleam@dynamic:dynamic_(), + binary(), + fun((gleam@dynamic:dynamic_()) -> {ok, BWZ} | {error, BWZ}) +) -> {BWZ, list(decode_error())}. +run_dynamic_function(Data, Name, F) -> + case F(Data) of + {ok, Data@1} -> + {Data@1, []}; + + {error, Zero} -> + {Zero, + [{decode_error, Name, gleam_stdlib:classify_dynamic(Data), []}]} + end. + +-file("src/gleam/dynamic/decode.gleam", 658). +-spec decode_bool(gleam@dynamic:dynamic_()) -> {boolean(), list(decode_error())}. +decode_bool(Data) -> + case gleam_stdlib:identity(true) =:= Data of + true -> + {true, []}; + + false -> + case gleam_stdlib:identity(false) =:= Data of + true -> + {false, []}; + + false -> + {false, decode_error(<<"Bool"/utf8>>, Data)} + end + end. + +-file("src/gleam/dynamic/decode.gleam", 680). +-spec decode_int(gleam@dynamic:dynamic_()) -> {integer(), list(decode_error())}. +decode_int(Data) -> + run_dynamic_function(Data, <<"Int"/utf8>>, fun gleam_stdlib:int/1). + +-file("src/gleam/dynamic/decode.gleam", 699). +-spec decode_float(gleam@dynamic:dynamic_()) -> {float(), list(decode_error())}. +decode_float(Data) -> + run_dynamic_function(Data, <<"Float"/utf8>>, fun gleam_stdlib:float/1). + +-file("src/gleam/dynamic/decode.gleam", 733). +-spec decode_bit_array(gleam@dynamic:dynamic_()) -> {bitstring(), + list(decode_error())}. +decode_bit_array(Data) -> + run_dynamic_function( + Data, + <<"BitArray"/utf8>>, + fun gleam_stdlib:bit_array/1 + ). + +-file("src/gleam/dynamic/decode.gleam", 908). +?DOC( + " Replace all errors produced by a decoder with one single error for a named\n" + " expected type.\n" + "\n" + " This function may be useful if you wish to simplify errors before\n" + " presenting them to a user, particularly when using the `one_of` function.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " let decoder = decode.string |> decode.collapse_errors(\"MyThing\")\n" + " let result = decode.run(dynamic.int(1000), decoder)\n" + " assert result == Error([DecodeError(\"MyThing\", \"Int\", [])])\n" + " ```\n" +). +-spec collapse_errors(decoder(BZL), binary()) -> decoder(BZL). +collapse_errors(Decoder, Name) -> + {decoder, + fun(Dynamic_data) -> + {Data, Errors} = Layer = (erlang:element(2, Decoder))(Dynamic_data), + case Errors of + [] -> + Layer; + + [_ | _] -> + {Data, decode_error(Name, Dynamic_data)} + end + end}. + +-file("src/gleam/dynamic/decode.gleam", 986). +?DOC( + " Define a decoder that always fails. The parameter for this function is the\n" + " name of the type that has failed to decode.\n" +). +-spec failure(CAD, binary()) -> decoder(CAD). +failure(Zero, Expected) -> + {decoder, fun(D) -> {Zero, decode_error(Expected, D)} end}. + +-file("src/gleam/dynamic/decode.gleam", 1015). +?DOC( + " Create a decoder for a new data type from a decoding function.\n" + "\n" + " This function is used for new primitive types. For example, you might\n" + " define a decoder for Erlang's pid type.\n" + "\n" + " A default \"zero\" value is also required to make a decoder. When this\n" + " decoder is used as part of a larger decoder this zero value used as\n" + " a placeholder so that the rest of the decoder can continue to run and\n" + " collect all decoding errors.\n" + "\n" + " If you were to make a decoder for the `String` type (rather than using the\n" + " build-in `string` decoder) you would define it like so:\n" + "\n" + " ```gleam\n" + " pub fn string_decoder() -> decode.Decoder(String) {\n" + " let default = \"\"\n" + " decode.new_primitive_decoder(\"String\", fn(data) {\n" + " case dynamic.string(data) {\n" + " Ok(x) -> Ok(x)\n" + " Error(_) -> Error(default)\n" + " }\n" + " })\n" + " }\n" + " ```\n" +). +-spec new_primitive_decoder( + binary(), + fun((gleam@dynamic:dynamic_()) -> {ok, CAF} | {error, CAF}) +) -> decoder(CAF). +new_primitive_decoder(Name, Decoding_function) -> + {decoder, fun(D) -> case Decoding_function(D) of + {ok, T} -> + {T, []}; + + {error, Zero} -> + {Zero, + [{decode_error, + Name, + gleam_stdlib:classify_dynamic(D), + []}]} + end end}. + +-file("src/gleam/dynamic/decode.gleam", 636). +-spec dynamic_string(gleam@dynamic:dynamic_()) -> {ok, binary()} | + {error, binary()}. +dynamic_string(Data) -> + case gleam_stdlib:bit_array(Data) of + {ok, Data@1} -> + case gleam@bit_array:to_string(Data@1) of + {ok, String} -> + {ok, String}; + + {error, _} -> + {error, <<""/utf8>>} + end; + + {error, _} -> + {error, <<""/utf8>>} + end. + +-file("src/gleam/dynamic/decode.gleam", 631). +-spec decode_string(gleam@dynamic:dynamic_()) -> {binary(), + list(decode_error())}. +decode_string(Data) -> + run_dynamic_function(Data, <<"String"/utf8>>, fun dynamic_string/1). + +-file("src/gleam/dynamic/decode.gleam", 807). +-spec fold_dict( + {gleam@dict:dict(BYK, BYL), list(decode_error())}, + gleam@dynamic:dynamic_(), + gleam@dynamic:dynamic_(), + fun((gleam@dynamic:dynamic_()) -> {BYK, list(decode_error())}), + fun((gleam@dynamic:dynamic_()) -> {BYL, list(decode_error())}) +) -> {gleam@dict:dict(BYK, BYL), list(decode_error())}. +fold_dict(Acc, Key, Value, Key_decoder, Value_decoder) -> + case Key_decoder(Key) of + {Key@1, []} -> + case Value_decoder(Value) of + {Value@1, []} -> + Dict = gleam@dict:insert( + erlang:element(1, Acc), + Key@1, + Value@1 + ), + {Dict, erlang:element(2, Acc)}; + + {_, Errors} -> + push_path({maps:new(), Errors}, [<<"values"/utf8>>]) + end; + + {_, Errors@1} -> + push_path({maps:new(), Errors@1}, [<<"keys"/utf8>>]) + end. + +-file("src/gleam/dynamic/decode.gleam", 787). +?DOC( + " A decoder that decodes dicts where all keys and vales are decoded with\n" + " given decoders.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " let values = dynamic.properties([\n" + " #(dynamic.string(\"one\"), dynamic.int(1)),\n" + " #(dynamic.string(\"two\"), dynamic.int(2)),\n" + " ])\n" + "\n" + " let result =\n" + " decode.run(values, decode.dict(decode.string, decode.int))\n" + " assert result == Ok(values)\n" + " ```\n" +). +-spec dict(decoder(BYD), decoder(BYF)) -> decoder(gleam@dict:dict(BYD, BYF)). +dict(Key, Value) -> + {decoder, fun(Data) -> case gleam_stdlib:dict(Data) of + {error, _} -> + {maps:new(), decode_error(<<"Dict"/utf8>>, Data)}; + + {ok, Dict} -> + gleam@dict:fold( + Dict, + {maps:new(), []}, + fun(A, K, V) -> case erlang:element(2, A) of + [] -> + fold_dict( + A, + K, + V, + erlang:element(2, Key), + erlang:element(2, Value) + ); + + [_ | _] -> + A + end end + ) + end end}. + +-file("src/gleam/dynamic/decode.gleam", 755). +?DOC( + " A decoder that decodes lists where all elements are decoded with a given\n" + " decoder.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " let result =\n" + " [1, 2, 3]\n" + " |> list.map(dynamic.int)\n" + " |> dynamic.list\n" + " |> decode.run(decode.list(of: decode.int))\n" + " assert result == Ok([1, 2, 3])\n" + " ```\n" +). +-spec list(decoder(BXR)) -> decoder(list(BXR)). +list(Inner) -> + {decoder, + fun(Data) -> + gleam_stdlib:list( + Data, + erlang:element(2, Inner), + fun(P, K) -> push_path(P, [K]) end, + 0, + [] + ) + end}. + +-file("src/gleam/dynamic/decode.gleam", 439). +-spec push_path({BWA, list(decode_error())}, list(any())) -> {BWA, + list(decode_error())}. +push_path(Layer, Path) -> + Decoder = one_of( + {decoder, fun decode_string/1}, + [begin + _pipe = {decoder, fun decode_int/1}, + map(_pipe, fun erlang:integer_to_binary/1) + end] + ), + Path@1 = gleam@list:map( + Path, + fun(Key) -> + Key@1 = gleam_stdlib:identity(Key), + case run(Key@1, Decoder) of + {ok, Key@2} -> + Key@2; + + {error, _} -> + <<<<"<"/utf8, + (gleam_stdlib:classify_dynamic(Key@1))/binary>>/binary, + ">"/utf8>> + end + end + ), + Errors = gleam@list:map( + erlang:element(2, Layer), + fun(Error) -> + {decode_error, + erlang:element(2, Error), + erlang:element(3, Error), + lists:append(Path@1, erlang:element(4, Error))} + end + ), + {erlang:element(1, Layer), Errors}. + +-file("src/gleam/dynamic/decode.gleam", 403). +-spec index( + list(BVO), + list(BVO), + fun((gleam@dynamic:dynamic_()) -> {BVR, list(decode_error())}), + gleam@dynamic:dynamic_(), + fun((gleam@dynamic:dynamic_(), list(BVO)) -> {BVR, list(decode_error())}) +) -> {BVR, list(decode_error())}. +index(Path, Position, Inner, Data, Handle_miss) -> + case Path of + [] -> + _pipe = Data, + _pipe@1 = Inner(_pipe), + push_path(_pipe@1, lists:reverse(Position)); + + [Key | Path@1] -> + case gleam_stdlib:index(Data, Key) of + {ok, {some, Data@1}} -> + index(Path@1, [Key | Position], Inner, Data@1, Handle_miss); + + {ok, none} -> + Handle_miss(Data, [Key | Position]); + + {error, Kind} -> + {Default, _} = Inner(Data), + _pipe@2 = {Default, + [{decode_error, + Kind, + gleam_stdlib:classify_dynamic(Data), + []}]}, + push_path(_pipe@2, lists:reverse(Position)) + end + end. + +-file("src/gleam/dynamic/decode.gleam", 324). +?DOC( + " The same as [`field`](#field), except taking a path to the value rather\n" + " than a field name.\n" + "\n" + " This function will index into dictionaries with any key type, and if the key is\n" + " an int then it'll also index into Erlang tuples and JavaScript arrays, and\n" + " the first eight elements of Gleam lists.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " let data = dynamic.properties([\n" + " #(dynamic.string(\"data\"), dynamic.properties([\n" + " #(dynamic.string(\"email\"), dynamic.string(\"lucy@example.com\")),\n" + " #(dynamic.string(\"name\"), dynamic.string(\"Lucy\")),\n" + " ])\n" + " ]))\n" + "\n" + " let decoder = {\n" + " use name <- decode.subfield([\"data\", \"name\"], decode.string)\n" + " use email <- decode.subfield([\"data\", \"email\"], decode.string)\n" + " decode.success(SignUp(name: name, email: email))\n" + " }\n" + " let result = decode.run(data, decoder)\n" + " assert result == Ok(SignUp(name: \"Lucy\", email: \"lucy@example.com\"))\n" + " ```\n" +). +-spec subfield(list(any()), decoder(BUZ), fun((BUZ) -> decoder(BVB))) -> decoder(BVB). +subfield(Field_path, Field_decoder, Next) -> + {decoder, + fun(Data) -> + {Out, Errors1} = index( + Field_path, + [], + erlang:element(2, Field_decoder), + Data, + fun(Data@1, Position) -> + {Default, _} = (erlang:element(2, Field_decoder))(Data@1), + _pipe = {Default, + [{decode_error, + <<"Field"/utf8>>, + <<"Nothing"/utf8>>, + []}]}, + push_path(_pipe, lists:reverse(Position)) + end + ), + {Out@1, Errors2} = (erlang:element(2, Next(Out)))(Data), + {Out@1, lists:append(Errors1, Errors2)} + end}. + +-file("src/gleam/dynamic/decode.gleam", 393). +?DOC( + " A decoder that decodes a value that is nested within other values. For\n" + " example, decoding a value that is within some deeply nested JSON objects.\n" + "\n" + " This function will index into dictionaries with any key type, and if the key is\n" + " an int then it'll also index into Erlang tuples and JavaScript arrays, and\n" + " the first eight elements of Gleam lists.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " let decoder = decode.at([\"one\", \"two\"], decode.int)\n" + "\n" + " let data = dynamic.properties([\n" + " #(dynamic.string(\"one\"), dynamic.properties([\n" + " #(dynamic.string(\"two\"), dynamic.int(1000)),\n" + " ])),\n" + " ]))\n" + "\n" + "\n" + " decode.run(data, decoder)\n" + " // -> Ok(1000)\n" + " ```\n" + "\n" + " ```gleam\n" + " dynamic.nil()\n" + " |> decode.run(decode.optional(decode.int))\n" + " // -> Ok(option.None)\n" + " ```\n" +). +-spec at(list(any()), decoder(BVL)) -> decoder(BVL). +at(Path, Inner) -> + {decoder, + fun(Data) -> + index( + Path, + [], + erlang:element(2, Inner), + Data, + fun(Data@1, Position) -> + {Default, _} = (erlang:element(2, Inner))(Data@1), + _pipe = {Default, + [{decode_error, + <<"Field"/utf8>>, + <<"Nothing"/utf8>>, + []}]}, + push_path(_pipe, lists:reverse(Position)) + end + ) + end}. + +-file("src/gleam/dynamic/decode.gleam", 524). +?DOC( + " Run a decoder on a field of a `Dynamic` value, decoding the value if it is\n" + " of the desired type, or returning errors. An error is returned if there is\n" + " no field for the specified key.\n" + "\n" + " This function will index into dictionaries with any key type, and if the key is\n" + " an int then it'll also index into Erlang tuples and JavaScript arrays, and\n" + " the first eight elements of Gleam lists.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " let data = dynamic.properties([\n" + " #(dynamic.string(\"email\"), dynamic.string(\"lucy@example.com\")),\n" + " #(dynamic.string(\"name\"), dynamic.string(\"Lucy\")),\n" + " ]))\n" + "\n" + " let decoder = {\n" + " use name <- decode.field(\"name\", string)\n" + " use email <- decode.field(\"email\", string)\n" + " decode.success(SignUp(name: name, email: email))\n" + " }\n" + "\n" + " let result = decode.run(data, decoder)\n" + " assert result == Ok(SignUp(name: \"Lucy\", email: \"lucy@example.com\"))\n" + " ```\n" + "\n" + " If you wish to decode a value that is more deeply nested within the dynamic\n" + " data, see [`subfield`](#subfield) and [`at`](#at).\n" + "\n" + " If you wish to return a default in the event that a field is not present,\n" + " see [`optional_field`](#optional_field) and / [`optionally_at`](#optionally_at).\n" +). +-spec field(any(), decoder(BWJ), fun((BWJ) -> decoder(BWL))) -> decoder(BWL). +field(Field_name, Field_decoder, Next) -> + subfield([Field_name], Field_decoder, Next). + +-file("src/gleam/dynamic/decode.gleam", 557). +?DOC( + " Run a decoder on a field of a `Dynamic` value, decoding the value if it is\n" + " of the desired type, or returning errors. The given default value is\n" + " returned if there is no field for the specified key.\n" + "\n" + " This function will index into dictionaries with any key type, and if the key is\n" + " an int then it'll also index into Erlang tuples and JavaScript arrays, and\n" + " the first eight elements of Gleam lists.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " let data = dynamic.properties([\n" + " #(dynamic.string(\"name\"), dynamic.string(\"Lucy\")),\n" + " ]))\n" + "\n" + " let decoder = {\n" + " use name <- decode.field(\"name\", string)\n" + " use email <- decode.optional_field(\"email\", \"n/a\", string)\n" + " decode.success(SignUp(name: name, email: email))\n" + " }\n" + "\n" + " let result = decode.run(data, decoder)\n" + " assert result == Ok(SignUp(name: \"Lucy\", email: \"n/a\"))\n" + " ```\n" +). +-spec optional_field(any(), BWP, decoder(BWP), fun((BWP) -> decoder(BWR))) -> decoder(BWR). +optional_field(Key, Default, Field_decoder, Next) -> + {decoder, + fun(Data) -> + {Out, Errors1} = begin + _pipe = case gleam_stdlib:index(Data, Key) of + {ok, {some, Data@1}} -> + (erlang:element(2, Field_decoder))(Data@1); + + {ok, none} -> + {Default, []}; + + {error, Kind} -> + {Default, + [{decode_error, + Kind, + gleam_stdlib:classify_dynamic(Data), + []}]} + end, + push_path(_pipe, [Key]) + end, + {Out@1, Errors2} = (erlang:element(2, Next(Out)))(Data), + {Out@1, lists:append(Errors1, Errors2)} + end}. + +-file("src/gleam/dynamic/decode.gleam", 599). +?DOC( + " A decoder that decodes a value that is nested within other values. For\n" + " example, decoding a value that is within some deeply nested JSON objects.\n" + "\n" + " This function will index into dictionaries with any key type, and if the key is\n" + " an int then it'll also index into Erlang tuples and JavaScript arrays, and\n" + " the first eight elements of Gleam lists.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " let decoder = decode.optionally_at([\"one\", \"two\"], 100, decode.int)\n" + "\n" + " let data = dynamic.properties([\n" + " #(dynamic.string(\"one\"), dynamic.properties([])),\n" + " ]))\n" + "\n" + "\n" + " decode.run(data, decoder)\n" + " // -> Ok(100)\n" + " ```\n" +). +-spec optionally_at(list(any()), BWW, decoder(BWW)) -> decoder(BWW). +optionally_at(Path, Default, Inner) -> + {decoder, + fun(Data) -> + index( + Path, + [], + erlang:element(2, Inner), + Data, + fun(_, _) -> {Default, []} end + ) + end}. diff --git a/build/packages/gleam_stdlib/src/gleam@float.erl b/build/packages/gleam_stdlib/src/gleam@float.erl new file mode 100644 index 0000000..6b55b47 --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam@float.erl @@ -0,0 +1,744 @@ +-module(gleam@float). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/float.gleam"). +-export([parse/1, to_string/1, compare/2, min/2, max/2, clamp/3, ceiling/1, floor/1, truncate/1, absolute_value/1, loosely_compare/3, loosely_equals/3, power/2, square_root/1, negate/1, round/1, to_precision/2, sum/1, product/1, random/0, modulo/2, divide/2, add/2, multiply/2, subtract/2, logarithm/1, exponential/1]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC( + " Functions for working with floats.\n" + "\n" + " ## Float representation\n" + "\n" + " Floats are represented as 64 bit floating point numbers on both the Erlang\n" + " and JavaScript runtimes. The floating point behaviour is native to their\n" + " respective runtimes, so their exact behaviour will be slightly different on\n" + " the two runtimes.\n" + "\n" + " ### Infinity and NaN\n" + "\n" + " Under the JavaScript runtime, exceeding the maximum (or minimum)\n" + " representable value for a floating point value will result in Infinity (or\n" + " -Infinity). Should you try to divide two infinities you will get NaN as a\n" + " result.\n" + "\n" + " When running on BEAM, exceeding the maximum (or minimum) representable\n" + " value for a floating point value will raise an error.\n" + "\n" + " ## Division by zero\n" + "\n" + " Gleam runs on the Erlang virtual machine, which does not follow the IEEE\n" + " 754 standard for floating point arithmetic and does not have an `Infinity`\n" + " value. In Erlang division by zero results in a crash, however Gleam does\n" + " not have partial functions and operators in core so instead division by zero\n" + " returns zero, a behaviour taken from Pony, Coq, and Lean.\n" + "\n" + " This may seem unexpected at first, but it is no less mathematically valid\n" + " than crashing or returning a special value. Division by zero is undefined\n" + " in mathematics.\n" +). + +-file("src/gleam/float.gleam", 51). +?DOC( + " Attempts to parse a string as a `Float`, returning `Error(Nil)` if it was\n" + " not possible.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " parse(\"2.3\")\n" + " // -> Ok(2.3)\n" + " ```\n" + "\n" + " ```gleam\n" + " parse(\"ABC\")\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec parse(binary()) -> {ok, float()} | {error, nil}. +parse(String) -> + gleam_stdlib:parse_float(String). + +-file("src/gleam/float.gleam", 64). +?DOC( + " Returns the string representation of the provided `Float`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " to_string(2.3)\n" + " // -> \"2.3\"\n" + " ```\n" +). +-spec to_string(float()) -> binary(). +to_string(X) -> + gleam_stdlib:float_to_string(X). + +-file("src/gleam/float.gleam", 95). +?DOC( + " Compares two `Float`s, returning an `Order`:\n" + " `Lt` for lower than, `Eq` for equals, or `Gt` for greater than.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " compare(2.0, 2.3)\n" + " // -> Lt\n" + " ```\n" + "\n" + " To handle\n" + " [Floating Point Imprecision](https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems)\n" + " you may use [`loosely_compare`](#loosely_compare) instead.\n" +). +-spec compare(float(), float()) -> gleam@order:order(). +compare(A, B) -> + case A =:= B of + true -> + eq; + + false -> + case A < B of + true -> + lt; + + false -> + gt + end + end. + +-file("src/gleam/float.gleam", 176). +?DOC( + " Compares two `Float`s, returning the smaller of the two.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " min(2.0, 2.3)\n" + " // -> 2.0\n" + " ```\n" +). +-spec min(float(), float()) -> float(). +min(A, B) -> + case A < B of + true -> + A; + + false -> + B + end. + +-file("src/gleam/float.gleam", 192). +?DOC( + " Compares two `Float`s, returning the larger of the two.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " max(2.0, 2.3)\n" + " // -> 2.3\n" + " ```\n" +). +-spec max(float(), float()) -> float(). +max(A, B) -> + case A > B of + true -> + A; + + false -> + B + end. + +-file("src/gleam/float.gleam", 75). +?DOC( + " Restricts a `Float` between a lower and upper bound.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " clamp(1.2, min: 1.4, max: 1.6)\n" + " // -> 1.4\n" + " ```\n" +). +-spec clamp(float(), float(), float()) -> float(). +clamp(X, Min_bound, Max_bound) -> + _pipe = X, + _pipe@1 = min(_pipe, Max_bound), + max(_pipe@1, Min_bound). + +-file("src/gleam/float.gleam", 210). +?DOC( + " Rounds the value to the next highest whole number as a `Float`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " ceiling(2.3)\n" + " // -> 3.0\n" + " ```\n" +). +-spec ceiling(float()) -> float(). +ceiling(X) -> + math:ceil(X). + +-file("src/gleam/float.gleam", 223). +?DOC( + " Rounds the value to the next lowest whole number as a `Float`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " floor(2.3)\n" + " // -> 2.0\n" + " ```\n" +). +-spec floor(float()) -> float(). +floor(X) -> + math:floor(X). + +-file("src/gleam/float.gleam", 261). +?DOC( + " Returns the value as an `Int`, truncating all decimal digits.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " truncate(2.4343434847383438)\n" + " // -> 2\n" + " ```\n" +). +-spec truncate(float()) -> integer(). +truncate(X) -> + erlang:trunc(X). + +-file("src/gleam/float.gleam", 311). +?DOC( + " Returns the absolute value of the input as a `Float`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " absolute_value(-12.5)\n" + " // -> 12.5\n" + " ```\n" + "\n" + " ```gleam\n" + " absolute_value(10.2)\n" + " // -> 10.2\n" + " ```\n" +). +-spec absolute_value(float()) -> float(). +absolute_value(X) -> + case X >= +0.0 of + true -> + X; + + false -> + +0.0 - X + end. + +-file("src/gleam/float.gleam", 125). +?DOC( + " Compares two `Float`s within a tolerance, returning an `Order`:\n" + " `Lt` for lower than, `Eq` for equals, or `Gt` for greater than.\n" + "\n" + " This function allows Float comparison while handling\n" + " [Floating Point Imprecision](https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems).\n" + "\n" + " Notice: For `Float`s the tolerance won't be exact:\n" + " `5.3 - 5.0` is not exactly `0.3`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " loosely_compare(5.0, with: 5.3, tolerating: 0.5)\n" + " // -> Eq\n" + " ```\n" + "\n" + " If you want to check only for equality you may use\n" + " [`loosely_equals`](#loosely_equals) instead.\n" +). +-spec loosely_compare(float(), float(), float()) -> gleam@order:order(). +loosely_compare(A, B, Tolerance) -> + Difference = absolute_value(A - B), + case Difference =< Tolerance of + true -> + eq; + + false -> + compare(A, B) + end. + +-file("src/gleam/float.gleam", 158). +?DOC( + " Checks for equality of two `Float`s within a tolerance,\n" + " returning an `Bool`.\n" + "\n" + " This function allows Float comparison while handling\n" + " [Floating Point Imprecision](https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems).\n" + "\n" + " Notice: For `Float`s the tolerance won't be exact:\n" + " `5.3 - 5.0` is not exactly `0.3`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " loosely_equals(5.0, with: 5.3, tolerating: 0.5)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " loosely_equals(5.0, with: 5.1, tolerating: 0.1)\n" + " // -> False\n" + " ```\n" +). +-spec loosely_equals(float(), float(), float()) -> boolean(). +loosely_equals(A, B, Tolerance) -> + Difference = absolute_value(A - B), + Difference =< Tolerance. + +-file("src/gleam/float.gleam", 348). +?DOC( + " Returns the results of the base being raised to the power of the\n" + " exponent, as a `Float`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " power(2.0, -1.0)\n" + " // -> Ok(0.5)\n" + " ```\n" + "\n" + " ```gleam\n" + " power(2.0, 2.0)\n" + " // -> Ok(4.0)\n" + " ```\n" + "\n" + " ```gleam\n" + " power(8.0, 1.5)\n" + " // -> Ok(22.627416997969522)\n" + " ```\n" + "\n" + " ```gleam\n" + " 4.0 |> power(of: 2.0)\n" + " // -> Ok(16.0)\n" + " ```\n" + "\n" + " ```gleam\n" + " power(-1.0, 0.5)\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec power(float(), float()) -> {ok, float()} | {error, nil}. +power(Base, Exponent) -> + Fractional = (math:ceil(Exponent) - Exponent) > +0.0, + case ((Base < +0.0) andalso Fractional) orelse ((Base =:= +0.0) andalso (Exponent + < +0.0)) of + true -> + {error, nil}; + + false -> + {ok, math:pow(Base, Exponent)} + end. + +-file("src/gleam/float.gleam", 380). +?DOC( + " Returns the square root of the input as a `Float`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " square_root(4.0)\n" + " // -> Ok(2.0)\n" + " ```\n" + "\n" + " ```gleam\n" + " square_root(-16.0)\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec square_root(float()) -> {ok, float()} | {error, nil}. +square_root(X) -> + power(X, 0.5). + +-file("src/gleam/float.gleam", 393). +?DOC( + " Returns the negative of the value provided.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " negate(1.0)\n" + " // -> -1.0\n" + " ```\n" +). +-spec negate(float()) -> float(). +negate(X) -> + -1.0 * X. + +-file("src/gleam/float.gleam", 240). +?DOC( + " Rounds the value to the nearest whole number as an `Int`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " round(2.3)\n" + " // -> 2\n" + " ```\n" + "\n" + " ```gleam\n" + " round(2.5)\n" + " // -> 3\n" + " ```\n" +). +-spec round(float()) -> integer(). +round(X) -> + erlang:round(X). + +-file("src/gleam/float.gleam", 280). +?DOC( + " Converts the value to a given precision as a `Float`.\n" + " The precision is the number of allowed decimal places.\n" + " Negative precisions are allowed and force rounding\n" + " to the nearest tenth, hundredth, thousandth etc.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " to_precision(2.43434348473, precision: 2)\n" + " // -> 2.43\n" + " ```\n" + "\n" + " ```gleam\n" + " to_precision(547890.453444, precision: -3)\n" + " // -> 548000.0\n" + " ```\n" +). +-spec to_precision(float(), integer()) -> float(). +to_precision(X, Precision) -> + case Precision =< 0 of + true -> + Factor = math:pow(10.0, erlang:float(- Precision)), + erlang:float(erlang:round(case Factor of + +0.0 -> +0.0; + -0.0 -> -0.0; + Gleam@denominator -> X / Gleam@denominator + end)) * Factor; + + false -> + Factor@1 = math:pow(10.0, erlang:float(Precision)), + case Factor@1 of + +0.0 -> +0.0; + -0.0 -> -0.0; + Gleam@denominator@1 -> erlang:float(erlang:round(X * Factor@1)) + / Gleam@denominator@1 + end + end. + +-file("src/gleam/float.gleam", 410). +-spec sum_loop(list(float()), float()) -> float(). +sum_loop(Numbers, Initial) -> + case Numbers of + [First | Rest] -> + sum_loop(Rest, First + Initial); + + [] -> + Initial + end. + +-file("src/gleam/float.gleam", 406). +?DOC( + " Sums a list of `Float`s.\n" + "\n" + " ## Example\n" + "\n" + " ```gleam\n" + " sum([1.0, 2.2, 3.3])\n" + " // -> 6.5\n" + " ```\n" +). +-spec sum(list(float())) -> float(). +sum(Numbers) -> + sum_loop(Numbers, +0.0). + +-file("src/gleam/float.gleam", 430). +-spec product_loop(list(float()), float()) -> float(). +product_loop(Numbers, Initial) -> + case Numbers of + [First | Rest] -> + product_loop(Rest, First * Initial); + + [] -> + Initial + end. + +-file("src/gleam/float.gleam", 426). +?DOC( + " Multiplies a list of `Float`s and returns the product.\n" + "\n" + " ## Example\n" + "\n" + " ```gleam\n" + " product([2.5, 3.2, 4.2])\n" + " // -> 33.6\n" + " ```\n" +). +-spec product(list(float())) -> float(). +product(Numbers) -> + product_loop(Numbers, 1.0). + +-file("src/gleam/float.gleam", 452). +?DOC( + " Generates a random float between the given zero (inclusive) and one\n" + " (exclusive).\n" + "\n" + " On Erlang this updates the random state in the process dictionary.\n" + " See: \n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " random()\n" + " // -> 0.646355926896028\n" + " ```\n" +). +-spec random() -> float(). +random() -> + rand:uniform(). + +-file("src/gleam/float.gleam", 481). +?DOC( + " Computes the modulo of an float division of inputs as a `Result`.\n" + "\n" + " Returns division of the inputs as a `Result`: If the given divisor equals\n" + " `0`, this function returns an `Error`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " modulo(13.3, by: 3.3)\n" + " // -> Ok(0.1)\n" + " ```\n" + "\n" + " ```gleam\n" + " modulo(-13.3, by: 3.3)\n" + " // -> Ok(3.2)\n" + " ```\n" + "\n" + " ```gleam\n" + " modulo(13.3, by: -3.3)\n" + " // -> Ok(-3.2)\n" + " ```\n" + "\n" + " ```gleam\n" + " modulo(-13.3, by: -3.3)\n" + " // -> Ok(-0.1)\n" + " ```\n" +). +-spec modulo(float(), float()) -> {ok, float()} | {error, nil}. +modulo(Dividend, Divisor) -> + case Divisor of + +0.0 -> + {error, nil}; + + _ -> + {ok, Dividend - (math:floor(case Divisor of + +0.0 -> +0.0; + -0.0 -> -0.0; + Gleam@denominator -> Dividend / Gleam@denominator + end) * Divisor)} + end. + +-file("src/gleam/float.gleam", 502). +?DOC( + " Returns division of the inputs as a `Result`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " divide(0.0, 1.0)\n" + " // -> Ok(0.0)\n" + " ```\n" + "\n" + " ```gleam\n" + " divide(1.0, 0.0)\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec divide(float(), float()) -> {ok, float()} | {error, nil}. +divide(A, B) -> + case B of + +0.0 -> + {error, nil}; + + B@1 -> + {ok, case B@1 of + +0.0 -> +0.0; + -0.0 -> -0.0; + Gleam@denominator -> A / Gleam@denominator + end} + end. + +-file("src/gleam/float.gleam", 533). +?DOC( + " Adds two floats together.\n" + "\n" + " It's the function equivalent of the `+.` operator.\n" + " This function is useful in higher order functions or pipes.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " add(1.0, 2.0)\n" + " // -> 3.0\n" + " ```\n" + "\n" + " ```gleam\n" + " import gleam/list\n" + "\n" + " list.fold([1.0, 2.0, 3.0], 0.0, add)\n" + " // -> 6.0\n" + " ```\n" + "\n" + " ```gleam\n" + " 3.0 |> add(2.0)\n" + " // -> 5.0\n" + " ```\n" +). +-spec add(float(), float()) -> float(). +add(A, B) -> + A + B. + +-file("src/gleam/float.gleam", 561). +?DOC( + " Multiplies two floats together.\n" + "\n" + " It's the function equivalent of the `*.` operator.\n" + " This function is useful in higher order functions or pipes.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " multiply(2.0, 4.0)\n" + " // -> 8.0\n" + " ```\n" + "\n" + " ```gleam\n" + " import gleam/list\n" + "\n" + " list.fold([2.0, 3.0, 4.0], 1.0, multiply)\n" + " // -> 24.0\n" + " ```\n" + "\n" + " ```gleam\n" + " 3.0 |> multiply(2.0)\n" + " // -> 6.0\n" + " ```\n" +). +-spec multiply(float(), float()) -> float(). +multiply(A, B) -> + A * B. + +-file("src/gleam/float.gleam", 594). +?DOC( + " Subtracts one float from another.\n" + "\n" + " It's the function equivalent of the `-.` operator.\n" + " This function is useful in higher order functions or pipes.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " subtract(3.0, 1.0)\n" + " // -> 2.0\n" + " ```\n" + "\n" + " ```gleam\n" + " import gleam/list\n" + "\n" + " list.fold([1.0, 2.0, 3.0], 10.0, subtract)\n" + " // -> 4.0\n" + " ```\n" + "\n" + " ```gleam\n" + " 3.0 |> subtract(_, 2.0)\n" + " // -> 1.0\n" + " ```\n" + "\n" + " ```gleam\n" + " 3.0 |> subtract(2.0, _)\n" + " // -> -1.0\n" + " ```\n" +). +-spec subtract(float(), float()) -> float(). +subtract(A, B) -> + A - B. + +-file("src/gleam/float.gleam", 623). +?DOC( + " Returns the natural logarithm (base e) of the given as a `Result`. If the\n" + " input is less than or equal to 0, returns `Error(Nil)`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " logarithm(1.0)\n" + " // -> Ok(0.0)\n" + " ```\n" + "\n" + " ```gleam\n" + " logarithm(2.718281828459045) // e\n" + " // -> Ok(1.0)\n" + " ```\n" + "\n" + " ```gleam\n" + " logarithm(0.0)\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " logarithm(-1.0)\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec logarithm(float()) -> {ok, float()} | {error, nil}. +logarithm(X) -> + case X =< +0.0 of + true -> + {error, nil}; + + false -> + {ok, math:log(X)} + end. + +-file("src/gleam/float.gleam", 661). +?DOC( + " Returns e (Euler's number) raised to the power of the given exponent, as\n" + " a `Float`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " exponential(0.0)\n" + " // -> Ok(1.0)\n" + " ```\n" + "\n" + " ```gleam\n" + " exponential(1.0)\n" + " // -> Ok(2.718281828459045)\n" + " ```\n" + "\n" + " ```gleam\n" + " exponential(-1.0)\n" + " // -> Ok(0.36787944117144233)\n" + " ```\n" +). +-spec exponential(float()) -> float(). +exponential(X) -> + math:exp(X). diff --git a/build/packages/gleam_stdlib/src/gleam@function.erl b/build/packages/gleam_stdlib/src/gleam@function.erl new file mode 100644 index 0000000..a6dec81 --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam@function.erl @@ -0,0 +1,30 @@ +-module(gleam@function). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/function.gleam"). +-export([identity/1, tap/2]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +-file("src/gleam/function.gleam", 3). +?DOC(" Takes a single argument and always returns its input value.\n"). +-spec identity(CLA) -> CLA. +identity(X) -> + X. + +-file("src/gleam/function.gleam", 12). +?DOC( + " Takes an argument and a single function, calls that function with that\n" + " argument and returns that argument instead of the function return value.\n" + "\n" + " Useful for running synchronous side effects in a pipeline.\n" +). +-spec tap(CLB, fun((CLB) -> any())) -> CLB. +tap(Arg, Effect) -> + Effect(Arg), + Arg. diff --git a/build/packages/gleam_stdlib/src/gleam@int.erl b/build/packages/gleam_stdlib/src/gleam@int.erl new file mode 100644 index 0000000..5e298da --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam@int.erl @@ -0,0 +1,986 @@ +-module(gleam@int). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/int.gleam"). +-export([absolute_value/1, parse/1, base_parse/2, to_string/1, to_base_string/2, to_base2/1, to_base8/1, to_base16/1, to_base36/1, to_float/1, power/2, square_root/1, compare/2, min/2, max/2, clamp/3, is_even/1, is_odd/1, negate/1, sum/1, product/1, digits/2, undigits/2, random/1, divide/2, remainder/2, modulo/2, floor_divide/2, add/2, multiply/2, subtract/2, bitwise_and/2, bitwise_not/1, bitwise_or/2, bitwise_exclusive_or/2, bitwise_shift_left/2, bitwise_shift_right/2]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC( + " Functions for working with integers.\n" + "\n" + " ## Division by zero\n" + "\n" + " In Erlang division by zero results in a crash, however Gleam does not have\n" + " partial functions and operators in core so instead division by zero returns\n" + " zero, a behaviour taken from Pony, Coq, and Lean.\n" + "\n" + " This may seem unexpected at first, but it is no less mathematically valid\n" + " than crashing or returning a special value. Division by zero is undefined\n" + " in mathematics.\n" +). + +-file("src/gleam/int.gleam", 30). +?DOC( + " Returns the absolute value of the input.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " absolute_value(-12)\n" + " // -> 12\n" + " ```\n" + "\n" + " ```gleam\n" + " absolute_value(10)\n" + " // -> 10\n" + " ```\n" +). +-spec absolute_value(integer()) -> integer(). +absolute_value(X) -> + case X >= 0 of + true -> + X; + + false -> + X * -1 + end. + +-file("src/gleam/int.gleam", 109). +?DOC( + " Parses a given string as an int if possible.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " parse(\"2\")\n" + " // -> Ok(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " parse(\"ABC\")\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec parse(binary()) -> {ok, integer()} | {error, nil}. +parse(String) -> + gleam_stdlib:parse_int(String). + +-file("src/gleam/int.gleam", 141). +?DOC( + " Parses a given string as an int in a given base if possible.\n" + " Supports only bases 2 to 36, for values outside of which this function returns an `Error(Nil)`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " base_parse(\"10\", 2)\n" + " // -> Ok(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " base_parse(\"30\", 16)\n" + " // -> Ok(48)\n" + " ```\n" + "\n" + " ```gleam\n" + " base_parse(\"1C\", 36)\n" + " // -> Ok(48)\n" + " ```\n" + "\n" + " ```gleam\n" + " base_parse(\"48\", 1)\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " base_parse(\"48\", 37)\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec base_parse(binary(), integer()) -> {ok, integer()} | {error, nil}. +base_parse(String, Base) -> + case (Base >= 2) andalso (Base =< 36) of + true -> + gleam_stdlib:int_from_base_string(String, Base); + + false -> + {error, nil} + end. + +-file("src/gleam/int.gleam", 163). +?DOC( + " Prints a given int to a string.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " to_string(2)\n" + " // -> \"2\"\n" + " ```\n" +). +-spec to_string(integer()) -> binary(). +to_string(X) -> + erlang:integer_to_binary(X). + +-file("src/gleam/int.gleam", 196). +?DOC( + " Prints a given int to a string using the base number provided.\n" + " Supports only bases 2 to 36, for values outside of which this function returns an `Error(Nil)`.\n" + " For common bases (2, 8, 16, 36), use the `to_baseN` functions.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " to_base_string(2, 2)\n" + " // -> Ok(\"10\")\n" + " ```\n" + "\n" + " ```gleam\n" + " to_base_string(48, 16)\n" + " // -> Ok(\"30\")\n" + " ```\n" + "\n" + " ```gleam\n" + " to_base_string(48, 36)\n" + " // -> Ok(\"1C\")\n" + " ```\n" + "\n" + " ```gleam\n" + " to_base_string(48, 1)\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " to_base_string(48, 37)\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec to_base_string(integer(), integer()) -> {ok, binary()} | {error, nil}. +to_base_string(X, Base) -> + case (Base >= 2) andalso (Base =< 36) of + true -> + {ok, erlang:integer_to_binary(X, Base)}; + + false -> + {error, nil} + end. + +-file("src/gleam/int.gleam", 216). +?DOC( + " Prints a given int to a string using base-2.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " to_base2(2)\n" + " // -> \"10\"\n" + " ```\n" +). +-spec to_base2(integer()) -> binary(). +to_base2(X) -> + erlang:integer_to_binary(X, 2). + +-file("src/gleam/int.gleam", 229). +?DOC( + " Prints a given int to a string using base-8.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " to_base8(15)\n" + " // -> \"17\"\n" + " ```\n" +). +-spec to_base8(integer()) -> binary(). +to_base8(X) -> + erlang:integer_to_binary(X, 8). + +-file("src/gleam/int.gleam", 242). +?DOC( + " Prints a given int to a string using base-16.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " to_base16(48)\n" + " // -> \"30\"\n" + " ```\n" +). +-spec to_base16(integer()) -> binary(). +to_base16(X) -> + erlang:integer_to_binary(X, 16). + +-file("src/gleam/int.gleam", 255). +?DOC( + " Prints a given int to a string using base-36.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " to_base36(48)\n" + " // -> \"1C\"\n" + " ```\n" +). +-spec to_base36(integer()) -> binary(). +to_base36(X) -> + erlang:integer_to_binary(X, 36). + +-file("src/gleam/int.gleam", 280). +?DOC( + " Takes an int and returns its value as a float.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " to_float(5)\n" + " // -> 5.0\n" + " ```\n" + "\n" + " ```gleam\n" + " to_float(0)\n" + " // -> 0.0\n" + " ```\n" + "\n" + " ```gleam\n" + " to_float(-3)\n" + " // -> -3.0\n" + " ```\n" +). +-spec to_float(integer()) -> float(). +to_float(X) -> + erlang:float(X). + +-file("src/gleam/int.gleam", 67). +?DOC( + " Returns the results of the base being raised to the power of the\n" + " exponent, as a `Float`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " power(2, -1.0)\n" + " // -> Ok(0.5)\n" + " ```\n" + "\n" + " ```gleam\n" + " power(2, 2.0)\n" + " // -> Ok(4.0)\n" + " ```\n" + "\n" + " ```gleam\n" + " power(8, 1.5)\n" + " // -> Ok(22.627416997969522)\n" + " ```\n" + "\n" + " ```gleam\n" + " 4 |> power(of: 2.0)\n" + " // -> Ok(16.0)\n" + " ```\n" + "\n" + " ```gleam\n" + " power(-1, 0.5)\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec power(integer(), float()) -> {ok, float()} | {error, nil}. +power(Base, Exponent) -> + _pipe = Base, + _pipe@1 = erlang:float(_pipe), + gleam@float:power(_pipe@1, Exponent). + +-file("src/gleam/int.gleam", 87). +?DOC( + " Returns the square root of the input as a `Float`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " square_root(4)\n" + " // -> Ok(2.0)\n" + " ```\n" + "\n" + " ```gleam\n" + " square_root(-16)\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec square_root(integer()) -> {ok, float()} | {error, nil}. +square_root(X) -> + _pipe = X, + _pipe@1 = erlang:float(_pipe), + gleam@float:square_root(_pipe@1). + +-file("src/gleam/int.gleam", 316). +?DOC( + " Compares two ints, returning an order.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " compare(2, 3)\n" + " // -> Lt\n" + " ```\n" + "\n" + " ```gleam\n" + " compare(4, 3)\n" + " // -> Gt\n" + " ```\n" + "\n" + " ```gleam\n" + " compare(3, 3)\n" + " // -> Eq\n" + " ```\n" +). +-spec compare(integer(), integer()) -> gleam@order:order(). +compare(A, B) -> + case A =:= B of + true -> + eq; + + false -> + case A < B of + true -> + lt; + + false -> + gt + end + end. + +-file("src/gleam/int.gleam", 336). +?DOC( + " Compares two ints, returning the smaller of the two.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " min(2, 3)\n" + " // -> 2\n" + " ```\n" +). +-spec min(integer(), integer()) -> integer(). +min(A, B) -> + case A < B of + true -> + A; + + false -> + B + end. + +-file("src/gleam/int.gleam", 352). +?DOC( + " Compares two ints, returning the larger of the two.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " max(2, 3)\n" + " // -> 3\n" + " ```\n" +). +-spec max(integer(), integer()) -> integer(). +max(A, B) -> + case A > B of + true -> + A; + + false -> + B + end. + +-file("src/gleam/int.gleam", 291). +?DOC( + " Restricts an int between a lower and upper bound.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " clamp(40, min: 50, max: 60)\n" + " // -> 50\n" + " ```\n" +). +-spec clamp(integer(), integer(), integer()) -> integer(). +clamp(X, Min_bound, Max_bound) -> + _pipe = X, + _pipe@1 = min(_pipe, Max_bound), + max(_pipe@1, Min_bound). + +-file("src/gleam/int.gleam", 373). +?DOC( + " Returns whether the value provided is even.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " is_even(2)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " is_even(3)\n" + " // -> False\n" + " ```\n" +). +-spec is_even(integer()) -> boolean(). +is_even(X) -> + (X rem 2) =:= 0. + +-file("src/gleam/int.gleam", 391). +?DOC( + " Returns whether the value provided is odd.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " is_odd(3)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " is_odd(2)\n" + " // -> False\n" + " ```\n" +). +-spec is_odd(integer()) -> boolean(). +is_odd(X) -> + (X rem 2) /= 0. + +-file("src/gleam/int.gleam", 404). +?DOC( + " Returns the negative of the value provided.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " negate(1)\n" + " // -> -1\n" + " ```\n" +). +-spec negate(integer()) -> integer(). +negate(X) -> + -1 * X. + +-file("src/gleam/int.gleam", 421). +-spec sum_loop(list(integer()), integer()) -> integer(). +sum_loop(Numbers, Initial) -> + case Numbers of + [First | Rest] -> + sum_loop(Rest, First + Initial); + + [] -> + Initial + end. + +-file("src/gleam/int.gleam", 417). +?DOC( + " Sums a list of ints.\n" + "\n" + " ## Example\n" + "\n" + " ```gleam\n" + " sum([1, 2, 3])\n" + " // -> 6\n" + " ```\n" +). +-spec sum(list(integer())) -> integer(). +sum(Numbers) -> + sum_loop(Numbers, 0). + +-file("src/gleam/int.gleam", 441). +-spec product_loop(list(integer()), integer()) -> integer(). +product_loop(Numbers, Initial) -> + case Numbers of + [First | Rest] -> + product_loop(Rest, First * Initial); + + [] -> + Initial + end. + +-file("src/gleam/int.gleam", 437). +?DOC( + " Multiplies a list of ints and returns the product.\n" + "\n" + " ## Example\n" + "\n" + " ```gleam\n" + " product([2, 3, 4])\n" + " // -> 24\n" + " ```\n" +). +-spec product(list(integer())) -> integer(). +product(Numbers) -> + product_loop(Numbers, 1). + +-file("src/gleam/int.gleam", 456). +-spec digits_loop(integer(), integer(), list(integer())) -> list(integer()). +digits_loop(X, Base, Acc) -> + case absolute_value(X) < Base of + true -> + [X | Acc]; + + false -> + digits_loop(case Base of + 0 -> 0; + Gleam@denominator -> X div Gleam@denominator + end, Base, [case Base of + 0 -> 0; + Gleam@denominator@1 -> X rem Gleam@denominator@1 + end | Acc]) + end. + +-file("src/gleam/int.gleam", 449). +-spec digits(integer(), integer()) -> {ok, list(integer())} | {error, nil}. +digits(X, Base) -> + case Base < 2 of + true -> + {error, nil}; + + false -> + {ok, digits_loop(X, Base, [])} + end. + +-file("src/gleam/int.gleam", 471). +-spec undigits_loop(list(integer()), integer(), integer()) -> {ok, integer()} | + {error, nil}. +undigits_loop(Numbers, Base, Acc) -> + case Numbers of + [] -> + {ok, Acc}; + + [Digit | _] when Digit >= Base -> + {error, nil}; + + [Digit@1 | Rest] -> + undigits_loop(Rest, Base, (Acc * Base) + Digit@1) + end. + +-file("src/gleam/int.gleam", 464). +-spec undigits(list(integer()), integer()) -> {ok, integer()} | {error, nil}. +undigits(Numbers, Base) -> + case Base < 2 of + true -> + {error, nil}; + + false -> + undigits_loop(Numbers, Base, 0) + end. + +-file("src/gleam/int.gleam", 500). +?DOC( + " Generates a random int between zero and the given maximum.\n" + "\n" + " The lower number is inclusive, the upper number is exclusive.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " random(10)\n" + " // -> 4\n" + " ```\n" + "\n" + " ```gleam\n" + " random(1)\n" + " // -> 0\n" + " ```\n" + "\n" + " ```gleam\n" + " random(-1)\n" + " // -> -1\n" + " ```\n" +). +-spec random(integer()) -> integer(). +random(Max) -> + _pipe = (rand:uniform() * erlang:float(Max)), + _pipe@1 = math:floor(_pipe), + erlang:round(_pipe@1). + +-file("src/gleam/int.gleam", 533). +?DOC( + " Performs a truncated integer division.\n" + "\n" + " Returns division of the inputs as a `Result`: If the given divisor equals\n" + " `0`, this function returns an `Error`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " divide(0, 1)\n" + " // -> Ok(0)\n" + " ```\n" + "\n" + " ```gleam\n" + " divide(1, 0)\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " divide(5, 2)\n" + " // -> Ok(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " divide(-99, 2)\n" + " // -> Ok(-49)\n" + " ```\n" +). +-spec divide(integer(), integer()) -> {ok, integer()} | {error, nil}. +divide(Dividend, Divisor) -> + case Divisor of + 0 -> + {error, nil}; + + Divisor@1 -> + {ok, case Divisor@1 of + 0 -> 0; + Gleam@denominator -> Dividend div Gleam@denominator + end} + end. + +-file("src/gleam/int.gleam", 585). +?DOC( + " Computes the remainder of an integer division of inputs as a `Result`.\n" + "\n" + " Returns division of the inputs as a `Result`: If the given divisor equals\n" + " `0`, this function returns an `Error`.\n" + "\n" + " Most the time you will want to use the `%` operator instead of this\n" + " function.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " remainder(3, 2)\n" + " // -> Ok(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " remainder(1, 0)\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " remainder(10, -1)\n" + " // -> Ok(0)\n" + " ```\n" + "\n" + " ```gleam\n" + " remainder(13, by: 3)\n" + " // -> Ok(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " remainder(-13, by: 3)\n" + " // -> Ok(-1)\n" + " ```\n" + "\n" + " ```gleam\n" + " remainder(13, by: -3)\n" + " // -> Ok(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " remainder(-13, by: -3)\n" + " // -> Ok(-1)\n" + " ```\n" +). +-spec remainder(integer(), integer()) -> {ok, integer()} | {error, nil}. +remainder(Dividend, Divisor) -> + case Divisor of + 0 -> + {error, nil}; + + Divisor@1 -> + {ok, case Divisor@1 of + 0 -> 0; + Gleam@denominator -> Dividend rem Gleam@denominator + end} + end. + +-file("src/gleam/int.gleam", 627). +?DOC( + " Computes the modulo of an integer division of inputs as a `Result`.\n" + "\n" + " Returns division of the inputs as a `Result`: If the given divisor equals\n" + " `0`, this function returns an `Error`.\n" + "\n" + " Most the time you will want to use the `%` operator instead of this\n" + " function.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " modulo(3, 2)\n" + " // -> Ok(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " modulo(1, 0)\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " modulo(10, -1)\n" + " // -> Ok(0)\n" + " ```\n" + "\n" + " ```gleam\n" + " modulo(13, by: 3)\n" + " // -> Ok(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " modulo(-13, by: 3)\n" + " // -> Ok(2)\n" + " ```\n" +). +-spec modulo(integer(), integer()) -> {ok, integer()} | {error, nil}. +modulo(Dividend, Divisor) -> + case Divisor of + 0 -> + {error, nil}; + + _ -> + Remainder = case Divisor of + 0 -> 0; + Gleam@denominator -> Dividend rem Gleam@denominator + end, + case (Remainder * Divisor) < 0 of + true -> + {ok, Remainder + Divisor}; + + false -> + {ok, Remainder} + end + end. + +-file("src/gleam/int.gleam", 671). +?DOC( + " Performs a *floored* integer division, which means that the result will\n" + " always be rounded towards negative infinity.\n" + "\n" + " If you want to perform truncated integer division (rounding towards zero),\n" + " use `int.divide()` or the `/` operator instead.\n" + "\n" + " Returns division of the inputs as a `Result`: If the given divisor equals\n" + " `0`, this function returns an `Error`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " floor_divide(1, 0)\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " floor_divide(5, 2)\n" + " // -> Ok(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " floor_divide(6, -4)\n" + " // -> Ok(-2)\n" + " ```\n" + "\n" + " ```gleam\n" + " floor_divide(-99, 2)\n" + " // -> Ok(-50)\n" + " ```\n" +). +-spec floor_divide(integer(), integer()) -> {ok, integer()} | {error, nil}. +floor_divide(Dividend, Divisor) -> + case Divisor of + 0 -> + {error, nil}; + + Divisor@1 -> + case ((Dividend * Divisor@1) < 0) andalso ((case Divisor@1 of + 0 -> 0; + Gleam@denominator -> Dividend rem Gleam@denominator + end) /= 0) of + true -> + {ok, (case Divisor@1 of + 0 -> 0; + Gleam@denominator@1 -> Dividend div Gleam@denominator@1 + end) - 1}; + + false -> + {ok, case Divisor@1 of + 0 -> 0; + Gleam@denominator@2 -> Dividend div Gleam@denominator@2 + end} + end + end. + +-file("src/gleam/int.gleam", 705). +?DOC( + " Adds two integers together.\n" + "\n" + " It's the function equivalent of the `+` operator.\n" + " This function is useful in higher order functions or pipes.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " add(1, 2)\n" + " // -> 3\n" + " ```\n" + "\n" + " ```gleam\n" + " import gleam/list\n" + " list.fold([1, 2, 3], 0, add)\n" + " // -> 6\n" + " ```\n" + "\n" + " ```gleam\n" + " 3 |> add(2)\n" + " // -> 5\n" + " ```\n" +). +-spec add(integer(), integer()) -> integer(). +add(A, B) -> + A + B. + +-file("src/gleam/int.gleam", 733). +?DOC( + " Multiplies two integers together.\n" + "\n" + " It's the function equivalent of the `*` operator.\n" + " This function is useful in higher order functions or pipes.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " multiply(2, 4)\n" + " // -> 8\n" + " ```\n" + "\n" + " ```gleam\n" + " import gleam/list\n" + "\n" + " list.fold([2, 3, 4], 1, multiply)\n" + " // -> 24\n" + " ```\n" + "\n" + " ```gleam\n" + " 3 |> multiply(2)\n" + " // -> 6\n" + " ```\n" +). +-spec multiply(integer(), integer()) -> integer(). +multiply(A, B) -> + A * B. + +-file("src/gleam/int.gleam", 766). +?DOC( + " Subtracts one int from another.\n" + "\n" + " It's the function equivalent of the `-` operator.\n" + " This function is useful in higher order functions or pipes.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " subtract(3, 1)\n" + " // -> 2\n" + " ```\n" + "\n" + " ```gleam\n" + " import gleam/list\n" + "\n" + " list.fold([1, 2, 3], 10, subtract)\n" + " // -> 4\n" + " ```\n" + "\n" + " ```gleam\n" + " 3 |> subtract(2)\n" + " // -> 1\n" + " ```\n" + "\n" + " ```gleam\n" + " 3 |> subtract(2, _)\n" + " // -> -1\n" + " ```\n" +). +-spec subtract(integer(), integer()) -> integer(). +subtract(A, B) -> + A - B. + +-file("src/gleam/int.gleam", 778). +?DOC( + " Calculates the bitwise AND of its arguments.\n" + "\n" + " The exact behaviour of this function depends on the target platform.\n" + " On Erlang it is equivalent to bitwise operations on ints, on JavaScript it\n" + " is equivalent to bitwise operations on big-ints.\n" +). +-spec bitwise_and(integer(), integer()) -> integer(). +bitwise_and(X, Y) -> + erlang:'band'(X, Y). + +-file("src/gleam/int.gleam", 788). +?DOC( + " Calculates the bitwise NOT of its argument.\n" + "\n" + " The exact behaviour of this function depends on the target platform.\n" + " On Erlang it is equivalent to bitwise operations on ints, on JavaScript it\n" + " is equivalent to bitwise operations on big-ints.\n" +). +-spec bitwise_not(integer()) -> integer(). +bitwise_not(X) -> + erlang:'bnot'(X). + +-file("src/gleam/int.gleam", 798). +?DOC( + " Calculates the bitwise OR of its arguments.\n" + "\n" + " The exact behaviour of this function depends on the target platform.\n" + " On Erlang it is equivalent to bitwise operations on ints, on JavaScript it\n" + " is equivalent to bitwise operations on big-ints.\n" +). +-spec bitwise_or(integer(), integer()) -> integer(). +bitwise_or(X, Y) -> + erlang:'bor'(X, Y). + +-file("src/gleam/int.gleam", 808). +?DOC( + " Calculates the bitwise XOR of its arguments.\n" + "\n" + " The exact behaviour of this function depends on the target platform.\n" + " On Erlang it is equivalent to bitwise operations on ints, on JavaScript it\n" + " is equivalent to bitwise operations on big-ints.\n" +). +-spec bitwise_exclusive_or(integer(), integer()) -> integer(). +bitwise_exclusive_or(X, Y) -> + erlang:'bxor'(X, Y). + +-file("src/gleam/int.gleam", 818). +?DOC( + " Calculates the result of an arithmetic left bitshift.\n" + "\n" + " The exact behaviour of this function depends on the target platform.\n" + " On Erlang it is equivalent to bitwise operations on ints, on JavaScript it\n" + " is equivalent to bitwise operations on big-ints.\n" +). +-spec bitwise_shift_left(integer(), integer()) -> integer(). +bitwise_shift_left(X, Y) -> + erlang:'bsl'(X, Y). + +-file("src/gleam/int.gleam", 828). +?DOC( + " Calculates the result of an arithmetic right bitshift.\n" + "\n" + " The exact behaviour of this function depends on the target platform.\n" + " On Erlang it is equivalent to bitwise operations on ints, on JavaScript it\n" + " is equivalent to bitwise operations on big-ints.\n" +). +-spec bitwise_shift_right(integer(), integer()) -> integer(). +bitwise_shift_right(X, Y) -> + erlang:'bsr'(X, Y). diff --git a/build/packages/gleam_stdlib/src/gleam@io.erl b/build/packages/gleam_stdlib/src/gleam@io.erl new file mode 100644 index 0000000..e60295e --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam@io.erl @@ -0,0 +1,80 @@ +-module(gleam@io). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/io.gleam"). +-export([print/1, print_error/1, println/1, println_error/1]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +-file("src/gleam/io.gleam", 15). +?DOC( + " Writes a string to standard output (stdout).\n" + "\n" + " If you want your output to be printed on its own line see `println`.\n" + "\n" + " ## Example\n" + "\n" + " ```gleam\n" + " io.print(\"Hi mum\")\n" + " // -> Nil\n" + " // Hi mum\n" + " ```\n" +). +-spec print(binary()) -> nil. +print(String) -> + gleam_stdlib:print(String). + +-file("src/gleam/io.gleam", 31). +?DOC( + " Writes a string to standard error (stderr).\n" + "\n" + " If you want your output to be printed on its own line see `println_error`.\n" + "\n" + " ## Example\n" + "\n" + " ```\n" + " io.print_error(\"Hi pop\")\n" + " // -> Nil\n" + " // Hi pop\n" + " ```\n" +). +-spec print_error(binary()) -> nil. +print_error(String) -> + gleam_stdlib:print_error(String). + +-file("src/gleam/io.gleam", 45). +?DOC( + " Writes a string to standard output (stdout), appending a newline to the end.\n" + "\n" + " ## Example\n" + "\n" + " ```gleam\n" + " io.println(\"Hi mum\")\n" + " // -> Nil\n" + " // Hi mum\n" + " ```\n" +). +-spec println(binary()) -> nil. +println(String) -> + gleam_stdlib:println(String). + +-file("src/gleam/io.gleam", 59). +?DOC( + " Writes a string to standard error (stderr), appending a newline to the end.\n" + "\n" + " ## Example\n" + "\n" + " ```gleam\n" + " io.println_error(\"Hi pop\")\n" + " // -> Nil\n" + " // Hi pop\n" + " ```\n" +). +-spec println_error(binary()) -> nil. +println_error(String) -> + gleam_stdlib:println_error(String). diff --git a/build/packages/gleam_stdlib/src/gleam@list.erl b/build/packages/gleam_stdlib/src/gleam@list.erl new file mode 100644 index 0000000..0c96ecb --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam@list.erl @@ -0,0 +1,2873 @@ +-module(gleam@list). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/list.gleam"). +-export([length/1, count/2, reverse/1, is_empty/1, contains/2, first/1, rest/1, group/2, filter/2, filter_map/2, map/2, map2/3, map_fold/3, index_map/2, try_map/2, drop/2, take/2, new/0, wrap/1, append/2, prepend/2, flatten/1, flat_map/2, fold/3, fold_right/3, index_fold/3, try_fold/3, fold_until/3, find/2, find_map/2, all/2, any/2, zip/2, strict_zip/2, unzip/1, intersperse/2, unique/1, sort/2, range/2, repeat/2, split/2, split_while/2, key_find/2, key_filter/2, key_pop/2, key_set/3, each/2, try_each/2, partition/2, window/2, window_by_2/1, drop_while/2, take_while/2, chunk/2, sized_chunk/2, reduce/2, scan/3, last/1, combinations/2, combination_pairs/1, transpose/1, interleave/1, shuffle/1, max/2, sample/2, permutations/1]). +-export_type([continue_or_stop/1, sorting/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC( + " Lists are an ordered sequence of elements and are one of the most common\n" + " data types in Gleam.\n" + "\n" + " New elements can be added and removed from the front of a list in\n" + " constant time, while adding and removing from the end requires traversing\n" + " and copying the whole list, so keep this in mind when designing your\n" + " programs.\n" + "\n" + " There is a dedicated syntax for prefixing to a list:\n" + "\n" + " ```gleam\n" + " let new_list = [1, 2, ..existing_list]\n" + " ```\n" + "\n" + " And a matching syntax for getting the first elements of a list:\n" + "\n" + " ```gleam\n" + " case list {\n" + " [first_element, ..rest] -> first_element\n" + " _ -> \"this pattern matches when the list is empty\"\n" + " }\n" + " ```\n" + "\n" +). + +-type continue_or_stop(XG) :: {continue, XG} | {stop, XG}. + +-type sorting() :: ascending | descending. + +-file("src/gleam/list.gleam", 60). +-spec length_loop(list(any()), integer()) -> integer(). +length_loop(List, Count) -> + case List of + [_ | List@1] -> + length_loop(List@1, Count + 1); + + [] -> + Count + end. + +-file("src/gleam/list.gleam", 56). +?DOC( + " Counts the number of elements in a given list.\n" + "\n" + " This function has to traverse the list to determine the number of elements,\n" + " so it runs in linear time.\n" + "\n" + " This function is natively implemented by the virtual machine and is highly\n" + " optimised.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " length([])\n" + " // -> 0\n" + " ```\n" + "\n" + " ```gleam\n" + " length([1])\n" + " // -> 1\n" + " ```\n" + "\n" + " ```gleam\n" + " length([1, 2])\n" + " // -> 2\n" + " ```\n" +). +-spec length(list(any())) -> integer(). +length(List) -> + erlang:length(List). + +-file("src/gleam/list.gleam", 93). +-spec count_loop(list(XN), fun((XN) -> boolean()), integer()) -> integer(). +count_loop(List, Predicate, Acc) -> + case List of + [] -> + Acc; + + [First | Rest] -> + case Predicate(First) of + true -> + count_loop(Rest, Predicate, Acc + 1); + + false -> + count_loop(Rest, Predicate, Acc) + end + end. + +-file("src/gleam/list.gleam", 89). +?DOC( + " Counts the number of elements in a given list satisfying a given predicate.\n" + "\n" + " This function has to traverse the list to determine the number of elements,\n" + " so it runs in linear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " count([], fn(a) { a > 0 })\n" + " // -> 0\n" + " ```\n" + "\n" + " ```gleam\n" + " count([1], fn(a) { a > 0 })\n" + " // -> 1\n" + " ```\n" + "\n" + " ```gleam\n" + " count([1, 2, 3], int.is_odd)\n" + " // -> 2\n" + " ```\n" +). +-spec count(list(XL), fun((XL) -> boolean())) -> integer(). +count(List, Predicate) -> + count_loop(List, Predicate, 0). + +-file("src/gleam/list.gleam", 131). +?DOC( + " Creates a new list from a given list containing the same elements but in the\n" + " opposite order.\n" + "\n" + " This function has to traverse the list to create the new reversed list, so\n" + " it runs in linear time.\n" + "\n" + " This function is natively implemented by the virtual machine and is highly\n" + " optimised.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " reverse([])\n" + " // -> []\n" + " ```\n" + "\n" + " ```gleam\n" + " reverse([1])\n" + " // -> [1]\n" + " ```\n" + "\n" + " ```gleam\n" + " reverse([1, 2])\n" + " // -> [2, 1]\n" + " ```\n" +). +-spec reverse(list(XP)) -> list(XP). +reverse(List) -> + lists:reverse(List). + +-file("src/gleam/list.gleam", 168). +?DOC( + " Determines whether or not the list is empty.\n" + "\n" + " This function runs in constant time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " is_empty([])\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " is_empty([1])\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " is_empty([1, 1])\n" + " // -> False\n" + " ```\n" +). +-spec is_empty(list(any())) -> boolean(). +is_empty(List) -> + List =:= []. + +-file("src/gleam/list.gleam", 204). +?DOC( + " Determines whether or not a given element exists within a given list.\n" + "\n" + " This function traverses the list to find the element, so it runs in linear\n" + " time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " [] |> contains(any: 0)\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " [0] |> contains(any: 0)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " [1] |> contains(any: 0)\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " [1, 1] |> contains(any: 0)\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " [1, 0] |> contains(any: 0)\n" + " // -> True\n" + " ```\n" +). +-spec contains(list(XY), XY) -> boolean(). +contains(List, Elem) -> + case List of + [] -> + false; + + [First | _] when First =:= Elem -> + true; + + [_ | Rest] -> + contains(Rest, Elem) + end. + +-file("src/gleam/list.gleam", 231). +?DOC( + " Gets the first element from the start of the list, if there is one.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " first([])\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " first([0])\n" + " // -> Ok(0)\n" + " ```\n" + "\n" + " ```gleam\n" + " first([1, 2])\n" + " // -> Ok(1)\n" + " ```\n" +). +-spec first(list(YA)) -> {ok, YA} | {error, nil}. +first(List) -> + case List of + [] -> + {error, nil}; + + [First | _] -> + {ok, First} + end. + +-file("src/gleam/list.gleam", 260). +?DOC( + " Returns the list minus the first element. If the list is empty, `Error(Nil)` is\n" + " returned.\n" + "\n" + " This function runs in constant time and does not make a copy of the list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " rest([])\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " rest([0])\n" + " // -> Ok([])\n" + " ```\n" + "\n" + " ```gleam\n" + " rest([1, 2])\n" + " // -> Ok([2])\n" + " ```\n" +). +-spec rest(list(YE)) -> {ok, list(YE)} | {error, nil}. +rest(List) -> + case List of + [] -> + {error, nil}; + + [_ | Rest] -> + {ok, Rest} + end. + +-file("src/gleam/list.gleam", 302). +-spec group_loop(list(YP), fun((YP) -> YR), gleam@dict:dict(YR, list(YP))) -> gleam@dict:dict(YR, list(YP)). +group_loop(List, To_key, Groups) -> + case List of + [] -> + Groups; + + [First | Rest] -> + Key = To_key(First), + Groups@1 = case gleam_stdlib:map_get(Groups, Key) of + {error, _} -> + gleam@dict:insert(Groups, Key, [First]); + + {ok, Existing} -> + gleam@dict:insert(Groups, Key, [First | Existing]) + end, + group_loop(Rest, To_key, Groups@1) + end. + +-file("src/gleam/list.gleam", 298). +?DOC( + " Groups the elements from the given list by the given key function.\n" + "\n" + " Does not preserve the initial value order.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " import gleam/dict\n" + "\n" + " [Ok(3), Error(\"Wrong\"), Ok(200), Ok(73)]\n" + " |> group(by: fn(i) {\n" + " case i {\n" + " Ok(_) -> \"Successful\"\n" + " Error(_) -> \"Failed\"\n" + " }\n" + " })\n" + " |> dict.to_list\n" + " // -> [\n" + " // #(\"Failed\", [Error(\"Wrong\")]),\n" + " // #(\"Successful\", [Ok(73), Ok(200), Ok(3)])\n" + " // ]\n" + " ```\n" + "\n" + " ```gleam\n" + " import gleam/dict\n" + "\n" + " group([1,2,3,4,5], by: fn(i) { i - i / 3 * 3 })\n" + " |> dict.to_list\n" + " // -> [#(0, [3]), #(1, [4, 1]), #(2, [5, 2])]\n" + " ```\n" +). +-spec group(list(YJ), fun((YJ) -> YL)) -> gleam@dict:dict(YL, list(YJ)). +group(List, Key) -> + group_loop(List, Key, maps:new()). + +-file("src/gleam/list.gleam", 339). +-spec filter_loop(list(AAB), fun((AAB) -> boolean()), list(AAB)) -> list(AAB). +filter_loop(List, Fun, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [First | Rest] -> + New_acc = case Fun(First) of + true -> + [First | Acc]; + + false -> + Acc + end, + filter_loop(Rest, Fun, New_acc) + end. + +-file("src/gleam/list.gleam", 335). +?DOC( + " Returns a new list containing only the elements from the first list for\n" + " which the given functions returns `True`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " filter([2, 4, 6, 1], fn(x) { x > 2 })\n" + " // -> [4, 6]\n" + " ```\n" + "\n" + " ```gleam\n" + " filter([2, 4, 6, 1], fn(x) { x > 6 })\n" + " // -> []\n" + " ```\n" +). +-spec filter(list(YY), fun((YY) -> boolean())) -> list(YY). +filter(List, Predicate) -> + filter_loop(List, Predicate, []). + +-file("src/gleam/list.gleam", 371). +-spec filter_map_loop( + list(AAM), + fun((AAM) -> {ok, AAO} | {error, any()}), + list(AAO) +) -> list(AAO). +filter_map_loop(List, Fun, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [First | Rest] -> + New_acc = case Fun(First) of + {ok, First@1} -> + [First@1 | Acc]; + + {error, _} -> + Acc + end, + filter_map_loop(Rest, Fun, New_acc) + end. + +-file("src/gleam/list.gleam", 367). +?DOC( + " Returns a new list containing only the elements from the first list for\n" + " which the given functions returns `Ok(_)`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " filter_map([2, 4, 6, 1], Error)\n" + " // -> []\n" + " ```\n" + "\n" + " ```gleam\n" + " filter_map([2, 4, 6, 1], fn(x) { Ok(x + 1) })\n" + " // -> [3, 5, 7, 2]\n" + " ```\n" +). +-spec filter_map(list(AAF), fun((AAF) -> {ok, AAH} | {error, any()})) -> list(AAH). +filter_map(List, Fun) -> + filter_map_loop(List, Fun, []). + +-file("src/gleam/list.gleam", 402). +-spec map_loop(list(AAY), fun((AAY) -> ABA), list(ABA)) -> list(ABA). +map_loop(List, Fun, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [First | Rest] -> + map_loop(Rest, Fun, [Fun(First) | Acc]) + end. + +-file("src/gleam/list.gleam", 398). +?DOC( + " Returns a new list containing only the elements of the first list after the\n" + " function has been applied to each one.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " map([2, 4, 6], fn(x) { x * 2 })\n" + " // -> [4, 8, 12]\n" + " ```\n" +). +-spec map(list(AAU), fun((AAU) -> AAW)) -> list(AAW). +map(List, Fun) -> + map_loop(List, Fun, []). + +-file("src/gleam/list.gleam", 429). +-spec map2_loop(list(ABJ), list(ABL), fun((ABJ, ABL) -> ABN), list(ABN)) -> list(ABN). +map2_loop(List1, List2, Fun, Acc) -> + case {List1, List2} of + {[], _} -> + lists:reverse(Acc); + + {_, []} -> + lists:reverse(Acc); + + {[A | As_], [B | Bs]} -> + map2_loop(As_, Bs, Fun, [Fun(A, B) | Acc]) + end. + +-file("src/gleam/list.gleam", 425). +?DOC( + " Combines two lists into a single list using the given function.\n" + "\n" + " If a list is longer than the other the extra elements are dropped.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " map2([1, 2, 3], [4, 5, 6], fn(x, y) { x + y })\n" + " // -> [5, 7, 9]\n" + " ```\n" + "\n" + " ```gleam\n" + " map2([1, 2], [\"a\", \"b\", \"c\"], fn(i, x) { #(i, x) })\n" + " // -> [#(1, \"a\"), #(2, \"b\")]\n" + " ```\n" +). +-spec map2(list(ABD), list(ABF), fun((ABD, ABF) -> ABH)) -> list(ABH). +map2(List1, List2, Fun) -> + map2_loop(List1, List2, Fun, []). + +-file("src/gleam/list.gleam", 462). +-spec map_fold_loop(list(ABV), fun((ABX, ABV) -> {ABX, ABY}), ABX, list(ABY)) -> {ABX, + list(ABY)}. +map_fold_loop(List, Fun, Acc, List_acc) -> + case List of + [] -> + {Acc, lists:reverse(List_acc)}; + + [First | Rest] -> + {Acc@1, First@1} = Fun(Acc, First), + map_fold_loop(Rest, Fun, Acc@1, [First@1 | List_acc]) + end. + +-file("src/gleam/list.gleam", 454). +?DOC( + " Similar to `map` but also lets you pass around an accumulated value.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " map_fold(\n" + " over: [1, 2, 3],\n" + " from: 100,\n" + " with: fn(memo, i) { #(memo + i, i * 2) }\n" + " )\n" + " // -> #(106, [2, 4, 6])\n" + " ```\n" +). +-spec map_fold(list(ABQ), ABS, fun((ABS, ABQ) -> {ABS, ABT})) -> {ABS, + list(ABT)}. +map_fold(List, Initial, Fun) -> + map_fold_loop(List, Fun, Initial, []). + +-file("src/gleam/list.gleam", 494). +-spec index_map_loop( + list(ACF), + fun((ACF, integer()) -> ACH), + integer(), + list(ACH) +) -> list(ACH). +index_map_loop(List, Fun, Index, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [First | Rest] -> + Acc@1 = [Fun(First, Index) | Acc], + index_map_loop(Rest, Fun, Index + 1, Acc@1) + end. + +-file("src/gleam/list.gleam", 490). +?DOC( + " Returns a new list containing only the elements of the first list after the\n" + " function has been applied to each one and their index.\n" + "\n" + " The index starts at 0, so the first element is 0, the second is 1, and so\n" + " on.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " index_map([\"a\", \"b\"], fn(x, i) { #(i, x) })\n" + " // -> [#(0, \"a\"), #(1, \"b\")]\n" + " ```\n" +). +-spec index_map(list(ACB), fun((ACB, integer()) -> ACD)) -> list(ACD). +index_map(List, Fun) -> + index_map_loop(List, Fun, 0, []). + +-file("src/gleam/list.gleam", 548). +-spec try_map_loop(list(ACT), fun((ACT) -> {ok, ACV} | {error, ACW}), list(ACV)) -> {ok, + list(ACV)} | + {error, ACW}. +try_map_loop(List, Fun, Acc) -> + case List of + [] -> + {ok, lists:reverse(Acc)}; + + [First | Rest] -> + case Fun(First) of + {ok, First@1} -> + try_map_loop(Rest, Fun, [First@1 | Acc]); + + {error, Error} -> + {error, Error} + end + end. + +-file("src/gleam/list.gleam", 541). +?DOC( + " Takes a function that returns a `Result` and applies it to each element in a\n" + " given list in turn.\n" + "\n" + " If the function returns `Ok(new_value)` for all elements in the list then a\n" + " list of the new values is returned.\n" + "\n" + " If the function returns `Error(reason)` for any of the elements then it is\n" + " returned immediately. None of the elements in the list are processed after\n" + " one returns an `Error`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " try_map([1, 2, 3], fn(x) { Ok(x + 2) })\n" + " // -> Ok([3, 4, 5])\n" + " ```\n" + "\n" + " ```gleam\n" + " try_map([1, 2, 3], fn(_) { Error(0) })\n" + " // -> Error(0)\n" + " ```\n" + "\n" + " ```gleam\n" + " try_map([[1], [2, 3]], first)\n" + " // -> Ok([1, 2])\n" + " ```\n" + "\n" + " ```gleam\n" + " try_map([[1], [], [2]], first)\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec try_map(list(ACK), fun((ACK) -> {ok, ACM} | {error, ACN})) -> {ok, + list(ACM)} | + {error, ACN}. +try_map(List, Fun) -> + try_map_loop(List, Fun, []). + +-file("src/gleam/list.gleam", 583). +?DOC( + " Returns a list that is the given list with up to the given number of\n" + " elements removed from the front of the list.\n" + "\n" + " If the element has less than the number of elements an empty list is\n" + " returned.\n" + "\n" + " This function runs in linear time but does not copy the list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " drop([1, 2, 3, 4], 2)\n" + " // -> [3, 4]\n" + " ```\n" + "\n" + " ```gleam\n" + " drop([1, 2, 3, 4], 9)\n" + " // -> []\n" + " ```\n" +). +-spec drop(list(ADD), integer()) -> list(ADD). +drop(List, N) -> + case N =< 0 of + true -> + List; + + false -> + case List of + [] -> + []; + + [_ | Rest] -> + drop(Rest, N - 1) + end + end. + +-file("src/gleam/list.gleam", 618). +-spec take_loop(list(ADJ), integer(), list(ADJ)) -> list(ADJ). +take_loop(List, N, Acc) -> + case N =< 0 of + true -> + lists:reverse(Acc); + + false -> + case List of + [] -> + lists:reverse(Acc); + + [First | Rest] -> + take_loop(Rest, N - 1, [First | Acc]) + end + end. + +-file("src/gleam/list.gleam", 614). +?DOC( + " Returns a list containing the first given number of elements from the given\n" + " list.\n" + "\n" + " If the element has less than the number of elements then the full list is\n" + " returned.\n" + "\n" + " This function runs in linear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " take([1, 2, 3, 4], 2)\n" + " // -> [1, 2]\n" + " ```\n" + "\n" + " ```gleam\n" + " take([1, 2, 3, 4], 9)\n" + " // -> [1, 2, 3, 4]\n" + " ```\n" +). +-spec take(list(ADG), integer()) -> list(ADG). +take(List, N) -> + take_loop(List, N, []). + +-file("src/gleam/list.gleam", 638). +?DOC( + " Returns a new empty list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " new()\n" + " // -> []\n" + " ```\n" +). +-spec new() -> list(any()). +new() -> + []. + +-file("src/gleam/list.gleam", 658). +?DOC( + " Returns the given item wrapped in a list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " wrap(1)\n" + " // -> [1]\n" + "\n" + " wrap([\"a\", \"b\", \"c\"])\n" + " // -> [[\"a\", \"b\", \"c\"]]\n" + "\n" + " wrap([[]])\n" + " // -> [[[]]]\n" + " ```\n" +). +-spec wrap(ADP) -> list(ADP). +wrap(Item) -> + [Item]. + +-file("src/gleam/list.gleam", 679). +-spec append_loop(list(ADV), list(ADV)) -> list(ADV). +append_loop(First, Second) -> + case First of + [] -> + Second; + + [First@1 | Rest] -> + append_loop(Rest, [First@1 | Second]) + end. + +-file("src/gleam/list.gleam", 675). +?DOC( + " Joins one list onto the end of another.\n" + "\n" + " This function runs in linear time, and it traverses and copies the first\n" + " list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " append([1, 2], [3])\n" + " // -> [1, 2, 3]\n" + " ```\n" +). +-spec append(list(ADR), list(ADR)) -> list(ADR). +append(First, Second) -> + lists:append(First, Second). + +-file("src/gleam/list.gleam", 699). +?DOC( + " Prefixes an item to a list. This can also be done using the dedicated\n" + " syntax instead\n" + "\n" + " ```gleam\n" + " let existing_list = [2, 3, 4]\n" + "\n" + " [1, ..existing_list]\n" + " // -> [1, 2, 3, 4]\n" + "\n" + " prepend(to: existing_list, this: 1)\n" + " // -> [1, 2, 3, 4]\n" + " ```\n" +). +-spec prepend(list(ADZ), ADZ) -> list(ADZ). +prepend(List, Item) -> + [Item | List]. + +-file("src/gleam/list.gleam", 720). +-spec flatten_loop(list(list(AEG)), list(AEG)) -> list(AEG). +flatten_loop(Lists, Acc) -> + case Lists of + [] -> + lists:reverse(Acc); + + [List | Further_lists] -> + flatten_loop(Further_lists, lists:reverse(List, Acc)) + end. + +-file("src/gleam/list.gleam", 716). +?DOC( + " Joins a list of lists into a single list.\n" + "\n" + " This function traverses all elements twice on the JavaScript target.\n" + " This function traverses all elements once on the Erlang target.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " flatten([[1], [2, 3], []])\n" + " // -> [1, 2, 3]\n" + " ```\n" +). +-spec flatten(list(list(AEC))) -> list(AEC). +flatten(Lists) -> + lists:append(Lists). + +-file("src/gleam/list.gleam", 737). +?DOC( + " Maps the list with the given function into a list of lists, and then flattens it.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " flat_map([2, 4, 6], fn(x) { [x, x + 1] })\n" + " // -> [2, 3, 4, 5, 6, 7]\n" + " ```\n" +). +-spec flat_map(list(AEL), fun((AEL) -> list(AEN))) -> list(AEN). +flat_map(List, Fun) -> + lists:append(map(List, Fun)). + +-file("src/gleam/list.gleam", 749). +?DOC( + " Reduces a list of elements into a single value by calling a given function\n" + " on each element, going from left to right.\n" + "\n" + " `fold([1, 2, 3], 0, add)` is the equivalent of\n" + " `add(add(add(0, 1), 2), 3)`.\n" + "\n" + " This function runs in linear time.\n" +). +-spec fold(list(AEQ), AES, fun((AES, AEQ) -> AES)) -> AES. +fold(List, Initial, Fun) -> + case List of + [] -> + Initial; + + [First | Rest] -> + fold(Rest, Fun(Initial, First), Fun) + end. + +-file("src/gleam/list.gleam", 771). +?DOC( + " Reduces a list of elements into a single value by calling a given function\n" + " on each element, going from right to left.\n" + "\n" + " `fold_right([1, 2, 3], 0, add)` is the equivalent of\n" + " `add(add(add(0, 3), 2), 1)`.\n" + "\n" + " This function runs in linear time.\n" + "\n" + " Unlike `fold` this function is not tail recursive. Where possible use\n" + " `fold` instead as it will use less memory.\n" +). +-spec fold_right(list(AET), AEV, fun((AEV, AET) -> AEV)) -> AEV. +fold_right(List, Initial, Fun) -> + case List of + [] -> + Initial; + + [First | Rest] -> + Fun(fold_right(Rest, Initial, Fun), First) + end. + +-file("src/gleam/list.gleam", 808). +-spec index_fold_loop( + list(AEZ), + AFB, + fun((AFB, AEZ, integer()) -> AFB), + integer() +) -> AFB. +index_fold_loop(Over, Acc, With, Index) -> + case Over of + [] -> + Acc; + + [First | Rest] -> + index_fold_loop(Rest, With(Acc, First, Index), With, Index + 1) + end. + +-file("src/gleam/list.gleam", 800). +?DOC( + " Like fold but the folding function also receives the index of the current element.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " [\"a\", \"b\", \"c\"]\n" + " |> index_fold(\"\", fn(acc, item, index) {\n" + " acc <> int.to_string(index) <> \":\" <> item <> \" \"\n" + " })\n" + " // -> \"0:a 1:b 2:c\"\n" + " ```\n" + "\n" + " ```gleam\n" + " [10, 20, 30]\n" + " |> index_fold(0, fn(acc, item, index) { acc + item * index })\n" + " // -> 80\n" + " ```\n" +). +-spec index_fold(list(AEW), AEY, fun((AEY, AEW, integer()) -> AEY)) -> AEY. +index_fold(List, Initial, Fun) -> + index_fold_loop(List, Initial, Fun, 0). + +-file("src/gleam/list.gleam", 840). +?DOC( + " A variant of fold that might fail.\n" + "\n" + " The folding function should return `Result(accumulator, error)`.\n" + " If the returned value is `Ok(accumulator)` try_fold will try the next value in the list.\n" + " If the returned value is `Error(error)` try_fold will stop and return that error.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " [1, 2, 3, 4]\n" + " |> try_fold(0, fn(acc, i) {\n" + " case i < 3 {\n" + " True -> Ok(acc + i)\n" + " False -> Error(Nil)\n" + " }\n" + " })\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec try_fold(list(AFC), AFE, fun((AFE, AFC) -> {ok, AFE} | {error, AFF})) -> {ok, + AFE} | + {error, AFF}. +try_fold(List, Initial, Fun) -> + case List of + [] -> + {ok, Initial}; + + [First | Rest] -> + case Fun(Initial, First) of + {ok, Result} -> + try_fold(Rest, Result, Fun); + + {error, _} = Error -> + Error + end + end. + +-file("src/gleam/list.gleam", 879). +?DOC( + " A variant of fold that allows to stop folding earlier.\n" + "\n" + " The folding function should return `ContinueOrStop(accumulator)`.\n" + " If the returned value is `Continue(accumulator)` fold_until will try the next value in the list.\n" + " If the returned value is `Stop(accumulator)` fold_until will stop and return that accumulator.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " [1, 2, 3, 4]\n" + " |> fold_until(0, fn(acc, i) {\n" + " case i < 3 {\n" + " True -> Continue(acc + i)\n" + " False -> Stop(acc)\n" + " }\n" + " })\n" + " // -> 3\n" + " ```\n" +). +-spec fold_until(list(AFK), AFM, fun((AFM, AFK) -> continue_or_stop(AFM))) -> AFM. +fold_until(List, Initial, Fun) -> + case List of + [] -> + Initial; + + [First | Rest] -> + case Fun(Initial, First) of + {continue, Next_accumulator} -> + fold_until(Rest, Next_accumulator, Fun); + + {stop, B} -> + B + end + end. + +-file("src/gleam/list.gleam", 916). +?DOC( + " Finds the first element in a given list for which the given function returns\n" + " `True`.\n" + "\n" + " Returns `Error(Nil)` if no such element is found.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " find([1, 2, 3], fn(x) { x > 2 })\n" + " // -> Ok(3)\n" + " ```\n" + "\n" + " ```gleam\n" + " find([1, 2, 3], fn(x) { x > 4 })\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " find([], fn(_) { True })\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec find(list(AFO), fun((AFO) -> boolean())) -> {ok, AFO} | {error, nil}. +find(List, Is_desired) -> + case List of + [] -> + {error, nil}; + + [First | Rest] -> + case Is_desired(First) of + true -> + {ok, First}; + + false -> + find(Rest, Is_desired) + end + end. + +-file("src/gleam/list.gleam", 952). +?DOC( + " Finds the first element in a given list for which the given function returns\n" + " `Ok(new_value)`, then returns the wrapped `new_value`.\n" + "\n" + " Returns `Error(Nil)` if no such element is found.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " find_map([[], [2], [3]], first)\n" + " // -> Ok(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " find_map([[], []], first)\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " find_map([], first)\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec find_map(list(AFS), fun((AFS) -> {ok, AFU} | {error, any()})) -> {ok, AFU} | + {error, nil}. +find_map(List, Fun) -> + case List of + [] -> + {error, nil}; + + [First | Rest] -> + case Fun(First) of + {ok, First@1} -> + {ok, First@1}; + + {error, _} -> + find_map(Rest, Fun) + end + end. + +-file("src/gleam/list.gleam", 987). +?DOC( + " Returns `True` if the given function returns `True` for all the elements in\n" + " the given list. If the function returns `False` for any of the elements it\n" + " immediately returns `False` without checking the rest of the list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " all([], fn(x) { x > 3 })\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " all([4, 5], fn(x) { x > 3 })\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " all([4, 3], fn(x) { x > 3 })\n" + " // -> False\n" + " ```\n" +). +-spec all(list(AGA), fun((AGA) -> boolean())) -> boolean(). +all(List, Predicate) -> + case List of + [] -> + true; + + [First | Rest] -> + case Predicate(First) of + true -> + all(Rest, Predicate); + + false -> + false + end + end. + +-file("src/gleam/list.gleam", 1024). +?DOC( + " Returns `True` if the given function returns `True` for any the elements in\n" + " the given list. If the function returns `True` for any of the elements it\n" + " immediately returns `True` without checking the rest of the list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " any([], fn(x) { x > 3 })\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " any([4, 5], fn(x) { x > 3 })\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " any([4, 3], fn(x) { x > 4 })\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " any([3, 4], fn(x) { x > 3 })\n" + " // -> True\n" + " ```\n" +). +-spec any(list(AGC), fun((AGC) -> boolean())) -> boolean(). +any(List, Predicate) -> + case List of + [] -> + false; + + [First | Rest] -> + case Predicate(First) of + true -> + true; + + false -> + any(Rest, Predicate) + end + end. + +-file("src/gleam/list.gleam", 1066). +-spec zip_loop(list(AGJ), list(AGL), list({AGJ, AGL})) -> list({AGJ, AGL}). +zip_loop(One, Other, Acc) -> + case {One, Other} of + {[First_one | Rest_one], [First_other | Rest_other]} -> + zip_loop(Rest_one, Rest_other, [{First_one, First_other} | Acc]); + + {_, _} -> + lists:reverse(Acc) + end. + +-file("src/gleam/list.gleam", 1062). +?DOC( + " Takes two lists and returns a single list of 2-element tuples.\n" + "\n" + " If one of the lists is longer than the other, the remaining elements from\n" + " the longer list are not used.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " zip([], [])\n" + " // -> []\n" + " ```\n" + "\n" + " ```gleam\n" + " zip([1, 2], [3])\n" + " // -> [#(1, 3)]\n" + " ```\n" + "\n" + " ```gleam\n" + " zip([1], [3, 4])\n" + " // -> [#(1, 3)]\n" + " ```\n" + "\n" + " ```gleam\n" + " zip([1, 2], [3, 4])\n" + " // -> [#(1, 3), #(2, 4)]\n" + " ```\n" +). +-spec zip(list(AGE), list(AGG)) -> list({AGE, AGG}). +zip(List, Other) -> + zip_loop(List, Other, []). + +-file("src/gleam/list.gleam", 1107). +-spec strict_zip_loop(list(AGW), list(AGY), list({AGW, AGY})) -> {ok, + list({AGW, AGY})} | + {error, nil}. +strict_zip_loop(One, Other, Acc) -> + case {One, Other} of + {[], []} -> + {ok, lists:reverse(Acc)}; + + {[], _} -> + {error, nil}; + + {_, []} -> + {error, nil}; + + {[First_one | Rest_one], [First_other | Rest_other]} -> + strict_zip_loop( + Rest_one, + Rest_other, + [{First_one, First_other} | Acc] + ) + end. + +-file("src/gleam/list.gleam", 1100). +?DOC( + " Takes two lists and returns a single list of 2-element tuples.\n" + "\n" + " If one of the lists is longer than the other, an `Error` is returned.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " strict_zip([], [])\n" + " // -> Ok([])\n" + " ```\n" + "\n" + " ```gleam\n" + " strict_zip([1, 2], [3])\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " strict_zip([1], [3, 4])\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " strict_zip([1, 2], [3, 4])\n" + " // -> Ok([#(1, 3), #(2, 4)])\n" + " ```\n" +). +-spec strict_zip(list(AGP), list(AGR)) -> {ok, list({AGP, AGR})} | {error, nil}. +strict_zip(List, Other) -> + strict_zip_loop(List, Other, []). + +-file("src/gleam/list.gleam", 1138). +-spec unzip_loop(list({AHJ, AHK}), list(AHJ), list(AHK)) -> {list(AHJ), + list(AHK)}. +unzip_loop(Input, One, Other) -> + case Input of + [] -> + {lists:reverse(One), lists:reverse(Other)}; + + [{First_one, First_other} | Rest] -> + unzip_loop(Rest, [First_one | One], [First_other | Other]) + end. + +-file("src/gleam/list.gleam", 1134). +?DOC( + " Takes a single list of 2-element tuples and returns two lists.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " unzip([#(1, 2), #(3, 4)])\n" + " // -> #([1, 3], [2, 4])\n" + " ```\n" + "\n" + " ```gleam\n" + " unzip([])\n" + " // -> #([], [])\n" + " ```\n" +). +-spec unzip(list({AHE, AHF})) -> {list(AHE), list(AHF)}. +unzip(Input) -> + unzip_loop(Input, [], []). + +-file("src/gleam/list.gleam", 1173). +-spec intersperse_loop(list(AHT), AHT, list(AHT)) -> list(AHT). +intersperse_loop(List, Separator, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [First | Rest] -> + intersperse_loop(Rest, Separator, [First, Separator | Acc]) + end. + +-file("src/gleam/list.gleam", 1166). +?DOC( + " Inserts a given value between each existing element in a given list.\n" + "\n" + " This function runs in linear time and copies the list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " intersperse([1, 1, 1], 2)\n" + " // -> [1, 2, 1, 2, 1]\n" + " ```\n" + "\n" + " ```gleam\n" + " intersperse([], 2)\n" + " // -> []\n" + " ```\n" +). +-spec intersperse(list(AHQ), AHQ) -> list(AHQ). +intersperse(List, Elem) -> + case List of + [] -> + List; + + [_] -> + List; + + [First | Rest] -> + intersperse_loop(Rest, Elem, [First]) + end. + +-file("src/gleam/list.gleam", 1196). +-spec unique_loop(list(AIA), gleam@dict:dict(AIA, nil), list(AIA)) -> list(AIA). +unique_loop(List, Seen, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [First | Rest] -> + case gleam@dict:has_key(Seen, First) of + true -> + unique_loop(Rest, Seen, Acc); + + false -> + unique_loop( + Rest, + gleam@dict:insert(Seen, First, nil), + [First | Acc] + ) + end + end. + +-file("src/gleam/list.gleam", 1192). +?DOC( + " Removes any duplicate elements from a given list.\n" + "\n" + " This function returns in loglinear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " unique([1, 1, 1, 4, 7, 3, 3, 4])\n" + " // -> [1, 4, 7, 3]\n" + " ```\n" +). +-spec unique(list(AHX)) -> list(AHX). +unique(List) -> + unique_loop(List, maps:new(), []). + +-file("src/gleam/list.gleam", 1282). +?DOC( + " Given a list it returns slices of it that are locally sorted in ascending\n" + " order.\n" + "\n" + " Imagine you have this list:\n" + "\n" + " ```\n" + " [1, 2, 3, 2, 1, 0]\n" + " ^^^^^^^ ^^^^^^^ This is a slice in descending order\n" + " |\n" + " | This is a slice that is sorted in ascending order\n" + " ```\n" + "\n" + " So the produced result will contain these two slices, each one sorted in\n" + " ascending order: `[[1, 2, 3], [0, 1, 2]]`.\n" + "\n" + " - `growing` is an accumulator with the current slice being grown\n" + " - `direction` is the growing direction of the slice being grown, it could\n" + " either be ascending or strictly descending\n" + " - `prev` is the previous element that needs to be added to the growing slice\n" + " it is carried around to check whether we have to keep growing the current\n" + " slice or not\n" + " - `acc` is the accumulator containing the slices sorted in ascending order\n" +). +-spec sequences( + list(AIJ), + fun((AIJ, AIJ) -> gleam@order:order()), + list(AIJ), + sorting(), + AIJ, + list(list(AIJ)) +) -> list(list(AIJ)). +sequences(List, Compare, Growing, Direction, Prev, Acc) -> + Growing@1 = [Prev | Growing], + case List of + [] -> + case Direction of + ascending -> + [lists:reverse(Growing@1) | Acc]; + + descending -> + [Growing@1 | Acc] + end; + + [New | Rest] -> + case {Compare(Prev, New), Direction} of + {gt, descending} -> + sequences(Rest, Compare, Growing@1, Direction, New, Acc); + + {lt, ascending} -> + sequences(Rest, Compare, Growing@1, Direction, New, Acc); + + {eq, ascending} -> + sequences(Rest, Compare, Growing@1, Direction, New, Acc); + + {gt, ascending} -> + Acc@1 = case Direction of + ascending -> + [lists:reverse(Growing@1) | Acc]; + + descending -> + [Growing@1 | Acc] + end, + case Rest of + [] -> + [[New] | Acc@1]; + + [Next | Rest@1] -> + Direction@1 = case Compare(New, Next) of + lt -> + ascending; + + eq -> + ascending; + + gt -> + descending + end, + sequences( + Rest@1, + Compare, + [New], + Direction@1, + Next, + Acc@1 + ) + end; + + {lt, descending} -> + Acc@1 = case Direction of + ascending -> + [lists:reverse(Growing@1) | Acc]; + + descending -> + [Growing@1 | Acc] + end, + case Rest of + [] -> + [[New] | Acc@1]; + + [Next | Rest@1] -> + Direction@1 = case Compare(New, Next) of + lt -> + ascending; + + eq -> + ascending; + + gt -> + descending + end, + sequences( + Rest@1, + Compare, + [New], + Direction@1, + Next, + Acc@1 + ) + end; + + {eq, descending} -> + Acc@1 = case Direction of + ascending -> + [lists:reverse(Growing@1) | Acc]; + + descending -> + [Growing@1 | Acc] + end, + case Rest of + [] -> + [[New] | Acc@1]; + + [Next | Rest@1] -> + Direction@1 = case Compare(New, Next) of + lt -> + ascending; + + eq -> + ascending; + + gt -> + descending + end, + sequences( + Rest@1, + Compare, + [New], + Direction@1, + Next, + Acc@1 + ) + end + end + end. + +-file("src/gleam/list.gleam", 1430). +?DOC( + " Merges two lists sorted in ascending order into a single list sorted in\n" + " descending order according to the given comparator function.\n" + "\n" + " This reversing of the sort order is not avoidable if we want to implement\n" + " merge as a tail recursive function. We could reverse the accumulator before\n" + " returning it but that would end up being less efficient; so the merging\n" + " algorithm has to play around this.\n" +). +-spec merge_ascendings( + list(AJG), + list(AJG), + fun((AJG, AJG) -> gleam@order:order()), + list(AJG) +) -> list(AJG). +merge_ascendings(List1, List2, Compare, Acc) -> + case {List1, List2} of + {[], List} -> + lists:reverse(List, Acc); + + {List, []} -> + lists:reverse(List, Acc); + + {[First1 | Rest1], [First2 | Rest2]} -> + case Compare(First1, First2) of + lt -> + merge_ascendings(Rest1, List2, Compare, [First1 | Acc]); + + gt -> + merge_ascendings(List1, Rest2, Compare, [First2 | Acc]); + + eq -> + merge_ascendings(List1, Rest2, Compare, [First2 | Acc]) + end + end. + +-file("src/gleam/list.gleam", 1383). +?DOC( + " Given a list of ascending lists, it merges adjacent pairs into a single\n" + " descending list, halving their number.\n" + " It returns a list of the remaining descending lists.\n" +). +-spec merge_ascending_pairs( + list(list(AIU)), + fun((AIU, AIU) -> gleam@order:order()), + list(list(AIU)) +) -> list(list(AIU)). +merge_ascending_pairs(Sequences, Compare, Acc) -> + case Sequences of + [] -> + lists:reverse(Acc); + + [Sequence] -> + lists:reverse([lists:reverse(Sequence) | Acc]); + + [Ascending1, Ascending2 | Rest] -> + Descending = merge_ascendings(Ascending1, Ascending2, Compare, []), + merge_ascending_pairs(Rest, Compare, [Descending | Acc]) + end. + +-file("src/gleam/list.gleam", 1457). +?DOC( + " This is exactly the same as merge_ascendings but mirrored: it merges two\n" + " lists sorted in descending order into a single list sorted in ascending\n" + " order according to the given comparator function.\n" + "\n" + " This reversing of the sort order is not avoidable if we want to implement\n" + " merge as a tail recursive function. We could reverse the accumulator before\n" + " returning it but that would end up being less efficient; so the merging\n" + " algorithm has to play around this.\n" +). +-spec merge_descendings( + list(AJL), + list(AJL), + fun((AJL, AJL) -> gleam@order:order()), + list(AJL) +) -> list(AJL). +merge_descendings(List1, List2, Compare, Acc) -> + case {List1, List2} of + {[], List} -> + lists:reverse(List, Acc); + + {List, []} -> + lists:reverse(List, Acc); + + {[First1 | Rest1], [First2 | Rest2]} -> + case Compare(First1, First2) of + lt -> + merge_descendings(List1, Rest2, Compare, [First2 | Acc]); + + gt -> + merge_descendings(Rest1, List2, Compare, [First1 | Acc]); + + eq -> + merge_descendings(Rest1, List2, Compare, [First1 | Acc]) + end + end. + +-file("src/gleam/list.gleam", 1405). +?DOC(" This is the same as merge_ascending_pairs but flipped for descending lists.\n"). +-spec merge_descending_pairs( + list(list(AJA)), + fun((AJA, AJA) -> gleam@order:order()), + list(list(AJA)) +) -> list(list(AJA)). +merge_descending_pairs(Sequences, Compare, Acc) -> + case Sequences of + [] -> + lists:reverse(Acc); + + [Sequence] -> + lists:reverse([lists:reverse(Sequence) | Acc]); + + [Descending1, Descending2 | Rest] -> + Ascending = merge_descendings(Descending1, Descending2, Compare, []), + merge_descending_pairs(Rest, Compare, [Ascending | Acc]) + end. + +-file("src/gleam/list.gleam", 1349). +?DOC( + " Given some some sorted sequences (assumed to be sorted in `direction`) it\n" + " merges them all together until we're left with just a list sorted in\n" + " ascending order.\n" +). +-spec merge_all( + list(list(AIQ)), + sorting(), + fun((AIQ, AIQ) -> gleam@order:order()) +) -> list(AIQ). +merge_all(Sequences, Direction, Compare) -> + case {Sequences, Direction} of + {[], _} -> + []; + + {[Sequence], ascending} -> + Sequence; + + {[Sequence@1], descending} -> + lists:reverse(Sequence@1); + + {_, ascending} -> + Sequences@1 = merge_ascending_pairs(Sequences, Compare, []), + merge_all(Sequences@1, descending, Compare); + + {_, descending} -> + Sequences@2 = merge_descending_pairs(Sequences, Compare, []), + merge_all(Sequences@2, ascending, Compare) + end. + +-file("src/gleam/list.gleam", 1220). +?DOC( + " Sorts from smallest to largest based upon the ordering specified by a given\n" + " function.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " import gleam/int\n" + "\n" + " sort([4, 3, 6, 5, 4, 1, 2], by: int.compare)\n" + " // -> [1, 2, 3, 4, 4, 5, 6]\n" + " ```\n" +). +-spec sort(list(AIG), fun((AIG, AIG) -> gleam@order:order())) -> list(AIG). +sort(List, Compare) -> + case List of + [] -> + []; + + [X] -> + [X]; + + [X@1, Y | Rest] -> + Direction = case Compare(X@1, Y) of + lt -> + ascending; + + eq -> + ascending; + + gt -> + descending + end, + Sequences = sequences(Rest, Compare, [X@1], Direction, Y, []), + merge_all(Sequences, ascending, Compare) + end. + +-file("src/gleam/list.gleam", 1497). +-spec range_loop(integer(), integer(), list(integer())) -> list(integer()). +range_loop(Start, Stop, Acc) -> + case gleam@int:compare(Start, Stop) of + eq -> + [Stop | Acc]; + + gt -> + range_loop(Start, Stop + 1, [Stop | Acc]); + + lt -> + range_loop(Start, Stop - 1, [Stop | Acc]) + end. + +-file("src/gleam/list.gleam", 1493). +?DOC( + " Creates a list of ints ranging from a given start and finish.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " range(0, 0)\n" + " // -> [0]\n" + " ```\n" + "\n" + " ```gleam\n" + " range(0, 5)\n" + " // -> [0, 1, 2, 3, 4, 5]\n" + " ```\n" + "\n" + " ```gleam\n" + " range(1, -5)\n" + " // -> [1, 0, -1, -2, -3, -4, -5]\n" + " ```\n" +). +-spec range(integer(), integer()) -> list(integer()). +range(Start, Stop) -> + range_loop(Start, Stop, []). + +-file("src/gleam/list.gleam", 1523). +-spec repeat_loop(AJV, integer(), list(AJV)) -> list(AJV). +repeat_loop(Item, Times, Acc) -> + case Times =< 0 of + true -> + Acc; + + false -> + repeat_loop(Item, Times - 1, [Item | Acc]) + end. + +-file("src/gleam/list.gleam", 1519). +?DOC( + " Builds a list of a given value a given number of times.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " repeat(\"a\", times: 0)\n" + " // -> []\n" + " ```\n" + "\n" + " ```gleam\n" + " repeat(\"a\", times: 5)\n" + " // -> [\"a\", \"a\", \"a\", \"a\", \"a\"]\n" + " ```\n" +). +-spec repeat(AJT, integer()) -> list(AJT). +repeat(A, Times) -> + repeat_loop(A, Times, []). + +-file("src/gleam/list.gleam", 1556). +-spec split_loop(list(AKC), integer(), list(AKC)) -> {list(AKC), list(AKC)}. +split_loop(List, N, Taken) -> + case N =< 0 of + true -> + {lists:reverse(Taken), List}; + + false -> + case List of + [] -> + {lists:reverse(Taken), []}; + + [First | Rest] -> + split_loop(Rest, N - 1, [First | Taken]) + end + end. + +-file("src/gleam/list.gleam", 1552). +?DOC( + " Splits a list in two before the given index.\n" + "\n" + " If the list is not long enough to have the given index the before list will\n" + " be the input list, and the after list will be empty.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " split([6, 7, 8, 9], 0)\n" + " // -> #([], [6, 7, 8, 9])\n" + " ```\n" + "\n" + " ```gleam\n" + " split([6, 7, 8, 9], 2)\n" + " // -> #([6, 7], [8, 9])\n" + " ```\n" + "\n" + " ```gleam\n" + " split([6, 7, 8, 9], 4)\n" + " // -> #([6, 7, 8, 9], [])\n" + " ```\n" +). +-spec split(list(AJY), integer()) -> {list(AJY), list(AJY)}. +split(List, Index) -> + split_loop(List, Index, []). + +-file("src/gleam/list.gleam", 1592). +-spec split_while_loop(list(AKL), fun((AKL) -> boolean()), list(AKL)) -> {list(AKL), + list(AKL)}. +split_while_loop(List, F, Acc) -> + case List of + [] -> + {lists:reverse(Acc), []}; + + [First | Rest] -> + case F(First) of + true -> + split_while_loop(Rest, F, [First | Acc]); + + false -> + {lists:reverse(Acc), List} + end + end. + +-file("src/gleam/list.gleam", 1585). +?DOC( + " Splits a list in two before the first element that a given function returns\n" + " `False` for.\n" + "\n" + " If the function returns `True` for all elements the first list will be the\n" + " input list, and the second list will be empty.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " split_while([1, 2, 3, 4, 5], fn(x) { x <= 3 })\n" + " // -> #([1, 2, 3], [4, 5])\n" + " ```\n" + "\n" + " ```gleam\n" + " split_while([1, 2, 3, 4, 5], fn(x) { x <= 5 })\n" + " // -> #([1, 2, 3, 4, 5], [])\n" + " ```\n" +). +-spec split_while(list(AKH), fun((AKH) -> boolean())) -> {list(AKH), list(AKH)}. +split_while(List, Predicate) -> + split_while_loop(List, Predicate, []). + +-file("src/gleam/list.gleam", 1632). +?DOC( + " Given a list of 2-element tuples, finds the first tuple that has a given\n" + " key as the first element and returns the second element.\n" + "\n" + " If no tuple is found with the given key then `Error(Nil)` is returned.\n" + "\n" + " This function may be useful for interacting with Erlang code where lists of\n" + " tuples are common.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " key_find([#(\"a\", 0), #(\"b\", 1)], \"a\")\n" + " // -> Ok(0)\n" + " ```\n" + "\n" + " ```gleam\n" + " key_find([#(\"a\", 0), #(\"b\", 1)], \"b\")\n" + " // -> Ok(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " key_find([#(\"a\", 0), #(\"b\", 1)], \"c\")\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec key_find(list({AKQ, AKR}), AKQ) -> {ok, AKR} | {error, nil}. +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 + ). + +-file("src/gleam/list.gleam", 1663). +?DOC( + " Given a list of 2-element tuples, finds all tuples that have a given\n" + " key as the first element and returns the second element.\n" + "\n" + " This function may be useful for interacting with Erlang code where lists of\n" + " tuples are common.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " key_filter([#(\"a\", 0), #(\"b\", 1), #(\"a\", 2)], \"a\")\n" + " // -> [0, 2]\n" + " ```\n" + "\n" + " ```gleam\n" + " key_filter([#(\"a\", 0), #(\"b\", 1)], \"c\")\n" + " // -> []\n" + " ```\n" +). +-spec key_filter(list({AKV, AKW}), AKV) -> list(AKW). +key_filter(Keyword_list, Desired_key) -> + filter_map( + Keyword_list, + fun(Keyword) -> + {Key, Value} = Keyword, + case Key =:= Desired_key of + true -> + {ok, Value}; + + false -> + {error, nil} + end + end + ). + +-file("src/gleam/list.gleam", 1703). +-spec key_pop_loop(list({ALF, ALG}), ALF, list({ALF, ALG})) -> {ok, + {ALG, list({ALF, ALG})}} | + {error, nil}. +key_pop_loop(List, Key, Checked) -> + case List of + [] -> + {error, nil}; + + [{K, V} | Rest] when K =:= Key -> + {ok, {V, lists:reverse(Checked, Rest)}}; + + [First | Rest@1] -> + key_pop_loop(Rest@1, Key, [First | Checked]) + end. + +-file("src/gleam/list.gleam", 1699). +?DOC( + " Given a list of 2-element tuples, finds the first tuple that has a given\n" + " key as the first element. This function will return the second element\n" + " of the found tuple and list with tuple removed.\n" + "\n" + " If no tuple is found with the given key then `Error(Nil)` is returned.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " key_pop([#(\"a\", 0), #(\"b\", 1)], \"a\")\n" + " // -> Ok(#(0, [#(\"b\", 1)]))\n" + " ```\n" + "\n" + " ```gleam\n" + " key_pop([#(\"a\", 0), #(\"b\", 1)], \"b\")\n" + " // -> Ok(#(1, [#(\"a\", 0)]))\n" + " ```\n" + "\n" + " ```gleam\n" + " key_pop([#(\"a\", 0), #(\"b\", 1)], \"c\")\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec key_pop(list({AKZ, ALA}), AKZ) -> {ok, {ALA, list({AKZ, ALA})}} | + {error, nil}. +key_pop(List, Key) -> + key_pop_loop(List, Key, []). + +-file("src/gleam/list.gleam", 1737). +-spec key_set_loop(list({ALQ, ALR}), ALQ, ALR, list({ALQ, ALR})) -> list({ALQ, + ALR}). +key_set_loop(List, Key, Value, Inspected) -> + case List of + [{K, _} | Rest] when K =:= Key -> + lists:reverse(Inspected, [{K, Value} | Rest]); + + [First | Rest@1] -> + key_set_loop(Rest@1, Key, Value, [First | Inspected]); + + [] -> + lists:reverse([{Key, Value} | Inspected]) + end. + +-file("src/gleam/list.gleam", 1733). +?DOC( + " Given a list of 2-element tuples, inserts a key and value into the list.\n" + "\n" + " If there was already a tuple with the key then it is replaced, otherwise it\n" + " is added to the end of the list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " key_set([#(5, 0), #(4, 1)], 4, 100)\n" + " // -> [#(5, 0), #(4, 100)]\n" + " ```\n" + "\n" + " ```gleam\n" + " key_set([#(5, 0), #(4, 1)], 1, 100)\n" + " // -> [#(5, 0), #(4, 1), #(1, 100)]\n" + " ```\n" +). +-spec key_set(list({ALM, ALN}), ALM, ALN) -> list({ALM, ALN}). +key_set(List, Key, Value) -> + key_set_loop(List, Key, Value, []). + +-file("src/gleam/list.gleam", 1765). +?DOC( + " Calls a function for each element in a list, discarding the return value.\n" + "\n" + " Useful for calling a side effect for every item of a list.\n" + "\n" + " ```gleam\n" + " import gleam/io\n" + "\n" + " each([\"1\", \"2\", \"3\"], io.println)\n" + " // -> Nil\n" + " // 1\n" + " // 2\n" + " // 3\n" + " ```\n" +). +-spec each(list(ALV), fun((ALV) -> any())) -> nil. +each(List, F) -> + case List of + [] -> + nil; + + [First | Rest] -> + F(First), + each(Rest, F) + end. + +-file("src/gleam/list.gleam", 1791). +?DOC( + " Calls a `Result` returning function for each element in a list, discarding\n" + " the return value. If the function returns `Error` then the iteration is\n" + " stopped and the error is returned.\n" + "\n" + " Useful for calling a side effect for every item of a list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " try_each(\n" + " over: [1, 2, 3],\n" + " with: function_that_might_fail,\n" + " )\n" + " // -> Ok(Nil)\n" + " ```\n" +). +-spec try_each(list(ALY), fun((ALY) -> {ok, any()} | {error, AMB})) -> {ok, nil} | + {error, AMB}. +try_each(List, Fun) -> + case List of + [] -> + {ok, nil}; + + [First | Rest] -> + case Fun(First) of + {ok, _} -> + try_each(Rest, Fun); + + {error, E} -> + {error, E} + end + end. + +-file("src/gleam/list.gleam", 1824). +-spec partition_loop(list(BFR), fun((BFR) -> boolean()), list(BFR), list(BFR)) -> {list(BFR), + list(BFR)}. +partition_loop(List, Categorise, Trues, Falses) -> + case List of + [] -> + {lists:reverse(Trues), lists:reverse(Falses)}; + + [First | Rest] -> + case Categorise(First) of + true -> + partition_loop(Rest, Categorise, [First | Trues], Falses); + + false -> + partition_loop(Rest, Categorise, Trues, [First | Falses]) + end + end. + +-file("src/gleam/list.gleam", 1817). +?DOC( + " Partitions a list into a tuple/pair of lists\n" + " by a given categorisation function.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " import gleam/int\n" + "\n" + " [1, 2, 3, 4, 5] |> partition(int.is_odd)\n" + " // -> #([1, 3, 5], [2, 4])\n" + " ```\n" +). +-spec partition(list(AMG), fun((AMG) -> boolean())) -> {list(AMG), list(AMG)}. +partition(List, Categorise) -> + partition_loop(List, Categorise, [], []). + +-file("src/gleam/list.gleam", 1904). +-spec window_loop(list(list(ANN)), list(ANN), integer()) -> list(list(ANN)). +window_loop(Acc, List, N) -> + Window = take(List, N), + case erlang:length(Window) =:= N of + true -> + window_loop([Window | Acc], drop(List, 1), N); + + false -> + lists:reverse(Acc) + end. + +-file("src/gleam/list.gleam", 1897). +?DOC( + " Returns a list of sliding windows.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " window([1,2,3,4,5], 3)\n" + " // -> [[1, 2, 3], [2, 3, 4], [3, 4, 5]]\n" + " ```\n" + "\n" + " ```gleam\n" + " window([1, 2], 4)\n" + " // -> []\n" + " ```\n" +). +-spec window(list(ANJ), integer()) -> list(list(ANJ)). +window(List, N) -> + case N =< 0 of + true -> + []; + + false -> + window_loop([], List, N) + end. + +-file("src/gleam/list.gleam", 1927). +?DOC( + " Returns a list of tuples containing two contiguous elements.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " window_by_2([1,2,3,4])\n" + " // -> [#(1, 2), #(2, 3), #(3, 4)]\n" + " ```\n" + "\n" + " ```gleam\n" + " window_by_2([1])\n" + " // -> []\n" + " ```\n" +). +-spec window_by_2(list(ANT)) -> list({ANT, ANT}). +window_by_2(List) -> + zip(List, drop(List, 1)). + +-file("src/gleam/list.gleam", 1940). +?DOC( + " Drops the first elements in a given list for which the predicate function returns `True`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " drop_while([1, 2, 3, 4], fn (x) { x < 3 })\n" + " // -> [3, 4]\n" + " ```\n" +). +-spec drop_while(list(ANW), fun((ANW) -> boolean())) -> list(ANW). +drop_while(List, Predicate) -> + case List of + [] -> + []; + + [First | Rest] -> + case Predicate(First) of + true -> + drop_while(Rest, Predicate); + + false -> + [First | Rest] + end + end. + +-file("src/gleam/list.gleam", 1970). +-spec take_while_loop(list(AOC), fun((AOC) -> boolean()), list(AOC)) -> list(AOC). +take_while_loop(List, Predicate, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [First | Rest] -> + case Predicate(First) of + true -> + take_while_loop(Rest, Predicate, [First | Acc]); + + false -> + lists:reverse(Acc) + end + end. + +-file("src/gleam/list.gleam", 1963). +?DOC( + " Takes the first elements in a given list for which the predicate function returns `True`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " take_while([1, 2, 3, 2, 4], fn (x) { x < 3 })\n" + " // -> [1, 2]\n" + " ```\n" +). +-spec take_while(list(ANZ), fun((ANZ) -> boolean())) -> list(ANZ). +take_while(List, Predicate) -> + take_while_loop(List, Predicate, []). + +-file("src/gleam/list.gleam", 2002). +-spec chunk_loop(list(AOL), fun((AOL) -> AON), AON, list(AOL), list(list(AOL))) -> list(list(AOL)). +chunk_loop(List, F, Previous_key, Current_chunk, Acc) -> + case List of + [First | Rest] -> + Key = F(First), + case Key =:= Previous_key of + true -> + chunk_loop(Rest, F, Key, [First | Current_chunk], Acc); + + false -> + New_acc = [lists:reverse(Current_chunk) | Acc], + chunk_loop(Rest, F, Key, [First], New_acc) + end; + + [] -> + lists:reverse([lists:reverse(Current_chunk) | Acc]) + end. + +-file("src/gleam/list.gleam", 1995). +?DOC( + " Returns a list of chunks in which\n" + " the return value of calling `f` on each element is the same.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " [1, 2, 2, 3, 4, 4, 6, 7, 7] |> chunk(by: fn(n) { n % 2 })\n" + " // -> [[1], [2, 2], [3], [4, 4, 6], [7, 7]]\n" + " ```\n" +). +-spec chunk(list(AOG), fun((AOG) -> any())) -> list(list(AOG)). +chunk(List, F) -> + case List of + [] -> + []; + + [First | Rest] -> + chunk_loop(Rest, F, F(First), [First], []) + end. + +-file("src/gleam/list.gleam", 2047). +-spec sized_chunk_loop( + list(AOX), + integer(), + integer(), + list(AOX), + list(list(AOX)) +) -> list(list(AOX)). +sized_chunk_loop(List, Count, Left, Current_chunk, Acc) -> + case List of + [] -> + case Current_chunk of + [] -> + lists:reverse(Acc); + + Remaining -> + lists:reverse([lists:reverse(Remaining) | Acc]) + end; + + [First | Rest] -> + Chunk = [First | Current_chunk], + case Left > 1 of + true -> + sized_chunk_loop(Rest, Count, Left - 1, Chunk, Acc); + + false -> + sized_chunk_loop( + Rest, + Count, + Count, + [], + [lists:reverse(Chunk) | Acc] + ) + end + end. + +-file("src/gleam/list.gleam", 2043). +?DOC( + " Returns a list of chunks containing `count` elements each.\n" + "\n" + " If the last chunk does not have `count` elements, it is instead\n" + " a partial chunk, with less than `count` elements.\n" + "\n" + " For any `count` less than 1 this function behaves as if it was set to 1.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " [1, 2, 3, 4, 5, 6] |> sized_chunk(into: 2)\n" + " // -> [[1, 2], [3, 4], [5, 6]]\n" + " ```\n" + "\n" + " ```gleam\n" + " [1, 2, 3, 4, 5, 6, 7, 8] |> sized_chunk(into: 3)\n" + " // -> [[1, 2, 3], [4, 5, 6], [7, 8]]\n" + " ```\n" +). +-spec sized_chunk(list(AOT), integer()) -> list(list(AOT)). +sized_chunk(List, Count) -> + sized_chunk_loop(List, Count, Count, [], []). + +-file("src/gleam/list.gleam", 2091). +?DOC( + " This function acts similar to fold, but does not take an initial state.\n" + " Instead, it starts from the first element in the list\n" + " and combines it with each subsequent element in turn using the given\n" + " function. The function is called as `fun(accumulator, current_element)`.\n" + "\n" + " Returns `Ok` to indicate a successful run, and `Error` if called on an\n" + " empty list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " [] |> reduce(fn(acc, x) { acc + x })\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " [1, 2, 3, 4, 5] |> reduce(fn(acc, x) { acc + x })\n" + " // -> Ok(15)\n" + " ```\n" +). +-spec reduce(list(APE), fun((APE, APE) -> APE)) -> {ok, APE} | {error, nil}. +reduce(List, Fun) -> + case List of + [] -> + {error, nil}; + + [First | Rest] -> + {ok, fold(Rest, First, Fun)} + end. + +-file("src/gleam/list.gleam", 2115). +-spec scan_loop(list(APM), APO, list(APO), fun((APO, APM) -> APO)) -> list(APO). +scan_loop(List, Accumulator, Accumulated, Fun) -> + case List of + [] -> + lists:reverse(Accumulated); + + [First | Rest] -> + Next = Fun(Accumulator, First), + scan_loop(Rest, Next, [Next | Accumulated], Fun) + end. + +-file("src/gleam/list.gleam", 2107). +?DOC( + " Similar to `fold`, but yields the state of the accumulator at each stage.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " scan(over: [1, 2, 3], from: 100, with: fn(acc, i) { acc + i })\n" + " // -> [101, 103, 106]\n" + " ```\n" +). +-spec scan(list(API), APK, fun((APK, API) -> APK)) -> list(APK). +scan(List, Initial, Fun) -> + scan_loop(List, Initial, [], Fun). + +-file("src/gleam/list.gleam", 2148). +?DOC( + " Returns the last element in the given list.\n" + "\n" + " Returns `Error(Nil)` if the list is empty.\n" + "\n" + " This function runs in linear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " last([])\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " last([1, 2, 3, 4, 5])\n" + " // -> Ok(5)\n" + " ```\n" +). +-spec last(list(APR)) -> {ok, APR} | {error, nil}. +last(List) -> + case List of + [] -> + {error, nil}; + + [Last] -> + {ok, Last}; + + [_ | Rest] -> + last(Rest) + end. + +-file("src/gleam/list.gleam", 2170). +?DOC( + " Return unique combinations of elements in the list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " combinations([1, 2, 3], 2)\n" + " // -> [[1, 2], [1, 3], [2, 3]]\n" + " ```\n" + "\n" + " ```gleam\n" + " combinations([1, 2, 3, 4], 3)\n" + " // -> [[1, 2, 3], [1, 2, 4], [1, 3, 4], [2, 3, 4]]\n" + " ```\n" +). +-spec combinations(list(APV), integer()) -> list(list(APV)). +combinations(Items, N) -> + case {N, Items} of + {0, _} -> + [[]]; + + {_, []} -> + []; + + {_, [First | Rest]} -> + _pipe = Rest, + _pipe@1 = combinations(_pipe, N - 1), + _pipe@2 = map( + _pipe@1, + fun(Combination) -> [First | Combination] end + ), + _pipe@3 = lists:reverse(_pipe@2), + fold(_pipe@3, combinations(Rest, N), fun(Acc, C) -> [C | Acc] end) + end. + +-file("src/gleam/list.gleam", 2196). +-spec combination_pairs_loop(list(AQC), list({AQC, AQC})) -> list({AQC, AQC}). +combination_pairs_loop(Items, Acc) -> + case Items of + [] -> + lists:reverse(Acc); + + [First | Rest] -> + First_combinations = map(Rest, fun(Other) -> {First, Other} end), + Acc@1 = lists:reverse(First_combinations, Acc), + combination_pairs_loop(Rest, Acc@1) + end. + +-file("src/gleam/list.gleam", 2192). +?DOC( + " Return unique pair combinations of elements in the list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " combination_pairs([1, 2, 3])\n" + " // -> [#(1, 2), #(1, 3), #(2, 3)]\n" + " ```\n" +). +-spec combination_pairs(list(APZ)) -> list({APZ, APZ}). +combination_pairs(Items) -> + combination_pairs_loop(Items, []). + +-file("src/gleam/list.gleam", 2252). +-spec take_firsts(list(list(AQW)), list(AQW), list(list(AQW))) -> {list(AQW), + list(list(AQW))}. +take_firsts(Rows, Column, Remaining_rows) -> + case Rows of + [] -> + {lists:reverse(Column), lists:reverse(Remaining_rows)}; + + [[] | Rest] -> + take_firsts(Rest, Column, Remaining_rows); + + [[First | Remaining_row] | Rest_rows] -> + Remaining_rows@1 = [Remaining_row | Remaining_rows], + take_firsts(Rest_rows, [First | Column], Remaining_rows@1) + end. + +-file("src/gleam/list.gleam", 2239). +-spec transpose_loop(list(list(AQP)), list(list(AQP))) -> list(list(AQP)). +transpose_loop(Rows, Columns) -> + case Rows of + [] -> + lists:reverse(Columns); + + _ -> + {Column, Rest} = take_firsts(Rows, [], []), + case Column of + [_ | _] -> + transpose_loop(Rest, [Column | Columns]); + + [] -> + transpose_loop(Rest, Columns) + end + end. + +-file("src/gleam/list.gleam", 2235). +?DOC( + " Transpose rows and columns of the list of lists.\n" + "\n" + " Notice: This function is not tail recursive,\n" + " and thus may exceed stack size if called,\n" + " with large lists (on the JavaScript target).\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " transpose([[1, 2, 3], [101, 102, 103]])\n" + " // -> [[1, 101], [2, 102], [3, 103]]\n" + " ```\n" +). +-spec transpose(list(list(AQK))) -> list(list(AQK)). +transpose(List_of_lists) -> + transpose_loop(List_of_lists, []). + +-file("src/gleam/list.gleam", 2216). +?DOC( + " Make a list alternating the elements from the given lists\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " interleave([[1, 2], [101, 102], [201, 202]])\n" + " // -> [1, 101, 201, 2, 102, 202]\n" + " ```\n" +). +-spec interleave(list(list(AQG))) -> list(AQG). +interleave(List) -> + _pipe = List, + _pipe@1 = transpose(_pipe), + lists:append(_pipe@1). + +-file("src/gleam/list.gleam", 2285). +-spec shuffle_pair_unwrap_loop(list({float(), ARI}), list(ARI)) -> list(ARI). +shuffle_pair_unwrap_loop(List, Acc) -> + case List of + [] -> + Acc; + + [Elem_pair | Enumerable] -> + shuffle_pair_unwrap_loop( + Enumerable, + [erlang:element(2, Elem_pair) | Acc] + ) + end. + +-file("src/gleam/list.gleam", 2293). +-spec do_shuffle_by_pair_indexes(list({float(), ARM})) -> list({float(), ARM}). +do_shuffle_by_pair_indexes(List_of_pairs) -> + sort( + List_of_pairs, + fun(A_pair, B_pair) -> + gleam@float:compare( + erlang:element(1, A_pair), + erlang:element(1, B_pair) + ) + end + ). + +-file("src/gleam/list.gleam", 2278). +?DOC( + " Takes a list, randomly sorts all items and returns the shuffled list.\n" + "\n" + " This function uses `float.random` to decide the order of the elements.\n" + "\n" + " ## Example\n" + "\n" + " ```gleam\n" + " range(1, 10) |> shuffle()\n" + " // -> [1, 6, 9, 10, 3, 8, 4, 2, 7, 5]\n" + " ```\n" +). +-spec shuffle(list(ARF)) -> list(ARF). +shuffle(List) -> + _pipe = List, + _pipe@1 = fold(_pipe, [], fun(Acc, A) -> [{rand:uniform(), A} | Acc] end), + _pipe@2 = do_shuffle_by_pair_indexes(_pipe@1), + shuffle_pair_unwrap_loop(_pipe@2, []). + +-file("src/gleam/list.gleam", 2325). +-spec max_loop(list(ARW), fun((ARW, ARW) -> gleam@order:order()), ARW) -> ARW. +max_loop(List, Compare, Max) -> + case List of + [] -> + Max; + + [First | Rest] -> + case Compare(First, Max) of + gt -> + max_loop(Rest, Compare, First); + + lt -> + max_loop(Rest, Compare, Max); + + eq -> + max_loop(Rest, Compare, Max) + end + end. + +-file("src/gleam/list.gleam", 2315). +?DOC( + " Takes a list and a comparator, and returns the maximum element in the list\n" + "\n" + "\n" + " ## Example\n" + "\n" + " ```gleam\n" + " range(1, 10) |> list.max(int.compare)\n" + " // -> Ok(10)\n" + " ```\n" + "\n" + " ```gleam\n" + " [\"a\", \"c\", \"b\"] |> list.max(string.compare)\n" + " // -> Ok(\"c\")\n" + " ```\n" +). +-spec max(list(ARP), fun((ARP, ARP) -> gleam@order:order())) -> {ok, ARP} | + {error, nil}. +max(List, Compare) -> + case List of + [] -> + {error, nil}; + + [First | Rest] -> + {ok, max_loop(Rest, Compare, First)} + end. + +-file("src/gleam/list.gleam", 2406). +-spec build_reservoir_loop( + list(ASL), + integer(), + gleam@dict:dict(integer(), ASL) +) -> {gleam@dict:dict(integer(), ASL), list(ASL)}. +build_reservoir_loop(List, Size, Reservoir) -> + Reservoir_size = maps:size(Reservoir), + case Reservoir_size >= Size of + true -> + {Reservoir, List}; + + false -> + case List of + [] -> + {Reservoir, []}; + + [First | Rest] -> + Reservoir@1 = gleam@dict:insert( + Reservoir, + Reservoir_size, + First + ), + build_reservoir_loop(Rest, Size, Reservoir@1) + end + end. + +-file("src/gleam/list.gleam", 2402). +?DOC( + " Builds the initial reservoir used by Algorithm L.\n" + " This is a dictionary with keys ranging from `0` up to `n - 1` where each\n" + " value is the corresponding element at that position in `list`.\n" + "\n" + " This also returns the remaining elements of `list` that didn't end up in\n" + " the reservoir.\n" +). +-spec build_reservoir(list(ASG), integer()) -> {gleam@dict:dict(integer(), ASG), + list(ASG)}. +build_reservoir(List, N) -> + build_reservoir_loop(List, N, maps:new()). + +-file("src/gleam/list.gleam", 2390). +-spec log_random() -> float(). +log_random() -> + Random@1 = case gleam@float:logarithm( + rand:uniform() + 2.2250738585072014e-308 + ) of + {ok, Random} -> Random; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Pattern match failed, no pattern matched the value."/utf8>>, + file => <>, + module => <<"gleam/list"/utf8>>, + function => <<"log_random"/utf8>>, + line => 2391, + value => _assert_fail, + start => 56078, + 'end' => 56149, + pattern_start => 56089, + pattern_end => 56099}) + end, + Random@1. + +-file("src/gleam/list.gleam", 2367). +-spec sample_loop( + list(ASA), + gleam@dict:dict(integer(), ASA), + integer(), + float() +) -> gleam@dict:dict(integer(), ASA). +sample_loop(List, Reservoir, N, W) -> + Skip = begin + Log@1 = case gleam@float:logarithm(1.0 - W) of + {ok, Log} -> Log; + _assert_fail -> + erlang:error(#{gleam_error => let_assert, + message => <<"Pattern match failed, no pattern matched the value."/utf8>>, + file => <>, + module => <<"gleam/list"/utf8>>, + function => <<"sample_loop"/utf8>>, + line => 2374, + value => _assert_fail, + start => 55639, + 'end' => 55685, + pattern_start => 55650, + pattern_end => 55657}) + end, + erlang:round(math:floor(case Log@1 of + +0.0 -> +0.0; + -0.0 -> -0.0; + Gleam@denominator -> log_random() / Gleam@denominator + end)) + end, + case drop(List, Skip) of + [] -> + Reservoir; + + [First | Rest] -> + Reservoir@1 = gleam@dict:insert( + Reservoir, + gleam@int:random(N), + First + ), + W@1 = W * math:exp(case erlang:float(N) of + +0.0 -> +0.0; + -0.0 -> -0.0; + Gleam@denominator@1 -> log_random() / Gleam@denominator@1 + end), + sample_loop(Rest, Reservoir@1, N, W@1) + end. + +-file("src/gleam/list.gleam", 2349). +?DOC( + " Returns a random sample of up to n elements from a list using reservoir\n" + " sampling via [Algorithm L](https://en.wikipedia.org/wiki/Reservoir_sampling#Optimal:_Algorithm_L).\n" + " Returns an empty list if the sample size is less than or equal to 0.\n" + "\n" + " Order is not random, only selection is.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " reservoir_sample([1, 2, 3, 4, 5], 3)\n" + " // -> [2, 4, 5] // A random sample of 3 items\n" + " ```\n" +). +-spec sample(list(ARX), integer()) -> list(ARX). +sample(List, N) -> + {Reservoir, Rest} = build_reservoir(List, N), + case gleam@dict:is_empty(Reservoir) of + true -> + []; + + false -> + W = math:exp(case erlang:float(N) of + +0.0 -> +0.0; + -0.0 -> -0.0; + Gleam@denominator -> log_random() / Gleam@denominator + end), + maps:values(sample_loop(Rest, Reservoir, N, W)) + end. + +-file("src/gleam/list.gleam", 1851). +-spec permutation_zip(list(AMT), list(AMT), list(list(AMT))) -> list(list(AMT)). +permutation_zip(List, Rest, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [Head | Tail] -> + permutation_prepend( + Head, + permutations(lists:reverse(Rest, Tail)), + Tail, + [Head | Rest], + Acc + ) + end. + +-file("src/gleam/list.gleam", 1869). +-spec permutation_prepend( + ANA, + list(list(ANA)), + list(ANA), + list(ANA), + list(list(ANA)) +) -> list(list(ANA)). +permutation_prepend(El, Permutations, List_1, List_2, Acc) -> + case Permutations of + [] -> + permutation_zip(List_1, List_2, Acc); + + [Head | Tail] -> + permutation_prepend(El, Tail, List_1, List_2, [[El | Head] | Acc]) + end. + +-file("src/gleam/list.gleam", 1844). +?DOC( + " Returns all the permutations of a list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " permutations([1, 2])\n" + " // -> [[1, 2], [2, 1]]\n" + " ```\n" +). +-spec permutations(list(AMP)) -> list(list(AMP)). +permutations(List) -> + case List of + [] -> + [[]]; + + L -> + permutation_zip(L, [], []) + end. diff --git a/build/packages/gleam_stdlib/src/gleam@option.erl b/build/packages/gleam_stdlib/src/gleam@option.erl new file mode 100644 index 0000000..8e86a8e --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam@option.erl @@ -0,0 +1,413 @@ +-module(gleam@option). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/option.gleam"). +-export([all/1, is_some/1, is_none/1, to_result/2, from_result/1, unwrap/2, lazy_unwrap/2, map/2, flatten/1, then/2, 'or'/2, lazy_or/2, values/1]). +-export_type([option/1]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +-type option(FG) :: {some, FG} | none. + +-file("src/gleam/option.gleam", 59). +-spec reverse_and_prepend(list(FV), list(FV)) -> list(FV). +reverse_and_prepend(Prefix, Suffix) -> + case Prefix of + [] -> + Suffix; + + [First | Rest] -> + reverse_and_prepend(Rest, [First | Suffix]) + end. + +-file("src/gleam/option.gleam", 44). +-spec all_loop(list(option(FM)), list(FM)) -> option(list(FM)). +all_loop(List, Acc) -> + case List of + [] -> + {some, lists:reverse(Acc)}; + + [none | _] -> + none; + + [{some, First} | Rest] -> + all_loop(Rest, [First | Acc]) + end. + +-file("src/gleam/option.gleam", 40). +?DOC( + " Combines a list of `Option`s into a single `Option`.\n" + " If all elements in the list are `Some` then returns a `Some` holding the list of values.\n" + " If any element is `None` then returns`None`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " all([Some(1), Some(2)])\n" + " // -> Some([1, 2])\n" + " ```\n" + "\n" + " ```gleam\n" + " all([Some(1), None])\n" + " // -> None\n" + " ```\n" +). +-spec all(list(option(FH))) -> option(list(FH)). +all(List) -> + all_loop(List, []). + +-file("src/gleam/option.gleam", 80). +?DOC( + " Checks whether the `Option` is a `Some` value.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " is_some(Some(1))\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " is_some(None)\n" + " // -> False\n" + " ```\n" +). +-spec is_some(option(any())) -> boolean(). +is_some(Option) -> + Option /= none. + +-file("src/gleam/option.gleam", 98). +?DOC( + " Checks whether the `Option` is a `None` value.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " is_none(Some(1))\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " is_none(None)\n" + " // -> True\n" + " ```\n" +). +-spec is_none(option(any())) -> boolean(). +is_none(Option) -> + Option =:= none. + +-file("src/gleam/option.gleam", 116). +?DOC( + " Converts an `Option` type to a `Result` type.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " to_result(Some(1), \"some_error\")\n" + " // -> Ok(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " to_result(None, \"some_error\")\n" + " // -> Error(\"some_error\")\n" + " ```\n" +). +-spec to_result(option(GD), GG) -> {ok, GD} | {error, GG}. +to_result(Option, E) -> + case Option of + {some, A} -> + {ok, A}; + + none -> + {error, E} + end. + +-file("src/gleam/option.gleam", 137). +?DOC( + " Converts a `Result` type to an `Option` type.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " from_result(Ok(1))\n" + " // -> Some(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " from_result(Error(\"some_error\"))\n" + " // -> None\n" + " ```\n" +). +-spec from_result({ok, GJ} | {error, any()}) -> option(GJ). +from_result(Result) -> + case Result of + {ok, A} -> + {some, A}; + + {error, _} -> + none + end. + +-file("src/gleam/option.gleam", 158). +?DOC( + " Extracts the value from an `Option`, returning a default value if there is none.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " unwrap(Some(1), 0)\n" + " // -> 1\n" + " ```\n" + "\n" + " ```gleam\n" + " unwrap(None, 0)\n" + " // -> 0\n" + " ```\n" +). +-spec unwrap(option(GO), GO) -> GO. +unwrap(Option, Default) -> + case Option of + {some, X} -> + X; + + none -> + Default + end. + +-file("src/gleam/option.gleam", 179). +?DOC( + " Extracts the value from an `Option`, evaluating the default function if the option is `None`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " lazy_unwrap(Some(1), fn() { 0 })\n" + " // -> 1\n" + " ```\n" + "\n" + " ```gleam\n" + " lazy_unwrap(None, fn() { 0 })\n" + " // -> 0\n" + " ```\n" +). +-spec lazy_unwrap(option(GQ), fun(() -> GQ)) -> GQ. +lazy_unwrap(Option, Default) -> + case Option of + {some, X} -> + X; + + none -> + Default() + end. + +-file("src/gleam/option.gleam", 204). +?DOC( + " Updates a value held within the `Some` of an `Option` by calling a given function\n" + " on it.\n" + "\n" + " If the `Option` is a `None` rather than `Some`, the function is not called and the\n" + " `Option` stays the same.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " map(over: Some(1), with: fn(x) { x + 1 })\n" + " // -> Some(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " map(over: None, with: fn(x) { x + 1 })\n" + " // -> None\n" + " ```\n" +). +-spec map(option(GS), fun((GS) -> GU)) -> option(GU). +map(Option, Fun) -> + case Option of + {some, X} -> + {some, Fun(X)}; + + none -> + none + end. + +-file("src/gleam/option.gleam", 230). +?DOC( + " Merges a nested `Option` into a single layer.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " flatten(Some(Some(1)))\n" + " // -> Some(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " flatten(Some(None))\n" + " // -> None\n" + " ```\n" + "\n" + " ```gleam\n" + " flatten(None)\n" + " // -> None\n" + " ```\n" +). +-spec flatten(option(option(GW))) -> option(GW). +flatten(Option) -> + case Option of + {some, X} -> + X; + + none -> + none + end. + +-file("src/gleam/option.gleam", 269). +?DOC( + " Updates a value held within the `Some` of an `Option` by calling a given function\n" + " on it, where the given function also returns an `Option`. The two options are\n" + " then merged together into one `Option`.\n" + "\n" + " If the `Option` is a `None` rather than `Some` the function is not called and the\n" + " option stays the same.\n" + "\n" + " This function is the equivalent of calling `map` followed by `flatten`, and\n" + " it is useful for chaining together multiple functions that return `Option`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " then(Some(1), fn(x) { Some(x + 1) })\n" + " // -> Some(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " then(Some(1), fn(x) { Some(#(\"a\", x)) })\n" + " // -> Some(#(\"a\", 1))\n" + " ```\n" + "\n" + " ```gleam\n" + " then(Some(1), fn(_) { None })\n" + " // -> None\n" + " ```\n" + "\n" + " ```gleam\n" + " then(None, fn(x) { Some(x + 1) })\n" + " // -> None\n" + " ```\n" +). +-spec then(option(HA), fun((HA) -> option(HC))) -> option(HC). +then(Option, Fun) -> + case Option of + {some, X} -> + Fun(X); + + none -> + none + end. + +-file("src/gleam/option.gleam", 300). +?DOC( + " Returns the first value if it is `Some`, otherwise returns the second value.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " or(Some(1), Some(2))\n" + " // -> Some(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " or(Some(1), None)\n" + " // -> Some(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " or(None, Some(2))\n" + " // -> Some(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " or(None, None)\n" + " // -> None\n" + " ```\n" +). +-spec 'or'(option(HF), option(HF)) -> option(HF). +'or'(First, Second) -> + case First of + {some, _} -> + First; + + none -> + Second + end. + +-file("src/gleam/option.gleam", 331). +?DOC( + " Returns the first value if it is `Some`, otherwise evaluates the given function for a fallback value.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " lazy_or(Some(1), fn() { Some(2) })\n" + " // -> Some(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " lazy_or(Some(1), fn() { None })\n" + " // -> Some(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " lazy_or(None, fn() { Some(2) })\n" + " // -> Some(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " lazy_or(None, fn() { None })\n" + " // -> None\n" + " ```\n" +). +-spec lazy_or(option(HJ), fun(() -> option(HJ))) -> option(HJ). +lazy_or(First, Second) -> + case First of + {some, _} -> + First; + + none -> + Second() + end. + +-file("src/gleam/option.gleam", 352). +-spec values_loop(list(option(HR)), list(HR)) -> list(HR). +values_loop(List, Acc) -> + case List of + [] -> + lists:reverse(Acc); + + [none | Rest] -> + values_loop(Rest, Acc); + + [{some, First} | Rest@1] -> + values_loop(Rest@1, [First | Acc]) + end. + +-file("src/gleam/option.gleam", 348). +?DOC( + " Given a list of `Option`s,\n" + " returns only the values inside `Some`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " values([Some(1), None, Some(3)])\n" + " // -> [1, 3]\n" + " ```\n" +). +-spec values(list(option(HN))) -> list(HN). +values(Options) -> + values_loop(Options, []). diff --git a/build/packages/gleam_stdlib/src/gleam@order.erl b/build/packages/gleam_stdlib/src/gleam@order.erl new file mode 100644 index 0000000..ec2bb84 --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam@order.erl @@ -0,0 +1,200 @@ +-module(gleam@order). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/order.gleam"). +-export([negate/1, to_int/1, compare/2, reverse/1, break_tie/2, lazy_break_tie/2]). +-export_type([order/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +-type order() :: lt | eq | gt. + +-file("src/gleam/order.gleam", 35). +?DOC( + " Inverts an order, so less-than becomes greater-than and greater-than\n" + " becomes less-than.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " negate(Lt)\n" + " // -> Gt\n" + " ```\n" + "\n" + " ```gleam\n" + " negate(Eq)\n" + " // -> Eq\n" + " ```\n" + "\n" + " ```gleam\n" + " negate(Gt)\n" + " // -> Lt\n" + " ```\n" +). +-spec negate(order()) -> order(). +negate(Order) -> + case Order of + lt -> + gt; + + eq -> + eq; + + gt -> + lt + end. + +-file("src/gleam/order.gleam", 62). +?DOC( + " Produces a numeric representation of the order.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " to_int(Lt)\n" + " // -> -1\n" + " ```\n" + "\n" + " ```gleam\n" + " to_int(Eq)\n" + " // -> 0\n" + " ```\n" + "\n" + " ```gleam\n" + " to_int(Gt)\n" + " // -> 1\n" + " ```\n" +). +-spec to_int(order()) -> integer(). +to_int(Order) -> + case Order of + lt -> + -1; + + eq -> + 0; + + gt -> + 1 + end. + +-file("src/gleam/order.gleam", 79). +?DOC( + " Compares two `Order` values to one another, producing a new `Order`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " compare(Eq, with: Lt)\n" + " // -> Gt\n" + " ```\n" +). +-spec compare(order(), order()) -> order(). +compare(A, B) -> + case {A, B} of + {X, Y} when X =:= Y -> + eq; + + {lt, _} -> + lt; + + {eq, gt} -> + lt; + + {_, _} -> + gt + end. + +-file("src/gleam/order.gleam", 100). +?DOC( + " Inverts an ordering function, so less-than becomes greater-than and greater-than\n" + " becomes less-than.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " import gleam/int\n" + " import gleam/list\n" + "\n" + " list.sort([1, 5, 4], by: reverse(int.compare))\n" + " // -> [5, 4, 1]\n" + " ```\n" +). +-spec reverse(fun((I, I) -> order())) -> fun((I, I) -> order()). +reverse(Orderer) -> + fun(A, B) -> Orderer(B, A) end. + +-file("src/gleam/order.gleam", 122). +?DOC( + " Return a fallback `Order` in case the first argument is `Eq`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " import gleam/int\n" + "\n" + " break_tie(in: int.compare(1, 1), with: Lt)\n" + " // -> Lt\n" + " ```\n" + "\n" + " ```gleam\n" + " import gleam/int\n" + "\n" + " break_tie(in: int.compare(1, 0), with: Eq)\n" + " // -> Gt\n" + " ```\n" +). +-spec break_tie(order(), order()) -> order(). +break_tie(Order, Other) -> + case Order of + lt -> + Order; + + gt -> + Order; + + eq -> + Other + end. + +-file("src/gleam/order.gleam", 151). +?DOC( + " Invokes a fallback function returning an `Order` in case the first argument\n" + " is `Eq`.\n" + "\n" + " This can be useful when the fallback comparison might be expensive and it\n" + " needs to be delayed until strictly necessary.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " import gleam/int\n" + "\n" + " lazy_break_tie(in: int.compare(1, 1), with: fn() { Lt })\n" + " // -> Lt\n" + " ```\n" + "\n" + " ```gleam\n" + " import gleam/int\n" + "\n" + " lazy_break_tie(in: int.compare(1, 0), with: fn() { Eq })\n" + " // -> Gt\n" + " ```\n" +). +-spec lazy_break_tie(order(), fun(() -> order())) -> order(). +lazy_break_tie(Order, Comparison) -> + case Order of + lt -> + Order; + + gt -> + Order; + + eq -> + Comparison() + end. diff --git a/build/packages/gleam_stdlib/src/gleam@pair.erl b/build/packages/gleam_stdlib/src/gleam@pair.erl new file mode 100644 index 0000000..cb18264 --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam@pair.erl @@ -0,0 +1,110 @@ +-module(gleam@pair). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/pair.gleam"). +-export([first/1, second/1, swap/1, map_first/2, map_second/2, new/2]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +-file("src/gleam/pair.gleam", 10). +?DOC( + " Returns the first element in a pair.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " first(#(1, 2))\n" + " // -> 1\n" + " ```\n" +). +-spec first({CLF, any()}) -> CLF. +first(Pair) -> + {A, _} = Pair, + A. + +-file("src/gleam/pair.gleam", 24). +?DOC( + " Returns the second element in a pair.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " second(#(1, 2))\n" + " // -> 2\n" + " ```\n" +). +-spec second({any(), CLI}) -> CLI. +second(Pair) -> + {_, A} = Pair, + A. + +-file("src/gleam/pair.gleam", 38). +?DOC( + " Returns a new pair with the elements swapped.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " swap(#(1, 2))\n" + " // -> #(2, 1)\n" + " ```\n" +). +-spec swap({CLJ, CLK}) -> {CLK, CLJ}. +swap(Pair) -> + {A, B} = Pair, + {B, A}. + +-file("src/gleam/pair.gleam", 53). +?DOC( + " Returns a new pair with the first element having had `with` applied to\n" + " it.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " #(1, 2) |> map_first(fn(n) { n * 2 })\n" + " // -> #(2, 2)\n" + " ```\n" +). +-spec map_first({CLL, CLM}, fun((CLL) -> CLN)) -> {CLN, CLM}. +map_first(Pair, Fun) -> + {A, B} = Pair, + {Fun(A), B}. + +-file("src/gleam/pair.gleam", 68). +?DOC( + " Returns a new pair with the second element having had `with` applied to\n" + " it.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " #(1, 2) |> map_second(fn(n) { n * 2 })\n" + " // -> #(1, 4)\n" + " ```\n" +). +-spec map_second({CLO, CLP}, fun((CLP) -> CLQ)) -> {CLO, CLQ}. +map_second(Pair, Fun) -> + {A, B} = Pair, + {A, Fun(B)}. + +-file("src/gleam/pair.gleam", 83). +?DOC( + " Returns a new pair with the given elements. This can also be done using the dedicated\n" + " syntax instead: `new(1, 2) == #(1, 2)`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " new(1, 2)\n" + " // -> #(1, 2)\n" + " ```\n" +). +-spec new(CLR, CLS) -> {CLR, CLS}. +new(First, Second) -> + {First, Second}. diff --git a/build/packages/gleam_stdlib/src/gleam@result.erl b/build/packages/gleam_stdlib/src/gleam@result.erl new file mode 100644 index 0000000..9d89ff7 --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam@result.erl @@ -0,0 +1,550 @@ +-module(gleam@result). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/result.gleam"). +-export([is_ok/1, is_error/1, map/2, map_error/2, flatten/1, 'try'/2, then/2, unwrap/2, lazy_unwrap/2, unwrap_error/2, unwrap_both/1, 'or'/2, lazy_or/2, all/1, partition/1, replace/2, replace_error/2, values/1, try_recover/2]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC( + " Result represents the result of something that may succeed or not.\n" + " `Ok` means it was successful, `Error` means it was not successful.\n" +). + +-file("src/gleam/result.gleam", 20). +?DOC( + " Checks whether the result is an `Ok` value.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " is_ok(Ok(1))\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " is_ok(Error(Nil))\n" + " // -> False\n" + " ```\n" +). +-spec is_ok({ok, any()} | {error, any()}) -> boolean(). +is_ok(Result) -> + case Result of + {error, _} -> + false; + + {ok, _} -> + true + end. + +-file("src/gleam/result.gleam", 41). +?DOC( + " Checks whether the result is an `Error` value.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " is_error(Ok(1))\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " is_error(Error(Nil))\n" + " // -> True\n" + " ```\n" +). +-spec is_error({ok, any()} | {error, any()}) -> boolean(). +is_error(Result) -> + case Result of + {ok, _} -> + false; + + {error, _} -> + true + end. + +-file("src/gleam/result.gleam", 66). +?DOC( + " Updates a value held within the `Ok` of a result by calling a given function\n" + " on it.\n" + "\n" + " If the result is an `Error` rather than `Ok` the function is not called and the\n" + " result stays the same.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " map(over: Ok(1), with: fn(x) { x + 1 })\n" + " // -> Ok(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " map(over: Error(1), with: fn(x) { x + 1 })\n" + " // -> Error(1)\n" + " ```\n" +). +-spec map({ok, CMC} | {error, CMD}, fun((CMC) -> CMG)) -> {ok, CMG} | + {error, CMD}. +map(Result, Fun) -> + case Result of + {ok, X} -> + {ok, Fun(X)}; + + {error, E} -> + {error, E} + end. + +-file("src/gleam/result.gleam", 91). +?DOC( + " Updates a value held within the `Error` of a result by calling a given function\n" + " on it.\n" + "\n" + " If the result is `Ok` rather than `Error` the function is not called and the\n" + " result stays the same.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " map_error(over: Error(1), with: fn(x) { x + 1 })\n" + " // -> Error(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " map_error(over: Ok(1), with: fn(x) { x + 1 })\n" + " // -> Ok(1)\n" + " ```\n" +). +-spec map_error({ok, CMJ} | {error, CMK}, fun((CMK) -> CMN)) -> {ok, CMJ} | + {error, CMN}. +map_error(Result, Fun) -> + case Result of + {ok, X} -> + {ok, X}; + + {error, Error} -> + {error, Fun(Error)} + end. + +-file("src/gleam/result.gleam", 120). +?DOC( + " Merges a nested `Result` into a single layer.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " flatten(Ok(Ok(1)))\n" + " // -> Ok(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " flatten(Ok(Error(\"\")))\n" + " // -> Error(\"\")\n" + " ```\n" + "\n" + " ```gleam\n" + " flatten(Error(Nil))\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec flatten({ok, {ok, CMQ} | {error, CMR}} | {error, CMR}) -> {ok, CMQ} | + {error, CMR}. +flatten(Result) -> + case Result of + {ok, X} -> + X; + + {error, Error} -> + {error, Error} + end. + +-file("src/gleam/result.gleam", 158). +?DOC( + " \"Updates\" an `Ok` result by passing its value to a function that yields a result,\n" + " and returning the yielded result. (This may \"replace\" the `Ok` with an `Error`.)\n" + "\n" + " If the input is an `Error` rather than an `Ok`, the function is not called and\n" + " the original `Error` is returned.\n" + "\n" + " This function is the equivalent of calling `map` followed by `flatten`, and\n" + " it is useful for chaining together multiple functions that may fail.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " try(Ok(1), fn(x) { Ok(x + 1) })\n" + " // -> Ok(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " try(Ok(1), fn(x) { Ok(#(\"a\", x)) })\n" + " // -> Ok(#(\"a\", 1))\n" + " ```\n" + "\n" + " ```gleam\n" + " try(Ok(1), fn(_) { Error(\"Oh no\") })\n" + " // -> Error(\"Oh no\")\n" + " ```\n" + "\n" + " ```gleam\n" + " try(Error(Nil), fn(x) { Ok(x + 1) })\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec 'try'({ok, CMY} | {error, CMZ}, fun((CMY) -> {ok, CNC} | {error, CMZ})) -> {ok, + CNC} | + {error, CMZ}. +'try'(Result, Fun) -> + case Result of + {ok, X} -> + Fun(X); + + {error, E} -> + {error, E} + end. + +-file("src/gleam/result.gleam", 169). +-spec then({ok, CNH} | {error, CNI}, fun((CNH) -> {ok, CNL} | {error, CNI})) -> {ok, + CNL} | + {error, CNI}. +then(Result, Fun) -> + 'try'(Result, Fun). + +-file("src/gleam/result.gleam", 191). +?DOC( + " Extracts the `Ok` value from a result, returning a default value if the result\n" + " is an `Error`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " unwrap(Ok(1), 0)\n" + " // -> 1\n" + " ```\n" + "\n" + " ```gleam\n" + " unwrap(Error(\"\"), 0)\n" + " // -> 0\n" + " ```\n" +). +-spec unwrap({ok, CNQ} | {error, any()}, CNQ) -> CNQ. +unwrap(Result, Default) -> + case Result of + {ok, V} -> + V; + + {error, _} -> + Default + end. + +-file("src/gleam/result.gleam", 213). +?DOC( + " Extracts the `Ok` value from a result, evaluating the default function if the result\n" + " is an `Error`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " lazy_unwrap(Ok(1), fn() { 0 })\n" + " // -> 1\n" + " ```\n" + "\n" + " ```gleam\n" + " lazy_unwrap(Error(\"\"), fn() { 0 })\n" + " // -> 0\n" + " ```\n" +). +-spec lazy_unwrap({ok, CNU} | {error, any()}, fun(() -> CNU)) -> CNU. +lazy_unwrap(Result, Default) -> + case Result of + {ok, V} -> + V; + + {error, _} -> + Default() + end. + +-file("src/gleam/result.gleam", 235). +?DOC( + " Extracts the `Error` value from a result, returning a default value if the result\n" + " is an `Ok`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " unwrap_error(Error(1), 0)\n" + " // -> 1\n" + " ```\n" + "\n" + " ```gleam\n" + " unwrap_error(Ok(\"\"), 0)\n" + " // -> 0\n" + " ```\n" +). +-spec unwrap_error({ok, any()} | {error, CNZ}, CNZ) -> CNZ. +unwrap_error(Result, Default) -> + case Result of + {ok, _} -> + Default; + + {error, E} -> + E + end. + +-file("src/gleam/result.gleam", 243). +-spec unwrap_both({ok, COC} | {error, COC}) -> COC. +unwrap_both(Result) -> + case Result of + {ok, A} -> + A; + + {error, A@1} -> + A@1 + end. + +-file("src/gleam/result.gleam", 274). +?DOC( + " Returns the first value if it is `Ok`, otherwise returns the second value.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " or(Ok(1), Ok(2))\n" + " // -> Ok(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " or(Ok(1), Error(\"Error 2\"))\n" + " // -> Ok(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " or(Error(\"Error 1\"), Ok(2))\n" + " // -> Ok(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " or(Error(\"Error 1\"), Error(\"Error 2\"))\n" + " // -> Error(\"Error 2\")\n" + " ```\n" +). +-spec 'or'({ok, COF} | {error, COG}, {ok, COF} | {error, COG}) -> {ok, COF} | + {error, COG}. +'or'(First, Second) -> + case First of + {ok, _} -> + First; + + {error, _} -> + Second + end. + +-file("src/gleam/result.gleam", 307). +?DOC( + " Returns the first value if it is `Ok`, otherwise evaluates the given function for a fallback value.\n" + "\n" + " If you need access to the initial error value, use `result.try_recover`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " lazy_or(Ok(1), fn() { Ok(2) })\n" + " // -> Ok(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " lazy_or(Ok(1), fn() { Error(\"Error 2\") })\n" + " // -> Ok(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " lazy_or(Error(\"Error 1\"), fn() { Ok(2) })\n" + " // -> Ok(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " lazy_or(Error(\"Error 1\"), fn() { Error(\"Error 2\") })\n" + " // -> Error(\"Error 2\")\n" + " ```\n" +). +-spec lazy_or({ok, CON} | {error, COO}, fun(() -> {ok, CON} | {error, COO})) -> {ok, + CON} | + {error, COO}. +lazy_or(First, Second) -> + case First of + {ok, _} -> + First; + + {error, _} -> + Second() + end. + +-file("src/gleam/result.gleam", 333). +?DOC( + " Combines a list of results into a single result.\n" + " If all elements in the list are `Ok` then returns an `Ok` holding the list of values.\n" + " If any element is `Error` then returns the first error.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " all([Ok(1), Ok(2)])\n" + " // -> Ok([1, 2])\n" + " ```\n" + "\n" + " ```gleam\n" + " all([Ok(1), Error(\"e\")])\n" + " // -> Error(\"e\")\n" + " ```\n" +). +-spec all(list({ok, COV} | {error, COW})) -> {ok, list(COV)} | {error, COW}. +all(Results) -> + gleam@list:try_map(Results, fun(Result) -> Result end). + +-file("src/gleam/result.gleam", 353). +-spec partition_loop(list({ok, CPK} | {error, CPL}), list(CPK), list(CPL)) -> {list(CPK), + list(CPL)}. +partition_loop(Results, Oks, Errors) -> + case Results of + [] -> + {Oks, Errors}; + + [{ok, A} | Rest] -> + partition_loop(Rest, [A | Oks], Errors); + + [{error, E} | Rest@1] -> + partition_loop(Rest@1, Oks, [E | Errors]) + end. + +-file("src/gleam/result.gleam", 349). +?DOC( + " Given a list of results, returns a pair where the first element is a list\n" + " of all the values inside `Ok` and the second element is a list with all the\n" + " values inside `Error`. The values in both lists appear in reverse order with\n" + " respect to their position in the original list of results.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " partition([Ok(1), Error(\"a\"), Error(\"b\"), Ok(2)])\n" + " // -> #([2, 1], [\"b\", \"a\"])\n" + " ```\n" +). +-spec partition(list({ok, CPD} | {error, CPE})) -> {list(CPD), list(CPE)}. +partition(Results) -> + partition_loop(Results, [], []). + +-file("src/gleam/result.gleam", 375). +?DOC( + " Replace the value within a result\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " replace(Ok(1), Nil)\n" + " // -> Ok(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " replace(Error(1), Nil)\n" + " // -> Error(1)\n" + " ```\n" +). +-spec replace({ok, any()} | {error, CPT}, CPW) -> {ok, CPW} | {error, CPT}. +replace(Result, Value) -> + case Result of + {ok, _} -> + {ok, Value}; + + {error, Error} -> + {error, Error} + end. + +-file("src/gleam/result.gleam", 396). +?DOC( + " Replace the error within a result\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " replace_error(Error(1), Nil)\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " replace_error(Ok(1), Nil)\n" + " // -> Ok(1)\n" + " ```\n" +). +-spec replace_error({ok, CPZ} | {error, any()}, CQD) -> {ok, CPZ} | {error, CQD}. +replace_error(Result, Error) -> + case Result of + {ok, X} -> + {ok, X}; + + {error, _} -> + {error, Error} + end. + +-file("src/gleam/result.gleam", 412). +?DOC( + " Given a list of results, returns only the values inside `Ok`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " values([Ok(1), Error(\"a\"), Ok(3)])\n" + " // -> [1, 3]\n" + " ```\n" +). +-spec values(list({ok, CQG} | {error, any()})) -> list(CQG). +values(Results) -> + gleam@list:filter_map(Results, fun(Result) -> Result end). + +-file("src/gleam/result.gleam", 445). +?DOC( + " Updates a value held within the `Error` of a result by calling a given function\n" + " on it, where the given function also returns a result. The two results are\n" + " then merged together into one result.\n" + "\n" + " If the result is an `Ok` rather than `Error` the function is not called and the\n" + " result stays the same.\n" + "\n" + " This function is useful for chaining together computations that may fail\n" + " and trying to recover from possible errors.\n" + "\n" + " If you do not need access to the initial error value, use `result.lazy_or`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " Ok(1) |> try_recover(with: fn(_) { Error(\"failed to recover\") })\n" + " // -> Ok(1)\n" + " ```\n" + "\n" + " ```gleam\n" + " Error(1) |> try_recover(with: fn(error) { Ok(error + 1) })\n" + " // -> Ok(2)\n" + " ```\n" + "\n" + " ```gleam\n" + " Error(1) |> try_recover(with: fn(error) { Error(\"failed to recover\") })\n" + " // -> Error(\"failed to recover\")\n" + " ```\n" +). +-spec try_recover( + {ok, CQM} | {error, CQN}, + fun((CQN) -> {ok, CQM} | {error, CQQ}) +) -> {ok, CQM} | {error, CQQ}. +try_recover(Result, Fun) -> + case Result of + {ok, Value} -> + {ok, Value}; + + {error, Error} -> + Fun(Error) + end. diff --git a/build/packages/gleam_stdlib/src/gleam@set.erl b/build/packages/gleam_stdlib/src/gleam@set.erl new file mode 100644 index 0000000..bb3c417 --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam@set.erl @@ -0,0 +1,429 @@ +-module(gleam@set). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/set.gleam"). +-export([new/0, size/1, is_empty/1, contains/2, delete/2, to_list/1, fold/3, filter/2, drop/2, take/2, intersection/2, difference/2, is_subset/2, is_disjoint/2, each/2, insert/2, from_list/1, map/2, union/2, symmetric_difference/2]). +-export_type([set/1]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +-opaque set(CVL) :: {set, gleam@dict:dict(CVL, list(nil))}. + +-file("src/gleam/set.gleam", 32). +?DOC(" Creates a new empty set.\n"). +-spec new() -> set(any()). +new() -> + {set, maps:new()}. + +-file("src/gleam/set.gleam", 50). +?DOC( + " Gets the number of members in a set.\n" + "\n" + " This function runs in constant time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " new()\n" + " |> insert(1)\n" + " |> insert(2)\n" + " |> size\n" + " // -> 2\n" + " ```\n" +). +-spec size(set(any())) -> integer(). +size(Set) -> + maps:size(erlang:element(2, Set)). + +-file("src/gleam/set.gleam", 68). +?DOC( + " Determines whether or not the set is empty.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " new() |> is_empty\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " new() |> insert(1) |> is_empty\n" + " // -> False\n" + " ```\n" +). +-spec is_empty(set(any())) -> boolean(). +is_empty(Set) -> + Set =:= new(). + +-file("src/gleam/set.gleam", 110). +?DOC( + " Checks whether a set contains a given member.\n" + "\n" + " This function runs in logarithmic time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " new()\n" + " |> insert(2)\n" + " |> contains(2)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " new()\n" + " |> insert(2)\n" + " |> contains(1)\n" + " // -> False\n" + " ```\n" +). +-spec contains(set(CVW), CVW) -> boolean(). +contains(Set, Member) -> + _pipe = erlang:element(2, Set), + _pipe@1 = gleam_stdlib:map_get(_pipe, Member), + gleam@result:is_ok(_pipe@1). + +-file("src/gleam/set.gleam", 131). +?DOC( + " Removes a member from a set. If the set does not contain the member then\n" + " the set is returned unchanged.\n" + "\n" + " This function runs in logarithmic time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " new()\n" + " |> insert(2)\n" + " |> delete(2)\n" + " |> contains(1)\n" + " // -> False\n" + " ```\n" +). +-spec delete(set(CVY), CVY) -> set(CVY). +delete(Set, Member) -> + {set, gleam@dict:delete(erlang:element(2, Set), Member)}. + +-file("src/gleam/set.gleam", 149). +?DOC( + " Converts the set into a list of the contained members.\n" + "\n" + " The list has no specific ordering, any unintentional ordering may change in\n" + " future versions of Gleam or Erlang.\n" + "\n" + " This function runs in linear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " new() |> insert(2) |> to_list\n" + " // -> [2]\n" + " ```\n" +). +-spec to_list(set(CWB)) -> list(CWB). +to_list(Set) -> + maps:keys(erlang:element(2, Set)). + +-file("src/gleam/set.gleam", 190). +?DOC( + " Combines all entries into a single value by calling a given function on each\n" + " one.\n" + "\n" + " Sets are not ordered so the values are not returned in any specific order.\n" + " Do not write code that relies on the order entries are used by this\n" + " function as it may change in later versions of Gleam or Erlang.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " from_list([1, 3, 9])\n" + " |> fold(0, fn(accumulator, member) { accumulator + member })\n" + " // -> 13\n" + " ```\n" +). +-spec fold(set(CWH), CWJ, fun((CWJ, CWH) -> CWJ)) -> CWJ. +fold(Set, Initial, Reducer) -> + gleam@dict:fold( + erlang:element(2, Set), + Initial, + fun(A, K, _) -> Reducer(A, K) end + ). + +-file("src/gleam/set.gleam", 214). +?DOC( + " Creates a new set from an existing set, minus any members that a given\n" + " function returns `False` for.\n" + "\n" + " This function runs in loglinear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " import gleam/int\n" + "\n" + " from_list([1, 4, 6, 3, 675, 44, 67])\n" + " |> filter(keeping: int.is_even)\n" + " |> to_list\n" + " // -> [4, 6, 44]\n" + " ```\n" +). +-spec filter(set(CWK), fun((CWK) -> boolean())) -> set(CWK). +filter(Set, Predicate) -> + {set, + gleam@dict:filter(erlang:element(2, Set), fun(M, _) -> Predicate(M) end)}. + +-file("src/gleam/set.gleam", 249). +?DOC( + " Creates a new set from a given set with all the same entries except any\n" + " entry found on the given list.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " from_list([1, 2, 3, 4])\n" + " |> drop([1, 3])\n" + " |> to_list\n" + " // -> [2, 4]\n" + " ```\n" +). +-spec drop(set(CWR), list(CWR)) -> set(CWR). +drop(Set, Disallowed) -> + gleam@list:fold(Disallowed, Set, fun delete/2). + +-file("src/gleam/set.gleam", 267). +?DOC( + " Creates a new set from a given set, only including any members which are in\n" + " a given list.\n" + "\n" + " This function runs in loglinear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " from_list([1, 2, 3])\n" + " |> take([1, 3, 5])\n" + " |> to_list\n" + " // -> [1, 3]\n" + " ```\n" +). +-spec take(set(CWV), list(CWV)) -> set(CWV). +take(Set, Desired) -> + {set, gleam@dict:take(erlang:element(2, Set), Desired)}. + +-file("src/gleam/set.gleam", 287). +-spec order(set(CXD), set(CXD)) -> {set(CXD), set(CXD)}. +order(First, Second) -> + case maps:size(erlang:element(2, First)) > maps:size( + erlang:element(2, Second) + ) of + true -> + {First, Second}; + + false -> + {Second, First} + end. + +-file("src/gleam/set.gleam", 305). +?DOC( + " Creates a new set that contains members that are present in both given sets.\n" + "\n" + " This function runs in loglinear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " intersection(from_list([1, 2]), from_list([2, 3])) |> to_list\n" + " // -> [2]\n" + " ```\n" +). +-spec intersection(set(CXI), set(CXI)) -> set(CXI). +intersection(First, Second) -> + {Larger, Smaller} = order(First, Second), + take(Larger, to_list(Smaller)). + +-file("src/gleam/set.gleam", 323). +?DOC( + " Creates a new set that contains members that are present in the first set\n" + " but not the second.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " difference(from_list([1, 2]), from_list([2, 3, 4])) |> to_list\n" + " // -> [1]\n" + " ```\n" +). +-spec difference(set(CXM), set(CXM)) -> set(CXM). +difference(First, Second) -> + drop(First, to_list(Second)). + +-file("src/gleam/set.gleam", 344). +?DOC( + " Determines if a set is fully contained by another.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " is_subset(from_list([1]), from_list([1, 2]))\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " is_subset(from_list([1, 2, 3]), from_list([3, 4, 5]))\n" + " // -> False\n" + " ```\n" +). +-spec is_subset(set(CXQ), set(CXQ)) -> boolean(). +is_subset(First, Second) -> + intersection(First, Second) =:= First. + +-file("src/gleam/set.gleam", 362). +?DOC( + " Determines if two sets contain no common members\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " is_disjoint(from_list([1, 2, 3]), from_list([4, 5, 6]))\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " is_disjoint(from_list([1, 2, 3]), from_list([3, 4, 5]))\n" + " // -> False\n" + " ```\n" +). +-spec is_disjoint(set(CXT), set(CXT)) -> boolean(). +is_disjoint(First, Second) -> + intersection(First, Second) =:= new(). + +-file("src/gleam/set.gleam", 402). +?DOC( + " Calls a function for each member in a set, discarding the return\n" + " value.\n" + "\n" + " Useful for producing a side effect for every item of a set.\n" + "\n" + " ```gleam\n" + " let set = from_list([\"apple\", \"banana\", \"cherry\"])\n" + "\n" + " each(set, io.println)\n" + " // -> Nil\n" + " // apple\n" + " // banana\n" + " // cherry\n" + " ```\n" + "\n" + " The order of elements in the iteration is an implementation detail that\n" + " should not be relied upon.\n" +). +-spec each(set(CYA), fun((CYA) -> any())) -> nil. +each(Set, Fun) -> + fold( + Set, + nil, + fun(Nil, Member) -> + Fun(Member), + Nil + end + ). + +-file("src/gleam/set.gleam", 86). +?DOC( + " Inserts an member into the set.\n" + "\n" + " This function runs in logarithmic time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " new()\n" + " |> insert(1)\n" + " |> insert(2)\n" + " |> size\n" + " // -> 2\n" + " ```\n" +). +-spec insert(set(CVT), CVT) -> set(CVT). +insert(Set, Member) -> + {set, gleam@dict:insert(erlang:element(2, Set), Member, [])}. + +-file("src/gleam/set.gleam", 167). +?DOC( + " Creates a new set of the members in a given list.\n" + "\n" + " This function runs in loglinear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " import gleam/int\n" + " import gleam/list\n" + "\n" + " [1, 1, 2, 4, 3, 2] |> from_list |> to_list |> list.sort(by: int.compare)\n" + " // -> [1, 2, 3, 4]\n" + " ```\n" +). +-spec from_list(list(CWE)) -> set(CWE). +from_list(Members) -> + Dict = gleam@list:fold( + Members, + maps:new(), + fun(M, K) -> gleam@dict:insert(M, K, []) end + ), + {set, Dict}. + +-file("src/gleam/set.gleam", 232). +?DOC( + " Creates a new set from a given set with the result of applying the given\n" + " function to each member.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " from_list([1, 2, 3, 4])\n" + " |> map(with: fn(x) { x * 2 })\n" + " |> to_list\n" + " // -> [2, 4, 6, 8]\n" + " ```\n" +). +-spec map(set(CWN), fun((CWN) -> CWP)) -> set(CWP). +map(Set, Fun) -> + fold(Set, new(), fun(Acc, Member) -> insert(Acc, Fun(Member)) end). + +-file("src/gleam/set.gleam", 282). +?DOC( + " Creates a new set that contains all members of both given sets.\n" + "\n" + " This function runs in loglinear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " union(from_list([1, 2]), from_list([2, 3])) |> to_list\n" + " // -> [1, 2, 3]\n" + " ```\n" +). +-spec union(set(CWZ), set(CWZ)) -> set(CWZ). +union(First, Second) -> + {Larger, Smaller} = order(First, Second), + fold(Smaller, Larger, fun insert/2). + +-file("src/gleam/set.gleam", 374). +?DOC( + " Creates a new set that contains members that are present in either set, but\n" + " not both.\n" + "\n" + " ```gleam\n" + " symmetric_difference(from_list([1, 2, 3]), from_list([3, 4])) |> to_list\n" + " // -> [1, 2, 4]\n" + " ```\n" +). +-spec symmetric_difference(set(CXW), set(CXW)) -> set(CXW). +symmetric_difference(First, Second) -> + difference(union(First, Second), intersection(First, Second)). diff --git a/build/packages/gleam_stdlib/src/gleam@string.erl b/build/packages/gleam_stdlib/src/gleam@string.erl new file mode 100644 index 0000000..63006b8 --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam@string.erl @@ -0,0 +1,1012 @@ +-module(gleam@string). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/string.gleam"). +-export([is_empty/1, length/1, reverse/1, replace/3, lowercase/1, uppercase/1, compare/2, slice/3, crop/2, drop_end/2, contains/2, starts_with/2, ends_with/2, split_once/2, append/2, concat/1, repeat/2, join/2, pad_start/3, pad_end/3, trim_start/1, trim_end/1, trim/1, pop_grapheme/1, to_graphemes/1, split/2, to_utf_codepoints/1, from_utf_codepoints/1, utf_codepoint/1, utf_codepoint_to_int/1, to_option/1, first/1, last/1, capitalise/1, inspect/1, byte_size/1, drop_start/2]). +-export_type([direction/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC( + " Strings in Gleam are UTF-8 binaries. They can be written in your code as\n" + " text surrounded by `\"double quotes\"`.\n" +). + +-type direction() :: leading | trailing. + +-file("src/gleam/string.gleam", 23). +?DOC( + " Determines if a `String` is empty.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " is_empty(\"\")\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " is_empty(\"the world\")\n" + " // -> False\n" + " ```\n" +). +-spec is_empty(binary()) -> boolean(). +is_empty(Str) -> + Str =:= <<""/utf8>>. + +-file("src/gleam/string.gleam", 51). +?DOC( + " Gets the number of grapheme clusters in a given `String`.\n" + "\n" + " This function has to iterate across the whole string to count the number of\n" + " graphemes, so it runs in linear time. Avoid using this in a loop.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " length(\"Gleam\")\n" + " // -> 5\n" + " ```\n" + "\n" + " ```gleam\n" + " length(\"ß↑e̊\")\n" + " // -> 3\n" + " ```\n" + "\n" + " ```gleam\n" + " length(\"\")\n" + " // -> 0\n" + " ```\n" +). +-spec length(binary()) -> integer(). +length(String) -> + string:length(String). + +-file("src/gleam/string.gleam", 65). +?DOC( + " Reverses a `String`.\n" + "\n" + " This function has to iterate across the whole `String` so it runs in linear\n" + " time. Avoid using this in a loop.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " reverse(\"stressed\")\n" + " // -> \"desserts\"\n" + " ```\n" +). +-spec reverse(binary()) -> binary(). +reverse(String) -> + _pipe = String, + _pipe@1 = gleam_stdlib:identity(_pipe), + _pipe@2 = string:reverse(_pipe@1), + unicode:characters_to_binary(_pipe@2). + +-file("src/gleam/string.gleam", 86). +?DOC( + " Creates a new `String` by replacing all occurrences of a given substring.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " replace(\"www.example.com\", each: \".\", with: \"-\")\n" + " // -> \"www-example-com\"\n" + " ```\n" + "\n" + " ```gleam\n" + " replace(\"a,b,c,d,e\", each: \",\", with: \"/\")\n" + " // -> \"a/b/c/d/e\"\n" + " ```\n" +). +-spec replace(binary(), binary(), binary()) -> binary(). +replace(String, Pattern, Substitute) -> + _pipe = String, + _pipe@1 = gleam_stdlib:identity(_pipe), + _pipe@2 = gleam_stdlib:string_replace(_pipe@1, Pattern, Substitute), + unicode:characters_to_binary(_pipe@2). + +-file("src/gleam/string.gleam", 111). +?DOC( + " Creates a new `String` with all the graphemes in the input `String` converted to\n" + " lowercase.\n" + "\n" + " Useful for case-insensitive comparisons.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " lowercase(\"X-FILES\")\n" + " // -> \"x-files\"\n" + " ```\n" +). +-spec lowercase(binary()) -> binary(). +lowercase(String) -> + string:lowercase(String). + +-file("src/gleam/string.gleam", 127). +?DOC( + " Creates a new `String` with all the graphemes in the input `String` converted to\n" + " uppercase.\n" + "\n" + " Useful for case-insensitive comparisons and VIRTUAL YELLING.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " uppercase(\"skinner\")\n" + " // -> \"SKINNER\"\n" + " ```\n" +). +-spec uppercase(binary()) -> binary(). +uppercase(String) -> + string:uppercase(String). + +-file("src/gleam/string.gleam", 145). +?DOC( + " Compares two `String`s to see which is \"larger\" by comparing their graphemes.\n" + "\n" + " This does not compare the size or length of the given `String`s.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " compare(\"Anthony\", \"Anthony\")\n" + " // -> order.Eq\n" + " ```\n" + "\n" + " ```gleam\n" + " compare(\"A\", \"B\")\n" + " // -> order.Lt\n" + " ```\n" +). +-spec compare(binary(), binary()) -> gleam@order:order(). +compare(A, B) -> + case A =:= B of + true -> + eq; + + _ -> + case gleam_stdlib:less_than(A, B) of + true -> + lt; + + false -> + gt + end + end. + +-file("src/gleam/string.gleam", 194). +?DOC( + " Takes a substring given a start grapheme index and a length. Negative indexes\n" + " are taken starting from the *end* of the list.\n" + "\n" + " This function runs in linear time with the size of the index and the\n" + " length. Negative indexes are linear with the size of the input string in\n" + " addition to the other costs.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " slice(from: \"gleam\", at_index: 1, length: 2)\n" + " // -> \"le\"\n" + " ```\n" + "\n" + " ```gleam\n" + " slice(from: \"gleam\", at_index: 1, length: 10)\n" + " // -> \"leam\"\n" + " ```\n" + "\n" + " ```gleam\n" + " slice(from: \"gleam\", at_index: 10, length: 3)\n" + " // -> \"\"\n" + " ```\n" + "\n" + " ```gleam\n" + " slice(from: \"gleam\", at_index: -2, length: 2)\n" + " // -> \"am\"\n" + " ```\n" + "\n" + " ```gleam\n" + " slice(from: \"gleam\", at_index: -12, length: 2)\n" + " // -> \"\"\n" + " ```\n" +). +-spec slice(binary(), integer(), integer()) -> binary(). +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 -> + gleam_stdlib:slice(String, Translated_idx, Len) + end; + + false -> + gleam_stdlib:slice(String, Idx, Len) + end + end. + +-file("src/gleam/string.gleam", 232). +?DOC( + " Drops contents of the first `String` that occur before the second `String`.\n" + " If the `from` string does not contain the `before` string, `from` is\n" + " returned unchanged.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " crop(from: \"The Lone Gunmen\", before: \"Lone\")\n" + " // -> \"Lone Gunmen\"\n" + " ```\n" +). +-spec crop(binary(), binary()) -> binary(). +crop(String, Substring) -> + gleam_stdlib:crop_string(String, Substring). + +-file("src/gleam/string.gleam", 268). +?DOC( + " Drops *n* graphemes from the end of a `String`.\n" + "\n" + " This function traverses the full string, so it runs in linear time with the\n" + " size of the string. Avoid using this in a loop.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " drop_end(from: \"Cigarette Smoking Man\", up_to: 2)\n" + " // -> \"Cigarette Smoking M\"\n" + " ```\n" +). +-spec drop_end(binary(), integer()) -> binary(). +drop_end(String, Num_graphemes) -> + case Num_graphemes =< 0 of + true -> + String; + + false -> + slice(String, 0, string:length(String) - Num_graphemes) + end. + +-file("src/gleam/string.gleam", 296). +?DOC( + " Checks if the first `String` contains the second.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " contains(does: \"theory\", contain: \"ory\")\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " contains(does: \"theory\", contain: \"the\")\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " contains(does: \"theory\", contain: \"THE\")\n" + " // -> False\n" + " ```\n" +). +-spec contains(binary(), binary()) -> boolean(). +contains(Haystack, Needle) -> + gleam_stdlib:contains_string(Haystack, Needle). + +-file("src/gleam/string.gleam", 309). +?DOC( + " Checks whether the first `String` starts with the second one.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " starts_with(\"theory\", \"ory\")\n" + " // -> False\n" + " ```\n" +). +-spec starts_with(binary(), binary()) -> boolean(). +starts_with(String, Prefix) -> + gleam_stdlib:string_starts_with(String, Prefix). + +-file("src/gleam/string.gleam", 322). +?DOC( + " Checks whether the first `String` ends with the second one.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " ends_with(\"theory\", \"ory\")\n" + " // -> True\n" + " ```\n" +). +-spec ends_with(binary(), binary()) -> boolean(). +ends_with(String, Suffix) -> + gleam_stdlib:string_ends_with(String, Suffix). + +-file("src/gleam/string.gleam", 361). +?DOC( + " Splits a `String` a single time on the given substring.\n" + "\n" + " Returns an `Error` if substring not present.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " split_once(\"home/gleam/desktop/\", on: \"/\")\n" + " // -> Ok(#(\"home\", \"gleam/desktop/\"))\n" + " ```\n" + "\n" + " ```gleam\n" + " split_once(\"home/gleam/desktop/\", on: \"?\")\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec split_once(binary(), binary()) -> {ok, {binary(), binary()}} | + {error, nil}. +split_once(String, Substring) -> + case string:split(String, Substring) of + [First, Rest] -> + {ok, {First, Rest}}; + + _ -> + {error, nil} + end. + +-file("src/gleam/string.gleam", 392). +?DOC( + " Creates a new `String` by joining two `String`s together.\n" + "\n" + " This function typically copies both `String`s and runs in linear time, but\n" + " the exact behaviour will depend on how the runtime you are using optimises\n" + " your code. Benchmark and profile your code if you need to understand its\n" + " performance better.\n" + "\n" + " If you are joining together large string and want to avoid copying any data\n" + " you may want to investigate using the [`string_tree`](../gleam/string_tree.html)\n" + " module.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " append(to: \"butter\", suffix: \"fly\")\n" + " // -> \"butterfly\"\n" + " ```\n" +). +-spec append(binary(), binary()) -> binary(). +append(First, Second) -> + <>. + +-file("src/gleam/string.gleam", 412). +-spec concat_loop(list(binary()), binary()) -> binary(). +concat_loop(Strings, Accumulator) -> + case Strings of + [String | Strings@1] -> + concat_loop(Strings@1, <>); + + [] -> + Accumulator + end. + +-file("src/gleam/string.gleam", 408). +?DOC( + " Creates a new `String` by joining many `String`s together.\n" + "\n" + " This function copies all the `String`s and runs in linear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " concat([\"never\", \"the\", \"less\"])\n" + " // -> \"nevertheless\"\n" + " ```\n" +). +-spec concat(list(binary())) -> binary(). +concat(Strings) -> + erlang:list_to_binary(Strings). + +-file("src/gleam/string.gleam", 437). +-spec repeat_loop(integer(), binary(), binary()) -> binary(). +repeat_loop(Times, Doubling_acc, Acc) -> + Acc@1 = case Times rem 2 of + 0 -> + Acc; + + _ -> + <> + end, + Times@1 = Times div 2, + case Times@1 =< 0 of + true -> + Acc@1; + + false -> + repeat_loop( + Times@1, + <>, + Acc@1 + ) + end. + +-file("src/gleam/string.gleam", 430). +?DOC( + " Creates a new `String` by repeating a `String` a given number of times.\n" + "\n" + " This function runs in loglinear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " repeat(\"ha\", times: 3)\n" + " // -> \"hahaha\"\n" + " ```\n" +). +-spec repeat(binary(), integer()) -> binary(). +repeat(String, Times) -> + case Times =< 0 of + true -> + <<""/utf8>>; + + false -> + repeat_loop(Times, String, <<""/utf8>>) + end. + +-file("src/gleam/string.gleam", 467). +-spec join_loop(list(binary()), binary(), binary()) -> binary(). +join_loop(Strings, Separator, Accumulator) -> + case Strings of + [] -> + Accumulator; + + [String | Strings@1] -> + join_loop( + Strings@1, + Separator, + <<<>/binary, + String/binary>> + ) + end. + +-file("src/gleam/string.gleam", 460). +?DOC( + " Joins many `String`s together with a given separator.\n" + "\n" + " This function runs in linear time.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " join([\"home\",\"evan\",\"Desktop\"], with: \"/\")\n" + " // -> \"home/evan/Desktop\"\n" + " ```\n" +). +-spec join(list(binary()), binary()) -> binary(). +join(Strings, Separator) -> + case Strings of + [] -> + <<""/utf8>>; + + [First | Rest] -> + join_loop(Rest, Separator, First) + end. + +-file("src/gleam/string.gleam", 545). +-spec padding(integer(), binary()) -> binary(). +padding(Size, Pad_string) -> + Pad_string_length = string:length(Pad_string), + Num_pads = case Pad_string_length of + 0 -> 0; + Gleam@denominator -> Size div Gleam@denominator + end, + Extra = case Pad_string_length of + 0 -> 0; + Gleam@denominator@1 -> Size rem Gleam@denominator@1 + end, + <<(repeat(Pad_string, Num_pads))/binary, + (slice(Pad_string, 0, Extra))/binary>>. + +-file("src/gleam/string.gleam", 498). +?DOC( + " Pads the start of a `String` until it has a given length.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " pad_start(\"121\", to: 5, with: \".\")\n" + " // -> \"..121\"\n" + " ```\n" + "\n" + " ```gleam\n" + " pad_start(\"121\", to: 3, with: \".\")\n" + " // -> \"121\"\n" + " ```\n" + "\n" + " ```gleam\n" + " pad_start(\"121\", to: 2, with: \".\")\n" + " // -> \"121\"\n" + " ```\n" +). +-spec pad_start(binary(), integer(), binary()) -> binary(). +pad_start(String, Desired_length, Pad_string) -> + Current_length = string:length(String), + To_pad_length = Desired_length - Current_length, + case To_pad_length =< 0 of + true -> + String; + + false -> + <<(padding(To_pad_length, Pad_string))/binary, String/binary>> + end. + +-file("src/gleam/string.gleam", 531). +?DOC( + " Pads the end of a `String` until it has a given length.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " pad_end(\"123\", to: 5, with: \".\")\n" + " // -> \"123..\"\n" + " ```\n" + "\n" + " ```gleam\n" + " pad_end(\"123\", to: 3, with: \".\")\n" + " // -> \"123\"\n" + " ```\n" + "\n" + " ```gleam\n" + " pad_end(\"123\", to: 2, with: \".\")\n" + " // -> \"123\"\n" + " ```\n" +). +-spec pad_end(binary(), integer(), binary()) -> binary(). +pad_end(String, Desired_length, Pad_string) -> + Current_length = string:length(String), + To_pad_length = Desired_length - Current_length, + case To_pad_length =< 0 of + true -> + String; + + false -> + <> + end. + +-file("src/gleam/string.gleam", 589). +?DOC( + " Removes whitespace at the start of a `String`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " trim_start(\" hats \\n\")\n" + " // -> \"hats \\n\"\n" + " ```\n" +). +-spec trim_start(binary()) -> binary(). +trim_start(String) -> + string:trim(String, leading). + +-file("src/gleam/string.gleam", 603). +?DOC( + " Removes whitespace at the end of a `String`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " trim_end(\" hats \\n\")\n" + " // -> \" hats\"\n" + " ```\n" +). +-spec trim_end(binary()) -> binary(). +trim_end(String) -> + string:trim(String, trailing). + +-file("src/gleam/string.gleam", 567). +?DOC( + " Removes whitespace on both sides of a `String`.\n" + "\n" + " Whitespace in this function is the set of nonbreakable whitespace\n" + " codepoints, defined as Pattern_White_Space in [Unicode Standard Annex #31][1].\n" + "\n" + " [1]: https://unicode.org/reports/tr31/\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " trim(\" hats \\n\")\n" + " // -> \"hats\"\n" + " ```\n" +). +-spec trim(binary()) -> binary(). +trim(String) -> + _pipe = String, + _pipe@1 = trim_start(_pipe), + trim_end(_pipe@1). + +-file("src/gleam/string.gleam", 630). +?DOC( + " Splits a non-empty `String` into its first element (head) and rest (tail).\n" + " This lets you pattern match on `String`s exactly as you would with lists.\n" + "\n" + " ## Performance\n" + "\n" + " There is a notable overhead to using this function, so you may not want to\n" + " use it in a tight loop. If you wish to efficiently parse a string you may\n" + " want to use alternatives such as the [splitter package](https://hex.pm/packages/splitter).\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " pop_grapheme(\"gleam\")\n" + " // -> Ok(#(\"g\", \"leam\"))\n" + " ```\n" + "\n" + " ```gleam\n" + " pop_grapheme(\"\")\n" + " // -> Error(Nil)\n" + " ```\n" +). +-spec pop_grapheme(binary()) -> {ok, {binary(), binary()}} | {error, nil}. +pop_grapheme(String) -> + gleam_stdlib:string_pop_grapheme(String). + +-file("src/gleam/string.gleam", 647). +-spec to_graphemes_loop(binary(), list(binary())) -> list(binary()). +to_graphemes_loop(String, Acc) -> + case gleam_stdlib:string_pop_grapheme(String) of + {ok, {Grapheme, Rest}} -> + to_graphemes_loop(Rest, [Grapheme | Acc]); + + {error, _} -> + Acc + end. + +-file("src/gleam/string.gleam", 641). +?DOC( + " Converts a `String` to a list of\n" + " [graphemes](https://en.wikipedia.org/wiki/Grapheme).\n" + "\n" + " ```gleam\n" + " to_graphemes(\"abc\")\n" + " // -> [\"a\", \"b\", \"c\"]\n" + " ```\n" +). +-spec to_graphemes(binary()) -> list(binary()). +to_graphemes(String) -> + _pipe = String, + _pipe@1 = to_graphemes_loop(_pipe, []), + lists:reverse(_pipe@1). + +-file("src/gleam/string.gleam", 333). +?DOC( + " Creates a list of `String`s by splitting a given string on a given substring.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " split(\"home/gleam/desktop/\", on: \"/\")\n" + " // -> [\"home\", \"gleam\", \"desktop\", \"\"]\n" + " ```\n" +). +-spec split(binary(), binary()) -> list(binary()). +split(X, Substring) -> + case Substring of + <<""/utf8>> -> + to_graphemes(X); + + _ -> + _pipe = X, + _pipe@1 = gleam_stdlib:identity(_pipe), + _pipe@2 = gleam@string_tree:split(_pipe@1, Substring), + gleam@list:map(_pipe@2, fun unicode:characters_to_binary/1) + end. + +-file("src/gleam/string.gleam", 694). +-spec to_utf_codepoints_loop(bitstring(), list(integer())) -> list(integer()). +to_utf_codepoints_loop(Bit_array, Acc) -> + case Bit_array of + <> -> + to_utf_codepoints_loop(Rest, [First | Acc]); + + _ -> + lists:reverse(Acc) + end. + +-file("src/gleam/string.gleam", 689). +-spec do_to_utf_codepoints(binary()) -> list(integer()). +do_to_utf_codepoints(String) -> + to_utf_codepoints_loop(<>, []). + +-file("src/gleam/string.gleam", 684). +?DOC( + " Converts a `String` to a `List` of `UtfCodepoint`.\n" + "\n" + " See and\n" + " for an\n" + " explanation on code points.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " \"a\" |> to_utf_codepoints\n" + " // -> [UtfCodepoint(97)]\n" + " ```\n" + "\n" + " ```gleam\n" + " // Semantically the same as:\n" + " // [\"🏳\", \"️\", \"‍\", \"🌈\"] or:\n" + " // [waving_white_flag, variant_selector_16, zero_width_joiner, rainbow]\n" + " \"🏳️‍🌈\" |> to_utf_codepoints\n" + " // -> [\n" + " // UtfCodepoint(127987),\n" + " // UtfCodepoint(65039),\n" + " // UtfCodepoint(8205),\n" + " // UtfCodepoint(127752),\n" + " // ]\n" + " ```\n" +). +-spec to_utf_codepoints(binary()) -> list(integer()). +to_utf_codepoints(String) -> + do_to_utf_codepoints(String). + +-file("src/gleam/string.gleam", 734). +?DOC( + " Converts a `List` of `UtfCodepoint`s to a `String`.\n" + "\n" + " See and\n" + " for an\n" + " explanation on code points.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let assert Ok(a) = utf_codepoint(97)\n" + " let assert Ok(b) = utf_codepoint(98)\n" + " let assert Ok(c) = utf_codepoint(99)\n" + " from_utf_codepoints([a, b, c])\n" + " // -> \"abc\"\n" + " ```\n" +). +-spec from_utf_codepoints(list(integer())) -> binary(). +from_utf_codepoints(Utf_codepoints) -> + gleam_stdlib:utf_codepoint_list_to_string(Utf_codepoints). + +-file("src/gleam/string.gleam", 740). +?DOC( + " Converts an integer to a `UtfCodepoint`.\n" + "\n" + " Returns an `Error` if the integer does not represent a valid UTF codepoint.\n" +). +-spec utf_codepoint(integer()) -> {ok, integer()} | {error, nil}. +utf_codepoint(Value) -> + case Value of + I when I > 1114111 -> + {error, nil}; + + I@1 when (I@1 >= 55296) andalso (I@1 =< 57343) -> + {error, nil}; + + I@2 when I@2 < 0 -> + {error, nil}; + + I@3 -> + {ok, gleam_stdlib:identity(I@3)} + end. + +-file("src/gleam/string.gleam", 761). +?DOC( + " Converts an UtfCodepoint to its ordinal code point value.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let assert [utf_codepoint, ..] = to_utf_codepoints(\"💜\")\n" + " utf_codepoint_to_int(utf_codepoint)\n" + " // -> 128156\n" + " ```\n" +). +-spec utf_codepoint_to_int(integer()) -> integer(). +utf_codepoint_to_int(Cp) -> + gleam_stdlib:identity(Cp). + +-file("src/gleam/string.gleam", 778). +?DOC( + " Converts a `String` into `Option(String)` where an empty `String` becomes\n" + " `None`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " to_option(\"\")\n" + " // -> None\n" + " ```\n" + "\n" + " ```gleam\n" + " to_option(\"hats\")\n" + " // -> Some(\"hats\")\n" + " ```\n" +). +-spec to_option(binary()) -> gleam@option:option(binary()). +to_option(String) -> + case String of + <<""/utf8>> -> + none; + + _ -> + {some, String} + end. + +-file("src/gleam/string.gleam", 801). +?DOC( + " Returns the first grapheme cluster in a given `String` and wraps it in a\n" + " `Result(String, Nil)`. If the `String` is empty, it returns `Error(Nil)`.\n" + " Otherwise, it returns `Ok(String)`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " first(\"\")\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " first(\"icecream\")\n" + " // -> Ok(\"i\")\n" + " ```\n" +). +-spec first(binary()) -> {ok, binary()} | {error, nil}. +first(String) -> + case gleam_stdlib:string_pop_grapheme(String) of + {ok, {First, _}} -> + {ok, First}; + + {error, E} -> + {error, E} + end. + +-file("src/gleam/string.gleam", 827). +?DOC( + " Returns the last grapheme cluster in a given `String` and wraps it in a\n" + " `Result(String, Nil)`. If the `String` is empty, it returns `Error(Nil)`.\n" + " Otherwise, it returns `Ok(String)`.\n" + "\n" + " This function traverses the full string, so it runs in linear time with the\n" + " length of the string. Avoid using this in a loop.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " last(\"\")\n" + " // -> Error(Nil)\n" + " ```\n" + "\n" + " ```gleam\n" + " last(\"icecream\")\n" + " // -> Ok(\"m\")\n" + " ```\n" +). +-spec last(binary()) -> {ok, binary()} | {error, nil}. +last(String) -> + case gleam_stdlib:string_pop_grapheme(String) of + {ok, {First, <<""/utf8>>}} -> + {ok, First}; + + {ok, {_, Rest}} -> + {ok, slice(Rest, -1, 1)}; + + {error, E} -> + {error, E} + end. + +-file("src/gleam/string.gleam", 845). +?DOC( + " Creates a new `String` with the first grapheme in the input `String`\n" + " converted to uppercase and the remaining graphemes to lowercase.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " capitalise(\"mamouna\")\n" + " // -> \"Mamouna\"\n" + " ```\n" +). +-spec capitalise(binary()) -> binary(). +capitalise(String) -> + case gleam_stdlib:string_pop_grapheme(String) of + {ok, {First, Rest}} -> + append(string:uppercase(First), string:lowercase(Rest)); + + {error, _} -> + <<""/utf8>> + end. + +-file("src/gleam/string.gleam", 876). +?DOC( + " Returns a `String` representation of a term in Gleam syntax.\n" + "\n" + " This may be occasionally useful for quick-and-dirty printing of values in\n" + " scripts. For error reporting and other uses prefer constructing strings by\n" + " pattern matching on the values.\n" + "\n" + " ## Limitations\n" + "\n" + " The output format of this function is not stable and could change at any\n" + " time. The output is not suitable for parsing.\n" + "\n" + " This function works using runtime reflection, so the output may not be\n" + " perfectly accurate for data structures where the runtime structure doesn't\n" + " hold enough information to determine the original syntax. For example,\n" + " tuples with an Erlang atom in the first position will be mistaken for Gleam\n" + " records.\n" + "\n" + " ## Security and safety\n" + "\n" + " There is no limit to how large the strings that this function can produce.\n" + " Be careful not to call this function with large data structures or you\n" + " could use very large amounts of memory, potentially causing runtime\n" + " problems.\n" +). +-spec inspect(any()) -> binary(). +inspect(Term) -> + _pipe = Term, + _pipe@1 = gleam_stdlib:inspect(_pipe), + unicode:characters_to_binary(_pipe@1). + +-file("src/gleam/string.gleam", 900). +?DOC( + " Returns the number of bytes in a `String`.\n" + "\n" + " This function runs in constant time on Erlang and in linear time on\n" + " JavaScript.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " byte_size(\"🏳️‍⚧️🏳️‍🌈👩🏾‍❤️‍👨🏻\")\n" + " // -> 58\n" + " ```\n" +). +-spec byte_size(binary()) -> integer(). +byte_size(String) -> + erlang:byte_size(String). + +-file("src/gleam/string.gleam", 245). +?DOC( + " Drops *n* graphemes from the start of a `String`.\n" + "\n" + " This function runs in linear time with the number of graphemes to drop.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " drop_start(from: \"The Lone Gunmen\", up_to: 2)\n" + " // -> \"e Lone Gunmen\"\n" + " ```\n" +). +-spec drop_start(binary(), integer()) -> binary(). +drop_start(String, Num_graphemes) -> + case Num_graphemes =< 0 of + true -> + String; + + false -> + Prefix = gleam_stdlib:slice(String, 0, Num_graphemes), + Prefix_size = erlang:byte_size(Prefix), + binary:part( + String, + Prefix_size, + erlang:byte_size(String) - Prefix_size + ) + end. diff --git a/build/packages/gleam_stdlib/src/gleam@string_tree.erl b/build/packages/gleam_stdlib/src/gleam@string_tree.erl new file mode 100644 index 0000000..0d72b4d --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam@string_tree.erl @@ -0,0 +1,207 @@ +-module(gleam@string_tree). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/string_tree.gleam"). +-export([append_tree/2, prepend_tree/2, from_strings/1, new/0, concat/1, from_string/1, prepend/2, append/2, to_string/1, byte_size/1, join/2, lowercase/1, uppercase/1, reverse/1, split/2, replace/3, is_equal/2, is_empty/1]). +-export_type([string_tree/0, direction/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +-type string_tree() :: any(). + +-type direction() :: all. + +-file("src/gleam/string_tree.gleam", 61). +?DOC( + " Appends some `StringTree` onto the end of another.\n" + "\n" + " Runs in constant time.\n" +). +-spec append_tree(string_tree(), string_tree()) -> string_tree(). +append_tree(Tree, Suffix) -> + gleam_stdlib:iodata_append(Tree, Suffix). + +-file("src/gleam/string_tree.gleam", 48). +?DOC( + " Prepends some `StringTree` onto the start of another.\n" + "\n" + " Runs in constant time.\n" +). +-spec prepend_tree(string_tree(), string_tree()) -> string_tree(). +prepend_tree(Tree, Prefix) -> + gleam_stdlib:iodata_append(Prefix, Tree). + +-file("src/gleam/string_tree.gleam", 69). +?DOC( + " Converts a list of strings into a `StringTree`.\n" + "\n" + " Runs in constant time.\n" +). +-spec from_strings(list(binary())) -> string_tree(). +from_strings(Strings) -> + gleam_stdlib:identity(Strings). + +-file("src/gleam/string_tree.gleam", 24). +?DOC( + " Create an empty `StringTree`. Useful as the start of a pipe chaining many\n" + " trees together.\n" +). +-spec new() -> string_tree(). +new() -> + gleam_stdlib:identity([]). + +-file("src/gleam/string_tree.gleam", 77). +?DOC( + " Joins a list of trees into a single tree.\n" + "\n" + " Runs in constant time.\n" +). +-spec concat(list(string_tree())) -> string_tree(). +concat(Trees) -> + gleam_stdlib:identity(Trees). + +-file("src/gleam/string_tree.gleam", 85). +?DOC( + " Converts a string into a `StringTree`.\n" + "\n" + " Runs in constant time.\n" +). +-spec from_string(binary()) -> string_tree(). +from_string(String) -> + gleam_stdlib:identity(String). + +-file("src/gleam/string_tree.gleam", 32). +?DOC( + " Prepends a `String` onto the start of some `StringTree`.\n" + "\n" + " Runs in constant time.\n" +). +-spec prepend(string_tree(), binary()) -> string_tree(). +prepend(Tree, Prefix) -> + gleam_stdlib:iodata_append(gleam_stdlib:identity(Prefix), Tree). + +-file("src/gleam/string_tree.gleam", 40). +?DOC( + " Appends a `String` onto the end of some `StringTree`.\n" + "\n" + " Runs in constant time.\n" +). +-spec append(string_tree(), binary()) -> string_tree(). +append(Tree, Second) -> + gleam_stdlib:iodata_append(Tree, gleam_stdlib:identity(Second)). + +-file("src/gleam/string_tree.gleam", 94). +?DOC( + " Turns a `StringTree` into a `String`\n" + "\n" + " This function is implemented natively by the virtual machine and is highly\n" + " optimised.\n" +). +-spec to_string(string_tree()) -> binary(). +to_string(Tree) -> + unicode:characters_to_binary(Tree). + +-file("src/gleam/string_tree.gleam", 100). +?DOC(" Returns the size of the `StringTree` in bytes.\n"). +-spec byte_size(string_tree()) -> integer(). +byte_size(Tree) -> + erlang:iolist_size(Tree). + +-file("src/gleam/string_tree.gleam", 104). +?DOC(" Joins the given trees into a new tree separated with the given string.\n"). +-spec join(list(string_tree()), binary()) -> string_tree(). +join(Trees, Sep) -> + _pipe = Trees, + _pipe@1 = gleam@list:intersperse(_pipe, gleam_stdlib:identity(Sep)), + gleam_stdlib:identity(_pipe@1). + +-file("src/gleam/string_tree.gleam", 115). +?DOC( + " Converts a `StringTree` to a new one where the contents have been\n" + " lowercased.\n" +). +-spec lowercase(string_tree()) -> string_tree(). +lowercase(Tree) -> + string:lowercase(Tree). + +-file("src/gleam/string_tree.gleam", 122). +?DOC( + " Converts a `StringTree` to a new one where the contents have been\n" + " uppercased.\n" +). +-spec uppercase(string_tree()) -> string_tree(). +uppercase(Tree) -> + string:uppercase(Tree). + +-file("src/gleam/string_tree.gleam", 127). +?DOC(" Converts a `StringTree` to a new one with the contents reversed.\n"). +-spec reverse(string_tree()) -> string_tree(). +reverse(Tree) -> + string:reverse(Tree). + +-file("src/gleam/string_tree.gleam", 145). +?DOC(" Splits a `StringTree` on a given pattern into a list of trees.\n"). +-spec split(string_tree(), binary()) -> list(string_tree()). +split(Tree, Pattern) -> + string:split(Tree, Pattern, all). + +-file("src/gleam/string_tree.gleam", 156). +?DOC(" Replaces all instances of a pattern with a given string substitute.\n"). +-spec replace(string_tree(), binary(), binary()) -> string_tree(). +replace(Tree, Pattern, Substitute) -> + gleam_stdlib:string_replace(Tree, Pattern, Substitute). + +-file("src/gleam/string_tree.gleam", 182). +?DOC( + " Compares two string trees to determine if they have the same textual\n" + " content.\n" + "\n" + " Comparing two string trees using the `==` operator may return `False` even\n" + " if they have the same content as they may have been build in different ways,\n" + " so using this function is often preferred.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " from_strings([\"a\", \"b\"]) == from_string(\"ab\")\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " is_equal(from_strings([\"a\", \"b\"]), from_string(\"ab\"))\n" + " // -> True\n" + " ```\n" +). +-spec is_equal(string_tree(), string_tree()) -> boolean(). +is_equal(A, B) -> + string:equal(A, B). + +-file("src/gleam/string_tree.gleam", 206). +?DOC( + " Inspects a `StringTree` to determine if it is equivalent to an empty string.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " from_string(\"ok\") |> is_empty\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " from_string(\"\") |> is_empty\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " from_strings([]) |> is_empty\n" + " // -> True\n" + " ```\n" +). +-spec is_empty(string_tree()) -> boolean(). +is_empty(Tree) -> + string:is_empty(Tree). diff --git a/build/packages/gleam_stdlib/src/gleam@uri.erl b/build/packages/gleam_stdlib/src/gleam@uri.erl new file mode 100644 index 0000000..0819463 --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam@uri.erl @@ -0,0 +1,1044 @@ +-module(gleam@uri). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/uri.gleam"). +-export([parse_query/1, percent_encode/1, query_to_string/1, percent_decode/1, path_segments/1, to_string/1, origin/1, merge/2, parse/1]). +-export_type([uri/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC( + " Utilities for working with URIs\n" + "\n" + " This module provides functions for working with URIs (for example, parsing\n" + " URIs or encoding query strings). The functions in this module are implemented\n" + " according to [RFC 3986](https://tools.ietf.org/html/rfc3986).\n" + "\n" + " Query encoding (Form encoding) is defined in the\n" + " [W3C specification](https://www.w3.org/TR/html52/sec-forms.html#urlencoded-form-data).\n" +). + +-type uri() :: {uri, + gleam@option:option(binary()), + gleam@option:option(binary()), + gleam@option:option(binary()), + gleam@option:option(integer()), + binary(), + gleam@option:option(binary()), + gleam@option:option(binary())}. + +-file("src/gleam/uri.gleam", 289). +-spec is_valid_host_within_brackets_char(integer()) -> boolean(). +is_valid_host_within_brackets_char(Char) -> + (((((48 >= Char) andalso (Char =< 57)) orelse ((65 >= Char) andalso (Char =< 90))) + orelse ((97 >= Char) andalso (Char =< 122))) + orelse (Char =:= 58)) + orelse (Char =:= 46). + +-file("src/gleam/uri.gleam", 506). +-spec parse_fragment(binary(), uri()) -> {ok, uri()} | {error, nil}. +parse_fragment(Rest, Pieces) -> + {ok, + {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + {some, Rest}}}. + +-file("src/gleam/uri.gleam", 478). +-spec parse_query_with_question_mark_loop(binary(), binary(), uri(), integer()) -> {ok, + uri()} | + {error, nil}. +parse_query_with_question_mark_loop(Original, Uri_string, Pieces, Size) -> + case Uri_string of + <<"#"/utf8, Rest/binary>> when Size =:= 0 -> + parse_fragment(Rest, Pieces); + + <<"#"/utf8, Rest@1/binary>> -> + Query = binary:part(Original, 0, Size), + Pieces@1 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + erlang:element(6, Pieces), + {some, Query}, + erlang:element(8, Pieces)}, + parse_fragment(Rest@1, Pieces@1); + + <<""/utf8>> -> + {ok, + {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + erlang:element(6, Pieces), + {some, Original}, + erlang:element(8, Pieces)}}; + + _ -> + {_, Rest@2} = gleam_stdlib:string_pop_codeunit(Uri_string), + parse_query_with_question_mark_loop( + Original, + Rest@2, + Pieces, + Size + 1 + ) + end. + +-file("src/gleam/uri.gleam", 471). +-spec parse_query_with_question_mark(binary(), uri()) -> {ok, uri()} | + {error, nil}. +parse_query_with_question_mark(Uri_string, Pieces) -> + parse_query_with_question_mark_loop(Uri_string, Uri_string, Pieces, 0). + +-file("src/gleam/uri.gleam", 437). +-spec parse_path_loop(binary(), binary(), uri(), integer()) -> {ok, uri()} | + {error, nil}. +parse_path_loop(Original, Uri_string, Pieces, Size) -> + case Uri_string of + <<"?"/utf8, Rest/binary>> -> + Path = binary:part(Original, 0, Size), + Pieces@1 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + Path, + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_query_with_question_mark(Rest, Pieces@1); + + <<"#"/utf8, Rest@1/binary>> -> + Path@1 = binary:part(Original, 0, Size), + Pieces@2 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + Path@1, + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_fragment(Rest@1, Pieces@2); + + <<""/utf8>> -> + {ok, + {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + Original, + erlang:element(7, Pieces), + erlang:element(8, Pieces)}}; + + _ -> + {_, Rest@2} = gleam_stdlib:string_pop_codeunit(Uri_string), + parse_path_loop(Original, Rest@2, Pieces, Size + 1) + end. + +-file("src/gleam/uri.gleam", 433). +-spec parse_path(binary(), uri()) -> {ok, uri()} | {error, nil}. +parse_path(Uri_string, Pieces) -> + parse_path_loop(Uri_string, Uri_string, Pieces, 0). + +-file("src/gleam/uri.gleam", 388). +-spec parse_port_loop(binary(), uri(), integer()) -> {ok, uri()} | {error, nil}. +parse_port_loop(Uri_string, Pieces, Port) -> + case Uri_string of + <<"0"/utf8, Rest/binary>> -> + parse_port_loop(Rest, Pieces, Port * 10); + + <<"1"/utf8, Rest@1/binary>> -> + parse_port_loop(Rest@1, Pieces, (Port * 10) + 1); + + <<"2"/utf8, Rest@2/binary>> -> + parse_port_loop(Rest@2, Pieces, (Port * 10) + 2); + + <<"3"/utf8, Rest@3/binary>> -> + parse_port_loop(Rest@3, Pieces, (Port * 10) + 3); + + <<"4"/utf8, Rest@4/binary>> -> + parse_port_loop(Rest@4, Pieces, (Port * 10) + 4); + + <<"5"/utf8, Rest@5/binary>> -> + parse_port_loop(Rest@5, Pieces, (Port * 10) + 5); + + <<"6"/utf8, Rest@6/binary>> -> + parse_port_loop(Rest@6, Pieces, (Port * 10) + 6); + + <<"7"/utf8, Rest@7/binary>> -> + parse_port_loop(Rest@7, Pieces, (Port * 10) + 7); + + <<"8"/utf8, Rest@8/binary>> -> + parse_port_loop(Rest@8, Pieces, (Port * 10) + 8); + + <<"9"/utf8, Rest@9/binary>> -> + parse_port_loop(Rest@9, Pieces, (Port * 10) + 9); + + <<"?"/utf8, Rest@10/binary>> -> + Pieces@1 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + {some, Port}, + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_query_with_question_mark(Rest@10, Pieces@1); + + <<"#"/utf8, Rest@11/binary>> -> + Pieces@2 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + {some, Port}, + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_fragment(Rest@11, Pieces@2); + + <<"/"/utf8, _/binary>> -> + Pieces@3 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + {some, Port}, + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_path(Uri_string, Pieces@3); + + <<""/utf8>> -> + {ok, + {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + {some, Port}, + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}}; + + _ -> + {error, nil} + end. + +-file("src/gleam/uri.gleam", 353). +-spec parse_port(binary(), uri()) -> {ok, uri()} | {error, nil}. +parse_port(Uri_string, Pieces) -> + case Uri_string of + <<":0"/utf8, Rest/binary>> -> + parse_port_loop(Rest, Pieces, 0); + + <<":1"/utf8, Rest@1/binary>> -> + parse_port_loop(Rest@1, Pieces, 1); + + <<":2"/utf8, Rest@2/binary>> -> + parse_port_loop(Rest@2, Pieces, 2); + + <<":3"/utf8, Rest@3/binary>> -> + parse_port_loop(Rest@3, Pieces, 3); + + <<":4"/utf8, Rest@4/binary>> -> + parse_port_loop(Rest@4, Pieces, 4); + + <<":5"/utf8, Rest@5/binary>> -> + parse_port_loop(Rest@5, Pieces, 5); + + <<":6"/utf8, Rest@6/binary>> -> + parse_port_loop(Rest@6, Pieces, 6); + + <<":7"/utf8, Rest@7/binary>> -> + parse_port_loop(Rest@7, Pieces, 7); + + <<":8"/utf8, Rest@8/binary>> -> + parse_port_loop(Rest@8, Pieces, 8); + + <<":9"/utf8, Rest@9/binary>> -> + parse_port_loop(Rest@9, Pieces, 9); + + <<":"/utf8>> -> + {ok, Pieces}; + + <<""/utf8>> -> + {ok, Pieces}; + + <<"?"/utf8, Rest@10/binary>> -> + parse_query_with_question_mark(Rest@10, Pieces); + + <<":?"/utf8, Rest@10/binary>> -> + parse_query_with_question_mark(Rest@10, Pieces); + + <<"#"/utf8, Rest@11/binary>> -> + parse_fragment(Rest@11, Pieces); + + <<":#"/utf8, Rest@11/binary>> -> + parse_fragment(Rest@11, Pieces); + + <<"/"/utf8, _/binary>> -> + parse_path(Uri_string, Pieces); + + <<":"/utf8, Rest@12/binary>> -> + case Rest@12 of + <<"/"/utf8, _/binary>> -> + parse_path(Rest@12, Pieces); + + _ -> + {error, nil} + end; + + _ -> + {error, nil} + end. + +-file("src/gleam/uri.gleam", 309). +-spec parse_host_outside_of_brackets_loop(binary(), binary(), uri(), integer()) -> {ok, + uri()} | + {error, nil}. +parse_host_outside_of_brackets_loop(Original, Uri_string, Pieces, Size) -> + case Uri_string of + <<""/utf8>> -> + {ok, + {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Original}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}}; + + <<":"/utf8, _/binary>> -> + Host = binary:part(Original, 0, Size), + Pieces@1 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Host}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_port(Uri_string, Pieces@1); + + <<"/"/utf8, _/binary>> -> + Host@1 = binary:part(Original, 0, Size), + Pieces@2 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Host@1}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_path(Uri_string, Pieces@2); + + <<"?"/utf8, Rest/binary>> -> + Host@2 = binary:part(Original, 0, Size), + Pieces@3 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Host@2}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_query_with_question_mark(Rest, Pieces@3); + + <<"#"/utf8, Rest@1/binary>> -> + Host@3 = binary:part(Original, 0, Size), + Pieces@4 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Host@3}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_fragment(Rest@1, Pieces@4); + + _ -> + {_, Rest@2} = gleam_stdlib:string_pop_codeunit(Uri_string), + parse_host_outside_of_brackets_loop( + Original, + Rest@2, + Pieces, + Size + 1 + ) + end. + +-file("src/gleam/uri.gleam", 229). +-spec parse_host_within_brackets_loop(binary(), binary(), uri(), integer()) -> {ok, + uri()} | + {error, nil}. +parse_host_within_brackets_loop(Original, Uri_string, Pieces, Size) -> + case Uri_string of + <<""/utf8>> -> + {ok, + {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Uri_string}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}}; + + <<"]"/utf8, Rest/binary>> when Size =:= 0 -> + parse_port(Rest, Pieces); + + <<"]"/utf8, Rest@1/binary>> -> + Host = binary:part(Original, 0, Size + 1), + Pieces@1 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Host}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_port(Rest@1, Pieces@1); + + <<"/"/utf8, _/binary>> when Size =:= 0 -> + parse_path(Uri_string, Pieces); + + <<"/"/utf8, _/binary>> -> + Host@1 = binary:part(Original, 0, Size), + Pieces@2 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Host@1}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_path(Uri_string, Pieces@2); + + <<"?"/utf8, Rest@2/binary>> when Size =:= 0 -> + parse_query_with_question_mark(Rest@2, Pieces); + + <<"?"/utf8, Rest@3/binary>> -> + Host@2 = binary:part(Original, 0, Size), + Pieces@3 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Host@2}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_query_with_question_mark(Rest@3, Pieces@3); + + <<"#"/utf8, Rest@4/binary>> when Size =:= 0 -> + parse_fragment(Rest@4, Pieces); + + <<"#"/utf8, Rest@5/binary>> -> + Host@3 = binary:part(Original, 0, Size), + Pieces@4 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, Host@3}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_fragment(Rest@5, Pieces@4); + + _ -> + {Char, Rest@6} = gleam_stdlib:string_pop_codeunit(Uri_string), + case is_valid_host_within_brackets_char(Char) of + true -> + parse_host_within_brackets_loop( + Original, + Rest@6, + Pieces, + Size + 1 + ); + + false -> + parse_host_outside_of_brackets_loop( + Original, + Original, + Pieces, + 0 + ) + end + end. + +-file("src/gleam/uri.gleam", 222). +-spec parse_host_within_brackets(binary(), uri()) -> {ok, uri()} | {error, nil}. +parse_host_within_brackets(Uri_string, Pieces) -> + parse_host_within_brackets_loop(Uri_string, Uri_string, Pieces, 0). + +-file("src/gleam/uri.gleam", 302). +-spec parse_host_outside_of_brackets(binary(), uri()) -> {ok, uri()} | + {error, nil}. +parse_host_outside_of_brackets(Uri_string, Pieces) -> + parse_host_outside_of_brackets_loop(Uri_string, Uri_string, Pieces, 0). + +-file("src/gleam/uri.gleam", 199). +-spec parse_host(binary(), uri()) -> {ok, uri()} | {error, nil}. +parse_host(Uri_string, Pieces) -> + case Uri_string of + <<"["/utf8, _/binary>> -> + parse_host_within_brackets(Uri_string, Pieces); + + <<":"/utf8, _/binary>> -> + Pieces@1 = {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, <<""/utf8>>}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_port(Uri_string, Pieces@1); + + <<""/utf8>> -> + {ok, + {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, <<""/utf8>>}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}}; + + _ -> + parse_host_outside_of_brackets(Uri_string, Pieces) + end. + +-file("src/gleam/uri.gleam", 167). +-spec parse_userinfo_loop(binary(), binary(), uri(), integer()) -> {ok, uri()} | + {error, nil}. +parse_userinfo_loop(Original, Uri_string, Pieces, Size) -> + case Uri_string of + <<"@"/utf8, Rest/binary>> when Size =:= 0 -> + parse_host(Rest, Pieces); + + <<"@"/utf8, Rest@1/binary>> -> + Userinfo = binary:part(Original, 0, Size), + Pieces@1 = {uri, + erlang:element(2, Pieces), + {some, Userinfo}, + erlang:element(4, Pieces), + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_host(Rest@1, Pieces@1); + + <<""/utf8>> -> + parse_host(Original, Pieces); + + <<"/"/utf8, _/binary>> -> + parse_host(Original, Pieces); + + <<"?"/utf8, _/binary>> -> + parse_host(Original, Pieces); + + <<"#"/utf8, _/binary>> -> + parse_host(Original, Pieces); + + _ -> + {_, Rest@2} = gleam_stdlib:string_pop_codeunit(Uri_string), + parse_userinfo_loop(Original, Rest@2, Pieces, Size + 1) + end. + +-file("src/gleam/uri.gleam", 163). +-spec parse_authority_pieces(binary(), uri()) -> {ok, uri()} | {error, nil}. +parse_authority_pieces(String, Pieces) -> + parse_userinfo_loop(String, String, Pieces, 0). + +-file("src/gleam/uri.gleam", 150). +-spec parse_authority_with_slashes(binary(), uri()) -> {ok, uri()} | + {error, nil}. +parse_authority_with_slashes(Uri_string, Pieces) -> + case Uri_string of + <<"//"/utf8>> -> + {ok, + {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + {some, <<""/utf8>>}, + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}}; + + <<"//"/utf8, Rest/binary>> -> + parse_authority_pieces(Rest, Pieces); + + _ -> + parse_path(Uri_string, Pieces) + end. + +-file("src/gleam/uri.gleam", 91). +-spec parse_scheme_loop(binary(), binary(), uri(), integer()) -> {ok, uri()} | + {error, nil}. +parse_scheme_loop(Original, Uri_string, Pieces, Size) -> + case Uri_string of + <<"/"/utf8, _/binary>> when Size =:= 0 -> + parse_authority_with_slashes(Uri_string, Pieces); + + <<"/"/utf8, _/binary>> -> + Scheme = binary:part(Original, 0, Size), + Pieces@1 = {uri, + {some, string:lowercase(Scheme)}, + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_authority_with_slashes(Uri_string, Pieces@1); + + <<"?"/utf8, Rest/binary>> when Size =:= 0 -> + parse_query_with_question_mark(Rest, Pieces); + + <<"?"/utf8, Rest@1/binary>> -> + Scheme@1 = binary:part(Original, 0, Size), + Pieces@2 = {uri, + {some, string:lowercase(Scheme@1)}, + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_query_with_question_mark(Rest@1, Pieces@2); + + <<"#"/utf8, Rest@2/binary>> when Size =:= 0 -> + parse_fragment(Rest@2, Pieces); + + <<"#"/utf8, Rest@3/binary>> -> + Scheme@2 = binary:part(Original, 0, Size), + Pieces@3 = {uri, + {some, string:lowercase(Scheme@2)}, + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_fragment(Rest@3, Pieces@3); + + <<":"/utf8, _/binary>> when Size =:= 0 -> + {error, nil}; + + <<":"/utf8, Rest@4/binary>> -> + Scheme@3 = binary:part(Original, 0, Size), + Pieces@4 = {uri, + {some, string:lowercase(Scheme@3)}, + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + erlang:element(6, Pieces), + erlang:element(7, Pieces), + erlang:element(8, Pieces)}, + parse_authority_with_slashes(Rest@4, Pieces@4); + + <<""/utf8>> -> + {ok, + {uri, + erlang:element(2, Pieces), + erlang:element(3, Pieces), + erlang:element(4, Pieces), + erlang:element(5, Pieces), + Original, + erlang:element(7, Pieces), + erlang:element(8, Pieces)}}; + + _ -> + {_, Rest@5} = gleam_stdlib:string_pop_codeunit(Uri_string), + parse_scheme_loop(Original, Rest@5, Pieces, Size + 1) + end. + +-file("src/gleam/uri.gleam", 537). +?DOC( + " Parses an urlencoded query string into a list of key value pairs.\n" + " Returns an error for invalid encoding.\n" + "\n" + " The opposite operation is `uri.query_to_string`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " parse_query(\"a=1&b=2\")\n" + " // -> Ok([#(\"a\", \"1\"), #(\"b\", \"2\")])\n" + " ```\n" +). +-spec parse_query(binary()) -> {ok, list({binary(), binary()})} | {error, nil}. +parse_query(Query) -> + gleam_stdlib:parse_query(Query). + +-file("src/gleam/uri.gleam", 573). +?DOC( + " Encodes a string into a percent encoded representation.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " percent_encode(\"100% great\")\n" + " // -> \"100%25%20great\"\n" + " ```\n" +). +-spec percent_encode(binary()) -> binary(). +percent_encode(Value) -> + gleam_stdlib:percent_encode(Value). + +-file("src/gleam/uri.gleam", 558). +-spec query_pair({binary(), binary()}) -> gleam@string_tree:string_tree(). +query_pair(Pair) -> + gleam_stdlib:identity( + [gleam_stdlib:percent_encode(erlang:element(1, Pair)), + <<"="/utf8>>, + gleam_stdlib:percent_encode(erlang:element(2, Pair))] + ). + +-file("src/gleam/uri.gleam", 550). +?DOC( + " Encodes a list of key value pairs as a URI query string.\n" + "\n" + " The opposite operation is `uri.parse_query`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " query_to_string([#(\"a\", \"1\"), #(\"b\", \"2\")])\n" + " // -> \"a=1&b=2\"\n" + " ```\n" +). +-spec query_to_string(list({binary(), binary()})) -> binary(). +query_to_string(Query) -> + _pipe = Query, + _pipe@1 = gleam@list:map(_pipe, fun query_pair/1), + _pipe@2 = gleam@list:intersperse( + _pipe@1, + gleam_stdlib:identity(<<"&"/utf8>>) + ), + _pipe@3 = gleam_stdlib:identity(_pipe@2), + unicode:characters_to_binary(_pipe@3). + +-file("src/gleam/uri.gleam", 586). +?DOC( + " Decodes a percent encoded string.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " percent_decode(\"100%25%20great+fun\")\n" + " // -> Ok(\"100% great+fun\")\n" + " ```\n" +). +-spec percent_decode(binary()) -> {ok, binary()} | {error, nil}. +percent_decode(Value) -> + gleam_stdlib:percent_decode(Value). + +-file("src/gleam/uri.gleam", 608). +-spec remove_dot_segments_loop(list(binary()), list(binary())) -> list(binary()). +remove_dot_segments_loop(Input, Accumulator) -> + case Input of + [] -> + lists: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, + remove_dot_segments_loop(Rest, Accumulator@5) + end. + +-file("src/gleam/uri.gleam", 604). +-spec remove_dot_segments(list(binary())) -> list(binary()). +remove_dot_segments(Input) -> + remove_dot_segments_loop(Input, []). + +-file("src/gleam/uri.gleam", 600). +?DOC( + " Splits the path section of a URI into it's constituent segments.\n" + "\n" + " Removes empty segments and resolves dot-segments as specified in\n" + " [section 5.2](https://www.ietf.org/rfc/rfc3986.html#section-5.2) of the RFC.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " path_segments(\"/users/1\")\n" + " // -> [\"users\" ,\"1\"]\n" + " ```\n" +). +-spec path_segments(binary()) -> list(binary()). +path_segments(Path) -> + remove_dot_segments(gleam@string:split(Path, <<"/"/utf8>>)). + +-file("src/gleam/uri.gleam", 639). +?DOC( + " Encodes a `Uri` value as a URI string.\n" + "\n" + " The opposite operation is `uri.parse`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let uri = Uri(..empty, scheme: Some(\"https\"), host: Some(\"example.com\"))\n" + " to_string(uri)\n" + " // -> \"https://example.com\"\n" + " ```\n" +). +-spec to_string(uri()) -> binary(). +to_string(Uri) -> + Parts = case erlang:element(8, Uri) of + {some, Fragment} -> + [<<"#"/utf8>>, Fragment]; + + none -> + [] + end, + Parts@1 = case erlang:element(7, Uri) of + {some, Query} -> + [<<"?"/utf8>>, Query | Parts]; + + none -> + Parts + end, + Parts@2 = [erlang:element(6, Uri) | Parts@1], + Parts@3 = case {erlang:element(4, Uri), + gleam_stdlib:string_starts_with(erlang:element(6, Uri), <<"/"/utf8>>)} of + {{some, Host}, false} when Host =/= <<""/utf8>> -> + [<<"/"/utf8>> | Parts@2]; + + {_, _} -> + Parts@2 + end, + Parts@4 = case {erlang:element(4, Uri), erlang:element(5, Uri)} of + {{some, _}, {some, Port}} -> + [<<":"/utf8>>, erlang:integer_to_binary(Port) | Parts@3]; + + {_, _} -> + Parts@3 + end, + Parts@5 = case {erlang:element(2, Uri), + erlang:element(3, Uri), + erlang:element(4, Uri)} of + {{some, S}, {some, U}, {some, H}} -> + [S, <<"://"/utf8>>, U, <<"@"/utf8>>, H | Parts@4]; + + {{some, S@1}, none, {some, H@1}} -> + [S@1, <<"://"/utf8>>, H@1 | Parts@4]; + + {{some, S@2}, {some, _}, none} -> + [S@2, <<":"/utf8>> | Parts@4]; + + {{some, S@2}, none, none} -> + [S@2, <<":"/utf8>> | Parts@4]; + + {none, none, {some, H@2}} -> + [<<"//"/utf8>>, H@2 | Parts@4]; + + {_, _, _} -> + Parts@4 + end, + erlang:list_to_binary(Parts@5). + +-file("src/gleam/uri.gleam", 683). +?DOC( + " Fetches the origin of a URI.\n" + "\n" + " Returns the origin of a uri as defined in\n" + " [RFC 6454](https://tools.ietf.org/html/rfc6454)\n" + "\n" + " The supported URI schemes are `http` and `https`.\n" + " URLs without a scheme will return `Error`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " let assert Ok(uri) = parse(\"https://example.com/path?foo#bar\")\n" + " origin(uri)\n" + " // -> Ok(\"https://example.com\")\n" + " ```\n" +). +-spec origin(uri()) -> {ok, binary()} | {error, nil}. +origin(Uri) -> + {uri, Scheme, _, Host, Port, _, _, _} = Uri, + case {Host, Scheme} of + {{some, H}, {some, <<"https"/utf8>>}} when Port =:= {some, 443} -> + {ok, erlang:list_to_binary([<<"https://"/utf8>>, H])}; + + {{some, H@1}, {some, <<"http"/utf8>>}} when Port =:= {some, 80} -> + {ok, erlang:list_to_binary([<<"http://"/utf8>>, H@1])}; + + {{some, H@2}, {some, S}} when (S =:= <<"http"/utf8>>) orelse (S =:= <<"https"/utf8>>) -> + case Port of + {some, P} -> + {ok, + erlang:list_to_binary( + [S, + <<"://"/utf8>>, + H@2, + <<":"/utf8>>, + erlang:integer_to_binary(P)] + )}; + + none -> + {ok, erlang:list_to_binary([S, <<"://"/utf8>>, H@2])} + end; + + {_, _} -> + {error, nil} + end. + +-file("src/gleam/uri.gleam", 764). +-spec drop_last(list(DDO)) -> list(DDO). +drop_last(Elements) -> + gleam@list:take(Elements, erlang:length(Elements) - 1). + +-file("src/gleam/uri.gleam", 768). +-spec join_segments(list(binary())) -> binary(). +join_segments(Segments) -> + gleam@string:join([<<""/utf8>> | Segments], <<"/"/utf8>>). + +-file("src/gleam/uri.gleam", 706). +?DOC( + " Resolves a URI with respect to the given base URI.\n" + "\n" + " The base URI must be an absolute URI or this function will return an error.\n" + " The algorithm for merging uris is described in\n" + " [RFC 3986](https://tools.ietf.org/html/rfc3986#section-5.2).\n" +). +-spec merge(uri(), uri()) -> {ok, uri()} | {error, nil}. +merge(Base, Relative) -> + case Base of + {uri, {some, _}, _, {some, _}, _, _, _, _} -> + case Relative of + {uri, _, _, {some, _}, _, _, _, _} -> + Path = begin + _pipe = erlang:element(6, Relative), + _pipe@1 = gleam@string:split(_pipe, <<"/"/utf8>>), + _pipe@2 = remove_dot_segments(_pipe@1), + join_segments(_pipe@2) + end, + Resolved = {uri, + gleam@option:'or'( + erlang:element(2, Relative), + erlang:element(2, Base) + ), + none, + erlang:element(4, Relative), + gleam@option:'or'( + erlang:element(5, Relative), + erlang:element(5, Base) + ), + Path, + erlang:element(7, Relative), + erlang:element(8, Relative)}, + {ok, Resolved}; + + _ -> + {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_stdlib:string_starts_with( + erlang:element(6, Relative), + <<"/"/utf8>> + ) of + true -> + gleam@string:split( + erlang:element(6, Relative), + <<"/"/utf8>> + ); + + false -> + _pipe@3 = erlang:element(6, Base), + _pipe@4 = gleam@string:split( + _pipe@3, + <<"/"/utf8>> + ), + _pipe@5 = drop_last(_pipe@4), + lists:append( + _pipe@5, + gleam@string:split( + erlang:element(6, Relative), + <<"/"/utf8>> + ) + ) + end, + Path@1 = begin + _pipe@6 = Path_segments, + _pipe@7 = remove_dot_segments(_pipe@6), + join_segments(_pipe@7) + end, + {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. + +-file("src/gleam/uri.gleam", 81). +?DOC( + " Parses a compliant URI string into the `Uri` Type.\n" + " If the string is not a valid URI string then an error is returned.\n" + "\n" + " The opposite operation is `uri.to_string`.\n" + "\n" + " ## Examples\n" + "\n" + " ```gleam\n" + " parse(\"https://example.com:1234/a/b?query=true#fragment\")\n" + " // -> Ok(\n" + " // Uri(\n" + " // scheme: Some(\"https\"),\n" + " // userinfo: None,\n" + " // host: Some(\"example.com\"),\n" + " // port: Some(1234),\n" + " // path: \"/a/b\",\n" + " // query: Some(\"query=true\"),\n" + " // fragment: Some(\"fragment\")\n" + " // )\n" + " // )\n" + " ```\n" +). +-spec parse(binary()) -> {ok, uri()} | {error, nil}. +parse(Uri_string) -> + gleam_stdlib:uri_parse(Uri_string). diff --git a/build/packages/gleam_stdlib/src/gleam_stdlib.app.src b/build/packages/gleam_stdlib/src/gleam_stdlib.app.src new file mode 100644 index 0000000..761251e --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam_stdlib.app.src @@ -0,0 +1,31 @@ +{application, gleam_stdlib, [ + {vsn, "0.65.0"}, + {applications, []}, + {description, "A standard library for the Gleam programming language"}, + {modules, [gleam@bit_array, + gleam@bool, + gleam@bytes_tree, + gleam@dict, + gleam@dynamic, + gleam@dynamic@decode, + gleam@float, + gleam@function, + gleam@int, + gleam@io, + gleam@list, + gleam@option, + gleam@order, + gleam@pair, + gleam@result, + gleam@set, + gleam@string, + gleam@string_tree, + gleam@uri, + gleam_stdlib, + gleam_stdlib@@main, + gleam_stdlib_test_ffi, + gleeunit_ffi, + gleeunit_gleam_panic_ffi, + gleeunit_progress]}, + {registered, []} +]}. diff --git a/build/packages/gleam_stdlib/src/gleam_stdlib.erl b/build/packages/gleam_stdlib/src/gleam_stdlib.erl new file mode 100644 index 0000000..2c416f4 --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam_stdlib.erl @@ -0,0 +1,534 @@ +-module(gleam_stdlib). + +-export([ + map_get/2, iodata_append/2, identity/1, parse_int/1, parse_float/1, + less_than/2, string_pop_grapheme/1, string_pop_codeunit/1, + string_starts_with/2, wrap_list/1, string_ends_with/2, string_pad/4, + uri_parse/1, bit_array_slice/3, percent_encode/1, percent_decode/1, + base64_decode/1, parse_query/1, bit_array_concat/1, + base64_encode/2, tuple_get/2, classify_dynamic/1, print/1, + println/1, print_error/1, println_error/1, inspect/1, float_to_string/1, + int_from_base_string/2, utf_codepoint_list_to_string/1, contains_string/2, + crop_string/2, base16_encode/1, base16_decode/1, string_replace/3, slice/3, + bit_array_to_int_and_size/1, bit_array_pad_to_bytes/1, index/2, list/5, + dict/1, int/1, float/1, bit_array/1, is_null/1 +]). + +%% Taken from OTP's uri_string module +-define(DEC2HEX(X), + if ((X) >= 0) andalso ((X) =< 9) -> (X) + $0; + ((X) >= 10) andalso ((X) =< 15) -> (X) + $A - 10 + end). + +%% Taken from OTP's uri_string module +-define(HEX2DEC(X), + if ((X) >= $0) andalso ((X) =< $9) -> (X) - $0; + ((X) >= $A) andalso ((X) =< $F) -> (X) - $A + 10; + ((X) >= $a) andalso ((X) =< $f) -> (X) - $a + 10 + end). + +-define(is_lowercase_char(X), + (X > 96 andalso X < 123)). +-define(is_underscore_char(X), + (X == 95)). +-define(is_digit_char(X), + (X > 47 andalso X < 58)). +-define(is_ascii_character(X), + (erlang:is_integer(X) andalso X >= 32 andalso X =< 126)). + +uppercase(X) -> X - 32. + +map_get(Map, Key) -> + case maps:find(Key, Map) of + error -> {error, nil}; + OkFound -> OkFound + end. + +iodata_append(Iodata, String) -> [Iodata, String]. + +identity(X) -> X. + +classify_dynamic(nil) -> <<"Nil">>; +classify_dynamic(null) -> <<"Nil">>; +classify_dynamic(undefined) -> <<"Nil">>; +classify_dynamic(X) when is_boolean(X) -> <<"Bool">>; +classify_dynamic(X) when is_atom(X) -> <<"Atom">>; +classify_dynamic(X) when is_binary(X) -> <<"String">>; +classify_dynamic(X) when is_bitstring(X) -> <<"BitArray">>; +classify_dynamic(X) when is_integer(X) -> <<"Int">>; +classify_dynamic(X) when is_float(X) -> <<"Float">>; +classify_dynamic(X) when is_list(X) -> <<"List">>; +classify_dynamic(X) when is_map(X) -> <<"Dict">>; +classify_dynamic(X) when is_tuple(X) -> <<"Array">>; +classify_dynamic(X) when is_reference(X) -> <<"Reference">>; +classify_dynamic(X) when is_pid(X) -> <<"Pid">>; +classify_dynamic(X) when is_port(X) -> <<"Port">>; +classify_dynamic(X) when + is_function(X, 0) orelse is_function(X, 1) orelse is_function(X, 2) orelse + is_function(X, 3) orelse is_function(X, 4) orelse is_function(X, 5) orelse + is_function(X, 6) orelse is_function(X, 7) orelse is_function(X, 8) orelse + is_function(X, 9) orelse is_function(X, 10) orelse is_function(X, 11) orelse + is_function(X, 12) -> <<"Function">>; +classify_dynamic(_) -> <<"Unknown">>. + +tuple_get(_tup, Index) when Index < 0 -> {error, nil}; +tuple_get(Data, Index) when Index >= tuple_size(Data) -> {error, nil}; +tuple_get(Data, Index) -> {ok, element(Index + 1, Data)}. + +int_from_base_string(String, Base) -> + case catch binary_to_integer(String, Base) of + Int when is_integer(Int) -> {ok, Int}; + _ -> {error, nil} + end. + +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. + +less_than(Lhs, Rhs) -> + Lhs < Rhs. + +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) -> + Chars = string:pad(String, Length, Dir, binary_to_list(PadString)), + case unicode:characters_to_binary(Chars) of + Bin when is_binary(Bin) -> Bin; + Error -> erlang:error({gleam_error, {string_invalid_utf8, Error}}) + end. + +string_pop_grapheme(String) -> + case string:next_grapheme(String) of + [ Next | Rest ] when is_binary(Rest) -> + {ok, {unicode:characters_to_binary([Next]), Rest}}; + + [ Next | Rest ] -> + {ok, {unicode:characters_to_binary([Next]), unicode:characters_to_binary(Rest)}}; + + _ -> {error, nil} + end. + +string_pop_codeunit(<>) -> {Cp, Rest}; +string_pop_codeunit(Binary) -> {0, Binary}. + +bit_array_pad_to_bytes(Bin) -> + case erlang:bit_size(Bin) rem 8 of + 0 -> Bin; + TrailingBits -> + PaddingBits = 8 - TrailingBits, + <> + end. + +bit_array_concat(BitArrays) -> + list_to_bitstring(BitArrays). + +-if(?OTP_RELEASE >= 26). +base64_encode(Bin, Padding) -> + PaddedBin = bit_array_pad_to_bytes(Bin), + base64:encode(PaddedBin, #{padding => Padding}). +-else. +base64_encode(_Bin, _Padding) -> + erlang:error(<<"Erlang OTP/26 or higher is required to use base64:encode">>). +-endif. + +bit_array_slice(Bin, Pos, Len) -> + try {ok, binary:part(Bin, Pos, Len)} + catch error:badarg -> {error, nil} + end. + +base64_decode(S) -> + try {ok, base64:decode(S)} + catch error:_ -> {error, nil} + end. + +wrap_list(X) when is_list(X) -> X; +wrap_list(X) -> [X]. + +parse_query(Query) -> + case uri_string:dissect_query(Query) of + {error, _, _} -> {error, nil}; + Pairs -> + Pairs1 = lists:map(fun + ({K, true}) -> {K, <<"">>}; + (Pair) -> Pair + end, Pairs), + {ok, Pairs1} + end. + +percent_encode(B) -> percent_encode(B, <<>>). +percent_encode(<<>>, Acc) -> + Acc; +percent_encode(<>, Acc) -> + case percent_ok(H) of + true -> + percent_encode(T, <>); + false -> + <> = <>, + percent_encode(T, <>) + end. + +percent_decode(Cs) -> percent_decode(Cs, <<>>). +percent_decode(<<$%, C0, C1, Cs/binary>>, Acc) -> + case is_hex_digit(C0) andalso is_hex_digit(C1) of + true -> + B = ?HEX2DEC(C0)*16+?HEX2DEC(C1), + percent_decode(Cs, <>); + false -> + {error, nil} + end; +percent_decode(<>, Acc) -> + percent_decode(Cs, <>); +percent_decode(<<>>, Acc) -> + check_utf8(Acc). + +percent_ok($!) -> true; +percent_ok($$) -> true; +percent_ok($') -> true; +percent_ok($() -> true; +percent_ok($)) -> true; +percent_ok($*) -> true; +percent_ok($+) -> true; +percent_ok($-) -> true; +percent_ok($.) -> true; +percent_ok($_) -> true; +percent_ok($~) -> true; +percent_ok(C) when $0 =< C, C =< $9 -> true; +percent_ok(C) when $A =< C, C =< $Z -> true; +percent_ok(C) when $a =< C, C =< $z -> true; +percent_ok(_) -> false. + +is_hex_digit(C) -> + ($0 =< C andalso C =< $9) orelse ($a =< C andalso C =< $f) orelse ($A =< C andalso C =< $F). + +check_utf8(Cs) -> + case unicode:characters_to_list(Cs) of + {incomplete, _, _} -> {error, nil}; + {error, _, _} -> {error, nil}; + _ -> {ok, Cs} + end. + +uri_parse(String) -> + case uri_string:parse(String) of + {error, _, _} -> {error, nil}; + Uri -> + Port = + try maps:get(port, Uri) of + undefined -> none; + Value -> {some, Value} + catch _:_ -> none + end, + {ok, {uri, + maps_get_optional(Uri, scheme), + maps_get_optional(Uri, userinfo), + maps_get_optional(Uri, host), + Port, + maps_get_or(Uri, path, <<>>), + maps_get_optional(Uri, query), + maps_get_optional(Uri, fragment) + }} + end. + +maps_get_optional(Map, Key) -> + try {some, maps:get(Key, Map)} + catch _:_ -> none + end. + +maps_get_or(Map, Key, Default) -> + try maps:get(Key, Map) + catch _:_ -> Default + end. + +print(String) -> + io:put_chars(String), + nil. + +println(String) -> + io:put_chars([String, $\n]), + nil. + +print_error(String) -> + io:put_chars(standard_error, String), + nil. + +println_error(String) -> + io:put_chars(standard_error, [String, $\n]), + nil. + +inspect(true) -> + "True"; +inspect(false) -> + "False"; +inspect(nil) -> + "Nil"; +inspect(Data) when is_map(Data) -> + Fields = [ + [<<"#(">>, inspect(Key), <<", ">>, inspect(Value), <<")">>] + || {Key, Value} <- maps:to_list(Data) + ], + ["dict.from_list([", lists:join(", ", Fields), "])"]; +inspect(Atom) when is_atom(Atom) -> + erlang:element(2, inspect_atom(Atom)); +inspect(Any) when is_integer(Any) -> + erlang:integer_to_list(Any); +inspect(Any) when is_float(Any) -> + io_lib_format:fwrite_g(Any); +inspect(Binary) when is_binary(Binary) -> + case inspect_maybe_utf8_string(Binary, <<>>) of + {ok, InspectedUtf8String} -> InspectedUtf8String; + {error, not_a_utf8_string} -> + Segments = [erlang:integer_to_list(X) || <> <= Binary], + ["<<", lists:join(", ", Segments), ">>"] + end; +inspect(Bits) when is_bitstring(Bits) -> + inspect_bit_array(Bits); +inspect(List) when is_list(List) -> + case inspect_list(List, true) of + {charlist, _} -> ["charlist.from_string(\"", list_to_binary(List), "\")"]; + {proper, Elements} -> ["[", Elements, "]"]; + {improper, Elements} -> ["//erl([", Elements, "])"] + end; +inspect(Any) when is_tuple(Any) % Record constructors + andalso is_atom(element(1, Any)) + andalso element(1, Any) =/= false + andalso element(1, Any) =/= true + andalso element(1, Any) =/= nil +-> + [Atom | ArgsList] = erlang:tuple_to_list(Any), + InspectedArgs = lists:map(fun inspect/1, ArgsList), + case inspect_atom(Atom) of + {gleam_atom, GleamAtom} -> + Args = lists:join(<<", ">>, InspectedArgs), + [GleamAtom, "(", Args, ")"]; + {erlang_atom, ErlangAtom} -> + Args = lists:join(<<", ">>, [ErlangAtom | InspectedArgs]), + ["#(", Args, ")"] + end; +inspect(Tuple) when is_tuple(Tuple) -> + Elements = lists:map(fun inspect/1, erlang:tuple_to_list(Tuple)), + ["#(", lists:join(", ", Elements), ")"]; +inspect(Any) when is_function(Any) -> + {arity, Arity} = erlang:fun_info(Any, arity), + ArgsAsciiCodes = lists:seq($a, $a + Arity - 1), + Args = lists:join(<<", ">>, + lists:map(fun(Arg) -> <> end, ArgsAsciiCodes) + ), + ["//fn(", Args, ") { ... }"]; +inspect(Any) -> + ["//erl(", io_lib:format("~p", [Any]), ")"]. + +inspect_atom(Atom) -> + Binary = erlang:atom_to_binary(Atom), + case inspect_maybe_gleam_atom(Binary, none, <<>>) of + {ok, Inspected} -> {gleam_atom, Inspected}; + {error, _} -> {erlang_atom, ["atom.create(\"", Binary, "\")"]} + end. + +inspect_maybe_gleam_atom(<<>>, none, _) -> + {error, nil}; +inspect_maybe_gleam_atom(<>, none, _) when ?is_digit_char(First) -> + {error, nil}; +inspect_maybe_gleam_atom(<<"_", _Rest/binary>>, none, _) -> + {error, nil}; +inspect_maybe_gleam_atom(<<"_">>, _PrevChar, _Acc) -> + {error, nil}; +inspect_maybe_gleam_atom(<<"_", _Rest/binary>>, $_, _Acc) -> + {error, nil}; +inspect_maybe_gleam_atom(<>, _PrevChar, _Acc) + when not (?is_lowercase_char(First) orelse ?is_underscore_char(First) orelse ?is_digit_char(First)) -> + {error, nil}; +inspect_maybe_gleam_atom(<>, none, Acc) -> + inspect_maybe_gleam_atom(Rest, First, <>); +inspect_maybe_gleam_atom(<<"_", Rest/binary>>, _PrevChar, Acc) -> + inspect_maybe_gleam_atom(Rest, $_, Acc); +inspect_maybe_gleam_atom(<>, $_, Acc) -> + inspect_maybe_gleam_atom(Rest, First, <>); +inspect_maybe_gleam_atom(<>, _PrevChar, Acc) -> + inspect_maybe_gleam_atom(Rest, First, <>); +inspect_maybe_gleam_atom(<<>>, _PrevChar, Acc) -> + {ok, Acc}; +inspect_maybe_gleam_atom(A, B, C) -> + erlang:display({A, B, C}), + throw({gleam_error, A, B, C}). + +inspect_list([], _) -> + {proper, []}; +inspect_list([First], true) when ?is_ascii_character(First) -> + {charlist, nil}; +inspect_list([First], _) -> + {proper, [inspect(First)]}; +inspect_list([First | Rest], ValidCharlist) when is_list(Rest) -> + StillValidCharlist = ValidCharlist andalso ?is_ascii_character(First), + {Kind, Inspected} = inspect_list(Rest, StillValidCharlist), + {Kind, [inspect(First), <<", ">> | Inspected]}; +inspect_list([First | ImproperTail], _) -> + {improper, [inspect(First), <<" | ">>, inspect(ImproperTail)]}. + +inspect_bit_array(Bits) -> + Text = inspect_bit_array(Bits, <<"<<">>), + <>">>. + +inspect_bit_array(<<>>, Acc) -> + Acc; +inspect_bit_array(<>, Acc) -> + inspect_bit_array(Rest, append_segment(Acc, erlang:integer_to_binary(X))); +inspect_bit_array(Rest, Acc) -> + Size = bit_size(Rest), + <> = Rest, + X1 = erlang:integer_to_binary(X), + Size1 = erlang:integer_to_binary(Size), + Segment = <>, + inspect_bit_array(<<>>, append_segment(Acc, Segment)). + +bit_array_to_int_and_size(A) -> + Size = bit_size(A), + <> = A, + {A1, Size}. + +append_segment(<<"<<">>, Segment) -> + <<"<<", Segment/binary>>; +append_segment(Acc, Segment) -> + <>. + + +inspect_maybe_utf8_string(Binary, Acc) -> + case Binary of + <<>> -> {ok, <<$", Acc/binary, $">>}; + <> -> + Escaped = case First of + $" -> <<$\\, $">>; + $\\ -> <<$\\, $\\>>; + $\r -> <<$\\, $r>>; + $\n -> <<$\\, $n>>; + $\t -> <<$\\, $t>>; + $\f -> <<$\\, $f>>; + X when X > 126, X < 160 -> convert_to_u(X); + X when X < 32 -> convert_to_u(X); + Other -> <> + end, + inspect_maybe_utf8_string(Rest, <>); + _ -> {error, not_a_utf8_string} + end. + +convert_to_u(Code) -> + list_to_binary(io_lib:format("\\u{~4.16.0B}", [Code])). + +float_to_string(Float) when is_float(Float) -> + erlang:iolist_to_binary(io_lib_format:fwrite_g(Float)). + +utf_codepoint_list_to_string(List) -> + case unicode:characters_to_binary(List) of + {error, _} -> erlang:error({gleam_error, {string_invalid_utf8, List}}); + Binary -> Binary + end. + +crop_string(String, Prefix) -> + case string:find(String, Prefix) of + nomatch -> String; + New -> New + end. + +contains_string(String, Substring) -> + is_bitstring(string:find(String, Substring)). + +base16_encode(Bin) -> + PaddedBin = bit_array_pad_to_bytes(Bin), + binary:encode_hex(PaddedBin). + +base16_decode(String) -> + try + {ok, binary:decode_hex(String)} + catch + _:_ -> {error, nil} + end. + +string_replace(String, Pattern, Replacement) -> + string:replace(String, Pattern, Replacement, all). + +slice(String, Index, Length) -> + case string:slice(String, Index, Length) of + X when is_binary(X) -> X; + X when is_list(X) -> unicode:characters_to_binary(X) + end. + +index([X | _], 0) -> + {ok, {some, X}}; +index([_, X | _], 1) -> + {ok, {some, X}}; +index([_, _, X | _], 2) -> + {ok, {some, X}}; +index([_, _, _, X | _], 3) -> + {ok, {some, X}}; +index([_, _, _, _, X | _], 4) -> + {ok, {some, X}}; +index([_, _, _, _, _, X | _], 5) -> + {ok, {some, X}}; +index([_, _, _, _, _, _, X | _], 6) -> + {ok, {some, X}}; +index([_, _, _, _, _, _, _, X | _], 7) -> + {ok, {some, X}}; +index(Tuple, Index) when is_tuple(Tuple) andalso is_integer(Index) -> + {ok, try + {some, element(Index + 1, Tuple)} + catch _:_ -> + none + end}; +index(Map, Key) when is_map(Map) -> + {ok, try + {some, maps:get(Key, Map)} + catch _:_ -> + none + end}; +index(_, Index) when is_integer(Index) -> + {error, <<"Indexable">>}; +index(_, _) -> + {error, <<"Dict">>}. + +list(T, A, B, C, D) when is_tuple(T) -> + list(tuple_to_list(T), A, B, C, D); +list([], _, _, _, Acc) -> + {lists:reverse(Acc), []}; +list([X | Xs], Decode, PushPath, Index, Acc) -> + {Out, Errors} = Decode(X), + case Errors of + [] -> list(Xs, Decode, PushPath, Index + 1, [Out | Acc]); + _ -> PushPath({[], Errors}, integer_to_binary(Index)) + end; +list(Unexpected, _, _, _, []) -> + Found = gleam@dynamic:classify(Unexpected), + Error = {decode_error, <<"List"/utf8>>, Found, []}, + {[], [Error]}; +list(_, _, _, _, Acc) -> + {lists:reverse(Acc), []}. + +dict(#{} = Data) -> {ok, Data}; +dict(_) -> {error, nil}. + +int(I) when is_integer(I) -> {ok, I}; +int(_) -> {error, 0}. + +float(F) when is_float(F) -> {ok, F}; +float(_) -> {error, 0.0}. + +bit_array(B) when is_bitstring(B) -> {ok, B}; +bit_array(_) -> {error, <<>>}. + +is_null(X) -> + X =:= undefined orelse X =:= null orelse X =:= nil. diff --git a/build/packages/gleam_stdlib/src/gleam_stdlib.mjs b/build/packages/gleam_stdlib/src/gleam_stdlib.mjs new file mode 100644 index 0000000..ebac45f --- /dev/null +++ b/build/packages/gleam_stdlib/src/gleam_stdlib.mjs @@ -0,0 +1,1048 @@ +import { + BitArray, + Error, + List, + Ok, + Result, + UtfCodepoint, + stringBits, + toBitArray, + bitArraySlice, + NonEmpty, + Empty, + CustomType, +} from "./gleam.mjs"; +import { Some, None } from "./gleam/option.mjs"; +import Dict from "./dict.mjs"; +import { classify } from "./gleam/dynamic.mjs"; +import { DecodeError } from "./gleam/dynamic/decode.mjs"; + +const Nil = undefined; +const NOT_FOUND = {}; + +export function identity(x) { + return x; +} + +export function parse_int(value) { + if (/^[-+]?(\d+)$/.test(value)) { + return new Ok(parseInt(value)); + } else { + return new Error(Nil); + } +} + +export function parse_float(value) { + if (/^[-+]?(\d+)\.(\d+)([eE][-+]?\d+)?$/.test(value)) { + return new Ok(parseFloat(value)); + } else { + return new Error(Nil); + } +} + +export function to_string(term) { + return term.toString(); +} + +export function int_to_base_string(int, base) { + return int.toString(base).toUpperCase(); +} + +const int_base_patterns = { + 2: /[^0-1]/, + 3: /[^0-2]/, + 4: /[^0-3]/, + 5: /[^0-4]/, + 6: /[^0-5]/, + 7: /[^0-6]/, + 8: /[^0-7]/, + 9: /[^0-8]/, + 10: /[^0-9]/, + 11: /[^0-9a]/, + 12: /[^0-9a-b]/, + 13: /[^0-9a-c]/, + 14: /[^0-9a-d]/, + 15: /[^0-9a-e]/, + 16: /[^0-9a-f]/, + 17: /[^0-9a-g]/, + 18: /[^0-9a-h]/, + 19: /[^0-9a-i]/, + 20: /[^0-9a-j]/, + 21: /[^0-9a-k]/, + 22: /[^0-9a-l]/, + 23: /[^0-9a-m]/, + 24: /[^0-9a-n]/, + 25: /[^0-9a-o]/, + 26: /[^0-9a-p]/, + 27: /[^0-9a-q]/, + 28: /[^0-9a-r]/, + 29: /[^0-9a-s]/, + 30: /[^0-9a-t]/, + 31: /[^0-9a-u]/, + 32: /[^0-9a-v]/, + 33: /[^0-9a-w]/, + 34: /[^0-9a-x]/, + 35: /[^0-9a-y]/, + 36: /[^0-9a-z]/, +}; + +export function int_from_base_string(string, base) { + if (int_base_patterns[base].test(string.replace(/^-/, "").toLowerCase())) { + return new Error(Nil); + } + + const result = parseInt(string, base); + + if (isNaN(result)) { + return new Error(Nil); + } + + return new Ok(result); +} + +export function string_replace(string, target, substitute) { + return string.replaceAll(target, substitute); +} + +export function string_reverse(string) { + return [...string].reverse().join(""); +} + +export function string_length(string) { + if (string === "") { + return 0; + } + const iterator = graphemes_iterator(string); + if (iterator) { + let i = 0; + for (const _ of iterator) { + i++; + } + return i; + } else { + return string.match(/./gsu).length; + } +} + +export function graphemes(string) { + const iterator = graphemes_iterator(string); + if (iterator) { + return List.fromArray(Array.from(iterator).map((item) => item.segment)); + } else { + return List.fromArray(string.match(/./gsu)); + } +} + +let segmenter = undefined; + +function graphemes_iterator(string) { + if (globalThis.Intl && Intl.Segmenter) { + segmenter ||= new Intl.Segmenter(); + return segmenter.segment(string)[Symbol.iterator](); + } +} + +export function pop_grapheme(string) { + let first; + const iterator = graphemes_iterator(string); + if (iterator) { + first = iterator.next().value?.segment; + } else { + first = string.match(/./su)?.[0]; + } + if (first) { + return new Ok([first, string.slice(first.length)]); + } else { + return new Error(Nil); + } +} + +export function pop_codeunit(str) { + return [str.charCodeAt(0) | 0, str.slice(1)]; +} + +export function lowercase(string) { + return string.toLowerCase(); +} + +export function uppercase(string) { + return string.toUpperCase(); +} + +export function less_than(a, b) { + return a < b; +} + +export function add(a, b) { + return a + b; +} + +export function split(xs, pattern) { + return List.fromArray(xs.split(pattern)); +} + +export function concat(xs) { + let result = ""; + for (const x of xs) { + result = result + x; + } + return result; +} + +export function length(data) { + return data.length; +} + +export function string_byte_slice(string, index, length) { + return string.slice(index, index + length); +} + +export function string_grapheme_slice(string, idx, len) { + if (len <= 0 || idx >= string.length) { + return ""; + } + + const iterator = graphemes_iterator(string); + if (iterator) { + while (idx-- > 0) { + iterator.next(); + } + + let result = ""; + + while (len-- > 0) { + const v = iterator.next().value; + if (v === undefined) { + break; + } + + result += v.segment; + } + + return result; + } else { + return string + .match(/./gsu) + .slice(idx, idx + len) + .join(""); + } +} + +export function string_codeunit_slice(str, from, length) { + return str.slice(from, from + length); +} +export function crop_string(string, substring) { + return string.substring(string.indexOf(substring)); +} + +export function contains_string(haystack, needle) { + return haystack.indexOf(needle) >= 0; +} + +export function starts_with(haystack, needle) { + return haystack.startsWith(needle); +} + +export function ends_with(haystack, needle) { + return haystack.endsWith(needle); +} + +export function split_once(haystack, needle) { + const index = haystack.indexOf(needle); + if (index >= 0) { + const before = haystack.slice(0, index); + const after = haystack.slice(index + needle.length); + return new Ok([before, after]); + } else { + return new Error(Nil); + } +} + +const unicode_whitespaces = [ + "\u0020", // Space + "\u0009", // Horizontal tab + "\u000A", // Line feed + "\u000B", // Vertical tab + "\u000C", // Form feed + "\u000D", // Carriage return + "\u0085", // Next line + "\u2028", // Line separator + "\u2029", // Paragraph separator +].join(""); + +const trim_start_regex = /* @__PURE__ */ new RegExp( + `^[${unicode_whitespaces}]*`, +); +const trim_end_regex = /* @__PURE__ */ new RegExp(`[${unicode_whitespaces}]*$`); + +export function trim_start(string) { + return string.replace(trim_start_regex, ""); +} + +export function trim_end(string) { + return string.replace(trim_end_regex, ""); +} + +export function bit_array_from_string(string) { + return toBitArray([stringBits(string)]); +} + +export function bit_array_bit_size(bit_array) { + return bit_array.bitSize; +} + +export function bit_array_byte_size(bit_array) { + return bit_array.byteSize; +} + +export function bit_array_pad_to_bytes(bit_array) { + const trailingBitsCount = bit_array.bitSize % 8; + + // If the bit array is a whole number of bytes it can be returned unchanged + if (trailingBitsCount === 0) { + return bit_array; + } + + const finalByte = bit_array.byteAt(bit_array.byteSize - 1); + + // The required final byte has its unused trailing bits set to zero + const unusedBitsCount = 8 - trailingBitsCount; + const correctFinalByte = (finalByte >> unusedBitsCount) << unusedBitsCount; + + // If the unused bits in the final byte are already set to zero then the + // existing buffer can be re-used, avoiding a copy + if (finalByte === correctFinalByte) { + return new BitArray( + bit_array.rawBuffer, + bit_array.byteSize * 8, + bit_array.bitOffset, + ); + } + + // Copy the bit array into a new aligned buffer and set the correct final byte + const buffer = new Uint8Array(bit_array.byteSize); + for (let i = 0; i < buffer.length - 1; i++) { + buffer[i] = bit_array.byteAt(i); + } + buffer[buffer.length - 1] = correctFinalByte; + + return new BitArray(buffer); +} + +export function bit_array_concat(bit_arrays) { + return toBitArray(bit_arrays.toArray()); +} + +export function console_log(term) { + console.log(term); +} + +export function console_error(term) { + console.error(term); +} + +export function crash(message) { + throw new globalThis.Error(message); +} + +export function bit_array_to_string(bit_array) { + // If the bit array isn't a whole number of bytes then return an error + if (bit_array.bitSize % 8 !== 0) { + return new Error(Nil); + } + + try { + const decoder = new TextDecoder("utf-8", { fatal: true }); + + if (bit_array.bitOffset === 0) { + return new Ok(decoder.decode(bit_array.rawBuffer)); + } else { + // The input data isn't aligned, so copy it into a new aligned buffer so + // that TextDecoder can be used + const buffer = new Uint8Array(bit_array.byteSize); + for (let i = 0; i < buffer.length; i++) { + buffer[i] = bit_array.byteAt(i); + } + return new Ok(decoder.decode(buffer)); + } + } catch { + return new Error(Nil); + } +} + +export function print(string) { + if (typeof process === "object" && process.stdout?.write) { + process.stdout.write(string); // We can write without a trailing newline + } else if (typeof Deno === "object") { + Deno.stdout.writeSync(new TextEncoder().encode(string)); // We can write without a trailing newline + } else { + console.log(string); // We're in a browser. Newlines are mandated + } +} + +export function print_error(string) { + if (typeof process === "object" && process.stderr?.write) { + process.stderr.write(string); // We can write without a trailing newline + } else if (typeof Deno === "object") { + Deno.stderr.writeSync(new TextEncoder().encode(string)); // We can write without a trailing newline + } else { + console.error(string); // We're in a browser. Newlines are mandated + } +} + +export function print_debug(string) { + if (typeof process === "object" && process.stderr?.write) { + process.stderr.write(string + "\n"); // If we're in Node.js, use `stderr` + } else if (typeof Deno === "object") { + Deno.stderr.writeSync(new TextEncoder().encode(string + "\n")); // If we're in Deno, use `stderr` + } else { + console.log(string); // Otherwise, use `console.log` (so that it doesn't look like an error) + } +} + +export function ceiling(float) { + return Math.ceil(float); +} + +export function floor(float) { + return Math.floor(float); +} + +export function round(float) { + return Math.round(float); +} + +export function truncate(float) { + return Math.trunc(float); +} + +export function power(base, exponent) { + // It is checked in Gleam that: + // - The base is non-negative and that the exponent is not fractional. + // - The base is non-zero and the exponent is non-negative (otherwise + // the result will essentially be division by zero). + // It can thus be assumed that valid input is passed to the Math.pow + // function and a NaN or Infinity value will not be produced. + return Math.pow(base, exponent); +} + +export function random_uniform() { + const random_uniform_result = Math.random(); + // With round-to-nearest-even behavior, the ranges claimed for the functions below + // (excluding the one for Math.random() itself) aren't exact. + // If extremely large bounds are chosen (2^53 or higher), + // it's possible in extremely rare cases to calculate the usually-excluded upper bound. + // Note that as numbers in JavaScript are IEEE 754 floating point numbers + // See: + // Because of this, we just loop 'until' we get a valid result where 0.0 <= x < 1.0: + if (random_uniform_result === 1.0) { + return random_uniform(); + } + return random_uniform_result; +} + +export function bit_array_slice(bits, position, length) { + const start = Math.min(position, position + length); + const end = Math.max(position, position + length); + + if (start < 0 || end * 8 > bits.bitSize) { + return new Error(Nil); + } + + return new Ok(bitArraySlice(bits, start * 8, end * 8)); +} + +export function codepoint(int) { + return new UtfCodepoint(int); +} + +export function string_to_codepoint_integer_list(string) { + return List.fromArray(Array.from(string).map((item) => item.codePointAt(0))); +} + +export function utf_codepoint_list_to_string(utf_codepoint_integer_list) { + return utf_codepoint_integer_list + .toArray() + .map((x) => String.fromCodePoint(x.value)) + .join(""); +} + +export function utf_codepoint_to_int(utf_codepoint) { + return utf_codepoint.value; +} + +export function new_map() { + return Dict.new(); +} + +export function map_size(map) { + return map.size; +} + +export function map_to_list(map) { + return List.fromArray(map.entries()); +} + +export function map_remove(key, map) { + return map.delete(key); +} + +export function map_get(map, key) { + const value = map.get(key, NOT_FOUND); + if (value === NOT_FOUND) { + return new Error(Nil); + } + return new Ok(value); +} + +export function map_insert(key, value, map) { + return map.set(key, value); +} + +function unsafe_percent_decode(string) { + return decodeURIComponent(string || ""); +} + +function unsafe_percent_decode_query(string) { + return decodeURIComponent((string || "").replace("+", " ")); +} + +export function percent_decode(string) { + try { + return new Ok(unsafe_percent_decode(string)); + } catch { + return new Error(Nil); + } +} + +export function percent_encode(string) { + return encodeURIComponent(string).replace("%2B", "+"); +} + +export function parse_query(query) { + try { + const pairs = []; + for (const section of query.split("&")) { + const [key, value] = section.split("="); + if (!key) continue; + + const decodedKey = unsafe_percent_decode_query(key); + const decodedValue = unsafe_percent_decode_query(value); + pairs.push([decodedKey, decodedValue]); + } + return new Ok(List.fromArray(pairs)); + } catch { + return new Error(Nil); + } +} + +const b64EncodeLookup = [ + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, + 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, + 122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 43, 47, +]; + +let b64TextDecoder; + +// Implementation based on https://github.com/mitschabaude/fast-base64/blob/main/js.js +export function base64_encode(bit_array, padding) { + b64TextDecoder ??= new TextDecoder(); + + bit_array = bit_array_pad_to_bytes(bit_array); + + const m = bit_array.byteSize; + const k = m % 3; + const n = Math.floor(m / 3) * 4 + (k && k + 1); + const N = Math.ceil(m / 3) * 4; + const encoded = new Uint8Array(N); + + for (let i = 0, j = 0; j < m; i += 4, j += 3) { + const y = + (bit_array.byteAt(j) << 16) + + (bit_array.byteAt(j + 1) << 8) + + (bit_array.byteAt(j + 2) | 0); + + encoded[i] = b64EncodeLookup[y >> 18]; + encoded[i + 1] = b64EncodeLookup[(y >> 12) & 0x3f]; + encoded[i + 2] = b64EncodeLookup[(y >> 6) & 0x3f]; + encoded[i + 3] = b64EncodeLookup[y & 0x3f]; + } + + let base64 = b64TextDecoder.decode(new Uint8Array(encoded.buffer, 0, n)); + + if (padding) { + if (k === 1) { + base64 += "=="; + } else if (k === 2) { + base64 += "="; + } + } + + return base64; +} + +// From https://developer.mozilla.org/en-US/docs/Glossary/Base64 +export function base64_decode(sBase64) { + try { + const binString = atob(sBase64); + const length = binString.length; + const array = new Uint8Array(length); + for (let i = 0; i < length; i++) { + array[i] = binString.charCodeAt(i); + } + return new Ok(new BitArray(array)); + } catch { + return new Error(Nil); + } +} + +export function classify_dynamic(data) { + if (typeof data === "string") { + return "String"; + } else if (typeof data === "boolean") { + return "Bool"; + } else if (data instanceof Result) { + return "Result"; + } else if (data instanceof List) { + return "List"; + } else if (data instanceof BitArray) { + return "BitArray"; + } else if (data instanceof Dict) { + return "Dict"; + } else if (Number.isInteger(data)) { + return "Int"; + } else if (Array.isArray(data)) { + return `Array`; + } else if (typeof data === "number") { + return "Float"; + } else if (data === null) { + return "Nil"; + } else if (data === undefined) { + return "Nil"; + } else { + const type = typeof data; + return type.charAt(0).toUpperCase() + type.slice(1); + } +} + +export function byte_size(string) { + return new TextEncoder().encode(string).length; +} + +// In JavaScript bitwise operations convert numbers to a sequence of 32 bits +// while Erlang uses arbitrary precision. +// To get around this problem and get consistent results use BigInt and then +// downcast the value back to a Number value. + +export function bitwise_and(x, y) { + return Number(BigInt(x) & BigInt(y)); +} + +export function bitwise_not(x) { + return Number(~BigInt(x)); +} + +export function bitwise_or(x, y) { + return Number(BigInt(x) | BigInt(y)); +} + +export function bitwise_exclusive_or(x, y) { + return Number(BigInt(x) ^ BigInt(y)); +} + +export function bitwise_shift_left(x, y) { + return Number(BigInt(x) << BigInt(y)); +} + +export function bitwise_shift_right(x, y) { + return Number(BigInt(x) >> BigInt(y)); +} + +export function inspect(v) { + return new Inspector().inspect(v); +} + +export function float_to_string(float) { + const string = float.toString().replace("+", ""); + if (string.indexOf(".") >= 0) { + return string; + } else { + const index = string.indexOf("e"); + if (index >= 0) { + return string.slice(0, index) + ".0" + string.slice(index); + } else { + return string + ".0"; + } + } +} + +class Inspector { + #references = new Set(); + + inspect(v) { + const t = typeof v; + if (v === true) return "True"; + if (v === false) return "False"; + if (v === null) return "//js(null)"; + if (v === undefined) return "Nil"; + if (t === "string") return this.#string(v); + if (t === "bigint" || Number.isInteger(v)) return v.toString(); + if (t === "number") return float_to_string(v); + if (v instanceof UtfCodepoint) return this.#utfCodepoint(v); + if (v instanceof BitArray) return this.#bit_array(v); + if (v instanceof RegExp) return `//js(${v})`; + if (v instanceof Date) return `//js(Date("${v.toISOString()}"))`; + if (v instanceof globalThis.Error) return `//js(${v.toString()})`; + if (v instanceof Function) { + const args = []; + for (const i of Array(v.length).keys()) + args.push(String.fromCharCode(i + 97)); + return `//fn(${args.join(", ")}) { ... }`; + } + + if (this.#references.size === this.#references.add(v).size) { + return "//js(circular reference)"; + } + + let printed; + if (Array.isArray(v)) { + printed = `#(${v.map((v) => this.inspect(v)).join(", ")})`; + } else if (v instanceof List) { + printed = this.#list(v); + } else if (v instanceof CustomType) { + printed = this.#customType(v); + } else if (v instanceof Dict) { + printed = this.#dict(v); + } else if (v instanceof Set) { + return `//js(Set(${[...v].map((v) => this.inspect(v)).join(", ")}))`; + } else { + printed = this.#object(v); + } + this.#references.delete(v); + return printed; + } + + #object(v) { + const name = Object.getPrototypeOf(v)?.constructor?.name || "Object"; + const props = []; + for (const k of Object.keys(v)) { + props.push(`${this.inspect(k)}: ${this.inspect(v[k])}`); + } + const body = props.length ? " " + props.join(", ") + " " : ""; + const head = name === "Object" ? "" : name + " "; + return `//js(${head}{${body}})`; + } + + #dict(map) { + let body = "dict.from_list(["; + let first = true; + map.forEach((value, key) => { + if (!first) body = body + ", "; + body = body + "#(" + this.inspect(key) + ", " + this.inspect(value) + ")"; + first = false; + }); + return body + "])"; + } + + #customType(record) { + const props = Object.keys(record) + .map((label) => { + const value = this.inspect(record[label]); + return isNaN(parseInt(label)) ? `${label}: ${value}` : value; + }) + .join(", "); + return props + ? `${record.constructor.name}(${props})` + : record.constructor.name; + } + + #list(list) { + if (list instanceof Empty) { + return "[]"; + } + + let char_out = 'charlist.from_string("'; + let list_out = "["; + + let current = list; + while (current instanceof NonEmpty) { + let element = current.head; + current = current.tail; + + if (list_out !== "[") { + list_out += ", "; + } + list_out += this.inspect(element); + + if (char_out) { + if (Number.isInteger(element) && element >= 32 && element <= 126) { + char_out += String.fromCharCode(element); + } else { + char_out = null; + } + } + } + + if (char_out) { + return char_out + '")'; + } else { + return list_out + "]"; + } + } + + #string(str) { + let new_str = '"'; + for (let i = 0; i < str.length; i++) { + const char = str[i]; + switch (char) { + case "\n": + new_str += "\\n"; + break; + case "\r": + new_str += "\\r"; + break; + case "\t": + new_str += "\\t"; + break; + case "\f": + new_str += "\\f"; + break; + case "\\": + new_str += "\\\\"; + break; + case '"': + new_str += '\\"'; + break; + default: + if (char < " " || (char > "~" && char < "\u{00A0}")) { + new_str += + "\\u{" + + char.charCodeAt(0).toString(16).toUpperCase().padStart(4, "0") + + "}"; + } else { + new_str += char; + } + } + } + new_str += '"'; + return new_str; + } + + #utfCodepoint(codepoint) { + return `//utfcodepoint(${String.fromCodePoint(codepoint.value)})`; + } + + #bit_array(bits) { + if (bits.bitSize === 0) { + return "<<>>"; + } + + let acc = "<<"; + + for (let i = 0; i < bits.byteSize - 1; i++) { + acc += bits.byteAt(i).toString(); + acc += ", "; + } + + if (bits.byteSize * 8 === bits.bitSize) { + acc += bits.byteAt(bits.byteSize - 1).toString(); + } else { + const trailingBitsCount = bits.bitSize % 8; + acc += bits.byteAt(bits.byteSize - 1) >> (8 - trailingBitsCount); + acc += `:size(${trailingBitsCount})`; + } + + acc += ">>"; + return acc; + } +} + +export function base16_encode(bit_array) { + const trailingBitsCount = bit_array.bitSize % 8; + + let result = ""; + + for (let i = 0; i < bit_array.byteSize; i++) { + let byte = bit_array.byteAt(i); + + if (i === bit_array.byteSize - 1 && trailingBitsCount !== 0) { + const unusedBitsCount = 8 - trailingBitsCount; + byte = (byte >> unusedBitsCount) << unusedBitsCount; + } + + result += byte.toString(16).padStart(2, "0").toUpperCase(); + } + + return result; +} + +export function base16_decode(string) { + const bytes = new Uint8Array(string.length / 2); + for (let i = 0; i < string.length; i += 2) { + const a = parseInt(string[i], 16); + const b = parseInt(string[i + 1], 16); + if (isNaN(a) || isNaN(b)) return new Error(Nil); + bytes[i / 2] = a * 16 + b; + } + return new Ok(new BitArray(bytes)); +} + +export function bit_array_to_int_and_size(bits) { + const trailingBitsCount = bits.bitSize % 8; + const unusedBitsCount = trailingBitsCount === 0 ? 0 : 8 - trailingBitsCount; + + return [bits.byteAt(0) >> unusedBitsCount, bits.bitSize]; +} + +export function bit_array_starts_with(bits, prefix) { + if (prefix.bitSize > bits.bitSize) { + return false; + } + + // Check any whole bytes + const byteCount = Math.trunc(prefix.bitSize / 8); + for (let i = 0; i < byteCount; i++) { + if (bits.byteAt(i) !== prefix.byteAt(i)) { + return false; + } + } + + // Check any trailing bits at the end of the prefix + if (prefix.bitSize % 8 !== 0) { + const unusedBitsCount = 8 - (prefix.bitSize % 8); + if ( + bits.byteAt(byteCount) >> unusedBitsCount !== + prefix.byteAt(byteCount) >> unusedBitsCount + ) { + return false; + } + } + + return true; +} + +export function log(x) { + // It is checked in Gleam that: + // - The input is strictly positive (x > 0) + // - This ensures that Math.log will never return NaN or -Infinity + // The function can thus safely pass the input to Math.log + // and a valid finite float will always be produced. + return Math.log(x); +} + +export function exp(x) { + return Math.exp(x); +} + +export function list_to_array(list) { + let current = list; + let array = []; + while (current instanceof NonEmpty) { + array.push(current.head); + current = current.tail; + } + return array; +} + +export function index(data, key) { + // Dictionaries and dictionary-like objects can be indexed + if (data instanceof Dict || data instanceof WeakMap || data instanceof Map) { + const token = {}; + const entry = data.get(key, token); + if (entry === token) return new Ok(new None()); + return new Ok(new Some(entry)); + } + + const key_is_int = Number.isInteger(key); + + // Only elements 0-7 of lists can be indexed, negative indices are not allowed + if (key_is_int && key >= 0 && key < 8 && data instanceof List) { + let i = 0; + for (const value of data) { + if (i === key) return new Ok(new Some(value)); + i++; + } + return new Error("Indexable"); + } + + // Arrays and objects can be indexed + if ( + (key_is_int && Array.isArray(data)) || + (data && typeof data === "object") || + (data && Object.getPrototypeOf(data) === Object.prototype) + ) { + if (key in data) return new Ok(new Some(data[key])); + return new Ok(new None()); + } + + return new Error(key_is_int ? "Indexable" : "Dict"); +} + +export function list(data, decode, pushPath, index, emptyList) { + if (!(data instanceof List || Array.isArray(data))) { + const error = new DecodeError("List", classify(data), emptyList); + return [emptyList, List.fromArray([error])]; + } + + const decoded = []; + + for (const element of data) { + const layer = decode(element); + const [out, errors] = layer; + + if (errors instanceof NonEmpty) { + const [_, errors] = pushPath(layer, index.toString()); + return [emptyList, errors]; + } + decoded.push(out); + index++; + } + + return [List.fromArray(decoded), emptyList]; +} + +export function dict(data) { + if (data instanceof Dict) { + return new Ok(data); + } + if (data instanceof Map || data instanceof WeakMap) { + return new Ok(Dict.fromMap(data)); + } + if (data == null) { + return new Error("Dict"); + } + if (typeof data !== "object") { + return new Error("Dict"); + } + const proto = Object.getPrototypeOf(data); + if (proto === Object.prototype || proto === null) { + return new Ok(Dict.fromObject(data)); + } + return new Error("Dict"); +} + +export function bit_array(data) { + if (data instanceof BitArray) return new Ok(data); + if (data instanceof Uint8Array) return new Ok(new BitArray(data)); + return new Error(new BitArray(new Uint8Array())); +} + +export function float(data) { + if (typeof data === "number") return new Ok(data); + return new Error(0.0); +} + +export function int(data) { + if (Number.isInteger(data)) return new Ok(data); + return new Error(0); +} + +export function string(data) { + if (typeof data === "string") return new Ok(data); + return new Error(""); +} + +export function is_null(data) { + return data === null || data === undefined; +} diff --git a/build/packages/gleam_time/README.md b/build/packages/gleam_time/README.md new file mode 100644 index 0000000..4c4360f --- /dev/null +++ b/build/packages/gleam_time/README.md @@ -0,0 +1,101 @@ +# Time 🕰️ + +Work with time in Gleam! + +[![Package Version](https://img.shields.io/hexpm/v/gleam_time)](https://hex.pm/packages/gleam_time) +[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/gleam_time/) + +```sh +gleam add gleam_time +``` + +This package is the foundation of all code that works with time in Gleam. If +your program uses time then you should be using the types in this package, and +you might choose to add other packages to provide additional functionality. + +## How not to have time related bugs + +Time is famously difficult to work with! It's a very complex area, and there's +many approaches that seem reasonable or obvious, but then commonly result in +bugs. This package is carefully designed to help you avoid these problems, so +it is wise to read this documentation before continuing. + +It is important to understand there are two main ways that time is represented: + +- **Calendar time**: This is how humans commonly think and communicate about + time. For example, "10pm on the 5th of January". This is easy for a human to + read, but is it typically ambiguous and hard to work with! 10pm in + Killorglin, Ireland is not the same point in time as 10pm in Harare, + Zimbabwe. The exact meaning of calendar time depends on daylight savings + time, leap years, leap seconds, and continuously changing national and + political declarations. To make calendar time unambiguous you will need to + know what time zone it is for, and to have an up-to-date time zone database. + +- **Epoch time**: Epoch time is defined as an exact amount of since some fixed + point in time. It is always unambiguous as is not impacted by geopolitics, + time zones, etc. It is efficient for computers to work with, and it is less + likely to result in buggy code. + +In this package epoch time is provided by the `gleam/time/timestamp` module, +and calendar time is provided by the `gleam/time/calendar` module. + +Time zone information has to be loaded from elsewhere, but which approch is +best will depend on your application. User interfaces may want to read current +time zone information from the user's web browser or operating system. Server +side applications may want to embed or downloads a full copy of the time zone +database and then ask clients which time zone they want to use. + +For an entertaining overview of some of the problems of calendar time view this +video: ["The Problem with Time & Timezones" by Computerphile](https://www.youtube.com/watch?v=-5wpm-gesOY). + +### Which time representation should you use? + +> **tldr**: Use `gleam/time/timestamp`. + +The longer, more detailed answer: + +- Default to `gleam/time/timestamp`, which is epoch time. It is + unambiguous, efficient, and significantly less likely to result in logic + bugs. + +- When writing time to a database or other data storage use epoch time, + using whatever epoch format it supports. For example, PostgreSQL + `timestamp` and `timestampz` are both epoch time, and `timestamp` is + preferred as it is more straightforward to use as your application is + also using epoch time. + +- When communicating with other computer systems continue to use epoch + time. For example, when sending times to another program you could + encode time as UNIX timestamps (seconds since 00:00:00 UTC on 1 January + 1970). + +- When communicating with humans use epoch time internally, and convert + to-and-from calendar time at the last moment, when iteracting with the + human user. It may also help the users to also show the time as a fuzzy + duration from the present time, such as "about 4 days ago". + +- When representing "fuzzy" human time concepts that don't exact periods + in time, such as "one month" (varies depending on which month, which + year, and in which time zone) and "Christmas Day" (varies depending on + which year and time zone) then use calendar time. + +Any time you do use calendar time you should be extra careful! It is very +easy to make mistake with. Avoid it where possible. + +## Special thanks + +This package was created with great help from several kind contributors. In +alphabetical order: + +- [Hayleigh Thompson](https://github.com/hayleigh-dot-dev) +- [John Strunk](https://github.com/jrstrunk) +- [Ryan Moore](https://github.com/mooreryan) +- [Shayan Javani](https://github.com/massivefermion) + +These non-Gleam projects where highly influential on the design of this +package: + +- Elm's `elm/time` package. +- Go's `time` module. +- Rust's `std::time` module. +- Elixir's standard library time modules and `timex` package. diff --git a/build/packages/gleam_time/gleam.toml b/build/packages/gleam_time/gleam.toml new file mode 100644 index 0000000..90f4a22 --- /dev/null +++ b/build/packages/gleam_time/gleam.toml @@ -0,0 +1,19 @@ +name = "gleam_time" +version = "1.6.0" +description = "Work with time in Gleam!" +gleam = ">= 1.11.0" +licences = ["Apache-2.0"] +repository = { type = "github", user = "gleam-lang", repo = "time" } +links = [ + { title = "Sponsor", href = "https://github.com/sponsors/lpil" } +] + +[dependencies] +gleam_stdlib = ">= 0.44.0 and < 2.0.0" + +[dev-dependencies] +gleeunit = ">= 1.0.0 and < 2.0.0" +qcheck = ">= 1.0.0 and < 2.0.0" +simplifile = ">= 2.2.0 and < 3.0.0" +gleam_regexp = ">= 1.0.0 and < 2.0.0" +prng = ">= 4.0.1 and < 5.0.0" diff --git a/build/packages/gleam_time/include/gleam@time@calendar_Date.hrl b/build/packages/gleam_time/include/gleam@time@calendar_Date.hrl new file mode 100644 index 0000000..b746fad --- /dev/null +++ b/build/packages/gleam_time/include/gleam@time@calendar_Date.hrl @@ -0,0 +1,5 @@ +-record(date, { + year :: integer(), + month :: gleam@time@calendar:month(), + day :: integer() +}). diff --git a/build/packages/gleam_time/include/gleam@time@calendar_TimeOfDay.hrl b/build/packages/gleam_time/include/gleam@time@calendar_TimeOfDay.hrl new file mode 100644 index 0000000..b5a103d --- /dev/null +++ b/build/packages/gleam_time/include/gleam@time@calendar_TimeOfDay.hrl @@ -0,0 +1,6 @@ +-record(time_of_day, { + hours :: integer(), + minutes :: integer(), + seconds :: integer(), + nanoseconds :: integer() +}). diff --git a/build/packages/gleam_time/include/gleam@time@duration_Duration.hrl b/build/packages/gleam_time/include/gleam@time@duration_Duration.hrl new file mode 100644 index 0000000..5477733 --- /dev/null +++ b/build/packages/gleam_time/include/gleam@time@duration_Duration.hrl @@ -0,0 +1 @@ +-record(duration, {seconds :: integer(), nanoseconds :: integer()}). diff --git a/build/packages/gleam_time/include/gleam@time@timestamp_Timestamp.hrl b/build/packages/gleam_time/include/gleam@time@timestamp_Timestamp.hrl new file mode 100644 index 0000000..b05bafe --- /dev/null +++ b/build/packages/gleam_time/include/gleam@time@timestamp_Timestamp.hrl @@ -0,0 +1 @@ +-record(timestamp, {seconds :: integer(), nanoseconds :: integer()}). diff --git a/build/packages/gleam_time/src/gleam/time/calendar.gleam b/build/packages/gleam_time/src/gleam/time/calendar.gleam new file mode 100644 index 0000000..ac81c11 --- /dev/null +++ b/build/packages/gleam_time/src/gleam/time/calendar.gleam @@ -0,0 +1,346 @@ +//// This module is for working with the Gregorian calendar, established by +//// Pope Gregory XIII in 1582! +//// +//// ## When should you use this module? +//// +//// > **tldr:** You probably want to use [`gleam/time/timestamp`](./timestamp.html) +//// > instead! +//// +//// Calendar time is difficult to work with programmatically, it is the source +//// of most time-related bugs in software. Compared to _epoch time_, which the +//// `gleam/time/timestamp` module uses, there are many disadvantages to +//// calendar time: +//// +//// - They are ambiguous if you don't know what time-zone is being used. +//// +//// - A time-zone database is required to understand calendar time even when +//// you have the time zone. These are large and your program has to +//// continously be updated as new versions of the database are published. +//// +//// - The type permits invalid states. e.g. `days` could be set to the number +//// 32, but this should not be possible! +//// +//// - There is not a single unique canonical value for each point in time, +//// thanks to time zones. Two different `Date` + `TimeOfDay` value pairs +//// could represent the same point in time. This means that you can't check +//// for time equality with `==` when using calendar types. +//// +//// - They are computationally complex, using a more memory to represent and +//// requiring a lot more CPU time to manipulate. +//// +//// There are also advantages to calendar time: +//// +//// - Calendar time is how human's talk about time, so if you want to show a +//// time or take a time from a human user then calendar time will make it +//// easier for them. +//// +//// - They can represent more abstract time periods such as "New Year's Day". +//// This may seem like an exact window of time at first, but really the +//// definition of "New Year's Day" is more fuzzy than that. When it starts +//// and ends will depend where in the world you are, so if you want to refer +//// to a day as a global concept instead of a fixed window of time for that +//// day in a specific location, then calendar time can represent that. +//// +//// So when should you use calendar time? These are our recommendations: +//// +//// - Default to `gleam/time/timestamp`, which is epoch time. It is +//// unambiguous, efficient, and significantly less likely to result in logic +//// bugs. +//// +//// - When writing time to a database or other data storage use epoch time, +//// using whatever epoch format it supports. For example, PostgreSQL +//// `timestamp` and `timestampz` are both epoch time, and `timestamp` is +//// preferred as it is more straightforward to use as your application is +//// also using epoch time. +//// +//// - When communicating with other computer systems continue to use epoch +//// time. For example, when sending times to another program you could +//// encode time as UNIX timestamps (seconds since 00:00:00 UTC on 1 January +//// 1970). +//// +//// - When communicating with humans use epoch time internally, and convert +//// to-and-from calendar time at the last moment, when iteracting with the +//// human user. It may also help the users to also show the time as a fuzzy +//// duration from the present time, such as "about 4 days ago". +//// +//// - When representing "fuzzy" human time concepts that don't exact periods +//// in time, such as "one month" (varies depending on which month, which +//// year, and in which time zone) and "Christmas Day" (varies depending on +//// which year and time zone) then use calendar time. +//// +//// Any time you do use calendar time you should be extra careful! It is very +//// easy to make mistake with. Avoid it where possible. +//// +//// ## Time zone offsets +//// +//// This package includes the `utc_offset` value and the `local_offset` +//// function, which are the offset for the UTC time zone and get the time +//// offset the computer running the program is configured to respectively. +//// +//// If you need to use other offsets in your program then you will need to get +//// them from somewhere else, such as from a package which loads the +//// [IANA Time Zone Database](https://www.iana.org/time-zones), or from the +//// website visitor's web browser, which your frontend can send for you. +//// +//// ## Use in APIs +//// +//// If you are making an API such as a HTTP JSON API you are encouraged to use +//// Unix timestamps instead of calendar times. + +import gleam/int +import gleam/order.{type Order} +import gleam/time/duration + +/// The Gregorian calendar date. Ambiguous without a time zone. +/// +/// Prefer to represent your time using the `Timestamp` type, and convert it +/// only to calendar types when you need to display them. See the documentation +/// for this module for more information. +/// +pub type Date { + Date(year: Int, month: Month, day: Int) +} + +/// The time of day. Ambiguous without a date and time zone. +/// +pub type TimeOfDay { + TimeOfDay(hours: Int, minutes: Int, seconds: Int, nanoseconds: Int) +} + +/// The 12 months of the year. +pub type Month { + January + February + March + April + May + June + July + August + September + October + November + December +} + +/// The offset for the [Coordinated Universal Time (UTC)](https://en.wikipedia.org/wiki/Coordinated_Universal_Time) +/// time zone. +/// +/// The utc zone has no time adjustments, it is always zero. It never observes +/// daylight-saving time and it never shifts around based on political +/// restructuring. +/// +pub const utc_offset = duration.empty + +/// Get the offset for the computer's currently configured time zone. +/// +/// Note this may not be the time zone that is correct to use for your user. +/// For example, if you are making a web application that runs on a server you +/// want _their_ computer's time zone, not yours. +/// +/// This is the _current local_ offset, not the current local time zone. This +/// means that while it will result in the expected outcome for the current +/// time, it may result in unexpected output if used with other timestamps. For +/// example: a timestamp that would locally be during daylight savings time if +/// is it not currently daylight savings time when this function is called. +/// +pub fn local_offset() -> duration.Duration { + duration.seconds(local_time_offset_seconds()) +} + +@external(erlang, "gleam_time_ffi", "local_time_offset_seconds") +@external(javascript, "../../gleam_time_ffi.mjs", "local_time_offset_seconds") +fn local_time_offset_seconds() -> Int + +/// Returns the English name for a month. +/// +/// # Examples +/// +/// ```gleam +/// month_to_string(April) +/// // -> "April" +/// ``` +pub fn month_to_string(month: Month) -> String { + case month { + January -> "January" + February -> "February" + March -> "March" + April -> "April" + May -> "May" + June -> "June" + July -> "July" + August -> "August" + September -> "September" + October -> "October" + November -> "November" + December -> "December" + } +} + +/// Returns the number for the month, where January is 1 and December is 12. +/// +/// # Examples +/// +/// ```gleam +/// month_to_int(January) +/// // -> 1 +/// ``` +pub fn month_to_int(month: Month) -> Int { + case month { + January -> 1 + February -> 2 + March -> 3 + April -> 4 + May -> 5 + June -> 6 + July -> 7 + August -> 8 + September -> 9 + October -> 10 + November -> 11 + December -> 12 + } +} + +/// Returns the month for a given number, where January is 1 and December is 12. +/// +/// # Examples +/// +/// ```gleam +/// month_from_int(1) +/// // -> Ok(January) +/// ``` +pub fn month_from_int(month: Int) -> Result(Month, Nil) { + case month { + 1 -> Ok(January) + 2 -> Ok(February) + 3 -> Ok(March) + 4 -> Ok(April) + 5 -> Ok(May) + 6 -> Ok(June) + 7 -> Ok(July) + 8 -> Ok(August) + 9 -> Ok(September) + 10 -> Ok(October) + 11 -> Ok(November) + 12 -> Ok(December) + _ -> Error(Nil) + } +} + +/// Checks if a given date is valid. +/// +/// This function properly accounts for leap years when validating February days. +/// A leap year occurs every 4 years, except for years divisible by 100, +/// unless they are also divisible by 400. +/// +/// # Examples +/// +/// ```gleam +/// is_valid_date(Date(2023, April, 15)) +/// // -> True +/// ``` +/// +/// ```gleam +/// is_valid_date(Date(2023, April, 31)) +/// // -> False +/// ``` +/// +/// ```gleam +/// is_valid_date(Date(2024, February, 29)) +/// // -> True (2024 is a leap year) +/// ``` +/// +pub fn is_valid_date(date: Date) -> Bool { + let Date(year:, month:, day:) = date + case day < 1 { + True -> False + False -> + case month { + January | March | May | July | August | October | December -> day <= 31 + April | June | September | November -> day <= 30 + February -> { + let max_february_days = case is_leap_year(year) { + True -> 29 + False -> 28 + } + day <= max_february_days + } + } + } +} + +/// Determines if a given year is a leap year. +/// +/// A leap year occurs every 4 years, except for years divisible by 100, +/// unless they are also divisible by 400. +/// +/// # Examples +/// +/// ```gleam +/// is_leap_year(2024) +/// // -> True +/// ``` +/// +/// ```gleam +/// is_leap_year(2023) +/// // -> False +/// ``` +/// +pub fn is_leap_year(year: Int) -> Bool { + case year % 400 == 0 { + True -> True + False -> + case year % 100 == 0 { + True -> False + False -> year % 4 == 0 + } + } +} + +/// Checks if a time of day is valid. +/// +/// Validates that hours are 0-23, minutes are 0-59, seconds are 0-59, +/// and nanoseconds are 0-999,999,999. +/// +/// # Examples +/// +/// ```gleam +/// is_valid_time_of_day(TimeOfDay(12, 30, 45, 123456789)) +/// // -> True +/// ``` +/// +pub fn is_valid_time_of_day(time: TimeOfDay) -> Bool { + let TimeOfDay(hours:, minutes:, seconds:, nanoseconds:) = time + + hours >= 0 + && hours <= 23 + && minutes >= 0 + && minutes <= 59 + && seconds >= 0 + && seconds <= 59 + && nanoseconds >= 0 + && nanoseconds <= 999_999_999 +} + +/// Naively compares two dates without any time zone information, returning an +/// order. +/// +/// ## Correctness +/// +/// This function compares dates without any time zone information, only using +/// the rules for the gregorian calendar. This is typically sufficient, but be +/// aware that in reality some time zones will change their calendar date +/// occasionally. This can result in days being skipped, out of order, or +/// happening multiple times. +/// +/// If you need real-world correct time ordering then use the +/// `gleam/time/timestamp` module instead. +/// +pub fn naive_date_compare(one: Date, other: Date) -> Order { + int.compare(one.year, other.year) + |> order.lazy_break_tie(fn() { + int.compare(month_to_int(one.month), month_to_int(other.month)) + }) + |> order.lazy_break_tie(fn() { int.compare(one.day, other.day) }) +} diff --git a/build/packages/gleam_time/src/gleam/time/duration.gleam b/build/packages/gleam_time/src/gleam/time/duration.gleam new file mode 100644 index 0000000..a3b7483 --- /dev/null +++ b/build/packages/gleam_time/src/gleam/time/duration.gleam @@ -0,0 +1,297 @@ +import gleam/bool +import gleam/int +import gleam/order +import gleam/string + +/// An amount of time, with up to nanosecond precision. +/// +/// This type does not represent calendar periods such as "1 month" or "2 +/// days". Those periods will be different lengths of time depending on which +/// month or day they apply to. For example, January is longer than February. +/// A different type should be used for calendar periods. +/// +pub opaque type Duration { + // When compiling to JavaScript ints have limited precision and size. This + // means that if we were to store the the timestamp in a single int the + // duration would not be able to represent very large or small durations. + // Durations are instead represented as a number of seconds and a number of + // nanoseconds. + // + // If you have manually adjusted the seconds and nanoseconds values the + // `normalise` function can be used to ensure the time is represented the + // intended way, with `nanoseconds` being positive and less than 1 second. + // + // The duration is the sum of the seconds and the nanoseconds. + Duration(seconds: Int, nanoseconds: Int) +} + +/// A division of time. +/// +/// Note that not all months and years are the same length, so a reasonable +/// average length is used by this module. +/// +pub type Unit { + Nanosecond + /// 1000 nanoseconds. + Microsecond + /// 1000 microseconds. + Millisecond + /// 1000 milliseconds. + Second + /// 60 seconds. + Minute + /// 60 minutes. + Hour + /// 24 hours. + Day + /// 7 days. + Week + /// About 30.4375 days. Real calendar months vary in length. + Month + /// About 365.25 days. Real calendar years vary in length. + Year +} + +/// Convert a duration to a number of the largest number of a unit, serving as +/// a rough description of the duration that a human can understand. +/// +/// The size used for each unit are described in the documentation for the +/// `Unit` type. +/// +/// ```gleam +/// seconds(125) +/// |> approximate +/// // -> #(2, Minute) +/// ``` +/// +/// This function rounds _towards zero_. This means that if a duration is just +/// short of 2 days then it will approximate to 1 day. +/// +/// ```gleam +/// hours(47) +/// |> approximate +/// // -> #(1, Day) +/// ``` +/// +pub fn approximate(duration: Duration) -> #(Int, Unit) { + let Duration(seconds: s, nanoseconds: ns) = duration + let minute = 60 + let hour = minute * 60 + let day = hour * 24 + let week = day * 7 + let year = day * 365 + hour * 6 + let month = year / 12 + let microsecond = 1000 + let millisecond = microsecond * 1000 + case Nil { + _ if s < 0 -> { + let #(amount, unit) = Duration(-s, -ns) |> normalise |> approximate + #(-amount, unit) + } + _ if s >= year -> #(s / year, Year) + _ if s >= month -> #(s / month, Month) + _ if s >= week -> #(s / week, Week) + _ if s >= day -> #(s / day, Day) + _ if s >= hour -> #(s / hour, Hour) + _ if s >= minute -> #(s / minute, Minute) + _ if s > 0 -> #(s, Second) + _ if ns >= millisecond -> #(ns / millisecond, Millisecond) + _ if ns >= microsecond -> #(ns / microsecond, Microsecond) + _ -> #(ns, Nanosecond) + } +} + +/// Ensure the duration is represented with `nanoseconds` being positive and +/// less than 1 second. +/// +/// This function does not change the amount of time that the duratoin refers +/// to, it only adjusts the values used to represent the time. +/// +fn normalise(duration: Duration) -> Duration { + let multiplier = 1_000_000_000 + let nanoseconds = duration.nanoseconds % multiplier + let overflow = duration.nanoseconds - nanoseconds + let seconds = duration.seconds + overflow / multiplier + case nanoseconds >= 0 { + True -> Duration(seconds, nanoseconds) + False -> Duration(seconds - 1, multiplier + nanoseconds) + } +} + +/// Compare one duration to another, indicating whether the first spans a +/// larger amount of time (and so is greater) or smaller amount of time (and so +/// is lesser) than the second. +/// +/// # Examples +/// +/// ```gleam +/// compare(seconds(1), seconds(2)) +/// // -> order.Lt +/// ``` +/// +/// Whether a duration is negative or positive doesn't matter for comparing +/// them, only the amount of time spanned matters. +/// +/// ```gleam +/// compare(seconds(-2), seconds(1)) +/// // -> order.Gt +/// ``` +/// +pub fn compare(left: Duration, right: Duration) -> order.Order { + let parts = fn(x: Duration) { + case x.seconds >= 0 { + True -> #(x.seconds, x.nanoseconds) + False -> #(x.seconds * -1 - 1, 1_000_000_000 - x.nanoseconds) + } + } + let #(ls, lns) = parts(left) + let #(rs, rns) = parts(right) + int.compare(ls, rs) + |> order.break_tie(int.compare(lns, rns)) +} + +/// Calculate the difference between two durations. +/// +/// This is effectively substracting the first duration from the second. +/// +/// # Examples +/// +/// ```gleam +/// difference(seconds(1), seconds(5)) +/// // -> seconds(4) +/// ``` +/// +pub fn difference(left: Duration, right: Duration) -> Duration { + Duration(right.seconds - left.seconds, right.nanoseconds - left.nanoseconds) + |> normalise +} + +/// Add two durations together. +/// +/// # Examples +/// +/// ```gleam +/// add(seconds(1), seconds(5)) +/// // -> seconds(6) +/// ``` +/// +pub fn add(left: Duration, right: Duration) -> Duration { + Duration(left.seconds + right.seconds, left.nanoseconds + right.nanoseconds) + |> normalise +} + +/// Convert the duration to an [ISO8601][1] formatted duration string. +/// +/// The ISO8601 duration format is ambiguous without context due to months and +/// years having different lengths, and because of leap seconds. This function +/// encodes the duration as days, hours, and seconds without any leap seconds. +/// Be sure to take this into account when using the duration strings. +/// +/// [1]: https://en.wikipedia.org/wiki/ISO_8601#Durations +/// +pub fn to_iso8601_string(duration: Duration) -> String { + use <- bool.guard(duration == empty, "PT0S") + let split = fn(total, limit) { + let amount = total % limit + let remainder = { total - amount } / limit + #(amount, remainder) + } + let #(seconds, rest) = split(duration.seconds, 60) + let #(minutes, rest) = split(rest, 60) + let #(hours, rest) = split(rest, 24) + let days = rest + let add = fn(out, value, unit) { + case value { + 0 -> out + _ -> out <> int.to_string(value) <> unit + } + } + let output = + "P" + |> add(days, "D") + |> string.append("T") + |> add(hours, "H") + |> add(minutes, "M") + case seconds, duration.nanoseconds { + 0, 0 -> output + _, 0 -> output <> int.to_string(seconds) <> "S" + _, _ -> { + let f = nanosecond_digits(duration.nanoseconds, 0, "") + output <> int.to_string(seconds) <> "." <> f <> "S" + } + } +} + +fn nanosecond_digits(n: Int, position: Int, acc: String) -> String { + case position { + 9 -> acc + _ if acc == "" && n % 10 == 0 -> { + nanosecond_digits(n / 10, position + 1, acc) + } + _ -> { + let acc = int.to_string(n % 10) <> acc + nanosecond_digits(n / 10, position + 1, acc) + } + } +} + +/// Create a duration of a number of seconds. +pub fn seconds(amount: Int) -> Duration { + Duration(amount, 0) +} + +/// Create a duration of a number of minutes. +pub fn minutes(amount: Int) -> Duration { + seconds(amount * 60) +} + +/// Create a duration of a number of hours. +pub fn hours(amount: Int) -> Duration { + seconds(amount * 60 * 60) +} + +/// Create a duration of a number of milliseconds. +pub fn milliseconds(amount: Int) -> Duration { + let remainder = amount % 1000 + let overflow = amount - remainder + let nanoseconds = remainder * 1_000_000 + let seconds = overflow / 1000 + Duration(seconds, nanoseconds) + |> normalise +} + +/// Create a duration of a number of nanoseconds. +/// +/// # JavaScript int limitations +/// +/// Remember that JavaScript can only perfectly represent ints between positive +/// and negative 9,007,199,254,740,991! If you use a single call to this +/// function to create durations larger than that number of nanoseconds then +/// you will likely not get exactly the value you expect. Use `seconds` and +/// `milliseconds` as much as possible for large durations. +/// +pub fn nanoseconds(amount: Int) -> Duration { + Duration(0, amount) + |> normalise +} + +/// Convert the duration to a number of seconds. +/// +/// There may be some small loss of precision due to `Duration` being +/// nanosecond accurate and `Float` not being able to represent this. +/// +pub fn to_seconds(duration: Duration) -> Float { + let seconds = int.to_float(duration.seconds) + let nanoseconds = int.to_float(duration.nanoseconds) + seconds +. { nanoseconds /. 1_000_000_000.0 } +} + +/// Convert the duration to a number of seconds and nanoseconds. There is no +/// loss of precision with this conversion on any target. +/// +pub fn to_seconds_and_nanoseconds(duration: Duration) -> #(Int, Int) { + #(duration.seconds, duration.nanoseconds) +} + +@internal +pub const empty = Duration(0, 0) diff --git a/build/packages/gleam_time/src/gleam/time/timestamp.gleam b/build/packages/gleam_time/src/gleam/time/timestamp.gleam new file mode 100644 index 0000000..02fa890 --- /dev/null +++ b/build/packages/gleam_time/src/gleam/time/timestamp.gleam @@ -0,0 +1,899 @@ +//// Welcome to the timestamp module! This module and its `Timestamp` type are +//// what you will be using most commonly when working with time in Gleam. +//// +//// A timestamp represents a moment in time, represented as an amount of time +//// since the calendar time 00:00:00 UTC on 1 January 1970, also known as the +//// _Unix epoch_. +//// +//// # Wall clock time and monotonicity +//// +//// Time is very complicated, especially on computers! While they generally do +//// a good job of keeping track of what the time is, computers can get +//// out-of-sync and start to report a time that is too late or too early. Most +//// computers use "network time protocol" to tell each other what they think +//// the time is, and computers that realise they are running too fast or too +//// slow will adjust their clock to correct it. When this happens it can seem +//// to your program that the current time has changed, and it may have even +//// jumped backwards in time! +//// +//// This measure of time is called _wall clock time_, and it is what people +//// commonly think of when they think of time. It is important to be aware that +//// it can go backwards, and your program must not rely on it only ever going +//// forwards at a steady rate. For example, for tracking what order events happen +//// in. +//// +//// This module uses wall clock time. If your program needs time values to always +//// increase you will need a _monotonic_ time instead. It's uncommon that you +//// would need monotonic time, one example might be if you're making a +//// benchmarking framework. +//// +//// The exact way that time works will depend on what runtime you use. The +//// Erlang documentation on time has a lot of detail about time generally as well +//// as how it works on the BEAM, it is worth reading. +//// . +//// +//// # Converting to local calendar time +//// +//// Timestamps don't take into account time zones, so a moment in time will +//// have the same timestamp value regardless of where you are in the world. To +//// convert them to local time you will need to know the offset for the time +//// zone you wish to use, likely from a time zone database. See the +//// `gleam/time/calendar` module for more information. +//// + +import gleam/bit_array +import gleam/float +import gleam/int +import gleam/list +import gleam/order +import gleam/result +import gleam/string +import gleam/time/calendar +import gleam/time/duration.{type Duration} + +const seconds_per_day: Int = 86_400 + +const seconds_per_hour: Int = 3600 + +const seconds_per_minute: Int = 60 + +const nanoseconds_per_second: Int = 1_000_000_000 + +/// The `:` character as a byte +const byte_colon: Int = 0x3A + +/// The `-` character as a byte +const byte_minus: Int = 0x2D + +/// The `0` character as a byte +const byte_zero: Int = 0x30 + +/// The `9` character as a byte +const byte_nine: Int = 0x39 + +/// The `t` character as a byte +const byte_t_lowercase: Int = 0x74 + +/// The `T` character as a byte +const byte_t_uppercase: Int = 0x54 + +/// The `T` character as a byte +const byte_space: Int = 0x20 + +/// The Julian seconds of the UNIX epoch (Julian day is 2_440_588) +const julian_seconds_unix_epoch: Int = 210_866_803_200 + +/// The main time type, which you should favour over other types such as +/// calendar time types. It is efficient, unambiguous, and it is not possible +/// to construct an invalid timestamp. +/// +/// The most common situation in which you may need a different time data +/// structure is when you need to display time to human for them to read. When +/// you need to do this convert the timestamp to calendar time when presenting +/// it, but internally always keep the time as a timestamp. +/// +pub opaque type Timestamp { + // When compiling to JavaScript ints have limited precision and size. This + // means that if we were to store the the timestamp in a single int the + // timestamp would not be able to represent times far in the future or in the + // past, or distinguish between two times that are close together. Timestamps + // are instead represented as a number of seconds and a number of nanoseconds. + // + // If you have manually adjusted the seconds and nanoseconds values the + // `normalise` function can be used to ensure the time is represented the + // intended way, with `nanoseconds` being positive and less than 1 second. + // + // The timestamp is the sum of the seconds and the nanoseconds. + Timestamp(seconds: Int, nanoseconds: Int) +} + +/// The epoch of Unix time, which is 00:00:00 UTC on 1 January 1970. +pub const unix_epoch = Timestamp(0, 0) + +/// Ensure the time is represented with `nanoseconds` being positive and less +/// than 1 second. +/// +/// This function does not change the time that the timestamp refers to, it +/// only adjusts the values used to represent the time. +/// +fn normalise(timestamp: Timestamp) -> Timestamp { + let multiplier = 1_000_000_000 + let nanoseconds = timestamp.nanoseconds % multiplier + let overflow = timestamp.nanoseconds - nanoseconds + let seconds = timestamp.seconds + overflow / multiplier + case nanoseconds >= 0 { + True -> Timestamp(seconds, nanoseconds) + False -> Timestamp(seconds - 1, multiplier + nanoseconds) + } +} + +/// Compare one timestamp to another, indicating whether the first is further +/// into the future (greater) or further into the past (lesser) than the +/// second. +/// +/// # Examples +/// +/// ```gleam +/// compare(from_unix_seconds(1), from_unix_seconds(2)) +/// // -> order.Lt +/// ``` +/// +pub fn compare(left: Timestamp, right: Timestamp) -> order.Order { + order.break_tie( + int.compare(left.seconds, right.seconds), + int.compare(left.nanoseconds, right.nanoseconds), + ) +} + +/// Get the current system time. +/// +/// Note this time is not unique or monotonic, it could change at any time or +/// even go backwards! The exact behaviour will depend on the runtime used. See +/// the module documentation for more information. +/// +/// On Erlang this uses [`erlang:system_time/1`][1]. On JavaScript this uses +/// [`Date.now`][2]. +/// +/// [1]: https://www.erlang.org/doc/apps/erts/erlang#system_time/1 +/// [2]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now +/// +pub fn system_time() -> Timestamp { + let #(seconds, nanoseconds) = get_system_time() + normalise(Timestamp(seconds, nanoseconds)) +} + +@external(erlang, "gleam_time_ffi", "system_time") +@external(javascript, "../../gleam_time_ffi.mjs", "system_time") +fn get_system_time() -> #(Int, Int) + +/// Calculate the difference between two timestamps. +/// +/// This is effectively substracting the first timestamp from the second. +/// +/// # Examples +/// +/// ```gleam +/// difference(from_unix_seconds(1), from_unix_seconds(5)) +/// // -> duration.seconds(4) +/// ``` +/// +pub fn difference(left: Timestamp, right: Timestamp) -> Duration { + let seconds = duration.seconds(right.seconds - left.seconds) + let nanoseconds = duration.nanoseconds(right.nanoseconds - left.nanoseconds) + duration.add(seconds, nanoseconds) +} + +/// Add a duration to a timestamp. +/// +/// # Examples +/// +/// ```gleam +/// add(from_unix_seconds(1000), duration.seconds(5)) +/// // -> from_unix_seconds(1005) +/// ``` +/// +pub fn add(timestamp: Timestamp, duration: Duration) -> Timestamp { + let #(seconds, nanoseconds) = duration.to_seconds_and_nanoseconds(duration) + Timestamp(timestamp.seconds + seconds, timestamp.nanoseconds + nanoseconds) + |> normalise +} + +/// Convert a timestamp to a RFC 3339 formatted time string, with an offset +/// supplied as an additional argument. +/// +/// The output of this function is also ISO 8601 compatible so long as the +/// offset not negative. Offsets have at-most minute precision, so an offset +/// with higher precision will be rounded to the nearest minute. +/// +/// If you are making an API such as a HTTP JSON API you are encouraged to use +/// Unix timestamps instead of this format or ISO 8601. Unix timestamps are a +/// better choice as they don't contain offset information. Consider: +/// +/// - UTC offsets are not time zones. This does not and cannot tell us the time +/// zone in which the date was recorded. So what are we supposed to do with +/// this information? +/// - Users typically want dates formatted according to their local time zone. +/// What if the provided UTC offset is different from the current user's time +/// zone? What are we supposed to do with it then? +/// - Despite it being useless (or worse, a source of bugs), the UTC offset +/// creates a larger payload to transfer. +/// +/// They also uses more memory than a unix timestamp. The way they are better +/// than Unix timestamp is that it is easier for a human to read them, but +/// this is a hinderance that tooling can remedy, and APIs are not primarily +/// for humans. +/// +/// # Examples +/// +/// ```gleam +/// timestamp.from_unix_seconds_and_nanoseconds(1000, 123_000_000) +/// |> to_rfc3339(calendar.utc_offset) +/// // -> "1970-01-01T00:16:40.123Z" +/// ``` +/// +/// ```gleam +/// timestamp.from_unix_seconds(1000) +/// |> to_rfc3339(duration.seconds(3600)) +/// // -> "1970-01-01T01:16:40+01:00" +/// ``` +/// +pub fn to_rfc3339(timestamp: Timestamp, offset: Duration) -> String { + let offset = duration_to_minutes(offset) + let #(years, months, days, hours, minutes, seconds) = + to_calendar_from_offset(timestamp, offset) + + let offset_minutes = modulo(offset, 60) + let offset_hours = int.absolute_value(floored_div(offset, 60.0)) + + let n2 = pad_digit(_, to: 2) + let n4 = pad_digit(_, to: 4) + let out = "" + let out = out <> n4(years) <> "-" <> n2(months) <> "-" <> n2(days) + let out = out <> "T" + let out = out <> n2(hours) <> ":" <> n2(minutes) <> ":" <> n2(seconds) + let out = out <> show_second_fraction(timestamp.nanoseconds) + case int.compare(offset, 0) { + order.Eq -> out <> "Z" + order.Gt -> out <> "+" <> n2(offset_hours) <> ":" <> n2(offset_minutes) + order.Lt -> out <> "-" <> n2(offset_hours) <> ":" <> n2(offset_minutes) + } +} + +fn pad_digit(digit: Int, to desired_length: Int) -> String { + int.to_string(digit) |> string.pad_start(desired_length, "0") +} + +/// Convert a `Timestamp` to calendar time, suitable for presenting to a human +/// to read. +/// +/// If you want a machine to use the time value then you should not use this +/// function and should instead keep it as a timestamp. See the documentation +/// for the `gleam/time/calendar` module for more information. +/// +/// # Examples +/// +/// ```gleam +/// timestamp.from_unix_seconds(0) +/// |> timestamp.to_calendar(calendar.utc_offset) +/// // -> #(Date(1970, January, 1), TimeOfDay(0, 0, 0, 0)) +/// ``` +/// +pub fn to_calendar( + timestamp: Timestamp, + offset: Duration, +) -> #(calendar.Date, calendar.TimeOfDay) { + let offset = duration_to_minutes(offset) + let #(year, month, day, hours, minutes, seconds) = + to_calendar_from_offset(timestamp, offset) + let month = case month { + 1 -> calendar.January + 2 -> calendar.February + 3 -> calendar.March + 4 -> calendar.April + 5 -> calendar.May + 6 -> calendar.June + 7 -> calendar.July + 8 -> calendar.August + 9 -> calendar.September + 10 -> calendar.October + 11 -> calendar.November + _ -> calendar.December + } + let nanoseconds = timestamp.nanoseconds + let date = calendar.Date(year:, month:, day:) + let time = calendar.TimeOfDay(hours:, minutes:, seconds:, nanoseconds:) + #(date, time) +} + +fn duration_to_minutes(duration: duration.Duration) -> Int { + float.round(duration.to_seconds(duration) /. 60.0) +} + +fn to_calendar_from_offset( + timestamp: Timestamp, + offset: Int, +) -> #(Int, Int, Int, Int, Int, Int) { + let total = timestamp.seconds + { offset * 60 } + let seconds = modulo(total, 60) + let total_minutes = floored_div(total, 60.0) + let minutes = modulo(total, 60 * 60) / 60 + let hours = modulo(total, 24 * 60 * 60) / { 60 * 60 } + let #(year, month, day) = to_civil(total_minutes) + #(year, month, day, hours, minutes, seconds) +} + +/// Create a `Timestamp` from a human-readable calendar time. +/// +/// # Examples +/// +/// ```gleam +/// timestamp.from_calendar( +/// date: calendar.Date(2024, calendar.December, 25), +/// time: calendar.TimeOfDay(12, 30, 50, 0), +/// offset: calendar.utc_offset, +/// ) +/// |> timestamp.to_rfc3339(calendar.utc_offset) +/// // -> "2024-12-25T12:30:50Z" +/// ``` +/// +pub fn from_calendar( + date date: calendar.Date, + time time: calendar.TimeOfDay, + offset offset: Duration, +) -> Timestamp { + let month = case date.month { + calendar.January -> 1 + calendar.February -> 2 + calendar.March -> 3 + calendar.April -> 4 + calendar.May -> 5 + calendar.June -> 6 + calendar.July -> 7 + calendar.August -> 8 + calendar.September -> 9 + calendar.October -> 10 + calendar.November -> 11 + calendar.December -> 12 + } + from_date_time( + year: date.year, + month:, + day: date.day, + hours: time.hours, + minutes: time.minutes, + seconds: time.seconds, + second_fraction_as_nanoseconds: time.nanoseconds, + offset_seconds: float.round(duration.to_seconds(offset)), + ) +} + +fn modulo(n: Int, m: Int) -> Int { + case int.modulo(n, m) { + Ok(n) -> n + Error(_) -> 0 + } +} + +fn floored_div(numerator: Int, denominator: Float) -> Int { + let n = int.to_float(numerator) /. denominator + float.round(float.floor(n)) +} + +// Adapted from Elm's Time module +fn to_civil(minutes: Int) -> #(Int, Int, Int) { + let raw_day = floored_div(minutes, { 60.0 *. 24.0 }) + 719_468 + let era = case raw_day >= 0 { + True -> raw_day / 146_097 + False -> { raw_day - 146_096 } / 146_097 + } + let day_of_era = raw_day - era * 146_097 + let year_of_era = + { + day_of_era + - { day_of_era / 1460 } + + { day_of_era / 36_524 } + - { day_of_era / 146_096 } + } + / 365 + let year = year_of_era + era * 400 + let day_of_year = + day_of_era + - { 365 * year_of_era + { year_of_era / 4 } - { year_of_era / 100 } } + let mp = { 5 * day_of_year + 2 } / 153 + let month = case mp < 10 { + True -> mp + 3 + False -> mp - 9 + } + let day = day_of_year - { 153 * mp + 2 } / 5 + 1 + let year = case month <= 2 { + True -> year + 1 + False -> year + } + #(year, month, day) +} + +/// Converts nanoseconds into a `String` representation of fractional seconds. +/// +/// Assumes that `nanoseconds < 1_000_000_000`, which will be true for any +/// normalised timestamp. +/// +fn show_second_fraction(nanoseconds: Int) -> String { + case int.compare(nanoseconds, 0) { + // Zero fractional seconds are not shown. + order.Lt | order.Eq -> "" + order.Gt -> { + let second_fraction_part = { + nanoseconds + |> get_zero_padded_digits + |> remove_trailing_zeros + |> list.map(int.to_string) + |> string.join("") + } + + "." <> second_fraction_part + } + } +} + +/// Given a list of digits, return new list with any trailing zeros removed. +/// +fn remove_trailing_zeros(digits: List(Int)) -> List(Int) { + let reversed_digits = list.reverse(digits) + + do_remove_trailing_zeros(reversed_digits) +} + +fn do_remove_trailing_zeros(reversed_digits) { + case reversed_digits { + [] -> [] + [digit, ..digits] if digit == 0 -> do_remove_trailing_zeros(digits) + reversed_digits -> list.reverse(reversed_digits) + } +} + +/// Returns the list of digits of `number`. If the number of digits is less +/// than 9, the result is zero-padded at the front. +/// +fn get_zero_padded_digits(number: Int) -> List(Int) { + do_get_zero_padded_digits(number, [], 0) +} + +fn do_get_zero_padded_digits( + number: Int, + digits: List(Int), + count: Int, +) -> List(Int) { + case number { + number if number <= 0 && count >= 9 -> digits + number if number <= 0 -> + // Zero-pad the digits at the front until we have at least 9 digits. + do_get_zero_padded_digits(number, [0, ..digits], count + 1) + number -> { + let digit = number % 10 + let number = floored_div(number, 10.0) + do_get_zero_padded_digits(number, [digit, ..digits], count + 1) + } + } +} + +/// Parses an [RFC 3339 formatted time string][spec] into a `Timestamp`. +/// +/// [spec]: https://datatracker.ietf.org/doc/html/rfc3339#section-5.6 +/// +/// # Examples +/// +/// ```gleam +/// let assert Ok(ts) = timestamp.parse_rfc3339("1970-01-01T00:00:01Z") +/// timestamp.to_unix_seconds_and_nanoseconds(ts) +/// // -> #(1, 0) +/// ``` +/// +/// Parsing an invalid timestamp returns an error. +/// +/// ```gleam +/// let assert Error(Nil) = timestamp.parse_rfc3339("1995-10-31") +/// ``` +/// +/// ## Time zones +/// +/// It may at first seem that the RFC 3339 format includes timezone +/// information, as it can specify an offset such as `Z` or `+3`, so why does +/// this function not return calendar time with a time zone? There are multiple +/// reasons: +/// +/// - RFC 3339's timestamp format is based on calendar time, but it is +/// unambigous, so it can be converted into epoch time when being parsed. It +/// is always better to internally use epoch time to represent unambiguous +/// points in time, so we perform that conversion as a convenience and to +/// ensure that programmers with less time experience don't accidentally use +/// a less suitable time representation. +/// +/// - RFC 3339's contains _calendar time offset_ information, not time zone +/// information. This is enough to convert it to an unambiguous timestamp, +/// but it is not enough information to reliably work with calendar time. +/// Without the time zone and the time zone database it's not possible to +/// know what time period that offset is valid for, so it cannot be used +/// without risk of bugs. +/// +/// ## Behaviour details +/// +/// - Follows the grammar specified in section 5.6 Internet Date/Time Format of +/// RFC 3339 . +/// - The `T` and `Z` characters may alternatively be lower case `t` or `z`, +/// respectively. +/// - Full dates and full times must be separated by `T` or `t`. A space is also +/// permitted. +/// - Leap seconds rules are not considered. That is, any timestamp may +/// specify digts `00` - `60` for the seconds. +/// - Any part of a fractional second that cannot be represented in the +/// nanosecond precision is tructated. That is, for the time string, +/// `"1970-01-01T00:00:00.1234567899Z"`, the fractional second `.1234567899` +/// will be represented as `123_456_789` in the `Timestamp`. +/// +pub fn parse_rfc3339(input: String) -> Result(Timestamp, Nil) { + let bytes = bit_array.from_string(input) + + // Date + use #(year, bytes) <- result.try(parse_year(from: bytes)) + use bytes <- result.try(accept_byte(from: bytes, value: byte_minus)) + use #(month, bytes) <- result.try(parse_month(from: bytes)) + use bytes <- result.try(accept_byte(from: bytes, value: byte_minus)) + use #(day, bytes) <- result.try(parse_day(from: bytes, year:, month:)) + + use bytes <- result.try(accept_date_time_separator(from: bytes)) + + // Time + use #(hours, bytes) <- result.try(parse_hours(from: bytes)) + use bytes <- result.try(accept_byte(from: bytes, value: byte_colon)) + use #(minutes, bytes) <- result.try(parse_minutes(from: bytes)) + use bytes <- result.try(accept_byte(from: bytes, value: byte_colon)) + use #(seconds, bytes) <- result.try(parse_seconds(from: bytes)) + use #(second_fraction_as_nanoseconds, bytes) <- result.try( + parse_second_fraction_as_nanoseconds(from: bytes), + ) + + // Offset + use #(offset_seconds, bytes) <- result.try(parse_offset(from: bytes)) + + // Done + use Nil <- result.try(accept_empty(bytes)) + + Ok(from_date_time( + year:, + month:, + day:, + hours:, + minutes:, + seconds:, + second_fraction_as_nanoseconds:, + offset_seconds:, + )) +} + +fn parse_year(from bytes: BitArray) -> Result(#(Int, BitArray), Nil) { + parse_digits(from: bytes, count: 4) +} + +fn parse_month(from bytes: BitArray) -> Result(#(Int, BitArray), Nil) { + use #(month, bytes) <- result.try(parse_digits(from: bytes, count: 2)) + case 1 <= month && month <= 12 { + True -> Ok(#(month, bytes)) + False -> Error(Nil) + } +} + +fn parse_day( + from bytes: BitArray, + year year, + month month, +) -> Result(#(Int, BitArray), Nil) { + use #(day, bytes) <- result.try(parse_digits(from: bytes, count: 2)) + + use max_day <- result.try(case month { + 1 | 3 | 5 | 7 | 8 | 10 | 12 -> Ok(31) + 4 | 6 | 9 | 11 -> Ok(30) + 2 -> { + case is_leap_year(year) { + True -> Ok(29) + False -> Ok(28) + } + } + _ -> Error(Nil) + }) + + case 1 <= day && day <= max_day { + True -> Ok(#(day, bytes)) + False -> Error(Nil) + } +} + +// Implementation from RFC 3339 Appendix C +fn is_leap_year(year: Int) -> Bool { + year % 4 == 0 && { year % 100 != 0 || year % 400 == 0 } +} + +fn parse_hours(from bytes: BitArray) -> Result(#(Int, BitArray), Nil) { + use #(hours, bytes) <- result.try(parse_digits(from: bytes, count: 2)) + case 0 <= hours && hours <= 23 { + True -> Ok(#(hours, bytes)) + False -> Error(Nil) + } +} + +fn parse_minutes(from bytes: BitArray) -> Result(#(Int, BitArray), Nil) { + use #(minutes, bytes) <- result.try(parse_digits(from: bytes, count: 2)) + case 0 <= minutes && minutes <= 59 { + True -> Ok(#(minutes, bytes)) + False -> Error(Nil) + } +} + +fn parse_seconds(from bytes: BitArray) -> Result(#(Int, BitArray), Nil) { + use #(seconds, bytes) <- result.try(parse_digits(from: bytes, count: 2)) + // Max of 60 for leap seconds. We don't bother to check if this leap second + // actually occurred in the past or not. + case 0 <= seconds && seconds <= 60 { + True -> Ok(#(seconds, bytes)) + False -> Error(Nil) + } +} + +// Truncates any part of the fraction that is beyond the nanosecond precision. +fn parse_second_fraction_as_nanoseconds(from bytes: BitArray) { + case bytes { + <<".", byte, remaining_bytes:bytes>> + if byte_zero <= byte && byte <= byte_nine + -> { + do_parse_second_fraction_as_nanoseconds( + from: <>, + acc: 0, + power: nanoseconds_per_second, + ) + } + // bytes starts with a ".", which should introduce a fraction, but it does + // not, and so it is an ill-formed input. + <<".", _:bytes>> -> Error(Nil) + // bytes does not start with a "." so there is no fraction. Call this 0 + // nanoseconds. + _ -> Ok(#(0, bytes)) + } +} + +fn do_parse_second_fraction_as_nanoseconds( + from bytes: BitArray, + acc acc: Int, + power power: Int, +) -> Result(#(Int, BitArray), a) { + // Each digit place to the left in the fractional second is 10x fewer + // nanoseconds. + let power = power / 10 + + case bytes { + <> + if byte_zero <= byte && byte <= byte_nine && power < 1 + -> { + // We already have the max precision for nanoseconds. Truncate any + // remaining digits. + do_parse_second_fraction_as_nanoseconds( + from: remaining_bytes, + acc:, + power:, + ) + } + <> if byte_zero <= byte && byte <= byte_nine -> { + // We have not yet reached the precision limit. Parse the next digit. + let digit = byte - 0x30 + do_parse_second_fraction_as_nanoseconds( + from: remaining_bytes, + acc: acc + digit * power, + power:, + ) + } + _ -> Ok(#(acc, bytes)) + } +} + +fn parse_offset(from bytes: BitArray) -> Result(#(Int, BitArray), Nil) { + case bytes { + <<"Z", remaining_bytes:bytes>> | <<"z", remaining_bytes:bytes>> -> + Ok(#(0, remaining_bytes)) + _ -> parse_numeric_offset(bytes) + } +} + +fn parse_numeric_offset(from bytes: BitArray) -> Result(#(Int, BitArray), Nil) { + use #(sign, bytes) <- result.try(parse_sign(from: bytes)) + use #(hours, bytes) <- result.try(parse_hours(from: bytes)) + use bytes <- result.try(accept_byte(from: bytes, value: byte_colon)) + use #(minutes, bytes) <- result.try(parse_minutes(from: bytes)) + + let offset_seconds = offset_to_seconds(sign, hours:, minutes:) + + Ok(#(offset_seconds, bytes)) +} + +fn parse_sign(from bytes) { + case bytes { + <<"+", remaining_bytes:bytes>> -> Ok(#("+", remaining_bytes)) + <<"-", remaining_bytes:bytes>> -> Ok(#("-", remaining_bytes)) + _ -> Error(Nil) + } +} + +fn offset_to_seconds(sign, hours hours, minutes minutes) { + let abs_seconds = hours * seconds_per_hour + minutes * seconds_per_minute + + case sign { + "-" -> -abs_seconds + _ -> abs_seconds + } +} + +/// Parse and return the given number of digits from the given bytes. +/// +fn parse_digits( + from bytes: BitArray, + count count: Int, +) -> Result(#(Int, BitArray), Nil) { + do_parse_digits(from: bytes, count:, acc: 0, k: 0) +} + +fn do_parse_digits( + from bytes: BitArray, + count count: Int, + acc acc: Int, + k k: Int, +) -> Result(#(Int, BitArray), Nil) { + case bytes { + _ if k >= count -> Ok(#(acc, bytes)) + <> if byte_zero <= byte && byte <= byte_nine -> + do_parse_digits( + from: remaining_bytes, + count:, + acc: acc * 10 + { byte - 0x30 }, + k: k + 1, + ) + _ -> Error(Nil) + } +} + +/// Accept the given value from `bytes` and move past it if found. +/// +fn accept_byte(from bytes: BitArray, value value: Int) -> Result(BitArray, Nil) { + case bytes { + <> if byte == value -> Ok(remaining_bytes) + _ -> Error(Nil) + } +} + +fn accept_date_time_separator(from bytes: BitArray) -> Result(BitArray, Nil) { + case bytes { + <> + if byte == byte_t_uppercase + || byte == byte_t_lowercase + || byte == byte_space + -> Ok(remaining_bytes) + _ -> Error(Nil) + } +} + +fn accept_empty(from bytes: BitArray) -> Result(Nil, Nil) { + case bytes { + <<>> -> Ok(Nil) + _ -> Error(Nil) + } +} + +/// Note: The caller of this function must ensure that all inputs are valid. +/// +fn from_date_time( + year year: Int, + month month: Int, + day day: Int, + hours hours: Int, + minutes minutes: Int, + seconds seconds: Int, + second_fraction_as_nanoseconds second_fraction_as_nanoseconds: Int, + offset_seconds offset_seconds: Int, +) -> Timestamp { + let julian_seconds = + julian_seconds_from_parts(year:, month:, day:, hours:, minutes:, seconds:) + + let julian_seconds_since_epoch = julian_seconds - julian_seconds_unix_epoch + + Timestamp( + seconds: julian_seconds_since_epoch - offset_seconds, + nanoseconds: second_fraction_as_nanoseconds, + ) + |> normalise +} + +/// `julian_seconds_from_parts(year, month, day, hours, minutes, seconds)` +/// returns the number of Julian +/// seconds represented by the given arguments. +/// +/// Note: It is the callers responsibility to ensure the inputs are valid. +/// +/// See https://www.tondering.dk/claus/cal/julperiod.php#formula +/// +fn julian_seconds_from_parts( + year year: Int, + month month: Int, + day day: Int, + hours hours: Int, + minutes minutes: Int, + seconds seconds: Int, +) { + let julian_day_seconds = + julian_day_from_ymd(year:, month:, day:) * seconds_per_day + + julian_day_seconds + + { hours * seconds_per_hour } + + { minutes * seconds_per_minute } + + seconds +} + +/// Note: It is the callers responsibility to ensure the inputs are valid. +/// +/// See https://www.tondering.dk/claus/cal/julperiod.php#formula +/// +fn julian_day_from_ymd(year year: Int, month month: Int, day day: Int) -> Int { + let adjustment = { 14 - month } / 12 + let adjusted_year = year + 4800 - adjustment + let adjusted_month = month + 12 * adjustment - 3 + + day + + { { 153 * adjusted_month } + 2 } + / 5 + + 365 + * adjusted_year + + { adjusted_year / 4 } + - { adjusted_year / 100 } + + { adjusted_year / 400 } + - 32_045 +} + +/// Create a timestamp from a number of seconds since 00:00:00 UTC on 1 January +/// 1970. +/// +pub fn from_unix_seconds(seconds: Int) -> Timestamp { + Timestamp(seconds, 0) +} + +/// Create a timestamp from a number of seconds and nanoseconds since 00:00:00 +/// UTC on 1 January 1970. +/// +/// # JavaScript int limitations +/// +/// Remember that JavaScript can only perfectly represent ints between positive +/// and negative 9,007,199,254,740,991! If you only use the nanosecond field +/// then you will almost certainly not get the date value you want due to this +/// loss of precision. Always use seconds primarily and then use nanoseconds +/// for the final sub-second adjustment. +/// +pub fn from_unix_seconds_and_nanoseconds( + seconds seconds: Int, + nanoseconds nanoseconds: Int, +) -> Timestamp { + Timestamp(seconds, nanoseconds) + |> normalise +} + +/// Convert the timestamp to a number of seconds since 00:00:00 UTC on 1 +/// January 1970. +/// +/// There may be some small loss of precision due to `Timestamp` being +/// nanosecond accurate and `Float` not being able to represent this. +/// +pub fn to_unix_seconds(timestamp: Timestamp) -> Float { + let seconds = int.to_float(timestamp.seconds) + let nanoseconds = int.to_float(timestamp.nanoseconds) + seconds +. { nanoseconds /. 1_000_000_000.0 } +} + +/// Convert the timestamp to a number of seconds and nanoseconds since 00:00:00 +/// UTC on 1 January 1970. There is no loss of precision with this conversion +/// on any target. +pub fn to_unix_seconds_and_nanoseconds(timestamp: Timestamp) -> #(Int, Int) { + #(timestamp.seconds, timestamp.nanoseconds) +} diff --git a/build/packages/gleam_time/src/gleam@time@calendar.erl b/build/packages/gleam_time/src/gleam@time@calendar.erl new file mode 100644 index 0000000..e295d09 --- /dev/null +++ b/build/packages/gleam_time/src/gleam@time@calendar.erl @@ -0,0 +1,468 @@ +-module(gleam@time@calendar). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/time/calendar.gleam"). +-export([local_offset/0, month_to_string/1, month_to_int/1, month_from_int/1, is_leap_year/1, is_valid_date/1, is_valid_time_of_day/1, naive_date_compare/2]). +-export_type([date/0, time_of_day/0, month/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC( + " This module is for working with the Gregorian calendar, established by\n" + " Pope Gregory XIII in 1582!\n" + "\n" + " ## When should you use this module?\n" + "\n" + " > **tldr:** You probably want to use [`gleam/time/timestamp`](./timestamp.html)\n" + " > instead!\n" + "\n" + " Calendar time is difficult to work with programmatically, it is the source\n" + " of most time-related bugs in software. Compared to _epoch time_, which the\n" + " `gleam/time/timestamp` module uses, there are many disadvantages to\n" + " calendar time:\n" + "\n" + " - They are ambiguous if you don't know what time-zone is being used.\n" + "\n" + " - A time-zone database is required to understand calendar time even when\n" + " you have the time zone. These are large and your program has to\n" + " continously be updated as new versions of the database are published.\n" + "\n" + " - The type permits invalid states. e.g. `days` could be set to the number\n" + " 32, but this should not be possible!\n" + "\n" + " - There is not a single unique canonical value for each point in time,\n" + " thanks to time zones. Two different `Date` + `TimeOfDay` value pairs\n" + " could represent the same point in time. This means that you can't check\n" + " for time equality with `==` when using calendar types.\n" + "\n" + " - They are computationally complex, using a more memory to represent and\n" + " requiring a lot more CPU time to manipulate.\n" + "\n" + " There are also advantages to calendar time:\n" + "\n" + " - Calendar time is how human's talk about time, so if you want to show a\n" + " time or take a time from a human user then calendar time will make it\n" + " easier for them.\n" + "\n" + " - They can represent more abstract time periods such as \"New Year's Day\".\n" + " This may seem like an exact window of time at first, but really the\n" + " definition of \"New Year's Day\" is more fuzzy than that. When it starts\n" + " and ends will depend where in the world you are, so if you want to refer\n" + " to a day as a global concept instead of a fixed window of time for that\n" + " day in a specific location, then calendar time can represent that.\n" + "\n" + " So when should you use calendar time? These are our recommendations:\n" + "\n" + " - Default to `gleam/time/timestamp`, which is epoch time. It is\n" + " unambiguous, efficient, and significantly less likely to result in logic\n" + " bugs.\n" + "\n" + " - When writing time to a database or other data storage use epoch time,\n" + " using whatever epoch format it supports. For example, PostgreSQL\n" + " `timestamp` and `timestampz` are both epoch time, and `timestamp` is\n" + " preferred as it is more straightforward to use as your application is\n" + " also using epoch time.\n" + "\n" + " - When communicating with other computer systems continue to use epoch\n" + " time. For example, when sending times to another program you could\n" + " encode time as UNIX timestamps (seconds since 00:00:00 UTC on 1 January\n" + " 1970).\n" + "\n" + " - When communicating with humans use epoch time internally, and convert\n" + " to-and-from calendar time at the last moment, when iteracting with the\n" + " human user. It may also help the users to also show the time as a fuzzy\n" + " duration from the present time, such as \"about 4 days ago\".\n" + "\n" + " - When representing \"fuzzy\" human time concepts that don't exact periods\n" + " in time, such as \"one month\" (varies depending on which month, which\n" + " year, and in which time zone) and \"Christmas Day\" (varies depending on\n" + " which year and time zone) then use calendar time.\n" + "\n" + " Any time you do use calendar time you should be extra careful! It is very\n" + " easy to make mistake with. Avoid it where possible.\n" + "\n" + " ## Time zone offsets\n" + "\n" + " This package includes the `utc_offset` value and the `local_offset`\n" + " function, which are the offset for the UTC time zone and get the time\n" + " offset the computer running the program is configured to respectively.\n" + "\n" + " If you need to use other offsets in your program then you will need to get\n" + " them from somewhere else, such as from a package which loads the\n" + " [IANA Time Zone Database](https://www.iana.org/time-zones), or from the\n" + " website visitor's web browser, which your frontend can send for you.\n" + "\n" + " ## Use in APIs\n" + "\n" + " If you are making an API such as a HTTP JSON API you are encouraged to use\n" + " Unix timestamps instead of calendar times.\n" +). + +-type date() :: {date, integer(), month(), integer()}. + +-type time_of_day() :: {time_of_day, integer(), integer(), integer(), integer()}. + +-type month() :: january | + february | + march | + april | + may | + june | + july | + august | + september | + october | + november | + december. + +-file("src/gleam/time/calendar.gleam", 147). +?DOC( + " Get the offset for the computer's currently configured time zone.\n" + "\n" + " Note this may not be the time zone that is correct to use for your user.\n" + " For example, if you are making a web application that runs on a server you\n" + " want _their_ computer's time zone, not yours.\n" + "\n" + " This is the _current local_ offset, not the current local time zone. This\n" + " means that while it will result in the expected outcome for the current\n" + " time, it may result in unexpected output if used with other timestamps. For\n" + " example: a timestamp that would locally be during daylight savings time if\n" + " is it not currently daylight savings time when this function is called.\n" +). +-spec local_offset() -> gleam@time@duration:duration(). +local_offset() -> + gleam@time@duration:seconds(gleam_time_ffi:local_time_offset_seconds()). + +-file("src/gleam/time/calendar.gleam", 163). +?DOC( + " Returns the English name for a month.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " month_to_string(April)\n" + " // -> \"April\"\n" + " ```\n" +). +-spec month_to_string(month()) -> binary(). +month_to_string(Month) -> + case Month of + january -> + <<"January"/utf8>>; + + february -> + <<"February"/utf8>>; + + march -> + <<"March"/utf8>>; + + april -> + <<"April"/utf8>>; + + may -> + <<"May"/utf8>>; + + june -> + <<"June"/utf8>>; + + july -> + <<"July"/utf8>>; + + august -> + <<"August"/utf8>>; + + september -> + <<"September"/utf8>>; + + october -> + <<"October"/utf8>>; + + november -> + <<"November"/utf8>>; + + december -> + <<"December"/utf8>> + end. + +-file("src/gleam/time/calendar.gleam", 188). +?DOC( + " Returns the number for the month, where January is 1 and December is 12.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " month_to_int(January)\n" + " // -> 1\n" + " ```\n" +). +-spec month_to_int(month()) -> integer(). +month_to_int(Month) -> + case Month of + january -> + 1; + + february -> + 2; + + march -> + 3; + + april -> + 4; + + may -> + 5; + + june -> + 6; + + july -> + 7; + + august -> + 8; + + september -> + 9; + + october -> + 10; + + november -> + 11; + + december -> + 12 + end. + +-file("src/gleam/time/calendar.gleam", 213). +?DOC( + " Returns the month for a given number, where January is 1 and December is 12.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " month_from_int(1)\n" + " // -> Ok(January)\n" + " ```\n" +). +-spec month_from_int(integer()) -> {ok, month()} | {error, nil}. +month_from_int(Month) -> + case Month of + 1 -> + {ok, january}; + + 2 -> + {ok, february}; + + 3 -> + {ok, march}; + + 4 -> + {ok, april}; + + 5 -> + {ok, may}; + + 6 -> + {ok, june}; + + 7 -> + {ok, july}; + + 8 -> + {ok, august}; + + 9 -> + {ok, september}; + + 10 -> + {ok, october}; + + 11 -> + {ok, november}; + + 12 -> + {ok, december}; + + _ -> + {error, nil} + end. + +-file("src/gleam/time/calendar.gleam", 290). +?DOC( + " Determines if a given year is a leap year.\n" + "\n" + " A leap year occurs every 4 years, except for years divisible by 100,\n" + " unless they are also divisible by 400.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " is_leap_year(2024)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " is_leap_year(2023)\n" + " // -> False\n" + " ```\n" +). +-spec is_leap_year(integer()) -> boolean(). +is_leap_year(Year) -> + case (Year rem 400) =:= 0 of + true -> + true; + + false -> + case (Year rem 100) =:= 0 of + true -> + false; + + false -> + (Year rem 4) =:= 0 + end + end. + +-file("src/gleam/time/calendar.gleam", 254). +?DOC( + " Checks if a given date is valid.\n" + "\n" + " This function properly accounts for leap years when validating February days.\n" + " A leap year occurs every 4 years, except for years divisible by 100,\n" + " unless they are also divisible by 400.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " is_valid_date(Date(2023, April, 15))\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " is_valid_date(Date(2023, April, 31))\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " is_valid_date(Date(2024, February, 29))\n" + " // -> True (2024 is a leap year)\n" + " ```\n" +). +-spec is_valid_date(date()) -> boolean(). +is_valid_date(Date) -> + {date, Year, Month, Day} = Date, + case Day < 1 of + true -> + false; + + false -> + case Month of + january -> + Day =< 31; + + march -> + Day =< 31; + + may -> + Day =< 31; + + july -> + Day =< 31; + + august -> + Day =< 31; + + october -> + Day =< 31; + + december -> + Day =< 31; + + april -> + Day =< 30; + + june -> + Day =< 30; + + september -> + Day =< 30; + + november -> + Day =< 30; + + february -> + Max_february_days = case is_leap_year(Year) of + true -> + 29; + + false -> + 28 + end, + Day =< Max_february_days + end + end. + +-file("src/gleam/time/calendar.gleam", 313). +?DOC( + " Checks if a time of day is valid.\n" + "\n" + " Validates that hours are 0-23, minutes are 0-59, seconds are 0-59,\n" + " and nanoseconds are 0-999,999,999.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " is_valid_time_of_day(TimeOfDay(12, 30, 45, 123456789))\n" + " // -> True\n" + " ```\n" +). +-spec is_valid_time_of_day(time_of_day()) -> boolean(). +is_valid_time_of_day(Time) -> + {time_of_day, Hours, Minutes, Seconds, Nanoseconds} = Time, + (((((((Hours >= 0) andalso (Hours =< 23)) andalso (Minutes >= 0)) andalso (Minutes + =< 59)) + andalso (Seconds >= 0)) + andalso (Seconds =< 59)) + andalso (Nanoseconds >= 0)) + andalso (Nanoseconds =< 999999999). + +-file("src/gleam/time/calendar.gleam", 340). +?DOC( + " Naively compares two dates without any time zone information, returning an\n" + " order.\n" + "\n" + " ## Correctness\n" + "\n" + " This function compares dates without any time zone information, only using\n" + " the rules for the gregorian calendar. This is typically sufficient, but be\n" + " aware that in reality some time zones will change their calendar date\n" + " occasionally. This can result in days being skipped, out of order, or\n" + " happening multiple times.\n" + "\n" + " If you need real-world correct time ordering then use the\n" + " `gleam/time/timestamp` module instead.\n" +). +-spec naive_date_compare(date(), date()) -> gleam@order:order(). +naive_date_compare(One, Other) -> + _pipe = gleam@int:compare(erlang:element(2, One), erlang:element(2, Other)), + _pipe@1 = gleam@order:lazy_break_tie( + _pipe, + fun() -> + gleam@int:compare( + month_to_int(erlang:element(3, One)), + month_to_int(erlang:element(3, Other)) + ) + end + ), + gleam@order:lazy_break_tie( + _pipe@1, + fun() -> + gleam@int:compare(erlang:element(4, One), erlang:element(4, Other)) + end + ). diff --git a/build/packages/gleam_time/src/gleam@time@duration.erl b/build/packages/gleam_time/src/gleam@time@duration.erl new file mode 100644 index 0000000..7ba7ad2 --- /dev/null +++ b/build/packages/gleam_time/src/gleam@time@duration.erl @@ -0,0 +1,381 @@ +-module(gleam@time@duration). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/time/duration.gleam"). +-export([approximate/1, compare/2, difference/2, add/2, seconds/1, minutes/1, hours/1, milliseconds/1, nanoseconds/1, to_seconds/1, to_seconds_and_nanoseconds/1, to_iso8601_string/1]). +-export_type([duration/0, unit/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +-opaque duration() :: {duration, integer(), integer()}. + +-type unit() :: nanosecond | + microsecond | + millisecond | + second | + minute | + hour | + day | + week | + month | + year. + +-file("src/gleam/time/duration.gleam", 110). +?DOC( + " Ensure the duration is represented with `nanoseconds` being positive and\n" + " less than 1 second.\n" + "\n" + " This function does not change the amount of time that the duratoin refers\n" + " to, it only adjusts the values used to represent the time.\n" +). +-spec normalise(duration()) -> duration(). +normalise(Duration) -> + Multiplier = 1000000000, + Nanoseconds = case Multiplier of + 0 -> 0; + Gleam@denominator -> erlang:element(3, Duration) rem Gleam@denominator + end, + Overflow = erlang:element(3, Duration) - Nanoseconds, + Seconds = erlang:element(2, Duration) + (case Multiplier of + 0 -> 0; + Gleam@denominator@1 -> Overflow div Gleam@denominator@1 + end), + case Nanoseconds >= 0 of + true -> + {duration, Seconds, Nanoseconds}; + + false -> + {duration, Seconds - 1, Multiplier + Nanoseconds} + end. + +-file("src/gleam/time/duration.gleam", 76). +?DOC( + " Convert a duration to a number of the largest number of a unit, serving as\n" + " a rough description of the duration that a human can understand.\n" + "\n" + " The size used for each unit are described in the documentation for the\n" + " `Unit` type.\n" + "\n" + " ```gleam\n" + " seconds(125)\n" + " |> approximate\n" + " // -> #(2, Minute)\n" + " ```\n" + "\n" + " This function rounds _towards zero_. This means that if a duration is just\n" + " short of 2 days then it will approximate to 1 day.\n" + "\n" + " ```gleam\n" + " hours(47)\n" + " |> approximate\n" + " // -> #(1, Day)\n" + " ```\n" +). +-spec approximate(duration()) -> {integer(), unit()}. +approximate(Duration) -> + {duration, S, Ns} = Duration, + Minute = 60, + Hour = Minute * 60, + Day = Hour * 24, + Week = Day * 7, + Year = (Day * 365) + (Hour * 6), + Month = Year div 12, + Microsecond = 1000, + Millisecond = Microsecond * 1000, + case nil of + _ when S < 0 -> + {Amount, Unit} = begin + _pipe = {duration, - S, - Ns}, + _pipe@1 = normalise(_pipe), + approximate(_pipe@1) + end, + {- Amount, Unit}; + + _ when S >= Year -> + {case Year of + 0 -> 0; + Gleam@denominator -> S div Gleam@denominator + end, year}; + + _ when S >= Month -> + {case Month of + 0 -> 0; + Gleam@denominator@1 -> S div Gleam@denominator@1 + end, month}; + + _ when S >= Week -> + {case Week of + 0 -> 0; + Gleam@denominator@2 -> S div Gleam@denominator@2 + end, week}; + + _ when S >= Day -> + {case Day of + 0 -> 0; + Gleam@denominator@3 -> S div Gleam@denominator@3 + end, day}; + + _ when S >= Hour -> + {case Hour of + 0 -> 0; + Gleam@denominator@4 -> S div Gleam@denominator@4 + end, hour}; + + _ when S >= Minute -> + {case Minute of + 0 -> 0; + Gleam@denominator@5 -> S div Gleam@denominator@5 + end, minute}; + + _ when S > 0 -> + {S, second}; + + _ when Ns >= Millisecond -> + {case Millisecond of + 0 -> 0; + Gleam@denominator@6 -> Ns div Gleam@denominator@6 + end, millisecond}; + + _ when Ns >= Microsecond -> + {case Microsecond of + 0 -> 0; + Gleam@denominator@7 -> Ns div Gleam@denominator@7 + end, microsecond}; + + _ -> + {Ns, nanosecond} + end. + +-file("src/gleam/time/duration.gleam", 140). +?DOC( + " Compare one duration to another, indicating whether the first spans a\n" + " larger amount of time (and so is greater) or smaller amount of time (and so\n" + " is lesser) than the second.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " compare(seconds(1), seconds(2))\n" + " // -> order.Lt\n" + " ```\n" + "\n" + " Whether a duration is negative or positive doesn't matter for comparing\n" + " them, only the amount of time spanned matters.\n" + "\n" + " ```gleam\n" + " compare(seconds(-2), seconds(1))\n" + " // -> order.Gt\n" + " ```\n" +). +-spec compare(duration(), duration()) -> gleam@order:order(). +compare(Left, Right) -> + Parts = fun(X) -> case erlang:element(2, X) >= 0 of + true -> + {erlang:element(2, X), erlang:element(3, X)}; + + false -> + {(erlang:element(2, X) * -1) - 1, + 1000000000 - erlang:element(3, X)} + end end, + {Ls, Lns} = Parts(Left), + {Rs, Rns} = Parts(Right), + _pipe = gleam@int:compare(Ls, Rs), + gleam@order:break_tie(_pipe, gleam@int:compare(Lns, Rns)). + +-file("src/gleam/time/duration.gleam", 164). +?DOC( + " Calculate the difference between two durations.\n" + "\n" + " This is effectively substracting the first duration from the second.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " difference(seconds(1), seconds(5))\n" + " // -> seconds(4)\n" + " ```\n" +). +-spec difference(duration(), duration()) -> duration(). +difference(Left, Right) -> + _pipe = {duration, + erlang:element(2, Right) - erlang:element(2, Left), + erlang:element(3, Right) - erlang:element(3, Left)}, + normalise(_pipe). + +-file("src/gleam/time/duration.gleam", 178). +?DOC( + " Add two durations together.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " add(seconds(1), seconds(5))\n" + " // -> seconds(6)\n" + " ```\n" +). +-spec add(duration(), duration()) -> duration(). +add(Left, Right) -> + _pipe = {duration, + erlang:element(2, Left) + erlang:element(2, Right), + erlang:element(3, Left) + erlang:element(3, Right)}, + normalise(_pipe). + +-file("src/gleam/time/duration.gleam", 225). +-spec nanosecond_digits(integer(), integer(), binary()) -> binary(). +nanosecond_digits(N, Position, Acc) -> + case Position of + 9 -> + Acc; + + _ when (Acc =:= <<""/utf8>>) andalso ((N rem 10) =:= 0) -> + nanosecond_digits(N div 10, Position + 1, Acc); + + _ -> + Acc@1 = <<(erlang:integer_to_binary(N rem 10))/binary, Acc/binary>>, + nanosecond_digits(N div 10, Position + 1, Acc@1) + end. + +-file("src/gleam/time/duration.gleam", 239). +?DOC(" Create a duration of a number of seconds.\n"). +-spec seconds(integer()) -> duration(). +seconds(Amount) -> + {duration, Amount, 0}. + +-file("src/gleam/time/duration.gleam", 244). +?DOC(" Create a duration of a number of minutes.\n"). +-spec minutes(integer()) -> duration(). +minutes(Amount) -> + seconds(Amount * 60). + +-file("src/gleam/time/duration.gleam", 249). +?DOC(" Create a duration of a number of hours.\n"). +-spec hours(integer()) -> duration(). +hours(Amount) -> + seconds((Amount * 60) * 60). + +-file("src/gleam/time/duration.gleam", 254). +?DOC(" Create a duration of a number of milliseconds.\n"). +-spec milliseconds(integer()) -> duration(). +milliseconds(Amount) -> + Remainder = Amount rem 1000, + Overflow = Amount - Remainder, + Nanoseconds = Remainder * 1000000, + Seconds = Overflow div 1000, + _pipe = {duration, Seconds, Nanoseconds}, + normalise(_pipe). + +-file("src/gleam/time/duration.gleam", 273). +?DOC( + " Create a duration of a number of nanoseconds.\n" + "\n" + " # JavaScript int limitations\n" + "\n" + " Remember that JavaScript can only perfectly represent ints between positive\n" + " and negative 9,007,199,254,740,991! If you use a single call to this\n" + " function to create durations larger than that number of nanoseconds then\n" + " you will likely not get exactly the value you expect. Use `seconds` and\n" + " `milliseconds` as much as possible for large durations.\n" +). +-spec nanoseconds(integer()) -> duration(). +nanoseconds(Amount) -> + _pipe = {duration, 0, Amount}, + normalise(_pipe). + +-file("src/gleam/time/duration.gleam", 283). +?DOC( + " Convert the duration to a number of seconds.\n" + "\n" + " There may be some small loss of precision due to `Duration` being\n" + " nanosecond accurate and `Float` not being able to represent this.\n" +). +-spec to_seconds(duration()) -> float(). +to_seconds(Duration) -> + Seconds = erlang:float(erlang:element(2, Duration)), + Nanoseconds = erlang:float(erlang:element(3, Duration)), + Seconds + (Nanoseconds / 1000000000.0). + +-file("src/gleam/time/duration.gleam", 292). +?DOC( + " Convert the duration to a number of seconds and nanoseconds. There is no\n" + " loss of precision with this conversion on any target.\n" +). +-spec to_seconds_and_nanoseconds(duration()) -> {integer(), integer()}. +to_seconds_and_nanoseconds(Duration) -> + {erlang:element(2, Duration), erlang:element(3, Duration)}. + +-file("src/gleam/time/duration.gleam", 192). +?DOC( + " Convert the duration to an [ISO8601][1] formatted duration string.\n" + "\n" + " The ISO8601 duration format is ambiguous without context due to months and\n" + " years having different lengths, and because of leap seconds. This function\n" + " encodes the duration as days, hours, and seconds without any leap seconds.\n" + " Be sure to take this into account when using the duration strings.\n" + "\n" + " [1]: https://en.wikipedia.org/wiki/ISO_8601#Durations\n" +). +-spec to_iso8601_string(duration()) -> binary(). +to_iso8601_string(Duration) -> + gleam@bool:guard( + Duration =:= {duration, 0, 0}, + <<"PT0S"/utf8>>, + fun() -> + Split = fun(Total, Limit) -> + Amount = case Limit of + 0 -> 0; + Gleam@denominator -> Total rem Gleam@denominator + end, + Remainder = case Limit of + 0 -> 0; + Gleam@denominator@1 -> (Total - Amount) div Gleam@denominator@1 + end, + {Amount, Remainder} + end, + {Seconds, Rest} = Split(erlang:element(2, Duration), 60), + {Minutes, Rest@1} = Split(Rest, 60), + {Hours, Rest@2} = Split(Rest@1, 24), + Days = Rest@2, + Add = fun(Out, Value, Unit) -> case Value of + 0 -> + Out; + + _ -> + <<<>/binary, + Unit/binary>> + end end, + Output = begin + _pipe = <<"P"/utf8>>, + _pipe@1 = Add(_pipe, Days, <<"D"/utf8>>), + _pipe@2 = gleam@string:append(_pipe@1, <<"T"/utf8>>), + _pipe@3 = Add(_pipe@2, Hours, <<"H"/utf8>>), + Add(_pipe@3, Minutes, <<"M"/utf8>>) + end, + case {Seconds, erlang:element(3, Duration)} of + {0, 0} -> + Output; + + {_, 0} -> + <<<>/binary, + "S"/utf8>>; + + {_, _} -> + F = nanosecond_digits( + erlang:element(3, Duration), + 0, + <<""/utf8>> + ), + <<<<<<<>/binary, + "."/utf8>>/binary, + F/binary>>/binary, + "S"/utf8>> + end + end + ). diff --git a/build/packages/gleam_time/src/gleam@time@timestamp.erl b/build/packages/gleam_time/src/gleam@time@timestamp.erl new file mode 100644 index 0000000..0d7413a --- /dev/null +++ b/build/packages/gleam_time/src/gleam@time@timestamp.erl @@ -0,0 +1,1188 @@ +-module(gleam@time@timestamp). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/time/timestamp.gleam"). +-export([compare/2, system_time/0, difference/2, add/2, to_calendar/2, to_rfc3339/2, from_unix_seconds/1, from_unix_seconds_and_nanoseconds/2, to_unix_seconds/1, to_unix_seconds_and_nanoseconds/1, from_calendar/3, parse_rfc3339/1]). +-export_type([timestamp/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC( + " Welcome to the timestamp module! This module and its `Timestamp` type are\n" + " what you will be using most commonly when working with time in Gleam.\n" + "\n" + " A timestamp represents a moment in time, represented as an amount of time\n" + " since the calendar time 00:00:00 UTC on 1 January 1970, also known as the\n" + " _Unix epoch_.\n" + "\n" + " # Wall clock time and monotonicity\n" + "\n" + " Time is very complicated, especially on computers! While they generally do\n" + " a good job of keeping track of what the time is, computers can get\n" + " out-of-sync and start to report a time that is too late or too early. Most\n" + " computers use \"network time protocol\" to tell each other what they think\n" + " the time is, and computers that realise they are running too fast or too\n" + " slow will adjust their clock to correct it. When this happens it can seem\n" + " to your program that the current time has changed, and it may have even\n" + " jumped backwards in time!\n" + "\n" + " This measure of time is called _wall clock time_, and it is what people\n" + " commonly think of when they think of time. It is important to be aware that\n" + " it can go backwards, and your program must not rely on it only ever going\n" + " forwards at a steady rate. For example, for tracking what order events happen\n" + " in. \n" + "\n" + " This module uses wall clock time. If your program needs time values to always\n" + " increase you will need a _monotonic_ time instead. It's uncommon that you\n" + " would need monotonic time, one example might be if you're making a\n" + " benchmarking framework.\n" + "\n" + " The exact way that time works will depend on what runtime you use. The\n" + " Erlang documentation on time has a lot of detail about time generally as well\n" + " as how it works on the BEAM, it is worth reading.\n" + " .\n" + "\n" + " # Converting to local calendar time\n" + "\n" + " Timestamps don't take into account time zones, so a moment in time will\n" + " have the same timestamp value regardless of where you are in the world. To\n" + " convert them to local time you will need to know the offset for the time\n" + " zone you wish to use, likely from a time zone database. See the\n" + " `gleam/time/calendar` module for more information.\n" + "\n" +). + +-opaque timestamp() :: {timestamp, integer(), integer()}. + +-file("src/gleam/time/timestamp.gleam", 119). +?DOC( + " Ensure the time is represented with `nanoseconds` being positive and less\n" + " than 1 second.\n" + "\n" + " This function does not change the time that the timestamp refers to, it\n" + " only adjusts the values used to represent the time.\n" +). +-spec normalise(timestamp()) -> timestamp(). +normalise(Timestamp) -> + Multiplier = 1000000000, + Nanoseconds = case Multiplier of + 0 -> 0; + Gleam@denominator -> erlang:element(3, Timestamp) rem Gleam@denominator + end, + Overflow = erlang:element(3, Timestamp) - Nanoseconds, + Seconds = erlang:element(2, Timestamp) + (case Multiplier of + 0 -> 0; + Gleam@denominator@1 -> Overflow div Gleam@denominator@1 + end), + case Nanoseconds >= 0 of + true -> + {timestamp, Seconds, Nanoseconds}; + + false -> + {timestamp, Seconds - 1, Multiplier + Nanoseconds} + end. + +-file("src/gleam/time/timestamp.gleam", 141). +?DOC( + " Compare one timestamp to another, indicating whether the first is further\n" + " into the future (greater) or further into the past (lesser) than the\n" + " second.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " compare(from_unix_seconds(1), from_unix_seconds(2))\n" + " // -> order.Lt\n" + " ```\n" +). +-spec compare(timestamp(), timestamp()) -> gleam@order:order(). +compare(Left, Right) -> + gleam@order:break_tie( + gleam@int:compare(erlang:element(2, Left), erlang:element(2, Right)), + gleam@int:compare(erlang:element(3, Left), erlang:element(3, Right)) + ). + +-file("src/gleam/time/timestamp.gleam", 160). +?DOC( + " Get the current system time.\n" + "\n" + " Note this time is not unique or monotonic, it could change at any time or\n" + " even go backwards! The exact behaviour will depend on the runtime used. See\n" + " the module documentation for more information.\n" + "\n" + " On Erlang this uses [`erlang:system_time/1`][1]. On JavaScript this uses\n" + " [`Date.now`][2].\n" + "\n" + " [1]: https://www.erlang.org/doc/apps/erts/erlang#system_time/1\n" + " [2]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now\n" +). +-spec system_time() -> timestamp(). +system_time() -> + {Seconds, Nanoseconds} = gleam_time_ffi:system_time(), + normalise({timestamp, Seconds, Nanoseconds}). + +-file("src/gleam/time/timestamp.gleam", 180). +?DOC( + " Calculate the difference between two timestamps.\n" + "\n" + " This is effectively substracting the first timestamp from the second.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " difference(from_unix_seconds(1), from_unix_seconds(5))\n" + " // -> duration.seconds(4)\n" + " ```\n" +). +-spec difference(timestamp(), timestamp()) -> gleam@time@duration:duration(). +difference(Left, Right) -> + Seconds = gleam@time@duration:seconds( + erlang:element(2, Right) - erlang:element(2, Left) + ), + Nanoseconds = gleam@time@duration:nanoseconds( + erlang:element(3, Right) - erlang:element(3, Left) + ), + gleam@time@duration:add(Seconds, Nanoseconds). + +-file("src/gleam/time/timestamp.gleam", 195). +?DOC( + " Add a duration to a timestamp.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " add(from_unix_seconds(1000), duration.seconds(5))\n" + " // -> from_unix_seconds(1005)\n" + " ```\n" +). +-spec add(timestamp(), gleam@time@duration:duration()) -> timestamp(). +add(Timestamp, Duration) -> + {Seconds, Nanoseconds} = gleam@time@duration:to_seconds_and_nanoseconds( + Duration + ), + _pipe = {timestamp, + erlang:element(2, Timestamp) + Seconds, + erlang:element(3, Timestamp) + Nanoseconds}, + normalise(_pipe). + +-file("src/gleam/time/timestamp.gleam", 262). +-spec pad_digit(integer(), integer()) -> binary(). +pad_digit(Digit, Desired_length) -> + _pipe = erlang:integer_to_binary(Digit), + gleam@string:pad_start(_pipe, Desired_length, <<"0"/utf8>>). + +-file("src/gleam/time/timestamp.gleam", 308). +-spec duration_to_minutes(gleam@time@duration:duration()) -> integer(). +duration_to_minutes(Duration) -> + erlang:round(gleam@time@duration:to_seconds(Duration) / 60.0). + +-file("src/gleam/time/timestamp.gleam", 370). +-spec modulo(integer(), integer()) -> integer(). +modulo(N, M) -> + case gleam@int:modulo(N, M) of + {ok, N@1} -> + N@1; + + {error, _} -> + 0 + end. + +-file("src/gleam/time/timestamp.gleam", 377). +-spec floored_div(integer(), float()) -> integer(). +floored_div(Numerator, Denominator) -> + N = case Denominator of + +0.0 -> +0.0; + -0.0 -> -0.0; + Gleam@denominator -> erlang:float(Numerator) / Gleam@denominator + end, + erlang:round(math:floor(N)). + +-file("src/gleam/time/timestamp.gleam", 383). +-spec to_civil(integer()) -> {integer(), integer(), integer()}. +to_civil(Minutes) -> + Raw_day = floored_div(Minutes, (60.0 * 24.0)) + 719468, + Era = case Raw_day >= 0 of + true -> + Raw_day div 146097; + + false -> + (Raw_day - 146096) div 146097 + end, + Day_of_era = Raw_day - (Era * 146097), + Year_of_era = (((Day_of_era - (Day_of_era div 1460)) + (Day_of_era div 36524)) + - (Day_of_era div 146096)) + div 365, + Year = Year_of_era + (Era * 400), + Day_of_year = Day_of_era - (((365 * Year_of_era) + (Year_of_era div 4)) - (Year_of_era + div 100)), + Mp = ((5 * Day_of_year) + 2) div 153, + Month = case Mp < 10 of + true -> + Mp + 3; + + false -> + Mp - 9 + end, + Day = (Day_of_year - (((153 * Mp) + 2) div 5)) + 1, + Year@1 = case Month =< 2 of + true -> + Year + 1; + + false -> + Year + end, + {Year@1, Month, Day}. + +-file("src/gleam/time/timestamp.gleam", 312). +-spec to_calendar_from_offset(timestamp(), integer()) -> {integer(), + integer(), + integer(), + integer(), + integer(), + integer()}. +to_calendar_from_offset(Timestamp, Offset) -> + Total = erlang:element(2, Timestamp) + (Offset * 60), + Seconds = modulo(Total, 60), + Total_minutes = floored_div(Total, 60.0), + Minutes = modulo(Total, 60 * 60) div 60, + Hours = case (60 * 60) of + 0 -> 0; + Gleam@denominator -> modulo(Total, (24 * 60) * 60) div Gleam@denominator + end, + {Year, Month, Day} = to_civil(Total_minutes), + {Year, Month, Day, Hours, Minutes, Seconds}. + +-file("src/gleam/time/timestamp.gleam", 281). +?DOC( + " Convert a `Timestamp` to calendar time, suitable for presenting to a human\n" + " to read.\n" + "\n" + " If you want a machine to use the time value then you should not use this\n" + " function and should instead keep it as a timestamp. See the documentation\n" + " for the `gleam/time/calendar` module for more information.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " timestamp.from_unix_seconds(0)\n" + " |> timestamp.to_calendar(calendar.utc_offset)\n" + " // -> #(Date(1970, January, 1), TimeOfDay(0, 0, 0, 0))\n" + " ```\n" +). +-spec to_calendar(timestamp(), gleam@time@duration:duration()) -> {gleam@time@calendar:date(), + gleam@time@calendar:time_of_day()}. +to_calendar(Timestamp, Offset) -> + Offset@1 = duration_to_minutes(Offset), + {Year, Month, Day, Hours, Minutes, Seconds} = to_calendar_from_offset( + Timestamp, + Offset@1 + ), + Month@1 = case Month of + 1 -> + january; + + 2 -> + february; + + 3 -> + march; + + 4 -> + april; + + 5 -> + may; + + 6 -> + june; + + 7 -> + july; + + 8 -> + august; + + 9 -> + september; + + 10 -> + october; + + 11 -> + november; + + _ -> + december + end, + Nanoseconds = erlang:element(3, Timestamp), + Date = {date, Year, Month@1, Day}, + Time = {time_of_day, Hours, Minutes, Seconds, Nanoseconds}, + {Date, Time}. + +-file("src/gleam/time/timestamp.gleam", 446). +-spec do_remove_trailing_zeros(list(integer())) -> list(integer()). +do_remove_trailing_zeros(Reversed_digits) -> + case Reversed_digits of + [] -> + []; + + [Digit | Digits] when Digit =:= 0 -> + do_remove_trailing_zeros(Digits); + + Reversed_digits@1 -> + lists:reverse(Reversed_digits@1) + end. + +-file("src/gleam/time/timestamp.gleam", 440). +?DOC(" Given a list of digits, return new list with any trailing zeros removed.\n"). +-spec remove_trailing_zeros(list(integer())) -> list(integer()). +remove_trailing_zeros(Digits) -> + Reversed_digits = lists:reverse(Digits), + do_remove_trailing_zeros(Reversed_digits). + +-file("src/gleam/time/timestamp.gleam", 461). +-spec do_get_zero_padded_digits(integer(), list(integer()), integer()) -> list(integer()). +do_get_zero_padded_digits(Number, Digits, Count) -> + case Number of + Number@1 when (Number@1 =< 0) andalso (Count >= 9) -> + Digits; + + Number@2 when Number@2 =< 0 -> + do_get_zero_padded_digits(Number@2, [0 | Digits], Count + 1); + + Number@3 -> + Digit = Number@3 rem 10, + Number@4 = floored_div(Number@3, 10.0), + do_get_zero_padded_digits(Number@4, [Digit | Digits], Count + 1) + end. + +-file("src/gleam/time/timestamp.gleam", 457). +?DOC( + " Returns the list of digits of `number`. If the number of digits is less \n" + " than 9, the result is zero-padded at the front.\n" +). +-spec get_zero_padded_digits(integer()) -> list(integer()). +get_zero_padded_digits(Number) -> + do_get_zero_padded_digits(Number, [], 0). + +-file("src/gleam/time/timestamp.gleam", 420). +?DOC( + " Converts nanoseconds into a `String` representation of fractional seconds.\n" + " \n" + " Assumes that `nanoseconds < 1_000_000_000`, which will be true for any \n" + " normalised timestamp.\n" +). +-spec show_second_fraction(integer()) -> binary(). +show_second_fraction(Nanoseconds) -> + case gleam@int:compare(Nanoseconds, 0) of + lt -> + <<""/utf8>>; + + eq -> + <<""/utf8>>; + + gt -> + Second_fraction_part = begin + _pipe = Nanoseconds, + _pipe@1 = get_zero_padded_digits(_pipe), + _pipe@2 = remove_trailing_zeros(_pipe@1), + _pipe@3 = gleam@list:map( + _pipe@2, + fun erlang:integer_to_binary/1 + ), + gleam@string:join(_pipe@3, <<""/utf8>>) + end, + <<"."/utf8, Second_fraction_part/binary>> + end. + +-file("src/gleam/time/timestamp.gleam", 240). +?DOC( + " Convert a timestamp to a RFC 3339 formatted time string, with an offset\n" + " supplied as an additional argument.\n" + "\n" + " The output of this function is also ISO 8601 compatible so long as the\n" + " offset not negative. Offsets have at-most minute precision, so an offset\n" + " with higher precision will be rounded to the nearest minute.\n" + "\n" + " If you are making an API such as a HTTP JSON API you are encouraged to use\n" + " Unix timestamps instead of this format or ISO 8601. Unix timestamps are a\n" + " better choice as they don't contain offset information. Consider:\n" + "\n" + " - UTC offsets are not time zones. This does not and cannot tell us the time\n" + " zone in which the date was recorded. So what are we supposed to do with\n" + " this information?\n" + " - Users typically want dates formatted according to their local time zone.\n" + " What if the provided UTC offset is different from the current user's time\n" + " zone? What are we supposed to do with it then?\n" + " - Despite it being useless (or worse, a source of bugs), the UTC offset\n" + " creates a larger payload to transfer.\n" + "\n" + " They also uses more memory than a unix timestamp. The way they are better\n" + " than Unix timestamp is that it is easier for a human to read them, but\n" + " this is a hinderance that tooling can remedy, and APIs are not primarily\n" + " for humans.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " timestamp.from_unix_seconds_and_nanoseconds(1000, 123_000_000)\n" + " |> to_rfc3339(calendar.utc_offset)\n" + " // -> \"1970-01-01T00:16:40.123Z\"\n" + " ```\n" + "\n" + " ```gleam\n" + " timestamp.from_unix_seconds(1000)\n" + " |> to_rfc3339(duration.seconds(3600))\n" + " // -> \"1970-01-01T01:16:40+01:00\"\n" + " ```\n" +). +-spec to_rfc3339(timestamp(), gleam@time@duration:duration()) -> binary(). +to_rfc3339(Timestamp, Offset) -> + Offset@1 = duration_to_minutes(Offset), + {Years, Months, Days, Hours, Minutes, Seconds} = to_calendar_from_offset( + Timestamp, + Offset@1 + ), + Offset_minutes = modulo(Offset@1, 60), + Offset_hours = gleam@int:absolute_value(floored_div(Offset@1, 60.0)), + N2 = fun(_capture) -> pad_digit(_capture, 2) end, + N4 = fun(_capture@1) -> pad_digit(_capture@1, 4) end, + Out = <<""/utf8>>, + Out@1 = <<<<<<<<<>/binary, "-"/utf8>>/binary, + (N2(Months))/binary>>/binary, + "-"/utf8>>/binary, + (N2(Days))/binary>>, + Out@2 = <>, + Out@3 = <<<<<<<<<>/binary, ":"/utf8>>/binary, + (N2(Minutes))/binary>>/binary, + ":"/utf8>>/binary, + (N2(Seconds))/binary>>, + Out@4 = <>, + case gleam@int:compare(Offset@1, 0) of + eq -> + <>; + + gt -> + <<<<<<<>/binary, (N2(Offset_hours))/binary>>/binary, + ":"/utf8>>/binary, + (N2(Offset_minutes))/binary>>; + + lt -> + <<<<<<<>/binary, (N2(Offset_hours))/binary>>/binary, + ":"/utf8>>/binary, + (N2(Offset_minutes))/binary>> + end. + +-file("src/gleam/time/timestamp.gleam", 611). +-spec is_leap_year(integer()) -> boolean(). +is_leap_year(Year) -> + ((Year rem 4) =:= 0) andalso (((Year rem 100) /= 0) orelse ((Year rem 400) + =:= 0)). + +-file("src/gleam/time/timestamp.gleam", 715). +-spec parse_sign(bitstring()) -> {ok, {binary(), bitstring()}} | {error, nil}. +parse_sign(Bytes) -> + case Bytes of + <<"+"/utf8, Remaining_bytes/binary>> -> + {ok, {<<"+"/utf8>>, Remaining_bytes}}; + + <<"-"/utf8, Remaining_bytes@1/binary>> -> + {ok, {<<"-"/utf8>>, Remaining_bytes@1}}; + + _ -> + {error, nil} + end. + +-file("src/gleam/time/timestamp.gleam", 762). +?DOC(" Accept the given value from `bytes` and move past it if found.\n"). +-spec accept_byte(bitstring(), integer()) -> {ok, bitstring()} | {error, nil}. +accept_byte(Bytes, Value) -> + case Bytes of + <> when Byte =:= Value -> + {ok, Remaining_bytes}; + + _ -> + {error, nil} + end. + +-file("src/gleam/time/timestamp.gleam", 780). +-spec accept_empty(bitstring()) -> {ok, nil} | {error, nil}. +accept_empty(Bytes) -> + case Bytes of + <<>> -> + {ok, nil}; + + _ -> + {error, nil} + end. + +-file("src/gleam/time/timestamp.gleam", 840). +?DOC( + " Note: It is the callers responsibility to ensure the inputs are valid.\n" + " \n" + " See https://www.tondering.dk/claus/cal/julperiod.php#formula\n" +). +-spec julian_day_from_ymd(integer(), integer(), integer()) -> integer(). +julian_day_from_ymd(Year, Month, Day) -> + Adjustment = (14 - Month) div 12, + Adjusted_year = (Year + 4800) - Adjustment, + Adjusted_month = (Month + (12 * Adjustment)) - 3, + (((((Day + (((153 * Adjusted_month) + 2) div 5)) + (365 * Adjusted_year)) + (Adjusted_year + div 4)) + - (Adjusted_year div 100)) + + (Adjusted_year div 400)) + - 32045. + +-file("src/gleam/time/timestamp.gleam", 859). +?DOC( + " Create a timestamp from a number of seconds since 00:00:00 UTC on 1 January\n" + " 1970.\n" +). +-spec from_unix_seconds(integer()) -> timestamp(). +from_unix_seconds(Seconds) -> + {timestamp, Seconds, 0}. + +-file("src/gleam/time/timestamp.gleam", 874). +?DOC( + " Create a timestamp from a number of seconds and nanoseconds since 00:00:00\n" + " UTC on 1 January 1970.\n" + "\n" + " # JavaScript int limitations\n" + "\n" + " Remember that JavaScript can only perfectly represent ints between positive\n" + " and negative 9,007,199,254,740,991! If you only use the nanosecond field\n" + " then you will almost certainly not get the date value you want due to this\n" + " loss of precision. Always use seconds primarily and then use nanoseconds\n" + " for the final sub-second adjustment.\n" +). +-spec from_unix_seconds_and_nanoseconds(integer(), integer()) -> timestamp(). +from_unix_seconds_and_nanoseconds(Seconds, Nanoseconds) -> + _pipe = {timestamp, Seconds, Nanoseconds}, + normalise(_pipe). + +-file("src/gleam/time/timestamp.gleam", 888). +?DOC( + " Convert the timestamp to a number of seconds since 00:00:00 UTC on 1\n" + " January 1970.\n" + "\n" + " There may be some small loss of precision due to `Timestamp` being\n" + " nanosecond accurate and `Float` not being able to represent this.\n" +). +-spec to_unix_seconds(timestamp()) -> float(). +to_unix_seconds(Timestamp) -> + Seconds = erlang:float(erlang:element(2, Timestamp)), + Nanoseconds = erlang:float(erlang:element(3, Timestamp)), + Seconds + (Nanoseconds / 1000000000.0). + +-file("src/gleam/time/timestamp.gleam", 897). +?DOC( + " Convert the timestamp to a number of seconds and nanoseconds since 00:00:00\n" + " UTC on 1 January 1970. There is no loss of precision with this conversion\n" + " on any target.\n" +). +-spec to_unix_seconds_and_nanoseconds(timestamp()) -> {integer(), integer()}. +to_unix_seconds_and_nanoseconds(Timestamp) -> + {erlang:element(2, Timestamp), erlang:element(3, Timestamp)}. + +-file("src/gleam/time/timestamp.gleam", 723). +-spec offset_to_seconds(binary(), integer(), integer()) -> integer(). +offset_to_seconds(Sign, Hours, Minutes) -> + Abs_seconds = (Hours * 3600) + (Minutes * 60), + case Sign of + <<"-"/utf8>> -> + - Abs_seconds; + + _ -> + Abs_seconds + end. + +-file("src/gleam/time/timestamp.gleam", 819). +?DOC( + " `julian_seconds_from_parts(year, month, day, hours, minutes, seconds)` \n" + " returns the number of Julian \n" + " seconds represented by the given arguments.\n" + " \n" + " Note: It is the callers responsibility to ensure the inputs are valid.\n" + " \n" + " See https://www.tondering.dk/claus/cal/julperiod.php#formula\n" +). +-spec julian_seconds_from_parts( + integer(), + integer(), + integer(), + integer(), + integer(), + integer() +) -> integer(). +julian_seconds_from_parts(Year, Month, Day, Hours, Minutes, Seconds) -> + Julian_day_seconds = julian_day_from_ymd(Year, Month, Day) * 86400, + ((Julian_day_seconds + (Hours * 3600)) + (Minutes * 60)) + Seconds. + +-file("src/gleam/time/timestamp.gleam", 662). +-spec do_parse_second_fraction_as_nanoseconds(bitstring(), integer(), integer()) -> {ok, + {integer(), bitstring()}} | + {error, any()}. +do_parse_second_fraction_as_nanoseconds(Bytes, Acc, Power) -> + Power@1 = Power div 10, + case Bytes of + <> when ((16#30 =< Byte) andalso (Byte =< 16#39)) andalso (Power@1 < 1) -> + do_parse_second_fraction_as_nanoseconds( + Remaining_bytes, + Acc, + Power@1 + ); + + <> when (16#30 =< Byte@1) andalso (Byte@1 =< 16#39) -> + Digit = Byte@1 - 16#30, + do_parse_second_fraction_as_nanoseconds( + Remaining_bytes@1, + Acc + (Digit * Power@1), + Power@1 + ); + + _ -> + {ok, {Acc, Bytes}} + end. + +-file("src/gleam/time/timestamp.gleam", 642). +-spec parse_second_fraction_as_nanoseconds(bitstring()) -> {ok, + {integer(), bitstring()}} | + {error, nil}. +parse_second_fraction_as_nanoseconds(Bytes) -> + case Bytes of + <<"."/utf8, Byte, Remaining_bytes/binary>> when (16#30 =< Byte) andalso (Byte =< 16#39) -> + do_parse_second_fraction_as_nanoseconds( + <>, + 0, + 1000000000 + ); + + <<"."/utf8, _/binary>> -> + {error, nil}; + + _ -> + {ok, {0, Bytes}} + end. + +-file("src/gleam/time/timestamp.gleam", 741). +-spec do_parse_digits(bitstring(), integer(), integer(), integer()) -> {ok, + {integer(), bitstring()}} | + {error, nil}. +do_parse_digits(Bytes, Count, Acc, K) -> + case Bytes of + _ when K >= Count -> + {ok, {Acc, Bytes}}; + + <> when (16#30 =< Byte) andalso (Byte =< 16#39) -> + do_parse_digits( + Remaining_bytes, + Count, + (Acc * 10) + (Byte - 16#30), + K + 1 + ); + + _ -> + {error, nil} + end. + +-file("src/gleam/time/timestamp.gleam", 734). +?DOC(" Parse and return the given number of digits from the given bytes.\n"). +-spec parse_digits(bitstring(), integer()) -> {ok, {integer(), bitstring()}} | + {error, nil}. +parse_digits(Bytes, Count) -> + do_parse_digits(Bytes, Count, 0, 0). + +-file("src/gleam/time/timestamp.gleam", 573). +-spec parse_year(bitstring()) -> {ok, {integer(), bitstring()}} | {error, nil}. +parse_year(Bytes) -> + parse_digits(Bytes, 4). + +-file("src/gleam/time/timestamp.gleam", 577). +-spec parse_month(bitstring()) -> {ok, {integer(), bitstring()}} | {error, nil}. +parse_month(Bytes) -> + gleam@result:'try'( + parse_digits(Bytes, 2), + fun(_use0) -> + {Month, Bytes@1} = _use0, + case (1 =< Month) andalso (Month =< 12) of + true -> + {ok, {Month, Bytes@1}}; + + false -> + {error, nil} + end + end + ). + +-file("src/gleam/time/timestamp.gleam", 585). +-spec parse_day(bitstring(), integer(), integer()) -> {ok, + {integer(), bitstring()}} | + {error, nil}. +parse_day(Bytes, Year, Month) -> + gleam@result:'try'( + parse_digits(Bytes, 2), + fun(_use0) -> + {Day, Bytes@1} = _use0, + gleam@result:'try'(case Month of + 1 -> + {ok, 31}; + + 3 -> + {ok, 31}; + + 5 -> + {ok, 31}; + + 7 -> + {ok, 31}; + + 8 -> + {ok, 31}; + + 10 -> + {ok, 31}; + + 12 -> + {ok, 31}; + + 4 -> + {ok, 30}; + + 6 -> + {ok, 30}; + + 9 -> + {ok, 30}; + + 11 -> + {ok, 30}; + + 2 -> + case is_leap_year(Year) of + true -> + {ok, 29}; + + false -> + {ok, 28} + end; + + _ -> + {error, nil} + end, fun(Max_day) -> case (1 =< Day) andalso (Day =< Max_day) of + true -> + {ok, {Day, Bytes@1}}; + + false -> + {error, nil} + end end) + end + ). + +-file("src/gleam/time/timestamp.gleam", 615). +-spec parse_hours(bitstring()) -> {ok, {integer(), bitstring()}} | {error, nil}. +parse_hours(Bytes) -> + gleam@result:'try'( + parse_digits(Bytes, 2), + fun(_use0) -> + {Hours, Bytes@1} = _use0, + case (0 =< Hours) andalso (Hours =< 23) of + true -> + {ok, {Hours, Bytes@1}}; + + false -> + {error, nil} + end + end + ). + +-file("src/gleam/time/timestamp.gleam", 623). +-spec parse_minutes(bitstring()) -> {ok, {integer(), bitstring()}} | + {error, nil}. +parse_minutes(Bytes) -> + gleam@result:'try'( + parse_digits(Bytes, 2), + fun(_use0) -> + {Minutes, Bytes@1} = _use0, + case (0 =< Minutes) andalso (Minutes =< 59) of + true -> + {ok, {Minutes, Bytes@1}}; + + false -> + {error, nil} + end + end + ). + +-file("src/gleam/time/timestamp.gleam", 631). +-spec parse_seconds(bitstring()) -> {ok, {integer(), bitstring()}} | + {error, nil}. +parse_seconds(Bytes) -> + gleam@result:'try'( + parse_digits(Bytes, 2), + fun(_use0) -> + {Seconds, Bytes@1} = _use0, + case (0 =< Seconds) andalso (Seconds =< 60) of + true -> + {ok, {Seconds, Bytes@1}}; + + false -> + {error, nil} + end + end + ). + +-file("src/gleam/time/timestamp.gleam", 704). +-spec parse_numeric_offset(bitstring()) -> {ok, {integer(), bitstring()}} | + {error, nil}. +parse_numeric_offset(Bytes) -> + gleam@result:'try'( + parse_sign(Bytes), + fun(_use0) -> + {Sign, Bytes@1} = _use0, + gleam@result:'try'( + parse_hours(Bytes@1), + fun(_use0@1) -> + {Hours, Bytes@2} = _use0@1, + gleam@result:'try'( + accept_byte(Bytes@2, 16#3A), + fun(Bytes@3) -> + gleam@result:'try'( + parse_minutes(Bytes@3), + fun(_use0@2) -> + {Minutes, Bytes@4} = _use0@2, + Offset_seconds = offset_to_seconds( + Sign, + Hours, + Minutes + ), + {ok, {Offset_seconds, Bytes@4}} + end + ) + end + ) + end + ) + end + ). + +-file("src/gleam/time/timestamp.gleam", 696). +-spec parse_offset(bitstring()) -> {ok, {integer(), bitstring()}} | {error, nil}. +parse_offset(Bytes) -> + case Bytes of + <<"Z"/utf8, Remaining_bytes/binary>> -> + {ok, {0, Remaining_bytes}}; + + <<"z"/utf8, Remaining_bytes/binary>> -> + {ok, {0, Remaining_bytes}}; + + _ -> + parse_numeric_offset(Bytes) + end. + +-file("src/gleam/time/timestamp.gleam", 769). +-spec accept_date_time_separator(bitstring()) -> {ok, bitstring()} | + {error, nil}. +accept_date_time_separator(Bytes) -> + case Bytes of + <> when ((Byte =:= 16#54) orelse (Byte =:= 16#74)) orelse (Byte =:= 16#20) -> + {ok, Remaining_bytes}; + + _ -> + {error, nil} + end. + +-file("src/gleam/time/timestamp.gleam", 789). +?DOC(" Note: The caller of this function must ensure that all inputs are valid.\n"). +-spec from_date_time( + integer(), + integer(), + integer(), + integer(), + integer(), + integer(), + integer(), + integer() +) -> timestamp(). +from_date_time( + Year, + Month, + Day, + Hours, + Minutes, + Seconds, + Second_fraction_as_nanoseconds, + Offset_seconds +) -> + Julian_seconds = julian_seconds_from_parts( + Year, + Month, + Day, + Hours, + Minutes, + Seconds + ), + Julian_seconds_since_epoch = Julian_seconds - 210866803200, + _pipe = {timestamp, + Julian_seconds_since_epoch - Offset_seconds, + Second_fraction_as_nanoseconds}, + normalise(_pipe). + +-file("src/gleam/time/timestamp.gleam", 339). +?DOC( + " Create a `Timestamp` from a human-readable calendar time.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " timestamp.from_calendar(\n" + " date: calendar.Date(2024, calendar.December, 25),\n" + " time: calendar.TimeOfDay(12, 30, 50, 0),\n" + " offset: calendar.utc_offset,\n" + " )\n" + " |> timestamp.to_rfc3339(calendar.utc_offset)\n" + " // -> \"2024-12-25T12:30:50Z\"\n" + " ```\n" +). +-spec from_calendar( + gleam@time@calendar:date(), + gleam@time@calendar:time_of_day(), + gleam@time@duration:duration() +) -> timestamp(). +from_calendar(Date, Time, Offset) -> + Month = case erlang:element(3, Date) of + january -> + 1; + + february -> + 2; + + march -> + 3; + + april -> + 4; + + may -> + 5; + + june -> + 6; + + july -> + 7; + + august -> + 8; + + september -> + 9; + + october -> + 10; + + november -> + 11; + + december -> + 12 + end, + from_date_time( + erlang:element(2, Date), + Month, + erlang:element(4, Date), + erlang:element(2, Time), + erlang:element(3, Time), + erlang:element(4, Time), + erlang:element(5, Time), + erlang:round(gleam@time@duration:to_seconds(Offset)) + ). + +-file("src/gleam/time/timestamp.gleam", 533). +?DOC( + " Parses an [RFC 3339 formatted time string][spec] into a `Timestamp`.\n" + "\n" + " [spec]: https://datatracker.ietf.org/doc/html/rfc3339#section-5.6\n" + " \n" + " # Examples\n" + "\n" + " ```gleam\n" + " let assert Ok(ts) = timestamp.parse_rfc3339(\"1970-01-01T00:00:01Z\")\n" + " timestamp.to_unix_seconds_and_nanoseconds(ts)\n" + " // -> #(1, 0)\n" + " ```\n" + " \n" + " Parsing an invalid timestamp returns an error.\n" + " \n" + " ```gleam\n" + " let assert Error(Nil) = timestamp.parse_rfc3339(\"1995-10-31\")\n" + " ```\n" + "\n" + " ## Time zones\n" + "\n" + " It may at first seem that the RFC 3339 format includes timezone\n" + " information, as it can specify an offset such as `Z` or `+3`, so why does\n" + " this function not return calendar time with a time zone? There are multiple\n" + " reasons:\n" + "\n" + " - RFC 3339's timestamp format is based on calendar time, but it is\n" + " unambigous, so it can be converted into epoch time when being parsed. It\n" + " is always better to internally use epoch time to represent unambiguous\n" + " points in time, so we perform that conversion as a convenience and to\n" + " ensure that programmers with less time experience don't accidentally use\n" + " a less suitable time representation.\n" + "\n" + " - RFC 3339's contains _calendar time offset_ information, not time zone\n" + " information. This is enough to convert it to an unambiguous timestamp,\n" + " but it is not enough information to reliably work with calendar time.\n" + " Without the time zone and the time zone database it's not possible to\n" + " know what time period that offset is valid for, so it cannot be used\n" + " without risk of bugs.\n" + "\n" + " ## Behaviour details\n" + " \n" + " - Follows the grammar specified in section 5.6 Internet Date/Time Format of \n" + " RFC 3339 .\n" + " - The `T` and `Z` characters may alternatively be lower case `t` or `z`, \n" + " respectively.\n" + " - Full dates and full times must be separated by `T` or `t`. A space is also \n" + " permitted.\n" + " - Leap seconds rules are not considered. That is, any timestamp may \n" + " specify digts `00` - `60` for the seconds.\n" + " - Any part of a fractional second that cannot be represented in the \n" + " nanosecond precision is tructated. That is, for the time string, \n" + " `\"1970-01-01T00:00:00.1234567899Z\"`, the fractional second `.1234567899` \n" + " will be represented as `123_456_789` in the `Timestamp`.\n" +). +-spec parse_rfc3339(binary()) -> {ok, timestamp()} | {error, nil}. +parse_rfc3339(Input) -> + Bytes = gleam_stdlib:identity(Input), + gleam@result:'try'( + parse_year(Bytes), + fun(_use0) -> + {Year, Bytes@1} = _use0, + gleam@result:'try'( + accept_byte(Bytes@1, 16#2D), + fun(Bytes@2) -> + gleam@result:'try'( + parse_month(Bytes@2), + fun(_use0@1) -> + {Month, Bytes@3} = _use0@1, + gleam@result:'try'( + accept_byte(Bytes@3, 16#2D), + fun(Bytes@4) -> + gleam@result:'try'( + parse_day(Bytes@4, Year, Month), + fun(_use0@2) -> + {Day, Bytes@5} = _use0@2, + gleam@result:'try'( + accept_date_time_separator( + Bytes@5 + ), + fun(Bytes@6) -> + gleam@result:'try'( + parse_hours(Bytes@6), + fun(_use0@3) -> + {Hours, Bytes@7} = _use0@3, + gleam@result:'try'( + accept_byte( + Bytes@7, + 16#3A + ), + fun(Bytes@8) -> + gleam@result:'try'( + parse_minutes( + Bytes@8 + ), + fun( + _use0@4 + ) -> + {Minutes, + Bytes@9} = _use0@4, + gleam@result:'try'( + accept_byte( + Bytes@9, + 16#3A + ), + fun( + Bytes@10 + ) -> + gleam@result:'try'( + parse_seconds( + Bytes@10 + ), + fun( + _use0@5 + ) -> + {Seconds, + Bytes@11} = _use0@5, + gleam@result:'try'( + parse_second_fraction_as_nanoseconds( + Bytes@11 + ), + fun( + _use0@6 + ) -> + {Second_fraction_as_nanoseconds, + Bytes@12} = _use0@6, + gleam@result:'try'( + parse_offset( + Bytes@12 + ), + fun( + _use0@7 + ) -> + {Offset_seconds, + Bytes@13} = _use0@7, + gleam@result:'try'( + accept_empty( + Bytes@13 + ), + fun( + _use0@8 + ) -> + nil = _use0@8, + {ok, + from_date_time( + Year, + Month, + Day, + Hours, + Minutes, + Seconds, + Second_fraction_as_nanoseconds, + Offset_seconds + )} + end + ) + end + ) + end + ) + end + ) + end + ) + end + ) + end + ) + end + ) + end + ) + end + ) + end + ) + end + ) + end + ) + end + ). diff --git a/build/packages/gleam_time/src/gleam_time.app.src b/build/packages/gleam_time/src/gleam_time.app.src new file mode 100644 index 0000000..1e7cce4 --- /dev/null +++ b/build/packages/gleam_time/src/gleam_time.app.src @@ -0,0 +1,12 @@ +{application, gleam_time, [ + {vsn, "1.6.0"}, + {applications, [gleam_stdlib]}, + {description, "Work with time in Gleam!"}, + {modules, [gleam@time@calendar, + gleam@time@duration, + gleam@time@timestamp, + gleam_time@@main, + gleam_time_ffi, + gleam_time_test_ffi]}, + {registered, []} +]}. diff --git a/build/packages/gleam_time/src/gleam_time_ffi.erl b/build/packages/gleam_time/src/gleam_time_ffi.erl new file mode 100644 index 0000000..34d8c88 --- /dev/null +++ b/build/packages/gleam_time/src/gleam_time_ffi.erl @@ -0,0 +1,12 @@ +-module(gleam_time_ffi). +-export([system_time/0, local_time_offset_seconds/0]). + +system_time() -> + {0, erlang:system_time(nanosecond)}. + +local_time_offset_seconds() -> + Utc = calendar:universal_time(), + Local = calendar:local_time(), + UtcSeconds = calendar:datetime_to_gregorian_seconds(Utc), + LocalSeconds = calendar:datetime_to_gregorian_seconds(Local), + LocalSeconds - UtcSeconds. diff --git a/build/packages/gleam_time/src/gleam_time_ffi.mjs b/build/packages/gleam_time/src/gleam_time_ffi.mjs new file mode 100644 index 0000000..27d09aa --- /dev/null +++ b/build/packages/gleam_time/src/gleam_time_ffi.mjs @@ -0,0 +1,11 @@ +export function system_time() { + const now = Date.now(); + const milliseconds = now % 1_000; + const nanoseconds = milliseconds * 1000_000; + const seconds = (now - milliseconds) / 1_000; + return [seconds, nanoseconds]; +} + +export function local_time_offset_seconds() { + return new Date().getTimezoneOffset() * -60; +} diff --git a/build/packages/gleeunit/LICENCE b/build/packages/gleeunit/LICENCE new file mode 100644 index 0000000..c7967c3 --- /dev/null +++ b/build/packages/gleeunit/LICENCE @@ -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 2021, 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/build/packages/gleeunit/README.md b/build/packages/gleeunit/README.md new file mode 100644 index 0000000..4d81364 --- /dev/null +++ b/build/packages/gleeunit/README.md @@ -0,0 +1,44 @@ +# gleeunit + +A simple test runner for Gleam, using EUnit on Erlang and a custom runner on JS. + +[![Package Version](https://img.shields.io/hexpm/v/gleeunit)](https://hex.pm/packages/gleeunit) +[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/gleeunit/) + + +```sh +gleam add gleeunit@1 --dev +``` +```gleam +// In test/yourapp_test.gleam +import gleeunit + +pub fn main() { + gleeunit.main() +} +``` + +Now any public function with a name ending in `_test` in the `test` directory +will be found and run as a test. + +```gleam +pub fn some_function_test() { + assert some_function() == "Hello!" +} +``` + +Run the tests by entering `gleam test` in the command line. + +### Deno + +If using the Deno JavaScript runtime, you will need to add the following to your +`gleam.toml`. + +```toml +[javascript.deno] +allow_read = [ + "gleam.toml", + "test", + "build", +] +``` diff --git a/build/packages/gleeunit/gleam.toml b/build/packages/gleeunit/gleam.toml new file mode 100644 index 0000000..c7df0c5 --- /dev/null +++ b/build/packages/gleeunit/gleam.toml @@ -0,0 +1,16 @@ +name = "gleeunit" +version = "1.9.0" +licences = ["Apache-2.0"] +description = "A simple test runner for Gleam, using EUnit on Erlang" +repository = { type = "github", user = "lpil", repo = "gleeunit" } +links = [{ title = "Sponsor", href = "https://github.com/sponsors/lpil" }] +gleam = ">= 1.13.0" + +[javascript.deno] +allow_read = ["gleam.toml", "test", "build"] + +[dependencies] +gleam_stdlib = ">= 0.60.0 and < 1.0.0" + +[dev-dependencies] +testhelper = { "path" = "./testhelper" } diff --git a/build/packages/gleeunit/include/gleeunit@internal@gleam_panic_Assert.hrl b/build/packages/gleeunit/include/gleeunit@internal@gleam_panic_Assert.hrl new file mode 100644 index 0000000..9360941 --- /dev/null +++ b/build/packages/gleeunit/include/gleeunit@internal@gleam_panic_Assert.hrl @@ -0,0 +1,6 @@ +-record(assert, { + start :: integer(), + 'end' :: integer(), + expression_start :: integer(), + kind :: gleeunit@internal@gleam_panic:assert_kind() +}). diff --git a/build/packages/gleeunit/include/gleeunit@internal@gleam_panic_AssertedExpression.hrl b/build/packages/gleeunit/include/gleeunit@internal@gleam_panic_AssertedExpression.hrl new file mode 100644 index 0000000..812663c --- /dev/null +++ b/build/packages/gleeunit/include/gleeunit@internal@gleam_panic_AssertedExpression.hrl @@ -0,0 +1,5 @@ +-record(asserted_expression, { + start :: integer(), + 'end' :: integer(), + kind :: gleeunit@internal@gleam_panic:expression_kind() +}). diff --git a/build/packages/gleeunit/include/gleeunit@internal@gleam_panic_BinaryOperator.hrl b/build/packages/gleeunit/include/gleeunit@internal@gleam_panic_BinaryOperator.hrl new file mode 100644 index 0000000..eee44c9 --- /dev/null +++ b/build/packages/gleeunit/include/gleeunit@internal@gleam_panic_BinaryOperator.hrl @@ -0,0 +1,5 @@ +-record(binary_operator, { + operator :: binary(), + left :: gleeunit@internal@gleam_panic:asserted_expression(), + right :: gleeunit@internal@gleam_panic:asserted_expression() +}). diff --git a/build/packages/gleeunit/include/gleeunit@internal@gleam_panic_Expression.hrl b/build/packages/gleeunit/include/gleeunit@internal@gleam_panic_Expression.hrl new file mode 100644 index 0000000..e7ffaa0 --- /dev/null +++ b/build/packages/gleeunit/include/gleeunit@internal@gleam_panic_Expression.hrl @@ -0,0 +1 @@ +-record(expression, {value :: gleam@dynamic:dynamic_()}). diff --git a/build/packages/gleeunit/include/gleeunit@internal@gleam_panic_FunctionCall.hrl b/build/packages/gleeunit/include/gleeunit@internal@gleam_panic_FunctionCall.hrl new file mode 100644 index 0000000..9d55488 --- /dev/null +++ b/build/packages/gleeunit/include/gleeunit@internal@gleam_panic_FunctionCall.hrl @@ -0,0 +1,3 @@ +-record(function_call, { + arguments :: list(gleeunit@internal@gleam_panic:asserted_expression()) +}). diff --git a/build/packages/gleeunit/include/gleeunit@internal@gleam_panic_GleamPanic.hrl b/build/packages/gleeunit/include/gleeunit@internal@gleam_panic_GleamPanic.hrl new file mode 100644 index 0000000..cf36764 --- /dev/null +++ b/build/packages/gleeunit/include/gleeunit@internal@gleam_panic_GleamPanic.hrl @@ -0,0 +1,8 @@ +-record(gleam_panic, { + message :: binary(), + file :: binary(), + module :: binary(), + function :: binary(), + line :: integer(), + kind :: gleeunit@internal@gleam_panic:panic_kind() +}). diff --git a/build/packages/gleeunit/include/gleeunit@internal@gleam_panic_LetAssert.hrl b/build/packages/gleeunit/include/gleeunit@internal@gleam_panic_LetAssert.hrl new file mode 100644 index 0000000..11f865e --- /dev/null +++ b/build/packages/gleeunit/include/gleeunit@internal@gleam_panic_LetAssert.hrl @@ -0,0 +1,7 @@ +-record(let_assert, { + start :: integer(), + 'end' :: integer(), + pattern_start :: integer(), + pattern_end :: integer(), + value :: gleam@dynamic:dynamic_() +}). diff --git a/build/packages/gleeunit/include/gleeunit@internal@gleam_panic_Literal.hrl b/build/packages/gleeunit/include/gleeunit@internal@gleam_panic_Literal.hrl new file mode 100644 index 0000000..2396489 --- /dev/null +++ b/build/packages/gleeunit/include/gleeunit@internal@gleam_panic_Literal.hrl @@ -0,0 +1 @@ +-record(literal, {value :: gleam@dynamic:dynamic_()}). diff --git a/build/packages/gleeunit/include/gleeunit@internal@gleam_panic_OtherExpression.hrl b/build/packages/gleeunit/include/gleeunit@internal@gleam_panic_OtherExpression.hrl new file mode 100644 index 0000000..0424990 --- /dev/null +++ b/build/packages/gleeunit/include/gleeunit@internal@gleam_panic_OtherExpression.hrl @@ -0,0 +1,3 @@ +-record(other_expression, { + expression :: gleeunit@internal@gleam_panic:asserted_expression() +}). diff --git a/build/packages/gleeunit/include/gleeunit@internal@reporting_State.hrl b/build/packages/gleeunit/include/gleeunit@internal@reporting_State.hrl new file mode 100644 index 0000000..575ccce --- /dev/null +++ b/build/packages/gleeunit/include/gleeunit@internal@reporting_State.hrl @@ -0,0 +1 @@ +-record(state, {passed :: integer(), failed :: integer(), skipped :: integer()}). diff --git a/build/packages/gleeunit/src/gleeunit.app.src b/build/packages/gleeunit/src/gleeunit.app.src new file mode 100644 index 0000000..e72987c --- /dev/null +++ b/build/packages/gleeunit/src/gleeunit.app.src @@ -0,0 +1,16 @@ +{application, gleeunit, [ + {vsn, "1.9.0"}, + {applications, [gleam_stdlib]}, + {description, "A simple test runner for Gleam, using EUnit on Erlang"}, + {modules, [erlang_test_module, + gleeunit, + gleeunit@@main, + gleeunit@internal@gleam_panic, + gleeunit@internal@reporting, + gleeunit@should, + gleeunit_ffi, + gleeunit_gleam_panic_ffi, + gleeunit_progress, + gleeunit_test_ffi]}, + {registered, []} +]}. diff --git a/build/packages/gleeunit/src/gleeunit.erl b/build/packages/gleeunit/src/gleeunit.erl new file mode 100644 index 0000000..bcb58b6 --- /dev/null +++ b/build/packages/gleeunit/src/gleeunit.erl @@ -0,0 +1,89 @@ +-module(gleeunit). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleeunit.gleam"). +-export([main/0]). +-export_type([atom_/0, encoding/0, report_module_name/0, gleeunit_progress_option/0, eunit_option/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +-type atom_() :: any(). + +-type encoding() :: utf8. + +-type report_module_name() :: gleeunit_progress. + +-type gleeunit_progress_option() :: {colored, boolean()}. + +-type eunit_option() :: verbose | + no_tty | + {report, {report_module_name(), list(gleeunit_progress_option())}} | + {scale_timeouts, integer()}. + +-file("src/gleeunit.gleam", 42). +-spec gleam_to_erlang_module_name(binary()) -> binary(). +gleam_to_erlang_module_name(Path) -> + case gleam_stdlib:string_ends_with(Path, <<".gleam"/utf8>>) of + true -> + _pipe = Path, + _pipe@1 = gleam@string:replace( + _pipe, + <<".gleam"/utf8>>, + <<""/utf8>> + ), + gleam@string:replace(_pipe@1, <<"/"/utf8>>, <<"@"/utf8>>); + + false -> + _pipe@2 = Path, + _pipe@3 = gleam@string:split(_pipe@2, <<"/"/utf8>>), + _pipe@4 = gleam@list:last(_pipe@3), + _pipe@5 = gleam@result:unwrap(_pipe@4, Path), + gleam@string:replace(_pipe@5, <<".erl"/utf8>>, <<""/utf8>>) + end. + +-file("src/gleeunit.gleam", 18). +-spec do_main() -> nil. +do_main() -> + Options = [verbose, + no_tty, + {report, {gleeunit_progress, [{colored, true}]}}, + {scale_timeouts, 10}], + Result = begin + _pipe = gleeunit_ffi:find_files( + <<"**/*.{erl,gleam}"/utf8>>, + <<"test"/utf8>> + ), + _pipe@1 = gleam@list:map(_pipe, fun gleam_to_erlang_module_name/1), + _pipe@2 = gleam@list:map( + _pipe@1, + fun(_capture) -> erlang:binary_to_atom(_capture, utf8) end + ), + gleeunit_ffi:run_eunit(_pipe@2, Options) + end, + Code = case Result of + {ok, _} -> + 0; + + {error, _} -> + 1 + end, + erlang:halt(Code). + +-file("src/gleeunit.gleam", 13). +?DOC( + " Find and run all test functions for the current project using Erlang's EUnit\n" + " test framework, or a custom JavaScript test runner.\n" + "\n" + " Any Erlang or Gleam function in the `test` directory with a name ending in\n" + " `_test` is considered a test function and will be run.\n" + "\n" + " A test that panics is considered a failure.\n" +). +-spec main() -> nil. +main() -> + do_main(). diff --git a/build/packages/gleeunit/src/gleeunit.gleam b/build/packages/gleeunit/src/gleeunit.gleam new file mode 100644 index 0000000..7d3155e --- /dev/null +++ b/build/packages/gleeunit/src/gleeunit.gleam @@ -0,0 +1,86 @@ +import gleam/list +import gleam/result +import gleam/string + +/// Find and run all test functions for the current project using Erlang's EUnit +/// test framework, or a custom JavaScript test runner. +/// +/// Any Erlang or Gleam function in the `test` directory with a name ending in +/// `_test` is considered a test function and will be run. +/// +/// A test that panics is considered a failure. +/// +pub fn main() -> Nil { + do_main() +} + +@external(javascript, "./gleeunit_ffi.mjs", "main") +fn do_main() -> Nil { + let options = [ + Verbose, + NoTty, + Report(#(GleeunitProgress, [Colored(True)])), + ScaleTimeouts(10), + ] + + let result = + find_files(matching: "**/*.{erl,gleam}", in: "test") + |> list.map(gleam_to_erlang_module_name) + |> list.map(dangerously_convert_string_to_atom(_, Utf8)) + |> run_eunit(options) + + let code = case result { + Ok(_) -> 0 + Error(_) -> 1 + } + halt(code) +} + +@external(erlang, "erlang", "halt") +fn halt(a: Int) -> Nil + +fn gleam_to_erlang_module_name(path: String) -> String { + case string.ends_with(path, ".gleam") { + True -> + path + |> string.replace(".gleam", "") + |> string.replace("/", "@") + + False -> + path + |> string.split("/") + |> list.last + |> result.unwrap(path) + |> string.replace(".erl", "") + } +} + +@external(erlang, "gleeunit_ffi", "find_files") +fn find_files(matching matching: String, in in: String) -> List(String) + +type Atom + +type Encoding { + Utf8 +} + +@external(erlang, "erlang", "binary_to_atom") +fn dangerously_convert_string_to_atom(a: String, b: Encoding) -> Atom + +type ReportModuleName { + GleeunitProgress +} + +type GleeunitProgressOption { + Colored(Bool) +} + +type EunitOption { + Verbose + NoTty + Report(#(ReportModuleName, List(GleeunitProgressOption))) + ScaleTimeouts(Int) +} + +@external(erlang, "gleeunit_ffi", "run_eunit") +fn run_eunit(a: List(Atom), b: List(EunitOption)) -> Result(Nil, a) diff --git a/build/packages/gleeunit/src/gleeunit/internal/gleam_panic.gleam b/build/packages/gleeunit/src/gleeunit/internal/gleam_panic.gleam new file mode 100644 index 0000000..6a5d309 --- /dev/null +++ b/build/packages/gleeunit/src/gleeunit/internal/gleam_panic.gleam @@ -0,0 +1,49 @@ +import gleam/dynamic + +pub type GleamPanic { + GleamPanic( + message: String, + file: String, + module: String, + function: String, + line: Int, + kind: PanicKind, + ) +} + +pub type PanicKind { + Todo + Panic + LetAssert( + start: Int, + end: Int, + pattern_start: Int, + pattern_end: Int, + value: dynamic.Dynamic, + ) + Assert(start: Int, end: Int, expression_start: Int, kind: AssertKind) +} + +pub type AssertKind { + BinaryOperator( + operator: String, + left: AssertedExpression, + right: AssertedExpression, + ) + FunctionCall(arguments: List(AssertedExpression)) + OtherExpression(expression: AssertedExpression) +} + +pub type AssertedExpression { + AssertedExpression(start: Int, end: Int, kind: ExpressionKind) +} + +pub type ExpressionKind { + Literal(value: dynamic.Dynamic) + Expression(value: dynamic.Dynamic) + Unevaluated +} + +@external(erlang, "gleeunit_gleam_panic_ffi", "from_dynamic") +@external(javascript, "./gleeunit_gleam_panic_ffi.mjs", "from_dynamic") +pub fn from_dynamic(data: dynamic.Dynamic) -> Result(GleamPanic, Nil) diff --git a/build/packages/gleeunit/src/gleeunit/internal/gleeunit_gleam_panic_ffi.erl b/build/packages/gleeunit/src/gleeunit/internal/gleeunit_gleam_panic_ffi.erl new file mode 100644 index 0000000..d78f5e5 --- /dev/null +++ b/build/packages/gleeunit/src/gleeunit/internal/gleeunit_gleam_panic_ffi.erl @@ -0,0 +1,49 @@ +-module(gleeunit_gleam_panic_ffi). +-export([from_dynamic/1]). + +from_dynamic(#{ + gleam_error := assert, + start := Start, + 'end' := End, + expression_start := EStart +} = E) -> + wrap(E, {assert, Start, End, EStart, assert_kind(E)}); +from_dynamic(#{ + gleam_error := let_assert, + start := Start, + 'end' := End, + pattern_start := PStart, + pattern_end := PEnd, + value := Value +} = E) -> + wrap(E, {let_assert, Start, End, PStart, PEnd, Value}); +from_dynamic(#{gleam_error := panic} = E) -> + wrap(E, panic); +from_dynamic(#{gleam_error := todo} = E) -> + wrap(E, todo); +from_dynamic(_) -> + {error, nil}. + +assert_kind(#{kind := binary_operator, left := L, right := R, operator := O}) -> + {binary_operator, atom_to_binary(O), expression(L), expression(R)}; +assert_kind(#{kind := function_call, arguments := Arguments}) -> + {function_call, lists:map(fun expression/1, Arguments)}; +assert_kind(#{kind := expression, expression := Expression}) -> + {other_expression, expression(Expression)}. + +expression(#{start := S, 'end' := E, kind := literal, value := Value}) -> + {asserted_expression, S, E, {literal, Value}}; +expression(#{start := S, 'end' := E, kind := expression, value := Value}) -> + {asserted_expression, S, E, {expression, Value}}; +expression(#{start := S, 'end' := E, kind := unevaluated}) -> + {asserted_expression, S, E, unevaluated}. + +wrap(#{ + gleam_error := _, + file := File, + message := Message, + module := Module, + function := Function, + line := Line +}, Kind) -> + {ok, {gleam_panic, Message, File, Module, Function, Line, Kind}}. diff --git a/build/packages/gleeunit/src/gleeunit/internal/gleeunit_gleam_panic_ffi.mjs b/build/packages/gleeunit/src/gleeunit/internal/gleeunit_gleam_panic_ffi.mjs new file mode 100644 index 0000000..03f6025 --- /dev/null +++ b/build/packages/gleeunit/src/gleeunit/internal/gleeunit_gleam_panic_ffi.mjs @@ -0,0 +1,91 @@ +import { Result$Ok, Result$Error, List$Empty, List$NonEmpty } from "../../gleam.mjs"; +import { + GleamPanic$GleamPanic, + PanicKind$Todo, + PanicKind$Panic, + PanicKind$LetAssert, + PanicKind$Assert, + AssertKind$BinaryOperator, + AssertKind$FunctionCall, + AssertKind$OtherExpression, + AssertedExpression$AssertedExpression, + ExpressionKind$Literal, + ExpressionKind$Expression, + ExpressionKind$Unevaluated, +} from "./gleam_panic.mjs"; + +export function from_dynamic(error) { + if (!(error instanceof globalThis.Error) || !error.gleam_error) { + return Result$Error(undefined); + } + + if (error.gleam_error === "todo") { + return wrap(error, PanicKind$Todo()); + } + + if (error.gleam_error === "panic") { + return wrap(error, PanicKind$Panic()); + } + + if (error.gleam_error === "let_assert") { + let kind = PanicKind$LetAssert( + error.start, + error.end, + error.pattern_start, + error.pattern_end, + error.value, + ); + return wrap(error, kind); + } + + if (error.gleam_error === "assert") { + let kind = PanicKind$Assert( + error.start, + error.end, + error.expression_start, + assert_kind(error), + ); + return wrap(error, kind); + } + + return Result$Error(undefined); +} + +function assert_kind(error) { + if (error.kind == "binary_operator") { + return AssertKind$BinaryOperator( + error.operator, + expression(error.left), + expression(error.right), + ); + } + + if (error.kind == "function_call") { + let list = List$Empty(); + let i = error.arguments.length; + while (i--) { + list = List$NonEmpty(expression(error.arguments[i]), list); + } + return AssertKind$FunctionCall(list); + } + + return AssertKind$OtherExpression(expression(error.expression)); +} + +function expression(data) { + const expression = AssertedExpression$AssertedExpression(data.start, data.end, undefined); + if (data.kind == "literal") { + expression.kind = ExpressionKind$Literal(data.value); + } else if (data.kind == "expression") { + expression.kind = ExpressionKind$Expression(data.value); + } else { + expression.kind = ExpressionKind$Unevaluated(); + } + return expression; +} + +function wrap(e, kind) { + return Result$Ok( + GleamPanic$GleamPanic(e.message, e.file, e.module, e.function, e.line, kind), + ); +} diff --git a/build/packages/gleeunit/src/gleeunit/internal/reporting.gleam b/build/packages/gleeunit/src/gleeunit/internal/reporting.gleam new file mode 100644 index 0000000..72f766f --- /dev/null +++ b/build/packages/gleeunit/src/gleeunit/internal/reporting.gleam @@ -0,0 +1,240 @@ +import gleam/bit_array +import gleam/dynamic +import gleam/int +import gleam/io +import gleam/list +import gleam/option.{type Option} +import gleam/result +import gleam/string +import gleeunit/internal/gleam_panic.{type GleamPanic} + +pub type State { + State(passed: Int, failed: Int, skipped: Int) +} + +pub fn new_state() -> State { + State(passed: 0, failed: 0, skipped: 0) +} + +pub fn finished(state: State) -> Int { + case state { + State(passed: 0, failed: 0, skipped: 0) -> { + io.println("\nNo tests found!") + 1 + } + State(failed: 0, skipped: 0, ..) -> { + let message = + "\n" <> int.to_string(state.passed) <> " passed, no failures" + io.println(green(message)) + 0 + } + State(skipped: 0, ..) -> { + let message = + "\n" + <> int.to_string(state.passed) + <> " passed, " + <> int.to_string(state.failed) + <> " failures" + io.println(red(message)) + 1 + } + State(failed: 0, ..) -> { + let message = + "\n" + <> int.to_string(state.passed) + <> " passed, 0 failures, " + <> int.to_string(state.skipped) + <> " skipped" + io.println(yellow(message)) + 1 + } + State(..) -> { + let message = + "\n" + <> int.to_string(state.passed) + <> " passed, " + <> int.to_string(state.failed) + <> " failures, " + <> int.to_string(state.skipped) + <> " skipped" + io.println(red(message)) + 1 + } + } +} + +pub fn test_passed(state: State) -> State { + io.print(green(".")) + State(..state, passed: state.passed + 1) +} + +pub fn test_failed( + state: State, + module: String, + function: String, + error: dynamic.Dynamic, +) -> State { + let message = case gleam_panic.from_dynamic(error) { + Ok(error) -> { + let src = option.from_result(read_file(error.file)) + format_gleam_error(error, module, function, src) + } + Error(_) -> format_unknown(module, function, error) + } + + io.print("\n" <> message) + State(..state, failed: state.failed + 1) +} + +pub fn eunit_missing() -> Result(never, Nil) { + let message = bold(red("Error")) <> ": EUnit libraries not found. + +Your Erlang installation seems to be incomplete. If you installed Erlang using +a package manager ensure that you have installed the full Erlang +distribution instead of a stripped-down version. +" + io.print_error(message) + Error(Nil) +} + +fn format_unknown( + module: String, + function: String, + error: dynamic.Dynamic, +) -> String { + string.concat([ + grey(module <> "." <> function) <> "\n", + "An unexpected error occurred:\n", + "\n", + " " <> string.inspect(error) <> "\n", + ]) +} + +fn format_gleam_error( + error: GleamPanic, + module: String, + function: String, + src: Option(BitArray), +) -> String { + let location = grey(error.file <> ":" <> int.to_string(error.line)) + + case error.kind { + gleam_panic.Panic -> { + string.concat([ + bold(red("panic")) <> " " <> location <> "\n", + cyan(" test") <> ": " <> module <> "." <> function <> "\n", + cyan(" info") <> ": " <> error.message <> "\n", + ]) + } + + gleam_panic.Todo -> { + string.concat([ + bold(yellow("todo")) <> " " <> location <> "\n", + cyan(" test") <> ": " <> module <> "." <> function <> "\n", + cyan(" info") <> ": " <> error.message <> "\n", + ]) + } + + gleam_panic.Assert(start:, end:, kind:, ..) -> { + string.concat([ + bold(red("assert")) <> " " <> location <> "\n", + cyan(" test") <> ": " <> module <> "." <> function <> "\n", + code_snippet(src, start, end), + assert_info(kind), + cyan(" info") <> ": " <> error.message <> "\n", + ]) + } + + gleam_panic.LetAssert(start:, end:, value:, ..) -> { + string.concat([ + bold(red("let assert")) <> " " <> location <> "\n", + cyan(" test") <> ": " <> module <> "." <> function <> "\n", + code_snippet(src, start, end), + cyan("value") <> ": " <> string.inspect(value) <> "\n", + cyan(" info") <> ": " <> error.message <> "\n", + ]) + } + } +} + +fn assert_info(kind: gleam_panic.AssertKind) -> String { + case kind { + gleam_panic.BinaryOperator(left:, right:, ..) -> { + string.concat([assert_value(" left", left), assert_value("right", right)]) + } + + gleam_panic.FunctionCall(arguments:) -> { + arguments + |> list.index_map(fn(e, i) { + let number = string.pad_start(int.to_string(i), 5, " ") + assert_value(number, e) + }) + |> string.concat + } + + gleam_panic.OtherExpression(..) -> "" + } +} + +fn assert_value(name: String, value: gleam_panic.AssertedExpression) -> String { + cyan(name) <> ": " <> inspect_value(value) <> "\n" +} + +fn inspect_value(value: gleam_panic.AssertedExpression) -> String { + case value.kind { + gleam_panic.Unevaluated -> grey("unevaluated") + gleam_panic.Literal(..) -> grey("literal") + gleam_panic.Expression(value:) -> string.inspect(value) + } +} + +fn code_snippet(src: Option(BitArray), start: Int, end: Int) -> String { + { + use src <- result.try(option.to_result(src, Nil)) + use snippet <- result.try(bit_array.slice(src, start, end - start)) + use snippet <- result.try(bit_array.to_string(snippet)) + let snippet = cyan(" code") <> ": " <> snippet <> "\n" + Ok(snippet) + } + |> result.unwrap("") +} + +pub fn test_skipped(state: State, module: String, function: String) -> State { + io.print("\n" <> module <> "." <> function <> yellow(" skipped")) + State(..state, skipped: state.skipped + 1) +} + +fn bold(text: String) -> String { + "\u{001b}[1m" <> text <> "\u{001b}[22m" +} + +fn cyan(text: String) -> String { + "\u{001b}[36m" <> text <> "\u{001b}[39m" +} + +fn yellow(text: String) -> String { + "\u{001b}[33m" <> text <> "\u{001b}[39m" +} + +fn green(text: String) -> String { + "\u{001b}[32m" <> text <> "\u{001b}[39m" +} + +fn red(text: String) -> String { + "\u{001b}[31m" <> text <> "\u{001b}[39m" +} + +fn grey(text: String) -> String { + "\u{001b}[90m" <> text <> "\u{001b}[39m" +} + +@external(erlang, "file", "read_file") +fn read_file(path: String) -> Result(BitArray, dynamic.Dynamic) { + case read_file_text(path) { + Ok(text) -> Ok(bit_array.from_string(text)) + Error(e) -> Error(e) + } +} + +@external(javascript, "../../gleeunit_ffi.mjs", "read_file") +fn read_file_text(path: String) -> Result(String, dynamic.Dynamic) diff --git a/build/packages/gleeunit/src/gleeunit/should.gleam b/build/packages/gleeunit/src/gleeunit/should.gleam new file mode 100644 index 0000000..99cd16c --- /dev/null +++ b/build/packages/gleeunit/src/gleeunit/should.gleam @@ -0,0 +1,72 @@ +//// Use the `assert` keyword instead of this module. + +import gleam/option.{type Option, None, Some} +import gleam/string + +pub fn equal(a: t, b: t) -> Nil { + case a == b { + True -> Nil + _ -> + panic as string.concat([ + "\n", + string.inspect(a), + "\nshould equal\n", + string.inspect(b), + ]) + } +} + +pub fn not_equal(a: t, b: t) -> Nil { + case a != b { + True -> Nil + _ -> + panic as string.concat([ + "\n", + string.inspect(a), + "\nshould not equal\n", + string.inspect(b), + ]) + } +} + +pub fn be_ok(a: Result(a, e)) -> a { + case a { + Ok(value) -> value + _ -> panic as string.concat(["\n", string.inspect(a), "\nshould be ok"]) + } +} + +pub fn be_error(a: Result(a, e)) -> e { + case a { + Error(error) -> error + _ -> panic as string.concat(["\n", string.inspect(a), "\nshould be error"]) + } +} + +pub fn be_some(a: Option(a)) -> a { + case a { + Some(value) -> value + _ -> panic as string.concat(["\n", string.inspect(a), "\nshould be some"]) + } +} + +pub fn be_none(a: Option(a)) -> Nil { + case a { + None -> Nil + _ -> panic as string.concat(["\n", string.inspect(a), "\nshould be none"]) + } +} + +pub fn be_true(actual: Bool) -> Nil { + actual + |> equal(True) +} + +pub fn be_false(actual: Bool) -> Nil { + actual + |> equal(False) +} + +pub fn fail() -> Nil { + be_true(False) +} diff --git a/build/packages/gleeunit/src/gleeunit@internal@gleam_panic.erl b/build/packages/gleeunit/src/gleeunit@internal@gleam_panic.erl new file mode 100644 index 0000000..398ea7d --- /dev/null +++ b/build/packages/gleeunit/src/gleeunit@internal@gleam_panic.erl @@ -0,0 +1,56 @@ +-module(gleeunit@internal@gleam_panic). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleeunit/internal/gleam_panic.gleam"). +-export([from_dynamic/1]). +-export_type([gleam_panic/0, panic_kind/0, assert_kind/0, asserted_expression/0, expression_kind/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC(false). + +-type gleam_panic() :: {gleam_panic, + binary(), + binary(), + binary(), + binary(), + integer(), + panic_kind()}. + +-type panic_kind() :: todo | + panic | + {let_assert, + integer(), + integer(), + integer(), + integer(), + gleam@dynamic:dynamic_()} | + {assert, integer(), integer(), integer(), assert_kind()}. + +-type assert_kind() :: {binary_operator, + binary(), + asserted_expression(), + asserted_expression()} | + {function_call, list(asserted_expression())} | + {other_expression, asserted_expression()}. + +-type asserted_expression() :: {asserted_expression, + integer(), + integer(), + expression_kind()}. + +-type expression_kind() :: {literal, gleam@dynamic:dynamic_()} | + {expression, gleam@dynamic:dynamic_()} | + unevaluated. + +-file("src/gleeunit/internal/gleam_panic.gleam", 49). +?DOC(false). +-spec from_dynamic(gleam@dynamic:dynamic_()) -> {ok, gleam_panic()} | + {error, nil}. +from_dynamic(Data) -> + gleeunit_gleam_panic_ffi:from_dynamic(Data). diff --git a/build/packages/gleeunit/src/gleeunit@internal@reporting.erl b/build/packages/gleeunit/src/gleeunit@internal@reporting.erl new file mode 100644 index 0000000..8c37c79 --- /dev/null +++ b/build/packages/gleeunit/src/gleeunit@internal@reporting.erl @@ -0,0 +1,343 @@ +-module(gleeunit@internal@reporting). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleeunit/internal/reporting.gleam"). +-export([new_state/0, test_skipped/3, test_passed/1, finished/1, eunit_missing/0, test_failed/4]). +-export_type([state/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC(false). + +-type state() :: {state, integer(), integer(), integer()}. + +-file("src/gleeunit/internal/reporting.gleam", 15). +?DOC(false). +-spec new_state() -> state(). +new_state() -> + {state, 0, 0, 0}. + +-file("src/gleeunit/internal/reporting.gleam", 207). +?DOC(false). +-spec bold(binary()) -> binary(). +bold(Text) -> + <<<<"\x{001b}[1m"/utf8, Text/binary>>/binary, "\x{001b}[22m"/utf8>>. + +-file("src/gleeunit/internal/reporting.gleam", 211). +?DOC(false). +-spec cyan(binary()) -> binary(). +cyan(Text) -> + <<<<"\x{001b}[36m"/utf8, Text/binary>>/binary, "\x{001b}[39m"/utf8>>. + +-file("src/gleeunit/internal/reporting.gleam", 191). +?DOC(false). +-spec code_snippet(gleam@option:option(bitstring()), integer(), integer()) -> binary(). +code_snippet(Src, Start, End) -> + _pipe = begin + gleam@result:'try'( + gleam@option:to_result(Src, nil), + fun(Src@1) -> + gleam@result:'try'( + gleam_stdlib:bit_array_slice(Src@1, Start, End - Start), + fun(Snippet) -> + gleam@result:'try'( + gleam@bit_array:to_string(Snippet), + fun(Snippet@1) -> + Snippet@2 = <<<<<<(cyan(<<" code"/utf8>>))/binary, + ": "/utf8>>/binary, + Snippet@1/binary>>/binary, + "\n"/utf8>>, + {ok, Snippet@2} + end + ) + end + ) + end + ) + end, + gleam@result:unwrap(_pipe, <<""/utf8>>). + +-file("src/gleeunit/internal/reporting.gleam", 215). +?DOC(false). +-spec yellow(binary()) -> binary(). +yellow(Text) -> + <<<<"\x{001b}[33m"/utf8, Text/binary>>/binary, "\x{001b}[39m"/utf8>>. + +-file("src/gleeunit/internal/reporting.gleam", 202). +?DOC(false). +-spec test_skipped(state(), binary(), binary()) -> state(). +test_skipped(State, Module, Function) -> + gleam_stdlib:print( + <<<<<<<<"\n"/utf8, Module/binary>>/binary, "."/utf8>>/binary, + Function/binary>>/binary, + (yellow(<<" skipped"/utf8>>))/binary>> + ), + {state, + erlang:element(2, State), + erlang:element(3, State), + erlang:element(4, State) + 1}. + +-file("src/gleeunit/internal/reporting.gleam", 219). +?DOC(false). +-spec green(binary()) -> binary(). +green(Text) -> + <<<<"\x{001b}[32m"/utf8, Text/binary>>/binary, "\x{001b}[39m"/utf8>>. + +-file("src/gleeunit/internal/reporting.gleam", 66). +?DOC(false). +-spec test_passed(state()) -> state(). +test_passed(State) -> + gleam_stdlib:print(green(<<"."/utf8>>)), + {state, + erlang:element(2, State) + 1, + erlang:element(3, State), + erlang:element(4, State)}. + +-file("src/gleeunit/internal/reporting.gleam", 223). +?DOC(false). +-spec red(binary()) -> binary(). +red(Text) -> + <<<<"\x{001b}[31m"/utf8, Text/binary>>/binary, "\x{001b}[39m"/utf8>>. + +-file("src/gleeunit/internal/reporting.gleam", 19). +?DOC(false). +-spec finished(state()) -> integer(). +finished(State) -> + case State of + {state, 0, 0, 0} -> + gleam_stdlib:println(<<"\nNo tests found!"/utf8>>), + 1; + + {state, _, 0, 0} -> + Message = <<<<"\n"/utf8, + (erlang:integer_to_binary(erlang:element(2, State)))/binary>>/binary, + " passed, no failures"/utf8>>, + gleam_stdlib:println(green(Message)), + 0; + + {state, _, _, 0} -> + Message@1 = <<<<<<<<"\n"/utf8, + (erlang:integer_to_binary(erlang:element(2, State)))/binary>>/binary, + " passed, "/utf8>>/binary, + (erlang:integer_to_binary(erlang:element(3, State)))/binary>>/binary, + " failures"/utf8>>, + gleam_stdlib:println(red(Message@1)), + 1; + + {state, _, 0, _} -> + Message@2 = <<<<<<<<"\n"/utf8, + (erlang:integer_to_binary(erlang:element(2, State)))/binary>>/binary, + " passed, 0 failures, "/utf8>>/binary, + (erlang:integer_to_binary(erlang:element(4, State)))/binary>>/binary, + " skipped"/utf8>>, + gleam_stdlib:println(yellow(Message@2)), + 1; + + {state, _, _, _} -> + Message@3 = <<<<<<<<<<<<"\n"/utf8, + (erlang:integer_to_binary( + erlang:element(2, State) + ))/binary>>/binary, + " passed, "/utf8>>/binary, + (erlang:integer_to_binary(erlang:element(3, State)))/binary>>/binary, + " failures, "/utf8>>/binary, + (erlang:integer_to_binary(erlang:element(4, State)))/binary>>/binary, + " skipped"/utf8>>, + gleam_stdlib:println(red(Message@3)), + 1 + end. + +-file("src/gleeunit/internal/reporting.gleam", 89). +?DOC(false). +-spec eunit_missing() -> {ok, any()} | {error, nil}. +eunit_missing() -> + Message = <<(bold(red(<<"Error"/utf8>>)))/binary, + ": EUnit libraries not found. + +Your Erlang installation seems to be incomplete. If you installed Erlang using +a package manager ensure that you have installed the full Erlang +distribution instead of a stripped-down version. +"/utf8>>, + gleam_stdlib:print_error(Message), + {error, nil}. + +-file("src/gleeunit/internal/reporting.gleam", 227). +?DOC(false). +-spec grey(binary()) -> binary(). +grey(Text) -> + <<<<"\x{001b}[90m"/utf8, Text/binary>>/binary, "\x{001b}[39m"/utf8>>. + +-file("src/gleeunit/internal/reporting.gleam", 100). +?DOC(false). +-spec format_unknown(binary(), binary(), gleam@dynamic:dynamic_()) -> binary(). +format_unknown(Module, Function, Error) -> + erlang:list_to_binary( + [<<(grey(<<<>/binary, Function/binary>>))/binary, + "\n"/utf8>>, + <<"An unexpected error occurred:\n"/utf8>>, + <<"\n"/utf8>>, + <<<<" "/utf8, (gleam@string:inspect(Error))/binary>>/binary, + "\n"/utf8>>] + ). + +-file("src/gleeunit/internal/reporting.gleam", 183). +?DOC(false). +-spec inspect_value(gleeunit@internal@gleam_panic:asserted_expression()) -> binary(). +inspect_value(Value) -> + case erlang:element(4, Value) of + unevaluated -> + grey(<<"unevaluated"/utf8>>); + + {literal, _} -> + grey(<<"literal"/utf8>>); + + {expression, Value@1} -> + gleam@string:inspect(Value@1) + end. + +-file("src/gleeunit/internal/reporting.gleam", 179). +?DOC(false). +-spec assert_value( + binary(), + gleeunit@internal@gleam_panic:asserted_expression() +) -> binary(). +assert_value(Name, Value) -> + <<<<<<(cyan(Name))/binary, ": "/utf8>>/binary, + (inspect_value(Value))/binary>>/binary, + "\n"/utf8>>. + +-file("src/gleeunit/internal/reporting.gleam", 160). +?DOC(false). +-spec assert_info(gleeunit@internal@gleam_panic:assert_kind()) -> binary(). +assert_info(Kind) -> + case Kind of + {binary_operator, _, Left, Right} -> + erlang:list_to_binary( + [assert_value(<<" left"/utf8>>, Left), + assert_value(<<"right"/utf8>>, Right)] + ); + + {function_call, Arguments} -> + _pipe = Arguments, + _pipe@1 = gleam@list:index_map( + _pipe, + fun(E, I) -> + Number = gleam@string:pad_start( + erlang:integer_to_binary(I), + 5, + <<" "/utf8>> + ), + assert_value(Number, E) + end + ), + erlang:list_to_binary(_pipe@1); + + {other_expression, _} -> + <<""/utf8>> + end. + +-file("src/gleeunit/internal/reporting.gleam", 113). +?DOC(false). +-spec format_gleam_error( + gleeunit@internal@gleam_panic:gleam_panic(), + binary(), + binary(), + gleam@option:option(bitstring()) +) -> binary(). +format_gleam_error(Error, Module, Function, Src) -> + Location = grey( + <<<<(erlang:element(3, Error))/binary, ":"/utf8>>/binary, + (erlang:integer_to_binary(erlang:element(6, Error)))/binary>> + ), + case erlang:element(7, Error) of + panic -> + erlang:list_to_binary( + [<<<<<<(bold(red(<<"panic"/utf8>>)))/binary, " "/utf8>>/binary, + Location/binary>>/binary, + "\n"/utf8>>, + <<<<<<<<<<(cyan(<<" test"/utf8>>))/binary, ": "/utf8>>/binary, + Module/binary>>/binary, + "."/utf8>>/binary, + Function/binary>>/binary, + "\n"/utf8>>, + <<<<<<(cyan(<<" info"/utf8>>))/binary, ": "/utf8>>/binary, + (erlang:element(2, Error))/binary>>/binary, + "\n"/utf8>>] + ); + + todo -> + erlang:list_to_binary( + [<<<<<<(bold(yellow(<<"todo"/utf8>>)))/binary, " "/utf8>>/binary, + Location/binary>>/binary, + "\n"/utf8>>, + <<<<<<<<<<(cyan(<<" test"/utf8>>))/binary, ": "/utf8>>/binary, + Module/binary>>/binary, + "."/utf8>>/binary, + Function/binary>>/binary, + "\n"/utf8>>, + <<<<<<(cyan(<<" info"/utf8>>))/binary, ": "/utf8>>/binary, + (erlang:element(2, Error))/binary>>/binary, + "\n"/utf8>>] + ); + + {assert, Start, End, _, Kind} -> + erlang:list_to_binary( + [<<<<<<(bold(red(<<"assert"/utf8>>)))/binary, " "/utf8>>/binary, + Location/binary>>/binary, + "\n"/utf8>>, + <<<<<<<<<<(cyan(<<" test"/utf8>>))/binary, ": "/utf8>>/binary, + Module/binary>>/binary, + "."/utf8>>/binary, + Function/binary>>/binary, + "\n"/utf8>>, + code_snippet(Src, Start, End), + assert_info(Kind), + <<<<<<(cyan(<<" info"/utf8>>))/binary, ": "/utf8>>/binary, + (erlang:element(2, Error))/binary>>/binary, + "\n"/utf8>>] + ); + + {let_assert, Start@1, End@1, _, _, Value} -> + erlang:list_to_binary( + [<<<<<<(bold(red(<<"let assert"/utf8>>)))/binary, " "/utf8>>/binary, + Location/binary>>/binary, + "\n"/utf8>>, + <<<<<<<<<<(cyan(<<" test"/utf8>>))/binary, ": "/utf8>>/binary, + Module/binary>>/binary, + "."/utf8>>/binary, + Function/binary>>/binary, + "\n"/utf8>>, + code_snippet(Src, Start@1, End@1), + <<<<<<(cyan(<<"value"/utf8>>))/binary, ": "/utf8>>/binary, + (gleam@string:inspect(Value))/binary>>/binary, + "\n"/utf8>>, + <<<<<<(cyan(<<" info"/utf8>>))/binary, ": "/utf8>>/binary, + (erlang:element(2, Error))/binary>>/binary, + "\n"/utf8>>] + ) + end. + +-file("src/gleeunit/internal/reporting.gleam", 71). +?DOC(false). +-spec test_failed(state(), binary(), binary(), gleam@dynamic:dynamic_()) -> state(). +test_failed(State, Module, Function, Error) -> + Message = case gleeunit_gleam_panic_ffi:from_dynamic(Error) of + {ok, Error@1} -> + Src = gleam@option:from_result( + file:read_file(erlang:element(3, Error@1)) + ), + format_gleam_error(Error@1, Module, Function, Src); + + {error, _} -> + format_unknown(Module, Function, Error) + end, + gleam_stdlib:print(<<"\n"/utf8, Message/binary>>), + {state, + erlang:element(2, State), + erlang:element(3, State) + 1, + erlang:element(4, State)}. diff --git a/build/packages/gleeunit/src/gleeunit@should.erl b/build/packages/gleeunit/src/gleeunit@should.erl new file mode 100644 index 0000000..81048de --- /dev/null +++ b/build/packages/gleeunit/src/gleeunit@should.erl @@ -0,0 +1,153 @@ +-module(gleeunit@should). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleeunit/should.gleam"). +-export([equal/2, not_equal/2, be_ok/1, be_error/1, be_some/1, be_none/1, be_true/1, be_false/1, fail/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC(" Use the `assert` keyword instead of this module.\n"). + +-file("src/gleeunit/should.gleam", 6). +-spec equal(DOF, DOF) -> nil. +equal(A, B) -> + case A =:= B of + true -> + nil; + + _ -> + erlang:error(#{gleam_error => panic, + message => erlang:list_to_binary( + [<<"\n"/utf8>>, + gleam@string:inspect(A), + <<"\nshould equal\n"/utf8>>, + gleam@string:inspect(B)] + ), + file => <>, + module => <<"gleeunit/should"/utf8>>, + function => <<"equal"/utf8>>, + line => 10}) + end. + +-file("src/gleeunit/should.gleam", 19). +-spec not_equal(DOG, DOG) -> nil. +not_equal(A, B) -> + case A /= B of + true -> + nil; + + _ -> + erlang:error(#{gleam_error => panic, + message => erlang:list_to_binary( + [<<"\n"/utf8>>, + gleam@string:inspect(A), + <<"\nshould not equal\n"/utf8>>, + gleam@string:inspect(B)] + ), + file => <>, + module => <<"gleeunit/should"/utf8>>, + function => <<"not_equal"/utf8>>, + line => 23}) + end. + +-file("src/gleeunit/should.gleam", 32). +-spec be_ok({ok, DOH} | {error, any()}) -> DOH. +be_ok(A) -> + case A of + {ok, Value} -> + Value; + + _ -> + erlang:error(#{gleam_error => panic, + message => erlang:list_to_binary( + [<<"\n"/utf8>>, + gleam@string:inspect(A), + <<"\nshould be ok"/utf8>>] + ), + file => <>, + module => <<"gleeunit/should"/utf8>>, + function => <<"be_ok"/utf8>>, + line => 35}) + end. + +-file("src/gleeunit/should.gleam", 39). +-spec be_error({ok, any()} | {error, DOM}) -> DOM. +be_error(A) -> + case A of + {error, Error} -> + Error; + + _ -> + erlang:error(#{gleam_error => panic, + message => erlang:list_to_binary( + [<<"\n"/utf8>>, + gleam@string:inspect(A), + <<"\nshould be error"/utf8>>] + ), + file => <>, + module => <<"gleeunit/should"/utf8>>, + function => <<"be_error"/utf8>>, + line => 42}) + end. + +-file("src/gleeunit/should.gleam", 46). +-spec be_some(gleam@option:option(DOP)) -> DOP. +be_some(A) -> + case A of + {some, Value} -> + Value; + + _ -> + erlang:error(#{gleam_error => panic, + message => erlang:list_to_binary( + [<<"\n"/utf8>>, + gleam@string:inspect(A), + <<"\nshould be some"/utf8>>] + ), + file => <>, + module => <<"gleeunit/should"/utf8>>, + function => <<"be_some"/utf8>>, + line => 49}) + end. + +-file("src/gleeunit/should.gleam", 53). +-spec be_none(gleam@option:option(any())) -> nil. +be_none(A) -> + case A of + none -> + nil; + + _ -> + erlang:error(#{gleam_error => panic, + message => erlang:list_to_binary( + [<<"\n"/utf8>>, + gleam@string:inspect(A), + <<"\nshould be none"/utf8>>] + ), + file => <>, + module => <<"gleeunit/should"/utf8>>, + function => <<"be_none"/utf8>>, + line => 56}) + end. + +-file("src/gleeunit/should.gleam", 60). +-spec be_true(boolean()) -> nil. +be_true(Actual) -> + _pipe = Actual, + equal(_pipe, true). + +-file("src/gleeunit/should.gleam", 65). +-spec be_false(boolean()) -> nil. +be_false(Actual) -> + _pipe = Actual, + equal(_pipe, false). + +-file("src/gleeunit/should.gleam", 70). +-spec fail() -> nil. +fail() -> + be_true(false). diff --git a/build/packages/gleeunit/src/gleeunit_ffi.erl b/build/packages/gleeunit/src/gleeunit_ffi.erl new file mode 100644 index 0000000..05c7490 --- /dev/null +++ b/build/packages/gleeunit/src/gleeunit_ffi.erl @@ -0,0 +1,21 @@ +-module(gleeunit_ffi). + +-export([find_files/2, run_eunit/2]). + +find_files(Pattern, In) -> + Results = filelib:wildcard(binary_to_list(Pattern), binary_to_list(In)), + lists:map(fun list_to_binary/1, Results). + +run_eunit(Tests, Options) -> + case code:which(eunit) of + non_existing -> + gleeunit@internal@reporting:eunit_missing(); + + _ -> + case eunit:test(Tests, Options) of + ok -> {ok, nil}; + error -> {error, nil}; + {error, Term} -> {error, Term} + end + end. + diff --git a/build/packages/gleeunit/src/gleeunit_ffi.mjs b/build/packages/gleeunit/src/gleeunit_ffi.mjs new file mode 100644 index 0000000..7bdc071 --- /dev/null +++ b/build/packages/gleeunit/src/gleeunit_ffi.mjs @@ -0,0 +1,100 @@ +import { readFileSync } from "node:fs"; +import { Result$Ok, Result$Error } from "./gleam.mjs"; +import * as reporting from "./gleeunit/internal/reporting.mjs"; + +export function read_file(path) { + try { + return Result$Ok(readFileSync(path)); + } catch { + return Result$Error(undefined); + } +} + +async function* gleamFiles(directory) { + for (let entry of await read_dir(directory)) { + let path = join_path(directory, entry); + if (path.endsWith(".gleam")) { + yield path; + } else { + try { + yield* gleamFiles(path); + } catch (error) { + // Could not read directory, assume it's a file + } + } + } +} + +async function readRootPackageName() { + let toml = await async_read_file("gleam.toml", "utf-8"); + for (let line of toml.split("\n")) { + let matches = line.match(/\s*name\s*=\s*"([a-z][a-z0-9_]*)"/); // Match regexp in compiler-cli/src/new.rs in validate_name() + if (matches) return matches[1]; + } + throw new Error("Could not determine package name from gleam.toml"); +} + +export async function main() { + let state = reporting.new_state(); + + let packageName = await readRootPackageName(); + let dist = `../${packageName}/`; + + for await (let path of await gleamFiles("test")) { + let js_path = path.slice("test/".length).replace(".gleam", ".mjs"); + let module = await import(join_path(dist, js_path)); + for (let fnName of Object.keys(module)) { + if (!fnName.endsWith("_test")) continue; + try { + await module[fnName](); + state = reporting.test_passed(state); + } catch (error) { + let moduleName = js_path.slice(0, -4); + state = reporting.test_failed(state, moduleName, fnName, error); + } + } + } + + const status = reporting.finished(state); + exit(status); +} + +export function crash(message) { + throw new Error(message); +} + +function exit(code) { + if (globalThis.Deno) { + Deno.exit(code); + } else { + process.exit(code); + } +} + +async function read_dir(path) { + if (globalThis.Deno) { + let items = []; + for await (let item of Deno.readDir(path, { withFileTypes: true })) { + items.push(item.name); + } + return items; + } else { + let { readdir } = await import("node:fs/promises"); + return readdir(path); + } +} + +function join_path(a, b) { + if (a.endsWith("/")) return a + b; + return a + "/" + b; +} + +async function async_read_file(path) { + if (globalThis.Deno) { + return Deno.readTextFile(path); + } else { + let { readFile } = await import("node:fs/promises"); + let contents = await readFile(path); + return contents.toString(); + } +} diff --git a/build/packages/gleeunit/src/gleeunit_progress.erl b/build/packages/gleeunit/src/gleeunit_progress.erl new file mode 100644 index 0000000..e6576a5 --- /dev/null +++ b/build/packages/gleeunit/src/gleeunit_progress.erl @@ -0,0 +1,72 @@ +%% A formatter adapted from Sean Cribb's https://github.com/seancribbs/eunit_formatters + +-module(gleeunit_progress). +-define(NOTEST, true). + +%% eunit_listener callbacks +-export([ + init/1, handle_begin/3, handle_end/3, handle_cancel/3, terminate/2, + start/0, start/1 +]). + +-define(reporting, gleeunit@internal@reporting). + +start() -> + start([]). + +start(Options) -> + eunit_listener:start(?MODULE, Options). + +init(_Options) -> + ?reporting:new_state(). + +handle_begin(_test_or_group, _data, State) -> + State. + +handle_end(group, _data, State) -> + State; +handle_end(test, Data, State) -> + {AtomModule, AtomFunction, _Arity} = proplists:get_value(source, Data), + Module = erlang:atom_to_binary(AtomModule), + Function = erlang:atom_to_binary(AtomFunction), + + % EUnit swallows stdout, so print it to make debugging easier. + case proplists:get_value(output, Data) of + undefined -> ok; + <<>> -> ok; + Out -> gleam@io:print(Out) + end, + + case proplists:get_value(status, Data) of + ok -> + ?reporting:test_passed(State); + {skipped, _Reason} -> + ?reporting:test_skipped(State, Module, Function); + {error, {_, Exception, _Stack}} -> + ?reporting:test_failed(State, Module, Function, Exception) + end. + + +handle_cancel(_test_or_group, Data, State) -> + ?reporting:test_failed(State, <<"gleeunit">>, <<"main">>, Data). + +terminate({ok, _Data}, State) -> + ?reporting:finished(State), + ok; +terminate({error, Reason}, State) -> + ?reporting:finished(State), + io:fwrite(" +Eunit failed: + +~80p + +This is probably a bug in gleeunit. Please report it. +", [Reason]), + sync_end(error). + +sync_end(Result) -> + receive + {stop, Reference, ReplyTo} -> + ReplyTo ! {result, Reference, Result}, + ok + end. diff --git a/build/packages/packages.toml b/build/packages/packages.toml new file mode 100644 index 0000000..6098d78 --- /dev/null +++ b/build/packages/packages.toml @@ -0,0 +1,7 @@ +[packages] +gleam_stdlib = "0.65.0" +gleam_community_colour = "2.0.2" +gleam_json = "3.1.0" +gleeunit = "1.9.0" +paint = "1.0.0" +gleam_time = "1.6.0" diff --git a/build/packages/paint/LICENSE.txt b/build/packages/paint/LICENSE.txt new file mode 100644 index 0000000..40cce2a --- /dev/null +++ b/build/packages/paint/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Eli Adelhult + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/build/packages/paint/README.md b/build/packages/paint/README.md new file mode 100644 index 0000000..d762383 --- /dev/null +++ b/build/packages/paint/README.md @@ -0,0 +1,34 @@ +# Paint +**Make 2D drawings, animations, and games using Gleam and the HTML Canvas!** + +[![Package Version](https://img.shields.io/hexpm/v/paint)](https://hex.pm/packages/paint) +[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/paint/) + +Paint is a tiny embedded domain specific language (inspired by [Gloss](https://hackage.haskell.org/package/gloss)). +Make pictures out of basic shapes then style, transform, and combine them using the provided functions. + +![Frame 3(2)](https://github.com/user-attachments/assets/a8b83b58-990a-432a-9034-deebc4d210a6) + +```gleam +import paint as p +import paint/canvas + +fn main() { + let my_picture = p.combine([ + p.circle(30.0), + p.circle(20.0) |> p.fill(p.colour_rgb(0, 200, 200)), + p.rectangle(50.0, 30.0) |> p.rotate(p.angle_deg(30.0)), + p.text("Hello world", 10) |> p.translate_y(-35.0), + ]) + + canvas.display(fn(_: canvas.Config) { my_picture }, "#canvas_id") +} +``` + +**Want to learn more? Read the [docs](https://hexdocs.pm/paint) or browse the [visual examples](https://adelhult.github.io/paint/).** + +## Logo +Lucy is borrowed from the [Gleam branding page](https://gleam.run/branding/) and the brush is made by [Delapouite (game icons)](https://game-icons.net/1x1/delapouite/paint-brush.html). + +## Changelog +API additions and breaking changes can be found in the file `CHANGELOG.md`. diff --git a/build/packages/paint/gleam.toml b/build/packages/paint/gleam.toml new file mode 100644 index 0000000..d912cdd --- /dev/null +++ b/build/packages/paint/gleam.toml @@ -0,0 +1,23 @@ +name = "paint" +version = "1.0.0" +target = "javascript" + +# Fill out these fields if you intend to generate HTML documentation or publish +# your project to the Hex package manager. +# +description = "Make 2D drawings, animations, and games (HTML Canvas)" +licences = ["MIT"] +repository = { type = "github", user = "adelhult", repo = "paint" } +links = [ + { title = "Visual examples", href = "https://adelhult.github.io/paint/" }, +] +dev-dependencies = { gleeunit = ">= 1.6.1 and < 2.0.0" } +# +# For a full reference of all the available options, you can have a look at +# https://gleam.run/writing-gleam/gleam-toml/. + + +[dependencies] +gleam_stdlib = ">= 0.58.0 and < 2.0.0" +gleam_community_colour = ">= 2.0.0 and < 3.0.0" +gleam_json = ">= 3.0.2 and < 4.0.0" diff --git a/build/packages/paint/src/impl_canvas_bindings.mjs b/build/packages/paint/src/impl_canvas_bindings.mjs new file mode 100644 index 0000000..5fed16a --- /dev/null +++ b/build/packages/paint/src/impl_canvas_bindings.mjs @@ -0,0 +1,271 @@ +import { Ok, Error } from "./gleam.mjs"; + +class PaintCanvas extends HTMLElement { + // Open an issue if you are in need of any other attributes :) + static observedAttributes = ["width", "height", "style", "picture"]; + + constructor() { + super(); + // Create a canvas + this.canvas = document.createElement("canvas"); + const style = document.createElement("style"); + style.textContent = ` + :host { + display: inline-block; + } + `; + this.shadow = this.attachShadow({ mode: "open" }); + this.shadow.appendChild(style); + this.shadow.appendChild(this.canvas); + this.ctx = this.canvas.getContext("2d"); + } + + attributeChangedCallback(name, _oldValue, newValue) { + if (name === "picture") { + this.picture = newValue; + return; + } else if (name === "width") { + this.width = newValue; + } else if (name === "height") { + this.height = newValue; + } + } + + drawPicture() { + if (!this.pictureString) { + return; + } + + this.ctx.reset(); + const display = + window.PAINT_STATE[ + "display_on_rendering_context_with_default_drawing_state" + ]; + + display(this.pictureString, this.ctx); + } + + set picture(value) { + this.pictureString = value; + this.drawPicture(); + } + + set width(value) { + this.canvas.width = value; + this.drawPicture(); + } + + set height(value) { + this.canvas.height = value; + this.drawPicture(); + } + + get width() { + return this.canvas.width; + } + + get height() { + return this.canvas.height; + } +} + +export function define_web_component() { + window.customElements.define("paint-canvas", PaintCanvas); +} + +export function get_rendering_context(selector) { + // TODO: Handle the case where the canvas element is not found. + return document.querySelector(selector).getContext("2d"); +} + +export function setup_request_animation_frame(callback) { + window.requestAnimationFrame((time) => { + callback(time); + }); +} + +export function setup_input_handler(event_name, callback) { + window.addEventListener(event_name, callback); +} + +export function get_key_code(event) { + return event.keyCode; +} + +export function set_global(state, id) { + if (typeof window.PAINT_STATE == "undefined") { + window.PAINT_STATE = {}; + } + window.PAINT_STATE[id] = state; +} + +export function get_global(id) { + if (!window.PAINT_STATE) { + return new Error(undefined); + } + if (!(id in window.PAINT_STATE)) { + return new Error(undefined); + } + return new Ok(window.PAINT_STATE[id]); +} + +export function get_width(ctx) { + return ctx.canvas.clientWidth; +} + +export function get_height(ctx) { + return ctx.canvas.clientHeight; +} + +// Based on https://stackoverflow.com/questions/17130395/real-mouse-position-in-canvas +export function mouse_pos(ctx, event) { + // Calculate the scaling of the canvas vs its content + const rect = ctx.canvas.getBoundingClientRect(); + const scaleX = ctx.canvas.width / rect.width; + const scaleY = ctx.canvas.height / rect.height; + + return [ + (event.clientX - rect.left) * scaleX, + (event.clientY - rect.top) * scaleY, + ]; +} + +// if check_pressed is true, the function will return true if the button was pressed +// if check_pressed is false, the function will return true if the button was released +export function check_mouse_button( + event, + previous_event, + button_index, + check_pressed, +) { + let previous_buttons = previous_event?.buttons ?? 0; + let current_buttons = event.buttons; + + // ~001 && + // 011 + // ----- + // 010 found the newly pressed! + // + // 011 && + // ~001 + // ----- + // 010 found the newly released! + if (check_pressed) { + previous_buttons = ~previous_buttons; + } else { + current_buttons = ~current_buttons; + } + + let button = previous_buttons & current_buttons & (1 << button_index); + return !!button; +} + +export function reset(ctx) { + ctx.reset(); +} + +export function arc(ctx, radius, start, end, fill, stroke) { + ctx.beginPath(); + ctx.arc(0, 0, radius, start, end); + if (fill) { + ctx.fill(); + } + if (stroke) { + ctx.stroke(); + } +} + +export function polygon(ctx, points, closed, fill, stroke) { + ctx.beginPath(); + ctx.moveTo(0, 0); + let started = false; + for (const point of points) { + let x = point[0]; + let y = point[1]; + if (started) { + ctx.lineTo(x, y); + } else { + ctx.moveTo(x, y); + started = true; + } + } + + if (closed) { + ctx.closePath(); + } + + if (fill && closed) { + ctx.fill(); + } + + if (stroke) { + ctx.stroke(); + } +} + +export function text(ctx, text, style) { + ctx.font = style; + ctx.fillText(text, 0, 0); +} + +export function save(ctx) { + ctx.save(); +} + +export function restore(ctx) { + ctx.restore(); +} + +export function set_fill_colour(ctx, css_colour) { + ctx.fillStyle = css_colour; +} + +export function set_stroke_color(ctx, css_color) { + ctx.strokeStyle = css_color; +} + +export function set_line_width(ctx, width) { + ctx.lineWidth = width; +} + +export function translate(ctx, x, y) { + ctx.translate(x, y); +} + +export function scale(ctx, x, y) { + ctx.scale(x, y); +} + +export function rotate(ctx, radians) { + ctx.rotate(radians); +} + +export function reset_transform(ctx) { + ctx.resetTransform(); +} + +export function draw_image(ctx, image, width_px, height_px) { + ctx.drawImage(image, 0, 0, width_px, height_px); +} + +export function image_from_query(selector) { + return document.querySelector(selector); +} + +export function image_from_src(src) { + const image = new Image(); + image.src = src; + return image; +} + +export function on_image_load(image, callback) { + if (image.complete) { + callback(); + } else { + image.addEventListener("load", callback); + } +} + +export function set_image_smoothing_enabled(ctx, value) { + ctx.imageSmoothingEnabled = value; +} diff --git a/build/packages/paint/src/numbers_ffi.mjs b/build/packages/paint/src/numbers_ffi.mjs new file mode 100644 index 0000000..0e6e6d3 --- /dev/null +++ b/build/packages/paint/src/numbers_ffi.mjs @@ -0,0 +1,3 @@ +export function pi() { + return Math.PI; +} diff --git a/build/packages/paint/src/paint.gleam b/build/packages/paint/src/paint.gleam new file mode 100644 index 0000000..6729228 --- /dev/null +++ b/build/packages/paint/src/paint.gleam @@ -0,0 +1,201 @@ +//// This module contains the main `Picture` type as well as the +//// function you can use to construct, modify and combine pictures. + +import gleam/result +import gleam_community/colour +import paint/internal/types as internal_implementation + +/// A 2D picture. This is the type which this entire library revolves around. +/// +///> [!NOTE] +///> Unless you intend to author a new backend you should **consider this type opaque and never use any of its constructors**. +///> Instead, make use of the many utility functions defined in this module (`circle`, `combine`, `fill`, etc.) +pub type Picture = + internal_implementation.Picture + +/// A reference to an image (i.e. a texture), not to be confused with the `Picture` type. +/// To create an image, see the image functions in the `canvas` back-end. +pub type Image = + internal_implementation.Image + +/// An angle in clock-wise direction. +/// See: `angle_rad` and `angle_deg`. +pub type Angle = + internal_implementation.Angle + +/// Create an angle expressed in radians +pub fn angle_rad(radians: Float) -> Angle { + internal_implementation.Radians(radians) +} + +/// Create an angle expressed in degrees +pub fn angle_deg(degrees: Float) -> Angle { + internal_implementation.Radians(degrees *. pi() /. 180.0) +} + +/// A rexport of the Colour type from [gleam_community/colour](https://hexdocs.pm/gleam_community_colour/). +/// Paint also includes the functions `colour_hex` and `colour_rgb` to +/// easily construct Colours, but feel free to import the `gleam_community/colour` module +/// and use the many utility that are provided from there. +pub type Colour = + colour.Colour + +/// A utility around [colour.from_rgb_hex_string](https://hexdocs.pm/gleam_community_colour/gleam_community/colour.html#from_rgb_hex_string) +/// (from `gleam_community/colour`) that **panics** on an invalid hex code. +pub fn colour_hex(string: String) -> Colour { + result.lazy_unwrap(colour.from_rgb_hex_string(string), fn() { + panic as "Failed to parse hex code" + }) +} + +/// A utility around [colour.from_rgb255](https://hexdocs.pm/gleam_community_colour/gleam_community/colour.html#from_rgb255) +/// (from `gleam_community/colour`) that **panics** if the values are outside of the allowed range. +pub fn colour_rgb(red: Int, green: Int, blue: Int) -> Colour { + result.lazy_unwrap(colour.from_rgb255(red, green, blue), fn() { + panic as "The value was not inside of the valid range [0-255]" + }) +} + +pub type Vec2 = + #(Float, Float) + +/// A blank picture +pub fn blank() -> Picture { + internal_implementation.Blank +} + +/// A circle with some given radius +pub fn circle(radius: Float) -> Picture { + internal_implementation.Arc( + radius, + start: internal_implementation.Radians(0.0), + end: internal_implementation.Radians(2.0 *. pi()), + ) +} + +/// An arc with some radius going from some +/// starting angle to some other angle in clock-wise direction +pub fn arc(radius: Float, start: Angle, end: Angle) -> Picture { + internal_implementation.Arc(radius, start: start, end: end) +} + +/// A polygon consisting of a list of 2d points +pub fn polygon(points: List(#(Float, Float))) -> Picture { + internal_implementation.Polygon(points, True) +} + +/// Lines (same as a polygon but not a closed shape) +pub fn lines(points: List(#(Float, Float))) -> Picture { + internal_implementation.Polygon(points, False) +} + +/// A rectangle with some given width and height +pub fn rectangle(width: Float, height: Float) -> Picture { + polygon([#(0.0, 0.0), #(width, 0.0), #(width, height), #(0.0, height)]) +} + +/// A square +pub fn square(length: Float) -> Picture { + rectangle(length, length) +} + +/// Draw an image such as a PNG, JPEG or an SVG. See the `canvas` back-end for more details on how to load images. +pub fn image(image: Image, width_px width_px, height_px height_px) -> Picture { + // TODO: add a function that allows us to draw only part of an image, flip, and if we want smooth scaling or not + internal_implementation.ImageRef(image, width_px:, height_px:) +} + +/// Set image scaling to be smooth (this is the default behaviour) +pub fn image_scaling_smooth(picture: Picture) -> Picture { + internal_implementation.ImageScalingBehaviour( + picture, + internal_implementation.ScalingSmooth, + ) +} + +/// Disable smooth image scaling, suitable for pixel art. +pub fn image_scaling_pixelated(picture: Picture) -> Picture { + internal_implementation.ImageScalingBehaviour( + picture, + internal_implementation.ScalingPixelated, + ) +} + +/// Text with some given font size +pub fn text(text: String, px font_size: Int) -> Picture { + internal_implementation.Text( + text, + style: internal_implementation.FontProperties(font_size, "sans-serif"), + ) + // TODO: expose more styling options (font and text alignment) +} + +/// Translate a picture in horizontal and vertical direction +pub fn translate_xy(picture: Picture, x: Float, y: Float) -> Picture { + internal_implementation.Translate(picture, #(x, y)) +} + +/// Translate a picture in the horizontal direction +pub fn translate_x(picture: Picture, x: Float) -> Picture { + translate_xy(picture, x, 0.0) +} + +/// Translate a picture in the vertical direction +pub fn translate_y(picture: Picture, y: Float) -> Picture { + translate_xy(picture, 0.0, y) +} + +/// Scale the picture in the horizontal direction +pub fn scale_x(picture: Picture, factor: Float) -> Picture { + internal_implementation.Scale(picture, #(factor, 1.0)) +} + +/// Scale the picture in the vertical direction +pub fn scale_y(picture: Picture, factor: Float) -> Picture { + internal_implementation.Scale(picture, #(1.0, factor)) +} + +/// Scale the picture uniformly in horizontal and vertical direction +pub fn scale_uniform(picture: Picture, factor: Float) -> Picture { + internal_implementation.Scale(picture, #(factor, factor)) +} + +/// Rotate the picture in a clock-wise direction +pub fn rotate(picture: Picture, angle: Angle) -> Picture { + internal_implementation.Rotate(picture, angle) +} + +/// Fill a picture with some given colour, see `Colour`. +pub fn fill(picture: Picture, colour: Colour) -> Picture { + internal_implementation.Fill(picture, colour) +} + +/// Set a solid stroke with some given colour and width +pub fn stroke(picture: Picture, colour: Colour, width width: Float) -> Picture { + internal_implementation.Stroke( + picture, + internal_implementation.SolidStroke(colour, width), + ) +} + +/// Remove the stroke of the given picture +pub fn stroke_none(picture: Picture) -> Picture { + internal_implementation.Stroke(picture, internal_implementation.NoStroke) +} + +/// Concatenate two pictures +pub fn concat(picture: Picture, another_picture: Picture) -> Picture { + combine([picture, another_picture]) +} + +/// Combine multiple pictures into one +pub fn combine(pictures: List(Picture)) -> Picture { + internal_implementation.Combine(pictures) +} + +// Internal utility function to get Pi π +@external(erlang, "math", "pi") +@external(javascript, "./numbers_ffi.mjs", "pi") +fn pi() -> Float { + 3.1415926 +} diff --git a/build/packages/paint/src/paint/canvas.gleam b/build/packages/paint/src/paint/canvas.gleam new file mode 100644 index 0000000..061c102 --- /dev/null +++ b/build/packages/paint/src/paint/canvas.gleam @@ -0,0 +1,461 @@ +//// A HTML canvas backend that can be used for displaying +//// your `Picture`s. There are three different ways of doing so: +//// - `display` (provide a picture and a CSS selector to some canvas element) +//// - `define_web_component` (an alternative to `display` using custom web components, useful if you are using a web framework like Lustre) +//// - `interact` (allows you to make animations and interactive programs) + +import gleam/int +import gleam/option.{type Option, None, Some} +import gleam_community/colour +import paint.{translate_xy} +import paint/encode +import paint/event.{type Event} +import paint/internal/impl_canvas +import paint/internal/types.{ + type Image, type Picture, Arc, Blank, Combine, Fill, FontProperties, Image, + NoStroke, Polygon, Radians, Rotate, Scale, SolidStroke, Stroke, Text, + Translate, +} + +/// The configuration of the "canvas" +pub type Config { + Config(width: Float, height: Float) +} + +/// Create a reference to an image using a CSS query selector. For example: +/// ``` +/// fn kitten() { +/// canvas.image_from_query("#kitten") +/// } +/// // In the HTML file: +/// // +/// ``` +/// +/// > [!WARNING] +/// > **Important**: Make sure the image has loaded before trying to draw a pictures referencing it. +/// > You can do this using `canvas.wait_until_loaded` function. +pub fn image_from_query(selector: String) -> Image { + let id = "image-selector-" <> selector + case impl_canvas.get_global(id) { + // Re-use the cached image if we can + Ok(_) -> { + Nil + } + Error(Nil) -> { + let image = impl_canvas.image_from_query(selector) + impl_canvas.set_global(image, id) + } + } + Image(id) +} + +/// Create a reference to an image using a source path. +/// ``` +/// fn my_logo_image() { +/// canvas.image_from_src("./priv/static/logo.svg") +/// } +/// ``` +/// +/// > [!WARNING] +/// > **Important**: Make sure the image has loaded before trying to draw a pictures referencing it. +/// > You can do this using `canvas.wait_until_loaded` function. +pub fn image_from_src(src: String) -> Image { + let id = "image-src-" <> src + case impl_canvas.get_global(id) { + // Re-use the cached image if we can + Ok(_) -> { + Nil + } + Error(Nil) -> { + let image = impl_canvas.image_from_src(src) + impl_canvas.set_global(image, id) + } + } + Image(id) +} + +/// Wait until a list of images have all been loaded, for example: +/// ``` +/// fn lucy() { +/// canvas.image_from_query("#lucy") +/// } +/// +/// fn cat() { +/// canvas.image_from_src("./path/to/kitten.png") +/// } +/// +/// pub fn main() { +/// use <- canvas.wait_until_loaded([lucy(), kitten()]) +/// // It is now safe to draw Pictures containing the images lucy and kitten :) +/// } +/// ``` +pub fn wait_until_loaded(images: List(Image), on_loaded: fn() -> Nil) -> Nil { + case images { + [] -> on_loaded() + [image, ..rest] -> { + let Image(id:) = image + let assert Ok(js_image) = impl_canvas.get_global(id) + impl_canvas.on_image_load(js_image, fn() { + wait_until_loaded(rest, on_loaded) + }) + } + } +} + +/// Display a picture on a HTML canvas element +/// (specified by some [CSS Selector](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_selectors)). +/// ``` +/// canvas.display(fn (_: canvas.Config) { circle(50.0) }, "#mycanvas") +/// ``` +pub fn display(init: fn(Config) -> Picture, selector: String) { + let ctx = impl_canvas.get_rendering_context(selector) + impl_canvas.reset(ctx) + let picture = + init(Config(impl_canvas.get_width(ctx), impl_canvas.get_height(ctx))) + display_on_rendering_context(picture, ctx, default_drawing_state) +} + +/// Additional state used when drawing +/// (note that the fill and stroke color as well as the stroke width +/// is stored inside of the context) +type DrawingState { + DrawingState(fill: Bool, stroke: Bool) +} + +const default_drawing_state = DrawingState(fill: False, stroke: True) + +fn display_on_rendering_context( + picture: Picture, + ctx: impl_canvas.RenderingContext2D, + state: DrawingState, +) { + case picture { + Blank -> Nil + + Text(text, properties) -> { + let FontProperties(size_px, font_family) = properties + impl_canvas.save(ctx) + impl_canvas.text( + ctx, + text, + int.to_string(size_px) <> "px " <> font_family, + ) + impl_canvas.restore(ctx) + } + + Polygon(points, closed) -> { + impl_canvas.polygon(ctx, points, closed, state.fill, state.stroke) + } + + Arc(radius, start, end) -> { + let Radians(start_radians) = start + let Radians(end_radians) = end + impl_canvas.arc( + ctx, + radius, + start_radians, + end_radians, + state.fill, + state.stroke, + ) + } + + Fill(p, colour) -> { + impl_canvas.save(ctx) + impl_canvas.set_fill_colour(ctx, colour.to_css_rgba_string(colour)) + display_on_rendering_context(p, ctx, DrawingState(..state, fill: True)) + impl_canvas.restore(ctx) + } + + Stroke(p, stroke) -> { + case stroke { + NoStroke -> + display_on_rendering_context( + p, + ctx, + DrawingState(..state, stroke: False), + ) + SolidStroke(color, width) -> { + impl_canvas.save(ctx) + impl_canvas.set_stroke_color(ctx, colour.to_css_rgba_string(color)) + impl_canvas.set_line_width(ctx, width) + display_on_rendering_context( + p, + ctx, + DrawingState(..state, stroke: True), + ) + impl_canvas.restore(ctx) + } + } + } + + Translate(p, vec) -> { + let #(x, y) = vec + impl_canvas.save(ctx) + impl_canvas.translate(ctx, x, y) + display_on_rendering_context(p, ctx, state) + impl_canvas.restore(ctx) + } + + Scale(p, vec) -> { + let #(x, y) = vec + impl_canvas.save(ctx) + impl_canvas.scale(ctx, x, y) + display_on_rendering_context(p, ctx, state) + impl_canvas.restore(ctx) + } + + Rotate(p, angle) -> { + let Radians(rad) = angle + impl_canvas.save(ctx) + impl_canvas.rotate(ctx, rad) + display_on_rendering_context(p, ctx, state) + impl_canvas.restore(ctx) + } + + Combine(pictures) -> { + case pictures { + [] -> Nil + [p, ..ps] -> { + display_on_rendering_context(p, ctx, state) + display_on_rendering_context(Combine(ps), ctx, state) + } + } + } + types.ImageRef(Image(id:), width_px:, height_px:) -> { + // TODO: log an error if this fails? + let assert Ok(image) = impl_canvas.get_global(id) + impl_canvas.draw_image(ctx, image, width_px, height_px) + } + types.ImageScalingBehaviour(p, behaviour) -> { + impl_canvas.save(ctx) + impl_canvas.set_image_smoothing_enabled(ctx, case behaviour { + types.ScalingPixelated -> False + types.ScalingSmooth -> True + }) + display_on_rendering_context(p, ctx, state) + impl_canvas.restore(ctx) + } + } +} + +/// Animations, interactive applications and tiny games can be built using the +/// `interact` function. It roughly follows the [Elm architecture](https://guide.elm-lang.org/architecture/). +/// Here is a short example: +/// ``` +/// type State = +/// Int +/// +/// fn init(_: canvas.Config) -> State { +/// 0 +/// } +/// +/// fn update(state: State, event: event.Event) -> State { +/// case event { +/// event.Tick(_) -> state + 1 +/// _ -> state +/// } +/// } +/// +/// fn view(state: State) -> Picture { +/// paint.circle(int.to_float(state)) +/// } +/// +/// fn main() { +/// interact(init, update, view, "#mycanvas") +/// } +/// ``` +pub fn interact( + init: fn(Config) -> state, + update: fn(state, Event) -> state, + view: fn(state) -> Picture, + selector: String, +) { + let ctx = impl_canvas.get_rendering_context(selector) + let initial_state = + init(Config(impl_canvas.get_width(ctx), impl_canvas.get_height(ctx))) + + impl_canvas.set_global(initial_state, selector) + + // Handle keyboard input + let create_key_handler = fn(event_name, constructor) { + impl_canvas.setup_input_handler( + event_name, + fn(event: impl_canvas.KeyboardEvent) { + let key = parse_key_code(impl_canvas.get_key_code(event)) + case key { + Some(key) -> { + let assert Ok(old_state) = impl_canvas.get_global(selector) + let new_state = update(old_state, constructor(key)) + impl_canvas.set_global(new_state, selector) + } + None -> Nil + } + }, + ) + } + create_key_handler("keydown", event.KeyboardPressed) + create_key_handler("keyup", event.KeyboardRelased) + + // Handle mouse movement + impl_canvas.setup_input_handler( + "mousemove", + fn(event: impl_canvas.MouseEvent) { + let #(x, y) = impl_canvas.mouse_pos(ctx, event) + let assert Ok(old_state) = impl_canvas.get_global(selector) + let new_state = update(old_state, event.MouseMoved(x, y)) + impl_canvas.set_global(new_state, selector) + Nil + }, + ) + + // Handle mouse buttons + let create_mouse_button_handler = fn(event_name, constructor, check_pressed) { + impl_canvas.setup_input_handler( + event_name, + fn(event: impl_canvas.MouseEvent) { + // Read the previous state of the mouse + let previous_event_id = "PAINT_PREVIOUS_MOUSE_INPUT_FOR_" <> selector + let previous_event = impl_canvas.get_global(previous_event_id) + // Save this state + impl_canvas.set_global(event, previous_event_id) + + // A utility to check which buttons was just pressed/released + let check_button = fn(i) { + impl_canvas.check_mouse_button( + event, + previous_event, + i, + check_pressed, + ) + } + + let trigger_update = fn(button) { + let assert Ok(old_state) = impl_canvas.get_global(selector) + let new_state = update(old_state, constructor(button)) + impl_canvas.set_global(new_state, selector) + } + + // Note: it is rather rare, but it seems that multiple buttons + // can be pressed in the very same MouseEvent, so we may need to + // trigger multiple events at once. + case check_button(0) { + True -> trigger_update(event.MouseButtonLeft) + False -> Nil + } + case check_button(1) { + True -> trigger_update(event.MouseButtonRight) + False -> Nil + } + case check_button(2) { + True -> trigger_update(event.MouseButtonMiddle) + False -> Nil + } + + Nil + }, + ) + } + create_mouse_button_handler("mousedown", event.MousePressed, True) + create_mouse_button_handler("mouseup", event.MouseReleased, False) + + impl_canvas.setup_request_animation_frame(get_tick_func( + ctx, + view, + update, + selector, + )) +} + +fn parse_key_code(key_code: Int) -> Option(event.Key) { + case key_code { + 32 -> Some(event.KeySpace) + 37 -> Some(event.KeyLeftArrow) + 38 -> Some(event.KeyUpArrow) + 39 -> Some(event.KeyRightArrow) + 40 -> Some(event.KeyDownArrow) + 87 -> Some(event.KeyW) + 65 -> Some(event.KeyA) + 83 -> Some(event.KeyS) + 68 -> Some(event.KeyD) + 90 -> Some(event.KeyZ) + 88 -> Some(event.KeyX) + 67 -> Some(event.KeyC) + 18 -> Some(event.KeyEnter) + 27 -> Some(event.KeyEscape) + 8 -> Some(event.KeyBackspace) + _ -> None + } +} + +// Gleam does not have recursive let bindings, so I need +// to do this workaround... +fn get_tick_func(ctx, view, update, selector) { + fn(time) { + let assert Ok(current_state) = impl_canvas.get_global(selector) + + // Trigger a tick event before drawing + let new_state = update(current_state, event.Tick(time)) + impl_canvas.set_global(new_state, selector) + + // Create the picture + let picture = view(new_state) + + // Render the picture on the canvas + impl_canvas.reset(ctx) + display_on_rendering_context(picture, ctx, default_drawing_state) + impl_canvas.setup_request_animation_frame( + // call myself + get_tick_func(ctx, view, update, selector), + ) + } +} + +/// If you are using [Lustre](https://github.com/lustre-labs/lustre) or some other framework to build +/// your web application you may prefer to use the [web components](https://developer.mozilla.org/en-US/docs/Web/API/Web_components) API +/// and the `define_web_component` function. +/// ``` +/// // Call this function once to register a custom HTML element +/// canvas.define_web_component() +/// // You can then display your picture by setting the "picture" +/// // property or attribute on the element. +/// +/// // In Lustre it would look something like this: +/// fn canvas(picture: paint.Picture, attributes: List(attribute.Attribute(a))) { +/// element.element( +/// "paint-canvas", +/// [attribute.attribute("picture", encode.to_string(picture)), ..attributes], +/// [], +/// ) +///} +/// ``` +/// A more detailed example for using this API can be found in the `demos/with_lustre` directory. +pub fn define_web_component() -> Nil { + impl_canvas.define_web_component() + // somewhat of an ugly hack, but the setter for the web component will need to call this + // when the picture property changes. Therefore we + // bind this function to the window object so we can access it from the JS side of things. + // + // However, we should be careful of changing this. It is not part of the public API but it seems like + // [Tiramisu](https://hexdocs.pm/tiramisu/index.html) might makes use of it. + impl_canvas.set_global( + fn(encoded_picture, ctx) { + let assert Ok(picture) = encode.from_string(encoded_picture) + as "Invalid picture provided to web component" + display_on_rendering_context(picture, ctx, default_drawing_state) + }, + "display_on_rendering_context_with_default_drawing_state", + ) +} + +/// Utility to set the origin in the center of the canvas +pub fn center(picture: Picture) -> fn(Config) -> Picture { + fn(config) { + let Config(width, height) = config + picture |> translate_xy(width *. 0.5, height *. 0.5) + } +} diff --git a/build/packages/paint/src/paint/encode.gleam b/build/packages/paint/src/paint/encode.gleam new file mode 100644 index 0000000..96aea01 --- /dev/null +++ b/build/packages/paint/src/paint/encode.gleam @@ -0,0 +1,260 @@ +import gleam/dynamic/decode.{type Decoder} +import gleam/json.{type Json} +import gleam_community/colour +import paint.{type Picture} +import paint/internal/types.{ + type Angle, type FontProperties, type StrokeProperties, FontProperties, + NoStroke, Radians, SolidStroke, +} + +/// Serialize a `Picture` to a string. +/// +/// Note, serializing an `Image` texture will only store an ID referencing the image. This means that if you deserialize a Picture containing +/// references to images, you are responsible for making sure all images are loaded before drawing the picture. +/// More advanced APIs to support use cases such as these are planned for a future release. +/// +/// Also, if you wish to store the serialized data, remember that the library currently makes no stability guarantee that +/// the data can be deserialized by *future* versions of the library. +pub fn to_string(picture: Picture) -> String { + let version = "paint:unstable" + json.object([ + #("version", json.string(version)), + #("picture", picture_to_json(picture)), + ]) + |> json.to_string +} + +/// Attempt to deserialize a `Picture` +pub fn from_string(string: String) { + let decoder = { + use picture <- decode.field("picture", decode_picture()) + decode.success(picture) + } + json.parse(string, decoder) +} + +fn decode_angle() { + use radians <- decode.field("radians", decode.float) + decode.success(Radians(radians)) +} + +fn decode_picture() -> Decoder(Picture) { + use <- decode.recursive + use ty <- decode.field("type", decode.string) + + case ty { + "arc" -> { + use radius <- decode.field("radius", decode.float) + use start <- decode.field("start", decode_angle()) + use end <- decode.field("end", decode_angle()) + decode.success(types.Arc(radius, start:, end:)) + } + "blank" -> decode.success(types.Blank) + "combine" -> { + use pictures <- decode.field( + "pictures", + decode.list(of: decode_picture()), + ) + decode.success(types.Combine(pictures)) + } + "fill" -> { + use picture <- decode.field("picture", decode_picture()) + use colour <- decode.field("colour", colour.decoder()) + decode.success(types.Fill(picture, colour)) + } + "polygon" -> { + use points <- decode.field("points", decode.list(of: decode_vec2())) + use closed <- decode.field("closed", decode.bool) + decode.success(types.Polygon(points, closed)) + } + "rotate" -> { + use angle <- decode.field("angle", decode_angle()) + use picture <- decode.field("picture", decode_picture()) + decode.success(types.Rotate(picture, angle)) + } + "scale" -> { + use x <- decode.field("x", decode.float) + use y <- decode.field("y", decode.float) + use picture <- decode.field("picture", decode_picture()) + decode.success(types.Scale(picture, #(x, y))) + } + "stroke" -> { + use stroke <- decode.field("stroke", decode_stroke()) + use picture <- decode.field("picture", decode_picture()) + decode.success(types.Stroke(picture, stroke)) + } + "text" -> { + use text <- decode.field("text", decode.string) + use style <- decode.field("style", decode_font()) + decode.success(types.Text(text, style)) + } + "translate" -> { + use x <- decode.field("x", decode.float) + use y <- decode.field("y", decode.float) + use picture <- decode.field("picture", decode_picture()) + decode.success(types.Translate(picture, #(x, y))) + } + "image" -> { + use id <- decode.field("id", decode.string) + use width_px <- decode.field("width_px", decode.int) + use height_px <- decode.field("height_px", decode.int) + decode.success(types.ImageRef(types.Image(id:), width_px:, height_px:)) + } + "image_scaling_behaviour" -> { + use behaviour <- decode.field("behaviour", decode.string) + use picture <- decode.field("picture", decode_picture()) + case behaviour { + "smooth" -> + decode.success(types.ImageScalingBehaviour( + picture, + types.ScalingSmooth, + )) + "pixelated" -> + decode.success(types.ImageScalingBehaviour( + picture, + types.ScalingPixelated, + )) + _ -> decode.failure(types.Blank, "Picture") + } + } + _ -> decode.failure(types.Blank, "Picture") + } +} + +fn decode_font() -> Decoder(FontProperties) { + use size_px <- decode.field("sizePx", decode.int) + use font_family <- decode.field("fontFamily", decode.string) + decode.success(FontProperties(size_px:, font_family:)) +} + +fn decode_stroke() -> Decoder(StrokeProperties) { + use stroke_type <- decode.field("type", decode.string) + case stroke_type { + "noStroke" -> decode.success(NoStroke) + "solidStroke" -> { + use colour <- decode.field("colour", colour.decoder()) + use thickness <- decode.field("thickness", decode.float) + decode.success(SolidStroke(colour, thickness)) + } + _ -> decode.failure(NoStroke, "StrokeProperties") + } +} + +fn decode_vec2() -> Decoder(#(Float, Float)) { + use x <- decode.field("x", decode.float) + use y <- decode.field("y", decode.float) + decode.success(#(x, y)) +} + +fn picture_to_json(picture: Picture) -> Json { + case picture { + types.Arc(radius:, start:, end:) -> + json.object([ + #("type", json.string("arc")), + #("radius", json.float(radius)), + #("start", angle_to_json(start)), + #("end", angle_to_json(end)), + ]) + types.Blank -> json.object([#("type", json.string("blank"))]) + types.Combine(from) -> + json.object([ + #("type", json.string("combine")), + #("pictures", json.array(from:, of: picture_to_json)), + ]) + types.Fill(picture, colour) -> + json.object([ + #("type", json.string("fill")), + #("colour", colour.encode(colour)), + #("picture", picture_to_json(picture)), + ]) + types.Polygon(points, closed:) -> + json.object([ + #("type", json.string("polygon")), + #( + "points", + json.array(from: points, of: fn(point) { + let #(x, y) = point + json.object([#("x", json.float(x)), #("y", json.float(y))]) + }), + ), + #("closed", json.bool(closed)), + ]) + types.Rotate(picture, angle) -> + json.object([ + #("type", json.string("rotate")), + #("angle", angle_to_json(angle)), + #("picture", picture_to_json(picture)), + ]) + types.Scale(picture, #(x, y)) -> + json.object([ + #("type", json.string("scale")), + #("x", json.float(x)), + #("y", json.float(y)), + #("picture", picture_to_json(picture)), + ]) + types.Stroke(picture, stroke) -> + json.object([ + #("type", json.string("stroke")), + #("stroke", stroke_to_json(stroke)), + #("picture", picture_to_json(picture)), + ]) + types.Text(text:, style:) -> + json.object([ + #("type", json.string("text")), + #("text", json.string(text)), + #("style", font_to_json(style)), + ]) + types.Translate(picture, #(x, y)) -> + json.object([ + #("type", json.string("translate")), + #("x", json.float(x)), + #("y", json.float(y)), + #("picture", picture_to_json(picture)), + ]) + types.ImageRef(types.Image(id:), width_px:, height_px:) -> { + json.object([ + #("type", json.string("image")), + #("id", json.string(id)), + #("width_px", json.int(width_px)), + #("height_px", json.int(height_px)), + ]) + } + types.ImageScalingBehaviour(picture, behaviour) -> + json.object([ + #("type", json.string("image_scaling_behaviour")), + #( + "behaviour", + json.string(case behaviour { + types.ScalingPixelated -> "pixelated" + types.ScalingSmooth -> "smooth" + }), + ), + #("picture", picture_to_json(picture)), + ]) + } +} + +fn font_to_json(font: FontProperties) -> Json { + let FontProperties(size_px:, font_family:) = font + json.object([ + #("sizePx", json.int(size_px)), + #("fontFamily", json.string(font_family)), + ]) +} + +fn stroke_to_json(stroke: StrokeProperties) -> Json { + case stroke { + NoStroke -> json.object([#("type", json.string("noStroke"))]) + SolidStroke(colour, thickness) -> + json.object([ + #("type", json.string("solidStroke")), + #("colour", colour.encode(colour)), + #("thickness", json.float(thickness)), + ]) + } +} + +fn angle_to_json(angle: Angle) -> Json { + let Radians(rad) = angle + json.object([#("radians", json.float(rad))]) +} diff --git a/build/packages/paint/src/paint/event.gleam b/build/packages/paint/src/paint/event.gleam new file mode 100644 index 0000000..08298de --- /dev/null +++ b/build/packages/paint/src/paint/event.gleam @@ -0,0 +1,50 @@ +//// This module contains events that can be triggered when +//// building interactive applications. +//// +//// See `paint/canvas` and the `canvas.interact` function for a +//// practical example of how this is used. + +pub type Event { + /// Triggered before drawing. Contains the number of milliseconds elapsed. + Tick(Float) + /// Triggered when a key is pressed + KeyboardPressed(Key) + /// Triggered when a key is released + KeyboardRelased(Key) + /// Triggered when the mouse is moved. Contains + /// the `x` and `y` value for the mouse position. + MouseMoved(Float, Float) + /// Triggered when a mouse button is pressed + MousePressed(MouseButton) + /// Triggered when a mouse button is released. + /// + /// Note, on the web you might encounter issues where the + /// release event for the right mouse button is not triggered + /// because of the context menu. + MouseReleased(MouseButton) +} + +pub type Key { + KeyLeftArrow + KeyRightArrow + KeyUpArrow + KeyDownArrow + KeySpace + KeyW + KeyA + KeyS + KeyD + KeyZ + KeyX + KeyC + KeyEnter + KeyEscape + KeyBackspace +} + +pub type MouseButton { + MouseButtonLeft + MouseButtonRight + /// The scroll wheel button + MouseButtonMiddle +} diff --git a/build/packages/paint/src/paint/internal/impl_canvas.gleam b/build/packages/paint/src/paint/internal/impl_canvas.gleam new file mode 100644 index 0000000..2b94e9a --- /dev/null +++ b/build/packages/paint/src/paint/internal/impl_canvas.gleam @@ -0,0 +1,118 @@ +pub type RenderingContext2D + +@external(javascript, "./../../impl_canvas_bindings.mjs", "define_web_component") +pub fn define_web_component() -> Nil + +// TODO: forward the timestamp from the callback +@external(javascript, "./../../impl_canvas_bindings.mjs", "setup_request_animation_frame") +pub fn setup_request_animation_frame(callback: fn(Float) -> Nil) -> Nil + +@external(javascript, "./../../impl_canvas_bindings.mjs", "get_rendering_context") +pub fn get_rendering_context(selector: String) -> RenderingContext2D + +@external(javascript, "../../impl_canvas_bindings.mjs", "setup_input_handler") +pub fn setup_input_handler(event: String, callback: fn(event) -> Nil) -> Nil + +pub type KeyboardEvent + +@external(javascript, "./../../impl_canvas_bindings.mjs", "get_key_code") +pub fn get_key_code(event: KeyboardEvent) -> Int + +pub type MouseEvent + +@external(javascript, "./../../impl_canvas_bindings.mjs", "mouse_pos") +pub fn mouse_pos(ctx: RenderingContext2D, event: MouseEvent) -> #(Float, Float) + +@external(javascript, "./../../impl_canvas_bindings.mjs", "check_mouse_button") +pub fn check_mouse_button( + event: MouseEvent, + previous_event: Result(MouseEvent, Nil), + button_index: Int, + check_pressed check_pressed: Bool, +) -> Bool + +@external(javascript, "../../impl_canvas_bindings.mjs", "get_width") +pub fn get_width(ctx: RenderingContext2D) -> Float + +@external(javascript, "../../impl_canvas_bindings.mjs", "get_height") +pub fn get_height(ctx: RenderingContext2D) -> Float + +@external(javascript, "../../impl_canvas_bindings.mjs", "set_global") +pub fn set_global(state: state, id: String) -> Nil + +@external(javascript, "../../impl_canvas_bindings.mjs", "get_global") +pub fn get_global(id: String) -> Result(state, Nil) + +@external(javascript, "../../impl_canvas_bindings.mjs", "reset") +pub fn reset(ctx: RenderingContext2D) -> Nil + +@external(javascript, "../../impl_canvas_bindings.mjs", "save") +pub fn save(ctx: RenderingContext2D) -> Nil + +@external(javascript, "../../impl_canvas_bindings.mjs", "restore") +pub fn restore(ctx: RenderingContext2D) -> Nil + +@external(javascript, "../../impl_canvas_bindings.mjs", "translate") +pub fn translate(ctx: RenderingContext2D, x: Float, y: Float) -> Nil + +@external(javascript, "../../impl_canvas_bindings.mjs", "scale") +pub fn scale(ctx: RenderingContext2D, x: Float, y: Float) -> Nil + +@external(javascript, "../../impl_canvas_bindings.mjs", "rotate") +pub fn rotate(ctx: RenderingContext2D, radians: Float) -> Nil + +@external(javascript, "../../impl_canvas_bindings.mjs", "reset_transform") +pub fn reset_transform(ctx: RenderingContext2D) -> Nil + +@external(javascript, "../../impl_canvas_bindings.mjs", "set_fill_colour") +pub fn set_fill_colour(ctx: RenderingContext2D, css_colour: String) -> Nil + +@external(javascript, "../../impl_canvas_bindings.mjs", "set_stroke_color") +pub fn set_stroke_color(ctx: RenderingContext2D, css_color: String) -> Nil + +@external(javascript, "../../impl_canvas_bindings.mjs", "set_line_width") +pub fn set_line_width(ctx: RenderingContext2D, width: Float) -> Nil + +@external(javascript, "../../impl_canvas_bindings.mjs", "set_image_smoothing_enabled") +pub fn set_image_smoothing_enabled(ctx: RenderingContext2D, value: Bool) -> Nil + +@external(javascript, "../../impl_canvas_bindings.mjs", "arc") +pub fn arc( + ctx: RenderingContext2D, + radius: Float, + start: Float, + end: Float, + fill: Bool, + stroke: Bool, +) -> Nil + +@external(javascript, "../../impl_canvas_bindings.mjs", "polygon") +pub fn polygon( + ctx: RenderingContext2D, + points: List(#(Float, Float)), + closed: Bool, + fill: Bool, + stroke: Bool, +) -> Nil + +@external(javascript, "../../impl_canvas_bindings.mjs", "text") +pub fn text(ctx: RenderingContext2D, text: String, style: String) -> Nil + +pub type JsImage + +@external(javascript, "../../impl_canvas_bindings.mjs", "draw_image") +pub fn draw_image( + ctx: RenderingContext2D, + image: JsImage, + width_px: Int, + height_px: Int, +) -> Nil + +@external(javascript, "../../impl_canvas_bindings.mjs", "image_from_query") +pub fn image_from_query(selector: String) -> JsImage + +@external(javascript, "../../impl_canvas_bindings.mjs", "image_from_src") +pub fn image_from_src(src: String) -> JsImage + +@external(javascript, "../../impl_canvas_bindings.mjs", "on_image_load") +pub fn on_image_load(image: JsImage, callback: fn() -> Nil) -> Nil diff --git a/build/packages/paint/src/paint/internal/types.gleam b/build/packages/paint/src/paint/internal/types.gleam new file mode 100644 index 0000000..4cd6b5c --- /dev/null +++ b/build/packages/paint/src/paint/internal/types.gleam @@ -0,0 +1,48 @@ +import gleam_community/colour.{type Colour} + +pub type Picture { + // Shapes + Blank + Polygon(List(Vec2), closed: Bool) + Arc(radius: Float, start: Angle, end: Angle) + Text(text: String, style: FontProperties) + ImageRef(Image, width_px: Int, height_px: Int) + // Styling + // TODO: font + Fill(Picture, Colour) + Stroke(Picture, StrokeProperties) + ImageScalingBehaviour(Picture, ImageScalingBehaviour) + // Transform + Translate(Picture, Vec2) + Scale(Picture, Vec2) + Rotate(Picture, Angle) + // Combine + Combine(List(Picture)) +} + +// The ID for an image +// Invariant: the image object is assumed to already be created and stored somewhere (like the PAINT_STATE for the canvas backend) +pub type Image { + Image(id: String) +} + +pub type ImageScalingBehaviour { + ScalingSmooth + ScalingPixelated +} + +pub type StrokeProperties { + NoStroke + SolidStroke(Colour, Float) +} + +pub type FontProperties { + FontProperties(size_px: Int, font_family: String) +} + +pub type Angle { + Radians(Float) +} + +pub type Vec2 = + #(Float, Float) diff --git a/dev_server.py b/dev_server.py new file mode 100644 index 0000000..e87b2ab --- /dev/null +++ b/dev_server.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python3 +import os +from livereload import Server + +server = Server() + +server.watch('build/dev/javascript/stellar_prune/game.mjs') +server.watch('index.html') + +server.serve(port=3000, host='0.0.0.0', root='.') diff --git a/gleam.toml b/gleam.toml new file mode 100644 index 0000000..78d0eda --- /dev/null +++ b/gleam.toml @@ -0,0 +1,22 @@ +name = "stellar_prune" +version = "1.0.0" +target = "javascript" + +# Fill out these fields if you intend to generate HTML documentation or publish +# your project to the Hex package manager. +# +# description = "" +# licences = ["Apache-2.0"] +# repository = { type = "github", user = "", repo = "" } +# links = [{ title = "Website", href = "" }] +# +# For a full reference of all the available options, you can have a look at +# https://gleam.run/writing-gleam/gleam-toml/. + +[dependencies] +gleam_stdlib = ">= 0.44.0 and < 2.0.0" +paint = ">= 1.0.0 and < 2.0.0" +gleam_time = ">= 1.6.0 and < 2.0.0" + +[dev-dependencies] +gleeunit = ">= 1.0.0 and < 2.0.0" diff --git a/index.html b/index.html new file mode 100644 index 0000000..50f716e --- /dev/null +++ b/index.html @@ -0,0 +1,59 @@ + + + + + + Stellar prune + + + + + + + diff --git a/manifest.toml b/manifest.toml new file mode 100644 index 0000000..7890632 --- /dev/null +++ b/manifest.toml @@ -0,0 +1,17 @@ +# This file was generated by Gleam +# You typically do not need to edit this file + +packages = [ + { name = "gleam_community_colour", version = "2.0.2", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "E34DD2C896AC3792151EDA939DA435FF3B69922F33415ED3C4406C932FBE9634" }, + { name = "gleam_json", version = "3.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "44FDAA8847BE8FC48CA7A1C089706BD54BADCC4C45B237A992EDDF9F2CDB2836" }, + { name = "gleam_stdlib", version = "0.65.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "7C69C71D8C493AE11A5184828A77110EB05A7786EBF8B25B36A72F879C3EE107" }, + { name = "gleam_time", version = "1.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_time", source = "hex", outer_checksum = "0DF3834D20193F0A38D0EB21F0A78D48F2EC276C285969131B86DF8D4EF9E762" }, + { name = "gleeunit", version = "1.9.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "DA9553CE58B67924B3C631F96FE3370C49EB6D6DC6B384EC4862CC4AAA718F3C" }, + { name = "paint", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_json", "gleam_stdlib"], otp_app = "paint", source = "hex", outer_checksum = "7DD98FE1C1C27D5B4257BB4A08B6E22FD0DCA38CEEEDB2FEE9CB8676C4A7A8E8" }, +] + +[requirements] +gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" } +gleam_time = { version = ">= 1.6.0 and < 2.0.0" } +gleeunit = { version = ">= 1.0.0 and < 2.0.0" } +paint = { version = ">= 1.0.0 and < 2.0.0" } diff --git a/res/diamond.svg b/res/diamond.svg new file mode 100644 index 0000000..dbee7c4 --- /dev/null +++ b/res/diamond.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/res/grain.png b/res/grain.png new file mode 100644 index 0000000000000000000000000000000000000000..3a388dbbeeadecacbdc6cf9106f9cc98f058afc8 GIT binary patch literal 511323 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

WwK*K5kP^ZTgfpO|bOGydEdf4Mv|rWOzdK?(Z@B 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�_9dNKEfYBNP3fzu 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η# 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 + + diff --git a/res/lucy.svg b/res/lucy.svg new file mode 100644 index 0000000..81a3ce2 --- /dev/null +++ b/res/lucy.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/res/no-heart.svg b/res/no-heart.svg new file mode 100644 index 0000000..d5368f3 --- /dev/null +++ b/res/no-heart.svg @@ -0,0 +1,4 @@ + + + + diff --git a/res/rocket.svg b/res/rocket.svg new file mode 100644 index 0000000..237f0f9 --- /dev/null +++ b/res/rocket.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..e5c2005 --- /dev/null +++ b/shell.nix @@ -0,0 +1,11 @@ +{ pkgs ? import {} }: + +pkgs.mkShell { + buildInputs = with pkgs; [ + gleam + erlang + watchexec + python3 + python3Packages.livereload + ]; +} diff --git a/src/browser_ffi.mjs b/src/browser_ffi.mjs new file mode 100644 index 0000000..ef62a4c --- /dev/null +++ b/src/browser_ffi.mjs @@ -0,0 +1,40 @@ +let mouseX = 0; +let mouseY = 0; +let mousePressed = false; + +if (typeof window !== 'undefined') { + const canvas = document.getElementById('canvas'); + + window.addEventListener('mousemove', (e) => { + if (canvas) { + const rect = canvas.getBoundingClientRect(); + const scaleX = canvas.width / rect.width; + const scaleY = canvas.height / rect.height; + mouseX = (e.clientX - rect.left) * scaleX; + mouseY = (e.clientY - rect.top) * scaleY; + } else { + mouseX = e.clientX; + mouseY = e.clientY; + } + }); + + window.addEventListener('mousedown', () => { + mousePressed = true; + }); + + window.addEventListener('mouseup', () => { + mousePressed = false; + }); +} + +export function request_animation_frame(callback) { + requestAnimationFrame(() => callback()); +} + +export function get_mouse_position() { + return { x: mouseX, y: mouseY }; +} + +export function is_mouse_pressed() { + return mousePressed; +} diff --git a/src/game.gleam b/src/game.gleam new file mode 100644 index 0000000..bece8d6 --- /dev/null +++ b/src/game.gleam @@ -0,0 +1,263 @@ +import gleam/list +import hud.{CrossHair, Health, Hud, Score} +import paint +import physics.{type Vec2, Gravity, Physics, Vec2} +import rendering +import target.{type Target, Diamond, Lucy, Rocket} +import types.{ + type GameState, type PlayingState, GameOver, MainMenu, Playing, PlayingState, +} + +pub type TargetClickEffect { + TargetClickEffect(score_change: Int, health_change: Int) +} + +pub type TargetFilterResult { + TargetFilterResult(remaining_targets: List(Target), health_loss: Int) +} + +pub type SpawnResult { + SpawnResult(targets: List(Target), spawn_timer: Float) +} + +@external(javascript, "./browser_ffi.mjs", "request_animation_frame") +pub fn request_animation_frame(callback: fn() -> Nil) -> Nil + +@external(javascript, "./browser_ffi.mjs", "get_mouse_position") +fn get_mouse_position() -> Vec2 + +@external(javascript, "./browser_ffi.mjs", "is_mouse_pressed") +pub fn is_mouse_pressed() -> Bool + +pub fn main() { + game_loop(MainMenu) +} + +fn init_playing_state() -> PlayingState { + PlayingState( + targets: [], + physics: Physics(gravity: Gravity(acceleration: Vec2(0.0, 0.08))), + hud: Hud( + score: Score( + position: Vec2(x: 1050.0, y: 70.0), + colorable: target.Colorable(color: paint.colour_hex("#ffffff")), + score: 0, + ), + cross_hair: CrossHair( + position: Vec2(0.0, 0.0), + colorable: target.Colorable(color: paint.colour_hex("#ffffff")), + size: 5.0, + ), + health: Health( + position: Vec2(x: 30.0, y: 30.0), + size: 120.0, + amount: 3, + max_health: 3, + ), + ), + mouse: Vec2(0.0, 0.0), + spawn_timer: 0.0, + ) +} + +fn get_target_click_effects(kind: target.TargetKind) -> TargetClickEffect { + case kind { + Lucy -> TargetClickEffect(score_change: 1, health_change: 0) + Diamond(points_value) -> + TargetClickEffect(score_change: points_value, health_change: 0) + Rocket -> TargetClickEffect(score_change: 0, health_change: -1) + } +} + +fn filter_out_of_bounds_targets( + targets: List(Target), +) -> TargetFilterResult { + list.fold( + targets, + TargetFilterResult(remaining_targets: [], health_loss: 0), + fn(acc, target) { + case target.position.y >. 900.0 { + True -> { + case target.kind { + Rocket -> acc + _ -> + TargetFilterResult( + remaining_targets: acc.remaining_targets, + health_loss: acc.health_loss + 1, + ) + } + } + False -> + TargetFilterResult( + remaining_targets: [target, ..acc.remaining_targets], + health_loss: acc.health_loss, + ) + } + }, + ) +} + +fn should_spawn_new_target( + spawn_timer: Float, + active_targets: List(Target), + score: Int, +) -> Bool { + let spawn_interval = 60.0 + let max_targets = 1 + score / 5 + spawn_timer >=. spawn_interval && list.length(active_targets) < max_targets +} + +fn handle_target_click( + playing_state: PlayingState, + clicked_target: Target, +) -> PlayingState { + let new_targets = + list.filter(playing_state.targets, fn(target) { target != clicked_target }) + + let effect = get_target_click_effects(clicked_target.kind) + + PlayingState( + ..playing_state, + targets: new_targets, + hud: Hud( + ..playing_state.hud, + score: Score( + ..playing_state.hud.score, + score: playing_state.hud.score.score + effect.score_change, + ), + health: Health( + ..playing_state.hud.health, + amount: playing_state.hud.health.amount + effect.health_change, + ), + ), + ) +} + +fn handle_playing_click(playing_state: PlayingState) -> PlayingState { + case find_clicked_target(playing_state.mouse, playing_state.targets) { + Ok(clicked_target) -> handle_target_click(playing_state, clicked_target) + Error(_) -> playing_state + } +} + +fn update_playing_state(playing_state: PlayingState) -> PlayingState { + let Physics(gravity) = playing_state.physics + + let updated_targets = + list.map(playing_state.targets, fn(target) { + target.update_target(target, gravity.acceleration) + }) + + let filter_result = filter_out_of_bounds_targets(updated_targets) + + let new_spawn_timer = playing_state.spawn_timer +. 1.0 + + let spawn_result = case + should_spawn_new_target( + new_spawn_timer, + filter_result.remaining_targets, + playing_state.hud.score.score, + ) + { + True -> + SpawnResult( + targets: [target.create_target(), ..filter_result.remaining_targets], + spawn_timer: 0.0, + ) + False -> + SpawnResult( + targets: filter_result.remaining_targets, + spawn_timer: new_spawn_timer, + ) + } + + let new_mouse = get_mouse_position() + + PlayingState( + ..playing_state, + targets: spawn_result.targets, + spawn_timer: spawn_result.spawn_timer, + hud: Hud( + ..playing_state.hud, + cross_hair: CrossHair(..playing_state.hud.cross_hair, position: new_mouse), + health: Health( + ..playing_state.hud.health, + amount: playing_state.hud.health.amount - filter_result.health_loss, + ), + ), + mouse: new_mouse, + ) +} + +fn update_state(state: GameState) -> GameState { + case state { + MainMenu -> MainMenu + Playing(playing_state, prev_mouse) -> + Playing(update_playing_state(playing_state), prev_mouse) + GameOver(score, prev_mouse) -> GameOver(score, prev_mouse) + } +} + +fn find_clicked_target( + mouse: Vec2, + targets: List(Target), +) -> Result(Target, Nil) { + list.find(targets, fn(target) { target.is_point_in_target(mouse, target) }) +} + +fn handle_main_menu_click(mouse_pressed: Bool) -> GameState { + case mouse_pressed { + True -> Playing(init_playing_state(), False) + False -> MainMenu + } +} + +fn handle_playing_state( + playing_state: PlayingState, + mouse_pressed: Bool, +) -> GameState { + let new_state = case mouse_pressed { + True -> handle_playing_click(playing_state) + False -> playing_state + } + + case new_state.hud.health.amount <= 0 { + True -> GameOver(new_state.hud.score.score, mouse_pressed) + False -> Playing(new_state, mouse_pressed) + } +} + +fn handle_game_over_click( + final_score: Int, + mouse_pressed: Bool, + previous_mouse_pressed: Bool, +) -> GameState { + let just_clicked = mouse_pressed && !previous_mouse_pressed + case just_clicked { + True -> Playing(init_playing_state(), False) + False -> GameOver(final_score, mouse_pressed) + } +} + +fn handle_events(state: GameState) -> GameState { + let current_mouse_pressed = is_mouse_pressed() + + case state { + MainMenu -> handle_main_menu_click(current_mouse_pressed) + Playing(playing_state, _) -> + handle_playing_state(playing_state, current_mouse_pressed) + GameOver(final_score, previous_mouse_pressed) -> + handle_game_over_click( + final_score, + current_mouse_pressed, + previous_mouse_pressed, + ) + } +} + +fn game_loop(state: GameState) { + rendering.render(state) + request_animation_frame(fn() { + handle_events(state) |> update_state() |> game_loop() + }) +} diff --git a/src/hud.gleam b/src/hud.gleam new file mode 100644 index 0000000..77bb4c6 --- /dev/null +++ b/src/hud.gleam @@ -0,0 +1,73 @@ +import gleam/float +import gleam/int +import paint +import paint/canvas +import physics.{type Vec2, Vec2} +import target.{type Colorable, Colorable} + +pub type Score { + Score(position: Vec2, colorable: Colorable, score: Int) +} + +pub type CrossHair { + CrossHair(position: Vec2, colorable: Colorable, size: Float) +} + +pub type Health { + Health(position: Vec2, size: Float, amount: Int, max_health: Int) +} + +pub type Hud { + Hud(score: Score, cross_hair: CrossHair, health: Health) +} + +pub fn score_to_picture(score: Score) -> paint.Picture { + let Score(position, Colorable(color), score) = score + paint.text("Score: " <> int.to_string(score), 32) + |> paint.translate_xy(position.x, position.y) + |> paint.fill(color) +} + +pub fn cross_hair_to_picture(cross_hair: CrossHair) -> paint.Picture { + let CrossHair(Vec2(x, y), Colorable(color), size) = cross_hair + paint.circle(size) + |> paint.stroke(color, 5.0) + |> paint.translate_xy(x, y) +} + +fn health_to_picture_helper( + health: Health, + acc: paint.Picture, + count: Int, +) -> paint.Picture { + case count < health.max_health { + True -> { + let heart_image = case count < health.amount { + True -> canvas.image_from_src("../res/heart.svg") + False -> canvas.image_from_src("../res/no-heart.svg") + } + + let size = float.round(health.size) + + health_to_picture_helper( + health, + paint.combine([ + acc, + paint.image(heart_image, size, size) + |> paint.image_scaling_pixelated() + |> paint.translate_xy( + int.to_float(count) *. { health.size +. 10.0 }, + 0.0, + ), + ]), + count + 1, + ) + } + False -> acc + } +} + +pub fn health_to_picture(health: Health) -> paint.Picture { + health_to_picture_helper(health, paint.blank(), 0) + |> paint.translate_xy(health.position.x, health.position.y) +} diff --git a/src/math/math.gleam b/src/math/math.gleam new file mode 100644 index 0000000..37c7a5f --- /dev/null +++ b/src/math/math.gleam @@ -0,0 +1,28 @@ +@external(javascript, "./math_ffi.mjs", "sin") +pub fn sin(x: Float) -> Float + +@external(javascript, "./math_ffi.mjs", "cos") +pub fn cos(x: Float) -> Float + +@external(javascript, "./math_ffi.mjs", "tan") +pub fn tan(x: Float) -> Float + +@external(javascript, "./math_ffi.mjs", "atan2") +pub fn atan2(y: Float, x: Float) -> Float + +@external(javascript, "./math_ffi.mjs", "floor") +pub fn floor(x: Float) -> Float + +@external(javascript, "./math_ffi.mjs", "ceil") +pub fn ceil(x: Float) -> Float + +@external(javascript, "./math_ffi.mjs", "round") +pub fn round(x: Float) -> Float + +@external(javascript, "./math_ffi.mjs", "random") +pub fn random() -> Float + +@external(javascript, "./math_ffi.mjs", "pi") +pub fn pi() -> Float { + 3.1415926 +} diff --git a/src/math/math_ffi.mjs b/src/math/math_ffi.mjs new file mode 100644 index 0000000..8736bc2 --- /dev/null +++ b/src/math/math_ffi.mjs @@ -0,0 +1,35 @@ +export function sin(x) { + return Math.sin(x); +} + +export function cos(x) { + return Math.cos(x); +} + +export function tan(x) { + return Math.tan(x); +} + +export function atan2(y, x) { + return Math.atan2(y, x); +} + +export function floor(x) { + return Math.floor(x); +} + +export function ceil(x) { + return Math.ceil(x); +} + +export function round(x) { + return Math.round(x); +} + +export function random() { + return Math.random(); +} + +export function pi() { + return Math.PI; +} diff --git a/src/physics.gleam b/src/physics.gleam new file mode 100644 index 0000000..d3de569 --- /dev/null +++ b/src/physics.gleam @@ -0,0 +1,24 @@ +pub type Vec2 { + Vec2(x: Float, y: Float) +} + +pub type RigidBody { + RigidBody( + velocity: Vec2, + acceleration: Vec2, + rotation: Float, + rotation_velocity: Float, + ) +} + +pub type Gravity { + Gravity(acceleration: Vec2) +} + +pub type Physics { + Physics(gravity: Gravity) +} + +pub fn add_vec2(v1: Vec2, v2: Vec2) -> Vec2 { + Vec2(v1.x +. v2.x, v1.y +. v2.y) +} diff --git a/src/rendering.gleam b/src/rendering.gleam new file mode 100644 index 0000000..c372fe7 --- /dev/null +++ b/src/rendering.gleam @@ -0,0 +1,107 @@ +import gleam/int +import gleam/list +import hud +import paint +import paint/canvas +import target +import types.{type GameState, type PlayingState, GameOver, MainMenu, Playing} + +fn draw_main_menu() -> paint.Picture { + let lucy_image = canvas.image_from_src("../res/lucy.svg") + let diamond_image = canvas.image_from_src("../res/diamond.svg") + let rocket_image = canvas.image_from_src("../res/rocket.svg") + + paint.combine([ + paint.text("Stellar prune", 72) + |> paint.translate_xy(300.0, 100.0) + |> paint.fill(paint.colour_hex("#ffffff")) + |> paint.image_scaling_pixelated(), + paint.text("Click/swipe the targets to score points!", 28) + |> paint.translate_xy(320.0, 200.0) + |> paint.fill(paint.colour_hex("#aaaaaa")) + |> paint.image_scaling_pixelated(), + paint.text("Don't let them escape or you'll lose health!", 28) + |> paint.translate_xy(320.0, 240.0) + |> paint.fill(paint.colour_hex("#aaaaaa")) + |> paint.image_scaling_pixelated(), + paint.image(lucy_image, 80, 80) + |> paint.image_scaling_pixelated() + |> paint.translate_xy(320.0, 320.0), + paint.text("Lucy - Click for 1 point.", 24) + |> paint.translate_xy(420.0, 370.0) + |> paint.fill(paint.colour_hex("#ffffff")) + |> paint.image_scaling_pixelated(), + paint.image(diamond_image, 80, 80) + |> paint.image_scaling_pixelated() + |> paint.translate_xy(320.0, 420.0), + paint.text("Diamond - Click for 3 points!", 24) + |> paint.translate_xy(420.0, 460.0) + |> paint.fill(paint.colour_hex("#ffffff")) + |> paint.image_scaling_pixelated(), + paint.image(rocket_image, 80, 80) + |> paint.image_scaling_pixelated() + |> paint.translate_xy(320.0, 520.0), + paint.text("Rocket - AVOID! Loses 1 health", 24) + |> paint.translate_xy(420.0, 560.0) + |> paint.fill(paint.colour_hex("#ff6666")) + |> paint.image_scaling_pixelated(), + paint.text("Click to play!", 48) + |> paint.translate_xy(420.0, 680.0) + |> paint.fill(paint.colour_hex("#ffaff3")) + |> paint.image_scaling_pixelated(), + ]) +} + +fn draw_game_over(final_score: Int) -> paint.Picture { + paint.combine([ + paint.text("Well done!", 128) + |> paint.translate_xy(300.0, 175.0) + |> paint.fill(paint.colour_hex("#ffaff3")) + |> paint.image_scaling_pixelated(), + paint.text("Final Score: ", 48) + |> paint.translate_xy(440.0, 400.0) + |> paint.fill(paint.colour_hex("#ffffff")) + |> paint.image_scaling_pixelated(), + paint.text(int.to_string(final_score), 48) + |> paint.translate_xy(710.0, 400.0) + |> paint.fill(paint.colour_hex("#ffaff3")) + |> paint.image_scaling_pixelated(), + paint.text("Click to play again!", 32) + |> paint.translate_xy(455.0, 600.0) + |> paint.fill(paint.colour_hex("#ffaff3")) + |> paint.image_scaling_pixelated(), + ]) +} + +fn draw_playing(playing_state: PlayingState) -> paint.Picture { + let target_pictures = + list.map(playing_state.targets, fn(target) { + let target.Target(position, rigid_body, paintable, _kind) = target + target.paintable_to_picture(paintable) + |> paint.rotate(paint.angle_deg(rigid_body.rotation)) + |> paint.translate_xy(position.x, position.y) + }) + + let hud_without_crosshair = + paint.combine([ + hud.health_to_picture(playing_state.hud.health), + hud.score_to_picture(playing_state.hud.score), + ]) + + let crosshair = hud.cross_hair_to_picture(playing_state.hud.cross_hair) + + paint.combine([ + hud_without_crosshair, + ..list.append(target_pictures, [crosshair]) + ]) +} + +pub fn render(state: GameState) { + let picture = case state { + MainMenu -> draw_main_menu() + Playing(playing_state, _) -> draw_playing(playing_state) + GameOver(final_score, _) -> draw_game_over(final_score) + } + + canvas.display(fn(_: canvas.Config) { picture }, "#canvas") +} diff --git a/src/target.gleam b/src/target.gleam new file mode 100644 index 0000000..6b4def3 --- /dev/null +++ b/src/target.gleam @@ -0,0 +1,126 @@ +import gleam/int +import math/math +import paint +import paint/canvas +import physics.{type RigidBody, type Vec2, RigidBody, Vec2, add_vec2} + +pub type Colorable { + Colorable(color: paint.Colour) +} + +pub type Size { + Size(width: Int, height: Int) +} + +pub type Paintable { + Square(colorable: Colorable, size: Float) + Sprite(image: paint.Image, size: Size) +} + +pub type TargetKind { + Lucy + Diamond(points_value: Int) + Rocket +} + +pub type Target { + Target( + position: Vec2, + rigid_body: RigidBody, + paintable: Paintable, + kind: TargetKind, + ) +} + +pub fn create_target() -> Target { + let vx = math.random() *. 6.0 -. 3.0 + let vy = math.random() *. -4.0 -. 8.0 + let rotation_vel = math.random() *. 2.0 -. 1.0 + let start_x = math.random() *. 600.0 +. 200.0 + + let random_type = math.random() + let #(sprite_path, target_kind) = case random_type { + r if r <. 0.7 -> #("../res/lucy.svg", Lucy) + r if r <. 0.85 -> #("../res/diamond.svg", Diamond(points_value: 3)) + _ -> #("../res/rocket.svg", Rocket) + } + + Target( + position: Vec2(x: start_x, y: 900.0), + rigid_body: RigidBody( + velocity: Vec2(vx, vy), + acceleration: Vec2(0.0, 0.0), + rotation: 0.0, + rotation_velocity: rotation_vel, + ), + paintable: Sprite( + image: canvas.image_from_src(sprite_path), + size: Size(120, 120), + ), + kind: target_kind, + ) +} + +pub fn update_target(target: Target, gravity: Vec2) -> Target { + let RigidBody(velocity, acceleration, rotation, rotation_velocity) = + target.rigid_body + let total_acceleration = add_vec2(acceleration, gravity) + let new_velocity = add_vec2(velocity, total_acceleration) + let new_position = add_vec2(target.position, new_velocity) + + let new_rigid_body = + RigidBody( + new_velocity, + acceleration, + rotation +. rotation_velocity, + rotation_velocity, + ) + + Target(..target, rigid_body: new_rigid_body, position: new_position) +} + +pub fn is_point_in_target(point: Vec2, target: Target) -> Bool { + let dx = point.x -. target.position.x + let dy = point.y -. target.position.y + + let angle_rad = target.rigid_body.rotation *. math.pi() /. 180.0 + let cos_angle = math.cos(angle_rad) + let sin_angle = math.sin(angle_rad) + + let local_x = dx *. cos_angle +. dy *. sin_angle + let local_y = dy *. cos_angle -. dx *. sin_angle + + case target.paintable { + Square(_, size) -> { + let half_size = size /. 2.0 + local_x >=. 0.0 -. half_size + && local_x <=. half_size + && local_y >=. 0.0 -. half_size + && local_y <=. half_size + } + Sprite(_, size) -> { + let half_width = int.to_float(size.width) /. 2.0 + let half_height = int.to_float(size.height) /. 2.0 + local_x >=. 0.0 -. half_width + && local_x <=. half_width + && local_y >=. 0.0 -. half_height + && local_y <=. half_height + } + } +} + +pub fn paintable_to_picture(paintable: Paintable) -> paint.Picture { + case paintable { + Square(colorable, size) -> { + paint.square(size) |> paint.fill(colorable.color) + } + Sprite(image, size) -> { + paint.image(image, size.width, size.height) + |> paint.image_scaling_pixelated() + |> paint.translate_xy( + 0.0 -. int.to_float(size.width) /. 2.0, + 0.0 -. int.to_float(size.height) /. 2.0, + ) + } + } +} diff --git a/src/types.gleam b/src/types.gleam new file mode 100644 index 0000000..d5e4292 --- /dev/null +++ b/src/types.gleam @@ -0,0 +1,19 @@ +import hud.{type Hud} +import physics.{type Physics, type Vec2} +import target.{type Target} + +pub type PlayingState { + PlayingState( + targets: List(Target), + physics: Physics, + hud: Hud, + mouse: Vec2, + spawn_timer: Float, + ) +} + +pub type GameState { + MainMenu + Playing(state: PlayingState, previous_mouse_pressed: Bool) + GameOver(final_score: Int, previous_mouse_pressed: Bool) +}

LaI<4p_mljUZIcNUO{qwy~ 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 z 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%0AVKFJ+e1x)2MQbL-h%D_@CXswB>%;KK~fBpnC85 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 new Date(year, month, day); +export const Date$isDate = (value) => value instanceof Date; +export const Date$Date$year = (value) => value.year; +export const Date$Date$0 = (value) => value.year; +export const Date$Date$month = (value) => value.month; +export const Date$Date$1 = (value) => value.month; +export const Date$Date$day = (value) => value.day; +export const Date$Date$2 = (value) => value.day; + +export class TimeOfDay extends $CustomType { + constructor(hours, minutes, seconds, nanoseconds) { + super(); + this.hours = hours; + this.minutes = minutes; + this.seconds = seconds; + this.nanoseconds = nanoseconds; + } +} +export const TimeOfDay$TimeOfDay = (hours, minutes, seconds, nanoseconds) => + new TimeOfDay(hours, minutes, seconds, nanoseconds); +export const TimeOfDay$isTimeOfDay = (value) => value instanceof TimeOfDay; +export const TimeOfDay$TimeOfDay$hours = (value) => value.hours; +export const TimeOfDay$TimeOfDay$0 = (value) => value.hours; +export const TimeOfDay$TimeOfDay$minutes = (value) => value.minutes; +export const TimeOfDay$TimeOfDay$1 = (value) => value.minutes; +export const TimeOfDay$TimeOfDay$seconds = (value) => value.seconds; +export const TimeOfDay$TimeOfDay$2 = (value) => value.seconds; +export const TimeOfDay$TimeOfDay$nanoseconds = (value) => value.nanoseconds; +export const TimeOfDay$TimeOfDay$3 = (value) => value.nanoseconds; + +export class January extends $CustomType {} +export const Month$January = () => new January(); +export const Month$isJanuary = (value) => value instanceof January; + +export class February extends $CustomType {} +export const Month$February = () => new February(); +export const Month$isFebruary = (value) => value instanceof February; + +export class March extends $CustomType {} +export const Month$March = () => new March(); +export const Month$isMarch = (value) => value instanceof March; + +export class April extends $CustomType {} +export const Month$April = () => new April(); +export const Month$isApril = (value) => value instanceof April; + +export class May extends $CustomType {} +export const Month$May = () => new May(); +export const Month$isMay = (value) => value instanceof May; + +export class June extends $CustomType {} +export const Month$June = () => new June(); +export const Month$isJune = (value) => value instanceof June; + +export class July extends $CustomType {} +export const Month$July = () => new July(); +export const Month$isJuly = (value) => value instanceof July; + +export class August extends $CustomType {} +export const Month$August = () => new August(); +export const Month$isAugust = (value) => value instanceof August; + +export class September extends $CustomType {} +export const Month$September = () => new September(); +export const Month$isSeptember = (value) => value instanceof September; + +export class October extends $CustomType {} +export const Month$October = () => new October(); +export const Month$isOctober = (value) => value instanceof October; + +export class November extends $CustomType {} +export const Month$November = () => new November(); +export const Month$isNovember = (value) => value instanceof November; + +export class December extends $CustomType {} +export const Month$December = () => new December(); +export const Month$isDecember = (value) => value instanceof December; + +/** + * Get the offset for the computer's currently configured time zone. + * + * Note this may not be the time zone that is correct to use for your user. + * For example, if you are making a web application that runs on a server you + * want _their_ computer's time zone, not yours. + * + * This is the _current local_ offset, not the current local time zone. This + * means that while it will result in the expected outcome for the current + * time, it may result in unexpected output if used with other timestamps. For + * example: a timestamp that would locally be during daylight savings time if + * is it not currently daylight savings time when this function is called. + */ +export function local_offset() { + return $duration.seconds(local_time_offset_seconds()); +} + +/** + * Returns the English name for a month. + * + * # Examples + * + * ```gleam + * month_to_string(April) + * // -> "April" + * ``` + */ +export function month_to_string(month) { + if (month instanceof January) { + return "January"; + } else if (month instanceof February) { + return "February"; + } else if (month instanceof March) { + return "March"; + } else if (month instanceof April) { + return "April"; + } else if (month instanceof May) { + return "May"; + } else if (month instanceof June) { + return "June"; + } else if (month instanceof July) { + return "July"; + } else if (month instanceof August) { + return "August"; + } else if (month instanceof September) { + return "September"; + } else if (month instanceof October) { + return "October"; + } else if (month instanceof November) { + return "November"; + } else { + return "December"; + } +} + +/** + * Returns the number for the month, where January is 1 and December is 12. + * + * # Examples + * + * ```gleam + * month_to_int(January) + * // -> 1 + * ``` + */ +export function month_to_int(month) { + if (month instanceof January) { + return 1; + } else if (month instanceof February) { + return 2; + } else if (month instanceof March) { + return 3; + } else if (month instanceof April) { + return 4; + } else if (month instanceof May) { + return 5; + } else if (month instanceof June) { + return 6; + } else if (month instanceof July) { + return 7; + } else if (month instanceof August) { + return 8; + } else if (month instanceof September) { + return 9; + } else if (month instanceof October) { + return 10; + } else if (month instanceof November) { + return 11; + } else { + return 12; + } +} + +/** + * Returns the month for a given number, where January is 1 and December is 12. + * + * # Examples + * + * ```gleam + * month_from_int(1) + * // -> Ok(January) + * ``` + */ +export function month_from_int(month) { + if (month === 1) { + return new Ok(new January()); + } else if (month === 2) { + return new Ok(new February()); + } else if (month === 3) { + return new Ok(new March()); + } else if (month === 4) { + return new Ok(new April()); + } else if (month === 5) { + return new Ok(new May()); + } else if (month === 6) { + return new Ok(new June()); + } else if (month === 7) { + return new Ok(new July()); + } else if (month === 8) { + return new Ok(new August()); + } else if (month === 9) { + return new Ok(new September()); + } else if (month === 10) { + return new Ok(new October()); + } else if (month === 11) { + return new Ok(new November()); + } else if (month === 12) { + return new Ok(new December()); + } else { + return new Error(undefined); + } +} + +/** + * Determines if a given year is a leap year. + * + * A leap year occurs every 4 years, except for years divisible by 100, + * unless they are also divisible by 400. + * + * # Examples + * + * ```gleam + * is_leap_year(2024) + * // -> True + * ``` + * + * ```gleam + * is_leap_year(2023) + * // -> False + * ``` + */ +export function is_leap_year(year) { + let $ = (year % 400) === 0; + if ($) { + return $; + } else { + let $1 = (year % 100) === 0; + if ($1) { + return false; + } else { + return (year % 4) === 0; + } + } +} + +/** + * Checks if a given date is valid. + * + * This function properly accounts for leap years when validating February days. + * A leap year occurs every 4 years, except for years divisible by 100, + * unless they are also divisible by 400. + * + * # Examples + * + * ```gleam + * is_valid_date(Date(2023, April, 15)) + * // -> True + * ``` + * + * ```gleam + * is_valid_date(Date(2023, April, 31)) + * // -> False + * ``` + * + * ```gleam + * is_valid_date(Date(2024, February, 29)) + * // -> True (2024 is a leap year) + * ``` + */ +export function is_valid_date(date) { + let year; + let month; + let day; + year = date.year; + month = date.month; + day = date.day; + let $ = day < 1; + if ($) { + return false; + } else { + if (month instanceof January) { + return day <= 31; + } else if (month instanceof February) { + let _block; + let $1 = is_leap_year(year); + if ($1) { + _block = 29; + } else { + _block = 28; + } + let max_february_days = _block; + return day <= max_february_days; + } else if (month instanceof March) { + return day <= 31; + } else if (month instanceof April) { + return day <= 30; + } else if (month instanceof May) { + return day <= 31; + } else if (month instanceof June) { + return day <= 30; + } else if (month instanceof July) { + return day <= 31; + } else if (month instanceof August) { + return day <= 31; + } else if (month instanceof September) { + return day <= 30; + } else if (month instanceof October) { + return day <= 31; + } else if (month instanceof November) { + return day <= 30; + } else { + return day <= 31; + } + } +} + +/** + * Checks if a time of day is valid. + * + * Validates that hours are 0-23, minutes are 0-59, seconds are 0-59, + * and nanoseconds are 0-999,999,999. + * + * # Examples + * + * ```gleam + * is_valid_time_of_day(TimeOfDay(12, 30, 45, 123456789)) + * // -> True + * ``` + */ +export function is_valid_time_of_day(time) { + let hours; + let minutes; + let seconds; + let nanoseconds; + hours = time.hours; + minutes = time.minutes; + seconds = time.seconds; + nanoseconds = time.nanoseconds; + return (((((((hours >= 0) && (hours <= 23)) && (minutes >= 0)) && (minutes <= 59)) && (seconds >= 0)) && (seconds <= 59)) && (nanoseconds >= 0)) && (nanoseconds <= 999_999_999); +} + +/** + * Naively compares two dates without any time zone information, returning an + * order. + * + * ## Correctness + * + * This function compares dates without any time zone information, only using + * the rules for the gregorian calendar. This is typically sufficient, but be + * aware that in reality some time zones will change their calendar date + * occasionally. This can result in days being skipped, out of order, or + * happening multiple times. + * + * If you need real-world correct time ordering then use the + * `gleam/time/timestamp` module instead. + */ +export function naive_date_compare(one, other) { + let _pipe = $int.compare(one.year, other.year); + let _pipe$1 = $order.lazy_break_tie( + _pipe, + () => { + return $int.compare(month_to_int(one.month), month_to_int(other.month)); + }, + ); + return $order.lazy_break_tie( + _pipe$1, + () => { return $int.compare(one.day, other.day); }, + ); +} + +/** + * The offset for the [Coordinated Universal Time (UTC)](https://en.wikipedia.org/wiki/Coordinated_Universal_Time) + * time zone. + * + * The utc zone has no time adjustments, it is always zero. It never observes + * daylight-saving time and it never shifts around based on political + * restructuring. + */ +export const utc_offset = $duration.empty; diff --git a/build/dev/javascript/gleam_time/gleam/time/duration.mjs b/build/dev/javascript/gleam_time/gleam/time/duration.mjs new file mode 100644 index 0000000..53657d0 --- /dev/null +++ b/build/dev/javascript/gleam_time/gleam/time/duration.mjs @@ -0,0 +1,382 @@ +import * as $bool from "../../../gleam_stdlib/gleam/bool.mjs"; +import * as $int from "../../../gleam_stdlib/gleam/int.mjs"; +import * as $order from "../../../gleam_stdlib/gleam/order.mjs"; +import * as $string from "../../../gleam_stdlib/gleam/string.mjs"; +import { CustomType as $CustomType, remainderInt, divideInt, isEqual } from "../../gleam.mjs"; + +class Duration extends $CustomType { + constructor(seconds, nanoseconds) { + super(); + this.seconds = seconds; + this.nanoseconds = nanoseconds; + } +} + +export class Nanosecond extends $CustomType {} +export const Unit$Nanosecond = () => new Nanosecond(); +export const Unit$isNanosecond = (value) => value instanceof Nanosecond; + +export class Microsecond extends $CustomType {} +export const Unit$Microsecond = () => new Microsecond(); +export const Unit$isMicrosecond = (value) => value instanceof Microsecond; + +export class Millisecond extends $CustomType {} +export const Unit$Millisecond = () => new Millisecond(); +export const Unit$isMillisecond = (value) => value instanceof Millisecond; + +export class Second extends $CustomType {} +export const Unit$Second = () => new Second(); +export const Unit$isSecond = (value) => value instanceof Second; + +export class Minute extends $CustomType {} +export const Unit$Minute = () => new Minute(); +export const Unit$isMinute = (value) => value instanceof Minute; + +export class Hour extends $CustomType {} +export const Unit$Hour = () => new Hour(); +export const Unit$isHour = (value) => value instanceof Hour; + +export class Day extends $CustomType {} +export const Unit$Day = () => new Day(); +export const Unit$isDay = (value) => value instanceof Day; + +export class Week extends $CustomType {} +export const Unit$Week = () => new Week(); +export const Unit$isWeek = (value) => value instanceof Week; + +export class Month extends $CustomType {} +export const Unit$Month = () => new Month(); +export const Unit$isMonth = (value) => value instanceof Month; + +export class Year extends $CustomType {} +export const Unit$Year = () => new Year(); +export const Unit$isYear = (value) => value instanceof Year; + +/** + * Ensure the duration is represented with `nanoseconds` being positive and + * less than 1 second. + * + * This function does not change the amount of time that the duratoin refers + * to, it only adjusts the values used to represent the time. + * + * @ignore + */ +function normalise(duration) { + let multiplier = 1_000_000_000; + let nanoseconds$1 = remainderInt(duration.nanoseconds, multiplier); + let overflow = duration.nanoseconds - nanoseconds$1; + let seconds$1 = duration.seconds + (divideInt(overflow, multiplier)); + let $ = nanoseconds$1 >= 0; + if ($) { + return new Duration(seconds$1, nanoseconds$1); + } else { + return new Duration(seconds$1 - 1, multiplier + nanoseconds$1); + } +} + +/** + * Convert a duration to a number of the largest number of a unit, serving as + * a rough description of the duration that a human can understand. + * + * The size used for each unit are described in the documentation for the + * `Unit` type. + * + * ```gleam + * seconds(125) + * |> approximate + * // -> #(2, Minute) + * ``` + * + * This function rounds _towards zero_. This means that if a duration is just + * short of 2 days then it will approximate to 1 day. + * + * ```gleam + * hours(47) + * |> approximate + * // -> #(1, Day) + * ``` + */ +export function approximate(duration) { + let s; + let ns; + s = duration.seconds; + ns = duration.nanoseconds; + let minute = 60; + let hour = minute * 60; + let day = hour * 24; + let week = day * 7; + let year = day * 365 + hour * 6; + let month = globalThis.Math.trunc(year / 12); + let microsecond = 1000; + let millisecond = microsecond * 1000; + let $ = undefined; + if (s < 0) { + let _block; + let _pipe = new Duration(- s, - ns); + let _pipe$1 = normalise(_pipe); + _block = approximate(_pipe$1); + let $1 = _block; + let amount; + let unit; + amount = $1[0]; + unit = $1[1]; + return [- amount, unit]; + } else if (s >= year) { + return [divideInt(s, year), new Year()]; + } else if (s >= month) { + return [divideInt(s, month), new Month()]; + } else if (s >= week) { + return [divideInt(s, week), new Week()]; + } else if (s >= day) { + return [divideInt(s, day), new Day()]; + } else if (s >= hour) { + return [divideInt(s, hour), new Hour()]; + } else if (s >= minute) { + return [divideInt(s, minute), new Minute()]; + } else if (s > 0) { + return [s, new Second()]; + } else if (ns >= millisecond) { + return [divideInt(ns, millisecond), new Millisecond()]; + } else if (ns >= microsecond) { + return [divideInt(ns, microsecond), new Microsecond()]; + } else { + return [ns, new Nanosecond()]; + } +} + +/** + * Compare one duration to another, indicating whether the first spans a + * larger amount of time (and so is greater) or smaller amount of time (and so + * is lesser) than the second. + * + * # Examples + * + * ```gleam + * compare(seconds(1), seconds(2)) + * // -> order.Lt + * ``` + * + * Whether a duration is negative or positive doesn't matter for comparing + * them, only the amount of time spanned matters. + * + * ```gleam + * compare(seconds(-2), seconds(1)) + * // -> order.Gt + * ``` + */ +export function compare(left, right) { + let parts = (x) => { + let $ = x.seconds >= 0; + if ($) { + return [x.seconds, x.nanoseconds]; + } else { + return [x.seconds * -1 - 1, 1_000_000_000 - x.nanoseconds]; + } + }; + let $ = parts(left); + let ls; + let lns; + ls = $[0]; + lns = $[1]; + let $1 = parts(right); + let rs; + let rns; + rs = $1[0]; + rns = $1[1]; + let _pipe = $int.compare(ls, rs); + return $order.break_tie(_pipe, $int.compare(lns, rns)); +} + +/** + * Calculate the difference between two durations. + * + * This is effectively substracting the first duration from the second. + * + * # Examples + * + * ```gleam + * difference(seconds(1), seconds(5)) + * // -> seconds(4) + * ``` + */ +export function difference(left, right) { + let _pipe = new Duration( + right.seconds - left.seconds, + right.nanoseconds - left.nanoseconds, + ); + return normalise(_pipe); +} + +/** + * Add two durations together. + * + * # Examples + * + * ```gleam + * add(seconds(1), seconds(5)) + * // -> seconds(6) + * ``` + */ +export function add(left, right) { + let _pipe = new Duration( + left.seconds + right.seconds, + left.nanoseconds + right.nanoseconds, + ); + return normalise(_pipe); +} + +function nanosecond_digits(loop$n, loop$position, loop$acc) { + while (true) { + let n = loop$n; + let position = loop$position; + let acc = loop$acc; + if (position === 9) { + return acc; + } else if ((acc === "") && ((remainderInt(n, 10)) === 0)) { + loop$n = globalThis.Math.trunc(n / 10); + loop$position = position + 1; + loop$acc = acc; + } else { + let acc$1 = $int.to_string(n % 10) + acc; + loop$n = globalThis.Math.trunc(n / 10); + loop$position = position + 1; + loop$acc = acc$1; + } + } +} + +/** + * Create a duration of a number of seconds. + */ +export function seconds(amount) { + return new Duration(amount, 0); +} + +/** + * Create a duration of a number of minutes. + */ +export function minutes(amount) { + return seconds(amount * 60); +} + +/** + * Create a duration of a number of hours. + */ +export function hours(amount) { + return seconds(amount * 60 * 60); +} + +/** + * Create a duration of a number of milliseconds. + */ +export function milliseconds(amount) { + let remainder = amount % 1000; + let overflow = amount - remainder; + let nanoseconds$1 = remainder * 1_000_000; + let seconds$1 = globalThis.Math.trunc(overflow / 1000); + let _pipe = new Duration(seconds$1, nanoseconds$1); + return normalise(_pipe); +} + +/** + * Create a duration of a number of nanoseconds. + * + * # JavaScript int limitations + * + * Remember that JavaScript can only perfectly represent ints between positive + * and negative 9,007,199,254,740,991! If you use a single call to this + * function to create durations larger than that number of nanoseconds then + * you will likely not get exactly the value you expect. Use `seconds` and + * `milliseconds` as much as possible for large durations. + */ +export function nanoseconds(amount) { + let _pipe = new Duration(0, amount); + return normalise(_pipe); +} + +/** + * Convert the duration to a number of seconds. + * + * There may be some small loss of precision due to `Duration` being + * nanosecond accurate and `Float` not being able to represent this. + */ +export function to_seconds(duration) { + let seconds$1 = $int.to_float(duration.seconds); + let nanoseconds$1 = $int.to_float(duration.nanoseconds); + return seconds$1 + (nanoseconds$1 / 1_000_000_000.0); +} + +/** + * Convert the duration to a number of seconds and nanoseconds. There is no + * loss of precision with this conversion on any target. + */ +export function to_seconds_and_nanoseconds(duration) { + return [duration.seconds, duration.nanoseconds]; +} + +export const empty = /* @__PURE__ */ new Duration(0, 0); + +/** + * Convert the duration to an [ISO8601][1] formatted duration string. + * + * The ISO8601 duration format is ambiguous without context due to months and + * years having different lengths, and because of leap seconds. This function + * encodes the duration as days, hours, and seconds without any leap seconds. + * Be sure to take this into account when using the duration strings. + * + * [1]: https://en.wikipedia.org/wiki/ISO_8601#Durations + */ +export function to_iso8601_string(duration) { + return $bool.guard( + isEqual(duration, empty), + "PT0S", + () => { + let split = (total, limit) => { + let amount = remainderInt(total, limit); + let remainder = divideInt((total - amount), limit); + return [amount, remainder]; + }; + let $ = split(duration.seconds, 60); + let seconds$1; + let rest; + seconds$1 = $[0]; + rest = $[1]; + let $1 = split(rest, 60); + let minutes$1; + let rest$1; + minutes$1 = $1[0]; + rest$1 = $1[1]; + let $2 = split(rest$1, 24); + let hours$1; + let rest$2; + hours$1 = $2[0]; + rest$2 = $2[1]; + let days = rest$2; + let add$1 = (out, value, unit) => { + if (value === 0) { + return out; + } else { + return (out + $int.to_string(value)) + unit; + } + }; + let _block; + let _pipe = "P"; + let _pipe$1 = add$1(_pipe, days, "D"); + let _pipe$2 = $string.append(_pipe$1, "T"); + let _pipe$3 = add$1(_pipe$2, hours$1, "H"); + _block = add$1(_pipe$3, minutes$1, "M"); + let output = _block; + let $3 = duration.nanoseconds; + if ($3 === 0) { + if (seconds$1 === 0) { + return output; + } else { + return (output + $int.to_string(seconds$1)) + "S"; + } + } else { + let f = nanosecond_digits(duration.nanoseconds, 0, ""); + return (((output + $int.to_string(seconds$1)) + ".") + f) + "S"; + } + }, + ); +} diff --git a/build/dev/javascript/gleam_time/gleam/time/timestamp.mjs b/build/dev/javascript/gleam_time/gleam/time/timestamp.mjs new file mode 100644 index 0000000..2f3ec9b --- /dev/null +++ b/build/dev/javascript/gleam_time/gleam/time/timestamp.mjs @@ -0,0 +1,1215 @@ +import * as $bit_array from "../../../gleam_stdlib/gleam/bit_array.mjs"; +import * as $float from "../../../gleam_stdlib/gleam/float.mjs"; +import * as $int from "../../../gleam_stdlib/gleam/int.mjs"; +import * as $list from "../../../gleam_stdlib/gleam/list.mjs"; +import * as $order from "../../../gleam_stdlib/gleam/order.mjs"; +import * as $result from "../../../gleam_stdlib/gleam/result.mjs"; +import * as $string from "../../../gleam_stdlib/gleam/string.mjs"; +import { + Ok, + Error, + toList, + Empty as $Empty, + prepend as listPrepend, + CustomType as $CustomType, + remainderInt, + divideFloat, + divideInt, + toBitArray, + bitArraySlice, +} from "../../gleam.mjs"; +import * as $calendar from "../../gleam/time/calendar.mjs"; +import * as $duration from "../../gleam/time/duration.mjs"; +import { system_time as get_system_time } from "../../gleam_time_ffi.mjs"; + +class Timestamp extends $CustomType { + constructor(seconds, nanoseconds) { + super(); + this.seconds = seconds; + this.nanoseconds = nanoseconds; + } +} + +/** + * Ensure the time is represented with `nanoseconds` being positive and less + * than 1 second. + * + * This function does not change the time that the timestamp refers to, it + * only adjusts the values used to represent the time. + * + * @ignore + */ +function normalise(timestamp) { + let multiplier = 1_000_000_000; + let nanoseconds = remainderInt(timestamp.nanoseconds, multiplier); + let overflow = timestamp.nanoseconds - nanoseconds; + let seconds = timestamp.seconds + (divideInt(overflow, multiplier)); + let $ = nanoseconds >= 0; + if ($) { + return new Timestamp(seconds, nanoseconds); + } else { + return new Timestamp(seconds - 1, multiplier + nanoseconds); + } +} + +/** + * Compare one timestamp to another, indicating whether the first is further + * into the future (greater) or further into the past (lesser) than the + * second. + * + * # Examples + * + * ```gleam + * compare(from_unix_seconds(1), from_unix_seconds(2)) + * // -> order.Lt + * ``` + */ +export function compare(left, right) { + return $order.break_tie( + $int.compare(left.seconds, right.seconds), + $int.compare(left.nanoseconds, right.nanoseconds), + ); +} + +/** + * Get the current system time. + * + * Note this time is not unique or monotonic, it could change at any time or + * even go backwards! The exact behaviour will depend on the runtime used. See + * the module documentation for more information. + * + * On Erlang this uses [`erlang:system_time/1`][1]. On JavaScript this uses + * [`Date.now`][2]. + * + * [1]: https://www.erlang.org/doc/apps/erts/erlang#system_time/1 + * [2]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now + */ +export function system_time() { + let $ = get_system_time(); + let seconds; + let nanoseconds; + seconds = $[0]; + nanoseconds = $[1]; + return normalise(new Timestamp(seconds, nanoseconds)); +} + +/** + * Calculate the difference between two timestamps. + * + * This is effectively substracting the first timestamp from the second. + * + * # Examples + * + * ```gleam + * difference(from_unix_seconds(1), from_unix_seconds(5)) + * // -> duration.seconds(4) + * ``` + */ +export function difference(left, right) { + let seconds = $duration.seconds(right.seconds - left.seconds); + let nanoseconds = $duration.nanoseconds(right.nanoseconds - left.nanoseconds); + return $duration.add(seconds, nanoseconds); +} + +/** + * Add a duration to a timestamp. + * + * # Examples + * + * ```gleam + * add(from_unix_seconds(1000), duration.seconds(5)) + * // -> from_unix_seconds(1005) + * ``` + */ +export function add(timestamp, duration) { + let $ = $duration.to_seconds_and_nanoseconds(duration); + let seconds; + let nanoseconds; + seconds = $[0]; + nanoseconds = $[1]; + let _pipe = new Timestamp( + timestamp.seconds + seconds, + timestamp.nanoseconds + nanoseconds, + ); + return normalise(_pipe); +} + +function pad_digit(digit, desired_length) { + let _pipe = $int.to_string(digit); + return $string.pad_start(_pipe, desired_length, "0"); +} + +function duration_to_minutes(duration) { + return $float.round($duration.to_seconds(duration) / 60.0); +} + +function modulo(n, m) { + let $ = $int.modulo(n, m); + if ($ instanceof Ok) { + let n$1 = $[0]; + return n$1; + } else { + return 0; + } +} + +function floored_div(numerator, denominator) { + let n = divideFloat($int.to_float(numerator), denominator); + return $float.round($float.floor(n)); +} + +function to_civil(minutes) { + let raw_day = floored_div(minutes, (60.0 * 24.0)) + 719_468; + let _block; + let $ = raw_day >= 0; + if ($) { + _block = globalThis.Math.trunc(raw_day / 146_097); + } else { + _block = globalThis.Math.trunc((raw_day - 146_096) / 146_097); + } + let era = _block; + let day_of_era = raw_day - era * 146_097; + let year_of_era = globalThis.Math.trunc( + (((day_of_era - (globalThis.Math.trunc(day_of_era / 1460))) + (globalThis.Math.trunc( + day_of_era / 36_524 + ))) - (globalThis.Math.trunc(day_of_era / 146_096))) / 365 + ); + let year = year_of_era + era * 400; + let day_of_year = day_of_era - ((365 * year_of_era + (globalThis.Math.trunc( + year_of_era / 4 + ))) - (globalThis.Math.trunc(year_of_era / 100))); + let mp = globalThis.Math.trunc((5 * day_of_year + 2) / 153); + let _block$1; + let $1 = mp < 10; + if ($1) { + _block$1 = mp + 3; + } else { + _block$1 = mp - 9; + } + let month = _block$1; + let day = (day_of_year - (globalThis.Math.trunc((153 * mp + 2) / 5))) + 1; + let _block$2; + let $2 = month <= 2; + if ($2) { + _block$2 = year + 1; + } else { + _block$2 = year; + } + let year$1 = _block$2; + return [year$1, month, day]; +} + +function to_calendar_from_offset(timestamp, offset) { + let total = timestamp.seconds + offset * 60; + let seconds = modulo(total, 60); + let total_minutes = floored_div(total, 60.0); + let minutes = globalThis.Math.trunc(modulo(total, 60 * 60) / 60); + let hours = divideInt(modulo(total, 24 * 60 * 60), 60 * 60); + let $ = to_civil(total_minutes); + let year; + let month; + let day; + year = $[0]; + month = $[1]; + day = $[2]; + return [year, month, day, hours, minutes, seconds]; +} + +/** + * Convert a `Timestamp` to calendar time, suitable for presenting to a human + * to read. + * + * If you want a machine to use the time value then you should not use this + * function and should instead keep it as a timestamp. See the documentation + * for the `gleam/time/calendar` module for more information. + * + * # Examples + * + * ```gleam + * timestamp.from_unix_seconds(0) + * |> timestamp.to_calendar(calendar.utc_offset) + * // -> #(Date(1970, January, 1), TimeOfDay(0, 0, 0, 0)) + * ``` + */ +export function to_calendar(timestamp, offset) { + let offset$1 = duration_to_minutes(offset); + let $ = to_calendar_from_offset(timestamp, offset$1); + let year; + let month; + let day; + let hours; + let minutes; + let seconds; + year = $[0]; + month = $[1]; + day = $[2]; + hours = $[3]; + minutes = $[4]; + seconds = $[5]; + let _block; + if (month === 1) { + _block = new $calendar.January(); + } else if (month === 2) { + _block = new $calendar.February(); + } else if (month === 3) { + _block = new $calendar.March(); + } else if (month === 4) { + _block = new $calendar.April(); + } else if (month === 5) { + _block = new $calendar.May(); + } else if (month === 6) { + _block = new $calendar.June(); + } else if (month === 7) { + _block = new $calendar.July(); + } else if (month === 8) { + _block = new $calendar.August(); + } else if (month === 9) { + _block = new $calendar.September(); + } else if (month === 10) { + _block = new $calendar.October(); + } else if (month === 11) { + _block = new $calendar.November(); + } else { + _block = new $calendar.December(); + } + let month$1 = _block; + let nanoseconds = timestamp.nanoseconds; + let date = new $calendar.Date(year, month$1, day); + let time = new $calendar.TimeOfDay(hours, minutes, seconds, nanoseconds); + return [date, time]; +} + +function do_remove_trailing_zeros(loop$reversed_digits) { + while (true) { + let reversed_digits = loop$reversed_digits; + if (reversed_digits instanceof $Empty) { + return reversed_digits; + } else { + let digit = reversed_digits.head; + if (digit === 0) { + let digits = reversed_digits.tail; + loop$reversed_digits = digits; + } else { + let reversed_digits$1 = reversed_digits; + return $list.reverse(reversed_digits$1); + } + } + } +} + +/** + * Given a list of digits, return new list with any trailing zeros removed. + * + * @ignore + */ +function remove_trailing_zeros(digits) { + let reversed_digits = $list.reverse(digits); + return do_remove_trailing_zeros(reversed_digits); +} + +function do_get_zero_padded_digits(loop$number, loop$digits, loop$count) { + while (true) { + let number = loop$number; + let digits = loop$digits; + let count = loop$count; + let number$1 = number; + if ((number$1 <= 0) && (count >= 9)) { + return digits; + } else { + let number$2 = number; + if (number$2 <= 0) { + loop$number = number$2; + loop$digits = listPrepend(0, digits); + loop$count = count + 1; + } else { + let number$3 = number; + let digit = number$3 % 10; + let number$4 = floored_div(number$3, 10.0); + loop$number = number$4; + loop$digits = listPrepend(digit, digits); + loop$count = count + 1; + } + } + } +} + +/** + * Returns the list of digits of `number`. If the number of digits is less + * than 9, the result is zero-padded at the front. + * + * @ignore + */ +function get_zero_padded_digits(number) { + return do_get_zero_padded_digits(number, toList([]), 0); +} + +/** + * Converts nanoseconds into a `String` representation of fractional seconds. + * + * Assumes that `nanoseconds < 1_000_000_000`, which will be true for any + * normalised timestamp. + * + * @ignore + */ +function show_second_fraction(nanoseconds) { + let $ = $int.compare(nanoseconds, 0); + if ($ instanceof $order.Lt) { + return ""; + } else if ($ instanceof $order.Eq) { + return ""; + } else { + let _block; + let _pipe = nanoseconds; + let _pipe$1 = get_zero_padded_digits(_pipe); + let _pipe$2 = remove_trailing_zeros(_pipe$1); + let _pipe$3 = $list.map(_pipe$2, $int.to_string); + _block = $string.join(_pipe$3, ""); + let second_fraction_part = _block; + return "." + second_fraction_part; + } +} + +/** + * Convert a timestamp to a RFC 3339 formatted time string, with an offset + * supplied as an additional argument. + * + * The output of this function is also ISO 8601 compatible so long as the + * offset not negative. Offsets have at-most minute precision, so an offset + * with higher precision will be rounded to the nearest minute. + * + * If you are making an API such as a HTTP JSON API you are encouraged to use + * Unix timestamps instead of this format or ISO 8601. Unix timestamps are a + * better choice as they don't contain offset information. Consider: + * + * - UTC offsets are not time zones. This does not and cannot tell us the time + * zone in which the date was recorded. So what are we supposed to do with + * this information? + * - Users typically want dates formatted according to their local time zone. + * What if the provided UTC offset is different from the current user's time + * zone? What are we supposed to do with it then? + * - Despite it being useless (or worse, a source of bugs), the UTC offset + * creates a larger payload to transfer. + * + * They also uses more memory than a unix timestamp. The way they are better + * than Unix timestamp is that it is easier for a human to read them, but + * this is a hinderance that tooling can remedy, and APIs are not primarily + * for humans. + * + * # Examples + * + * ```gleam + * timestamp.from_unix_seconds_and_nanoseconds(1000, 123_000_000) + * |> to_rfc3339(calendar.utc_offset) + * // -> "1970-01-01T00:16:40.123Z" + * ``` + * + * ```gleam + * timestamp.from_unix_seconds(1000) + * |> to_rfc3339(duration.seconds(3600)) + * // -> "1970-01-01T01:16:40+01:00" + * ``` + */ +export function to_rfc3339(timestamp, offset) { + let offset$1 = duration_to_minutes(offset); + let $ = to_calendar_from_offset(timestamp, offset$1); + let years; + let months; + let days; + let hours; + let minutes; + let seconds; + years = $[0]; + months = $[1]; + days = $[2]; + hours = $[3]; + minutes = $[4]; + seconds = $[5]; + let offset_minutes = modulo(offset$1, 60); + let offset_hours = $int.absolute_value(floored_div(offset$1, 60.0)); + let n2 = (_capture) => { return pad_digit(_capture, 2); }; + let n4 = (_capture) => { return pad_digit(_capture, 4); }; + let out = ""; + let out$1 = ((((out + n4(years)) + "-") + n2(months)) + "-") + n2(days); + let out$2 = out$1 + "T"; + let out$3 = ((((out$2 + n2(hours)) + ":") + n2(minutes)) + ":") + n2(seconds); + let out$4 = out$3 + show_second_fraction(timestamp.nanoseconds); + let $1 = $int.compare(offset$1, 0); + if ($1 instanceof $order.Lt) { + return (((out$4 + "-") + n2(offset_hours)) + ":") + n2(offset_minutes); + } else if ($1 instanceof $order.Eq) { + return out$4 + "Z"; + } else { + return (((out$4 + "+") + n2(offset_hours)) + ":") + n2(offset_minutes); + } +} + +function is_leap_year(year) { + return ((year % 4) === 0) && (((year % 100) !== 0) || ((year % 400) === 0)); +} + +function parse_sign(bytes) { + if (bytes.bitSize >= 8) { + if (bytes.byteAt(0) === 43) { + if ((bytes.bitSize - 8) % 8 === 0) { + let remaining_bytes = bitArraySlice(bytes, 8); + return new Ok(["+", remaining_bytes]); + } else { + return new Error(undefined); + } + } else if (bytes.byteAt(0) === 45 && (bytes.bitSize - 8) % 8 === 0) { + let remaining_bytes = bitArraySlice(bytes, 8); + return new Ok(["-", remaining_bytes]); + } else { + return new Error(undefined); + } + } else { + return new Error(undefined); + } +} + +/** + * Accept the given value from `bytes` and move past it if found. + * + * @ignore + */ +function accept_byte(bytes, value) { + if (bytes.bitSize >= 8 && (bytes.bitSize - 8) % 8 === 0) { + let byte = bytes.byteAt(0); + if (byte === value) { + let remaining_bytes = bitArraySlice(bytes, 8); + return new Ok(remaining_bytes); + } else { + return new Error(undefined); + } + } else { + return new Error(undefined); + } +} + +function accept_empty(bytes) { + if (bytes.bitSize === 0) { + return new Ok(undefined); + } else { + return new Error(undefined); + } +} + +/** + * Note: It is the callers responsibility to ensure the inputs are valid. + * + * See https://www.tondering.dk/claus/cal/julperiod.php#formula + * + * @ignore + */ +function julian_day_from_ymd(year, month, day) { + let adjustment = globalThis.Math.trunc((14 - month) / 12); + let adjusted_year = (year + 4800) - adjustment; + let adjusted_month = (month + 12 * adjustment) - 3; + return (((((day + (globalThis.Math.trunc((153 * adjusted_month + 2) / 5))) + 365 * adjusted_year) + (globalThis.Math.trunc( + adjusted_year / 4 + ))) - (globalThis.Math.trunc(adjusted_year / 100))) + (globalThis.Math.trunc( + adjusted_year / 400 + ))) - 32_045; +} + +/** + * Create a timestamp from a number of seconds since 00:00:00 UTC on 1 January + * 1970. + */ +export function from_unix_seconds(seconds) { + return new Timestamp(seconds, 0); +} + +/** + * Create a timestamp from a number of seconds and nanoseconds since 00:00:00 + * UTC on 1 January 1970. + * + * # JavaScript int limitations + * + * Remember that JavaScript can only perfectly represent ints between positive + * and negative 9,007,199,254,740,991! If you only use the nanosecond field + * then you will almost certainly not get the date value you want due to this + * loss of precision. Always use seconds primarily and then use nanoseconds + * for the final sub-second adjustment. + */ +export function from_unix_seconds_and_nanoseconds(seconds, nanoseconds) { + let _pipe = new Timestamp(seconds, nanoseconds); + return normalise(_pipe); +} + +/** + * Convert the timestamp to a number of seconds since 00:00:00 UTC on 1 + * January 1970. + * + * There may be some small loss of precision due to `Timestamp` being + * nanosecond accurate and `Float` not being able to represent this. + */ +export function to_unix_seconds(timestamp) { + let seconds = $int.to_float(timestamp.seconds); + let nanoseconds = $int.to_float(timestamp.nanoseconds); + return seconds + (nanoseconds / 1_000_000_000.0); +} + +/** + * Convert the timestamp to a number of seconds and nanoseconds since 00:00:00 + * UTC on 1 January 1970. There is no loss of precision with this conversion + * on any target. + */ +export function to_unix_seconds_and_nanoseconds(timestamp) { + return [timestamp.seconds, timestamp.nanoseconds]; +} + +const seconds_per_day = 86_400; + +const seconds_per_hour = 3600; + +const seconds_per_minute = 60; + +function offset_to_seconds(sign, hours, minutes) { + let abs_seconds = hours * seconds_per_hour + minutes * seconds_per_minute; + if (sign === "-") { + return - abs_seconds; + } else { + return abs_seconds; + } +} + +/** + * `julian_seconds_from_parts(year, month, day, hours, minutes, seconds)` + * returns the number of Julian + * seconds represented by the given arguments. + * + * Note: It is the callers responsibility to ensure the inputs are valid. + * + * See https://www.tondering.dk/claus/cal/julperiod.php#formula + * + * @ignore + */ +function julian_seconds_from_parts(year, month, day, hours, minutes, seconds) { + let julian_day_seconds = julian_day_from_ymd(year, month, day) * seconds_per_day; + return ((julian_day_seconds + hours * seconds_per_hour) + minutes * seconds_per_minute) + seconds; +} + +const nanoseconds_per_second = 1_000_000_000; + +/** + * The `:` character as a byte + * + * @ignore + */ +const byte_colon = 0x3A; + +/** + * The `-` character as a byte + * + * @ignore + */ +const byte_minus = 0x2D; + +/** + * The `0` character as a byte + * + * @ignore + */ +const byte_zero = 0x30; + +/** + * The `9` character as a byte + * + * @ignore + */ +const byte_nine = 0x39; + +function do_parse_second_fraction_as_nanoseconds( + loop$bytes, + loop$acc, + loop$power +) { + while (true) { + let bytes = loop$bytes; + let acc = loop$acc; + let power = loop$power; + let power$1 = globalThis.Math.trunc(power / 10); + if (bytes.bitSize >= 8 && (bytes.bitSize - 8) % 8 === 0) { + let byte = bytes.byteAt(0); + if (((0x30 <= byte) && (byte <= 0x39)) && (power$1 < 1)) { + let remaining_bytes = bitArraySlice(bytes, 8); + loop$bytes = remaining_bytes; + loop$acc = acc; + loop$power = power$1; + } else { + let byte$1 = bytes.byteAt(0); + if ((0x30 <= byte$1) && (byte$1 <= 0x39)) { + let remaining_bytes = bitArraySlice(bytes, 8); + let digit = byte$1 - 0x30; + loop$bytes = remaining_bytes; + loop$acc = acc + digit * power$1; + loop$power = power$1; + } else { + return new Ok([acc, bytes]); + } + } + } else { + return new Ok([acc, bytes]); + } + } +} + +function parse_second_fraction_as_nanoseconds(bytes) { + if (bytes.bitSize >= 8 && bytes.byteAt(0) === 46) { + if (bytes.bitSize >= 16 && (bytes.bitSize - 16) % 8 === 0) { + let byte = bytes.byteAt(1); + if ((0x30 <= byte) && (byte <= 0x39)) { + let remaining_bytes = bitArraySlice(bytes, 16); + return do_parse_second_fraction_as_nanoseconds( + toBitArray([byte, remaining_bytes]), + 0, + nanoseconds_per_second, + ); + } else if ((bytes.bitSize - 8) % 8 === 0) { + return new Error(undefined); + } else { + return new Ok([0, bytes]); + } + } else if ((bytes.bitSize - 8) % 8 === 0) { + return new Error(undefined); + } else { + return new Ok([0, bytes]); + } + } else { + return new Ok([0, bytes]); + } +} + +function do_parse_digits(loop$bytes, loop$count, loop$acc, loop$k) { + while (true) { + let bytes = loop$bytes; + let count = loop$count; + let acc = loop$acc; + let k = loop$k; + if (k >= count) { + return new Ok([acc, bytes]); + } else if (bytes.bitSize >= 8 && (bytes.bitSize - 8) % 8 === 0) { + let byte = bytes.byteAt(0); + if ((0x30 <= byte) && (byte <= 0x39)) { + let remaining_bytes = bitArraySlice(bytes, 8); + loop$bytes = remaining_bytes; + loop$count = count; + loop$acc = acc * 10 + (byte - 0x30); + loop$k = k + 1; + } else { + return new Error(undefined); + } + } else { + return new Error(undefined); + } + } +} + +/** + * Parse and return the given number of digits from the given bytes. + * + * @ignore + */ +function parse_digits(bytes, count) { + return do_parse_digits(bytes, count, 0, 0); +} + +function parse_year(bytes) { + return parse_digits(bytes, 4); +} + +function parse_month(bytes) { + return $result.try$( + parse_digits(bytes, 2), + (_use0) => { + let month; + let bytes$1; + month = _use0[0]; + bytes$1 = _use0[1]; + let $ = (1 <= month) && (month <= 12); + if ($) { + return new Ok([month, bytes$1]); + } else { + return new Error(undefined); + } + }, + ); +} + +function parse_day(bytes, year, month) { + return $result.try$( + parse_digits(bytes, 2), + (_use0) => { + let day; + let bytes$1; + day = _use0[0]; + bytes$1 = _use0[1]; + return $result.try$( + (() => { + if (month === 1) { + return new Ok(31); + } else if (month === 3) { + return new Ok(31); + } else if (month === 5) { + return new Ok(31); + } else if (month === 7) { + return new Ok(31); + } else if (month === 8) { + return new Ok(31); + } else if (month === 10) { + return new Ok(31); + } else if (month === 12) { + return new Ok(31); + } else if (month === 4) { + return new Ok(30); + } else if (month === 6) { + return new Ok(30); + } else if (month === 9) { + return new Ok(30); + } else if (month === 11) { + return new Ok(30); + } else if (month === 2) { + let $ = is_leap_year(year); + if ($) { + return new Ok(29); + } else { + return new Ok(28); + } + } else { + return new Error(undefined); + } + })(), + (max_day) => { + let $ = (1 <= day) && (day <= max_day); + if ($) { + return new Ok([day, bytes$1]); + } else { + return new Error(undefined); + } + }, + ); + }, + ); +} + +function parse_hours(bytes) { + return $result.try$( + parse_digits(bytes, 2), + (_use0) => { + let hours; + let bytes$1; + hours = _use0[0]; + bytes$1 = _use0[1]; + let $ = (0 <= hours) && (hours <= 23); + if ($) { + return new Ok([hours, bytes$1]); + } else { + return new Error(undefined); + } + }, + ); +} + +function parse_minutes(bytes) { + return $result.try$( + parse_digits(bytes, 2), + (_use0) => { + let minutes; + let bytes$1; + minutes = _use0[0]; + bytes$1 = _use0[1]; + let $ = (0 <= minutes) && (minutes <= 59); + if ($) { + return new Ok([minutes, bytes$1]); + } else { + return new Error(undefined); + } + }, + ); +} + +function parse_seconds(bytes) { + return $result.try$( + parse_digits(bytes, 2), + (_use0) => { + let seconds; + let bytes$1; + seconds = _use0[0]; + bytes$1 = _use0[1]; + let $ = (0 <= seconds) && (seconds <= 60); + if ($) { + return new Ok([seconds, bytes$1]); + } else { + return new Error(undefined); + } + }, + ); +} + +function parse_numeric_offset(bytes) { + return $result.try$( + parse_sign(bytes), + (_use0) => { + let sign; + let bytes$1; + sign = _use0[0]; + bytes$1 = _use0[1]; + return $result.try$( + parse_hours(bytes$1), + (_use0) => { + let hours; + let bytes$2; + hours = _use0[0]; + bytes$2 = _use0[1]; + return $result.try$( + accept_byte(bytes$2, byte_colon), + (bytes) => { + return $result.try$( + parse_minutes(bytes), + (_use0) => { + let minutes; + let bytes$1; + minutes = _use0[0]; + bytes$1 = _use0[1]; + let offset_seconds = offset_to_seconds(sign, hours, minutes); + return new Ok([offset_seconds, bytes$1]); + }, + ); + }, + ); + }, + ); + }, + ); +} + +function parse_offset(bytes) { + if (bytes.bitSize >= 8) { + if (bytes.byteAt(0) === 90) { + if ((bytes.bitSize - 8) % 8 === 0) { + let remaining_bytes = bitArraySlice(bytes, 8); + return new Ok([0, remaining_bytes]); + } else { + return parse_numeric_offset(bytes); + } + } else if (bytes.byteAt(0) === 122 && (bytes.bitSize - 8) % 8 === 0) { + let remaining_bytes = bitArraySlice(bytes, 8); + return new Ok([0, remaining_bytes]); + } else { + return parse_numeric_offset(bytes); + } + } else { + return parse_numeric_offset(bytes); + } +} + +/** + * The `t` character as a byte + * + * @ignore + */ +const byte_t_lowercase = 0x74; + +/** + * The `T` character as a byte + * + * @ignore + */ +const byte_t_uppercase = 0x54; + +/** + * The `T` character as a byte + * + * @ignore + */ +const byte_space = 0x20; + +function accept_date_time_separator(bytes) { + if (bytes.bitSize >= 8 && (bytes.bitSize - 8) % 8 === 0) { + let byte = bytes.byteAt(0); + if (((byte === 0x54) || (byte === 0x74)) || (byte === 0x20)) { + let remaining_bytes = bitArraySlice(bytes, 8); + return new Ok(remaining_bytes); + } else { + return new Error(undefined); + } + } else { + return new Error(undefined); + } +} + +/** + * The Julian seconds of the UNIX epoch (Julian day is 2_440_588) + * + * @ignore + */ +const julian_seconds_unix_epoch = 210_866_803_200; + +/** + * Note: The caller of this function must ensure that all inputs are valid. + * + * @ignore + */ +function from_date_time( + year, + month, + day, + hours, + minutes, + seconds, + second_fraction_as_nanoseconds, + offset_seconds +) { + let julian_seconds = julian_seconds_from_parts( + year, + month, + day, + hours, + minutes, + seconds, + ); + let julian_seconds_since_epoch = julian_seconds - julian_seconds_unix_epoch; + let _pipe = new Timestamp( + julian_seconds_since_epoch - offset_seconds, + second_fraction_as_nanoseconds, + ); + return normalise(_pipe); +} + +/** + * Create a `Timestamp` from a human-readable calendar time. + * + * # Examples + * + * ```gleam + * timestamp.from_calendar( + * date: calendar.Date(2024, calendar.December, 25), + * time: calendar.TimeOfDay(12, 30, 50, 0), + * offset: calendar.utc_offset, + * ) + * |> timestamp.to_rfc3339(calendar.utc_offset) + * // -> "2024-12-25T12:30:50Z" + * ``` + */ +export function from_calendar(date, time, offset) { + let _block; + let $ = date.month; + if ($ instanceof $calendar.January) { + _block = 1; + } else if ($ instanceof $calendar.February) { + _block = 2; + } else if ($ instanceof $calendar.March) { + _block = 3; + } else if ($ instanceof $calendar.April) { + _block = 4; + } else if ($ instanceof $calendar.May) { + _block = 5; + } else if ($ instanceof $calendar.June) { + _block = 6; + } else if ($ instanceof $calendar.July) { + _block = 7; + } else if ($ instanceof $calendar.August) { + _block = 8; + } else if ($ instanceof $calendar.September) { + _block = 9; + } else if ($ instanceof $calendar.October) { + _block = 10; + } else if ($ instanceof $calendar.November) { + _block = 11; + } else { + _block = 12; + } + let month = _block; + return from_date_time( + date.year, + month, + date.day, + time.hours, + time.minutes, + time.seconds, + time.nanoseconds, + $float.round($duration.to_seconds(offset)), + ); +} + +/** + * Parses an [RFC 3339 formatted time string][spec] into a `Timestamp`. + * + * [spec]: https://datatracker.ietf.org/doc/html/rfc3339#section-5.6 + * + * # Examples + * + * ```gleam + * let assert Ok(ts) = timestamp.parse_rfc3339("1970-01-01T00:00:01Z") + * timestamp.to_unix_seconds_and_nanoseconds(ts) + * // -> #(1, 0) + * ``` + * + * Parsing an invalid timestamp returns an error. + * + * ```gleam + * let assert Error(Nil) = timestamp.parse_rfc3339("1995-10-31") + * ``` + * + * ## Time zones + * + * It may at first seem that the RFC 3339 format includes timezone + * information, as it can specify an offset such as `Z` or `+3`, so why does + * this function not return calendar time with a time zone? There are multiple + * reasons: + * + * - RFC 3339's timestamp format is based on calendar time, but it is + * unambigous, so it can be converted into epoch time when being parsed. It + * is always better to internally use epoch time to represent unambiguous + * points in time, so we perform that conversion as a convenience and to + * ensure that programmers with less time experience don't accidentally use + * a less suitable time representation. + * + * - RFC 3339's contains _calendar time offset_ information, not time zone + * information. This is enough to convert it to an unambiguous timestamp, + * but it is not enough information to reliably work with calendar time. + * Without the time zone and the time zone database it's not possible to + * know what time period that offset is valid for, so it cannot be used + * without risk of bugs. + * + * ## Behaviour details + * + * - Follows the grammar specified in section 5.6 Internet Date/Time Format of + * RFC 3339 . + * - The `T` and `Z` characters may alternatively be lower case `t` or `z`, + * respectively. + * - Full dates and full times must be separated by `T` or `t`. A space is also + * permitted. + * - Leap seconds rules are not considered. That is, any timestamp may + * specify digts `00` - `60` for the seconds. + * - Any part of a fractional second that cannot be represented in the + * nanosecond precision is tructated. That is, for the time string, + * `"1970-01-01T00:00:00.1234567899Z"`, the fractional second `.1234567899` + * will be represented as `123_456_789` in the `Timestamp`. + */ +export function parse_rfc3339(input) { + let bytes = $bit_array.from_string(input); + return $result.try$( + parse_year(bytes), + (_use0) => { + let year; + let bytes$1; + year = _use0[0]; + bytes$1 = _use0[1]; + return $result.try$( + accept_byte(bytes$1, byte_minus), + (bytes) => { + return $result.try$( + parse_month(bytes), + (_use0) => { + let month; + let bytes$1; + month = _use0[0]; + bytes$1 = _use0[1]; + return $result.try$( + accept_byte(bytes$1, byte_minus), + (bytes) => { + return $result.try$( + parse_day(bytes, year, month), + (_use0) => { + let day; + let bytes$1; + day = _use0[0]; + bytes$1 = _use0[1]; + return $result.try$( + accept_date_time_separator(bytes$1), + (bytes) => { + return $result.try$( + parse_hours(bytes), + (_use0) => { + let hours; + let bytes$1; + hours = _use0[0]; + bytes$1 = _use0[1]; + return $result.try$( + accept_byte(bytes$1, byte_colon), + (bytes) => { + return $result.try$( + parse_minutes(bytes), + (_use0) => { + let minutes; + let bytes$1; + minutes = _use0[0]; + bytes$1 = _use0[1]; + return $result.try$( + accept_byte(bytes$1, byte_colon), + (bytes) => { + return $result.try$( + parse_seconds(bytes), + (_use0) => { + let seconds; + let bytes$1; + seconds = _use0[0]; + bytes$1 = _use0[1]; + return $result.try$( + parse_second_fraction_as_nanoseconds( + bytes$1, + ), + (_use0) => { + let second_fraction_as_nanoseconds; + let bytes$2; + second_fraction_as_nanoseconds = _use0[0]; + bytes$2 = _use0[1]; + return $result.try$( + parse_offset(bytes$2), + (_use0) => { + let offset_seconds; + let bytes$3; + offset_seconds = _use0[0]; + bytes$3 = _use0[1]; + return $result.try$( + accept_empty(bytes$3), + (_use0) => { + + return new Ok( + from_date_time( + year, + month, + day, + hours, + minutes, + seconds, + second_fraction_as_nanoseconds, + offset_seconds, + ), + ); + }, + ); + }, + ); + }, + ); + }, + ); + }, + ); + }, + ); + }, + ); + }, + ); + }, + ); + }, + ); + }, + ); + }, + ); + }, + ); + }, + ); +} + +/** + * The epoch of Unix time, which is 00:00:00 UTC on 1 January 1970. + */ +export const unix_epoch = /* @__PURE__ */ new Timestamp(0, 0); diff --git a/build/dev/javascript/gleam_time/gleam@time@calendar.erl b/build/dev/javascript/gleam_time/gleam@time@calendar.erl new file mode 100644 index 0000000..e295d09 --- /dev/null +++ b/build/dev/javascript/gleam_time/gleam@time@calendar.erl @@ -0,0 +1,468 @@ +-module(gleam@time@calendar). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/time/calendar.gleam"). +-export([local_offset/0, month_to_string/1, month_to_int/1, month_from_int/1, is_leap_year/1, is_valid_date/1, is_valid_time_of_day/1, naive_date_compare/2]). +-export_type([date/0, time_of_day/0, month/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC( + " This module is for working with the Gregorian calendar, established by\n" + " Pope Gregory XIII in 1582!\n" + "\n" + " ## When should you use this module?\n" + "\n" + " > **tldr:** You probably want to use [`gleam/time/timestamp`](./timestamp.html)\n" + " > instead!\n" + "\n" + " Calendar time is difficult to work with programmatically, it is the source\n" + " of most time-related bugs in software. Compared to _epoch time_, which the\n" + " `gleam/time/timestamp` module uses, there are many disadvantages to\n" + " calendar time:\n" + "\n" + " - They are ambiguous if you don't know what time-zone is being used.\n" + "\n" + " - A time-zone database is required to understand calendar time even when\n" + " you have the time zone. These are large and your program has to\n" + " continously be updated as new versions of the database are published.\n" + "\n" + " - The type permits invalid states. e.g. `days` could be set to the number\n" + " 32, but this should not be possible!\n" + "\n" + " - There is not a single unique canonical value for each point in time,\n" + " thanks to time zones. Two different `Date` + `TimeOfDay` value pairs\n" + " could represent the same point in time. This means that you can't check\n" + " for time equality with `==` when using calendar types.\n" + "\n" + " - They are computationally complex, using a more memory to represent and\n" + " requiring a lot more CPU time to manipulate.\n" + "\n" + " There are also advantages to calendar time:\n" + "\n" + " - Calendar time is how human's talk about time, so if you want to show a\n" + " time or take a time from a human user then calendar time will make it\n" + " easier for them.\n" + "\n" + " - They can represent more abstract time periods such as \"New Year's Day\".\n" + " This may seem like an exact window of time at first, but really the\n" + " definition of \"New Year's Day\" is more fuzzy than that. When it starts\n" + " and ends will depend where in the world you are, so if you want to refer\n" + " to a day as a global concept instead of a fixed window of time for that\n" + " day in a specific location, then calendar time can represent that.\n" + "\n" + " So when should you use calendar time? These are our recommendations:\n" + "\n" + " - Default to `gleam/time/timestamp`, which is epoch time. It is\n" + " unambiguous, efficient, and significantly less likely to result in logic\n" + " bugs.\n" + "\n" + " - When writing time to a database or other data storage use epoch time,\n" + " using whatever epoch format it supports. For example, PostgreSQL\n" + " `timestamp` and `timestampz` are both epoch time, and `timestamp` is\n" + " preferred as it is more straightforward to use as your application is\n" + " also using epoch time.\n" + "\n" + " - When communicating with other computer systems continue to use epoch\n" + " time. For example, when sending times to another program you could\n" + " encode time as UNIX timestamps (seconds since 00:00:00 UTC on 1 January\n" + " 1970).\n" + "\n" + " - When communicating with humans use epoch time internally, and convert\n" + " to-and-from calendar time at the last moment, when iteracting with the\n" + " human user. It may also help the users to also show the time as a fuzzy\n" + " duration from the present time, such as \"about 4 days ago\".\n" + "\n" + " - When representing \"fuzzy\" human time concepts that don't exact periods\n" + " in time, such as \"one month\" (varies depending on which month, which\n" + " year, and in which time zone) and \"Christmas Day\" (varies depending on\n" + " which year and time zone) then use calendar time.\n" + "\n" + " Any time you do use calendar time you should be extra careful! It is very\n" + " easy to make mistake with. Avoid it where possible.\n" + "\n" + " ## Time zone offsets\n" + "\n" + " This package includes the `utc_offset` value and the `local_offset`\n" + " function, which are the offset for the UTC time zone and get the time\n" + " offset the computer running the program is configured to respectively.\n" + "\n" + " If you need to use other offsets in your program then you will need to get\n" + " them from somewhere else, such as from a package which loads the\n" + " [IANA Time Zone Database](https://www.iana.org/time-zones), or from the\n" + " website visitor's web browser, which your frontend can send for you.\n" + "\n" + " ## Use in APIs\n" + "\n" + " If you are making an API such as a HTTP JSON API you are encouraged to use\n" + " Unix timestamps instead of calendar times.\n" +). + +-type date() :: {date, integer(), month(), integer()}. + +-type time_of_day() :: {time_of_day, integer(), integer(), integer(), integer()}. + +-type month() :: january | + february | + march | + april | + may | + june | + july | + august | + september | + october | + november | + december. + +-file("src/gleam/time/calendar.gleam", 147). +?DOC( + " Get the offset for the computer's currently configured time zone.\n" + "\n" + " Note this may not be the time zone that is correct to use for your user.\n" + " For example, if you are making a web application that runs on a server you\n" + " want _their_ computer's time zone, not yours.\n" + "\n" + " This is the _current local_ offset, not the current local time zone. This\n" + " means that while it will result in the expected outcome for the current\n" + " time, it may result in unexpected output if used with other timestamps. For\n" + " example: a timestamp that would locally be during daylight savings time if\n" + " is it not currently daylight savings time when this function is called.\n" +). +-spec local_offset() -> gleam@time@duration:duration(). +local_offset() -> + gleam@time@duration:seconds(gleam_time_ffi:local_time_offset_seconds()). + +-file("src/gleam/time/calendar.gleam", 163). +?DOC( + " Returns the English name for a month.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " month_to_string(April)\n" + " // -> \"April\"\n" + " ```\n" +). +-spec month_to_string(month()) -> binary(). +month_to_string(Month) -> + case Month of + january -> + <<"January"/utf8>>; + + february -> + <<"February"/utf8>>; + + march -> + <<"March"/utf8>>; + + april -> + <<"April"/utf8>>; + + may -> + <<"May"/utf8>>; + + june -> + <<"June"/utf8>>; + + july -> + <<"July"/utf8>>; + + august -> + <<"August"/utf8>>; + + september -> + <<"September"/utf8>>; + + october -> + <<"October"/utf8>>; + + november -> + <<"November"/utf8>>; + + december -> + <<"December"/utf8>> + end. + +-file("src/gleam/time/calendar.gleam", 188). +?DOC( + " Returns the number for the month, where January is 1 and December is 12.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " month_to_int(January)\n" + " // -> 1\n" + " ```\n" +). +-spec month_to_int(month()) -> integer(). +month_to_int(Month) -> + case Month of + january -> + 1; + + february -> + 2; + + march -> + 3; + + april -> + 4; + + may -> + 5; + + june -> + 6; + + july -> + 7; + + august -> + 8; + + september -> + 9; + + october -> + 10; + + november -> + 11; + + december -> + 12 + end. + +-file("src/gleam/time/calendar.gleam", 213). +?DOC( + " Returns the month for a given number, where January is 1 and December is 12.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " month_from_int(1)\n" + " // -> Ok(January)\n" + " ```\n" +). +-spec month_from_int(integer()) -> {ok, month()} | {error, nil}. +month_from_int(Month) -> + case Month of + 1 -> + {ok, january}; + + 2 -> + {ok, february}; + + 3 -> + {ok, march}; + + 4 -> + {ok, april}; + + 5 -> + {ok, may}; + + 6 -> + {ok, june}; + + 7 -> + {ok, july}; + + 8 -> + {ok, august}; + + 9 -> + {ok, september}; + + 10 -> + {ok, october}; + + 11 -> + {ok, november}; + + 12 -> + {ok, december}; + + _ -> + {error, nil} + end. + +-file("src/gleam/time/calendar.gleam", 290). +?DOC( + " Determines if a given year is a leap year.\n" + "\n" + " A leap year occurs every 4 years, except for years divisible by 100,\n" + " unless they are also divisible by 400.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " is_leap_year(2024)\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " is_leap_year(2023)\n" + " // -> False\n" + " ```\n" +). +-spec is_leap_year(integer()) -> boolean(). +is_leap_year(Year) -> + case (Year rem 400) =:= 0 of + true -> + true; + + false -> + case (Year rem 100) =:= 0 of + true -> + false; + + false -> + (Year rem 4) =:= 0 + end + end. + +-file("src/gleam/time/calendar.gleam", 254). +?DOC( + " Checks if a given date is valid.\n" + "\n" + " This function properly accounts for leap years when validating February days.\n" + " A leap year occurs every 4 years, except for years divisible by 100,\n" + " unless they are also divisible by 400.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " is_valid_date(Date(2023, April, 15))\n" + " // -> True\n" + " ```\n" + "\n" + " ```gleam\n" + " is_valid_date(Date(2023, April, 31))\n" + " // -> False\n" + " ```\n" + "\n" + " ```gleam\n" + " is_valid_date(Date(2024, February, 29))\n" + " // -> True (2024 is a leap year)\n" + " ```\n" +). +-spec is_valid_date(date()) -> boolean(). +is_valid_date(Date) -> + {date, Year, Month, Day} = Date, + case Day < 1 of + true -> + false; + + false -> + case Month of + january -> + Day =< 31; + + march -> + Day =< 31; + + may -> + Day =< 31; + + july -> + Day =< 31; + + august -> + Day =< 31; + + october -> + Day =< 31; + + december -> + Day =< 31; + + april -> + Day =< 30; + + june -> + Day =< 30; + + september -> + Day =< 30; + + november -> + Day =< 30; + + february -> + Max_february_days = case is_leap_year(Year) of + true -> + 29; + + false -> + 28 + end, + Day =< Max_february_days + end + end. + +-file("src/gleam/time/calendar.gleam", 313). +?DOC( + " Checks if a time of day is valid.\n" + "\n" + " Validates that hours are 0-23, minutes are 0-59, seconds are 0-59,\n" + " and nanoseconds are 0-999,999,999.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " is_valid_time_of_day(TimeOfDay(12, 30, 45, 123456789))\n" + " // -> True\n" + " ```\n" +). +-spec is_valid_time_of_day(time_of_day()) -> boolean(). +is_valid_time_of_day(Time) -> + {time_of_day, Hours, Minutes, Seconds, Nanoseconds} = Time, + (((((((Hours >= 0) andalso (Hours =< 23)) andalso (Minutes >= 0)) andalso (Minutes + =< 59)) + andalso (Seconds >= 0)) + andalso (Seconds =< 59)) + andalso (Nanoseconds >= 0)) + andalso (Nanoseconds =< 999999999). + +-file("src/gleam/time/calendar.gleam", 340). +?DOC( + " Naively compares two dates without any time zone information, returning an\n" + " order.\n" + "\n" + " ## Correctness\n" + "\n" + " This function compares dates without any time zone information, only using\n" + " the rules for the gregorian calendar. This is typically sufficient, but be\n" + " aware that in reality some time zones will change their calendar date\n" + " occasionally. This can result in days being skipped, out of order, or\n" + " happening multiple times.\n" + "\n" + " If you need real-world correct time ordering then use the\n" + " `gleam/time/timestamp` module instead.\n" +). +-spec naive_date_compare(date(), date()) -> gleam@order:order(). +naive_date_compare(One, Other) -> + _pipe = gleam@int:compare(erlang:element(2, One), erlang:element(2, Other)), + _pipe@1 = gleam@order:lazy_break_tie( + _pipe, + fun() -> + gleam@int:compare( + month_to_int(erlang:element(3, One)), + month_to_int(erlang:element(3, Other)) + ) + end + ), + gleam@order:lazy_break_tie( + _pipe@1, + fun() -> + gleam@int:compare(erlang:element(4, One), erlang:element(4, Other)) + end + ). diff --git a/build/dev/javascript/gleam_time/gleam@time@duration.erl b/build/dev/javascript/gleam_time/gleam@time@duration.erl new file mode 100644 index 0000000..7ba7ad2 --- /dev/null +++ b/build/dev/javascript/gleam_time/gleam@time@duration.erl @@ -0,0 +1,381 @@ +-module(gleam@time@duration). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/time/duration.gleam"). +-export([approximate/1, compare/2, difference/2, add/2, seconds/1, minutes/1, hours/1, milliseconds/1, nanoseconds/1, to_seconds/1, to_seconds_and_nanoseconds/1, to_iso8601_string/1]). +-export_type([duration/0, unit/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +-opaque duration() :: {duration, integer(), integer()}. + +-type unit() :: nanosecond | + microsecond | + millisecond | + second | + minute | + hour | + day | + week | + month | + year. + +-file("src/gleam/time/duration.gleam", 110). +?DOC( + " Ensure the duration is represented with `nanoseconds` being positive and\n" + " less than 1 second.\n" + "\n" + " This function does not change the amount of time that the duratoin refers\n" + " to, it only adjusts the values used to represent the time.\n" +). +-spec normalise(duration()) -> duration(). +normalise(Duration) -> + Multiplier = 1000000000, + Nanoseconds = case Multiplier of + 0 -> 0; + Gleam@denominator -> erlang:element(3, Duration) rem Gleam@denominator + end, + Overflow = erlang:element(3, Duration) - Nanoseconds, + Seconds = erlang:element(2, Duration) + (case Multiplier of + 0 -> 0; + Gleam@denominator@1 -> Overflow div Gleam@denominator@1 + end), + case Nanoseconds >= 0 of + true -> + {duration, Seconds, Nanoseconds}; + + false -> + {duration, Seconds - 1, Multiplier + Nanoseconds} + end. + +-file("src/gleam/time/duration.gleam", 76). +?DOC( + " Convert a duration to a number of the largest number of a unit, serving as\n" + " a rough description of the duration that a human can understand.\n" + "\n" + " The size used for each unit are described in the documentation for the\n" + " `Unit` type.\n" + "\n" + " ```gleam\n" + " seconds(125)\n" + " |> approximate\n" + " // -> #(2, Minute)\n" + " ```\n" + "\n" + " This function rounds _towards zero_. This means that if a duration is just\n" + " short of 2 days then it will approximate to 1 day.\n" + "\n" + " ```gleam\n" + " hours(47)\n" + " |> approximate\n" + " // -> #(1, Day)\n" + " ```\n" +). +-spec approximate(duration()) -> {integer(), unit()}. +approximate(Duration) -> + {duration, S, Ns} = Duration, + Minute = 60, + Hour = Minute * 60, + Day = Hour * 24, + Week = Day * 7, + Year = (Day * 365) + (Hour * 6), + Month = Year div 12, + Microsecond = 1000, + Millisecond = Microsecond * 1000, + case nil of + _ when S < 0 -> + {Amount, Unit} = begin + _pipe = {duration, - S, - Ns}, + _pipe@1 = normalise(_pipe), + approximate(_pipe@1) + end, + {- Amount, Unit}; + + _ when S >= Year -> + {case Year of + 0 -> 0; + Gleam@denominator -> S div Gleam@denominator + end, year}; + + _ when S >= Month -> + {case Month of + 0 -> 0; + Gleam@denominator@1 -> S div Gleam@denominator@1 + end, month}; + + _ when S >= Week -> + {case Week of + 0 -> 0; + Gleam@denominator@2 -> S div Gleam@denominator@2 + end, week}; + + _ when S >= Day -> + {case Day of + 0 -> 0; + Gleam@denominator@3 -> S div Gleam@denominator@3 + end, day}; + + _ when S >= Hour -> + {case Hour of + 0 -> 0; + Gleam@denominator@4 -> S div Gleam@denominator@4 + end, hour}; + + _ when S >= Minute -> + {case Minute of + 0 -> 0; + Gleam@denominator@5 -> S div Gleam@denominator@5 + end, minute}; + + _ when S > 0 -> + {S, second}; + + _ when Ns >= Millisecond -> + {case Millisecond of + 0 -> 0; + Gleam@denominator@6 -> Ns div Gleam@denominator@6 + end, millisecond}; + + _ when Ns >= Microsecond -> + {case Microsecond of + 0 -> 0; + Gleam@denominator@7 -> Ns div Gleam@denominator@7 + end, microsecond}; + + _ -> + {Ns, nanosecond} + end. + +-file("src/gleam/time/duration.gleam", 140). +?DOC( + " Compare one duration to another, indicating whether the first spans a\n" + " larger amount of time (and so is greater) or smaller amount of time (and so\n" + " is lesser) than the second.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " compare(seconds(1), seconds(2))\n" + " // -> order.Lt\n" + " ```\n" + "\n" + " Whether a duration is negative or positive doesn't matter for comparing\n" + " them, only the amount of time spanned matters.\n" + "\n" + " ```gleam\n" + " compare(seconds(-2), seconds(1))\n" + " // -> order.Gt\n" + " ```\n" +). +-spec compare(duration(), duration()) -> gleam@order:order(). +compare(Left, Right) -> + Parts = fun(X) -> case erlang:element(2, X) >= 0 of + true -> + {erlang:element(2, X), erlang:element(3, X)}; + + false -> + {(erlang:element(2, X) * -1) - 1, + 1000000000 - erlang:element(3, X)} + end end, + {Ls, Lns} = Parts(Left), + {Rs, Rns} = Parts(Right), + _pipe = gleam@int:compare(Ls, Rs), + gleam@order:break_tie(_pipe, gleam@int:compare(Lns, Rns)). + +-file("src/gleam/time/duration.gleam", 164). +?DOC( + " Calculate the difference between two durations.\n" + "\n" + " This is effectively substracting the first duration from the second.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " difference(seconds(1), seconds(5))\n" + " // -> seconds(4)\n" + " ```\n" +). +-spec difference(duration(), duration()) -> duration(). +difference(Left, Right) -> + _pipe = {duration, + erlang:element(2, Right) - erlang:element(2, Left), + erlang:element(3, Right) - erlang:element(3, Left)}, + normalise(_pipe). + +-file("src/gleam/time/duration.gleam", 178). +?DOC( + " Add two durations together.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " add(seconds(1), seconds(5))\n" + " // -> seconds(6)\n" + " ```\n" +). +-spec add(duration(), duration()) -> duration(). +add(Left, Right) -> + _pipe = {duration, + erlang:element(2, Left) + erlang:element(2, Right), + erlang:element(3, Left) + erlang:element(3, Right)}, + normalise(_pipe). + +-file("src/gleam/time/duration.gleam", 225). +-spec nanosecond_digits(integer(), integer(), binary()) -> binary(). +nanosecond_digits(N, Position, Acc) -> + case Position of + 9 -> + Acc; + + _ when (Acc =:= <<""/utf8>>) andalso ((N rem 10) =:= 0) -> + nanosecond_digits(N div 10, Position + 1, Acc); + + _ -> + Acc@1 = <<(erlang:integer_to_binary(N rem 10))/binary, Acc/binary>>, + nanosecond_digits(N div 10, Position + 1, Acc@1) + end. + +-file("src/gleam/time/duration.gleam", 239). +?DOC(" Create a duration of a number of seconds.\n"). +-spec seconds(integer()) -> duration(). +seconds(Amount) -> + {duration, Amount, 0}. + +-file("src/gleam/time/duration.gleam", 244). +?DOC(" Create a duration of a number of minutes.\n"). +-spec minutes(integer()) -> duration(). +minutes(Amount) -> + seconds(Amount * 60). + +-file("src/gleam/time/duration.gleam", 249). +?DOC(" Create a duration of a number of hours.\n"). +-spec hours(integer()) -> duration(). +hours(Amount) -> + seconds((Amount * 60) * 60). + +-file("src/gleam/time/duration.gleam", 254). +?DOC(" Create a duration of a number of milliseconds.\n"). +-spec milliseconds(integer()) -> duration(). +milliseconds(Amount) -> + Remainder = Amount rem 1000, + Overflow = Amount - Remainder, + Nanoseconds = Remainder * 1000000, + Seconds = Overflow div 1000, + _pipe = {duration, Seconds, Nanoseconds}, + normalise(_pipe). + +-file("src/gleam/time/duration.gleam", 273). +?DOC( + " Create a duration of a number of nanoseconds.\n" + "\n" + " # JavaScript int limitations\n" + "\n" + " Remember that JavaScript can only perfectly represent ints between positive\n" + " and negative 9,007,199,254,740,991! If you use a single call to this\n" + " function to create durations larger than that number of nanoseconds then\n" + " you will likely not get exactly the value you expect. Use `seconds` and\n" + " `milliseconds` as much as possible for large durations.\n" +). +-spec nanoseconds(integer()) -> duration(). +nanoseconds(Amount) -> + _pipe = {duration, 0, Amount}, + normalise(_pipe). + +-file("src/gleam/time/duration.gleam", 283). +?DOC( + " Convert the duration to a number of seconds.\n" + "\n" + " There may be some small loss of precision due to `Duration` being\n" + " nanosecond accurate and `Float` not being able to represent this.\n" +). +-spec to_seconds(duration()) -> float(). +to_seconds(Duration) -> + Seconds = erlang:float(erlang:element(2, Duration)), + Nanoseconds = erlang:float(erlang:element(3, Duration)), + Seconds + (Nanoseconds / 1000000000.0). + +-file("src/gleam/time/duration.gleam", 292). +?DOC( + " Convert the duration to a number of seconds and nanoseconds. There is no\n" + " loss of precision with this conversion on any target.\n" +). +-spec to_seconds_and_nanoseconds(duration()) -> {integer(), integer()}. +to_seconds_and_nanoseconds(Duration) -> + {erlang:element(2, Duration), erlang:element(3, Duration)}. + +-file("src/gleam/time/duration.gleam", 192). +?DOC( + " Convert the duration to an [ISO8601][1] formatted duration string.\n" + "\n" + " The ISO8601 duration format is ambiguous without context due to months and\n" + " years having different lengths, and because of leap seconds. This function\n" + " encodes the duration as days, hours, and seconds without any leap seconds.\n" + " Be sure to take this into account when using the duration strings.\n" + "\n" + " [1]: https://en.wikipedia.org/wiki/ISO_8601#Durations\n" +). +-spec to_iso8601_string(duration()) -> binary(). +to_iso8601_string(Duration) -> + gleam@bool:guard( + Duration =:= {duration, 0, 0}, + <<"PT0S"/utf8>>, + fun() -> + Split = fun(Total, Limit) -> + Amount = case Limit of + 0 -> 0; + Gleam@denominator -> Total rem Gleam@denominator + end, + Remainder = case Limit of + 0 -> 0; + Gleam@denominator@1 -> (Total - Amount) div Gleam@denominator@1 + end, + {Amount, Remainder} + end, + {Seconds, Rest} = Split(erlang:element(2, Duration), 60), + {Minutes, Rest@1} = Split(Rest, 60), + {Hours, Rest@2} = Split(Rest@1, 24), + Days = Rest@2, + Add = fun(Out, Value, Unit) -> case Value of + 0 -> + Out; + + _ -> + <<<>/binary, + Unit/binary>> + end end, + Output = begin + _pipe = <<"P"/utf8>>, + _pipe@1 = Add(_pipe, Days, <<"D"/utf8>>), + _pipe@2 = gleam@string:append(_pipe@1, <<"T"/utf8>>), + _pipe@3 = Add(_pipe@2, Hours, <<"H"/utf8>>), + Add(_pipe@3, Minutes, <<"M"/utf8>>) + end, + case {Seconds, erlang:element(3, Duration)} of + {0, 0} -> + Output; + + {_, 0} -> + <<<>/binary, + "S"/utf8>>; + + {_, _} -> + F = nanosecond_digits( + erlang:element(3, Duration), + 0, + <<""/utf8>> + ), + <<<<<<<>/binary, + "."/utf8>>/binary, + F/binary>>/binary, + "S"/utf8>> + end + end + ). diff --git a/build/dev/javascript/gleam_time/gleam@time@timestamp.erl b/build/dev/javascript/gleam_time/gleam@time@timestamp.erl new file mode 100644 index 0000000..0d7413a --- /dev/null +++ b/build/dev/javascript/gleam_time/gleam@time@timestamp.erl @@ -0,0 +1,1188 @@ +-module(gleam@time@timestamp). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleam/time/timestamp.gleam"). +-export([compare/2, system_time/0, difference/2, add/2, to_calendar/2, to_rfc3339/2, from_unix_seconds/1, from_unix_seconds_and_nanoseconds/2, to_unix_seconds/1, to_unix_seconds_and_nanoseconds/1, from_calendar/3, parse_rfc3339/1]). +-export_type([timestamp/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC( + " Welcome to the timestamp module! This module and its `Timestamp` type are\n" + " what you will be using most commonly when working with time in Gleam.\n" + "\n" + " A timestamp represents a moment in time, represented as an amount of time\n" + " since the calendar time 00:00:00 UTC on 1 January 1970, also known as the\n" + " _Unix epoch_.\n" + "\n" + " # Wall clock time and monotonicity\n" + "\n" + " Time is very complicated, especially on computers! While they generally do\n" + " a good job of keeping track of what the time is, computers can get\n" + " out-of-sync and start to report a time that is too late or too early. Most\n" + " computers use \"network time protocol\" to tell each other what they think\n" + " the time is, and computers that realise they are running too fast or too\n" + " slow will adjust their clock to correct it. When this happens it can seem\n" + " to your program that the current time has changed, and it may have even\n" + " jumped backwards in time!\n" + "\n" + " This measure of time is called _wall clock time_, and it is what people\n" + " commonly think of when they think of time. It is important to be aware that\n" + " it can go backwards, and your program must not rely on it only ever going\n" + " forwards at a steady rate. For example, for tracking what order events happen\n" + " in. \n" + "\n" + " This module uses wall clock time. If your program needs time values to always\n" + " increase you will need a _monotonic_ time instead. It's uncommon that you\n" + " would need monotonic time, one example might be if you're making a\n" + " benchmarking framework.\n" + "\n" + " The exact way that time works will depend on what runtime you use. The\n" + " Erlang documentation on time has a lot of detail about time generally as well\n" + " as how it works on the BEAM, it is worth reading.\n" + " .\n" + "\n" + " # Converting to local calendar time\n" + "\n" + " Timestamps don't take into account time zones, so a moment in time will\n" + " have the same timestamp value regardless of where you are in the world. To\n" + " convert them to local time you will need to know the offset for the time\n" + " zone you wish to use, likely from a time zone database. See the\n" + " `gleam/time/calendar` module for more information.\n" + "\n" +). + +-opaque timestamp() :: {timestamp, integer(), integer()}. + +-file("src/gleam/time/timestamp.gleam", 119). +?DOC( + " Ensure the time is represented with `nanoseconds` being positive and less\n" + " than 1 second.\n" + "\n" + " This function does not change the time that the timestamp refers to, it\n" + " only adjusts the values used to represent the time.\n" +). +-spec normalise(timestamp()) -> timestamp(). +normalise(Timestamp) -> + Multiplier = 1000000000, + Nanoseconds = case Multiplier of + 0 -> 0; + Gleam@denominator -> erlang:element(3, Timestamp) rem Gleam@denominator + end, + Overflow = erlang:element(3, Timestamp) - Nanoseconds, + Seconds = erlang:element(2, Timestamp) + (case Multiplier of + 0 -> 0; + Gleam@denominator@1 -> Overflow div Gleam@denominator@1 + end), + case Nanoseconds >= 0 of + true -> + {timestamp, Seconds, Nanoseconds}; + + false -> + {timestamp, Seconds - 1, Multiplier + Nanoseconds} + end. + +-file("src/gleam/time/timestamp.gleam", 141). +?DOC( + " Compare one timestamp to another, indicating whether the first is further\n" + " into the future (greater) or further into the past (lesser) than the\n" + " second.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " compare(from_unix_seconds(1), from_unix_seconds(2))\n" + " // -> order.Lt\n" + " ```\n" +). +-spec compare(timestamp(), timestamp()) -> gleam@order:order(). +compare(Left, Right) -> + gleam@order:break_tie( + gleam@int:compare(erlang:element(2, Left), erlang:element(2, Right)), + gleam@int:compare(erlang:element(3, Left), erlang:element(3, Right)) + ). + +-file("src/gleam/time/timestamp.gleam", 160). +?DOC( + " Get the current system time.\n" + "\n" + " Note this time is not unique or monotonic, it could change at any time or\n" + " even go backwards! The exact behaviour will depend on the runtime used. See\n" + " the module documentation for more information.\n" + "\n" + " On Erlang this uses [`erlang:system_time/1`][1]. On JavaScript this uses\n" + " [`Date.now`][2].\n" + "\n" + " [1]: https://www.erlang.org/doc/apps/erts/erlang#system_time/1\n" + " [2]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now\n" +). +-spec system_time() -> timestamp(). +system_time() -> + {Seconds, Nanoseconds} = gleam_time_ffi:system_time(), + normalise({timestamp, Seconds, Nanoseconds}). + +-file("src/gleam/time/timestamp.gleam", 180). +?DOC( + " Calculate the difference between two timestamps.\n" + "\n" + " This is effectively substracting the first timestamp from the second.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " difference(from_unix_seconds(1), from_unix_seconds(5))\n" + " // -> duration.seconds(4)\n" + " ```\n" +). +-spec difference(timestamp(), timestamp()) -> gleam@time@duration:duration(). +difference(Left, Right) -> + Seconds = gleam@time@duration:seconds( + erlang:element(2, Right) - erlang:element(2, Left) + ), + Nanoseconds = gleam@time@duration:nanoseconds( + erlang:element(3, Right) - erlang:element(3, Left) + ), + gleam@time@duration:add(Seconds, Nanoseconds). + +-file("src/gleam/time/timestamp.gleam", 195). +?DOC( + " Add a duration to a timestamp.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " add(from_unix_seconds(1000), duration.seconds(5))\n" + " // -> from_unix_seconds(1005)\n" + " ```\n" +). +-spec add(timestamp(), gleam@time@duration:duration()) -> timestamp(). +add(Timestamp, Duration) -> + {Seconds, Nanoseconds} = gleam@time@duration:to_seconds_and_nanoseconds( + Duration + ), + _pipe = {timestamp, + erlang:element(2, Timestamp) + Seconds, + erlang:element(3, Timestamp) + Nanoseconds}, + normalise(_pipe). + +-file("src/gleam/time/timestamp.gleam", 262). +-spec pad_digit(integer(), integer()) -> binary(). +pad_digit(Digit, Desired_length) -> + _pipe = erlang:integer_to_binary(Digit), + gleam@string:pad_start(_pipe, Desired_length, <<"0"/utf8>>). + +-file("src/gleam/time/timestamp.gleam", 308). +-spec duration_to_minutes(gleam@time@duration:duration()) -> integer(). +duration_to_minutes(Duration) -> + erlang:round(gleam@time@duration:to_seconds(Duration) / 60.0). + +-file("src/gleam/time/timestamp.gleam", 370). +-spec modulo(integer(), integer()) -> integer(). +modulo(N, M) -> + case gleam@int:modulo(N, M) of + {ok, N@1} -> + N@1; + + {error, _} -> + 0 + end. + +-file("src/gleam/time/timestamp.gleam", 377). +-spec floored_div(integer(), float()) -> integer(). +floored_div(Numerator, Denominator) -> + N = case Denominator of + +0.0 -> +0.0; + -0.0 -> -0.0; + Gleam@denominator -> erlang:float(Numerator) / Gleam@denominator + end, + erlang:round(math:floor(N)). + +-file("src/gleam/time/timestamp.gleam", 383). +-spec to_civil(integer()) -> {integer(), integer(), integer()}. +to_civil(Minutes) -> + Raw_day = floored_div(Minutes, (60.0 * 24.0)) + 719468, + Era = case Raw_day >= 0 of + true -> + Raw_day div 146097; + + false -> + (Raw_day - 146096) div 146097 + end, + Day_of_era = Raw_day - (Era * 146097), + Year_of_era = (((Day_of_era - (Day_of_era div 1460)) + (Day_of_era div 36524)) + - (Day_of_era div 146096)) + div 365, + Year = Year_of_era + (Era * 400), + Day_of_year = Day_of_era - (((365 * Year_of_era) + (Year_of_era div 4)) - (Year_of_era + div 100)), + Mp = ((5 * Day_of_year) + 2) div 153, + Month = case Mp < 10 of + true -> + Mp + 3; + + false -> + Mp - 9 + end, + Day = (Day_of_year - (((153 * Mp) + 2) div 5)) + 1, + Year@1 = case Month =< 2 of + true -> + Year + 1; + + false -> + Year + end, + {Year@1, Month, Day}. + +-file("src/gleam/time/timestamp.gleam", 312). +-spec to_calendar_from_offset(timestamp(), integer()) -> {integer(), + integer(), + integer(), + integer(), + integer(), + integer()}. +to_calendar_from_offset(Timestamp, Offset) -> + Total = erlang:element(2, Timestamp) + (Offset * 60), + Seconds = modulo(Total, 60), + Total_minutes = floored_div(Total, 60.0), + Minutes = modulo(Total, 60 * 60) div 60, + Hours = case (60 * 60) of + 0 -> 0; + Gleam@denominator -> modulo(Total, (24 * 60) * 60) div Gleam@denominator + end, + {Year, Month, Day} = to_civil(Total_minutes), + {Year, Month, Day, Hours, Minutes, Seconds}. + +-file("src/gleam/time/timestamp.gleam", 281). +?DOC( + " Convert a `Timestamp` to calendar time, suitable for presenting to a human\n" + " to read.\n" + "\n" + " If you want a machine to use the time value then you should not use this\n" + " function and should instead keep it as a timestamp. See the documentation\n" + " for the `gleam/time/calendar` module for more information.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " timestamp.from_unix_seconds(0)\n" + " |> timestamp.to_calendar(calendar.utc_offset)\n" + " // -> #(Date(1970, January, 1), TimeOfDay(0, 0, 0, 0))\n" + " ```\n" +). +-spec to_calendar(timestamp(), gleam@time@duration:duration()) -> {gleam@time@calendar:date(), + gleam@time@calendar:time_of_day()}. +to_calendar(Timestamp, Offset) -> + Offset@1 = duration_to_minutes(Offset), + {Year, Month, Day, Hours, Minutes, Seconds} = to_calendar_from_offset( + Timestamp, + Offset@1 + ), + Month@1 = case Month of + 1 -> + january; + + 2 -> + february; + + 3 -> + march; + + 4 -> + april; + + 5 -> + may; + + 6 -> + june; + + 7 -> + july; + + 8 -> + august; + + 9 -> + september; + + 10 -> + october; + + 11 -> + november; + + _ -> + december + end, + Nanoseconds = erlang:element(3, Timestamp), + Date = {date, Year, Month@1, Day}, + Time = {time_of_day, Hours, Minutes, Seconds, Nanoseconds}, + {Date, Time}. + +-file("src/gleam/time/timestamp.gleam", 446). +-spec do_remove_trailing_zeros(list(integer())) -> list(integer()). +do_remove_trailing_zeros(Reversed_digits) -> + case Reversed_digits of + [] -> + []; + + [Digit | Digits] when Digit =:= 0 -> + do_remove_trailing_zeros(Digits); + + Reversed_digits@1 -> + lists:reverse(Reversed_digits@1) + end. + +-file("src/gleam/time/timestamp.gleam", 440). +?DOC(" Given a list of digits, return new list with any trailing zeros removed.\n"). +-spec remove_trailing_zeros(list(integer())) -> list(integer()). +remove_trailing_zeros(Digits) -> + Reversed_digits = lists:reverse(Digits), + do_remove_trailing_zeros(Reversed_digits). + +-file("src/gleam/time/timestamp.gleam", 461). +-spec do_get_zero_padded_digits(integer(), list(integer()), integer()) -> list(integer()). +do_get_zero_padded_digits(Number, Digits, Count) -> + case Number of + Number@1 when (Number@1 =< 0) andalso (Count >= 9) -> + Digits; + + Number@2 when Number@2 =< 0 -> + do_get_zero_padded_digits(Number@2, [0 | Digits], Count + 1); + + Number@3 -> + Digit = Number@3 rem 10, + Number@4 = floored_div(Number@3, 10.0), + do_get_zero_padded_digits(Number@4, [Digit | Digits], Count + 1) + end. + +-file("src/gleam/time/timestamp.gleam", 457). +?DOC( + " Returns the list of digits of `number`. If the number of digits is less \n" + " than 9, the result is zero-padded at the front.\n" +). +-spec get_zero_padded_digits(integer()) -> list(integer()). +get_zero_padded_digits(Number) -> + do_get_zero_padded_digits(Number, [], 0). + +-file("src/gleam/time/timestamp.gleam", 420). +?DOC( + " Converts nanoseconds into a `String` representation of fractional seconds.\n" + " \n" + " Assumes that `nanoseconds < 1_000_000_000`, which will be true for any \n" + " normalised timestamp.\n" +). +-spec show_second_fraction(integer()) -> binary(). +show_second_fraction(Nanoseconds) -> + case gleam@int:compare(Nanoseconds, 0) of + lt -> + <<""/utf8>>; + + eq -> + <<""/utf8>>; + + gt -> + Second_fraction_part = begin + _pipe = Nanoseconds, + _pipe@1 = get_zero_padded_digits(_pipe), + _pipe@2 = remove_trailing_zeros(_pipe@1), + _pipe@3 = gleam@list:map( + _pipe@2, + fun erlang:integer_to_binary/1 + ), + gleam@string:join(_pipe@3, <<""/utf8>>) + end, + <<"."/utf8, Second_fraction_part/binary>> + end. + +-file("src/gleam/time/timestamp.gleam", 240). +?DOC( + " Convert a timestamp to a RFC 3339 formatted time string, with an offset\n" + " supplied as an additional argument.\n" + "\n" + " The output of this function is also ISO 8601 compatible so long as the\n" + " offset not negative. Offsets have at-most minute precision, so an offset\n" + " with higher precision will be rounded to the nearest minute.\n" + "\n" + " If you are making an API such as a HTTP JSON API you are encouraged to use\n" + " Unix timestamps instead of this format or ISO 8601. Unix timestamps are a\n" + " better choice as they don't contain offset information. Consider:\n" + "\n" + " - UTC offsets are not time zones. This does not and cannot tell us the time\n" + " zone in which the date was recorded. So what are we supposed to do with\n" + " this information?\n" + " - Users typically want dates formatted according to their local time zone.\n" + " What if the provided UTC offset is different from the current user's time\n" + " zone? What are we supposed to do with it then?\n" + " - Despite it being useless (or worse, a source of bugs), the UTC offset\n" + " creates a larger payload to transfer.\n" + "\n" + " They also uses more memory than a unix timestamp. The way they are better\n" + " than Unix timestamp is that it is easier for a human to read them, but\n" + " this is a hinderance that tooling can remedy, and APIs are not primarily\n" + " for humans.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " timestamp.from_unix_seconds_and_nanoseconds(1000, 123_000_000)\n" + " |> to_rfc3339(calendar.utc_offset)\n" + " // -> \"1970-01-01T00:16:40.123Z\"\n" + " ```\n" + "\n" + " ```gleam\n" + " timestamp.from_unix_seconds(1000)\n" + " |> to_rfc3339(duration.seconds(3600))\n" + " // -> \"1970-01-01T01:16:40+01:00\"\n" + " ```\n" +). +-spec to_rfc3339(timestamp(), gleam@time@duration:duration()) -> binary(). +to_rfc3339(Timestamp, Offset) -> + Offset@1 = duration_to_minutes(Offset), + {Years, Months, Days, Hours, Minutes, Seconds} = to_calendar_from_offset( + Timestamp, + Offset@1 + ), + Offset_minutes = modulo(Offset@1, 60), + Offset_hours = gleam@int:absolute_value(floored_div(Offset@1, 60.0)), + N2 = fun(_capture) -> pad_digit(_capture, 2) end, + N4 = fun(_capture@1) -> pad_digit(_capture@1, 4) end, + Out = <<""/utf8>>, + Out@1 = <<<<<<<<<>/binary, "-"/utf8>>/binary, + (N2(Months))/binary>>/binary, + "-"/utf8>>/binary, + (N2(Days))/binary>>, + Out@2 = <>, + Out@3 = <<<<<<<<<>/binary, ":"/utf8>>/binary, + (N2(Minutes))/binary>>/binary, + ":"/utf8>>/binary, + (N2(Seconds))/binary>>, + Out@4 = <>, + case gleam@int:compare(Offset@1, 0) of + eq -> + <>; + + gt -> + <<<<<<<>/binary, (N2(Offset_hours))/binary>>/binary, + ":"/utf8>>/binary, + (N2(Offset_minutes))/binary>>; + + lt -> + <<<<<<<>/binary, (N2(Offset_hours))/binary>>/binary, + ":"/utf8>>/binary, + (N2(Offset_minutes))/binary>> + end. + +-file("src/gleam/time/timestamp.gleam", 611). +-spec is_leap_year(integer()) -> boolean(). +is_leap_year(Year) -> + ((Year rem 4) =:= 0) andalso (((Year rem 100) /= 0) orelse ((Year rem 400) + =:= 0)). + +-file("src/gleam/time/timestamp.gleam", 715). +-spec parse_sign(bitstring()) -> {ok, {binary(), bitstring()}} | {error, nil}. +parse_sign(Bytes) -> + case Bytes of + <<"+"/utf8, Remaining_bytes/binary>> -> + {ok, {<<"+"/utf8>>, Remaining_bytes}}; + + <<"-"/utf8, Remaining_bytes@1/binary>> -> + {ok, {<<"-"/utf8>>, Remaining_bytes@1}}; + + _ -> + {error, nil} + end. + +-file("src/gleam/time/timestamp.gleam", 762). +?DOC(" Accept the given value from `bytes` and move past it if found.\n"). +-spec accept_byte(bitstring(), integer()) -> {ok, bitstring()} | {error, nil}. +accept_byte(Bytes, Value) -> + case Bytes of + <> when Byte =:= Value -> + {ok, Remaining_bytes}; + + _ -> + {error, nil} + end. + +-file("src/gleam/time/timestamp.gleam", 780). +-spec accept_empty(bitstring()) -> {ok, nil} | {error, nil}. +accept_empty(Bytes) -> + case Bytes of + <<>> -> + {ok, nil}; + + _ -> + {error, nil} + end. + +-file("src/gleam/time/timestamp.gleam", 840). +?DOC( + " Note: It is the callers responsibility to ensure the inputs are valid.\n" + " \n" + " See https://www.tondering.dk/claus/cal/julperiod.php#formula\n" +). +-spec julian_day_from_ymd(integer(), integer(), integer()) -> integer(). +julian_day_from_ymd(Year, Month, Day) -> + Adjustment = (14 - Month) div 12, + Adjusted_year = (Year + 4800) - Adjustment, + Adjusted_month = (Month + (12 * Adjustment)) - 3, + (((((Day + (((153 * Adjusted_month) + 2) div 5)) + (365 * Adjusted_year)) + (Adjusted_year + div 4)) + - (Adjusted_year div 100)) + + (Adjusted_year div 400)) + - 32045. + +-file("src/gleam/time/timestamp.gleam", 859). +?DOC( + " Create a timestamp from a number of seconds since 00:00:00 UTC on 1 January\n" + " 1970.\n" +). +-spec from_unix_seconds(integer()) -> timestamp(). +from_unix_seconds(Seconds) -> + {timestamp, Seconds, 0}. + +-file("src/gleam/time/timestamp.gleam", 874). +?DOC( + " Create a timestamp from a number of seconds and nanoseconds since 00:00:00\n" + " UTC on 1 January 1970.\n" + "\n" + " # JavaScript int limitations\n" + "\n" + " Remember that JavaScript can only perfectly represent ints between positive\n" + " and negative 9,007,199,254,740,991! If you only use the nanosecond field\n" + " then you will almost certainly not get the date value you want due to this\n" + " loss of precision. Always use seconds primarily and then use nanoseconds\n" + " for the final sub-second adjustment.\n" +). +-spec from_unix_seconds_and_nanoseconds(integer(), integer()) -> timestamp(). +from_unix_seconds_and_nanoseconds(Seconds, Nanoseconds) -> + _pipe = {timestamp, Seconds, Nanoseconds}, + normalise(_pipe). + +-file("src/gleam/time/timestamp.gleam", 888). +?DOC( + " Convert the timestamp to a number of seconds since 00:00:00 UTC on 1\n" + " January 1970.\n" + "\n" + " There may be some small loss of precision due to `Timestamp` being\n" + " nanosecond accurate and `Float` not being able to represent this.\n" +). +-spec to_unix_seconds(timestamp()) -> float(). +to_unix_seconds(Timestamp) -> + Seconds = erlang:float(erlang:element(2, Timestamp)), + Nanoseconds = erlang:float(erlang:element(3, Timestamp)), + Seconds + (Nanoseconds / 1000000000.0). + +-file("src/gleam/time/timestamp.gleam", 897). +?DOC( + " Convert the timestamp to a number of seconds and nanoseconds since 00:00:00\n" + " UTC on 1 January 1970. There is no loss of precision with this conversion\n" + " on any target.\n" +). +-spec to_unix_seconds_and_nanoseconds(timestamp()) -> {integer(), integer()}. +to_unix_seconds_and_nanoseconds(Timestamp) -> + {erlang:element(2, Timestamp), erlang:element(3, Timestamp)}. + +-file("src/gleam/time/timestamp.gleam", 723). +-spec offset_to_seconds(binary(), integer(), integer()) -> integer(). +offset_to_seconds(Sign, Hours, Minutes) -> + Abs_seconds = (Hours * 3600) + (Minutes * 60), + case Sign of + <<"-"/utf8>> -> + - Abs_seconds; + + _ -> + Abs_seconds + end. + +-file("src/gleam/time/timestamp.gleam", 819). +?DOC( + " `julian_seconds_from_parts(year, month, day, hours, minutes, seconds)` \n" + " returns the number of Julian \n" + " seconds represented by the given arguments.\n" + " \n" + " Note: It is the callers responsibility to ensure the inputs are valid.\n" + " \n" + " See https://www.tondering.dk/claus/cal/julperiod.php#formula\n" +). +-spec julian_seconds_from_parts( + integer(), + integer(), + integer(), + integer(), + integer(), + integer() +) -> integer(). +julian_seconds_from_parts(Year, Month, Day, Hours, Minutes, Seconds) -> + Julian_day_seconds = julian_day_from_ymd(Year, Month, Day) * 86400, + ((Julian_day_seconds + (Hours * 3600)) + (Minutes * 60)) + Seconds. + +-file("src/gleam/time/timestamp.gleam", 662). +-spec do_parse_second_fraction_as_nanoseconds(bitstring(), integer(), integer()) -> {ok, + {integer(), bitstring()}} | + {error, any()}. +do_parse_second_fraction_as_nanoseconds(Bytes, Acc, Power) -> + Power@1 = Power div 10, + case Bytes of + <> when ((16#30 =< Byte) andalso (Byte =< 16#39)) andalso (Power@1 < 1) -> + do_parse_second_fraction_as_nanoseconds( + Remaining_bytes, + Acc, + Power@1 + ); + + <> when (16#30 =< Byte@1) andalso (Byte@1 =< 16#39) -> + Digit = Byte@1 - 16#30, + do_parse_second_fraction_as_nanoseconds( + Remaining_bytes@1, + Acc + (Digit * Power@1), + Power@1 + ); + + _ -> + {ok, {Acc, Bytes}} + end. + +-file("src/gleam/time/timestamp.gleam", 642). +-spec parse_second_fraction_as_nanoseconds(bitstring()) -> {ok, + {integer(), bitstring()}} | + {error, nil}. +parse_second_fraction_as_nanoseconds(Bytes) -> + case Bytes of + <<"."/utf8, Byte, Remaining_bytes/binary>> when (16#30 =< Byte) andalso (Byte =< 16#39) -> + do_parse_second_fraction_as_nanoseconds( + <>, + 0, + 1000000000 + ); + + <<"."/utf8, _/binary>> -> + {error, nil}; + + _ -> + {ok, {0, Bytes}} + end. + +-file("src/gleam/time/timestamp.gleam", 741). +-spec do_parse_digits(bitstring(), integer(), integer(), integer()) -> {ok, + {integer(), bitstring()}} | + {error, nil}. +do_parse_digits(Bytes, Count, Acc, K) -> + case Bytes of + _ when K >= Count -> + {ok, {Acc, Bytes}}; + + <> when (16#30 =< Byte) andalso (Byte =< 16#39) -> + do_parse_digits( + Remaining_bytes, + Count, + (Acc * 10) + (Byte - 16#30), + K + 1 + ); + + _ -> + {error, nil} + end. + +-file("src/gleam/time/timestamp.gleam", 734). +?DOC(" Parse and return the given number of digits from the given bytes.\n"). +-spec parse_digits(bitstring(), integer()) -> {ok, {integer(), bitstring()}} | + {error, nil}. +parse_digits(Bytes, Count) -> + do_parse_digits(Bytes, Count, 0, 0). + +-file("src/gleam/time/timestamp.gleam", 573). +-spec parse_year(bitstring()) -> {ok, {integer(), bitstring()}} | {error, nil}. +parse_year(Bytes) -> + parse_digits(Bytes, 4). + +-file("src/gleam/time/timestamp.gleam", 577). +-spec parse_month(bitstring()) -> {ok, {integer(), bitstring()}} | {error, nil}. +parse_month(Bytes) -> + gleam@result:'try'( + parse_digits(Bytes, 2), + fun(_use0) -> + {Month, Bytes@1} = _use0, + case (1 =< Month) andalso (Month =< 12) of + true -> + {ok, {Month, Bytes@1}}; + + false -> + {error, nil} + end + end + ). + +-file("src/gleam/time/timestamp.gleam", 585). +-spec parse_day(bitstring(), integer(), integer()) -> {ok, + {integer(), bitstring()}} | + {error, nil}. +parse_day(Bytes, Year, Month) -> + gleam@result:'try'( + parse_digits(Bytes, 2), + fun(_use0) -> + {Day, Bytes@1} = _use0, + gleam@result:'try'(case Month of + 1 -> + {ok, 31}; + + 3 -> + {ok, 31}; + + 5 -> + {ok, 31}; + + 7 -> + {ok, 31}; + + 8 -> + {ok, 31}; + + 10 -> + {ok, 31}; + + 12 -> + {ok, 31}; + + 4 -> + {ok, 30}; + + 6 -> + {ok, 30}; + + 9 -> + {ok, 30}; + + 11 -> + {ok, 30}; + + 2 -> + case is_leap_year(Year) of + true -> + {ok, 29}; + + false -> + {ok, 28} + end; + + _ -> + {error, nil} + end, fun(Max_day) -> case (1 =< Day) andalso (Day =< Max_day) of + true -> + {ok, {Day, Bytes@1}}; + + false -> + {error, nil} + end end) + end + ). + +-file("src/gleam/time/timestamp.gleam", 615). +-spec parse_hours(bitstring()) -> {ok, {integer(), bitstring()}} | {error, nil}. +parse_hours(Bytes) -> + gleam@result:'try'( + parse_digits(Bytes, 2), + fun(_use0) -> + {Hours, Bytes@1} = _use0, + case (0 =< Hours) andalso (Hours =< 23) of + true -> + {ok, {Hours, Bytes@1}}; + + false -> + {error, nil} + end + end + ). + +-file("src/gleam/time/timestamp.gleam", 623). +-spec parse_minutes(bitstring()) -> {ok, {integer(), bitstring()}} | + {error, nil}. +parse_minutes(Bytes) -> + gleam@result:'try'( + parse_digits(Bytes, 2), + fun(_use0) -> + {Minutes, Bytes@1} = _use0, + case (0 =< Minutes) andalso (Minutes =< 59) of + true -> + {ok, {Minutes, Bytes@1}}; + + false -> + {error, nil} + end + end + ). + +-file("src/gleam/time/timestamp.gleam", 631). +-spec parse_seconds(bitstring()) -> {ok, {integer(), bitstring()}} | + {error, nil}. +parse_seconds(Bytes) -> + gleam@result:'try'( + parse_digits(Bytes, 2), + fun(_use0) -> + {Seconds, Bytes@1} = _use0, + case (0 =< Seconds) andalso (Seconds =< 60) of + true -> + {ok, {Seconds, Bytes@1}}; + + false -> + {error, nil} + end + end + ). + +-file("src/gleam/time/timestamp.gleam", 704). +-spec parse_numeric_offset(bitstring()) -> {ok, {integer(), bitstring()}} | + {error, nil}. +parse_numeric_offset(Bytes) -> + gleam@result:'try'( + parse_sign(Bytes), + fun(_use0) -> + {Sign, Bytes@1} = _use0, + gleam@result:'try'( + parse_hours(Bytes@1), + fun(_use0@1) -> + {Hours, Bytes@2} = _use0@1, + gleam@result:'try'( + accept_byte(Bytes@2, 16#3A), + fun(Bytes@3) -> + gleam@result:'try'( + parse_minutes(Bytes@3), + fun(_use0@2) -> + {Minutes, Bytes@4} = _use0@2, + Offset_seconds = offset_to_seconds( + Sign, + Hours, + Minutes + ), + {ok, {Offset_seconds, Bytes@4}} + end + ) + end + ) + end + ) + end + ). + +-file("src/gleam/time/timestamp.gleam", 696). +-spec parse_offset(bitstring()) -> {ok, {integer(), bitstring()}} | {error, nil}. +parse_offset(Bytes) -> + case Bytes of + <<"Z"/utf8, Remaining_bytes/binary>> -> + {ok, {0, Remaining_bytes}}; + + <<"z"/utf8, Remaining_bytes/binary>> -> + {ok, {0, Remaining_bytes}}; + + _ -> + parse_numeric_offset(Bytes) + end. + +-file("src/gleam/time/timestamp.gleam", 769). +-spec accept_date_time_separator(bitstring()) -> {ok, bitstring()} | + {error, nil}. +accept_date_time_separator(Bytes) -> + case Bytes of + <> when ((Byte =:= 16#54) orelse (Byte =:= 16#74)) orelse (Byte =:= 16#20) -> + {ok, Remaining_bytes}; + + _ -> + {error, nil} + end. + +-file("src/gleam/time/timestamp.gleam", 789). +?DOC(" Note: The caller of this function must ensure that all inputs are valid.\n"). +-spec from_date_time( + integer(), + integer(), + integer(), + integer(), + integer(), + integer(), + integer(), + integer() +) -> timestamp(). +from_date_time( + Year, + Month, + Day, + Hours, + Minutes, + Seconds, + Second_fraction_as_nanoseconds, + Offset_seconds +) -> + Julian_seconds = julian_seconds_from_parts( + Year, + Month, + Day, + Hours, + Minutes, + Seconds + ), + Julian_seconds_since_epoch = Julian_seconds - 210866803200, + _pipe = {timestamp, + Julian_seconds_since_epoch - Offset_seconds, + Second_fraction_as_nanoseconds}, + normalise(_pipe). + +-file("src/gleam/time/timestamp.gleam", 339). +?DOC( + " Create a `Timestamp` from a human-readable calendar time.\n" + "\n" + " # Examples\n" + "\n" + " ```gleam\n" + " timestamp.from_calendar(\n" + " date: calendar.Date(2024, calendar.December, 25),\n" + " time: calendar.TimeOfDay(12, 30, 50, 0),\n" + " offset: calendar.utc_offset,\n" + " )\n" + " |> timestamp.to_rfc3339(calendar.utc_offset)\n" + " // -> \"2024-12-25T12:30:50Z\"\n" + " ```\n" +). +-spec from_calendar( + gleam@time@calendar:date(), + gleam@time@calendar:time_of_day(), + gleam@time@duration:duration() +) -> timestamp(). +from_calendar(Date, Time, Offset) -> + Month = case erlang:element(3, Date) of + january -> + 1; + + february -> + 2; + + march -> + 3; + + april -> + 4; + + may -> + 5; + + june -> + 6; + + july -> + 7; + + august -> + 8; + + september -> + 9; + + october -> + 10; + + november -> + 11; + + december -> + 12 + end, + from_date_time( + erlang:element(2, Date), + Month, + erlang:element(4, Date), + erlang:element(2, Time), + erlang:element(3, Time), + erlang:element(4, Time), + erlang:element(5, Time), + erlang:round(gleam@time@duration:to_seconds(Offset)) + ). + +-file("src/gleam/time/timestamp.gleam", 533). +?DOC( + " Parses an [RFC 3339 formatted time string][spec] into a `Timestamp`.\n" + "\n" + " [spec]: https://datatracker.ietf.org/doc/html/rfc3339#section-5.6\n" + " \n" + " # Examples\n" + "\n" + " ```gleam\n" + " let assert Ok(ts) = timestamp.parse_rfc3339(\"1970-01-01T00:00:01Z\")\n" + " timestamp.to_unix_seconds_and_nanoseconds(ts)\n" + " // -> #(1, 0)\n" + " ```\n" + " \n" + " Parsing an invalid timestamp returns an error.\n" + " \n" + " ```gleam\n" + " let assert Error(Nil) = timestamp.parse_rfc3339(\"1995-10-31\")\n" + " ```\n" + "\n" + " ## Time zones\n" + "\n" + " It may at first seem that the RFC 3339 format includes timezone\n" + " information, as it can specify an offset such as `Z` or `+3`, so why does\n" + " this function not return calendar time with a time zone? There are multiple\n" + " reasons:\n" + "\n" + " - RFC 3339's timestamp format is based on calendar time, but it is\n" + " unambigous, so it can be converted into epoch time when being parsed. It\n" + " is always better to internally use epoch time to represent unambiguous\n" + " points in time, so we perform that conversion as a convenience and to\n" + " ensure that programmers with less time experience don't accidentally use\n" + " a less suitable time representation.\n" + "\n" + " - RFC 3339's contains _calendar time offset_ information, not time zone\n" + " information. This is enough to convert it to an unambiguous timestamp,\n" + " but it is not enough information to reliably work with calendar time.\n" + " Without the time zone and the time zone database it's not possible to\n" + " know what time period that offset is valid for, so it cannot be used\n" + " without risk of bugs.\n" + "\n" + " ## Behaviour details\n" + " \n" + " - Follows the grammar specified in section 5.6 Internet Date/Time Format of \n" + " RFC 3339 .\n" + " - The `T` and `Z` characters may alternatively be lower case `t` or `z`, \n" + " respectively.\n" + " - Full dates and full times must be separated by `T` or `t`. A space is also \n" + " permitted.\n" + " - Leap seconds rules are not considered. That is, any timestamp may \n" + " specify digts `00` - `60` for the seconds.\n" + " - Any part of a fractional second that cannot be represented in the \n" + " nanosecond precision is tructated. That is, for the time string, \n" + " `\"1970-01-01T00:00:00.1234567899Z\"`, the fractional second `.1234567899` \n" + " will be represented as `123_456_789` in the `Timestamp`.\n" +). +-spec parse_rfc3339(binary()) -> {ok, timestamp()} | {error, nil}. +parse_rfc3339(Input) -> + Bytes = gleam_stdlib:identity(Input), + gleam@result:'try'( + parse_year(Bytes), + fun(_use0) -> + {Year, Bytes@1} = _use0, + gleam@result:'try'( + accept_byte(Bytes@1, 16#2D), + fun(Bytes@2) -> + gleam@result:'try'( + parse_month(Bytes@2), + fun(_use0@1) -> + {Month, Bytes@3} = _use0@1, + gleam@result:'try'( + accept_byte(Bytes@3, 16#2D), + fun(Bytes@4) -> + gleam@result:'try'( + parse_day(Bytes@4, Year, Month), + fun(_use0@2) -> + {Day, Bytes@5} = _use0@2, + gleam@result:'try'( + accept_date_time_separator( + Bytes@5 + ), + fun(Bytes@6) -> + gleam@result:'try'( + parse_hours(Bytes@6), + fun(_use0@3) -> + {Hours, Bytes@7} = _use0@3, + gleam@result:'try'( + accept_byte( + Bytes@7, + 16#3A + ), + fun(Bytes@8) -> + gleam@result:'try'( + parse_minutes( + Bytes@8 + ), + fun( + _use0@4 + ) -> + {Minutes, + Bytes@9} = _use0@4, + gleam@result:'try'( + accept_byte( + Bytes@9, + 16#3A + ), + fun( + Bytes@10 + ) -> + gleam@result:'try'( + parse_seconds( + Bytes@10 + ), + fun( + _use0@5 + ) -> + {Seconds, + Bytes@11} = _use0@5, + gleam@result:'try'( + parse_second_fraction_as_nanoseconds( + Bytes@11 + ), + fun( + _use0@6 + ) -> + {Second_fraction_as_nanoseconds, + Bytes@12} = _use0@6, + gleam@result:'try'( + parse_offset( + Bytes@12 + ), + fun( + _use0@7 + ) -> + {Offset_seconds, + Bytes@13} = _use0@7, + gleam@result:'try'( + accept_empty( + Bytes@13 + ), + fun( + _use0@8 + ) -> + nil = _use0@8, + {ok, + from_date_time( + Year, + Month, + Day, + Hours, + Minutes, + Seconds, + Second_fraction_as_nanoseconds, + Offset_seconds + )} + end + ) + end + ) + end + ) + end + ) + end + ) + end + ) + end + ) + end + ) + end + ) + end + ) + end + ) + end + ) + end + ) + end + ). diff --git a/build/dev/javascript/gleam_time/gleam_time_ffi.erl b/build/dev/javascript/gleam_time/gleam_time_ffi.erl new file mode 100644 index 0000000..34d8c88 --- /dev/null +++ b/build/dev/javascript/gleam_time/gleam_time_ffi.erl @@ -0,0 +1,12 @@ +-module(gleam_time_ffi). +-export([system_time/0, local_time_offset_seconds/0]). + +system_time() -> + {0, erlang:system_time(nanosecond)}. + +local_time_offset_seconds() -> + Utc = calendar:universal_time(), + Local = calendar:local_time(), + UtcSeconds = calendar:datetime_to_gregorian_seconds(Utc), + LocalSeconds = calendar:datetime_to_gregorian_seconds(Local), + LocalSeconds - UtcSeconds. diff --git a/build/dev/javascript/gleam_time/gleam_time_ffi.mjs b/build/dev/javascript/gleam_time/gleam_time_ffi.mjs new file mode 100644 index 0000000..27d09aa --- /dev/null +++ b/build/dev/javascript/gleam_time/gleam_time_ffi.mjs @@ -0,0 +1,11 @@ +export function system_time() { + const now = Date.now(); + const milliseconds = now % 1_000; + const nanoseconds = milliseconds * 1000_000; + const seconds = (now - milliseconds) / 1_000; + return [seconds, nanoseconds]; +} + +export function local_time_offset_seconds() { + return new Date().getTimezoneOffset() * -60; +} diff --git a/build/dev/javascript/gleam_version b/build/dev/javascript/gleam_version new file mode 100644 index 0000000..f88cf52 --- /dev/null +++ b/build/dev/javascript/gleam_version @@ -0,0 +1 @@ +1.13.0 \ No newline at end of file diff --git a/build/dev/javascript/gleeunit/_gleam_artefacts/gleeunit.cache b/build/dev/javascript/gleeunit/_gleam_artefacts/gleeunit.cache new file mode 100644 index 0000000000000000000000000000000000000000..b2264149d868b0e47176c36d1ee6bde3be23ad2f GIT binary patch literal 8472 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 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleeunit/_gleam_artefacts/gleeunit.cache_inline b/build/dev/javascript/gleeunit/_gleam_artefacts/gleeunit.cache_inline new file mode 100644 index 0000000000000000000000000000000000000000..1b1cb4d44c57c2d7a5122870fa6ac3e62ff7e94e GIT binary patch literal 8 JcmZR80ssIA00961 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleeunit/_gleam_artefacts/gleeunit.cache_meta b/build/dev/javascript/gleeunit/_gleam_artefacts/gleeunit.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..5c75cc992e83eca70e609b683e4827f800e34f78 GIT binary patch literal 479 zcmbQ@$ezgn1&qv41{ai0&q+SvM?}+vNAA8vNAA8vobKqure_Cure@AVP#;L z%F4hnpOt}O0V@N;237`!?W_z8J6RbRcCj)rykKQu&|_m@&}U;{aA#v+h+<=4h+$)3 zh-G77sAgke=w@SJ=w)MI=woAGSjfh}u$qm5VKW;8!+SOc1{QV(1~zsE26lD^1~YaB MhAJoqg)ayL04$6@YybcN literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleeunit/_gleam_artefacts/gleeunit@internal@gleam_panic.cache b/build/dev/javascript/gleeunit/_gleam_artefacts/gleeunit@internal@gleam_panic.cache new file mode 100644 index 0000000000000000000000000000000000000000..1f02d4328d69d8f36971b5f2780a955d293d782f GIT binary patch literal 14778 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 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleeunit/_gleam_artefacts/gleeunit@internal@gleam_panic.cache_inline b/build/dev/javascript/gleeunit/_gleam_artefacts/gleeunit@internal@gleam_panic.cache_inline new file mode 100644 index 0000000000000000000000000000000000000000..1b1cb4d44c57c2d7a5122870fa6ac3e62ff7e94e GIT binary patch literal 8 JcmZR80ssIA00961 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleeunit/_gleam_artefacts/gleeunit@internal@gleam_panic.cache_meta b/build/dev/javascript/gleeunit/_gleam_artefacts/gleeunit@internal@gleam_panic.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..8e2ed2a9188b90091516c49c2dadd3cbab677088 GIT binary patch literal 278 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 X1!aagpk+La#*al_fRZu4sE8;4cDgf3 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleeunit/_gleam_artefacts/gleeunit@internal@reporting.cache_inline b/build/dev/javascript/gleeunit/_gleam_artefacts/gleeunit@internal@reporting.cache_inline new file mode 100644 index 0000000000000000000000000000000000000000..1b1cb4d44c57c2d7a5122870fa6ac3e62ff7e94e GIT binary patch literal 8 JcmZR80ssIA00961 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleeunit/_gleam_artefacts/gleeunit@internal@reporting.cache_meta b/build/dev/javascript/gleeunit/_gleam_artefacts/gleeunit@internal@reporting.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..34ab986f3e8d6df32e28b332821560cc8a7c04bc GIT binary patch literal 1277 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 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/gleeunit/gleam.mjs b/build/dev/javascript/gleeunit/gleam.mjs new file mode 100644 index 0000000..197cbbc --- /dev/null +++ b/build/dev/javascript/gleeunit/gleam.mjs @@ -0,0 +1 @@ +export * from "../prelude.mjs"; diff --git a/build/dev/javascript/gleeunit/gleeunit.erl b/build/dev/javascript/gleeunit/gleeunit.erl new file mode 100644 index 0000000..bcb58b6 --- /dev/null +++ b/build/dev/javascript/gleeunit/gleeunit.erl @@ -0,0 +1,89 @@ +-module(gleeunit). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleeunit.gleam"). +-export([main/0]). +-export_type([atom_/0, encoding/0, report_module_name/0, gleeunit_progress_option/0, eunit_option/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +-type atom_() :: any(). + +-type encoding() :: utf8. + +-type report_module_name() :: gleeunit_progress. + +-type gleeunit_progress_option() :: {colored, boolean()}. + +-type eunit_option() :: verbose | + no_tty | + {report, {report_module_name(), list(gleeunit_progress_option())}} | + {scale_timeouts, integer()}. + +-file("src/gleeunit.gleam", 42). +-spec gleam_to_erlang_module_name(binary()) -> binary(). +gleam_to_erlang_module_name(Path) -> + case gleam_stdlib:string_ends_with(Path, <<".gleam"/utf8>>) of + true -> + _pipe = Path, + _pipe@1 = gleam@string:replace( + _pipe, + <<".gleam"/utf8>>, + <<""/utf8>> + ), + gleam@string:replace(_pipe@1, <<"/"/utf8>>, <<"@"/utf8>>); + + false -> + _pipe@2 = Path, + _pipe@3 = gleam@string:split(_pipe@2, <<"/"/utf8>>), + _pipe@4 = gleam@list:last(_pipe@3), + _pipe@5 = gleam@result:unwrap(_pipe@4, Path), + gleam@string:replace(_pipe@5, <<".erl"/utf8>>, <<""/utf8>>) + end. + +-file("src/gleeunit.gleam", 18). +-spec do_main() -> nil. +do_main() -> + Options = [verbose, + no_tty, + {report, {gleeunit_progress, [{colored, true}]}}, + {scale_timeouts, 10}], + Result = begin + _pipe = gleeunit_ffi:find_files( + <<"**/*.{erl,gleam}"/utf8>>, + <<"test"/utf8>> + ), + _pipe@1 = gleam@list:map(_pipe, fun gleam_to_erlang_module_name/1), + _pipe@2 = gleam@list:map( + _pipe@1, + fun(_capture) -> erlang:binary_to_atom(_capture, utf8) end + ), + gleeunit_ffi:run_eunit(_pipe@2, Options) + end, + Code = case Result of + {ok, _} -> + 0; + + {error, _} -> + 1 + end, + erlang:halt(Code). + +-file("src/gleeunit.gleam", 13). +?DOC( + " Find and run all test functions for the current project using Erlang's EUnit\n" + " test framework, or a custom JavaScript test runner.\n" + "\n" + " Any Erlang or Gleam function in the `test` directory with a name ending in\n" + " `_test` is considered a test function and will be run.\n" + "\n" + " A test that panics is considered a failure.\n" +). +-spec main() -> nil. +main() -> + do_main(). diff --git a/build/dev/javascript/gleeunit/gleeunit.mjs b/build/dev/javascript/gleeunit/gleeunit.mjs new file mode 100644 index 0000000..83018dd --- /dev/null +++ b/build/dev/javascript/gleeunit/gleeunit.mjs @@ -0,0 +1,62 @@ +import * as $list from "../gleam_stdlib/gleam/list.mjs"; +import * as $result from "../gleam_stdlib/gleam/result.mjs"; +import * as $string from "../gleam_stdlib/gleam/string.mjs"; +import { CustomType as $CustomType } from "./gleam.mjs"; +import { main as do_main } from "./gleeunit_ffi.mjs"; + +class Utf8 extends $CustomType {} + +class GleeunitProgress extends $CustomType {} + +class Colored extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} + +class Verbose extends $CustomType {} + +class NoTty extends $CustomType {} + +class Report extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} + +class ScaleTimeouts extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} + +function gleam_to_erlang_module_name(path) { + let $ = $string.ends_with(path, ".gleam"); + if ($) { + let _pipe = path; + let _pipe$1 = $string.replace(_pipe, ".gleam", ""); + return $string.replace(_pipe$1, "/", "@"); + } else { + let _pipe = path; + let _pipe$1 = $string.split(_pipe, "/"); + let _pipe$2 = $list.last(_pipe$1); + let _pipe$3 = $result.unwrap(_pipe$2, path); + return $string.replace(_pipe$3, ".erl", ""); + } +} + +/** + * Find and run all test functions for the current project using Erlang's EUnit + * test framework, or a custom JavaScript test runner. + * + * Any Erlang or Gleam function in the `test` directory with a name ending in + * `_test` is considered a test function and will be run. + * + * A test that panics is considered a failure. + */ +export function main() { + return do_main(); +} diff --git a/build/dev/javascript/gleeunit/gleeunit/internal/gleam_panic.mjs b/build/dev/javascript/gleeunit/gleeunit/internal/gleam_panic.mjs new file mode 100644 index 0000000..f6af89d --- /dev/null +++ b/build/dev/javascript/gleeunit/gleeunit/internal/gleam_panic.mjs @@ -0,0 +1,180 @@ +import * as $dynamic from "../../../gleam_stdlib/gleam/dynamic.mjs"; +import { CustomType as $CustomType } from "../../gleam.mjs"; +import { from_dynamic } from "./gleeunit_gleam_panic_ffi.mjs"; + +export { from_dynamic }; + +export class GleamPanic extends $CustomType { + constructor(message, file, module, function$, line, kind) { + super(); + this.message = message; + this.file = file; + this.module = module; + this.function = function$; + this.line = line; + this.kind = kind; + } +} +export const GleamPanic$GleamPanic = (message, file, module, function$, line, kind) => + new GleamPanic(message, file, module, function$, line, kind); +export const GleamPanic$isGleamPanic = (value) => value instanceof GleamPanic; +export const GleamPanic$GleamPanic$message = (value) => value.message; +export const GleamPanic$GleamPanic$0 = (value) => value.message; +export const GleamPanic$GleamPanic$file = (value) => value.file; +export const GleamPanic$GleamPanic$1 = (value) => value.file; +export const GleamPanic$GleamPanic$module = (value) => value.module; +export const GleamPanic$GleamPanic$2 = (value) => value.module; +export const GleamPanic$GleamPanic$function = (value) => value.function; +export const GleamPanic$GleamPanic$3 = (value) => value.function; +export const GleamPanic$GleamPanic$line = (value) => value.line; +export const GleamPanic$GleamPanic$4 = (value) => value.line; +export const GleamPanic$GleamPanic$kind = (value) => value.kind; +export const GleamPanic$GleamPanic$5 = (value) => value.kind; + +export class Todo extends $CustomType {} +export const PanicKind$Todo = () => new Todo(); +export const PanicKind$isTodo = (value) => value instanceof Todo; + +export class Panic extends $CustomType {} +export const PanicKind$Panic = () => new Panic(); +export const PanicKind$isPanic = (value) => value instanceof Panic; + +export class LetAssert extends $CustomType { + constructor(start, end, pattern_start, pattern_end, value) { + super(); + this.start = start; + this.end = end; + this.pattern_start = pattern_start; + this.pattern_end = pattern_end; + this.value = value; + } +} +export const PanicKind$LetAssert = (start, end, pattern_start, pattern_end, value) => + new LetAssert(start, end, pattern_start, pattern_end, value); +export const PanicKind$isLetAssert = (value) => value instanceof LetAssert; +export const PanicKind$LetAssert$start = (value) => value.start; +export const PanicKind$LetAssert$0 = (value) => value.start; +export const PanicKind$LetAssert$end = (value) => value.end; +export const PanicKind$LetAssert$1 = (value) => value.end; +export const PanicKind$LetAssert$pattern_start = (value) => value.pattern_start; +export const PanicKind$LetAssert$2 = (value) => value.pattern_start; +export const PanicKind$LetAssert$pattern_end = (value) => value.pattern_end; +export const PanicKind$LetAssert$3 = (value) => value.pattern_end; +export const PanicKind$LetAssert$value = (value) => value.value; +export const PanicKind$LetAssert$4 = (value) => value.value; + +export class Assert extends $CustomType { + constructor(start, end, expression_start, kind) { + super(); + this.start = start; + this.end = end; + this.expression_start = expression_start; + this.kind = kind; + } +} +export const PanicKind$Assert = (start, end, expression_start, kind) => + new Assert(start, end, expression_start, kind); +export const PanicKind$isAssert = (value) => value instanceof Assert; +export const PanicKind$Assert$start = (value) => value.start; +export const PanicKind$Assert$0 = (value) => value.start; +export const PanicKind$Assert$end = (value) => value.end; +export const PanicKind$Assert$1 = (value) => value.end; +export const PanicKind$Assert$expression_start = (value) => + value.expression_start; +export const PanicKind$Assert$2 = (value) => value.expression_start; +export const PanicKind$Assert$kind = (value) => value.kind; +export const PanicKind$Assert$3 = (value) => value.kind; + +export class BinaryOperator extends $CustomType { + constructor(operator, left, right) { + super(); + this.operator = operator; + this.left = left; + this.right = right; + } +} +export const AssertKind$BinaryOperator = (operator, left, right) => + new BinaryOperator(operator, left, right); +export const AssertKind$isBinaryOperator = (value) => + value instanceof BinaryOperator; +export const AssertKind$BinaryOperator$operator = (value) => value.operator; +export const AssertKind$BinaryOperator$0 = (value) => value.operator; +export const AssertKind$BinaryOperator$left = (value) => value.left; +export const AssertKind$BinaryOperator$1 = (value) => value.left; +export const AssertKind$BinaryOperator$right = (value) => value.right; +export const AssertKind$BinaryOperator$2 = (value) => value.right; + +export class FunctionCall extends $CustomType { + constructor(arguments$) { + super(); + this.arguments = arguments$; + } +} +export const AssertKind$FunctionCall = (arguments$) => + new FunctionCall(arguments$); +export const AssertKind$isFunctionCall = (value) => + value instanceof FunctionCall; +export const AssertKind$FunctionCall$arguments = (value) => value.arguments; +export const AssertKind$FunctionCall$0 = (value) => value.arguments; + +export class OtherExpression extends $CustomType { + constructor(expression) { + super(); + this.expression = expression; + } +} +export const AssertKind$OtherExpression = (expression) => + new OtherExpression(expression); +export const AssertKind$isOtherExpression = (value) => + value instanceof OtherExpression; +export const AssertKind$OtherExpression$expression = (value) => + value.expression; +export const AssertKind$OtherExpression$0 = (value) => value.expression; + +export class AssertedExpression extends $CustomType { + constructor(start, end, kind) { + super(); + this.start = start; + this.end = end; + this.kind = kind; + } +} +export const AssertedExpression$AssertedExpression = (start, end, kind) => + new AssertedExpression(start, end, kind); +export const AssertedExpression$isAssertedExpression = (value) => + value instanceof AssertedExpression; +export const AssertedExpression$AssertedExpression$start = (value) => + value.start; +export const AssertedExpression$AssertedExpression$0 = (value) => value.start; +export const AssertedExpression$AssertedExpression$end = (value) => value.end; +export const AssertedExpression$AssertedExpression$1 = (value) => value.end; +export const AssertedExpression$AssertedExpression$kind = (value) => value.kind; +export const AssertedExpression$AssertedExpression$2 = (value) => value.kind; + +export class Literal extends $CustomType { + constructor(value) { + super(); + this.value = value; + } +} +export const ExpressionKind$Literal = (value) => new Literal(value); +export const ExpressionKind$isLiteral = (value) => value instanceof Literal; +export const ExpressionKind$Literal$value = (value) => value.value; +export const ExpressionKind$Literal$0 = (value) => value.value; + +export class Expression extends $CustomType { + constructor(value) { + super(); + this.value = value; + } +} +export const ExpressionKind$Expression = (value) => new Expression(value); +export const ExpressionKind$isExpression = (value) => + value instanceof Expression; +export const ExpressionKind$Expression$value = (value) => value.value; +export const ExpressionKind$Expression$0 = (value) => value.value; + +export class Unevaluated extends $CustomType {} +export const ExpressionKind$Unevaluated = () => new Unevaluated(); +export const ExpressionKind$isUnevaluated = (value) => + value instanceof Unevaluated; diff --git a/build/dev/javascript/gleeunit/gleeunit/internal/gleeunit_gleam_panic_ffi.erl b/build/dev/javascript/gleeunit/gleeunit/internal/gleeunit_gleam_panic_ffi.erl new file mode 100644 index 0000000..d78f5e5 --- /dev/null +++ b/build/dev/javascript/gleeunit/gleeunit/internal/gleeunit_gleam_panic_ffi.erl @@ -0,0 +1,49 @@ +-module(gleeunit_gleam_panic_ffi). +-export([from_dynamic/1]). + +from_dynamic(#{ + gleam_error := assert, + start := Start, + 'end' := End, + expression_start := EStart +} = E) -> + wrap(E, {assert, Start, End, EStart, assert_kind(E)}); +from_dynamic(#{ + gleam_error := let_assert, + start := Start, + 'end' := End, + pattern_start := PStart, + pattern_end := PEnd, + value := Value +} = E) -> + wrap(E, {let_assert, Start, End, PStart, PEnd, Value}); +from_dynamic(#{gleam_error := panic} = E) -> + wrap(E, panic); +from_dynamic(#{gleam_error := todo} = E) -> + wrap(E, todo); +from_dynamic(_) -> + {error, nil}. + +assert_kind(#{kind := binary_operator, left := L, right := R, operator := O}) -> + {binary_operator, atom_to_binary(O), expression(L), expression(R)}; +assert_kind(#{kind := function_call, arguments := Arguments}) -> + {function_call, lists:map(fun expression/1, Arguments)}; +assert_kind(#{kind := expression, expression := Expression}) -> + {other_expression, expression(Expression)}. + +expression(#{start := S, 'end' := E, kind := literal, value := Value}) -> + {asserted_expression, S, E, {literal, Value}}; +expression(#{start := S, 'end' := E, kind := expression, value := Value}) -> + {asserted_expression, S, E, {expression, Value}}; +expression(#{start := S, 'end' := E, kind := unevaluated}) -> + {asserted_expression, S, E, unevaluated}. + +wrap(#{ + gleam_error := _, + file := File, + message := Message, + module := Module, + function := Function, + line := Line +}, Kind) -> + {ok, {gleam_panic, Message, File, Module, Function, Line, Kind}}. diff --git a/build/dev/javascript/gleeunit/gleeunit/internal/gleeunit_gleam_panic_ffi.mjs b/build/dev/javascript/gleeunit/gleeunit/internal/gleeunit_gleam_panic_ffi.mjs new file mode 100644 index 0000000..03f6025 --- /dev/null +++ b/build/dev/javascript/gleeunit/gleeunit/internal/gleeunit_gleam_panic_ffi.mjs @@ -0,0 +1,91 @@ +import { Result$Ok, Result$Error, List$Empty, List$NonEmpty } from "../../gleam.mjs"; +import { + GleamPanic$GleamPanic, + PanicKind$Todo, + PanicKind$Panic, + PanicKind$LetAssert, + PanicKind$Assert, + AssertKind$BinaryOperator, + AssertKind$FunctionCall, + AssertKind$OtherExpression, + AssertedExpression$AssertedExpression, + ExpressionKind$Literal, + ExpressionKind$Expression, + ExpressionKind$Unevaluated, +} from "./gleam_panic.mjs"; + +export function from_dynamic(error) { + if (!(error instanceof globalThis.Error) || !error.gleam_error) { + return Result$Error(undefined); + } + + if (error.gleam_error === "todo") { + return wrap(error, PanicKind$Todo()); + } + + if (error.gleam_error === "panic") { + return wrap(error, PanicKind$Panic()); + } + + if (error.gleam_error === "let_assert") { + let kind = PanicKind$LetAssert( + error.start, + error.end, + error.pattern_start, + error.pattern_end, + error.value, + ); + return wrap(error, kind); + } + + if (error.gleam_error === "assert") { + let kind = PanicKind$Assert( + error.start, + error.end, + error.expression_start, + assert_kind(error), + ); + return wrap(error, kind); + } + + return Result$Error(undefined); +} + +function assert_kind(error) { + if (error.kind == "binary_operator") { + return AssertKind$BinaryOperator( + error.operator, + expression(error.left), + expression(error.right), + ); + } + + if (error.kind == "function_call") { + let list = List$Empty(); + let i = error.arguments.length; + while (i--) { + list = List$NonEmpty(expression(error.arguments[i]), list); + } + return AssertKind$FunctionCall(list); + } + + return AssertKind$OtherExpression(expression(error.expression)); +} + +function expression(data) { + const expression = AssertedExpression$AssertedExpression(data.start, data.end, undefined); + if (data.kind == "literal") { + expression.kind = ExpressionKind$Literal(data.value); + } else if (data.kind == "expression") { + expression.kind = ExpressionKind$Expression(data.value); + } else { + expression.kind = ExpressionKind$Unevaluated(); + } + return expression; +} + +function wrap(e, kind) { + return Result$Ok( + GleamPanic$GleamPanic(e.message, e.file, e.module, e.function, e.line, kind), + ); +} diff --git a/build/dev/javascript/gleeunit/gleeunit/internal/reporting.mjs b/build/dev/javascript/gleeunit/gleeunit/internal/reporting.mjs new file mode 100644 index 0000000..8ebf9a2 --- /dev/null +++ b/build/dev/javascript/gleeunit/gleeunit/internal/reporting.mjs @@ -0,0 +1,256 @@ +import * as $bit_array from "../../../gleam_stdlib/gleam/bit_array.mjs"; +import * as $dynamic from "../../../gleam_stdlib/gleam/dynamic.mjs"; +import * as $int from "../../../gleam_stdlib/gleam/int.mjs"; +import * as $io from "../../../gleam_stdlib/gleam/io.mjs"; +import * as $list from "../../../gleam_stdlib/gleam/list.mjs"; +import * as $option from "../../../gleam_stdlib/gleam/option.mjs"; +import * as $result from "../../../gleam_stdlib/gleam/result.mjs"; +import * as $string from "../../../gleam_stdlib/gleam/string.mjs"; +import { Ok, Error, toList, CustomType as $CustomType } from "../../gleam.mjs"; +import * as $gleam_panic from "../../gleeunit/internal/gleam_panic.mjs"; +import { read_file as read_file_text } from "../../gleeunit_ffi.mjs"; + +export class State extends $CustomType { + constructor(passed, failed, skipped) { + super(); + this.passed = passed; + this.failed = failed; + this.skipped = skipped; + } +} +export const State$State = (passed, failed, skipped) => + new State(passed, failed, skipped); +export const State$isState = (value) => value instanceof State; +export const State$State$passed = (value) => value.passed; +export const State$State$0 = (value) => value.passed; +export const State$State$failed = (value) => value.failed; +export const State$State$1 = (value) => value.failed; +export const State$State$skipped = (value) => value.skipped; +export const State$State$2 = (value) => value.skipped; + +export function new_state() { + return new State(0, 0, 0); +} + +function bold(text) { + return ("\u{001b}[1m" + text) + "\u{001b}[22m"; +} + +function cyan(text) { + return ("\u{001b}[36m" + text) + "\u{001b}[39m"; +} + +function code_snippet(src, start, end) { + let _pipe = $result.try$( + $option.to_result(src, undefined), + (src) => { + return $result.try$( + $bit_array.slice(src, start, end - start), + (snippet) => { + return $result.try$( + $bit_array.to_string(snippet), + (snippet) => { + let snippet$1 = ((cyan(" code") + ": ") + snippet) + "\n"; + return new Ok(snippet$1); + }, + ); + }, + ); + }, + ); + return $result.unwrap(_pipe, ""); +} + +function yellow(text) { + return ("\u{001b}[33m" + text) + "\u{001b}[39m"; +} + +export function test_skipped(state, module, function$) { + $io.print(((("\n" + module) + ".") + function$) + yellow(" skipped")); + return new State(state.passed, state.failed, state.skipped + 1); +} + +function green(text) { + return ("\u{001b}[32m" + text) + "\u{001b}[39m"; +} + +export function test_passed(state) { + $io.print(green(".")); + return new State(state.passed + 1, state.failed, state.skipped); +} + +function red(text) { + return ("\u{001b}[31m" + text) + "\u{001b}[39m"; +} + +export function finished(state) { + let $ = state.failed; + if ($ === 0) { + let $1 = state.skipped; + if ($1 === 0) { + let $2 = state.passed; + if ($2 === 0) { + $io.println("\nNo tests found!"); + return 1; + } else { + let message = ("\n" + $int.to_string(state.passed)) + " passed, no failures"; + $io.println(green(message)); + return 0; + } + } else { + let message = ((("\n" + $int.to_string(state.passed)) + " passed, 0 failures, ") + $int.to_string( + state.skipped, + )) + " skipped"; + $io.println(yellow(message)); + return 1; + } + } else { + let $1 = state.skipped; + if ($1 === 0) { + let message = ((("\n" + $int.to_string(state.passed)) + " passed, ") + $int.to_string( + state.failed, + )) + " failures"; + $io.println(red(message)); + return 1; + } else { + let message = ((((("\n" + $int.to_string(state.passed)) + " passed, ") + $int.to_string( + state.failed, + )) + " failures, ") + $int.to_string(state.skipped)) + " skipped"; + $io.println(red(message)); + return 1; + } + } +} + +export function eunit_missing() { + let message = bold(red("Error")) + ": EUnit libraries not found.\n\nYour Erlang installation seems to be incomplete. If you installed Erlang using\na package manager ensure that you have installed the full Erlang\ndistribution instead of a stripped-down version.\n"; + $io.print_error(message); + return new Error(undefined); +} + +function grey(text) { + return ("\u{001b}[90m" + text) + "\u{001b}[39m"; +} + +function format_unknown(module, function$, error) { + return $string.concat( + toList([ + grey((module + ".") + function$) + "\n", + "An unexpected error occurred:\n", + "\n", + (" " + $string.inspect(error)) + "\n", + ]), + ); +} + +function inspect_value(value) { + let $ = value.kind; + if ($ instanceof $gleam_panic.Literal) { + return grey("literal"); + } else if ($ instanceof $gleam_panic.Expression) { + let value$1 = $.value; + return $string.inspect(value$1); + } else { + return grey("unevaluated"); + } +} + +function assert_value(name, value) { + return ((cyan(name) + ": ") + inspect_value(value)) + "\n"; +} + +function assert_info(kind) { + if (kind instanceof $gleam_panic.BinaryOperator) { + let left = kind.left; + let right = kind.right; + return $string.concat( + toList([assert_value(" left", left), assert_value("right", right)]), + ); + } else if (kind instanceof $gleam_panic.FunctionCall) { + let arguments$ = kind.arguments; + let _pipe = arguments$; + let _pipe$1 = $list.index_map( + _pipe, + (e, i) => { + let number = $string.pad_start($int.to_string(i), 5, " "); + return assert_value(number, e); + }, + ); + return $string.concat(_pipe$1); + } else { + return ""; + } +} + +function format_gleam_error(error, module, function$, src) { + let location = grey((error.file + ":") + $int.to_string(error.line)); + let $ = error.kind; + if ($ instanceof $gleam_panic.Todo) { + return $string.concat( + toList([ + ((bold(yellow("todo")) + " ") + location) + "\n", + ((((cyan(" test") + ": ") + module) + ".") + function$) + "\n", + ((cyan(" info") + ": ") + error.message) + "\n", + ]), + ); + } else if ($ instanceof $gleam_panic.Panic) { + return $string.concat( + toList([ + ((bold(red("panic")) + " ") + location) + "\n", + ((((cyan(" test") + ": ") + module) + ".") + function$) + "\n", + ((cyan(" info") + ": ") + error.message) + "\n", + ]), + ); + } else if ($ instanceof $gleam_panic.LetAssert) { + let start = $.start; + let end = $.end; + let value = $.value; + return $string.concat( + toList([ + ((bold(red("let assert")) + " ") + location) + "\n", + ((((cyan(" test") + ": ") + module) + ".") + function$) + "\n", + code_snippet(src, start, end), + ((cyan("value") + ": ") + $string.inspect(value)) + "\n", + ((cyan(" info") + ": ") + error.message) + "\n", + ]), + ); + } else { + let start = $.start; + let end = $.end; + let kind = $.kind; + return $string.concat( + toList([ + ((bold(red("assert")) + " ") + location) + "\n", + ((((cyan(" test") + ": ") + module) + ".") + function$) + "\n", + code_snippet(src, start, end), + assert_info(kind), + ((cyan(" info") + ": ") + error.message) + "\n", + ]), + ); + } +} + +function read_file(path) { + let $ = read_file_text(path); + if ($ instanceof Ok) { + let text = $[0]; + return new Ok($bit_array.from_string(text)); + } else { + return $; + } +} + +export function test_failed(state, module, function$, error) { + let _block; + let $ = $gleam_panic.from_dynamic(error); + if ($ instanceof Ok) { + let error$1 = $[0]; + let src = $option.from_result(read_file(error$1.file)); + _block = format_gleam_error(error$1, module, function$, src); + } else { + _block = format_unknown(module, function$, error); + } + let message = _block; + $io.print("\n" + message); + return new State(state.passed, state.failed + 1, state.skipped); +} diff --git a/build/dev/javascript/gleeunit/gleeunit/should.mjs b/build/dev/javascript/gleeunit/gleeunit/should.mjs new file mode 100644 index 0000000..c34c700 --- /dev/null +++ b/build/dev/javascript/gleeunit/gleeunit/should.mjs @@ -0,0 +1,135 @@ +import * as $option from "../../gleam_stdlib/gleam/option.mjs"; +import { None, Some } from "../../gleam_stdlib/gleam/option.mjs"; +import * as $string from "../../gleam_stdlib/gleam/string.mjs"; +import { Ok, Error, toList, makeError, isEqual } from "../gleam.mjs"; + +const FILEPATH = "src/gleeunit/should.gleam"; + +export function equal(a, b) { + let $ = isEqual(a, b); + if ($) { + return undefined; + } else { + throw makeError( + "panic", + FILEPATH, + "gleeunit/should", + 10, + "equal", + $string.concat( + toList([ + "\n", + $string.inspect(a), + "\nshould equal\n", + $string.inspect(b), + ]), + ), + {} + ) + } +} + +export function not_equal(a, b) { + let $ = !isEqual(a, b); + if ($) { + return undefined; + } else { + throw makeError( + "panic", + FILEPATH, + "gleeunit/should", + 23, + "not_equal", + $string.concat( + toList([ + "\n", + $string.inspect(a), + "\nshould not equal\n", + $string.inspect(b), + ]), + ), + {} + ) + } +} + +export function be_ok(a) { + if (a instanceof Ok) { + let value = a[0]; + return value; + } else { + throw makeError( + "panic", + FILEPATH, + "gleeunit/should", + 35, + "be_ok", + $string.concat(toList(["\n", $string.inspect(a), "\nshould be ok"])), + {} + ) + } +} + +export function be_error(a) { + if (a instanceof Error) { + let error = a[0]; + return error; + } else { + throw makeError( + "panic", + FILEPATH, + "gleeunit/should", + 42, + "be_error", + $string.concat(toList(["\n", $string.inspect(a), "\nshould be error"])), + {} + ) + } +} + +export function be_some(a) { + if (a instanceof Some) { + let value = a[0]; + return value; + } else { + throw makeError( + "panic", + FILEPATH, + "gleeunit/should", + 49, + "be_some", + $string.concat(toList(["\n", $string.inspect(a), "\nshould be some"])), + {} + ) + } +} + +export function be_none(a) { + if (a instanceof None) { + return undefined; + } else { + throw makeError( + "panic", + FILEPATH, + "gleeunit/should", + 56, + "be_none", + $string.concat(toList(["\n", $string.inspect(a), "\nshould be none"])), + {} + ) + } +} + +export function be_true(actual) { + let _pipe = actual; + return equal(_pipe, true); +} + +export function be_false(actual) { + let _pipe = actual; + return equal(_pipe, false); +} + +export function fail() { + return be_true(false); +} diff --git a/build/dev/javascript/gleeunit/gleeunit@internal@gleam_panic.erl b/build/dev/javascript/gleeunit/gleeunit@internal@gleam_panic.erl new file mode 100644 index 0000000..398ea7d --- /dev/null +++ b/build/dev/javascript/gleeunit/gleeunit@internal@gleam_panic.erl @@ -0,0 +1,56 @@ +-module(gleeunit@internal@gleam_panic). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleeunit/internal/gleam_panic.gleam"). +-export([from_dynamic/1]). +-export_type([gleam_panic/0, panic_kind/0, assert_kind/0, asserted_expression/0, expression_kind/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC(false). + +-type gleam_panic() :: {gleam_panic, + binary(), + binary(), + binary(), + binary(), + integer(), + panic_kind()}. + +-type panic_kind() :: todo | + panic | + {let_assert, + integer(), + integer(), + integer(), + integer(), + gleam@dynamic:dynamic_()} | + {assert, integer(), integer(), integer(), assert_kind()}. + +-type assert_kind() :: {binary_operator, + binary(), + asserted_expression(), + asserted_expression()} | + {function_call, list(asserted_expression())} | + {other_expression, asserted_expression()}. + +-type asserted_expression() :: {asserted_expression, + integer(), + integer(), + expression_kind()}. + +-type expression_kind() :: {literal, gleam@dynamic:dynamic_()} | + {expression, gleam@dynamic:dynamic_()} | + unevaluated. + +-file("src/gleeunit/internal/gleam_panic.gleam", 49). +?DOC(false). +-spec from_dynamic(gleam@dynamic:dynamic_()) -> {ok, gleam_panic()} | + {error, nil}. +from_dynamic(Data) -> + gleeunit_gleam_panic_ffi:from_dynamic(Data). diff --git a/build/dev/javascript/gleeunit/gleeunit@internal@reporting.erl b/build/dev/javascript/gleeunit/gleeunit@internal@reporting.erl new file mode 100644 index 0000000..8c37c79 --- /dev/null +++ b/build/dev/javascript/gleeunit/gleeunit@internal@reporting.erl @@ -0,0 +1,343 @@ +-module(gleeunit@internal@reporting). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleeunit/internal/reporting.gleam"). +-export([new_state/0, test_skipped/3, test_passed/1, finished/1, eunit_missing/0, test_failed/4]). +-export_type([state/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC(false). + +-type state() :: {state, integer(), integer(), integer()}. + +-file("src/gleeunit/internal/reporting.gleam", 15). +?DOC(false). +-spec new_state() -> state(). +new_state() -> + {state, 0, 0, 0}. + +-file("src/gleeunit/internal/reporting.gleam", 207). +?DOC(false). +-spec bold(binary()) -> binary(). +bold(Text) -> + <<<<"\x{001b}[1m"/utf8, Text/binary>>/binary, "\x{001b}[22m"/utf8>>. + +-file("src/gleeunit/internal/reporting.gleam", 211). +?DOC(false). +-spec cyan(binary()) -> binary(). +cyan(Text) -> + <<<<"\x{001b}[36m"/utf8, Text/binary>>/binary, "\x{001b}[39m"/utf8>>. + +-file("src/gleeunit/internal/reporting.gleam", 191). +?DOC(false). +-spec code_snippet(gleam@option:option(bitstring()), integer(), integer()) -> binary(). +code_snippet(Src, Start, End) -> + _pipe = begin + gleam@result:'try'( + gleam@option:to_result(Src, nil), + fun(Src@1) -> + gleam@result:'try'( + gleam_stdlib:bit_array_slice(Src@1, Start, End - Start), + fun(Snippet) -> + gleam@result:'try'( + gleam@bit_array:to_string(Snippet), + fun(Snippet@1) -> + Snippet@2 = <<<<<<(cyan(<<" code"/utf8>>))/binary, + ": "/utf8>>/binary, + Snippet@1/binary>>/binary, + "\n"/utf8>>, + {ok, Snippet@2} + end + ) + end + ) + end + ) + end, + gleam@result:unwrap(_pipe, <<""/utf8>>). + +-file("src/gleeunit/internal/reporting.gleam", 215). +?DOC(false). +-spec yellow(binary()) -> binary(). +yellow(Text) -> + <<<<"\x{001b}[33m"/utf8, Text/binary>>/binary, "\x{001b}[39m"/utf8>>. + +-file("src/gleeunit/internal/reporting.gleam", 202). +?DOC(false). +-spec test_skipped(state(), binary(), binary()) -> state(). +test_skipped(State, Module, Function) -> + gleam_stdlib:print( + <<<<<<<<"\n"/utf8, Module/binary>>/binary, "."/utf8>>/binary, + Function/binary>>/binary, + (yellow(<<" skipped"/utf8>>))/binary>> + ), + {state, + erlang:element(2, State), + erlang:element(3, State), + erlang:element(4, State) + 1}. + +-file("src/gleeunit/internal/reporting.gleam", 219). +?DOC(false). +-spec green(binary()) -> binary(). +green(Text) -> + <<<<"\x{001b}[32m"/utf8, Text/binary>>/binary, "\x{001b}[39m"/utf8>>. + +-file("src/gleeunit/internal/reporting.gleam", 66). +?DOC(false). +-spec test_passed(state()) -> state(). +test_passed(State) -> + gleam_stdlib:print(green(<<"."/utf8>>)), + {state, + erlang:element(2, State) + 1, + erlang:element(3, State), + erlang:element(4, State)}. + +-file("src/gleeunit/internal/reporting.gleam", 223). +?DOC(false). +-spec red(binary()) -> binary(). +red(Text) -> + <<<<"\x{001b}[31m"/utf8, Text/binary>>/binary, "\x{001b}[39m"/utf8>>. + +-file("src/gleeunit/internal/reporting.gleam", 19). +?DOC(false). +-spec finished(state()) -> integer(). +finished(State) -> + case State of + {state, 0, 0, 0} -> + gleam_stdlib:println(<<"\nNo tests found!"/utf8>>), + 1; + + {state, _, 0, 0} -> + Message = <<<<"\n"/utf8, + (erlang:integer_to_binary(erlang:element(2, State)))/binary>>/binary, + " passed, no failures"/utf8>>, + gleam_stdlib:println(green(Message)), + 0; + + {state, _, _, 0} -> + Message@1 = <<<<<<<<"\n"/utf8, + (erlang:integer_to_binary(erlang:element(2, State)))/binary>>/binary, + " passed, "/utf8>>/binary, + (erlang:integer_to_binary(erlang:element(3, State)))/binary>>/binary, + " failures"/utf8>>, + gleam_stdlib:println(red(Message@1)), + 1; + + {state, _, 0, _} -> + Message@2 = <<<<<<<<"\n"/utf8, + (erlang:integer_to_binary(erlang:element(2, State)))/binary>>/binary, + " passed, 0 failures, "/utf8>>/binary, + (erlang:integer_to_binary(erlang:element(4, State)))/binary>>/binary, + " skipped"/utf8>>, + gleam_stdlib:println(yellow(Message@2)), + 1; + + {state, _, _, _} -> + Message@3 = <<<<<<<<<<<<"\n"/utf8, + (erlang:integer_to_binary( + erlang:element(2, State) + ))/binary>>/binary, + " passed, "/utf8>>/binary, + (erlang:integer_to_binary(erlang:element(3, State)))/binary>>/binary, + " failures, "/utf8>>/binary, + (erlang:integer_to_binary(erlang:element(4, State)))/binary>>/binary, + " skipped"/utf8>>, + gleam_stdlib:println(red(Message@3)), + 1 + end. + +-file("src/gleeunit/internal/reporting.gleam", 89). +?DOC(false). +-spec eunit_missing() -> {ok, any()} | {error, nil}. +eunit_missing() -> + Message = <<(bold(red(<<"Error"/utf8>>)))/binary, + ": EUnit libraries not found. + +Your Erlang installation seems to be incomplete. If you installed Erlang using +a package manager ensure that you have installed the full Erlang +distribution instead of a stripped-down version. +"/utf8>>, + gleam_stdlib:print_error(Message), + {error, nil}. + +-file("src/gleeunit/internal/reporting.gleam", 227). +?DOC(false). +-spec grey(binary()) -> binary(). +grey(Text) -> + <<<<"\x{001b}[90m"/utf8, Text/binary>>/binary, "\x{001b}[39m"/utf8>>. + +-file("src/gleeunit/internal/reporting.gleam", 100). +?DOC(false). +-spec format_unknown(binary(), binary(), gleam@dynamic:dynamic_()) -> binary(). +format_unknown(Module, Function, Error) -> + erlang:list_to_binary( + [<<(grey(<<<>/binary, Function/binary>>))/binary, + "\n"/utf8>>, + <<"An unexpected error occurred:\n"/utf8>>, + <<"\n"/utf8>>, + <<<<" "/utf8, (gleam@string:inspect(Error))/binary>>/binary, + "\n"/utf8>>] + ). + +-file("src/gleeunit/internal/reporting.gleam", 183). +?DOC(false). +-spec inspect_value(gleeunit@internal@gleam_panic:asserted_expression()) -> binary(). +inspect_value(Value) -> + case erlang:element(4, Value) of + unevaluated -> + grey(<<"unevaluated"/utf8>>); + + {literal, _} -> + grey(<<"literal"/utf8>>); + + {expression, Value@1} -> + gleam@string:inspect(Value@1) + end. + +-file("src/gleeunit/internal/reporting.gleam", 179). +?DOC(false). +-spec assert_value( + binary(), + gleeunit@internal@gleam_panic:asserted_expression() +) -> binary(). +assert_value(Name, Value) -> + <<<<<<(cyan(Name))/binary, ": "/utf8>>/binary, + (inspect_value(Value))/binary>>/binary, + "\n"/utf8>>. + +-file("src/gleeunit/internal/reporting.gleam", 160). +?DOC(false). +-spec assert_info(gleeunit@internal@gleam_panic:assert_kind()) -> binary(). +assert_info(Kind) -> + case Kind of + {binary_operator, _, Left, Right} -> + erlang:list_to_binary( + [assert_value(<<" left"/utf8>>, Left), + assert_value(<<"right"/utf8>>, Right)] + ); + + {function_call, Arguments} -> + _pipe = Arguments, + _pipe@1 = gleam@list:index_map( + _pipe, + fun(E, I) -> + Number = gleam@string:pad_start( + erlang:integer_to_binary(I), + 5, + <<" "/utf8>> + ), + assert_value(Number, E) + end + ), + erlang:list_to_binary(_pipe@1); + + {other_expression, _} -> + <<""/utf8>> + end. + +-file("src/gleeunit/internal/reporting.gleam", 113). +?DOC(false). +-spec format_gleam_error( + gleeunit@internal@gleam_panic:gleam_panic(), + binary(), + binary(), + gleam@option:option(bitstring()) +) -> binary(). +format_gleam_error(Error, Module, Function, Src) -> + Location = grey( + <<<<(erlang:element(3, Error))/binary, ":"/utf8>>/binary, + (erlang:integer_to_binary(erlang:element(6, Error)))/binary>> + ), + case erlang:element(7, Error) of + panic -> + erlang:list_to_binary( + [<<<<<<(bold(red(<<"panic"/utf8>>)))/binary, " "/utf8>>/binary, + Location/binary>>/binary, + "\n"/utf8>>, + <<<<<<<<<<(cyan(<<" test"/utf8>>))/binary, ": "/utf8>>/binary, + Module/binary>>/binary, + "."/utf8>>/binary, + Function/binary>>/binary, + "\n"/utf8>>, + <<<<<<(cyan(<<" info"/utf8>>))/binary, ": "/utf8>>/binary, + (erlang:element(2, Error))/binary>>/binary, + "\n"/utf8>>] + ); + + todo -> + erlang:list_to_binary( + [<<<<<<(bold(yellow(<<"todo"/utf8>>)))/binary, " "/utf8>>/binary, + Location/binary>>/binary, + "\n"/utf8>>, + <<<<<<<<<<(cyan(<<" test"/utf8>>))/binary, ": "/utf8>>/binary, + Module/binary>>/binary, + "."/utf8>>/binary, + Function/binary>>/binary, + "\n"/utf8>>, + <<<<<<(cyan(<<" info"/utf8>>))/binary, ": "/utf8>>/binary, + (erlang:element(2, Error))/binary>>/binary, + "\n"/utf8>>] + ); + + {assert, Start, End, _, Kind} -> + erlang:list_to_binary( + [<<<<<<(bold(red(<<"assert"/utf8>>)))/binary, " "/utf8>>/binary, + Location/binary>>/binary, + "\n"/utf8>>, + <<<<<<<<<<(cyan(<<" test"/utf8>>))/binary, ": "/utf8>>/binary, + Module/binary>>/binary, + "."/utf8>>/binary, + Function/binary>>/binary, + "\n"/utf8>>, + code_snippet(Src, Start, End), + assert_info(Kind), + <<<<<<(cyan(<<" info"/utf8>>))/binary, ": "/utf8>>/binary, + (erlang:element(2, Error))/binary>>/binary, + "\n"/utf8>>] + ); + + {let_assert, Start@1, End@1, _, _, Value} -> + erlang:list_to_binary( + [<<<<<<(bold(red(<<"let assert"/utf8>>)))/binary, " "/utf8>>/binary, + Location/binary>>/binary, + "\n"/utf8>>, + <<<<<<<<<<(cyan(<<" test"/utf8>>))/binary, ": "/utf8>>/binary, + Module/binary>>/binary, + "."/utf8>>/binary, + Function/binary>>/binary, + "\n"/utf8>>, + code_snippet(Src, Start@1, End@1), + <<<<<<(cyan(<<"value"/utf8>>))/binary, ": "/utf8>>/binary, + (gleam@string:inspect(Value))/binary>>/binary, + "\n"/utf8>>, + <<<<<<(cyan(<<" info"/utf8>>))/binary, ": "/utf8>>/binary, + (erlang:element(2, Error))/binary>>/binary, + "\n"/utf8>>] + ) + end. + +-file("src/gleeunit/internal/reporting.gleam", 71). +?DOC(false). +-spec test_failed(state(), binary(), binary(), gleam@dynamic:dynamic_()) -> state(). +test_failed(State, Module, Function, Error) -> + Message = case gleeunit_gleam_panic_ffi:from_dynamic(Error) of + {ok, Error@1} -> + Src = gleam@option:from_result( + file:read_file(erlang:element(3, Error@1)) + ), + format_gleam_error(Error@1, Module, Function, Src); + + {error, _} -> + format_unknown(Module, Function, Error) + end, + gleam_stdlib:print(<<"\n"/utf8, Message/binary>>), + {state, + erlang:element(2, State), + erlang:element(3, State) + 1, + erlang:element(4, State)}. diff --git a/build/dev/javascript/gleeunit/gleeunit@should.erl b/build/dev/javascript/gleeunit/gleeunit@should.erl new file mode 100644 index 0000000..81048de --- /dev/null +++ b/build/dev/javascript/gleeunit/gleeunit@should.erl @@ -0,0 +1,153 @@ +-module(gleeunit@should). +-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). +-define(FILEPATH, "src/gleeunit/should.gleam"). +-export([equal/2, not_equal/2, be_ok/1, be_error/1, be_some/1, be_none/1, be_true/1, be_false/1, fail/0]). + +-if(?OTP_RELEASE >= 27). +-define(MODULEDOC(Str), -moduledoc(Str)). +-define(DOC(Str), -doc(Str)). +-else. +-define(MODULEDOC(Str), -compile([])). +-define(DOC(Str), -compile([])). +-endif. + +?MODULEDOC(" Use the `assert` keyword instead of this module.\n"). + +-file("src/gleeunit/should.gleam", 6). +-spec equal(DOF, DOF) -> nil. +equal(A, B) -> + case A =:= B of + true -> + nil; + + _ -> + erlang:error(#{gleam_error => panic, + message => erlang:list_to_binary( + [<<"\n"/utf8>>, + gleam@string:inspect(A), + <<"\nshould equal\n"/utf8>>, + gleam@string:inspect(B)] + ), + file => <>, + module => <<"gleeunit/should"/utf8>>, + function => <<"equal"/utf8>>, + line => 10}) + end. + +-file("src/gleeunit/should.gleam", 19). +-spec not_equal(DOG, DOG) -> nil. +not_equal(A, B) -> + case A /= B of + true -> + nil; + + _ -> + erlang:error(#{gleam_error => panic, + message => erlang:list_to_binary( + [<<"\n"/utf8>>, + gleam@string:inspect(A), + <<"\nshould not equal\n"/utf8>>, + gleam@string:inspect(B)] + ), + file => <>, + module => <<"gleeunit/should"/utf8>>, + function => <<"not_equal"/utf8>>, + line => 23}) + end. + +-file("src/gleeunit/should.gleam", 32). +-spec be_ok({ok, DOH} | {error, any()}) -> DOH. +be_ok(A) -> + case A of + {ok, Value} -> + Value; + + _ -> + erlang:error(#{gleam_error => panic, + message => erlang:list_to_binary( + [<<"\n"/utf8>>, + gleam@string:inspect(A), + <<"\nshould be ok"/utf8>>] + ), + file => <>, + module => <<"gleeunit/should"/utf8>>, + function => <<"be_ok"/utf8>>, + line => 35}) + end. + +-file("src/gleeunit/should.gleam", 39). +-spec be_error({ok, any()} | {error, DOM}) -> DOM. +be_error(A) -> + case A of + {error, Error} -> + Error; + + _ -> + erlang:error(#{gleam_error => panic, + message => erlang:list_to_binary( + [<<"\n"/utf8>>, + gleam@string:inspect(A), + <<"\nshould be error"/utf8>>] + ), + file => <>, + module => <<"gleeunit/should"/utf8>>, + function => <<"be_error"/utf8>>, + line => 42}) + end. + +-file("src/gleeunit/should.gleam", 46). +-spec be_some(gleam@option:option(DOP)) -> DOP. +be_some(A) -> + case A of + {some, Value} -> + Value; + + _ -> + erlang:error(#{gleam_error => panic, + message => erlang:list_to_binary( + [<<"\n"/utf8>>, + gleam@string:inspect(A), + <<"\nshould be some"/utf8>>] + ), + file => <>, + module => <<"gleeunit/should"/utf8>>, + function => <<"be_some"/utf8>>, + line => 49}) + end. + +-file("src/gleeunit/should.gleam", 53). +-spec be_none(gleam@option:option(any())) -> nil. +be_none(A) -> + case A of + none -> + nil; + + _ -> + erlang:error(#{gleam_error => panic, + message => erlang:list_to_binary( + [<<"\n"/utf8>>, + gleam@string:inspect(A), + <<"\nshould be none"/utf8>>] + ), + file => <>, + module => <<"gleeunit/should"/utf8>>, + function => <<"be_none"/utf8>>, + line => 56}) + end. + +-file("src/gleeunit/should.gleam", 60). +-spec be_true(boolean()) -> nil. +be_true(Actual) -> + _pipe = Actual, + equal(_pipe, true). + +-file("src/gleeunit/should.gleam", 65). +-spec be_false(boolean()) -> nil. +be_false(Actual) -> + _pipe = Actual, + equal(_pipe, false). + +-file("src/gleeunit/should.gleam", 70). +-spec fail() -> nil. +fail() -> + be_true(false). diff --git a/build/dev/javascript/gleeunit/gleeunit_ffi.erl b/build/dev/javascript/gleeunit/gleeunit_ffi.erl new file mode 100644 index 0000000..05c7490 --- /dev/null +++ b/build/dev/javascript/gleeunit/gleeunit_ffi.erl @@ -0,0 +1,21 @@ +-module(gleeunit_ffi). + +-export([find_files/2, run_eunit/2]). + +find_files(Pattern, In) -> + Results = filelib:wildcard(binary_to_list(Pattern), binary_to_list(In)), + lists:map(fun list_to_binary/1, Results). + +run_eunit(Tests, Options) -> + case code:which(eunit) of + non_existing -> + gleeunit@internal@reporting:eunit_missing(); + + _ -> + case eunit:test(Tests, Options) of + ok -> {ok, nil}; + error -> {error, nil}; + {error, Term} -> {error, Term} + end + end. + diff --git a/build/dev/javascript/gleeunit/gleeunit_ffi.mjs b/build/dev/javascript/gleeunit/gleeunit_ffi.mjs new file mode 100644 index 0000000..7bdc071 --- /dev/null +++ b/build/dev/javascript/gleeunit/gleeunit_ffi.mjs @@ -0,0 +1,100 @@ +import { readFileSync } from "node:fs"; +import { Result$Ok, Result$Error } from "./gleam.mjs"; +import * as reporting from "./gleeunit/internal/reporting.mjs"; + +export function read_file(path) { + try { + return Result$Ok(readFileSync(path)); + } catch { + return Result$Error(undefined); + } +} + +async function* gleamFiles(directory) { + for (let entry of await read_dir(directory)) { + let path = join_path(directory, entry); + if (path.endsWith(".gleam")) { + yield path; + } else { + try { + yield* gleamFiles(path); + } catch (error) { + // Could not read directory, assume it's a file + } + } + } +} + +async function readRootPackageName() { + let toml = await async_read_file("gleam.toml", "utf-8"); + for (let line of toml.split("\n")) { + let matches = line.match(/\s*name\s*=\s*"([a-z][a-z0-9_]*)"/); // Match regexp in compiler-cli/src/new.rs in validate_name() + if (matches) return matches[1]; + } + throw new Error("Could not determine package name from gleam.toml"); +} + +export async function main() { + let state = reporting.new_state(); + + let packageName = await readRootPackageName(); + let dist = `../${packageName}/`; + + for await (let path of await gleamFiles("test")) { + let js_path = path.slice("test/".length).replace(".gleam", ".mjs"); + let module = await import(join_path(dist, js_path)); + for (let fnName of Object.keys(module)) { + if (!fnName.endsWith("_test")) continue; + try { + await module[fnName](); + state = reporting.test_passed(state); + } catch (error) { + let moduleName = js_path.slice(0, -4); + state = reporting.test_failed(state, moduleName, fnName, error); + } + } + } + + const status = reporting.finished(state); + exit(status); +} + +export function crash(message) { + throw new Error(message); +} + +function exit(code) { + if (globalThis.Deno) { + Deno.exit(code); + } else { + process.exit(code); + } +} + +async function read_dir(path) { + if (globalThis.Deno) { + let items = []; + for await (let item of Deno.readDir(path, { withFileTypes: true })) { + items.push(item.name); + } + return items; + } else { + let { readdir } = await import("node:fs/promises"); + return readdir(path); + } +} + +function join_path(a, b) { + if (a.endsWith("/")) return a + b; + return a + "/" + b; +} + +async function async_read_file(path) { + if (globalThis.Deno) { + return Deno.readTextFile(path); + } else { + let { readFile } = await import("node:fs/promises"); + let contents = await readFile(path); + return contents.toString(); + } +} diff --git a/build/dev/javascript/gleeunit/gleeunit_progress.erl b/build/dev/javascript/gleeunit/gleeunit_progress.erl new file mode 100644 index 0000000..e6576a5 --- /dev/null +++ b/build/dev/javascript/gleeunit/gleeunit_progress.erl @@ -0,0 +1,72 @@ +%% A formatter adapted from Sean Cribb's https://github.com/seancribbs/eunit_formatters + +-module(gleeunit_progress). +-define(NOTEST, true). + +%% eunit_listener callbacks +-export([ + init/1, handle_begin/3, handle_end/3, handle_cancel/3, terminate/2, + start/0, start/1 +]). + +-define(reporting, gleeunit@internal@reporting). + +start() -> + start([]). + +start(Options) -> + eunit_listener:start(?MODULE, Options). + +init(_Options) -> + ?reporting:new_state(). + +handle_begin(_test_or_group, _data, State) -> + State. + +handle_end(group, _data, State) -> + State; +handle_end(test, Data, State) -> + {AtomModule, AtomFunction, _Arity} = proplists:get_value(source, Data), + Module = erlang:atom_to_binary(AtomModule), + Function = erlang:atom_to_binary(AtomFunction), + + % EUnit swallows stdout, so print it to make debugging easier. + case proplists:get_value(output, Data) of + undefined -> ok; + <<>> -> ok; + Out -> gleam@io:print(Out) + end, + + case proplists:get_value(status, Data) of + ok -> + ?reporting:test_passed(State); + {skipped, _Reason} -> + ?reporting:test_skipped(State, Module, Function); + {error, {_, Exception, _Stack}} -> + ?reporting:test_failed(State, Module, Function, Exception) + end. + + +handle_cancel(_test_or_group, Data, State) -> + ?reporting:test_failed(State, <<"gleeunit">>, <<"main">>, Data). + +terminate({ok, _Data}, State) -> + ?reporting:finished(State), + ok; +terminate({error, Reason}, State) -> + ?reporting:finished(State), + io:fwrite(" +Eunit failed: + +~80p + +This is probably a bug in gleeunit. Please report it. +", [Reason]), + sync_end(error). + +sync_end(Result) -> + receive + {stop, Reference, ReplyTo} -> + ReplyTo ! {result, Reference, Result}, + ok + end. diff --git a/build/dev/javascript/paint/_gleam_artefacts/paint.cache b/build/dev/javascript/paint/_gleam_artefacts/paint.cache new file mode 100644 index 0000000000000000000000000000000000000000..18552154b4a7fe4b9f32ad9e44d493d6d0ef7cd7 GIT binary patch literal 18173 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 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/paint/_gleam_artefacts/paint@canvas.cache b/build/dev/javascript/paint/_gleam_artefacts/paint@canvas.cache new file mode 100644 index 0000000000000000000000000000000000000000..315922bc999f3e705a373271cab134b26ba56169 GIT binary patch literal 20428 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 I}l28PFs3=Cq( za`DOexw)lznI)C_$@w|?rA1E|85kIu7#LWgstOV_^GcYQ7#L)k5XOQ9^;7eb^HWmg zm>3ugnHU(jku~V2mZj#E7%?$01Trx&NTEn&=9Q!t!o~Jl`Saty(tCe}onwS5W?*2jW?*2bWnf@9!@$50$;iO4 zgpq*(6c!*qgM0+?0LY;r7lG^onKg-tf#CrY1A_)L1A{3u1A{p;1A_%K14A4$1498b z1H&|C28J2T3=A`w85o{2GcdemW?=Zw%)p?|!oZ-*!oc9h!oU#3!oX0#!oYBvg@NHd z3j@PT76yioEDQ`^Sr{1DSQ!|ySs57Cure?_WMyFBVq;*?V`E?lXJcSUV`E?_U}In? zV`E_GVq;)9%*Md*nvH?sBO3$5S2hL)Ms@}UW_AV!7Ip>(8+HZ;7j_1QSat@6$?Oaa zGuas!7P2!iEM;e4*v8JlAkD$R5W~U1(8a;Pu#$s;;VcIO!#55F23Af627XQk22oB1 z22D-|hGG#LvL+jGuvlSAc;*MSy`pLx6$7QhF9aAEz6mfe=m|0~mU(aLvaR%5OD^EHgN`qrQ!?>tHl`@)`&AO91~|?cq-1o zz$n4Mpd!J*5GKLEP%6Q|P%Xj0P$R*>FkOOyVXXuM!+r?{hHDZG3_Owy3_g+!3}KQC z3~`bS3@MTf4D%%!7*0ttFfd6mFgQptFt|xEFjPu0Fie$VV7Mv8z`!WYz@RG4z@R71 zz>px#z)&yEz|bnqz|bkpz|bwtz|bSjz;Iidfx%OTfgw$Xfgw+ZfuUZ8fuU1|fuUE1 zfnl2r1H(QU28JUt3=Ah^7#Nsk85qQ585k^O85n$I85lxj85p8u85rVZ85mk+85q{e zGB9kGWnkDY%fN74mVx2BECa(|Sq27HIR*wXIR*w}IR=IZIR=I@IR=I{OL6L#MN0EUcNs)meN0EV{RFQ$9T9JXFUXg)en<4|lYefczH;N1l z>`Dv_T1pHI)=CTv$w~|iGnE(^ZYwb`NGmfi_$o6ngex;J#3?f{Bq=j6Br7v8)G9MD zoKt3C_^iypz^cN)ps&Kf5U;|(uvUeE;fo3b1HUQ*gNG^ugQqG3L%%8m!zNV*hC8YZ z4DVGL7`W9K7^Kt~7);d|7#!6Y7`)XO7<|+i7|PTb81|?!F#J_xU@%r^V6atZV6anX zU}#lmU^u7F!0=0*fk9G(fgwdF)(;*F))N^F)+kw zF)(ClF)&nUF)-9>F)-9?F)%b}F)-}VVqiF>#lY}Vi-Cbhn}I=Bn}H!vn}MN3n}MNU zn}K1KHUq;^Z3c!1+6)Z8wHX-1bQl;6br=`|bQl;4bQl;W=`b)X&|zR$qQk(jOoxGC zxef!v104p2KROHyI=T!DmbwfKwYm%pb-D};D|Hzdp6W6%i0Uygi0Lsf80axD1nDs_ z1nV&{RO>M?Ow(gvxTD9wz^%`~prFsd5Twt*5TVb&5Tnn)5U0<;5U~vh73anhB<}|4DSpX7+8!L7^ICD7;=pm7%Gey z80w7}7+Q=N7785o|LGBBu^F)(PDF)(PEF)(PGF)*Z>F)%DJV_;Zs#=vmR UjDdmQoPj~uoPj|UieWV<0Po;R8UO$Q literal 0 HcmV?d00001 diff --git a/build/dev/javascript/paint/_gleam_artefacts/paint@encode.cache b/build/dev/javascript/paint/_gleam_artefacts/paint@encode.cache new file mode 100644 index 0000000000000000000000000000000000000000..ab7aec2e5a668a73355d739a315c5daa685ac78a GIT binary patch literal 10664 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 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/paint/_gleam_artefacts/paint@encode.cache_inline b/build/dev/javascript/paint/_gleam_artefacts/paint@encode.cache_inline new file mode 100644 index 0000000000000000000000000000000000000000..1b1cb4d44c57c2d7a5122870fa6ac3e62ff7e94e GIT binary patch literal 8 JcmZR80ssIA00961 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/paint/_gleam_artefacts/paint@encode.cache_meta b/build/dev/javascript/paint/_gleam_artefacts/paint@encode.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..a0d85838d8b2e138a7eb7105dbb930644955e7bf GIT binary patch literal 1250 zcmZ>H^gn|E3K&_T3=t@uo|BrGtDjPtmzbNGte=vaoS%{klG0*eVBi8V7#J8JYO;#+ z^RyWl7(5sl7{rie;*;}pb4&9wODgq~^KSyMaq!#5R=IEDH7Niz8GcYjlF)}b@+I1=1(ReVOl@V$W0|NudYLLMob3p1A zGB7Z_W?*38Wn^IBXJlXyU}Ru0XJlZoW@KPE$;iN9$;7}gm5G62CKCgLFf#)~Br^j; z2Qvf1N@fOzoy-gj51APlcv%=2gjg6D)L0l8G*}oIG+7uJ0$3OrN?8~f+F2MFj<7H= zJYZp9c*4TKz{JYHz{1MFz{<+NAj!(W5Wvd7P|V7}P|C`{P{zu@uz;0;VHYa{!z)$> zhS#hN4BuH97}(et80^^?7%JEp7}l~eFg#{sV0g*Kz#ziTz@W~~z~If!z!1;Qz|h6c zz%YrOf#Co<1H&Hv>a3 zHv_{YZU%;9+zbq7xEUDka5FFn@h~uW@h~t{@Gvmc@h~t<=3!ts#KXYwk%xi7fR}+G zgO`CJkC%a=o|l1PBQFEP8(szmO+E&Oay|xzT0RDb1$+z)5BL}uNAxwaQp-X^)VUhp?!wdlih7$q| z3}*xw7|sbWFq{`)VE8M*z+foIz)&E_z_3z~fnl{E1H&3Y28Mfr3=HBz3=BF#3=H8y z3=DZf3=FG<7#Lm&F)+vrGcc$MGcZI8Gcd#pGcY6yGcY6xGcZgPW?U;4iZU>;h%qqmiZL)4h%qp@h%qqah%qqC5My9CA;!RPQH+7%yBGrlw>SfX znK%Q3qc{UYs5k>dhd2Yn3ULO858@0A|HK&>I3*Yu3?&#CtRxs1;v^UtiX<2q=14Fw z9Ft&R_$I-?@Lz&~K}C{*!9tROAyJZnp+S;?VZI~-!+A*th8vO$41XmV7zCvl7_6lj z7&4?77-mQ@Ff5c}U^pbjz;I29f#IJN1A~e*1B0(L14EcJ14F$u1H)u#28ILD3=H?B z85sUaGcf4MFff?QFfdfeFfjDUFfeSDVPLo}!@$5K%fO&7%fMhE%fMhK%fL`7%fQen z%fN6%mVx2CECa(cSq27PIR*xMIR=JoIR=JGIR=InIR=I;atsW6I!Zpt$-NGmWfI4UqO zgeovFv?wqzY*1iexUay#@Irxs;f(?V!#f2AhW82#3=)bA491EK3`vR%3@K0yD;)q; C!?~*f literal 0 HcmV?d00001 diff --git a/build/dev/javascript/paint/_gleam_artefacts/paint@event.cache b/build/dev/javascript/paint/_gleam_artefacts/paint@event.cache new file mode 100644 index 0000000000000000000000000000000000000000..787d70de55eca5d48fdd80fc7795bed9f199ffdf GIT binary patch literal 9101 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 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/paint/_gleam_artefacts/paint@internal@impl_canvas.cache b/build/dev/javascript/paint/_gleam_artefacts/paint@internal@impl_canvas.cache new file mode 100644 index 0000000000000000000000000000000000000000..f7d3a724dfe6868290a132205977c0dee90e914c GIT binary patch literal 16937 zcmY#nVlmgNiE7t%+b%xEy#&aPRuJ~PArxOOa9jnElw>e*3aNB zP0!EOFGwva&d*ECNl7i!FD^;V$w@4VFDNR_OVv**&CE&BFGx(zPE1cN)(30WFD^=k zvNH3~tw=1^OV3G7%w=G36krmRb#jzp7u-6_aSyw2EMt^&0wcTdU&bOQCiZ%-V$a;f z^wdBbCN{xNc0s{gfeMU_f|6W79LV{}Ifj!(n3sE%vj{hfup&>DvkDK3a4gR(VJ{{|;YaL@ z!b}{D!d~2r!bRMS!l(Ecg&*-T3VZS>2}=nv3M+{-3LA+t3SW|96#gW|D7;CQQTUWB zqi~l3qwp#PMqwuvM&Te8M&T-TM&U{7jKV=$jKW!3jKW&FjKWU3jKWn0jKY%)7= z*^fa!J|{mhB~XHiEm(z-ku#8kkx`J5i-C!ek%2)_v_0?$6B`3_AO{m01EZj+lc4D> zK~Hu;TO&am#y|%~MzAVZ_CObqD)O@)1EZj*lAx%Ope(zfAeW#3W1t8lBfDQ_jy$9+ z0cB56ZYQXQk(H52P_Za5hmlcG@s?l?qo5|2U*?w1>FHQ}#Nzm*%)FG$Jp1%wz1*x~bki9ajb{j47GM;SFG?*= zEdd2{;2Dfib`Q(}g`Sq&M3lPoLH7R6v9ahPdI@+K}s-b5ez1obUt`%patLl@r0Rf|<&|sC<*1gHhyvdTL30QEFZaBdA_ak5A5* z2UqMklUV?AcCB$SA0KOR$1bP?t-vgb`Gf1sX6i z3Mw$-@*0r=YRJmPC@8w=Ke$-a)6-{$Hx=O}8>C=kV8CS)1EcX?#@CF{P|i-RjA!6a z&QF0P%@bJ4x(wvNrE*=4wV3nFD?yGcNT6l~7Jy2-TY?4PIwA+9jsT?>gilBc%PgY9 z5@7)YqcIb+HZ!Bh|Kik=(t`M+RHnkx)Z&u(#JtSh#FEVXym zjEsURjD$Q%QgDAFD!2&*D+8mkB4Z*b38$o{W#*;Emoug&#V6#X-W$d<-6dtRH3J>fSF)#||_b`h52R97k(~21E^K;{i zi;_Xbq_7bqBWl}_iBYhNk%9Staz<)$c6@F=V`*_}d{Svi33GlPdtyU>+v4G-W8t z&KSIg5tOO|1;CBL(kQ{oTY|mpf{jLk4UB;;jEoG7f}K`^okoHa+XY*>1X~z`B|yz^ zMqyD#C1FuUBVk!ac40wAE@1&i#$XYMlpv#)uppz6up}e9FfSvQFb^Xms2HKBduJb% zT3ng~FGFBmgeXDFTY|pqf{sRl4vc{=VE1||33?g{2C@sfatXRH26|A|uf*sVu%j37 z{7(7#=v@ab^db;L5hE)ji=g5xSO%Q|u572ERJPdCI+1AxQq#^NDtltHhLJIVk&%%_ zkWt)`g-MXLNRWdOn)M2b801rni&Ik=nT-uujuV6&0?7<@qSi^yo z7{NkcCnqy6mBBu~JTs*vgR%%QqD6#|*F=NF2;NE$lx~sz zPD&&YQR$%@&%kIrh5G_GywdxDUi=t}wlfCLfK+-C;7V`nEWy3E1TV4+9yJm?!Wei0 zT#1}D5bCa93yc;7_!nM&koKro5mWro|5$IEUsc7xWkx6g?jJ1YF_pfa5!KmSF5H z!9sSyR3pI@#=s14+~isb<{Al>vI}N{NAPmMf|*u=nMPohTu_zN7)i%c3o&95BQorn z1r_m*4S_<2l)xuqYzSl|1EaAkUmG7N^t<3KPsaS*_`=fEqDolpA9w_<$tj55S0tvf zPQ{5Aa3dXWO%5v2Q{&T$ko-bQ@DWjyW7@!Eynz2CKPW7Nz~dgEN`iqAT1^bt2rd!b zc*x*J5WY?iXmA7SA5y|Gh+r9j98wI7#$v4Yte_BF1gdRI3*s~L7z;{E;xiKSQrL4+ zi=c&r0cPRQ9(Vy-Eh{F&%#DOKBGJ^V@j7$t1M2vY8 znWvcq6&Dd`ZDRV6l+ay7ICLR{7-*r$z-S!AG@S_)&Q|tC`6Y=ZsnCEWvzb5W+A>yn z>M^K~NeO5xf&mRRl!4JWlCz%^6tYeK!Lv8;z8vB3NUncT;m(q~RAMwU{1_|B;Q~j%z6x3 zLxE}|&;&P;4J(MUcIZ5K0V5-pfi_0a^gKH;)f>2ZMKI;U{6d423p1L5(YTdu5gRD` z8_5?ZC+5KFDKdKpM%a1>MzrW15Y=}hGEp-ND&n1A2L}u(u|UN9I@nAGM&qj-eBjzF z3S4l&;sug?121573)%wB*;8G*O zC5(Y9z@ryywFK8132t>4T*)Q4f-%?wGCAYQs3q*mXe8{*$S&;2$R+H+$QbMbk#c0z z5_V)X682q&WaJXIU}OxoVWdt{nVG$8zgvYQT<1M_&3bdY$L4Xmq(i6054Z5IXqxDmWs11;fW?(da&gTss_9-b!%qz}G zEMZ`TMeayzso)(&LJA&IVuXlMBqWm=7>)fnx;a4E%^kd)JjzjlMR4LQ#~Ccbyo^Q8 z0*oxejf_st4U8-()ngzJ6C0=kPGt`a0p}V~YOZW%c3^VAzJeQUZzj87CVOB6#9msg z3WL~>w&odZbt=0c!pqcNS_SeotegPbmdGxc$Q}qT8L75z1MD|J#&Sm)CP7oDKodqL zK}V%P2Sz5rRM0>WlQ1u16lm!JldvpfQLq9dBa^T$qgJpEh>^)SE4YA>kqPEV1_nV^ zc0opF2WHTc0T54KkdfJug;|hONsyOakd+G}?!XM~VlglXa+(V=3bQh^IC3xxib@Ge z%7gl0o<@Sc@`A2h%>LO742~?|<6OiaeiOhHV>^O+Vf8Er zUdZ>H&-fRgD8I2fzXiXs7k@CnaR~nce&ehB*Z7SU1pEYy{RL(SFy9ssU=m;w04;xK zWOrb4U>4+LGi2S*WSq*z&*)gfEF8&rOE{G=NH~*GT)2|)mT)U$lW-@axbRHITf$2j zorIS%iVJULyd`{)@ssdbMseZ0jJJeeGFl10WE2_cg-o}EYnfIFw=#(fPi49#ypX9$cqx;( z@K&Zg-cm(2{*EA67FOX7oN#-OL!?um+)E^apAozw}el!bO~Q% z5f^^Qa!dFvOO^0P7IC~a33$*DI&lG71H-{6z{tpKEX%6KieDe7!v`8Ef{*1O>CWY< z!M{5Oww4%4&ja@7>;!GZw~Q0XEDc6iM!aT0N28InBygs2;?)8UsBHK`TqLcW%%aSA zwSuN`k<=gOImLrlJ^FqJB)#(m)(GI$3+tdE>FMC@=f$h%KlYW22qOf`y6_tS8}){d zCgG9ScTf{nKXduJI=b!Qa5%6LooA!C&AQ$}}TRVE`) zrEaXsG@Vg+E3=XCR%R>Vt<1}XLs^W3Ls_hZLs`_JwPKNQD~r1DQWhiOr7T*)OIgH) zx3U-sZ)H&u-pb-G{FUXFFe9szFe7WYa3HG@2pb2ovNH+?vKa{nvS|qivbE#)1Gasv zjsZ-93m64wFw&o7WESLseqjPh2YEX!sL!bY+W01jK{7HM+cLX@ zyl{~DG>8giiDv=1ou!2ZvfBjIxo}Kj5$coLDjgirK3)^-OrNXWbij;}$Q$Q382RkTI`Z*?nC?QT! zP~1%BoCc!AxFkVwvzBWEh;rrj0L9G}?&~0`hNm7BHEi1JQU3fv z{EUpo=LIeaFftln7q}$=2~Ox-gQEtMU;v|_2P1hTBct&X##bP}mM~R;JgLuS3i8%$ zwuK;S5!VWkW39LyK&~v{D+9Txp05=|9pXO$a+tM%Bg{pR74VK6Oo9T8f*gzxl9AE4 zn57aFaLd@%5GX3q`tax`F&nRB+6Ho@EVC9fBox3yv+&Z0(Rds0E?$TXXy^@JdSx`$ zXEp*Q)Hy8kL9R3AHv@UcmEQw@hJf{^9YvS~H9%X=aFLA6#xwZlgF;Gu|`5uUJX7vD-Hv3o) zgQ#kb22kQ{<>&%Y|2UXIiI;AK?_FWNk&HF11v{CQFEH*JcughDF<06$EyUgK#$i5L|x~- Q4Wgd%z64QUd4KW(043g-GXMYp literal 0 HcmV?d00001 diff --git a/build/dev/javascript/paint/_gleam_artefacts/paint@internal@impl_canvas.cache_inline b/build/dev/javascript/paint/_gleam_artefacts/paint@internal@impl_canvas.cache_inline new file mode 100644 index 0000000000000000000000000000000000000000..1b1cb4d44c57c2d7a5122870fa6ac3e62ff7e94e GIT binary patch literal 8 JcmZR80ssIA00961 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/paint/_gleam_artefacts/paint@internal@impl_canvas.cache_meta b/build/dev/javascript/paint/_gleam_artefacts/paint@internal@impl_canvas.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..db2fae1046593a2b093df03f6ce20e110850a64e GIT binary patch literal 525 zcmcC}{6B*M3K*d@L-6a*C$~w;R+qyB7#J927#J9285kJy85kI*FfcGoWnf@9#=yX! z$jHEu$;iNv#mK<0myv;iiHU)MnTdfRh>3w=J`)4O0wxBA?Mw^|JD3<4n3x$DjF}l2 zOqdxMJeV06Jee67CNMKFJYZ&Ec*xAaV93J2;KRbekjTQo(8I#Ou#km;;TQ`8!vz)w zhKno=3}UPd44$kE3|_1Z3=>!x7>=+qFdSuNVBltBV6bCjV6bOnU}$AyVA#pVz_5#r zf#EkB1A_)T1A`_z14AY|1H)8y28L zax*Y^aWgPX;bvgC!Og&MlbeCTfQNyhfro*ik%xid01pGh86F0P=R6DyKX@1zICvQt z#CRDPRCpN}On4a>Y^X;WaM23I}?hCV(9hJHQE%m4)dKM+Jn literal 0 HcmV?d00001 diff --git a/build/dev/javascript/paint/_gleam_artefacts/paint@internal@types.cache b/build/dev/javascript/paint/_gleam_artefacts/paint@internal@types.cache new file mode 100644 index 0000000000000000000000000000000000000000..72e9025e695d3aa96a89732ed095dd1d5353ac67 GIT binary patch literal 14729 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 Biv$1w literal 0 HcmV?d00001 diff --git a/build/dev/javascript/paint/_gleam_artefacts/paint@internal@types.cache_inline b/build/dev/javascript/paint/_gleam_artefacts/paint@internal@types.cache_inline new file mode 100644 index 0000000000000000000000000000000000000000..1b1cb4d44c57c2d7a5122870fa6ac3e62ff7e94e GIT binary patch literal 8 JcmZR80ssIA00961 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/paint/_gleam_artefacts/paint@internal@types.cache_meta b/build/dev/javascript/paint/_gleam_artefacts/paint@internal@types.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..b521eaed65924bcf0341f2511db397d836e1910a GIT binary patch literal 283 zcmZ>H^gn|E3K$uo3^6F3o|BrG8=st?n_HTfSyHK=oS&0lS_G2RW?*2bIQfdt{jo71d=rS-cI503U_%JXqgn-mBFfc4C6la8O#g} tP0S1o+n5;`b}%zA>||zOIK|AsaGRNd;Vv@+!#!pOhIh;$$3ZdFF948hC&&N* literal 0 HcmV?d00001 diff --git a/build/dev/javascript/paint/gleam.mjs b/build/dev/javascript/paint/gleam.mjs new file mode 100644 index 0000000..197cbbc --- /dev/null +++ b/build/dev/javascript/paint/gleam.mjs @@ -0,0 +1 @@ +export * from "../prelude.mjs"; diff --git a/build/dev/javascript/paint/impl_canvas_bindings.mjs b/build/dev/javascript/paint/impl_canvas_bindings.mjs new file mode 100644 index 0000000..5fed16a --- /dev/null +++ b/build/dev/javascript/paint/impl_canvas_bindings.mjs @@ -0,0 +1,271 @@ +import { Ok, Error } from "./gleam.mjs"; + +class PaintCanvas extends HTMLElement { + // Open an issue if you are in need of any other attributes :) + static observedAttributes = ["width", "height", "style", "picture"]; + + constructor() { + super(); + // Create a canvas + this.canvas = document.createElement("canvas"); + const style = document.createElement("style"); + style.textContent = ` + :host { + display: inline-block; + } + `; + this.shadow = this.attachShadow({ mode: "open" }); + this.shadow.appendChild(style); + this.shadow.appendChild(this.canvas); + this.ctx = this.canvas.getContext("2d"); + } + + attributeChangedCallback(name, _oldValue, newValue) { + if (name === "picture") { + this.picture = newValue; + return; + } else if (name === "width") { + this.width = newValue; + } else if (name === "height") { + this.height = newValue; + } + } + + drawPicture() { + if (!this.pictureString) { + return; + } + + this.ctx.reset(); + const display = + window.PAINT_STATE[ + "display_on_rendering_context_with_default_drawing_state" + ]; + + display(this.pictureString, this.ctx); + } + + set picture(value) { + this.pictureString = value; + this.drawPicture(); + } + + set width(value) { + this.canvas.width = value; + this.drawPicture(); + } + + set height(value) { + this.canvas.height = value; + this.drawPicture(); + } + + get width() { + return this.canvas.width; + } + + get height() { + return this.canvas.height; + } +} + +export function define_web_component() { + window.customElements.define("paint-canvas", PaintCanvas); +} + +export function get_rendering_context(selector) { + // TODO: Handle the case where the canvas element is not found. + return document.querySelector(selector).getContext("2d"); +} + +export function setup_request_animation_frame(callback) { + window.requestAnimationFrame((time) => { + callback(time); + }); +} + +export function setup_input_handler(event_name, callback) { + window.addEventListener(event_name, callback); +} + +export function get_key_code(event) { + return event.keyCode; +} + +export function set_global(state, id) { + if (typeof window.PAINT_STATE == "undefined") { + window.PAINT_STATE = {}; + } + window.PAINT_STATE[id] = state; +} + +export function get_global(id) { + if (!window.PAINT_STATE) { + return new Error(undefined); + } + if (!(id in window.PAINT_STATE)) { + return new Error(undefined); + } + return new Ok(window.PAINT_STATE[id]); +} + +export function get_width(ctx) { + return ctx.canvas.clientWidth; +} + +export function get_height(ctx) { + return ctx.canvas.clientHeight; +} + +// Based on https://stackoverflow.com/questions/17130395/real-mouse-position-in-canvas +export function mouse_pos(ctx, event) { + // Calculate the scaling of the canvas vs its content + const rect = ctx.canvas.getBoundingClientRect(); + const scaleX = ctx.canvas.width / rect.width; + const scaleY = ctx.canvas.height / rect.height; + + return [ + (event.clientX - rect.left) * scaleX, + (event.clientY - rect.top) * scaleY, + ]; +} + +// if check_pressed is true, the function will return true if the button was pressed +// if check_pressed is false, the function will return true if the button was released +export function check_mouse_button( + event, + previous_event, + button_index, + check_pressed, +) { + let previous_buttons = previous_event?.buttons ?? 0; + let current_buttons = event.buttons; + + // ~001 && + // 011 + // ----- + // 010 found the newly pressed! + // + // 011 && + // ~001 + // ----- + // 010 found the newly released! + if (check_pressed) { + previous_buttons = ~previous_buttons; + } else { + current_buttons = ~current_buttons; + } + + let button = previous_buttons & current_buttons & (1 << button_index); + return !!button; +} + +export function reset(ctx) { + ctx.reset(); +} + +export function arc(ctx, radius, start, end, fill, stroke) { + ctx.beginPath(); + ctx.arc(0, 0, radius, start, end); + if (fill) { + ctx.fill(); + } + if (stroke) { + ctx.stroke(); + } +} + +export function polygon(ctx, points, closed, fill, stroke) { + ctx.beginPath(); + ctx.moveTo(0, 0); + let started = false; + for (const point of points) { + let x = point[0]; + let y = point[1]; + if (started) { + ctx.lineTo(x, y); + } else { + ctx.moveTo(x, y); + started = true; + } + } + + if (closed) { + ctx.closePath(); + } + + if (fill && closed) { + ctx.fill(); + } + + if (stroke) { + ctx.stroke(); + } +} + +export function text(ctx, text, style) { + ctx.font = style; + ctx.fillText(text, 0, 0); +} + +export function save(ctx) { + ctx.save(); +} + +export function restore(ctx) { + ctx.restore(); +} + +export function set_fill_colour(ctx, css_colour) { + ctx.fillStyle = css_colour; +} + +export function set_stroke_color(ctx, css_color) { + ctx.strokeStyle = css_color; +} + +export function set_line_width(ctx, width) { + ctx.lineWidth = width; +} + +export function translate(ctx, x, y) { + ctx.translate(x, y); +} + +export function scale(ctx, x, y) { + ctx.scale(x, y); +} + +export function rotate(ctx, radians) { + ctx.rotate(radians); +} + +export function reset_transform(ctx) { + ctx.resetTransform(); +} + +export function draw_image(ctx, image, width_px, height_px) { + ctx.drawImage(image, 0, 0, width_px, height_px); +} + +export function image_from_query(selector) { + return document.querySelector(selector); +} + +export function image_from_src(src) { + const image = new Image(); + image.src = src; + return image; +} + +export function on_image_load(image, callback) { + if (image.complete) { + callback(); + } else { + image.addEventListener("load", callback); + } +} + +export function set_image_smoothing_enabled(ctx, value) { + ctx.imageSmoothingEnabled = value; +} diff --git a/build/dev/javascript/paint/numbers_ffi.mjs b/build/dev/javascript/paint/numbers_ffi.mjs new file mode 100644 index 0000000..0e6e6d3 --- /dev/null +++ b/build/dev/javascript/paint/numbers_ffi.mjs @@ -0,0 +1,3 @@ +export function pi() { + return Math.PI; +} diff --git a/build/dev/javascript/paint/paint.mjs b/build/dev/javascript/paint/paint.mjs new file mode 100644 index 0000000..ba06945 --- /dev/null +++ b/build/dev/javascript/paint/paint.mjs @@ -0,0 +1,246 @@ +import * as $colour from "../gleam_community_colour/gleam_community/colour.mjs"; +import * as $result from "../gleam_stdlib/gleam/result.mjs"; +import { toList, makeError } from "./gleam.mjs"; +import { pi } from "./numbers_ffi.mjs"; +import * as $internal_implementation from "./paint/internal/types.mjs"; + +const FILEPATH = "src/paint.gleam"; + +/** + * Create an angle expressed in radians + */ +export function angle_rad(radians) { + return new $internal_implementation.Radians(radians); +} + +/** + * A utility around [colour.from_rgb_hex_string](https://hexdocs.pm/gleam_community_colour/gleam_community/colour.html#from_rgb_hex_string) + * (from `gleam_community/colour`) that **panics** on an invalid hex code. + */ +export function colour_hex(string) { + return $result.lazy_unwrap( + $colour.from_rgb_hex_string(string), + () => { + throw makeError( + "panic", + FILEPATH, + "paint", + 47, + "colour_hex", + "Failed to parse hex code", + {} + ) + }, + ); +} + +/** + * A utility around [colour.from_rgb255](https://hexdocs.pm/gleam_community_colour/gleam_community/colour.html#from_rgb255) + * (from `gleam_community/colour`) that **panics** if the values are outside of the allowed range. + */ +export function colour_rgb(red, green, blue) { + return $result.lazy_unwrap( + $colour.from_rgb255(red, green, blue), + () => { + throw makeError( + "panic", + FILEPATH, + "paint", + 55, + "colour_rgb", + "The value was not inside of the valid range [0-255]", + {} + ) + }, + ); +} + +/** + * A blank picture + */ +export function blank() { + return new $internal_implementation.Blank(); +} + +/** + * An arc with some radius going from some + * starting angle to some other angle in clock-wise direction + */ +export function arc(radius, start, end) { + return new $internal_implementation.Arc(radius, start, end); +} + +/** + * A polygon consisting of a list of 2d points + */ +export function polygon(points) { + return new $internal_implementation.Polygon(points, true); +} + +/** + * Lines (same as a polygon but not a closed shape) + */ +export function lines(points) { + return new $internal_implementation.Polygon(points, false); +} + +/** + * A rectangle with some given width and height + */ +export function rectangle(width, height) { + return polygon( + toList([[0.0, 0.0], [width, 0.0], [width, height], [0.0, height]]), + ); +} + +/** + * A square + */ +export function square(length) { + return rectangle(length, length); +} + +/** + * Draw an image such as a PNG, JPEG or an SVG. See the `canvas` back-end for more details on how to load images. + */ +export function image(image, width_px, height_px) { + return new $internal_implementation.ImageRef(image, width_px, height_px); +} + +/** + * Set image scaling to be smooth (this is the default behaviour) + */ +export function image_scaling_smooth(picture) { + return new $internal_implementation.ImageScalingBehaviour( + picture, + new $internal_implementation.ScalingSmooth(), + ); +} + +/** + * Disable smooth image scaling, suitable for pixel art. + */ +export function image_scaling_pixelated(picture) { + return new $internal_implementation.ImageScalingBehaviour( + picture, + new $internal_implementation.ScalingPixelated(), + ); +} + +/** + * Text with some given font size + */ +export function text(text, font_size) { + return new $internal_implementation.Text( + text, + new $internal_implementation.FontProperties(font_size, "sans-serif"), + ); +} + +/** + * Translate a picture in horizontal and vertical direction + */ +export function translate_xy(picture, x, y) { + return new $internal_implementation.Translate(picture, [x, y]); +} + +/** + * Translate a picture in the horizontal direction + */ +export function translate_x(picture, x) { + return translate_xy(picture, x, 0.0); +} + +/** + * Translate a picture in the vertical direction + */ +export function translate_y(picture, y) { + return translate_xy(picture, 0.0, y); +} + +/** + * Scale the picture in the horizontal direction + */ +export function scale_x(picture, factor) { + return new $internal_implementation.Scale(picture, [factor, 1.0]); +} + +/** + * Scale the picture in the vertical direction + */ +export function scale_y(picture, factor) { + return new $internal_implementation.Scale(picture, [1.0, factor]); +} + +/** + * Scale the picture uniformly in horizontal and vertical direction + */ +export function scale_uniform(picture, factor) { + return new $internal_implementation.Scale(picture, [factor, factor]); +} + +/** + * Rotate the picture in a clock-wise direction + */ +export function rotate(picture, angle) { + return new $internal_implementation.Rotate(picture, angle); +} + +/** + * Fill a picture with some given colour, see `Colour`. + */ +export function fill(picture, colour) { + return new $internal_implementation.Fill(picture, colour); +} + +/** + * Set a solid stroke with some given colour and width + */ +export function stroke(picture, colour, width) { + return new $internal_implementation.Stroke( + picture, + new $internal_implementation.SolidStroke(colour, width), + ); +} + +/** + * Remove the stroke of the given picture + */ +export function stroke_none(picture) { + return new $internal_implementation.Stroke( + picture, + new $internal_implementation.NoStroke(), + ); +} + +/** + * Combine multiple pictures into one + */ +export function combine(pictures) { + return new $internal_implementation.Combine(pictures); +} + +/** + * Concatenate two pictures + */ +export function concat(picture, another_picture) { + return combine(toList([picture, another_picture])); +} + +/** + * Create an angle expressed in degrees + */ +export function angle_deg(degrees) { + return new $internal_implementation.Radians(((degrees * pi())) / 180.0); +} + +/** + * A circle with some given radius + */ +export function circle(radius) { + return new $internal_implementation.Arc( + radius, + new $internal_implementation.Radians(0.0), + new $internal_implementation.Radians(2.0 * pi()), + ); +} diff --git a/build/dev/javascript/paint/paint/canvas.mjs b/build/dev/javascript/paint/paint/canvas.mjs new file mode 100644 index 0000000..1bb61c7 --- /dev/null +++ b/build/dev/javascript/paint/paint/canvas.mjs @@ -0,0 +1,655 @@ +import * as $colour from "../../gleam_community_colour/gleam_community/colour.mjs"; +import * as $int from "../../gleam_stdlib/gleam/int.mjs"; +import * as $option from "../../gleam_stdlib/gleam/option.mjs"; +import { None, Some } from "../../gleam_stdlib/gleam/option.mjs"; +import { Ok, Empty as $Empty, CustomType as $CustomType, makeError } from "../gleam.mjs"; +import * as $paint from "../paint.mjs"; +import { translate_xy } from "../paint.mjs"; +import * as $encode from "../paint/encode.mjs"; +import * as $event from "../paint/event.mjs"; +import * as $impl_canvas from "../paint/internal/impl_canvas.mjs"; +import * as $types from "../paint/internal/types.mjs"; +import { + Arc, + Blank, + Combine, + Fill, + FontProperties, + Image, + NoStroke, + Polygon, + Radians, + Rotate, + Scale, + SolidStroke, + Stroke, + Text, + Translate, +} from "../paint/internal/types.mjs"; + +const FILEPATH = "src/paint/canvas.gleam"; + +export class Config extends $CustomType { + constructor(width, height) { + super(); + this.width = width; + this.height = height; + } +} +export const Config$Config = (width, height) => new Config(width, height); +export const Config$isConfig = (value) => value instanceof Config; +export const Config$Config$width = (value) => value.width; +export const Config$Config$0 = (value) => value.width; +export const Config$Config$height = (value) => value.height; +export const Config$Config$1 = (value) => value.height; + +class DrawingState extends $CustomType { + constructor(fill, stroke) { + super(); + this.fill = fill; + this.stroke = stroke; + } +} + +/** + * Create a reference to an image using a CSS query selector. For example: + * ``` + * fn kitten() { + * canvas.image_from_query("#kitten") + * } + * // In the HTML file: + * // + * ``` + * + * > [!WARNING] + * > **Important**: Make sure the image has loaded before trying to draw a pictures referencing it. + * > You can do this using `canvas.wait_until_loaded` function. + */ +export function image_from_query(selector) { + let id = "image-selector-" + selector; + let $ = $impl_canvas.get_global(id); + if ($ instanceof Ok) { + undefined + } else { + let image = $impl_canvas.image_from_query(selector); + $impl_canvas.set_global(image, id) + } + return new Image(id); +} + +/** + * Create a reference to an image using a source path. + * ``` + * fn my_logo_image() { + * canvas.image_from_src("./priv/static/logo.svg") + * } + * ``` + * + * > [!WARNING] + * > **Important**: Make sure the image has loaded before trying to draw a pictures referencing it. + * > You can do this using `canvas.wait_until_loaded` function. + */ +export function image_from_src(src) { + let id = "image-src-" + src; + let $ = $impl_canvas.get_global(id); + if ($ instanceof Ok) { + undefined + } else { + let image = $impl_canvas.image_from_src(src); + $impl_canvas.set_global(image, id) + } + return new Image(id); +} + +/** + * Wait until a list of images have all been loaded, for example: + * ``` + * fn lucy() { + * canvas.image_from_query("#lucy") + * } + * + * fn cat() { + * canvas.image_from_src("./path/to/kitten.png") + * } + * + * pub fn main() { + * use <- canvas.wait_until_loaded([lucy(), kitten()]) + * // It is now safe to draw Pictures containing the images lucy and kitten :) + * } + * ``` + */ +export function wait_until_loaded(images, on_loaded) { + if (images instanceof $Empty) { + return on_loaded(); + } else { + let image = images.head; + let rest = images.tail; + let id; + id = image.id; + let $ = $impl_canvas.get_global(id); + let js_image; + if ($ instanceof Ok) { + js_image = $[0]; + } else { + throw makeError( + "let_assert", + FILEPATH, + "paint/canvas", + 101, + "wait_until_loaded", + "Pattern match failed, no pattern matched the value.", + { + value: $, + start: 3025, + end: 3077, + pattern_start: 3036, + pattern_end: 3048 + } + ) + } + return $impl_canvas.on_image_load( + js_image, + () => { return wait_until_loaded(rest, on_loaded); }, + ); + } +} + +function display_on_rendering_context(loop$picture, loop$ctx, loop$state) { + while (true) { + let picture = loop$picture; + let ctx = loop$ctx; + let state = loop$state; + if (picture instanceof Blank) { + return undefined; + } else if (picture instanceof Polygon) { + let points = picture[0]; + let closed = picture.closed; + return $impl_canvas.polygon(ctx, points, closed, state.fill, state.stroke); + } else if (picture instanceof Arc) { + let radius = picture.radius; + let start = picture.start; + let end = picture.end; + let start_radians; + start_radians = start[0]; + let end_radians; + end_radians = end[0]; + return $impl_canvas.arc( + ctx, + radius, + start_radians, + end_radians, + state.fill, + state.stroke, + ); + } else if (picture instanceof Text) { + let text = picture.text; + let properties = picture.style; + let size_px; + let font_family; + size_px = properties.size_px; + font_family = properties.font_family; + $impl_canvas.save(ctx); + $impl_canvas.text( + ctx, + text, + ($int.to_string(size_px) + "px ") + font_family, + ); + return $impl_canvas.restore(ctx); + } else if (picture instanceof $types.ImageRef) { + let width_px = picture.width_px; + let height_px = picture.height_px; + let id = picture[0].id; + let $ = $impl_canvas.get_global(id); + let image; + if ($ instanceof Ok) { + image = $[0]; + } else { + throw makeError( + "let_assert", + FILEPATH, + "paint/canvas", + 231, + "display_on_rendering_context", + "Pattern match failed, no pattern matched the value.", + { + value: $, + start: 6608, + end: 6657, + pattern_start: 6619, + pattern_end: 6628 + } + ) + } + return $impl_canvas.draw_image(ctx, image, width_px, height_px); + } else if (picture instanceof Fill) { + let p = picture[0]; + let colour = picture[1]; + $impl_canvas.save(ctx); + $impl_canvas.set_fill_colour(ctx, $colour.to_css_rgba_string(colour)); + display_on_rendering_context(p, ctx, new DrawingState(true, state.stroke)); + return $impl_canvas.restore(ctx); + } else if (picture instanceof Stroke) { + let p = picture[0]; + let stroke = picture[1]; + if (stroke instanceof NoStroke) { + loop$picture = p; + loop$ctx = ctx; + loop$state = new DrawingState(state.fill, false); + } else { + let color = stroke[0]; + let width = stroke[1]; + $impl_canvas.save(ctx); + $impl_canvas.set_stroke_color(ctx, $colour.to_css_rgba_string(color)); + $impl_canvas.set_line_width(ctx, width); + display_on_rendering_context(p, ctx, new DrawingState(state.fill, true)); + return $impl_canvas.restore(ctx); + } + } else if (picture instanceof $types.ImageScalingBehaviour) { + let p = picture[0]; + let behaviour = picture[1]; + $impl_canvas.save(ctx); + $impl_canvas.set_image_smoothing_enabled( + ctx, + (() => { + if (behaviour instanceof $types.ScalingSmooth) { + return true; + } else { + return false; + } + })(), + ); + display_on_rendering_context(p, ctx, state); + return $impl_canvas.restore(ctx); + } else if (picture instanceof Translate) { + let p = picture[0]; + let vec = picture[1]; + let x; + let y; + x = vec[0]; + y = vec[1]; + $impl_canvas.save(ctx); + $impl_canvas.translate(ctx, x, y); + display_on_rendering_context(p, ctx, state); + return $impl_canvas.restore(ctx); + } else if (picture instanceof Scale) { + let p = picture[0]; + let vec = picture[1]; + let x; + let y; + x = vec[0]; + y = vec[1]; + $impl_canvas.save(ctx); + $impl_canvas.scale(ctx, x, y); + display_on_rendering_context(p, ctx, state); + return $impl_canvas.restore(ctx); + } else if (picture instanceof Rotate) { + let p = picture[0]; + let angle = picture[1]; + let rad; + rad = angle[0]; + $impl_canvas.save(ctx); + $impl_canvas.rotate(ctx, rad); + display_on_rendering_context(p, ctx, state); + return $impl_canvas.restore(ctx); + } else { + let pictures = picture[0]; + if (pictures instanceof $Empty) { + return undefined; + } else { + let p = pictures.head; + let ps = pictures.tail; + display_on_rendering_context(p, ctx, state); + loop$picture = new Combine(ps); + loop$ctx = ctx; + loop$state = state; + } + } + } +} + +function parse_key_code(key_code) { + if (key_code === 32) { + return new Some(new $event.KeySpace()); + } else if (key_code === 37) { + return new Some(new $event.KeyLeftArrow()); + } else if (key_code === 38) { + return new Some(new $event.KeyUpArrow()); + } else if (key_code === 39) { + return new Some(new $event.KeyRightArrow()); + } else if (key_code === 40) { + return new Some(new $event.KeyDownArrow()); + } else if (key_code === 87) { + return new Some(new $event.KeyW()); + } else if (key_code === 65) { + return new Some(new $event.KeyA()); + } else if (key_code === 83) { + return new Some(new $event.KeyS()); + } else if (key_code === 68) { + return new Some(new $event.KeyD()); + } else if (key_code === 90) { + return new Some(new $event.KeyZ()); + } else if (key_code === 88) { + return new Some(new $event.KeyX()); + } else if (key_code === 67) { + return new Some(new $event.KeyC()); + } else if (key_code === 18) { + return new Some(new $event.KeyEnter()); + } else if (key_code === 27) { + return new Some(new $event.KeyEscape()); + } else if (key_code === 8) { + return new Some(new $event.KeyBackspace()); + } else { + return new None(); + } +} + +/** + * Utility to set the origin in the center of the canvas + */ +export function center(picture) { + return (config) => { + let width; + let height; + width = config.width; + height = config.height; + let _pipe = picture; + return translate_xy(_pipe, width * 0.5, height * 0.5); + }; +} + +const default_drawing_state = /* @__PURE__ */ new DrawingState(false, true); + +/** + * Display a picture on a HTML canvas element + * (specified by some [CSS Selector](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_selectors)). + * ``` + * canvas.display(fn (_: canvas.Config) { circle(50.0) }, "#mycanvas") + * ``` + */ +export function display(init, selector) { + let ctx = $impl_canvas.get_rendering_context(selector); + $impl_canvas.reset(ctx); + let picture = init( + new Config($impl_canvas.get_width(ctx), $impl_canvas.get_height(ctx)), + ); + return display_on_rendering_context(picture, ctx, default_drawing_state); +} + +function get_tick_func(ctx, view, update, selector) { + return (time) => { + let $ = $impl_canvas.get_global(selector); + let current_state; + if ($ instanceof Ok) { + current_state = $[0]; + } else { + throw makeError( + "let_assert", + FILEPATH, + "paint/canvas", + 399, + "get_tick_func", + "Pattern match failed, no pattern matched the value.", + { + value: $, + start: 11581, + end: 11644, + pattern_start: 11592, + pattern_end: 11609 + } + ) + } + let new_state = update(current_state, new $event.Tick(time)); + $impl_canvas.set_global(new_state, selector); + let picture = view(new_state); + $impl_canvas.reset(ctx); + display_on_rendering_context(picture, ctx, default_drawing_state); + return $impl_canvas.setup_request_animation_frame( + get_tick_func(ctx, view, update, selector), + ); + }; +} + +/** + * Animations, interactive applications and tiny games can be built using the + * `interact` function. It roughly follows the [Elm architecture](https://guide.elm-lang.org/architecture/). + * Here is a short example: + * ``` + * type State = + * Int + * + * fn init(_: canvas.Config) -> State { + * 0 + * } + * + * fn update(state: State, event: event.Event) -> State { + * case event { + * event.Tick(_) -> state + 1 + * _ -> state + * } + * } + * + * fn view(state: State) -> Picture { + * paint.circle(int.to_float(state)) + * } + * + * fn main() { + * interact(init, update, view, "#mycanvas") + * } + * ``` + */ +export function interact(init, update, view, selector) { + let ctx = $impl_canvas.get_rendering_context(selector); + let initial_state = init( + new Config($impl_canvas.get_width(ctx), $impl_canvas.get_height(ctx)), + ); + $impl_canvas.set_global(initial_state, selector); + let create_key_handler = (event_name, constructor) => { + return $impl_canvas.setup_input_handler( + event_name, + (event) => { + let key = parse_key_code($impl_canvas.get_key_code(event)); + if (key instanceof Some) { + let key$1 = key[0]; + let $ = $impl_canvas.get_global(selector); + let old_state; + if ($ instanceof Ok) { + old_state = $[0]; + } else { + throw makeError( + "let_assert", + FILEPATH, + "paint/canvas", + 292, + "interact", + "Pattern match failed, no pattern matched the value.", + { + value: $, + start: 8332, + end: 8391, + pattern_start: 8343, + pattern_end: 8356 + } + ) + } + let new_state = update(old_state, constructor(key$1)); + return $impl_canvas.set_global(new_state, selector); + } else { + return undefined; + } + }, + ); + }; + create_key_handler( + "keydown", + (var0) => { return new $event.KeyboardPressed(var0); }, + ); + create_key_handler( + "keyup", + (var0) => { return new $event.KeyboardRelased(var0); }, + ); + $impl_canvas.setup_input_handler( + "mousemove", + (event) => { + let $ = $impl_canvas.mouse_pos(ctx, event); + let x; + let y; + x = $[0]; + y = $[1]; + let $1 = $impl_canvas.get_global(selector); + let old_state; + if ($1 instanceof Ok) { + old_state = $1[0]; + } else { + throw makeError( + "let_assert", + FILEPATH, + "paint/canvas", + 309, + "interact", + "Pattern match failed, no pattern matched the value.", + { + value: $1, + start: 8863, + end: 8922, + pattern_start: 8874, + pattern_end: 8887 + } + ) + } + let new_state = update(old_state, new $event.MouseMoved(x, y)); + $impl_canvas.set_global(new_state, selector); + return undefined; + }, + ); + let create_mouse_button_handler = (event_name, constructor, check_pressed) => { + return $impl_canvas.setup_input_handler( + event_name, + (event) => { + let previous_event_id = "PAINT_PREVIOUS_MOUSE_INPUT_FOR_" + selector; + let previous_event = $impl_canvas.get_global(previous_event_id); + $impl_canvas.set_global(event, previous_event_id); + let check_button = (i) => { + return $impl_canvas.check_mouse_button( + event, + previous_event, + i, + check_pressed, + ); + }; + let trigger_update = (button) => { + let $ = $impl_canvas.get_global(selector); + let old_state; + if ($ instanceof Ok) { + old_state = $[0]; + } else { + throw makeError( + "let_assert", + FILEPATH, + "paint/canvas", + 338, + "interact", + "Pattern match failed, no pattern matched the value.", + { + value: $, + start: 9856, + end: 9915, + pattern_start: 9867, + pattern_end: 9880 + } + ) + } + let new_state = update(old_state, constructor(button)); + return $impl_canvas.set_global(new_state, selector); + }; + let $ = check_button(0); + if ($) { + trigger_update(new $event.MouseButtonLeft()) + } else { + undefined + } + let $1 = check_button(1); + if ($1) { + trigger_update(new $event.MouseButtonRight()) + } else { + undefined + } + let $2 = check_button(2); + if ($2) { + trigger_update(new $event.MouseButtonMiddle()) + } else { + undefined + } + return undefined; + }, + ); + }; + create_mouse_button_handler( + "mousedown", + (var0) => { return new $event.MousePressed(var0); }, + true, + ); + create_mouse_button_handler( + "mouseup", + (var0) => { return new $event.MouseReleased(var0); }, + false, + ); + return $impl_canvas.setup_request_animation_frame( + get_tick_func(ctx, view, update, selector), + ); +} + +/** + * If you are using [Lustre](https://github.com/lustre-labs/lustre) or some other framework to build + * your web application you may prefer to use the [web components](https://developer.mozilla.org/en-US/docs/Web/API/Web_components) API + * and the `define_web_component` function. + * ``` + * // Call this function once to register a custom HTML element + * canvas.define_web_component() + * // You can then display your picture by setting the "picture" + * // property or attribute on the element. + * + * // In Lustre it would look something like this: + * fn canvas(picture: paint.Picture, attributes: List(attribute.Attribute(a))) { + * element.element( + * "paint-canvas", + * [attribute.attribute("picture", encode.to_string(picture)), ..attributes], + * [], + * ) + *} + * ``` + * A more detailed example for using this API can be found in the `demos/with_lustre` directory. + */ +export function define_web_component() { + $impl_canvas.define_web_component(); + return $impl_canvas.set_global( + (encoded_picture, ctx) => { + let $ = $encode.from_string(encoded_picture); + let picture; + if ($ instanceof Ok) { + picture = $[0]; + } else { + throw makeError( + "let_assert", + FILEPATH, + "paint/canvas", + 447, + "define_web_component", + "Invalid picture provided to web component", + { + value: $, + start: 13602, + end: 13662, + pattern_start: 13613, + pattern_end: 13624 + } + ) + } + return display_on_rendering_context(picture, ctx, default_drawing_state); + }, + "display_on_rendering_context_with_default_drawing_state", + ); +} diff --git a/build/dev/javascript/paint/paint/encode.mjs b/build/dev/javascript/paint/paint/encode.mjs new file mode 100644 index 0000000..f9b88f3 --- /dev/null +++ b/build/dev/javascript/paint/paint/encode.mjs @@ -0,0 +1,506 @@ +import * as $colour from "../../gleam_community_colour/gleam_community/colour.mjs"; +import * as $json from "../../gleam_json/gleam/json.mjs"; +import * as $decode from "../../gleam_stdlib/gleam/dynamic/decode.mjs"; +import { toList } from "../gleam.mjs"; +import * as $paint from "../paint.mjs"; +import * as $types from "../paint/internal/types.mjs"; +import { FontProperties, NoStroke, Radians, SolidStroke } from "../paint/internal/types.mjs"; + +function decode_angle() { + return $decode.field( + "radians", + $decode.float, + (radians) => { return $decode.success(new Radians(radians)); }, + ); +} + +function decode_font() { + return $decode.field( + "sizePx", + $decode.int, + (size_px) => { + return $decode.field( + "fontFamily", + $decode.string, + (font_family) => { + return $decode.success(new FontProperties(size_px, font_family)); + }, + ); + }, + ); +} + +function decode_stroke() { + return $decode.field( + "type", + $decode.string, + (stroke_type) => { + if (stroke_type === "noStroke") { + return $decode.success(new NoStroke()); + } else if (stroke_type === "solidStroke") { + return $decode.field( + "colour", + $colour.decoder(), + (colour) => { + return $decode.field( + "thickness", + $decode.float, + (thickness) => { + return $decode.success(new SolidStroke(colour, thickness)); + }, + ); + }, + ); + } else { + return $decode.failure(new NoStroke(), "StrokeProperties"); + } + }, + ); +} + +function decode_vec2() { + return $decode.field( + "x", + $decode.float, + (x) => { + return $decode.field( + "y", + $decode.float, + (y) => { return $decode.success([x, y]); }, + ); + }, + ); +} + +function decode_picture() { + return $decode.recursive( + () => { + return $decode.field( + "type", + $decode.string, + (ty) => { + if (ty === "arc") { + return $decode.field( + "radius", + $decode.float, + (radius) => { + return $decode.field( + "start", + decode_angle(), + (start) => { + return $decode.field( + "end", + decode_angle(), + (end) => { + return $decode.success( + new $types.Arc(radius, start, end), + ); + }, + ); + }, + ); + }, + ); + } else if (ty === "blank") { + return $decode.success(new $types.Blank()); + } else if (ty === "combine") { + return $decode.field( + "pictures", + $decode.list(decode_picture()), + (pictures) => { + return $decode.success(new $types.Combine(pictures)); + }, + ); + } else if (ty === "fill") { + return $decode.field( + "picture", + decode_picture(), + (picture) => { + return $decode.field( + "colour", + $colour.decoder(), + (colour) => { + return $decode.success(new $types.Fill(picture, colour)); + }, + ); + }, + ); + } else if (ty === "polygon") { + return $decode.field( + "points", + $decode.list(decode_vec2()), + (points) => { + return $decode.field( + "closed", + $decode.bool, + (closed) => { + return $decode.success(new $types.Polygon(points, closed)); + }, + ); + }, + ); + } else if (ty === "rotate") { + return $decode.field( + "angle", + decode_angle(), + (angle) => { + return $decode.field( + "picture", + decode_picture(), + (picture) => { + return $decode.success(new $types.Rotate(picture, angle)); + }, + ); + }, + ); + } else if (ty === "scale") { + return $decode.field( + "x", + $decode.float, + (x) => { + return $decode.field( + "y", + $decode.float, + (y) => { + return $decode.field( + "picture", + decode_picture(), + (picture) => { + return $decode.success( + new $types.Scale(picture, [x, y]), + ); + }, + ); + }, + ); + }, + ); + } else if (ty === "stroke") { + return $decode.field( + "stroke", + decode_stroke(), + (stroke) => { + return $decode.field( + "picture", + decode_picture(), + (picture) => { + return $decode.success(new $types.Stroke(picture, stroke)); + }, + ); + }, + ); + } else if (ty === "text") { + return $decode.field( + "text", + $decode.string, + (text) => { + return $decode.field( + "style", + decode_font(), + (style) => { + return $decode.success(new $types.Text(text, style)); + }, + ); + }, + ); + } else if (ty === "translate") { + return $decode.field( + "x", + $decode.float, + (x) => { + return $decode.field( + "y", + $decode.float, + (y) => { + return $decode.field( + "picture", + decode_picture(), + (picture) => { + return $decode.success( + new $types.Translate(picture, [x, y]), + ); + }, + ); + }, + ); + }, + ); + } else if (ty === "image") { + return $decode.field( + "id", + $decode.string, + (id) => { + return $decode.field( + "width_px", + $decode.int, + (width_px) => { + return $decode.field( + "height_px", + $decode.int, + (height_px) => { + return $decode.success( + new $types.ImageRef( + new $types.Image(id), + width_px, + height_px, + ), + ); + }, + ); + }, + ); + }, + ); + } else if (ty === "image_scaling_behaviour") { + return $decode.field( + "behaviour", + $decode.string, + (behaviour) => { + return $decode.field( + "picture", + decode_picture(), + (picture) => { + if (behaviour === "smooth") { + return $decode.success( + new $types.ImageScalingBehaviour( + picture, + new $types.ScalingSmooth(), + ), + ); + } else if (behaviour === "pixelated") { + return $decode.success( + new $types.ImageScalingBehaviour( + picture, + new $types.ScalingPixelated(), + ), + ); + } else { + return $decode.failure(new $types.Blank(), "Picture"); + } + }, + ); + }, + ); + } else { + return $decode.failure(new $types.Blank(), "Picture"); + } + }, + ); + }, + ); +} + +/** + * Attempt to deserialize a `Picture` + */ +export function from_string(string) { + let decoder = $decode.field( + "picture", + decode_picture(), + (picture) => { return $decode.success(picture); }, + ); + return $json.parse(string, decoder); +} + +function font_to_json(font) { + let size_px; + let font_family; + size_px = font.size_px; + font_family = font.font_family; + return $json.object( + toList([ + ["sizePx", $json.int(size_px)], + ["fontFamily", $json.string(font_family)], + ]), + ); +} + +function stroke_to_json(stroke) { + if (stroke instanceof NoStroke) { + return $json.object(toList([["type", $json.string("noStroke")]])); + } else { + let colour = stroke[0]; + let thickness = stroke[1]; + return $json.object( + toList([ + ["type", $json.string("solidStroke")], + ["colour", $colour.encode(colour)], + ["thickness", $json.float(thickness)], + ]), + ); + } +} + +function angle_to_json(angle) { + let rad; + rad = angle[0]; + return $json.object(toList([["radians", $json.float(rad)]])); +} + +function picture_to_json(picture) { + if (picture instanceof $types.Blank) { + return $json.object(toList([["type", $json.string("blank")]])); + } else if (picture instanceof $types.Polygon) { + let points = picture[0]; + let closed = picture.closed; + return $json.object( + toList([ + ["type", $json.string("polygon")], + [ + "points", + $json.array( + points, + (point) => { + let x; + let y; + x = point[0]; + y = point[1]; + return $json.object( + toList([["x", $json.float(x)], ["y", $json.float(y)]]), + ); + }, + ), + ], + ["closed", $json.bool(closed)], + ]), + ); + } else if (picture instanceof $types.Arc) { + let radius = picture.radius; + let start = picture.start; + let end = picture.end; + return $json.object( + toList([ + ["type", $json.string("arc")], + ["radius", $json.float(radius)], + ["start", angle_to_json(start)], + ["end", angle_to_json(end)], + ]), + ); + } else if (picture instanceof $types.Text) { + let text = picture.text; + let style = picture.style; + return $json.object( + toList([ + ["type", $json.string("text")], + ["text", $json.string(text)], + ["style", font_to_json(style)], + ]), + ); + } else if (picture instanceof $types.ImageRef) { + let width_px = picture.width_px; + let height_px = picture.height_px; + let id = picture[0].id; + return $json.object( + toList([ + ["type", $json.string("image")], + ["id", $json.string(id)], + ["width_px", $json.int(width_px)], + ["height_px", $json.int(height_px)], + ]), + ); + } else if (picture instanceof $types.Fill) { + let picture$1 = picture[0]; + let colour = picture[1]; + return $json.object( + toList([ + ["type", $json.string("fill")], + ["colour", $colour.encode(colour)], + ["picture", picture_to_json(picture$1)], + ]), + ); + } else if (picture instanceof $types.Stroke) { + let picture$1 = picture[0]; + let stroke = picture[1]; + return $json.object( + toList([ + ["type", $json.string("stroke")], + ["stroke", stroke_to_json(stroke)], + ["picture", picture_to_json(picture$1)], + ]), + ); + } else if (picture instanceof $types.ImageScalingBehaviour) { + let picture$1 = picture[0]; + let behaviour = picture[1]; + return $json.object( + toList([ + ["type", $json.string("image_scaling_behaviour")], + [ + "behaviour", + $json.string( + (() => { + if (behaviour instanceof $types.ScalingSmooth) { + return "smooth"; + } else { + return "pixelated"; + } + })(), + ), + ], + ["picture", picture_to_json(picture$1)], + ]), + ); + } else if (picture instanceof $types.Translate) { + let picture$1 = picture[0]; + let x = picture[1][0]; + let y = picture[1][1]; + return $json.object( + toList([ + ["type", $json.string("translate")], + ["x", $json.float(x)], + ["y", $json.float(y)], + ["picture", picture_to_json(picture$1)], + ]), + ); + } else if (picture instanceof $types.Scale) { + let picture$1 = picture[0]; + let x = picture[1][0]; + let y = picture[1][1]; + return $json.object( + toList([ + ["type", $json.string("scale")], + ["x", $json.float(x)], + ["y", $json.float(y)], + ["picture", picture_to_json(picture$1)], + ]), + ); + } else if (picture instanceof $types.Rotate) { + let picture$1 = picture[0]; + let angle = picture[1]; + return $json.object( + toList([ + ["type", $json.string("rotate")], + ["angle", angle_to_json(angle)], + ["picture", picture_to_json(picture$1)], + ]), + ); + } else { + let from = picture[0]; + return $json.object( + toList([ + ["type", $json.string("combine")], + ["pictures", $json.array(from, picture_to_json)], + ]), + ); + } +} + +/** + * Serialize a `Picture` to a string. + * + * Note, serializing an `Image` texture will only store an ID referencing the image. This means that if you deserialize a Picture containing + * references to images, you are responsible for making sure all images are loaded before drawing the picture. + * More advanced APIs to support use cases such as these are planned for a future release. + * + * Also, if you wish to store the serialized data, remember that the library currently makes no stability guarantee that + * the data can be deserialized by *future* versions of the library. + */ +export function to_string(picture) { + let version = "paint:unstable"; + let _pipe = $json.object( + toList([ + ["version", $json.string(version)], + ["picture", picture_to_json(picture)], + ]), + ); + return $json.to_string(_pipe); +} diff --git a/build/dev/javascript/paint/paint/event.mjs b/build/dev/javascript/paint/paint/event.mjs new file mode 100644 index 0000000..86faa42 --- /dev/null +++ b/build/dev/javascript/paint/paint/event.mjs @@ -0,0 +1,163 @@ +import { CustomType as $CustomType } from "../gleam.mjs"; + +/** + * Triggered before drawing. Contains the number of milliseconds elapsed. + */ +export class Tick extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} +export const Event$Tick = ($0) => new Tick($0); +export const Event$isTick = (value) => value instanceof Tick; +export const Event$Tick$0 = (value) => value[0]; + +/** + * Triggered when a key is pressed + */ +export class KeyboardPressed extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} +export const Event$KeyboardPressed = ($0) => new KeyboardPressed($0); +export const Event$isKeyboardPressed = (value) => + value instanceof KeyboardPressed; +export const Event$KeyboardPressed$0 = (value) => value[0]; + +/** + * Triggered when a key is released + */ +export class KeyboardRelased extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} +export const Event$KeyboardRelased = ($0) => new KeyboardRelased($0); +export const Event$isKeyboardRelased = (value) => + value instanceof KeyboardRelased; +export const Event$KeyboardRelased$0 = (value) => value[0]; + +/** + * Triggered when the mouse is moved. Contains + * the `x` and `y` value for the mouse position. + */ +export class MouseMoved extends $CustomType { + constructor($0, $1) { + super(); + this[0] = $0; + this[1] = $1; + } +} +export const Event$MouseMoved = ($0, $1) => new MouseMoved($0, $1); +export const Event$isMouseMoved = (value) => value instanceof MouseMoved; +export const Event$MouseMoved$0 = (value) => value[0]; +export const Event$MouseMoved$1 = (value) => value[1]; + +/** + * Triggered when a mouse button is pressed + */ +export class MousePressed extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} +export const Event$MousePressed = ($0) => new MousePressed($0); +export const Event$isMousePressed = (value) => value instanceof MousePressed; +export const Event$MousePressed$0 = (value) => value[0]; + +/** + * Triggered when a mouse button is released. + * + * Note, on the web you might encounter issues where the + * release event for the right mouse button is not triggered + * because of the context menu. + */ +export class MouseReleased extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} +export const Event$MouseReleased = ($0) => new MouseReleased($0); +export const Event$isMouseReleased = (value) => value instanceof MouseReleased; +export const Event$MouseReleased$0 = (value) => value[0]; + +export class KeyLeftArrow extends $CustomType {} +export const Key$KeyLeftArrow = () => new KeyLeftArrow(); +export const Key$isKeyLeftArrow = (value) => value instanceof KeyLeftArrow; + +export class KeyRightArrow extends $CustomType {} +export const Key$KeyRightArrow = () => new KeyRightArrow(); +export const Key$isKeyRightArrow = (value) => value instanceof KeyRightArrow; + +export class KeyUpArrow extends $CustomType {} +export const Key$KeyUpArrow = () => new KeyUpArrow(); +export const Key$isKeyUpArrow = (value) => value instanceof KeyUpArrow; + +export class KeyDownArrow extends $CustomType {} +export const Key$KeyDownArrow = () => new KeyDownArrow(); +export const Key$isKeyDownArrow = (value) => value instanceof KeyDownArrow; + +export class KeySpace extends $CustomType {} +export const Key$KeySpace = () => new KeySpace(); +export const Key$isKeySpace = (value) => value instanceof KeySpace; + +export class KeyW extends $CustomType {} +export const Key$KeyW = () => new KeyW(); +export const Key$isKeyW = (value) => value instanceof KeyW; + +export class KeyA extends $CustomType {} +export const Key$KeyA = () => new KeyA(); +export const Key$isKeyA = (value) => value instanceof KeyA; + +export class KeyS extends $CustomType {} +export const Key$KeyS = () => new KeyS(); +export const Key$isKeyS = (value) => value instanceof KeyS; + +export class KeyD extends $CustomType {} +export const Key$KeyD = () => new KeyD(); +export const Key$isKeyD = (value) => value instanceof KeyD; + +export class KeyZ extends $CustomType {} +export const Key$KeyZ = () => new KeyZ(); +export const Key$isKeyZ = (value) => value instanceof KeyZ; + +export class KeyX extends $CustomType {} +export const Key$KeyX = () => new KeyX(); +export const Key$isKeyX = (value) => value instanceof KeyX; + +export class KeyC extends $CustomType {} +export const Key$KeyC = () => new KeyC(); +export const Key$isKeyC = (value) => value instanceof KeyC; + +export class KeyEnter extends $CustomType {} +export const Key$KeyEnter = () => new KeyEnter(); +export const Key$isKeyEnter = (value) => value instanceof KeyEnter; + +export class KeyEscape extends $CustomType {} +export const Key$KeyEscape = () => new KeyEscape(); +export const Key$isKeyEscape = (value) => value instanceof KeyEscape; + +export class KeyBackspace extends $CustomType {} +export const Key$KeyBackspace = () => new KeyBackspace(); +export const Key$isKeyBackspace = (value) => value instanceof KeyBackspace; + +export class MouseButtonLeft extends $CustomType {} +export const MouseButton$MouseButtonLeft = () => new MouseButtonLeft(); +export const MouseButton$isMouseButtonLeft = (value) => + value instanceof MouseButtonLeft; + +export class MouseButtonRight extends $CustomType {} +export const MouseButton$MouseButtonRight = () => new MouseButtonRight(); +export const MouseButton$isMouseButtonRight = (value) => + value instanceof MouseButtonRight; + +export class MouseButtonMiddle extends $CustomType {} +export const MouseButton$MouseButtonMiddle = () => new MouseButtonMiddle(); +export const MouseButton$isMouseButtonMiddle = (value) => + value instanceof MouseButtonMiddle; diff --git a/build/dev/javascript/paint/paint/internal/impl_canvas.mjs b/build/dev/javascript/paint/paint/internal/impl_canvas.mjs new file mode 100644 index 0000000..10ddff7 --- /dev/null +++ b/build/dev/javascript/paint/paint/internal/impl_canvas.mjs @@ -0,0 +1,65 @@ +import { + setup_input_handler, + get_width, + get_height, + set_global, + get_global, + reset, + save, + restore, + translate, + scale, + rotate, + reset_transform, + set_fill_colour, + set_stroke_color, + set_line_width, + set_image_smoothing_enabled, + arc, + polygon, + text, + draw_image, + image_from_query, + image_from_src, + on_image_load, +} from "../../impl_canvas_bindings.mjs"; +import { + define_web_component, + setup_request_animation_frame, + get_rendering_context, + get_key_code, + mouse_pos, + check_mouse_button, +} from "./../../impl_canvas_bindings.mjs"; + +export { + arc, + check_mouse_button, + define_web_component, + draw_image, + get_global, + get_height, + get_key_code, + get_rendering_context, + get_width, + image_from_query, + image_from_src, + mouse_pos, + on_image_load, + polygon, + reset, + reset_transform, + restore, + rotate, + save, + scale, + set_fill_colour, + set_global, + set_image_smoothing_enabled, + set_line_width, + set_stroke_color, + setup_input_handler, + setup_request_animation_frame, + text, + translate, +}; diff --git a/build/dev/javascript/paint/paint/internal/types.mjs b/build/dev/javascript/paint/paint/internal/types.mjs new file mode 100644 index 0000000..7e53029 --- /dev/null +++ b/build/dev/javascript/paint/paint/internal/types.mjs @@ -0,0 +1,217 @@ +import * as $colour from "../../../gleam_community_colour/gleam_community/colour.mjs"; +import { CustomType as $CustomType } from "../../gleam.mjs"; + +export class Blank extends $CustomType {} +export const Picture$Blank = () => new Blank(); +export const Picture$isBlank = (value) => value instanceof Blank; + +export class Polygon extends $CustomType { + constructor($0, closed) { + super(); + this[0] = $0; + this.closed = closed; + } +} +export const Picture$Polygon = ($0, closed) => new Polygon($0, closed); +export const Picture$isPolygon = (value) => value instanceof Polygon; +export const Picture$Polygon$0 = (value) => value[0]; +export const Picture$Polygon$closed = (value) => value.closed; +export const Picture$Polygon$1 = (value) => value.closed; + +export class Arc extends $CustomType { + constructor(radius, start, end) { + super(); + this.radius = radius; + this.start = start; + this.end = end; + } +} +export const Picture$Arc = (radius, start, end) => new Arc(radius, start, end); +export const Picture$isArc = (value) => value instanceof Arc; +export const Picture$Arc$radius = (value) => value.radius; +export const Picture$Arc$0 = (value) => value.radius; +export const Picture$Arc$start = (value) => value.start; +export const Picture$Arc$1 = (value) => value.start; +export const Picture$Arc$end = (value) => value.end; +export const Picture$Arc$2 = (value) => value.end; + +export class Text extends $CustomType { + constructor(text, style) { + super(); + this.text = text; + this.style = style; + } +} +export const Picture$Text = (text, style) => new Text(text, style); +export const Picture$isText = (value) => value instanceof Text; +export const Picture$Text$text = (value) => value.text; +export const Picture$Text$0 = (value) => value.text; +export const Picture$Text$style = (value) => value.style; +export const Picture$Text$1 = (value) => value.style; + +export class ImageRef extends $CustomType { + constructor($0, width_px, height_px) { + super(); + this[0] = $0; + this.width_px = width_px; + this.height_px = height_px; + } +} +export const Picture$ImageRef = ($0, width_px, height_px) => + new ImageRef($0, width_px, height_px); +export const Picture$isImageRef = (value) => value instanceof ImageRef; +export const Picture$ImageRef$0 = (value) => value[0]; +export const Picture$ImageRef$width_px = (value) => value.width_px; +export const Picture$ImageRef$1 = (value) => value.width_px; +export const Picture$ImageRef$height_px = (value) => value.height_px; +export const Picture$ImageRef$2 = (value) => value.height_px; + +export class Fill extends $CustomType { + constructor($0, $1) { + super(); + this[0] = $0; + this[1] = $1; + } +} +export const Picture$Fill = ($0, $1) => new Fill($0, $1); +export const Picture$isFill = (value) => value instanceof Fill; +export const Picture$Fill$0 = (value) => value[0]; +export const Picture$Fill$1 = (value) => value[1]; + +export class Stroke extends $CustomType { + constructor($0, $1) { + super(); + this[0] = $0; + this[1] = $1; + } +} +export const Picture$Stroke = ($0, $1) => new Stroke($0, $1); +export const Picture$isStroke = (value) => value instanceof Stroke; +export const Picture$Stroke$0 = (value) => value[0]; +export const Picture$Stroke$1 = (value) => value[1]; + +export class ImageScalingBehaviour extends $CustomType { + constructor($0, $1) { + super(); + this[0] = $0; + this[1] = $1; + } +} +export const Picture$ImageScalingBehaviour = ($0, $1) => + new ImageScalingBehaviour($0, $1); +export const Picture$isImageScalingBehaviour = (value) => + value instanceof ImageScalingBehaviour; +export const Picture$ImageScalingBehaviour$0 = (value) => value[0]; +export const Picture$ImageScalingBehaviour$1 = (value) => value[1]; + +export class Translate extends $CustomType { + constructor($0, $1) { + super(); + this[0] = $0; + this[1] = $1; + } +} +export const Picture$Translate = ($0, $1) => new Translate($0, $1); +export const Picture$isTranslate = (value) => value instanceof Translate; +export const Picture$Translate$0 = (value) => value[0]; +export const Picture$Translate$1 = (value) => value[1]; + +export class Scale extends $CustomType { + constructor($0, $1) { + super(); + this[0] = $0; + this[1] = $1; + } +} +export const Picture$Scale = ($0, $1) => new Scale($0, $1); +export const Picture$isScale = (value) => value instanceof Scale; +export const Picture$Scale$0 = (value) => value[0]; +export const Picture$Scale$1 = (value) => value[1]; + +export class Rotate extends $CustomType { + constructor($0, $1) { + super(); + this[0] = $0; + this[1] = $1; + } +} +export const Picture$Rotate = ($0, $1) => new Rotate($0, $1); +export const Picture$isRotate = (value) => value instanceof Rotate; +export const Picture$Rotate$0 = (value) => value[0]; +export const Picture$Rotate$1 = (value) => value[1]; + +export class Combine extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} +export const Picture$Combine = ($0) => new Combine($0); +export const Picture$isCombine = (value) => value instanceof Combine; +export const Picture$Combine$0 = (value) => value[0]; + +export class Image extends $CustomType { + constructor(id) { + super(); + this.id = id; + } +} +export const Image$Image = (id) => new Image(id); +export const Image$isImage = (value) => value instanceof Image; +export const Image$Image$id = (value) => value.id; +export const Image$Image$0 = (value) => value.id; + +export class ScalingSmooth extends $CustomType {} +export const ImageScalingBehaviour$ScalingSmooth = () => new ScalingSmooth(); +export const ImageScalingBehaviour$isScalingSmooth = (value) => + value instanceof ScalingSmooth; + +export class ScalingPixelated extends $CustomType {} +export const ImageScalingBehaviour$ScalingPixelated = () => + new ScalingPixelated(); +export const ImageScalingBehaviour$isScalingPixelated = (value) => + value instanceof ScalingPixelated; + +export class NoStroke extends $CustomType {} +export const StrokeProperties$NoStroke = () => new NoStroke(); +export const StrokeProperties$isNoStroke = (value) => value instanceof NoStroke; + +export class SolidStroke extends $CustomType { + constructor($0, $1) { + super(); + this[0] = $0; + this[1] = $1; + } +} +export const StrokeProperties$SolidStroke = ($0, $1) => new SolidStroke($0, $1); +export const StrokeProperties$isSolidStroke = (value) => + value instanceof SolidStroke; +export const StrokeProperties$SolidStroke$0 = (value) => value[0]; +export const StrokeProperties$SolidStroke$1 = (value) => value[1]; + +export class FontProperties extends $CustomType { + constructor(size_px, font_family) { + super(); + this.size_px = size_px; + this.font_family = font_family; + } +} +export const FontProperties$FontProperties = (size_px, font_family) => + new FontProperties(size_px, font_family); +export const FontProperties$isFontProperties = (value) => + value instanceof FontProperties; +export const FontProperties$FontProperties$size_px = (value) => value.size_px; +export const FontProperties$FontProperties$0 = (value) => value.size_px; +export const FontProperties$FontProperties$font_family = (value) => + value.font_family; +export const FontProperties$FontProperties$1 = (value) => value.font_family; + +export class Radians extends $CustomType { + constructor($0) { + super(); + this[0] = $0; + } +} +export const Angle$Radians = ($0) => new Radians($0); +export const Angle$isRadians = (value) => value instanceof Radians; +export const Angle$Radians$0 = (value) => value[0]; diff --git a/build/dev/javascript/prelude.mjs b/build/dev/javascript/prelude.mjs new file mode 100644 index 0000000..6acd225 --- /dev/null +++ b/build/dev/javascript/prelude.mjs @@ -0,0 +1,1575 @@ +export class CustomType { + withFields(fields) { + let properties = Object.keys(this).map((label) => + label in fields ? fields[label] : this[label], + ); + return new this.constructor(...properties); + } +} + +export class List { + static fromArray(array, tail) { + let t = tail || new Empty(); + for (let i = array.length - 1; i >= 0; --i) { + t = new NonEmpty(array[i], t); + } + return t; + } + + [Symbol.iterator]() { + return new ListIterator(this); + } + + toArray() { + return [...this]; + } + + atLeastLength(desired) { + let current = this; + while (desired-- > 0 && current) current = current.tail; + return current !== undefined; + } + + hasLength(desired) { + let current = this; + while (desired-- > 0 && current) current = current.tail; + return desired === -1 && current instanceof Empty; + } + + countLength() { + let current = this; + let length = 0; + while (current) { + current = current.tail; + length++; + } + return length - 1; + } +} + +export function prepend(element, tail) { + return new NonEmpty(element, tail); +} + +export function toList(elements, tail) { + return List.fromArray(elements, tail); +} + +class ListIterator { + #current; + + constructor(current) { + this.#current = current; + } + + next() { + if (this.#current instanceof Empty) { + return { done: true }; + } else { + let { head, tail } = this.#current; + this.#current = tail; + return { value: head, done: false }; + } + } +} + +export class Empty extends List {} +export const List$Empty = () => new Empty(); +export const List$isEmpty = (value) => value instanceof Empty; + +export class NonEmpty extends List { + constructor(head, tail) { + super(); + this.head = head; + this.tail = tail; + } +} +export const List$NonEmpty = (head, tail) => new NonEmpty(head, tail); +export const List$isNonEmpty = (value) => value instanceof NonEmpty; + +export const List$NonEmpty$first = (value) => value.head; +export const List$NonEmpty$rest = (value) => value.tail; + +/** + * A bit array is a contiguous sequence of bits similar to Erlang's Binary type. + */ +export class BitArray { + /** + * The size in bits of this bit array's data. + * + * @type {number} + */ + bitSize; + + /** + * The size in bytes of this bit array's data. If this bit array doesn't store + * a whole number of bytes then this value is rounded up. + * + * @type {number} + */ + byteSize; + + /** + * The number of unused high bits in the first byte of this bit array's + * buffer prior to the start of its data. The value of any unused high bits is + * undefined. + * + * The bit offset will be in the range 0-7. + * + * @type {number} + */ + bitOffset; + + /** + * The raw bytes that hold this bit array's data. + * + * If `bitOffset` is not zero then there are unused high bits in the first + * byte of this buffer. + * + * If `bitOffset + bitSize` is not a multiple of 8 then there are unused low + * bits in the last byte of this buffer. + * + * @type {Uint8Array} + */ + rawBuffer; + + /** + * Constructs a new bit array from a `Uint8Array`, an optional size in + * bits, and an optional bit offset. + * + * If no bit size is specified it is taken as `buffer.length * 8`, i.e. all + * bytes in the buffer make up the new bit array's data. + * + * If no bit offset is specified it defaults to zero, i.e. there are no unused + * high bits in the first byte of the buffer. + * + * @param {Uint8Array} buffer + * @param {number} [bitSize] + * @param {number} [bitOffset] + */ + constructor(buffer, bitSize, bitOffset) { + if (!(buffer instanceof Uint8Array)) { + throw globalThis.Error( + "BitArray can only be constructed from a Uint8Array", + ); + } + + this.bitSize = bitSize ?? buffer.length * 8; + this.byteSize = Math.trunc((this.bitSize + 7) / 8); + this.bitOffset = bitOffset ?? 0; + + // Validate the bit size + if (this.bitSize < 0) { + throw globalThis.Error(`BitArray bit size is invalid: ${this.bitSize}`); + } + + // Validate the bit offset + if (this.bitOffset < 0 || this.bitOffset > 7) { + throw globalThis.Error( + `BitArray bit offset is invalid: ${this.bitOffset}`, + ); + } + + // Validate the length of the buffer + if (buffer.length !== Math.trunc((this.bitOffset + this.bitSize + 7) / 8)) { + throw globalThis.Error("BitArray buffer length is invalid"); + } + + this.rawBuffer = buffer; + } + + /** + * Returns a specific byte in this bit array. If the byte index is out of + * range then `undefined` is returned. + * + * When returning the final byte of a bit array with a bit size that's not a + * multiple of 8, the content of the unused low bits are undefined. + * + * @param {number} index + * @returns {number | undefined} + */ + byteAt(index) { + if (index < 0 || index >= this.byteSize) { + return undefined; + } + + return bitArrayByteAt(this.rawBuffer, this.bitOffset, index); + } + + equals(other) { + if (this.bitSize !== other.bitSize) { + return false; + } + + const wholeByteCount = Math.trunc(this.bitSize / 8); + + // If both bit offsets are zero do a byte-aligned equality check which is + // faster + if (this.bitOffset === 0 && other.bitOffset === 0) { + // Compare any whole bytes + for (let i = 0; i < wholeByteCount; i++) { + if (this.rawBuffer[i] !== other.rawBuffer[i]) { + return false; + } + } + + // Compare any trailing bits, excluding unused low bits + const trailingBitsCount = this.bitSize % 8; + if (trailingBitsCount) { + const unusedLowBitCount = 8 - trailingBitsCount; + if ( + this.rawBuffer[wholeByteCount] >> unusedLowBitCount !== + other.rawBuffer[wholeByteCount] >> unusedLowBitCount + ) { + return false; + } + } + } else { + // Compare any whole bytes + for (let i = 0; i < wholeByteCount; i++) { + const a = bitArrayByteAt(this.rawBuffer, this.bitOffset, i); + const b = bitArrayByteAt(other.rawBuffer, other.bitOffset, i); + + if (a !== b) { + return false; + } + } + + // Compare any trailing bits + const trailingBitsCount = this.bitSize % 8; + if (trailingBitsCount) { + const a = bitArrayByteAt( + this.rawBuffer, + this.bitOffset, + wholeByteCount, + ); + const b = bitArrayByteAt( + other.rawBuffer, + other.bitOffset, + wholeByteCount, + ); + + const unusedLowBitCount = 8 - trailingBitsCount; + if (a >> unusedLowBitCount !== b >> unusedLowBitCount) { + return false; + } + } + } + + return true; + } + + /** + * Returns this bit array's internal buffer. + * + * @deprecated Use `BitArray.byteAt()` or `BitArray.rawBuffer` instead. + * + * @returns {Uint8Array} + */ + get buffer() { + bitArrayPrintDeprecationWarning( + "buffer", + "Use BitArray.byteAt() or BitArray.rawBuffer instead", + ); + + if (this.bitOffset !== 0 || this.bitSize % 8 !== 0) { + throw new globalThis.Error( + "BitArray.buffer does not support unaligned bit arrays", + ); + } + + return this.rawBuffer; + } + + /** + * Returns the length in bytes of this bit array's internal buffer. + * + * @deprecated Use `BitArray.bitSize` or `BitArray.byteSize` instead. + * + * @returns {number} + */ + get length() { + bitArrayPrintDeprecationWarning( + "length", + "Use BitArray.bitSize or BitArray.byteSize instead", + ); + + if (this.bitOffset !== 0 || this.bitSize % 8 !== 0) { + throw new globalThis.Error( + "BitArray.length does not support unaligned bit arrays", + ); + } + + return this.rawBuffer.length; + } +} + +export const BitArray$BitArray = (buffer, bitSize, bitOffset) => + new BitArray(buffer, bitSize, bitOffset); + +/** + * Returns the nth byte in the given buffer, after applying the specified bit + * offset. If the index is out of bounds then zero is returned. + * + * @param {Uint8Array} buffer + * @param {number} bitOffset + * @param {number} index + * @returns {number} + */ +function bitArrayByteAt(buffer, bitOffset, index) { + if (bitOffset === 0) { + return buffer[index] ?? 0; + } else { + const a = (buffer[index] << bitOffset) & 0xff; + const b = buffer[index + 1] >> (8 - bitOffset); + + return a | b; + } +} + +export class UtfCodepoint { + constructor(value) { + this.value = value; + } +} + +const isBitArrayDeprecationMessagePrinted = {}; +function bitArrayPrintDeprecationWarning(name, message) { + if (isBitArrayDeprecationMessagePrinted[name]) { + return; + } + + console.warn( + `Deprecated BitArray.${name} property used in JavaScript FFI code. ${message}.`, + ); + + isBitArrayDeprecationMessagePrinted[name] = true; +} + +/** + * Slices a bit array to produce a new bit array. If `end` is not supplied then + * all bits from `start` onward are returned. + * + * If the slice is out of bounds then an exception is thrown. + * + * @param {BitArray} bitArray + * @param {number} start + * @param {number} [end] + * @returns {BitArray} + */ +export function bitArraySlice(bitArray, start, end) { + end ??= bitArray.bitSize; + + bitArrayValidateRange(bitArray, start, end); + + // Handle zero-length slices + if (start === end) { + return new BitArray(new Uint8Array()); + } + + // Early return for slices that cover the whole bit array + if (start === 0 && end === bitArray.bitSize) { + return bitArray; + } + + start += bitArray.bitOffset; + end += bitArray.bitOffset; + + const startByteIndex = Math.trunc(start / 8); + const endByteIndex = Math.trunc((end + 7) / 8); + const byteLength = endByteIndex - startByteIndex; + + // Avoid creating a new Uint8Array if the view of the underlying ArrayBuffer + // is the same. This can occur when slicing off just the first or last bit of + // a bit array, i.e. when only the bit offset or bit size need to be updated. + let buffer; + if (startByteIndex === 0 && byteLength === bitArray.rawBuffer.byteLength) { + buffer = bitArray.rawBuffer; + } else { + buffer = new Uint8Array( + bitArray.rawBuffer.buffer, + bitArray.rawBuffer.byteOffset + startByteIndex, + byteLength, + ); + } + + return new BitArray(buffer, end - start, start % 8); +} + +/** + * Interprets a slice of this bit array as a floating point number, either + * 32-bit or 64-bit, with the specified endianness. + * + * The value of `end - start` must be exactly 32 or 64, otherwise an exception + * will be thrown. + * + * @param {BitArray} bitArray + * @param {number} start + * @param {number} end + * @param {boolean} isBigEndian + * @returns {number} + */ +export function bitArraySliceToFloat(bitArray, start, end, isBigEndian) { + bitArrayValidateRange(bitArray, start, end); + + const floatSize = end - start; + + // Check size is valid + if (floatSize !== 16 && floatSize !== 32 && floatSize !== 64) { + const msg = + `Sized floats must be 16-bit, 32-bit or 64-bit, got size of ` + + `${floatSize} bits`; + throw new globalThis.Error(msg); + } + + start += bitArray.bitOffset; + + const isStartByteAligned = start % 8 === 0; + + // If the bit range is byte aligned then the float can be read directly out + // of the existing buffer + if (isStartByteAligned) { + const view = new DataView( + bitArray.rawBuffer.buffer, + bitArray.rawBuffer.byteOffset + start / 8, + ); + + if (floatSize === 64) { + return view.getFloat64(0, !isBigEndian); + } else if (floatSize === 32) { + return view.getFloat32(0, !isBigEndian); + } else if (floatSize === 16) { + return fp16UintToNumber(view.getUint16(0, !isBigEndian)); + } + } + + // Copy the unaligned bytes into an aligned array so a DataView can be used + const alignedBytes = new Uint8Array(floatSize / 8); + const byteOffset = Math.trunc(start / 8); + for (let i = 0; i < alignedBytes.length; i++) { + alignedBytes[i] = bitArrayByteAt( + bitArray.rawBuffer, + start % 8, + byteOffset + i, + ); + } + + // Read the float out of the aligned buffer + const view = new DataView(alignedBytes.buffer); + if (floatSize === 64) { + return view.getFloat64(0, !isBigEndian); + } else if (floatSize === 32) { + return view.getFloat32(0, !isBigEndian); + } else { + return fp16UintToNumber(view.getUint16(0, !isBigEndian)); + } +} + +/** + * Interprets a slice of this bit array as a signed or unsigned integer with the + * specified endianness. + * + * @param {BitArray} bitArray + * @param {number} start + * @param {number} end + * @param {boolean} isBigEndian + * @param {boolean} isSigned + * @returns {number} + */ +export function bitArraySliceToInt( + bitArray, + start, + end, + isBigEndian, + isSigned, +) { + bitArrayValidateRange(bitArray, start, end); + + if (start === end) { + return 0; + } + + start += bitArray.bitOffset; + end += bitArray.bitOffset; + + const isStartByteAligned = start % 8 === 0; + const isEndByteAligned = end % 8 === 0; + + // If the slice is byte-aligned then there is no need to handle unaligned + // slices, meaning a simpler and faster implementation can be used instead + if (isStartByteAligned && isEndByteAligned) { + return intFromAlignedSlice( + bitArray, + start / 8, + end / 8, + isBigEndian, + isSigned, + ); + } + + const size = end - start; + + const startByteIndex = Math.trunc(start / 8); + const endByteIndex = Math.trunc((end - 1) / 8); + + // Handle the case of the slice being completely contained in a single byte + if (startByteIndex == endByteIndex) { + const mask = 0xff >> start % 8; + const unusedLowBitCount = (8 - (end % 8)) % 8; + + let value = + (bitArray.rawBuffer[startByteIndex] & mask) >> unusedLowBitCount; + + // For signed integers, if the high bit is set reinterpret as two's + // complement + if (isSigned) { + const highBit = 2 ** (size - 1); + if (value >= highBit) { + value -= highBit * 2; + } + } + + return value; + } + + // The integer value to be read is not aligned and crosses at least one byte + // boundary in the input array + + if (size <= 53) { + return intFromUnalignedSliceUsingNumber( + bitArray.rawBuffer, + start, + end, + isBigEndian, + isSigned, + ); + } else { + return intFromUnalignedSliceUsingBigInt( + bitArray.rawBuffer, + start, + end, + isBigEndian, + isSigned, + ); + } +} + +/** + * Joins the given segments into a new bit array, tightly packing them together. + * Each segment must be one of the following types: + * + * - A `number`: A single byte value in the range 0-255. Values outside this + * range will be wrapped. + * - A `Uint8Array`: A sequence of byte values of any length. + * - A `BitArray`: A sequence of bits of any length, which may not be byte + * aligned. + * + * The bit size of the returned bit array will be the sum of the size in bits + * of the input segments. + * + * @param {(number | Uint8Array | BitArray)[]} segments + * @returns {BitArray} + */ +export function toBitArray(segments) { + if (segments.length === 0) { + return new BitArray(new Uint8Array()); + } + + if (segments.length === 1) { + const segment = segments[0]; + + // When there is a single BitArray segment it can be returned as-is + if (segment instanceof BitArray) { + return segment; + } + + // When there is a single Uint8Array segment, pass it directly to the bit + // array constructor to avoid a copy + if (segment instanceof Uint8Array) { + return new BitArray(segment); + } + + return new BitArray(new Uint8Array(/** @type {number[]} */ (segments))); + } + + // Count the total number of bits and check if all segments are numbers, i.e. + // single bytes + let bitSize = 0; + let areAllSegmentsNumbers = true; + for (const segment of segments) { + if (segment instanceof BitArray) { + bitSize += segment.bitSize; + areAllSegmentsNumbers = false; + } else if (segment instanceof Uint8Array) { + bitSize += segment.byteLength * 8; + areAllSegmentsNumbers = false; + } else { + bitSize += 8; + } + } + + // If all segments are numbers then pass the segments array directly to the + // Uint8Array constructor + if (areAllSegmentsNumbers) { + return new BitArray(new Uint8Array(/** @type {number[]} */ (segments))); + } + + // Pack the segments into a Uint8Array + const buffer = new Uint8Array(Math.trunc((bitSize + 7) / 8)); + + // The current write position in bits into the above array. Byte-aligned + // segments, i.e. when the cursor is a multiple of 8, are able to be processed + // faster due to being able to copy bytes directly. + let cursor = 0; + + for (let segment of segments) { + const isCursorByteAligned = cursor % 8 === 0; + + if (segment instanceof BitArray) { + if (isCursorByteAligned && segment.bitOffset === 0) { + buffer.set(segment.rawBuffer, cursor / 8); + cursor += segment.bitSize; + + // Zero any unused bits in the last byte of the buffer. Their content is + // undefined and shouldn't be included in the output. + const trailingBitsCount = segment.bitSize % 8; + if (trailingBitsCount !== 0) { + const lastByteIndex = Math.trunc(cursor / 8); + buffer[lastByteIndex] >>= 8 - trailingBitsCount; + buffer[lastByteIndex] <<= 8 - trailingBitsCount; + } + } else { + appendUnalignedBits( + segment.rawBuffer, + segment.bitSize, + segment.bitOffset, + ); + } + } else if (segment instanceof Uint8Array) { + if (isCursorByteAligned) { + buffer.set(segment, cursor / 8); + cursor += segment.byteLength * 8; + } else { + appendUnalignedBits(segment, segment.byteLength * 8, 0); + } + } else { + if (isCursorByteAligned) { + buffer[cursor / 8] = segment; + cursor += 8; + } else { + appendUnalignedBits(new Uint8Array([segment]), 8, 0); + } + } + } + + function appendUnalignedBits(unalignedBits, size, offset) { + if (size === 0) { + return; + } + + const byteSize = Math.trunc(size + 7 / 8); + + const highBitsCount = cursor % 8; + const lowBitsCount = 8 - highBitsCount; + + let byteIndex = Math.trunc(cursor / 8); + + for (let i = 0; i < byteSize; i++) { + let byte = bitArrayByteAt(unalignedBits, offset, i); + + // If this is a partial byte then zero out the trailing bits as their + // content is undefined and shouldn't be included in the output + if (size < 8) { + byte >>= 8 - size; + byte <<= 8 - size; + } + + // Copy the high bits of the input byte to the low bits of the current + // output byte + buffer[byteIndex] |= byte >> highBitsCount; + + let appendedBitsCount = size - Math.max(0, size - lowBitsCount); + size -= appendedBitsCount; + cursor += appendedBitsCount; + + if (size === 0) { + break; + } + + // Copy the low bits of the input byte to the high bits of the next output + // byte + buffer[++byteIndex] = byte << lowBitsCount; + appendedBitsCount = size - Math.max(0, size - highBitsCount); + size -= appendedBitsCount; + cursor += appendedBitsCount; + } + } + + return new BitArray(buffer, bitSize); +} + +/** + * Encodes a floating point value into a `Uint8Array`. This is used to create + * float segments that are part of bit array expressions. + * + * @param {number} value + * @param {number} size + * @param {boolean} isBigEndian + * @returns {Uint8Array} + */ +export function sizedFloat(value, size, isBigEndian) { + if (size !== 16 && size !== 32 && size !== 64) { + const msg = `Sized floats must be 16-bit, 32-bit or 64-bit, got size of ${size} bits`; + throw new globalThis.Error(msg); + } + + if (size === 16) { + return numberToFp16Uint(value, isBigEndian); + } + + const buffer = new Uint8Array(size / 8); + + const view = new DataView(buffer.buffer); + + if (size == 64) { + view.setFloat64(0, value, !isBigEndian); + } else { + view.setFloat32(0, value, !isBigEndian); + } + + return buffer; +} + +/** + * Encodes an integer value into a `Uint8Array`, or a `BitArray` if the size in + * bits is not a multiple of 8. This is used to create integer segments used in + * bit array expressions. + * + * @param {number} value + * @param {number} size + * @param {boolean} isBigEndian + * @returns {Uint8Array | BitArray} + */ +export function sizedInt(value, size, isBigEndian) { + if (size <= 0) { + return new Uint8Array(); + } + + // Fast path when size is 8 bits. This relies on the rounding behavior of the + // Uint8Array constructor. + if (size === 8) { + return new Uint8Array([value]); + } + + // Fast path when size is less than 8 bits: shift the value up to the high + // bits + if (size < 8) { + value <<= 8 - size; + return new BitArray(new Uint8Array([value]), size); + } + + // Allocate output buffer + const buffer = new Uint8Array(Math.trunc((size + 7) / 8)); + + // The number of trailing bits in the final byte. Will be zero if the size is + // an exact number of bytes. + const trailingBitsCount = size % 8; + + // The number of unused bits in the final byte of the buffer + const unusedBitsCount = 8 - trailingBitsCount; + + // For output sizes not exceeding 32 bits the number type is used. For larger + // output sizes the BigInt type is needed. + // + // The code in each of these two paths must be kept in sync. + if (size <= 32) { + if (isBigEndian) { + let i = buffer.length - 1; + + // Set the trailing bits at the end of the output buffer + if (trailingBitsCount) { + buffer[i--] = (value << unusedBitsCount) & 0xff; + value >>= trailingBitsCount; + } + + for (; i >= 0; i--) { + buffer[i] = value; + value >>= 8; + } + } else { + let i = 0; + + const wholeByteCount = Math.trunc(size / 8); + for (; i < wholeByteCount; i++) { + buffer[i] = value; + value >>= 8; + } + + // Set the trailing bits at the end of the output buffer + if (trailingBitsCount) { + buffer[i] = value << unusedBitsCount; + } + } + } else { + const bigTrailingBitsCount = BigInt(trailingBitsCount); + const bigUnusedBitsCount = BigInt(unusedBitsCount); + + let bigValue = BigInt(value); + + if (isBigEndian) { + let i = buffer.length - 1; + + // Set the trailing bits at the end of the output buffer + if (trailingBitsCount) { + buffer[i--] = Number(bigValue << bigUnusedBitsCount); + bigValue >>= bigTrailingBitsCount; + } + + for (; i >= 0; i--) { + buffer[i] = Number(bigValue); + bigValue >>= 8n; + } + } else { + let i = 0; + + const wholeByteCount = Math.trunc(size / 8); + for (; i < wholeByteCount; i++) { + buffer[i] = Number(bigValue); + bigValue >>= 8n; + } + + // Set the trailing bits at the end of the output buffer + if (trailingBitsCount) { + buffer[i] = Number(bigValue << bigUnusedBitsCount); + } + } + } + + // Integers that aren't a whole number of bytes are returned as a BitArray so + // their size in bits is tracked + if (trailingBitsCount) { + return new BitArray(buffer, size); + } + + return buffer; +} + +/** + * Reads an aligned slice of any size as an integer. + * + * @param {BitArray} bitArray + * @param {number} start + * @param {number} end + * @param {boolean} isBigEndian + * @param {boolean} isSigned + * @returns {number} + */ +function intFromAlignedSlice(bitArray, start, end, isBigEndian, isSigned) { + const byteSize = end - start; + + if (byteSize <= 6) { + return intFromAlignedSliceUsingNumber( + bitArray.rawBuffer, + start, + end, + isBigEndian, + isSigned, + ); + } else { + return intFromAlignedSliceUsingBigInt( + bitArray.rawBuffer, + start, + end, + isBigEndian, + isSigned, + ); + } +} + +/** + * Reads an aligned slice up to 48 bits in size as an integer. Uses the + * JavaScript `number` type internally. + * + * @param {Uint8Array} buffer + * @param {number} start + * @param {number} end + * @param {boolean} isBigEndian + * @param {boolean} isSigned + * @returns {number} + */ +function intFromAlignedSliceUsingNumber( + buffer, + start, + end, + isBigEndian, + isSigned, +) { + const byteSize = end - start; + + let value = 0; + + // Read bytes as an unsigned integer + if (isBigEndian) { + for (let i = start; i < end; i++) { + value *= 256; + value += buffer[i]; + } + } else { + for (let i = end - 1; i >= start; i--) { + value *= 256; + value += buffer[i]; + } + } + + // For signed integers, if the high bit is set reinterpret as two's + // complement + if (isSigned) { + const highBit = 2 ** (byteSize * 8 - 1); + if (value >= highBit) { + value -= highBit * 2; + } + } + + return value; +} + +/** + * Reads an aligned slice of any size as an integer. Uses the JavaScript + * `BigInt` type internally. + * + * @param {Uint8Array} buffer + * @param {number} start + * @param {number} end + * @param {boolean} isBigEndian + * @param {boolean} isSigned + * @returns {number} + */ +function intFromAlignedSliceUsingBigInt( + buffer, + start, + end, + isBigEndian, + isSigned, +) { + const byteSize = end - start; + + let value = 0n; + + // Read bytes as an unsigned integer value + if (isBigEndian) { + for (let i = start; i < end; i++) { + value *= 256n; + value += BigInt(buffer[i]); + } + } else { + for (let i = end - 1; i >= start; i--) { + value *= 256n; + value += BigInt(buffer[i]); + } + } + + // For signed integers, if the high bit is set reinterpret as two's + // complement + if (isSigned) { + const highBit = 1n << BigInt(byteSize * 8 - 1); + if (value >= highBit) { + value -= highBit * 2n; + } + } + + // Convert the result into a JS number. This may cause quantizing/error on + // values outside JavaScript's safe integer range. + return Number(value); +} + +/** + * Reads an unaligned slice up to 53 bits in size as an integer. Uses the + * JavaScript `number` type internally. + * + * This function assumes that the slice crosses at least one byte boundary in + * the input. + * + * @param {Uint8Array} buffer + * @param {number} start + * @param {number} end + * @param {boolean} isBigEndian + * @param {boolean} isSigned + * @returns {number} + */ +function intFromUnalignedSliceUsingNumber( + buffer, + start, + end, + isBigEndian, + isSigned, +) { + const isStartByteAligned = start % 8 === 0; + + let size = end - start; + let byteIndex = Math.trunc(start / 8); + + let value = 0; + + if (isBigEndian) { + // Read any leading bits + if (!isStartByteAligned) { + const leadingBitsCount = 8 - (start % 8); + value = buffer[byteIndex++] & ((1 << leadingBitsCount) - 1); + size -= leadingBitsCount; + } + + // Read any whole bytes + while (size >= 8) { + value *= 256; + value += buffer[byteIndex++]; + size -= 8; + } + + // Read any trailing bits + if (size > 0) { + value *= 2 ** size; + value += buffer[byteIndex] >> (8 - size); + } + } else { + // For little endian, if the start is aligned then whole bytes can be read + // directly out of the input array, with the trailing bits handled at the + // end + if (isStartByteAligned) { + let size = end - start; + let scale = 1; + + // Read whole bytes + while (size >= 8) { + value += buffer[byteIndex++] * scale; + scale *= 256; + size -= 8; + } + + // Read trailing bits + value += (buffer[byteIndex] >> (8 - size)) * scale; + } else { + // Read little endian data where the start is not byte-aligned. This is + // done by reading whole bytes that cross a byte boundary in the input + // data, then reading any trailing bits. + + const highBitsCount = start % 8; + const lowBitsCount = 8 - highBitsCount; + + let size = end - start; + let scale = 1; + + // Extract whole bytes + while (size >= 8) { + const byte = + (buffer[byteIndex] << highBitsCount) | + (buffer[byteIndex + 1] >> lowBitsCount); + + value += (byte & 0xff) * scale; + + scale *= 256; + size -= 8; + byteIndex++; + } + + // Read any trailing bits. These trailing bits may cross a byte boundary + // in the input buffer. + if (size > 0) { + const lowBitsUsed = size - Math.max(0, size - lowBitsCount); + + let trailingByte = + (buffer[byteIndex] & ((1 << lowBitsCount) - 1)) >> + (lowBitsCount - lowBitsUsed); + + size -= lowBitsUsed; + + if (size > 0) { + trailingByte *= 2 ** size; + trailingByte += buffer[byteIndex + 1] >> (8 - size); + } + + value += trailingByte * scale; + } + } + } + + // For signed integers, if the high bit is set reinterpret as two's + // complement + if (isSigned) { + const highBit = 2 ** (end - start - 1); + if (value >= highBit) { + value -= highBit * 2; + } + } + + return value; +} + +/** + * Reads an unaligned slice of any size as an integer. Uses the JavaScript + * `BigInt` type internally. + * + * This function assumes that the slice crosses at least one byte boundary in + * the input. + * + * @param {Uint8Array} buffer + * @param {number} start + * @param {number} end + * @param {boolean} isBigEndian + * @param {boolean} isSigned + * @returns {number} + */ +function intFromUnalignedSliceUsingBigInt( + buffer, + start, + end, + isBigEndian, + isSigned, +) { + const isStartByteAligned = start % 8 === 0; + + let size = end - start; + let byteIndex = Math.trunc(start / 8); + + let value = 0n; + + if (isBigEndian) { + // Read any leading bits + if (!isStartByteAligned) { + const leadingBitsCount = 8 - (start % 8); + value = BigInt(buffer[byteIndex++] & ((1 << leadingBitsCount) - 1)); + size -= leadingBitsCount; + } + + // Read any whole bytes + while (size >= 8) { + value *= 256n; + value += BigInt(buffer[byteIndex++]); + size -= 8; + } + + // Read any trailing bits + if (size > 0) { + value <<= BigInt(size); + value += BigInt(buffer[byteIndex] >> (8 - size)); + } + } else { + // For little endian, if the start is aligned then whole bytes can be read + // directly out of the input array, with the trailing bits handled at the + // end + if (isStartByteAligned) { + let size = end - start; + let shift = 0n; + + // Read whole bytes + while (size >= 8) { + value += BigInt(buffer[byteIndex++]) << shift; + shift += 8n; + size -= 8; + } + + // Read trailing bits + value += BigInt(buffer[byteIndex] >> (8 - size)) << shift; + } else { + // Read little endian data where the start is not byte-aligned. This is + // done by reading whole bytes that cross a byte boundary in the input + // data, then reading any trailing bits. + + const highBitsCount = start % 8; + const lowBitsCount = 8 - highBitsCount; + + let size = end - start; + let shift = 0n; + + // Extract whole bytes + while (size >= 8) { + const byte = + (buffer[byteIndex] << highBitsCount) | + (buffer[byteIndex + 1] >> lowBitsCount); + + value += BigInt(byte & 0xff) << shift; + + shift += 8n; + size -= 8; + byteIndex++; + } + + // Read any trailing bits. These trailing bits may cross a byte boundary + // in the input buffer. + if (size > 0) { + const lowBitsUsed = size - Math.max(0, size - lowBitsCount); + + let trailingByte = + (buffer[byteIndex] & ((1 << lowBitsCount) - 1)) >> + (lowBitsCount - lowBitsUsed); + + size -= lowBitsUsed; + + if (size > 0) { + trailingByte <<= size; + trailingByte += buffer[byteIndex + 1] >> (8 - size); + } + + value += BigInt(trailingByte) << shift; + } + } + } + + // For signed integers, if the high bit is set reinterpret as two's + // complement + if (isSigned) { + const highBit = 2n ** BigInt(end - start - 1); + if (value >= highBit) { + value -= highBit * 2n; + } + } + + // Convert the result into a JS number. This may cause quantizing/error on + // values outside JavaScript's safe integer range. + return Number(value); +} + +/** + * Interprets a 16-bit unsigned integer value as a 16-bit floating point value. + * + * @param {number} intValue + * @returns {number} + */ +function fp16UintToNumber(intValue) { + const sign = intValue >= 0x8000 ? -1 : 1; + const exponent = (intValue & 0x7c00) >> 10; + const fraction = intValue & 0x03ff; + + let value; + if (exponent === 0) { + value = 6.103515625e-5 * (fraction / 0x400); + } else if (exponent === 0x1f) { + value = fraction === 0 ? Infinity : NaN; + } else { + value = Math.pow(2, exponent - 15) * (1 + fraction / 0x400); + } + + return sign * value; +} + +/** + * Converts a floating point number to bytes for a 16-bit floating point value. + * + * @param {number} intValue + * @param {boolean} isBigEndian + * @returns {Uint8Array} + */ +function numberToFp16Uint(value, isBigEndian) { + const buffer = new Uint8Array(2); + + if (isNaN(value)) { + buffer[1] = 0x7e; + } else if (value === Infinity) { + buffer[1] = 0x7c; + } else if (value === -Infinity) { + buffer[1] = 0xfc; + } else if (value === 0) { + // Both values are already zero + } else { + const sign = value < 0 ? 1 : 0; + value = Math.abs(value); + + let exponent = Math.floor(Math.log2(value)); + let fraction = value / Math.pow(2, exponent) - 1; + + exponent += 15; + + if (exponent <= 0) { + exponent = 0; + fraction = value / Math.pow(2, -14); + } else if (exponent >= 31) { + exponent = 31; + fraction = 0; + } + + fraction = Math.round(fraction * 1024); + + buffer[1] = + (sign << 7) | ((exponent & 0x1f) << 2) | ((fraction >> 8) & 0x03); + buffer[0] = fraction & 0xff; + } + + if (isBigEndian) { + const a = buffer[0]; + buffer[0] = buffer[1]; + buffer[1] = a; + } + + return buffer; +} + +/** + * Throws an exception if the given start and end values are out of bounds for + * a bit array. + * + * @param {BitArray} bitArray + * @param {number} start + * @param {number} end + */ +function bitArrayValidateRange(bitArray, start, end) { + if ( + start < 0 || + start > bitArray.bitSize || + end < start || + end > bitArray.bitSize + ) { + const msg = + `Invalid bit array slice: start = ${start}, end = ${end}, ` + + `bit size = ${bitArray.bitSize}`; + throw new globalThis.Error(msg); + } +} + +/** @type {TextEncoder | undefined} */ +let utf8Encoder; + +/** + * Returns the UTF-8 bytes for a string. + * + * @param {string} string + * @returns {Uint8Array} + */ +export function stringBits(string) { + utf8Encoder ??= new TextEncoder(); + return utf8Encoder.encode(string); +} + +/** + * Returns the UTF-8 bytes for a single UTF codepoint. + * + * @param {UtfCodepoint} codepoint + * @returns {Uint8Array} + */ +export function codepointBits(codepoint) { + return stringBits(String.fromCodePoint(codepoint.value)); +} + +/** + * Returns the UTF-16 bytes for a string. + * + * @param {string} string + * @param {boolean} isBigEndian + * @returns {Uint8Array} + */ +export function stringToUtf16(string, isBigEndian) { + const buffer = new ArrayBuffer(string.length * 2); + const bufferView = new DataView(buffer); + + for (let i = 0; i < string.length; i++) { + bufferView.setUint16(i * 2, string.charCodeAt(i), !isBigEndian); + } + + return new Uint8Array(buffer); +} + +/** + * Returns the UTF-16 bytes for a single UTF codepoint. + * + * @param {UtfCodepoint} codepoint + * @param {boolean} isBigEndian + * @returns {Uint8Array} + */ +export function codepointToUtf16(codepoint, isBigEndian) { + return stringToUtf16(String.fromCodePoint(codepoint.value), isBigEndian); +} + +/** + * Returns the UTF-32 bytes for a string. + * + * @param {string} string + * @param {boolean} isBigEndian + * @returns {Uint8Array} + */ +export function stringToUtf32(string, isBigEndian) { + const buffer = new ArrayBuffer(string.length * 4); + const bufferView = new DataView(buffer); + let length = 0; + + for (let i = 0; i < string.length; i++) { + const codepoint = string.codePointAt(i); + + bufferView.setUint32(length * 4, codepoint, !isBigEndian); + length++; + + if (codepoint > 0xffff) { + i++; + } + } + + return new Uint8Array(buffer.slice(0, length * 4)); +} + +/** + * Returns the UTF-32 bytes for a single UTF codepoint. + * + * @param {UtfCodepoint} codepoint + * @param {boolean} isBigEndian + * @returns {Uint8Array} + */ +export function codepointToUtf32(codepoint, isBigEndian) { + return stringToUtf32(String.fromCodePoint(codepoint.value), isBigEndian); +} + +export class Result extends CustomType { + static isResult(data) { + return data instanceof Result; + } +} + +export class Ok extends Result { + constructor(value) { + super(); + this[0] = value; + } + + isOk() { + return true; + } +} +export const Result$Ok = (value) => new Ok(value); +export const Result$isOk = (value) => value instanceof Ok; +export const Result$Ok$0 = (value) => value[0]; + +export class Error extends Result { + constructor(detail) { + super(); + this[0] = detail; + } + + isOk() { + return false; + } +} +export const Result$Error = (detail) => new Error(detail); +export const Result$isError = (value) => value instanceof Error; +export const Result$Error$0 = (value) => value[0]; + +export function isEqual(x, y) { + let values = [x, y]; + + while (values.length) { + let a = values.pop(); + let b = values.pop(); + if (a === b) continue; + + if (!isObject(a) || !isObject(b)) return false; + let unequal = + !structurallyCompatibleObjects(a, b) || + unequalDates(a, b) || + unequalBuffers(a, b) || + unequalArrays(a, b) || + unequalMaps(a, b) || + unequalSets(a, b) || + unequalRegExps(a, b); + if (unequal) return false; + + const proto = Object.getPrototypeOf(a); + if (proto !== null && typeof proto.equals === "function") { + try { + if (a.equals(b)) continue; + else return false; + } catch {} + } + + let [keys, get] = getters(a); + const ka = keys(a); + const kb = keys(b); + if (ka.length !== kb.length) return false; + for (let k of ka) { + values.push(get(a, k), get(b, k)); + } + } + + return true; +} + +function getters(object) { + if (object instanceof Map) { + return [(x) => x.keys(), (x, y) => x.get(y)]; + } else { + let extra = object instanceof globalThis.Error ? ["message"] : []; + return [(x) => [...extra, ...Object.keys(x)], (x, y) => x[y]]; + } +} + +function unequalDates(a, b) { + return a instanceof Date && (a > b || a < b); +} + +function unequalBuffers(a, b) { + return ( + !(a instanceof BitArray) && + a.buffer instanceof ArrayBuffer && + a.BYTES_PER_ELEMENT && + !(a.byteLength === b.byteLength && a.every((n, i) => n === b[i])) + ); +} + +function unequalArrays(a, b) { + return Array.isArray(a) && a.length !== b.length; +} + +function unequalMaps(a, b) { + return a instanceof Map && a.size !== b.size; +} + +function unequalSets(a, b) { + return ( + a instanceof Set && (a.size != b.size || [...a].some((e) => !b.has(e))) + ); +} + +function unequalRegExps(a, b) { + return a instanceof RegExp && (a.source !== b.source || a.flags !== b.flags); +} + +function isObject(a) { + return typeof a === "object" && a !== null; +} + +function structurallyCompatibleObjects(a, b) { + if (typeof a !== "object" && typeof b !== "object" && (!a || !b)) + return false; + + let nonstructural = [Promise, WeakSet, WeakMap, Function]; + if (nonstructural.some((c) => a instanceof c)) return false; + + return a.constructor === b.constructor; +} + +export function remainderInt(a, b) { + if (b === 0) { + return 0; + } else { + return a % b; + } +} + +export function divideInt(a, b) { + return Math.trunc(divideFloat(a, b)); +} + +export function divideFloat(a, b) { + if (b === 0) { + return 0; + } else { + return a / b; + } +} + +export function makeError(variant, file, module, line, fn, message, extra) { + let error = new globalThis.Error(message); + error.gleam_error = variant; + error.file = file; + error.module = module; + error.line = line; + error.function = fn; + // TODO: Remove this with Gleam v2.0.0 + error.fn = fn; + for (let k in extra) error[k] = extra[k]; + return error; +} diff --git a/build/dev/javascript/stellar_prune/_gleam_artefacts/game.cache b/build/dev/javascript/stellar_prune/_gleam_artefacts/game.cache new file mode 100644 index 0000000000000000000000000000000000000000..40230afa49fae3501e1647b40250a3ba7f51b546 GIT binary patch literal 14636 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 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/stellar_prune/_gleam_artefacts/game.cache_inline b/build/dev/javascript/stellar_prune/_gleam_artefacts/game.cache_inline new file mode 100644 index 0000000000000000000000000000000000000000..1b1cb4d44c57c2d7a5122870fa6ac3e62ff7e94e GIT binary patch literal 8 JcmZR80ssIA00961 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/stellar_prune/_gleam_artefacts/game.cache_meta b/build/dev/javascript/stellar_prune/_gleam_artefacts/game.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..6cd0de369cbda28f29aaf0906071e5f75a227392 GIT binary patch literal 1262 zcmdlVqLazMz`(HX<997ab`Xn!fq@H3r{|<5=IZBU7MFko1Q{3@m_ZB%28N8%6d?u% z1{($j23Dw0L1JcJi7f*IgC_$61I(m?jLPE7cky!2WI z28Lb+1_m~$L`h;%dTL1@0|UbWB%@0z3sQ>@GB7YGGBPmKtkIiOup#FN2P0Gu0|Nud zEg-8w_JE88830m$fq{WRfsuhhiIIUpnUR6PfsuisnvsE_mXU#>j*)?34kH7@6GjGx zXN(LC&lwpQ{xLEzm@_djSTZp%STQj$G%ztR>}6tL*vG`c@Sll+L7tg`L4lcpA)c9m zp^=$^p^2G+VG=U~!wO~whE>cA46B(L7_KriFg#^uVED$&z+lV5z~Ifoz!1;Ez|hUY zz;K0yf#Deo1H*e31_mBh1_nJ=28ME028J$H28PM33=At-85l0JGBCVgWnlQt%D^DR z#=s!W#=xM&#=v09#=sEF#=sEH#=sEC#=sE8#=x+Qje%hc8w0~vHUFf8R}VA#pcz;KqEfkBpsfkBRkfgzfQfgy&6fgy*7 zfuWIyfnhce1H(!l28JU%3=B7T7#M!@FfiEjGB9}XGBCvPGB7mpGBBLtWnj3>%fN7p zmx19PF9X8^UIvDTybKH)d<+bQd<+cx_!t=O@G&qv;A3ET#K*w!n2&)$i=TlZlAnPg zil2d@h@XLBF+T&t9exIe$NUToPxu)aGz1tJGzAzKk^~qSk_8wTY6Tb=W(hDbY!qN% zxFNv6@K=C=fmx7&fnAV+L0FK1K~0c>p+t~@p-+&3VVWQV!(u@OhHZij3@-#382E%3 z7{r7a7^H+47^H<57@UL{7@UO|7=ncu7&3$y7`lZR7`6#9FdPwLV7M>Dz+foMz~C;- zz>qD>z_3G@f#IMq1H%bn28MgW3=EHi85o`jGcY_AW?&E#VPH@dVPLQmVPMD*VPIG; z!ocuMgn{9e2m`|#5eA00A`A?yq6`coq6`eWq6`ckq6`evL>U-nh%zwD6lGwzF3Q00 zPLzQ`UW|dkRE&YaQjCGYT8x3gMvQ?WRE&Y4M2vxOVKD}V&teP=eBulY zLgEYz!r}}Jw&Dy7HR22mtHl`@){8SRY!qi;*d)%ta8{gw;h{JK!w+!=1_=oU22BYD zhI9!AhFS>*hIJAQ3=bq27@kNlFg%lBV0bRUz#u8fz~Cjxz~C*(zz`+Lz%WCSfnld4 z1H&_pg81_oa#28JXl28Ko{28M-F3=Au!7#P+{F)*x`Vqn-H#lUb;ih<#^6axc~ UGy{X1Gy{XTGy{V#6vIji0EsZRjQ{`u literal 0 HcmV?d00001 diff --git a/build/dev/javascript/stellar_prune/_gleam_artefacts/game.cache_warnings b/build/dev/javascript/stellar_prune/_gleam_artefacts/game.cache_warnings new file mode 100644 index 0000000000000000000000000000000000000000..1b1cb4d44c57c2d7a5122870fa6ac3e62ff7e94e GIT binary patch literal 8 JcmZR80ssIA00961 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/stellar_prune/_gleam_artefacts/hud.cache b/build/dev/javascript/stellar_prune/_gleam_artefacts/hud.cache new file mode 100644 index 0000000000000000000000000000000000000000..7e8f2999123e7d2a0e7867270f995758f790c220 GIT binary patch literal 9498 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 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/stellar_prune/_gleam_artefacts/hud.cache_inline b/build/dev/javascript/stellar_prune/_gleam_artefacts/hud.cache_inline new file mode 100644 index 0000000000000000000000000000000000000000..1b1cb4d44c57c2d7a5122870fa6ac3e62ff7e94e GIT binary patch literal 8 JcmZR80ssIA00961 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/stellar_prune/_gleam_artefacts/hud.cache_meta b/build/dev/javascript/stellar_prune/_gleam_artefacts/hud.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..0ed27b291e8e24d044936619d35889ae91e06d98 GIT binary patch literal 491 zcmea()yZUFU|Z41{KOKFh!6t<11E^Vz`y{J%FHVf zW?*1YW?*1og-R48X6BWsFfcF}FfcIiAPedzC+3wU78^1!Ft{)GBYqNW@cbm%FMv9jG2MqFf#+gRb~c;*UStIKbaXA_*fVi3|SZ$>{%EXN>~^e z*0C@!ykTKr_`<@#@RfyuL7tU?L4lQl!GV>5A%K;EA%m5Hp`4Y0VHqm}!&O!Wh99g9 z3}$Q$44!NZ3=wP$3@L034CQPL4E1ab46SSo4Aa;c7-q3CFw9|NV3^Csz;Kg|fq{>m Qfx(BJfx!=oL16{L00y!{fB*mh literal 0 HcmV?d00001 diff --git a/build/dev/javascript/stellar_prune/_gleam_artefacts/hud.cache_warnings b/build/dev/javascript/stellar_prune/_gleam_artefacts/hud.cache_warnings new file mode 100644 index 0000000000000000000000000000000000000000..1b1cb4d44c57c2d7a5122870fa6ac3e62ff7e94e GIT binary patch literal 8 JcmZR80ssIA00961 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/stellar_prune/_gleam_artefacts/math@math.cache b/build/dev/javascript/stellar_prune/_gleam_artefacts/math@math.cache new file mode 100644 index 0000000000000000000000000000000000000000..cb4cbb83f6e030e29ac7237592c08ee88c6c3721 GIT binary patch literal 2990 zcmXrG#T>xM!@wY@8zdOXE*Lpo_#+p)plXruTQ2ip6&^+=L&aN+!WleF!nr)`!kIj5 z&KW$+|8o;dGW2s3OBfh4{uh^|=Hw(6#WTnk6qV+s{?`vJPAw|d&tNM}&(GB_NG&SP z&r8fnNiEZdsRXOjFD^<3838sYLoYoil{qojfyq&YNzhfv(S=1glTpbzgONqJmr2RF zhlxdaBeRk724)svK~^JY0ag}aQ#K=K6E+s%NOmjd2zC}>M-DA#2M!kDR8A@96iycQ z;>^522`09{4UC+D9E^;Dj9d&%jEoEnf}-w$0ZeQR%z-RSYz&NoqDCMj%PuI$B`Clc zD8k4n@0OFFSR$XElbV>zzz9|gQX*<3h+h>WDg7$@#^kc_=x*n1NBSb|Rw)dr9ITjG>amJkmT=l93&pU1$?BF$r9Eewptl1%zcj3V|$iFqmcxnQ4_V5C|0KoL-y1(krr zm6MF10?-Oi8iU%0$H`V0i43ZTfzddJshWvVgg-eoGY9NqdM7e1JU#=v3Xi|EF#HA9 z!N6#Ig7GdRqX=_BCfHBZiUlb=Ze=dW#N$#a43{z&WHK-sZ(%yY#3<{)565R0akI zW_h>7oMJGG-7gcYM;@eyg;|hONsyOakd+G}?!asxlv-SxQ^LR?$Z0OfD9pkLHdsnf zQXW)adm0J)$_u)3G5cpTFgUU>2XZj63951ls&X+hF>uJc78T_eVMs7CGO#n>WD;b3 zBWTMmpfBj_Cs;dMaL#GLm#>Va8C@BT-5ARmji)ot07cDnM&lQZl1#>yOjbghj%@2+&OoAMY0*s8z#>GsPOvoyj3o?;pcQfrpkp-ta zB>D4N?{c5cQDp8Hmzk(qm#|G&W|k08#Z!Egt zV0s6tm#|HOuYNHw{13`Z&rEU3Phnt$gkC0c=*2J!#-fB?kf5ZKpenndXpx`$0^RACXeWpr}3VPp}GWb$&3VPX;9%B3xu7z9P#14EeD7?=ZDK+yvVZBZvd(j%4`n@$2@$OuXe z42+^ojEsVcPJtSXjDjAFppcQ|Vqk{38WtK142+BcjEo?si#xI~39>o~vM|=CLlP_l zgW-P(coGaG%A@3@EUdnSW*V47(GxxvU-}??iKZFDmsx@wjQO1S&8x3JNeH`x_SG4D7}- zjP8t#l3<5{L*oOM(C9%94I@EQBSBksK|?M<1I9oTaA+7B2^ty+TCxl3atZ1%1{yFj z%Dd&{Czi-VN>4~?#S|jhukaMX#3-n!1y1)7@Du^|1KelOM9(D1sP4$YB*-fy$ioOB z87mnX87mkV1ckJa3lEU*gG(6Eyl*JV&KS(Y2o3-NaB?Ws5-fERY-ATKED|hX46FbL z0Cqtm!BQi^T6V!gF2MrEz!I>^LLFM>=n=po$mkBPJiP=J7zGt432HD3 zYI+IkFbe8s2^ui|FUl`Tgyc;IMkY9qF}^G{CqFr}gt3y5`9G9h$-sb~qZt_mT{nS^ zcXE_r5sqYZa*kkR5pHGDa&BQ_5zb@@a?W94fz_~qJWOnYf#QOD`9b-Ffe~BDi&m^) zj}K;WIhqy7!N`nJ!~}9Mu?f0n33_sYEFdc{W0>j4!YIfHDZJ@tDXboWh1ym;p+=y- zf}~mA4qUH3A%!V3$ryrRD2*+XGlRsW1GNv5)wG-!h($O&><(vnIUCV zAP*z6V60GJ3?nnBgaU_+qmZB@IBbjrMU4by*#!l;1O*sD%}Xp99;6FYzLBA`l7Rsn zcF^K-fSrO|q=3UX)Lqz#kulH(T!@lahQU%Hs3|`wP=%3Mu+}TEfstA8s#o9*MrOe* zM!_aTfrL^7LBewqG*c536WEF}1_rn&0|Nsi6U0<6Xxb;qR8UcfR4jr`a$*#W0<}#b z4G1YlK_*5}rKg2j!!R%iG8Um1Yl4jI0ZeQG;J{_{LMm_>Klq6?lh{Nq8ZnQ}6;tMkZlNCaquzCPpUVP$s3|5D=r1 zDJZxG#9(A`3SwbovJXlvF3l-nU=ZXq7i1J>VRYnR78I2dl#~Y*W}ZfZzVd>uT+II2 zpdyGF9G9wGf~s7MObi_Iu0=)pMHmu{j127H0+@k8kX2rgk=c=jS&&mpkk?$0l?y82 z$iW;a!o()%D>3FI=ft z;qg1==jVWIH3x+PC}6b&CBe0Xs1bz0A5v7B%D~{jEbo?>Qw(OY`(=Xlz(Y<+ke6MM zl?x*7fYPD^nGIJLBq*3J$jOCVJc9;Dm;-f~*aS1Z1ar9pbC}o!S7r&Wfc!L8c_Pd*oX`O5IG zB%`r3qYR_57h^6Xb3UU0lK_(dXt;xs-GRx0S&)-WkhNa$X}zNev!JDupka)ZJXnK`k;zc;7Nal+JCiUkJG(F^ zJDW2HJM;gd)V!3`qRc!7#`OQiC8;?%iAC`Y@&!eud8z;PLyJ?3iuE(tO4IXm^$Svq ziu3alb5c^v^kFK&>hz0?lJ#Ncr0b>Uq%tSwIxsm3FbT?5IV!LSb23&r^Dwdq8!}}% zn=r8mCo)?(Cor@8Pbo?)kIzlaWU$YR&rQuM4U}MF3v6NJ4CG*B6lCOLU|?i~U_nv# zKoKT32Ic@JHU>sPQ71vsRf4MQf|5pp5{!W|jEwaGnaL%kMXCP_5;OBk^fU7qOHzyS z5_9xR_$v!ii{-(>3=9EGY>Z3+jEoG7jEsVcRe>%bDoD_UQBaXf(19^ffsrv#g^^KE zgb~AS$N|N`z{teFD!AqXqZmY8dSWhveQJDuS!xm34+pUN!95V`Qw(OY`(=Xl$b0nAbBK-(>#FY^=r-$YR{cvYN&C4htKru`X*Et8pXiR#xNttO9JtR%{7u z#+__i*o}f~7@y-|ua6;_cxz?&Y1&YrKp16|eDo-jBS-pLj+1j1BpM_>BAc zPV*U`=exjX%*QXlFTfIi!e^N#314Lr7ka>~OZX|%cHybaLBcbctAyt=s|)XCHWJ>;>?FLG*UhZqGvF$yy=G71YZGX4kGK>EqB5YNm_OiztZD`K$E&y6oG zN(N;`2}VHyMnNQ!fkE)?Zw3Zq9Y!lg1_t9?#%e}roFwPxO99y4h9C}3eF}@kR!RwxEL6W?{GciLN0R9Dh)>tCXgpNz+UBGWMnqh;5OlAWHc`3 ztLH=32Q9rES(pSl7zIEIfAaq4g(krN;LuD=%3(~6FUgND$V}!3)&4NW3=GCBJQ6&h zl>EOqIll;`h!La;qzF`sK+I%dFy`l#=S8*^;##C835I(G*Y9OyG$;k?QTc11>NVopg$dizv+RfU}%D`Z}kL?gBUMtvZKoMHZQ4WewDNZ?1gdXNR4x-Ar zsVpv3^^DXjo0(-<%4*uJ~=-(DKjq>)Fe~@Cvgmtfx&nd<3f;o<2lkm zt~kPTnumeGcq;E4UWg(8ONtWnigOZ682D4;D=Hmrm;@^r1q&EyL4p!F<71F(W-~1Z zgXPi0x zL1FowlNpppmUC?eQTaU8uxL-q%*mlvv@A;2mMa=r_j zCmS?PB<7{(q{gSDGBBniInIH{lLun|f8WH+Jm1v3QU(SFOgGF}z{qH9%4Z98!+)?F z;)|1+5_2;1(&Gy-l#`1Fc=##d4i(p8xt?cgZr4Tf+!vq zWl(IrW?=$(*M>D1M9H(6fznq#TN{W9X3qk}-CuSQP~3gx-~q+mJkG5kDu^o+6opLO zlF(TA4{CV&m!%eA4TZjGjEu&U`DXKhiYoiWf`Zh%6hX#K|C5XIi;Lqk5*ag#Af>E4 zxQ`{sD2^1=5xlv)f{alpNd;BKQQp(Mf{a;^G8 zsYZfRl?11X3oczHxYkK%+4pk zhvq?h=ls01OypGgj_(T}qzMn|>A}K~k)7JR7=kA+FfbVVa)f{q JK{R&~HvqH4h+Y5y literal 0 HcmV?d00001 diff --git a/build/dev/javascript/stellar_prune/_gleam_artefacts/rendering.cache_inline b/build/dev/javascript/stellar_prune/_gleam_artefacts/rendering.cache_inline new file mode 100644 index 0000000000000000000000000000000000000000..1b1cb4d44c57c2d7a5122870fa6ac3e62ff7e94e GIT binary patch literal 8 JcmZR80ssIA00961 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/stellar_prune/_gleam_artefacts/rendering.cache_meta b/build/dev/javascript/stellar_prune/_gleam_artefacts/rendering.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..3cec355ce2b9a79ca7581bbbaa640fd70c2898db GIT binary patch literal 643 zcmewwsFTUSz`$T4a88_&9mHZ_VBmz(={c#1x%!!TB_O^40|NsWh{3?X0FlVaEG`jb zU|>*UU|?W|%4C$LC^IlH=rS-cutJ3j5;OBk^cWZztQZ&=c#sA4lN0mG5{s=F7#RE* z7#P@~awUmH>8U0D3=9l0NXC>@7Ni!(GB7Z#VqjpX+*Q}`e0{^39HU|7Szz;Ky?fq|cqfgzBQfng#e1H&Xn28Jb!3=Ah285lk>GB7AJF);Wt zF);KnF)(anVqkdA#K0iP%)k)B%)n5`%)qdonStRZGXuk4W(Ec|76yhu76yh!76yjZ zEDQ{HSQr@CSQ!{}Ss56@SQ!`^Ss56%vNAB-XJudzU}IpgVq;)PU}IqDWMg31!p6Yx zl8u2un4N*ag`I&RkDY;GGCKpqUUmkCckB!d5*!Q+vK$Ny@*E5d3LFd!;T#MMnH&rZ z-5d-ITR9jQo^dcR2yikmm~b*M#Bnk(^m8&W?Brx%c)`iQAjHMM;LOFqkjuruFoTPM z;Sd)C!)GoA23c+f1|@C=1{H1w232kbhE#3_hFWe0hIQNw4BtWGJPZs8JPZs|co-OF z^Dr>X;bCCd#lygGj)#GPm6w6RikE@Gk(YtNiI;()hnIn&mzRNIJ}(2qE?x$PSG)`i z?|B&*KJqd!eBxzb5aDBBFyv!k2;yU4=;vc#IL*hvaGsBW;Q}8610O#Fg8&qR;tYfV DMv_)- literal 0 HcmV?d00001 diff --git a/build/dev/javascript/stellar_prune/_gleam_artefacts/rendering.cache_warnings b/build/dev/javascript/stellar_prune/_gleam_artefacts/rendering.cache_warnings new file mode 100644 index 0000000000000000000000000000000000000000..1b1cb4d44c57c2d7a5122870fa6ac3e62ff7e94e GIT binary patch literal 8 JcmZR80ssIA00961 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/stellar_prune/_gleam_artefacts/target.cache b/build/dev/javascript/stellar_prune/_gleam_artefacts/target.cache new file mode 100644 index 0000000000000000000000000000000000000000..fc7937e0aa16d2a9e2997b04a33cfbcd0ba55896 GIT binary patch literal 10406 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 GRz?8NXu4?t literal 0 HcmV?d00001 diff --git a/build/dev/javascript/stellar_prune/_gleam_artefacts/types.cache_inline b/build/dev/javascript/stellar_prune/_gleam_artefacts/types.cache_inline new file mode 100644 index 0000000000000000000000000000000000000000..1b1cb4d44c57c2d7a5122870fa6ac3e62ff7e94e GIT binary patch literal 8 JcmZR80ssIA00961 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/stellar_prune/_gleam_artefacts/types.cache_meta b/build/dev/javascript/stellar_prune/_gleam_artefacts/types.cache_meta new file mode 100644 index 0000000000000000000000000000000000000000..63d7ea9e17a7743cd2500e493a5bab5c4c645fd8 GIT binary patch literal 193 zcmd-x(#d3CU|@Lk!Bv@&8N^~>V1UsXr70j0Q3eJEb`XPsfuSIyvN$ujSd4*z!H$7} zfek8Ll30|UT4K+@z!1g2z`%c1by14U;*BCuSr7%%0g{hqU|@)0U|=X=U|?urU|^Wf zz`(GTfq~&L0|Ubq1_p*V3=9k(7#J8nF)%QEW?*38U}RtrW@KOpU}RwEW@KRKg<_DI GAPfNCav4eh literal 0 HcmV?d00001 diff --git a/build/dev/javascript/stellar_prune/_gleam_artefacts/types.cache_warnings b/build/dev/javascript/stellar_prune/_gleam_artefacts/types.cache_warnings new file mode 100644 index 0000000000000000000000000000000000000000..1b1cb4d44c57c2d7a5122870fa6ac3e62ff7e94e GIT binary patch literal 8 JcmZR80ssIA00961 literal 0 HcmV?d00001 diff --git a/build/dev/javascript/stellar_prune/browser_ffi.mjs b/build/dev/javascript/stellar_prune/browser_ffi.mjs new file mode 100644 index 0000000..ef62a4c --- /dev/null +++ b/build/dev/javascript/stellar_prune/browser_ffi.mjs @@ -0,0 +1,40 @@ +let mouseX = 0; +let mouseY = 0; +let mousePressed = false; + +if (typeof window !== 'undefined') { + const canvas = document.getElementById('canvas'); + + window.addEventListener('mousemove', (e) => { + if (canvas) { + const rect = canvas.getBoundingClientRect(); + const scaleX = canvas.width / rect.width; + const scaleY = canvas.height / rect.height; + mouseX = (e.clientX - rect.left) * scaleX; + mouseY = (e.clientY - rect.top) * scaleY; + } else { + mouseX = e.clientX; + mouseY = e.clientY; + } + }); + + window.addEventListener('mousedown', () => { + mousePressed = true; + }); + + window.addEventListener('mouseup', () => { + mousePressed = false; + }); +} + +export function request_animation_frame(callback) { + requestAnimationFrame(() => callback()); +} + +export function get_mouse_position() { + return { x: mouseX, y: mouseY }; +} + +export function is_mouse_pressed() { + return mousePressed; +} diff --git a/build/dev/javascript/stellar_prune/game.mjs b/build/dev/javascript/stellar_prune/game.mjs new file mode 100644 index 0000000..7410e69 --- /dev/null +++ b/build/dev/javascript/stellar_prune/game.mjs @@ -0,0 +1,325 @@ +import * as $list from "../gleam_stdlib/gleam/list.mjs"; +import * as $paint from "../paint/paint.mjs"; +import { request_animation_frame, get_mouse_position, is_mouse_pressed } from "./browser_ffi.mjs"; +import { Ok, toList, prepend as listPrepend, CustomType as $CustomType, isEqual } from "./gleam.mjs"; +import * as $hud from "./hud.mjs"; +import { CrossHair, Health, Hud, Score } from "./hud.mjs"; +import * as $physics from "./physics.mjs"; +import { Gravity, Physics, Vec2 } from "./physics.mjs"; +import * as $rendering from "./rendering.mjs"; +import * as $target from "./target.mjs"; +import { Diamond, Lucy, Rocket } from "./target.mjs"; +import * as $types from "./types.mjs"; +import { GameOver, MainMenu, Playing, PlayingState } from "./types.mjs"; + +export { is_mouse_pressed, request_animation_frame }; + +export class TargetClickEffect extends $CustomType { + constructor(score_change, health_change) { + super(); + this.score_change = score_change; + this.health_change = health_change; + } +} +export const TargetClickEffect$TargetClickEffect = (score_change, health_change) => + new TargetClickEffect(score_change, health_change); +export const TargetClickEffect$isTargetClickEffect = (value) => + value instanceof TargetClickEffect; +export const TargetClickEffect$TargetClickEffect$score_change = (value) => + value.score_change; +export const TargetClickEffect$TargetClickEffect$0 = (value) => + value.score_change; +export const TargetClickEffect$TargetClickEffect$health_change = (value) => + value.health_change; +export const TargetClickEffect$TargetClickEffect$1 = (value) => + value.health_change; + +export class TargetFilterResult extends $CustomType { + constructor(remaining_targets, health_loss) { + super(); + this.remaining_targets = remaining_targets; + this.health_loss = health_loss; + } +} +export const TargetFilterResult$TargetFilterResult = (remaining_targets, health_loss) => + new TargetFilterResult(remaining_targets, health_loss); +export const TargetFilterResult$isTargetFilterResult = (value) => + value instanceof TargetFilterResult; +export const TargetFilterResult$TargetFilterResult$remaining_targets = (value) => + value.remaining_targets; +export const TargetFilterResult$TargetFilterResult$0 = (value) => + value.remaining_targets; +export const TargetFilterResult$TargetFilterResult$health_loss = (value) => + value.health_loss; +export const TargetFilterResult$TargetFilterResult$1 = (value) => + value.health_loss; + +export class SpawnResult extends $CustomType { + constructor(targets, spawn_timer) { + super(); + this.targets = targets; + this.spawn_timer = spawn_timer; + } +} +export const SpawnResult$SpawnResult = (targets, spawn_timer) => + new SpawnResult(targets, spawn_timer); +export const SpawnResult$isSpawnResult = (value) => + value instanceof SpawnResult; +export const SpawnResult$SpawnResult$targets = (value) => value.targets; +export const SpawnResult$SpawnResult$0 = (value) => value.targets; +export const SpawnResult$SpawnResult$spawn_timer = (value) => value.spawn_timer; +export const SpawnResult$SpawnResult$1 = (value) => value.spawn_timer; + +function init_playing_state() { + return new PlayingState( + toList([]), + new Physics(new Gravity(new Vec2(0.0, 0.08))), + new Hud( + new Score( + new Vec2(1050.0, 70.0), + new $target.Colorable($paint.colour_hex("#ffffff")), + 0, + ), + new CrossHair( + new Vec2(0.0, 0.0), + new $target.Colorable($paint.colour_hex("#ffffff")), + 5.0, + ), + new Health(new Vec2(30.0, 30.0), 120.0, 3, 3), + ), + new Vec2(0.0, 0.0), + 0.0, + ); +} + +function get_target_click_effects(kind) { + if (kind instanceof Lucy) { + return new TargetClickEffect(1, 0); + } else if (kind instanceof Diamond) { + let points_value = kind.points_value; + return new TargetClickEffect(points_value, 0); + } else { + return new TargetClickEffect(0, -1); + } +} + +function filter_out_of_bounds_targets(targets) { + return $list.fold( + targets, + new TargetFilterResult(toList([]), 0), + (acc, target) => { + let $ = target.position.y > 900.0; + if ($) { + let $1 = target.kind; + if ($1 instanceof Rocket) { + return acc; + } else { + return new TargetFilterResult( + acc.remaining_targets, + acc.health_loss + 1, + ); + } + } else { + return new TargetFilterResult( + listPrepend(target, acc.remaining_targets), + acc.health_loss, + ); + } + }, + ); +} + +function should_spawn_new_target(spawn_timer, active_targets, score) { + let spawn_interval = 60.0; + let max_targets = 1 + (globalThis.Math.trunc(score / 5)); + return (spawn_timer >= spawn_interval) && ($list.length(active_targets) < max_targets); +} + +function handle_target_click(playing_state, clicked_target) { + let new_targets = $list.filter( + playing_state.targets, + (target) => { return !isEqual(target, clicked_target); }, + ); + let effect = get_target_click_effects(clicked_target.kind); + return new PlayingState( + new_targets, + playing_state.physics, + (() => { + let _record = playing_state.hud; + return new Hud( + (() => { + let _record$1 = playing_state.hud.score; + return new Score( + _record$1.position, + _record$1.colorable, + playing_state.hud.score.score + effect.score_change, + ); + })(), + _record.cross_hair, + (() => { + let _record$1 = playing_state.hud.health; + return new Health( + _record$1.position, + _record$1.size, + playing_state.hud.health.amount + effect.health_change, + _record$1.max_health, + ); + })(), + ); + })(), + playing_state.mouse, + playing_state.spawn_timer, + ); +} + +function update_playing_state(playing_state) { + let $ = playing_state.physics; + let gravity; + gravity = $.gravity; + let updated_targets = $list.map( + playing_state.targets, + (target) => { return $target.update_target(target, gravity.acceleration); }, + ); + let filter_result = filter_out_of_bounds_targets(updated_targets); + let new_spawn_timer = playing_state.spawn_timer + 1.0; + let _block; + let $1 = should_spawn_new_target( + new_spawn_timer, + filter_result.remaining_targets, + playing_state.hud.score.score, + ); + if ($1) { + _block = new SpawnResult( + listPrepend($target.create_target(), filter_result.remaining_targets), + 0.0, + ); + } else { + _block = new SpawnResult(filter_result.remaining_targets, new_spawn_timer); + } + let spawn_result = _block; + let new_mouse = get_mouse_position(); + return new PlayingState( + spawn_result.targets, + playing_state.physics, + (() => { + let _record = playing_state.hud; + return new Hud( + _record.score, + (() => { + let _record$1 = playing_state.hud.cross_hair; + return new CrossHair(new_mouse, _record$1.colorable, _record$1.size); + })(), + (() => { + let _record$1 = playing_state.hud.health; + return new Health( + _record$1.position, + _record$1.size, + playing_state.hud.health.amount - filter_result.health_loss, + _record$1.max_health, + ); + })(), + ); + })(), + new_mouse, + spawn_result.spawn_timer, + ); +} + +function update_state(state) { + if (state instanceof MainMenu) { + return state; + } else if (state instanceof Playing) { + let playing_state = state.state; + let prev_mouse = state.previous_mouse_pressed; + return new Playing(update_playing_state(playing_state), prev_mouse); + } else { + return state; + } +} + +function find_clicked_target(mouse, targets) { + return $list.find( + targets, + (target) => { return $target.is_point_in_target(mouse, target); }, + ); +} + +function handle_playing_click(playing_state) { + let $ = find_clicked_target(playing_state.mouse, playing_state.targets); + if ($ instanceof Ok) { + let clicked_target = $[0]; + return handle_target_click(playing_state, clicked_target); + } else { + return playing_state; + } +} + +function handle_main_menu_click(mouse_pressed) { + if (mouse_pressed) { + return new Playing(init_playing_state(), false); + } else { + return new MainMenu(); + } +} + +function handle_playing_state(playing_state, mouse_pressed) { + let _block; + if (mouse_pressed) { + _block = handle_playing_click(playing_state); + } else { + _block = playing_state; + } + let new_state = _block; + let $ = new_state.hud.health.amount <= 0; + if ($) { + return new GameOver(new_state.hud.score.score, mouse_pressed); + } else { + return new Playing(new_state, mouse_pressed); + } +} + +function handle_game_over_click( + final_score, + mouse_pressed, + previous_mouse_pressed +) { + let just_clicked = mouse_pressed && !previous_mouse_pressed; + if (just_clicked) { + return new Playing(init_playing_state(), false); + } else { + return new GameOver(final_score, mouse_pressed); + } +} + +function handle_events(state) { + let current_mouse_pressed = is_mouse_pressed(); + if (state instanceof MainMenu) { + return handle_main_menu_click(current_mouse_pressed); + } else if (state instanceof Playing) { + let playing_state = state.state; + return handle_playing_state(playing_state, current_mouse_pressed); + } else { + let final_score = state.final_score; + let previous_mouse_pressed = state.previous_mouse_pressed; + return handle_game_over_click( + final_score, + current_mouse_pressed, + previous_mouse_pressed, + ); + } +} + +function game_loop(state) { + $rendering.render(state); + return request_animation_frame( + () => { + let _pipe = handle_events(state); + let _pipe$1 = update_state(_pipe); + return game_loop(_pipe$1); + }, + ); +} + +export function main() { + return game_loop(new MainMenu()); +} diff --git a/build/dev/javascript/stellar_prune/gleam.mjs b/build/dev/javascript/stellar_prune/gleam.mjs new file mode 100644 index 0000000..197cbbc --- /dev/null +++ b/build/dev/javascript/stellar_prune/gleam.mjs @@ -0,0 +1 @@ +export * from "../prelude.mjs"; diff --git a/build/dev/javascript/stellar_prune/hud.mjs b/build/dev/javascript/stellar_prune/hud.mjs new file mode 100644 index 0000000..fafec79 --- /dev/null +++ b/build/dev/javascript/stellar_prune/hud.mjs @@ -0,0 +1,153 @@ +import * as $float from "../gleam_stdlib/gleam/float.mjs"; +import * as $int from "../gleam_stdlib/gleam/int.mjs"; +import * as $paint from "../paint/paint.mjs"; +import * as $canvas from "../paint/paint/canvas.mjs"; +import { toList, CustomType as $CustomType } from "./gleam.mjs"; +import * as $physics from "./physics.mjs"; +import { Vec2 } from "./physics.mjs"; +import * as $target from "./target.mjs"; +import { Colorable } from "./target.mjs"; + +export class Score extends $CustomType { + constructor(position, colorable, score) { + super(); + this.position = position; + this.colorable = colorable; + this.score = score; + } +} +export const Score$Score = (position, colorable, score) => + new Score(position, colorable, score); +export const Score$isScore = (value) => value instanceof Score; +export const Score$Score$position = (value) => value.position; +export const Score$Score$0 = (value) => value.position; +export const Score$Score$colorable = (value) => value.colorable; +export const Score$Score$1 = (value) => value.colorable; +export const Score$Score$score = (value) => value.score; +export const Score$Score$2 = (value) => value.score; + +export class CrossHair extends $CustomType { + constructor(position, colorable, size) { + super(); + this.position = position; + this.colorable = colorable; + this.size = size; + } +} +export const CrossHair$CrossHair = (position, colorable, size) => + new CrossHair(position, colorable, size); +export const CrossHair$isCrossHair = (value) => value instanceof CrossHair; +export const CrossHair$CrossHair$position = (value) => value.position; +export const CrossHair$CrossHair$0 = (value) => value.position; +export const CrossHair$CrossHair$colorable = (value) => value.colorable; +export const CrossHair$CrossHair$1 = (value) => value.colorable; +export const CrossHair$CrossHair$size = (value) => value.size; +export const CrossHair$CrossHair$2 = (value) => value.size; + +export class Health extends $CustomType { + constructor(position, size, amount, max_health) { + super(); + this.position = position; + this.size = size; + this.amount = amount; + this.max_health = max_health; + } +} +export const Health$Health = (position, size, amount, max_health) => + new Health(position, size, amount, max_health); +export const Health$isHealth = (value) => value instanceof Health; +export const Health$Health$position = (value) => value.position; +export const Health$Health$0 = (value) => value.position; +export const Health$Health$size = (value) => value.size; +export const Health$Health$1 = (value) => value.size; +export const Health$Health$amount = (value) => value.amount; +export const Health$Health$2 = (value) => value.amount; +export const Health$Health$max_health = (value) => value.max_health; +export const Health$Health$3 = (value) => value.max_health; + +export class Hud extends $CustomType { + constructor(score, cross_hair, health) { + super(); + this.score = score; + this.cross_hair = cross_hair; + this.health = health; + } +} +export const Hud$Hud = (score, cross_hair, health) => + new Hud(score, cross_hair, health); +export const Hud$isHud = (value) => value instanceof Hud; +export const Hud$Hud$score = (value) => value.score; +export const Hud$Hud$0 = (value) => value.score; +export const Hud$Hud$cross_hair = (value) => value.cross_hair; +export const Hud$Hud$1 = (value) => value.cross_hair; +export const Hud$Hud$health = (value) => value.health; +export const Hud$Hud$2 = (value) => value.health; + +export function score_to_picture(score) { + let position; + let color; + let score$1; + position = score.position; + score$1 = score.score; + color = score.colorable.color; + let _pipe = $paint.text("Score: " + $int.to_string(score$1), 32); + let _pipe$1 = $paint.translate_xy(_pipe, position.x, position.y); + return $paint.fill(_pipe$1, color); +} + +export function cross_hair_to_picture(cross_hair) { + let x; + let y; + let color; + let size; + size = cross_hair.size; + x = cross_hair.position.x; + y = cross_hair.position.y; + color = cross_hair.colorable.color; + let _pipe = $paint.circle(size); + let _pipe$1 = $paint.stroke(_pipe, color, 5.0); + return $paint.translate_xy(_pipe$1, x, y); +} + +function health_to_picture_helper(loop$health, loop$acc, loop$count) { + while (true) { + let health = loop$health; + let acc = loop$acc; + let count = loop$count; + let $ = count < health.max_health; + if ($) { + let _block; + let $1 = count < health.amount; + if ($1) { + _block = $canvas.image_from_src("../res/heart.svg"); + } else { + _block = $canvas.image_from_src("../res/no-heart.svg"); + } + let heart_image = _block; + let size = $float.round(health.size); + loop$health = health; + loop$acc = $paint.combine( + toList([ + acc, + (() => { + let _pipe = $paint.image(heart_image, size, size); + let _pipe$1 = $paint.image_scaling_pixelated(_pipe); + return $paint.translate_xy( + _pipe$1, + $int.to_float(count) * (health.size + 10.0), + 0.0, + ); + })(), + ]), + ); + loop$count = count + 1; + } else { + return acc; + } + } +} + +export function health_to_picture(health) { + let _pipe = health_to_picture_helper(health, $paint.blank(), 0); + return $paint.translate_xy(_pipe, health.position.x, health.position.y); +} diff --git a/build/dev/javascript/stellar_prune/math/math.mjs b/build/dev/javascript/stellar_prune/math/math.mjs new file mode 100644 index 0000000..cbe2232 --- /dev/null +++ b/build/dev/javascript/stellar_prune/math/math.mjs @@ -0,0 +1,3 @@ +import { sin, cos, tan, atan2, floor, ceil, round, random, pi } from "./math_ffi.mjs"; + +export { atan2, ceil, cos, floor, pi, random, round, sin, tan }; diff --git a/build/dev/javascript/stellar_prune/math/math_ffi.mjs b/build/dev/javascript/stellar_prune/math/math_ffi.mjs new file mode 100644 index 0000000..8736bc2 --- /dev/null +++ b/build/dev/javascript/stellar_prune/math/math_ffi.mjs @@ -0,0 +1,35 @@ +export function sin(x) { + return Math.sin(x); +} + +export function cos(x) { + return Math.cos(x); +} + +export function tan(x) { + return Math.tan(x); +} + +export function atan2(y, x) { + return Math.atan2(y, x); +} + +export function floor(x) { + return Math.floor(x); +} + +export function ceil(x) { + return Math.ceil(x); +} + +export function round(x) { + return Math.round(x); +} + +export function random() { + return Math.random(); +} + +export function pi() { + return Math.PI; +} diff --git a/build/dev/javascript/stellar_prune/physics.mjs b/build/dev/javascript/stellar_prune/physics.mjs new file mode 100644 index 0000000..d0eb5a4 --- /dev/null +++ b/build/dev/javascript/stellar_prune/physics.mjs @@ -0,0 +1,63 @@ +import { CustomType as $CustomType } from "./gleam.mjs"; + +export class Vec2 extends $CustomType { + constructor(x, y) { + super(); + this.x = x; + this.y = y; + } +} +export const Vec2$Vec2 = (x, y) => new Vec2(x, y); +export const Vec2$isVec2 = (value) => value instanceof Vec2; +export const Vec2$Vec2$x = (value) => value.x; +export const Vec2$Vec2$0 = (value) => value.x; +export const Vec2$Vec2$y = (value) => value.y; +export const Vec2$Vec2$1 = (value) => value.y; + +export class RigidBody extends $CustomType { + constructor(velocity, acceleration, rotation, rotation_velocity) { + super(); + this.velocity = velocity; + this.acceleration = acceleration; + this.rotation = rotation; + this.rotation_velocity = rotation_velocity; + } +} +export const RigidBody$RigidBody = (velocity, acceleration, rotation, rotation_velocity) => + new RigidBody(velocity, acceleration, rotation, rotation_velocity); +export const RigidBody$isRigidBody = (value) => value instanceof RigidBody; +export const RigidBody$RigidBody$velocity = (value) => value.velocity; +export const RigidBody$RigidBody$0 = (value) => value.velocity; +export const RigidBody$RigidBody$acceleration = (value) => value.acceleration; +export const RigidBody$RigidBody$1 = (value) => value.acceleration; +export const RigidBody$RigidBody$rotation = (value) => value.rotation; +export const RigidBody$RigidBody$2 = (value) => value.rotation; +export const RigidBody$RigidBody$rotation_velocity = (value) => + value.rotation_velocity; +export const RigidBody$RigidBody$3 = (value) => value.rotation_velocity; + +export class Gravity extends $CustomType { + constructor(acceleration) { + super(); + this.acceleration = acceleration; + } +} +export const Gravity$Gravity = (acceleration) => new Gravity(acceleration); +export const Gravity$isGravity = (value) => value instanceof Gravity; +export const Gravity$Gravity$acceleration = (value) => value.acceleration; +export const Gravity$Gravity$0 = (value) => value.acceleration; + +export class Physics extends $CustomType { + constructor(gravity) { + super(); + this.gravity = gravity; + } +} +export const Physics$Physics = (gravity) => new Physics(gravity); +export const Physics$isPhysics = (value) => value instanceof Physics; +export const Physics$Physics$gravity = (value) => value.gravity; +export const Physics$Physics$0 = (value) => value.gravity; + +export function add_vec2(v1, v2) { + return new Vec2(v1.x + v2.x, v1.y + v2.y); +} diff --git a/build/dev/javascript/stellar_prune/rendering.mjs b/build/dev/javascript/stellar_prune/rendering.mjs new file mode 100644 index 0000000..457b2f8 --- /dev/null +++ b/build/dev/javascript/stellar_prune/rendering.mjs @@ -0,0 +1,155 @@ +import * as $int from "../gleam_stdlib/gleam/int.mjs"; +import * as $list from "../gleam_stdlib/gleam/list.mjs"; +import * as $paint from "../paint/paint.mjs"; +import * as $canvas from "../paint/paint/canvas.mjs"; +import { toList, prepend as listPrepend } from "./gleam.mjs"; +import * as $hud from "./hud.mjs"; +import * as $target from "./target.mjs"; +import * as $types from "./types.mjs"; +import { GameOver, MainMenu, Playing } from "./types.mjs"; + +function draw_main_menu() { + let lucy_image = $canvas.image_from_src("../res/lucy.svg"); + let diamond_image = $canvas.image_from_src("../res/diamond.svg"); + let rocket_image = $canvas.image_from_src("../res/rocket.svg"); + return $paint.combine( + toList([ + (() => { + let _pipe = $paint.text("Stellar prune", 72); + let _pipe$1 = $paint.translate_xy(_pipe, 300.0, 100.0); + let _pipe$2 = $paint.fill(_pipe$1, $paint.colour_hex("#ffffff")); + return $paint.image_scaling_pixelated(_pipe$2); + })(), + (() => { + let _pipe = $paint.text("Click/swipe the targets to score points!", 28); + let _pipe$1 = $paint.translate_xy(_pipe, 320.0, 200.0); + let _pipe$2 = $paint.fill(_pipe$1, $paint.colour_hex("#aaaaaa")); + return $paint.image_scaling_pixelated(_pipe$2); + })(), + (() => { + let _pipe = $paint.text( + "Don't let them escape or you'll lose health!", + 28, + ); + let _pipe$1 = $paint.translate_xy(_pipe, 320.0, 240.0); + let _pipe$2 = $paint.fill(_pipe$1, $paint.colour_hex("#aaaaaa")); + return $paint.image_scaling_pixelated(_pipe$2); + })(), + (() => { + let _pipe = $paint.image(lucy_image, 80, 80); + let _pipe$1 = $paint.image_scaling_pixelated(_pipe); + return $paint.translate_xy(_pipe$1, 320.0, 320.0); + })(), + (() => { + let _pipe = $paint.text("Lucy - Click for 1 point.", 24); + let _pipe$1 = $paint.translate_xy(_pipe, 420.0, 370.0); + let _pipe$2 = $paint.fill(_pipe$1, $paint.colour_hex("#ffffff")); + return $paint.image_scaling_pixelated(_pipe$2); + })(), + (() => { + let _pipe = $paint.image(diamond_image, 80, 80); + let _pipe$1 = $paint.image_scaling_pixelated(_pipe); + return $paint.translate_xy(_pipe$1, 320.0, 420.0); + })(), + (() => { + let _pipe = $paint.text("Diamond - Click for 3 points!", 24); + let _pipe$1 = $paint.translate_xy(_pipe, 420.0, 460.0); + let _pipe$2 = $paint.fill(_pipe$1, $paint.colour_hex("#ffffff")); + return $paint.image_scaling_pixelated(_pipe$2); + })(), + (() => { + let _pipe = $paint.image(rocket_image, 80, 80); + let _pipe$1 = $paint.image_scaling_pixelated(_pipe); + return $paint.translate_xy(_pipe$1, 320.0, 520.0); + })(), + (() => { + let _pipe = $paint.text("Rocket - AVOID! Loses 1 health", 24); + let _pipe$1 = $paint.translate_xy(_pipe, 420.0, 560.0); + let _pipe$2 = $paint.fill(_pipe$1, $paint.colour_hex("#ff6666")); + return $paint.image_scaling_pixelated(_pipe$2); + })(), + (() => { + let _pipe = $paint.text("Click to play!", 48); + let _pipe$1 = $paint.translate_xy(_pipe, 420.0, 680.0); + let _pipe$2 = $paint.fill(_pipe$1, $paint.colour_hex("#ffaff3")); + return $paint.image_scaling_pixelated(_pipe$2); + })(), + ]), + ); +} + +function draw_game_over(final_score) { + return $paint.combine( + toList([ + (() => { + let _pipe = $paint.text("Well done!", 128); + let _pipe$1 = $paint.translate_xy(_pipe, 300.0, 175.0); + let _pipe$2 = $paint.fill(_pipe$1, $paint.colour_hex("#ffaff3")); + return $paint.image_scaling_pixelated(_pipe$2); + })(), + (() => { + let _pipe = $paint.text("Final Score: ", 48); + let _pipe$1 = $paint.translate_xy(_pipe, 440.0, 400.0); + let _pipe$2 = $paint.fill(_pipe$1, $paint.colour_hex("#ffffff")); + return $paint.image_scaling_pixelated(_pipe$2); + })(), + (() => { + let _pipe = $paint.text($int.to_string(final_score), 48); + let _pipe$1 = $paint.translate_xy(_pipe, 710.0, 400.0); + let _pipe$2 = $paint.fill(_pipe$1, $paint.colour_hex("#ffaff3")); + return $paint.image_scaling_pixelated(_pipe$2); + })(), + (() => { + let _pipe = $paint.text("Click to play again!", 32); + let _pipe$1 = $paint.translate_xy(_pipe, 455.0, 600.0); + let _pipe$2 = $paint.fill(_pipe$1, $paint.colour_hex("#ffaff3")); + return $paint.image_scaling_pixelated(_pipe$2); + })(), + ]), + ); +} + +function draw_playing(playing_state) { + let target_pictures = $list.map( + playing_state.targets, + (target) => { + let position; + let rigid_body; + let paintable; + position = target.position; + rigid_body = target.rigid_body; + paintable = target.paintable; + let _pipe = $target.paintable_to_picture(paintable); + let _pipe$1 = $paint.rotate(_pipe, $paint.angle_deg(rigid_body.rotation)); + return $paint.translate_xy(_pipe$1, position.x, position.y); + }, + ); + let hud_without_crosshair = $paint.combine( + toList([ + $hud.health_to_picture(playing_state.hud.health), + $hud.score_to_picture(playing_state.hud.score), + ]), + ); + let crosshair = $hud.cross_hair_to_picture(playing_state.hud.cross_hair); + return $paint.combine( + listPrepend( + hud_without_crosshair, + $list.append(target_pictures, toList([crosshair])), + ), + ); +} + +export function render(state) { + let _block; + if (state instanceof MainMenu) { + _block = draw_main_menu(); + } else if (state instanceof Playing) { + let playing_state = state.state; + _block = draw_playing(playing_state); + } else { + let final_score = state.final_score; + _block = draw_game_over(final_score); + } + let picture = _block; + return $canvas.display((_) => { return picture; }, "#canvas"); +} diff --git a/build/dev/javascript/stellar_prune/target.mjs b/build/dev/javascript/stellar_prune/target.mjs new file mode 100644 index 0000000..04732a2 --- /dev/null +++ b/build/dev/javascript/stellar_prune/target.mjs @@ -0,0 +1,194 @@ +import * as $int from "../gleam_stdlib/gleam/int.mjs"; +import * as $paint from "../paint/paint.mjs"; +import * as $canvas from "../paint/paint/canvas.mjs"; +import { CustomType as $CustomType } from "./gleam.mjs"; +import * as $math from "./math/math.mjs"; +import * as $physics from "./physics.mjs"; +import { RigidBody, Vec2, add_vec2 } from "./physics.mjs"; + +export class Colorable extends $CustomType { + constructor(color) { + super(); + this.color = color; + } +} +export const Colorable$Colorable = (color) => new Colorable(color); +export const Colorable$isColorable = (value) => value instanceof Colorable; +export const Colorable$Colorable$color = (value) => value.color; +export const Colorable$Colorable$0 = (value) => value.color; + +export class Size extends $CustomType { + constructor(width, height) { + super(); + this.width = width; + this.height = height; + } +} +export const Size$Size = (width, height) => new Size(width, height); +export const Size$isSize = (value) => value instanceof Size; +export const Size$Size$width = (value) => value.width; +export const Size$Size$0 = (value) => value.width; +export const Size$Size$height = (value) => value.height; +export const Size$Size$1 = (value) => value.height; + +export class Square extends $CustomType { + constructor(colorable, size) { + super(); + this.colorable = colorable; + this.size = size; + } +} +export const Paintable$Square = (colorable, size) => + new Square(colorable, size); +export const Paintable$isSquare = (value) => value instanceof Square; +export const Paintable$Square$colorable = (value) => value.colorable; +export const Paintable$Square$0 = (value) => value.colorable; +export const Paintable$Square$size = (value) => value.size; +export const Paintable$Square$1 = (value) => value.size; + +export class Sprite extends $CustomType { + constructor(image, size) { + super(); + this.image = image; + this.size = size; + } +} +export const Paintable$Sprite = (image, size) => new Sprite(image, size); +export const Paintable$isSprite = (value) => value instanceof Sprite; +export const Paintable$Sprite$image = (value) => value.image; +export const Paintable$Sprite$0 = (value) => value.image; +export const Paintable$Sprite$size = (value) => value.size; +export const Paintable$Sprite$1 = (value) => value.size; + +export class Lucy extends $CustomType {} +export const TargetKind$Lucy = () => new Lucy(); +export const TargetKind$isLucy = (value) => value instanceof Lucy; + +export class Diamond extends $CustomType { + constructor(points_value) { + super(); + this.points_value = points_value; + } +} +export const TargetKind$Diamond = (points_value) => new Diamond(points_value); +export const TargetKind$isDiamond = (value) => value instanceof Diamond; +export const TargetKind$Diamond$points_value = (value) => value.points_value; +export const TargetKind$Diamond$0 = (value) => value.points_value; + +export class Rocket extends $CustomType {} +export const TargetKind$Rocket = () => new Rocket(); +export const TargetKind$isRocket = (value) => value instanceof Rocket; + +export class Target extends $CustomType { + constructor(position, rigid_body, paintable, kind) { + super(); + this.position = position; + this.rigid_body = rigid_body; + this.paintable = paintable; + this.kind = kind; + } +} +export const Target$Target = (position, rigid_body, paintable, kind) => + new Target(position, rigid_body, paintable, kind); +export const Target$isTarget = (value) => value instanceof Target; +export const Target$Target$position = (value) => value.position; +export const Target$Target$0 = (value) => value.position; +export const Target$Target$rigid_body = (value) => value.rigid_body; +export const Target$Target$1 = (value) => value.rigid_body; +export const Target$Target$paintable = (value) => value.paintable; +export const Target$Target$2 = (value) => value.paintable; +export const Target$Target$kind = (value) => value.kind; +export const Target$Target$3 = (value) => value.kind; + +export function create_target() { + let vx = ($math.random() * 6.0) - 3.0; + let vy = ($math.random() * -4.0) - 8.0; + let rotation_vel = ($math.random() * 2.0) - 1.0; + let start_x = ($math.random() * 600.0) + 200.0; + let random_type = $math.random(); + let _block; + let r = random_type; + if (r < 0.7) { + _block = ["../res/lucy.svg", new Lucy()]; + } else { + let r$1 = random_type; + if (r$1 < 0.85) { + _block = ["../res/diamond.svg", new Diamond(3)]; + } else { + _block = ["../res/rocket.svg", new Rocket()]; + } + } + let $ = _block; + let sprite_path; + let target_kind; + sprite_path = $[0]; + target_kind = $[1]; + return new Target( + new Vec2(start_x, 900.0), + new RigidBody(new Vec2(vx, vy), new Vec2(0.0, 0.0), 0.0, rotation_vel), + new Sprite($canvas.image_from_src(sprite_path), new Size(120, 120)), + target_kind, + ); +} + +export function update_target(target, gravity) { + let $ = target.rigid_body; + let velocity; + let acceleration; + let rotation; + let rotation_velocity; + velocity = $.velocity; + acceleration = $.acceleration; + rotation = $.rotation; + rotation_velocity = $.rotation_velocity; + let total_acceleration = add_vec2(acceleration, gravity); + let new_velocity = add_vec2(velocity, total_acceleration); + let new_position = add_vec2(target.position, new_velocity); + let new_rigid_body = new RigidBody( + new_velocity, + acceleration, + rotation + rotation_velocity, + rotation_velocity, + ); + return new Target(new_position, new_rigid_body, target.paintable, target.kind); +} + +export function is_point_in_target(point, target) { + let dx = point.x - target.position.x; + let dy = point.y - target.position.y; + let angle_rad = ((target.rigid_body.rotation * $math.pi())) / 180.0; + let cos_angle = $math.cos(angle_rad); + let sin_angle = $math.sin(angle_rad); + let local_x = (dx * cos_angle) + (dy * sin_angle); + let local_y = (dy * cos_angle) - (dx * sin_angle); + let $ = target.paintable; + if ($ instanceof Square) { + let size = $.size; + let half_size = size / 2.0; + return (((local_x >= (0.0 - half_size)) && (local_x <= half_size)) && (local_y >= (0.0 - half_size))) && (local_y <= half_size); + } else { + let size = $.size; + let half_width = $int.to_float(size.width) / 2.0; + let half_height = $int.to_float(size.height) / 2.0; + return (((local_x >= (0.0 - half_width)) && (local_x <= half_width)) && (local_y >= (0.0 - half_height))) && (local_y <= half_height); + } +} + +export function paintable_to_picture(paintable) { + if (paintable instanceof Square) { + let colorable = paintable.colorable; + let size = paintable.size; + let _pipe = $paint.square(size); + return $paint.fill(_pipe, colorable.color); + } else { + let image = paintable.image; + let size = paintable.size; + let _pipe = $paint.image(image, size.width, size.height); + let _pipe$1 = $paint.image_scaling_pixelated(_pipe); + return $paint.translate_xy( + _pipe$1, + 0.0 - ($int.to_float(size.width) / 2.0), + 0.0 - ($int.to_float(size.height) / 2.0), + ); + } +} diff --git a/build/dev/javascript/stellar_prune/types.mjs b/build/dev/javascript/stellar_prune/types.mjs new file mode 100644 index 0000000..72fff8f --- /dev/null +++ b/build/dev/javascript/stellar_prune/types.mjs @@ -0,0 +1,66 @@ +import { CustomType as $CustomType } from "./gleam.mjs"; +import * as $hud from "./hud.mjs"; +import * as $physics from "./physics.mjs"; +import * as $target from "./target.mjs"; + +export class PlayingState extends $CustomType { + constructor(targets, physics, hud, mouse, spawn_timer) { + super(); + this.targets = targets; + this.physics = physics; + this.hud = hud; + this.mouse = mouse; + this.spawn_timer = spawn_timer; + } +} +export const PlayingState$PlayingState = (targets, physics, hud, mouse, spawn_timer) => + new PlayingState(targets, physics, hud, mouse, spawn_timer); +export const PlayingState$isPlayingState = (value) => + value instanceof PlayingState; +export const PlayingState$PlayingState$targets = (value) => value.targets; +export const PlayingState$PlayingState$0 = (value) => value.targets; +export const PlayingState$PlayingState$physics = (value) => value.physics; +export const PlayingState$PlayingState$1 = (value) => value.physics; +export const PlayingState$PlayingState$hud = (value) => value.hud; +export const PlayingState$PlayingState$2 = (value) => value.hud; +export const PlayingState$PlayingState$mouse = (value) => value.mouse; +export const PlayingState$PlayingState$3 = (value) => value.mouse; +export const PlayingState$PlayingState$spawn_timer = (value) => + value.spawn_timer; +export const PlayingState$PlayingState$4 = (value) => value.spawn_timer; + +export class MainMenu extends $CustomType {} +export const GameState$MainMenu = () => new MainMenu(); +export const GameState$isMainMenu = (value) => value instanceof MainMenu; + +export class Playing extends $CustomType { + constructor(state, previous_mouse_pressed) { + super(); + this.state = state; + this.previous_mouse_pressed = previous_mouse_pressed; + } +} +export const GameState$Playing = (state, previous_mouse_pressed) => + new Playing(state, previous_mouse_pressed); +export const GameState$isPlaying = (value) => value instanceof Playing; +export const GameState$Playing$state = (value) => value.state; +export const GameState$Playing$0 = (value) => value.state; +export const GameState$Playing$previous_mouse_pressed = (value) => + value.previous_mouse_pressed; +export const GameState$Playing$1 = (value) => value.previous_mouse_pressed; + +export class GameOver extends $CustomType { + constructor(final_score, previous_mouse_pressed) { + super(); + this.final_score = final_score; + this.previous_mouse_pressed = previous_mouse_pressed; + } +} +export const GameState$GameOver = (final_score, previous_mouse_pressed) => + new GameOver(final_score, previous_mouse_pressed); +export const GameState$isGameOver = (value) => value instanceof GameOver; +export const GameState$GameOver$final_score = (value) => value.final_score; +export const GameState$GameOver$0 = (value) => value.final_score; +export const GameState$GameOver$previous_mouse_pressed = (value) => + value.previous_mouse_pressed; +export const GameState$GameOver$1 = (value) => value.previous_mouse_pressed; diff --git a/build/gleam-dev-erlang.lock b/build/gleam-dev-erlang.lock new file mode 100644 index 0000000..e69de29 diff --git a/build/gleam-dev-javascript.lock b/build/gleam-dev-javascript.lock new file mode 100644 index 0000000..e69de29 diff --git a/build/gleam-lsp-erlang.lock b/build/gleam-lsp-erlang.lock new file mode 100644 index 0000000..e69de29 diff --git a/build/gleam-lsp-javascript.lock b/build/gleam-lsp-javascript.lock new file mode 100644 index 0000000..e69de29 diff --git a/build/gleam-prod-erlang.lock b/build/gleam-prod-erlang.lock new file mode 100644 index 0000000..e69de29 diff --git a/build/gleam-prod-javascript.lock b/build/gleam-prod-javascript.lock new file mode 100644 index 0000000..e69de29 diff --git a/build/packages/gleam.lock b/build/packages/gleam.lock new file mode 100644 index 0000000..e69de29 diff --git a/build/packages/gleam_community_colour/LICENCE b/build/packages/gleam_community_colour/LICENCE new file mode 100644 index 0000000..a84f0ec --- /dev/null +++ b/build/packages/gleam_community_colour/LICENCE @@ -0,0 +1,190 @@ + 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 2023 Gleam Community Contributors + + 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. \ No newline at end of file diff --git a/build/packages/gleam_community_colour/README.md b/build/packages/gleam_community_colour/README.md new file mode 100644 index 0000000..0eccdd7 --- /dev/null +++ b/build/packages/gleam_community_colour/README.md @@ -0,0 +1,36 @@ +# gleam-community/colour + +A package for a standard Colour type, conversions, and other utilities. + +[![Package Version](https://img.shields.io/hexpm/v/gleam_community_colour)](https://hex.pm/packages/gleam_community_colour) +[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/gleam_community_colour/) + +✨ This project is written in pure Gleam so you can use it anywhere Gleam runs: Erlang, Elixir, Node, Deno, and the browser! + +--- + +## Quickstart + +```gleam +import gleam_community/colour +import gleam_community/colour/accessibility + +pub fn main() { + let foreground = colour.from_hsl(h: 0.858, s: 1.0, l: 0.843) + + let background_options = [colour.light_grey, colour.dark_grey] + + let background = accessibility.maximum_contrast(foreground, background_options) +} +``` + +## Installation + +`gleam_community` packages are published to [hex.pm](https://hex.pm/packages/gleam_community_colour) +with the prefix `gleam_community_`. You can add them to your Gleam projects directly: + +```sh +gleam add gleam_community_colour +``` + +The docs can be found over at [hexdocs.pm](https://hexdocs.pm/gleam_community_colour). diff --git a/build/packages/gleam_community_colour/gleam.toml b/build/packages/gleam_community_colour/gleam.toml new file mode 100644 index 0000000..3b7155c --- /dev/null +++ b/build/packages/gleam_community_colour/gleam.toml @@ -0,0 +1,13 @@ +name = "gleam_community_colour" +version = "2.0.2" +licences = ["Apache-2.0"] +description = "Colour types, conversions, and other utilities" +repository = { type = "github", user = "gleam-community", repo = "colour" } +gleam = ">= 1.4.0" + +[dependencies] +gleam_stdlib = ">= 0.50.0 and < 2.0.0" +gleam_json = ">= 2.2.0 and < 4.0.0" + +[dev-dependencies] +gleeunit = ">= 1.3.0 and < 2.0.0" diff --git a/build/packages/gleam_community_colour/include/gleam_community@colour_Hsla.hrl b/build/packages/gleam_community_colour/include/gleam_community@colour_Hsla.hrl new file mode 100644 index 0000000..06116df --- /dev/null +++ b/build/packages/gleam_community_colour/include/gleam_community@colour_Hsla.hrl @@ -0,0 +1 @@ +-record(hsla, {h :: float(), s :: float(), l :: float(), a :: float()}). diff --git a/build/packages/gleam_community_colour/include/gleam_community@colour_Rgba.hrl b/build/packages/gleam_community_colour/include/gleam_community@colour_Rgba.hrl new file mode 100644 index 0000000..fff139e --- /dev/null +++ b/build/packages/gleam_community_colour/include/gleam_community@colour_Rgba.hrl @@ -0,0 +1 @@ +-record(rgba, {r :: float(), g :: float(), b :: float(), a :: float()}). diff --git a/build/packages/gleam_community_colour/src/gleam_community/colour.gleam b/build/packages/gleam_community_colour/src/gleam_community/colour.gleam new file mode 100644 index 0000000..f394cff --- /dev/null +++ b/build/packages/gleam_community_colour/src/gleam_community/colour.gleam @@ -0,0 +1,1214 @@ +//// +//// - **Types** +//// - [`Colour`](#Colour) +//// - [`Color`](#Color) +//// - **Constructors** +//// - [`from_rgb255`](#from_rgb255) +//// - [`from_rgb`](#from_rgb) +//// - [`from_rgba`](#from_rgba) +//// - [`from_hsl`](#from_hsl) +//// - [`from_hsla`](#from_hsla) +//// - [`from_rgb_hex`](#from_rgb_hex) +//// - [`from_rgba_hex`](#from_rgba_hex) +//// - [`from_rgb_hex_string`](#from_rgb_hex_string) +//// - [`from_rgba_hex_string`](#from_rgba_hex_string) +//// - **Conversions** +//// - [`to_rgba`](#to_rgba) +//// - [`to_hsla`](#hsla) +//// - [`to_css_rgba_string`](#to_css_rgba_string) +//// - [`to_rgba_hex_string`](#to_rgba_hex_string) +//// - [`to_rgb_hex_string`](#to_rgb_hex_string) +//// - [`to_rgba_hex`](#to_rgba_hex) +//// - [`to_rgb_hex`](#to_rgb_hex) +//// - **JSON** +//// - [`encode`](#encode) +//// - [`decoder`](#decoder) +//// - **Colours** +//// - [`light_red`](#light_red) +//// - [`red`](#red) +//// - [`dark_red`](#dark_red) +//// - [`light_orange`](#light_orange) +//// - [`orange`](#orange) +//// - [`dark_orange`](#dark_orange) +//// - [`light_yellow`](#light_yellow) +//// - [`yellow`](#yellow) +//// - [`dark_yellow`](#dark_yellow) +//// - [`light_green`](#light_green) +//// - [`green`](#green) +//// - [`dark_green`](#dark_green) +//// - [`light_blue`](#light_blue) +//// - [`blue`](#blue) +//// - [`dark_blue`](#dark_blue) +//// - [`light_purple`](#light_purple) +//// - [`purple`](#purple) +//// - [`dark_purple`](#dark_purple) +//// - [`light_brown`](#light_brown) +//// - [`brown`](#brown) +//// - [`dark_brown`](#dark_brown) +//// - [`black`](#black) +//// - [`white`](#white) +//// - [`light_grey`](#light_grey) +//// - [`grey`](#grey) +//// - [`dark_grey`](#dark_grey) +//// - [`light_gray`](#light_gray) +//// - [`gray`](#gray) +//// - [`dark_gray`](#dark_gray) +//// - [`light_charcoal`](#light_charcoal) +//// - [`charcoal`](#charcoal) +//// - [`dark_charcoal`](#dark_charcoal) +//// - [`pink`](#pink) +//// +//// --- +//// +//// This package was heavily inspired by the `elm-color` module. +//// The original source code can be found +//// here. +//// +////