1use serde_columnar::ColumnarError;
2use thiserror::Error;
3
4use crate::{ContainerID, InternalString, PeerID, TreeID, ID};
5
6pub type LoroResult<T> = Result<T, LoroError>;
7
8#[derive(Error, Debug, PartialEq)]
9pub enum LoroError {
10 #[error("Context's client_id({found:?}) does not match Container's client_id({expected:?})")]
11 UnmatchedContext { expected: PeerID, found: PeerID },
12 #[error("Decode error: Version vector error. Please provide correct version.")]
13 DecodeVersionVectorError,
14 #[error("Decode error: ({0})")]
15 DecodeError(Box<str>),
16 #[error(
17 "Decode error: The data is either corrupted or originates from an older version that is incompatible due to a breaking change."
19 )]
20 DecodeDataCorruptionError,
21 #[error("Decode error: Checksum mismatch. The data is corrupted.")]
22 DecodeChecksumMismatchError,
23 #[error("Decode error: Encoding version \"{0}\" is incompatible. Loro's encoding is backward compatible but not forward compatible. Please upgrade the version of Loro to support this version of the exported data.")]
24 IncompatibleFutureEncodingError(usize),
25 #[error("Js error ({0})")]
26 JsError(Box<str>),
27 #[error("Cannot get lock or the lock is poisoned")]
28 LockError,
29 #[error("Each AppState can only have one transaction at a time")]
30 DuplicatedTransactionError,
31 #[error("Cannot find ({0})")]
32 NotFoundError(Box<str>),
33 #[error("Transaction error ({0})")]
34 TransactionError(Box<str>),
35 #[error("Index out of bound. The given pos is {pos}, but the length is {len}. {info}")]
36 OutOfBound {
37 pos: usize,
38 len: usize,
39 info: Box<str>,
40 },
41 #[error("Every op id should be unique. ID {id} has been used. You should use a new PeerID to edit the content. ")]
42 UsedOpID { id: ID },
43 #[error("Concurrent ops with the same peer id is not allowed. PeerID: {peer}, LastCounter: {last_counter}, CurrentCounter: {current}")]
44 ConcurrentOpsWithSamePeerID {
45 peer: PeerID,
46 last_counter: i32,
47 current: i32,
48 },
49 #[error("Movable Tree Error: {0}")]
50 TreeError(#[from] LoroTreeError),
51 #[error("Invalid argument ({0})")]
52 ArgErr(Box<str>),
53 #[error("Auto commit has not started. The doc is readonly when detached and detached editing is not enabled.")]
54 AutoCommitNotStarted,
55 #[error("Style configuration missing for \"({0:?})\". Please provide the style configuration using `configTextStyle` on your Loro doc.")]
56 StyleConfigMissing(InternalString),
57 #[error("Unknown Error ({0})")]
58 Unknown(Box<str>),
59 #[error("The given ID ({0}) is not contained by the doc")]
60 FrontiersNotFound(ID),
61 #[error("Cannot import when the doc is in a transaction")]
62 ImportWhenInTxn,
63 #[error("The given method ({method}) is not allowed when the container is detached. You should insert the container to the doc first.")]
64 MisuseDetachedContainer { method: &'static str },
65 #[error("Not implemented: {0}")]
66 NotImplemented(&'static str),
67 #[error("Reattach a container that is already attached")]
68 ReattachAttachedContainer,
69 #[error("Edit is not allowed when the doc is in the detached mode.")]
70 EditWhenDetached,
71 #[error("The given ID ({0}) is not contained by the doc")]
72 UndoInvalidIdSpan(ID),
73 #[error("PeerID cannot be changed. Expected: {expected:?}, Actual: {actual:?}")]
74 UndoWithDifferentPeerId { expected: PeerID, actual: PeerID },
75 #[error("There is already an active undo group, call `group_end` first")]
76 UndoGroupAlreadyStarted,
77 #[error("There is no active undo group, call `group_start` first")]
78 InvalidJsonSchema,
79 #[error("Cannot insert or delete utf-8 in the middle of the codepoint in Unicode")]
80 UTF8InUnicodeCodePoint { pos: usize },
81 #[error("Cannot insert or delete utf-16 in the middle of the codepoint in Unicode")]
82 UTF16InUnicodeCodePoint { pos: usize },
83 #[error("The end index cannot be less than the start index")]
84 EndIndexLessThanStartIndex { start: usize, end: usize },
85 #[error("Invalid root container name! Don't include '/' or '\\0'")]
86 InvalidRootContainerName,
87 #[error("Import Failed: The dependencies of the importing updates are not included in the shallow history of the doc.")]
88 ImportUpdatesThatDependsOnOutdatedVersion,
89 #[error(
90 "You cannot switch a document to a version before the shallow history's start version."
91 )]
92 SwitchToVersionBeforeShallowRoot,
93 #[error(
94 "The container {container} is deleted. You cannot apply the op on a deleted container."
95 )]
96 ContainerDeleted { container: Box<ContainerID> },
97 #[error("You cannot set the `PeerID` with `PeerID::MAX`, which is an internal specific value")]
98 InvalidPeerID,
99 #[error("The containers {containers:?} are not found in the doc")]
100 ContainersNotFound { containers: Box<Vec<ContainerID>> },
101}
102
103#[derive(Error, Debug, PartialEq)]
104pub enum LoroTreeError {
105 #[error("`Cycle move` occurs when moving tree nodes.")]
106 CyclicMoveError,
107 #[error("The provided parent id is invalid")]
108 InvalidParent,
109 #[error("The parent of tree node is not found {0:?}")]
110 TreeNodeParentNotFound(TreeID),
111 #[error("TreeID {0:?} doesn't exist")]
112 TreeNodeNotExist(TreeID),
113 #[error("The index({index}) should be <= the length of children ({len})")]
114 IndexOutOfBound { len: usize, index: usize },
115 #[error("Fractional index is not enabled, you should enable it first by `LoroTree::set_enable_fractional_index`")]
116 FractionalIndexNotEnabled,
117 #[error("TreeID {0:?} is deleted or does not exist")]
118 TreeNodeDeletedOrNotExist(TreeID),
119}
120
121#[non_exhaustive]
122#[derive(Error, Debug, PartialEq)]
123pub enum LoroEncodeError {
124 #[error("The frontiers are not found in this doc: {0}")]
125 FrontiersNotFound(String),
126 #[error("Shallow snapshot incompatible with old snapshot format. Use new snapshot format or avoid shallow snapshots for storage.")]
127 ShallowSnapshotIncompatibleWithOldFormat,
128 #[error("Cannot export shallow snapshot with unknown container type. Please upgrade the Loro version.")]
129 UnknownContainer,
130}
131
132#[cfg(feature = "wasm")]
133pub mod wasm {
134 use wasm_bindgen::JsValue;
135
136 use crate::{LoroEncodeError, LoroError};
137
138 impl From<LoroError> for JsValue {
139 fn from(value: LoroError) -> Self {
140 JsValue::from_str(&value.to_string())
141 }
142 }
143
144 impl From<LoroEncodeError> for JsValue {
145 fn from(value: LoroEncodeError) -> Self {
146 JsValue::from_str(&value.to_string())
147 }
148 }
149
150 impl From<JsValue> for LoroError {
151 fn from(v: JsValue) -> Self {
152 Self::JsError(
153 v.as_string()
154 .unwrap_or_else(|| "unknown error".to_owned())
155 .into_boxed_str(),
156 )
157 }
158 }
159}
160
161impl From<ColumnarError> for LoroError {
162 fn from(e: ColumnarError) -> Self {
163 match e {
164 ColumnarError::ColumnarDecodeError(_)
165 | ColumnarError::RleEncodeError(_)
166 | ColumnarError::RleDecodeError(_)
167 | ColumnarError::OverflowError => {
168 LoroError::DecodeError(format!("Failed to decode Columnar: {e}").into_boxed_str())
169 }
170 e => LoroError::Unknown(e.to_string().into_boxed_str()),
171 }
172 }
173}