diff --git a/lib/elixir/lib/regex.ex b/lib/elixir/lib/regex.ex index 7a5c242476..b422a3e0bf 100644 --- a/lib/elixir/lib/regex.ex +++ b/lib/elixir/lib/regex.ex @@ -102,8 +102,8 @@ defmodule Regex do (the previous `r` option is deprecated in favor of `U`) * `:export` (E) (since Elixir 1.19.3) - uses an exported pattern - which can be shared across nodes or through config, at the cost of a runtime - overhead every time to re-import it every time it is executed. + which can be shared across nodes or passed through config, at the cost of a runtime + overhead to re-import it every time it is executed. This modifier only has an effect starting on Erlang/OTP 28, and it is ignored on older versions (i.e. `~r/foo/E == ~r/foo/`). This is because patterns cannot and do not need to be exported in order to be shared in these versions. @@ -275,6 +275,35 @@ defmodule Regex do regex end + @doc """ + Imports a `regex` that has been exported, otherwise returns the `regex` unchanged. + + This means it will lose the ability to be sent across nodes or passed through config, + but will be faster since it won't need to be imported on the fly every time it is executed. + + Exported regexes only exist on OTP 28, so this has no effect on older versions. + + ## Examples + + Regex.import(~r/foo/E) + ~r/foo/ + + Regex.import(~r/foo/) + ~r/foo/ + + """ + @doc since: "1.20.0" + @spec import(t) :: t + def import(%Regex{re_pattern: re_pattern} = regex) do + case re_pattern do + {:re_exported_pattern, _, _, _, _} -> + %{regex | re_pattern: :re.import(re_pattern), opts: regex.opts -- [:export]} + + _ -> + regex + end + end + @doc """ Returns the version of the underlying Regex engine. """ diff --git a/lib/elixir/test/elixir/regex_test.exs b/lib/elixir/test/elixir/regex_test.exs index 1fed09c248..926c53435c 100644 --- a/lib/elixir/test/elixir/regex_test.exs +++ b/lib/elixir/test/elixir/regex_test.exs @@ -132,6 +132,18 @@ defmodule RegexTest do end end + test "import/1" do + # no-op for non-exported regexes + regex = ~r/foo/ + assert Regex.import(regex) == regex + + imported = Regex.import(~r/foo/E) + + assert imported.opts == [] + assert "foo" =~ imported + assert {:re_pattern, _, _, _, _} = imported.re_pattern + end + test "opts/1" do assert Regex.opts(Regex.compile!("foo", "i")) == [:caseless] assert Regex.opts(Regex.compile!("foo", [:ucp])) == [:ucp]