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