44
55use TH \DocTest \Example ;
66use TH \DocTest \Location ;
7+ use TH \Maybe \Option ;
78
89final class AllExamples implements Examples
910{
1011 /**
11- * @param list< string>|null $acceptedLanguages Use empty string for unspecified language, and null for any languages
12+ * @param Option<array< string>> $languageFilter Use empty string for unspecified language
1213 */
1314 public function __construct (
1415 private readonly Comments $ comments ,
15- private readonly ? array $ acceptedLanguages ,
16+ private readonly Option $ languageFilter ,
1617 ) {}
1718
1819 /**
@@ -27,15 +28,15 @@ public function getIterator(): \Traversable
2728
2829 /**
2930 * @param array<string> $paths paths to files and folder to look for PHP comments code examples in
30- * @param list< string>|null $acceptedLanguages Use empty string for unspecified language, and null for any languages
31+ * @param Option<array< string>> $languageFilter Use empty string for unspecified language
3132 */
3233 public static function fromPaths (
3334 array $ paths ,
34- ? array $ acceptedLanguages ,
35+ Option $ languageFilter ,
3536 ): self {
3637 return new self (
3738 SourceComments::fromPaths ($ paths ),
38- $ acceptedLanguages ,
39+ $ languageFilter ,
3940 );
4041 }
4142
@@ -49,62 +50,66 @@ private function iterateComment(
4950 $ lines = new \ArrayIterator (\explode (PHP_EOL , $ comment ));
5051 $ index = 1 ;
5152
52- while ($ example = $ this ->nextExample ($ lines , $ location , $ index ++)) {
53- yield $ example ;
54- }
53+ do {
54+ $ example = $ this ->nextExample ($ lines , $ location , $ index ++);
55+
56+ if ($ example ->isNone ()) {
57+ return ;
58+ }
59+
60+ yield $ example ->unwrap ();
61+ } while (true );
5562 }
5663
5764 /**
5865 * @param \ArrayIterator<int,string> $lines
66+ * @return Option<Example>
5967 */
6068 private function nextExample (
6169 \ArrayIterator $ lines ,
6270 Location $ location ,
6371 int $ index ,
64- ): ?Example {
65- $ codeblockStartedAt = $ this ->findFencedPHPCodeBlockStart ($ lines );
66-
67- if ($ codeblockStartedAt === null ) {
68- return null ;
69- }
70-
71- return $ this ->readExample (
72- $ lines ,
73- $ location ->startingAt ($ codeblockStartedAt , $ index ),
74- );
72+ ): Option {
73+ return $ this ->findFencedPHPCodeBlockStart ($ lines )
74+ ->andThen (fn ($ codeblockStartedAt ) => $ this ->readExample (
75+ $ lines ,
76+ $ location ->startingAt ($ codeblockStartedAt , $ index ),
77+ ));
7578 }
7679
7780 /**
7881 * @param \ArrayIterator<int,string> $lines
82+ * @return Option<Example>
7983 */
8084 private function readExample (
8185 \ArrayIterator $ lines ,
8286 Location $ location ,
83- ): ? Example {
87+ ): Option {
8488 $ buffer = [];
8589
8690 while ($ lines ->valid ()) {
8791 $ line = $ lines ->current ();
8892 $ lines ->next ();
8993
9094 if ($ this ->endOfAFencedCodeBlock ($ line )) {
91- return new Example (
95+ return Option \some ( new Example (
9296 \implode (PHP_EOL , $ buffer ),
9397 $ location ->ofLength ($ lines ->key ()),
94- );
98+ )) ;
9599 }
96100
97101 $ buffer [] = \preg_replace ("/^\s*\*( ?)/ " , "" , $ line );
98102 }
99103
100- return null ;
104+ return Option \none () ;
101105 }
102106
103107 /**
104108 * @param \ArrayIterator<int,string> $lines
105109 * phpcs:disable SlevomatCodingStandard.Complexity.Cognitive.ComplexityTooHigh
110+ * @return Option<int>
106111 */
107- private function findFencedPHPCodeBlockStart (\ArrayIterator $ lines ): ? int
112+ private function findFencedPHPCodeBlockStart (\ArrayIterator $ lines ): Option
108113 {
109114 $ insideAFencedCodeBlock = false ;
110115
@@ -119,43 +124,45 @@ private function findFencedPHPCodeBlockStart(\ArrayIterator $lines): ?int
119124 } else {
120125 $ lang = $ this ->startOfAFencedCodeBlock ($ line );
121126
122- if ($ lang === false ) {
123- continue ;
127+ if ($ lang-> mapOr ( $ this -> isAcceptedLanguage (...), default: false ) ) {
128+ return Option \some ( $ lines -> key ()) ;
124129 }
125130
126- if ($ this -> isAcceptedLanguage ( $ lang )) {
127- return $ lines -> key () ;
131+ if ($ lang -> isNone ( )) {
132+ continue ;
128133 }
129134
130135 $ insideAFencedCodeBlock = true ;
131136 }
132137 }
133138
134- return null ;
139+ return Option \none () ;
135140 }
136141
137142 private function isAcceptedLanguage (string $ lang ): bool
138143 {
139- if ($ this ->acceptedLanguages === null ) {
140- return true ;
141- }
142-
143- return \in_array (needle: $ lang , haystack: $ this ->acceptedLanguages , strict: true );
144+ return $ this ->languageFilter ->mapOr (
145+ callback: static fn (array $ languages ) => \in_array (needle: $ lang , haystack: $ languages , strict: true ),
146+ default: true ,
147+ );
144148 }
145149
146150 private function endOfAFencedCodeBlock (string $ line ): bool
147151 {
148152 return \ltrim ($ line ) === "* ``` " ;
149153 }
150154
151- private function startOfAFencedCodeBlock (string $ line ): false |string
155+ /**
156+ * @return Option<string>
157+ */
158+ private function startOfAFencedCodeBlock (string $ line ): Option
152159 {
153160 $ line = \trim ($ line );
154161
155162 if (!\str_starts_with ($ line , "* ``` " )) {
156- return false ;
163+ return Option \none () ;
157164 }
158165
159- return \ substr ($ line , 5 );
166+ return Option \some ( \ substr ($ line , 5 ) );
160167 }
161168}
0 commit comments