1#[derive(Debug, Clone, PartialEq)]
18pub struct ParsedName {
19 pub number: Option<u32>,
21 pub name: String,
24 pub display_title: String,
26}
27
28fn slugify(s: &str) -> String {
30 s.to_lowercase().replace([' ', '_'], "-")
31}
32
33pub fn parse_entry_name(name: &str) -> ParsedName {
43 if let Some(dash_pos) = name.find('-') {
45 let prefix = &name[..dash_pos];
46 if let Ok(num) = prefix.parse::<u32>() {
47 let raw = &name[dash_pos + 1..];
48 return ParsedName {
49 number: Some(num),
50 name: slugify(raw),
51 display_title: raw.replace('-', " "),
52 };
53 }
54 }
55 if let Ok(num) = name.parse::<u32>() {
57 return ParsedName {
58 number: Some(num),
59 name: String::new(),
60 display_title: String::new(),
61 };
62 }
63 ParsedName {
65 number: None,
66 name: slugify(name),
67 display_title: name.replace('-', " "),
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74
75 #[test]
76 fn numbered_with_multi_word_name() {
77 let p = parse_entry_name("020-My-Best-Photos");
78 assert_eq!(p.number, Some(20));
79 assert_eq!(p.name, "my-best-photos");
80 assert_eq!(p.display_title, "My Best Photos");
81 }
82
83 #[test]
84 fn numbered_single_word() {
85 let p = parse_entry_name("010-Landscapes");
86 assert_eq!(p.number, Some(10));
87 assert_eq!(p.name, "landscapes");
88 assert_eq!(p.display_title, "Landscapes");
89 }
90
91 #[test]
92 fn number_only_no_dash() {
93 let p = parse_entry_name("001");
94 assert_eq!(p.number, Some(1));
95 assert_eq!(p.name, "");
96 assert_eq!(p.display_title, "");
97 }
98
99 #[test]
100 fn number_with_trailing_dash() {
101 let p = parse_entry_name("001-");
102 assert_eq!(p.number, Some(1));
103 assert_eq!(p.name, "");
104 assert_eq!(p.display_title, "");
105 }
106
107 #[test]
108 fn unnumbered_single_word() {
109 let p = parse_entry_name("Museum");
110 assert_eq!(p.number, None);
111 assert_eq!(p.name, "museum");
112 assert_eq!(p.display_title, "Museum");
113 }
114
115 #[test]
116 fn unnumbered_with_dashes() {
117 let p = parse_entry_name("wip-drafts");
118 assert_eq!(p.number, None);
119 assert_eq!(p.name, "wip-drafts");
120 assert_eq!(p.display_title, "wip drafts");
121 }
122
123 #[test]
124 fn image_stem_numbered_with_title() {
125 let p = parse_entry_name("001-Museum");
126 assert_eq!(p.number, Some(1));
127 assert_eq!(p.name, "museum");
128 assert_eq!(p.display_title, "Museum");
129 }
130
131 #[test]
132 fn image_stem_dashes_become_spaces() {
133 let p = parse_entry_name("001-My-Museum");
134 assert_eq!(p.number, Some(1));
135 assert_eq!(p.name, "my-museum");
136 assert_eq!(p.display_title, "My Museum");
137 }
138
139 #[test]
140 fn page_name_dashes_become_spaces() {
141 let p = parse_entry_name("040-who-am-i");
142 assert_eq!(p.number, Some(40));
143 assert_eq!(p.name, "who-am-i");
144 assert_eq!(p.display_title, "who am i");
145 }
146
147 #[test]
148 fn large_number_prefix() {
149 let p = parse_entry_name("999-Last");
150 assert_eq!(p.number, Some(999));
151 assert_eq!(p.name, "last");
152 assert_eq!(p.display_title, "Last");
153 }
154
155 #[test]
156 fn zero_prefix() {
157 let p = parse_entry_name("000-First");
158 assert_eq!(p.number, Some(0));
159 assert_eq!(p.name, "first");
160 assert_eq!(p.display_title, "First");
161 }
162
163 #[test]
164 fn underscores_become_hyphens_in_slug() {
165 let p = parse_entry_name("010-My_Photos");
166 assert_eq!(p.number, Some(10));
167 assert_eq!(p.name, "my-photos");
168 assert_eq!(p.display_title, "My_Photos");
169 }
170
171 #[test]
172 fn spaces_become_hyphens_in_slug() {
173 let p = parse_entry_name("001-Magna Graecia With Theo");
174 assert_eq!(p.number, Some(1));
175 assert_eq!(p.name, "magna-graecia-with-theo");
176 assert_eq!(p.display_title, "Magna Graecia With Theo");
177 }
178}