1use crate::console::{Console, ConsoleOptions};
11use crate::protocol::Renderable;
12use crate::segment::Segment;
13use crate::spinner::Spinner;
14use crate::style::StyleType;
15
16pub struct Status {
18 status: String,
19 spinner_style: StyleType,
20 speed: f64,
21 spinner: Spinner,
22}
23
24impl Status {
25 pub fn new(message: impl Into<String>) -> Self {
28 let status = message.into();
29 let spinner_style = StyleType::Name("status.spinner".to_string());
30 Status {
31 spinner: Spinner::new("dots")
32 .text(status.clone())
33 .style(spinner_style.clone()),
34 status,
35 spinner_style,
36 speed: 1.0,
37 }
38 }
39
40 fn rebuild(mut self, name: &str) -> Self {
41 self.spinner = Spinner::new(name)
42 .text(self.status.clone())
43 .style(self.spinner_style.clone())
44 .speed(self.speed);
45 self
46 }
47
48 pub fn spinner(self, name: &str) -> Self {
50 self.rebuild(name)
51 }
52
53 pub fn spinner_style(mut self, style: impl Into<StyleType>) -> Self {
55 self.spinner_style = style.into();
56 self.spinner = self.spinner.style(self.spinner_style.clone());
57 self
58 }
59
60 pub fn speed(mut self, speed: f64) -> Self {
62 self.speed = speed;
63 self.spinner = self.spinner.speed(speed);
64 self
65 }
66
67 pub fn update(
72 &mut self,
73 status: Option<&str>,
74 spinner: Option<&str>,
75 spinner_style: Option<StyleType>,
76 speed: Option<f64>,
77 ) {
78 if let Some(status) = status {
79 self.status = status.to_string();
80 }
81 if let Some(style) = spinner_style {
82 self.spinner_style = style;
83 }
84 if let Some(speed) = speed.filter(|s| *s != 0.0) {
85 self.speed = speed;
86 }
87 if let Some(name) = spinner {
88 self.spinner = Spinner::new(name)
89 .text(self.status.clone())
90 .style(self.spinner_style.clone())
91 .speed(self.speed);
92 } else {
93 self.spinner.update(
94 Some(&self.status),
95 Some(self.spinner_style.clone()),
96 Some(self.speed),
97 );
98 }
99 }
100
101 pub fn renderable(&self) -> &Spinner {
103 &self.spinner
104 }
105}
106
107impl Renderable for Status {
108 fn rich_render(&self, console: &Console, options: &ConsoleOptions) -> Vec<Segment> {
109 self.spinner.rich_render(console, options)
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117 use crate::color::ColorSystem;
118
119 fn render(status: &Status) -> String {
120 Console::builder()
121 .force_terminal(true)
122 .color_system(Some(ColorSystem::Truecolor))
123 .width(30)
124 .no_color(false)
125 .build()
126 .render_to_string(status)
127 }
128
129 #[test]
130 fn default_status_frame() {
131 assert_eq!(
133 render(&Status::new("Loading data")),
134 "\x1b[32m⠋\x1b[0m Loading data"
135 );
136 }
137
138 #[test]
139 fn custom_spinner() {
140 assert_eq!(
141 render(&Status::new("Building").spinner("line")),
142 "\x1b[32m-\x1b[0m Building"
143 );
144 }
145}