# `Owl.IO`
[🔗](https://github.com/fuelen/owl/blob/v0.13.1/lib/owl/io.ex#L1)

A set of functions for handling I/O with support for `t:Owl.Data.t/0`.

# `cast_input`

```elixir
@type cast_input() :: (String.t() | nil -&gt;
                   {:ok, value :: any()} | {:error, reason :: String.Chars.t()})
```

# `confirm_option`

```elixir
@type confirm_option() ::
  {:message, Owl.Data.t()}
  | {:default, boolean()}
  | {:answers,
     true: {primary_true_answer :: binary(), other_true_answers :: [binary()]},
     false:
       {primary_false_answer :: binary(), other_false_answers :: [binary()]}}
```

# `input_option`

```elixir
@type input_option() ::
  {:label, Owl.Data.t()}
  | {:cast, atom() | {atom(), Keyword.t()} | cast_input()}
  | {:optional, boolean()}
  | {:secret, boolean()}
```

# `multiselect_option`

```elixir
@type multiselect_option() ::
  {:label, Owl.Data.t() | nil}
  | {:render_as, (any() -&gt; Owl.Data.t())}
  | {:min, non_neg_integer() | nil}
  | {:max, non_neg_integer() | nil}
```

# `open_in_editor_option`

```elixir
@type open_in_editor_option() :: {:editor, String.t()} | {:format, String.t()}
```

# `select_option`

```elixir
@type select_option() ::
  {:label, Owl.Data.t() | nil} | {:render_as, (any() -&gt; Owl.Data.t())}
```

# `columns`

```elixir
@spec columns(IO.device()) :: pos_integer() | nil
```

Returns the width of a terminal.

A wrapper around `:io.columns/1`, but returns `nil` if a terminal is not found.
This is useful for conveniently falling back to another value using the `||/2` operator.

## Example

    Owl.IO.columns() || 80

# `confirm`

```elixir
@spec confirm([confirm_option()]) :: boolean()
```

Asks the user to type a confirmation.

Valid inputs are a blank string and values specified in the `:answers` option.
The user will be asked to type a confirmation again on invalid input.

## Options

* `:message` - typically a question about performing an operation. Defaults to `"Are you sure?"`.
* `:default` - the value used when the user responds with a blank string. Defaults to `false`.
* `:answers` - allows specifying alternative answers. Defaults to `[true: {"y", ["yes"]}, false: {"n", ["no"]}]`.

## Examples

    Owl.IO.confirm()
    #=> Are you sure? [yN] n
    false

    Owl.IO.confirm(message: Owl.Data.tag("Really?", :red), default: true)
    #=> Really? [Yn]
    true

    Owl.IO.confirm(
      message: Owl.Data.tag("Справді?", :red),
      answers: [true: {"т", ["так", "y", "yes"]}, false: {"н", ["ні", "n", "no"]}]
    )
    #=> Справді? [тН] НІ
    false

# `input`

```elixir
@spec input([input_option()]) :: any()
```

Reads a line from `stdio` and casts it to the given type.

After reading a line from `stdio`, it is automatically trimmed with `String.trim/2`.
The final value is returned when the user types a valid value.

## Options

* `:secret` - set to `true` to make input invisible. Defaults to `false`.
* `:label` - a text label. Defaults to `nil` (no label).
* `:optional` - whether the value is optional. Defaults to `false`.
* `:cast` - casts a value after reading it from `stdio`. Defaults to `:string`. Possible values:
  * an anonymous function with arity 1 that is described by `t:cast_input/0`
  * a pair with built-in type represented as atom and a keyword-list with options. Built-in types:
    * `:integer`, options:
      * `:min` - the minimum allowed value. Defaults to `nil` (no lower bound).
      * `:max` - the maximum allowed value. Defaults to `nil` (no upper bound).
    * `:float`, options:
      * `:min` - the minimum allowed value. Defaults to `nil` (no lower bound).
      * `:max` - the maximum allowed value. Defaults to `nil` (no upper bound).
    * `:string`, options:
      * no options
  * an atom which is simply an alias to `{atom(), []}`

## Examples

    Owl.IO.input()
    #=> > hello world
    "hello world"

    Owl.IO.input(secret: true)
    #=> >
    "password"

    Owl.IO.input(optional: true)
    #=> >
    nil

    Owl.IO.input(label: "Your age", cast: {:integer, min: 18, max: 100})
    #=> Your age
    #=> > 12
    #=> must be greater than or equal to 18
    #=> Your age
    #=> > 102
    #=> must be less than or equal to 100
    #=> Your age
    #=> > 18
    18

    Owl.IO.input(label: "Birth date in ISO 8601 format:", cast: &Date.from_iso8601/1)
    #=> Birth date in ISO 8601 format:
    #=> > 1 January
    #=> invalid_format
    #=> Birth date in ISO 8601 format:
    #=> > 2021-01-01
    ~D[2021-01-01]

# `inspect`

```elixir
@spec inspect(item, keyword(), IO.device()) :: item when item: var
```

Wrapper around `IO.inspect/3` with changed defaults.

As in `puts/2`, the `device` argument is moved to the end.
Options are the same as for `IO.inspect/3` with small changes:
* `:pretty` is `true` by default.
* `:syntax_colors` uses color schema from `IEx` by default.
* `:label` is extended and accepts `t:Owl.Data.t/0`.

## Examples

    "Hello"
    |> Owl.IO.inspect(label: "Greeting")
    |> String.upcase()
    |> Owl.IO.inspect(label: Owl.Data.tag("GREETING", :cyan))
    #=> Greeting: "Hello"
    #=> GREETING: "HELLO"

    # inspect data above rendered live blocks
    Owl.IO.inspect("Hello", [], Owl.LiveScreen)
    #=> "Hello"

# `multiselect`

```elixir
@spec multiselect([item, ...], [multiselect_option()]) :: [item] when item: any()
```

Selects multiple values from the given nonempty list.

Input item numbers must be separated by any non-digit character. Most likely you'll want to use spaces or commas.
You can specify a range of numbers using a hyphen.

## Options

* `:label` - a text label. Defaults to `nil` (no label).
* `:render_as` - a function that renders given item. Defaults to `Function.identity/1`.
* `:min` - the minimum output list length. Defaults to `nil` (no lower bound).
* `:max` - the maximum output list length. Defaults to `nil` (no upper bound).

## Examples

    Owl.IO.multiselect(["one", "two", "three"], min: 2, label: "Select 2 numbers:", render_as: &String.upcase/1)
    #=> 1. ONE
    #=> 2. TWO
    #=> 3. THREE
    #=>
    #=> Select 2 numbers:
    #=> > 1
    #=> the number of elements must be greater than or equal to 2
    #=> Select 2 numbers:
    #=> > 1 3
    #=>
    ["one", "three"]

    Owl.IO.multiselect(Enum.to_list(1..5), render_as: &to_string/1)
    #=> 1. 1
    #=> 2. 2
    #=> 3. 3
    #=> 4. 4
    #=> 5. 5
    #=>
    #=> > 1-3 5
    #=>
    [1, 2, 3, 5]

# `open_in_editor`

```elixir
@spec open_in_editor(iodata()) :: String.t()
```

Opens `data` in an editor for editing.

Returns the updated data when the file is saved and the editor is closed.
Similar to `IEx.Helpers.open/1`, this function uses the `ELIXIR_EDITOR` environment variable by default.
`__FILE__` notation is supported as well.

## Example

    # use neovim in the Alacritty terminal emulator as an editor
    $ export ELIXIR_EDITOR="alacritty -e nvim"

    # open the editor from Elixir code
    Owl.IO.open_in_editor("hello\nworld")

    # specify the editor explicitly
    Owl.IO.open_in_editor("hello\nworld", "alacritty -e nvim")

    # pass options as a keyword list
    Owl.IO.open_in_editor("hello\nworld", editor: "alacritty -e nvim")

    # specify file format to help editor highlight correctly
    Owl.IO.open_in_editor("%{foo: :bar}\n", editor: "alacritty -e nvim", format: "ex")

# `open_in_editor`

```elixir
@spec open_in_editor(iodata(), String.t() | [open_in_editor_option()]) :: String.t()
```

# `puts`

```elixir
@spec puts(Owl.Data.t(), device :: IO.device()) :: :ok
```

Wrapper around `IO.puts/2` that accepts `t:Owl.Data.t/0`.

The other difference is that the `device` argument is moved to the second position.

## Examples

    Owl.IO.puts(["Hello ", Owl.Data.tag("world", :green)])
    #=> Hello world

    # specify Owl.LiveScreen as the device to print data above rendered live blocks
    Owl.IO.puts(["Hello ", Owl.Data.tag("world", :green)], Owl.LiveScreen)
    #=> Hello world

# `rows`

```elixir
@spec rows(IO.device()) :: pos_integer() | nil
```

Returns the height of a terminal.

A wrapper around `:io.rows/1`, but returns `nil` if a terminal is not found.
This is useful for conveniently falling back to another value using the `||/2` operator.

## Example

    Owl.IO.rows() || 20

# `select`

```elixir
@spec select([item, ...], [select_option()]) :: item when item: any()
```

Selects one item from the given nonempty list.

Returns the value immediately if the list contains only one element.

## Options

* `:label` - a text label. Defaults to `nil` (no label).
* `:render_as` - a function that renders given item. Defaults to `Function.identity/1`.

## Examples

    Owl.IO.select(["one", "two", "three"])
    #=> 1. one
    #=> 2. two
    #=> 3. three
    #=>
    #=> > 1
    #=>
    "one"

    ~D[2001-01-01]
    |> Date.range(~D[2001-01-03])
    |> Enum.to_list()
    |> Owl.IO.select(render_as: &Date.to_iso8601/1, label: "Please select a date")
    #=> 1. 2001-01-01
    #=> 2. 2001-01-02
    #=> 3. 2001-01-03
    #=>
    #=> Please select a date
    #=> > 2
    #=>
    ~D[2001-01-02]

    packages = [
      %{name: "elixir", description: "programming language"},
      %{name: "asdf", description: "version manager"},
      %{name: "neovim", description: "fork of vim"}
    ]
    Owl.IO.select(packages,
      render_as: fn %{name: name, description: description} ->
        [Owl.Data.tag(name, :cyan), "\n  ", Owl.Data.tag(description, :light_black)]
      end
    )
    #=> 1. elixir
    #=>      programming language
    #=> 2. asdf
    #=>      version manager
    #=> 3. neovim
    #=>      fork of vim
    #=>
    #=> > 3
    #=>
    %{description: "fork of vim", name: "neovim"}

---

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