Skip to main content

repl_lang/
error.rs

1//! Why a session refused a line.
2
3use core::fmt;
4
5/// The reason a [`Session`](crate::Session) could not accept a line.
6///
7/// Both variants mean the pending entry was too large to keep, so the session
8/// discards it before returning the error: the next line starts a fresh entry
9/// and the session stays usable. Nothing already committed is affected. The
10/// pipeline is never called with input that triggered an error.
11///
12/// The enum is `#[non_exhaustive]`; match it with a wildcard arm.
13///
14/// # Examples
15///
16/// ```
17/// use repl_lang::{Session, SessionError, Status};
18///
19/// let mut session = Session::with_limit(8);
20/// let err = session.feed("a line that is far too long", |_| Status::Complete(())).unwrap_err();
21/// assert!(matches!(err, SessionError::TooLong { limit: 8, .. }));
22/// assert!(!session.is_pending()); // the oversized entry was dropped
23/// ```
24#[derive(Clone, Debug, PartialEq, Eq)]
25#[non_exhaustive]
26pub enum SessionError {
27    /// The entry, with this line added, would exceed the session's size limit.
28    ///
29    /// The limit bounds how much text one entry can accumulate — and so how
30    /// much the pipeline re-reads on every continuation line — guarding
31    /// against a runaway paste or a pipeline that never declares input
32    /// complete. Report it to the user; raise the limit with
33    /// [`Session::with_limit`](crate::Session::with_limit) if legitimate
34    /// entries hit it.
35    TooLong {
36        /// Byte length the entry would have reached, including line terminators.
37        len: usize,
38        /// The session's limit, in bytes.
39        limit: usize,
40    },
41
42    /// The session's source map has no room left for the entry.
43    ///
44    /// Committed entries share one 32-bit position space, so a session can hold
45    /// about 4 GiB of entries in total. A shorter entry may still fit, but the
46    /// session is effectively full: start a fresh [`Session`](crate::Session).
47    SpaceExhausted {
48        /// Byte length of the entry that did not fit.
49        needed: u64,
50        /// Bytes of position space that remained.
51        available: u64,
52    },
53}
54
55impl fmt::Display for SessionError {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        match self {
58            Self::TooLong { len, limit } => write!(
59                f,
60                "entry too long: {len} bytes exceeds the session limit of {limit} bytes"
61            ),
62            Self::SpaceExhausted { needed, available } => write!(
63                f,
64                "session source space exhausted: entry needs {needed} bytes, \
65                 {available} remain"
66            ),
67        }
68    }
69}
70
71impl core::error::Error for SessionError {}
72
73#[cfg(test)]
74mod tests {
75    use alloc::string::ToString;
76
77    use super::*;
78
79    #[test]
80    fn test_display_too_long_names_both_sizes() {
81        let text = SessionError::TooLong { len: 12, limit: 8 }.to_string();
82        assert_eq!(
83            text,
84            "entry too long: 12 bytes exceeds the session limit of 8 bytes"
85        );
86    }
87
88    #[test]
89    fn test_display_space_exhausted_names_both_sizes() {
90        let text = SessionError::SpaceExhausted {
91            needed: 9,
92            available: 2,
93        }
94        .to_string();
95        assert_eq!(
96            text,
97            "session source space exhausted: entry needs 9 bytes, 2 remain"
98        );
99    }
100
101    #[test]
102    fn test_error_trait_is_implemented() {
103        fn assert_error<E: core::error::Error + Send + Sync + 'static>() {}
104        assert_error::<SessionError>();
105    }
106}