Skip to main content

oversync_core/
error.rs

1#[derive(Debug, thiserror::Error)]
2pub enum OversyncError {
3	#[error("surrealdb: {0}")]
4	SurrealDb(String),
5
6	#[error("connector: {0}")]
7	Connector(String),
8
9	#[error("sink: {0}")]
10	Sink(String),
11
12	#[error("config: {0}")]
13	Config(String),
14
15	#[error("migration: {0}")]
16	Migration(String),
17
18	#[error("plugin: {0}")]
19	Plugin(String),
20
21	#[error("fail-safe: {deleted_count}/{previous_count} rows deleted (>{threshold_pct:.0}%)")]
22	FailSafe {
23		deleted_count: usize,
24		previous_count: usize,
25		threshold_pct: f64,
26	},
27
28	#[error("internal: {0}")]
29	Internal(String),
30}
31
32impl OversyncError {
33	pub fn is_fail_safe(&self) -> bool {
34		matches!(self, Self::FailSafe { .. })
35	}
36}
37
38impl From<serde_json::Error> for OversyncError {
39	fn from(e: serde_json::Error) -> Self {
40		Self::Internal(e.to_string())
41	}
42}
43
44#[cfg(test)]
45mod tests {
46	use super::*;
47
48	#[test]
49	fn display_all_variants() {
50		let cases: Vec<(OversyncError, &str)> = vec![
51			(OversyncError::SurrealDb("conn".into()), "surrealdb: conn"),
52			(
53				OversyncError::Connector("timeout".into()),
54				"connector: timeout",
55			),
56			(OversyncError::Sink("full".into()), "sink: full"),
57			(OversyncError::Config("bad".into()), "config: bad"),
58			(OversyncError::Migration("v1".into()), "migration: v1"),
59			(OversyncError::Plugin("missing".into()), "plugin: missing"),
60			(
61				OversyncError::FailSafe {
62					deleted_count: 8,
63					previous_count: 10,
64					threshold_pct: 30.0,
65				},
66				"fail-safe: 8/10 rows deleted (>30%)",
67			),
68			(OversyncError::Internal("oops".into()), "internal: oops"),
69		];
70		for (err, expected) in cases {
71			assert_eq!(err.to_string(), expected);
72		}
73	}
74
75	#[test]
76	fn serde_json_error_converts() {
77		let bad_json = serde_json::from_str::<serde_json::Value>("not json");
78		let oversync_err: OversyncError = bad_json.unwrap_err().into();
79		assert!(matches!(oversync_err, OversyncError::Internal(_)));
80	}
81
82	#[test]
83	fn fail_safe_helper_detects_variant() {
84		let err = OversyncError::FailSafe {
85			deleted_count: 4,
86			previous_count: 5,
87			threshold_pct: 30.0,
88		};
89		assert!(err.is_fail_safe());
90		assert!(!OversyncError::Internal("oops".into()).is_fail_safe());
91	}
92}