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