Skip to main content

brokaw/types/response/
capabilities.rs

1use std::collections::{hash_map, HashMap, HashSet};
2use std::convert::TryFrom;
3use std::fmt;
4
5use crate::error::{Error, Result};
6use crate::types::prelude::*;
7use crate::types::response::util::err_if_not_kind;
8
9/// Server capabilities
10#[derive(Clone, Debug, Eq, PartialEq)]
11pub struct Capabilities(HashMap<String, Capability>);
12
13/// A capability advertised by the server
14#[derive(Clone, Debug, Eq, PartialEq)]
15pub struct Capability {
16    pub name: String,
17    pub args: Option<HashSet<String>>,
18}
19
20impl Capabilities {
21    /// An iterator over the capabilities
22    pub fn iter(&self) -> Iter<'_> {
23        Iter {
24            inner: self.0.values(),
25        }
26    }
27
28    /// Retrieve a capability if it exists
29    pub fn get(&self, key: impl AsRef<str>) -> Option<&Capability> {
30        self.0.get(key.as_ref())
31    }
32}
33
34impl fmt::Display for Capability {
35    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36        write!(f, "{}", self.name)?;
37        if let Some(args) = self.args.as_ref() {
38            args.iter()
39                .map(|arg| {
40                    write!(f, " {}", arg)?;
41                    Ok::<_, fmt::Error>(())
42                })
43                .for_each(drop);
44        }
45
46        Ok(())
47    }
48}
49
50/// Created by [`Capabilities::iter`]
51#[derive(Clone, Debug)]
52pub struct Iter<'a> {
53    inner: hash_map::Values<'a, String, Capability>,
54}
55
56impl<'a> Iterator for Iter<'a> {
57    type Item = &'a Capability;
58
59    fn next(&mut self) -> Option<Self::Item> {
60        self.inner.next()
61    }
62}
63
64impl TryFrom<&RawResponse> for Capabilities {
65    type Error = Error;
66
67    /// Parse capabilities from a response
68    ///
69    /// The specific format is taken from [RFC 3977](https://tools.ietf.org/html/rfc3977#section-9.5)
70    fn try_from(resp: &RawResponse) -> Result<Self> {
71        err_if_not_kind(resp, Kind::Capabilities)?;
72
73        let db_iter = resp
74            .data_blocks
75            .as_ref()
76            .ok_or_else(|| Error::de("Missing data blocks."))
77            .map(DataBlocks::unterminated)?;
78
79        let capabilities: HashMap<String, Capability> = db_iter
80            .map(String::from_utf8_lossy)
81            .map(|entry| {
82                let mut entry_iter = entry.split_whitespace().peekable();
83                let label = entry_iter
84                    .next()
85                    .map(ToString::to_string)
86                    .ok_or_else(|| Error::de("Entry does not have a label"))?;
87
88                let args = if entry_iter.peek().is_some() {
89                    Some(entry_iter.map(ToString::to_string).collect::<HashSet<_>>())
90                } else {
91                    None
92                };
93
94                let cap = Capability {
95                    name: label.clone(),
96                    args,
97                };
98
99                Ok((label, cap))
100            })
101            .collect::<Result<_>>()?;
102
103        Ok(Self(capabilities))
104    }
105}