1extern crate alloc;
2
3use alloc::fmt;
4
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Deserialize, Serialize)]
9pub struct ErrorResponse {
10 #[serde(default)]
11 pub message: String,
12 #[serde(default)]
13 pub code: String,
14 pub details: Option<String>,
15 pub hint: Option<String>,
16}
17
18#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
20pub enum PostgrestUtilError {
21 Postgres(PostgresError),
22 Postgrest(PostgrestError),
23 Custom(CustomError),
24}
25
26impl PostgrestUtilError {
27 #[must_use]
29 pub fn from_error_response(resp: ErrorResponse) -> Self {
30 if resp.code.starts_with("PGRST") {
31 Self::Postgrest(PostgrestError::from_response(resp))
32 } else if resp.code.len() == 5 || resp.code.starts_with("XX") {
33 Self::Postgres(PostgresError::from_response(resp))
34 } else {
35 Self::Custom(CustomError::from_response(resp))
36 }
37 }
38
39 #[must_use]
41 pub const fn http_status_code(&self, is_authenticated: bool) -> u16 {
42 match self {
43 Self::Postgres(err) => err.http_status_code(is_authenticated),
44 Self::Postgrest(err) => err.http_status_code(),
45 Self::Custom(_) => 400, }
47 }
48}
49
50impl core::fmt::Display for PostgrestUtilError {
51 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
52 match self {
53 Self::Postgres(err) => {
54 write!(fmt, "Postgres [{}]: {}", err.code, err.message)
55 }
56 Self::Postgrest(err) => {
57 write!(fmt, "Postgrest [{}]: {}", err.code, err.message)
58 }
59 Self::Custom(err) => write!(fmt, "Custom [{}]: {}", err.code, err.message),
60 }
61 }
62}
63
64impl core::error::Error for PostgrestUtilError {}
65
66#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
68pub struct PostgresError {
69 pub code: PostgresErrorCode,
70 pub message: String,
71 pub details: Option<String>,
72 pub hint: Option<String>,
73}
74
75impl PostgresError {
76 #[must_use]
77 pub fn from_response(resp: ErrorResponse) -> Self {
78 let code = PostgresErrorCode::from_code(&resp.code);
79 Self {
80 code,
81 message: resp.message,
82 details: resp.details,
83 hint: resp.hint,
84 }
85 }
86
87 #[must_use]
88 pub const fn http_status_code(&self, is_authenticated: bool) -> u16 {
89 self.code.http_status_code(is_authenticated)
90 }
91}
92
93#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
95pub enum PostgresErrorCode {
96 NotNullViolation, ForeignKeyViolation, UniqueViolation, ReadOnlySqlTransaction, UndefinedFunction, UndefinedTable, InfiniteRecursion, InsufficientPrivilege, ConfigLimitExceeded, RaiseException, ConnectionException, TriggeredActionException, InvalidGrantor, InvalidRoleSpecification, InvalidTransactionState, InvalidAuthorizationSpecification, InvalidTransactionTermination, ExternalRoutineException, ExternalRoutineInvocationException, SavepointException, TransactionRollback, InsufficientResources, ProgramLimitExceeded, ObjectNotInPrerequisiteState, OperatorIntervention, SystemError, ConfigFileError, FdwError, PlpgsqlError, InternalError, Other(String), }
131
132impl PostgresErrorCode {
133 #[must_use]
134 pub fn from_code(code: &str) -> Self {
135 match code {
136 "23502" => Self::NotNullViolation,
138 "23503" => Self::ForeignKeyViolation,
139 "23505" => Self::UniqueViolation,
140 "25006" => Self::ReadOnlySqlTransaction,
141 "42883" => Self::UndefinedFunction,
142 "42P01" => Self::UndefinedTable,
143 "42P17" => Self::InfiniteRecursion,
144 "42501" => Self::InsufficientPrivilege,
145 "53400" => Self::ConfigLimitExceeded,
146 "P0001" => Self::RaiseException,
147 _ => {
148 if code.starts_with("08") {
150 Self::ConnectionException
151 } else if code.starts_with("09") {
152 Self::TriggeredActionException
153 } else if code.starts_with("0L") {
154 Self::InvalidGrantor
155 } else if code.starts_with("0P") {
156 Self::InvalidRoleSpecification
157 } else if code.starts_with("25") {
158 Self::InvalidTransactionState
159 } else if code.starts_with("28") {
160 Self::InvalidAuthorizationSpecification
161 } else if code.starts_with("2D") {
162 Self::InvalidTransactionTermination
163 } else if code.starts_with("38") {
164 Self::ExternalRoutineException
165 } else if code.starts_with("39") {
166 Self::ExternalRoutineInvocationException
167 } else if code.starts_with("3B") {
168 Self::SavepointException
169 } else if code.starts_with("40") {
170 Self::TransactionRollback
171 } else if code.starts_with("53") {
172 Self::InsufficientResources
173 } else if code.starts_with("54") {
174 Self::ProgramLimitExceeded
175 } else if code.starts_with("55") {
176 Self::ObjectNotInPrerequisiteState
177 } else if code.starts_with("57") {
178 Self::OperatorIntervention
179 } else if code.starts_with("58") {
180 Self::SystemError
181 } else if code.starts_with("F0") {
182 Self::ConfigFileError
183 } else if code.starts_with("HV") {
184 Self::FdwError
185 } else if code.starts_with("P0") {
186 Self::PlpgsqlError
187 } else if code.starts_with("XX") {
188 Self::InternalError
189 } else {
190 Self::Other(code.to_owned())
191 }
192 }
193 }
194 }
195
196 #[must_use]
197 pub const fn http_status_code(&self, is_authenticated: bool) -> u16 {
198 match self {
199 Self::TriggeredActionException
201 | Self::InvalidTransactionState
202 | Self::InvalidTransactionTermination
203 | Self::ExternalRoutineException
204 | Self::ExternalRoutineInvocationException
205 | Self::SavepointException
206 | Self::TransactionRollback
207 | Self::ProgramLimitExceeded
208 | Self::ObjectNotInPrerequisiteState
209 | Self::OperatorIntervention
210 | Self::SystemError
211 | Self::ConfigFileError
212 | Self::FdwError
213 | Self::PlpgsqlError
214 | Self::InternalError
215 | Self::ConfigLimitExceeded
216 | Self::InfiniteRecursion => 500,
217
218 Self::ConnectionException | Self::InsufficientResources => 503,
220
221 Self::InvalidGrantor
223 | Self::InvalidRoleSpecification
224 | Self::InvalidAuthorizationSpecification => 403,
225
226 Self::UndefinedFunction | Self::UndefinedTable => 404,
228
229 Self::NotNullViolation | Self::RaiseException | Self::Other(_) => 400,
231
232 Self::ForeignKeyViolation | Self::UniqueViolation => 409,
234
235 Self::ReadOnlySqlTransaction => 405,
237
238 Self::InsufficientPrivilege => {
240 if is_authenticated {
241 403
242 } else {
243 401
244 }
245 }
246 }
247 }
248}
249
250impl core::fmt::Display for PostgresErrorCode {
251 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
252 match self {
253 Self::NotNullViolation => write!(fmt, "23502"),
254 Self::ForeignKeyViolation => write!(fmt, "23503"),
255 Self::UniqueViolation => write!(fmt, "23505"),
256 Self::ReadOnlySqlTransaction => write!(fmt, "25006"),
257 Self::UndefinedFunction => write!(fmt, "42883"),
258 Self::UndefinedTable => write!(fmt, "42P01"),
259 Self::InfiniteRecursion => write!(fmt, "42P17"),
260 Self::InsufficientPrivilege => write!(fmt, "42501"),
261 Self::ConfigLimitExceeded => write!(fmt, "53400"),
262 Self::RaiseException => write!(fmt, "P0001"),
263 Self::ConnectionException => write!(fmt, "08*"),
264 Self::TriggeredActionException => write!(fmt, "09*"),
265 Self::InvalidGrantor => write!(fmt, "0L*"),
266 Self::InvalidRoleSpecification => write!(fmt, "0P*"),
267 Self::InvalidTransactionState => write!(fmt, "25*"),
268 Self::InvalidAuthorizationSpecification => write!(fmt, "28*"),
269 Self::InvalidTransactionTermination => write!(fmt, "2D*"),
270 Self::ExternalRoutineException => write!(fmt, "38*"),
271 Self::ExternalRoutineInvocationException => write!(fmt, "39*"),
272 Self::SavepointException => write!(fmt, "3B*"),
273 Self::TransactionRollback => write!(fmt, "40*"),
274 Self::InsufficientResources => write!(fmt, "53*"),
275 Self::ProgramLimitExceeded => write!(fmt, "54*"),
276 Self::ObjectNotInPrerequisiteState => write!(fmt, "55*"),
277 Self::OperatorIntervention => write!(fmt, "57*"),
278 Self::SystemError => write!(fmt, "58*"),
279 Self::ConfigFileError => write!(fmt, "F0*"),
280 Self::FdwError => write!(fmt, "HV*"),
281 Self::PlpgsqlError => write!(fmt, "P0*"),
282 Self::InternalError => write!(fmt, "XX*"),
283 Self::Other(code) => write!(fmt, "{code}"),
284 }
285 }
286}
287
288#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
290pub struct PostgrestError {
291 pub code: PostgrestErrorCode,
292 pub message: String,
293 pub details: Option<String>,
294 pub hint: Option<String>,
295}
296
297impl PostgrestError {
298 #[must_use]
299 pub fn from_response(resp: ErrorResponse) -> Self {
300 let code = PostgrestErrorCode::from_code(&resp.code);
301 Self {
302 code,
303 message: resp.message,
304 details: resp.details,
305 hint: resp.hint,
306 }
307 }
308
309 #[must_use]
310 pub const fn http_status_code(&self) -> u16 {
311 self.code.http_status_code()
312 }
313}
314
315#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
317pub enum PostgrestErrorCode {
318 CouldNotConnectDatabase, InternalConnectionError, CouldNotConnectSchemaCache, RequestTimedOut, ParsingErrorQueryParameter, FunctionOnlySupportsGetOrPost, InvalidRequestBody, InvalidRange, InvalidPutRequest, SchemaNotInConfig, InvalidContentType, FilterOnMissingEmbeddedResource, LimitedUpdateDeleteWithoutOrdering, LimitedUpdateDeleteExceededMaxRows, InvalidResponseHeaders, InvalidStatusCode, UpsertPutWithLimitsOffsets, UpsertPutPrimaryKeyMismatch, InvalidSingularResponse, UnsupportedHttpVerb, CannotOrderByRelatedTable, CannotSpreadRelatedTable, InvalidEmbeddedResourceFilter, InvalidRaiseErrorJson, InvalidPreferHeader, RelationshipNotFound, AmbiguousEmbedding, FunctionNotFound, OverloadedFunctionAmbiguous, ColumnNotFound, JwtSecretMissing, JwtInvalid, AnonymousRoleDisabled, InternalLibraryError, Other(String), }
365
366impl PostgrestErrorCode {
367 #[must_use]
368 pub fn from_code(code: &str) -> Self {
369 match code {
370 "PGRST000" => Self::CouldNotConnectDatabase,
371 "PGRST001" => Self::InternalConnectionError,
372 "PGRST002" => Self::CouldNotConnectSchemaCache,
373 "PGRST003" => Self::RequestTimedOut,
374 "PGRST100" => Self::ParsingErrorQueryParameter,
375 "PGRST101" => Self::FunctionOnlySupportsGetOrPost,
376 "PGRST102" => Self::InvalidRequestBody,
377 "PGRST103" => Self::InvalidRange,
378 "PGRST105" => Self::InvalidPutRequest,
379 "PGRST106" => Self::SchemaNotInConfig,
380 "PGRST107" => Self::InvalidContentType,
381 "PGRST108" => Self::FilterOnMissingEmbeddedResource,
382 "PGRST109" => Self::LimitedUpdateDeleteWithoutOrdering,
383 "PGRST110" => Self::LimitedUpdateDeleteExceededMaxRows,
384 "PGRST111" => Self::InvalidResponseHeaders,
385 "PGRST112" => Self::InvalidStatusCode,
386 "PGRST114" => Self::UpsertPutWithLimitsOffsets,
387 "PGRST115" => Self::UpsertPutPrimaryKeyMismatch,
388 "PGRST116" => Self::InvalidSingularResponse,
389 "PGRST117" => Self::UnsupportedHttpVerb,
390 "PGRST118" => Self::CannotOrderByRelatedTable,
391 "PGRST119" => Self::CannotSpreadRelatedTable,
392 "PGRST120" => Self::InvalidEmbeddedResourceFilter,
393 "PGRST121" => Self::InvalidRaiseErrorJson,
394 "PGRST122" => Self::InvalidPreferHeader,
395 "PGRST200" => Self::RelationshipNotFound,
396 "PGRST201" => Self::AmbiguousEmbedding,
397 "PGRST202" => Self::FunctionNotFound,
398 "PGRST203" => Self::OverloadedFunctionAmbiguous,
399 "PGRST204" => Self::ColumnNotFound,
400 "PGRST300" => Self::JwtSecretMissing,
401 "PGRST301" => Self::JwtInvalid,
402 "PGRST302" => Self::AnonymousRoleDisabled,
403 "PGRSTX00" => Self::InternalLibraryError,
404 _ => Self::Other(code.to_owned()),
405 }
406 }
407
408 #[must_use]
409 pub const fn http_status_code(&self) -> u16 {
410 match self {
411 Self::InternalConnectionError
413 | Self::CouldNotConnectSchemaCache
414 | Self::InvalidResponseHeaders
415 | Self::InvalidStatusCode
416 | Self::InvalidRaiseErrorJson
417 | Self::JwtSecretMissing
418 | Self::InternalLibraryError
419 | Self::Other(_) => 500,
420 Self::CouldNotConnectDatabase => 503,
422 Self::RequestTimedOut => 504,
424 Self::ParsingErrorQueryParameter
426 | Self::InvalidRequestBody
427 | Self::FilterOnMissingEmbeddedResource
428 | Self::LimitedUpdateDeleteWithoutOrdering
429 | Self::LimitedUpdateDeleteExceededMaxRows
430 | Self::UpsertPutWithLimitsOffsets
431 | Self::UpsertPutPrimaryKeyMismatch
432 | Self::CannotOrderByRelatedTable
433 | Self::CannotSpreadRelatedTable
434 | Self::InvalidEmbeddedResourceFilter
435 | Self::InvalidPreferHeader
436 | Self::RelationshipNotFound
437 | Self::ColumnNotFound => 400,
438 Self::FunctionOnlySupportsGetOrPost
440 | Self::InvalidPutRequest
441 | Self::UnsupportedHttpVerb => 405,
442 Self::InvalidRange => 416,
444 Self::SchemaNotInConfig | Self::InvalidSingularResponse => 406,
446 Self::InvalidContentType => 415,
448 Self::AmbiguousEmbedding | Self::OverloadedFunctionAmbiguous => 300,
450 Self::FunctionNotFound => 404,
452 Self::JwtInvalid | Self::AnonymousRoleDisabled => 401,
454 }
455 }
456}
457
458impl core::fmt::Display for PostgrestErrorCode {
459 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
460 match self {
461 Self::CouldNotConnectDatabase => write!(fmt, "PGRST000"),
462 Self::InternalConnectionError => write!(fmt, "PGRST001"),
463 Self::CouldNotConnectSchemaCache => write!(fmt, "PGRST002"),
464 Self::RequestTimedOut => write!(fmt, "PGRST003"),
465 Self::ParsingErrorQueryParameter => write!(fmt, "PGRST100"),
466 Self::FunctionOnlySupportsGetOrPost => write!(fmt, "PGRST101"),
467 Self::InvalidRequestBody => write!(fmt, "PGRST102"),
468 Self::InvalidRange => write!(fmt, "PGRST103"),
469 Self::InvalidPutRequest => write!(fmt, "PGRST105"),
470 Self::SchemaNotInConfig => write!(fmt, "PGRST106"),
471 Self::InvalidContentType => write!(fmt, "PGRST107"),
472 Self::FilterOnMissingEmbeddedResource => write!(fmt, "PGRST108"),
473 Self::LimitedUpdateDeleteWithoutOrdering => write!(fmt, "PGRST109"),
474 Self::LimitedUpdateDeleteExceededMaxRows => write!(fmt, "PGRST110"),
475 Self::InvalidResponseHeaders => write!(fmt, "PGRST111"),
476 Self::InvalidStatusCode => write!(fmt, "PGRST112"),
477 Self::UpsertPutWithLimitsOffsets => write!(fmt, "PGRST114"),
478 Self::UpsertPutPrimaryKeyMismatch => write!(fmt, "PGRST115"),
479 Self::InvalidSingularResponse => write!(fmt, "PGRST116"),
480 Self::UnsupportedHttpVerb => write!(fmt, "PGRST117"),
481 Self::CannotOrderByRelatedTable => write!(fmt, "PGRST118"),
482 Self::CannotSpreadRelatedTable => write!(fmt, "PGRST119"),
483 Self::InvalidEmbeddedResourceFilter => write!(fmt, "PGRST120"),
484 Self::InvalidRaiseErrorJson => write!(fmt, "PGRST121"),
485 Self::InvalidPreferHeader => write!(fmt, "PGRST122"),
486 Self::RelationshipNotFound => write!(fmt, "PGRST200"),
487 Self::AmbiguousEmbedding => write!(fmt, "PGRST201"),
488 Self::FunctionNotFound => write!(fmt, "PGRST202"),
489 Self::OverloadedFunctionAmbiguous => write!(fmt, "PGRST203"),
490 Self::ColumnNotFound => write!(fmt, "PGRST204"),
491 Self::JwtSecretMissing => write!(fmt, "PGRST300"),
492 Self::JwtInvalid => write!(fmt, "PGRST301"),
493 Self::AnonymousRoleDisabled => write!(fmt, "PGRST302"),
494 Self::InternalLibraryError => write!(fmt, "PGRSTX00"),
495 Self::Other(code) => write!(fmt, "{code}"),
496 }
497 }
498}
499
500#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
502pub struct CustomError {
503 pub code: String,
504 pub message: String,
505 pub details: Option<String>,
506 pub hint: Option<String>,
507}
508
509impl CustomError {
510 #[must_use]
511 pub fn from_response(resp: ErrorResponse) -> Self {
512 Self {
513 code: resp.code,
514 message: resp.message,
515 details: resp.details,
516 hint: resp.hint,
517 }
518 }
519}
520
521#[cfg(test)]
522#[expect(clippy::panic, reason = "Allowed in test code for simplicity")]
523#[expect(
524 clippy::wildcard_enum_match_arm,
525 reason = "Allowed in test code for simplicity"
526)]
527mod tests {
528 use super::*;
529
530 #[test]
531 fn test_postgres_error_transformation() {
532 let error_response = ErrorResponse {
534 message: "duplicate key value violates unique constraint".to_owned(),
535 code: "23505".to_owned(),
536 details: Some("Key (id)=(1) already exists.".to_owned()),
537 hint: None,
538 };
539 let is_authenticated = true;
540 let error = PostgrestUtilError::from_error_response(error_response);
541
542 match error {
543 PostgrestUtilError::Postgres(pg_error) => {
544 assert_eq!(pg_error.code, PostgresErrorCode::UniqueViolation);
545 assert_eq!(pg_error.http_status_code(is_authenticated), 409);
546 assert_eq!(
547 pg_error.message,
548 "duplicate key value violates unique constraint"
549 );
550 assert_eq!(
551 pg_error.details,
552 Some("Key (id)=(1) already exists.".to_owned())
553 );
554 }
555 _ => panic!("Expected PostgresError"),
556 }
557 }
558
559 #[test]
560 fn test_postgrest_error_transformation() {
561 let error_response = ErrorResponse {
563 message: "More than one item found".to_owned(),
564 code: "PGRST116".to_owned(),
565 details: None,
566 hint: Some("Use limit to restrict the number of results.".to_owned()),
567 };
568 let error = PostgrestUtilError::from_error_response(error_response);
569
570 match error {
571 PostgrestUtilError::Postgrest(pgrst_error) => {
572 assert_eq!(
573 pgrst_error.code,
574 PostgrestErrorCode::InvalidSingularResponse
575 );
576 assert_eq!(pgrst_error.http_status_code(), 406);
577 assert_eq!(pgrst_error.message, "More than one item found");
578 assert_eq!(
579 pgrst_error.hint,
580 Some("Use limit to restrict the number of results.".to_owned())
581 );
582 }
583 _ => panic!("Expected PostgrestError"),
584 }
585 }
586
587 #[test]
588 fn test_custom_error_transformation() {
589 let error_response = ErrorResponse {
591 message: "Custom error message".to_owned(),
592 code: "CUSTOM123".to_owned(),
593 details: Some("Some custom details.".to_owned()),
594 hint: Some("Some custom hint.".to_owned()),
595 };
596 let error = PostgrestUtilError::from_error_response(error_response);
597
598 match error {
599 PostgrestUtilError::Custom(custom_error) => {
600 assert_eq!(custom_error.code, "CUSTOM123");
601 assert_eq!(custom_error.message, "Custom error message");
602 assert_eq!(
603 custom_error.details,
604 Some("Some custom details.".to_owned())
605 );
606 assert_eq!(custom_error.hint, Some("Some custom hint.".to_owned()));
607 }
608 _ => panic!("Expected CustomError"),
609 }
610 }
611
612 #[test]
613 fn test_insufficient_privilege_error_authenticated() {
614 let error_response = ErrorResponse {
616 message: "permission denied for relation".to_owned(),
617 code: "42501".to_owned(),
618 details: None,
619 hint: None,
620 };
621 let is_authenticated = true;
622 let error = PostgrestUtilError::from_error_response(error_response);
623
624 match error {
625 PostgrestUtilError::Postgres(pg_error) => {
626 assert_eq!(pg_error.code, PostgresErrorCode::InsufficientPrivilege);
627 assert_eq!(pg_error.http_status_code(is_authenticated), 403);
628 }
629 _ => panic!("Expected PostgresError"),
630 }
631 }
632
633 #[test]
634 fn test_insufficient_privilege_error_unauthenticated() {
635 let error_response = ErrorResponse {
637 message: "permission denied for relation".to_owned(),
638 code: "42501".to_owned(),
639 details: None,
640 hint: None,
641 };
642 let is_authenticated = false;
643 let error = PostgrestUtilError::from_error_response(error_response);
644
645 match error {
646 PostgrestUtilError::Postgres(pg_error) => {
647 assert_eq!(pg_error.code, PostgresErrorCode::InsufficientPrivilege);
648 assert_eq!(pg_error.http_status_code(is_authenticated), 401);
649 }
650 _ => panic!("Expected PostgresError"),
651 }
652 }
653
654 #[test]
655 fn test_pattern_error_transformation() {
656 let error_response = ErrorResponse {
658 message: "An error occurred while connecting to the database".to_owned(),
659 code: "08006".to_owned(),
660 details: None,
661 hint: None,
662 };
663 let is_authenticated = true;
664 let error = PostgrestUtilError::from_error_response(error_response);
665
666 match error {
667 PostgrestUtilError::Postgres(pg_error) => {
668 assert_eq!(pg_error.code, PostgresErrorCode::ConnectionException);
669 assert_eq!(pg_error.http_status_code(is_authenticated), 503);
670 }
671 _ => panic!("Expected PostgresError"),
672 }
673 }
674
675 #[test]
676 fn test_postgrest_internal_error() {
677 let error_response = ErrorResponse {
679 message: "Internal server error".to_owned(),
680 code: "PGRSTX00".to_owned(),
681 details: Some("An unexpected error occurred.".to_owned()),
682 hint: None,
683 };
684 let error = PostgrestUtilError::from_error_response(error_response);
685
686 match error {
687 PostgrestUtilError::Postgrest(pgrst_error) => {
688 assert_eq!(pgrst_error.code, PostgrestErrorCode::InternalLibraryError);
689 assert_eq!(pgrst_error.http_status_code(), 500);
690 }
691 _ => panic!("Expected PostgrestError"),
692 }
693 }
694
695 #[test]
696 fn test_unknown_postgres_error_code() {
697 let error_response = ErrorResponse {
699 message: "Unknown error".to_owned(),
700 code: "99999".to_owned(),
701 details: None,
702 hint: None,
703 };
704 let is_authenticated = true;
705 let error = PostgrestUtilError::from_error_response(error_response);
706
707 match &error {
708 PostgrestUtilError::Postgres(pg_error) => {
709 match &pg_error.code {
710 PostgresErrorCode::Other(code) => assert_eq!(code, "99999"),
711 _ => panic!("Expected Other variant"),
712 }
713 assert_eq!(pg_error.http_status_code(is_authenticated), 400);
714 }
715 _ => panic!("Expected PostgresError"),
716 }
717 }
718
719 #[test]
720 fn test_unknown_postgrest_error_code() {
721 let error_response = ErrorResponse {
723 message: "Unknown PostgREST error".to_owned(),
724 code: "PGRST999".to_owned(),
725 details: None,
726 hint: None,
727 };
728 let error = PostgrestUtilError::from_error_response(error_response);
729
730 match &error {
731 PostgrestUtilError::Postgrest(pgrst_error) => {
732 match &pgrst_error.code {
733 PostgrestErrorCode::Other(code) => assert_eq!(code, "PGRST999"),
734 _ => panic!("Expected Other variant"),
735 }
736 assert_eq!(pgrst_error.http_status_code(), 500);
737 }
738 _ => panic!("Expected PostgrestError"),
739 }
740 }
741
742 #[test]
743 fn test_raise_exception_error() {
744 let error_response = ErrorResponse {
746 message: "I refuse!".to_owned(),
747 code: "P0001".to_owned(),
748 details: Some("Pretty simple".to_owned()),
749 hint: Some("There is nothing you can do.".to_owned()),
750 };
751 let is_authenticated = true;
752 let error = PostgrestUtilError::from_error_response(error_response);
753
754 match error {
755 PostgrestUtilError::Postgres(pg_error) => {
756 assert_eq!(pg_error.code, PostgresErrorCode::RaiseException);
757 assert_eq!(pg_error.http_status_code(is_authenticated), 400);
758 assert_eq!(pg_error.message, "I refuse!");
759 assert_eq!(pg_error.details, Some("Pretty simple".to_owned()));
760 assert_eq!(
761 pg_error.hint,
762 Some("There is nothing you can do.".to_owned())
763 );
764 }
765 _ => panic!("Expected PostgresError"),
766 }
767 }
768
769 #[test]
770 fn test_custom_status_code_in_raise() {
771 let error_response = ErrorResponse {
773 message: "Payment Required".to_owned(),
774 code: "PT402".to_owned(),
775 details: Some("Quota exceeded".to_owned()),
776 hint: Some("Upgrade your plan".to_owned()),
777 };
778 let error = PostgrestUtilError::Custom(CustomError::from_response(error_response));
779
780 match error {
781 PostgrestUtilError::Custom(custom_error) => {
782 assert_eq!(custom_error.code, "PT402");
783 assert_eq!(custom_error.message, "Payment Required");
784 assert_eq!(custom_error.details, Some("Quota exceeded".to_owned()));
785 assert_eq!(custom_error.hint, Some("Upgrade your plan".to_owned()));
786 }
787 _ => panic!("Expected CustomError"),
788 }
789 }
790
791 #[test]
792 fn test_error_display_trait() {
793 let error_response = ErrorResponse {
795 message: "Not null violation".to_owned(),
796 code: "23502".to_owned(),
797 details: None,
798 hint: None,
799 };
800 let error = PostgrestUtilError::from_error_response(error_response);
801
802 assert_eq!(format!("{error}"), "Postgres [23502]: Not null violation");
803 }
804
805 #[test]
806 fn test_error_trait() {
807 let error_response = ErrorResponse {
809 message: "Some error".to_owned(),
810 code: "23502".to_owned(),
811 details: None,
812 hint: None,
813 };
814 let error = PostgrestUtilError::from_error_response(error_response);
815
816 let std_error: &dyn core::error::Error = &error;
817 assert_eq!(std_error.to_string(), "Postgres [23502]: Some error");
818 }
819
820 #[test]
821 fn non_standard_error() {
822 let error_response = ErrorResponse {
823 message: "no Route matched with those values".to_owned(),
824 code: String::new(),
825 details: None,
826 hint: None,
827 };
828 let error = PostgrestUtilError::from_error_response(error_response);
829 let std_error: &dyn core::error::Error = &error;
830 assert_eq!(
831 std_error.to_string(),
832 "Custom []: no Route matched with those values"
833 );
834 }
835}