Current section

Files

Jump to
gleam_stdlib src gleam string_builder.gleam
Raw

src/gleam/string_builder.gleam

import gleam/list
/// `StringBuilder` is a type used for efficiently building text content to be
/// written to a file or a socket. Internally it is represented as tree so to
/// append or prepend to a string builder is a constant time operation that
/// allocates a new node in the tree without copying any of the content. When
/// writing to an output stream the tree is traversed and the content is sent
/// directly rather than copying it into a single buffer beforehand.
///
/// On Erlang this type is compatible with Erlang's iodata. On JavaScript this
/// type is compatible with normal strings.
///
/// The BEAM virtual machine has an optimisation for appending strings, where it
/// will mutate the string buffer when safe to do so, so if you are looking to
/// build a string through appending many small strings then you may get better
/// performance by not using a string builder. Always benchmark your performance
/// sensitive code.
///
pub type StringBuilder
/// Create an empty `StringBuilder`. Useful as the start of a pipe chaining many
/// builders together.
///
pub fn new() -> StringBuilder {
do_from_strings([])
}
/// Prepends a `String` onto the start of some `StringBuilder`.
///
/// Runs in constant time.
///
pub fn prepend(
to builder: StringBuilder,
prefix prefix: String,
) -> StringBuilder {
append_builder(from_string(prefix), builder)
}
/// Appends a `String` onto the end of some `StringBuilder`.
///
/// Runs in constant time.
///
pub fn append(to builder: StringBuilder, suffix second: String) -> StringBuilder {
append_builder(builder, from_string(second))
}
/// Prepends some `StringBuilder` onto the start of another.
///
/// Runs in constant time.
///
pub fn prepend_builder(
to builder: StringBuilder,
prefix prefix: StringBuilder,
) -> StringBuilder {
do_append(prefix, builder)
}
/// Appends some `StringBuilder` onto the end of another.
///
/// Runs in constant time.
///
pub fn append_builder(
to builder: StringBuilder,
suffix suffix: StringBuilder,
) -> StringBuilder {
do_append(builder, suffix)
}
@external(erlang, "gleam_stdlib", "iodata_append")
@external(javascript, "../gleam_stdlib.mjs", "add")
fn do_append(a: StringBuilder, b: StringBuilder) -> StringBuilder
/// Converts a list of strings into a builder.
///
/// Runs in constant time.
///
pub fn from_strings(strings: List(String)) -> StringBuilder {
do_from_strings(strings)
}
@external(erlang, "gleam_stdlib", "identity")
@external(javascript, "../gleam_stdlib.mjs", "concat")
fn do_from_strings(a: List(String)) -> StringBuilder
/// Joins a list of builders into a single builder.
///
/// Runs in constant time.
///
pub fn concat(builders: List(StringBuilder)) -> StringBuilder {
do_concat(builders)
}
@external(erlang, "gleam_stdlib", "identity")
@external(javascript, "../gleam_stdlib.mjs", "concat")
fn do_concat(a: List(StringBuilder)) -> StringBuilder
/// Converts a string into a builder.
///
/// Runs in constant time.
///
pub fn from_string(string: String) -> StringBuilder {
do_from_string(string)
}
@external(erlang, "gleam_stdlib", "identity")
@external(javascript, "../gleam_stdlib.mjs", "identity")
fn do_from_string(a: String) -> StringBuilder
/// Turns an `StringBuilder` into a `String`
///
/// This function is implemented natively by the virtual machine and is highly
/// optimised.
///
pub fn to_string(builder: StringBuilder) -> String {
do_to_string(builder)
}
@external(erlang, "unicode", "characters_to_binary")
@external(javascript, "../gleam_stdlib.mjs", "identity")
fn do_to_string(a: StringBuilder) -> String
/// Returns the size of the `StringBuilder` in bytes.
///
pub fn byte_size(builder: StringBuilder) -> Int {
do_byte_size(builder)
}
@external(erlang, "erlang", "iolist_size")
@external(javascript, "../gleam_stdlib.mjs", "length")
fn do_byte_size(a: StringBuilder) -> Int
/// Joins the given builders into a new builder separated with the given string
///
pub fn join(builders: List(StringBuilder), with sep: String) -> StringBuilder {
builders
|> list.intersperse(from_string(sep))
|> concat
}
/// Converts a builder to a new builder where the contents have been
/// lowercased.
///
pub fn lowercase(builder: StringBuilder) -> StringBuilder {
do_lowercase(builder)
}
@external(erlang, "string", "lowercase")
@external(javascript, "../gleam_stdlib.mjs", "lowercase")
fn do_lowercase(a: StringBuilder) -> StringBuilder
/// Converts a builder to a new builder where the contents have been
/// uppercased.
///
pub fn uppercase(builder: StringBuilder) -> StringBuilder {
do_uppercase(builder)
}
@external(erlang, "string", "uppercase")
@external(javascript, "../gleam_stdlib.mjs", "uppercase")
fn do_uppercase(a: StringBuilder) -> StringBuilder
/// Converts a builder to a new builder with the contents reversed.
///
pub fn reverse(builder: StringBuilder) -> StringBuilder {
do_reverse(builder)
}
@target(erlang)
@external(erlang, "string", "reverse")
fn do_reverse(a: StringBuilder) -> StringBuilder
@target(javascript)
fn do_reverse(builder: StringBuilder) -> StringBuilder {
builder
|> to_string
|> do_to_graphemes
|> list.reverse
|> from_strings
}
@target(javascript)
@external(javascript, "../gleam_stdlib.mjs", "graphemes")
fn do_to_graphemes(string string: String) -> List(String)
/// Splits a builder on a given pattern into a list of builders.
///
pub fn split(iodata: StringBuilder, on pattern: String) -> List(StringBuilder) {
do_split(iodata, pattern)
}
@target(erlang)
type Direction {
All
}
@target(erlang)
@external(erlang, "string", "split")
fn erl_split(a: StringBuilder, b: String, c: Direction) -> List(StringBuilder)
@target(erlang)
fn do_split(iodata: StringBuilder, pattern: String) -> List(StringBuilder) {
erl_split(iodata, pattern, All)
}
@target(javascript)
@external(javascript, "../gleam_stdlib.mjs", "split")
fn do_split(
builder builder: StringBuilder,
pattern pattern: String,
) -> List(StringBuilder)
/// Replaces all instances of a pattern with a given string substitute.
///
@external(erlang, "gleam_stdlib", "string_replace")
@external(javascript, "../gleam_stdlib.mjs", "string_replace")
pub fn replace(
in builder: StringBuilder,
each pattern: String,
with substitute: String,
) -> StringBuilder
/// Compares two builders to determine if they have the same textual content.
///
/// Comparing two iodata using the `==` operator may return `False` even if they
/// have the same content as they may have been build in different ways, so
/// using this function is often preferred.
///
/// ## Examples
///
/// ```gleam
/// from_strings(["a", "b"]) == from_string("ab")
/// // -> False
/// ```
///
/// ```gleam
/// is_equal(from_strings(["a", "b"]), from_string("ab"))
/// // -> True
/// ```
///
@external(erlang, "string", "equal")
pub fn is_equal(a: StringBuilder, b: StringBuilder) -> Bool {
a == b
}
/// Inspects a builder to determine if it is equivalent to an empty string.
///
/// ## Examples
///
/// ```gleam
/// from_string("ok") |> is_empty
/// // -> False
/// ```
///
/// ```gleam
/// from_string("") |> is_empty
/// // -> True
/// ```
///
/// ```gleam
/// from_strings([]) |> is_empty
/// // -> True
/// ```
///
@external(erlang, "string", "is_empty")
pub fn is_empty(builder: StringBuilder) -> Bool {
from_string("") == builder
}