@@ -40,14 +40,71 @@ impl fmt::Display for ReadOnlyMode {
40
40
pub fn custom ( status : StatusCode , detail : impl Into < Cow < ' static , str > > ) -> BoxedAppError {
41
41
Box :: new ( CustomApiError {
42
42
status,
43
- detail : detail. into ( ) ,
43
+ detail : Detail :: Single ( detail. into ( ) ) ,
44
44
} )
45
45
}
46
46
47
47
#[ derive( Debug , Clone ) ]
48
48
pub struct CustomApiError {
49
49
status : StatusCode ,
50
- detail : Cow < ' static , str > ,
50
+ detail : Detail ,
51
+ }
52
+
53
+ #[ derive( Debug , Clone ) ]
54
+ enum Detail {
55
+ Empty ,
56
+ Single ( Cow < ' static , str > ) ,
57
+ Multiple ( Vec < Cow < ' static , str > > ) ,
58
+ }
59
+
60
+ impl fmt:: Display for Detail {
61
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
62
+ match self {
63
+ Self :: Empty => write ! ( f, "" ) ,
64
+ Self :: Single ( msg) => write ! ( f, "{msg}" ) ,
65
+ Self :: Multiple ( msgs) => write ! ( f, "{}" , msgs. join( ", " ) ) ,
66
+ }
67
+ }
68
+ }
69
+
70
+ impl CustomApiError {
71
+ pub fn new ( status : StatusCode ) -> Self {
72
+ Self {
73
+ status,
74
+ detail : Detail :: Empty ,
75
+ }
76
+ }
77
+
78
+ pub fn contains_errors ( & self ) -> bool {
79
+ !self . is_empty ( )
80
+ }
81
+
82
+ pub fn is_empty ( & self ) -> bool {
83
+ matches ! ( & self . detail, Detail :: Empty )
84
+ }
85
+
86
+ pub fn push ( & mut self , detail : impl Into < Cow < ' static , str > > ) -> & mut Self {
87
+ match & mut self . detail {
88
+ Detail :: Empty => {
89
+ self . detail = Detail :: Single ( detail. into ( ) ) ;
90
+ }
91
+ Detail :: Single ( msg) => {
92
+ let msg = msg. clone ( ) ;
93
+ self . detail = Detail :: Multiple ( vec ! [ msg, detail. into( ) ] ) ;
94
+ }
95
+ Detail :: Multiple ( msgs) => {
96
+ msgs. push ( detail. into ( ) ) ;
97
+ }
98
+ }
99
+
100
+ self
101
+ }
102
+ }
103
+
104
+ impl From < CustomApiError > for BoxedAppError {
105
+ fn from ( value : CustomApiError ) -> Self {
106
+ Box :: new ( value)
107
+ }
51
108
}
52
109
53
110
impl fmt:: Display for CustomApiError {
@@ -58,7 +115,33 @@ impl fmt::Display for CustomApiError {
58
115
59
116
impl AppError for CustomApiError {
60
117
fn response ( & self ) -> Response {
61
- json_error ( & self . detail , self . status )
118
+ #[ derive( Serialize ) ]
119
+ struct ErrorContent {
120
+ detail : Cow < ' static , str > ,
121
+ }
122
+
123
+ impl From < & Cow < ' static , str > > for ErrorContent {
124
+ fn from ( value : & Cow < ' static , str > ) -> Self {
125
+ Self {
126
+ detail : value. clone ( ) ,
127
+ }
128
+ }
129
+ }
130
+
131
+ #[ derive( Serialize ) ]
132
+ struct ErrorBody {
133
+ errors : Vec < ErrorContent > ,
134
+ }
135
+
136
+ let body = ErrorBody {
137
+ errors : match & self . detail {
138
+ Detail :: Empty => Vec :: new ( ) ,
139
+ Detail :: Single ( msg) => vec ! [ msg. into( ) ] ,
140
+ Detail :: Multiple ( msgs) => msgs. iter ( ) . map ( |msg| msg. into ( ) ) . collect ( ) ,
141
+ } ,
142
+ } ;
143
+
144
+ ( self . status , Json ( body) ) . into_response ( )
62
145
}
63
146
}
64
147
0 commit comments