Coverage for little_loops / config / cli.py: 100%
72 statements
« prev ^ index » next coverage.py v7.12.0, created at 2026-05-22 16:19 -0500
« prev ^ index » next coverage.py v7.12.0, created at 2026-05-22 16:19 -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, EPIC)."""
61 BUG: str = "38;5;208"
62 FEAT: str = "32"
63 ENH: str = "34"
64 EPIC: str = "35"
66 @classmethod
67 def from_dict(cls, data: dict[str, Any]) -> CliColorsTypeConfig:
68 """Create CliColorsTypeConfig from dictionary."""
69 return cls(
70 BUG=data.get("BUG", "38;5;208"),
71 FEAT=data.get("FEAT", "32"),
72 ENH=data.get("ENH", "34"),
73 EPIC=data.get("EPIC", "35"),
74 )
77@dataclass
78class CliColorsEdgeLabelsConfig:
79 """ANSI color overrides for FSM transition edge labels in loop diagrams."""
81 yes: str = "32"
82 no: str = "38;5;208"
83 error: str = "31"
84 partial: str = "33"
85 next: str = "2"
86 default: str = "2"
87 blocked: str = "31"
88 retry_exhausted: str = "38;5;208"
89 rate_limit_exhausted: str = "38;5;214"
91 @classmethod
92 def from_dict(cls, data: dict[str, Any]) -> CliColorsEdgeLabelsConfig:
93 """Create CliColorsEdgeLabelsConfig from dictionary."""
94 return cls(
95 yes=data.get("yes", "32"),
96 no=data.get("no", "38;5;208"),
97 error=data.get("error", "31"),
98 partial=data.get("partial", "33"),
99 next=data.get("next", "2"),
100 default=data.get("default", "2"),
101 blocked=data.get("blocked", "31"),
102 retry_exhausted=data.get("retry_exhausted", "38;5;208"),
103 rate_limit_exhausted=data.get("rate_limit_exhausted", "38;5;214"),
104 )
106 def to_dict(self) -> dict[str, str]:
107 """Convert to a label→SGR-code dict for use by _colorize_diagram_labels.
109 Maps 'default' back to '_' to match the key used in _EDGE_LABEL_COLORS.
110 """
111 return {
112 "yes": self.yes,
113 "no": self.no,
114 "error": self.error,
115 "partial": self.partial,
116 "next": self.next,
117 "_": self.default,
118 "blocked": self.blocked,
119 "retry_exhausted": self.retry_exhausted,
120 "rate_limit_exhausted": self.rate_limit_exhausted,
121 }
124@dataclass
125class CliColorsConfig:
126 """ANSI color overrides for logger levels, priority labels, and type labels."""
128 logger: CliColorsLoggerConfig = field(default_factory=CliColorsLoggerConfig)
129 priority: CliColorsPriorityConfig = field(default_factory=CliColorsPriorityConfig)
130 type: CliColorsTypeConfig = field(default_factory=CliColorsTypeConfig)
131 fsm_active_state: str = "32"
132 fsm_edge_labels: CliColorsEdgeLabelsConfig = field(default_factory=CliColorsEdgeLabelsConfig)
134 @classmethod
135 def from_dict(cls, data: dict[str, Any]) -> CliColorsConfig:
136 """Create CliColorsConfig from dictionary."""
137 return cls(
138 logger=CliColorsLoggerConfig.from_dict(data.get("logger", {})),
139 priority=CliColorsPriorityConfig.from_dict(data.get("priority", {})),
140 type=CliColorsTypeConfig.from_dict(data.get("type", {})),
141 fsm_active_state=data.get("fsm_active_state", "32"),
142 fsm_edge_labels=CliColorsEdgeLabelsConfig.from_dict(data.get("fsm_edge_labels", {})),
143 )
146@dataclass
147class RefineStatusConfig:
148 """refine-status display configuration."""
150 columns: list[str] = field(default_factory=list)
151 elide_order: list[str] = field(default_factory=list)
153 @classmethod
154 def from_dict(cls, data: dict[str, Any]) -> RefineStatusConfig:
155 """Create RefineStatusConfig from dictionary."""
156 return cls(
157 columns=data.get("columns", []),
158 elide_order=data.get("elide_order", []),
159 )
162@dataclass
163class CliConfig:
164 """CLI output configuration."""
166 color: bool = True
167 colors: CliColorsConfig = field(default_factory=CliColorsConfig)
169 @classmethod
170 def from_dict(cls, data: dict[str, Any]) -> CliConfig:
171 """Create CliConfig from dictionary."""
172 return cls(
173 color=data.get("color", True),
174 colors=CliColorsConfig.from_dict(data.get("colors", {})),
175 )