ssh_packet/arch/
namelist.rs

1use binrw::binrw;
2
3use super::Ascii;
4
5/// A `name-list` as defined in the SSH protocol,
6/// a `,`-separated list of **ASCII** identifiers.
7///
8/// see <https://datatracker.ietf.org/doc/html/rfc4251#section-5>.
9#[binrw]
10#[derive(Debug, Default, Clone)]
11pub struct NameList<'b>(pub Ascii<'b>);
12
13impl NameList<'_> {
14    /// Retrieve the first name from `self` that is also in `other`.
15    pub fn preferred_in(&self, other: &Self) -> Option<Ascii<'_>> {
16        self.into_iter()
17            .find(|this| other.into_iter().any(|other| this == &other))
18    }
19}
20
21impl<A> FromIterator<A> for NameList<'_>
22where
23    A: AsRef<str>,
24{
25    fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self {
26        Self(
27            Ascii::owned(
28                iter.into_iter()
29                    .filter(|item| !item.as_ref().is_empty())
30                    .map(|item| item.as_ref().to_owned())
31                    .collect::<Vec<_>>()
32                    .join(","),
33            )
34            .expect("unable to collect the iterator into a `NameList`"),
35        )
36    }
37}
38
39impl<'a: 'b, 'b> IntoIterator for &'a NameList<'b> {
40    type Item = Ascii<'b>;
41
42    type IntoIter =
43        std::iter::FilterMap<std::str::Split<'b, char>, fn(&'b str) -> Option<Self::Item>>;
44
45    fn into_iter(self) -> Self::IntoIter {
46        #[allow(deprecated)]
47        self.0
48            .split(',')
49            .filter_map(|name| (!name.is_empty()).then_some(Ascii::borrowed_unchecked(name)))
50    }
51}