diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6c33240..b770e76 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -105,4 +105,4 @@ jobs: run: "./tools/composer update --no-ansi --no-interaction --no-progress" - name: "Run mutation tests with Infection" - run: "./tools/infection" + run: "./tools/infection --only-covered" diff --git a/.psalm/baseline.xml b/.psalm/baseline.xml index 2a811d0..68fc820 100644 --- a/.psalm/baseline.xml +++ b/.psalm/baseline.xml @@ -1,2 +1,8 @@ - + + + + $line + + + diff --git a/src/Parser.php b/src/Parser.php index c19a41a..235fea0 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -9,10 +9,10 @@ */ namespace SebastianBergmann\CsvParser; -use function array_shift; -use function file; -use function str_getcsv; +use function is_array; use Generator; +use RuntimeException; +use SplFileObject; /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for this library @@ -27,31 +27,39 @@ final class Parser */ public function parse(string $filename, Schema $schema, bool $ignoreFirstLine = true): Generator { - $lines = @file($filename); - - if ($lines === false) { - throw CannotReadCsvFileException::from($filename); + try { + $file = new SplFileObject($filename); + } catch (RuntimeException $e) { + throw new CannotReadCsvFileException($e->getMessage()); } - if ($ignoreFirstLine) { - array_shift($lines); - } + $file->setFlags(SplFileObject::READ_CSV | SplFileObject::SKIP_EMPTY | SplFileObject::DROP_NEW_LINE); - return $this->generator($lines, $schema); + return $this->generator($file, $schema, $ignoreFirstLine); } /** - * @psalm-param list $lines - * * @psalm-return Generator> */ - private function generator(array $lines, Schema $schema): Generator + private function generator(SplFileObject $file, Schema $schema, bool $ignoreFirstLine): Generator { - foreach ($lines as $line) { + $firstLine = true; + + foreach ($file as $line) { + if ($ignoreFirstLine && $firstLine) { + $firstLine = false; + + continue; + } + + if (!is_array($line)) { + continue; + } + $data = []; foreach ($schema->columnDefinitions() as $columnDefinition) { - $columnDefinition->parse(str_getcsv($line), $data); + $columnDefinition->parse($line, $data); } yield $data; diff --git a/src/exception/CannotReadCsvFileException.php b/src/exception/CannotReadCsvFileException.php index c2e9074..d7975ad 100644 --- a/src/exception/CannotReadCsvFileException.php +++ b/src/exception/CannotReadCsvFileException.php @@ -9,18 +9,8 @@ */ namespace SebastianBergmann\CsvParser; -use function sprintf; use RuntimeException; final class CannotReadCsvFileException extends RuntimeException implements Exception { - public static function from(string $filename): self - { - return new self( - sprintf( - 'Reading from CSV file %s failed', - $filename - ) - ); - } } diff --git a/src/exception/InvalidValueException.php b/src/exception/InvalidValueException.php deleted file mode 100644 index 7f261c0..0000000 --- a/src/exception/InvalidValueException.php +++ /dev/null @@ -1,16 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ -namespace SebastianBergmann\CsvParser; - -use RuntimeException; - -final class InvalidValueException extends RuntimeException implements Exception -{ -} diff --git a/src/schema/ColumnDefinition.php b/src/schema/ColumnDefinition.php index e1397d9..466e496 100644 --- a/src/schema/ColumnDefinition.php +++ b/src/schema/ColumnDefinition.php @@ -50,7 +50,7 @@ private function __construct(int $position, string $name, Type $type) } /** - * @psalm-param list $input + * @psalm-param list $input * @psalm-param array $output * * @throws OutOfBoundsException @@ -65,10 +65,6 @@ public function parse(array $input, array &$output): void $value = $input[$this->position - 1]; - if ($value === null) { - throw new InvalidValueException('Input array has element with invalid value at position ' . $this->position); - } - $output[$this->name] = $this->type->cast($value); } diff --git a/tests/unit/ParserTest.php b/tests/unit/ParserTest.php index e863a3b..0fb44f1 100644 --- a/tests/unit/ParserTest.php +++ b/tests/unit/ParserTest.php @@ -17,7 +17,6 @@ use PHPUnit\Framework\TestCase; #[CoversClass(Parser::class)] -#[CoversClass(CannotReadCsvFileException::class)] #[UsesClass(Schema::class)] #[UsesClass(ColumnDefinition::class)] #[UsesClass(Type::class)] @@ -88,7 +87,6 @@ public function test_Parses_CSV_file_according_to_schema(array $expected, Schema public function test_Cannot_read_from_CSV_file_that_does_not_exist(): void { $this->expectException(CannotReadCsvFileException::class); - $this->expectExceptionMessage('Reading from CSV file does_not_exist.csv failed'); (new Parser)->parse('does_not_exist.csv', Schema::from([])); } diff --git a/tests/unit/schema/ColumnDefinitionTest.php b/tests/unit/schema/ColumnDefinitionTest.php index 5c071c9..d627cf7 100644 --- a/tests/unit/schema/ColumnDefinitionTest.php +++ b/tests/unit/schema/ColumnDefinitionTest.php @@ -56,17 +56,6 @@ public function testCannotParseColumnFromInputArrayThatDoesNotExist(): void $this->column()->parse($input, $output); } - public function testCannotParseColumnWithInvalidValue(): void - { - $input = [null]; - $output = []; - - $this->expectException(InvalidValueException::class); - $this->expectExceptionMessage('Input array has element with invalid value at position 1'); - - $this->column()->parse($input, $output); - } - private function column(): ColumnDefinition { return ColumnDefinition::from(1, 'name', Type::integer());