text: from dataclasses import dataclass
  from typing import TYPE_CHECKING, Optional
  
  from httpie.context import Environment
  
  if TYPE_CHECKING:
      from rich.console import Console
  
  
  @dataclass
  class BaseDisplay:
      env: Environment
  
      def start(
          self, *, total: Optional[float], at: float, description: str
      ) -> None:
          ...
  
      def update(self, steps: float) -> None:
          ...
  
      def stop(self, time_spent: float) -> None:
          ...
  
      @property
      def console(self) -> 'Console':
          """Returns the default console to be used with displays (stderr)."""
          return self.env.rich_error_console
  
      def _print_summary(
          self, is_finished: bool, observed_steps: int, time_spent: float
      ):
          from rich import filesize
  
          if is_finished:
              verb = 'Done'
          else:
              verb = 'Interrupted'
  
          total_size = filesize.decimal(observed_steps)
          avg_speed = filesize.decimal(observed_steps / time_spent)
  
          minutes, seconds = divmod(time_spent, 60)
          hours, minutes = divmod(int(minutes), 60)
          if hours:
              total_time = f'{hours:d}:{minutes:02d}:{seconds:0.5f}'
          else:
              total_time = f'{minutes:02d}:{seconds:0.5f}'
  
          self.console.print(
              f'[progress.description]{verb}. {total_size} in {total_time} ({avg_speed}/s)'
          )
  
  
  class DummyDisplay(BaseDisplay):
      """
      A dummy display object to be used when the progress bars,
      spinners etc. are disabled globally (or during tests).
      """
  
  
  class StatusDisplay(BaseDisplay):
      def start(
          self, *, total: Optional[float], at: float, description: str
      ) -> None:
          self.observed = at
          self.description = (
              f'[progress.description]{description}[/progress.description]'
          )
  
          self.status = self.console.status(self.description, spinner='line')
          self.status.start()
  
      def update(self, steps: float) -> None:
          from rich import filesize
  
          self.observed += steps
  
          observed_amount, observed_unit = filesize.decimal(
              self.observed
          ).split()
          self.status.update(
              status=f'{self.description} [progress.download]{observed_amount}/? {observed_unit}[/progress.download]'
          )
  
      def stop(self, time_spent: float) -> None:
          self.status.stop()
          self.console.print(self.description)
          if time_spent:
              self._print_summary(
                  is_finished=True,
                  observed_steps=self.observed,
                  time_spent=time_spent,
              )
  
  
  class ProgressDisplay(BaseDisplay):
      def start(
          self, *, total: Optional[float], at: float, description: str
      ) -> None:
          from rich.progress import (
              Progress,
              BarColumn,
              DownloadColumn,
              TimeRemainingColumn,
              TransferSpeedColumn,
          )
  
          assert total is not None
          self.console.print(f'[progress.description]{description}')
          self.progress_bar = Progress(
              '[',
              BarColumn(),
              ']',
              '[progress.percentage]{task.percentage:>3.0f}%',
              '(',
              DownloadColumn(),
              ')',
              TimeRemainingColumn(),
              TransferSpeedColumn(),
              console=self.console,
              transient=True,
          )
          self.progress_bar.start()
          self.transfer_task = self.progress_bar.add_task(
              description, completed=at, total=total
          )
  
      def update(self, steps: float) -> None:
          self.progress_bar.advance(self.transfer_task, steps)
  
      def stop(self, time_spent: Optional[float]) -> None:
          self.progress_bar.stop()
  
          if time_spent:
              [task] = self.progress_bar.tasks
              self._print_summary(
                  is_finished=task.finished,
                  observed_steps=task.completed,
                  time_spent=time_spent,
              )
  