use crate::error::{Error, Result};
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Capabilities(Vec<String>);
impl Capabilities {
pub fn parse(raw: &str) -> Self {
Capabilities(
raw.split(' ')
.map(str::trim)
.filter(|s| !s.is_empty())
.map(str::to_owned)
.collect(),
)
}
pub fn parse_bytes(raw: &[u8]) -> Result<Self> {
let text = std::str::from_utf8(raw)
.map_err(|_| Error::protocol("the capability list is not UTF-8"))?;
Ok(Self::parse(text))
}
pub fn from_items<S: Into<String>>(items: impl IntoIterator<Item = S>) -> Self {
Capabilities(items.into_iter().map(Into::into).collect())
}
pub fn has(&self, name: &str) -> bool {
self.0.iter().any(|c| {
c == name || (c.starts_with(name) && c.as_bytes().get(name.len()) == Some(&b'='))
})
}
pub fn value(&self, name: &str) -> Option<&str> {
self.0.iter().find_map(|c| {
let rest = c.strip_prefix(name)?;
rest.strip_prefix('=')
})
}
pub fn all(&self) -> &[String] {
&self.0
}
pub fn render(&self) -> String {
self.0.join(" ")
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
pub fn split(line: &[u8]) -> (&[u8], Option<&[u8]>) {
match line.iter().position(|&b| b == 0) {
Some(i) => (&line[..i], Some(&line[i + 1..])),
None => (line, None),
}
}
pub fn attach(line: &mut Vec<u8>, capabilities: &[String]) {
if capabilities.is_empty() {
return;
}
line.push(0);
line.extend_from_slice(capabilities.join(" ").as_bytes());
}