# `Agenda.Track`
[🔗](https://github.com/elixir-tempo/agenda/blob/v0.3.0/lib/agenda/track.ex#L1)

A family of sessions constrained against *each other*.

Every other constraint in this library relates a session to a
resource. A track is the first that does not: it says these sessions
share an audience, so no two of them may run at once, and — if asked
— a delegate must be able to walk between consecutive ones in the gap
between them.

**Not overlapping is intrinsic, not an option.** A set of sessions
that may collide with each other is just a list; what makes a track a
track is that it cannot clash with itself. `reachable/2` is the
optional extra.

The word is deliberately not "stream": Elixir's `Stream` owns that
name and a conference stream would shadow it at every call site.

### Example

    iex> import Tempo.Sigils
    iex> keynote = Agenda.session("Keynote", duration: ~o"PT1H")
    iex> deep_dive = Agenda.session("OTP internals", duration: ~o"PT1H")
    iex> {:ok, track} =
    ...>   Agenda.track("Elixir", of: [keynote, deep_dive])
    ...>   |> Agenda.Track.reachable(within: ~o"PT10M")
    iex> {length(track.sessions), track.reachable_within}
    {2, ~o"PT10M"}

> *"No two talks in the Elixir track may overlap, and a delegate must
> be able to walk between consecutive ones inside the ten-minute
> break."*

### In iCalendar

iCalendar can say which sessions make up a track, but not what makes
them one. An export writes two RFC 9253 properties on each of the
track's sessions:

* **`REFID`** — the track's name, as text: the key that groups
  associated components, as an itinerary groups its legs.

* **`CONCEPT`** — a URI for the track as a classification, such as
  `https://example.com/tracks/elixir`, which the adapter mints from
  the track's name.

Both name the grouping, which is why the word stays `Track`. The
rule is lost: iCalendar has no way to say that a track's sessions
must not overlap each other, nor that a delegate must be able to
walk between them (`reachable/2`). A track read back from iCalendar
is a group of sessions that may clash.

# `t`

```elixir
@type t() :: %Agenda.Track{
  name: String.t(),
  reachable_within: Agenda.Availability.pattern() | nil,
  sessions: [Agenda.Session.t()]
}
```

Sessions that cannot clash with each other.

# `add`

```elixir
@spec add(t(), Agenda.Session.t()) :: t()
```

Add a session to the track.

### Arguments

* `track` is a `t:t/0`.

* `session` is a `t:Agenda.Session.t/0`.

### Returns

* the track, with the session added.

### Examples

    iex> session = Agenda.session("Keynote")
    iex> track = Agenda.Track.add(Agenda.Track.new("Elixir"), session)
    iex> Enum.map(track.sessions, & &1.name)
    ["Keynote"]

# `new`

```elixir
@spec new(String.t(), keyword()) :: t()
```

Build a track.

### Arguments

* `name` is the track's name.

* `options` is a keyword list of options.

### Options

* `:of` is the list of `t:Agenda.Session.t/0` in the track. The
  default is `[]`.

### Returns

* a `t:t/0`.

### Examples

    iex> Agenda.Track.new("Elixir").name
    "Elixir"

# `reachable`

```elixir
@spec reachable(t(), keyword()) :: {:ok, t()} | {:error, {:missing_option, :within}}
```

Require that a delegate can get between consecutive sessions.

Two sessions running back to back in rooms twenty minutes apart, with
a ten-minute break, cannot both be attended. This is what makes the
place tree earn its keep — the journey is derived from where the
rooms are, not configured per pair.

### Arguments

* `track` is a `t:t/0`.

* `options` is a keyword list of options.

### Options

* `:within` is the longest journey a delegate is expected to make, a
  duration.

### Returns

* `{:ok, track}` with the reachability requirement set, or

* `{:error, {:missing_option, :within}}`.

### Examples

    iex> import Tempo.Sigils
    iex> track = Agenda.Track.new("Elixir")
    iex> {:ok, track} = Agenda.Track.reachable(track, within: ~o"PT10M")
    iex> track.reachable_within
    ~o"PT10M"

    iex> Agenda.Track.reachable(Agenda.Track.new("Elixir"), [])
    {:error, {:missing_option, :within}}

# `session_names`

```elixir
@spec session_names(t()) :: [String.t()]
```

The names of the sessions in `track`.

### Arguments

* `track` is a `t:t/0`.

### Returns

* the session names, in track order.

### Examples

    iex> track = Agenda.track("Elixir", of: [Agenda.session("Keynote")])
    iex> Agenda.Track.session_names(track)
    ["Keynote"]

---

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