Coverage for src/bayernwerk_client/formatting.py: 100%

40 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-17 13:07 +0000

1"""Generic, schema-less human-readable rendering for CLI output. 

2 

3Response shapes vary per endpoint/service and can change over time, so 

4these render whatever comes back - a list of dicts as one indented 

5paragraph per item, a single dict as indented key/value lines - rather 

6than assuming fixed columns or fields. A wide table was tried first but 

7discarded: flattening nested fields (customer, address, ...) into columns 

8made it unreadable, and dropping them silently hid exactly the useful 

9data. Shared across services (`map`, and later `efix`); service-specific 

10renderers that pick out a curated "headline" for a well-known shape (like 

11`map.formatting.render_orders` or `render_instance_tree`) live with that 

12service instead. 

13""" 

14 

15from __future__ import annotations 

16 

17from typing import Any 

18 

19 

20def format_result(data: Any) -> str: 

21 if isinstance(data, list): 

22 if not data: 

23 return "(leer)" 

24 if all(isinstance(item, dict) for item in data): 

25 return format_list(data) 

26 return "\n".join(str(item) for item in data) 

27 if isinstance(data, dict): 

28 return format_dict(data) 

29 return str(data) 

30 

31 

32def format_list(items: list[dict[str, Any]]) -> str: 

33 """One numbered paragraph per item, full contents indented via `format_dict`.""" 

34 blocks = [] 

35 for i, item in enumerate(items, start=1): 

36 body = format_dict(item, indent=1) 

37 blocks.append(f"[{i}]\n{body}" if body else f"[{i}]") 

38 return "\n\n".join(blocks) 

39 

40 

41def format_dict(obj: dict[str, Any], indent: int = 0) -> str: 

42 pad = " " * indent 

43 lines: list[str] = [] 

44 for key, value in obj.items(): 

45 if isinstance(value, dict): 

46 if not value: 

47 lines.append(f"{pad}{key}: {{}}") 

48 else: 

49 lines.append(f"{pad}{key}:") 

50 lines.append(format_dict(value, indent + 1)) 

51 elif isinstance(value, list): 

52 if not value: 

53 lines.append(f"{pad}{key}: []") 

54 elif all(isinstance(item, dict) for item in value): 

55 lines.append(f"{pad}{key}:") 

56 for i, item in enumerate(value): 

57 lines.append(f"{pad} [{i}]") 

58 lines.append(format_dict(item, indent + 2)) 

59 else: 

60 lines.append(f"{pad}{key}: {', '.join(str(item) for item in value)}") 

61 else: 

62 lines.append(f"{pad}{key}: {_scalar(value)}") 

63 return "\n".join(lines) 

64 

65 

66def _scalar(value: Any) -> str: 

67 return "" if value is None else str(value)