Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
60.00% covered (warning)
60.00%
3 / 5
CRAP
87.69% covered (warning)
87.69%
57 / 65
EntitySchema\Services\Diff\SchemaPatcher
0.00% covered (danger)
0.00%
0 / 1
60.00% covered (warning)
60.00%
3 / 5
26.17
87.69% covered (warning)
87.69%
57 / 65
 patchSchema
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
5 / 5
 patchFingerprint
100.00% covered (success)
100.00%
1 / 1
4
100.00% covered (success)
100.00%
11 / 11
 patchTermlist
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
5 / 5
 patchTerm
0.00% covered (danger)
0.00%
0 / 1
9.32
84.21% covered (warning)
84.21%
16 / 19
 patchString
0.00% covered (danger)
0.00%
0 / 1
7.39
80.00% covered (warning)
80.00%
20 / 25
<?php
namespace EntitySchema\Services\Diff;
use Diff\DiffOp\AtomicDiffOp;
use Diff\DiffOp\Diff\Diff;
use Diff\DiffOp\DiffOp;
use Diff\DiffOp\DiffOpAdd;
use Diff\DiffOp\DiffOpChange;
use Diff\DiffOp\DiffOpRemove;
use Diff\Patcher\PatcherException;
use EntitySchema\Services\SchemaConverter\FullArraySchemaData;
/**
 * @license GPL-2.0-or-later
 */
class SchemaPatcher {
    /**
     * @param FullArraySchemaData $baseSchema
     * @param Diff $patch
     *
     * @return FullArraySchemaData
     *
     * @throws PatcherException throws exception if patch cannot be applied
     * @suppress PhanPluginDuplicateConditionalNullCoalescing
     */
    public function patchSchema( FullArraySchemaData $baseSchema, Diff $patch ): FullArraySchemaData {
        $patchedSchema = $this->patchFingerprint( $baseSchema->data, $patch );
        $patchedSchema['schemaText'] = $this->patchString(
            $baseSchema->data['schemaText'] ?? '',
            isset( $patch['schemaText'] ) ? $patch['schemaText'] : null
        );
        return new FullArraySchemaData( $patchedSchema );
    }
    /** @suppress PhanPluginDuplicateConditionalNullCoalescing */
    private function patchFingerprint( array $baseSchema, Diff $patch ): array {
        $aliasGroupPatcher = new AliasGroupListPatcher();
        $patchedSchema = [
            'labels' => $this->patchTermlist(
                $baseSchema['labels'] ?? [],
                isset( $patch['labels'] ) ? $patch['labels'] : null
            ),
            'descriptions' => $this->patchTermlist(
                $baseSchema['descriptions'] ?? [],
                isset( $patch['descriptions'] ) ? $patch['descriptions'] : null
            ),
            'aliases' => $aliasGroupPatcher->patchAliasGroupList(
                $baseSchema['aliases'] ?? [],
                isset( $patch['aliases'] ) ? $patch['aliases'] : null
            ),
        ];
        return $patchedSchema;
    }
    private function patchTermlist( array $terms, Diff $patch = null ): array {
        if ( $patch === null ) {
            return $terms;
        }
        foreach ( $patch as $lang => $diffOp ) {
            $terms = $this->patchTerm( $terms, $lang, $diffOp );
        }
        return $terms;
    }
    /**
     * @suppress PhanUndeclaredMethod
     */
    private function patchTerm( $terms, $lang, AtomicDiffOp $diffOp ) {
        switch ( true ) {
            case $diffOp instanceof DiffOpAdd:
                if ( !empty( $terms[$lang] ) ) {
                    throw new PatcherException( 'Term already exists' );
                }
                $terms[$lang] = $diffOp->getNewValue();
                break;
            case $diffOp instanceof DiffOpChange:
                if ( empty( $terms[$lang] )
                    || $terms[$lang] !== $diffOp->getOldValue()
                ) {
                    throw new PatcherException( 'Term had been changed' );
                }
                $terms[$lang] = $diffOp->getNewValue();
                break;
            case $diffOp instanceof DiffOpRemove:
                if ( !empty( $terms[$lang] )
                    && $terms[$lang] !== $diffOp->getOldValue()
                ) {
                    throw new PatcherException( 'Term had been changed' );
                }
                unset( $terms[$lang] );
                break;
            default:
                throw new PatcherException( 'Invalid terms diff' );
        }
        return $terms;
    }
    /**
     * @param string $base
     * @param DiffOp|null $diffOp
     *
     * @return string
     *
     * @suppress PhanUndeclaredMethod
     */
    private function patchString( $base, DiffOp $diffOp = null ) {
        switch ( true ) {
            case $diffOp instanceof DiffOpAdd:
                $from = '';
                $to = $diffOp->getNewValue();
                break;
            case $diffOp instanceof DiffOpRemove:
                $from = $diffOp->getOldValue();
                $to = '';
                break;
            case $diffOp instanceof DiffOpChange:
                $from = $diffOp->getOldValue();
                $to = $diffOp->getNewValue();
                break;
            case $diffOp === null;
                $from = $to = null;
                break;
        }
        if ( $from !== $to ) {
            $ok = wfMerge(
                $from,
                $to,
                $base,
                $result
            );
            if ( !$ok ) {
                throw new PatcherException( 'Patching the Schema failed because it has been changed.' );
            }
            return trim( $result );
        }
        return $base;
    }
}