Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
98.57% |
69 / 70 |
|
80.00% |
4 / 5 |
CRAP | |
0.00% |
0 / 1 |
| EntitySchemaPatcher | |
98.57% |
69 / 70 |
|
80.00% |
4 / 5 |
22 | |
0.00% |
0 / 1 |
| patchSchema | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 | |||
| patchFingerprint | |
100.00% |
16 / 16 |
|
100.00% |
1 / 1 |
1 | |||
| patchTermlist | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
| patchTerm | |
94.74% |
18 / 19 |
|
0.00% |
0 / 1 |
10.01 | |||
| patchString | |
100.00% |
24 / 24 |
|
100.00% |
1 / 1 |
7 | |||
| 1 | <?php |
| 2 | |
| 3 | declare( strict_types = 1 ); |
| 4 | |
| 5 | namespace EntitySchema\Services\Diff; |
| 6 | |
| 7 | use Diff\DiffOp\AtomicDiffOp; |
| 8 | use Diff\DiffOp\Diff\Diff; |
| 9 | use Diff\DiffOp\DiffOp; |
| 10 | use Diff\DiffOp\DiffOpAdd; |
| 11 | use Diff\DiffOp\DiffOpChange; |
| 12 | use Diff\DiffOp\DiffOpRemove; |
| 13 | use Diff\Patcher\PatcherException; |
| 14 | use EntitySchema\Services\Converter\FullArrayEntitySchemaData; |
| 15 | |
| 16 | /** |
| 17 | * @license GPL-2.0-or-later |
| 18 | */ |
| 19 | class EntitySchemaPatcher { |
| 20 | |
| 21 | /** |
| 22 | * @param FullArrayEntitySchemaData $baseSchema |
| 23 | * @param Diff $patch |
| 24 | * |
| 25 | * @return FullArrayEntitySchemaData |
| 26 | * |
| 27 | * @throws PatcherException throws exception if patch cannot be applied |
| 28 | */ |
| 29 | public function patchSchema( FullArrayEntitySchemaData $baseSchema, Diff $patch ): FullArrayEntitySchemaData { |
| 30 | $patchedSchema = $this->patchFingerprint( $baseSchema->data, $patch ); |
| 31 | |
| 32 | $patchedSchema['schemaText'] = $this->patchString( |
| 33 | $baseSchema->data['schemaText'] ?? '', |
| 34 | $patch['schemaText'] ?? null |
| 35 | ); |
| 36 | |
| 37 | return new FullArrayEntitySchemaData( $patchedSchema ); |
| 38 | } |
| 39 | |
| 40 | private function patchFingerprint( array $baseSchema, Diff $patch ): array { |
| 41 | $aliasGroupPatcher = new AliasGroupListPatcher(); |
| 42 | |
| 43 | $patchedSchema = [ |
| 44 | 'labels' => $this->patchTermlist( |
| 45 | $baseSchema['labels'] ?? [], |
| 46 | $patch['labels'] ?? null |
| 47 | ), |
| 48 | 'descriptions' => $this->patchTermlist( |
| 49 | $baseSchema['descriptions'] ?? [], |
| 50 | $patch['descriptions'] ?? null |
| 51 | ), |
| 52 | 'aliases' => $aliasGroupPatcher->patchAliasGroupList( |
| 53 | $baseSchema['aliases'] ?? [], |
| 54 | $patch['aliases'] ?? null |
| 55 | ), |
| 56 | ]; |
| 57 | |
| 58 | return $patchedSchema; |
| 59 | } |
| 60 | |
| 61 | private function patchTermlist( array $terms, ?Diff $patch = null ): array { |
| 62 | if ( $patch === null ) { |
| 63 | return $terms; |
| 64 | } |
| 65 | foreach ( $patch as $lang => $diffOp ) { |
| 66 | $terms = $this->patchTerm( $terms, $lang, $diffOp ); |
| 67 | } |
| 68 | return $terms; |
| 69 | } |
| 70 | |
| 71 | private function patchTerm( array $terms, string $lang, AtomicDiffOp $diffOp ): array { |
| 72 | switch ( true ) { |
| 73 | case $diffOp instanceof DiffOpAdd: |
| 74 | if ( !empty( $terms[$lang] ) ) { |
| 75 | throw new PatcherException( 'Term already exists' ); |
| 76 | } |
| 77 | $terms[$lang] = $diffOp->getNewValue(); |
| 78 | break; |
| 79 | |
| 80 | case $diffOp instanceof DiffOpChange: |
| 81 | if ( empty( $terms[$lang] ) |
| 82 | || $terms[$lang] !== $diffOp->getOldValue() |
| 83 | ) { |
| 84 | throw new PatcherException( 'Term had been changed' ); |
| 85 | } |
| 86 | $terms[$lang] = $diffOp->getNewValue(); |
| 87 | break; |
| 88 | |
| 89 | case $diffOp instanceof DiffOpRemove: |
| 90 | if ( !empty( $terms[$lang] ) |
| 91 | && $terms[$lang] !== $diffOp->getOldValue() |
| 92 | ) { |
| 93 | throw new PatcherException( 'Term had been changed' ); |
| 94 | } |
| 95 | unset( $terms[$lang] ); |
| 96 | break; |
| 97 | |
| 98 | default: |
| 99 | throw new PatcherException( 'Invalid terms diff' ); |
| 100 | } |
| 101 | |
| 102 | return $terms; |
| 103 | } |
| 104 | |
| 105 | /** |
| 106 | * @param string $base |
| 107 | * @param DiffOp|null $diffOp |
| 108 | * |
| 109 | * @return string |
| 110 | */ |
| 111 | private function patchString( string $base, ?DiffOp $diffOp = null ): string { |
| 112 | switch ( true ) { |
| 113 | case $diffOp instanceof DiffOpAdd: |
| 114 | $from = ''; |
| 115 | $to = $diffOp->getNewValue(); |
| 116 | break; |
| 117 | case $diffOp instanceof DiffOpRemove: |
| 118 | $from = $diffOp->getOldValue(); |
| 119 | $to = ''; |
| 120 | break; |
| 121 | case $diffOp instanceof DiffOpChange: |
| 122 | $from = $diffOp->getOldValue(); |
| 123 | $to = $diffOp->getNewValue(); |
| 124 | break; |
| 125 | default: |
| 126 | $from = $to = null; |
| 127 | } |
| 128 | if ( $from !== $to ) { |
| 129 | $ok = wfMerge( |
| 130 | $from, |
| 131 | $to, |
| 132 | $base, |
| 133 | $result |
| 134 | ); |
| 135 | if ( !$ok ) { |
| 136 | throw new PatcherException( 'Patching the Schema failed because it has been changed.' ); |
| 137 | } |
| 138 | return trim( $result ); |
| 139 | } |
| 140 | |
| 141 | return $base; |
| 142 | } |
| 143 | |
| 144 | } |