/
/
/
1"""
2Compute a before/after diff for a dry-run config write.
3
4SECURE_STRING values are masked on both sides so a preview never leaks
5a stored or proposed credential.
6"""
7# ruff: noqa: TID252 -- relative imports are the canonical MA-provider pattern.
8
9from __future__ import annotations
10
11from typing import TYPE_CHECKING, Any
12
13from music_assistant_models.constants import SECURE_STRING_SUBSTITUTE
14
15from ..models import DiffResult, ValueChange
16from .secret_handler import is_secret_key
17
18if TYPE_CHECKING:
19 from collections.abc import Mapping
20
21 from music_assistant_models.config_entries import ConfigEntry
22
23
24def compute_diff(
25 *,
26 target_type: str,
27 target_id: str,
28 entries: Mapping[str, ConfigEntry],
29 current: Mapping[str, Any],
30 proposed: Mapping[str, Any],
31) -> DiffResult:
32 """
33 Build a :class:`DiffResult` for the proposed write.
34
35 :param target_type: "provider" | "core" | "player".
36 :param target_id: The target identifier.
37 :param entries: ConfigEntry definitions (used to detect SECURE_STRING).
38 :param current: Currently stored values.
39 :param proposed: Proposed new values (only these keys are diffed).
40 """
41 changes: list[ValueChange] = []
42 for key, after in proposed.items():
43 secret = is_secret_key(entries, key)
44 before = current.get(key)
45 if secret:
46 before_display: Any = SECURE_STRING_SUBSTITUTE if before is not None else None
47 after_display: Any = SECURE_STRING_SUBSTITUTE
48 else:
49 before_display = before
50 after_display = after
51 changes.append(
52 ValueChange(key=key, before=before_display, after=after_display, secret=secret)
53 )
54 return DiffResult(target_type=target_type, target_id=target_id, changes=changes)
55