Code Coverage
 
Lines
Branches
Paths
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
25 / 25
100.00% covered (success)
100.00%
28 / 28
59.26% covered (warning)
59.26%
16 / 27
100.00% covered (success)
100.00%
6 / 6
CRAP
100.00% covered (success)
100.00%
1 / 1
Str
100.00% covered (success)
100.00%
25 / 25
100.00% covered (success)
100.00%
28 / 28
59.26% covered (warning)
59.26%
16 / 27
100.00% covered (success)
100.00%
6 / 6
33.31
100.00% covered (success)
100.00%
1 / 1
 coerce
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 coerceStrict
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
3
 invalid
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isCoercible
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
9 / 9
31.25% covered (danger)
31.25%
5 / 16
100.00% covered (success)
100.00%
1 / 1
13.12
 toString
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 isEmpty
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5namespace Celemas\Sire\Coercer;
6
7use Celemas\Sire\Coercion;
8use Celemas\Sire\CoercionMode;
9use Celemas\Sire\Contract;
10use Celemas\Sire\Failure;
11use Override;
12use Stringable;
13
14/** @api */
15final class Str implements Contract\Coercer
16{
17    public string $message {
18        get => '{label} must be a string';
19    }
20
21    #[Override]
22    public function coerce(mixed $pristine, CoercionMode $mode): Contract\Coercion
23    {
24        if ($mode === CoercionMode::Strict) {
25            return self::coerceStrict($pristine);
26        }
27
28        if (!self::isCoercible($pristine)) {
29            return self::invalid($pristine);
30        }
31
32        $value = self::toString($pristine);
33
34        return new Coercion($value, $pristine, empty: self::isEmpty($value));
35    }
36
37    private static function coerceStrict(mixed $pristine): Coercion
38    {
39        if ($pristine === null) {
40            return new Coercion(null, null, empty: true);
41        }
42
43        return is_string($pristine)
44            ? new Coercion($pristine, $pristine, empty: self::isEmpty($pristine))
45            : self::invalid($pristine);
46    }
47
48    private static function invalid(mixed $pristine): Coercion
49    {
50        return new Coercion(
51            $pristine,
52            $pristine,
53            Failure::invalid(),
54        );
55    }
56
57    private static function isCoercible(mixed $value): bool
58    {
59        return (
60            $value === null
61            || is_string($value)
62            || is_int($value)
63            || is_float($value)
64            || $value instanceof Stringable
65        );
66    }
67
68    private static function toString(mixed $value): ?string
69    {
70        return $value === null ? null : (string) $value;
71    }
72
73    private static function isEmpty(?string $value): bool
74    {
75        return $value === null || $value === '';
76    }
77}