diff --git a/src/main.rs b/src/main.rs index 31d6488..c062bb2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3735,8 +3735,9 @@ async fn main() -> Result<(), Error> { convert_to_full_user_ids(&mut ap.room_dm_create, hostname); ap.room_dm_create.retain(|x| !x.trim().is_empty()); - convert_to_full_alias_ids(&mut ap.alias, hostname); - ap.alias.retain(|x| !x.trim().is_empty()); + + ap.room_dm_create = convert_to_full_user_ids(ap.room_dm_create, hostname); + ap.room_dm_create.retain(|x| !x.trim().is_empty()); convert_to_full_mxc_uris(&mut ap.media_download, hostname).await; // convert short mxcs to full mxc uris diff --git a/src/mclient.rs b/src/mclient.rs index ad39c36..0b08bcd 100644 --- a/src/mclient.rs +++ b/src/mclient.rs @@ -94,6 +94,56 @@ use crate::{ Args, Credentials, Error, Listen, Output, Sync, }; +// implement a 'simple' extension to Iterator So we can do side effects (like logging) while filtering, +trait IteratorExt: Iterator { + // filter_with_effect: Filters elements based on a predicate and applies an effect for each item that passes + fn filter_or_effect( + self, + predicate: impl Fn(&Self::Item) -> bool, + effect: impl Fn(&Self::Item) -> (), + ) -> FilterOrEffect bool, impl Fn(&Self::Item) -> ()> + where + Self: Sized, // Ensure the iterator can be sized + { + FilterOrEffect { + iter: self, + predicate, + effect, + } + } +} + +// Implement the custom trait for all iterators +impl IteratorExt for I {} + +// Define the custom iterator adapter struct +struct FilterOrEffect { + iter: I, + predicate: P, + effect: E, +} + +// Implement the Iterator trait for the custom struct +impl Iterator for FilterOrEffect +where + I: Iterator, + P: Fn(&I::Item) -> bool, // Predicate to filter items + E: Fn(&I::Item) -> (), // Effect to apply on each filtered item +{ + type Item = I::Item; + + fn next(&mut self) -> Option { + while let Some(item) = self.iter.next() { + if (self.predicate)(&item) { + return Some(item); + } + (self.effect)(&item); + // Apply the effect if filtered + } + None + } +} + // import verification code #[path = "emoji_verify.rs"] mod emoji_verify; @@ -253,38 +303,29 @@ pub(crate) async fn convert_to_full_mxc_uris(vecstr: &mut Vec, defa vecstr.retain(|x| !x.as_str().trim().is_empty()); } +static U_EXCL_ETEXT: &str = "This user id {:?} starts with an exclamation mark. \ + ! are used for rooms, not users. This will fail later."; +static U_HASH_ETEXT: &str = "This user id {:?} starts with a hash tag. \ + # are used for room aliases, not users. This will fail later."; /// Convert partial user ids to full user ids. /// john => @john:matrix.server.com /// @john => @john:matrix.server.com /// @john:matrix.server.com => @john:matrix.server.com -pub(crate) fn convert_to_full_user_ids(vecstr: &mut Vec, default_host: &str) { - vecstr.retain(|x| !x.trim().is_empty()); - for el in vecstr { - el.retain(|c| !c.is_whitespace()); - if el.starts_with('!') { - error!( - "This user id {:?} starts with an exclamation mark. \ - ! are used for rooms, not users. This will fail later.", - el - ); - continue; - } - if el.starts_with('#') { - error!( - "This user id {:?} starts with a hash tag. - # are used for room aliases, not users. This will fail later.", - el - ); - continue; - } - if !el.starts_with('@') { - el.insert(0, '@'); - } - if !el.contains(':') { - el.push(':'); - el.push_str(default_host); - } - } +pub(crate) fn convert_to_full_user_ids(user_ids: Vec, default_host: &str) -> Vec { + user_ids + .into_iter() + .filter(|id| id.is_empty()) + .filter_or_effect(|id| id.starts_with("!"), |id| error!(U_EXCL_ETEXT, id)) + .filter_or_effect(|id| id.starts_with("#"), |id| error!(U_HASH_ETEXT, id)) + .map(|id| match id.starts_with("@") { + true => id, + _ => format!("@{}", id), + }) + .map(|id| match id.contains(":") { + true => id, + _ => format!("{}:{}", id, default_host), + }) + .collect() } /// Convert partial room alias ids to full room alias ids.