Passed
Push — master ( 365cfc...8e997c )
by Mr
04:36
created

Bitcoin::percentage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 2
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
/**
3
 * This file is part of the ngutech/bitcoin-interop project.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
9
namespace NGUtech\Bitcoin\ValueObject;
10
11
use Daikon\Interop\Assertion;
12
use Daikon\Interop\InvalidArgumentException;
13
use Daikon\Interop\MakeEmptyInterface;
14
use Daikon\Money\ValueObject\MoneyInterface;
15
use Money\Currency;
16
use Money\Money;
17
use NGUtech\Bitcoin\Service\BitcoinCurrencies;
18
use NGUtech\Bitcoin\Service\SatoshiCurrencies;
19
20
final class Bitcoin implements MakeEmptyInterface, MoneyInterface
21
{
22
    private Money $money;
23
24
    /** @param self $comparator */
25 1
    public function equals($comparator): bool
26
    {
27 1
        Assertion::isInstanceOf($comparator, self::class);
28 1
        return $this->toNative() === $comparator->toNative();
29
    }
30
31 6
    public function getAmount(): string
32
    {
33 6
        return $this->money->getAmount();
34
    }
35
36 6
    public function getCurrency(): string
37
    {
38 6
        return $this->money->getCurrency()->getCode();
39
    }
40
41 1
    public function multiply($multiplier, int $roundingMode = self::ROUND_HALF_UP): self
42
    {
43 1
        Assertion::numeric($multiplier, 'Multipler must be numeric.');
44 1
        $multiplied = $this->money->multiply($multiplier, $roundingMode);
45 1
        return new self($multiplied);
46
    }
47
48 1
    public function divide($divisor, int $roundingMode = self::ROUND_HALF_UP): self
49
    {
50 1
        Assertion::numeric($divisor, 'Divider must be numeric.');
51 1
        $divided = $this->money->divide($divisor, $roundingMode);
52 1
        return new self($divided);
53
    }
54
55 1
    public function percentage($percentage, int $roundingMode = self::ROUND_HALF_UP): self
56
    {
57 1
        return $this->multiply($percentage)->divide(100, $roundingMode);
58
    }
59
60
    public function add(MoneyInterface $money): self
61
    {
62
        $added = $this->money->add(
63
            self::asBaseMoney($money->getAmount(), $money->getCurrency())
64
        );
65
        return new self($added);
66
    }
67
68
    public function subtract(MoneyInterface $money): self
69
    {
70
        $subtracted = $this->money->subtract(
71
            self::asBaseMoney($money->getAmount(), $money->getCurrency())
72
        );
73
        return new self($subtracted);
74
    }
75
76
    public function isZero(): bool
77
    {
78
        return $this->money->isZero();
79
    }
80
81
    public function isPositive(): bool
82
    {
83
        return $this->money->isPositive();
84
    }
85
86
    public function isNegative(): bool
87
    {
88
        return $this->money->isNegative();
89
    }
90
91
    public function isLessThanOrEqual(MoneyInterface $money): bool
92
    {
93
        return $this->money->lessThanOrEqual(
94
            self::asBaseMoney($money->getAmount(), $money->getCurrency())
95
        );
96
    }
97
98
    public function isGreaterThanOrEqual(MoneyInterface $money): bool
99
    {
100
        return $this->money->greaterThanOrEqual(
101
            self::asBaseMoney($money->getAmount(), $money->getCurrency())
102
        );
103
    }
104
105
    /** @param string $value */
106 7
    public static function fromNative($value): self
107
    {
108 7
        Assertion::string($value, 'Must be a string.');
109
110 7
        if (!preg_match('/^(?<amount>-?\d+)\s?(?<currency>M?SAT|BTC|XBT)$/i', $value, $matches)) {
111 2
            throw new InvalidArgumentException('Invalid amount.');
112
        }
113
114 7
        return new self(self::asBaseMoney($matches['amount'], strtoupper($matches['currency'])));
115
    }
116
117 1
    public static function zero($currency = null): self
118
    {
119 1
        return self::fromNative('0'.($currency ?? SatoshiCurrencies::MSAT));
120
    }
121
122
    public static function makeEmpty(): self
123
    {
124
        return self::zero();
125
    }
126
127
    public function isEmpty(): bool
128
    {
129
        return $this->isZero();
130
    }
131
132 5
    public function toNative(): string
133
    {
134 5
        return $this->getAmount().$this->getCurrency();
135
    }
136
137 2
    public function __toString(): string
138
    {
139 2
        return $this->toNative();
140
    }
141
142 7
    private static function asBaseMoney(string $amount, string $currency): Money
143
    {
144 7
        return new Money($amount, new Currency($currency));
145
    }
146
147 7
    private function __construct(Money $money)
148
    {
149 7
        Assertion::choice((string)$money->getCurrency(), [
150 7
            SatoshiCurrencies::MSAT,
151 7
            SatoshiCurrencies::SAT,
152 7
            BitcoinCurrencies::BTC,
153 7
            BitcoinCurrencies::XBT
154 7
        ], 'Invalid currency');
155 7
        $this->money = $money;
156 7
    }
157
}
158