1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
use super::*;
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Channel(String);
impl Channel {
pub(crate) fn validate<C>(channel: C) -> Result<Channel, Error>
where
C: Into<Channel>,
{
let channel = channel.into();
if channel.0.is_empty() {
return Err(Error::EmptyChannelName);
}
Ok(channel)
}
}
impl<T> From<T> for Channel
where
T: ToString,
{
fn from(name: T) -> Self {
let name = name.to_string();
if name.is_empty() {
return Self("".into());
}
let name = name.to_lowercase();
let name = if !name.starts_with('#') {
["#", name.as_str()].concat()
} else {
name.to_string()
};
Self(name)
}
}
impl std::ops::Deref for Channel {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn good_channel() {
assert_eq!(Channel::validate("museun").unwrap().0, "#museun");
}
#[test]
fn bad_channel() {
let err = Channel::validate("").unwrap_err();
if let Error::EmptyChannelName = err {
} else {
panic!("wrong error: {}", err)
}
}
#[test]
fn into_channel() {
let s = String::from("museun");
let channels: Vec<Channel> = vec![
s.as_str().into(),
(&s).into(),
s.clone().into(),
s.into(),
"museun".into(),
String::from("museun").into(),
(&String::from("museun")).into(),
];
for name in channels {
assert_eq!(*name, "#museun");
}
}
}