# `Lotus.Visibility.Policy`
[🔗](https://github.com/typhoonworks/lotus/blob/v0.16.6/lib/lotus/visibility/policy.ex#L1)

Policy builders and validators for visibility rules.

This module provides functions to create and validate visibility policies
for different database resource types (schemas, tables, columns)

## Schema and Table Policies

Schemas and tables use simple allow/deny policies:

    Policy.schema_allow()  # => :allow
    Policy.schema_deny()   # => :deny

## Column Policies

Columns support more complex policies with various actions:

    # Simple actions
    Policy.column_allow()  # Show column normally
    Policy.column_omit()   # Remove from results
    Policy.column_error()  # Fail query if selected

    # Masking with strategies
    Policy.column_mask(:sha256)
    Policy.column_mask({:fixed, "REDACTED"})
    Policy.column_mask({:partial, keep_last: 4})

    # With options
    Policy.column_mask(:sha256, show_in_schema?: false)

# `column_action`

```elixir
@type column_action() :: :allow | :omit | :mask | :error
```

# `column_policy`

```elixir
@type column_policy() :: %{
  action: column_action(),
  mask: mask_strategy() | nil,
  show_in_schema?: boolean()
}
```

# `mask_strategy`

```elixir
@type mask_strategy() :: :null | :sha256 | {:fixed, any()} | {:partial, keyword()}
```

# `schema_policy`

```elixir
@type schema_policy() :: :allow | :deny
```

# `table_policy`

```elixir
@type table_policy() :: :allow | :deny
```

# `allows_column?`

```elixir
@spec allows_column?(column_policy()) :: boolean()
```

Checks if a column policy allows the column.

# `causes_error?`

```elixir
@spec causes_error?(column_policy()) :: boolean()
```

Checks if a column policy causes an error.

# `column_allow`

```elixir
@spec column_allow(keyword()) :: column_policy()
```

Creates a policy that allows a column to be shown normally.

## Examples

    iex> Policy.column_allow()
    %{action: :allow, show_in_schema?: true}

# `column_error`

```elixir
@spec column_error(keyword()) :: column_policy()
```

Creates a policy that causes queries to error if the column is selected.

## Examples

    iex> Policy.column_error()
    %{action: :error, show_in_schema?: true}

# `column_mask`

```elixir
@spec column_mask(
  mask_strategy(),
  keyword()
) :: column_policy()
```

Creates a policy that masks column values using the specified strategy.

## Mask Strategies

- `:null` - Replace with NULL
- `:sha256` - Replace with SHA256 hash
- `{:fixed, value}` - Replace with fixed value
- `{:partial, opts}` - Partial masking with options:
  - `keep_first: n` - Keep first n characters
  - `keep_last: n` - Keep last n characters
  - `replacement: str` - Character to use for masking (default: "*")

## Examples

    iex> Policy.column_mask(:sha256)
    %{action: :mask, mask: :sha256, show_in_schema?: true}

    iex> Policy.column_mask({:fixed, "REDACTED"})
    %{action: :mask, mask: {:fixed, "REDACTED"}, show_in_schema?: true}

    iex> Policy.column_mask({:partial, keep_last: 4})
    %{action: :mask, mask: {:partial, keep_last: 4}, show_in_schema?: true}

    iex> Policy.column_mask(:sha256, show_in_schema?: false)
    %{action: :mask, mask: :sha256, show_in_schema?: false}

# `column_omit`

```elixir
@spec column_omit(keyword()) :: column_policy()
```

Creates a policy that omits a column from query results.

The column is removed entirely from the result set.

## Examples

    iex> Policy.column_omit()
    %{action: :omit, show_in_schema?: true}

    iex> Policy.column_omit(show_in_schema?: false)
    %{action: :omit, show_in_schema?: false}

# `hidden_from_schema?`

```elixir
@spec hidden_from_schema?(column_policy() | nil) :: boolean()
```

Checks if a column should be hidden from schema introspection.

Returns true if the policy explicitly sets show_in_schema? to false,
false otherwise (including when policy is nil).

# `normalize_column_policy`

```elixir
@spec normalize_column_policy(keyword() | atom() | map()) :: column_policy()
```

Normalizes a column policy from various input formats.

Accepts:
- Keyword list: `[action: :mask, mask: :sha256]`
- Atom shorthand: `:omit`
- Map: `%{action: :mask, mask: :null}`

Returns a normalized policy map with all required fields.

# `omits_column?`

```elixir
@spec omits_column?(column_policy()) :: boolean()
```

Checks if a column policy omits the column.

# `requires_mask?`

```elixir
@spec requires_mask?(column_policy()) :: boolean()
```

Checks if a column policy requires masking.

# `schema_allow`

```elixir
@spec schema_allow() :: :allow
```

Creates an allow policy for schemas.

# `schema_deny`

```elixir
@spec schema_deny() :: :deny
```

Creates a deny policy for schemas.

# `table_allow`

```elixir
@spec table_allow() :: :allow
```

Creates an allow policy for tables.

# `table_deny`

```elixir
@spec table_deny() :: :deny
```

Creates a deny policy for tables.

# `valid_column_policy?`

```elixir
@spec valid_column_policy?(any()) :: boolean()
```

Validates if a value is a valid column policy.

# `valid_schema_policy?`

```elixir
@spec valid_schema_policy?(any()) :: boolean()
```

Validates if a value is a valid schema policy.

# `valid_table_policy?`

```elixir
@spec valid_table_policy?(any()) :: boolean()
```

Validates if a value is a valid table policy.

# `validate_mask_strategy!`

```elixir
@spec validate_mask_strategy!(any()) :: mask_strategy() | no_return()
```

Validates a mask strategy, raising if invalid.

---

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