Initial commit
This commit is contained in:
commit
a6272848f9
379 changed files with 74829 additions and 0 deletions
316
build/packages/gleam_json/src/gleam/json.gleam
Normal file
316
build/packages/gleam_json/src/gleam/json.gleam
Normal file
|
|
@ -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] }))
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue