headless_lms_chatbot/
chatbot_error.rs

1/*!
2Contains error and result types for all the chatbot functions.
3*/
4
5use std::fmt::Display;
6
7use backtrace::Backtrace;
8use headless_lms_models::ModelError;
9use tracing_error::SpanTrace;
10
11use headless_lms_utils::error::backend_error::BackendError;
12
13/**
14Used as the result types for all of chatbot.
15*/
16pub type ChatbotResult<T> = Result<T, ChatbotError>;
17
18/// The type of [ChatbotError] that occured.
19#[derive(Debug)]
20pub enum ChatbotErrorType {
21    InvalidMessageShape,
22    InvalidToolName,
23    InvalidToolArguments,
24    ChatbotModelError,
25    UrlParse,
26    TokioIo,
27    SerdeJson,
28    Other,
29    DeserializationError,
30}
31
32/**
33Error type used in [ChatbotError], which is used for errors related to chatbot functionality.
34
35All the information in the error is meant to be seen by the user. The type of error is determined by the [ChatbotErrorType] enum, which is stored inside this struct.
36
37## Examples
38
39### Usage without source error
40
41```no_run
42# use headless_lms_chatbot::prelude::*;
43# fn random_function() -> ChatbotResult<()> {
44#    let erroneous_condition = 1 == 1;
45if erroneous_condition {
46    return Err(ChatbotError::new(
47        ChatbotErrorType::Other,
48        "File not found".to_string(),
49        None,
50    ));
51}
52# Ok(())
53# }
54```
55
56### Usage with a source error
57
58Used when calling a function that returns an error that cannot be automatically converted to an ChatbotError. (See `impl From<X>` implementations on this struct.)
59
60```no_run
61# use headless_lms_chatbot::prelude::*;
62# fn some_function_returning_an_error() -> ChatbotResult<()> {
63#    return Err(ChatbotError::new(
64#        ChatbotErrorType::Other,
65#        "File not found".to_string(),
66#        None,
67#    ));
68# }
69#
70# fn random_function() -> ChatbotResult<()> {
71#    let erroneous_condition = 1 == 1;
72some_function_returning_an_error().map_err(|original_error| {
73    ChatbotError::new(
74        ChatbotErrorType::Other,
75        "Library x failed to do y".to_string(),
76        Some(original_error.into()),
77    )
78})?;
79# Ok(())
80# }
81```
82*/
83#[derive(Debug)]
84pub struct ChatbotError {
85    error_type: <ChatbotError as BackendError>::ErrorType,
86    message: String,
87    /// Original error that caused this error.
88    source: Option<anyhow::Error>,
89    /// A trace of tokio tracing spans, generated automatically when the error is generated.
90    span_trace: Box<SpanTrace>,
91    /// Stack trace, generated automatically when the error is created.
92    backtrace: Box<Backtrace>,
93}
94
95impl std::error::Error for ChatbotError {
96    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
97        self.source.as_ref().and_then(|o| o.source())
98    }
99
100    fn cause(&self) -> Option<&dyn std::error::Error> {
101        self.source()
102    }
103}
104
105impl Display for ChatbotError {
106    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107        write!(f, "ChatbotError {:?} {:?}", self.error_type, self.message)
108    }
109}
110
111impl BackendError for ChatbotError {
112    type ErrorType = ChatbotErrorType;
113
114    fn new<M: Into<String>, S: Into<Option<anyhow::Error>>>(
115        error_type: Self::ErrorType,
116        message: M,
117        source_error: S,
118    ) -> Self {
119        Self::new_with_traces(
120            error_type,
121            message,
122            source_error,
123            Backtrace::new(),
124            SpanTrace::capture(),
125        )
126    }
127
128    fn backtrace(&self) -> Option<&Backtrace> {
129        Some(&self.backtrace)
130    }
131
132    fn error_type(&self) -> &Self::ErrorType {
133        &self.error_type
134    }
135
136    fn message(&self) -> &str {
137        &self.message
138    }
139
140    fn span_trace(&self) -> &SpanTrace {
141        &self.span_trace
142    }
143
144    fn new_with_traces<M: Into<String>, S: Into<Option<anyhow::Error>>>(
145        error_type: Self::ErrorType,
146        message: M,
147        source_error: S,
148        backtrace: Backtrace,
149        span_trace: SpanTrace,
150    ) -> Self {
151        Self {
152            error_type,
153            message: message.into(),
154            source: source_error.into(),
155            span_trace: Box::new(span_trace),
156            backtrace: Box::new(backtrace),
157        }
158    }
159}
160
161impl From<url::ParseError> for ChatbotError {
162    fn from(source: url::ParseError) -> Self {
163        ChatbotError::new(
164            ChatbotErrorType::UrlParse,
165            source.to_string(),
166            Some(source.into()),
167        )
168    }
169}
170
171impl From<tokio::io::Error> for ChatbotError {
172    fn from(source: tokio::io::Error) -> Self {
173        ChatbotError::new(
174            ChatbotErrorType::TokioIo,
175            source.to_string(),
176            Some(source.into()),
177        )
178    }
179}
180
181impl From<serde_json::Error> for ChatbotError {
182    fn from(source: serde_json::Error) -> Self {
183        ChatbotError::new(
184            ChatbotErrorType::SerdeJson,
185            source.to_string(),
186            Some(source.into()),
187        )
188    }
189}
190
191impl From<anyhow::Error> for ChatbotError {
192    fn from(err: anyhow::Error) -> ChatbotError {
193        Self::new(ChatbotErrorType::Other, err.to_string(), Some(err))
194    }
195}
196
197impl From<ModelError> for ChatbotError {
198    fn from(err: ModelError) -> ChatbotError {
199        Self::new(
200            ChatbotErrorType::ChatbotModelError,
201            err.to_string(),
202            Some(err.into()),
203        )
204    }
205}