@@ -35,7 +35,11 @@ const ENCODING_SET: &AsciiSet = &CONTROLS
3535/// assert_eq!(status1.code(), status2.code());
3636/// ```
3737#[ derive( Clone ) ]
38- pub struct Status {
38+ pub struct Status ( Box < StatusInner > ) ;
39+
40+ /// Box the contents of Status to avoid large error variants
41+ #[ derive( Clone ) ]
42+ struct StatusInner {
3943 /// The gRPC status code, found in the `grpc-status` header.
4044 code : Code ,
4145 /// A relevant error message, found in the `grpc-message` header.
@@ -50,6 +54,12 @@ pub struct Status {
5054 source : Option < Arc < dyn Error + Send + Sync + ' static > > ,
5155}
5256
57+ impl StatusInner {
58+ fn into_status ( self ) -> Status {
59+ Status ( Box :: new ( self ) )
60+ }
61+ }
62+
5363/// gRPC status codes used by [`Status`].
5464///
5565/// These variants match the [gRPC status codes].
@@ -160,13 +170,14 @@ impl std::fmt::Display for Code {
160170impl Status {
161171 /// Create a new `Status` with the associated code and message.
162172 pub fn new ( code : Code , message : impl Into < String > ) -> Status {
163- Status {
173+ StatusInner {
164174 code,
165175 message : message. into ( ) ,
166176 details : Bytes :: new ( ) ,
167177 metadata : MetadataMap :: new ( ) ,
168178 source : None ,
169179 }
180+ . into_status ( )
170181 }
171182
172183 /// The operation completed successfully.
@@ -318,7 +329,7 @@ impl Status {
318329 pub fn from_error ( err : Box < dyn Error + Send + Sync + ' static > ) -> Status {
319330 Status :: try_from_error ( err) . unwrap_or_else ( |err| {
320331 let mut status = Status :: new ( Code :: Unknown , err. to_string ( ) ) ;
321- status. source = Some ( err. into ( ) ) ;
332+ status. 0 . source = Some ( err. into ( ) ) ;
322333 status
323334 } )
324335 }
@@ -361,7 +372,7 @@ impl Status {
361372 } ;
362373
363374 if let Some ( mut status) = find_status_in_source_chain ( & * err) {
364- status. source = Some ( err. into ( ) ) ;
375+ status. 0 . source = Some ( err. into ( ) ) ;
365376 return Ok ( status) ;
366377 }
367378
@@ -374,7 +385,7 @@ impl Status {
374385 let code = Self :: code_from_h2 ( & err) ;
375386
376387 let mut status = Self :: new ( code, format ! ( "h2 protocol error: {err}" ) ) ;
377- status. source = Some ( Arc :: new ( * err) ) ;
388+ status. 0 . source = Some ( Arc :: new ( * err) ) ;
378389 status
379390 }
380391
@@ -401,7 +412,7 @@ impl Status {
401412 #[ cfg( feature = "server" ) ]
402413 fn to_h2_error ( & self ) -> h2:: Error {
403414 // conservatively transform to h2 error codes...
404- let reason = match self . code {
415+ let reason = match self . code ( ) {
405416 Code :: Cancelled => h2:: Reason :: CANCEL ,
406417 _ => h2:: Reason :: INTERNAL_ERROR ,
407418 } ;
@@ -485,53 +496,56 @@ impl Status {
485496 }
486497 } ;
487498
488- Some ( Status {
489- code,
490- message,
491- details,
492- metadata : MetadataMap :: from_headers ( other_headers) ,
493- source : None ,
494- } )
499+ Some (
500+ StatusInner {
501+ code,
502+ message,
503+ details,
504+ metadata : MetadataMap :: from_headers ( other_headers) ,
505+ source : None ,
506+ }
507+ . into_status ( ) ,
508+ )
495509 }
496510
497511 /// Get the gRPC `Code` of this `Status`.
498512 pub fn code ( & self ) -> Code {
499- self . code
513+ self . 0 . code
500514 }
501515
502516 /// Get the text error message of this `Status`.
503517 pub fn message ( & self ) -> & str {
504- & self . message
518+ & self . 0 . message
505519 }
506520
507521 /// Get the opaque error details of this `Status`.
508522 pub fn details ( & self ) -> & [ u8 ] {
509- & self . details
523+ & self . 0 . details
510524 }
511525
512526 /// Get a reference to the custom metadata.
513527 pub fn metadata ( & self ) -> & MetadataMap {
514- & self . metadata
528+ & self . 0 . metadata
515529 }
516530
517531 /// Get a mutable reference to the custom metadata.
518532 pub fn metadata_mut ( & mut self ) -> & mut MetadataMap {
519- & mut self . metadata
533+ & mut self . 0 . metadata
520534 }
521535
522536 pub ( crate ) fn to_header_map ( & self ) -> Result < HeaderMap , Self > {
523- let mut header_map = HeaderMap :: with_capacity ( 3 + self . metadata . len ( ) ) ;
537+ let mut header_map = HeaderMap :: with_capacity ( 3 + self . 0 . metadata . len ( ) ) ;
524538 self . add_header ( & mut header_map) ?;
525539 Ok ( header_map)
526540 }
527541
528542 /// Add headers from this `Status` into `header_map`.
529543 pub fn add_header ( & self , header_map : & mut HeaderMap ) -> Result < ( ) , Self > {
530- header_map. extend ( self . metadata . clone ( ) . into_sanitized_headers ( ) ) ;
544+ header_map. extend ( self . 0 . metadata . clone ( ) . into_sanitized_headers ( ) ) ;
531545
532- header_map. insert ( Self :: GRPC_STATUS , self . code . to_header_value ( ) ) ;
546+ header_map. insert ( Self :: GRPC_STATUS , self . 0 . code . to_header_value ( ) ) ;
533547
534- if !self . message . is_empty ( ) {
548+ if !self . 0 . message . is_empty ( ) {
535549 let to_write = Bytes :: copy_from_slice (
536550 Cow :: from ( percent_encode ( self . message ( ) . as_bytes ( ) , ENCODING_SET ) ) . as_bytes ( ) ,
537551 ) ;
@@ -542,8 +556,8 @@ impl Status {
542556 ) ;
543557 }
544558
545- if !self . details . is_empty ( ) {
546- let details = crate :: util:: base64:: STANDARD_NO_PAD . encode ( & self . details [ ..] ) ;
559+ if !self . 0 . details . is_empty ( ) {
560+ let details = crate :: util:: base64:: STANDARD_NO_PAD . encode ( & self . 0 . details [ ..] ) ;
547561
548562 header_map. insert (
549563 Self :: GRPC_STATUS_DETAILS ,
@@ -571,18 +585,19 @@ impl Status {
571585 details : Bytes ,
572586 metadata : MetadataMap ,
573587 ) -> Status {
574- Status {
588+ StatusInner {
575589 code,
576590 message : message. into ( ) ,
577591 details,
578592 metadata,
579593 source : None ,
580594 }
595+ . into_status ( )
581596 }
582597
583598 /// Add a source error to this status.
584599 pub fn set_source ( & mut self , source : Arc < dyn Error + Send + Sync + ' static > ) -> & mut Status {
585- self . source = Some ( source) ;
600+ self . 0 . source = Some ( source) ;
586601 self
587602 }
588603
@@ -610,15 +625,18 @@ fn find_status_in_source_chain(err: &(dyn Error + 'static)) -> Option<Status> {
610625
611626 while let Some ( err) = source {
612627 if let Some ( status) = err. downcast_ref :: < Status > ( ) {
613- return Some ( Status {
614- code : status. code ,
615- message : status. message . clone ( ) ,
616- details : status. details . clone ( ) ,
617- metadata : status. metadata . clone ( ) ,
618- // Since `Status` is not `Clone`, any `source` on the original Status
619- // cannot be cloned so must remain with the original `Status`.
620- source : None ,
621- } ) ;
628+ return Some (
629+ StatusInner {
630+ code : status. 0 . code ,
631+ message : status. 0 . message . clone ( ) ,
632+ details : status. 0 . details . clone ( ) ,
633+ metadata : status. 0 . metadata . clone ( ) ,
634+ // Since `Status` is not `Clone`, any `source` on the original Status
635+ // cannot be cloned so must remain with the original `Status`.
636+ source : None ,
637+ }
638+ . into_status ( ) ,
639+ ) ;
622640 }
623641
624642 if let Some ( timeout) = err. downcast_ref :: < TimeoutExpired > ( ) {
@@ -649,6 +667,12 @@ fn find_status_in_source_chain(err: &(dyn Error + 'static)) -> Option<Status> {
649667}
650668
651669impl fmt:: Debug for Status {
670+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
671+ self . 0 . fmt ( f)
672+ }
673+ }
674+
675+ impl fmt:: Debug for StatusInner {
652676 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
653677 // A manual impl to reduce the noise of frequently empty fields.
654678 let mut builder = f. debug_struct ( "Status" ) ;
@@ -737,7 +761,7 @@ impl fmt::Display for Status {
737761
738762impl Error for Status {
739763 fn source ( & self ) -> Option < & ( dyn Error + ' static ) > {
740- self . source . as_ref ( ) . map ( |err| ( & * * err) as _ )
764+ self . 0 . source . as_ref ( ) . map ( |err| ( & * * err) as _ )
741765 }
742766}
743767
0 commit comments