Skip to main content

easypdf_core/
page_number.rs

1//! One-based PDF page number.
2
3use crate::{PageIndex, PdfError, Result};
4
5/// PDF 页的一基显示编号,适用于 PDF 引擎及用户展示。
6#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
7pub struct PageNumber(usize);
8
9impl PageNumber {
10    /// 创建一基页码。
11    ///
12    /// # Errors
13    ///
14    /// 当 `value` 为零时返回 [`PdfError::InvalidPage`]。
15    pub const fn new(value: usize) -> Result<Self> {
16        if value == 0 {
17            return Err(PdfError::InvalidPage(0));
18        }
19        Ok(Self(value))
20    }
21
22    /// 从零基页索引创建页码。
23    #[must_use]
24    pub const fn from_index(index: PageIndex) -> Self {
25        Self(index.value() + 1)
26    }
27
28    /// 返回底层一基页码。
29    #[must_use]
30    pub const fn value(self) -> usize {
31        self.0
32    }
33
34    /// 转换为零基页索引。
35    #[must_use]
36    pub const fn index(self) -> PageIndex {
37        PageIndex::new(self.0 - 1)
38    }
39}
40
41#[cfg(test)]
42#[allow(clippy::uninlined_format_args, clippy::float_cmp)]
43mod tests {
44    use super::*;
45
46    #[test]
47    fn new_valid_value() {
48        let pn = PageNumber::new(1).unwrap();
49        assert_eq!(pn.value(), 1);
50    }
51
52    #[test]
53    fn new_large_value() {
54        let pn = PageNumber::new(1000).unwrap();
55        assert_eq!(pn.value(), 1000);
56    }
57
58    #[test]
59    fn new_zero_returns_error() {
60        let result = PageNumber::new(0);
61        assert!(result.is_err());
62        let err = result.unwrap_err();
63        assert!(matches!(err, PdfError::InvalidPage(0)));
64    }
65
66    #[test]
67    fn from_index_converts_correctly() {
68        let idx = PageIndex::new(0);
69        let pn = PageNumber::from_index(idx);
70        assert_eq!(pn.value(), 1);
71
72        let idx = PageIndex::new(4);
73        let pn = PageNumber::from_index(idx);
74        assert_eq!(pn.value(), 5);
75    }
76
77    #[test]
78    fn value_returns_inner() {
79        let pn = PageNumber::new(42).unwrap();
80        assert_eq!(pn.value(), 42);
81    }
82
83    #[test]
84    fn index_converts_to_zero_based() {
85        let pn = PageNumber::new(1).unwrap();
86        assert_eq!(pn.index().value(), 0);
87
88        let pn = PageNumber::new(5).unwrap();
89        assert_eq!(pn.index().value(), 4);
90    }
91
92    #[test]
93    fn roundtrip_page_number_to_index_and_back() {
94        let pn = PageNumber::new(10).unwrap();
95        let idx = pn.index();
96        let pn2 = PageNumber::from_index(idx);
97        assert_eq!(pn, pn2);
98    }
99
100    #[test]
101    fn clone_and_copy() {
102        let pn = PageNumber::new(3).unwrap();
103        let cloned = pn;
104        assert_eq!(pn, cloned);
105    }
106
107    #[test]
108    fn debug_format() {
109        let pn = PageNumber::new(7).unwrap();
110        assert_eq!(format!("{:?}", pn), "PageNumber(7)");
111    }
112
113    #[test]
114    fn ordering() {
115        let a = PageNumber::new(1).unwrap();
116        let b = PageNumber::new(5).unwrap();
117        assert!(a < b);
118        assert!(b > a);
119    }
120
121    #[test]
122    fn equality() {
123        let a = PageNumber::new(3).unwrap();
124        let b = PageNumber::new(3).unwrap();
125        let c = PageNumber::new(4).unwrap();
126        assert_eq!(a, b);
127        assert_ne!(a, c);
128    }
129
130    #[test]
131    fn hash_same_values() {
132        use std::collections::HashSet;
133        let mut set = HashSet::new();
134        set.insert(PageNumber::new(1).unwrap());
135        set.insert(PageNumber::new(1).unwrap());
136        set.insert(PageNumber::new(2).unwrap());
137        assert_eq!(set.len(), 2);
138    }
139}