Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Development Commands

**Setup:**
```bash
mix deps.get # Install dependencies
```

**Testing:**
```bash
mix test # Run all tests
mix test test/valdi_test.exs # Run specific test file
mix coveralls # Run tests with coverage
mix coveralls.html # Generate HTML coverage report
```

**Code Quality:**
```bash
mix format # Format code according to .formatter.exs
mix docs # Generate documentation
```

**Build:**
```bash
mix compile # Compile the project
```

## Project Architecture

**Valdi** is an Elixir data validation library that provides comprehensive validation functions for different data types and structures.

### Core Module Structure

The main validation logic is contained in a single module `Valdi` (lib/valdi.ex) with these key functions:

- **Main validation functions:**
- `validate/2` - Main validation function that accepts value and list of validators
- `validate_list/2` - Validates each item in a list against given validators
- `validate_map/2` - Validates map values against a validation specification

- **Individual validators:**
- `validate_type/2` - Type checking (supports built-in types, structs, arrays)
- `validate_required/2` - Required field validation
- `validate_number/2` - Number range validation (min/max/equal_to/greater_than/less_than)
- `validate_decimal/2` - Decimal number validation using Decimal library
- `validate_length/2` - Length validation for strings, lists, maps, tuples
- `validate_format/2` - Regex pattern matching for strings (also accessible via `pattern` alias)
- `validate_inclusion/2` & `validate_exclusion/2` - Value inclusion/exclusion in enumerables
- `validate_each_item/2` - Applies validation to each array element

### Validation Flow

1. `validate/2` calls `prepare_validator/1` to prioritize validators (required → type → others)
2. `do_validate/3` processes validators sequentially, stopping at first error
3. Individual validator functions return `:ok` or `{:error, message}`
4. For list/map validation, errors include indexes/keys for failed items

### Supported Types

Built-in types: `:boolean`, `:integer`, `:float`, `:number`, `:string`/`:binary`, `:tuple`, `:array`/`:list`, `:atom`, `:function`, `:map`, `:keyword`, `:decimal`, `:date`, `:time`, `:datetime`, `:naive_datetime`, `:utc_datetime`

Extended types: struct modules (e.g., `User`), `{:array, type}` for typed arrays

### Testing

The test suite in test/valdi_test.exs provides comprehensive coverage with parameterized tests for different validation scenarios. Tests use ExUnit with doctest for embedded examples.
17 changes: 14 additions & 3 deletions lib/valdi.ex
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ defmodule Valdi do
:type,
:required,
:format,
:pattern,
:number,
:length,
:in,
Expand All @@ -55,7 +56,7 @@ defmodule Valdi do

**All supported validations**:
- `type`: validate datatype
- `format`: check if binary value matched given regex
- `format`|`pattern`: check if binary value matched given regex
- `number`: validate number value
- `length`: validate length of supported types. See `validate_length/2` for more details.
- `in`: validate inclusion
Expand Down Expand Up @@ -173,6 +174,7 @@ defmodule Valdi do
defp get_validator(:type), do: &validate_type/2
defp get_validator(:required), do: &validate_required/2
defp get_validator(:format), do: &validate_format/2
defp get_validator(:pattern), do: &validate_format/2
defp get_validator(:number), do: &validate_number/2
defp get_validator(:length), do: &validate_length/2
defp get_validator(:in), do: &validate_inclusion/2
Expand Down Expand Up @@ -439,17 +441,26 @@ defmodule Valdi do
defp get_length(_param), do: {:error, :wrong_type}

@doc """
Checks whether a string match the given regex.
Checks whether a string match the given regex pattern.

```elixir
iex> Valdi.validate_format("year: 2001", ~r/year:\\s\\d{4}/)
:ok
iex> Valdi.validate_format("hello", ~r/\d+/)
{:error, "does not match format"}
iex> Valdi.validate_format("hello", "h.*o")
:ok
```
"""
@spec validate_format(String.t(), Regex.t()) ::
@spec validate_format(String.t(), Regex.t() | String.t()) ::
:ok | error
def validate_format(value, check) when is_binary(value) and is_binary(check) do
case Regex.compile(check) do
{:ok, regex} -> validate_format(value, regex)
{:error, _} -> {:error, "invalid regex pattern"}
end
end

def validate_format(value, check) when is_binary(value) do
if Regex.match?(check, value), do: :ok, else: {:error, "does not match format"}
end
Expand Down
32 changes: 32 additions & 0 deletions test/valdi_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,38 @@ defmodule ValdiTest do
Valdi.validate(10, type: :integer, format: ~r/year:\s\d{4}/)
end

test "validate pattern with match string should ok" do
assert :ok = Valdi.validate("year: 1999", type: :string, pattern: ~r/year:\s\d{4}/)
end

test "validate pattern with not match string should error" do
assert {:error, "does not match format"} =
Valdi.validate("", type: :string, pattern: ~r/year:\s\d{4}/)
end

test "validate pattern with number should error" do
assert {:error, "format check only support string"} =
Valdi.validate(10, type: :integer, pattern: ~r/year:\s\d{4}/)
end

test "validate format with string pattern should ok" do
assert :ok = Valdi.validate("hello world", type: :string, format: "h.*d")
end

test "validate format with string pattern not match should error" do
assert {:error, "does not match format"} =
Valdi.validate("hello", type: :string, format: "\\d+")
end

test "validate format with invalid string pattern should error" do
assert {:error, "invalid regex pattern"} =
Valdi.validate("hello", type: :string, format: "[")
end

test "validate pattern with string pattern should ok" do
assert :ok = Valdi.validate("test123", type: :string, pattern: "test\\d+")
end

@number_tests [
[:equal_to, 10, 10, :ok],
[:equal_to, 10, 11, :error],
Expand Down
Loading