reovim_plugin_notification/
progress.rs

1//! Progress notification types
2//!
3//! Defines progress bars and spinners for long-running operations.
4
5/// A progress notification
6#[derive(Debug, Clone)]
7pub struct ProgressNotification {
8    /// Unique identifier
9    pub id: String,
10    /// Title of the operation (e.g., "Building", "Indexing")
11    pub title: String,
12    /// Source of the progress (e.g., "cargo", "rust-analyzer")
13    pub source: String,
14    /// Progress percentage (0-100) or None for indeterminate spinner
15    pub progress: Option<u8>,
16    /// Optional detail text (e.g., "4/250 (core)")
17    pub detail: Option<String>,
18    /// Whether the operation is complete
19    pub complete: bool,
20}
21
22impl ProgressNotification {
23    /// Create a new progress notification
24    #[must_use]
25    pub fn new(id: impl Into<String>, title: impl Into<String>, source: impl Into<String>) -> Self {
26        Self {
27            id: id.into(),
28            title: title.into(),
29            source: source.into(),
30            progress: None,
31            detail: None,
32            complete: false,
33        }
34    }
35
36    /// Set the progress percentage
37    #[must_use]
38    pub fn with_progress(mut self, progress: u8) -> Self {
39        self.progress = Some(progress.min(100));
40        self
41    }
42
43    /// Set the detail text
44    #[must_use]
45    pub fn with_detail(mut self, detail: impl Into<String>) -> Self {
46        self.detail = Some(detail.into());
47        self
48    }
49
50    /// Mark as complete
51    #[must_use]
52    pub const fn completed(mut self) -> Self {
53        self.complete = true;
54        self.progress = Some(100);
55        self
56    }
57
58    /// Check if this is an indeterminate (spinner) progress
59    #[must_use]
60    pub const fn is_indeterminate(&self) -> bool {
61        self.progress.is_none()
62    }
63}
64
65/// Configuration for progress bar rendering
66#[derive(Debug, Clone)]
67pub struct ProgressBarConfig {
68    /// Width in characters
69    pub width: u16,
70    /// Character for filled portion
71    pub filled_char: char,
72    /// Character for empty portion
73    pub empty_char: char,
74}
75
76impl Default for ProgressBarConfig {
77    fn default() -> Self {
78        Self {
79            width: 20,
80            filled_char: '█',
81            empty_char: '░',
82        }
83    }
84}
85
86impl ProgressBarConfig {
87    /// Render a progress bar string
88    #[must_use]
89    #[allow(clippy::cast_possible_truncation)]
90    pub fn render(&self, progress: Option<u8>) -> String {
91        progress.map_or_else(
92            || "⣷⣯⣟⡿⢿⣻⣽⣾".chars().next().unwrap_or('◌').to_string(),
93            |pct| {
94                let filled = (u16::from(pct.min(100)) * self.width / 100) as usize;
95                let empty = (self.width as usize).saturating_sub(filled);
96                format!(
97                    "{}{}",
98                    self.filled_char.to_string().repeat(filled),
99                    self.empty_char.to_string().repeat(empty)
100                )
101            },
102        )
103    }
104}