@@ -126,10 +126,8 @@ impl Retryability {
126126 }
127127}
128128
129- /// A trait modeling the behavior of a server side operation.
130- ///
131- /// No methods in this trait should have default behaviors to ensure that wrapper operations
132- /// replicate all behavior. Default behavior is provided by the `OperationDefault` trait.
129+ /// A trait modeling the behavior of a server side operation. This should not be implemented
130+ /// directly; use either the `BaseOperation` or `WrappedOperation` traits.
133131pub ( crate ) trait Operation {
134132 /// The output type of this operation.
135133 type O ;
@@ -291,9 +289,9 @@ impl<T: Send + Sync> From<&Collection<T>> for OperationTarget {
291289 }
292290}
293291
294- // A mirror of the `Operation` trait, with default behavior where appropriate. Should only be
292+ // A mirror of the `Operation` trait, with default behavior where appropriate. Should be
295293// implemented by operation types that do not delegate to other operations.
296- pub ( crate ) trait OperationWithDefaults : Send + Sync {
294+ pub ( crate ) trait BaseOperation : Send + Sync {
297295 /// The output type of this operation.
298296 type O ;
299297
@@ -411,66 +409,193 @@ pub(crate) trait OperationWithDefaults: Send + Sync {
411409 type Otel : crate :: otel:: OtelWitness < Op = Self > ;
412410}
413411
414- impl < T : OperationWithDefaults > Operation for T
412+ /// We'd like both `BaseOperation` and `WrappedOperation` to automatically implement `Operation`.
413+ /// However, Rust only allows one blanket impl per trait, so we can't do both
414+ /// `impl<T: BaseOperation> Operation for T` and `impl<T: WrappedOperation> Operation for T`.
415+ ///
416+ /// To work around this, `OperationDispatch` provides an indirection: the one blanket impl for
417+ /// `Operation` is a generic `OperationDispatch<K>`, and each specialized version of the trait
418+ /// has a blanket impl for `OperationDispatch` with a specific type parameter.
419+ ///
420+ /// `OperationImpl` is a helper trait for this setup that binds specific impls to the appropriate
421+ /// dispatch type.
422+ pub ( crate ) trait OperationDispatch < Kind > {
423+ type O ;
424+ const NAME : & ' static CStr ;
425+ const ZERO_COPY : bool ;
426+ fn build ( & mut self , description : & StreamDescription ) -> Result < Command > ;
427+ fn extract_at_cluster_time ( & self , response : & RawDocument ) -> Result < Option < Timestamp > > ;
428+ fn handle_response < ' a > (
429+ & ' a self ,
430+ response : Cow < ' a , RawCommandResponse > ,
431+ context : ExecutionContext < ' a > ,
432+ ) -> BoxFuture < ' a , Result < Self :: O > > ;
433+ fn handle_error ( & self , error : Error ) -> Result < Self :: O > ;
434+ fn selection_criteria ( & self ) -> Feature < & SelectionCriteria > ;
435+ fn read_concern ( & self ) -> Feature < & ReadConcern > ;
436+ fn write_concern ( & self ) -> Feature < & WriteConcern > ;
437+ fn supports_sessions ( & self ) -> bool ;
438+ fn retryability ( & self , options : & ClientOptions ) -> Retryability ;
439+ fn is_backpressure_retryable ( & self , options : & ClientOptions ) -> bool ;
440+ fn update_for_retry ( & mut self , retry : Option < & Retry > ) ;
441+ fn override_criteria ( & self ) -> OverrideCriteriaFn ;
442+ fn pinned_connection ( & self ) -> Option < & PinnedConnectionHandle > ;
443+ fn name ( & self ) -> & CStr ;
444+ fn target ( & self ) -> OperationTarget ;
445+ #[ cfg( feature = "opentelemetry" ) ]
446+ type Otel : crate :: otel:: OtelWitness < Op = Self > ;
447+ }
448+
449+ macro_rules! operation_dispatch {
450+ ( $trait: path, $tag: ty, $handle_response: ident) => {
451+ impl <T : $trait> OperationDispatch <$tag> for T {
452+ operation_dispatch_body! { $trait, $handle_response }
453+ }
454+ } ;
455+ }
456+
457+ macro_rules! operation_dispatch_body {
458+ ( $trait: path, $handle_response: ident) => {
459+ type O = <T as $trait>:: O ;
460+ const NAME : & ' static CStr = <T as $trait>:: NAME ;
461+ const ZERO_COPY : bool = <T as $trait>:: ZERO_COPY ;
462+ fn build( & mut self , description: & StreamDescription ) -> Result <Command > {
463+ <T as $trait>:: build( self , description)
464+ }
465+ fn extract_at_cluster_time( & self , response: & RawDocument ) -> Result <Option <Timestamp >> {
466+ <T as $trait>:: extract_at_cluster_time( self , response)
467+ }
468+ fn handle_response<' a>(
469+ & ' a self ,
470+ response: Cow <' a, RawCommandResponse >,
471+ context: ExecutionContext <' a>,
472+ ) -> BoxFuture <' a, Result <Self :: O >> {
473+ <T as $trait>:: $handle_response( self , response, context)
474+ }
475+ fn handle_error( & self , error: Error ) -> Result <Self :: O > {
476+ <T as $trait>:: handle_error( self , error)
477+ }
478+ fn selection_criteria( & self ) -> Feature <& SelectionCriteria > {
479+ <T as $trait>:: selection_criteria( self )
480+ }
481+ fn read_concern( & self ) -> Feature <& ReadConcern > {
482+ <T as $trait>:: read_concern( self )
483+ }
484+ fn write_concern( & self ) -> Feature <& WriteConcern > {
485+ <T as $trait>:: write_concern( self )
486+ }
487+ fn supports_sessions( & self ) -> bool {
488+ <T as $trait>:: supports_sessions( self )
489+ }
490+ fn retryability( & self , options: & ClientOptions ) -> Retryability {
491+ <T as $trait>:: retryability( self , options)
492+ }
493+ fn is_backpressure_retryable( & self , options: & ClientOptions ) -> bool {
494+ <T as $trait>:: is_backpressure_retryable( self , options)
495+ }
496+ fn update_for_retry( & mut self , retry: Option <& Retry >) {
497+ <T as $trait>:: update_for_retry( self , retry)
498+ }
499+ fn override_criteria( & self ) -> OverrideCriteriaFn {
500+ <T as $trait>:: override_criteria( self )
501+ }
502+ fn pinned_connection( & self ) -> Option <& PinnedConnectionHandle > {
503+ <T as $trait>:: pinned_connection( self )
504+ }
505+ fn name( & self ) -> & CStr {
506+ <T as $trait>:: name( self )
507+ }
508+ fn target( & self ) -> OperationTarget {
509+ <T as $trait>:: target( self )
510+ }
511+ #[ cfg( feature = "opentelemetry" ) ]
512+ type Otel = <T as $trait>:: Otel ;
513+ } ;
514+ }
515+
516+ pub ( crate ) trait OperationImpl {
517+ type Kind ;
518+ }
519+
520+ impl < K , T > Operation for T
415521where
416- T : Send + Sync ,
522+ T : OperationImpl < Kind = K > + OperationDispatch < K > + Send + Sync ,
417523{
418- type O = T :: O ;
419- const NAME : & ' static CStr = T :: NAME ;
420- const ZERO_COPY : bool = T :: ZERO_COPY ;
421- fn build ( & mut self , description : & StreamDescription ) -> Result < Command > {
422- self . build ( description)
423- }
424- fn extract_at_cluster_time ( & self , response : & RawDocument ) -> Result < Option < Timestamp > > {
425- self . extract_at_cluster_time ( response)
426- }
524+ operation_dispatch_body ! { OperationDispatch <K >, handle_response }
525+ }
526+
527+ pub ( crate ) struct Base ;
528+
529+ operation_dispatch ! { BaseOperation , Base , handle_response_async }
530+
531+ /// A trait for operations that delegate most behavior to another wrapped operation.
532+ pub ( crate ) trait WrappedOperation {
533+ // Required
534+ type Wrapped : Operation ;
535+ type O ;
536+ #[ cfg( feature = "opentelemetry" ) ]
537+ type Otel : crate :: otel:: OtelWitness < Op = Self > ;
538+
539+ fn wrapped ( & self ) -> & Self :: Wrapped ;
540+ fn wrapped_mut ( & mut self ) -> & mut Self :: Wrapped ;
541+
427542 fn handle_response < ' a > (
428543 & ' a self ,
429544 response : Cow < ' a , RawCommandResponse > ,
430545 context : ExecutionContext < ' a > ,
431- ) -> BoxFuture < ' a , Result < Self :: O > > {
432- self . handle_response_async ( response, context)
546+ ) -> BoxFuture < ' a , Result < Self :: O > > ;
547+
548+ // Delegated to wrapped
549+ const NAME : & ' static CStr = Self :: Wrapped :: NAME ;
550+ const ZERO_COPY : bool = Self :: Wrapped :: ZERO_COPY ;
551+ fn build ( & mut self , description : & StreamDescription ) -> Result < Command > {
552+ self . wrapped_mut ( ) . build ( description)
433553 }
434554 fn handle_error ( & self , error : Error ) -> Result < Self :: O > {
435- self . handle_error ( error)
555+ Err ( error)
556+ }
557+ fn extract_at_cluster_time ( & self , response : & RawDocument ) -> Result < Option < Timestamp > > {
558+ self . wrapped ( ) . extract_at_cluster_time ( response)
436559 }
437560 fn selection_criteria ( & self ) -> Feature < & SelectionCriteria > {
438- self . selection_criteria ( )
561+ self . wrapped ( ) . selection_criteria ( )
439562 }
440563 fn read_concern ( & self ) -> Feature < & ReadConcern > {
441- self . read_concern ( )
564+ self . wrapped ( ) . read_concern ( )
442565 }
443566 fn write_concern ( & self ) -> Feature < & WriteConcern > {
444- self . write_concern ( )
567+ self . wrapped ( ) . write_concern ( )
445568 }
446569 fn supports_sessions ( & self ) -> bool {
447- self . supports_sessions ( )
570+ self . wrapped ( ) . supports_sessions ( )
448571 }
449572 fn retryability ( & self , options : & ClientOptions ) -> Retryability {
450- self . retryability ( options)
573+ self . wrapped ( ) . retryability ( options)
451574 }
452575 fn is_backpressure_retryable ( & self , options : & ClientOptions ) -> bool {
453- self . is_backpressure_retryable ( options)
576+ self . wrapped ( ) . is_backpressure_retryable ( options)
454577 }
455578 fn update_for_retry ( & mut self , retry : Option < & Retry > ) {
456- self . update_for_retry ( retry)
579+ self . wrapped_mut ( ) . update_for_retry ( retry) ;
457580 }
458581 fn override_criteria ( & self ) -> OverrideCriteriaFn {
459- self . override_criteria ( )
582+ self . wrapped ( ) . override_criteria ( )
460583 }
461584 fn pinned_connection ( & self ) -> Option < & PinnedConnectionHandle > {
462- self . pinned_connection ( )
585+ self . wrapped ( ) . pinned_connection ( )
463586 }
464587 fn name ( & self ) -> & CStr {
465- self . name ( )
588+ self . wrapped ( ) . name ( )
466589 }
467590 fn target ( & self ) -> OperationTarget {
468- self . target ( )
591+ self . wrapped ( ) . target ( )
469592 }
470- #[ cfg( feature = "opentelemetry" ) ]
471- type Otel = <Self as OperationWithDefaults >:: Otel ;
472593}
473594
595+ pub ( crate ) struct Wrapped ;
596+
597+ operation_dispatch ! { WrappedOperation , Wrapped , handle_response }
598+
474599fn should_redact_body ( body : & RawDocumentBuf ) -> bool {
475600 if let Some ( Ok ( ( command_name, _) ) ) = body. into_iter ( ) . next ( ) {
476601 HELLO_COMMAND_NAMES . contains ( command_name. to_lowercase ( ) . as_str ( ) )
0 commit comments