README.md

# matcher

[![Hex.pm](https://img.shields.io/hexpm/v/matcher_erl.svg)](https://hex.pm/packages/matcher_erl)
[![Hex Docs](https://img.shields.io/badge/hex-docs-blue.svg)](https://hexdocs.pm/matcher_erl)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

A simple expression matcher and evaluator for Erlang.

Evaluate tuple-based expressions for comparisons, boolean logic, and substring matching — with optional case-insensitive variants and pluggable value providers.

## Installation

Add `matcher_erl` to your `rebar.config` dependencies:

```erlang
{deps, [
    {matcher_erl, "~> 1.0"}
]}.
```

Or directly from GitHub:

```erlang
{deps, [
    {matcher, {git, "https://github.com/ratopi/matcher.git", {tag, "1.1.0"}}}
]}.
```

Then run:

```shell
rebar3 compile
```

## Quick Start

```erlang
%% Simple equality
true = matcher:eval({'=', <<"Albert">>, <<"Albert">>}).

%% Case-insensitive equality
true = matcher:eval({'=~', <<"Albert">>, <<"albert">>}).

%% Boolean logic
true = matcher:eval({'|', [
    {'=', <<"A">>, <<"B">>},
    {'=', <<"A">>, <<"A">>}
]}).

%% Substring matching
true = matcher:eval({'?', <<"bert">>, <<"Albert">>}).
```

## Expressions

Expressions are tuples in one of the following forms:

```erlang
{Op, Arg}            %% Unary  (e.g. not)
{Op, Arg1, Arg2}     %% Binary (e.g. eq, lt, part_of)
{Op, [Arg, ...]}     %% N-ary  (e.g. and, or)
```

Expressions can be **nested** — any argument can be an expression again:

```erlang
{'&', [{'=', <<"Albert">>, <<"Albert">>}, {'!', {'=', <<"A">>, <<"B">>}}]}
```

## Providers

`eval/2` and `match/2` accept a **Provider** as the first argument. A provider is a `fun/1` that resolves values that cannot be evaluated further.

### Map as Provider

The most common use case — look up atom keys in a map:

```erlang
Map = #{name => <<"Albert">>, age => 42}.

true  = matcher:eval(Map, {'=', name, <<"Albert">>}).
true  = matcher:eval(Map, {'>', age, 18}).
false = matcher:eval(Map, {'=', name, <<"Bob">>}).
```

`matcher:eval(Map, Expr)` is shorthand for `matcher:eval(map_provider(Map), Expr)`.

### Custom Provider

You can use any `fun/1`:

```erlang
Provider = fun
    (temperature) -> 23.5;
    (unit) -> <<"°C">>;
    (X) -> X
end.

true = matcher:eval(Provider, {'>', temperature, 20}).
```

You can even implement custom operators in your provider by handling tuples like `{my_op, A, B}`.

## `eval` vs `match`

| Function | Returns | On unknown operator |
|----------|---------|---------------------|
| `eval/1`, `eval/2` | Any value | Returns unevaluated expression as-is |
| `match/1`, `match/2` | `true`, `false`, or `{error, ...}` | `{error, {unevaluated_expression, Expr}}` |

## Operators

### Unary (one operand)

| Short | Long | Description |
|-------|------|-------------|
| `!` | `not` | Logical negation |

### N-ary (list of operands)

| Short | Long | Description |
|-------|------|-------------|
| `&` | `and` | Logical AND (short-circuit) |
| `\|` | `or` | Logical OR (short-circuit) |

### Binary (two operands)

| Short | Long | Case-insensitive | Description |
|-------|------|------------------|-------------|
| `<` | `lt` | `<~` | Less than |
| `>` | `gt` | `>~` | Greater than |
| `>=`, `=>` | `ge` | `>=~`, `=>~` | Greater than or equal |
| `<=`, `=<` | `le` | `<=~`, `=<~` | Less than or equal |
| `=`, `==` | `eq` | `=~` | Equal |
| `?` | `part_of` | `?~` | First string is part of second |

Case-insensitive operators can also be written in long form, e.g. `{eq, case_insensitive}`.

## Types

The library exports the following types:

```erlang
-type provider()   :: fun((term()) -> term()).
-type expression() :: {unary_op(), expression()}
                    | {binary_op(), term(), term()}
                    | {nary_op(), [expression()]}
                    | term().
```

See the [module documentation](https://hexdocs.pm/matcher_erl/matcher.html) for full type definitions.

## Testing

```shell
rebar3 ct
```

## License

MIT — see [LICENSE](LICENSE) for details.

I'd love to hear from you if you find this library useful. :-)

## Links

- [Hex package](https://hex.pm/packages/matcher_erl)
- [Documentation](https://hexdocs.pm/matcher_erl)
- [GitHub](https://github.com/ratopi/matcher)