# `Moar.String`
[🔗](https://github.com/synchronal/moar/blob/main/lib/string.ex#L1)

String-related functions.

# `quote_type`

```elixir
@type quote_type() ::
  :double_curly | :double_straight | :single_curly | :single_straight
```

# `string_case`

```elixir
@type string_case() :: :camel_case | :kebab_case | :lower_camel_case | :snake_case
```

# `append_unless_blank`

```elixir
@spec append_unless_blank(binary() | nil, binary() | nil) :: binary()
```

Appends `suffix` to `string` unless `string` is blank according to `Moar.Term.blank?/1`.

```elixir
iex> Moar.String.append_unless_blank("foo", "-bar")
"foo-bar"

iex> Moar.String.append_unless_blank("", "-bar")
""

iex> Moar.String.append_unless_blank(nil, "-bar")
nil
```

# `compare`

```elixir
@spec compare(binary(), binary(), (binary() -&gt; binary()) | [(binary() -&gt; binary())]) ::
  :lt | :eq | :gt
```

Compares two strings, returning `:lt`, `:eq`, or `:gt` depending on whether the first arg is less than, equal to, or
greater than the second arg. Accepts one or more functions that transform the inputs before comparison.

See `Moar.String.compare?/2` for a version that returns `true` or `false`.

```
iex> Moar.String.compare("foo", "FOO")
:gt

iex> Moar.String.compare("foo", "FOO", &String.downcase/1)
:eq

iex> Moar.String.compare("foo bar", " FOO    bar ", [&String.downcase/1, &Moar.String.squish/1])
:eq
```

# `compare?`

```elixir
@spec compare?(binary(), binary(), (binary() -&gt; binary()) | [(binary() -&gt; binary())]) ::
  boolean()
```

Compares two strings, returning `true` if the first arg is less than or equal to the second arg, or `false` if the
first arg is greater than the second arg. Accepts one or more functions that transform the inputs before comparison.
Useful for sorter functions like what is passed into `Enum.sort/2`.

See `Moar.String.compare/2` for a version that returns `:lt`, `:eq`, or `:gt`.

```
iex> Moar.String.compare?("foo", "FOO")
false

iex> Moar.String.compare?("foo", "FOO", &String.downcase/1)
true

iex> Moar.String.compare?("foo bar", " FOO    bar ", [&String.downcase/1, &Moar.String.squish/1])
true
```

# `count_leading_spaces`

```elixir
@spec count_leading_spaces(binary()) :: non_neg_integer()
```

Returns the number of leading spaces. Only considers "plain" spaces, not all unicode whitespace.

```elixir
iex> Moar.String.count_leading_spaces("  foo")
2
```

# `dasherize`

```elixir
@spec dasherize(String.Chars.t() | [String.Chars.t()]) :: binary()
```

Dasherizes `term`. A shortcut to `slug(term, "-")`.

See docs for `slug/2`.

```elixir
iex> Moar.String.dasherize("foo bar")
"foo-bar"
```

# `inner_truncate`

```elixir
@spec inner_truncate(binary(), integer(), binary()) :: binary()
```

Truncate `s` to `max_length` by replacing the middle of the string with `replacement`, which defaults to
the single unicode character `…`.

Note that the final length of the string will be `max_length` plus the length of `replacement`.

```elixir
iex> Moar.String.inner_truncate("abcdefghijklmnopqrstuvwxyz", 10)
"abcde…vwxyz"

iex> Moar.String.inner_truncate("abcdefghijklmnopqrstuvwxyz", 10, "<==>")
"abcde<==>vwxyz"
```

# `join`

```elixir
@spec join(String.t(), list() | String.Chars.t()) :: String.t()
```

Join multiple items with `joiner`. `join/2` accepts a list of items; `join/3` through `join/6` accept
individual items for convenience. All items must implement `String.Chars` protocol (they must be
`to_string`-able).

```elixir
iex> Moar.String.join("-", ["a", "b", "c"])
"a-b-c"

iex> Moar.String.join("-", "a", "b", "c")
"a-b-c"
```

# `join`

```elixir
@spec join(String.t(), String.Chars.t(), String.Chars.t()) :: String.t()
```

# `join`

```elixir
@spec join(String.t(), String.Chars.t(), String.Chars.t(), String.Chars.t()) ::
  String.t()
```

# `join`

```elixir
@spec join(
  String.t(),
  String.Chars.t(),
  String.Chars.t(),
  String.Chars.t(),
  String.Chars.t()
) ::
  String.t()
```

# `join`

```elixir
@spec join(
  String.t(),
  String.Chars.t(),
  String.Chars.t(),
  String.Chars.t(),
  String.Chars.t(),
  String.Chars.t()
) :: String.t()
```

# `lorem`

```elixir
@spec lorem(non_neg_integer()) :: String.t()
```

Creates a lorem ipsum string of length `character_count`.

# `pluralize`

```elixir
@spec pluralize(number(), binary(), binary() | function(), atom() | [atom()]) ::
  binary()
```

Pluralizes a string.

When `count` is -1 or 1, returns the second argument (the singular string).

Otherwise, returns the third argument (the pluralized string), or if the third argument is a function,
calls the function with the singular string as an argument.

Options:
* `:include_number` will include the number in the result (e.g., "4 fishies")

```elixir
iex> Moar.String.pluralize(1, "fish", "fishies")
"fish"

iex> Moar.String.pluralize(1, "fish", "fishies", :include_number)
"1 fish"

iex> Moar.String.pluralize(2, "fish", "fishies")
"fishies"

iex> Moar.String.pluralize(2, "fish", fn singular -> singular <> "ies" end)
"fishies"

iex> Moar.String.pluralize(2, "fish", &(&1 <> "ies"), :include_number)
"2 fishies"
```

# `quote`

```elixir
@spec quote(String.Chars.t(), quote_type()) :: String.t()
```

Surrounds the given string (or anything that conforms to the `String.Chars` protocol) with quotes.
The default is double-curly quotes (“foo”) but can be set to double straight ("foo"), single curly (‘foo’),
or single straight ('foo'). Passing `nil` as an argument returns an (un-quoted) empty string.

# `remove_marked_whitespace`

```elixir
@spec remove_marked_whitespace(binary()) :: binary()
```

Removes all whitespace following a backspace+v escape code.

Especially useful in test assertions where only some of the whitespace matters.
```
iex> Moar.String.remove_marked_whitespace("one two three	   four five")
"one two threefour five"
```

# `secure_compare`

```elixir
@spec secure_compare(binary(), binary()) :: boolean()
```

Compares the two binaries in constant-time to avoid timing attacks.
See: <http://codahale.com/a-lesson-in-timing-attacks/>.

```elixir
iex> Moar.String.secure_compare("foo", "bar")
false
```

# `slug`

```elixir
@spec slug(String.Chars.t() | [String.Chars.t()], binary()) :: binary()
```

Creates slugs like `foo-bar-123` or `foo_bar` from various input types.

Converts strings, atoms, and anything else that implements `String.Chars`, plus lists of those things,
to a single string after removing non-alphanumeric characters, and then joins them with `joiner`.
Existing occurrences of `joiner` are kept, including leading and trailing ones.

`dasherize/1` and `underscore/1` are shortcuts that specify a joiner.

```elixir
iex> Moar.String.slug("foo bar", "_")
"foo_bar"

iex> Moar.String.slug("foo bar", "+")
"foo+bar"

iex> Moar.String.slug(["foo", "bar"], "+")
"foo+bar"

iex> Moar.String.slug("_foo bar", "_")
"_foo_bar"

iex> ["foo", "FOO", :foo] |> Enum.map(&Moar.String.slug(&1, "-"))
["foo", "foo", "foo"]

iex> ["foo-bar", "foo_bar", :foo_bar, " fooBar ", "  ?foo ! bar  "] |> Enum.map(&Moar.String.slug(&1, "-"))
["foo-bar", "foo-bar", "foo-bar", "foo-bar", "foo-bar"]
```

# `squish`

```elixir
@spec squish(binary()) :: binary()
```

Trims spaces from the beginning and end of a string, and replaces consecutive whitespace characters with a single
space.

```elixir
iex> Moar.String.squish("  foo   bar  	baz ")
"foo bar baz"
```

# `surround`

```elixir
@spec surround(binary(), binary()) :: binary()
```

Adds `surrounder` to the beginning and end of `s`.

```elixir
iex> Moar.String.surround("Hello", "**")
"**Hello**"
```

# `surround`

```elixir
@spec surround(binary(), binary(), binary()) :: binary()
```

Adds `prefix` to the beginning of `s` and `suffix` to the end.

```elixir
iex> Moar.String.surround("Hello", "“", "”")
"“Hello”"
```

# `to_case`

```elixir
@spec to_case(binary(), string_case()) :: binary()
```

Change the case of a string.

```elixir
iex> Moar.String.to_case("text_with_case", :camel_case)
"TextWithCase"
iex> Moar.String.to_case("textWithCase", :camel_case)
"TextWithCase"
iex> Moar.String.to_case("some random text", :camel_case)
"SomeRandomText"

iex> Moar.String.to_case("text_with_case", :lower_camel_case)
"textWithCase"
iex> Moar.String.to_case("textWithCase", :lower_camel_case)
"textWithCase"
iex> Moar.String.to_case("some random text", :lower_camel_case)
"someRandomText"

iex> Moar.String.to_case("text_with_case", :kebab_case)
"text-with-case"
iex> Moar.String.to_case("textWithCase", :kebab_case)
"text-with-case"
iex> Moar.String.to_case("some random text", :kebab_case)
"some-random-text"

iex> Moar.String.to_case("text_with_case", :snake_case)
"text_with_case"
iex> Moar.String.to_case("textWithCase", :snake_case)
"text_with_case"
iex> Moar.String.to_case("some random text", :snake_case)
"some_random_text"
```

# `to_integer`

```elixir
@spec to_integer(nil | binary()) :: integer()
```

Converts a string to an integer. Returns `nil` if the argument is `nil` or empty string.
Returns the argument without complaint if it is already an integer.

```elixir
iex> Moar.String.to_integer("12,345")
12_345

iex> Moar.String.to_integer("")
nil

iex> Moar.String.to_integer(12_345)
12_345
```

# `to_integer`

```elixir
@spec to_integer(binary(), :lenient | [{:default, binary()}]) :: integer()
```

Like `to_integer/1` but with options:
* `:lenient` option removes non-digit characters first
* `default:` option specifies a default in case `s` is nil

```elixir
iex> Moar.String.to_integer("USD$25", :lenient)
25

iex> Moar.String.to_integer(nil, default: 0)
0
```

# `trim`

```elixir
@spec trim(nil | binary()) :: nil | binary()
```

Like `String.trim/1` but returns `nil` if the argument is nil.

# `truncate_at`

Truncates `s` at the last instance of `at`, causing the string to be at most `limit` characters.

```elixir
iex> Moar.String.truncate_at("I like apples. I like bananas. I like cherries.", ".", 35)
"I like apples. I like bananas."
```

# `underscore`

```elixir
@spec underscore(String.Chars.t() | [String.Chars.t()]) :: binary()
```

Underscores `term`. Will be deprecated soon; use `to_case(:snake_case)` instead..

```elixir
iex> Moar.String.underscore("foo bar")
"foo_bar"
```

# `unindent`

```elixir
@spec unindent(binary()) :: binary()
```

Unindents a string by finding the smallest indentation of the string, and removing that many spaces from each line.
Only considers "plain" spaces, not all unicode whitespace.

```elixir
iex> """
...>      ant
...>    bat
...>      cat
...>        dog
...> """ |> Moar.String.unindent()
"  ant
bat
  cat
    dog
"
```

# `unindent`

```elixir
@spec unindent(binary(), non_neg_integer()) :: binary()
```

Unindents a string by `count` spaces. Only considers "plain" spaces, not all unicode whitespace.

```elixir
iex> """
...> foo
...>   bar
...>     baz
...> """ |> Moar.String.unindent(2)
"foo
bar
  baz
"
```

---

*Consult [api-reference.md](api-reference.md) for complete listing*
