Skip to main content

pito_tui/
label.rs

1use std::borrow::Cow;
2
3use pito_header::{Group, Section};
4use ratatui::style::Style;
5
6pub type Span = (Cow<'static, str>, Style);
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct Label {
10    name: String,
11    spans: Vec<Span>,
12    short: Option<Vec<Span>>,
13    section: Option<Section>,
14}
15
16fn owned<T: Into<Cow<'static, str>>>(spans: impl IntoIterator<Item = (T, Style)>) -> Vec<Span> {
17    spans
18        .into_iter()
19        .map(|(text, style)| (text.into(), style))
20        .collect()
21}
22
23fn joined(spans: &[Span]) -> String {
24    spans.iter().map(|(text, _)| text.as_ref()).collect()
25}
26
27fn plain(spans: &[Span]) -> bool {
28    spans.iter().all(|(_, style)| *style == Style::new())
29}
30
31fn borrowed(spans: &[Span]) -> Vec<(&str, Style)> {
32    spans
33        .iter()
34        .map(|(text, style)| (text.as_ref(), *style))
35        .collect()
36}
37
38impl Label {
39    pub fn new(text: impl Into<Cow<'static, str>>) -> Self {
40        let text = text.into();
41        Label {
42            name: text.to_string(),
43            spans: vec![(text, Style::new())],
44            short: None,
45            section: None,
46        }
47    }
48
49    pub fn spans<T: Into<Cow<'static, str>>>(spans: impl IntoIterator<Item = (T, Style)>) -> Self {
50        let spans = owned(spans);
51        Label {
52            name: joined(&spans),
53            spans,
54            short: None,
55            section: None,
56        }
57    }
58
59    pub fn short(mut self, text: impl Into<Cow<'static, str>>) -> Self {
60        self.short = Some(vec![(text.into(), Style::new())]);
61        self.section = None;
62        self
63    }
64
65    pub fn short_spans<T: Into<Cow<'static, str>>>(
66        mut self,
67        spans: impl IntoIterator<Item = (T, Style)>,
68    ) -> Self {
69        self.short = Some(owned(spans));
70        self.section = None;
71        self
72    }
73
74    pub fn text(&self) -> &str {
75        &self.name
76    }
77
78    pub fn short_text(&self) -> String {
79        joined(self.short_spans_or_full())
80    }
81
82    pub(crate) fn full(&self) -> &[Span] {
83        &self.spans
84    }
85
86    pub(crate) fn short_spans_or_full(&self) -> &[Span] {
87        self.short.as_deref().unwrap_or(&self.spans)
88    }
89
90    pub(crate) fn section(&self) -> Section {
91        if let Some(section) = &self.section {
92            return section.clone();
93        }
94        let section = if plain(&self.spans) {
95            Section::new(self.name.clone())
96        } else {
97            Section::spans(&borrowed(&self.spans))
98        };
99        match &self.short {
100            Some(short) if plain(short) => section.short(joined(short)),
101            Some(short) => section.short_spans(&borrowed(short)),
102            None => section,
103        }
104    }
105
106    pub(crate) fn group(&self) -> Group {
107        let group = Group::new(self.name.clone());
108        match &self.short {
109            Some(short) => group.short(joined(short)),
110            None => group,
111        }
112    }
113}
114
115impl From<&'static str> for Label {
116    fn from(text: &'static str) -> Self {
117        Label::new(text)
118    }
119}
120
121impl From<String> for Label {
122    fn from(text: String) -> Self {
123        Label::new(text)
124    }
125}
126
127impl From<Cow<'static, str>> for Label {
128    fn from(text: Cow<'static, str>) -> Self {
129        Label::new(text)
130    }
131}
132
133impl From<Section> for Label {
134    fn from(section: Section) -> Self {
135        let name = section.name().to_string();
136        let short = section.short_name().to_string();
137        let mut label = Label::new(name.clone());
138        if short != name {
139            label = label.short(short);
140        }
141        label.section = Some(section);
142        label
143    }
144}
145
146impl From<Group> for Label {
147    fn from(group: Group) -> Self {
148        let name = group.name().to_string();
149        let short = group.short_name().to_string();
150        let label = Label::new(name.clone());
151        if short != name {
152            label.short(short)
153        } else {
154            label
155        }
156    }
157}