1use std::borrow::Cow;
2
3use crate::Comment;
4
5const SCISSORS_MARKER: &str = "------------------------ >8 ------------------------";
6
7#[derive(Debug, PartialEq, Eq, Clone)]
9pub struct Scissors<'a> {
10 scissors: Cow<'a, str>,
11}
12
13impl<'a> Scissors<'a> {
14 pub(crate) fn guess_comment_character(message: &str) -> Option<char> {
15 Self::guess_comment_char_from_scissors(message)
16 .or_else(|| Self::guess_comment_char_from_last_possibility(message))
17 }
18
19 fn guess_comment_char_from_last_possibility(message: &str) -> Option<char> {
20 message
21 .lines()
22 .filter_map(|line| {
23 line.chars()
24 .next()
25 .filter(|first_letter| Comment::is_legal_comment_char(*first_letter))
26 })
27 .last()
28 }
29
30 fn guess_comment_char_from_scissors(message: &str) -> Option<char> {
31 message
32 .lines()
33 .filter_map(|line| {
34 let mut line_chars = line.chars();
35 let first_character = line_chars.next();
36 first_character.filter(|cc| Comment::is_legal_comment_char(*cc))?;
37 line_chars.next().filter(|cc| *cc == ' ')?;
38
39 if SCISSORS_MARKER != line_chars.as_str() {
40 return None;
41 }
42
43 first_character
44 })
45 .last()
46 }
47
48 pub(crate) fn parse_sections(message: &str) -> (Cow<'a, str>, Option<Self>) {
49 message
50 .lines()
51 .position(|line| line.ends_with(SCISSORS_MARKER))
52 .map_or_else(
53 || (message.to_string().into(), None),
54 |scissors_position| {
55 let lines = message.lines().collect::<Vec<_>>();
56 let body = lines
57 .clone()
58 .into_iter()
59 .take(scissors_position)
60 .collect::<Vec<_>>()
61 .join("\n");
62 let scissors_string = &lines
63 .into_iter()
64 .skip(scissors_position)
65 .collect::<Vec<_>>()
66 .join("\n");
67
68 let scissors = if message.ends_with('\n') {
69 Self::from(format!("{scissors_string}\n"))
70 } else {
71 Self::from(scissors_string.clone())
72 };
73
74 (body.into(), Some(scissors))
75 },
76 )
77 }
78}
79
80impl<'a> From<Cow<'a, str>> for Scissors<'a> {
81 fn from(scissors: Cow<'a, str>) -> Self {
82 Self { scissors }
83 }
84}
85
86impl<'a> From<&'a str> for Scissors<'a> {
87 fn from(scissors: &'a str) -> Self {
88 Self {
89 scissors: scissors.into(),
90 }
91 }
92}
93
94impl<'a> From<String> for Scissors<'a> {
95 fn from(scissors: String) -> Self {
96 Self {
97 scissors: scissors.into(),
98 }
99 }
100}
101
102impl<'a> From<Scissors<'a>> for String {
103 fn from(scissors: Scissors<'a>) -> Self {
104 scissors.scissors.into()
105 }
106}