1use core::fmt;
4
5use crate::{
6 book::Book,
7 language::Language,
8 language_data::{long_name, short_name},
9 passage::{BookPassage, ChapterPassage, Passage, PassageSequence, VersePassage},
10 reference::{Reference, VerseRange, VerseRef},
11};
12
13#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
15#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
16pub enum BookNameStyle {
17 #[default]
19 Long,
20 Short,
22}
23
24#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
29pub struct ReferenceFormatter {
30 language: Language,
31 book_name_style: BookNameStyle,
32 compact_ranges: bool,
33}
34
35impl ReferenceFormatter {
36 pub const DEFAULT: Self = Self::new();
38
39 #[must_use]
41 pub const fn new() -> Self {
42 Self {
43 language: Language::English,
44 book_name_style: BookNameStyle::Long,
45 compact_ranges: true,
46 }
47 }
48
49 #[must_use]
51 pub const fn with_options(
52 language: Language,
53 book_name_style: BookNameStyle,
54 compact_ranges: bool,
55 ) -> Self {
56 Self {
57 language,
58 book_name_style,
59 compact_ranges,
60 }
61 }
62
63 #[must_use]
65 pub const fn for_language(language: Language) -> Self {
66 Self::new().with_language(language)
67 }
68
69 #[must_use]
71 pub const fn language(self) -> Language {
72 self.language
73 }
74
75 #[must_use]
77 pub const fn book_name_style(self) -> BookNameStyle {
78 self.book_name_style
79 }
80
81 #[must_use]
83 pub const fn compact_ranges(self) -> bool {
84 self.compact_ranges
85 }
86
87 #[must_use]
89 pub const fn with_language(mut self, language: Language) -> Self {
90 self.language = language;
91 self
92 }
93
94 #[must_use]
96 pub const fn with_book_name_style(mut self, book_name_style: BookNameStyle) -> Self {
97 self.book_name_style = book_name_style;
98 self
99 }
100
101 #[must_use]
103 pub const fn with_compact_ranges(mut self, compact_ranges: bool) -> Self {
104 self.compact_ranges = compact_ranges;
105 self
106 }
107
108 #[must_use]
110 pub fn book_name(self, book: Book) -> &'static str {
111 match self.book_name_style {
112 BookNameStyle::Long => long_name(book, self.language),
113 BookNameStyle::Short => short_name(book, self.language),
114 }
115 }
116
117 #[must_use]
119 pub fn format_book_name(self, book: Book) -> &'static str {
120 self.book_name(book)
121 }
122
123 #[must_use = "formatting adapters must be displayed or converted to a string"]
125 pub const fn format_book(self, book: Book) -> FormattedBook {
126 FormattedBook {
127 formatter: self,
128 book,
129 }
130 }
131
132 #[must_use = "formatting adapters must be displayed or converted to a string"]
134 pub fn format(self, reference: impl Into<Reference>) -> FormattedReference {
135 self.format_reference(reference)
136 }
137
138 #[must_use = "formatting adapters must be displayed or converted to a string"]
140 pub fn format_reference(self, reference: impl Into<Reference>) -> FormattedReference {
141 FormattedReference {
142 formatter: self,
143 reference: reference.into(),
144 }
145 }
146
147 #[must_use = "formatting adapters must be displayed or converted to a string"]
149 pub const fn format_passage<'a>(self, passage: &'a Passage) -> FormattedPassage<'a> {
150 FormattedPassage {
151 formatter: self,
152 passage,
153 }
154 }
155
156 #[must_use = "formatting adapters must be displayed or converted to a string"]
158 pub const fn format_book_passage(self, passage: BookPassage) -> FormattedBookPassage {
159 FormattedBookPassage {
160 formatter: self,
161 passage,
162 }
163 }
164
165 #[must_use = "formatting adapters must be displayed or converted to a string"]
167 pub const fn format_chapter_passage(self, passage: ChapterPassage) -> FormattedChapterPassage {
168 FormattedChapterPassage {
169 formatter: self,
170 passage,
171 }
172 }
173
174 #[must_use = "formatting adapters must be displayed or converted to a string"]
176 pub const fn format_verse_passage<'a>(
177 self,
178 passage: &'a VersePassage,
179 ) -> FormattedVersePassage<'a> {
180 FormattedVersePassage {
181 formatter: self,
182 passage,
183 }
184 }
185
186 #[must_use = "formatting adapters must be displayed or converted to a string"]
188 pub const fn format_passage_sequence<'a>(
189 self,
190 passage: &'a PassageSequence,
191 ) -> FormattedPassageSequence<'a> {
192 FormattedPassageSequence {
193 formatter: self,
194 passage,
195 }
196 }
197
198 fn write_verse(self, output: &mut fmt::Formatter<'_>, verse: VerseRef) -> fmt::Result {
199 write!(
200 output,
201 "{} {}:{}",
202 self.book_name(verse.book()),
203 verse.chapter(),
204 verse.verse()
205 )
206 }
207
208 fn write_range(self, output: &mut fmt::Formatter<'_>, range: VerseRange) -> fmt::Result {
209 let start = range.start();
210 let end = range.end();
211
212 if !self.compact_ranges || start.book() != end.book() {
213 self.write_verse(output, start)?;
214 output.write_str("-")?;
215 return self.write_verse(output, end);
216 }
217
218 self.write_verse(output, start)?;
219 if start.chapter() == end.chapter() {
220 write!(output, "-{}", end.verse())
221 } else {
222 write!(output, "-{}:{}", end.chapter(), end.verse())
223 }
224 }
225
226 fn write_reference(self, output: &mut fmt::Formatter<'_>, reference: Reference) -> fmt::Result {
227 match reference {
228 Reference::Verse(verse) => self.write_verse(output, verse),
229 Reference::Range(range) => self.write_range(output, range),
230 }
231 }
232
233 fn write_chapter_passage(
234 self,
235 output: &mut fmt::Formatter<'_>,
236 passage: ChapterPassage,
237 ) -> fmt::Result {
238 write!(
239 output,
240 "{} {}",
241 self.book_name(passage.book()),
242 passage.start_chapter()
243 )?;
244 if let Some(end) = passage.end_chapter() {
245 write!(output, "-{end}")?;
246 }
247 Ok(())
248 }
249
250 fn write_verse_passage(
251 self,
252 output: &mut fmt::Formatter<'_>,
253 passage: &VersePassage,
254 ) -> fmt::Result {
255 let Some((first, rest)) = passage.selections().split_first() else {
256 return Ok(());
257 };
258
259 self.write_reference(output, *first)?;
260 if !self.compact_ranges || rest.is_empty() {
261 for selection in rest {
262 output.write_str(",")?;
263 self.write_reference(output, *selection)?;
264 }
265 return Ok(());
266 }
267
268 let anchor = first.start();
269 for selection in rest {
270 output.write_str(",")?;
271 self.write_relative_selection(output, *selection, anchor)?;
272 }
273 Ok(())
274 }
275
276 fn write_relative_selection(
277 self,
278 output: &mut fmt::Formatter<'_>,
279 selection: Reference,
280 anchor: VerseRef,
281 ) -> fmt::Result {
282 match selection {
283 Reference::Verse(verse) => self.write_relative_verse(output, verse, anchor),
284 Reference::Range(range) => self.write_relative_range(output, range, anchor),
285 }
286 }
287
288 fn write_relative_verse(
289 self,
290 output: &mut fmt::Formatter<'_>,
291 verse: VerseRef,
292 anchor: VerseRef,
293 ) -> fmt::Result {
294 if verse.book() != anchor.book() {
295 return self.write_verse(output, verse);
296 }
297 if verse.chapter() == anchor.chapter() {
298 write!(output, "{}", verse.verse())
299 } else {
300 write!(output, "{}:{}", verse.chapter(), verse.verse())
301 }
302 }
303
304 fn write_relative_range(
305 self,
306 output: &mut fmt::Formatter<'_>,
307 range: VerseRange,
308 anchor: VerseRef,
309 ) -> fmt::Result {
310 let start = range.start();
311 let end = range.end();
312 if start.book() != anchor.book() || end.book() != anchor.book() {
313 return self.write_range(output, range);
314 }
315
316 if start.chapter() == anchor.chapter() {
317 write!(output, "{}", start.verse())?;
318 } else {
319 write!(output, "{}:{}", start.chapter(), start.verse())?;
320 }
321 output.write_str("-")?;
322 if start.chapter() == end.chapter() {
323 write!(output, "{}", end.verse())
324 } else {
325 write!(output, "{}:{}", end.chapter(), end.verse())
326 }
327 }
328
329 fn write_passage(self, output: &mut fmt::Formatter<'_>, passage: &Passage) -> fmt::Result {
330 match passage {
331 Passage::Book(passage) => output.write_str(self.book_name(passage.book())),
332 Passage::Chapter(passage) => self.write_chapter_passage(output, *passage),
333 Passage::Verses(passage) => self.write_verse_passage(output, passage),
334 Passage::Sequence(sequence) => self.write_passage_sequence(output, sequence),
335 }
336 }
337
338 fn write_passage_sequence(
339 self,
340 output: &mut fmt::Formatter<'_>,
341 sequence: &PassageSequence,
342 ) -> fmt::Result {
343 let mut separator = "";
344 for passage in sequence.passages() {
345 output.write_str(separator)?;
346 self.write_passage(output, passage)?;
347 separator = "; ";
348 }
349 Ok(())
350 }
351}
352
353impl Default for ReferenceFormatter {
354 fn default() -> Self {
355 Self::new()
356 }
357}
358
359impl From<Language> for ReferenceFormatter {
360 fn from(language: Language) -> Self {
361 Self::for_language(language)
362 }
363}
364
365#[derive(Clone, Copy, Debug, Eq, PartialEq)]
367pub struct FormattedBook {
368 formatter: ReferenceFormatter,
369 book: Book,
370}
371
372impl fmt::Display for FormattedBook {
373 fn fmt(&self, output: &mut fmt::Formatter<'_>) -> fmt::Result {
374 output.write_str(self.formatter.book_name(self.book))
375 }
376}
377
378#[derive(Clone, Copy, Debug, Eq, PartialEq)]
380pub struct FormattedReference {
381 formatter: ReferenceFormatter,
382 reference: Reference,
383}
384
385impl fmt::Display for FormattedReference {
386 fn fmt(&self, output: &mut fmt::Formatter<'_>) -> fmt::Result {
387 self.formatter.write_reference(output, self.reference)
388 }
389}
390
391#[derive(Clone, Copy, Debug)]
393pub struct FormattedPassage<'a> {
394 formatter: ReferenceFormatter,
395 passage: &'a Passage,
396}
397
398impl fmt::Display for FormattedPassage<'_> {
399 fn fmt(&self, output: &mut fmt::Formatter<'_>) -> fmt::Result {
400 self.formatter.write_passage(output, self.passage)
401 }
402}
403
404#[derive(Clone, Copy, Debug, Eq, PartialEq)]
406pub struct FormattedBookPassage {
407 formatter: ReferenceFormatter,
408 passage: BookPassage,
409}
410
411impl fmt::Display for FormattedBookPassage {
412 fn fmt(&self, output: &mut fmt::Formatter<'_>) -> fmt::Result {
413 output.write_str(self.formatter.book_name(self.passage.book()))
414 }
415}
416
417#[derive(Clone, Copy, Debug, Eq, PartialEq)]
419pub struct FormattedChapterPassage {
420 formatter: ReferenceFormatter,
421 passage: ChapterPassage,
422}
423
424impl fmt::Display for FormattedChapterPassage {
425 fn fmt(&self, output: &mut fmt::Formatter<'_>) -> fmt::Result {
426 self.formatter.write_chapter_passage(output, self.passage)
427 }
428}
429
430#[derive(Clone, Copy, Debug)]
432pub struct FormattedVersePassage<'a> {
433 formatter: ReferenceFormatter,
434 passage: &'a VersePassage,
435}
436
437impl fmt::Display for FormattedVersePassage<'_> {
438 fn fmt(&self, output: &mut fmt::Formatter<'_>) -> fmt::Result {
439 self.formatter.write_verse_passage(output, self.passage)
440 }
441}
442
443#[derive(Clone, Copy, Debug)]
445pub struct FormattedPassageSequence<'a> {
446 formatter: ReferenceFormatter,
447 passage: &'a PassageSequence,
448}
449
450impl fmt::Display for FormattedPassageSequence<'_> {
451 fn fmt(&self, output: &mut fmt::Formatter<'_>) -> fmt::Result {
452 self.formatter.write_passage_sequence(output, self.passage)
453 }
454}
455
456#[cfg(test)]
457#[path = "../tests/unit/formatter.rs"]
458mod tests;