11//! Error type implementation.
22
3- use :: alloc:: { string :: ToString , vec} ;
3+ use :: alloc:: { borrow :: Cow , boxed :: Box , vec, vec :: Vec } ;
44use :: core:: {
55 any:: Any ,
66 error:: Error ,
77 fmt:: { Debug , Display , Formatter , Result as FmtResult } ,
88 panic:: Location ,
99} ;
1010
11- use crate :: features:: { AnyDebugSendSync , Box , ErrorSendSync , String , Vec } ;
11+ use crate :: features:: { AnyDebugSendSync , ErrorSendSync } ;
1212
1313/// Error information for humans.
1414/// Error message with location information.
1515#[ derive( Debug ) ]
1616pub ( crate ) struct HumanInfo {
1717 /// Message text.
18- pub ( crate ) message : String ,
18+ pub ( crate ) message : Cow < ' static , str > ,
1919 /// Location of occurrence.
2020 pub ( crate ) location : & ' static Location < ' static > ,
2121}
@@ -37,9 +37,9 @@ pub(crate) enum Info {
3737 /// Contextual information for machines.
3838 Machine ( MachineInfo ) ,
3939}
40- // Ensure size of Context is as expected. Can be adjusted though .
40+ // Ensure niche-optimization is active .
4141const _: ( ) = {
42- assert ! ( size_of:: <Info >( ) == 32 ) ;
42+ assert ! ( size_of:: <Info >( ) == size_of :: < HumanInfo > ( ) ) ;
4343} ;
4444
4545/// Generic rich error type for use within `Result`s, for libraries and applications.
@@ -138,27 +138,26 @@ impl CtxError {
138138 #[ track_caller]
139139 #[ must_use]
140140 #[ inline]
141- pub fn new < T : ToString > ( message : T ) -> Self {
142- let infos = vec ! [ Info :: Human ( HumanInfo {
143- message: message. to_string( ) ,
144- location: Location :: caller( ) ,
145- } ) ] ;
141+ pub fn new < C > ( context : C ) -> Self
142+ where
143+ C : Into < Cow < ' static , str > > ,
144+ {
145+ let infos =
146+ vec ! [ Info :: Human ( HumanInfo { message: context. into( ) , location: Location :: caller( ) } ) ] ;
146147 Self ( CtxErrorImpl { infos, ..Default :: default ( ) } )
147148 }
148149
149150 /// Create new error from source error.
150151 #[ track_caller]
151152 #[ must_use]
152153 #[ inline]
153- pub fn new_with_source < T , E > ( message : T , source : E ) -> Self
154+ pub fn new_with_source < C , E > ( context : C , source : E ) -> Self
154155 where
155- T : ToString ,
156+ C : Into < Cow < ' static , str > > ,
156157 E : ErrorSendSync + ' static ,
157158 {
158- let infos = vec ! [ Info :: Human ( HumanInfo {
159- message: message. to_string( ) ,
160- location: Location :: caller( ) ,
161- } ) ] ;
159+ let infos =
160+ vec ! [ Info :: Human ( HumanInfo { message: context. into( ) , location: Location :: caller( ) } ) ] ;
162161 Self ( CtxErrorImpl { infos, source : Some ( Box :: new ( source) ) } )
163162 }
164163
@@ -178,7 +177,7 @@ impl CtxError {
178177 #[ inline]
179178 pub fn context < C > ( self , context : C ) -> Self
180179 where
181- C : ToString ,
180+ C : Into < Cow < ' static , str > > ,
182181 {
183182 Self ( self . 0 . context ( context) )
184183 }
@@ -265,9 +264,9 @@ impl CtxErrorImpl {
265264 #[ inline]
266265 pub fn context < C > ( mut self , context : C ) -> Self
267266 where
268- C : ToString ,
267+ C : Into < Cow < ' static , str > > ,
269268 {
270- let context = HumanInfo { message : context. to_string ( ) , location : Location :: caller ( ) } ;
269+ let context = HumanInfo { message : context. into ( ) , location : Location :: caller ( ) } ;
271270 self . infos . push ( Info :: Human ( context) ) ;
272271 self
273272 }
@@ -292,20 +291,34 @@ impl CtxErrorImpl {
292291 /// This will override existing attachments of the same type. If you want to add attachments of
293292 /// the same type, use `attach` instead.
294293 #[ must_use]
295- pub fn attach_override < C > ( mut self , context : C ) -> Self
294+ pub fn attach_override < C > ( mut self , mut context : C ) -> Self
296295 where
297296 C : AnyDebugSendSync + ' static ,
298297 {
299- let context = MachineInfo { attachment : Box :: new ( context ) } ;
300- self . infos . retain ( |info| match info {
301- Info :: Human ( _ ) => true ,
298+ let mut inserted = false ;
299+ # [ expect ( trivial_casts , reason = "Not that trivial as it seems? False positive" ) ]
300+ self . infos . retain_mut ( |info| match info {
302301 Info :: Machine ( ctx) => {
303- #[ expect( trivial_casts, reason = "Not that trivial as it seems? False positive" ) ]
304- let any = ( ctx. attachment . as_ref ( ) ) as & ( dyn Any + ' static ) ;
305- !any. is :: < C > ( )
302+ if let Some ( content) =
303+ ( ctx. attachment . as_mut ( ) as & mut ( dyn Any + ' static ) ) . downcast_mut :: < C > ( )
304+ {
305+ if !inserted {
306+ core:: mem:: swap ( content, & mut context) ;
307+ inserted = true ;
308+ true // First attachment of same type, was replaced with new value, so keep it.
309+ } else {
310+ false // Another attachment of the same type, remove duplicate.
311+ }
312+ } else {
313+ true // Attachment of different type.
314+ }
306315 }
316+ _ => true ,
307317 } ) ;
308- self . infos . push ( Info :: Machine ( context) ) ;
318+ if !inserted {
319+ // No existing attachment of the same type was found to be replaced, so add a new one.
320+ self . infos . push ( Info :: Machine ( MachineInfo { attachment : Box :: new ( context) } ) ) ;
321+ }
309322 self
310323 }
311324
0 commit comments