opentalk_client_data_persistence/
opentalk_account_id.rs1use std::str::FromStr;
6
7use serde::Serialize;
8use snafu::{Snafu, ensure};
9
10pub const OPENTALK_ACCOUNT_ID_MIN_LENGTH: usize = 1;
11
12pub const OPENTALK_ACCOUNT_ID_MAX_LENGTH: usize = 100;
13
14#[derive(
16 Debug,
17 Clone,
18 PartialEq,
19 Eq,
20 PartialOrd,
21 Ord,
22 Hash,
23 Serialize,
24 derive_more::AsRef,
25 derive_more::Deref,
26 serde_with::DeserializeFromStr,
27)]
28pub struct OpenTalkAccountId(String);
29
30#[derive(Debug, Snafu)]
32pub enum ParseOpenTalkAccountIdError {
33 #[snafu(display("OpenTalk account id may only contain alphanumeric characters and \"-\""))]
35 InvalidCharacters,
36
37 #[snafu(display("OpenTalk account id must have at least {min_length} characters"))]
39 TooShort {
40 min_length: usize,
42 },
43
44 #[snafu(display("OpenTalk account id must not be longer than {max_length} characters"))]
46 TooLong {
47 max_length: usize,
49 },
50}
51
52impl FromStr for OpenTalkAccountId {
53 type Err = ParseOpenTalkAccountIdError;
54
55 fn from_str(s: &str) -> Result<Self, Self::Err> {
56 ensure_is_valid(s)?;
57
58 Ok(Self(s.to_string()))
59 }
60}
61
62fn ensure_is_valid(s: &str) -> Result<(), ParseOpenTalkAccountIdError> {
63 ensure!(
64 s.chars().all(|c| c.is_ascii_alphanumeric() || c == '-'),
65 InvalidCharactersSnafu
66 );
67
68 ensure!(
69 s.len() >= OPENTALK_ACCOUNT_ID_MIN_LENGTH,
70 TooShortSnafu {
71 min_length: OPENTALK_ACCOUNT_ID_MIN_LENGTH
72 }
73 );
74
75 ensure!(
76 s.len() <= OPENTALK_ACCOUNT_ID_MAX_LENGTH,
77 TooLongSnafu {
78 max_length: OPENTALK_ACCOUNT_ID_MAX_LENGTH
79 }
80 );
81
82 Ok(())
83}
84
85#[cfg(test)]
86mod tests {
87 use pretty_assertions::assert_matches;
88
89 use super::OpenTalkAccountId;
90 use crate::opentalk_account_id::ParseOpenTalkAccountIdError;
91
92 #[test]
93 fn success_from_str() {
94 let id: Result<OpenTalkAccountId, ParseOpenTalkAccountIdError> = "opentalk-id".parse();
95 assert!(id.is_ok());
96 }
97
98 #[test]
99 fn error_to_short_from_str() {
100 let id: Result<OpenTalkAccountId, ParseOpenTalkAccountIdError> = "".parse();
101 assert_matches!(
102 id,
103 Err(ParseOpenTalkAccountIdError::TooShort { min_length: _ })
104 );
105 }
106
107 #[test]
108 fn error_to_long_from_str() {
109 let id: Result<OpenTalkAccountId, ParseOpenTalkAccountIdError> = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".parse();
110 assert_matches!(
111 id,
112 Err(ParseOpenTalkAccountIdError::TooLong { max_length: _ })
113 );
114 }
115
116 #[test]
117 fn error_invalid_char_from_str() {
118 let id: Result<OpenTalkAccountId, ParseOpenTalkAccountIdError> = "*".parse();
119 assert_matches!(id, Err(ParseOpenTalkAccountIdError::InvalidCharacters));
120 }
121}