Initial commit
This commit is contained in:
commit
a6272848f9
379 changed files with 74829 additions and 0 deletions
191
build/packages/gleam_json/LICENCE
Normal file
191
build/packages/gleam_json/LICENCE
Normal file
|
|
@ -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 <louis@lpil.uk>.
|
||||
|
||||
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.
|
||||
|
||||
47
build/packages/gleam_json/README.md
Normal file
47
build/packages/gleam_json/README.md
Normal file
|
|
@ -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)
|
||||
}
|
||||
```
|
||||
18
build/packages/gleam_json/gleam.toml
Normal file
18
build/packages/gleam_json/gleam.toml
Normal file
|
|
@ -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"
|
||||
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] }))
|
||||
}
|
||||
304
build/packages/gleam_json/src/gleam@json.erl
Normal file
304
build/packages/gleam_json/src/gleam@json.erl
Normal file
|
|
@ -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
|
||||
)
|
||||
).
|
||||
9
build/packages/gleam_json/src/gleam_json.app.src
Normal file
9
build/packages/gleam_json/src/gleam_json.app.src
Normal file
|
|
@ -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, []}
|
||||
]}.
|
||||
66
build/packages/gleam_json/src/gleam_json_ffi.erl
Normal file
66
build/packages/gleam_json/src/gleam_json_ffi.erl
Normal file
|
|
@ -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.
|
||||
201
build/packages/gleam_json/src/gleam_json_ffi.mjs
Normal file
201
build/packages/gleam_json/src/gleam_json_ffi.mjs
Normal file
|
|
@ -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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue