Coverage for jsonschema_diff/core/tools/render.py: 100%
29 statements
« prev ^ index » next coverage.py v7.10.5, created at 2025-08-25 07:00 +0000
« prev ^ index » next coverage.py v7.10.5, created at 2025-08-25 07:00 +0000
1from typing import TYPE_CHECKING, Any, List, Sequence, TypeAlias
3if TYPE_CHECKING:
4 from ..abstraction import Statuses
5 from ..config import Config
7PATH_MAKER_IGNORE_RULES_TYPE: TypeAlias = Sequence[str]
10class RenderTool:
11 """
12 Small helper utilities used by the rendering subsystem.
13 """
15 # --------------------------------------------------------------------- #
16 # Basic helpers
17 # --------------------------------------------------------------------- #
19 @staticmethod
20 def make_tab(config: "Config", tab_level: int) -> str:
21 """
22 Return indentation string.
24 Parameters
25 ----------
26 config : Config
27 Application config that owns the ``TAB`` constant.
28 tab_level : int
29 Indentation depth.
31 Returns
32 -------
33 str
34 ``config.TAB`` repeated *tab_level* times.
35 """
36 return config.TAB * tab_level
38 @staticmethod
39 def make_prefix(status: "Statuses") -> str:
40 """
41 Convert a ``Statuses`` enum value to its printable form.
43 Parameters
44 ----------
45 status : Statuses
46 Validation status.
48 Returns
49 -------
50 str
51 ``status.value`` as plain text.
52 """
53 return f"{status.value}"
55 # --------------------------------------------------------------------- #
56 # Path builder
57 # --------------------------------------------------------------------- #
59 @staticmethod
60 def make_path(
61 schema_path: Sequence[Any],
62 json_path: Sequence[Any],
63 ignore: PATH_MAKER_IGNORE_RULES_TYPE = ("properties",),
64 ) -> str:
65 """
66 Compose a human‑readable path by synchronising two parallel paths.
68 The function walks through *schema_path* (tokens from JSON Schema) and
69 *json_path* (real path in the JSON instance) and emits a short textual
70 representation such as ``["items"][0].extra``.
72 Parameters
73 ----------
74 schema_path : Sequence[Any]
75 Tokens encountered while traversing the schema.
76 json_path : Sequence[Any]
77 Path tokens from the actual JSON document.
78 ignore : Sequence[str], default (``"properties"``,)
79 Schema‑only service tokens to skip.
81 Returns
82 -------
83 str
84 Compact path string.
86 Algorithm
87 ---------
88 i — index in *schema_path*, j — index in *json_path*.
90 1. If *schema_path[i]* is in *ignore* and **differs** from
91 *json_path[j]* → skip it.
92 2. If tokens are equal → emit as property/index and advance both.
93 3. Otherwise the token exists only in schema → emit as ``.token`` and
94 advance *i*.
95 4. After the schema is exhausted, append remaining elements of
96 *json_path*.
98 Integer‑like tokens are rendered as ``[n]``; everything else as
99 ``["key"]``.
100 """
101 parts: List[str] = []
102 i = j = 0
104 while i < len(schema_path):
105 s_tok = schema_path[i]
107 # 1. Ignore schema-only service tokens
108 if s_tok in ignore and (j >= len(json_path) or str(s_tok) != str(json_path[j])):
109 i += 1
110 continue
112 # 2. Token is present in both paths
113 if j < len(json_path) and str(s_tok) == str(json_path[j]):
114 tok = json_path[j]
115 parts.append(f"[{tok}]" if isinstance(tok, int) else f'["{tok}"]')
116 i += 1
117 j += 1
118 continue
120 # 3. Token is schema-only – treat as extra property
121 parts.append(f".{s_tok}")
122 i += 1
124 # 4. Append the rest of json_path
125 for tok in json_path[j:]:
126 parts.append(f"[{tok}]" if isinstance(tok, int) else f'["{tok}"]')
128 return "".join(parts)