1<?php 2 3declare(strict_types=1); 4 5/* 6 * This file is part of the league/commonmark package. 7 * 8 * (c) Colin O'Dell <colinodell@gmail.com> 9 * 10 * For the full copyright and license information, please view the LICENSE 11 * file that was distributed with this source code. 12 */ 13 14namespace League\CommonMark\Node; 15 16use League\CommonMark\Node\Query\AndExpr; 17use League\CommonMark\Node\Query\OrExpr; 18 19final class Query 20{ 21 /** @var callable(Node): bool $condition */ 22 private $condition; 23 24 public function __construct() 25 { 26 $this->condition = new AndExpr(); 27 } 28 29 public function where(callable ...$conditions): self 30 { 31 return $this->andWhere(...$conditions); 32 } 33 34 public function andWhere(callable ...$conditions): self 35 { 36 if ($this->condition instanceof AndExpr) { 37 foreach ($conditions as $condition) { 38 $this->condition->add($condition); 39 } 40 } else { 41 $this->condition = new AndExpr($this->condition, ...$conditions); 42 } 43 44 return $this; 45 } 46 47 public function orWhere(callable ...$conditions): self 48 { 49 if ($this->condition instanceof OrExpr) { 50 foreach ($conditions as $condition) { 51 $this->condition->add($condition); 52 } 53 } else { 54 $this->condition = new OrExpr($this->condition, ...$conditions); 55 } 56 57 return $this; 58 } 59 60 public function findOne(Node $node): ?Node 61 { 62 foreach ($node->iterator() as $n) { 63 if (\call_user_func($this->condition, $n)) { 64 return $n; 65 } 66 } 67 68 return null; 69 } 70 71 /** 72 * @return iterable<Node> 73 */ 74 public function findAll(Node $node, ?int $limit = PHP_INT_MAX): iterable 75 { 76 $resultCount = 0; 77 78 foreach ($node->iterator() as $n) { 79 if ($resultCount >= $limit) { 80 break; 81 } 82 83 if (! \call_user_func($this->condition, $n)) { 84 continue; 85 } 86 87 ++$resultCount; 88 89 yield $n; 90 } 91 } 92 93 /** 94 * @return callable(Node): bool 95 */ 96 public static function type(string $class): callable 97 { 98 return static fn (Node $node): bool => $node instanceof $class; 99 } 100 101 /** 102 * @psalm-param ?callable(Node): bool $condition 103 * 104 * @return callable(Node): bool 105 */ 106 public static function hasChild(?callable $condition = null): callable 107 { 108 return static function (Node $node) use ($condition): bool { 109 foreach ($node->children() as $child) { 110 if ($condition === null || $condition($child)) { 111 return true; 112 } 113 } 114 115 return false; 116 }; 117 } 118 119 /** 120 * @psalm-param ?callable(Node): bool $condition 121 * 122 * @return callable(Node): bool 123 */ 124 public static function hasParent(?callable $condition = null): callable 125 { 126 return static function (Node $node) use ($condition): bool { 127 $parent = $node->parent(); 128 if ($parent === null) { 129 return false; 130 } 131 132 if ($condition === null) { 133 return true; 134 } 135 136 return $condition($parent); 137 }; 138 } 139} 140