Add decoders

This commit is contained in:
Mikko Ahlroth 2024-02-16 23:31:34 +02:00
parent 75a020f08c
commit d7ecddda63
6 changed files with 102 additions and 4 deletions

View file

@ -1,3 +1,9 @@
2.1.0
-----
+ Added a gleam/dynamic Decoder.
+ Added a string decoder.
2.0.0
-----

View file

@ -1,5 +1,5 @@
name = "bigi"
version = "2.0.0"
version = "2.1.0"
# Fill out these fields if you intend to generate HTML documentation or publish
# your project to the Hex package manager.

View file

@ -1,4 +1,5 @@
import gleam/order
import gleam/dynamic
/// A big integer.
pub type BigInt
@ -9,11 +10,27 @@ pub type BigInt
pub fn zero() -> BigInt
/// Create a big integer from a regular integer.
///
/// Note that in the JavaScript target, if your integer is bigger than the
/// [maximum safe integer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER)
/// or smaller than the
/// [minimum safe integer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MIN_SAFE_INTEGER),
/// you may lose precision when operating on it, including when converting it
/// into a big integer (as the JavaScript Number type has already reduced the
/// precision of the value).
@external(erlang, "bigi_ffi", "from")
@external(javascript, "./bigi_ffi.mjs", "from")
pub fn from_int(int: Int) -> BigInt
/// Convert the big integer to a regular integer.
/// Convert a string into a big integer.
///
/// If the string does not represent a big integer in base 10, an error is
/// returned. Trailing non-digit content is not allowed.
@external(erlang, "bigi_ffi", "from_string")
@external(javascript, "./bigi_ffi.mjs", "from_string")
pub fn from_string(str: String) -> Result(BigInt, Nil)
/// Convert a big integer to a regular integer.
///
/// In Erlang, this cannot fail, as all Erlang integers are big integers. In the
/// JavaScript target, this will fail if the integer is bigger than the
@ -123,6 +140,11 @@ pub fn digits(bigint: BigInt) {
get_digit(bigint, [], divisor)
}
/// Decode a `gleam/dynamic` value into a big integer, if possible.
@external(erlang, "bigi_ffi", "decode")
@external(javascript, "./bigi_ffi.mjs", "decode")
pub fn decode(dyn: dynamic.Dynamic) -> Result(BigInt, dynamic.DecodeErrors)
fn get_digit(bigint: BigInt, digits: List(Int), divisor: BigInt) {
case compare(bigint, divisor) {
order.Gt -> {

View file

@ -2,6 +2,7 @@
-export([
from/1,
from_string/1,
to/1,
zero/0,
compare/2,
@ -14,11 +15,18 @@
remainder_no_zero/2,
modulo/2,
modulo_no_zero/2,
power/2
power/2,
decode/1
]).
from(Int) -> Int.
from_string(Str) ->
case string:to_integer(Str) of
{_, Rest} when Rest /= <<"">> -> {error, nil};
{Int, _} -> {ok, Int}
end.
to(BigInt) -> {ok, BigInt}.
zero() -> 0.
@ -65,3 +73,18 @@ do_power(A, N) ->
0 -> 1;
1 -> A
end).
decode(Dyn) when is_integer(Dyn) -> {ok, Dyn};
decode(Dyn) -> {error, {decode_error, <<"bigint">>, get_type(Dyn), []}}.
get_type(Val) when is_atom(Val) -> <<"atom">>;
get_type(Val) when is_function(Val) -> <<"function">>;
get_type(Val) when is_pid(Val) -> <<"pid">>;
get_type(Val) when is_binary(Val) -> <<"binary">>;
get_type(Val) when is_list(Val) -> <<"list">>;
get_type(Val) when is_map(Val) -> <<"map">>;
get_type(Val) when is_reference(Val) -> <<"reference">>;
get_type(Val) when is_float(Val) -> <<"float">>;
get_type(Val) when is_tuple(Val) -> <<"tuple">>;
get_type(Val) when is_port(Val) -> <<"port">>;
get_type(Val) when is_bitstring(Val) -> <<"bitstring">>.

View file

@ -1,10 +1,19 @@
import { Ok, Error } from "./gleam.mjs";
import { Ok, Error, toList } from "./gleam.mjs";
import { Lt, Eq, Gt } from "../gleam_stdlib/gleam/order.mjs";
import { DecodeError } from "../gleam_stdlib/gleam/dynamic.mjs";
export function from(int) {
return BigInt(int);
}
export function from_string(string) {
try {
return new Ok(BigInt(string));
} catch {
return new Error(undefined);
}
}
export function to(bigint) {
if (bigint > Number.MAX_SAFE_INTEGER || bigint < Number.MIN_SAFE_INTEGER) {
return new Error(undefined);
@ -106,3 +115,12 @@ export function power(a, b) {
return new Ok(a ** b);
}
export function decode(dyn) {
const dynType = typeof dyn;
if (dynType === "bigint") {
return new Ok(dyn);
} else {
return new Error(toList([new DecodeError("bigint", dynType, toList([]))]));
}
}

View file

@ -1,4 +1,5 @@
import gleam/order
import gleam/dynamic
import gleeunit
import gleeunit/should
import bigi
@ -145,6 +146,34 @@ pub fn negative_exponent_test() {
should.be_error(bigint)
}
pub fn dynamic_decoder_test() {
let bigint = bigi.from_int(1234)
let dyn = dynamic.from(bigint)
should.equal(bigi.decode(dyn), Ok(bigint))
}
pub fn dynamic_decoder_fail_test() {
let not_bigint = "foo"
let dyn = dynamic.from(not_bigint)
should.be_error(bigi.decode(dyn))
}
pub fn from_string_test() {
let str =
"214589024895249582498594209582039480239609385130598310670139580139855"
let parsed = bigi.from_string(str)
should.be_ok(parsed)
let assert Ok(parsed_data) = parsed
should.equal(bigi.to_string(parsed_data), str)
}
pub fn from_bad_string_test() {
let str =
"214589024895249582498594209582039480239-//-609385130598310670139580139855"
let parsed = bigi.from_string(str)
should.be_error(parsed)
}
pub fn power_test() {
let assert Ok(bigint) = bigi.power(bigi.from_int(2), bigi.from_int(65_535))