diff --git a/src/document_name.rs b/src/document_name.rs index ac29a77..5ccac59 100644 --- a/src/document_name.rs +++ b/src/document_name.rs @@ -334,7 +334,7 @@ impl DocumentName { { Ok(CollectionName::new( self.root_document_name, - self.document_path.collection(collection_path)?, + self.document_path.into_collection(collection_path)?, )) } diff --git a/src/document_path.rs b/src/document_path.rs index e3e4175..649ed1d 100644 --- a/src/document_path.rs +++ b/src/document_path.rs @@ -58,7 +58,7 @@ impl DocumentPath { /// /// ```rust /// # fn main() -> anyhow::Result<()> { - /// use firestore_path::{CollectionPath,DocumentPath}; + /// use firestore_path::{CollectionId,CollectionPath,DocumentPath}; /// use std::str::FromStr; /// /// let document_path = DocumentPath::from_str("chatrooms/chatroom1")?; @@ -66,58 +66,27 @@ impl DocumentPath { /// document_path.collection("messages")?, /// CollectionPath::from_str("chatrooms/chatroom1/messages")? /// ); - /// - /// let document_path = DocumentPath::from_str("chatrooms/chatroom1")?; /// assert_eq!( /// document_path.collection("messages/message1/col")?, /// CollectionPath::from_str("chatrooms/chatroom1/messages/message1/col")? /// ); + /// assert_eq!( + /// document_path.collection(CollectionId::from_str("messages")?)?, + /// CollectionPath::from_str("chatrooms/chatroom1/messages")? + /// ); + /// assert_eq!( + /// document_path.collection(CollectionPath::from_str("messages/message1/col")?)?, + /// CollectionPath::from_str("chatrooms/chatroom1/messages/message1/col")? + /// ); /// # Ok(()) /// # } /// ``` - pub fn collection(self, collection_path: T) -> Result + pub fn collection(&self, collection_path: T) -> Result where E: std::fmt::Display, T: TryInto, { - let mut collection_path: CollectionPath = collection_path - .try_into() - .map_err(|e| Error::from(ErrorKind::CollectionPathConversion(e.to_string())))?; - - enum I { - C(CollectionId), - D(DocumentId), - } - let mut path_components = vec![]; - loop { - let (parent, collection_id) = collection_path.into_tuple(); - path_components.push(I::C(collection_id)); - if let Some(document_path) = parent { - let (next_collection_path, document_id) = document_path.into_tuple(); - path_components.push(I::D(document_id)); - collection_path = next_collection_path; - } else { - break; - } - } - - enum P { - C(CollectionPath), - D(DocumentPath), - } - let mut result = P::D(self); - for path_component in path_components.into_iter().rev() { - result = match (result, path_component) { - (P::C(_), I::C(_)) | (P::D(_), I::D(_)) => unreachable!(), - (P::C(c), I::D(d)) => P::D(DocumentPath::new(c, d)), - (P::D(d), I::C(c)) => P::C(CollectionPath::new(Some(d), c)), - }; - } - let collection_path = match result { - P::C(c) => c, - P::D(_) => unreachable!(), - }; - Ok(collection_path) + self.clone().into_collection(collection_path) } /// Creates a new `DocumentPath` from this `DocumentPath` and `document_path`. @@ -199,6 +168,80 @@ impl DocumentPath { &self.document_id } + /// Creates a new `CollectionPath` by consuming the `DocumentPath` with the provided `collection_path`. + /// + /// # Examples + /// + /// ```rust + /// # fn main() -> anyhow::Result<()> { + /// use firestore_path::{CollectionId,CollectionPath,DocumentPath}; + /// use std::str::FromStr; + /// + /// let document_path = DocumentPath::from_str("chatrooms/chatroom1")?; + /// assert_eq!( + /// document_path.clone().into_collection("messages")?, + /// CollectionPath::from_str("chatrooms/chatroom1/messages")? + /// ); + /// assert_eq!( + /// document_path.clone().into_collection("messages/message1/col")?, + /// CollectionPath::from_str("chatrooms/chatroom1/messages/message1/col")? + /// ); + /// assert_eq!( + /// document_path.clone().into_collection(CollectionId::from_str("messages")?)?, + /// CollectionPath::from_str("chatrooms/chatroom1/messages")? + /// ); + /// assert_eq!( + /// document_path.into_collection(CollectionPath::from_str("messages/message1/col")?)?, + /// CollectionPath::from_str("chatrooms/chatroom1/messages/message1/col")? + /// ); + /// # Ok(()) + /// # } + /// ``` + pub fn into_collection(self, collection_path: T) -> Result + where + E: std::fmt::Display, + T: TryInto, + { + let mut collection_path: CollectionPath = collection_path + .try_into() + .map_err(|e| Error::from(ErrorKind::CollectionPathConversion(e.to_string())))?; + + enum I { + C(CollectionId), + D(DocumentId), + } + let mut path_components = vec![]; + loop { + let (parent, collection_id) = collection_path.into_tuple(); + path_components.push(I::C(collection_id)); + if let Some(document_path) = parent { + let (next_collection_path, document_id) = document_path.into_tuple(); + path_components.push(I::D(document_id)); + collection_path = next_collection_path; + } else { + break; + } + } + + enum P { + C(CollectionPath), + D(DocumentPath), + } + let mut result = P::D(self); + for path_component in path_components.into_iter().rev() { + result = match (result, path_component) { + (P::C(_), I::C(_)) | (P::D(_), I::D(_)) => unreachable!(), + (P::C(c), I::D(d)) => P::D(DocumentPath::new(c, d)), + (P::D(d), I::C(c)) => P::C(CollectionPath::new(Some(d), c)), + }; + } + let collection_path = match result { + P::C(c) => c, + P::D(_) => unreachable!(), + }; + Ok(collection_path) + } + /// Creates a new `DocumentPath` by consuming the `DocumentPath` and the provided `document_path`. /// /// # Examples @@ -371,14 +414,14 @@ mod tests { #[test] fn test_collection() -> anyhow::Result<()> { let document_path = DocumentPath::from_str("chatrooms/chatroom1")?; - let collection_path = document_path.collection("messages")?; + let collection_path = document_path.into_collection("messages")?; assert_eq!( collection_path, CollectionPath::from_str("chatrooms/chatroom1/messages")? ); let document_path = DocumentPath::from_str("chatrooms/chatroom1/messages/message1")?; - let collection_path = document_path.collection("col")?; + let collection_path = document_path.into_collection("col")?; assert_eq!( collection_path, CollectionPath::from_str("chatrooms/chatroom1/messages/message1/col")? @@ -386,7 +429,7 @@ mod tests { let document_path = DocumentPath::from_str("chatrooms/chatroom1")?; let collection_id = CollectionId::from_str("messages")?; - let collection_path = document_path.collection(collection_id)?; + let collection_path = document_path.into_collection(collection_id)?; assert_eq!( collection_path, CollectionPath::from_str("chatrooms/chatroom1/messages")? @@ -397,7 +440,7 @@ mod tests { #[test] fn test_collection_with_colleciton_path() -> anyhow::Result<()> { let document_path = DocumentPath::from_str("chatrooms/chatroom1")?; - let collection_path = document_path.collection("messages/message1/col")?; + let collection_path = document_path.into_collection("messages/message1/col")?; assert_eq!( collection_path, CollectionPath::from_str("chatrooms/chatroom1/messages/message1/col")? @@ -405,7 +448,7 @@ mod tests { let document_path = DocumentPath::from_str("chatrooms/chatroom1")?; let collection_path = CollectionPath::from_str("messages/message1/col")?; - let collection_path = document_path.collection(collection_path)?; + let collection_path = document_path.into_collection(collection_path)?; assert_eq!( collection_path, CollectionPath::from_str("chatrooms/chatroom1/messages/message1/col")? diff --git a/tests/v0_7_0.rs b/tests/v0_7_0.rs index 0a58512..c4b8ca7 100644 --- a/tests/v0_7_0.rs +++ b/tests/v0_7_0.rs @@ -342,6 +342,39 @@ fn test_document_name_into_doc() -> anyhow::Result<()> { Ok(()) } +#[test] +fn test_document_path_collection() -> anyhow::Result<()> { + // BREAKING CHANGE: DocumentPath::collection doesn't consume self. + let document_name = DocumentName::from_str( + "projects/my-project/databases/my-database/documents/chatrooms/chatroom1", + )?; + assert_eq!( + document_name.collection("messages")?, + CollectionName::from_str( + "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages" + )? + ); + assert_eq!( + document_name.collection("messages/message1/col")?, + CollectionName::from_str( + "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages/message1/col" + )? + ); + assert_eq!( + document_name.collection(CollectionId::from_str("messages")?)?, + CollectionName::from_str( + "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages" + )? + ); + assert_eq!( + document_name.collection(CollectionPath::from_str("messages/message1/col")?)?, + CollectionName::from_str( + "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages/message1/col" + )? + ); + Ok(()) +} + #[test] fn test_document_path_doc() -> anyhow::Result<()> { // BREAKING CHANGE: DocumentPath::doc doesn't consume self. @@ -365,6 +398,41 @@ fn test_document_path_doc() -> anyhow::Result<()> { Ok(()) } +#[test] +fn test_document_path_into_collection() -> anyhow::Result<()> { + // Added: DocumentPath::into_collection + let document_name = DocumentName::from_str( + "projects/my-project/databases/my-database/documents/chatrooms/chatroom1", + )?; + assert_eq!( + document_name.clone().into_collection("messages")?, + CollectionName::from_str( + "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages" + )? + ); + assert_eq!( + document_name.clone().into_collection("messages/message1/col")?, + CollectionName::from_str( + "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages/message1/col" + )? + ); + assert_eq!( + document_name + .clone() + .into_collection(CollectionId::from_str("messages")?)?, + CollectionName::from_str( + "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages" + )? + ); + assert_eq!( + document_name.into_collection(CollectionPath::from_str("messages/message1/col")?)?, + CollectionName::from_str( + "projects/my-project/databases/my-database/documents/chatrooms/chatroom1/messages/message1/col" + )? + ); + Ok(()) +} + #[test] fn test_document_path_into_doc() -> anyhow::Result<()> { // Added: DocumentPath::into_doc