use std::str::FromStr;
use crate::{error::Error, Result};
#[derive(Default, Debug, Getters, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct ServiceType {
name: String,
protocol: String,
sub_types: Vec<String>,
}
impl ServiceType {
pub fn new(name: &str, protocol: &str) -> Result<Self> {
Ok(Self {
name: check_valid_characters(name)?.to_string(),
protocol: check_valid_characters(protocol)?.to_string(),
sub_types: vec![],
})
}
pub fn with_sub_types(name: &str, protocol: &str, sub_types: Vec<&str>) -> Result<Self> {
Ok(Self {
name: check_valid_characters(name)?.to_string(),
protocol: check_valid_characters(protocol)?.to_string(),
sub_types: sub_types
.into_iter()
.map(|s| check_valid_characters(s).map(|valid| valid.to_string()))
.collect::<Result<Vec<_>>>()?,
})
}
}
impl FromStr for ServiceType {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
let parts = s.split('.').collect::<Vec<_>>();
if parts.len() != 2 {
return Err("invalid name and protocol".into());
}
let name = lstrip_underscore(check_valid_characters(parts[0])?);
let protocol = lstrip_underscore(check_valid_characters(parts[1])?);
Self::new(name, protocol)
}
}
pub fn check_valid_characters(part: &str) -> Result<&str> {
if part.contains('.') {
Err("invalid character: .".into())
} else if part.contains(',') {
Err("invalid character: ,".into())
} else if part.is_empty() {
Err("cannot be empty".into())
} else {
Ok(part)
}
}
pub fn lstrip_underscore(s: &str) -> &str {
if let Some(stripped) = s.strip_prefix('_') {
stripped
} else {
s
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_invalid() {
ServiceType::new(".http", "tcp").expect_err("invalid character: .");
ServiceType::new("http", ".tcp").expect_err("invalid character: .");
ServiceType::new(",http", "tcp").expect_err("invalid character: ,");
ServiceType::new("http", ",tcp").expect_err("invalid character: ,");
ServiceType::new("", "tcp").expect_err("cannot be empty");
ServiceType::new("http", "").expect_err("cannot be empty");
}
#[test]
fn from_str_requires_two_parts() {
ServiceType::from_str("_http").expect_err("invalid name and protocol");
ServiceType::from_str("_http._tcp._foo").expect_err("invalid name and protocol");
}
#[test]
fn from_str_success() {
assert_eq!(
ServiceType::from_str("_http._tcp").unwrap(),
ServiceType::new("http", "tcp").unwrap()
);
}
#[test]
fn check_valid_characters_returns_error_if_dot() {
check_valid_characters("foo.bar").expect_err("invalid character: .");
}
#[test]
fn check_valid_characters_returns_error_if_comma() {
check_valid_characters("foo,bar").expect_err("invalid character: ,");
}
#[test]
fn check_valid_characters_returns_error_if_empty() {
check_valid_characters("").expect_err("cannot be empty");
}
#[test]
fn check_valid_characters_success() {
assert_eq!(check_valid_characters("foo").unwrap(), "foo");
}
#[test]
fn lstrip_underscore_returns_stripped() {
assert_eq!(lstrip_underscore("_foo"), "foo");
}
#[test]
fn lstrip_underscore_returns_original() {
assert_eq!(lstrip_underscore("foo"), "foo");
}
}