code: func ChainUnaryClient(interceptors ...grpc.UnaryClientInterceptor) grpc.UnaryClientInterceptor {
  	n := len(interceptors)
  
  	if n > 1 {
  		lastI := n - 1
  		return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
  			var (
  				chainHandler grpc.UnaryInvoker
  				curI         int
  			)
  
  			chainHandler = func(currentCtx context.Context, currentMethod string, currentReq, currentRepl interface{}, currentConn *grpc.ClientConn, currentOpts ...grpc.CallOption) error {
  				if curI == lastI {
  					return invoker(currentCtx, currentMethod, currentReq, currentRepl, currentConn, currentOpts...)
  				}
  				curI++
  				err := interceptors[curI](currentCtx, currentMethod, currentReq, currentRepl, currentConn, chainHandler, currentOpts...)
  				curI--
  				return err
  			}
  
  			return interceptors[0](ctx, method, req, reply, cc, chainHandler, opts...)
  		}
  	}
  
  	if n == 1 {
  		return interceptors[0]
  	}
  
  	// n == 0; Dummy interceptor maintained for backward compatibility to avoid returning nil.
  	return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
  		return invoker(ctx, method, req, reply, cc, opts...)
  	}
  }
docs: // ChainUnaryClient creates a single interceptor out of a chain of many interceptors.
  //
  // Execution is done in left-to-right order, including passing of context.
  // For example ChainUnaryClient(one, two, three) will execute one before two before three.