From c614cbd09f4706e5fb164321fc68449d654337ef Mon Sep 17 00:00:00 2001 From: Rodolfo Berrios <20590102+rodber@users.noreply.github.com> Date: Sat, 2 Dec 2023 09:26:33 -0300 Subject: [PATCH] try --- .vscode/templates.code-snippets | 16 ++--- README.md | 26 ++++---- composer.json | 11 ++-- sonar-project.properties | 2 +- src/.git-keep | 0 src/Interfaces/WriterInterface.php | 29 +++++++++ src/Interfaces/WritersInterface.php | 65 +++++++++++++++++++ src/NullWriter.php | 34 ++++++++++ src/StreamWriter.php | 55 ++++++++++++++++ src/Traits/WriterTrait.php | 38 +++++++++++ src/Writers.php | 99 +++++++++++++++++++++++++++++ src/WritersInstance.php | 39 ++++++++++++ src/functions.php | 65 +++++++++++++++++++ tests/.git-keep | 0 tests/NullWriterTest.php | 30 +++++++++ tests/StreamWriterTest.php | 39 ++++++++++++ tests/WritersInstanceTest.php | 35 ++++++++++ tests/WritersTest.php | 57 +++++++++++++++++ 18 files changed, 614 insertions(+), 26 deletions(-) delete mode 100644 src/.git-keep create mode 100644 src/Interfaces/WriterInterface.php create mode 100644 src/Interfaces/WritersInterface.php create mode 100644 src/NullWriter.php create mode 100644 src/StreamWriter.php create mode 100644 src/Traits/WriterTrait.php create mode 100644 src/Writers.php create mode 100644 src/WritersInstance.php create mode 100644 src/functions.php delete mode 100644 tests/.git-keep create mode 100644 tests/NullWriterTest.php create mode 100644 tests/StreamWriterTest.php create mode 100644 tests/WritersInstanceTest.php create mode 100644 tests/WritersTest.php diff --git a/.vscode/templates.code-snippets b/.vscode/templates.code-snippets index b9607d8..2deba45 100644 --- a/.vscode/templates.code-snippets +++ b/.vscode/templates.code-snippets @@ -5,7 +5,7 @@ "body": [ " 🔔 Subscribe to the [newsletter](https://chv.to/chevere-newsletter) to don't miss any update regarding Chevere. ![Chevere](chevere.svg) -[![Build](https://img.shields.io/github/actions/workflow/status/chevere/reponame/test.yml?branch=%branch%&style=flat-square)](https://github.com/chevere/reponame/actions) -![Code size](https://img.shields.io/github/languages/code-size/chevere/reponame?style=flat-square) -[![Apache-2.0](https://img.shields.io/github/license/chevere/reponame?style=flat-square)](LICENSE) +[![Build](https://img.shields.io/github/actions/workflow/status/chevere/writer/test.yml?branch=1.0&style=flat-square)](https://github.com/chevere/writer/actions) +![Code size](https://img.shields.io/github/languages/code-size/chevere/writer?style=flat-square) +[![Apache-2.0](https://img.shields.io/github/license/chevere/writer?style=flat-square)](LICENSE) [![PHPStan](https://img.shields.io/badge/PHPStan-level%209-blueviolet?style=flat-square)](https://phpstan.org/) -[![Mutation testing badge](https://img.shields.io/endpoint?style=flat-square&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fchevere%2Freponame%2F%branch%)](https://dashboard.stryker-mutator.io/reports/github.com/chevere/reponame/%branch%) - -[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=chevere_reponame&metric=alert_status)](https://sonarcloud.io/dashboard?id=chevere_reponame) -[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=chevere_reponame&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=chevere_reponame) -[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=chevere_reponame&metric=reliability_rating)](https://sonarcloud.io/dashboard?id=chevere_reponame) -[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=chevere_reponame&metric=security_rating)](https://sonarcloud.io/dashboard?id=chevere_reponame) -[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=chevere_reponame&metric=coverage)](https://sonarcloud.io/dashboard?id=chevere_reponame) -[![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=chevere_reponame&metric=sqale_index)](https://sonarcloud.io/dashboard?id=chevere_reponame) -[![CodeFactor](https://www.codefactor.io/repository/github/chevere/reponame/badge)](https://www.codefactor.io/repository/github/chevere/reponame) +[![Mutation testing badge](https://img.shields.io/endpoint?style=flat-square&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fchevere%2Fwriter%2F1.0)](https://dashboard.stryker-mutator.io/reports/github.com/chevere/writer/1.0) + +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=chevere_writer&metric=alert_status)](https://sonarcloud.io/dashboard?id=chevere_writer) +[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=chevere_writer&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=chevere_writer) +[![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=chevere_writer&metric=reliability_rating)](https://sonarcloud.io/dashboard?id=chevere_writer) +[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=chevere_writer&metric=security_rating)](https://sonarcloud.io/dashboard?id=chevere_writer) +[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=chevere_writer&metric=coverage)](https://sonarcloud.io/dashboard?id=chevere_writer) +[![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=chevere_writer&metric=sqale_index)](https://sonarcloud.io/dashboard?id=chevere_writer) +[![CodeFactor](https://www.codefactor.io/repository/github/chevere/writer/badge)](https://www.codefactor.io/repository/github/chevere/writer) ## Documentation diff --git a/composer.json b/composer.json index 492f9a9..85903db 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { - "name": "chevere/reponame", - "description": "A chevere reponame package", + "name": "chevere/writer", + "description": "A chevere writer package", "homepage": "https://chevere.org", "type": "library", "license": "Apache-2.0", @@ -12,7 +12,10 @@ } ], "require": { - "chevere/chevere": "^3.0" + "php": "^8.1", + "chevere/message": "^0.1.0", + "nyholm/psr7": "^1.5", + "thecodingmachine/safe": "^2.5" }, "require-dev": { "phpstan/phpstan": "^1.9", @@ -24,7 +27,7 @@ "src/functions.php" ], "psr-4": { - "Chevere\\%namespace%\\": "src/" + "Chevere\\Writer\\": "src/" } }, "autoload-dev": { diff --git a/sonar-project.properties b/sonar-project.properties index 4c6eae7..36c0529 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,4 +1,4 @@ -sonar.projectKey=chevere_reponame +sonar.projectKey=chevere_writer sonar.organization=chevere sonar.host.url=https://sonarcloud.io sonar.sourceEncoding=UTF-8 diff --git a/src/.git-keep b/src/.git-keep deleted file mode 100644 index e69de29..0000000 diff --git a/src/Interfaces/WriterInterface.php b/src/Interfaces/WriterInterface.php new file mode 100644 index 0000000..d0232cc --- /dev/null +++ b/src/Interfaces/WriterInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Chevere\Writer\Interfaces; + +use Stringable; + +interface WriterInterface extends Stringable +{ + /** + * Returns the contents written. Must not alter the file cursor. + */ + public function __toString(): string; + + /** + * Writes the given string. + */ + public function write(string $string): void; +} diff --git a/src/Interfaces/WritersInterface.php b/src/Interfaces/WritersInterface.php new file mode 100644 index 0000000..f7c6904 --- /dev/null +++ b/src/Interfaces/WritersInterface.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Chevere\Writer\Interfaces; + +interface WritersInterface +{ + /** + * Return an instance with the specified $writer for all writers. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified $writer for all writers. + */ + public function with(WriterInterface $writer): self; + + /** + * Return an instance with the specified output WriterInterface. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified output WriterInterface. + */ + public function withOutput(WriterInterface $writer): self; + + public function output(): WriterInterface; + + /** + * Return an instance with the specified error WriterInterface. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified error WriterInterface. + */ + public function withError(WriterInterface $writer): self; + + public function error(): WriterInterface; + + /** + * Return an instance with the specified debug WriterInterface. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified debug WriterInterface. + */ + public function withDebug(WriterInterface $writer): self; + + public function debug(): WriterInterface; + + /** + * Return an instance with the specified log WriterInterface. + * + * This method MUST retain the state of the current instance, and return + * an instance that contains the specified log WriterInterface. + */ + public function withLog(WriterInterface $writer): self; + + public function log(): WriterInterface; +} diff --git a/src/NullWriter.php b/src/NullWriter.php new file mode 100644 index 0000000..973144a --- /dev/null +++ b/src/NullWriter.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Chevere\Writer; + +use Chevere\Writer\Interfaces\WriterInterface; + +final class NullWriter implements WriterInterface +{ + public function __construct() + { + // null + } + + public function __toString(): string + { + return ''; + } + + public function write(string $string): void + { + // null + } +} diff --git a/src/StreamWriter.php b/src/StreamWriter.php new file mode 100644 index 0000000..39f96b0 --- /dev/null +++ b/src/StreamWriter.php @@ -0,0 +1,55 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Chevere\Writer; + +use Chevere\Writer\Interfaces\WriterInterface; +use InvalidArgumentException; +use Psr\Http\Message\StreamInterface; +use RuntimeException; +use Throwable; +use function Chevere\Message\message; + +/** + * @codeCoverageIgnore + * @infection-ignore-all + */ +final class StreamWriter implements WriterInterface +{ + public function __construct( + private StreamInterface $stream + ) { + if (! $this->stream->isWritable()) { + throw new InvalidArgumentException( + (string) message('Stream provided is not writable') + ); + } + } + + public function __toString(): string + { + return $this->stream->__toString(); + } + + public function write(string $string): void + { + try { + $this->stream->write($string); + } catch (Throwable $e) { + throw new RuntimeException( + previous: $e, + message: (string) message('Unable to write provided string') + ); + } + } +} diff --git a/src/Traits/WriterTrait.php b/src/Traits/WriterTrait.php new file mode 100644 index 0000000..bddc9d7 --- /dev/null +++ b/src/Traits/WriterTrait.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Chevere\Writer\Traits; + +use Chevere\Writer\Interfaces\WriterInterface; + +/** + * @codeCoverageIgnore + * @infection-ignore-all + */ +trait WriterTrait +{ + private WriterInterface $writer; + + public function withWriter(WriterInterface $writer): static + { + $new = clone $this; + $new->writer = $writer; + + return $new; + } + + public function writer(): WriterInterface + { + return $this->writer; + } +} diff --git a/src/Writers.php b/src/Writers.php new file mode 100644 index 0000000..439520c --- /dev/null +++ b/src/Writers.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Chevere\Writer; + +use Chevere\Writer\Interfaces\WriterInterface; +use Chevere\Writer\Interfaces\WritersInterface; + +final class Writers implements WritersInterface +{ + private WriterInterface $output; + + private WriterInterface $error; + + private WriterInterface $debug; + + private WriterInterface $log; + + public function __construct() + { + $this->output = new StreamWriter(streamTemp('')); + $this->error = new StreamWriter(streamTemp('')); + $this->debug = new NullWriter(); + $this->log = new NullWriter(); + } + + public function with(WriterInterface $writer): WritersInterface + { + $new = clone $this; + $new->output = $writer; + $new->error = $writer; + $new->debug = $writer; + $new->log = $writer; + + return $new; + } + + public function withOutput(WriterInterface $writer): WritersInterface + { + $new = clone $this; + $new->output = $writer; + + return $new; + } + + public function withError(WriterInterface $writer): WritersInterface + { + $new = clone $this; + $new->error = $writer; + + return $new; + } + + public function withDebug(WriterInterface $writer): WritersInterface + { + $new = clone $this; + $new->debug = $writer; + + return $new; + } + + public function withLog(WriterInterface $writer): WritersInterface + { + $new = clone $this; + $new->log = $writer; + + return $new; + } + + public function output(): WriterInterface + { + return $this->output; + } + + public function error(): WriterInterface + { + return $this->error; + } + + public function debug(): WriterInterface + { + return $this->debug; + } + + public function log(): WriterInterface + { + return $this->log; + } +} diff --git a/src/WritersInstance.php b/src/WritersInstance.php new file mode 100644 index 0000000..b35fd9d --- /dev/null +++ b/src/WritersInstance.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Chevere\Writer; + +use Chevere\Writer\Interfaces\WritersInterface; +use LogicException; +use function Chevere\Message\message; + +final class WritersInstance +{ + private static ?WritersInterface $instance; + + public function __construct(WritersInterface $writers) + { + self::$instance = $writers; + } + + public static function get(): WritersInterface + { + if (! isset(self::$instance)) { + throw new LogicException( + (string) message('No writers instance present') + ); + } + + return self::$instance; + } +} diff --git a/src/functions.php b/src/functions.php new file mode 100644 index 0000000..4ae724e --- /dev/null +++ b/src/functions.php @@ -0,0 +1,65 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Chevere\Writer; + +use Chevere\Writer\Interfaces\WritersInterface; +use InvalidArgumentException; +use Nyholm\Psr7\Stream; +use Psr\Http\Message\StreamInterface; +use Throwable; +use function Chevere\Message\message; +use function Safe\fopen; + +/** + * @codeCoverageIgnore + */ +function writers(): WritersInterface +{ + return WritersInstance::get(); +} + +/** + * @codeCoverageIgnore + * + * @throws InvalidArgumentException + */ +function streamFor(string $uri, string $mode): StreamInterface +{ + try { + return Stream::create(fopen($uri, $mode)); + } catch (Throwable $e) { + throw new InvalidArgumentException( + previous: $e, + message: (string) message( + 'Unable to create stream for `%uri%`', + uri: $uri + ) + ); + } +} + +/** + * @codeCoverageIgnore + */ +function streamTemp(string $content = ''): StreamInterface +{ + try { + return Stream::create($content); + } catch (Throwable $e) { + throw new InvalidArgumentException( + previous: $e, + message: (string) message('Unable to create temp stream') + ); + } +} diff --git a/tests/.git-keep b/tests/.git-keep deleted file mode 100644 index e69de29..0000000 diff --git a/tests/NullWriterTest.php b/tests/NullWriterTest.php new file mode 100644 index 0000000..bea5460 --- /dev/null +++ b/tests/NullWriterTest.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Chevere\Tests; + +use Chevere\Writer\NullWriter; +use PHPUnit\Framework\TestCase; + +final class NullWriterTest extends TestCase +{ + public function testConstruct(): void + { + $letters = ['Q', 'W', 'E', 'R', 'T', 'Y']; + $writer = new NullWriter(); + foreach ($letters as $letter) { + $writer->write($letter); + } + $this->assertSame('', $writer->__toString()); + } +} diff --git a/tests/StreamWriterTest.php b/tests/StreamWriterTest.php new file mode 100644 index 0000000..186c502 --- /dev/null +++ b/tests/StreamWriterTest.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Chevere\Tests; + +use Chevere\Writer\StreamWriter; +use PHPUnit\Framework\TestCase; +use function Chevere\Writer\streamFor; +use function Chevere\Writer\streamTemp; + +final class StreamWriterTest extends TestCase +{ + public function testInvalidArgument(): void + { + $this->expectNotToPerformAssertions(); + $stream = streamFor('php://output', 'r'); + new StreamWriter($stream); + } + + public function testWrite(): void + { + $letters = ['Q', 'W', 'E', 'R', 'T', 'Y']; + $writer = new StreamWriter(streamTemp('')); + foreach ($letters as $letter) { + $writer->write($letter); + } + $this->assertSame(implode('', $letters), $writer->__toString()); + } +} diff --git a/tests/WritersInstanceTest.php b/tests/WritersInstanceTest.php new file mode 100644 index 0000000..4cd40ac --- /dev/null +++ b/tests/WritersInstanceTest.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Chevere\Tests; + +use Chevere\Writer\Writers; +use Chevere\Writer\WritersInstance; +use LogicException; +use PHPUnit\Framework\TestCase; + +final class WritersInstanceTest extends TestCase +{ + public function testNoConstruct(): void + { + $this->expectException(LogicException::class); + WritersInstance::get(); + } + + public function testConstruct(): void + { + $writers = new Writers(); + $instance = new WritersInstance($writers); + $this->assertSame($writers, $instance::get()); + } +} diff --git a/tests/WritersTest.php b/tests/WritersTest.php new file mode 100644 index 0000000..e583fd3 --- /dev/null +++ b/tests/WritersTest.php @@ -0,0 +1,57 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace Chevere\Tests; + +use Chevere\Writer\Interfaces\WriterInterface; +use Chevere\Writer\StreamWriter; +use Chevere\Writer\Writers; +use PHPUnit\Framework\TestCase; +use function Chevere\Writer\streamTemp; + +final class WritersTest extends TestCase +{ + public function testConstruct() + { + $writers = new Writers(); + foreach (['output', 'error', 'debug', 'log'] as $fnName) { + $this->assertInstanceOf( + WriterInterface::class, + $writers->{$fnName}() + ); + } + } + + public function testWith(): void + { + $writer = new StreamWriter(streamTemp('')); + $writers = new Writers(); + $writersWith = $writers->with($writer); + $this->assertNotSame($writers, $writersWith); + foreach (['output', 'error', 'debug', 'log'] as $name) { + $this->assertSame($writer, $writersWith->{$name}()); + } + } + + public function testWithX(): void + { + foreach (['output', 'error', 'debug', 'log'] as $name) { + $writer = new StreamWriter(streamTemp('')); + $withFn = 'with' . ucfirst($name); + $writers = new Writers(); + $writersWithX = $writers->{$withFn}($writer); + $this->assertNotSame($writers, $writersWithX); + $this->assertSame($writer, $writersWithX->{$name}()); + } + } +}