1#[derive(Debug, Clone)]
3pub struct RichLogState {
4 pub entries: Vec<RichLogEntry>,
6 pub(crate) scroll_offset: usize,
8 pub auto_scroll: bool,
10 pub max_entries: Option<usize>,
12}
13
14#[derive(Debug, Clone)]
16pub struct RichLogEntry {
17 pub segments: Vec<(String, Style)>,
19}
20
21impl RichLogState {
22 pub const DEFAULT_MAX_ENTRIES: usize = 10_000;
28
29 pub fn new() -> Self {
32 Self {
33 max_entries: Some(Self::DEFAULT_MAX_ENTRIES),
34 ..Self::new_unbounded()
35 }
36 }
37
38 pub fn new_unbounded() -> Self {
43 Self {
44 entries: Vec::new(),
45 scroll_offset: 0,
46 auto_scroll: true,
47 max_entries: None,
48 }
49 }
50
51 pub fn push(&mut self, text: impl Into<String>, style: Style) {
53 self.push_segments(vec![(text.into(), style)]);
54 }
55
56 pub fn push_plain(&mut self, text: impl Into<String>) {
58 self.push(text, Style::new());
59 }
60
61 pub fn push_segments(&mut self, segments: Vec<(String, Style)>) {
63 self.entries.push(RichLogEntry { segments });
64
65 if let Some(max_entries) = self.max_entries {
66 if self.entries.len() > max_entries {
67 let remove_count = self.entries.len() - max_entries;
68 self.entries.drain(0..remove_count);
69 self.scroll_offset = self.scroll_offset.saturating_sub(remove_count);
70 }
71 }
72
73 if self.auto_scroll {
74 self.scroll_offset = usize::MAX;
75 }
76 }
77
78 pub fn clear(&mut self) {
80 self.entries.clear();
81 self.scroll_offset = 0;
82 }
83
84 pub fn len(&self) -> usize {
86 self.entries.len()
87 }
88
89 pub fn is_empty(&self) -> bool {
91 self.entries.is_empty()
92 }
93}
94
95impl Default for RichLogState {
96 fn default() -> Self {
97 Self::new()
98 }
99}
100
101#[derive(Debug, Clone)]
103pub struct CalendarState {
104 pub year: i32,
106 pub month: u32,
108 pub selected_day: Option<u32>,
110 pub(crate) cursor_day: u32,
111}
112
113impl CalendarState {
114 pub fn new() -> Self {
116 let (year, month) = Self::current_year_month();
117 Self::from_ym(year, month)
118 }
119
120 pub fn from_ym(year: i32, month: u32) -> Self {
122 let month = month.clamp(1, 12);
123 Self {
124 year,
125 month,
126 selected_day: None,
127 cursor_day: 1,
128 }
129 }
130
131 pub fn selected_date(&self) -> Option<(i32, u32, u32)> {
133 self.selected_day.map(|day| (self.year, self.month, day))
134 }
135
136 pub fn prev_month(&mut self) {
138 if self.month == 1 {
139 self.month = 12;
140 self.year -= 1;
141 } else {
142 self.month -= 1;
143 }
144 self.clamp_days();
145 }
146
147 pub fn next_month(&mut self) {
149 if self.month == 12 {
150 self.month = 1;
151 self.year += 1;
152 } else {
153 self.month += 1;
154 }
155 self.clamp_days();
156 }
157
158 pub(crate) fn days_in_month(year: i32, month: u32) -> u32 {
159 match month {
160 1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
161 4 | 6 | 9 | 11 => 30,
162 2 => {
163 if Self::is_leap_year(year) {
164 29
165 } else {
166 28
167 }
168 }
169 _ => 30,
170 }
171 }
172
173 pub(crate) fn first_weekday(year: i32, month: u32) -> u32 {
174 let month = month.clamp(1, 12);
175 let offsets = [0_i32, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4];
176 let mut y = year;
177 if month < 3 {
178 y -= 1;
179 }
180 let sunday_based = (y + y / 4 - y / 100 + y / 400 + offsets[(month - 1) as usize] + 1) % 7;
181 ((sunday_based + 6) % 7) as u32
182 }
183
184 fn clamp_days(&mut self) {
185 let max_day = Self::days_in_month(self.year, self.month);
186 self.cursor_day = self.cursor_day.clamp(1, max_day);
187 if let Some(day) = self.selected_day {
188 self.selected_day = Some(day.min(max_day));
189 }
190 }
191
192 fn is_leap_year(year: i32) -> bool {
193 (year % 4 == 0 && year % 100 != 0) || year % 400 == 0
194 }
195
196 fn current_year_month() -> (i32, u32) {
197 let Ok(duration) = SystemTime::now().duration_since(UNIX_EPOCH) else {
198 return (1970, 1);
199 };
200 let days_since_epoch = (duration.as_secs() / 86_400) as i64;
201 let (year, month, _) = Self::civil_from_days(days_since_epoch);
202 (year, month)
203 }
204
205 fn civil_from_days(days_since_epoch: i64) -> (i32, u32, u32) {
206 let z = days_since_epoch + 719_468;
207 let era = if z >= 0 { z } else { z - 146_096 } / 146_097;
208 let doe = z - era * 146_097;
209 let yoe = (doe - doe / 1_460 + doe / 36_524 - doe / 146_096) / 365;
210 let mut year = (yoe as i32) + (era as i32) * 400;
211 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
212 let mp = (5 * doy + 2) / 153;
213 let day = (doy - (153 * mp + 2) / 5 + 1) as u32;
214 let month = (mp + if mp < 10 { 3 } else { -9 }) as u32;
215 if month <= 2 {
216 year += 1;
217 }
218 (year, month, day)
219 }
220}
221
222impl Default for CalendarState {
223 fn default() -> Self {
224 Self::new()
225 }
226}
227
228#[non_exhaustive]
238#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
239pub enum ButtonVariant {
240 #[default]
242 Default,
243 Primary,
245 Danger,
247 Outline,
249}
250
251#[non_exhaustive]
253#[derive(Debug, Clone, Copy, PartialEq, Eq)]
254pub enum Trend {
255 Up,
257 Down,
259}
260
261