Skip to main content

inkling/error/runtime/
internal.rs

1//! Internal errors from the `inkling` processing engine.
2
3use std::{error::Error, fmt};
4
5use crate::{
6    error::runtime::InklingError,
7    follow::ChoiceInfo,
8    knot::{Address, AddressKind},
9    node::Stack,
10};
11
12impl Error for InternalError {}
13
14#[derive(Clone, Debug)]
15/// Internal errors from `inkling`.
16///
17/// These are errors which arise when the library produces objects, trees, text
18/// or internal stacks that are inconsistent with each other or themselves.
19///
20/// If the library is well written these should not possibly occur; at least until
21/// this point every part of the internals are fully deterministic. That obviously
22/// goes for a lot of buggy code that has been written since forever, so nothing
23/// unique there.
24///
25/// Either way, all those sorts of errors are encapsulated here. They should never
26/// be caused by invalid user input or Ink files, those errors should be captured
27/// by either the parent [`InklingError`][crate::error::InklingError]
28/// or parsing [`ReadError`][crate::error::ReadError] error structures.
29pub enum InternalError {
30    /// The internal stack of knots is inconsistent or has not been set properly.
31    BadKnotStack(StackError),
32    /// Could not `Process` a line of text into its final form.
33    CouldNotProcess(ProcessError),
34    /// Selected branch index does not exist.
35    IncorrectChoiceIndex {
36        /// Selection index.
37        selection: usize,
38        /// Available choices at the branching point.
39        available_choices: Vec<ChoiceInfo>,
40        /// Index in `stack` where the error occurred.
41        stack_index: usize,
42        /// Stack of choices from the root node to the branching point.
43        stack: Stack,
44    },
45    /// Current stack is not properly representing the graph or has some indexing problems.
46    IncorrectNodeStack(IncorrectNodeStackError),
47    /// Tried to use a variable address as a location.
48    UseOfVariableAsLocation { name: String },
49    /// Tried to use an unvalidated address after the story was parsed.
50    UseOfUnvalidatedAddress { address: Address },
51}
52
53impl_from_error![
54    InternalError;
55    [BadKnotStack, StackError],
56    [CouldNotProcess, ProcessError],
57    [IncorrectNodeStack, IncorrectNodeStackError]
58];
59
60impl fmt::Display for InternalError {
61    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
62        use IncorrectNodeStackError::*;
63        use InternalError::*;
64        use ProcessErrorKind::*;
65        use StackError::*;
66
67        match self {
68            BadKnotStack(err) => match err {
69                BadAddress {
70                    address: Address::Validated(AddressKind::Location { knot, stitch }),
71                } => write!(
72                    f,
73                    "The currently set knot address (knot: {}, stitch: {}) does not \
74                     actually represent a knot in the story",
75                    knot, stitch
76                ),
77                BadAddress { address } => write!(
78                    f,
79                    "Tried to used a non-validated or non-location `Address` ('{:?}') in \
80                     a function",
81                    address
82                ),
83                NoLastChoices => write!(
84                    f,
85                    "Tried to follow with a choice but the last set of presented choices has \
86                     not been saved"
87                ),
88                NoRootKnot { knot_name } => write!(
89                    f,
90                    "After reading a set of knots, the root knot with name {} \
91                     does not exist in the set",
92                    knot_name
93                ),
94                NoStack => write!(
95                    f,
96                    "There is no currently set knot or address to follow the story from"
97                ),
98            },
99            CouldNotProcess(ProcessError { kind }) => match kind {
100                InvalidAlternativeIndex => write!(
101                    f,
102                    "When processing an alternative, an invalid index was used to pick an item"
103                ),
104                InklingError(err) => write!(f, "{}", err),
105            },
106            IncorrectChoiceIndex {
107                selection,
108                ref available_choices,
109                stack_index,
110                ref stack,
111            } => write!(
112                f,
113                "Tried to resume after a choice was made but the chosen index does not exist \
114                 in the set of choices. Somehow a faulty set of choices was created from this \
115                 branch point and returned upwards, the stack is wrong, or the wrong set of \
116                 choices was used elsewhere in the preparation of the choice list. \
117                 Selection index: {}, number of branches: {} \
118                 (node level: {}, stack: {:?})",
119                selection,
120                available_choices.len(),
121                stack_index,
122                stack
123            ),
124            IncorrectNodeStack(err) => match err {
125                EmptyStack => write!(f, "Tried to advance through a knot with an empty stack"),
126                ExpectedBranchingPoint { stack_index, stack } => {
127                    let item_number = stack[*stack_index];
128
129                    write!(
130                        f,
131                        "While resuming a follow the stack found a regular line where \
132                         it expected a branch point to nest deeper into. \
133                         The stack has been corrupted. \
134                         (stack level: {}, item number: {}, stack: {:?}",
135                        stack_index, item_number, stack
136                    )
137                }
138                MissingBranchIndex { stack_index, stack } => write!(
139                    f,
140                    "While resuming a follow the stack did not contain an index to \
141                         select a branch with from a set of choices. The stack has been \
142                         corrupted.
143                         (stack level: {}, attempted index: {}, stack: {:?}",
144                    stack_index,
145                    stack_index + 1,
146                    stack
147                ),
148                OutOfBounds {
149                    stack_index,
150                    stack,
151                    num_items,
152                } => write!(
153                    f,
154                    "Current stack has invalid index {} at node level {}: size of set is {} \
155                     (stack: {:?})",
156                    stack[*stack_index], stack_index, num_items, stack
157                ),
158            },
159            UseOfVariableAsLocation { name } => write!(
160                f,
161                "Tried to use variable '{}' as a location in the story",
162                name
163            ),
164            UseOfUnvalidatedAddress { address } => {
165                write!(f, "Tried to use unvalidated address '{:?}'", address)
166            }
167        }
168    }
169}
170
171#[derive(Clone, Debug)]
172/// Error from processing content into its final format.
173pub struct ProcessError {
174    /// Error variant.
175    pub kind: ProcessErrorKind,
176}
177
178impl From<InklingError> for ProcessError {
179    fn from(err: InklingError) -> Self {
180        ProcessError {
181            kind: ProcessErrorKind::InklingError(Box::new(err)),
182        }
183    }
184}
185
186#[derive(Clone, Debug)]
187/// Variant of `ProcessError`.
188pub enum ProcessErrorKind {
189    /// An `Alternative` sequence tried to access an item with an out-of-bounds index.
190    InvalidAlternativeIndex,
191    /// An `InklingError` encountered during processing.
192    InklingError(Box<InklingError>),
193}
194
195#[derive(Clone, Debug)]
196/// Errors related to the stack of `Knots`, `Stitches` and choices set to
197/// the [`Story`][crate::story::Story].
198pub enum StackError {
199    /// The current stack of `Address`es is empty and a follow was requested.
200    NoStack,
201    /// An invalid address was used inside the system.
202    ///
203    /// This means that some bad assumptions have been made somewhere. Addresses are
204    /// always supposed to be verified as valid before use.
205    BadAddress { address: Address },
206    /// No set of presented choices have been added to the system.
207    NoLastChoices,
208    /// No root knot was added to the stack when the `Story` was constructed.
209    NoRootKnot { knot_name: String },
210}
211
212#[derive(Clone, Debug)]
213/// Current node tree [`Stack`][crate::node::Stack] is incorrect.
214pub enum IncorrectNodeStackError {
215    /// Tried to follow through nodes with an empty stack.
216    EmptyStack,
217    /// Found a `Line` object where a set of branching choices should be.
218    ExpectedBranchingPoint { stack_index: usize, stack: Stack },
219    /// Tried to follow a branch but stack does not have an index for the follow,
220    /// it is too short.
221    MissingBranchIndex { stack_index: usize, stack: Stack },
222    /// Stack contains an invalid index for the current node level.
223    OutOfBounds {
224        stack_index: usize,
225        stack: Stack,
226        num_items: usize,
227    },
228}