# `IO.ANSI.Table`
[🔗](https://github.com/RaymondLoranger/io_ansi_table/blob/main/lib/io/ansi/table.ex#L4)

Writes data to `:stdio` in a table with borders and colors.
Can choose a table style to change the look of the table.

##### Inspired by the book [Programming Elixir](https://pragprog.com/titles/elixir16/programming-elixir-1-6) by Dave Thomas.

# `format`

```elixir
@spec format([Access.container()], keyword()) :: :ok
```

Sends a request to the table spec server identified by option `:spec_name`.
The server will write data from `maps` to `:stdio` in a table formatted per
its spec and `options`.

Also supports:

  - keywords
  - structs implementing the [Access](https://hexdocs.pm/elixir/Access.html)
    behaviour.

Does not support:

  - _nested_ maps, keywords or structs

See `IO.ANSI.Table.Options` for a list of all options in this API.

## Parameters

  - `maps`    - _flat_ maps/keywords/structs (list)
  - `options` - up to `6` options (keyword)

## Options

  - `:async`     - whether to write the table asynchronously (boolean)
  - `:bell`      - whether to ring the bell (boolean)
  - `:count`     - number of `maps` to format (integer)
  - `:max_width` - to cap column widths (non neg integer)
  - `:spec_name` - to identify the table spec server (string)
  - `:style`     - table style (atom)

## Examples

    alias IO.ANSI.Table

    people = [
      %{name: "Mike", likes: "ski, arts", dob: "1992-04-15", bmi: 23.9},
      %{name: "Mary", likes: "travels"  , dob: "1992-04-15", bmi: 26.8},
      %{name: "Ann" , likes: "reading"  , dob: "1992-04-15", bmi: 24.7},
      %{name: "Ray" , likes: "cycling"  , dob: "1977-08-28", bmi: 19.1},
      %{name: "Bill", likes: "karate"   , dob: "1977-08-28", bmi: 18.1},
      %{name: "Joe" , likes: "boxing"   , dob: "1977-08-28", bmi: 20.8},
      %{name: "Jill", likes: "cooking"  , dob: "1976-09-28", bmi: 25.8}
    ]

    Table.start([:name, :dob, :likes],
      header_fixes: %{~r[dob]i => "Date of Birth"},
      sort_specs: [asc: :dob],
      align_specs: [center: :dob],
      margins: [top: 2, bottom: 2, left: 2]
    )

    Table.format(people, style: :light)
    Table.format(people, style: :light_alt)
    Table.format(people, style: :light_mult)
    Table.format(people, style: :cyan_alt)
    Table.format(people, style: :cyan_mult)

## ![light](images/light.png)
## ![light_alt](images/light_alt.png)
## ![light_mult](images/light_mult.png)
## ![cyan_alt](images/cyan_alt.png)
## ![cyan_mult](images/cyan_mult.png)

# `get_spec`

```elixir
@spec get_spec(keyword()) :: IO.ANSI.Table.Spec.t()
```

Sends a request to the table spec server identified by option `:spec_name`.
Returns the server's table spec.

See `IO.ANSI.Table.Options` for a list of all options in this API.

## Parameters

  - `options` - up to `1` option (keyword)

## Options

  - `:spec_name` - to identify the table spec server (string)

## Examples

    iex> alias IO.ANSI.Table
    iex> Table.start([:name, :dob, :likes])
    iex> spec = Table.get_spec()
    iex> {spec.headers, spec.spec_name}
    {[:name, :dob, :likes], "io_ansi_table"}

    iex> alias IO.ANSI.Table
    iex> Table.start([:name, :dob, :likes], spec_name: "members")
    iex> spec = Table.get_spec(spec_name: "members")
    iex> {spec.headers, spec.spec_name}
    {[:name, :dob, :likes], "members"}

# `start`

```elixir
@spec start([IO.ANSI.Table.Header.t(), ...], keyword()) :: Supervisor.on_start_child()
```

Starts a new table spec server process and supervises it.
Upon request (see `format/2`), the server will write data from a list of
maps to `:stdio` in a table formatted per `headers` and `options`.

The table columns are identified by `headers` (`Map` keys).
We calculate the width of each column to fit the longest element
in that column, also considering the column heading.
However, the `:max_width` option prevails.

If the `:count` option is positive, we format the first _count_
maps in the list, once sorted. If negative, the last _count_ ones.

See `IO.ANSI.Table.Options` for a list of all options in this API.

## Parameters

  - `headers` - keys identifying each column (list)
  - `options` - up to `10` options (keyword)

## Options

  - `:align_specs`  - to align column elements (list/keyword)
  - `:bell`         - whether to ring the bell (boolean)
  - `:count`        - number of maps to format (integer)
  - `:header_fixes` - to alter the `headers` (map)
  - `:margins`      - to position the table (keyword)
  - `:max_width`    - to cap column widths (non neg integer)
  - `:sort_specs`   - to sort the maps (list/keyword)
  - `:sort_symbols` - to denote sort direction (keyword)
  - `:spec_name`    - to identify the table spec server (string)
  - `:style`        - table style (atom)

## Examples

    iex> alias IO.ANSI.Table
    iex> alias IO.ANSI.Table.SpecServer
    iex> {:ok, pid} = Table.start([:name, :dob, :likes], spec_name: "people")
    iex> pid == SpecServer.via("people") |> GenServer.whereis()
    true

# `stop`

```elixir
@spec stop(keyword()) :: :ok
```

Stops a table spec server process normally. It won't be restarted.
The table spec server to stop is identified by option `:spec_name`.

See `IO.ANSI.Table.Options` for a list of all options in this API.

## Parameters

  - `options` - up to `1` option (keyword)

## Options

  - `:spec_name` - to identify the table spec server (string)

## Examples

    iex> alias IO.ANSI.Table
    iex> Table.start([:name, :dob, :likes])
    iex> Table.stop
    :ok

    iex> alias IO.ANSI.Table
    iex> Table.start([:name, :dob, :likes], spec_name: "stats")
    iex> Table.stop(spec_name: "stats")
    :ok

# `write`

```elixir
@spec write(IO.ANSI.Table.Spec.t(), [Access.container()], keyword()) :: :ok
```

Writes data from `maps` to `:stdio` in a table formatted per `spec` and
`options`.

Also supports:

  - keywords
  - structs implementing the [Access](https://hexdocs.pm/elixir/Access.html)
    behaviour

Does not support:

  - _nested_ maps, keywords or structs

See `IO.ANSI.Table.Options` for a list of all options in this API.

## Parameters

  - `spec`    - table spec (struct)
  - `maps`    - _flat_ maps/keywords/structs (list)
  - `options` - up to `4` options (keyword)

## Options

  - `:bell`      - whether to ring the bell (boolean)
  - `:count`     - number of `maps` to format (integer)
  - `:max_width` - to cap column widths (non neg integer)
  - `:style`     - table style (atom)

## Examples

    alias IO.ANSI.Table
    alias IO.ANSI.Table.Spec

    people = [
      %{name: "Mike", likes: "ski, arts", dob: "1992-04-15", bmi: 23.9},
      %{name: "Mary", likes: "travels"  , dob: "1992-04-15", bmi: 26.8},
      %{name: "Ann" , likes: "reading"  , dob: "1992-04-15", bmi: 24.7},
      %{name: "Ray" , likes: "cycling"  , dob: "1977-08-28", bmi: 19.1},
      %{name: "Bill", likes: "karate"   , dob: "1977-08-28", bmi: 18.1},
      %{name: "Joe" , likes: "boxing"   , dob: "1977-08-28", bmi: 20.8},
      %{name: "Jill", likes: "cooking"  , dob: "1976-09-28", bmi: 25.8}
    ]

    spec = Spec.new([:name, :dob, :likes],
      header_fixes: %{~r[dob]i => "Date of Birth"},
      sort_specs: [asc: :dob],
      align_specs: [center: :dob],
      margins: [top: 2, bottom: 2, left: 2]
    ) |> Spec.develop()

    Table.write(spec, people, style: :light)
    Table.write(spec, people, style: :light_alt)
    Table.write(spec, people, style: :light_mult)
    Table.write(spec, people, style: :cyan_alt)
    Table.write(spec, people, style: :cyan_mult)

---

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