1use thiserror::Error;
2
3#[derive(Debug, Error)]
5#[non_exhaustive]
6pub enum SyncError {
7 #[error("outbox full: capacity {capacity}, current {current}")]
9 OutboxFull {
10 capacity: usize,
12 current: usize,
14 },
15
16 #[error("buffer full: capacity {0}")]
18 BufferFull(usize),
19
20 #[error("transport error: {0}")]
22 Transport(String),
23
24 #[error("conflict on entity {entity_type}/{entity_id}: {description}")]
26 Conflict {
27 entity_type: String,
29 entity_id: String,
31 description: String,
33 },
34
35 #[error("sync engine not initialized")]
37 NotInitialized,
38
39 #[error("serialization error: {0}")]
41 Serialization(String),
42
43 #[error("storage error: {0}")]
45 Storage(String),
46
47 #[error("invalid config: {0}")]
49 InvalidConfig(String),
50
51 #[error("duplicate event id: {0}")]
53 DuplicateEvent(String),
54
55 #[error("sequence {requested} is out of range (head: {head})")]
57 SequenceOutOfRange {
58 requested: u64,
60 head: u64,
62 },
63}
64
65impl From<serde_json::Error> for SyncError {
66 fn from(err: serde_json::Error) -> Self {
67 Self::Serialization(err.to_string())
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74
75 #[test]
76 fn error_display_outbox_full() {
77 let err = SyncError::OutboxFull { capacity: 1000, current: 1000 };
78 assert_eq!(err.to_string(), "outbox full: capacity 1000, current 1000");
79 }
80
81 #[test]
82 fn error_display_transport() {
83 let err = SyncError::Transport("connection refused".into());
84 assert_eq!(err.to_string(), "transport error: connection refused");
85 }
86
87 #[test]
88 fn error_display_conflict() {
89 let err = SyncError::Conflict {
90 entity_type: "order".into(),
91 entity_id: "ORD-123".into(),
92 description: "version mismatch".into(),
93 };
94 assert!(err.to_string().contains("order/ORD-123"));
95 }
96
97 #[test]
98 fn error_from_serde_json() {
99 let json_err = serde_json::from_str::<serde_json::Value>("not json").unwrap_err();
100 let sync_err: SyncError = json_err.into();
101 assert!(matches!(sync_err, SyncError::Serialization(_)));
102 }
103
104 #[test]
105 fn error_display_storage() {
106 let err = SyncError::Storage("disk full".into());
107 assert_eq!(err.to_string(), "storage error: disk full");
108 }
109
110 #[test]
111 fn error_display_sequence_out_of_range() {
112 let err = SyncError::SequenceOutOfRange { requested: 500, head: 100 };
113 assert!(err.to_string().contains("500"));
114 assert!(err.to_string().contains("100"));
115 }
116}