Passed
Push — master ( 9aac0b...149846 )
by Alexander
02:13
created

Config::getSectionRules()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 5
ccs 3
cts 3
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Vasoft\VersionIncrement;
6
7
use Vasoft\VersionIncrement\Commits\CommitCollection;
8
use Vasoft\VersionIncrement\Commits\Section;
9
use Vasoft\VersionIncrement\Contract\ChangelogFormatterInterface;
10
use Vasoft\VersionIncrement\Contract\CommitParserInterface;
11
use Vasoft\VersionIncrement\Contract\SectionRuleInterface;
12
use Vasoft\VersionIncrement\Contract\TagFormatterInterface;
13
use Vasoft\VersionIncrement\Contract\VcsExecutorInterface;
14
use Vasoft\VersionIncrement\Exceptions\UnknownPropertyException;
15
use Vasoft\VersionIncrement\SectionRules\DefaultRule;
16
17
/**
18
 * Class Config.
19
 *
20
 * Represents the configuration for the version increment tool. This class provides methods to configure various
21
 * aspects of the tool, such as sections, rules, version control settings, and formatters. It also manages default
22
 * configurations and ensures consistency across the application.
23
 */
24
final class Config
25
{
26
    private array $scopes = [];
27
    private array $props = [];
28
    private string $squashedCommitMessage = 'Squashed commit of the following:';
29
    private bool $processDefaultSquashedCommit = false;
30
    private array $minorTypes = [
31
        'feat',
32
    ];
33
    private array $majorTypes = [];
34
    private array $sectionRules = [];
35
    public const DEFAULT_SECTION = 'other';
36
37
    private int $defaultOrder = 500;
38
    private bool $sored = true;
39
    private string $masterBranch = 'master';
40
    private string $releaseSection = 'chore';
41
42
    private string $releaseScope = 'release';
43
44
    private string $aggregateSection = '';
45
46
    private bool $enabledComposerVersioning = true;
47
48
    private ?ChangelogFormatterInterface $changelogFormatter = null;
49
    private ?CommitParserInterface $commitParser = null;
50
    private ?VcsExecutorInterface $vcsExecutor = null;
51
    private ?TagFormatterInterface $tagFormatter = null;
52
53
    private bool $hideDoubles = false;
54
55
    private bool $ignoreUntrackedFiles = false;
56
    private array $sections = [
57
        'feat' => [
58
            'title' => 'New features',
59
            'order' => 10,
60
            'hidden' => false,
61
        ],
62
        'fix' => [
63
            'title' => 'Fixes',
64
            'order' => 20,
65
            'hidden' => false,
66
        ],
67
        'chore' => [
68
            'title' => 'Other changes',
69
            'order' => 30,
70
            'hidden' => false,
71
        ],
72
        'docs' => [
73
            'title' => 'Documentation',
74
            'order' => 40,
75
            'hidden' => false,
76
        ],
77
        'style' => [
78
            'title' => 'Styling',
79
            'order' => 50,
80
            'hidden' => false,
81
        ],
82
        'refactor' => [
83
            'title' => 'Refactoring',
84
            'order' => 60,
85
            'hidden' => false,
86
        ],
87
        'test' => [
88
            'title' => 'Tests',
89
            'order' => 70,
90
            'hidden' => false,
91
        ],
92
        'perf' => [
93
            'title' => 'Performance',
94
            'order' => 80,
95
            'hidden' => false,
96
        ],
97
        'ci' => [
98
            'title' => 'Configure CI',
99
            'order' => 90,
100
            'hidden' => false,
101
        ],
102
        'build' => [
103
            'title' => 'Change build system',
104
            'order' => 100,
105
            'hidden' => false,
106
        ],
107
        'other' => [
108
            'title' => 'Other',
109
            'order' => 110,
110
            'hidden' => false,
111
        ],
112
    ];
113
114
    /**
115
     * Sets the sections configuration for the tool.
116
     *
117
     * This method allows you to define a custom set of sections, each with its own title, order, and visibility settings.
118
     * Existing sections are cleared before applying the new configuration. The order of sections is reset, and the default
119
     * section (`other`) will be added automatically if not explicitly defined.
120
     *
121
     * Each section can be configured with the following optional parameters:
122
     * - `title`: The display name of the section in the CHANGELOG (defaults to the section key).
123
     * - `order`: The sorting priority of the section (auto-incremented if not provided).
124
     * - `hidden`: Whether the section should be hidden in the CHANGELOG (defaults to `false`).
125
     *
126
     * @param array $sections an associative array where keys are section codes and values are arrays containing
127
     *                        the section's configuration (`title`, `order`, and `hidden`)
128
     *
129
     * @return $this this Config instance for method chaining
130
     */
131 6
    public function setSections(array $sections): self
132
    {
133 6
        $this->sored = false;
134 6
        $this->sections = [];
135 6
        foreach ($sections as $key => $value) {
136 6
            $this->sections[$key] = [
137 6
                'title' => $value['title'] ?? $key,
138 6
                'order' => $value['order'] ?? ++$this->defaultOrder,
139 6
                'hidden' => $value['hidden'] ?? false,
140 6
            ];
141
        }
142
143 6
        return $this;
144
    }
145
146
    /**
147
     * Sets or updates the configuration for a specific section.
148
     *
149
     * This method allows you to define or modify the settings of a section, such as its title, order, and visibility.
150
     * If the section already exists, its configuration is updated; otherwise, a new section is created. Existing values
151
     * for `order` and `hidden` are preserved unless explicitly overridden.
152
     *
153
     * @param string    $key    the unique identifier (code) of the section
154
     * @param string    $title  the display name of the section in the CHANGELOG
155
     * @param int       $order  The sorting priority of the section. Defaults to `-1`, which preserves the existing order
156
     *                          or assigns a new auto-incremented value if the section does not exist.
157
     * @param null|bool $hidden Whether the section should be hidden in the CHANGELOG. Defaults to `null`, which
158
     *                          preserves the existing visibility setting or sets it to `false` if the section
159
     *                          does not exist.
160
     *
161
     * @return $this this Config instance for method chaining
162
     */
163 4
    public function setSection(
164
        string $key,
165
        string $title,
166
        int $order = -1,
167
        ?bool $hidden = null,
168
    ): self {
169 4
        $this->sored = false;
170 4
        $exists = $this->sections[$key] ?? null;
171 4
        $this->sections[$key] = [
172 4
            'title' => $title,
173 4
            'order' => -1 === $order ? ($exists ? $exists['order'] : ++$this->defaultOrder) : $order,
174 4
            'hidden' => null !== $hidden ? $hidden : ($exists ? $exists['hidden'] : false),
175 4
        ];
176
177 4
        return $this;
178
    }
179
180
    /**
181
     * Retrieves the index of sections as an array with empty values.
182
     *
183
     * This method sorts the sections internally and returns an array where the keys are section names and the values
184
     * are empty arrays. This is useful for initializing data structures that require a predefined set of sections.
185
     *
186
     * @return array an associative array with section names as keys and empty arrays as values
187
     */
188 3
    public function getSectionIndex(): array
189
    {
190 3
        $this->sortSections();
191
192 3
        return array_fill_keys(array_keys($this->sections), []);
193
    }
194
195
    /**
196
     * Retrieves the commit collection based on the configured sections.
197
     *
198
     * This method creates a `CommitCollection` object by converting the configured sections into `Section` objects.
199
     * It ensures that all sections are sorted and that a default section exists if not explicitly defined.
200
     *
201
     * @return CommitCollection a collection of commits grouped by sections
202
     */
203 24
    public function getCommitCollection(): CommitCollection
204
    {
205 24
        $this->sortSections();
206 24
        $sections = [];
207 24
        $default = null;
208 24
        foreach ($this->sections as $key => $section) {
209 24
            $section = new Section(
210 24
                $key,
211 24
                $section['title'],
212 24
                $section['hidden'],
213 24
                $this->getSectionRules($key),
214 24
                in_array($key, $this->majorTypes, true),
215 24
                in_array($key, $this->minorTypes, true),
216 24
                $this,
217 24
            );
218 24
            if (self::DEFAULT_SECTION === $key) {
219 24
                $default = $section;
220
            }
221 24
            $sections[$key] = $section;
222
        }
223
224 24
        return new CommitCollection($sections, /* @scrutinizer ignore-type */ $default);
225
    }
226
227 33
    private function sortSections(): void
228
    {
229 33
        if (!$this->sored) {
230 10
            $this->checkDefaultSection();
231 10
            uasort($this->sections, static fn($a, $b) => $a['order'] <=> $b['order']);
232 10
            $this->sored = true;
233
        }
234
    }
235
236
    /**
237
     * Retrieves the descriptions of all configured sections.
238
     *
239
     * This method generates a list of section descriptions in the format "key - title".
240
     * The sections are sorted internally before generating the descriptions. Each description consists of the section's
241
     * unique identifier (key) and its display name (title).
242
     *
243
     * Used for --list option
244
     *
245
     * @return array an array of strings, where each string represents a section description in the format "key - title"
246
     */
247 6
    public function getSectionDescriptions(): array
248
    {
249 6
        $this->sortSections();
250 6
        $result = [];
251 6
        foreach ($this->sections as $key => $section) {
252 6
            $result[] = sprintf('%s - %s', $key, $section['title']);
253
        }
254
255 6
        return $result;
256
    }
257
258
    /**
259
     * Retrieves the title of a specific section.
260
     *
261
     * This method returns the title of the section identified by the given key. If the section does not exist, the key
262
     * itself is returned as the title.
263
     *
264
     * @param string $key the key of the section
265
     *
266
     * @return string the title of the section or the key if the section does not exist
267
     */
268 2
    public function getSectionTitle(string $key): string
269
    {
270 2
        return $this->sections[$key]['title'] ?? $key;
271
    }
272
273
    /**
274
     * Checks whether a specific section is hidden.
275
     *
276
     * This method determines if the section identified by the given key is marked as hidden in the configuration. If
277
     * the section does not exist, it is considered visible (not hidden).
278
     *
279
     * @param string $key the key of the section
280
     *
281
     * @return bool returns `true` if the section is hidden, `false` otherwise
282
     */
283 2
    public function isSectionHidden(string $key): bool
284
    {
285 2
        return $this->sections[$key]['hidden'] ?? false;
286
    }
287
288
    /**
289
     * Sets the section to be used for release commits.
290
     *
291
     * This method defines the section that will be associated with release-related commits. The specified section will be
292
     * used when generating release commit messages or determining the scope of a release.
293
     *
294
     * @param string $section The key of the section to be used for releases (e.g., 'release').
295
     *
296
     * @return $this this Config instance for method chaining
297
     */
298 1
    public function setReleaseSection(string $section): self
299
    {
300 1
        $this->releaseSection = $section;
301
302 1
        return $this;
303
    }
304
305
    /**
306
     * Retrieves the key of the section configured for release commits.
307
     *
308
     * This method returns the key of the section that is associated with release-related commits. If the configured
309
     * release section does not exist in the sections list, the default section (`other`) is returned instead.
310
     *
311
     * @return string the key of the release section or the default section if the configured release section is invalid
312
     */
313 22
    public function getReleaseSection(): string
314
    {
315 22
        return isset($this->sections[$this->releaseSection]) ? $this->releaseSection : self::DEFAULT_SECTION;
316
    }
317
318 10
    private function checkDefaultSection(): void
319
    {
320 10
        if (!isset($this->sections[self::DEFAULT_SECTION])) {
321 6
            $maxOrder = 0;
322 6
            foreach ($this->sections as $section) {
323 6
                $maxOrder = max($maxOrder, $section['order']);
324
            }
325 6
            $this->sections[self::DEFAULT_SECTION] = [
326 6
                'title' => 'Other',
327 6
                'order' => $maxOrder + 1000,
328 6
                'hidden' => false,
329 6
            ];
330
        }
331
    }
332
333
    /**
334
     * Retrieves the name of the main branch in the repository.
335
     *
336
     * By default, it is set to "master".
337
     *
338
     * @return string The name of the main branch (e.g., "main" or "master").
339
     */
340 30
    public function getMasterBranch(): string
341
    {
342 30
        return $this->masterBranch;
343
    }
344
345
    /**
346
     * Sets the name of the main branch in the repository.
347
     *
348
     * This method allows you to configure the name of the main branch (e.g., "main" or "master") used by the tool.
349
     *
350
     * @param string $masterBranch the name of the main branch
351
     *
352
     * @return $this this Config instance for method chaining
353
     */
354 4
    public function setMasterBranch(string $masterBranch): self
355
    {
356 4
        $this->masterBranch = $masterBranch;
357
358 4
        return $this;
359
    }
360
361
    /**
362
     * Sets the types of changes that trigger a minor version increment.
363
     *
364
     * This method configures the list of commit types that, when present, will cause the minor version to be
365
     * incremented during version updates.
366
     *
367
     * @param array $minorTypes An array of commit type codes (e.g., ['feat', 'fix']).
368
     *
369
     * @return $this this Config instance for method chaining
370
     */
371 1
    public function setMinorTypes(array $minorTypes): self
372
    {
373 1
        $this->minorTypes = $minorTypes;
374
375 1
        return $this;
376
    }
377
378
    /**
379
     * Sets the types of changes that trigger a major version increment.
380
     *
381
     * This method configures the list of commit types that, when present, will cause the major version to be
382
     * incremented during version updates.
383
     *
384
     * @param array $majorTypes An array of commit type codes (e.g., ['breaking']).
385
     *
386
     * @return $this this Config instance for method chaining
387
     */
388 2
    public function setMajorTypes(array $majorTypes): self
389
    {
390 2
        $this->majorTypes = $majorTypes;
391
392 2
        return $this;
393
    }
394
395
    /**
396
     * Sets the scope to be used for release commit messages.
397
     *
398
     * This method defines the scope that will be included in the description of release-related commits.
399
     * If an empty string is provided, no scope will be added to the release commit message.
400
     *
401
     * @param string $releaseScope The scope to be used for release commits (e.g., 'rel').
402
     *                             Use an empty string to omit the scope from the commit message.
403
     *
404
     * @return $this this Config instance for method chaining
405
     */
406 3
    public function setReleaseScope(string $releaseScope): self
407
    {
408 3
        $this->releaseScope = $releaseScope;
409
410 3
        return $this;
411
    }
412
413
    /**
414
     * Retrieves the scope configured for release commit messages.
415
     *
416
     * This method returns the scope that will be included in the description of release-related commits.
417
     * If no scope is configured, an empty string is returned, indicating that no scope will be added to the commit message.
418
     *
419
     * @return string the scope for release commit messages, or an empty string if no scope is configured
420
     */
421 21
    public function getReleaseScope(): string
422
    {
423 21
        return $this->releaseScope;
424
    }
425
426
    /**
427
     * Enables or disables ignoring untracked files in the repository.
428
     *
429
     * This method configures whether untracked files should be ignored when running the version increment tool.
430
     * By default, untracked files are not ignored, and their presence may cause the tool to fail.
431
     *
432
     * @param bool $ignoreUntrackedFiles Whether to ignore untracked files:
433
     *                                   - `true`: Ignore untracked files.
434
     *                                   - `false`: Do not ignore untracked files (default behavior).
435
     *
436
     * @return $this this Config instance for method chaining
437
     */
438 1
    public function setIgnoreUntrackedFiles(bool $ignoreUntrackedFiles): self
439
    {
440 1
        $this->ignoreUntrackedFiles = $ignoreUntrackedFiles;
441
442 1
        return $this;
443
    }
444
445
    /**
446
     * Checks whether untracked files are ignored in the repository.
447
     *
448
     * This method retrieves the current configuration for ignoring untracked files. If enabled, the tool will not
449
     * consider untracked files when performing operations.
450
     *
451
     * @return bool returns `true` if untracked files are ignored, `false` otherwise
452
     */
453 29
    public function mastIgnoreUntrackedFiles(): bool
454
    {
455 29
        return $this->ignoreUntrackedFiles;
456
    }
457
458
    /**
459
     * Adds a rule for determining whether a commit belongs to a specific section.
460
     *
461
     * This method associates a rule with a specific section. The rule is used to evaluate whether a commit should be
462
     * included in the specified section.
463
     *
464
     * @param string               $key  the key of the section
465
     * @param SectionRuleInterface $rule the rule to be added
466
     *
467
     * @return $this this Config instance for method chaining
468
     */
469 3
    public function addSectionRule(string $key, SectionRuleInterface $rule): self
470
    {
471 3
        $this->sectionRules[$key][] = $rule;
472
473 3
        return $this;
474
    }
475
476
    /**
477
     * Retrieves the rules associated with a specific section.
478
     *
479
     * This method returns an array of rules for the specified section. If no custom rules are defined, a default rule
480
     * is automatically added and returned.
481
     *
482
     * @param string $key the key of the section
483
     *
484
     * @return SectionRuleInterface[] an array of rules for the specified section
485
     */
486 24
    public function getSectionRules(string $key): array
487
    {
488 24
        $this->sectionRules[$key]['default'] = new DefaultRule($key);
489
490 24
        return $this->sectionRules[$key];
491
    }
492
493
    /**
494
     * Sets the section to be used for identifying squashed (aggregate) commits.
495
     *
496
     * This method configures the section that will be treated as a marker for squashed commits. When a commit is
497
     * associated with this section, it will be processed as a squashed commit. Squashed commits typically contain
498
     * a summary of multiple commits and are parsed accordingly to extract individual changes.
499
     *
500
     * @param string $aggregateSection The key of the section to be used for identifying squashed commits
501
     *                                 (e.g., 'aggregate').
502
     *
503
     * @return $this this Config instance for method chaining
504
     */
505 1
    public function setAggregateSection(string $aggregateSection): self
506
    {
507 1
        $this->aggregateSection = $aggregateSection;
508
509 1
        return $this;
510
    }
511
512
    /**
513
     * Retrieves the section configured for identifying squashed (aggregate) commits.
514
     *
515
     * This method returns the key of the section that is used to identify squashed commits. If no section has been
516
     * explicitly configured, an empty string is returned, indicating that no specific section is assigned for
517
     * identifying squashed commits.
518
     *
519
     * @return string the key of the section used for identifying squashed commits, or an empty string if no section
520
     *                is configured
521
     */
522 24
    public function getAggregateSection(): string
523
    {
524 24
        return $this->aggregateSection;
525
    }
526
527
    /**
528
     * Sets the commit message template for squashed commits.
529
     *
530
     * This method allows you to customize the message used to identify squashed commits in the repository.
531
     *
532
     * @param string $squashedCommitMessage the custom message template for squashed commits
533
     *
534
     * @return $this this Config instance for method chaining
535
     */
536 1
    public function setSquashedCommitMessage(string $squashedCommitMessage): self
537
    {
538 1
        $this->squashedCommitMessage = $squashedCommitMessage;
539
540 1
        return $this;
541
    }
542
543
    /**
544
     * Retrieves the commit message template for squashed commits.
545
     *
546
     * @return string the message template for squashed commits
547
     */
548 24
    public function getSquashedCommitMessage(): string
549
    {
550 24
        return $this->squashedCommitMessage;
551
    }
552
553
    /**
554
     * Enables or disables processing of default squashed commits.
555
     *
556
     * This method configures whether the tool should process default squashed commits (those matching the default
557
     * message template).
558
     *
559
     * @param bool $processDefaultSquashedCommit whether to enable processing of default squashed commits
560
     *
561
     * @return $this this Config instance for method chaining
562
     */
563 2
    public function setProcessDefaultSquashedCommit(bool $processDefaultSquashedCommit): self
564
    {
565 2
        $this->processDefaultSquashedCommit = $processDefaultSquashedCommit;
566
567 2
        return $this;
568
    }
569
570
    /**
571
     * Checks whether processing of default squashed commits is enabled.
572
     *
573
     * @return bool returns `true` if processing of default squashed commits is enabled, `false` otherwise
574
     */
575 24
    public function shouldProcessDefaultSquashedCommit(): bool
576
    {
577 24
        return $this->processDefaultSquashedCommit;
578
    }
579
580
    /**
581
     * Retrieves the changelog formatter instance.
582
     *
583
     * If no custom changelog formatter is set, a default instance of `DefaultFormatter` is created and configured.
584
     * The formatter's configuration is automatically updated to use the current `Config` instance.
585
     *
586
     * @return ChangelogFormatterInterface the changelog formatter instance
587
     */
588 24
    public function getChangelogFormatter(): ChangelogFormatterInterface
589
    {
590 24
        if (null === $this->changelogFormatter) {
591 22
            $this->changelogFormatter = new Changelog\DefaultFormatter();
592 22
            $this->changelogFormatter->setConfig($this);
593
        }
594
595 24
        return $this->changelogFormatter;
596
    }
597
598
    /**
599
     * Sets a custom changelog formatter for the configuration.
600
     *
601
     * The provided formatter will be used for all changelog-related operations. The formatter's configuration
602
     * is automatically updated to use the current `Config` instance.
603
     *
604
     * @param ChangelogFormatterInterface $changelogFormatter the custom changelog formatter to set
605
     *
606
     * @return $this this Config instance for method chaining
607
     */
608 2
    public function setChangelogFormatter(ChangelogFormatterInterface $changelogFormatter): self
609
    {
610 2
        $this->changelogFormatter = $changelogFormatter;
611 2
        $this->changelogFormatter->setConfig($this);
612
613 2
        return $this;
614
    }
615
616
    /**
617
     * Retrieves the VCS executor instance.
618
     *
619
     * If no custom VCS executor is set, a default instance of `GitExecutor` is created and used.
620
     *
621
     * @return VcsExecutorInterface the VCS executor instance
622
     */
623 31
    public function getVcsExecutor(): VcsExecutorInterface
624
    {
625 31
        if (null === $this->vcsExecutor) {
626 1
            $this->vcsExecutor = new GitExecutor();
627
        }
628
629 31
        return $this->vcsExecutor;
630
    }
631
632
    /**
633
     * Sets a custom VCS executor for the configuration.
634
     *
635
     * The provided executor will be used for all version control system operations.
636
     *
637
     * @param VcsExecutorInterface $vcsExecutor the custom VCS executor to set
638
     *
639
     * @return $this this Config instance for method chaining
640
     */
641 31
    public function setVcsExecutor(VcsExecutorInterface $vcsExecutor): self
642
    {
643 31
        $this->vcsExecutor = $vcsExecutor;
644
645 31
        return $this;
646
    }
647
648
    /**
649
     * Retrieves the commit parser instance.
650
     *
651
     * If no custom commit parser is set, a default instance of `ShortParser` is created and configured.
652
     * The parser's configuration is automatically updated to use the current `Config` instance.
653
     *
654
     * @return CommitParserInterface the commit parser instance
655
     */
656 27
    public function getCommitParser(): CommitParserInterface
657
    {
658 27
        if (null === $this->commitParser) {
659 26
            $this->commitParser = new Commits\ShortParser();
660 26
            $this->commitParser->setConfig($this);
661
        }
662
663 27
        return $this->commitParser;
664
    }
665
666
    /**
667
     * Sets a custom commit parser for the configuration.
668
     *
669
     * The provided parser will be used for all commit parsing operations. The parser's configuration is automatically
670
     * updated to use the current `Config` instance.
671
     *
672
     * @return $this this Config instance for method chaining
673
     */
674 1
    public function setCommitParser(CommitParserInterface $changelogFormatter): self
675
    {
676 1
        $this->commitParser = $changelogFormatter;
677 1
        $this->commitParser->setConfig($this);
678
679 1
        return $this;
680
    }
681
682
    /**
683
     * Retrieves the tag formatter instance.
684
     *
685
     * If no custom tag formatter is set, a default instance of `DefaultFormatter` is created and configured.
686
     *
687
     * @return TagFormatterInterface the tag formatter instance
688
     */
689 6
    public function getTagFormatter(): TagFormatterInterface
690
    {
691 6
        if (null === $this->tagFormatter) {
692 5
            $this->tagFormatter = new Tag\DefaultFormatter();
693 5
            $this->tagFormatter->setConfig($this);
694
        }
695
696 6
        return $this->tagFormatter;
697
    }
698
699
    /**
700
     * Sets a custom tag formatter for the configuration.
701
     *
702
     * The provided formatter will be used for all tag-related operations. The formatter's configuration
703
     * is automatically updated to use the current `Config` instance.
704
     *
705
     * @param TagFormatterInterface $tagFormatter the custom tag formatter to set
706
     *
707
     * @return $this this Config instance for method chaining
708
     */
709 1
    public function setTagFormatter(TagFormatterInterface $tagFormatter): self
710
    {
711 1
        $this->tagFormatter = $tagFormatter;
712 1
        $this->tagFormatter->setConfig($this);
713
714 1
        return $this;
715
    }
716
717
    /**
718
     * Enables or disables version management in the `composer.json` file.
719
     *
720
     * When disabled, version management will rely solely on Git tags instead of updating `composer.json`.
721
     *
722
     * @param bool $enabledComposerVersioning Whether to enable version management in `composer.json`.
723
     *                                        - `true`: Enable version management in `composer.json`.
724
     *                                        - `false`: Disable version management in `composer.json`.
725
     *
726
     * @return $this this Config instance for method chaining
727
     */
728 8
    public function setEnabledComposerVersioning(bool $enabledComposerVersioning): self
729
    {
730 8
        $this->enabledComposerVersioning = $enabledComposerVersioning;
731
732 8
        return $this;
733
    }
734
735
    /**
736
     * Checks whether version management in the `composer.json` file is enabled.
737
     *
738
     * @return bool Returns `true` if version management in `composer.json` is enabled, `false` otherwise.
739
     */
740 29
    public function isEnabledComposerVersioning(): bool
741
    {
742 29
        return $this->enabledComposerVersioning;
743
    }
744
745
    /**
746
     * Sets a custom property in the configuration.
747
     *
748
     * This method allows you to store custom key-value pairs in the configuration. These properties can be used to pass
749
     * additional parameters required by custom implementations (e.g., formatters, VCS executors, parsers, etc.).
750
     *
751
     * @param string $key   The name of the property to set. This should be a unique identifier for the property.
752
     * @param mixed  $value The value to associate with the property. This can be any type of data required by the custom
753
     *                      implementation.
754
     *
755
     * @return $this this Config instance for method chaining
756
     *
757
     * @example
758
     * ```php
759
     * return (new \Vasoft\VersionIncrement\Config())
760
     *     ->set('customParam', 'customValue');
761
     * ```
762
     */
763 5
    public function set(string $key, mixed $value): self
764
    {
765 5
        $this->props[$key] = $value;
766
767 5
        return $this;
768
    }
769
770
    /**
771
     * Retrieves the value of a custom property from the configuration.
772
     *
773
     * This method retrieves the value associated with the specified property key. If the property does not exist,
774
     * an exception is thrown to indicate that the property is unknown.
775
     *
776
     * @param string $key The name of the property to retrieve. This should match the key used when setting the property.
777
     *
778
     * @return mixed the value associated with the property
779
     *
780
     * @throws UnknownPropertyException if the specified property does not exist in the configuration
781
     */
782 6
    public function get(string $key): mixed
783
    {
784 6
        if (!isset($this->props[$key])) {
785 1
            throw new UnknownPropertyException($key);
786
        }
787
788 5
        return $this->props[$key];
789
    }
790
791
    /**
792
     * Enables or disables hiding of duplicate entries within the same section in the CHANGELOG.
793
     *
794
     * This method configures whether duplicate entries (lines with identical content) should be hidden in the generated
795
     * CHANGELOG. When enabled, only the first occurrence of a duplicate entry will be displayed within each section.
796
     *
797
     * @param bool $hideDoubles Whether to hide duplicate entries:
798
     *                          - `true`: Hide duplicate entries within the same section.
799
     *                          - `false`: Display all entries, including duplicates (default behavior).
800
     *
801
     * @return $this this Config instance for method chaining
802
     */
803 1
    public function setHideDoubles(bool $hideDoubles): self
804
    {
805 1
        $this->hideDoubles = $hideDoubles;
806
807 1
        return $this;
808
    }
809
810
    /**
811
     * Checks whether hiding of duplicate entries within the same section is enabled.
812
     *
813
     * This method retrieves the current configuration for hiding duplicate entries in the CHANGELOG. If enabled, duplicate
814
     * entries within the same section will be hidden during the generation of the CHANGELOG.
815
     *
816
     * @return bool returns `true` if hiding of duplicate entries is enabled, `false` otherwise
817
     */
818 24
    public function isHideDoubles(): bool
819
    {
820 24
        return $this->hideDoubles;
821
    }
822
823
    /**
824
     * Adds a human-readable title for a specific scope.
825
     *
826
     * This method associates a scope key with a human-readable title. The title will be used in the CHANGELOG
827
     * instead of the raw scope name when generating commit messages. If the scope already exists, its title
828
     * will be updated.
829
     *
830
     * @param string $key   The scope key (e.g., 'dev', 'deprecated').
831
     * @param string $title The human-readable title for the scope (e.g., 'Development', 'Deprecated Features').
832
     */
833 2
    public function addScope(string $key, string $title): self
834
    {
835 2
        $this->scopes[$key] = $title;
836
837 2
        return $this;
838
    }
839
840
    /**
841
     * Retrieves all configured scopes and their human-readable titles.
842
     *
843
     * This method returns an associative array where keys are scope codes and values are their corresponding
844
     * human-readable titles. These titles are used in the CHANGELOG to replace raw scope names for better readability.
845
     *
846
     * @return array an associative array of scopes, where keys are scope codes and values are their titles
847
     */
848 6
    public function getScopes(): array
849
    {
850 6
        return $this->scopes;
851
    }
852
}
853