From d4c70b9b920496b3b645f081ec6040d33e4637ea Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Thu, 8 May 2025 11:49:21 +0200 Subject: [PATCH 01/23] add Cores::of() to statically make sure the value is positive --- composer.json | 3 ++- src/Facade/Cpu/LinuxFacade.php | 4 +++- src/Facade/Cpu/OSXFacade.php | 13 +++++++++---- src/Server/Cpu/Cores.php | 23 +++++++++++++---------- tests/Server/Cpu/CoresTest.php | 14 ++------------ tests/Server/CpuTest.php | 2 +- 6 files changed, 30 insertions(+), 29 deletions(-) diff --git a/composer.json b/composer.json index 92c4de5..2f6b577 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,8 @@ "innmind/time-continuum": "^4.1.1", "innmind/url": "~4.0", "psr/log": "~3.0", - "innmind/server-control": "~6.0" + "innmind/server-control": "~6.0", + "innmind/validation": "^2.0" }, "autoload": { "psr-4": { diff --git a/src/Facade/Cpu/LinuxFacade.php b/src/Facade/Cpu/LinuxFacade.php index c345bbf..c30cf89 100644 --- a/src/Facade/Cpu/LinuxFacade.php +++ b/src/Facade/Cpu/LinuxFacade.php @@ -12,6 +12,7 @@ Processes, Command, }; +use Innmind\Validation\Is; use Innmind\Immutable\{ Str, Attempt, @@ -85,6 +86,7 @@ private function parse(Str $output): Attempt ->toString(), ) ->map(static fn($cores) => (int) $cores) + ->keep(Is::int()->positive()->asPredicate()) ->match( static fn($cores) => $cores, static fn() => 1, @@ -99,7 +101,7 @@ private function parse(Str $output): Attempt new Percentage($user), new Percentage($sys), new Percentage($idle), - new Cores($cores), + Cores::of($cores), )) ->match( Attempt::result(...), diff --git a/src/Facade/Cpu/OSXFacade.php b/src/Facade/Cpu/OSXFacade.php index a8706d8..6fdf587 100644 --- a/src/Facade/Cpu/OSXFacade.php +++ b/src/Facade/Cpu/OSXFacade.php @@ -12,6 +12,7 @@ Processes, Command, }; +use Innmind\Validation\Is; use Innmind\Immutable\{ Str, Attempt, @@ -96,17 +97,21 @@ private function parse(Str $output): Attempt ->flatMap(static fn($output) => $output->get('cores')) ->map(static fn($cores) => $cores->toString()) ->map(static fn($cores) => (int) $cores) - ->otherwise(static fn() => Maybe::just(1)); + ->keep(Is::int()->positive()->asPredicate()) + ->match( + static fn($cores) => $cores, + static fn() => 1, + ); $user = $percentages->get('user'); $sys = $percentages->get('sys'); $idle = $percentages->get('idle'); - return Maybe::all($user, $sys, $idle, $cores) - ->map(static fn(float $user, float $sys, float $idle, int $cores) => new Cpu( + return Maybe::all($user, $sys, $idle) + ->map(static fn(float $user, float $sys, float $idle) => new Cpu( new Percentage($user), new Percentage($sys), new Percentage($idle), - new Cores($cores), + Cores::of($cores), )) ->match( Attempt::result(...), diff --git a/src/Server/Cpu/Cores.php b/src/Server/Cpu/Cores.php index 9f4f2bf..50e3d50 100644 --- a/src/Server/Cpu/Cores.php +++ b/src/Server/Cpu/Cores.php @@ -3,27 +3,30 @@ namespace Innmind\Server\Status\Server\Cpu; -use Innmind\Server\Status\Exception\DomainException; - /** * @psalm-immutable */ final class Cores { - private int $value; + /** + * @param int<1, max> $value + */ + private function __construct( + private int $value, + ) { + } /** - * @throws DomainException + * @param int<1, max> $value */ - public function __construct(int $value) + public static function of(int $value): self { - if ($value < 1) { - throw new DomainException((string) $value); - } - - $this->value = $value; + return new self($value); } + /** + * @return int<1, max> + */ public function toInt(): int { return $this->value; diff --git a/tests/Server/Cpu/CoresTest.php b/tests/Server/Cpu/CoresTest.php index c68ee35..2191f83 100644 --- a/tests/Server/Cpu/CoresTest.php +++ b/tests/Server/Cpu/CoresTest.php @@ -3,26 +3,16 @@ namespace Tests\Innmind\Server\Status\Server\Cpu; -use Innmind\Server\Status\{ - Server\Cpu\Cores, - Exception\DomainException, -}; +use Innmind\Server\Status\Server\Cpu\Cores; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class CoresTest extends TestCase { public function testInterface() { - $cores = new Cores(8); + $cores = Cores::of(8); $this->assertSame(8, $cores->toInt()); $this->assertSame('8', $cores->toString()); } - - public function testThrowWhenCoresLowerThanOne() - { - $this->expectException(DomainException::class); - - new Cores(0); - } } diff --git a/tests/Server/CpuTest.php b/tests/Server/CpuTest.php index fb387eb..a8c0c50 100644 --- a/tests/Server/CpuTest.php +++ b/tests/Server/CpuTest.php @@ -18,7 +18,7 @@ public function testInterface() $user = new Percentage(31), $system = new Percentage(33), $idle = new Percentage(36), - $cores = new Cores(4), + $cores = Cores::of(4), ); $this->assertSame($user, $cpu->user()); From f7adc027fe7895b6e6d98a90be2e9287e5b374d9 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Thu, 8 May 2025 11:57:23 +0200 Subject: [PATCH 02/23] add Percentage::maybe() to statically make sure the value is positive --- src/Facade/Cpu/LinuxFacade.php | 20 +++++++++++++------- src/Facade/Cpu/OSXFacade.php | 20 +++++++++++++------- src/Server/Cpu/Percentage.php | 16 ++++++++++------ src/Server/Processes/UnixProcesses.php | 9 ++++++--- tests/Server/Cpu/PercentageTest.php | 20 +++++++++++--------- tests/Server/CpuTest.php | 15 ++++++++++++--- tests/Server/ProcessTest.php | 5 ++++- 7 files changed, 69 insertions(+), 36 deletions(-) diff --git a/src/Facade/Cpu/LinuxFacade.php b/src/Facade/Cpu/LinuxFacade.php index c30cf89..896d9fa 100644 --- a/src/Facade/Cpu/LinuxFacade.php +++ b/src/Facade/Cpu/LinuxFacade.php @@ -92,15 +92,21 @@ private function parse(Str $output): Attempt static fn() => 1, ); - $user = $percentages->get('user'); - $sys = $percentages->get('sys'); - $idle = $percentages->get('idle'); + $user = $percentages + ->get('user') + ->flatMap(Percentage::maybe(...)); + $sys = $percentages + ->get('sys') + ->flatMap(Percentage::maybe(...)); + $idle = $percentages + ->get('idle') + ->flatMap(Percentage::maybe(...)); return Maybe::all($user, $sys, $idle) - ->map(static fn(float $user, float $sys, float $idle) => new Cpu( - new Percentage($user), - new Percentage($sys), - new Percentage($idle), + ->map(static fn(Percentage $user, Percentage $sys, Percentage $idle) => new Cpu( + $user, + $sys, + $idle, Cores::of($cores), )) ->match( diff --git a/src/Facade/Cpu/OSXFacade.php b/src/Facade/Cpu/OSXFacade.php index 6fdf587..cb79ac4 100644 --- a/src/Facade/Cpu/OSXFacade.php +++ b/src/Facade/Cpu/OSXFacade.php @@ -102,15 +102,21 @@ private function parse(Str $output): Attempt static fn($cores) => $cores, static fn() => 1, ); - $user = $percentages->get('user'); - $sys = $percentages->get('sys'); - $idle = $percentages->get('idle'); + $user = $percentages + ->get('user') + ->flatMap(Percentage::maybe(...)); + $sys = $percentages + ->get('sys') + ->flatMap(Percentage::maybe(...)); + $idle = $percentages + ->get('idle') + ->flatMap(Percentage::maybe(...)); return Maybe::all($user, $sys, $idle) - ->map(static fn(float $user, float $sys, float $idle) => new Cpu( - new Percentage($user), - new Percentage($sys), - new Percentage($idle), + ->map(static fn(Percentage $user, Percentage $sys, Percentage $idle) => new Cpu( + $user, + $sys, + $idle, Cores::of($cores), )) ->match( diff --git a/src/Server/Cpu/Percentage.php b/src/Server/Cpu/Percentage.php index f3ed13e..3153d30 100644 --- a/src/Server/Cpu/Percentage.php +++ b/src/Server/Cpu/Percentage.php @@ -3,25 +3,29 @@ namespace Innmind\Server\Status\Server\Cpu; -use Innmind\Server\Status\Exception\OutOfBoundsPercentage; +use Innmind\Immutable\Maybe; /** * @psalm-immutable */ final class Percentage { - private float $value; + private function __construct( + private float $value, + ) { + } /** - * @throws OutOfBoundsPercentage + * @return Maybe */ - public function __construct(float $value) + public static function maybe(float $value): Maybe { if ($value < 0) { - throw new OutOfBoundsPercentage((string) $value); + /** @var Maybe */ + return Maybe::nothing(); } - $this->value = $value; + return Maybe::just(new self($value)); } public function toFloat(): float diff --git a/src/Server/Processes/UnixProcesses.php b/src/Server/Processes/UnixProcesses.php index 50b3fa1..0a85112 100644 --- a/src/Server/Processes/UnixProcesses.php +++ b/src/Server/Processes/UnixProcesses.php @@ -112,15 +112,18 @@ private function parse(Str $output): Set ->map(static fn($part) => $part->toString()); $user = $parts->get(0); $pid = $parts->get(1); - $percentage = $parts->get(2); + $percentage = $parts + ->get(2) + ->map(static fn($value) => (float) $value) + ->flatMap(Percentage::maybe(...)); $memory = $parts->get(3); $command = $parts->get(4); return Maybe::all($user, $pid, $percentage, $memory, $command) - ->map(fn(string $user, string $pid, string $percentage, string $memory, string $command) => new Process( + ->map(fn(string $user, string $pid, Percentage $percentage, string $memory, string $command) => new Process( new Pid((int) $pid), new User($user), - new Percentage((float) $percentage), + $percentage, new Memory((float) $memory), $this->clock->at($start, Format::of('D M j H:i:s Y')), new Command($command), diff --git a/tests/Server/Cpu/PercentageTest.php b/tests/Server/Cpu/PercentageTest.php index d6a598c..7ac0270 100644 --- a/tests/Server/Cpu/PercentageTest.php +++ b/tests/Server/Cpu/PercentageTest.php @@ -3,26 +3,28 @@ namespace Tests\Innmind\Server\Status\Server\Cpu; -use Innmind\Server\Status\{ - Server\Cpu\Percentage, - Exception\OutOfBoundsPercentage, -}; +use Innmind\Server\Status\Server\Cpu\Percentage; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class PercentageTest extends TestCase { public function testInterface() { - $percentage = new Percentage(42.24); + $percentage = Percentage::maybe(42.24)->match( + static fn($percentage) => $percentage, + static fn() => null, + ); + $this->assertNotNull($percentage); $this->assertSame(42.24, $percentage->toFloat()); $this->assertSame('42.24%', $percentage->toString()); } - public function testThrowWhenPercentageLowerThanZero() + public function testReturnNothingWhenPercentageLowerThanZero() { - $this->expectException(OutOfBoundsPercentage::class); - - new Percentage(-1); + $this->assertNull(Percentage::maybe(-1)->match( + static fn($percentage) => $percentage, + static fn() => null, + )); } } diff --git a/tests/Server/CpuTest.php b/tests/Server/CpuTest.php index a8c0c50..0e33981 100644 --- a/tests/Server/CpuTest.php +++ b/tests/Server/CpuTest.php @@ -15,9 +15,18 @@ class CpuTest extends TestCase public function testInterface() { $cpu = new Cpu( - $user = new Percentage(31), - $system = new Percentage(33), - $idle = new Percentage(36), + $user = Percentage::maybe(31)->match( + static fn($percentage) => $percentage, + static fn() => throw new \Exception('Should be valid'), + ), + $system = Percentage::maybe(33)->match( + static fn($percentage) => $percentage, + static fn() => throw new \Exception('Should be valid'), + ), + $idle = Percentage::maybe(36)->match( + static fn($percentage) => $percentage, + static fn() => throw new \Exception('Should be valid'), + ), $cores = Cores::of(4), ); diff --git a/tests/Server/ProcessTest.php b/tests/Server/ProcessTest.php index a5c6b51..60ec0e3 100644 --- a/tests/Server/ProcessTest.php +++ b/tests/Server/ProcessTest.php @@ -30,7 +30,10 @@ public function testInterface() $process = new Process( $pid = new Pid(1), $user = new User('root'), - $cpu = new Percentage(42), + $cpu = Percentage::maybe(42)->match( + static fn($percentage) => $percentage, + static fn() => throw new \Exception('Should be valid'), + ), $memory = new Memory(42), $start = Maybe::just($pointInTime), $command = new Command('/sbin/launchd'), From 55dc16d5fe5614674a7f8d54b2c1fd210531f426 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Thu, 8 May 2025 12:08:26 +0200 Subject: [PATCH 03/23] add MountPoint::of() to statically make sure the value is correct --- CHANGELOG.md | 4 ++++ src/Exception/EmptyPathNotAllowed.php | 8 -------- src/Server/Disk/UnixDisk.php | 10 +++++++--- src/Server/Disk/Volume/MountPoint.php | 20 ++++++++++---------- tests/Server/Disk/LoggerDiskTest.php | 2 +- tests/Server/Disk/UnixDiskTest.php | 2 +- tests/Server/Disk/Volume/MountPointTest.php | 14 ++------------ tests/Server/Disk/VolumeTest.php | 2 +- 8 files changed, 26 insertions(+), 36 deletions(-) delete mode 100644 src/Exception/EmptyPathNotAllowed.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 09dcf01..193d316 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ - `Innmind\Server\Status\Server::cpu()` now returns `Innmind\Immutable\Attempt` - `Innmind\Server\Status\Server::memory()` now returns `Innmind\Immutable\Attempt` +### Removed + +- `Innmind\Server\Status\Exception\EmptyPathNotAllowed` + ## 4.1.1 - 2024-09-30 ### Fixed diff --git a/src/Exception/EmptyPathNotAllowed.php b/src/Exception/EmptyPathNotAllowed.php deleted file mode 100644 index e7aa80f..0000000 --- a/src/Exception/EmptyPathNotAllowed.php +++ /dev/null @@ -1,8 +0,0 @@ -map(static fn($parts) => $columns->zip($parts)) ->map(static fn($parts) => Map::of(...$parts->toList())); $volumes = $partsByLine->map(static function($parts): Maybe { - $mountPoint = $parts->get('mountPoint'); + $mountPoint = $parts + ->get('mountPoint') + ->keep(Is::string()->nonEmpty()->asPredicate()) + ->map(MountPoint::of(...)); $size = $parts ->get('size') ->flatMap(static fn($size) => Bytes::of($size)); @@ -111,8 +115,8 @@ private function parse(Str $output): Set $usage = $parts->get('usage'); return Maybe::all($mountPoint, $size, $available, $used, $usage) - ->map(static fn(string $mountPoint, Bytes $size, Bytes $available, Bytes $used, string $usage) => new Volume( - new MountPoint($mountPoint), + ->map(static fn(MountPoint $mountPoint, Bytes $size, Bytes $available, Bytes $used, string $usage) => new Volume( + $mountPoint, $size, $available, $used, diff --git a/src/Server/Disk/Volume/MountPoint.php b/src/Server/Disk/Volume/MountPoint.php index 42b7327..0317382 100644 --- a/src/Server/Disk/Volume/MountPoint.php +++ b/src/Server/Disk/Volume/MountPoint.php @@ -3,22 +3,22 @@ namespace Innmind\Server\Status\Server\Disk\Volume; -use Innmind\Server\Status\Exception\EmptyPathNotAllowed; - final class MountPoint { - private string $value; + /** + * @param non-empty-string $value + */ + private function __construct( + private string $value, + ) { + } /** - * @throws EmptyPathNotAllowed + * @param non-empty-string $value */ - public function __construct(string $value) + public static function of(string $value): self { - if ($value === '') { - throw new EmptyPathNotAllowed; - } - - $this->value = $value; + return new self($value); } public function equals(self $point): bool diff --git a/tests/Server/Disk/LoggerDiskTest.php b/tests/Server/Disk/LoggerDiskTest.php index bf2adb2..3c377c4 100644 --- a/tests/Server/Disk/LoggerDiskTest.php +++ b/tests/Server/Disk/LoggerDiskTest.php @@ -40,7 +40,7 @@ public function testGet() { $disk = new LoggerDisk($this->disk(), new NullLogger); - $this->assertInstanceOf(Volume::class, $disk->get(new MountPoint('/'))->match( + $this->assertInstanceOf(Volume::class, $disk->get(MountPoint::of('/'))->match( static fn($volume) => $volume, static fn() => null, )); diff --git a/tests/Server/Disk/UnixDiskTest.php b/tests/Server/Disk/UnixDiskTest.php index 30503e2..88fe325 100644 --- a/tests/Server/Disk/UnixDiskTest.php +++ b/tests/Server/Disk/UnixDiskTest.php @@ -56,7 +56,7 @@ public function testGet() { $volume = $this ->disk - ->get(new MountPoint('/')) + ->get(MountPoint::of('/')) ->match( static fn($volume) => $volume, static fn() => null, diff --git a/tests/Server/Disk/Volume/MountPointTest.php b/tests/Server/Disk/Volume/MountPointTest.php index 64451d0..842620c 100644 --- a/tests/Server/Disk/Volume/MountPointTest.php +++ b/tests/Server/Disk/Volume/MountPointTest.php @@ -3,25 +3,15 @@ namespace Tests\Innmind\Server\Status\Server\Disk\Volume; -use Innmind\Server\Status\{ - Server\Disk\Volume\MountPoint, - Exception\EmptyPathNotAllowed, -}; +use Innmind\Server\Status\Server\Disk\Volume\MountPoint; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class MountPointTest extends TestCase { public function testInterface() { - $mountPoint = new MountPoint('foo'); + $mountPoint = MountPoint::of('foo'); $this->assertSame('foo', $mountPoint->toString()); } - - public function testThrowWhenEmptyMountPoint() - { - $this->expectException(EmptyPathNotAllowed::class); - - new MountPoint(''); - } } diff --git a/tests/Server/Disk/VolumeTest.php b/tests/Server/Disk/VolumeTest.php index d121e89..7b0d29b 100644 --- a/tests/Server/Disk/VolumeTest.php +++ b/tests/Server/Disk/VolumeTest.php @@ -16,7 +16,7 @@ class VolumeTest extends TestCase public function testInterface() { $volume = new Volume( - $mount = new MountPoint('/'), + $mount = MountPoint::of('/'), $size = new Bytes(42), $available = new Bytes(42), $used = new Bytes(42), From ddfd5057b09e4358a054c253ac3a6aa3653013bf Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Thu, 8 May 2025 12:08:47 +0200 Subject: [PATCH 04/23] add Usage::maybe() to statically make sure the value is correct --- src/Server/Disk/UnixDisk.php | 9 +++++--- src/Server/Disk/Volume/Usage.php | 16 ++++++++------ tests/Server/Disk/Volume/UsageTest.php | 29 ++++++++++++++------------ tests/Server/Disk/VolumeTest.php | 5 ++++- 4 files changed, 36 insertions(+), 23 deletions(-) diff --git a/src/Server/Disk/UnixDisk.php b/src/Server/Disk/UnixDisk.php index 4a2094e..d439c95 100644 --- a/src/Server/Disk/UnixDisk.php +++ b/src/Server/Disk/UnixDisk.php @@ -112,15 +112,18 @@ private function parse(Str $output): Set $used = $parts ->get('used') ->flatMap(static fn($used) => Bytes::of($used)); - $usage = $parts->get('usage'); + $usage = $parts + ->get('usage') + ->map(static fn($value) => (float) $value) + ->flatMap(Usage::maybe(...)); return Maybe::all($mountPoint, $size, $available, $used, $usage) - ->map(static fn(MountPoint $mountPoint, Bytes $size, Bytes $available, Bytes $used, string $usage) => new Volume( + ->map(static fn(MountPoint $mountPoint, Bytes $size, Bytes $available, Bytes $used, Usage $usage) => new Volume( $mountPoint, $size, $available, $used, - new Usage((float) $usage), + $usage, )); }); diff --git a/src/Server/Disk/Volume/Usage.php b/src/Server/Disk/Volume/Usage.php index 5bdf518..11f4f18 100644 --- a/src/Server/Disk/Volume/Usage.php +++ b/src/Server/Disk/Volume/Usage.php @@ -3,22 +3,26 @@ namespace Innmind\Server\Status\Server\Disk\Volume; -use Innmind\Server\Status\Exception\OutOfBoundsPercentage; +use Innmind\Immutable\Maybe; final class Usage { - private float $value; + private function __construct( + private float $value, + ) { + } /** - * @throws OutOfBoundsPercentage + * @return Maybe */ - public function __construct(float $value) + public static function maybe(float $value): Maybe { if ($value < 0 || $value > 100) { - throw new OutOfBoundsPercentage((string) $value); + /** @var Maybe */ + return Maybe::nothing(); } - $this->value = $value; + return Maybe::just(new self($value)); } public function toFloat(): float diff --git a/tests/Server/Disk/Volume/UsageTest.php b/tests/Server/Disk/Volume/UsageTest.php index 3ceae01..c74da00 100644 --- a/tests/Server/Disk/Volume/UsageTest.php +++ b/tests/Server/Disk/Volume/UsageTest.php @@ -3,33 +3,36 @@ namespace Tests\Innmind\Server\Status\Server\Disk\Volume; -use Innmind\Server\Status\{ - Server\Disk\Volume\Usage, - Exception\OutOfBoundsPercentage, -}; +use Innmind\Server\Status\Server\Disk\Volume\Usage; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class UsageTest extends TestCase { public function testInterface() { - $usage = new Usage(42.24); + $usage = Usage::maybe(42.24)->match( + static fn($usage) => $usage, + static fn() => null, + ); + $this->assertNotNull($usage); $this->assertSame(42.24, $usage->toFloat()); $this->assertSame('42.24%', $usage->toString()); } - public function testThrowWhenUsageLowerThanZero() + public function testReturnNothingWhenUsageLowerThanZero() { - $this->expectException(OutOfBoundsPercentage::class); - - new Usage(-1); + $this->assertNull(Usage::maybe(-1)->match( + static fn($usage) => $usage, + static fn() => null, + )); } - public function testThrowWhenUsageHigherThanHundred() + public function testReturnNothingWhenUsageHigherThanHundred() { - $this->expectException(OutOfBoundsPercentage::class); - - new Usage(101); + $this->assertNull(Usage::maybe(101)->match( + static fn($usage) => $usage, + static fn() => null, + )); } } diff --git a/tests/Server/Disk/VolumeTest.php b/tests/Server/Disk/VolumeTest.php index 7b0d29b..6e5b017 100644 --- a/tests/Server/Disk/VolumeTest.php +++ b/tests/Server/Disk/VolumeTest.php @@ -20,7 +20,10 @@ public function testInterface() $size = new Bytes(42), $available = new Bytes(42), $used = new Bytes(42), - $usage = new Usage(100), + $usage = Usage::maybe(100)->match( + static fn($usage) => $usage, + static fn() => throw new \Exception('Should be valid'), + ), ); $this->assertSame($mount, $volume->mountPoint()); From acad5d2df8525dd740279e3165321914477adff1 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Thu, 8 May 2025 12:11:06 +0200 Subject: [PATCH 05/23] make Volume constructor private --- src/Server/Disk/UnixDisk.php | 8 +------- src/Server/Disk/Volume.php | 23 +++++++++++------------ tests/Server/Disk/VolumeTest.php | 2 +- 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/src/Server/Disk/UnixDisk.php b/src/Server/Disk/UnixDisk.php index d439c95..ade3667 100644 --- a/src/Server/Disk/UnixDisk.php +++ b/src/Server/Disk/UnixDisk.php @@ -118,13 +118,7 @@ private function parse(Str $output): Set ->flatMap(Usage::maybe(...)); return Maybe::all($mountPoint, $size, $available, $used, $usage) - ->map(static fn(MountPoint $mountPoint, Bytes $size, Bytes $available, Bytes $used, Usage $usage) => new Volume( - $mountPoint, - $size, - $available, - $used, - $usage, - )); + ->map(Volume::of(...)); }); return $volumes diff --git a/src/Server/Disk/Volume.php b/src/Server/Disk/Volume.php index 487e423..aa261ec 100644 --- a/src/Server/Disk/Volume.php +++ b/src/Server/Disk/Volume.php @@ -11,24 +11,23 @@ final class Volume { - private MountPoint $mountPoint; - private Bytes $size; - private Bytes $available; - private Bytes $used; - private Usage $usage; + private function __construct( + private MountPoint $mountPoint, + private Bytes $size, + private Bytes $available, + private Bytes $used, + private Usage $usage, + ) { + } - public function __construct( + public static function of( MountPoint $mountPoint, Bytes $size, Bytes $available, Bytes $used, Usage $usage, - ) { - $this->mountPoint = $mountPoint; - $this->size = $size; - $this->available = $available; - $this->used = $used; - $this->usage = $usage; + ): self { + return new self($mountPoint, $size, $available, $used, $usage); } public function mountPoint(): MountPoint diff --git a/tests/Server/Disk/VolumeTest.php b/tests/Server/Disk/VolumeTest.php index 6e5b017..4a8088d 100644 --- a/tests/Server/Disk/VolumeTest.php +++ b/tests/Server/Disk/VolumeTest.php @@ -15,7 +15,7 @@ class VolumeTest extends TestCase { public function testInterface() { - $volume = new Volume( + $volume = Volume::of( $mount = MountPoint::of('/'), $size = new Bytes(42), $available = new Bytes(42), From 779bca91fb51612cd61ca47b17216d110d05b087 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Thu, 8 May 2025 12:15:18 +0200 Subject: [PATCH 06/23] do not keep in memory string representation of bytes --- src/Server/Memory/Bytes.php | 67 +++++++++++++------------------------ 1 file changed, 23 insertions(+), 44 deletions(-) diff --git a/src/Server/Memory/Bytes.php b/src/Server/Memory/Bytes.php index 3bdbdd5..fe8f8de 100644 --- a/src/Server/Memory/Bytes.php +++ b/src/Server/Memory/Bytes.php @@ -22,7 +22,6 @@ final class Bytes private const PETABYTES = 1024 ** 6; private int $value; - private string $string; /** * @throws BytesCannotBeNegative @@ -34,48 +33,6 @@ public function __construct(int $value) } $this->value = $value; - $this->string = $value.'B'; - - switch (true) { - case $value < self::BYTES: - $this->string = $value.'B'; - break; - - case $value < self::KILOBYTES: - $this->string = \sprintf( - '%sKB', - \round($value/self::BYTES, 3), - ); - break; - - case $value < self::MEGABYTES: - $this->string = \sprintf( - '%sMB', - \round($value/self::KILOBYTES, 3), - ); - break; - - case $value < self::GIGABYTES: - $this->string = \sprintf( - '%sGB', - \round($value/self::MEGABYTES, 3), - ); - break; - - case $value < self::TERABYTES: - $this->string = \sprintf( - '%sTB', - \round($value/self::GIGABYTES, 3), - ); - break; - - case $value < self::PETABYTES: - $this->string = \sprintf( - '%sPB', - \round($value/self::TERABYTES, 3), - ); - break; - } } public function toInt(): int @@ -85,7 +42,29 @@ public function toInt(): int public function toString(): string { - return $this->string; + return match (true) { + $this->value < self::BYTES => $this->value.'B', + $this->value < self::KILOBYTES => \sprintf( + '%sKB', + \round($this->value/self::BYTES, 3), + ), + $this->value < self::MEGABYTES => \sprintf( + '%sMB', + \round($this->value/self::KILOBYTES, 3), + ), + $this->value < self::GIGABYTES => \sprintf( + '%sGB', + \round($this->value/self::MEGABYTES, 3), + ), + $this->value < self::TERABYTES => \sprintf( + '%sTB', + \round($this->value/self::GIGABYTES, 3), + ), + $this->value < self::PETABYTES => \sprintf( + '%sPB', + \round($this->value/self::TERABYTES, 3), + ), + }; } /** From aa33fd1ae36d5f32d4d83945d70bf9e2db688d97 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Thu, 8 May 2025 12:36:37 +0200 Subject: [PATCH 07/23] add Bytes::of() to statically make sure the value is correct --- CHANGELOG.md | 1 + src/Exception/BytesCannotBeNegative.php | 8 ---- src/Facade/Memory/LinuxFacade.php | 60 ++++++++++++++----------- src/Facade/Memory/OSXFacade.php | 20 +++++---- src/Server/Disk/UnixDisk.php | 6 +-- src/Server/Memory/Bytes.php | 44 ++++++++++++------ tests/Server/Disk/VolumeTest.php | 6 +-- tests/Server/Memory/BytesTest.php | 20 +++------ tests/Server/MemoryTest.php | 10 ++--- 9 files changed, 95 insertions(+), 80 deletions(-) delete mode 100644 src/Exception/BytesCannotBeNegative.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 193d316..541711e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ### Removed - `Innmind\Server\Status\Exception\EmptyPathNotAllowed` +- `Innmind\Server\Status\Exception\BytesCannotBeNegative` ## 4.1.1 - 2024-09-30 diff --git a/src/Exception/BytesCannotBeNegative.php b/src/Exception/BytesCannotBeNegative.php deleted file mode 100644 index 94a6185..0000000 --- a/src/Exception/BytesCannotBeNegative.php +++ /dev/null @@ -1,8 +0,0 @@ - */ $amounts = $output ->trim() ->split("\n") ->filter(static fn(Str $line) => $line->matches( '~^('.\implode('|', \array_keys(self::$entries)).'):~', )) - ->reduce( - Map::of(), - static function(Map $map, Str $line): Map { - $elements = $line - ->capture('~^(?P[a-zA-Z]+): +(?P\d+) kB$~') - ->map(static fn($_, $part) => $part->toString()); - - return Maybe::all($elements->get('key'), $elements->get('value')) - ->map(static fn(string $key, string $value) => [$key, (int) $value]) - ->match( - static fn($pair) => ($map)( - self::$entries[$pair[0]], - $pair[1] * 1024, // 1024 represents a kilobyte - ), - static fn() => $map, - ); - }, + ->map( + static fn($line) => $line + ->capture('~^(?P[a-zA-Z]+): +(?P\d+) kB$~') + ->map(static fn($_, $part) => $part->toString()), + ) + ->flatMap( + static fn($elements) => Maybe::all( + $elements->get('key'), + $elements + ->get('value') + ->map(static fn($value) => $value.'K') + ->flatMap(Bytes::maybe(...)), + ) + ->map(static fn(string $key, Bytes $value) => [$key, $value]) + ->toSequence(), ); + $amounts = Map::of(...$amounts->toList()) + ->map(static fn($_, $value) => Bytes::of( + $value->toInt() * 1024, // 1024 represents a kilobyte + )); $total = $amounts->get('total'); $free = $amounts->get('free'); $active = $amounts->get('active'); $swap = $amounts->get('swap'); - $used = Maybe::all($total, $free)->map(static fn(int $total, int $free) => $total - $free); + $used = Maybe::all($total, $free) + ->map(static fn(Bytes $total, Bytes $free) => $total->toInt() - $free->toInt()) + ->keep( + Is::int() + ->positive() + ->or(Is::value(0)) + ->asPredicate(), + ) + ->map(Bytes::of(...)); return Maybe::all($total, $active, $free, $swap, $used) - ->map(static fn(int $total, int $active, int $free, int $swap, int $used) => new Memory( - new Bytes($total), - new Bytes($active), - new Bytes($free), - new Bytes($swap), - new Bytes($used), + ->map(static fn(Bytes $total, Bytes $active, Bytes $free, Bytes $swap, Bytes $used) => new Memory( + $total, + $active, + $free, + $swap, + $used, )) ->match( Attempt::result(...), diff --git a/src/Facade/Memory/OSXFacade.php b/src/Facade/Memory/OSXFacade.php index 4eb3b9c..df09bd0 100644 --- a/src/Facade/Memory/OSXFacade.php +++ b/src/Facade/Memory/OSXFacade.php @@ -46,7 +46,8 @@ public function __invoke(): Attempt ->trim() ->capture('~^hw.memsize: (?P\d+)$~') ->get('total') - ->map(static fn($total) => $total->toString()); + ->map(static fn($total) => $total->toString()) + ->flatMap(Bytes::maybe(...)); $swap = $this ->run( Command::foreground('sysctl') @@ -56,7 +57,7 @@ public function __invoke(): Attempt ->capture('~used = (?P\d+[\.,]?\d*[KMGTP])~') ->get('swap') ->map(static fn($swap) => $swap->toString()) - ->flatMap(static fn($swap) => Bytes::of($swap)); + ->flatMap(Bytes::maybe(...)); $amounts = $this ->run( Command::foreground('top') @@ -74,10 +75,10 @@ public function __invoke(): Attempt ->map(static fn($_, $amount) => $amount->toString()); $unused = $amounts ->get('unused') - ->flatMap(static fn($unused) => Bytes::of($unused)); + ->flatMap(Bytes::maybe(...)); $used = $amounts ->get('used') - ->flatMap(static fn($used) => Bytes::of($used)); + ->flatMap(Bytes::maybe(...)); $active = $this ->run( Command::foreground('vm_stat')->pipe( @@ -88,12 +89,15 @@ public function __invoke(): Attempt ->trim() ->capture('~(?P\d+)~') ->get('active') - ->map(static fn($active) => $active->toString()); + ->map(static fn($active) => $active->toString()) + ->flatMap(Bytes::maybe(...)) + ->map(static fn($bytes) => $bytes->toInt() * 4096) + ->map(Bytes::of(...)); return Maybe::all($total, $active, $unused, $swap, $used) - ->map(static fn(string $total, string $active, Bytes $unused, Bytes $swap, Bytes $used) => new Memory( - new Bytes((int) $total), - new Bytes(((int) $active) * 4096), + ->map(static fn(Bytes $total, Bytes $active, Bytes $unused, Bytes $swap, Bytes $used) => new Memory( + $total, + $active, $unused, $swap, $used, diff --git a/src/Server/Disk/UnixDisk.php b/src/Server/Disk/UnixDisk.php index ade3667..ff817db 100644 --- a/src/Server/Disk/UnixDisk.php +++ b/src/Server/Disk/UnixDisk.php @@ -105,13 +105,13 @@ private function parse(Str $output): Set ->map(MountPoint::of(...)); $size = $parts ->get('size') - ->flatMap(static fn($size) => Bytes::of($size)); + ->flatMap(Bytes::maybe(...)); $available = $parts ->get('available') - ->flatMap(static fn($available) => Bytes::of($available)); + ->flatMap(Bytes::maybe(...)); $used = $parts ->get('used') - ->flatMap(static fn($used) => Bytes::of($used)); + ->flatMap(Bytes::maybe(...)); $usage = $parts ->get('usage') ->map(static fn($value) => (float) $value) diff --git a/src/Server/Memory/Bytes.php b/src/Server/Memory/Bytes.php index fe8f8de..5a6aa98 100644 --- a/src/Server/Memory/Bytes.php +++ b/src/Server/Memory/Bytes.php @@ -3,7 +3,7 @@ namespace Innmind\Server\Status\Server\Memory; -use Innmind\Server\Status\Exception\BytesCannotBeNegative; +use Innmind\Validation\Is; use Innmind\Immutable\{ Str, Maybe, @@ -21,20 +21,25 @@ final class Bytes private const TERABYTES = 1024 ** 5; private const PETABYTES = 1024 ** 6; - private int $value; + /** + * @param int<0, max> $value + */ + private function __construct( + private int $value, + ) { + } /** - * @throws BytesCannotBeNegative + * @param int<0, max> $value */ - public function __construct(int $value) + public static function of(int $value): self { - if ($value < 0) { - throw new BytesCannotBeNegative((string) $value); - } - - $this->value = $value; + return new self($value); } + /** + * @return int<0, max> + */ public function toInt(): int { return $this->value; @@ -70,14 +75,21 @@ public function toString(): string /** * @return Maybe */ - public static function of(string $bytes): Maybe + public static function maybe(string $bytes): Maybe { if ($bytes === (string) (int) $bytes) { - return Maybe::just(new self((int) $bytes)); + return Maybe::just((int) $bytes) + ->keep( + Is::int() + ->positive() + ->or(Is::value(0)) + ->asPredicate(), + ) + ->map(static fn($value) => new self($value)); } - return Maybe::just(Str::of($bytes)) - ->filter(static fn($bytes) => $bytes->length() >= 2) + return Str::of($bytes) + ->maybe(static fn($bytes) => $bytes->length() >= 2) ->flatMap(static fn($bytes) => self::attemptLinux($bytes)->otherwise( static fn() => self::attemptDarwin($bytes), )); @@ -133,6 +145,12 @@ private static function fromUnit(Str $bytes, Str $unit): Maybe return Maybe::of($multiplier) ->map(static fn($multiplier) => (int) (((float) $bytes->toString()) * (float) $multiplier)) + ->keep( + Is::int() + ->positive() + ->or(Is::value(0)) + ->asPredicate(), + ) ->map(static fn($bytes) => new self($bytes)); } } diff --git a/tests/Server/Disk/VolumeTest.php b/tests/Server/Disk/VolumeTest.php index 4a8088d..46ad4d6 100644 --- a/tests/Server/Disk/VolumeTest.php +++ b/tests/Server/Disk/VolumeTest.php @@ -17,9 +17,9 @@ public function testInterface() { $volume = Volume::of( $mount = MountPoint::of('/'), - $size = new Bytes(42), - $available = new Bytes(42), - $used = new Bytes(42), + $size = Bytes::of(42), + $available = Bytes::of(42), + $used = Bytes::of(42), $usage = Usage::maybe(100)->match( static fn($usage) => $usage, static fn() => throw new \Exception('Should be valid'), diff --git a/tests/Server/Memory/BytesTest.php b/tests/Server/Memory/BytesTest.php index 46230ed..79690a4 100644 --- a/tests/Server/Memory/BytesTest.php +++ b/tests/Server/Memory/BytesTest.php @@ -3,10 +3,7 @@ namespace Tests\Innmind\Server\Status\Server\Memory; -use Innmind\Server\Status\{ - Server\Memory\Bytes, - Exception\BytesCannotBeNegative, -}; +use Innmind\Server\Status\Server\Memory\Bytes; use Innmind\BlackBox\PHPUnit\Framework\TestCase; use PHPUnit\Framework\Attributes\DataProvider; @@ -15,23 +12,16 @@ class BytesTest extends TestCase #[DataProvider('steps')] public function testInterface($value, $expected) { - $bytes = new Bytes($value); + $bytes = Bytes::of($value); $this->assertSame($value, $bytes->toInt()); $this->assertSame($expected, $bytes->toString()); } - public function testThrowWhenNegative() - { - $this->expectException(BytesCannotBeNegative::class); - - new Bytes(-1); - } - #[DataProvider('strings')] public function testFromString($string, $expected) { - $bytes = Bytes::of($string)->match( + $bytes = Bytes::maybe($string)->match( static fn($bytes) => $bytes, static fn() => null, ); @@ -42,7 +32,7 @@ public function testFromString($string, $expected) public function testReturnNothingWhenUnknownFormat() { - $this->assertNull(Bytes::of('42Br')->match( + $this->assertNull(Bytes::maybe('42Br')->match( static fn($bytes) => $bytes, static fn() => null, )); @@ -51,7 +41,7 @@ public function testReturnNothingWhenUnknownFormat() #[DataProvider('invalidStrings')] public function testReturnNothingWhenStringTooShort($string) { - $this->assertNull(Bytes::of($string)->match( + $this->assertNull(Bytes::maybe($string)->match( static fn($bytes) => $bytes, static fn() => null, )); diff --git a/tests/Server/MemoryTest.php b/tests/Server/MemoryTest.php index e33c392..2ff286b 100644 --- a/tests/Server/MemoryTest.php +++ b/tests/Server/MemoryTest.php @@ -14,11 +14,11 @@ class MemoryTest extends TestCase public function testInterface() { $memory = new Memory( - $total = new Bytes(42), - $active = new Bytes(42), - $free = new Bytes(42), - $swap = new Bytes(42), - $used = new Bytes(42), + $total = Bytes::of(42), + $active = Bytes::of(42), + $free = Bytes::of(42), + $swap = Bytes::of(42), + $used = Bytes::of(42), ); $this->assertSame($total, $memory->total()); From fda2099fbce457cf100840c89f9b7bb909156eb9 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Thu, 8 May 2025 13:34:03 +0200 Subject: [PATCH 08/23] add Command::of() to statically make sure the value is correct --- CHANGELOG.md | 1 + src/Exception/EmptyCommandNotAllowed.php | 8 -------- src/Server/Process/Command.php | 22 +++++++++++++--------- src/Server/Processes/UnixProcesses.php | 10 +++++++--- tests/Server/Process/CommandTest.php | 16 +++------------- tests/Server/ProcessTest.php | 2 +- 6 files changed, 25 insertions(+), 34 deletions(-) delete mode 100644 src/Exception/EmptyCommandNotAllowed.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 541711e..3bbda9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - `Innmind\Server\Status\Exception\EmptyPathNotAllowed` - `Innmind\Server\Status\Exception\BytesCannotBeNegative` +- `Innmind\Server\Status\Exception\EmptyCommandNotAllowed` ## 4.1.1 - 2024-09-30 diff --git a/src/Exception/EmptyCommandNotAllowed.php b/src/Exception/EmptyCommandNotAllowed.php deleted file mode 100644 index 2c7c032..0000000 --- a/src/Exception/EmptyCommandNotAllowed.php +++ /dev/null @@ -1,8 +0,0 @@ -value = $value; + return new self($value); } public function matches(RegExp $pattern): bool @@ -33,6 +34,9 @@ public function matches(RegExp $pattern): bool return $pattern->matches(Str::of($this->value)); } + /** + * @return non-empty-string + */ public function toString(): string { return $this->value; diff --git a/src/Server/Processes/UnixProcesses.php b/src/Server/Processes/UnixProcesses.php index 0a85112..e9f1f05 100644 --- a/src/Server/Processes/UnixProcesses.php +++ b/src/Server/Processes/UnixProcesses.php @@ -17,6 +17,7 @@ Clock, Format, }; +use Innmind\Validation\Is; use Innmind\Immutable\{ Str, Sequence, @@ -117,16 +118,19 @@ private function parse(Str $output): Set ->map(static fn($value) => (float) $value) ->flatMap(Percentage::maybe(...)); $memory = $parts->get(3); - $command = $parts->get(4); + $command = $parts + ->get(4) + ->keep(Is::string()->nonEmpty()->asPredicate()) + ->map(Command::of(...)); return Maybe::all($user, $pid, $percentage, $memory, $command) - ->map(fn(string $user, string $pid, Percentage $percentage, string $memory, string $command) => new Process( + ->map(fn(string $user, string $pid, Percentage $percentage, string $memory, Command $command) => new Process( new Pid((int) $pid), new User($user), $percentage, new Memory((float) $memory), $this->clock->at($start, Format::of('D M j H:i:s Y')), - new Command($command), + $command, )); }); diff --git a/tests/Server/Process/CommandTest.php b/tests/Server/Process/CommandTest.php index 0dc8934..dd2244a 100644 --- a/tests/Server/Process/CommandTest.php +++ b/tests/Server/Process/CommandTest.php @@ -3,10 +3,7 @@ namespace Tests\Innmind\Server\Status\Server\Process; -use Innmind\Server\Status\{ - Server\Process\Command, - Exception\EmptyCommandNotAllowed, -}; +use Innmind\Server\Status\Server\Process\Command; use Innmind\Immutable\RegExp; use Innmind\BlackBox\PHPUnit\Framework\TestCase; @@ -14,23 +11,16 @@ class CommandTest extends TestCase { public function testInterface() { - $command = new Command('foo'); + $command = Command::of('foo'); $this->assertSame('foo', $command->toString()); } public function testMatches() { - $command = new Command('foo'); + $command = Command::of('foo'); $this->assertTrue($command->matches(RegExp::of('/^foo/'))); $this->assertFalse($command->matches(RegExp::of('/bar/'))); } - - public function testThrowWhenEmptyCommand() - { - $this->expectException(EmptyCommandNotAllowed::class); - - new Command(''); - } } diff --git a/tests/Server/ProcessTest.php b/tests/Server/ProcessTest.php index 60ec0e3..3fa2319 100644 --- a/tests/Server/ProcessTest.php +++ b/tests/Server/ProcessTest.php @@ -36,7 +36,7 @@ public function testInterface() ), $memory = new Memory(42), $start = Maybe::just($pointInTime), - $command = new Command('/sbin/launchd'), + $command = Command::of('/sbin/launchd'), ); $this->assertSame($pid, $process->pid()); From 7eeb557a9d4ffe68e93febc9cff5c5d9ec6acadc Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Thu, 8 May 2025 13:38:33 +0200 Subject: [PATCH 09/23] add Memory::maybe() to statically make sure the value is correct --- CHANGELOG.md | 1 + src/Exception/OutOfBoundsPercentage.php | 8 ------- src/Server/Process/Memory.php | 16 +++++++++----- src/Server/Processes/UnixProcesses.php | 9 +++++--- tests/Server/Process/MemoryTest.php | 29 ++++++++++++++----------- tests/Server/ProcessTest.php | 5 ++++- 6 files changed, 37 insertions(+), 31 deletions(-) delete mode 100644 src/Exception/OutOfBoundsPercentage.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bbda9a..4876e52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - `Innmind\Server\Status\Exception\EmptyPathNotAllowed` - `Innmind\Server\Status\Exception\BytesCannotBeNegative` - `Innmind\Server\Status\Exception\EmptyCommandNotAllowed` +- `Innmind\Server\Status\Exception\OutOfBoundsPercentage` ## 4.1.1 - 2024-09-30 diff --git a/src/Exception/OutOfBoundsPercentage.php b/src/Exception/OutOfBoundsPercentage.php deleted file mode 100644 index 59bbd80..0000000 --- a/src/Exception/OutOfBoundsPercentage.php +++ /dev/null @@ -1,8 +0,0 @@ - */ - public function __construct(float $value) + public static function maybe(float $value): Maybe { if ($value < 0 || $value > 100) { - throw new OutOfBoundsPercentage((string) $value); + /** @var Maybe */ + return Maybe::nothing(); } - $this->value = $value; + return Maybe::just(new self($value)); } public function toFloat(): float diff --git a/src/Server/Processes/UnixProcesses.php b/src/Server/Processes/UnixProcesses.php index e9f1f05..2802781 100644 --- a/src/Server/Processes/UnixProcesses.php +++ b/src/Server/Processes/UnixProcesses.php @@ -117,18 +117,21 @@ private function parse(Str $output): Set ->get(2) ->map(static fn($value) => (float) $value) ->flatMap(Percentage::maybe(...)); - $memory = $parts->get(3); + $memory = $parts + ->get(3) + ->map(static fn($value) => (float) $value) + ->flatMap(Memory::maybe(...)); $command = $parts ->get(4) ->keep(Is::string()->nonEmpty()->asPredicate()) ->map(Command::of(...)); return Maybe::all($user, $pid, $percentage, $memory, $command) - ->map(fn(string $user, string $pid, Percentage $percentage, string $memory, Command $command) => new Process( + ->map(fn(string $user, string $pid, Percentage $percentage, Memory $memory, Command $command) => new Process( new Pid((int) $pid), new User($user), $percentage, - new Memory((float) $memory), + $memory, $this->clock->at($start, Format::of('D M j H:i:s Y')), $command, )); diff --git a/tests/Server/Process/MemoryTest.php b/tests/Server/Process/MemoryTest.php index 6f9999d..cd9fd86 100644 --- a/tests/Server/Process/MemoryTest.php +++ b/tests/Server/Process/MemoryTest.php @@ -3,33 +3,36 @@ namespace Tests\Innmind\Server\Status\Server\Process; -use Innmind\Server\Status\{ - Server\Process\Memory, - Exception\OutOfBoundsPercentage, -}; +use Innmind\Server\Status\Server\Process\Memory; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class MemoryTest extends TestCase { public function testInterface() { - $memory = new Memory(42.24); + $memory = Memory::maybe(42.24)->match( + static fn($memory) => $memory, + static fn() => null, + ); + $this->assertNotNull($memory); $this->assertSame(42.24, $memory->toFloat()); $this->assertSame('42.24%', $memory->toString()); } - public function testThrowWhenMemoryLowerThanZero() + public function testReturnNothingWhenMemoryLowerThanZero() { - $this->expectException(OutOfBoundsPercentage::class); - - new Memory(-1); + $this->assertNull(Memory::maybe(-1)->match( + static fn($memory) => $memory, + static fn() => null, + )); } - public function testThrowWhenMemoryHigherThanHundred() + public function testReturnNothingWhenMemoryHigherThanHundred() { - $this->expectException(OutOfBoundsPercentage::class); - - new Memory(101); + $this->assertNull(Memory::maybe(101)->match( + static fn($memory) => $memory, + static fn() => null, + )); } } diff --git a/tests/Server/ProcessTest.php b/tests/Server/ProcessTest.php index 3fa2319..18218e1 100644 --- a/tests/Server/ProcessTest.php +++ b/tests/Server/ProcessTest.php @@ -34,7 +34,10 @@ public function testInterface() static fn($percentage) => $percentage, static fn() => throw new \Exception('Should be valid'), ), - $memory = new Memory(42), + $memory = Memory::maybe(42)->match( + static fn($memory) => $memory, + static fn() => throw new \Exception('Should be valid'), + ), $start = Maybe::just($pointInTime), $command = Command::of('/sbin/launchd'), ); From ade191a073edd763291aa3bb5d274de7cbd03aef Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Thu, 8 May 2025 13:42:00 +0200 Subject: [PATCH 10/23] add Pid::of() to statically make sure the value is correct --- CHANGELOG.md | 1 + src/Exception/LowestPidPossibleIsOne.php | 8 -------- src/Server/Process/Pid.php | 20 +++++++++---------- src/Server/Processes/UnixProcesses.php | 10 +++++++--- tests/Server/Process/PidTest.php | 14 ++----------- tests/Server/ProcessTest.php | 2 +- .../Server/Processes/LoggerProcessesTest.php | 2 +- tests/Server/Processes/UnixProcessesTest.php | 4 ++-- 8 files changed, 24 insertions(+), 37 deletions(-) delete mode 100644 src/Exception/LowestPidPossibleIsOne.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 4876e52..3badaa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - `Innmind\Server\Status\Exception\BytesCannotBeNegative` - `Innmind\Server\Status\Exception\EmptyCommandNotAllowed` - `Innmind\Server\Status\Exception\OutOfBoundsPercentage` +- `Innmind\Server\Status\Exception\LowestPidPossibleIsOne` ## 4.1.1 - 2024-09-30 diff --git a/src/Exception/LowestPidPossibleIsOne.php b/src/Exception/LowestPidPossibleIsOne.php deleted file mode 100644 index f19d11e..0000000 --- a/src/Exception/LowestPidPossibleIsOne.php +++ /dev/null @@ -1,8 +0,0 @@ - $value + */ + private function __construct( + private int $value, + ) { + } /** - * @throws LowestPidPossibleIsOne + * @param int<1, max> $value */ - public function __construct(int $value) + public static function of(int $value): self { - if ($value < 1) { - throw new LowestPidPossibleIsOne((string) $value); - } - - $this->value = $value; + return new self($value); } public function equals(self $pid): bool diff --git a/src/Server/Processes/UnixProcesses.php b/src/Server/Processes/UnixProcesses.php index 2802781..c7a2c39 100644 --- a/src/Server/Processes/UnixProcesses.php +++ b/src/Server/Processes/UnixProcesses.php @@ -112,7 +112,11 @@ private function parse(Str $output): Set ->drop(5) ->map(static fn($part) => $part->toString()); $user = $parts->get(0); - $pid = $parts->get(1); + $pid = $parts + ->get(1) + ->map(static fn($value) => (int) $value) + ->keep(Is::int()->positive()->asPredicate()) + ->map(Pid::of(...)); $percentage = $parts ->get(2) ->map(static fn($value) => (float) $value) @@ -127,8 +131,8 @@ private function parse(Str $output): Set ->map(Command::of(...)); return Maybe::all($user, $pid, $percentage, $memory, $command) - ->map(fn(string $user, string $pid, Percentage $percentage, Memory $memory, Command $command) => new Process( - new Pid((int) $pid), + ->map(fn(string $user, Pid $pid, Percentage $percentage, Memory $memory, Command $command) => new Process( + $pid, new User($user), $percentage, $memory, diff --git a/tests/Server/Process/PidTest.php b/tests/Server/Process/PidTest.php index fd9e15f..63641bc 100644 --- a/tests/Server/Process/PidTest.php +++ b/tests/Server/Process/PidTest.php @@ -3,26 +3,16 @@ namespace Tests\Innmind\Server\Status\Server\Process; -use Innmind\Server\Status\{ - Server\Process\Pid, - Exception\LowestPidPossibleIsOne, -}; +use Innmind\Server\Status\Server\Process\Pid; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class PidTest extends TestCase { public function testInterface() { - $pid = new Pid(42); + $pid = Pid::of(42); $this->assertSame(42, $pid->toInt()); $this->assertSame('42', $pid->toString()); } - - public function testThrowWhenPidTooLow() - { - $this->expectException(LowestPidPossibleIsOne::class); - - new Pid(0); - } } diff --git a/tests/Server/ProcessTest.php b/tests/Server/ProcessTest.php index 18218e1..ffda42e 100644 --- a/tests/Server/ProcessTest.php +++ b/tests/Server/ProcessTest.php @@ -28,7 +28,7 @@ public function testInterface() ->forAll(PointInTime::any()) ->then(function($pointInTime) { $process = new Process( - $pid = new Pid(1), + $pid = Pid::of(1), $user = new User('root'), $cpu = Percentage::maybe(42)->match( static fn($percentage) => $percentage, diff --git a/tests/Server/Processes/LoggerProcessesTest.php b/tests/Server/Processes/LoggerProcessesTest.php index 5815672..c75bf7e 100644 --- a/tests/Server/Processes/LoggerProcessesTest.php +++ b/tests/Server/Processes/LoggerProcessesTest.php @@ -40,7 +40,7 @@ public function testGet() { $processes = new LoggerProcesses($this->processes(), new NullLogger); - $this->assertInstanceOf(Process::class, $processes->get(new Pid(1))->match( + $this->assertInstanceOf(Process::class, $processes->get(Pid::of(1))->match( static fn($process) => $process, static fn() => null, )); diff --git a/tests/Server/Processes/UnixProcessesTest.php b/tests/Server/Processes/UnixProcessesTest.php index 6646658..79ec228 100644 --- a/tests/Server/Processes/UnixProcessesTest.php +++ b/tests/Server/Processes/UnixProcessesTest.php @@ -76,7 +76,7 @@ public function testGet() { $process = $this ->processes - ->get(new Pid(1)) + ->get(Pid::of(1)) ->match( static fn($process) => $process, static fn() => null, @@ -91,7 +91,7 @@ public function testReturnNothingWhenProcessDoesntExist() $this->assertNull( $this ->processes - ->get(new Pid(42424)) + ->get(Pid::of(42424)) ->match( static fn($process) => $process, static fn() => null, From 9e099668d70711af3c488df2c5a437329def7b97 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Thu, 8 May 2025 13:44:36 +0200 Subject: [PATCH 11/23] add User::of() to statically make sure the value is correct --- CHANGELOG.md | 1 + src/Exception/EmptyUserNotAllowed.php | 8 -------- src/Server/Process/User.php | 23 +++++++++++++---------- src/Server/Processes/UnixProcesses.php | 9 ++++++--- tests/Server/Process/UserTest.php | 14 ++------------ tests/Server/ProcessTest.php | 2 +- 6 files changed, 23 insertions(+), 34 deletions(-) delete mode 100644 src/Exception/EmptyUserNotAllowed.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 3badaa4..a6ff4af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ - `Innmind\Server\Status\Exception\EmptyCommandNotAllowed` - `Innmind\Server\Status\Exception\OutOfBoundsPercentage` - `Innmind\Server\Status\Exception\LowestPidPossibleIsOne` +- `Innmind\Server\Status\Exception\EmptyUserNotAllowed` ## 4.1.1 - 2024-09-30 diff --git a/src/Exception/EmptyUserNotAllowed.php b/src/Exception/EmptyUserNotAllowed.php deleted file mode 100644 index 55074f0..0000000 --- a/src/Exception/EmptyUserNotAllowed.php +++ /dev/null @@ -1,8 +0,0 @@ -value = $value; + return new self($value); } + /** + * @return non-empty-string + */ public function toString(): string { return $this->value; diff --git a/src/Server/Processes/UnixProcesses.php b/src/Server/Processes/UnixProcesses.php index c7a2c39..b286b21 100644 --- a/src/Server/Processes/UnixProcesses.php +++ b/src/Server/Processes/UnixProcesses.php @@ -111,7 +111,10 @@ private function parse(Str $output): Set $parts = $parts ->drop(5) ->map(static fn($part) => $part->toString()); - $user = $parts->get(0); + $user = $parts + ->get(0) + ->keep(Is::string()->nonEmpty()->asPredicate()) + ->map(User::of(...)); $pid = $parts ->get(1) ->map(static fn($value) => (int) $value) @@ -131,9 +134,9 @@ private function parse(Str $output): Set ->map(Command::of(...)); return Maybe::all($user, $pid, $percentage, $memory, $command) - ->map(fn(string $user, Pid $pid, Percentage $percentage, Memory $memory, Command $command) => new Process( + ->map(fn(User $user, Pid $pid, Percentage $percentage, Memory $memory, Command $command) => new Process( $pid, - new User($user), + $user, $percentage, $memory, $this->clock->at($start, Format::of('D M j H:i:s Y')), diff --git a/tests/Server/Process/UserTest.php b/tests/Server/Process/UserTest.php index 9421623..180ea2c 100644 --- a/tests/Server/Process/UserTest.php +++ b/tests/Server/Process/UserTest.php @@ -3,25 +3,15 @@ namespace Tests\Innmind\Server\Status\Server\Process; -use Innmind\Server\Status\{ - Server\Process\User, - Exception\EmptyUserNotAllowed, -}; +use Innmind\Server\Status\Server\Process\User; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class UserTest extends TestCase { public function testInterface() { - $user = new User('foo'); + $user = User::of('foo'); $this->assertSame('foo', $user->toString()); } - - public function testThrowWhenEmptyUser() - { - $this->expectException(EmptyUserNotAllowed::class); - - new User(''); - } } diff --git a/tests/Server/ProcessTest.php b/tests/Server/ProcessTest.php index ffda42e..5fc4325 100644 --- a/tests/Server/ProcessTest.php +++ b/tests/Server/ProcessTest.php @@ -29,7 +29,7 @@ public function testInterface() ->then(function($pointInTime) { $process = new Process( $pid = Pid::of(1), - $user = new User('root'), + $user = User::of('root'), $cpu = Percentage::maybe(42)->match( static fn($percentage) => $percentage, static fn() => throw new \Exception('Should be valid'), From 1f204c96c5edafc0943c025afa934ea52dba2e30 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Thu, 8 May 2025 13:47:16 +0200 Subject: [PATCH 12/23] make Cpu constructor private --- src/Facade/Cpu/LinuxFacade.php | 15 ++++----------- src/Facade/Cpu/OSXFacade.php | 15 ++++----------- src/Server/Cpu.php | 20 ++++++++++---------- tests/Server/CpuTest.php | 2 +- 4 files changed, 19 insertions(+), 33 deletions(-) diff --git a/src/Facade/Cpu/LinuxFacade.php b/src/Facade/Cpu/LinuxFacade.php index 896d9fa..30749ab 100644 --- a/src/Facade/Cpu/LinuxFacade.php +++ b/src/Facade/Cpu/LinuxFacade.php @@ -87,10 +87,8 @@ private function parse(Str $output): Attempt ) ->map(static fn($cores) => (int) $cores) ->keep(Is::int()->positive()->asPredicate()) - ->match( - static fn($cores) => $cores, - static fn() => 1, - ); + ->otherwise(static fn() => Maybe::just(1)) + ->map(Cores::of(...)); $user = $percentages ->get('user') @@ -102,13 +100,8 @@ private function parse(Str $output): Attempt ->get('idle') ->flatMap(Percentage::maybe(...)); - return Maybe::all($user, $sys, $idle) - ->map(static fn(Percentage $user, Percentage $sys, Percentage $idle) => new Cpu( - $user, - $sys, - $idle, - Cores::of($cores), - )) + return Maybe::all($user, $sys, $idle, $cores) + ->map(Cpu::of(...)) ->match( Attempt::result(...), static fn() => Attempt::error(new \RuntimeException('Failed to parse CPU usage')), diff --git a/src/Facade/Cpu/OSXFacade.php b/src/Facade/Cpu/OSXFacade.php index cb79ac4..3a09b94 100644 --- a/src/Facade/Cpu/OSXFacade.php +++ b/src/Facade/Cpu/OSXFacade.php @@ -98,10 +98,8 @@ private function parse(Str $output): Attempt ->map(static fn($cores) => $cores->toString()) ->map(static fn($cores) => (int) $cores) ->keep(Is::int()->positive()->asPredicate()) - ->match( - static fn($cores) => $cores, - static fn() => 1, - ); + ->otherwise(static fn() => Maybe::just(1)) + ->map(Cores::of(...)); $user = $percentages ->get('user') ->flatMap(Percentage::maybe(...)); @@ -112,13 +110,8 @@ private function parse(Str $output): Attempt ->get('idle') ->flatMap(Percentage::maybe(...)); - return Maybe::all($user, $sys, $idle) - ->map(static fn(Percentage $user, Percentage $sys, Percentage $idle) => new Cpu( - $user, - $sys, - $idle, - Cores::of($cores), - )) + return Maybe::all($user, $sys, $idle, $cores) + ->map(Cpu::of(...)) ->match( Attempt::result(...), static fn() => Attempt::error(new \RuntimeException('Failed to parse CPU usage')), diff --git a/src/Server/Cpu.php b/src/Server/Cpu.php index 815f22c..4f4f8fb 100644 --- a/src/Server/Cpu.php +++ b/src/Server/Cpu.php @@ -13,21 +13,21 @@ */ final class Cpu { - private Percentage $user; - private Percentage $system; - private Percentage $idle; - private Cores $cores; + private function __construct( + private Percentage $user, + private Percentage $system, + private Percentage $idle, + private Cores $cores, + ) { + } - public function __construct( + public static function of( Percentage $user, Percentage $system, Percentage $idle, Cores $cores, - ) { - $this->user = $user; - $this->system = $system; - $this->idle = $idle; - $this->cores = $cores; + ): self { + return new self($user, $system, $idle, $cores); } public function user(): Percentage diff --git a/tests/Server/CpuTest.php b/tests/Server/CpuTest.php index 0e33981..3e7dab3 100644 --- a/tests/Server/CpuTest.php +++ b/tests/Server/CpuTest.php @@ -14,7 +14,7 @@ class CpuTest extends TestCase { public function testInterface() { - $cpu = new Cpu( + $cpu = Cpu::of( $user = Percentage::maybe(31)->match( static fn($percentage) => $percentage, static fn() => throw new \Exception('Should be valid'), From d673c15357c145a144c519b5690f808dc9667d9e Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Thu, 8 May 2025 13:57:36 +0200 Subject: [PATCH 13/23] Server::loadAverage() now returns an Attempt as the load average may be invalid --- CHANGELOG.md | 2 + src/Exception/LoadAverageCannotBeNegative.php | 8 ---- src/Facade/LoadAverage/PhpFacade.php | 13 +++++-- src/Server.php | 6 ++- src/Server/LoadAverage.php | 26 ++++++------- src/Servers/Linux.php | 3 +- src/Servers/Logger.php | 18 ++++----- src/Servers/OSX.php | 3 +- tests/Facade/LoadAverage/PhpFacadeTest.php | 2 +- tests/Server/LoadAverageTest.php | 38 ++++++++++--------- tests/Servers/LinuxTest.php | 2 +- tests/Servers/LoggerTest.php | 2 +- tests/Servers/OSXTest.php | 2 +- 13 files changed, 66 insertions(+), 59 deletions(-) delete mode 100644 src/Exception/LoadAverageCannotBeNegative.php diff --git a/CHANGELOG.md b/CHANGELOG.md index a6ff4af..90496f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Requires `innmind/immutable:~5.14` - `Innmind\Server\Status\Server::cpu()` now returns `Innmind\Immutable\Attempt` - `Innmind\Server\Status\Server::memory()` now returns `Innmind\Immutable\Attempt` +- `Innmind\Server\Status\Server::loadAverage()` now returns `Innmind\Immutable\Attempt` ### Removed @@ -16,6 +17,7 @@ - `Innmind\Server\Status\Exception\OutOfBoundsPercentage` - `Innmind\Server\Status\Exception\LowestPidPossibleIsOne` - `Innmind\Server\Status\Exception\EmptyUserNotAllowed` +- `Innmind\Server\Status\Exception\LoadAverageCannotBeNegative` ## 4.1.1 - 2024-09-30 diff --git a/src/Exception/LoadAverageCannotBeNegative.php b/src/Exception/LoadAverageCannotBeNegative.php deleted file mode 100644 index a389481..0000000 --- a/src/Exception/LoadAverageCannotBeNegative.php +++ /dev/null @@ -1,8 +0,0 @@ - + */ + public function __invoke(): Attempt { - /** @var array{0: int, 1: int, 2: int} */ + /** @var array{0: float, 1: float, 2: float} */ $load = \sys_getloadavg(); - return new LoadAverage($load[0], $load[1], $load[2]); + return LoadAverage::maybe($load[0], $load[1], $load[2])->match( + Attempt::result(...), + static fn() => Attempt::error(new \RuntimeException('Failed to load load average')), + ); } } diff --git a/src/Server.php b/src/Server.php index 4be95f3..8357826 100644 --- a/src/Server.php +++ b/src/Server.php @@ -25,7 +25,11 @@ public function cpu(): Attempt; */ public function memory(): Attempt; public function processes(): Processes; - public function loadAverage(): LoadAverage; + + /** + * @return Attempt + */ + public function loadAverage(): Attempt; public function disk(): Disk; public function tmp(): Path; } diff --git a/src/Server/LoadAverage.php b/src/Server/LoadAverage.php index e819c66..b398204 100644 --- a/src/Server/LoadAverage.php +++ b/src/Server/LoadAverage.php @@ -3,34 +3,34 @@ namespace Innmind\Server\Status\Server; -use Innmind\Server\Status\Exception\LoadAverageCannotBeNegative; +use Innmind\Immutable\Maybe; /** * @psalm-immutable */ final class LoadAverage { - private float $lastMinute; - private float $lastFiveMinutes; - private float $lastFifteenMinutes; + private function __construct( + private float $lastMinute, + private float $lastFiveMinutes, + private float $lastFifteenMinutes, + ) { + } /** - * @throws LoadAverageCannotBeNegative + * @return Maybe */ - public function __construct( + public static function maybe( float $lastMinute, float $lastFiveMinutes, float $lastFifteenMinutes, - ) { + ): Maybe { if ($lastMinute < 0 || $lastFiveMinutes < 0 || $lastFifteenMinutes < 0) { - throw new LoadAverageCannotBeNegative( - (string) \min($lastMinute, $lastFiveMinutes, $lastFifteenMinutes), - ); + /** @var Maybe */ + return Maybe::nothing(); } - $this->lastMinute = $lastMinute; - $this->lastFiveMinutes = $lastFiveMinutes; - $this->lastFifteenMinutes = $lastFifteenMinutes; + return Maybe::just(new self($lastMinute, $lastFiveMinutes, $lastFifteenMinutes)); } public function lastMinute(): float diff --git a/src/Servers/Linux.php b/src/Servers/Linux.php index a39a0e4..110754f 100644 --- a/src/Servers/Linux.php +++ b/src/Servers/Linux.php @@ -6,7 +6,6 @@ use Innmind\Server\Status\{ Server, Server\Processes, - Server\LoadAverage, Facade\Cpu\LinuxFacade as CpuFacade, Facade\Memory\LinuxFacade as MemoryFacade, Facade\LoadAverage\PhpFacade as LoadAverageFacade, @@ -55,7 +54,7 @@ public function processes(): Processes } #[\Override] - public function loadAverage(): LoadAverage + public function loadAverage(): Attempt { return ($this->loadAverage)(); } diff --git a/src/Servers/Logger.php b/src/Servers/Logger.php index 14ea4ed..d74c917 100644 --- a/src/Servers/Logger.php +++ b/src/Servers/Logger.php @@ -6,7 +6,6 @@ use Innmind\Server\Status\{ Server, Server\Processes, - Server\LoadAverage, Server\Disk, }; use Innmind\Url\Path; @@ -66,16 +65,17 @@ public function processes(): Processes } #[\Override] - public function loadAverage(): LoadAverage + public function loadAverage(): Attempt { - $loadAverage = $this->server->loadAverage(); - $this->logger->debug('Load average: {one} {five} {fifteen}', [ - 'one' => $loadAverage->lastMinute(), - 'five' => $loadAverage->lastFiveMinutes(), - 'fifteen' => $loadAverage->lastFifteenMinutes(), - ]); + return $this->server->loadAverage()->map(function($loadAverage) { + $this->logger->debug('Load average: {one} {five} {fifteen}', [ + 'one' => $loadAverage->lastMinute(), + 'five' => $loadAverage->lastFiveMinutes(), + 'fifteen' => $loadAverage->lastFifteenMinutes(), + ]); - return $loadAverage; + return $loadAverage; + }); } #[\Override] diff --git a/src/Servers/OSX.php b/src/Servers/OSX.php index edf3901..a50b2ab 100644 --- a/src/Servers/OSX.php +++ b/src/Servers/OSX.php @@ -6,7 +6,6 @@ use Innmind\Server\Status\{ Server, Server\Processes, - Server\LoadAverage, Facade\Cpu\OSXFacade as CpuFacade, Facade\Memory\OSXFacade as MemoryFacade, Facade\LoadAverage\PhpFacade as LoadAverageFacade, @@ -56,7 +55,7 @@ public function processes(): Processes } #[\Override] - public function loadAverage(): LoadAverage + public function loadAverage(): Attempt { return ($this->loadAverage)(); } diff --git a/tests/Facade/LoadAverage/PhpFacadeTest.php b/tests/Facade/LoadAverage/PhpFacadeTest.php index cd47abb..7c293ee 100644 --- a/tests/Facade/LoadAverage/PhpFacadeTest.php +++ b/tests/Facade/LoadAverage/PhpFacadeTest.php @@ -15,6 +15,6 @@ public function testInterface() { $facade = new PhpFacade; - $this->assertInstanceOf(LoadAverage::class, $facade()); + $this->assertInstanceOf(LoadAverage::class, $facade()->unwrap()); } } diff --git a/tests/Server/LoadAverageTest.php b/tests/Server/LoadAverageTest.php index def5262..16e4879 100644 --- a/tests/Server/LoadAverageTest.php +++ b/tests/Server/LoadAverageTest.php @@ -3,41 +3,45 @@ namespace Tests\Innmind\Server\Status\Server; -use Innmind\Server\Status\{ - Server\LoadAverage, - Exception\LoadAverageCannotBeNegative, -}; +use Innmind\Server\Status\Server\LoadAverage; use Innmind\BlackBox\PHPUnit\Framework\TestCase; class LoadAverageTest extends TestCase { public function testInterface() { - $load = new LoadAverage(1, 5, 15); + $load = LoadAverage::maybe(1, 5, 15)->match( + static fn($load) => $load, + static fn() => null, + ); + $this->assertNotNull($load); $this->assertSame(1.0, $load->lastMinute()); $this->assertSame(5.0, $load->lastFiveMinutes()); $this->assertSame(15.0, $load->lastFifteenMinutes()); } - public function testThrowWhenNegativeLastMinuteLoad() + public function testReturnNothingWhenNegativeLastMinuteLoad() { - $this->expectException(LoadAverageCannotBeNegative::class); - - new LoadAverage(-1, 5, 15); + $this->assertNull(LoadAverage::maybe(-1, 5, 15)->match( + static fn($load) => $load, + static fn() => null, + )); } - public function testThrowWhenNegativeLastFiveMinuteLoad() + public function testReturnNothingWhenNegativeLastFiveMinuteLoad() { - $this->expectException(LoadAverageCannotBeNegative::class); - - new LoadAverage(1, -5, 15); + $this->assertNull(LoadAverage::maybe(1, -5, 15)->match( + static fn($load) => $load, + static fn() => null, + )); } - public function testThrowWhenNegativeLastFifteenMinuteLoad() + public function testReturnNothingWhenNegativeLastFifteenMinuteLoad() { - $this->expectException(LoadAverageCannotBeNegative::class); - - new LoadAverage(1, 5, -15); + $this->assertNull(LoadAverage::maybe(1, 5, -15)->match( + static fn($load) => $load, + static fn() => null, + )); } } diff --git a/tests/Servers/LinuxTest.php b/tests/Servers/LinuxTest.php index 3cc8fdb..646b13e 100644 --- a/tests/Servers/LinuxTest.php +++ b/tests/Servers/LinuxTest.php @@ -87,7 +87,7 @@ public function testProcesses() public function testLoadAverage() { - $this->assertInstanceOf(LoadAverage::class, $this->server->loadAverage()); + $this->assertInstanceOf(LoadAverage::class, $this->server->loadAverage()->unwrap()); } public function testDisk() diff --git a/tests/Servers/LoggerTest.php b/tests/Servers/LoggerTest.php index f62b0ba..8a3b3b9 100644 --- a/tests/Servers/LoggerTest.php +++ b/tests/Servers/LoggerTest.php @@ -66,7 +66,7 @@ public function testLoadAverage() { $server = new Logger($this->server(), new NullLogger); - $this->assertInstanceOf(LoadAverage::class, $server->loadAverage()); + $this->assertInstanceOf(LoadAverage::class, $server->loadAverage()->unwrap()); } public function testDisk() diff --git a/tests/Servers/OSXTest.php b/tests/Servers/OSXTest.php index 1285102..23d18a2 100644 --- a/tests/Servers/OSXTest.php +++ b/tests/Servers/OSXTest.php @@ -86,7 +86,7 @@ public function testProcesses() public function testLoadAverage() { - $this->assertInstanceOf(LoadAverage::class, $this->server->loadAverage()); + $this->assertInstanceOf(LoadAverage::class, $this->server->loadAverage()->unwrap()); } public function testDisk() From 996a84ccbbc80424778bf179f93bd59897ea48a7 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Thu, 8 May 2025 13:59:52 +0200 Subject: [PATCH 14/23] make Memory constructor private --- src/Facade/Memory/LinuxFacade.php | 8 +------- src/Facade/Memory/OSXFacade.php | 8 +------- src/Server/Memory.php | 23 +++++++++++------------ tests/Server/MemoryTest.php | 2 +- 4 files changed, 14 insertions(+), 27 deletions(-) diff --git a/src/Facade/Memory/LinuxFacade.php b/src/Facade/Memory/LinuxFacade.php index de76daf..3b214d7 100644 --- a/src/Facade/Memory/LinuxFacade.php +++ b/src/Facade/Memory/LinuxFacade.php @@ -109,13 +109,7 @@ private function parse(Str $output): Attempt ->map(Bytes::of(...)); return Maybe::all($total, $active, $free, $swap, $used) - ->map(static fn(Bytes $total, Bytes $active, Bytes $free, Bytes $swap, Bytes $used) => new Memory( - $total, - $active, - $free, - $swap, - $used, - )) + ->map(Memory::of(...)) ->match( Attempt::result(...), static fn() => Attempt::error(new \RuntimeException('Failed to parse memory usage')), diff --git a/src/Facade/Memory/OSXFacade.php b/src/Facade/Memory/OSXFacade.php index df09bd0..a094fab 100644 --- a/src/Facade/Memory/OSXFacade.php +++ b/src/Facade/Memory/OSXFacade.php @@ -95,13 +95,7 @@ public function __invoke(): Attempt ->map(Bytes::of(...)); return Maybe::all($total, $active, $unused, $swap, $used) - ->map(static fn(Bytes $total, Bytes $active, Bytes $unused, Bytes $swap, Bytes $used) => new Memory( - $total, - $active, - $unused, - $swap, - $used, - )) + ->map(Memory::of(...)) ->match( Attempt::result(...), static fn() => Attempt::error(new \RuntimeException('Failed to parse memory usage')), diff --git a/src/Server/Memory.php b/src/Server/Memory.php index 27918c1..7564942 100644 --- a/src/Server/Memory.php +++ b/src/Server/Memory.php @@ -10,24 +10,23 @@ */ final class Memory { - private Bytes $total; - private Bytes $active; - private Bytes $free; - private Bytes $swap; - private Bytes $used; + private function __construct( + private Bytes $total, + private Bytes $active, + private Bytes $free, + private Bytes $swap, + private Bytes $used, + ) { + } - public function __construct( + public static function of( Bytes $total, Bytes $active, Bytes $free, Bytes $swap, Bytes $used, - ) { - $this->total = $total; - $this->active = $active; - $this->free = $free; - $this->swap = $swap; - $this->used = $used; + ): self { + return new self($total, $active, $free, $swap, $used); } public function total(): Bytes diff --git a/tests/Server/MemoryTest.php b/tests/Server/MemoryTest.php index 2ff286b..d7a1df9 100644 --- a/tests/Server/MemoryTest.php +++ b/tests/Server/MemoryTest.php @@ -13,7 +13,7 @@ class MemoryTest extends TestCase { public function testInterface() { - $memory = new Memory( + $memory = Memory::of( $total = Bytes::of(42), $active = Bytes::of(42), $free = Bytes::of(42), From 54455c794d44579551f864b643a0778c506e3531 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Thu, 8 May 2025 14:00:33 +0200 Subject: [PATCH 15/23] remove unused exception --- CHANGELOG.md | 1 + src/Exception/DomainException.php | 8 -------- 2 files changed, 1 insertion(+), 8 deletions(-) delete mode 100644 src/Exception/DomainException.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 90496f4..807f169 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - `Innmind\Server\Status\Exception\LowestPidPossibleIsOne` - `Innmind\Server\Status\Exception\EmptyUserNotAllowed` - `Innmind\Server\Status\Exception\LoadAverageCannotBeNegative` +- `Innmind\Server\Status\Exception\DomainException` ## 4.1.1 - 2024-09-30 diff --git a/src/Exception/DomainException.php b/src/Exception/DomainException.php deleted file mode 100644 index 760deff..0000000 --- a/src/Exception/DomainException.php +++ /dev/null @@ -1,8 +0,0 @@ - Date: Thu, 8 May 2025 14:02:31 +0200 Subject: [PATCH 16/23] make Process constructor private --- src/Server/Process.php | 30 +++++++++++++------------- src/Server/Processes/UnixProcesses.php | 2 +- tests/Server/ProcessTest.php | 2 +- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/Server/Process.php b/src/Server/Process.php index 0906086..8275c33 100644 --- a/src/Server/Process.php +++ b/src/Server/Process.php @@ -18,31 +18,31 @@ */ final class Process { - private Pid $pid; - private User $user; - private Percentage $cpu; - private Memory $memory; - /** @var Maybe */ - private Maybe $start; - private Command $command; + /** + * @param Maybe $start + */ + private function __construct( + private Pid $pid, + private User $user, + private Percentage $cpu, + private Memory $memory, + private Maybe $start, + private Command $command, + ) { + } /** * @param Maybe $start */ - public function __construct( + public static function of( Pid $pid, User $user, Percentage $cpu, Memory $memory, Maybe $start, Command $command, - ) { - $this->pid = $pid; - $this->user = $user; - $this->cpu = $cpu; - $this->memory = $memory; - $this->start = $start; - $this->command = $command; + ): self { + return new self($pid, $user, $cpu, $memory, $start, $command); } public function pid(): Pid diff --git a/src/Server/Processes/UnixProcesses.php b/src/Server/Processes/UnixProcesses.php index b286b21..8027daf 100644 --- a/src/Server/Processes/UnixProcesses.php +++ b/src/Server/Processes/UnixProcesses.php @@ -134,7 +134,7 @@ private function parse(Str $output): Set ->map(Command::of(...)); return Maybe::all($user, $pid, $percentage, $memory, $command) - ->map(fn(User $user, Pid $pid, Percentage $percentage, Memory $memory, Command $command) => new Process( + ->map(fn(User $user, Pid $pid, Percentage $percentage, Memory $memory, Command $command) => Process::of( $pid, $user, $percentage, diff --git a/tests/Server/ProcessTest.php b/tests/Server/ProcessTest.php index 5fc4325..3da229c 100644 --- a/tests/Server/ProcessTest.php +++ b/tests/Server/ProcessTest.php @@ -27,7 +27,7 @@ public function testInterface() $this ->forAll(PointInTime::any()) ->then(function($pointInTime) { - $process = new Process( + $process = Process::of( $pid = Pid::of(1), $user = User::of('root'), $cpu = Percentage::maybe(42)->match( From 064e941b98ca30fc940599939d704492ecde110c Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Thu, 8 May 2025 14:06:35 +0200 Subject: [PATCH 17/23] flag constructors as pure --- src/Server/Cpu.php | 3 +++ src/Server/Cpu/Cores.php | 2 ++ src/Server/Cpu/Percentage.php | 2 ++ src/Server/Disk/Volume.php | 6 ++++++ src/Server/Disk/Volume/MountPoint.php | 5 +++++ src/Server/Disk/Volume/Usage.php | 5 +++++ src/Server/LoadAverage.php | 2 ++ src/Server/Memory.php | 3 +++ src/Server/Memory/Bytes.php | 10 ++++++++++ src/Server/Process.php | 2 ++ src/Server/Process/Command.php | 2 ++ src/Server/Process/Memory.php | 2 ++ src/Server/Process/Pid.php | 2 ++ src/Server/Process/User.php | 2 ++ 14 files changed, 48 insertions(+) diff --git a/src/Server/Cpu.php b/src/Server/Cpu.php index 4f4f8fb..ce6c02e 100644 --- a/src/Server/Cpu.php +++ b/src/Server/Cpu.php @@ -21,6 +21,9 @@ private function __construct( ) { } + /** + * @psalm-pure + */ public static function of( Percentage $user, Percentage $system, diff --git a/src/Server/Cpu/Cores.php b/src/Server/Cpu/Cores.php index 50e3d50..0fde59a 100644 --- a/src/Server/Cpu/Cores.php +++ b/src/Server/Cpu/Cores.php @@ -17,6 +17,8 @@ private function __construct( } /** + * @psalm-pure + * * @param int<1, max> $value */ public static function of(int $value): self diff --git a/src/Server/Cpu/Percentage.php b/src/Server/Cpu/Percentage.php index 3153d30..17c787c 100644 --- a/src/Server/Cpu/Percentage.php +++ b/src/Server/Cpu/Percentage.php @@ -16,6 +16,8 @@ private function __construct( } /** + * @psalm-pure + * * @return Maybe */ public static function maybe(float $value): Maybe diff --git a/src/Server/Disk/Volume.php b/src/Server/Disk/Volume.php index aa261ec..5fe52b9 100644 --- a/src/Server/Disk/Volume.php +++ b/src/Server/Disk/Volume.php @@ -9,6 +9,9 @@ Memory\Bytes, }; +/** + * @psalm-immutable + */ final class Volume { private function __construct( @@ -20,6 +23,9 @@ private function __construct( ) { } + /** + * @psalm-pure + */ public static function of( MountPoint $mountPoint, Bytes $size, diff --git a/src/Server/Disk/Volume/MountPoint.php b/src/Server/Disk/Volume/MountPoint.php index 0317382..62ab78b 100644 --- a/src/Server/Disk/Volume/MountPoint.php +++ b/src/Server/Disk/Volume/MountPoint.php @@ -3,6 +3,9 @@ namespace Innmind\Server\Status\Server\Disk\Volume; +/** + * @psalm-immutable + */ final class MountPoint { /** @@ -14,6 +17,8 @@ private function __construct( } /** + * @psalm-pure + * * @param non-empty-string $value */ public static function of(string $value): self diff --git a/src/Server/Disk/Volume/Usage.php b/src/Server/Disk/Volume/Usage.php index 11f4f18..f922f9d 100644 --- a/src/Server/Disk/Volume/Usage.php +++ b/src/Server/Disk/Volume/Usage.php @@ -5,6 +5,9 @@ use Innmind\Immutable\Maybe; +/** + * @psalm-immutable + */ final class Usage { private function __construct( @@ -13,6 +16,8 @@ private function __construct( } /** + * @psalm-pure + * * @return Maybe */ public static function maybe(float $value): Maybe diff --git a/src/Server/LoadAverage.php b/src/Server/LoadAverage.php index b398204..81fd144 100644 --- a/src/Server/LoadAverage.php +++ b/src/Server/LoadAverage.php @@ -18,6 +18,8 @@ private function __construct( } /** + * @psalm-pure + * * @return Maybe */ public static function maybe( diff --git a/src/Server/Memory.php b/src/Server/Memory.php index 7564942..87af0a2 100644 --- a/src/Server/Memory.php +++ b/src/Server/Memory.php @@ -19,6 +19,9 @@ private function __construct( ) { } + /** + * @psalm-pure + */ public static function of( Bytes $total, Bytes $active, diff --git a/src/Server/Memory/Bytes.php b/src/Server/Memory/Bytes.php index 5a6aa98..1b1ed5c 100644 --- a/src/Server/Memory/Bytes.php +++ b/src/Server/Memory/Bytes.php @@ -30,6 +30,8 @@ private function __construct( } /** + * @psalm-pure + * * @param int<0, max> $value */ public static function of(int $value): self @@ -73,6 +75,8 @@ public function toString(): string } /** + * @psalm-pure + * * @return Maybe */ public static function maybe(string $bytes): Maybe @@ -96,6 +100,8 @@ public static function maybe(string $bytes): Maybe } /** + * @psalm-pure + * * @return Maybe */ private static function attemptDarwin(Str $bytes): Maybe @@ -107,6 +113,8 @@ private static function attemptDarwin(Str $bytes): Maybe } /** + * @psalm-pure + * * @return Maybe */ private static function attemptLinux(Str $bytes): Maybe @@ -118,6 +126,8 @@ private static function attemptLinux(Str $bytes): Maybe } /** + * @psalm-pure + * * @return Maybe */ private static function fromUnit(Str $bytes, Str $unit): Maybe diff --git a/src/Server/Process.php b/src/Server/Process.php index 8275c33..43f3ebd 100644 --- a/src/Server/Process.php +++ b/src/Server/Process.php @@ -32,6 +32,8 @@ private function __construct( } /** + * @psalm-pure + * * @param Maybe $start */ public static function of( diff --git a/src/Server/Process/Command.php b/src/Server/Process/Command.php index a0aeb13..8c0eb09 100644 --- a/src/Server/Process/Command.php +++ b/src/Server/Process/Command.php @@ -22,6 +22,8 @@ private function __construct( } /** + * @psalm-pure + * * @param non-empty-string $value */ public static function of(string $value): self diff --git a/src/Server/Process/Memory.php b/src/Server/Process/Memory.php index e6fe411..a4c79f0 100644 --- a/src/Server/Process/Memory.php +++ b/src/Server/Process/Memory.php @@ -16,6 +16,8 @@ private function __construct( } /** + * @psalm-pure + * * @return Maybe */ public static function maybe(float $value): Maybe diff --git a/src/Server/Process/Pid.php b/src/Server/Process/Pid.php index af4297a..228c1cb 100644 --- a/src/Server/Process/Pid.php +++ b/src/Server/Process/Pid.php @@ -17,6 +17,8 @@ private function __construct( } /** + * @psalm-pure + * * @param int<1, max> $value */ public static function of(int $value): self diff --git a/src/Server/Process/User.php b/src/Server/Process/User.php index ae3f486..2b3a7e1 100644 --- a/src/Server/Process/User.php +++ b/src/Server/Process/User.php @@ -17,6 +17,8 @@ private function __construct( } /** + * @psalm-pure + * * @param non-empty-string $value */ public static function of(string $value): self From cf0e1286a9fec2ffe44143cb51322dc8b7da44b7 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Thu, 8 May 2025 14:06:45 +0200 Subject: [PATCH 18/23] add missing return types --- src/Server/Disk/Volume/MountPoint.php | 3 +++ src/Server/Process/Pid.php | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/Server/Disk/Volume/MountPoint.php b/src/Server/Disk/Volume/MountPoint.php index 62ab78b..85e8eb5 100644 --- a/src/Server/Disk/Volume/MountPoint.php +++ b/src/Server/Disk/Volume/MountPoint.php @@ -36,6 +36,9 @@ public function is(string $point): bool return $this->value === $point; } + /** + * @return non-empty-string + */ public function toString(): string { return $this->value; diff --git a/src/Server/Process/Pid.php b/src/Server/Process/Pid.php index 228c1cb..990d64c 100644 --- a/src/Server/Process/Pid.php +++ b/src/Server/Process/Pid.php @@ -36,6 +36,9 @@ public function is(int $value): bool return $this->value === $value; } + /** + * @return int<1, max> + */ public function toInt(): int { return $this->value; From b22b1d5f3dc884d3fb00024d68f9bd3abda7970b Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Thu, 8 May 2025 14:11:30 +0200 Subject: [PATCH 19/23] use unwrap to show the error --- tests/Facade/Memory/LinuxFacadeTest.php | 5 +---- tests/Servers/LinuxTest.php | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/tests/Facade/Memory/LinuxFacadeTest.php b/tests/Facade/Memory/LinuxFacadeTest.php index 25147e4..deefca5 100644 --- a/tests/Facade/Memory/LinuxFacadeTest.php +++ b/tests/Facade/Memory/LinuxFacadeTest.php @@ -36,10 +36,7 @@ public function testInterface() $facade = new LinuxFacade($this->server->processes()); - $this->assertInstanceOf(Memory::class, $facade()->match( - static fn($memory) => $memory, - static fn() => null, - )); + $this->assertInstanceOf(Memory::class, $facade()->unwrap()); } public function testReturnNohingWhenInfoNotAccessible() diff --git a/tests/Servers/LinuxTest.php b/tests/Servers/LinuxTest.php index 646b13e..c9dec45 100644 --- a/tests/Servers/LinuxTest.php +++ b/tests/Servers/LinuxTest.php @@ -73,10 +73,7 @@ public function testMemory() $this ->server ->memory() - ->match( - static fn($memory) => $memory, - static fn() => null, - ), + ->unwrap(), ); } From 21d162a1071233409be9bf0306d617f630699567 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Thu, 8 May 2025 14:14:40 +0200 Subject: [PATCH 20/23] avoid re-parsing bytes --- src/Facade/Memory/LinuxFacade.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Facade/Memory/LinuxFacade.php b/src/Facade/Memory/LinuxFacade.php index 3b214d7..3022105 100644 --- a/src/Facade/Memory/LinuxFacade.php +++ b/src/Facade/Memory/LinuxFacade.php @@ -84,8 +84,14 @@ private function parse(Str $output): Attempt $elements->get('key'), $elements ->get('value') - ->map(static fn($value) => $value.'K') - ->flatMap(Bytes::maybe(...)), + ->map(static fn($value) => (int) $value) + ->keep( + Is::int() + ->positive() + ->or(Is::value(0)) + ->asPredicate(), + ) + ->map(Bytes::of(...)), ) ->map(static fn(string $key, Bytes $value) => [$key, $value]) ->toSequence(), From a28ee2df0ffde29222ac201f404cb6cbd71034a1 Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Thu, 8 May 2025 14:16:17 +0200 Subject: [PATCH 21/23] debug --- src/Facade/Memory/LinuxFacade.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Facade/Memory/LinuxFacade.php b/src/Facade/Memory/LinuxFacade.php index 3022105..a284c80 100644 --- a/src/Facade/Memory/LinuxFacade.php +++ b/src/Facade/Memory/LinuxFacade.php @@ -96,6 +96,7 @@ private function parse(Str $output): Attempt ->map(static fn(string $key, Bytes $value) => [$key, $value]) ->toSequence(), ); + dump($amounts); $amounts = Map::of(...$amounts->toList()) ->map(static fn($_, $value) => Bytes::of( $value->toInt() * 1024, // 1024 represents a kilobyte From 9a317e0faac779a63be41f4dac4da08e46a8ba2b Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Thu, 8 May 2025 14:19:07 +0200 Subject: [PATCH 22/23] fix map key --- src/Facade/Memory/LinuxFacade.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Facade/Memory/LinuxFacade.php b/src/Facade/Memory/LinuxFacade.php index a284c80..ccd119a 100644 --- a/src/Facade/Memory/LinuxFacade.php +++ b/src/Facade/Memory/LinuxFacade.php @@ -93,7 +93,10 @@ private function parse(Str $output): Attempt ) ->map(Bytes::of(...)), ) - ->map(static fn(string $key, Bytes $value) => [$key, $value]) + ->map(static fn(string $key, Bytes $value) => [ + self::$entries[$key], + $value, + ]) ->toSequence(), ); dump($amounts); From d0f09ed072a5d74453a8e5b60b6314b8219aca7f Mon Sep 17 00:00:00 2001 From: Baptiste Langlade Date: Thu, 8 May 2025 14:19:12 +0200 Subject: [PATCH 23/23] Revert "debug" This reverts commit a28ee2df0ffde29222ac201f404cb6cbd71034a1. --- src/Facade/Memory/LinuxFacade.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Facade/Memory/LinuxFacade.php b/src/Facade/Memory/LinuxFacade.php index ccd119a..3a8c7a4 100644 --- a/src/Facade/Memory/LinuxFacade.php +++ b/src/Facade/Memory/LinuxFacade.php @@ -99,7 +99,6 @@ private function parse(Str $output): Attempt ]) ->toSequence(), ); - dump($amounts); $amounts = Map::of(...$amounts->toList()) ->map(static fn($_, $value) => Bytes::of( $value->toInt() * 1024, // 1024 represents a kilobyte