Skip to content

[detector-report] TypeSwitchDetector #525

Description

@jessegall

Detector: TypeSwitchDetector

Report (why the flagged code is CORRECT and the detector is wrong):
Compiler::travels is a from-source mapper, which this skill's own 'What is NOT this sin' exempts: it turns a domain object into a DIFFERENT wire type — a Signal into an Action, a Component into a Node — and the third arm is a scalar, which can implement no interface. Signal and Component share no hierarchy, so there is no interface to put a per-type method on; the two arms are unrelated types being given their serialised form by the serialiser that owns those forms.

Cleanest design the reporter can conceive:
Exactly what stands: a private mapper on the Compiler matching the value to its wire shape, with the primitive as the default arm. Moving the behaviour onto Signal and Component would make each domain type name its own wire DTO (View\Contracts\Signal knowing View\Values\Action), which inverts the dependency the mapper exception exists to protect AND reinstates the View\Contracts to View\Values namespace cycle that was just removed by moving Confirmable down. Threading the Compiler into the types through a shared Travels interface is worse again: it hands a domain object a back-reference to the compiler that is meant to read it, and still leaves an instanceof for the scalar arm.

⚖️ 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/View/Compiler.php:85

Code (src/View/Compiler.php:85):

   82          return $state;
   83      }
   8485      private function travels(mixed $value, NodeId $node, string $name): mixed
   86      {
   87          return match (true)
   88          {
   89              $value instanceof Signal => Action::raises($value),
   90              $value instanceof Component => $this->compileAt($value, $node->under($name, $value, 0)),
   91              default => $value,
   92          };
   93      }
   94  
   95      /**
   96       * @return list<Component>
   97       */
   98      private function composed(Composition $component, NodeId $node): array
   99      {
  100          $state = $this->states->of($component);
  101  
  102          if ($this->binds($component))
  103          {
  104              $state = new Bound($state, $this->bindings, $node, $component, self::COMPOSE);
  105          }
  106  
  107          $composed = $component::{self::COMPOSE}($state);
  108  
  109          return $composed instanceof Component ? [$composed] : [...$composed];

Where: src/View/Values/Action.php:26

Code (src/View/Values/Action.php:26):

  23       * A signal that says it wants asking about carries the question with it, so the press that
  24       * would raise it puts that question up first and sends nothing until it is answered.
  25       */
→ 26      public static function raises(Signal $signal): self
  27      {
  28          return new self(
  29              ClassAlias::of($signal::class),
  30              get_object_vars($signal),
  31              $signal instanceof Confirmable ? $signal::confirmation()->toArray() : [],
  32          );
  33      }
  34  
  35      /**
  36       * @return array<string, mixed>
  37       */
  38      public function jsonSerialize(): array
  39      {
  40          return array_filter([
  41              'signal' => $this->signal,
  42              'fields' => $this->fields,
  43              'confirmation' => $this->confirmation,
  44          ], static fn (mixed $value): bool => $value !== []);
  45      }
  46  }

Where: src/View/Contracts/Component.php:10

Code (src/View/Contracts/Component.php:10):

   7  use JesseGall\Workflows\View\Attributes\Slot;
   8  
   9  abstract class Component
→ 10  {
  11      public const string DEFAULT_SLOT = 'default';
  12  
  13      public string | null $key = null;
  14  
  15      #[Slot]
  16      public array $children = [];
  17  
  18      public array $attributes = [];
  19  
  20      public function place(Component ...$children): static
  21      {
  22          $this->children = [...$this->children, ...array_values($children)];
  23  
  24          return $this;
  25      }
  26  
  27      /**
  28       * @return array<string, list<self>>
  29       */
  30      public function slots(): array
  31      {
  32          $slots = [];
  33  
  34          foreach (self::declaredSlots() as $name => $declared)

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