1- use core:: fmt:: Display ;
1+ use core:: { error :: Error , fmt:: Display } ;
22
33use alloc:: {
44 borrow:: Cow ,
55 boxed:: Box ,
6- format,
76 string:: { String , ToString } ,
87} ;
98use sqlite_nostd:: { context, sqlite3, Connection , Context , ResultCode } ;
@@ -15,16 +14,31 @@ use crate::bson::BsonError;
1514///
1615/// We allocate errors in boxes to avoid large [Result] types (given the large size of the
1716/// [RawPowerSyncError] enum type).
17+ #[ derive( Debug ) ]
1818pub struct PowerSyncError {
1919 inner : Box < RawPowerSyncError > ,
2020}
2121
2222impl PowerSyncError {
23- pub fn from_sqlite ( code : ResultCode , context : impl Into < Cow < ' static , str > > ) -> Self {
24- RawPowerSyncError :: Sqlite {
23+ fn errstr ( db : * mut sqlite3 ) -> Option < String > {
24+ let message = db. errmsg ( ) . unwrap_or ( String :: from ( "Conversion error" ) ) ;
25+ if message != "not an error" {
26+ Some ( message)
27+ } else {
28+ None
29+ }
30+ }
31+
32+ pub fn from_sqlite (
33+ db : * mut sqlite3 ,
34+ code : ResultCode ,
35+ context : impl Into < Cow < ' static , str > > ,
36+ ) -> Self {
37+ RawPowerSyncError :: Sqlite ( SqliteError {
2538 code,
39+ errstr : Self :: errstr ( db) ,
2640 context : Some ( context. into ( ) ) ,
27- }
41+ } )
2842 . into ( )
2943 }
3044
@@ -93,32 +107,21 @@ impl PowerSyncError {
93107 /// Applies this error to a function result context, setting the error code and a descriptive
94108 /// text.
95109 pub fn apply_to_ctx ( self , description : & str , ctx : * mut context ) {
96- let mut desc = self . description ( ctx . db_handle ( ) ) ;
110+ let mut desc = self . to_string ( ) ;
97111 desc. insert_str ( 0 , description) ;
98112 desc. insert_str ( description. len ( ) , ": " ) ;
99113
100114 ctx. result_error ( & desc) ;
101115 ctx. result_error_code ( self . sqlite_error_code ( ) ) ;
102116 }
103117
104- /// Obtains a description of this error, fetching it from SQLite if necessary.
105- pub fn description ( & self , db : * mut sqlite3 ) -> String {
106- if let RawPowerSyncError :: Sqlite { .. } = & * self . inner {
107- let message = db. errmsg ( ) . unwrap_or ( String :: from ( "Conversion error" ) ) ;
108- if message != "not an error" {
109- return format ! ( "{}, caused by: {message}" , self . inner) ;
110- }
111- }
112-
113- self . inner . to_string ( )
114- }
115-
116118 pub fn sqlite_error_code ( & self ) -> ResultCode {
117119 use RawPowerSyncError :: * ;
118120
119121 match self . inner . as_ref ( ) {
120- Sqlite { code, .. } => * code,
121- ArgumentError { .. } | StateError { .. } => ResultCode :: MISUSE ,
122+ Sqlite ( desc) => desc. code ,
123+ ArgumentError { .. } => ResultCode :: CONSTRAINT_DATATYPE ,
124+ StateError { .. } => ResultCode :: MISUSE ,
122125 MissingClientId
123126 | SyncProtocolError { .. }
124127 | DownMigrationDidNotUpdateVersion { .. } => ResultCode :: ABORT ,
@@ -134,6 +137,8 @@ impl Display for PowerSyncError {
134137 }
135138}
136139
140+ impl Error for PowerSyncError { }
141+
137142impl From < RawPowerSyncError > for PowerSyncError {
138143 fn from ( value : RawPowerSyncError ) -> Self {
139144 return PowerSyncError {
@@ -144,10 +149,11 @@ impl From<RawPowerSyncError> for PowerSyncError {
144149
145150impl From < ResultCode > for PowerSyncError {
146151 fn from ( value : ResultCode ) -> Self {
147- return RawPowerSyncError :: Sqlite {
152+ return RawPowerSyncError :: Sqlite ( SqliteError {
148153 code : value,
154+ errstr : None ,
149155 context : None ,
150- }
156+ } )
151157 . into ( ) ;
152158 }
153159}
@@ -164,11 +170,8 @@ enum RawPowerSyncError {
164170 /// [PowerSyncError::description] to create a detailed error message.
165171 ///
166172 /// This error should _never_ be created for anything but rethrowing underlying SQLite errors.
167- #[ error( "internal SQLite call returned {code}" ) ]
168- Sqlite {
169- code : ResultCode ,
170- context : Option < Cow < ' static , str > > ,
171- } ,
173+ #[ error( "{0}" ) ]
174+ Sqlite ( SqliteError ) ,
172175 /// A user (e.g. the one calling a PowerSync function, likely an SDK) has provided invalid
173176 /// arguments.
174177 ///
@@ -205,6 +208,45 @@ enum RawPowerSyncError {
205208 Internal { cause : PowerSyncErrorCause } ,
206209}
207210
211+ #[ derive( Debug ) ]
212+ struct SqliteError {
213+ code : ResultCode ,
214+ errstr : Option < String > ,
215+ context : Option < Cow < ' static , str > > ,
216+ }
217+
218+ impl Display for SqliteError {
219+ fn fmt ( & self , f : & mut core:: fmt:: Formatter < ' _ > ) -> core:: fmt:: Result {
220+ if let Some ( context) = & self . context {
221+ write ! ( f, "{}: " , context) ?;
222+ }
223+
224+ write ! ( f, "internal SQLite call returned {}" , self . code) ?;
225+ if let Some ( desc) = & self . errstr {
226+ write ! ( f, ": {}" , desc) ?
227+ }
228+
229+ Ok ( ( ) )
230+ }
231+ }
232+
233+ pub trait PSResult < T > {
234+ fn into_db_result ( self , db : * mut sqlite3 ) -> Result < T , PowerSyncError > ;
235+ }
236+
237+ impl < T > PSResult < T > for Result < T , ResultCode > {
238+ fn into_db_result ( self , db : * mut sqlite3 ) -> Result < T , PowerSyncError > {
239+ self . map_err ( |code| {
240+ RawPowerSyncError :: Sqlite ( SqliteError {
241+ code,
242+ errstr : PowerSyncError :: errstr ( db) ,
243+ context : None ,
244+ } )
245+ . into ( )
246+ } )
247+ }
248+ }
249+
208250#[ derive( Debug ) ]
209251pub enum PowerSyncErrorCause {
210252 Json ( serde_json:: Error ) ,
0 commit comments