blz_cli/args/
pagination.rs1use clap::Args;
20use serde::{Deserialize, Serialize};
21
22fn validate_limit(s: &str) -> Result<usize, String> {
24 let value: usize = s
25 .parse()
26 .map_err(|_| format!("'{s}' is not a valid number"))?;
27 if value == 0 {
28 Err("limit must be at least 1".to_string())
29 } else {
30 Ok(value)
31 }
32}
33
34#[derive(Args, Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
60pub struct PaginationArgs {
61 #[arg(
65 short = 'l',
66 long,
67 value_name = "COUNT",
68 value_parser = validate_limit,
69 display_order = 50
70 )]
71 pub limit: Option<usize>,
72
73 #[arg(long, value_name = "COUNT", display_order = 51)]
77 pub offset: Option<usize>,
78}
79
80impl PaginationArgs {
81 #[must_use]
83 pub const fn with_limit(limit: usize) -> Self {
84 Self {
85 limit: Some(limit),
86 offset: None,
87 }
88 }
89
90 #[must_use]
92 pub const fn with_limit_and_offset(limit: usize, offset: usize) -> Self {
93 Self {
94 limit: Some(limit),
95 offset: Some(offset),
96 }
97 }
98
99 #[must_use]
101 pub const fn limit_or(&self, default: usize) -> usize {
102 match self.limit {
103 Some(limit) => limit,
104 None => default,
105 }
106 }
107
108 #[must_use]
110 pub const fn offset_or_default(&self) -> usize {
111 match self.offset {
112 Some(offset) => offset,
113 None => 0,
114 }
115 }
116}
117
118#[cfg(test)]
119#[allow(clippy::unwrap_used)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn test_validate_limit_valid() {
125 assert_eq!(validate_limit("1"), Ok(1));
126 assert_eq!(validate_limit("100"), Ok(100));
127 assert_eq!(validate_limit("999999"), Ok(999_999));
128 }
129
130 #[test]
131 fn test_validate_limit_zero() {
132 assert!(validate_limit("0").is_err());
133 assert!(validate_limit("0").unwrap_err().contains("at least 1"));
134 }
135
136 #[test]
137 fn test_validate_limit_invalid() {
138 assert!(validate_limit("abc").is_err());
139 assert!(validate_limit("-1").is_err());
140 assert!(validate_limit("1.5").is_err());
141 }
142
143 #[test]
144 fn test_default() {
145 let args = PaginationArgs::default();
146 assert_eq!(args.limit, None);
147 assert_eq!(args.offset, None);
148 }
149
150 #[test]
151 fn test_with_limit() {
152 let args = PaginationArgs::with_limit(10);
153 assert_eq!(args.limit, Some(10));
154 assert_eq!(args.offset, None);
155 }
156
157 #[test]
158 fn test_with_limit_and_offset() {
159 let args = PaginationArgs::with_limit_and_offset(10, 20);
160 assert_eq!(args.limit, Some(10));
161 assert_eq!(args.offset, Some(20));
162 }
163
164 #[test]
165 fn test_limit_or() {
166 let args = PaginationArgs::default();
167 assert_eq!(args.limit_or(50), 50);
168
169 let args = PaginationArgs::with_limit(10);
170 assert_eq!(args.limit_or(50), 10);
171 }
172
173 #[test]
174 fn test_offset_or_default() {
175 let args = PaginationArgs::default();
176 assert_eq!(args.offset_or_default(), 0);
177
178 let args = PaginationArgs::with_limit_and_offset(10, 20);
179 assert_eq!(args.offset_or_default(), 20);
180 }
181}