1use crate::INTERNAL_ERROR;
2use crate::schema::RivetErrorSchema;
3use serde::{Deserialize, Serialize};
4use std::{fmt, sync::OnceLock};
5
6static EXPOSE_INTERNAL_ERRORS: OnceLock<bool> = OnceLock::new();
7
8fn expose_internal_errors() -> bool {
9 *EXPOSE_INTERNAL_ERRORS
10 .get_or_init(|| matches!(std::env::var("RIVET_EXPOSE_ERRORS").as_deref(), Ok("1")))
11}
12
13#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
15#[serde(rename_all = "camelCase")]
16pub struct ActorSpecifier {
17 pub actor_id: String,
18 pub generation: u64,
19 #[serde(skip_serializing_if = "Option::is_none")]
20 pub key: Option<String>,
21}
22
23impl ActorSpecifier {
24 pub fn new(actor_id: impl Into<String>, generation: u64) -> Self {
25 Self {
26 actor_id: actor_id.into(),
27 generation,
28 key: None,
29 }
30 }
31
32 pub fn with_key(mut self, key: impl Into<String>) -> Self {
33 self.key = Some(key.into());
34 self
35 }
36}
37
38impl fmt::Display for ActorSpecifier {
39 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40 match &self.key {
41 Some(key) => write!(
42 f,
43 "actor {} generation {} key {}",
44 self.actor_id, self.generation, key
45 ),
46 None => write!(f, "actor {} generation {}", self.actor_id, self.generation),
47 }
48 }
49}
50
51impl std::error::Error for ActorSpecifier {}
52
53#[derive(Debug, Clone)]
54pub enum RivetErrorKind {
55 Static(&'static RivetErrorSchema),
56 Dynamic {
57 group: String,
58 code: String,
59 default_message: String,
60 },
61}
62
63impl RivetErrorKind {
64 pub fn group(&self) -> &str {
65 match self {
66 Self::Static(schema) => schema.group,
67 Self::Dynamic { group, .. } => group,
68 }
69 }
70
71 pub fn code(&self) -> &str {
72 match self {
73 Self::Static(schema) => schema.code,
74 Self::Dynamic { code, .. } => code,
75 }
76 }
77
78 pub fn default_message(&self) -> &str {
79 match self {
80 Self::Static(schema) => schema.default_message,
81 Self::Dynamic {
82 default_message, ..
83 } => default_message,
84 }
85 }
86
87 pub fn schema(&self) -> Option<&'static RivetErrorSchema> {
88 match self {
89 Self::Static(schema) => Some(schema),
90 Self::Dynamic { .. } => None,
91 }
92 }
93}
94
95#[derive(Debug, Clone)]
96pub struct RivetError {
97 pub kind: RivetErrorKind,
98 pub meta: Option<Box<serde_json::value::RawValue>>,
99 pub message: Option<String>,
100 pub actor: Option<ActorSpecifier>,
101}
102
103impl RivetError {
104 pub fn extract(error: &anyhow::Error) -> Self {
105 let actor = error.downcast_ref::<ActorSpecifier>().cloned();
109 let mut extracted = error
110 .chain()
111 .find_map(|x| x.downcast_ref::<Self>())
112 .cloned()
113 .unwrap_or_else(|| INTERNAL_ERROR.build_internal(error));
114 if extracted.actor.is_none() {
115 extracted.actor = actor;
116 }
117 extracted
118 }
119
120 pub(crate) fn build_internal(error: &anyhow::Error) -> Self {
121 let error_string = format!("{:?}", error);
122 let meta_json = serde_json::json!({
123 "error": error_string
124 });
125 let meta = serde_json::value::to_raw_value(&meta_json).ok();
126
127 Self {
128 kind: RivetErrorKind::Static(&INTERNAL_ERROR),
129 meta,
130 message: expose_internal_errors().then(|| format!("Internal error: {}", error)),
131 actor: None,
132 }
133 }
134
135 pub fn group(&self) -> &str {
136 self.kind.group()
137 }
138
139 pub fn code(&self) -> &str {
140 self.kind.code()
141 }
142
143 pub fn message(&self) -> &str {
144 self.message
145 .as_deref()
146 .unwrap_or_else(|| self.kind.default_message())
147 }
148
149 pub fn metadata(&self) -> Option<serde_json::Value> {
150 self.meta
151 .as_ref()
152 .and_then(|raw| serde_json::from_str(raw.get()).ok())
153 }
154
155 pub fn actor(&self) -> Option<&ActorSpecifier> {
156 self.actor.as_ref()
157 }
158
159 pub fn with_actor(mut self, actor: ActorSpecifier) -> Self {
160 self.actor = Some(actor);
161 self
162 }
163
164 pub fn schema(&self) -> Option<&'static RivetErrorSchema> {
165 self.kind.schema()
166 }
167}
168
169impl fmt::Display for RivetError {
170 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
171 write!(f, "{}: {}", self.code(), self.message())
172 }
173}
174
175impl std::error::Error for RivetError {}
176
177impl Serialize for RivetError {
178 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
179 where
180 S: serde::Serializer,
181 {
182 use serde::ser::SerializeStruct;
183
184 let field_count = 3 + usize::from(self.meta.is_some()) + usize::from(self.actor.is_some());
185 let mut state = serializer.serialize_struct("RivetError", field_count)?;
186
187 state.serialize_field("group", self.group())?;
188 state.serialize_field("code", self.code())?;
189 state.serialize_field("message", self.message())?;
190
191 if let Some(meta) = &self.meta {
192 state.serialize_field("meta", meta)?;
193 }
194
195 if let Some(actor) = &self.actor {
196 state.serialize_field("actor", actor)?;
197 }
198
199 state.end()
200 }
201}