use std::string::FromUtf8Error;
#[derive(Default, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(
feature = "serde",
derive(serde::Serialize, serde::Deserialize),
serde(transparent)
)]
pub struct Tags {
members: Vec<String>,
}
impl Tags {
pub fn new() -> Tags {
Self {
members: Vec::new(),
}
}
pub fn push(&mut self, tag: &str) {
self.members.push(tag.to_owned());
}
pub fn iter(&self) -> TagsIter<'_> {
TagsIter {
internal: self.members.iter(),
}
}
pub fn decode(buf: &[u8]) -> Result<Self, FromUtf8Error> {
Ok(buf
.split(|element| *element == 0)
.filter(|tag_name| !tag_name.is_empty())
.map(|tag_name| String::from_utf8(tag_name.to_vec()))
.collect::<Result<Vec<String>, _>>()?
.into())
}
pub fn encode(&self) -> Vec<u8> {
self.members.join("\0").into_bytes()
}
pub fn len(&self) -> usize {
self.members.len()
}
pub fn is_empty(&self) -> bool {
self.members.is_empty()
}
}
impl From<Vec<String>> for Tags {
fn from(members: Vec<String>) -> Tags {
Self { members }
}
}
pub struct TagsIter<'a> {
internal: std::slice::Iter<'a, String>,
}
impl<'a> Iterator for TagsIter<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
self.internal.next().map(|v| v.as_str())
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
#[cfg(feature = "serde")]
fn serialization() {
let serialized = r#"["foo","grandma's","coat?","bar"]"#;
let expected = Tags::from(vec![
"foo".to_owned(),
"grandma's".to_owned(),
"coat?".to_owned(),
"bar".to_owned(),
]);
assert_eq!(serialized, serde_json::to_string(&expected).unwrap());
assert_eq!(expected, serde_json::from_str::<Tags>(serialized).unwrap());
}
#[test]
fn decode_encode() {
let value = b"ez\0pz";
let tags = Tags::decode(value).unwrap();
assert_eq!(tags.iter().collect::<Vec<_>>(), &["ez", "pz"]);
assert_eq!(tags.encode(), value);
}
#[test]
fn decode_empty() {
let input = b"";
let expected: &[String] = &[];
let result = Tags::decode(input).unwrap();
assert_eq!(result.iter().collect::<Vec<_>>(), expected);
}
}