1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// Copyright 2019 Andrew Thomas Christensen
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the
// MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option. This file may not be copied,
// modified, or distributed except according to those terms.

use crate::{Automaton, Mode};

/// A meta-`trait` defining the common `Base` type and `Mode` storage conventions used by a related group of `Mode`
/// implementations. `Mode`s can **only** transition to other `Mode`s within the same `Family`, i.e. where both `Mode`s
/// share the same `Family` associated `type`.
/// 
/// # The `Base` type
/// The `Base` associated type may be either a `dyn Trait` or a concrete type that represents how the current `Mode` can
/// be accessed from outside the `Automaton`.
/// 
/// If given some `dyn Trait`, **only** functions common to the `trait` interface will be callable on the current
/// `Mode`, as the `Automaton` will **only** allow it to be borrowed via a `trait` reference. However, the `Automaton`
/// will allow swapping between different concrete implementations of this common interface, provided that the `Mode`
/// associated `type` is a pointer of some kind (e.g. `Box`) and they share the same `Family` associated `type`.
/// 
/// If given a concrete type, e.g. an `enum` or `struct`, **all** functions and members defined on the inner type will
/// be accessible from outside the `Automaton`. However, this also implies that **all** states in the `Automaton` will
/// be represented by instances of this same concrete type.
/// 
/// # Usage
/// To define a new `Family` of `Mode`s, simply define a new unit `struct` and `impl Family` for it. This will allow the
/// associated `type`s within `Family` to be defined for that specific `struct`, with the `struct` representing the
/// common usage pattern of all `Mode`s with a `Family` associated `type` equal to that `struct`. (See examples below.)
/// 
/// ## A `Family` where `Base` is a concrete type
/// ```
/// use mode::{Family, Mode};
/// 
/// enum SomeMode { A, B, C }
/// 
/// impl Mode for SomeMode {
///     type Family = SomeFamily;
/// }
/// 
/// impl SomeMode {
///     pub fn swap(self) -> Self {
///         match self {
///             Self::A => Self::B,
///             Self::B => Self::C,
///             Self::C => Self::A,
///         }
///     }
/// }
/// 
/// struct SomeFamily;
/// 
/// impl Family for SomeFamily {
///     type Base = SomeMode; // All SomeFamily Modes will be visible as SomeMode from outside the Automaton.
///     type Mode = SomeMode; // The current Mode in the Automaton will be stored as a SomeMode in-place.
/// }
/// ```
/// 
/// ## A `Family` where `Base` is a `dyn Trait`
/// ```
/// use mode::{Mode, Family};
/// 
/// trait SomeTrait : Mode<Family = SomeFamily> {
///     // ...
/// }
/// 
/// struct SomeFamily;
/// 
/// impl Family for SomeFamily {
///     type Base = dyn SomeTrait; // All SomeFamily Modes will expose their SomeTrait interface via the Automaton.
///     type Mode = Box<dyn SomeTrait>; // The current Mode in the Automaton will be stored as a Box<dyn SomeTrait>.
/// }
/// ```
/// 
pub trait Family {
    /// The user-facing interface for the `Mode` that will be exposed via the `Automaton::borrow_mode()` and
    /// `Automaton::borrow_mode_mut()` functions. This can be either a concrete type or a `dyn Trait`, depending on
    /// whether `Self::Mode` is a pointer type or not.
    /// 
    /// **NOTE:** This is **not** the actual type that will be stored in `Automaton`. This is just the public interface
    /// for the current `Mode` that will be exposed by the `Automaton`.
    /// 
    type Base : ?Sized;

    /// The actual type that will be stored in `Automaton` and moved into the `Automaton::next()` function. For a
    /// `Family` where `Self::Base` is a **concrete** type, this should be set to the **same** type as `Self::Base`. On
    /// the other hand, if `Self::Base` is a `dyn Trait`, this should usually be set to some pointer type capable of
    /// storing `Self::Base`, e.g. `Box<Self::Base>` or `Rc<Self::Base>`.
    /// 
    type Mode : Mode<Family = Self>;

    /// Convenience function allowing an `Automaton` to be created for this `Family` type. Note that this is shorthand
    /// for `Automaton::new()`, and therefore `Self::Mode` *must* implement `Default`. See
    /// [`Automaton::new()`](struct.Automaton.html#method.new) for more details.
    /// 
    /// # Usage
    /// ```
    /// use mode::*;
    /// # 
    /// # struct SomeFamily;
    /// # impl Family for SomeFamily {
    /// #     type Base = ModeWithDefault;
    /// #     type Mode = ModeWithDefault;
    /// # }
    /// 
    /// struct ModeWithDefault { count : u32 };
    /// 
    /// impl Mode for ModeWithDefault {
    ///     type Family = SomeFamily;
    /// }
    /// 
    /// impl Default for ModeWithDefault {
    ///     fn default() -> Self {
    ///         ModeWithDefault { count: 0 }
    ///     }
    /// }
    /// 
    /// // Create an Automaton with a default Mode.
    /// let mut automaton = SomeFamily::automaton();
    /// ```
    /// 
    fn automaton() -> Automaton<Self>
        where Self::Mode : Default
    {
        Automaton::new()
    }

    /// Convenience function that returns a new `Automaton` for this `Family` type with the specified `mode` as current.
    /// Note that this is shorthand for `Automaton::with_mode()`. See
    /// [`Automaton::with_mode()`](struct.Automaton.html#method.with_mode) for more details.
    /// 
    /// # Usage
    /// ```
    /// use mode::*;
    /// 
    /// struct SomeFamily;
    /// impl Family for SomeFamily {
    ///     type Base = SomeMode;
    ///     type Mode = SomeMode;
    /// }
    /// 
    /// enum SomeMode { A, B, C };
    /// 
    /// impl Mode for SomeMode {
    ///     type Family = SomeFamily;
    /// }
    /// 
    /// // Create an Automaton with A as the initial Mode.
    /// let mut automaton = SomeFamily::automaton_with_mode(SomeMode::A);
    /// ```
    /// 
    fn automaton_with_mode(mode : Self::Mode) -> Automaton<Self> {
        Automaton::with_mode(mode)
    }
}