Skip to content

Commit 55cb8ab

Browse files
committed
Merge pull request #1195 from alco/string-fixes
String fixes
2 parents d39a52a + 32cddf2 commit 55cb8ab

File tree

2 files changed

+108
-17
lines changed

2 files changed

+108
-17
lines changed

lib/elixir/lib/string.ex

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -811,12 +811,24 @@ defmodule String do
811811
"""
812812
@spec starts_with?(t, t | [t]) :: boolean
813813

814-
def starts_with?(string, prefixes) do
815-
case :binary.match(string, prefixes) do
816-
:nomatch -> false
817-
{0, _} -> true
818-
_ -> false
819-
end
814+
def starts_with?(string, prefixes) when is_list(prefixes) do
815+
Enum.any?(prefixes, do_starts_with(string, &1))
816+
end
817+
818+
def starts_with?(string, prefix) do
819+
do_starts_with(string, prefix)
820+
end
821+
822+
defp do_starts_with(_, "") do
823+
true
824+
end
825+
826+
defp do_starts_with(string, prefix) when is_binary(prefix) do
827+
match?({0,_}, :binary.match(string, prefix))
828+
end
829+
830+
defp do_starts_with(_, _) do
831+
raise ArgumentError
820832
end
821833

822834
@doc """
@@ -836,18 +848,31 @@ defmodule String do
836848
@spec ends_with?(t, t | [t]) :: boolean
837849

838850
def ends_with?(string, suffixes) when is_list(suffixes) do
851+
Enum.any?(suffixes, do_ends_with(string, &1))
852+
end
853+
854+
def ends_with?(string, suffix) do
855+
do_ends_with(string, suffix)
856+
end
857+
858+
defp do_ends_with(_, "") do
859+
true
860+
end
861+
862+
defp do_ends_with(string, suffix) when is_binary(suffix) do
839863
string_size = size(string)
840-
Enum.any? suffixes, fn suffix ->
841-
suffix_size = size(suffix)
842-
(suffix_size <= string_size) and suffix == :binary.part(string, {string_size, -suffix_size})
843-
end
864+
suffix_size = size(suffix)
865+
scope = {string_size - suffix_size, suffix_size}
866+
(suffix_size <= string_size) and (:nomatch != :binary.match(string, suffix, [scope: scope]))
844867
end
845868

846-
def ends_with?(string, suffix), do: ends_with?(string, [ suffix ])
869+
defp do_ends_with(_, _) do
870+
raise ArgumentError
871+
end
847872

848873
@doc """
849874
Returns true if `string` contains match, otherwise false.
850-
`matches` can be either a single match of a list of matches.
875+
`matches` can be either a single string or a list of strings.
851876
852877
## Examples
853878
@@ -859,13 +884,25 @@ defmodule String do
859884
false
860885
861886
"""
887+
@spec contains?(t, t | [t]) :: boolean
888+
862889
def contains?(string, matches) when is_list(matches) do
863-
case :binary.match(string, matches) do
864-
:nomatch -> false
865-
_ -> true
866-
end
890+
Enum.any?(matches, do_contains(string, &1))
867891
end
868892

869-
def contains?(string, match), do: contains?(string, [ match ])
893+
def contains?(string, match) do
894+
do_contains(string, match)
895+
end
896+
897+
defp do_contains(_, "") do
898+
true
899+
end
870900

901+
defp do_contains(string, match) when is_binary(match) do
902+
:nomatch != :binary.match(string, match)
903+
end
904+
905+
defp do_contains(_, _) do
906+
raise ArgumentError
907+
end
871908
end

lib/elixir/test/elixir/string_test.exs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ defmodule StringTest do
281281
end
282282

283283
test :starts_with? do
284+
## Normal cases ##
284285
assert String.starts_with? "hello", "he"
285286
assert String.starts_with? "hello", "hello"
286287
assert String.starts_with? "hello", ["hellö", "hell"]
@@ -289,9 +290,28 @@ defmodule StringTest do
289290
refute String.starts_with? "hello", "hellö"
290291
refute String.starts_with? "hello", ["hellö", "goodbye"]
291292
refute String.starts_with? "エリクシア", "仙丹"
293+
294+
## Edge cases ##
295+
assert String.starts_with? "", ""
296+
assert String.starts_with? "", ["", "a"]
297+
assert String.starts_with? "b", ["", "a"]
298+
299+
assert String.starts_with? "abc", ""
300+
assert String.starts_with? "abc", [""]
301+
302+
refute String.starts_with? "", "abc"
303+
refute String.starts_with? "", [" "]
304+
305+
## Sanity checks ##
306+
assert String.starts_with? "", ["", ""]
307+
assert String.starts_with? "abc", ["", ""]
308+
assert_raise ArgumentError, fn ->
309+
String.starts_with? "abc", [["a"], "a"]
310+
end
292311
end
293312

294313
test :ends_with? do
314+
## Normal cases ##
295315
assert String.ends_with? "hello", "lo"
296316
assert String.ends_with? "hello", "hello"
297317
assert String.ends_with? "hello", ["hell", "lo", "xx"]
@@ -301,15 +321,49 @@ defmodule StringTest do
301321
refute String.ends_with? "hello", "hellö"
302322
refute String.ends_with? "hello", ["hel", "goodbye"]
303323
refute String.ends_with? "エリクシア", "仙丹"
324+
325+
## Edge cases ##
326+
assert String.ends_with? "", ""
327+
assert String.ends_with? "", ["", "a"]
328+
refute String.ends_with? "", ["a", "b"]
329+
330+
assert String.ends_with? "abc", ""
331+
assert String.ends_with? "abc", ["", "x"]
332+
333+
refute String.ends_with? "", "abc"
334+
refute String.ends_with? "", [" "]
335+
336+
## Sanity checks ##
337+
assert String.ends_with? "", ["", ""]
338+
assert String.ends_with? "abc", ["", ""]
339+
assert_raise ArgumentError, fn ->
340+
String.ends_with? "abc", [["c"], "c"]
341+
end
304342
end
305343

306344
test :contains? do
345+
## Normal cases ##
307346
assert String.contains? "elixir of life", "of"
308347
assert String.contains? "エリクシア", "シ"
309348
assert String.contains? "elixir of life", ["mercury", "life"]
310349
refute String.contains? "exlixir of life", "death"
311350
refute String.contains? "エリクシア", "仙"
312351
refute String.contains? "elixir of life", ["death", "mercury", "eternal life"]
352+
353+
## Edge cases ##
354+
assert String.contains? "", ""
355+
assert String.contains? "abc", ""
356+
assert String.contains? "abc", ["", "x"]
357+
358+
refute String.contains? "", " "
359+
refute String.contains? "", "a"
360+
361+
## Sanity checks ##
362+
assert String.contains? "", ["", ""]
363+
assert String.contains? "abc", ["", ""]
364+
assert_raise ArgumentError, fn ->
365+
String.contains? "abc", [["b"], "b"]
366+
end
313367
end
314368

315369
end

0 commit comments

Comments
 (0)