Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
51 / 51
100.00% covered (success)
100.00%
9 / 9
CRAP
100.00% covered (success)
100.00%
1 / 1
Registry
100.00% covered (success)
100.00%
51 / 51
100.00% covered (success)
100.00%
9 / 9
11
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 register
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getHandler
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 default
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 resolveDefaults
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 withDefaults
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
1
 registerDefaultProperties
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
1 / 1
1
 className
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 deriveHandle
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5namespace Cosray\Collection\Schema;
6
7use Cosray\CollectionListMeta;
8use Cosray\Schema\Badge;
9use Cosray\Schema\Blueprints;
10use Cosray\Schema\Handle;
11use Cosray\Schema\Hidden;
12use Cosray\Schema\Icon;
13use Cosray\Schema\Label;
14use Cosray\Schema\Listing;
15use Cosray\Schema\Order;
16use Cosray\Schema\Permission;
17
18class Registry
19{
20    /** @var array<class-string, Handler> */
21    private array $handlers = [];
22
23    /** @var array<string, callable(class-string, array<string, mixed>): mixed> */
24    private array $defaults = [];
25
26    public function __construct()
27    {
28        $this->registerDefaultProperties();
29    }
30
31    /** @param class-string $schema */
32    public function register(string $schema, Handler $handler): void
33    {
34        $this->handlers[$schema] = $handler;
35    }
36
37    public function getHandler(object $schema): ?Handler
38    {
39        return $this->handlers[$schema::class] ?? null;
40    }
41
42    /**
43     * @param callable(class-string, array<string, mixed>): mixed $resolver
44     */
45    public function default(string $key, callable $resolver): void
46    {
47        $this->defaults[$key] = $resolver;
48    }
49
50    /**
51     * @param class-string $class
52     * @param array<string, mixed> $resolved
53     * @return array<string, mixed>
54     */
55    public function resolveDefaults(string $class, array $resolved): array
56    {
57        $properties = $resolved;
58
59        foreach ($this->defaults as $key => $resolver) {
60            if (array_key_exists($key, $properties)) {
61                continue;
62            }
63
64            $properties[$key] = $resolver($class, $properties);
65        }
66
67        return $properties;
68    }
69
70    public static function withDefaults(): self
71    {
72        $registry = new self();
73        $registry->register(Handle::class, new HandleHandler());
74        $registry->register(Label::class, new LabelHandler());
75        $registry->register(Icon::class, new IconHandler());
76        $registry->register(Badge::class, new BadgeHandler());
77        $registry->register(Permission::class, new PermissionHandler());
78        $registry->register(Hidden::class, new HiddenHandler());
79        $registry->register(Order::class, new OrderHandler());
80        $registry->register(Listing::class, new ListingHandler());
81        $registry->register(Blueprints::class, new BlueprintsHandler());
82
83        return $registry;
84    }
85
86    private function registerDefaultProperties(): void
87    {
88        $this->default('handle', static fn(
89            string $class,
90            array $properties,
91        ): string => self::deriveHandle(self::className($class)));
92        $this->default(
93            'label',
94            static fn(
95                string $class,
96                array $properties,
97            ): string => (string) preg_replace('/(?<!^)[A-Z]/', ' $0', self::className($class)),
98        );
99        $this->default('icon', static fn(string $class, array $properties): ?array => null);
100        $this->default('badge', static fn(string $class, array $properties): ?string => null);
101        $this->default('permission', static fn(string $class, array $properties): ?string => null);
102        $this->default('hidden', static fn(string $class, array $properties): bool => false);
103        $this->default('order', static fn(string $class, array $properties): int => 0);
104        $this->default(
105            'listing',
106            static fn(string $class, array $properties): CollectionListMeta => new CollectionListMeta(),
107        );
108        $this->default('blueprints', static fn(string $class, array $properties): array => []);
109    }
110
111    /**
112     * @param class-string $class
113     */
114    private static function className(string $class): string
115    {
116        return basename(str_replace('\\', '/', $class));
117    }
118
119    private static function deriveHandle(string $className): string
120    {
121        return ltrim(
122            strtolower(preg_replace(
123                '/[A-Z]([A-Z](?![a-z]))*/',
124                '-$0',
125                $className,
126            )),
127            '-',
128        );
129    }
130}