#[derive(Clone, Debug, PartialEq)]
pub struct ListItem {
text: String,
level: u8,
children: Vec<ListItem>,
}
impl ListItem {
#[must_use]
pub fn new(text: impl Into<String>) -> Self {
Self {
text: text.into(),
level: 0,
children: Vec::new(),
}
}
#[must_use]
pub const fn with_level(mut self, level: u8) -> Self {
self.level = level;
self
}
#[must_use]
pub fn with_child(mut self, child: ListItem) -> Self {
self.children.push(child);
self
}
#[must_use]
pub fn text(&self) -> &str {
&self.text
}
#[must_use]
pub const fn level(&self) -> u8 {
self.level
}
#[must_use]
pub fn children(&self) -> &[ListItem] {
&self.children
}
pub fn children_mut(&mut self) -> &mut Vec<ListItem> {
&mut self.children
}
}
#[cfg(test)]
#[allow(clippy::uninlined_format_args, clippy::float_cmp)]
mod tests {
use super::*;
#[test]
fn new_creates_default_item() {
let item = ListItem::new("Hello");
assert_eq!(item.text(), "Hello");
assert_eq!(item.level(), 0);
assert!(item.children().is_empty());
}
#[test]
fn new_accepts_string() {
let item = ListItem::new(String::from("Owned"));
assert_eq!(item.text(), "Owned");
}
#[test]
fn with_level_sets_level() {
let item = ListItem::new("Nested").with_level(3);
assert_eq!(item.level(), 3);
}
#[test]
fn with_level_zero_is_default() {
let item = ListItem::new("Top").with_level(0);
assert_eq!(item.level(), 0);
}
#[test]
fn with_child_adds_child() {
let item = ListItem::new("Parent").with_child(ListItem::new("Child"));
assert_eq!(item.children().len(), 1);
assert_eq!(item.children()[0].text(), "Child");
}
#[test]
fn with_child_multiple_children() {
let item = ListItem::new("Parent")
.with_child(ListItem::new("A"))
.with_child(ListItem::new("B"))
.with_child(ListItem::new("C"));
assert_eq!(item.children().len(), 3);
assert_eq!(item.children()[0].text(), "A");
assert_eq!(item.children()[1].text(), "B");
assert_eq!(item.children()[2].text(), "C");
}
#[test]
fn nested_children() {
let item = ListItem::new("L0").with_level(0).with_child(
ListItem::new("L1")
.with_level(1)
.with_child(ListItem::new("L2").with_level(2)),
);
assert_eq!(item.children().len(), 1);
assert_eq!(item.children()[0].children().len(), 1);
assert_eq!(item.children()[0].children()[0].text(), "L2");
}
#[test]
fn children_mut_returns_mutable_ref() {
let mut item = ListItem::new("Parent");
item.children_mut().push(ListItem::new("MutChild"));
assert_eq!(item.children().len(), 1);
assert_eq!(item.children()[0].text(), "MutChild");
}
#[test]
fn clone_preserves_structure() {
let item = ListItem::new("Original")
.with_level(1)
.with_child(ListItem::new("Child"));
let cloned = item.clone();
assert_eq!(item, cloned);
}
#[test]
fn partial_eq_works() {
let a = ListItem::new("Same").with_level(1);
let b = ListItem::new("Same").with_level(1);
let c = ListItem::new("Different").with_level(1);
assert_eq!(a, b);
assert_ne!(a, c);
}
#[test]
fn debug_includes_fields() {
let item = ListItem::new("Debug").with_level(2);
let dbg = format!("{:?}", item);
assert!(dbg.contains("Debug"));
assert!(dbg.contains('2'));
}
}