Skip to content

Commit 7aee1a3

Browse files
committed
Bitrate
1 parent dafe50b commit 7aee1a3

File tree

6 files changed

+252
-36
lines changed

6 files changed

+252
-36
lines changed

src/Compound/AbstractCompoundConverter.php

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,20 @@
1010

1111
abstract class AbstractCompoundConverter extends AbstractConverter
1212
{
13-
public AbstractConverter $measure;
14-
public AbstractConverter $deno;
13+
abstract public AbstractConverter $measure {
14+
get;
15+
}
16+
abstract public AbstractConverter $deno {
17+
get;
18+
}
19+
20+
protected array $unitExchanges {
21+
get => $this->compoundUnitExchanges;
22+
}
23+
24+
abstract protected array $compoundUnitExchanges {
25+
get;
26+
}
1527

1628
public string $baseUnit {
1729
set {
@@ -26,8 +38,6 @@ abstract class AbstractCompoundConverter extends AbstractConverter
2638
}
2739
}
2840

29-
protected array $unitExchanges = [];
30-
3141
/**
3242
* Some conversion needs 2-steps to convert the value to the atom unit.
3343
* This is the scale used for the intermediate conversion that can prevent
@@ -126,15 +136,10 @@ public function convertTo(
126136
$this->intermediateScale,
127137
$roundingMode
128138
);
139+
129140
// Now convert the value to the target child unit (X/y).
130-
$newValue = $new->convertValue(
131-
$newValue,
132-
$new->atomUnit,
133-
$toUnit,
134-
$scale,
135-
$roundingMode
136-
);
137-
return $new->with($newValue, $toUnit);
141+
return $new->with($newValue, $new->atomUnit)
142+
->convertTo($toUnit, $scale, $roundingMode);
138143
}
139144

140145
// Compound exchange, for example, m/s to km/h.
@@ -214,14 +219,28 @@ protected function normalizeUnit(string $unit): string
214219
}
215220

216221
#[\Override]
217-
protected function formatSuffix(string $suffix, BigDecimal $value, string $unit): string
218-
{
219-
$suffix = parent::formatSuffix($suffix, $value, $unit);
222+
public function format(
223+
string|\Closure|null $suffix = null,
224+
?string $unit = null,
225+
?int $scale = null,
226+
RoundingMode $roundingMode = RoundingMode::DOWN
227+
): string {
228+
$addDenoSuffix = $suffix === null;
229+
230+
if (!$suffix) {
231+
$suffix = $unit ?? $this->baseUnit;
232+
233+
if ($this->measure->getUnitExchangeRate($suffix) !== null) {
234+
$suffix .= '/' . $this->deno->formatSuffix($this->deno->baseUnit, $this->value, $this->deno->baseUnit);
235+
}
236+
}
237+
238+
$text = parent::format($suffix, $unit, $scale, $roundingMode);
220239

221-
if ($suffix === $this->baseUnit) {
222-
$suffix .= '/' . $this->deno->formatSuffix($this->deno->baseUnit, $value, $this->deno->baseUnit);
240+
if ($addDenoSuffix && $this->measure->getUnitExchangeRate($suffix) !== null) {
241+
$text .= '/' . $this->deno->formatSuffix($this->deno->baseUnit, $this->value, $this->deno->baseUnit);
223242
}
224243

225-
return $suffix;
244+
return $text;
226245
}
227246
}

src/Compound/Bitrate.php

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Asika\UnitConverter\Compound;
6+
7+
use Asika\UnitConverter\AbstractConverter;
8+
use Asika\UnitConverter\Duration;
9+
use Asika\UnitConverter\FileSize;
10+
11+
class Bitrate extends AbstractCompoundConverter
12+
{
13+
public const string UNIT_BITS_PER_SECOND = 'bits/s';
14+
15+
public const string UNIT_BYTES_PER_SECOND = 'bytes/s';
16+
17+
public const string UNIT_KBPS = 'Kbps';
18+
19+
public const string UNIT_MBPS = 'Mbps';
20+
21+
public const string UNIT_GBPS = 'Gbps';
22+
23+
public const string UNIT_TBPS = 'Tbps';
24+
25+
public const string UNIT_KIBPS = 'Kibps';
26+
27+
public const string UNIT_MIBPS = 'Mibps';
28+
29+
public const string UNIT_GIBPS = 'Gibps';
30+
31+
public const string UNIT_TIBPS = 'Tibps';
32+
33+
public AbstractConverter $measure {
34+
get => $this->measure ??= new FileSize();
35+
}
36+
37+
public AbstractConverter $deno {
38+
get => $this->deno ??= new Duration()
39+
->withShortUnitFormatters();
40+
}
41+
42+
public string $atomUnit = self::UNIT_BITS_PER_SECOND;
43+
44+
public string $defaultUnit = self::UNIT_BYTES_PER_SECOND;
45+
46+
protected array $compoundUnitExchanges = [
47+
self::UNIT_BITS_PER_SECOND => 1.0,
48+
self::UNIT_BYTES_PER_SECOND => 8.0,
49+
self::UNIT_KBPS => 1000.0,
50+
self::UNIT_MBPS => 1000_000.0,
51+
self::UNIT_GBPS => 1_000_000_000.0,
52+
self::UNIT_TBPS => 1_000_000_000_000.0,
53+
self::UNIT_KIBPS => 1024.0,
54+
self::UNIT_MIBPS => 1_048_576.0,
55+
self::UNIT_GIBPS => 1_073_741_824.0,
56+
self::UNIT_TIBPS => 1_099_511_627_776.0,
57+
];
58+
59+
protected function normalizeCompoundUnit(string $unit): string
60+
{
61+
return $unit;
62+
}
63+
}

src/Compound/Speed.php

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,31 +23,15 @@ class Speed extends AbstractCompoundConverter
2323
get => $this->measure ??= new Length();
2424
}
2525

26-
protected array $unitExchanges = [
26+
protected array $compoundUnitExchanges = [
2727
'm/s' => 1,
2828
self::UNIT_MPH => 0.44704,
2929
self::UNIT_KNOTS => 0.514444444,
3030
];
3131

3232
public AbstractConverter $deno {
3333
get => $this->deno ??= new Duration()
34-
->withSuffixFormatter(
35-
fn(string $unit): string => match (strtolower($unit)) {
36-
Duration::UNIT_FEMTOSECONDS => 'fs',
37-
Duration::UNIT_PICOSECONDS => 'ps',
38-
Duration::UNIT_NANOSECONDS => 'ns',
39-
Duration::UNIT_MICROSECONDS => 'μs',
40-
Duration::UNIT_MILLISECONDS => 'ms',
41-
Duration::UNIT_SECONDS => 's',
42-
Duration::UNIT_MINUTES => 'min',
43-
Duration::UNIT_HOURS => 'h',
44-
Duration::UNIT_DAYS => 'd',
45-
Duration::UNIT_WEEKS => 'w',
46-
Duration::UNIT_MONTHS => 'mo',
47-
Duration::UNIT_YEARS => 'y',
48-
default => $unit,
49-
}
50-
);
34+
->withShortUnitFormatters();
5135
}
5236

5337
protected function normalizeCompoundUnit(string $unit): string

src/Duration.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,4 +270,25 @@ protected function normalizeUnit(string $unit): string
270270

271271
return parent::normalizeUnit($unit);
272272
}
273+
274+
public function withShortUnitFormatters(): static
275+
{
276+
return $this->withSuffixFormatter(
277+
fn(string $unit): string => match (strtolower($unit)) {
278+
Duration::UNIT_FEMTOSECONDS => 'fs',
279+
Duration::UNIT_PICOSECONDS => 'ps',
280+
Duration::UNIT_NANOSECONDS => 'ns',
281+
Duration::UNIT_MICROSECONDS => 'μs',
282+
Duration::UNIT_MILLISECONDS => 'ms',
283+
Duration::UNIT_SECONDS => 's',
284+
Duration::UNIT_MINUTES => 'min',
285+
Duration::UNIT_HOURS => 'h',
286+
Duration::UNIT_DAYS => 'd',
287+
Duration::UNIT_WEEKS => 'w',
288+
Duration::UNIT_MONTHS => 'mo',
289+
Duration::UNIT_YEARS => 'y',
290+
default => $unit,
291+
}
292+
);
293+
}
273294
}

tests/Compound/BitrateTest.php

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Asika\UnitConverter\Tests\Compound;
6+
7+
use Asika\UnitConverter\Compound\Bitrate;
8+
use Asika\UnitConverter\Compound\Speed;
9+
use Asika\UnitConverter\Duration;
10+
use PHPUnit\Framework\Attributes\DataProvider;
11+
use PHPUnit\Framework\TestCase;
12+
13+
class BitrateTest extends TestCase
14+
{
15+
#[DataProvider('bitrateProvider')]
16+
#[DataProvider('presetProvider')]
17+
public function testConstructAndConvert(
18+
Duration|\Closure $converter,
19+
array|\Closure $formatArgs,
20+
string $formatted,
21+
): void {
22+
if ($converter instanceof \Closure) {
23+
$converter = $converter();
24+
}
25+
26+
self::assertEquals(
27+
$formatted,
28+
$formatArgs instanceof \Closure
29+
? $formatArgs($converter)
30+
: $converter->format(...$formatArgs)
31+
);
32+
}
33+
34+
public static function bitrateProvider(): array
35+
{
36+
return [
37+
'MiB/s to bit/s' => [
38+
fn () => new Bitrate(1, 'MiB/s')
39+
->convertTo('bit/s', scale: 10),
40+
[],
41+
'8388608b/s',
42+
],
43+
'MiB/s to Kib/s' => [
44+
fn () => new Bitrate(1, 'MiB/s')
45+
->convertTo('Kib/s', scale: 10),
46+
[],
47+
'8192Kib/s',
48+
],
49+
'MiB/s to KiB/s' => [
50+
fn () => new Bitrate(1, 'MiB/s')
51+
->convertTo('KiB/s', scale: 10),
52+
[],
53+
'1024KiB/s',
54+
],
55+
'MiB/s to B/s' => [
56+
fn () => new Bitrate(1, 'MiB/s')
57+
->convertTo('B/s', scale: 10),
58+
[],
59+
'1048576B/s',
60+
],
61+
'KiB/s to GiB/s' => [
62+
fn () => Bitrate::from('1KiB/s')
63+
->convertTo('GiB/s', scale: 15),
64+
[],
65+
'0.000000953674316GiB/s',
66+
],
67+
'KiB/s to GB/s' => [
68+
fn () => Bitrate::from('1KiB/s')
69+
->convertTo('GB/s', scale: 15),
70+
[],
71+
'0.000001024GB/s',
72+
],
73+
'KiB/s to Gib/s' => [
74+
fn () => Bitrate::from('1KiB/s')
75+
->convertTo('Gib/s', scale: 15),
76+
[],
77+
'0.000007629394531Gib/s',
78+
],
79+
'KiB/s to Gb/s' => [
80+
fn () => Bitrate::from('1KiB/s')
81+
->convertTo('Gb/s', scale: 15),
82+
[],
83+
'0.000008192Gb/s',
84+
],
85+
];
86+
}
87+
88+
public static function presetProvider(): array
89+
{
90+
return [
91+
'Mbps to Mibps' => [
92+
fn () => Bitrate::parse('1 Mbps')
93+
->convertTo('Mibps', scale: 10),
94+
[],
95+
'0.9536743164Mibps',
96+
],
97+
'Mbps to Kbps' => [
98+
fn () => Bitrate::parse('1 Mbps')
99+
->convertTo('Kbps', scale: 10),
100+
[],
101+
'1000Kbps',
102+
],
103+
'Mbps to Kibps' => [
104+
fn () => Bitrate::parse('1 Mbps')
105+
->convertTo('Kibps', scale: 10),
106+
[],
107+
'976.5625Kibps',
108+
],
109+
'Gibps to Mbps' => [
110+
fn () => Bitrate::parse('1 Gbps')
111+
->convertTo('Mbps', scale: 10),
112+
[],
113+
'1000Mbps',
114+
],
115+
'Gibps to Mibps' => [
116+
fn () => Bitrate::parse('1 Gbps')
117+
->convertTo('Mibps', scale: 10),
118+
[],
119+
'953.6743164062Mibps',
120+
],
121+
];
122+
}
123+
}

tests/Compound/SpeedTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ public static function speedProvider(): array
5454
fn (Speed $speed) => $speed->format('KMH'),
5555
'3.6KMH',
5656
],
57+
'm/h to km/h with format' => [
58+
fn () => new Speed(3000, 'm/h')
59+
->convertTo('km/h', scale: 10),
60+
[],
61+
'3km/h',
62+
],
5763
'mps to kph' => [
5864
fn () => Speed::parse('1mps')
5965
->convertTo('kph', scale: 10),

0 commit comments

Comments
 (0)