# `Tempus.Slots.Stream`
[🔗](https://github.com/am-kantox/tempus/blob/v1.0.0-rc.0/lib/tempus/slots/stream.ex#L1)

The default `Stream` implementation of `Tempus.Slots` ordered collection.

### Examples

    iex> import Tempus.Slots.Stream, only: [slots: 0]
    iex> slots = [
    ...>   Tempus.Slot.wrap(~D|2020-08-07|),
    ...>   Tempus.Slot.wrap(~D|2020-08-10|),
    ...>   %Tempus.Slot{
    ...>       from: ~U|2020-08-07 01:00:00Z|, to: ~U|2020-08-08 01:00:00Z|}]
    ...> slots |> Enum.into(slots()) |> Enum.to_list()
    [%Tempus.Slot{from: ~U[2020-08-07 00:00:00.000000Z], to: ~U[2020-08-08 01:00:00Z], from_open: false, to_open: true},
     %Tempus.Slot{from: ~U[2020-08-10 00:00:00.000000Z], to: ~U[2020-08-11 00:00:00.000000Z], from_open: false, to_open: true}]
    iex> Enum.map(slots, & &1.from)
    [~U[2020-08-07 00:00:00.000000Z], ~U[2020-08-10 00:00:00.000000Z], ~U[2020-08-07 01:00:00Z]]

# `t`

```elixir
@type t() :: Tempus.Slots.implementation(Tempus.Slots.Stream)
```

# `add`

```elixir
@spec add(t(), Tempus.Slot.t(), keyword()) :: t()
```

Adds another slot to the slots collection backed by stream.

Joins slots intersecting with the new one, if any.

### Example

    iex> import Tempus.Slots.Stream, only: [slots: 0]
    iex> Tempus.Slots.Stream.add(slots(), Tempus.Slot.wrap(~D|2020-08-07|)) |> Enum.to_list()
    [%Tempus.Slot{from: ~U[2020-08-07 00:00:00.000000Z], to: ~U[2020-08-08 00:00:00.000000Z], from_open: false, to_open: true}]

    iex> %Tempus.Slots.Stream{}
    ...> |> Tempus.Slots.Stream.add(Tempus.Slot.wrap(~D|2020-08-07|))
    ...> |> Tempus.Slots.Stream.add(Tempus.Slot.wrap(~D|2020-08-10|))
    ...> |> Tempus.Slots.Stream.add(%Tempus.Slot{
    ...>       from: ~U|2020-08-07 01:00:00Z|, to: ~U|2020-08-08 01:00:00Z|})
    ...> |> Enum.to_list()
    [%Tempus.Slot{from: ~U[2020-08-07 00:00:00.000000Z], to: ~U[2020-08-08 01:00:00Z], from_open: false, to_open: true},
     %Tempus.Slot{from: ~U[2020-08-10 00:00:00.000000Z], to: ~U[2020-08-11 00:00:00.000000Z], from_open: false, to_open: true}]

# `inverse`

Inverses `Slots` returning the new `Slots` instance with slots set where
  there were blanks.

### Example

    iex> [
    ...>   Tempus.Slot.wrap(~D|2020-08-07|),
    ...>   Tempus.Slot.wrap(~D|2020-08-08|),
    ...>   Tempus.Slot.wrap(~D|2020-08-10|),
    ...>   Tempus.Slot.wrap(~D|2020-08-12|)
    ...> ] |> Enum.into(%Tempus.Slots.List{})
    ...> |> Tempus.Slots.List.inverse()
    %Tempus.Slots.List{slots: [
      %Tempus.Slot{from: nil, to: ~U[2020-08-07 00:00:00.000000Z], from_open: true, to_open: true},
      %Tempus.Slot{from: ~U[2020-08-09 00:00:00.000000Z], to: ~U[2020-08-10 00:00:00.000000Z], from_open: false, to_open: true},
      %Tempus.Slot{from: ~U[2020-08-11 00:00:00.000000Z], to: ~U[2020-08-12 00:00:00.000000Z], from_open: false, to_open: true},
      %Tempus.Slot{from: ~U[2020-08-13 00:00:00.000000Z], to: nil, from_open: false, to_open: true}]}

    iex> [
    ...>   %Tempus.Slot{to: ~U[2020-08-09 00:00:00.000000Z], from_open: false, to_open: true},
    ...>   Tempus.Slot.wrap(~D|2020-08-10|),
    ...>   %Tempus.Slot{from: ~U[2020-08-12 00:00:00.000000Z], from_open: false, to_open: true}
    ...> ] |> Enum.into(%Tempus.Slots.List{})
    ...> |> Tempus.Slots.List.inverse()
    %Tempus.Slots.List{slots: [
      %Tempus.Slot{from: ~U[2020-08-09 00:00:00.000000Z], to: ~U[2020-08-10 00:00:00.000000Z], from_open: false, to_open: true},
      %Tempus.Slot{from: ~U[2020-08-11 00:00:00.000000Z], to: ~U[2020-08-12 00:00:00.000000Z], from_open: false, to_open: true}
    ]}

# `iterate`

```elixir
@spec iterate(Tempus.Slot.origin(), (Tempus.Slot.t() -&gt; Tempus.Slot.t()),
  join: nil | boolean() | pos_integer(),
  return_as: :slots | :stream
) :: t() | Tempus.Slots.t(Tempus.Slots.Stream)
```

Produces a stream of slots wrapped in `Tempus.Slots.Stream`, ensuring the order
  of elements emitted.

### Options

- `join` (_default:_ `false`) — `nil | boolean() | non_neg_integer()` specifies how
  slots are to be joined
- `return_as` (_default:_ `:stream`) — `:stream | :slots` if slots, the function returns
  the `t:Tempus.Slots.t/0` instance backed up by produced `t:Temput.Slots.Stream.t/0`

By default, slots will not be joined. Pass `true` to join if they are 1μsec aside,
  or an integer specifying the number of μsecs to join.

### Examples

    iex> import Tempus.Sigils
    ...> Tempus.Slots.Stream.iterate(~D|2024-01-20|,
    ...>   &Tempus.Slot.shift(&1, by: rem(&1.from.day, 2) + 1, unit: :day),
    ...>   join: true) |> Enum.take(2)
    [%Tempus.Slot{from: ~U[2024-01-20 00:00:00.000000Z], to: ~U[2024-01-22 00:00:00.000000Z], from_open: false, to_open: true},
     %Tempus.Slot{from: ~U[2024-01-23 00:00:00.000000Z], to: ~U[2024-01-24 00:00:00.000000Z], from_open: false, to_open: true}]

# `merge`

```elixir
@spec merge(t(), Tempus.Slots.container(), keyword()) :: t()
```

Merges `other` into `this` slots instance. `other` might be `Enum` _or_ `Stream`.
When `other` is a stream, it gets terminated immediately after the last element
in `this`.

### Examples

    iex> import Tempus.Slots.Stream, only: [slots: 0]
    iex> slots = [
    ...>   Tempus.Slot.wrap(~D|2020-08-07|),
    ...>   Tempus.Slot.wrap(~D|2020-08-10|)
    ...> ] |> Enum.into(slots())
    iex> other = [
    ...>   %Tempus.Slot{from: ~U|2020-08-07 23:00:00Z|, to: ~U|2020-08-08 12:00:00Z|},
    ...>   %Tempus.Slot{from: ~U|2020-08-12 23:00:00Z|, to: ~U|2020-08-12 23:30:00Z|}
    ...> ] |> Enum.into(slots())
    iex> slots |> Tempus.Slots.Stream.merge(other) |> Enum.to_list()
    [%Tempus.Slot{from: ~U[2020-08-07 00:00:00.000000Z], to: ~U[2020-08-08 12:00:00Z], from_open: false, to_open: true},
     %Tempus.Slot{from: ~U[2020-08-10 00:00:00.000000Z], to: ~U[2020-08-11 00:00:00.000000Z], from_open: false, to_open: true},
     %Tempus.Slot{from: ~U[2020-08-12 23:00:00Z], to: ~U[2020-08-12 23:30:00Z], from_open: false, to_open: true}]

# `new`

Creates the new instance of `Tempus.Slots.Stream` struct. One usually does not need to call this function directly.

# `recurrent`

```elixir
@spec recurrent(
  origin :: nil | Date.t(),
  from :: recurrent_time,
  to :: recurrent_time
) ::
  Tempus.Slots.t(Tempus.Slots.Stream)
  | {:error,
     :incompatible_calendars
     | :invalid_format
     | :invalid_input
     | :invalid_time
     | :time_zone_not_found
     | :utc_only_time_zone_database}
  | {:ambiguous, DateTime.t(), DateTime.t()}
  | {:gap, DateTime.t(), DateTime.t()}
when recurrent_time: {Calendar.day_of_week(), Time.t(), Calendar.time_zone()}
```

 Produces a stream or recurrent weekly slots, given starting and ending `{dow, time, zone}` triples.

 ### Examples
     iex> import Tempus.Sigils
     ...> Tempus.Slots.Stream.recurrent(Date.from_iso8601!("2024-01-22"), {7, "15:00:00", "Etc/UTC"}, {3, "01:00:00", "Etc/UTC"}) |> Enum.take(3)
     [~I(2024-01-21T15:00:00.000000Z → 2024-01-24T01:00:00.000000Z),
      ~I(2024-01-28T15:00:00.000000Z → 2024-01-31T01:00:00.000000Z),
      ~I(2024-02-04T15:00:00.000000Z → 2024-02-07T01:00:00.000000Z)]
     ...> Tempus.Slots.Stream.recurrent(Date.from_iso8601!("2024-01-22"), {1, "15:00:00", "Etc/UTC"}, {3, "01:00:00", "Etc/UTC"}) |> Enum.take(3)
     [~I(2024-01-22T15:00:00.000000Z → 2024-01-24T01:00:00.000000Z),
      ~I(2024-01-29T15:00:00.000000Z → 2024-01-31T01:00:00.000000Z),
      ~I(2024-02-05T15:00:00.000000Z → 2024-02-07T01:00:00.000000Z)]
     ...> Tempus.Slots.Stream.recurrent(Date.from_iso8601!("2024-01-22"), {1, "15:00:00", "Etc/UTC"}, {1, "15:00:00", "Australia/Sydney"}) |> Enum.take(3)
     [~I(2024-01-15T15:00:00.000000Z → 2024-01-22T15:00:00.000000+11:00),
      ~I(2024-01-22T15:00:00.000000Z → 2024-01-29T15:00:00.000000+11:00),
      ~I(2024-01-29T15:00:00.000000Z → 2024-02-05T15:00:00.000000+11:00)]
     ...> Tempus.Slots.Stream.recurrent(Date.from_iso8601!("2024-01-22"), {1, "09:00:00", "Australia/Sydney"}, {7, "23:00:00", "Etc/UTC"}) |> Enum.take(3)
     [%Tempus.Slot{from: DateTime.new!(~D"2024-01-29", ~T"09:00:00.000000", "Australia/Sydney"), to: ~U|2024-01-28T23:00:00.000000Z|, from_open: false, to_open: true},
      %Tempus.Slot{from: DateTime.new!(~D"2024-02-05", ~T"09:00:00.000000", "Australia/Sydney"), to: ~U|2024-02-04T23:00:00.000000Z|, from_open: false, to_open: true},
      %Tempus.Slot{from: DateTime.new!(~D"2024-02-12", ~T"09:00:00.000000", "Australia/Sydney"), to: ~U|2024-02-11T23:00:00.000000Z|, from_open: false, to_open: true}]
     ...> Tempus.Slots.Stream.recurrent(Date.from_iso8601!("2024-03-28"), {1, "09:00:00", "Australia/Sydney"}, {7, "23:00:00", "Etc/UTC"}) |> Enum.take(2)
     [%Tempus.Slot{from: DateTime.new!(~D"2024-04-01", ~T"09:00:00.000000", "Australia/Sydney"), to: ~U|2024-03-31T23:00:00.000000Z|, from_open: false, to_open: true},
      %Tempus.Slot{from: DateTime.new!(~D"2024-04-08", ~T"09:00:00.000000", "Australia/Sydney"), to: ~U|2024-04-07T23:00:00.000000Z|, from_open: false, to_open: true}]

# `split`

```elixir
@spec split(t(), Tempus.Slots.locator(), keyword()) ::
  {Enumerable.t(Tempus.Slot.t()), Enumerable.t(Tempus.Slot.t())}
```

Splits the slots by the pivot given as a `Slot.t` or as a function.

To keep it consistent, the function actually does _split it until_,
which is intuitive when the pivot is given and kinda counter-intuitive
when the _locator_ function is given.

See the examples below to grasp the reasoning behind this
architectural decision.

### Examples

    iex> import Tempus.Guards
    ...> import Tempus.Sigils
    iex> slots =
    ...> [~D|2020-08-07|, ~D|2020-08-08|, ~D|2020-08-10|, ~D|2020-08-12|]
    ...> |> Enum.into(%Tempus.Slots.Stream{})
    iex> slots |> Tempus.Slots.Stream.split(~U|2020-08-09T12:00:00Z|) |> Tuple.to_list() |> Enum.map(&Enum.to_list/1)
    [
      [%Tempus.Slot{from: ~U[2020-08-07 00:00:00.000000Z], to: ~U[2020-08-08 00:00:00.000000Z], from_open: false, to_open: true}, %Tempus.Slot{from: ~U[2020-08-08 00:00:00.000000Z], to: ~U[2020-08-09 00:00:00.000000Z], from_open: false, to_open: true}],
      [%Tempus.Slot{from: ~U[2020-08-10 00:00:00.000000Z], to: ~U[2020-08-11 00:00:00.000000Z], from_open: false, to_open: true}, %Tempus.Slot{from: ~U[2020-08-12 00:00:00.000000Z], to: ~U[2020-08-13 00:00:00.000000Z], from_open: false, to_open: true}]
    ]
    iex> slots
    ...> |> Tempus.Slots.Stream.split(&is_slot_coming_before(Tempus.Slot.wrap(~U|2020-08-09T12:00:00Z|), &1))
    ...> |> Tuple.to_list()
    ...> |> Enum.map(&Enum.to_list/1)
    [
      [%Tempus.Slot{from: ~U[2020-08-07 00:00:00.000000Z], to: ~U[2020-08-08 00:00:00.000000Z], from_open: false, to_open: true}, %Tempus.Slot{from: ~U[2020-08-08 00:00:00.000000Z], to: ~U[2020-08-09 00:00:00.000000Z], from_open: false, to_open: true}],
      [%Tempus.Slot{from: ~U[2020-08-10 00:00:00.000000Z], to: ~U[2020-08-11 00:00:00.000000Z], from_open: false, to_open: true}, %Tempus.Slot{from: ~U[2020-08-12 00:00:00.000000Z], to: ~U[2020-08-13 00:00:00.000000Z], from_open: false, to_open: true}]
    ]

---

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