1use alloc::vec::Vec;
8
9use crate::{Error, Event, EventSink, Result};
10
11macro_rules! block_kinds {
20 ( $( $kind:ident => ( $start:ident, $end:ident ) ),+ $(,)? ) => {
21 #[inline]
23 #[must_use]
24 pub fn block_kind_for_start(event: &Event) -> Option<BlockKind> {
25 match event {
26 $( Event::$start { .. } => Some(BlockKind::$kind), )+
27 _ => None,
28 }
29 }
30
31 #[inline]
33 #[must_use]
34 pub fn block_kind_for_end(event: &Event) -> Option<BlockKind> {
35 match event {
36 $( Event::$end => Some(BlockKind::$kind), )+
37 _ => None,
38 }
39 }
40
41 #[inline]
43 #[must_use]
44 pub fn end_event_for(kind: BlockKind) -> Event {
45 match kind {
46 $( BlockKind::$kind => Event::$end, )+
47 }
48 }
49 };
50}
51
52#[derive(Clone, Copy, PartialEq, Eq, Debug)]
57#[non_exhaustive]
58pub enum BlockKind {
59 Blockquote,
61 Caption,
63 DefinitionDetail,
65 DefinitionList,
67 DefinitionTerm,
69 Document,
71 Footnote,
73 Heading,
75 Link,
77 OrderedListItem,
79 Paragraph,
81 Preformatted,
83 Table,
85 TableCell,
87 TableHeader,
89 TableRow,
91 UnorderedListItem,
93}
94
95pub struct StackTrackingSink<S: EventSink> {
111 document_finished: bool,
112 sink: S,
113 stack: Vec<BlockKind>,
114}
115
116impl<S: EventSink> StackTrackingSink<S> {
117 fn handle_end_document(&mut self) -> Result<()> {
120 if !self.stack.contains(&BlockKind::Document) {
121 return Err(Error::InvalidSequence {
122 expected: "open Document".to_string(),
123 found: "EndDocument".to_string(),
124 message: "EndDocument received without StartDocument".to_string(),
125 });
126 }
127 while let Some(kind) = self.stack.pop() {
128 if kind != BlockKind::Document {
129 self.sink.handle_event(end_event_for(kind))?;
130 }
131 }
132 self.document_finished = true;
133 self.sink.handle_event(Event::EndDocument)
134 }
135
136 fn handle_end_event(&mut self, event: Event) -> Result<()> {
139 let Some(target_kind) = block_kind_for_end(&event) else {
140 return Err(Error::InvalidSequence {
141 expected: "valid End event".to_string(),
142 found: format!("{event:?}"),
143 message: "handle_end_event called with non-End event".to_string(),
144 });
145 };
146 if self.stack.is_empty() {
147 return Err(Error::InvalidSequence {
148 expected: "open block".to_string(),
149 found: format!("{target_kind:?}"),
150 message: "received End event with empty stack".to_string(),
151 });
152 }
153 if self.stack.contains(&target_kind) {
154 while self.stack.last() != Some(&target_kind) {
155 if let Some(popped_kind) = self.stack.pop() {
156 self.sink.handle_event(end_event_for(popped_kind))?;
157 }
158 }
159 self.stack.pop();
160 return self.sink.handle_event(event);
161 }
162 Err(Error::InvalidSequence {
163 expected: self
164 .stack
165 .last()
166 .map_or("empty".to_string(), |k| format!("{k:?}")),
167 found: format!("{target_kind:?}"),
168 message: format!("End event for {target_kind:?} does not match any open block"),
169 })
170 }
171
172 fn handle_other_event(&mut self, event: Event) -> Result<()> {
178 if matches!(event, Event::ThematicBreak { .. })
179 && self.stack.last() == Some(&BlockKind::Paragraph)
180 {
181 self.stack.pop();
182 self.sink.handle_event(Event::EndParagraph)?;
183 }
184
185 if matches!(event, Event::Text { .. } | Event::StartTextStyle { .. })
186 && !self.has_open_content()
187 {
188 let para = Event::StartParagraph {
189 alignment: None,
190 id: None,
191 };
192 self.stack.push(BlockKind::Paragraph);
193 self.sink.handle_event(para)?;
194 }
195
196 self.sink.handle_event(event)
197 }
198
199 fn handle_start_event(&mut self, kind: BlockKind, event: Event) -> Result<()> {
202 if kind == BlockKind::Document {
203 if self.stack.contains(&BlockKind::Document) {
204 return Err(Error::InvalidSequence {
205 expected: "single Document".to_string(),
206 found: "StartDocument".to_string(),
207 message: "StartDocument received while Document already open".to_string(),
208 });
209 }
210 if self.document_finished {
211 return Err(Error::InvalidSequence {
212 expected: "end of stream".to_string(),
213 found: "StartDocument".to_string(),
214 message: "StartDocument received after document already finished".to_string(),
215 });
216 }
217 }
218 if kind == BlockKind::Link && self.stack.contains(&BlockKind::Link) {
220 return Err(Error::InvalidSequence {
221 expected: "no nested links".to_string(),
222 found: "StartLink".to_string(),
223 message: "StartLink received while another link is already open".to_string(),
224 });
225 }
226 if kind != BlockKind::Link && self.stack.last() == Some(&BlockKind::Paragraph) {
227 self.stack.pop();
228 self.sink.handle_event(Event::EndParagraph)?;
229 }
230 self.stack.push(kind);
231 self.sink.handle_event(event)
232 }
233
234 #[inline]
249 pub fn has_open_content(&self) -> bool {
250 self.stack.iter().any(|kind| {
251 matches!(
252 kind,
253 BlockKind::Heading
254 | BlockKind::Paragraph
255 | BlockKind::Preformatted
256 | BlockKind::Link
257 | BlockKind::DefinitionTerm
258 )
259 })
260 }
261
262 #[inline]
264 pub fn is_inside(&self, kind: BlockKind) -> bool {
265 self.stack.contains(&kind)
266 }
267
268 #[inline]
270 pub fn new(sink: S) -> Self {
271 Self {
272 document_finished: false,
273 sink,
274 stack: Vec::new(),
275 }
276 }
277
278 #[cfg(test)]
280 fn sink(&self) -> &S {
281 &self.sink
282 }
283
284 #[inline]
289 pub fn stack(&self) -> &[BlockKind] {
290 &self.stack
291 }
292
293 #[cfg(test)]
295 fn stack_mut(&mut self) -> &mut Vec<BlockKind> {
296 &mut self.stack
297 }
298}
299
300impl<S: EventSink> EventSink for StackTrackingSink<S> {
301 #[inline]
302 fn finish(self) -> Result<()> {
303 self.sink.finish()
304 }
305
306 #[inline]
307 fn handle_event(&mut self, event: Event) -> Result<()> {
308 if self.document_finished && !matches!(event, Event::StartDocument { .. }) {
311 return Err(Error::InvalidSequence {
312 expected: "end of stream".to_string(),
313 found: format!("{event:?}"),
314 message: "event received after document already finished".to_string(),
315 });
316 }
317
318 if let Some(kind) = block_kind_for_start(&event) {
319 return self.handle_start_event(kind, event);
320 }
321
322 if matches!(event, Event::EndDocument) {
323 return self.handle_end_document();
324 }
325
326 if block_kind_for_end(&event).is_some() {
327 return self.handle_end_event(event);
328 }
329
330 self.handle_other_event(event)
331 }
332}
333
334block_kinds! {
335 Blockquote => (StartBlockQuote, EndBlockQuote),
336 Caption => (StartCaption, EndCaption),
337 DefinitionDetail => (StartDefinitionDetail, EndDefinitionDetail),
338 DefinitionList => (StartDefinitionList, EndDefinitionList),
339 DefinitionTerm => (StartDefinitionTerm, EndDefinitionTerm),
340 Document => (StartDocument, EndDocument),
341 Footnote => (StartFootnote, EndFootnote),
342 Heading => (StartHeading, EndHeading),
343 Link => (StartLink, EndLink),
344 OrderedListItem => (StartOrderedListItem, EndOrderedListItem),
345 Paragraph => (StartParagraph, EndParagraph),
346 Preformatted => (StartPreformatted, EndPreformatted),
347 Table => (StartTable, EndTable),
348 TableCell => (StartTableCell, EndTableCell),
349 TableHeader => (StartTableHeader, EndTableHeader),
350 TableRow => (StartTableRow, EndTableRow),
351 UnorderedListItem => (StartUnorderedListItem, EndUnorderedListItem),
352}
353
354#[cfg(test)]
355mod tests {
356 use alloc::vec::Vec;
357
358 use super::*;
359
360 struct MockSink {
361 events: Vec<Event>,
362 }
363
364 impl MockSink {
365 fn new() -> Self {
366 Self { events: Vec::new() }
367 }
368 }
369
370 impl EventSink for MockSink {
371 fn finish(self) -> Result<()> {
372 Ok(())
373 }
374
375 fn handle_event(&mut self, event: Event) -> Result<()> {
376 self.events.push(event);
377 Ok(())
378 }
379 }
380
381 fn send(sink: &mut StackTrackingSink<MockSink>, event: Event) {
382 let result = sink.handle_event(event);
383 assert!(result.is_ok());
384 }
385
386 #[test]
387 fn has_open_content_with_blockquote_returns_false() {
388 let mock = MockSink::new();
389 let mut sink = StackTrackingSink::new(mock);
390 sink.stack_mut().push(BlockKind::Document);
391 sink.stack_mut().push(BlockKind::Blockquote);
392 assert!(!sink.has_open_content());
394 }
395
396 #[test]
397 fn has_open_content_with_heading() {
398 let mock = MockSink::new();
399 let mut sink = StackTrackingSink::new(mock);
400 sink.stack_mut().push(BlockKind::Document);
401 sink.stack_mut().push(BlockKind::Heading);
402 assert!(sink.has_open_content());
403 }
404
405 #[test]
406 fn has_open_content_with_paragraph() {
407 let mock = MockSink::new();
408 let mut sink = StackTrackingSink::new(mock);
409 sink.stack_mut().push(BlockKind::Document);
410 sink.stack_mut().push(BlockKind::Paragraph);
411 assert!(sink.has_open_content());
412 }
413
414 #[test]
415 fn has_open_content_with_preformatted() {
416 let mock = MockSink::new();
417 let mut sink = StackTrackingSink::new(mock);
418 sink.stack_mut().push(BlockKind::Document);
419 sink.stack_mut().push(BlockKind::Preformatted);
420 assert!(sink.has_open_content());
421 }
422
423 #[test]
424 fn has_open_content_without_content_blocks() {
425 let mock = MockSink::new();
426 let mut sink = StackTrackingSink::new(mock);
427 sink.stack_mut().push(BlockKind::Document);
428 sink.stack_mut().push(BlockKind::Table);
429 sink.stack_mut().push(BlockKind::TableRow);
430 assert!(!sink.has_open_content());
431 }
432
433 #[test]
434 fn is_inside_finds_nested_kind() {
435 let mock = MockSink::new();
436 let mut sink = StackTrackingSink::new(mock);
437 sink.stack_mut().push(BlockKind::Document);
438 sink.stack_mut().push(BlockKind::Blockquote);
439 sink.stack_mut().push(BlockKind::Paragraph);
440 assert!(sink.is_inside(BlockKind::Blockquote));
441 }
442
443 #[test]
444 fn is_inside_returns_false_for_missing_kind() {
445 let mock = MockSink::new();
446 let mut sink = StackTrackingSink::new(mock);
447 sink.stack_mut().push(BlockKind::Document);
448 sink.stack_mut().push(BlockKind::Paragraph);
449 assert!(!sink.is_inside(BlockKind::Blockquote));
450 }
451
452 #[test]
453 fn stack_returns_current_stack() {
454 let mock = MockSink::new();
455 let mut sink = StackTrackingSink::new(mock);
456 sink.stack_mut().push(BlockKind::Document);
457 sink.stack_mut().push(BlockKind::Paragraph);
458 let stack = sink.stack();
459 assert_eq!(stack.len(), 2);
460 assert_eq!(stack.first(), Some(&BlockKind::Document));
461 assert_eq!(stack.get(1), Some(&BlockKind::Paragraph));
462 }
463
464 #[test]
465 fn passthrough_forwards_all_events() {
466 let mock = MockSink::new();
467 let mut sink = StackTrackingSink::new(mock);
468
469 send(
470 &mut sink,
471 Event::StartDocument {
472 id: None,
473 language: None,
474 metadata: None,
475 },
476 );
477 send(
478 &mut sink,
479 Event::StartParagraph {
480 alignment: None,
481 id: None,
482 },
483 );
484 send(
485 &mut sink,
486 Event::Text {
487 content: "hello".to_string(),
488 },
489 );
490 send(&mut sink, Event::EndParagraph);
491 send(&mut sink, Event::EndDocument);
492
493 assert_eq!(sink.sink().events.len(), 5);
494 assert!(matches!(
495 sink.sink().events.first(),
496 Some(Event::StartDocument { .. })
497 ));
498 assert!(matches!(
499 sink.sink().events.get(1),
500 Some(Event::StartParagraph { .. })
501 ));
502 assert!(matches!(
503 sink.sink().events.get(2),
504 Some(Event::Text { .. })
505 ));
506 assert!(matches!(
507 sink.sink().events.get(3),
508 Some(Event::EndParagraph)
509 ));
510 assert!(matches!(
511 sink.sink().events.get(4),
512 Some(Event::EndDocument)
513 ));
514 assert!(sink.stack().is_empty());
515 }
516
517 #[test]
518 fn orphan_text_gets_paragraph() {
519 let mock = MockSink::new();
520 let mut sink = StackTrackingSink::new(mock);
521
522 send(
523 &mut sink,
524 Event::StartDocument {
525 id: None,
526 language: None,
527 metadata: None,
528 },
529 );
530 send(
531 &mut sink,
532 Event::Text {
533 content: "hello".to_string(),
534 },
535 );
536 send(&mut sink, Event::EndDocument);
537
538 assert_eq!(sink.sink().events.len(), 5);
539 assert!(matches!(
540 sink.sink().events.first(),
541 Some(Event::StartDocument { .. })
542 ));
543 assert_eq!(
544 sink.sink().events.get(1),
545 Some(&Event::StartParagraph {
546 alignment: None,
547 id: None
548 })
549 );
550 assert!(matches!(
551 sink.sink().events.get(2),
552 Some(Event::Text { .. })
553 ));
554 assert_eq!(sink.sink().events.get(3), Some(&Event::EndParagraph));
555 assert!(matches!(
556 sink.sink().events.get(4),
557 Some(Event::EndDocument)
558 ));
559 }
560
561 #[test]
562 fn orphan_text_inside_table_cell_gets_paragraph() {
563 let mock = MockSink::new();
564 let mut sink = StackTrackingSink::new(mock);
565
566 send(
567 &mut sink,
568 Event::StartDocument {
569 id: None,
570 language: None,
571 metadata: None,
572 },
573 );
574 send(&mut sink, Event::StartTable { id: None });
575 send(&mut sink, Event::StartTableRow { id: None });
576 send(
577 &mut sink,
578 Event::StartTableCell {
579 colspan: None,
580 id: None,
581 rowspan: None,
582 },
583 );
584 send(
585 &mut sink,
586 Event::Text {
587 content: "cell".to_string(),
588 },
589 );
590 send(&mut sink, Event::EndTableCell);
591 send(&mut sink, Event::EndTableRow);
592 send(&mut sink, Event::EndTable);
593 send(&mut sink, Event::EndDocument);
594
595 assert_eq!(sink.sink().events.len(), 11);
596 assert_eq!(
597 sink.sink().events.get(4),
598 Some(&Event::StartParagraph {
599 alignment: None,
600 id: None
601 })
602 );
603 assert_eq!(sink.sink().events.get(6), Some(&Event::EndParagraph));
604 }
605
606 #[test]
607 fn orphan_text_inside_blockquote_gets_paragraph() {
608 let mock = MockSink::new();
609 let mut sink = StackTrackingSink::new(mock);
610
611 send(
612 &mut sink,
613 Event::StartDocument {
614 id: None,
615 language: None,
616 metadata: None,
617 },
618 );
619 send(&mut sink, Event::StartBlockQuote { id: None });
620 send(
621 &mut sink,
622 Event::Text {
623 content: "quoted".to_string(),
624 },
625 );
626 send(&mut sink, Event::EndBlockQuote);
627 send(&mut sink, Event::EndDocument);
628
629 assert_eq!(sink.sink().events.len(), 7);
631 assert!(matches!(
632 sink.sink().events.first(),
633 Some(Event::StartDocument { .. })
634 ));
635 assert!(matches!(
636 sink.sink().events.get(1),
637 Some(Event::StartBlockQuote { .. })
638 ));
639 assert_eq!(
640 sink.sink().events.get(2),
641 Some(&Event::StartParagraph {
642 alignment: None,
643 id: None
644 })
645 );
646 assert!(matches!(
647 sink.sink().events.get(3),
648 Some(Event::Text { .. })
649 ));
650 assert_eq!(sink.sink().events.get(4), Some(&Event::EndParagraph));
651 assert!(matches!(
652 sink.sink().events.get(5),
653 Some(Event::EndBlockQuote)
654 ));
655 assert!(matches!(
656 sink.sink().events.get(6),
657 Some(Event::EndDocument)
658 ));
659 }
660
661 #[test]
662 fn text_inside_paragraph_no_extra_insert() {
663 let mock = MockSink::new();
664 let mut sink = StackTrackingSink::new(mock);
665
666 send(
667 &mut sink,
668 Event::StartDocument {
669 id: None,
670 language: None,
671 metadata: None,
672 },
673 );
674 send(
675 &mut sink,
676 Event::StartParagraph {
677 alignment: None,
678 id: None,
679 },
680 );
681 send(
682 &mut sink,
683 Event::Text {
684 content: "hello".to_string(),
685 },
686 );
687 send(
688 &mut sink,
689 Event::Text {
690 content: "world".to_string(),
691 },
692 );
693 send(&mut sink, Event::EndParagraph);
694 send(&mut sink, Event::EndDocument);
695
696 assert_eq!(sink.sink().events.len(), 6);
697 }
698
699 #[test]
700 fn auto_close_paragraph_on_end_table() {
701 let mock = MockSink::new();
702 let mut sink = StackTrackingSink::new(mock);
703
704 send(
705 &mut sink,
706 Event::StartDocument {
707 id: None,
708 language: None,
709 metadata: None,
710 },
711 );
712 send(&mut sink, Event::StartTable { id: None });
713 send(&mut sink, Event::StartTableRow { id: None });
714 send(
715 &mut sink,
716 Event::StartTableCell {
717 colspan: None,
718 id: None,
719 rowspan: None,
720 },
721 );
722 send(
723 &mut sink,
724 Event::StartParagraph {
725 alignment: None,
726 id: None,
727 },
728 );
729 send(
730 &mut sink,
731 Event::Text {
732 content: "cell".to_string(),
733 },
734 );
735 send(&mut sink, Event::EndTable);
736
737 assert_eq!(sink.sink().events.len(), 10);
738 assert_eq!(sink.sink().events.get(6), Some(&Event::EndParagraph));
739 assert_eq!(sink.sink().events.get(7), Some(&Event::EndTableCell));
740 assert_eq!(sink.sink().events.get(8), Some(&Event::EndTableRow));
741 assert_eq!(sink.sink().events.get(9), Some(&Event::EndTable));
742 }
743
744 #[test]
745 fn auto_close_on_end_blockquote() {
746 let mock = MockSink::new();
747 let mut sink = StackTrackingSink::new(mock);
748
749 send(
750 &mut sink,
751 Event::StartDocument {
752 id: None,
753 language: None,
754 metadata: None,
755 },
756 );
757 send(&mut sink, Event::StartBlockQuote { id: None });
758 send(
759 &mut sink,
760 Event::StartParagraph {
761 alignment: None,
762 id: None,
763 },
764 );
765 send(
766 &mut sink,
767 Event::Text {
768 content: "quote".to_string(),
769 },
770 );
771 send(&mut sink, Event::EndBlockQuote);
772
773 assert_eq!(sink.sink().events.len(), 6);
774 assert_eq!(sink.sink().events.get(4), Some(&Event::EndParagraph));
775 assert_eq!(sink.sink().events.get(5), Some(&Event::EndBlockQuote));
776 }
777
778 #[test]
779 fn start_block_closes_open_paragraph() {
780 let mock = MockSink::new();
781 let mut sink = StackTrackingSink::new(mock);
782
783 send(
784 &mut sink,
785 Event::StartDocument {
786 id: None,
787 language: None,
788 metadata: None,
789 },
790 );
791 send(
792 &mut sink,
793 Event::StartParagraph {
794 alignment: None,
795 id: None,
796 },
797 );
798 send(&mut sink, Event::StartHeading { id: None, level: 1 });
799
800 assert_eq!(sink.sink().events.get(2), Some(&Event::EndParagraph));
801 assert!(matches!(
802 sink.sink().events.get(3),
803 Some(Event::StartHeading { .. })
804 ));
805 }
806
807 #[test]
808 fn start_document_while_document_open_returns_error() {
809 let mock = MockSink::new();
810 let mut sink = StackTrackingSink::new(mock);
811
812 send(
813 &mut sink,
814 Event::StartDocument {
815 id: None,
816 language: None,
817 metadata: None,
818 },
819 );
820 let result = sink.handle_event(Event::StartDocument {
821 id: None,
822 language: None,
823 metadata: None,
824 });
825
826 assert!(matches!(result, Err(Error::InvalidSequence { .. })));
827 }
828
829 #[test]
830 fn thematic_break_closes_open_paragraph() {
831 let mock = MockSink::new();
832 let mut sink = StackTrackingSink::new(mock);
833
834 send(
835 &mut sink,
836 Event::StartDocument {
837 id: None,
838 language: None,
839 metadata: None,
840 },
841 );
842 send(
843 &mut sink,
844 Event::StartParagraph {
845 alignment: None,
846 id: None,
847 },
848 );
849 send(&mut sink, Event::ThematicBreak { id: None });
850
851 assert_eq!(sink.sink().events.get(2), Some(&Event::EndParagraph));
852 assert!(matches!(
853 sink.sink().events.get(3),
854 Some(Event::ThematicBreak { .. })
855 ));
856 }
857
858 #[test]
859 fn end_document_closes_all() {
860 let mock = MockSink::new();
861 let mut sink = StackTrackingSink::new(mock);
862
863 send(
864 &mut sink,
865 Event::StartDocument {
866 id: None,
867 language: None,
868 metadata: None,
869 },
870 );
871 send(&mut sink, Event::StartBlockQuote { id: None });
872 send(
873 &mut sink,
874 Event::StartParagraph {
875 alignment: None,
876 id: None,
877 },
878 );
879 send(&mut sink, Event::EndDocument);
880
881 assert_eq!(sink.sink().events.len(), 6);
882 assert_eq!(sink.sink().events.get(3), Some(&Event::EndParagraph));
883 assert_eq!(sink.sink().events.get(4), Some(&Event::EndBlockQuote));
884 assert_eq!(sink.sink().events.get(5), Some(&Event::EndDocument));
885 assert!(sink.stack().is_empty());
886 }
887
888 #[test]
889 fn end_event_for_all_kinds() {
890 assert_eq!(end_event_for(BlockKind::Blockquote), Event::EndBlockQuote);
891 assert_eq!(end_event_for(BlockKind::Caption), Event::EndCaption);
892 assert_eq!(
893 end_event_for(BlockKind::DefinitionDetail),
894 Event::EndDefinitionDetail
895 );
896 assert_eq!(
897 end_event_for(BlockKind::DefinitionList),
898 Event::EndDefinitionList
899 );
900 assert_eq!(
901 end_event_for(BlockKind::DefinitionTerm),
902 Event::EndDefinitionTerm
903 );
904 assert_eq!(end_event_for(BlockKind::Document), Event::EndDocument);
905 assert_eq!(end_event_for(BlockKind::Footnote), Event::EndFootnote);
906 assert_eq!(end_event_for(BlockKind::Heading), Event::EndHeading);
907 assert_eq!(end_event_for(BlockKind::Link), Event::EndLink);
908 assert_eq!(
909 end_event_for(BlockKind::OrderedListItem),
910 Event::EndOrderedListItem
911 );
912 assert_eq!(
913 end_event_for(BlockKind::UnorderedListItem),
914 Event::EndUnorderedListItem
915 );
916 assert_eq!(end_event_for(BlockKind::Paragraph), Event::EndParagraph);
917 assert_eq!(
918 end_event_for(BlockKind::Preformatted),
919 Event::EndPreformatted
920 );
921 assert_eq!(end_event_for(BlockKind::Table), Event::EndTable);
922 assert_eq!(end_event_for(BlockKind::TableCell), Event::EndTableCell);
923 assert_eq!(end_event_for(BlockKind::TableHeader), Event::EndTableHeader);
924 assert_eq!(end_event_for(BlockKind::TableRow), Event::EndTableRow);
925 }
926}