Passed
Push — master ( 5168f4...b7b35d )
by Alexander
02:07
created

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