diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2957827..d651d87 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,7 +15,7 @@ jobs: - uses: erlef/setup-beam@v1 with: otp-version: "26.0.2" - gleam-version: "1.5.1" + gleam-version: "1.9.1" rebar3-version: "3" # elixir-version: "1.15.4" - run: gleam deps download diff --git a/gleam.toml b/gleam.toml index 5da8c45..1f91a2b 100644 --- a/gleam.toml +++ b/gleam.toml @@ -4,12 +4,10 @@ description = "Pretty print values with style!" licences = ["Apache-2.0"] repository = { type = "github", user = "MystPi", repo = "pprint" } -internal_modules = [ - "pprint/decoder" -] +internal_modules = ["pprint/decoder"] [dependencies] -gleam_stdlib = "~> 0.34 or ~> 1.0" +gleam_stdlib = ">= 0.50.0 and < 2.0.0" glam = "~> 2.0" [dev-dependencies] diff --git a/manifest.toml b/manifest.toml index ca977af..d31cfe7 100644 --- a/manifest.toml +++ b/manifest.toml @@ -3,21 +3,23 @@ packages = [ { name = "argv", version = "1.0.2", build_tools = ["gleam"], requirements = [], otp_app = "argv", source = "hex", outer_checksum = "BA1FF0929525DEBA1CE67256E5ADF77A7CDDFE729E3E3F57A5BDCAA031DED09D" }, - { name = "birdie", version = "1.1.4", build_tools = ["gleam"], requirements = ["argv", "filepath", "glance", "gleam_community_ansi", "gleam_erlang", "gleam_stdlib", "justin", "rank", "simplifile", "trie_again"], otp_app = "birdie", source = "hex", outer_checksum = "67E5D53B85540EDC6FAC738D7A4A2EEAA0C4920230AFFF4FA76C63670C057E5A" }, - { name = "filepath", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "EFB6FF65C98B2A16378ABC3EE2B14124168C0CE5201553DE652E2644DCFDB594" }, - { name = "glam", version = "2.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glam", source = "hex", outer_checksum = "66EC3BCD632E51EED029678F8DF419659C1E57B1A93D874C5131FE220DFAD2B2" }, - { name = "glance", version = "0.11.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "glexer"], otp_app = "glance", source = "hex", outer_checksum = "8F3314D27773B7C3B9FB58D8C02C634290422CE531988C0394FA0DF8676B964D" }, - { name = "gleam_community_ansi", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "FE79E08BF97009729259B6357EC058315B6FBB916FAD1C2FF9355115FEB0D3A4" }, - { name = "gleam_community_colour", version = "1.4.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "795964217EBEDB3DA656F5EB8F67D7AD22872EB95182042D3E7AFEF32D3FD2FE" }, - { name = "gleam_erlang", version = "0.25.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "054D571A7092D2A9727B3E5D183B7507DAB0DA41556EC9133606F09C15497373" }, - { name = "gleam_json", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "thoas"], otp_app = "gleam_json", source = "hex", outer_checksum = "9063D14D25406326C0255BDA0021541E797D8A7A12573D849462CAFED459F6EB" }, - { name = "gleam_stdlib", version = "0.37.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "5398BD6C2ABA17338F676F42F404B9B7BABE1C8DC7380031ACB05BBE1BCF3742" }, - { name = "gleeunit", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "72CDC3D3F719478F26C4E2C5FED3E657AC81EC14A47D2D2DEBB8693CA3220C3B" }, - { name = "glexer", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glexer", source = "hex", outer_checksum = "BD477AD657C2B637FEF75F2405FAEFFA533F277A74EF1A5E17B55B1178C228FB" }, + { name = "birdie", version = "1.2.6", build_tools = ["gleam"], requirements = ["argv", "edit_distance", "filepath", "glance", "gleam_community_ansi", "gleam_erlang", "gleam_stdlib", "justin", "rank", "simplifile", "term_size", "trie_again"], otp_app = "birdie", source = "hex", outer_checksum = "1363F4C7E7433A4A8350CC682BCDDBA5BBC6F66C94EFC63BC43025F796C4F6D0" }, + { name = "edit_distance", version = "2.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "edit_distance", source = "hex", outer_checksum = "A1E485C69A70210223E46E63985FA1008B8B2DDA9848B7897469171B29020C05" }, + { name = "filepath", version = "1.1.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "65F51013BCF78A603AFFD7992EF1CC6ECA96C74038EB48887F656DE44DBC1902" }, + { name = "glam", version = "2.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glam", source = "hex", outer_checksum = "4932A2D139AB0389E149396407F89654928D7B815E212BB02F13C66F53B1BBA1" }, + { name = "glance", version = "2.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "glexer"], otp_app = "glance", source = "hex", outer_checksum = "106111453AE9BA959184302B7DADF2E8CF322B27A7CB68EE78F3EE43FEACCE2C" }, + { name = "gleam_community_ansi", version = "1.4.3", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_regexp", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "8A62AE9CC6EA65BEA630D95016D6C07E4F9973565FA3D0DE68DC4200D8E0DD27" }, + { name = "gleam_community_colour", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "FDD6AC62C6EC8506C005949A4FCEF032038191D5EAAEC3C9A203CD53AE956ACA" }, + { name = "gleam_erlang", version = "0.34.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "0C38F2A128BAA0CEF17C3000BD2097EB80634E239CE31A86400C4416A5D0FDCC" }, + { name = "gleam_json", version = "2.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "C55C5C2B318533A8072D221C5E06E5A75711C129E420DD1CE463342106012E5D" }, + { name = "gleam_regexp", version = "1.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_regexp", source = "hex", outer_checksum = "7F5E0C0BBEB3C58E57C9CB05FA9002F970C85AD4A63BA1E55CBCB35C15809179" }, + { name = "gleam_stdlib", version = "0.58.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "091F2D2C4A3A4E2047986C47E2C2C9D728A4E068ABB31FDA17B0D347E6248467" }, + { name = "gleeunit", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "0E6C83834BA65EDCAAF4FE4FB94AC697D9262D83E6F58A750D63C9F6C8A9D9FF" }, + { name = "glexer", version = "2.2.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glexer", source = "hex", outer_checksum = "5C235CBDF4DA5203AD5EAB1D6D8B456ED8162C5424FE2309CFFB7EF438B7C269" }, { name = "justin", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "justin", source = "hex", outer_checksum = "7FA0C6DB78640C6DC5FBFD59BF3456009F3F8B485BF6825E97E1EB44E9A1E2CD" }, { name = "rank", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "rank", source = "hex", outer_checksum = "5660E361F0E49CBB714CC57CC4C89C63415D8986F05B2DA0C719D5642FAD91C9" }, - { name = "simplifile", version = "1.7.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "1D5DFA3A2F9319EC85825F6ED88B8E449F381B0D55A62F5E61424E748E7DDEB0" }, - { name = "thoas", version = "1.2.0", build_tools = ["rebar3"], requirements = [], otp_app = "thoas", source = "hex", outer_checksum = "540C8CB7D9257F2AD0A14145DC23560F91ACDCA995F0CCBA779EB33AF5D859D1" }, + { name = "simplifile", version = "2.2.1", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "C88E0EE2D509F6D86EB55161D631657675AA7684DAB83822F7E59EB93D9A60E3" }, + { name = "term_size", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "term_size", source = "hex", outer_checksum = "D00BD2BC8FB3EBB7E6AE076F3F1FF2AC9D5ED1805F004D0896C784D06C6645F1" }, { name = "trie_again", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "trie_again", source = "hex", outer_checksum = "5B19176F52B1BD98831B57FDC97BD1F88C8A403D6D8C63471407E78598E27184" }, ] diff --git a/src/pprint/decoder.gleam b/src/pprint/decoder.gleam index b1aed16..ef73040 100644 --- a/src/pprint/decoder.gleam +++ b/src/pprint/decoder.gleam @@ -1,5 +1,6 @@ import gleam/dict.{type Dict} import gleam/dynamic.{type Dynamic} +import gleam/dynamic/decode import gleam/result import gleam/string @@ -27,42 +28,54 @@ pub type Field { // ---- DECODERS --------------------------------------------------------------- pub fn classify(value: Dynamic) -> Type { - let assert Ok(t) = decode_type(value) + let assert Ok(t) = decode.run(value, type_decoder()) t } -/// This decoder will always be `Ok`. It returns `Result` so that it is compatible -/// with other decoders. -/// -fn decode_type(value: Dynamic) -> Result(Type, List(dynamic.DecodeError)) { - use <- result.lazy_or(result.map(dynamic.int(value), TInt)) - use <- result.lazy_or(result.map(dynamic.float(value), TFloat)) - use <- result.lazy_or(result.map(dynamic.string(value), TString)) - use <- result.lazy_or(result.map(dynamic.bool(value), TBool)) - use <- result.lazy_or(result.map(decode_nil(value), fn(_) { TNil })) - use <- result.lazy_or(result.map(dynamic.bit_array(value), TBitArray)) - use <- result.lazy_or(decode_custom_type(value)) - use <- result.lazy_or(result.map(decode_tuple(value), TTuple)) - use <- result.lazy_or(result.map(dynamic.shallow_list(value), TList)) - use <- result.lazy_or(result.map( - dynamic.dict(decode_type, decode_type)(value), - TDict, - )) - // Anything else we just inspect. This could be a function or an external object - // or type from the runtime. - Ok(TForeign(string.inspect(value))) +/// This decoder will always return `Ok`, as it ends with a catch-all +/// pattern returning a `TForeign`. +/// +fn type_decoder() -> decode.Decoder(Type) { + use <- decode.recursive + decode.one_of(decode.map(decode.int, TInt), [ + decode.map(decode.float, TFloat), + decode.map(decode.float, TFloat), + decode.map(decode.string, TString), + decode.map(decode.bool, TBool), + decode.map(nil(), fn(_) { TNil }), + decode.map(decode.bit_array, TBitArray), + custom_type(), + decode.map(tuple(), TTuple), + decode.map(decode.list(decode.dynamic), TList), + decode.map(decode.dict(type_decoder(), type_decoder()), TDict), + decode.map(decode.dynamic, fn(value) { TForeign(string.inspect(value)) }), + ]) +} + +fn tuple() -> decode.Decoder(List(Dynamic)) { + decode.new_primitive_decoder("Tuple", fn(dynamic) { + result.replace_error(decode_tuple(dynamic), []) + }) +} + +fn custom_type() -> decode.Decoder(Type) { + decode.new_primitive_decoder("CustomType", fn(dynamic) { + result.replace_error(decode_custom_type(dynamic), TCustom("", [])) + }) +} + +fn nil() -> decode.Decoder(Nil) { + decode.new_primitive_decoder("Nil", decode_nil) } @external(erlang, "pprint_ffi", "decode_custom_type") @external(javascript, "../pprint_ffi.mjs", "decode_custom_type") -fn decode_custom_type(value: Dynamic) -> Result(Type, List(dynamic.DecodeError)) +fn decode_custom_type(value: Dynamic) -> Result(Type, Nil) @external(erlang, "pprint_ffi", "decode_tuple") @external(javascript, "../pprint_ffi.mjs", "decode_tuple") -fn decode_tuple( - value: Dynamic, -) -> Result(List(Dynamic), List(dynamic.DecodeError)) +fn decode_tuple(value: Dynamic) -> Result(List(Dynamic), Nil) @external(erlang, "pprint_ffi", "decode_nil") @external(javascript, "../pprint_ffi.mjs", "decode_nil") -fn decode_nil(value: Dynamic) -> Result(Nil, List(dynamic.DecodeError)) +fn decode_nil(value: Dynamic) -> Result(Nil, Nil) diff --git a/src/pprint_ffi.erl b/src/pprint_ffi.erl index 86772bf..d3eeca9 100644 --- a/src/pprint_ffi.erl +++ b/src/pprint_ffi.erl @@ -8,13 +8,13 @@ decode_nil(X) -> case X of nil -> {ok, nil}; - _ -> decode_error("Nil", X) + _ -> {error, nil} end. decode_tuple(X) -> case X of Tuple when is_tuple(Tuple) -> {ok, tuple_to_list(Tuple)}; - _ -> decode_error("Tuple", X) + _ -> {error, nil} end. decode_custom_type(X) -> @@ -23,7 +23,7 @@ decode_custom_type(X) -> Atom when is_atom(Atom) -> case inspect_maybe_gleam_atom(erlang:atom_to_binary(Atom), none, <<>>) of {ok, AtomName} -> {ok, {t_custom, AtomName, []}}; - {error, nil} -> decode_error("CustomType", X) + {error, nil} -> {error, nil} end; % Variants with fields are encoded as tuples where the first items is an % atom with the variant's name. @@ -31,20 +31,20 @@ decode_custom_type(X) -> case tuple_to_list(Tuple) of [Atom | Elements] when is_atom(Atom) -> case inspect_maybe_gleam_atom(erlang:atom_to_binary(Atom), none, <<>>) of - {ok, AtomName} -> {ok, {t_custom, AtomName, lists:map(fun(E) -> {positional, E} end, Elements)}}; - {error, nil} -> decode_error("CustomType", X) + {ok, AtomName} -> + {ok, + {t_custom, AtomName, + lists:map(fun(E) -> {positional, E} end, Elements)}}; + {error, nil} -> + {error, nil} end; - _ -> decode_error("CustomType", X) + _ -> + {error, nil} end; - _ -> decode_error("CustomType", X) + _ -> + {error, nil} end. -decode_error(Expected, Got) -> - ExpectedString = list_to_binary(Expected), - GotString = gleam_stdlib:classify_dynamic(Got), - DecodeError = {decode_error, ExpectedString, GotString, []}, - {error, [DecodeError]}. - % This is copy pasted from gleam's stdlib and performs some additional checks to % make sure the given atom is a gleam's custom type atom. Stdlib doesn't export % it so I had to copy it. @@ -58,10 +58,11 @@ inspect_maybe_gleam_atom(<<"_", _Rest/binary>>, none, _) -> {error, nil}; inspect_maybe_gleam_atom(<<"_">>, _PrevChar, _Acc) -> {error, nil}; -inspect_maybe_gleam_atom(<<"_", _Rest/binary>>, $_, _Acc) -> +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)) -> +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, <>); diff --git a/src/pprint_ffi.mjs b/src/pprint_ffi.mjs index 931790b..ad8e300 100644 --- a/src/pprint_ffi.mjs +++ b/src/pprint_ffi.mjs @@ -1,20 +1,6 @@ -import * as $stdlib from '../gleam_stdlib/gleam_stdlib.mjs'; -import * as $dynamic from '../gleam_stdlib/gleam/dynamic.mjs'; import * as $gleam from './gleam.mjs'; import * as $decoder from './pprint/decoder.mjs'; -function decoder_error(expected, got) { - return decoder_error_no_classify(expected, $stdlib.classify_dynamic(got)); -} - -function decoder_error_no_classify(expected, got) { - return new $gleam.Error( - $gleam.List.fromArray([ - new $dynamic.DecodeError(expected, got, $gleam.toList([])), - ]) - ); -} - export function decode_custom_type(value) { if (value instanceof $gleam.CustomType) { const name = value.constructor.name; @@ -27,15 +13,15 @@ export function decode_custom_type(value) { return new $gleam.Ok(new $decoder.TCustom(name, $gleam.toList(fields))); } - return decoder_error('CustomType', value); + return new $gleam.Error(undefined); } export function decode_tuple(value) { if (Array.isArray(value)) return new $gleam.Ok($gleam.toList(value)); - return decoder_error('Tuple', value); + return new $gleam.Error(undefined); } export function decode_nil(value) { - if (value === undefined) return new $gleam.Ok(); - return decoder_error('Nil', value); + if (value === undefined) return new $gleam.Ok(undefined); + return new $gleam.Error(undefined); }