firestore_path/
document_name.rs

1use std::str::FromStr;
2
3use crate::{
4    error::ErrorKind, CollectionId, CollectionName, CollectionPath, DatabaseName, DocumentId,
5    DocumentPath, Error, RootDocumentName,
6};
7
8/// A document name.
9///
10/// # Format
11///
12/// `{database_name}/{document_path}`
13///
14/// # Examples
15///
16/// ```rust
17/// # fn main() -> anyhow::Result<()> {
18/// use firestore_path::DocumentName;
19/// # use firestore_path::{CollectionId,CollectionName,DatabaseName,DocumentId,DocumentPath};
20/// # use std::str::FromStr;
21///
22/// let document_name = DocumentName::from_str(
23///     "projects/my-project/databases/my-database/documents/chatrooms/chatroom1"
24/// )?;
25/// assert_eq!(
26///     document_name.to_string(),
27///     "projects/my-project/databases/my-database/documents/chatrooms/chatroom1"
28/// );
29///
30/// assert_eq!(
31///     document_name.clone().collection("messages")?,
32///     CollectionName::from_str(
33///         "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages"
34///     )?
35/// );
36/// assert_eq!(document_name.collection_id(), &CollectionId::from_str("chatrooms")?);
37/// assert_eq!(
38///     document_name.database_name(),
39///     &DatabaseName::from_str("projects/my-project/databases/my-database")?
40/// );
41/// assert_eq!(document_name.document_id(), &DocumentId::from_str("chatroom1")?);
42/// assert_eq!(document_name.document_path(), &DocumentPath::from_str("chatrooms/chatroom1")?);
43/// assert_eq!(
44///     document_name.clone().parent(),
45///     CollectionName::from_str("projects/my-project/databases/my-database/documents/chatrooms")?
46/// );
47///
48/// assert_eq!(
49///     DocumentPath::from(document_name.clone()),
50///     DocumentPath::from_str("chatrooms/chatroom1")?
51/// );
52///
53/// #     Ok(())
54/// # }
55/// ```
56///
57#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
58pub struct DocumentName {
59    document_path: DocumentPath,
60    root_document_name: RootDocumentName,
61}
62
63impl DocumentName {
64    /// Creates a new `DocumentName`.
65    ///
66    /// # Examples
67    ///
68    /// ```rust
69    /// # fn main() -> anyhow::Result<()> {
70    /// use firestore_path::{DatabaseName,DocumentName,DocumentPath,RootDocumentName};
71    /// use std::str::FromStr;
72    ///
73    /// let root_document_name = RootDocumentName::from_str("projects/my-project/databases/my-database/documents")?;
74    /// let document_path = DocumentPath::from_str("chatrooms/chatroom1")?;
75    /// let document_name = DocumentName::new(root_document_name, document_path);
76    /// assert_eq!(
77    ///     document_name.to_string(),
78    ///     "projects/my-project/databases/my-database/documents/chatrooms/chatroom1"
79    /// );
80    ///
81    /// let database_name = DatabaseName::from_str("projects/my-project/databases/my-database")?;
82    /// let document_path = DocumentPath::from_str("chatrooms/chatroom1")?;
83    /// let document_name = DocumentName::new(database_name, document_path);
84    /// assert_eq!(
85    ///     document_name.to_string(),
86    ///     "projects/my-project/databases/my-database/documents/chatrooms/chatroom1"
87    /// );
88    /// #     Ok(())
89    /// # }
90    /// ```
91    ///
92    pub fn new<D>(root_document_name: D, document_path: DocumentPath) -> Self
93    where
94        D: Into<RootDocumentName>,
95    {
96        Self {
97            document_path,
98            root_document_name: root_document_name.into(),
99        }
100    }
101
102    /// Creates a new `CollectionName` from this `DocumentName` and `collection_path`.
103    ///
104    /// # Examples
105    ///
106    /// ```rust
107    /// # fn main() -> anyhow::Result<()> {
108    /// use firestore_path::{CollectionId,CollectionName,CollectionPath,DocumentName};
109    /// use std::str::FromStr;
110    ///
111    /// let document_name = DocumentName::from_str(
112    ///     "projects/my-project/databases/my-database/documents/chatrooms/chatroom1"
113    /// )?;
114    /// assert_eq!(
115    ///     document_name.collection("messages")?,
116    ///     CollectionName::from_str(
117    ///         "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages"
118    ///     )?
119    /// );
120    /// assert_eq!(
121    ///     document_name.collection("messages/message1/col")?,
122    ///     CollectionName::from_str(
123    ///         "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages/message1/col"
124    ///     )?
125    /// );
126    /// assert_eq!(
127    ///     document_name.collection(CollectionId::from_str("messages")?)?,
128    ///     CollectionName::from_str(
129    ///         "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages"
130    ///     )?
131    /// );
132    /// assert_eq!(
133    ///     document_name.collection(CollectionPath::from_str("messages/message1/col")?)?,
134    ///     CollectionName::from_str(
135    ///         "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages/message1/col"
136    ///     )?
137    /// );
138    /// #     Ok(())
139    /// # }
140    /// ```
141    ///
142    pub fn collection<E, T>(&self, collection_path: T) -> Result<CollectionName, Error>
143    where
144        E: std::fmt::Display,
145        T: TryInto<CollectionPath, Error = E>,
146    {
147        self.clone().into_collection(collection_path)
148    }
149
150    /// Returns the `CollectionId` of this `DocumentName`.
151    ///
152    /// # Examples
153    ///
154    /// ```rust
155    /// # fn main() -> anyhow::Result<()> {
156    /// use firestore_path::{CollectionId,DocumentName};
157    /// use std::str::FromStr;
158    ///
159    /// let document_name = DocumentName::from_str(
160    ///     "projects/my-project/databases/my-database/documents/chatrooms/chatroom1"
161    /// )?;
162    /// assert_eq!(
163    ///     document_name.collection_id(),
164    ///     &CollectionId::from_str("chatrooms")?
165    /// );
166    /// #     Ok(())
167    /// # }
168    /// ```
169    pub fn collection_id(&self) -> &CollectionId {
170        self.document_path.collection_id()
171    }
172
173    /// Returns the `DatabaseName` of this `DocumentName`.
174    ///
175    /// # Examples
176    ///
177    /// ```rust
178    /// # fn main() -> anyhow::Result<()> {
179    /// use firestore_path::{DatabaseName,DocumentName,DocumentPath};
180    /// use std::str::FromStr;
181    ///
182    /// let document_name = DocumentName::from_str(
183    ///     "projects/my-project/databases/my-database/documents/chatrooms/chatroom1"
184    /// )?;
185    /// assert_eq!(
186    ///     document_name.database_name(),
187    ///     &DatabaseName::from_str("projects/my-project/databases/my-database")?
188    /// );
189    /// #     Ok(())
190    /// # }
191    /// ```
192    ///
193    pub fn database_name(&self) -> &DatabaseName {
194        self.root_document_name.as_database_name()
195    }
196
197    /// Creates a new `DocumentName` from this `DocumentName` and `document_path`.
198    ///
199    /// # Examples
200    ///
201    /// ```rust
202    /// # fn main() -> anyhow::Result<()> {
203    /// use firestore_path::{CollectionPath,DocumentName,DocumentPath};
204    /// use std::str::FromStr;
205    ///
206    /// let document_name = DocumentName::from_str(
207    ///     "projects/my-project/databases/my-database/documents/chatrooms/chatroom1"
208    /// )?;
209    /// assert_eq!(
210    ///     document_name.doc("messages/message1")?,
211    ///     DocumentName::from_str(
212    ///         "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages/message1"
213    ///     )?
214    /// );
215    /// assert_eq!(
216    ///     document_name.doc("messages/message1/col/doc")?,
217    ///     DocumentName::from_str(
218    ///         "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages/message1/col/doc"
219    ///     )?
220    /// );
221    /// assert_eq!(
222    ///     document_name.doc(DocumentPath::from_str("messages/message1")?)?,
223    ///     DocumentName::from_str(
224    ///         "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages/message1"
225    ///     )?
226    /// );
227    /// assert_eq!(
228    ///     document_name.doc(DocumentPath::from_str("messages/message1/col/doc")?)?,
229    ///     DocumentName::from_str(
230    ///         "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages/message1/col/doc"
231    ///     )?
232    /// );
233    /// #     Ok(())
234    /// # }
235    /// ```
236    ///
237    pub fn doc<E, T>(&self, document_path: T) -> Result<DocumentName, Error>
238    where
239        E: std::fmt::Display,
240        T: TryInto<DocumentPath, Error = E>,
241    {
242        self.clone().into_doc(document_path)
243    }
244
245    /// Returns the `DocumentId` of this `DocumentName`.
246    ///
247    /// # Examples
248    ///
249    /// ```rust
250    /// # fn main() -> anyhow::Result<()> {
251    /// use firestore_path::{DocumentId,DocumentName};
252    /// use std::str::FromStr;
253    ///
254    /// let document_name = DocumentName::from_str(
255    ///     "projects/my-project/databases/my-database/documents/chatrooms/chatroom1"
256    /// )?;
257    /// assert_eq!(document_name.document_id(), &DocumentId::from_str("chatroom1")?);
258    /// #     Ok(())
259    /// # }
260    /// ```
261    ///
262    pub fn document_id(&self) -> &DocumentId {
263        self.document_path.document_id()
264    }
265
266    /// Returns the `DocumentPath` of this `DocumentName`.
267    ///
268    /// # Examples
269    ///
270    /// ```rust
271    /// # fn main() -> anyhow::Result<()> {
272    /// use firestore_path::{DocumentName,DocumentPath};
273    /// use std::str::FromStr;
274    ///
275    /// let document_name = DocumentName::from_str(
276    ///     "projects/my-project/databases/my-database/documents/chatrooms/chatroom1"
277    /// )?;
278    /// assert_eq!(
279    ///     document_name.document_path(),
280    ///     &DocumentPath::from_str("chatrooms/chatroom1")?
281    /// );
282    /// #     Ok(())
283    /// # }
284    /// ```
285    ///
286    pub fn document_path(&self) -> &DocumentPath {
287        &self.document_path
288    }
289
290    /// Creates a new `CollectionName` from this `DocumentName` and `collection_path`.
291    ///
292    /// # Examples
293    ///
294    /// ```rust
295    /// # fn main() -> anyhow::Result<()> {
296    /// use firestore_path::{CollectionId,CollectionName,CollectionPath,DocumentName};
297    /// use std::str::FromStr;
298    ///
299    /// let document_name = DocumentName::from_str(
300    ///     "projects/my-project/databases/my-database/documents/chatrooms/chatroom1"
301    /// )?;
302    /// assert_eq!(
303    ///     document_name.clone().into_collection("messages")?,
304    ///     CollectionName::from_str(
305    ///         "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages"
306    ///     )?
307    /// );
308    /// assert_eq!(
309    ///     document_name.clone().into_collection("messages/message1/col")?,
310    ///     CollectionName::from_str(
311    ///         "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages/message1/col"
312    ///     )?
313    /// );
314    /// assert_eq!(
315    ///     document_name.clone().into_collection(CollectionId::from_str("messages")?)?,
316    ///     CollectionName::from_str(
317    ///         "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages"
318    ///     )?
319    /// );
320    /// assert_eq!(
321    ///     document_name.into_collection(CollectionPath::from_str("messages/message1/col")?)?,
322    ///     CollectionName::from_str(
323    ///         "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages/message1/col"
324    ///     )?
325    /// );
326    /// #     Ok(())
327    /// # }
328    /// ```
329    ///
330    pub fn into_collection<E, T>(self, collection_path: T) -> Result<CollectionName, Error>
331    where
332        E: std::fmt::Display,
333        T: TryInto<CollectionPath, Error = E>,
334    {
335        Ok(CollectionName::new(
336            self.root_document_name,
337            self.document_path.into_collection(collection_path)?,
338        ))
339    }
340
341    /// Creates a new `DocumentName` by consuming the `DocumentName` with the provided `document_path`.
342    ///
343    /// # Examples
344    ///
345    /// ```rust
346    /// # fn main() -> anyhow::Result<()> {
347    /// use firestore_path::{CollectionPath,DocumentName,DocumentPath};
348    /// use std::str::FromStr;
349    ///
350    /// let document_name = DocumentName::from_str(
351    ///     "projects/my-project/databases/my-database/documents/chatrooms/chatroom1"
352    /// )?;
353    /// assert_eq!(
354    ///     document_name.clone().into_doc("messages/message1")?,
355    ///     DocumentName::from_str(
356    ///         "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages/message1"
357    ///     )?
358    /// );
359    /// assert_eq!(
360    ///     document_name.clone().into_doc("messages/message1/col/doc")?,
361    ///     DocumentName::from_str(
362    ///         "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages/message1/col/doc"
363    ///     )?
364    /// );
365    /// assert_eq!(
366    ///     document_name.clone().into_doc(DocumentPath::from_str("messages/message1")?)?,
367    ///     DocumentName::from_str(
368    ///         "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages/message1"
369    ///     )?
370    /// );
371    /// assert_eq!(
372    ///     document_name.into_doc(DocumentPath::from_str("messages/message1/col/doc")?)?,
373    ///     DocumentName::from_str(
374    ///         "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages/message1/col/doc"
375    ///     )?
376    /// );
377    /// #     Ok(())
378    /// # }
379    /// ```
380    ///
381    pub fn into_doc<E, T>(self, document_path: T) -> Result<DocumentName, Error>
382    where
383        E: std::fmt::Display,
384        T: TryInto<DocumentPath, Error = E>,
385    {
386        Ok(DocumentName::new(
387            self.root_document_name,
388            self.document_path.into_doc(document_path)?,
389        ))
390    }
391
392    /// Consumes the `DocumentName`, returning the parent `CollectionName`.
393    ///
394    /// # Examples
395    ///
396    /// ```rust
397    /// # fn main() -> anyhow::Result<()> {
398    /// use firestore_path::{CollectionName,DocumentName};
399    /// use std::str::FromStr;
400    ///
401    /// let document_name = DocumentName::from_str(
402    ///     "projects/my-project/databases/my-database/documents/chatrooms/chatroom1"
403    /// )?;
404    /// assert_eq!(
405    ///     document_name.into_parent(),
406    ///     CollectionName::from_str(
407    ///         "projects/my-project/databases/my-database/documents/chatrooms"
408    ///     )?
409    /// );
410    /// #     Ok(())
411    /// # }
412    /// ```
413    ///
414    pub fn into_parent(self) -> CollectionName {
415        CollectionName::new(
416            self.root_document_name,
417            CollectionPath::from(self.document_path),
418        )
419    }
420
421    /// Consumes the `DocumentName`, returning the parent `DocumentName`.
422    ///
423    /// # Examples
424    ///
425    /// ```rust
426    /// # fn main() -> anyhow::Result<()> {
427    /// use firestore_path::{CollectionName,DocumentName};
428    /// use std::str::FromStr;
429    ///
430    /// let document_name = DocumentName::from_str(
431    ///     "projects/my-project/databases/my-database/documents/chatrooms/chatroom1"
432    /// )?;
433    /// assert_eq!(document_name.into_parent_document_name(), None);
434    /// let document_name = DocumentName::from_str(
435    ///     "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages/message1"
436    /// )?;
437    /// assert_eq!(
438    ///     document_name.into_parent_document_name(),
439    ///     Some(DocumentName::from_str(
440    ///         "projects/my-project/databases/my-database/documents/chatrooms/chatroom1"
441    ///     )?)
442    /// );
443    /// #     Ok(())
444    /// # }
445    /// ```
446    pub fn into_parent_document_name(self) -> Option<DocumentName> {
447        self.document_path
448            .into_parent()
449            .into_parent()
450            .map(|document_path| DocumentName::new(self.root_document_name, document_path))
451    }
452
453    /// Consumes the `DocumentName`, returning the `RootDocumentName`.
454    ///
455    /// # Examples
456    ///
457    /// ```rust
458    /// # fn main() -> anyhow::Result<()> {
459    /// use firestore_path::{DocumentName,RootDocumentName};
460    /// use std::str::FromStr;
461    ///
462    /// let document_name = DocumentName::from_str(
463    ///     "projects/my-project/databases/my-database/documents/chatrooms/chatroom1"
464    /// )?;
465    /// let root_document_name = document_name.into_root_document_name();
466    /// assert_eq!(
467    ///     root_document_name,
468    ///     RootDocumentName::from_str(
469    ///         "projects/my-project/databases/my-database/documents"
470    ///     )?
471    /// );
472    /// #     Ok(())
473    /// # }
474    pub fn into_root_document_name(self) -> RootDocumentName {
475        self.root_document_name
476    }
477
478    /// Returns the parent `CollectionName` of this `DocumentName`.
479    ///
480    /// # Examples
481    ///
482    /// ```rust
483    /// # fn main() -> anyhow::Result<()> {
484    /// use firestore_path::{CollectionName,DocumentName};
485    /// use std::str::FromStr;
486    ///
487    /// let document_name = DocumentName::from_str(
488    ///     "projects/my-project/databases/my-database/documents/chatrooms/chatroom1"
489    /// )?;
490    /// assert_eq!(
491    ///     document_name.parent(),
492    ///     CollectionName::from_str(
493    ///         "projects/my-project/databases/my-database/documents/chatrooms"
494    ///     )?
495    /// );
496    /// assert_eq!(
497    ///     document_name.parent(),
498    ///     CollectionName::from_str(
499    ///         "projects/my-project/databases/my-database/documents/chatrooms"
500    ///     )?
501    /// );
502    /// #     Ok(())
503    /// # }
504    /// ```
505    ///
506    pub fn parent(&self) -> CollectionName {
507        self.clone().into_parent()
508    }
509
510    /// Returns the parent `DocumentName` of this `DocumentName`.
511    ///
512    /// # Examples
513    ///
514    /// ```rust
515    /// # fn main() -> anyhow::Result<()> {
516    /// use firestore_path::{CollectionName,DocumentName};
517    /// use std::str::FromStr;
518    ///
519    /// let document_name = DocumentName::from_str(
520    ///     "projects/my-project/databases/my-database/documents/chatrooms/chatroom1"
521    /// )?;
522    /// assert_eq!(document_name.parent_document_name(), None);
523    /// let document_name = DocumentName::from_str(
524    ///     "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages/message1"
525    /// )?;
526    /// assert_eq!(
527    ///     document_name.parent_document_name(),
528    ///     Some(DocumentName::from_str(
529    ///         "projects/my-project/databases/my-database/documents/chatrooms/chatroom1"
530    ///     )?)
531    /// );
532    /// #     Ok(())
533    /// # }
534    /// ```
535    pub fn parent_document_name(&self) -> Option<DocumentName> {
536        self.clone().into_parent_document_name()
537    }
538
539    /// Returns the `RootDocumentName` of this `DocumentName`.
540    ///
541    /// # Examples
542    ///
543    /// ```rust
544    /// # fn main() -> anyhow::Result<()> {
545    /// use firestore_path::{DocumentName,RootDocumentName};
546    /// use std::str::FromStr;
547    ///
548    /// let document_name = DocumentName::from_str(
549    ///     "projects/my-project/databases/my-database/documents/chatrooms/chatroom1"
550    /// )?;
551    /// let root_document_name = document_name.root_document_name();
552    /// assert_eq!(
553    ///     root_document_name,
554    ///     &RootDocumentName::from_str(
555    ///         "projects/my-project/databases/my-database/documents"
556    ///     )?
557    /// );
558    /// #     Ok(())
559    /// # }
560    pub fn root_document_name(&self) -> &RootDocumentName {
561        &self.root_document_name
562    }
563}
564
565impl std::convert::From<DocumentName> for DatabaseName {
566    fn from(document_name: DocumentName) -> Self {
567        Self::from(document_name.root_document_name)
568    }
569}
570
571impl std::convert::From<DocumentName> for DocumentId {
572    fn from(document_name: DocumentName) -> Self {
573        Self::from(document_name.document_path)
574    }
575}
576
577impl std::convert::From<DocumentName> for DocumentPath {
578    fn from(document_name: DocumentName) -> Self {
579        document_name.document_path
580    }
581}
582
583impl std::convert::TryFrom<&str> for DocumentName {
584    type Error = Error;
585
586    fn try_from(s: &str) -> Result<Self, Self::Error> {
587        // <https://firebase.google.com/docs/firestore/quotas#collections_documents_and_fields>
588        if !(1..=6_144).contains(&s.len()) {
589            return Err(Error::from(ErrorKind::LengthOutOfBounds));
590        }
591
592        let parts = s.split('/').collect::<Vec<&str>>();
593        if parts.len() < 5 + 2 || (parts.len() - 5) % 2 != 0 {
594            return Err(Error::from(ErrorKind::InvalidNumberOfPathComponents));
595        }
596
597        Ok(Self {
598            root_document_name: RootDocumentName::from_str(&parts[0..5].join("/"))?,
599            document_path: DocumentPath::from_str(&parts[5..].join("/"))?,
600        })
601    }
602}
603
604impl std::convert::TryFrom<String> for DocumentName {
605    type Error = Error;
606
607    fn try_from(s: String) -> Result<Self, Self::Error> {
608        Self::try_from(s.as_str())
609    }
610}
611
612impl std::fmt::Display for DocumentName {
613    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
614        write!(f, "{}/{}", self.root_document_name, self.document_path)
615    }
616}
617
618impl std::str::FromStr for DocumentName {
619    type Err = Error;
620
621    fn from_str(s: &str) -> Result<Self, Self::Err> {
622        Self::try_from(s)
623    }
624}
625
626#[cfg(test)]
627mod tests {
628    use std::str::FromStr;
629
630    use crate::{CollectionPath, DocumentId};
631
632    use super::*;
633
634    #[test]
635    fn test() -> anyhow::Result<()> {
636        let s = "projects/my-project/databases/my-database/documents/chatrooms/chatroom1";
637        let document_name = DocumentName::from_str(s)?;
638        assert_eq!(document_name.to_string(), s);
639        Ok(())
640    }
641
642    #[test]
643    fn test_collection() -> anyhow::Result<()> {
644        let document_name = DocumentName::from_str(
645            "projects/my-project/databases/my-database/documents/chatrooms/chatroom1",
646        )?;
647        let collection_name = document_name.into_collection("messages")?;
648        assert_eq!(
649            collection_name,
650            CollectionName::from_str(
651                "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages"
652            )?
653        );
654
655        let document_name = DocumentName::from_str(
656            "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages/message1",
657        )?;
658        let collection_name = document_name.into_collection("col")?;
659        assert_eq!(
660            collection_name,
661            CollectionName::from_str(
662                "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages/message1/col"
663            )?
664        );
665
666        let document_name = DocumentName::from_str(
667            "projects/my-project/databases/my-database/documents/chatrooms/chatroom1",
668        )?;
669        let collection_id = CollectionId::from_str("messages")?;
670        let collection_name = document_name.into_collection(collection_id)?;
671        assert_eq!(
672            collection_name,
673            CollectionName::from_str(
674                "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages"
675            )?
676        );
677        Ok(())
678    }
679
680    #[test]
681    fn test_collection_with_colleciton_path() -> anyhow::Result<()> {
682        let document_name = DocumentName::from_str(
683            "projects/my-project/databases/my-database/documents/chatrooms/chatroom1",
684        )?;
685        let collection_name = document_name.into_collection("messages/message1/col")?;
686        assert_eq!(
687            collection_name,
688            CollectionName::from_str(
689                "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages/message1/col"
690            )?
691        );
692
693        let document_name = DocumentName::from_str(
694            "projects/my-project/databases/my-database/documents/chatrooms/chatroom1",
695        )?;
696        let collection_path = CollectionPath::from_str("messages/message1/col")?;
697        let collection_name = document_name.into_collection(collection_path)?;
698        assert_eq!(
699            collection_name,
700            CollectionName::from_str(
701                "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages/message1/col"
702            )?
703        );
704        Ok(())
705    }
706
707    #[test]
708    fn test_document_id() -> anyhow::Result<()> {
709        let document_name = DocumentName::from_str(
710            "projects/my-project/databases/my-database/documents/chatrooms/chatroom1",
711        )?;
712        assert_eq!(
713            document_name.document_id(),
714            &DocumentId::from_str("chatroom1")?
715        );
716        Ok(())
717    }
718
719    #[test]
720    fn test_impl_from_database_name_for_document_id() -> anyhow::Result<()> {
721        let document_name = DocumentName::from_str(
722            "projects/my-project/databases/my-database/documents/chatrooms/chatroom1",
723        )?;
724        assert_eq!(
725            DatabaseName::from(document_name),
726            DatabaseName::from_str("projects/my-project/databases/my-database")?
727        );
728        Ok(())
729    }
730
731    #[test]
732    fn test_impl_from_document_name_for_document_id() -> anyhow::Result<()> {
733        let document_name = DocumentName::from_str(
734            "projects/my-project/databases/my-database/documents/chatrooms/chatroom1",
735        )?;
736        assert_eq!(
737            DocumentId::from(document_name),
738            DocumentId::from_str("chatroom1")?
739        );
740        Ok(())
741    }
742
743    #[test]
744    fn test_impl_from_str_and_impl_try_from_string() -> anyhow::Result<()> {
745        let b = "projects/my-project/databases/my-database/documents";
746        let c1 = "x".repeat(1500);
747        let d1 = "x".repeat(1500);
748        let c2 = "y".repeat(1500);
749        let d2 = "y".repeat(1500);
750        let c3 = "z".repeat(80);
751        let d3_ok = "z".repeat(7);
752        let d3_err = "z".repeat(7 + 1);
753        let s1 = format!("{}/{}/{}/{}/{}/{}/{}", b, c1, d1, c2, d2, c3, d3_ok);
754        assert_eq!(s1.len(), 6_144);
755        let s2 = format!("{}/{}/{}/{}/{}/{}/{}", b, c1, d1, c2, d2, c3, d3_err);
756        assert_eq!(s2.len(), 6_145);
757
758        for (s, expected) in [
759            ("projects/my-project/databases/my-database/documents", false),
760            (
761                "projects/my-project/databases/my-database/documents/c",
762                false,
763            ),
764            (
765                "projects/my-project/databases/my-database/documents/c/d",
766                true,
767            ),
768            (
769                "projects/my-project/databases/my-database/documents/c/d/c",
770                false,
771            ),
772            (
773                "projects/my-project/databases/my-database/documents/c/d/c/d",
774                true,
775            ),
776            (s1.as_ref(), true),
777            (s2.as_ref(), false),
778        ] {
779            assert_eq!(DocumentName::from_str(s).is_ok(), expected);
780            assert_eq!(DocumentName::try_from(s).is_ok(), expected);
781            assert_eq!(DocumentName::try_from(s.to_string()).is_ok(), expected);
782            if expected {
783                assert_eq!(DocumentName::from_str(s)?, DocumentName::try_from(s)?);
784                assert_eq!(
785                    DocumentName::from_str(s)?,
786                    DocumentName::try_from(s.to_string())?
787                );
788                assert_eq!(DocumentName::from_str(s)?.to_string(), s);
789            }
790        }
791        Ok(())
792    }
793
794    #[test]
795    fn test_parent() -> anyhow::Result<()> {
796        let document_name = DocumentName::from_str(
797            "projects/my-project/databases/my-database/documents/chatrooms/chatroom1",
798        )?;
799        assert_eq!(
800            document_name.into_parent(),
801            CollectionName::from_str(
802                "projects/my-project/databases/my-database/documents/chatrooms",
803            )?
804        );
805        Ok(())
806    }
807}