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