Skip to main content

easypdf_core/model/
list_item.rs

1//! PDF 列表项,支持嵌套。
2
3/// 列表中的单个条目,支持递归嵌套子列表。
4///
5/// # Examples
6///
7/// ```
8/// use easypdf_core::ListItem;
9///
10/// let item = ListItem::new("Top level");
11/// assert_eq!(item.text(), "Top level");
12/// assert_eq!(item.level(), 0);
13/// assert!(item.children().is_empty());
14/// ```
15#[derive(Clone, Debug, PartialEq)]
16pub struct ListItem {
17    /// 条目文本。
18    text: String,
19    /// 嵌套层级,0 表示顶层。
20    level: u8,
21    /// 子条目(嵌套列表)。
22    children: Vec<ListItem>,
23}
24
25impl ListItem {
26    /// 创建指定层级的列表条目。
27    ///
28    /// # Examples
29    ///
30    /// ```
31    /// use easypdf_core::ListItem;
32    ///
33    /// let item = ListItem::new("Hello");
34    /// assert_eq!(item.text(), "Hello");
35    /// ```
36    #[must_use]
37    pub fn new(text: impl Into<String>) -> Self {
38        Self {
39            text: text.into(),
40            level: 0,
41            children: Vec::new(),
42        }
43    }
44
45    /// 设置嵌套层级。
46    ///
47    /// # Examples
48    ///
49    /// ```
50    /// use easypdf_core::ListItem;
51    ///
52    /// let item = ListItem::new("Nested").with_level(2);
53    /// assert_eq!(item.level(), 2);
54    /// ```
55    #[must_use]
56    pub const fn with_level(mut self, level: u8) -> Self {
57        self.level = level;
58        self
59    }
60
61    /// 追加子条目。
62    ///
63    /// # Examples
64    ///
65    /// ```
66    /// use easypdf_core::ListItem;
67    ///
68    /// let item = ListItem::new("Parent").with_child(ListItem::new("Child"));
69    /// assert_eq!(item.children().len(), 1);
70    /// ```
71    #[must_use]
72    pub fn with_child(mut self, child: ListItem) -> Self {
73        self.children.push(child);
74        self
75    }
76
77    /// 返回条目文本。
78    #[must_use]
79    pub fn text(&self) -> &str {
80        &self.text
81    }
82
83    /// 返回嵌套层级。
84    #[must_use]
85    pub const fn level(&self) -> u8 {
86        self.level
87    }
88
89    /// 返回子条目列表。
90    #[must_use]
91    pub fn children(&self) -> &[ListItem] {
92        &self.children
93    }
94
95    /// 返回可变子条目列表引用。
96    pub fn children_mut(&mut self) -> &mut Vec<ListItem> {
97        &mut self.children
98    }
99}
100
101#[cfg(test)]
102#[allow(clippy::uninlined_format_args, clippy::float_cmp)]
103mod tests {
104    use super::*;
105
106    #[test]
107    fn new_creates_default_item() {
108        let item = ListItem::new("Hello");
109        assert_eq!(item.text(), "Hello");
110        assert_eq!(item.level(), 0);
111        assert!(item.children().is_empty());
112    }
113
114    #[test]
115    fn new_accepts_string() {
116        let item = ListItem::new(String::from("Owned"));
117        assert_eq!(item.text(), "Owned");
118    }
119
120    #[test]
121    fn with_level_sets_level() {
122        let item = ListItem::new("Nested").with_level(3);
123        assert_eq!(item.level(), 3);
124    }
125
126    #[test]
127    fn with_level_zero_is_default() {
128        let item = ListItem::new("Top").with_level(0);
129        assert_eq!(item.level(), 0);
130    }
131
132    #[test]
133    fn with_child_adds_child() {
134        let item = ListItem::new("Parent").with_child(ListItem::new("Child"));
135        assert_eq!(item.children().len(), 1);
136        assert_eq!(item.children()[0].text(), "Child");
137    }
138
139    #[test]
140    fn with_child_multiple_children() {
141        let item = ListItem::new("Parent")
142            .with_child(ListItem::new("A"))
143            .with_child(ListItem::new("B"))
144            .with_child(ListItem::new("C"));
145        assert_eq!(item.children().len(), 3);
146        assert_eq!(item.children()[0].text(), "A");
147        assert_eq!(item.children()[1].text(), "B");
148        assert_eq!(item.children()[2].text(), "C");
149    }
150
151    #[test]
152    fn nested_children() {
153        let item = ListItem::new("L0").with_level(0).with_child(
154            ListItem::new("L1")
155                .with_level(1)
156                .with_child(ListItem::new("L2").with_level(2)),
157        );
158        assert_eq!(item.children().len(), 1);
159        assert_eq!(item.children()[0].children().len(), 1);
160        assert_eq!(item.children()[0].children()[0].text(), "L2");
161    }
162
163    #[test]
164    fn children_mut_returns_mutable_ref() {
165        let mut item = ListItem::new("Parent");
166        item.children_mut().push(ListItem::new("MutChild"));
167        assert_eq!(item.children().len(), 1);
168        assert_eq!(item.children()[0].text(), "MutChild");
169    }
170
171    #[test]
172    fn clone_preserves_structure() {
173        let item = ListItem::new("Original")
174            .with_level(1)
175            .with_child(ListItem::new("Child"));
176        let cloned = item.clone();
177        assert_eq!(item, cloned);
178    }
179
180    #[test]
181    fn partial_eq_works() {
182        let a = ListItem::new("Same").with_level(1);
183        let b = ListItem::new("Same").with_level(1);
184        let c = ListItem::new("Different").with_level(1);
185        assert_eq!(a, b);
186        assert_ne!(a, c);
187    }
188
189    #[test]
190    fn debug_includes_fields() {
191        let item = ListItem::new("Debug").with_level(2);
192        let dbg = format!("{:?}", item);
193        assert!(dbg.contains("Debug"));
194        assert!(dbg.contains('2'));
195    }
196}