1use super::ParseMention;
2use std::{marker::PhantomData, str::CharIndices};
3
4#[derive(Clone, Debug)]
29pub struct MentionIter<'a, T> {
30 buf: &'a str,
31 chars: CharIndices<'a>,
32 phantom: PhantomData<T>,
33}
34
35impl<'a, T> MentionIter<'a, T> {
36 #[must_use]
37 pub(in crate::parse) fn new(buf: &'a str) -> Self {
38 let chars = buf.char_indices();
39
40 Self {
41 buf,
42 chars,
43 phantom: PhantomData,
44 }
45 }
46
47 #[must_use]
49 pub const fn as_str(&self) -> &'a str {
50 self.buf
51 }
52}
53
54impl<T: ParseMention> Iterator for MentionIter<'_, T> {
55 type Item = (T, usize, usize);
60
61 fn next(&mut self) -> Option<Self::Item> {
62 loop {
66 let (start, '<') = self.chars.next()? else {
67 continue;
68 };
69
70 let mut found = false;
71
72 for sigil in T::SIGILS {
73 if self.chars.as_str().starts_with(sigil) {
74 found = true;
75
76 for _ in 0..sigil.chars().count() {
77 self.chars.next();
78 }
79 }
80 }
81
82 if !found {
83 continue;
84 }
85
86 let Some((end, _)) = self.chars.find(|c| c.1 == '>') else {
87 continue;
88 };
89
90 let buf = self.buf.get(start..=end)?;
91
92 if let Ok(id) = T::parse(buf) {
93 return Some((id, start, end));
94 }
95 }
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use crate::timestamp::{Timestamp, TimestampStyle};
102
103 use super::{
104 super::{MentionType, ParseMention},
105 MentionIter,
106 };
107 use static_assertions::{assert_impl_all, assert_obj_safe};
108 use std::fmt::Debug;
109 use twilight_model::id::{
110 marker::{ChannelMarker, EmojiMarker, RoleMarker, UserMarker},
111 Id,
112 };
113
114 assert_impl_all!(MentionIter<'_, Id<ChannelMarker>>: Clone, Debug, Iterator, Send, Sync);
115 assert_impl_all!(MentionIter<'_, Id<EmojiMarker>>: Clone, Debug, Iterator, Send, Sync);
116 assert_impl_all!(MentionIter<'_, MentionType>: Clone, Debug, Iterator, Send, Sync);
117 assert_impl_all!(MentionIter<'_, Id<RoleMarker>>: Clone, Debug, Iterator, Send, Sync);
118 assert_impl_all!(MentionIter<'_, Id<UserMarker>>: Clone, Debug, Iterator, Send, Sync);
119 assert_obj_safe!(
120 MentionIter<'_, Id<ChannelMarker>>,
121 MentionIter<'_, Id<EmojiMarker>>,
122 MentionIter<'_, MentionType>,
123 MentionIter<'_, Id<RoleMarker>>,
124 MentionIter<'_, Id<UserMarker>>,
125 );
126
127 #[test]
128 fn iter_channel_id() {
129 let mut iter = Id::<ChannelMarker>::iter("<#123>");
130 assert_eq!(Id::new(123), iter.next().unwrap().0);
131 assert!(iter.next().is_none());
132 }
133
134 #[test]
135 fn iter_multiple_ids() {
136 let buf = "one <@123>two<#456><@789> ----";
137 let mut iter = Id::<UserMarker>::iter(buf);
138 assert_eq!(Id::new(123), iter.next().unwrap().0);
139 let (mention, start, end) = iter.next().unwrap();
140 assert_eq!(Id::new(789), mention);
141 assert_eq!(19, start);
142 assert_eq!(24, end);
143 assert!(iter.next().is_none());
144 }
145
146 #[test]
147 fn iter_emoji_ids() {
148 let mut iter = Id::<EmojiMarker>::iter("some <:name:123> emojis <:emoji:456>");
149 assert_eq!(Id::new(123), iter.next().unwrap().0);
150 assert_eq!(Id::new(456), iter.next().unwrap().0);
151 assert!(iter.next().is_none());
152 }
153
154 #[test]
155 fn iter_mention_type() {
156 let mut iter = MentionType::iter("<#12><:name:34><@&56><@78>");
157 assert_eq!(MentionType::Channel(Id::new(12)), iter.next().unwrap().0);
158 assert_eq!(MentionType::Emoji(Id::new(34)), iter.next().unwrap().0);
159 assert_eq!(MentionType::Role(Id::new(56)), iter.next().unwrap().0);
160 assert_eq!(MentionType::User(Id::new(78)), iter.next().unwrap().0);
161 assert!(iter.next().is_none());
162 }
163
164 #[test]
165 fn iter_mention_type_with_timestamp() {
166 let mut iter = MentionType::iter("<#12> <t:34> <t:56:d>");
167 assert_eq!(MentionType::Channel(Id::new(12)), iter.next().unwrap().0);
168 assert_eq!(
169 MentionType::Timestamp(Timestamp::new(34, None)),
170 iter.next().unwrap().0
171 );
172 assert_eq!(
173 MentionType::Timestamp(Timestamp::new(56, Some(TimestampStyle::ShortDate))),
174 iter.next().unwrap().0
175 );
176 assert!(iter.next().is_none());
177 }
178
179 #[test]
180 fn iter_role_ids() {
181 let mut iter = Id::<RoleMarker>::iter("some <@&123> roles <@&456>");
182 assert_eq!(Id::new(123), iter.next().unwrap().0);
183 assert_eq!(Id::new(456), iter.next().unwrap().0);
184 assert!(iter.next().is_none());
185 }
186
187 #[test]
188 fn iter_timestamps() {
189 let mut iter = Timestamp::iter("some <t:123> roles <t:456:t>");
190 assert_eq!(Timestamp::new(123, None), iter.next().unwrap().0);
191 assert_eq!(
192 Timestamp::new(456, Some(TimestampStyle::ShortTime)),
193 iter.next().unwrap().0
194 );
195 assert!(iter.next().is_none());
196 }
197
198 #[test]
199 fn iter_user_ids() {
200 let mut iter = Id::<UserMarker>::iter("some <@123>users<@456>");
201 assert_eq!(Id::new(123), iter.next().unwrap().0);
202 assert_eq!(Id::new(456), iter.next().unwrap().0);
203 assert!(iter.next().is_none());
204 }
205
206 #[test]
207 fn iter_no_id() {
208 let mention = "this is not <# actually a mention";
209 let mut iter = Id::<ChannelMarker>::iter(mention);
210
211 assert!(iter.next().is_none());
212 }
213
214 #[test]
215 fn iter_ignores_other_types() {
216 let mention = "<#123> <:name:456> <@&789>";
217 let mut iter = Id::<UserMarker>::iter(mention);
218
219 assert!(iter.next().is_none());
220 }
221
222 #[test]
223 fn iter_as_str() {
224 let buf = "a buf";
225 let mut iter = Id::<RoleMarker>::iter(buf);
226 assert_eq!(buf, iter.as_str());
227 assert!(iter.next().is_none());
229 assert_eq!(buf, iter.as_str());
230 }
231}