Coverage for little_loops / config / cli.py: 99%
70 statements
« prev ^ index » next coverage.py v7.12.0, created at 2026-04-11 23:20 -0500
« prev ^ index » next coverage.py v7.12.0, created at 2026-04-11 23:20 -0500
1"""CLI presentation configuration dataclasses.
3Covers ANSI color overrides for log levels, priority labels, type labels,
4and general CLI display options.
5"""
7from __future__ import annotations
9from dataclasses import dataclass, field
10from typing import Any
13@dataclass
14class CliColorsLoggerConfig:
15 """ANSI color overrides for Logger log-level output."""
17 info: str = "36"
18 success: str = "32"
19 warning: str = "33"
20 error: str = "38;5;208"
22 @classmethod
23 def from_dict(cls, data: dict[str, Any]) -> CliColorsLoggerConfig:
24 """Create CliColorsLoggerConfig from dictionary."""
25 return cls(
26 info=data.get("info", "36"),
27 success=data.get("success", "32"),
28 warning=data.get("warning", "33"),
29 error=data.get("error", "38;5;208"),
30 )
33@dataclass
34class CliColorsPriorityConfig:
35 """ANSI color overrides for issue priority labels (P0–P5)."""
37 P0: str = "38;5;208;1"
38 P1: str = "38;5;208"
39 P2: str = "33"
40 P3: str = "0"
41 P4: str = "2"
42 P5: str = "2"
44 @classmethod
45 def from_dict(cls, data: dict[str, Any]) -> CliColorsPriorityConfig:
46 """Create CliColorsPriorityConfig from dictionary."""
47 return cls(
48 P0=data.get("P0", "38;5;208;1"),
49 P1=data.get("P1", "38;5;208"),
50 P2=data.get("P2", "33"),
51 P3=data.get("P3", "0"),
52 P4=data.get("P4", "2"),
53 P5=data.get("P5", "2"),
54 )
57@dataclass
58class CliColorsTypeConfig:
59 """ANSI color overrides for issue type labels (BUG, FEAT, ENH)."""
61 BUG: str = "38;5;208"
62 FEAT: str = "32"
63 ENH: str = "34"
65 @classmethod
66 def from_dict(cls, data: dict[str, Any]) -> CliColorsTypeConfig:
67 """Create CliColorsTypeConfig from dictionary."""
68 return cls(
69 BUG=data.get("BUG", "38;5;208"),
70 FEAT=data.get("FEAT", "32"),
71 ENH=data.get("ENH", "34"),
72 )
75@dataclass
76class CliColorsEdgeLabelsConfig:
77 """ANSI color overrides for FSM transition edge labels in loop diagrams."""
79 yes: str = "32"
80 no: str = "38;5;208"
81 error: str = "31"
82 partial: str = "33"
83 next: str = "2"
84 default: str = "2"
85 blocked: str = "31"
86 retry_exhausted: str = "38;5;208"
88 @classmethod
89 def from_dict(cls, data: dict[str, Any]) -> CliColorsEdgeLabelsConfig:
90 """Create CliColorsEdgeLabelsConfig from dictionary."""
91 return cls(
92 yes=data.get("yes", "32"),
93 no=data.get("no", "38;5;208"),
94 error=data.get("error", "31"),
95 partial=data.get("partial", "33"),
96 next=data.get("next", "2"),
97 default=data.get("default", "2"),
98 blocked=data.get("blocked", "31"),
99 retry_exhausted=data.get("retry_exhausted", "38;5;208"),
100 )
102 def to_dict(self) -> dict[str, str]:
103 """Convert to a label→SGR-code dict for use by _colorize_diagram_labels.
105 Maps 'default' back to '_' to match the key used in _EDGE_LABEL_COLORS.
106 """
107 return {
108 "yes": self.yes,
109 "no": self.no,
110 "error": self.error,
111 "partial": self.partial,
112 "next": self.next,
113 "_": self.default,
114 "blocked": self.blocked,
115 "retry_exhausted": self.retry_exhausted,
116 }
119@dataclass
120class CliColorsConfig:
121 """ANSI color overrides for logger levels, priority labels, and type labels."""
123 logger: CliColorsLoggerConfig = field(default_factory=CliColorsLoggerConfig)
124 priority: CliColorsPriorityConfig = field(default_factory=CliColorsPriorityConfig)
125 type: CliColorsTypeConfig = field(default_factory=CliColorsTypeConfig)
126 fsm_active_state: str = "32"
127 fsm_edge_labels: CliColorsEdgeLabelsConfig = field(default_factory=CliColorsEdgeLabelsConfig)
129 @classmethod
130 def from_dict(cls, data: dict[str, Any]) -> CliColorsConfig:
131 """Create CliColorsConfig from dictionary."""
132 return cls(
133 logger=CliColorsLoggerConfig.from_dict(data.get("logger", {})),
134 priority=CliColorsPriorityConfig.from_dict(data.get("priority", {})),
135 type=CliColorsTypeConfig.from_dict(data.get("type", {})),
136 fsm_active_state=data.get("fsm_active_state", "32"),
137 fsm_edge_labels=CliColorsEdgeLabelsConfig.from_dict(data.get("fsm_edge_labels", {})),
138 )
141@dataclass
142class RefineStatusConfig:
143 """refine-status display configuration."""
145 columns: list[str] = field(default_factory=list)
146 elide_order: list[str] = field(default_factory=list)
148 @classmethod
149 def from_dict(cls, data: dict[str, Any]) -> RefineStatusConfig:
150 """Create RefineStatusConfig from dictionary."""
151 return cls(
152 columns=data.get("columns", []),
153 elide_order=data.get("elide_order", []),
154 )
157@dataclass
158class CliConfig:
159 """CLI output configuration."""
161 color: bool = True
162 colors: CliColorsConfig = field(default_factory=CliColorsConfig)
164 @classmethod
165 def from_dict(cls, data: dict[str, Any]) -> CliConfig:
166 """Create CliConfig from dictionary."""
167 return cls(
168 color=data.get("color", True),
169 colors=CliColorsConfig.from_dict(data.get("colors", {})),
170 )