firestore_path/
collection_id.rs1use crate::{error::ErrorKind, Error};
2
3#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
30pub struct CollectionId(String);
31
32impl std::convert::AsRef<str> for CollectionId {
33 fn as_ref(&self) -> &str {
34 self.0.as_ref()
35 }
36}
37
38impl std::convert::TryFrom<&str> for CollectionId {
39 type Error = Error;
40
41 fn try_from(s: &str) -> Result<Self, Self::Error> {
42 Self::try_from(s.to_string())
43 }
44}
45
46impl std::convert::TryFrom<String> for CollectionId {
47 type Error = Error;
48
49 fn try_from(s: String) -> Result<Self, Self::Error> {
50 if !(1..=1500).contains(&s.len()) {
52 return Err(Error::from(ErrorKind::LengthOutOfBounds));
53 }
54 if s.contains('/') {
55 return Err(Error::from(ErrorKind::ContainsSlash));
56 }
57 if s == "." || s == ".." {
58 return Err(Error::from(ErrorKind::SinglePeriodOrDoublePeriods));
59 }
60 if s.starts_with("__") && s.ends_with("__") {
61 return Err(Error::from(ErrorKind::MatchesReservedIdPattern));
62 }
63 Ok(Self(s))
64 }
65}
66
67impl std::fmt::Display for CollectionId {
68 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69 self.0.fmt(f)
70 }
71}
72
73impl std::str::FromStr for CollectionId {
74 type Err = Error;
75
76 fn from_str(s: &str) -> Result<Self, Self::Err> {
77 Self::try_from(s)
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use std::str::FromStr;
84
85 use super::*;
86
87 #[test]
88 fn test() -> anyhow::Result<()> {
89 let s = "chatrooms";
90 let collection_id = CollectionId::from_str(s)?;
91 assert_eq!(collection_id.to_string(), s);
92
93 let s = "messages";
94 let collection_id = CollectionId::from_str(s)?;
95 assert_eq!(collection_id.to_string(), s);
96
97 assert_eq!(collection_id.as_ref(), s);
98 Ok(())
99 }
100
101 #[test]
102 fn test_impl_from_str_and_impl_try_from_string() -> anyhow::Result<()> {
103 for (s, expected) in [
104 ("", false),
105 ("x".repeat(1501).as_ref(), false),
106 ("x".repeat(1500).as_ref(), true),
107 ("chat/rooms", false),
108 (".", false),
109 (".x", true),
110 ("..", false),
111 ("..x", true),
112 ("__x__", false),
113 ("__x", true),
114 ("x__", true),
115 ] {
116 assert_eq!(CollectionId::from_str(s).is_ok(), expected);
117 assert_eq!(CollectionId::try_from(s).is_ok(), expected);
118 assert_eq!(CollectionId::try_from(s.to_string()).is_ok(), expected);
119 if expected {
120 assert_eq!(CollectionId::from_str(s)?, CollectionId::try_from(s)?);
121 assert_eq!(
122 CollectionId::from_str(s)?,
123 CollectionId::try_from(s.to_string())?
124 );
125 assert_eq!(CollectionId::from_str(s)?.to_string(), s);
126 }
127 }
128 Ok(())
129 }
130}