Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding entries to api #8

Merged
merged 1 commit into from
Oct 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,18 @@ $rule = $matcher->match($relative_path);

## TODO

- [ ] Add comments to the structure
- [x] Add comments to the structure
- [ ] Complete phpdocs
- [ ] Add error checking on `match` to normalize or reject impossible patterns (i.e. "" and "/...")
- [ ] Explain how `AutomataMatcher` works in this file.
- [x] Should I support whitespace escaping in patterns?
- [x] Add ability to turn `Owners` into string.
- [ ] Test owners entries.
- [ ] Test entries to string.
- [ ] Test pattern escaping.
- [ ] Test no-owners rules
- [x] Remove ext-json from requires.
- [x] Add ext-ctype to requires.

## Author(s)

Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
],
"require": {
"php": ">=7.4",
"ext-json": "*",
"ext-ctype": "*",
"symfony/polyfill-php80": "^1.31"
},
"require-dev": {
Expand Down
18 changes: 7 additions & 11 deletions src/AutomataMatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,21 @@ private function __construct(
}

/**
* @param Rule[] $rules
* @param iterable<Rule> $rules
* @return self
*/
public static function build(array $rules): self
public static function build(iterable $rules): self
{
$start = new State();
$as_array = [];
foreach ($rules as $index => $rule) {
$start->addTokens(
self::parsePattern($rule->getPattern()),
$index
);
$as_array[] = $rule;
}
return new self($rules, $start);
return new self($as_array, $start);
}

/**
Expand Down Expand Up @@ -122,16 +124,10 @@ public function match(string $path): ?Rule
: null;
}

/**
* @return array{priority: int, edges: array<string, State>, "**": bool}
*/
public function asJson(): array
{
return $this->start->jsonSerialize();
}

/**
* @return array{nodes: array<string, int>, edges: array{from: string, to: string, label: string}[]}
*
* @internal
*/
public function getDebugInfo(): array
{
Expand Down
17 changes: 3 additions & 14 deletions src/AutomataMatcher/State.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
namespace Kellegous\CodeOwners\AutomataMatcher;

use InvalidArgumentException;
use JsonSerializable;

final class State implements JsonSerializable
final class State
{
/**
* @var int
Expand Down Expand Up @@ -94,22 +93,12 @@ public function match(array $path): int
: $priority;
}

/**
* @return array{priority: int, edges: array<string, State>, "**": bool}
*/
public function jsonSerialize(): array
{
return [
'priority' => $this->priority,
'edges' => $this->edges,
'**' => $this->isRecursive,
];
}

/**
* @param array<string, int> $nodes
* @param array{from:int, to: int, label:string}[] $edges
* @return void
*
* @internal
*/
public function getDebugInfo(
array &$nodes,
Expand Down
34 changes: 34 additions & 0 deletions src/Blank.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace Kellegous\CodeOwners;

final class Blank implements Entry
{
private SourceInfo $sourceInfo;

public function __construct(
SourceInfo $sourceInfo
) {
$this->sourceInfo = $sourceInfo;
}

/**
* @Override
* @return SourceInfo
*/
public function getSourceInfo(): SourceInfo
{
return $this->sourceInfo;
}

/**
* @Override
* @return string
*/
public function toString(): string
{
return '';
}
}
38 changes: 38 additions & 0 deletions src/Comment.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace Kellegous\CodeOwners;

final class Comment implements Entry
{
private string $text;

private SourceInfo $sourceInfo;

public function __construct(
string $text,
SourceInfo $sourceInfo
) {
$this->text = $text;
$this->sourceInfo = $sourceInfo;
}

/**
* @Override
* @return SourceInfo
*/
public function getSourceInfo(): SourceInfo
{
return $this->sourceInfo;
}

/**
* @Override
* @return string
*/
public function toString(): string
{
return $this->text;
}
}
27 changes: 27 additions & 0 deletions src/Entry.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace Kellegous\CodeOwners;

/**
* Entry represents a line in a CODEOWNERS file. An entry can take several forms. It can be a rule, a comment, or it
* can be empty. Treating a CDOEOWNERS file as a collection of entries can be useful if you are mutating the file and
* with to preserve comments or spacial groupings.
*/
interface Entry
{
/**
* Returns the source information for this entry.
*
* @return SourceInfo
*/
public function getSourceInfo(): SourceInfo;

/**
* Returns the string representation of this entry.
*
* @return string
*/
public function toString(): string;
}
83 changes: 56 additions & 27 deletions src/Owners.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@
final class Owners
{
/**
* @var Rule[]
* @var Entry[]
*/
private array $rules;
private array $entries;

/**
* @param Rule[] $rules
* @param Entry[] $entries
*/
private function __construct(
array $rules
array $entries
) {
$this->rules = $rules;
$this->entries = $entries;
}

/**
Expand All @@ -29,46 +29,63 @@ private function __construct(
*/
public static function fromFile(string $filename): self
{
$rules = iterator_to_array(
self::rulesFrom(
$entries = iterator_to_array(
self::entriesFrom(
self::readLinesFrom($filename),
$filename
)
);

return new self($rules);
return new self($entries);
}

/**
* @param iterable<int, string> $iter
* @param string|null $filename
* @return Iterator<Rule>
* @return Iterator<Entry>
* @throws ParseException
*/
private static function rulesFrom(
private static function entriesFrom(
iterable $iter,
?string $filename
): Iterator {
foreach ($iter as $index => $line) {
$line = self::stripComment($line);
if ($line === '') {
continue;
}

yield Rule::parse(
yield self::parseEntry(
$line,
new SourceInfo($index + 1, $filename)
);
}
}

private static function stripComment(string $line): string
{
/**
* @param string $line
* @param SourceInfo $sourceInfo
* @return Entry
* @throws ParseException
*/
private static function parseEntry(
string $line,
SourceInfo $sourceInfo
): Entry {
$comment_start = strpos($line, '#');
if ($comment_start === false) {
return $line;
$content = trim($line);
$comment = '';
} else {
$content = trim(substr($line, 0, $comment_start));
$comment = trim(substr($line, $comment_start + 1));
}

if ($content === '' && $comment === '') {
return new Blank($sourceInfo);
} elseif ($content === '' && $comment !== '') {
return new Comment($content, $sourceInfo);
}
return trim(substr($line, 0, $comment_start));

return Rule::parse(
$content,
$sourceInfo,
$comment === '' ? null : $comment
);
}

/**
Expand Down Expand Up @@ -101,21 +118,33 @@ public static function fromString(
string $content,
?string $filename = null
): self {
$rules = iterator_to_array(
self::rulesFrom(
$entries = iterator_to_array(
self::entriesFrom(
explode(PHP_EOL, $content),
$filename
)
);

return new self($rules);
return new self($entries);
}

/**
* @return iterable<Rule>
*/
public function getRules(): iterable
{
foreach ($this->entries as $entry) {
if ($entry instanceof Rule) {
yield $entry;
}
}
}

/**
* @return Rule[]
* @return iterable<Entry>
*/
public function getRules(): array
public function getEntries(): iterable
{
return $this->rules;
return $this->entries;
}
}
Loading
Loading