summaryrefslogtreecommitdiffstats
path: root/src/debputy/plugin/api/example_processing.py
blob: 3bde8c3849759be6992f12b32ef29d3e0872203a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import dataclasses
from enum import Enum
from typing import Set, Tuple, List, cast, Dict, Sequence

from debputy.filesystem_scan import build_virtual_fs
from debputy.plugin.api import VirtualPath
from debputy.plugin.api.impl_types import (
    AutomaticDiscardRuleExample,
    PluginProvidedDiscardRule,
)
from debputy.util import _normalize_path


class DiscardVerdict(Enum):
    INCONSISTENT_CODE_KEPT = (
        None,
        "INCONSISTENT (code kept the path, but should have discarded)",
    )
    INCONSISTENT_CODE_DISCARDED = (
        None,
        "INCONSISTENT (code discarded the path, but should have kept it)",
    )
    KEPT = (False, "Kept")
    DISCARDED_BY_CODE = (True, "Discarded (directly by the rule)")
    DISCARDED_BY_DIRECTORY = (True, "Discarded (directory was discarded)")

    @property
    def message(self) -> str:
        return cast("str", self.value[1])

    @property
    def is_consistent(self) -> bool:
        return self.value[0] is not None

    @property
    def is_discarded(self) -> bool:
        return self.value[0] is True

    @property
    def is_kept(self) -> bool:
        return self.value[0] is False


@dataclasses.dataclass(slots=True, frozen=True)
class ProcessedDiscardRuleExample:
    rendered_paths: Sequence[Tuple[VirtualPath, DiscardVerdict]]
    inconsistent_paths: Set[VirtualPath]
    # To avoid the parents being garbage collected
    fs_root: VirtualPath


def process_discard_rule_example(
    discard_rule: PluginProvidedDiscardRule,
    example: AutomaticDiscardRuleExample,
) -> ProcessedDiscardRuleExample:
    fs_root: VirtualPath = build_virtual_fs([p for p, _ in example.content])

    actual_discarded: Dict[str, bool] = {}
    expected_output = {
        "/" + _normalize_path(p.path_name, with_prefix=False): v
        for p, v in example.content
    }
    inconsistent_paths = set()
    rendered_paths = []

    for p in fs_root.all_paths():
        parent = p.parent_dir
        discard_carry_over = False
        path_name = p.absolute
        if parent and actual_discarded[parent.absolute]:
            verdict = True
            discard_carry_over = True
        else:
            verdict = discard_rule.should_discard(p)

        actual_discarded[path_name] = verdict
        expected = expected_output.get(path_name)
        if expected is not None:
            inconsistent = expected != verdict
            if inconsistent:
                inconsistent_paths.add(p)
        else:
            continue

        if inconsistent:
            if verdict:
                verdict_code = DiscardVerdict.INCONSISTENT_CODE_DISCARDED
            else:
                verdict_code = DiscardVerdict.INCONSISTENT_CODE_KEPT
        elif verdict:
            if discard_carry_over:
                verdict_code = DiscardVerdict.DISCARDED_BY_DIRECTORY
            else:
                verdict_code = DiscardVerdict.DISCARDED_BY_CODE
        else:
            verdict_code = DiscardVerdict.KEPT
        rendered_paths.append((p, verdict_code))

    return ProcessedDiscardRuleExample(rendered_paths, inconsistent_paths, fs_root)