Skip to main content

urushi/render/
terminal.rs

1//! Lowering from Urushi text styles to backend-independent terminal styles.
2
3use urushi_terminal::TerminalStyle;
4
5use crate::{Hyperlink, TextStyle};
6
7/// A text style lowered to the terminal representation without discarding its hyperlink.
8///
9/// This conversion does not negotiate terminal capabilities. Callers rendering to a
10/// particular terminal should first resolve the [`TextStyle`] with
11/// [`RenderSettings`](crate::RenderSettings). Hyperlinks remain separate because they
12/// are scoped OSC 8 commands rather than SGR cell attributes.
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
14pub struct TerminalTextStyle<'a> {
15    style: TerminalStyle,
16    hyperlink: Option<&'a Hyperlink>,
17}
18
19impl<'a> TerminalTextStyle<'a> {
20    /// Returns the physical color, attribute, and underline state.
21    pub const fn style(self) -> TerminalStyle {
22        self.style
23    }
24
25    /// Returns the hyperlink that must surround the styled text, if any.
26    pub const fn hyperlink(self) -> Option<&'a Hyperlink> {
27        self.hyperlink
28    }
29}
30
31impl<'a> From<&'a TextStyle> for TerminalTextStyle<'a> {
32    fn from(value: &'a TextStyle) -> Self {
33        Self {
34            style: TerminalStyle {
35                foreground: value.get_foreground(),
36                background: value.get_background(),
37                attributes: value.get_attributes(),
38                underline: value.get_underline(),
39            },
40            hyperlink: value.get_hyperlink(),
41        }
42    }
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48    use crate::{Color, TextAttribute, Underline, UnderlineStyle};
49
50    #[test]
51    fn lowers_every_part_without_losing_the_hyperlink() {
52        let source = TextStyle::new()
53            .foreground(Color::Ansi(9))
54            .background(Color::Ansi256(200))
55            .add_attributes(TextAttribute::Bold | TextAttribute::Italic)
56            .underline_style(UnderlineStyle::Curly)
57            .underline_color(Color::Rgb(1, 2, 3))
58            .hyperlink(Hyperlink::new("https://example.com").parameter("id", "reference"));
59
60        let lowered = TerminalTextStyle::from(&source);
61
62        assert_eq!(
63            lowered.style(),
64            TerminalStyle {
65                foreground: Some(Color::Ansi(9)),
66                background: Some(Color::Ansi256(200)),
67                attributes: TextAttribute::Bold | TextAttribute::Italic,
68                underline: Some(Underline::new(UnderlineStyle::Curly).color(Color::Rgb(1, 2, 3)),),
69            }
70        );
71        assert_eq!(
72            lowered.hyperlink().map(Hyperlink::uri),
73            Some("https://example.com")
74        );
75    }
76}