defmodule RegexSolver do @moduledoc """ Regex operations using Rust's [RegexSolver](https://github.com/RegexSolver/regexsolver) """ version = Mix.Project.config()[:version] github_url = Mix.Project.config()[:package][:links]["GitHub"] use RustlerPrecompiled, otp_app: :regex_solver, base_url: "#{github_url}/releases/download/v-#{version}", force_build: System.get_env("RUSTLER_BUILD") in ["1", "true"], targets: Enum.uniq(["aarch64-unknown-linux-musl" | RustlerPrecompiled.Config.default_targets()]) -- ["riscv64gc-unknown-linux-gnu"], version: version @type op_error :: :invalid_regex | :empty_intersection @doc """ Computes the intersecion of two regular expressions ## Examples iex> RegexSolver.intersect(~r/[a-z]+/, ~r/abc|def/) {:ok, "(abc|def)"} iex> RegexSolver.intersect("[A-Z]+_[A-Z]+", "ENV_[A-Z]+") {:ok, "ENV_[A-Z]+"} iex> RegexSolver.intersect(~r/[0-9]+/, ~r/[A-Z][a-z]+/) {:error, :empty_intersection} iex> RegexSolver.intersect("a|b", "b") {:ok, "b"} iex> RegexSolver.intersect("a)", "a|b|c") {:error, "invalid regex: a)"} """ @spec intersect(Regex.t() | String.t(), Regex.t() | String.t()) :: {:ok, String.t()} | {:error, op_error()} def intersect(%Regex{} = re1, re2), do: intersect(re1.source, re2) def intersect(re1, %Regex{} = re2), do: intersect(re1, re2.source) def intersect(re1, re2) when is_binary(re1) and is_binary(re2) do case intersection(re1, re2) do {:ok, "[]"} -> {:error, :empty_intersection} {:ok, pattern} -> {:ok, pattern} {:error, _reason} = error -> error end end defp intersection(_str1, _str2), do: :erlang.nif_error(:nif_not_loaded) end