Skip to content

Commit

Permalink
Added phpdocs
Browse files Browse the repository at this point in the history
  • Loading branch information
kellegous committed Oct 28, 2024
1 parent d31ea18 commit ec5d0d0
Show file tree
Hide file tree
Showing 14 changed files with 212 additions and 2 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ $rule = $matcher->match($relative_path);
## TODO

- [x] Add comments to the structure
- [ ] Complete phpdocs
- [x] 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?
Expand All @@ -52,6 +52,7 @@ $rule = $matcher->match($relative_path);
- [x] Remove ext-json from requires.
- [x] Add ext-ctype to requires.
- [x] Convert json tests to PHP arrays.
- [ ] Add a command line tool for dot-renderer

## Author(s)

Expand Down
26 changes: 26 additions & 0 deletions src/AutomataMatcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,23 @@
use Kellegous\CodeOwners\AutomataMatcher\State;
use Kellegous\CodeOwners\AutomataMatcher\Token;

/**
* A RuleMatcher that combines all the patterns into a single automata.
*/
final class AutomataMatcher implements RuleMatcher
{
/**
* This is the start state for the automata.
*
* @var State
*/
private State $start;

/**
* The full collection of rules from the Owners instance. The NFA keeps the index of
* rules instead of a reference to the rules themselves since the index provides a straight-forward
* way to honor the last-match-wins rule (the largest of the matching indexes wins).
*
* @var Rule[]
*/
private array $rules;
Expand All @@ -27,6 +39,8 @@ private function __construct(
}

/**
* Builds a new AutomataMatcher from the given rules.
*
* @param iterable<Rule> $rules
* @return self
*/
Expand Down Expand Up @@ -76,6 +90,14 @@ private static function parsePattern(Pattern $pattern): array
return $tokens;
}

/**
* Strangely, a pattern is absolute not only if it starts with a slash,
* but also if it contains a wildcard.
*
* @param string $pattern
*
* @return bool
*/
private static function isAbsolute(string $pattern): bool
{
$ix = strpos($pattern, '/');
Expand Down Expand Up @@ -132,6 +154,8 @@ private static function parseToken(
}

/**
* @inerhitDoc
* @Override
* @param string $path
* @return Rule|null
*/
Expand All @@ -145,6 +169,8 @@ public function match(string $path): ?Rule
}

/**
* Used to return an internal representation of the automata for debugging purposes.
*
* @return array{nodes: array<string, int>, edges: array{from: string, to: string, label: string}[]}
*
* @internal
Expand Down
7 changes: 7 additions & 0 deletions src/AutomataMatcher/DotRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,16 @@

use Kellegous\CodeOwners\AutomataMatcher;

/**
* Intended for debugging, this class will emit a graphviz dot file to visualize
* the state machine used to match patterns in an AutomataMatcher.
*/
final class DotRenderer
{
/**
* Generate a graphviz dot file from the given AutomataMatcher. This can be
* rendered using the dot command.
*
* @param AutomataMatcher $matcher
* @return string
*/
Expand Down
24 changes: 24 additions & 0 deletions src/AutomataMatcher/State.php
Original file line number Diff line number Diff line change
@@ -1,27 +1,44 @@
<?php

declare(strict_types=1);

namespace Kellegous\CodeOwners\AutomataMatcher;

use InvalidArgumentException;

/**
* Represents a state in the NFA.
* @Internal
*/
final class State
{
/**
* The index of the rule. We call this priority because we choose the highest
* priority state (the last rule) to choose the best match.
*
* @var int
*/
private int $priority = -1;

/**
* The key is a regex pattern for the segment and the value is the next state.
*
* @var array<string, State>
*/
private array $edges = [];

/**
* A recursive state is a special ** state which is one that has a self-referencing
* epsilon state.
*
* @var bool
*/
private bool $isRecursive;

/**
* Create a new state. If the state is a terminating ** state, then it will
* be considered recursive.
*
* @param bool $isRecursive
*/
public function __construct(bool $isRecursive = false)
Expand All @@ -30,6 +47,8 @@ public function __construct(bool $isRecursive = false)
}

/**
* Add the states associated with the tokens of a parsed pattern.
*
* @param Token[] $tokens
* @param int $priority
* @return void
Expand Down Expand Up @@ -65,6 +84,9 @@ public function addTokens(
}

/**
* Find the highest priority match for the given path. Note that a return value
* of -1 indicates that no match was found.
*
* @param string[] $path
* @return int
*/
Expand Down Expand Up @@ -94,6 +116,8 @@ public function match(array $path): int
}

/**
* Used to return an internal representation of the automata for debugging purposes.
*
* @param array<string, int> $nodes
* @param array{from:int, to: int, label:string}[] $edges
* @return void
Expand Down
24 changes: 24 additions & 0 deletions src/AutomataMatcher/Token.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,30 @@

namespace Kellegous\CodeOwners\AutomataMatcher;

/**
* Represents a "segment" of a pattern. Each pattern consists of a number of
* tokens separated by "/" characters. This class holds the raw string
* representation of that segment as well as the regular expression that matches
* that segment.
*
* @internal
*/
final class Token
{
/**
* @var string
*/
private string $pattern;

/**
* @var string
*/
private string $regex;

/**
* @param string $pattern
* @param string $regex
*/
public function __construct(
string $pattern,
string $regex
Expand All @@ -18,11 +36,17 @@ public function __construct(
$this->regex = $regex;
}

/**
* @return string
*/
public function getPattern(): string
{
return $this->pattern;
}

/**
* @return string
*/
public function getRegex(): string
{
return $this->regex;
Expand Down
3 changes: 3 additions & 0 deletions src/Blank.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

namespace Kellegous\CodeOwners;

/**
* Represents a blank line in the code owners file.
*/
final class Blank implements Entry
{
private SourceInfo $sourceInfo;
Expand Down
16 changes: 16 additions & 0 deletions src/Comment.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,26 @@

namespace Kellegous\CodeOwners;

/**
* Represents a comment in the code owners file.
*/
final class Comment implements Entry
{
/**
* The text of the comment including the leading `#`.
* @var string
*/
private string $text;

/**
* @var SourceInfo
*/
private SourceInfo $sourceInfo;

/**
* @param string $text
* @param SourceInfo $sourceInfo
*/
public function __construct(
string $text,
SourceInfo $sourceInfo
Expand All @@ -19,6 +33,7 @@ public function __construct(
}

/**
* @inheritDoc
* @Override
* @return SourceInfo
*/
Expand All @@ -28,6 +43,7 @@ public function getSourceInfo(): SourceInfo
}

/**
* #inheritDoc
* @Override
* @return string
*/
Expand Down
12 changes: 12 additions & 0 deletions src/Owners.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

use Iterator;

/**
* Represents the contents of a code owners file.
*/
final class Owners
{
/**
Expand All @@ -23,6 +26,8 @@ private function __construct(
}

/**
* Parse the contents of a code owners file.
*
* @param string $filename
* @return self
* @throws ParseException
Expand Down Expand Up @@ -109,6 +114,8 @@ private static function readLinesFrom(string $filename): iterable
}

/**
* Parse the contents of a code owners file as a string.
*
* @param string $content
* @param string|null $filename
* @return self
Expand All @@ -129,6 +136,9 @@ public static function fromString(
}

/**
* Get only the rules that are present in the code owners structure. This omits
* blank lines and comments.
*
* @return iterable<Rule>
*/
public function getRules(): iterable
Expand All @@ -141,6 +151,8 @@ public function getRules(): iterable
}

/**
* Get all entries from the code owners file.
*
* @return iterable<Entry>
*/
public function getEntries(): iterable
Expand Down
3 changes: 3 additions & 0 deletions src/ParseException.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

use Exception;

/**
* Thrown when the contents of an owners file cannot be parsed.
*/
final class ParseException extends Exception
{
}
26 changes: 26 additions & 0 deletions src/Pattern.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,31 @@

use Closure;

/**
* Represents a file pattern part of a rule in a code owners file.
*/
final class Pattern
{
/**
* @var string
*/
private string $pattern;

/**
* @param string $pattern
*/
private function __construct(string $pattern)
{
$this->pattern = $pattern;
}

/**
* Parse the pattern from a string to a Pattern instance.
*
* @param string $pattern
* @return self
* @throws ParseException
*/
public static function parse(string $pattern): self
{
if (strpos($pattern, '***') !== false || $pattern === '') {
Expand All @@ -26,12 +39,19 @@ public static function parse(string $pattern): self
return new self($pattern);
}

/**
* Get the string representation of the pattern.
*
* @return string
*/
public function toString(): string
{
return $this->pattern;
}

/**
* Get a matcher function for the pattern.
*
* @return Closure(string):bool
*/
public function getMatcher(): Closure
Expand All @@ -46,6 +66,12 @@ public function getMatcher(): Closure
};
}

/**
* Create a regular expression from a pattern.
*
* @param string $pattern
* @return string
*/
private static function toRegexp(string $pattern): string
{
if ($pattern === '/') {
Expand Down
Loading

0 comments on commit ec5d0d0

Please sign in to comment.