1use notedthat_core::{Error as CoreError, StorageError};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum WriteEffect {
8 Stored,
10 Deleted,
12}
13
14impl std::fmt::Display for WriteEffect {
15 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16 f.write_str(match self {
17 Self::Stored => "stored",
18 Self::Deleted => "deleted",
19 })
20 }
21}
22
23#[derive(Debug, thiserror::Error)]
25pub enum WriteError {
26 #[error(transparent)]
28 Storage(StorageError),
29 #[error("payload too large: {size} bytes (limit {limit})")]
31 TooLarge {
32 size: u64,
34 limit: u64,
36 },
37 #[error(transparent)]
39 Path(CoreError),
40 #[error("indexer queue full during upsert")]
42 IndexerBackpressureUpsert,
43 #[error("indexer queue full during tombstone")]
45 IndexerBackpressureTombstone,
46 #[error("change event not published after the object was {after}")]
51 EventPublishFailed {
52 after: WriteEffect,
54 },
55 #[error("patch payload too large: {size} bytes (limit {limit})")]
57 PatchTooLarge {
58 size: u64,
60 limit: u64,
62 },
63 #[error("line range {first}..{last} out of range (total {total_lines} lines)")]
65 PatchLineOutOfRange {
66 first: u64,
68 last: u64,
70 total_lines: u64,
72 total_bytes: u64,
74 },
75 #[error("invalid patch request: {message}")]
77 PatchInvalidRange {
78 message: String,
80 },
81 #[error("invalid manifest: {message}")]
86 InvalidManifest {
87 message: String,
89 },
90 #[error("replace: no match found for old_string")]
92 ReplaceNoMatch,
93 #[error("replace: found {count} matches; use replace_all to replace them all")]
95 ReplaceAmbiguous {
96 count: u64,
98 },
99}
100
101impl From<StorageError> for WriteError {
102 fn from(err: StorageError) -> Self {
103 Self::Storage(err)
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::WriteError;
110
111 #[test]
112 fn patch_too_large_displays_size_limit_when_constructed() {
113 let err = WriteError::PatchTooLarge {
114 size: 200 * 1024 * 1024,
115 limit: 100 * 1024 * 1024,
116 };
117
118 assert!(err.to_string().contains("too large"));
119 }
120
121 #[test]
122 fn patch_line_out_of_range_constructs_with_line_and_byte_totals() {
123 let err = WriteError::PatchLineOutOfRange {
124 first: 999,
125 last: 1000,
126 total_lines: 20,
127 total_bytes: 100,
128 };
129
130 assert!(err.to_string().contains("out of range"));
131 }
132
133 #[test]
134 fn patch_invalid_range_displays_reason_when_constructed() {
135 let err = WriteError::PatchInvalidRange {
136 message: "test".into(),
137 };
138
139 assert!(err.to_string().contains("invalid patch"));
140 }
141
142 #[test]
143 fn replace_ambiguous_displays_match_count_when_constructed() {
144 let err = WriteError::ReplaceAmbiguous { count: 3 };
145
146 assert_eq!(
147 err.to_string(),
148 "replace: found 3 matches; use replace_all to replace them all"
149 );
150 }
151}