# `PhoenixGenApi.ArgumentHandler`
[🔗](https://github.com/ohhi-vn/phoenix_gen_api/blob/main/lib/phoenix_gen_api/argument_handler.ex#L1)

Validates and converts request arguments according to configured type specifications.

This module provides comprehensive argument validation and type conversion for API requests.
It ensures that all request arguments match their expected types and sizes before function
execution, preventing type errors and potential security issues.

## Argument Type Definitions

### Simple Format (Backward Compatible)

The simple format uses just the type atom:

    arg_types: %{
      "user_id" => :string,
      "age" => :num,
      "active" => :boolean
    }

### Extended Format (New)

The extended format uses a keyword list with `:type` and optional parameters:

    arg_types: %{
      "user_id" => [type: :string, max_bytes: 255, allow_nil?: true],
      "age" => [type: :num, default_value: 18],
      "tags" => [type: :list_string, max_items: 10, max_item_bytes: 100],
      "scores" => [type: :list_num, max_items: 50],
      "gps_list" => [type: :list_map, max_items: 100],
      "metadata" => [type: :map, max_items: 200, required: ["name"], accept: ["name", "email", "age"]]
    }

#### Extended Format Options

- `type:` - Required. The argument type (`:string`, `:num`, `:boolean`, etc.)
- `allow_nil?:` - Optional. When `true`, allows nil values (default: `false`)
- `default_value:` - Optional. Default value if argument is missing from request
- `max_bytes:` - For `:string` type, max byte size
- `max_items:` - For list/map types, max number of items
- `max_item_bytes:` - For `:list_string`, max bytes per item

#### Behavior

- If argument is **missing** from request and `default_value` is set -> uses default_value
- If argument is **missing** from request and no `default_value` -> error (unless `allow_nil?`)
- If argument is **explicitly nil** and `allow_nil?` is true -> uses nil
- If argument is **explicitly nil** and `allow_nil?` is false -> error
- If argument is **present** -> validates and converts the value

### Type-Specific Parameters

- **`:string`**: `max_bytes:` (default 3000)
- **`:list`**: `max_items:` (default 1000)
- **`:list_string`**: `max_items:` (default 1000), `max_item_bytes:` (default 3000)
- **`:list_num`**: `max_items:` (default 1000)
- **`:list_map`**: `max_items:` (default 1000)
- **`:map`**: `max_items:` (default 1000), `required:` (list of required key names), `accept:` (list of accepted key names — any key not in this list causes an error)

## Size Limits (Defaults)

- Strings: 3000 bytes (not characters, to prevent UTF-8 bypass attacks)
- Lists: 1000 items
- Maps: 1000 entries

These limits help prevent denial-of-service attacks through oversized payloads.

## Security Considerations

- Uses `byte_size/1` instead of `String.length/1` to prevent UTF-8 bypass attacks
- Validates against oversized payloads that could cause memory exhaustion
- Rejects nested structures to prevent deep recursion attacks
- Strict type checking prevents type confusion vulnerabilities

## Validation Rules

1. **Type Matching**: Argument values must match their declared types
2. **Size Limits**: Strings, lists, and maps must not exceed size limits
3. **Required Arguments**: All declared arguments must be present
4. **No Extra Arguments**: Requests cannot include undeclared arguments
5. **No Nil Values**: Nil values are not accepted for any argument
6. **No Nesting**: Nested lists and maps are not currently supported

## Examples

    # Configure argument types (simple format)
    config = %FunConfig{
      arg_types: %{
        "username" => :string,
        "age" => :num,
        "email" => :string,
        "tags" => :list_string,
        "scores" => :list_num
      },
      arg_orders: ["username", "age", "email", "tags", "scores"]
    }

    # Configure argument types (extended format with nil and default support)
    config = %FunConfig{
      arg_types: %{
        "username" => [type: :string, allow_nil?: false],
        "age" => [type: :num, default_value: 18],
        "email" => [type: :string, allow_nil?: true, max_bytes: 255],
        "tags" => [type: :list_string, default_value: [], max_items: 10, max_item_bytes: 100],
        "scores" => [type: :list_num, allow_nil?: true, max_items: 100]
      },
      arg_orders: ["username", "age", "email", "tags", "scores"]
    }

    # When allow_nil? is true, nil values are accepted
    # When default_value is set and argument is missing, default is used
    # If argument is missing and no default_value, and allow_nil? is false -> error
    # Valid request
    request = %Request{
      args: %{
        "username" => "alice",
        "age" => 30,
        "email" => "alice@example.com",
        "tags" => ["elixir", "phoenix"],
        "scores" => [95, 87, 92]
      }
    }

    args = ArgumentHandler.convert_args!(config, request)
    # => ["alice", 30, "alice@example.com", ["elixir", "phoenix"], [95, 87, 92]]

    # Invalid request - wrong type
    request = %Request{
      args: %{
        "username" => "alice",
        "age" => "thirty",  # String instead of number
        "email" => "alice@example.com",
        "tags" => ["elixir"],
        "scores" => [95]
      }
    }

    ArgumentHandler.convert_args!(config, request)
    # ** (ArgumentError) invalid argument type for "age": expected :num, got "thirty"

## Error Handling

All validation functions raise `ArgumentError` on validation failures with descriptive
messages. The `InvalidType` exception is raised for type conversion errors.

## Notes

- Argument order is preserved based on `arg_orders` configuration
- Functions with no arguments return an empty list
- Single-argument functions return a list with one element
- Validation happens before type conversion
- Nested structures (maps in maps, lists in lists) are not supported
- `arg_types` can be in simple format (`%{"user_id" => :string}`) or extended format
  (`%{"user_id" => [type: :string, allow_nil?: true, default_value: "hello"]}`).

# `convert_args!`

Validates and converts request arguments to the correct types and order.

This function performs both validation and conversion in one step. It first validates
that all required arguments are present with correct types and sizes, then converts
them to the order specified in the configuration.

## Parameters

  - `config` - A `FunConfig` struct containing:
    - `arg_types` - Map of argument names to type specifications
    - `arg_orders` - List of argument names in the expected order

  - `request` - A `Request` struct containing the arguments to validate

## Returns

A list of argument values in the order specified by `arg_orders`, or an empty list
if the function has no arguments.

## Raises

  - `RuntimeError` - If validation fails (wrong type, size, missing args, etc.)
  - `InvalidType` - If type conversion fails

## Examples

    # Simple format
    config = %FunConfig{
      arg_types: %{"name" => :string, "age" => :num},
      arg_orders: ["name", "age"]
    }

    request = %Request{
      args: %{"name" => "Alice", "age" => 30}
    }

    ArgumentHandler.convert_args!(config, request)
    # => ["Alice", 30]

    # Extended format with default values
    config = %FunConfig{
      arg_types: %{
        "name" => [type: :string],
        "age" => [type: :num, default_value: 25],
        "email" => [type: :string, allow_nil?: true]
      },
      arg_orders: ["name", "age", "email"]
    }

    # Missing "age" and "email" - will use default for age, nil for email
    request = %Request{
      args: %{"name" => "Bob"}
    }

    ArgumentHandler.convert_args!(config, request)
    # => ["Bob", 25, nil]

# `list_max_items`

Returns the configured maximum number of items in a list.

Configurable via `config :phoenix_gen_api, :argument_handler, list_max_items: N`.
Defaults to 1000.

# `map_max_items`

Returns the configured maximum number of items in a map.

Configurable via `config :phoenix_gen_api, :argument_handler, map_max_items: N`.
Defaults to 1000.

# `string_max_bytes`

Returns the configured maximum string size in bytes.

Configurable via `config :phoenix_gen_api, :argument_handler, string_max_bytes: N`.
Defaults to 3000.

# `validate_args!`

Validate request arguments.

---

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