summaryrefslogtreecommitdiffstats
path: root/httpie/output/ui/palette/rich.py
diff options
context:
space:
mode:
Diffstat (limited to 'httpie/output/ui/palette/rich.py')
-rw-r--r--httpie/output/ui/palette/rich.py111
1 files changed, 111 insertions, 0 deletions
diff --git a/httpie/output/ui/palette/rich.py b/httpie/output/ui/palette/rich.py
new file mode 100644
index 00000000..ecc1799f
--- /dev/null
+++ b/httpie/output/ui/palette/rich.py
@@ -0,0 +1,111 @@
+from collections import ChainMap
+from typing import TYPE_CHECKING, Any, Optional, List
+from enum import Enum, auto
+from dataclasses import dataclass, field
+
+if TYPE_CHECKING:
+ from rich.theme import Theme
+
+from httpie.output.ui.palette.pie import (
+ PIE_STYLE_TO_SHADE,
+ PieStyle,
+ PieColor,
+ get_color,
+) # noqa
+
+
+class RichTheme(Enum):
+ """Represents the color theme to use within rich."""
+ PIE = auto()
+ ANSI = auto()
+
+ @classmethod
+ def from_style_name(cls, style_name: str) -> 'RichTheme':
+ try:
+ PieStyle(style_name)
+ except ValueError:
+ return RichTheme.ANSI
+ else:
+ return RichTheme.PIE
+
+
+class RichColor(Enum):
+ """Generic colors that are safe to use everywhere within rich."""
+
+ # <https://rich.readthedocs.io/en/stable/appendix/colors.html>
+
+ WHITE = {RichTheme.PIE: PieColor.WHITE, RichTheme.ANSI: 'white'}
+ BLACK = {RichTheme.PIE: PieColor.BLACK, RichTheme.ANSI: 'black'}
+ GREEN = {RichTheme.PIE: PieColor.GREEN, RichTheme.ANSI: 'green'}
+ ORANGE = {RichTheme.PIE: PieColor.ORANGE, RichTheme.ANSI: 'yellow'}
+ YELLOW = {RichTheme.PIE: PieColor.YELLOW, RichTheme.ANSI: 'bright_yellow'}
+ BLUE = {RichTheme.PIE: PieColor.BLUE, RichTheme.ANSI: 'blue'}
+ PINK = {RichTheme.PIE: PieColor.PINK, RichTheme.ANSI: 'bright_magenta'}
+ PURPLE = {RichTheme.PIE: PieColor.PURPLE, RichTheme.ANSI: 'magenta'}
+ RED = {RichTheme.PIE: PieColor.RED, RichTheme.ANSI: 'red'}
+ AQUA = {RichTheme.PIE: PieColor.AQUA, RichTheme.ANSI: 'cyan'}
+ GREY = {RichTheme.PIE: PieColor.GREY, RichTheme.ANSI: 'bright_black'}
+
+ def apply_theme(
+ self, style: RichTheme, *, style_name: Optional[str] = None
+ ) -> str:
+ """Apply the given style to a particular value."""
+ exposed_color = self.value[style]
+ if style is RichTheme.PIE:
+ assert style_name is not None
+ shade = PIE_STYLE_TO_SHADE[PieStyle(style_name)]
+ return get_color(exposed_color, shade)
+ else:
+ return exposed_color
+
+
+@dataclass
+class _StyledRichColor:
+ color: RichColor
+ styles: List[str] = field(default_factory=list)
+
+
+class _RichColorCaster(dict):
+ """
+ Translate RichColor to a regular string on the attribute access
+ phase.
+ """
+
+ def _translate(self, key: Any) -> Any:
+ if isinstance(key, RichColor):
+ return key.name.lower()
+ else:
+ return key
+
+ def __getitem__(self, key: Any) -> Any:
+ return super().__getitem__(self._translate(key))
+
+ def get(self, key: Any) -> Any:
+ return super().get(self._translate(key))
+
+
+def make_rich_theme_from_style(style_name: Optional[str] = None) -> 'Theme':
+ from rich.style import Style
+ from rich.theme import Theme
+ from httpie.output.ui.palette.custom_styles import RICH_CUSTOM_STYLES
+
+ rich_theme = RichTheme.from_style_name(style_name)
+
+ theme = Theme()
+ for color, color_set in ChainMap(
+ RichColor.__members__, RICH_CUSTOM_STYLES
+ ).items():
+ if isinstance(color_set, _StyledRichColor):
+ properties = dict.fromkeys(color_set.styles, True)
+ color_set = color_set.color
+ else:
+ properties = {}
+
+ theme.styles[color.lower()] = Style(
+ color=color_set.apply_theme(rich_theme, style_name=style_name),
+ **properties,
+ )
+
+ # E.g translate RichColor.BLUE into blue on key access
+ theme.styles = _RichColorCaster(theme.styles)
+ return theme