Skip to content

Commit 4d004ac

Browse files
committed
Add more type builders page
1 parent 1726499 commit 4d004ac

File tree

2 files changed

+126
-2
lines changed

2 files changed

+126
-2
lines changed
Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,57 @@
11
# Custom Type Builder
22

3-
// TODO
3+
To create a custom type, it may make sense to create a factory (i.e., a type
4+
builder). This ensures that the [type object](types.md) is created correctly
5+
and with the required constructor arguments.
6+
7+
The builder object compiles the type based on configuration rules and external
8+
parameters, so don't be afraid to move all type assembly logic into this class.
9+
10+
For a fully custom implementation of the type builder, one should implement
11+
the interface `TypeLang\Mapper\Type\Builder\TypeBuilderInterface`, which
12+
requires the implementation of two methods:
13+
- `isSupported()` - Must return `bool(true)` if the specified type builder can
14+
build [the `TypeInterface`](types.md)
15+
- `build()` - Creates [a `TypeInterface` instance](types.md)
16+
17+
```php
18+
use TypeLang\Mapper\Type\Parser\TypeParserInterface;
19+
use TypeLang\Mapper\Type\Repository\TypeRepositoryInterface;
20+
use TypeLang\Mapper\Type\TypeInterface;
21+
use TypeLang\Parser\Node\Stmt\TypeStatement;
22+
23+
final readonly class CustomTypeBuilder implements TypeBuilderInterface
24+
{
25+
public function isSupported(TypeStatement $statement): bool
26+
{
27+
// TODO: Should match the TypeStatement AST object
28+
}
29+
30+
public function build(
31+
TypeStatement $statement,
32+
TypeRepositoryInterface $types,
33+
TypeParserInterface $parser,
34+
): TypeInterface {
35+
// TODO: Should create the TypeInterface instance
36+
}
37+
}
38+
```
39+
40+
Additionally, don't forget that you can pass additional external
41+
parameters to the type builder's constructor. For example,
42+
a JSON Schema Validator, if you want to create a type that validates
43+
a JSON inside a string, like: `json<"schema.json">`
44+
45+
```php
46+
final readonly class JsonTypeBuilder implements TypeBuilderInterface
47+
{
48+
public function __construct(
49+
private JsonSchemaValidator $validator,
50+
) {}
51+
}
52+
```
53+
54+
<warning>
55+
This is just an example, it is not necessary to pass the container
56+
to each type builder.
57+
</warning>
Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,73 @@
11
# Type Builders
22

3-
// TODO
3+
The Type Builder is a factory class that acts as a layer between the
4+
[type declaration](basic-types.md) (like `non-empty-string`) and the
5+
[type instance](types.md) (like `NonEmptyStringType`).
6+
7+
To register types in the `Mapper`, a [platform is used](mapper-platforms.md)
8+
containing a set of type builders that analyze the type description (and AST)
9+
and create a set of [type instances](types.md) used directly at runtime for
10+
checking and casting values.
11+
12+
To better understand the tasks of the builder, it makes sense to look at the
13+
graph illustrating the process of compiling a type when calling
14+
`$mapper->denormalize(42)` that should return `int(42)` using built-in
15+
`TypeInterface<int>` type
16+
17+
<code-block lang="mermaid" xmlns="">
18+
<![CDATA[
19+
flowchart TD
20+
Input["int(42)"] .-> Mapper["Mapper::denormalize(42)"]
21+
Mapper -. "cast value 42 using TypeInterface" .-> Type
22+
Mapper -- "start compilation" --> Extractor["TypeExtractor::extract(42)"]
23+
Extractor -- "for int(42) value the string(&quot;int&quot;) was inferred" --> Parser["TypeParser::parse(&quot;int&quot;)"]
24+
Parser -- "definition string(&quot;int&quot;) was parsed to AST object(TypeStatement)" --> Repository["TypeRepository::findType(TypeStatement)"]
25+
Platform -- "configure builders list&lt;TypeBuilderInterface>" --> Repository
26+
Repository -. TypeInterface&lt;int> .-> Type["TypeInterface&lt;int>::cast(42)"]
27+
Type .-> Output["int(42)"]
28+
]]>
29+
</code-block>
30+
31+
The diagram is quite complex, and it's okay if you don't quite understand
32+
the process. The main thing is that it makes sense:
33+
- [The platform](mapper-platforms.md) contains a set of type builders and
34+
[registers](standard-platform.md#additional-types) them in the repository.
35+
- The type builder returns a [specific `TypeInterface<T>`](types.md) from the
36+
type declaration string (or more precisely, from its Abstract Syntax Tree).
37+
38+
39+
That is, the code of type builder of `int<0, 10>` looks something like this:
40+
41+
```php
42+
class IntTypeBuilder interface TypeBuilderInterface
43+
{
44+
public function build(TypeStatement $stmt): TypeInterface
45+
{
46+
// [[[Expects named type|basic-types.md]]]
47+
assert($stmt instanceof NamedTypeNode);
48+
// [[[Expects 2 template arguments|generic-types.md]]]
49+
assert(count($stmt->arguments) === 2);
50+
// [[[Expects no shape fields|shape-types.md]]]
51+
assert($stmt->fields === null);
52+
53+
return new IntType(
54+
name: $stmt->name->toString(), // string("int");
55+
min: $stmt->arguments[0]->getValue(), // int(0)
56+
max: $stmt->arguments[1]->getValue(), // int(10)
57+
);
58+
}
59+
}
60+
```
61+
62+
<warning>
63+
This is not an exact declaration and is used as an example.
64+
65+
It's also worth noting that not all checks are performed in the
66+
example. For example, the type of the template arguments (that they are
67+
integers) also makes sense to check.
68+
</warning>
69+
70+
<tip>
71+
You can read more about custom type builders in the
72+
<a href="custom-type-builder.md">"custom type builders" page</a>.
73+
</tip>

0 commit comments

Comments
 (0)