stellar_prune/build/dev/javascript/gleam_stdlib/gleam/string.mjs
2025-11-30 15:44:22 +01:00

723 lines
16 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 <https://en.wikipedia.org/wiki/Code_point> and
* <https://en.wikipedia.org/wiki/Unicode#Codespace_and_Code_Points> 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,
);
}
}