yazi_parser/
step.rs

1use std::{num::ParseIntError, str::FromStr};
2
3use yazi_shared::data::Data;
4
5#[derive(Clone, Copy, Debug)]
6pub enum Step {
7	Top,
8	Bot,
9	Prev,
10	Next,
11	Offset(isize),
12	Percent(i8),
13}
14
15impl Default for Step {
16	fn default() -> Self { Self::Offset(0) }
17}
18
19impl From<isize> for Step {
20	fn from(n: isize) -> Self { Self::Offset(n) }
21}
22
23impl FromStr for Step {
24	type Err = ParseIntError;
25
26	fn from_str(s: &str) -> Result<Self, Self::Err> {
27		Ok(match s {
28			"top" => Self::Top,
29			"bot" => Self::Bot,
30			"prev" => Self::Prev,
31			"next" => Self::Next,
32			s if s.ends_with('%') => Self::Percent(s[..s.len() - 1].parse()?),
33			s => Self::Offset(s.parse()?),
34		})
35	}
36}
37
38impl TryFrom<&Data> for Step {
39	type Error = ParseIntError;
40
41	fn try_from(value: &Data) -> Result<Self, Self::Error> {
42		Ok(match value {
43			Data::Integer(i) => Self::from(*i as isize),
44			Data::String(s) => s.parse()?,
45			_ => "".parse()?,
46		})
47	}
48}
49
50impl Step {
51	pub fn add(self, pos: usize, len: usize, limit: usize) -> usize {
52		if len == 0 {
53			return 0;
54		}
55
56		let off = match self {
57			Self::Top => return 0,
58			Self::Bot => return len - 1,
59			Self::Prev => -1,
60			Self::Next => 1,
61			Self::Offset(n) => n,
62			Self::Percent(0) => 0,
63			Self::Percent(n) => n as isize * limit as isize / 100,
64		};
65
66		if matches!(self, Self::Prev | Self::Next) {
67			off.saturating_add_unsigned(pos).rem_euclid(len as _) as _
68		} else if off >= 0 {
69			pos.saturating_add_signed(off)
70		} else {
71			pos.saturating_sub(off.unsigned_abs())
72		}
73		.min(len - 1)
74	}
75}