Kote
Kote

Reputation: 723

Elixir method pattern matching with string contains

Is there any way to have String contains/regex in argument matching? For example, the string is "Some error happened". But I want it to match the substring "error happened". I tried this but it does not work:

  defp status({:error, ~r/error happened/}, state) do

  end

Upvotes: 5

Views: 6884

Answers (2)

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121000

Although the answer by @Dogbert is absolutely correct, there is a trick one could use when error messages can not be longer than, say, 140 symbols (aka twitter-sized error messages.)

defmodule Test do
  @pattern "error happened"
  defp status({:error, @pattern <> _rest }, state),
    do: IO.puts "Matched leading"
  Enum.each(1..140, fn i ->
    defp status({:error, 
                 <<_ :: binary-size(unquote(i)), 
                   unquote(@pattern) :: binary,
                   rest :: binary>>}, state),
      do: IO.puts "Matched"
  end)
  Enum.each(0..140, fn i ->
    defp status({:error, <<_ :: binary-size(unquote(i))>>}, state),
      do: IO.puts "Unmatched"
  end)
  # fallback
  defp status({:error, message}, state) do
    cond do
      message =~ "error happened" -> IO.puts "Matched >140"
      true -> IO.puts "Unatched >140"
    end
  end

  def test_status({:error, message}, state),
    do: status({:error, message}, state)
end

Tests:

iex|1 ▶ Test.test_status {:error, "sdf error happened sdfasdf"}, nil
Matched

iex|2 ▶ Test.test_status {:error, "sdf errors happened sdfasdf"}, nil
Unmatched

iex|3 ▶ Test.test_status {:error, "error happened sdfasdf"}, nil     
Matched leading

iex|4 ▶ Test.test_status {:error, 
...|4 ▷    String.duplicate(" ", 141) <> "error happened sdfasdf"}, nil
Matched >140

Upvotes: 1

Dogbert
Dogbert

Reputation: 222060

No, neither String contains nor Regex match can be done using either pattern matching or guard functions. Your best bet would be to match {:error, error} in the pattern and do the string matching inside the function using e.g. cond:

defp status({:error, error}, state) do
  cond do
    error =~ "error happened" -> ...
    ...
  end
end

What can be done in pattern matching is a prefix match. If that's good enough for you, you can do this:

defp status({:error, "error happened" <> _}, state) do

This will match any string starting with "error happened".

Upvotes: 13

Related Questions