Lines 96.66% 29 / 30
Functions and Methods 83.33% 5 / 6
Classes and Traits 0.00% 0 / 1
Name Lines Functions and Methods CRAP Classes and Traits
DomainServicesEmitter 96.66% 29 / 30 83.33% 5 / 6 15 0.00% 0 / 1
 __construct 0.00% 0 / 1 0.00% 0 / 1 2
 supportsDomain 100.00% 1 / 1 100.00% 1 / 1 1
 emitFile 100.00% 19 / 19 100.00% 1 / 1 5
 anyTargetable 100.00% 4 / 4 100.00% 1 / 1 3
 anyReturnsResponse 100.00% 4 / 4 100.00% 1 / 1 3
 listReservedMemberNames 100.00% 1 / 1 100.00% 1 / 1 2
1<?php
2
3declare(strict_types=1);
4
5namespace Stewart\Codegen\Emitter\Service;
6
7use Stewart\Codegen\Emitter\DomainFileEmitter;
8use Stewart\Codegen\Emitter\EmitContext;
9use Stewart\Codegen\Model\DomainModel;
10use Stewart\Codegen\Output\GeneratedFile;
11use Stewart\Codegen\Php\MemberScope;
12use Stewart\Codegen\Php\ReservesMemberNames;
13use Stewart\Codegen\Service\ServiceModel;
14use Stewart\Contracts\HaContext;
15use Stewart\Contracts\Service\ServiceResponse;
16use Stewart\Contracts\Service\ServiceTargetSource;
17use Stewart\Contracts\State\EventContext;
18
19final readonly class DomainServicesEmitter implements DomainFileEmitter, ReservesMemberNames
20{
21    private const array RESERVED_MEMBER_NAMES = ['ha'];
22
23    public function __construct(private ServiceMethodEmitter $methods) {}
24
25    public function supportsDomain(DomainModel $domain): bool
26    {
27        return $domain->hasServices();
28    }
29
30    public function emitFile(DomainModel $domain, EmitContext $context): GeneratedFile
31    {
32        $namespace = $context->createNamespace();
33        $context->importClass($namespace, HaContext::class, $domain->getServicesClass());
34
35        if ($this->anyReturnsResponse($domain)) {
36            $context->importClass($namespace, ServiceResponse::class, $domain->getServicesClass());
37        }
38
39        if (array_any($domain->services, static fn(ServiceModel $service): bool => !$service->definition->returnsResponse)) {
40            $context->importClass($namespace, EventContext::class, $domain->getServicesClass());
41        }
42
43        if ($this->anyTargetable($domain)) {
44            $context->importClass($namespace, ServiceTargetSource::class, $domain->getServicesClass());
45        }
46
47        $class = $namespace->addClass($domain->getServicesClass())
48            ->setFinal()
49            ->setReadOnly()
50            ->addComment(\sprintf('The `%s` services.', $domain->domain));
51
52        $class->addMethod('__construct')
53            ->addPromotedParameter('ha')
54            ->setType(HaContext::class)
55            ->setPrivate();
56
57        foreach ($domain->services as $service) {
58            $this->methods->addDomainServiceMethod($class, $service);
59        }
60
61        return $context->printFile($namespace, $domain->getServicesClass());
62    }
63
64    private function anyTargetable(DomainModel $domain): bool
65    {
66        foreach ($domain->services as $service) {
67            if ($service->definition->target->isTargetable) {
68                return true;
69            }
70        }
71
72        return false;
73    }
74
75    private function anyReturnsResponse(DomainModel $domain): bool
76    {
77        foreach ($domain->services as $service) {
78            if ($service->definition->returnsResponse) {
79                return true;
80            }
81        }
82
83        return false;
84    }
85
86    public function listReservedMemberNames(MemberScope $scope): array
87    {
88        return $scope === MemberScope::DomainServices ? self::RESERVED_MEMBER_NAMES : [];
89    }
90}