Skip to content

[detector-report] TypeSwitchDetector #453

Description

@jessegall

Detector: TypeSwitchDetector

Report (why the flagged code is CORRECT and the detector is wrong):
ClientRun is a translator between two protocols, and dispatching on the SOURCE type is what a translator is. The usual fix — move the behaviour onto the type — is unavailable in both directions: Scene's primitives must not know about the Client domain (Client is a new root-level domain that Scene will be deleted in favour of, so a toClient() on each Scene primitive would make the dying side depend on the living one), and Client's natives must not know about Scene's. A visitor would require Scene's primitives to accept a visitor, which is the same dependency wearing a pattern. The class is scaffolding with a defined death: when the pipes emit Client instructions directly it is deleted whole, and the match goes with it.

Cleanest design the reporter can conceive:
A single match(true) over instanceof in one private method, each arm delegating to a small typed method (mount(Mount), animate(Play), listen(Listen)) so the concrete type is known inside each. The dispatch is stated once, in one place, and every arm is a named method that reads its source primitive's typed fields — no string keys, no reflection, no registry indirection that hides which source type maps to which target.

⚖️ Maintainer litmus: a valid detector-report needs the flagged code to ALREADY BE the
cleanest design. If the design above differs from the flagged code at all, THAT design is
the owed fix — close this report; the fix is still owed.

Where: src/Scene/Pipeline/ClientRun.php:67

Code (src/Scene/Pipeline/ClientRun.php:67):

  64  
  65      private function translated(Primitive | Sequence $step): TranslatedRun
  66      {
→ 67          if ($step instanceof Sequence)
  68          {
  69              $steps = $this->allOf($step->steps());
  70  
  71              return self::emit($steps->run, $steps);
  72          }
  73  
  74          return match (true)
  75          {
  76              $step instanceof Navigate => new TranslatedRun([new ClientNavigate($step->url)], []),
  77              $step instanceof Mount => $this->mount($step),
  78              $step instanceof Listen => $this->listener($step),
  79              $step instanceof Toggle => $this->branch($step),
  80              $step instanceof Play => $this->animate($step),
  81              $step instanceof Unmount => new TranslatedRun([new ClientUnmount($step->node)], []),
  82              $step instanceof Anchor => new TranslatedRun([self::anchor($step)], []),
  83              $step instanceof Hotkey => $this->hotkey($step),
  84              $step instanceof Mark => new TranslatedRun([self::modify($step)], []),
  85              $step instanceof Send => self::raise($step),
  86              default => new TranslatedRun([], [$step->command()->value], isWhole: false),
  87          };
  88      }
  89  
  90      private function mount(Mount $mount): TranslatedRun
  91      {

Filed via commandments report from a consumer project.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions