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}