# `Finitomata.Transition`
[🔗](https://github.com/am-kantox/finitomata/blob/v0.35.0/lib/finitomata/transition.ex#L1)

The internal representation of `Transition`.

It includes `from` and `to` states, and the `event`, all represented as atoms.

# `event`

```elixir
@type event() :: atom()
```

The event in FSM

# `event_kind`

```elixir
@type event_kind() :: :soft | :hard | :normal
```

The kind of event

# `state`

```elixir
@type state() :: atom()
```

The state of FSM

# `t`

```elixir
@type t() :: %Finitomata.Transition{
  from: state(),
  to: state() | [state()],
  event: event()
}
```

The transition is represented by `from` and `to` states _and_ the `event`.

# `allowed`

```elixir
@spec allowed([t()],
  from: state(),
  to: state(),
  with: event(),
  as: :states | :transitions
) :: [
  {state(), state(), event()}
]
```

Returns the list of all the transitions, matching the options.

Used internally for the validations.

    iex> {:ok, transitions} =
    ...>   Finitomata.Mermaid.parse(
    ...>     "idle --> |to_s1| s1\n" <>
    ...>     "s1 --> |to_s2| s2\n" <>
    ...>     "s1 --> |to_s3| s3\n" <>
    ...>     "s2 --> |to_s3| s3")
    ...> Finitomata.Transition.allowed(transitions, to: [:idle, :*])
    [{:*, :idle, :__start__}, {:s3, :*, :__end__}]
    iex> Finitomata.Transition.allowed(transitions, from: :s1)
    [{:s1, :s2, :to_s2}, {:s1, :s3, :to_s3}]
    iex> Finitomata.Transition.allowed(transitions, from: :s1, to: :s3)
    [{:s1, :s3, :to_s3}]
    iex> Finitomata.Transition.allowed(transitions, from: :s1, with: :to_s3)
    [{:s1, :s3, :to_s3}]
    iex> Finitomata.Transition.allowed(transitions, from: :s2, with: :to_s2)
    []

# `allowed`

```elixir
@spec allowed([t()], state(), event()) :: [state()]
```

Returns the list of all the transitions, matching the `from` state and the `event`.

Used internally for the validations.

    iex> {:ok, transitions} =
    ...>   Finitomata.PlantUML.parse("[*] --> s1 : foo\ns1 --> s2 : ok\ns2 --> [*] : ko")
    ...> Finitomata.Transition.allowed(transitions, :s1, :foo)
    [:s2]
    ...> Finitomata.Transition.allowed(transitions, :s1, :*)
    []

# `allowed?`

```elixir
@spec allowed?([t()], state(), state()) :: boolean()
```

Returns `true` if the transition `from` → `to` is allowed, `false` otherwise.

    iex> {:ok, transitions} =
    ...>   Finitomata.PlantUML.parse("[*] --> s1 : foo\ns1 --> s2 : ok\ns2 --> [*] : ko")
    ...> Finitomata.Transition.allowed?(transitions, :s1, :s2)
    true
    ...> Finitomata.Transition.allowed?(transitions, :s1, :*)
    false

# `ambiguous`

```elixir
@spec ambiguous([t()]) :: [{state(), {event(), state()}}]
```

Returns keyword list of
  `{Finitomata.Transition.state(), [Finitomata.Transition.event()]}` for transitions
  which do not have a determined _to_ state.

Used internally for the validations.

    iex> {:ok, transitions} =
    ...>   Finitomata.Mermaid.parse(
    ...>     "idle --> |to_s1| s1\n" <>
    ...>     "s1 --> |to_s2| s2\n" <>
    ...>     "s1 --> |to_s3| s3\n" <>
    ...>     "s2 --> |to_s1| s3\n" <>
    ...>     "s2 --> |ambiguous| s3\n" <>
    ...>     "s2 --> |ambiguous| s4\n" <>
    ...>     "s3 --> |determined| s3\n" <>
    ...>     "s3 --> |determined| s4\n")
    ...> Finitomata.Transition.ambiguous(transitions)
    [s3: {:determined, [:s3, :s4]}, s2: {:ambiguous, [:s3, :s4]}]

# `continuation`

```elixir
@spec continuation(:states | :transitions, state(), [t()]) ::
  nil | [Finitomata.Transition.Path.t()] | [t()]
```

Returns the continuation from the state given which inevitably lead to other state(s).

All the transitions from this state are hard (ending with `!`,)
  which makes the _FSM_ to go through all these states in `:continue` callbacks.

    iex> {:ok, transitions} =
    ...>   Finitomata.PlantUML.parse("[*] --> entry : start\nentry --> exit : go!\nexit --> done : finish\ndone --> [*] : terminate")
    ...> Finitomata.Transition.continuation(:entry, Finitomata.Transition.hard(:transitions, transitions))
    [%Finitomata.Transition.Path{from: :entry, to: :exit, path: [go!: :exit]}]

# `count`

```elixir
@spec count(:states | :transitions, [t()]) :: non_neg_integer()
```

# `determined`

```elixir
@spec determined([t()]) :: [{state(), {event(), state()}}]
```

Returns keyword list of
  `{Finitomata.Transition.state(), Finitomata.Transition.event()}` tuples
  for determined transition from the current state.

The transition is determined, if it is the only transition allowed from the state.

Used internally for the validations.

    iex> {:ok, transitions} =
    ...>   Finitomata.Mermaid.parse(
    ...>     "idle --> |to_s1| s1\n" <>
    ...>     "s1 --> |to_s2| s2\n" <>
    ...>     "s1 --> |to_s3| s3\n" <>
    ...>     "s2 --> |to_s1| s3\n" <>
    ...>     "s2 --> |ambiguous| s3\n" <>
    ...>     "s2 --> |ambiguous| s4\n" <>
    ...>     "s3 --> |determined| s3\n" <>
    ...>     "s3 --> |determined| s4\n")
    ...> Finitomata.Transition.determined(transitions)
    [s4: :__end__, s3: :determined, idle: :to_s1]

# `determined`

```elixir
@spec determined([t()], state()) :: {:ok, {event(), state()}} | :error
```

Returns `{:ok, {event(), state()}}` tuple if there is a determined transition
  from the current state, `:error` otherwise.

The transition is determined, if it is the only transition allowed from the state.

Used internally for the validations.

    iex> {:ok, transitions} =
    ...>   Finitomata.Mermaid.parse(
    ...>     "idle --> |to_s1| s1\n" <>
    ...>     "s1 --> |to_s2| s2\n" <>
    ...>     "s1 --> |to_s3| s3\n" <>
    ...>     "s2 --> |to_s3| s3")
    ...> Finitomata.Transition.determined(transitions, :s1)
    :error
    iex> Finitomata.Transition.determined(transitions, :s2)
    {:ok, {:to_s3, :s3}}
    iex> Finitomata.Transition.determined(transitions, :s3)
    {:ok, {:__end__, :*}}

# `entry`

```elixir
@spec entry(:state | :transition, [t()]) :: state()
```

Returns the state _after_ starting one, so-called `entry` state.

    iex> {:ok, transitions} =
    ...>   Finitomata.PlantUML.parse("[*] --> entry : foo\nentry --> exit : go\nexit --> [*] : terminate")
    ...> Finitomata.Transition.entry(transitions)
    :entry

# `event_kind`

```elixir
@spec event_kind(event() | t()) :: event_kind()
```

Returns the kind of event.

If event ends up with an exclamation sign, it’s `:hard`, meaning the respective
  transition would be initiated automatically when the `from` state of such a transition
  is reached.

If event ends up with a question mark, it’s `:soft`, meaning no error would have
  been reported in a case transition fails.

Otherwise the event is `:normal`.

    iex> {:ok, [_, hard, _]} =
    ...>   Finitomata.PlantUML.parse("[*] --> entry : foo\nentry --> exit : go!\nexit --> [*] : terminate")
    ...> Finitomata.Transition.event_kind(hard)
    :hard

# `events`

```elixir
@spec events([t()], boolean()) :: [state()]
```

Returns the not ordered list of events, including or excluding
  the internal starting and ending transitions `:__start__` and `__end__`
  according to the second argument.

    iex> {:ok, transitions} =
    ...>   Finitomata.Mermaid.parse("s1 --> |ok| s2\ns1 --> |ko| s3")
    ...> Finitomata.Transition.events(transitions, true)
    [:ok, :ko]
    ...> Finitomata.Transition.events(transitions)
    [:__start__, :ok, :ko, :__end__]

# `exit`

```elixir
@spec exit(:states | :transitions, [t()]) :: [state()]
```

Returns the states _before_ ending one, so-called `exit` states.

    iex> {:ok, transitions} =
    ...>   Finitomata.Mermaid.parse(
    ...>     "entry --> |process| processing\nprocessing --> |ok| success\nprocessing --> |ko| error"
    ...>   )
    ...> Finitomata.Transition.exit(transitions)
    [:error, :success]

# `exiting`

```elixir
@spec exiting(:states | :transitions, [t()]) ::
  Enumerable.t([t()]) | [Finitomata.Transition.Path.t()]
```

Returns all the states which inevitably lead to the ending one.

All the transitions from these states to the ending one are hard (ending with `!`,)
  which makes the _FSM_ to go through all these states in `:continue` callbacks.

    iex> {:ok, transitions} =
    ...>   Finitomata.PlantUML.parse("[*] --> entry : start\nentry --> exit : go!\nexit --> [*] : terminate")
    ...> Finitomata.Transition.exiting(transitions)
    [%Finitomata.Transition.Path{from: :entry, to: :*, path: [go!: :exit, terminate: :*]}]

# `guess_next_state`

```elixir
@spec guess_next_state([t()], state(), event(), Finitomata.State.payload()) ::
  Finitomata.transition_resolution()
```

Tries to guess the next state based on current state and event

    iex> {:ok, transitions} = Finitomata.Mermaid.parse("s1 --> |ok| s2\ns1 --> |ko| s3")
    ...> Finitomata.Transition.guess_next_state(transitions, :s1, :ok, %{})
    {:ok, :s2, %{}}

    iex> {:ok, transitions} = Finitomata.Mermaid.parse("s1 --> |ok| s2\ns1 --> |ok| s3")
    ...> Finitomata.Transition.guess_next_state(transitions, :s1, :ok, %{})
    {:error, {:ambiguous_transition, {:s1, :ok}, [:s2, :s3]}}

# `hard`

```elixir
@spec hard(:states | :transitions, [t()]) ::
  Enumerable.t(t()) | [Finitomata.Transition.Path.t()]
```

Returns all the hard transitions which inevitably lead to the next state
  (events ending with an exclamation sign,)
  which makes the _FSM_ to go to the next state with `:continue` callback.

    iex> {:ok, transitions} =
    ...>   Finitomata.PlantUML.parse("[*] --> entry : start\nentry --> exit : go!\nexit --> [*] : terminate")
    ...> Finitomata.Transition.hard(transitions)
    [entry: :go!]
    ...> Finitomata.Transition.hard(:transitions, transitions)
    [%Finitomata.Transition{from: :entry, to: :exit, event: :go!}]

# `loops`

```elixir
@spec loops(:states | :transitions, [t()]) ::
  Enumerable.t(t()) | [Finitomata.Transition.Path.t()]
```

Returns all the loops aka internal paths where starting and ending states are the same one.

    iex> {:ok, transitions} =
    ...>   Finitomata.PlantUML.parse("[*] --> s1 : foo\ns1 --> s2 : ok\ns2 --> s1 : ok\ns2 --> [*] : ko")
    ...> Finitomata.Transition.loops(transitions)
    [%Finitomata.Transition.Path{from: :s1, to: :s1, path: [ok: :s2, ok: :s1]},
     %Finitomata.Transition.Path{from: :s2, to: :s2, path: [ok: :s1, ok: :s2]}]

# `paths`

```elixir
@spec paths(:states | :transitions, [t()], state(), state()) ::
  Enumerable.t(t()) | [Finitomata.Transition.Path.t()]
```

Returns all the paths from starting to ending state.

    iex> {:ok, transitions} =
    ...>   Finitomata.PlantUML.parse("[*] --> s1 : foo\ns1 --> s2 : ok\ns1 --> s3 : ok\ns2 --> [*] : ko\ns3 --> [*] : ko")
    ...> Finitomata.Transition.paths(transitions)
    [%Finitomata.Transition.Path{from: :*, to: :*, path: [foo: :s1, ok: :s2, ko: :*]},
     %Finitomata.Transition.Path{from: :*, to: :*, path: [foo: :s1, ok: :s3, ko: :*]}]

# `responds?`

```elixir
@spec responds?([t()], state(), event()) :: boolean()
```

Returns `true` if the state `from` hsa an outgoing transition with `event`, false otherwise.

    iex> {:ok, transitions} =
    ...>   Finitomata.PlantUML.parse("[*] --> s1 : foo\ns1 --> s2 : ok\ns2 --> [*] : ko")
    ...> Finitomata.Transition.responds?(transitions, :s1, :ok)
    true
    ...> Finitomata.Transition.responds?(transitions, :s1, :ko)
    false

# `shortest_paths`

```elixir
@spec shortest_paths(:states | :transitions, [t()], state(), state(), boolean()) ::
  Enumerable.t(t()) | [Finitomata.Transition.Path.t()]
```

Returns the shortest path from starting to ending state.

    iex> {:ok, transitions} =
    ...>   Finitomata.PlantUML.parse("[*] --> s1 : foo\ns1 --> s2 : ok\ns1 --> s3 : ok\ns2 --> [*] : ko\ns3 --> s4 : step\ns4 --> [*] : ko")
    ...> Finitomata.Transition.shortest_paths(transitions)
    [%Finitomata.Transition.Path{from: :*, to: :*, path: [foo: :s1, ok: :s2, ko: :*]}]

# `states`

```elixir
@spec states([t()], boolean()) :: [state()]
```

Returns the not ordered list of states, including or excluding
  the starting and ending states `:*` according to the second argument.

    iex> {:ok, transitions} =
    ...>   Finitomata.PlantUML.parse("[*] --> s1 : foo\ns1 --> s2 : ok\ns2 --> [*] : ko")
    ...> Finitomata.Transition.states(transitions, true)
    [:s1, :s2]
    ...> Finitomata.Transition.states(transitions)
    [:*, :s1, :s2]

# `steps`

```elixir
@spec steps([t()], state(), state()) :: non_neg_integer()
```

Returns the minimal number of steps required to get from `from` state to `to` state,
  including hard transitions

# `steps_handled`

```elixir
@spec steps_handled([t()], state(), state()) :: non_neg_integer()
```

Returns the minimal number of steps required to get from `from` state to `to` state,
  omitting hard transitions

# `straight_paths`

```elixir
@spec straight_paths(:states | :transitions, [t()], state(), state()) ::
  Enumerable.t(t()) | Finitomata.Transition.Path.t()
```

Returns the straight paths from starting to ending state.

    iex> {:ok, transitions} =
    ...>   Finitomata.PlantUML.parse("[*] --> s1 : foo\ns1 --> s2 : ok\ns1 --> s3 : ok\ns2 --> s2 : loop\ns2 --> [*] : ko\ns3 --> s4 : step\ns4 --> [*] : ko")
    ...> Finitomata.Transition.straight_paths(transitions)
    [%Finitomata.Transition.Path{from: :*, to: :*, path: [foo: :s1, ok: :s2, ko: :*]},
     %Finitomata.Transition.Path{from: :*, to: :*, path: [foo: :s1, ok: :s3, step: :s4, ko: :*]}]

---

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