use crate::properties::PropertyList;
use crate::Length;
#[derive(Debug, Clone)]
pub struct ListBlock<'a> {
pub properties: PropertyList<'a>,
pub items: Vec<ListItem<'a>>,
pub provisional_distance_between_starts: Length,
pub provisional_label_separation: Length,
}
#[derive(Debug, Clone)]
pub struct ListItem<'a> {
pub properties: PropertyList<'a>,
pub label: ListItemLabel<'a>,
pub body: ListItemBody<'a>,
}
#[derive(Debug, Clone)]
pub struct ListItemLabel<'a> {
pub properties: PropertyList<'a>,
pub blocks: Vec<super::Block<'a>>,
}
#[derive(Debug, Clone)]
pub struct ListItemBody<'a> {
pub properties: PropertyList<'a>,
pub blocks: Vec<super::Block<'a>>,
}
impl<'a> ListBlock<'a> {
pub fn new(properties: PropertyList<'a>) -> Self {
Self {
properties,
items: Vec::new(),
provisional_distance_between_starts: Length::from_pt(24.0), provisional_label_separation: Length::from_pt(6.0), }
}
pub fn item_count(&self) -> usize {
self.items.len()
}
pub fn add_item(&mut self, item: ListItem<'a>) {
self.items.push(item);
}
}
impl<'a> ListItem<'a> {
pub fn new(properties: PropertyList<'a>, label: ListItemLabel<'a>, body: ListItemBody<'a>) -> Self {
Self {
properties,
label,
body,
}
}
}
impl<'a> ListItemLabel<'a> {
pub fn new(properties: PropertyList<'a>) -> Self {
Self {
properties,
blocks: Vec::new(),
}
}
pub fn add_block(&mut self, block: super::Block<'a>) {
self.blocks.push(block);
}
}
impl<'a> ListItemBody<'a> {
pub fn new(properties: PropertyList<'a>) -> Self {
Self {
properties,
blocks: Vec::new(),
}
}
pub fn add_block(&mut self, block: super::Block<'a>) {
self.blocks.push(block);
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ListMarkerType {
Disc,
Circle,
Square,
Decimal,
LowerAlpha,
UpperAlpha,
LowerRoman,
UpperRoman,
None,
}
impl ListMarkerType {
pub fn marker_text(&self, index: usize) -> String {
match self {
Self::Disc => "•".to_string(),
Self::Circle => "○".to_string(),
Self::Square => "■".to_string(),
Self::Decimal => index.to_string(),
Self::LowerAlpha => Self::to_alpha(index, false),
Self::UpperAlpha => Self::to_alpha(index, true),
Self::LowerRoman => Self::to_roman(index, false),
Self::UpperRoman => Self::to_roman(index, true),
Self::None => String::new(),
}
}
fn to_alpha(mut n: usize, uppercase: bool) -> String {
if n == 0 {
return String::new();
}
let mut result = String::new();
while n > 0 {
n -= 1;
let ch = if uppercase {
(b'A' + (n % 26) as u8) as char
} else {
(b'a' + (n % 26) as u8) as char
};
result.insert(0, ch);
n /= 26;
}
result
}
fn to_roman(n: usize, uppercase: bool) -> String {
if n == 0 || n > 3999 {
return n.to_string(); }
let values = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
let symbols_lower = ["m", "cm", "d", "cd", "c", "xc", "l", "xl", "x", "ix", "v", "iv", "i"];
let symbols_upper = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"];
let symbols = if uppercase { symbols_upper } else { symbols_lower };
let mut result = String::new();
let mut num = n;
for (i, &value) in values.iter().enumerate() {
while num >= value {
result.push_str(symbols[i]);
num -= value;
}
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_list_block_creation() {
let props = PropertyList::new();
let list = ListBlock::new(props);
assert_eq!(list.item_count(), 0);
}
#[test]
fn test_add_list_item() {
let props = PropertyList::new();
let mut list = ListBlock::new(props);
let label = ListItemLabel::new(PropertyList::new());
let body = ListItemBody::new(PropertyList::new());
let item = ListItem::new(PropertyList::new(), label, body);
list.add_item(item);
assert_eq!(list.item_count(), 1);
}
#[test]
fn test_list_marker_disc() {
assert_eq!(ListMarkerType::Disc.marker_text(1), "•");
assert_eq!(ListMarkerType::Disc.marker_text(5), "•");
}
#[test]
fn test_list_marker_decimal() {
assert_eq!(ListMarkerType::Decimal.marker_text(1), "1");
assert_eq!(ListMarkerType::Decimal.marker_text(42), "42");
}
#[test]
fn test_list_marker_lower_alpha() {
assert_eq!(ListMarkerType::LowerAlpha.marker_text(1), "a");
assert_eq!(ListMarkerType::LowerAlpha.marker_text(2), "b");
assert_eq!(ListMarkerType::LowerAlpha.marker_text(26), "z");
assert_eq!(ListMarkerType::LowerAlpha.marker_text(27), "aa");
}
#[test]
fn test_list_marker_upper_alpha() {
assert_eq!(ListMarkerType::UpperAlpha.marker_text(1), "A");
assert_eq!(ListMarkerType::UpperAlpha.marker_text(26), "Z");
}
#[test]
fn test_list_marker_lower_roman() {
assert_eq!(ListMarkerType::LowerRoman.marker_text(1), "i");
assert_eq!(ListMarkerType::LowerRoman.marker_text(4), "iv");
assert_eq!(ListMarkerType::LowerRoman.marker_text(9), "ix");
assert_eq!(ListMarkerType::LowerRoman.marker_text(2023), "mmxxiii");
}
#[test]
fn test_list_marker_upper_roman() {
assert_eq!(ListMarkerType::UpperRoman.marker_text(1), "I");
assert_eq!(ListMarkerType::UpperRoman.marker_text(4), "IV");
assert_eq!(ListMarkerType::UpperRoman.marker_text(1994), "MCMXCIV");
}
#[test]
fn test_list_marker_none() {
assert_eq!(ListMarkerType::None.marker_text(1), "");
}
}