easypdf_core/
page_range.rs1use std::ops::Range;
4
5use crate::{PdfError, Result};
6
7#[derive(Clone, Debug, PartialEq, Eq, Hash)]
9pub struct PageRange(Range<usize>);
10
11impl PageRange {
12 pub fn new(range: Range<usize>) -> Result<Self> {
18 if range.end < range.start {
19 return Err(PdfError::InvalidPage(range.end));
20 }
21 Ok(Self(range))
22 }
23
24 #[must_use]
26 pub const fn empty_at(index: usize) -> Self {
27 Self(index..index)
28 }
29
30 #[must_use]
32 pub const fn start(&self) -> usize {
33 self.0.start
34 }
35
36 #[must_use]
38 pub const fn end(&self) -> usize {
39 self.0.end
40 }
41
42 #[must_use]
44 pub fn contains(&self, index: usize) -> bool {
45 self.0.contains(&index)
46 }
47
48 #[must_use]
50 pub const fn as_range(&self) -> &Range<usize> {
51 &self.0
52 }
53}
54
55impl TryFrom<Range<usize>> for PageRange {
56 type Error = PdfError;
57
58 fn try_from(value: Range<usize>) -> Result<Self> {
59 Self::new(value)
60 }
61}
62
63#[cfg(test)]
64#[allow(
65 clippy::uninlined_format_args,
66 clippy::float_cmp,
67 clippy::reversed_empty_ranges
68)]
69mod tests {
70 use super::*;
71
72 #[test]
73 fn new_valid_range() {
74 let pr = PageRange::new(0..10).unwrap();
75 assert_eq!(pr.start(), 0);
76 assert_eq!(pr.end(), 10);
77 }
78
79 #[test]
80 fn new_empty_range() {
81 let pr = PageRange::new(5..5).unwrap();
82 assert_eq!(pr.start(), 5);
83 assert_eq!(pr.end(), 5);
84 }
85
86 #[test]
87 fn new_reversed_range_returns_error() {
88 let result = PageRange::new(10..5);
89 assert!(result.is_err());
90 let err = result.unwrap_err();
91 assert!(matches!(err, PdfError::InvalidPage(5)));
92 }
93
94 #[test]
95 fn empty_at_creates_empty_range() {
96 let pr = PageRange::empty_at(7);
97 assert_eq!(pr.start(), 7);
98 assert_eq!(pr.end(), 7);
99 assert_eq!(pr.start(), pr.end());
101 }
102
103 #[test]
104 fn contains_within_range() {
105 let pr = PageRange::new(2..8).unwrap();
106 assert!(!pr.contains(1));
107 assert!(pr.contains(2));
108 assert!(pr.contains(5));
109 assert!(!pr.contains(8));
110 }
111
112 #[test]
113 fn contains_empty_range() {
114 let pr = PageRange::empty_at(3);
115 assert!(!pr.contains(3));
116 }
117
118 #[test]
119 fn as_range_returns_inner() {
120 let pr = PageRange::new(1..5).unwrap();
121 let range = pr.as_range();
122 assert_eq!(*range, 1..5);
123 }
124
125 #[test]
126 fn try_from_valid_range() {
127 let pr: PageRange = (0..3).try_into().unwrap();
128 assert_eq!(pr.start(), 0);
129 assert_eq!(pr.end(), 3);
130 }
131
132 #[test]
133 fn try_from_invalid_range() {
134 let result: Result<PageRange> = (5..2).try_into();
135 assert!(result.is_err());
136 }
137
138 #[test]
139 fn clone_preserves_values() {
140 let pr = PageRange::new(1..10).unwrap();
141 let cloned = pr.clone();
142 assert_eq!(pr, cloned);
143 }
144
145 #[test]
146 fn partial_eq_works() {
147 let a = PageRange::new(0..5).unwrap();
148 let b = PageRange::new(0..5).unwrap();
149 let c = PageRange::new(0..6).unwrap();
150 assert_eq!(a, b);
151 assert_ne!(a, c);
152 }
153
154 #[test]
155 fn debug_format() {
156 let pr = PageRange::new(2..8).unwrap();
157 let dbg = format!("{:?}", pr);
158 assert!(dbg.contains('2'));
159 assert!(dbg.contains('8'));
160 }
161
162 #[test]
163 fn hash_same_values() {
164 use std::collections::HashSet;
165 let mut set = HashSet::new();
166 set.insert(PageRange::new(0..5).unwrap());
167 set.insert(PageRange::new(0..5).unwrap());
168 set.insert(PageRange::new(0..6).unwrap());
169 assert_eq!(set.len(), 2);
170 }
171}