# `Cinder.Filter.Helpers`
[🔗](https://github.com/sevenseacat/cinder/blob/v0.15.0/lib/cinder/filter/helpers.ex#L1)

Helper functions for building and validating custom filters.

This module provides common patterns and utilities that custom filter
developers can use to simplify their implementations.

## Usage

    defmodule MyApp.Filters.CustomFilter do
      use Cinder.Filter
      import Cinder.Filter.Helpers

      @impl true
      def process(raw_value, column) do
        with {:ok, trimmed} <- validate_string_input(raw_value),
             {:ok, parsed} <- parse_custom_value(trimmed) do
          build_filter(:my_filter, parsed, :equals)
        else
          _ -> nil
        end
      end
    end

# `build_ash_filter`

Builds a relationship-aware Ash query filter with embedded field support.

Handles direct fields, relationship fields using dot notation, and embedded fields using
double-underscore notation.

## Examples

    build_ash_filter(query, "name", "John", :equals)
    build_ash_filter(query, "user.name", "John", :equals)
    build_ash_filter(query, "profile__first_name", "John", :equals)
    build_ash_filter(query, "settings__address__street", "Main St", :contains)

# `build_filter`

Builds a standard filter map.

## Examples

    iex> build_filter(:my_filter, "value", :equals)
    %{type: :my_filter, value: "value", operator: :equals}

    iex> build_filter(:slider, 50, :less_than_or_equal, case_sensitive: false)
    %{type: :slider, value: 50, operator: :less_than_or_equal, case_sensitive: false}

# `debug_filter`

Debug helper for filter development.

Logs filter processing information when debug is enabled.

## Examples

    debug_filter("MyFilter", "processing input", %{input: "test"})

# `empty_value?`

Common empty value check for most filter types.

## Examples

    iex> empty_value?(nil)
    true

    iex> empty_value?("")
    true

    iex> empty_value?([])
    true

    iex> empty_value?(%{value: nil})
    true

    iex> empty_value?("test")
    false

# `extract_option`

Extracts filter options with type safety.

## Examples

    iex> extract_option([min: 0, max: 100], :min, 50)
    0

    iex> extract_option([], :min, 50)
    50

    iex> extract_option(%{min: 0, max: 100}, :min, 50)
    0

# `humanize_embedded_field`

Converts field notation to human-readable labels.

## Examples

    iex> humanize_embedded_field("profile__first_name")
    "Profile > First Name"

    iex> humanize_embedded_field("user.profile__first_name")
    "User > Profile > First Name"

# `parse_field_notation`

Parses field notation to determine field type and structure.

Embedded fields are written with double-underscore notation (`profile__first_name`).

## Examples

    iex> parse_field_notation("username")
    {:direct, "username"}

    iex> parse_field_notation("user.name")
    {:relationship, ["user"], "name"}

    iex> parse_field_notation("profile__first_name")
    {:embedded, "profile", "first_name"}

    iex> parse_field_notation("settings__address__street")
    {:nested_embedded, "settings", ["address", "street"]}

    iex> parse_field_notation("user.profile__first_name")
    {:relationship_embedded, ["user"], "profile", "first_name"}

# `validate_csv_input`

Validates and parses comma-separated values.

## Examples

    iex> validate_csv_input("a,b,c")
    {:ok, ["a", "b", "c"]}

    iex> validate_csv_input("a, b , c ", trim: true)
    {:ok, ["a", "b", "c"]}

    iex> validate_csv_input("", min_length: 1)
    {:error, :empty}

# `validate_date_input`

Validates date input in ISO 8601 format.

## Examples

    iex> validate_date_input("2023-12-25")
    {:ok, ~D[2023-12-25]}

    iex> validate_date_input("invalid-date")
    {:error, :invalid}

# `validate_embedded_field_syntax`

Validates embedded field syntax and returns appropriate error messages.

## Examples

    iex> validate_embedded_field_syntax("profile[:first_name]")
    :ok

    iex> validate_embedded_field_syntax("profile[invalid]")
    {:error, "Invalid embedded field syntax: missing colon"}

# `validate_filter_implementation`

Validates that a module properly implements the Cinder.Filter behaviour.

## Examples

    iex> validate_filter_implementation(MyApp.Filters.ValidFilter)
    {:ok, "Filter implementation is valid"}

    iex> validate_filter_implementation(InvalidModule)
    {:error, ["Missing callback: render/4", "Missing callback: process/2"]}

# `validate_filter_structure`

Validates a filter structure has required fields.

## Examples

    iex> validate_filter_structure(%{type: :text, value: "test", operator: :equals})
    {:ok, %{type: :text, value: "test", operator: :equals}}

    iex> validate_filter_structure(%{type: :text, value: "test"})
    {:error, :missing_operator}

# `validate_float_input`

Validates float input with optional min/max bounds.

## Examples

    iex> validate_float_input("42.5")
    {:ok, 42.5}

    iex> validate_float_input("42.5", min: 0.0, max: 100.0)
    {:ok, 42.5}

    iex> validate_float_input("150.0", max: 100.0)
    {:error, :out_of_bounds}

    iex> validate_float_input("abc")
    {:error, :invalid}

# `validate_hex_color_input`

Validates hex color input.

## Examples

    iex> validate_hex_color_input("#FF0000")
    {:ok, "#ff0000"}

    iex> validate_hex_color_input("#fff")
    {:error, :invalid}

    iex> validate_hex_color_input("red")
    {:error, :invalid}

# `validate_integer_input`

Validates integer input with optional min/max bounds.

## Examples

    iex> validate_integer_input("42")
    {:ok, 42}

    iex> validate_integer_input("42", min: 0, max: 100)
    {:ok, 42}

    iex> validate_integer_input("150", max: 100)
    {:error, :out_of_bounds}

    iex> validate_integer_input("abc")
    {:error, :invalid}

# `validate_operator`

Validates operator is in allowed list.

## Examples

    iex> validate_operator(:equals, [:equals, :contains])
    {:ok, :equals}

    iex> validate_operator(:invalid, [:equals, :contains])
    {:error, :invalid_operator}

# `validate_string_input`

Validates and trims string input, returning error for empty strings.

## Examples

    iex> validate_string_input("  hello  ")
    {:ok, "hello"}

    iex> validate_string_input("")
    {:error, :empty}

    iex> validate_string_input(nil)
    {:error, :invalid}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
