firebase_rs_sdk/firestore/model/
field_path.rs

1use crate::firestore::error::{invalid_argument, FirestoreResult};
2
3#[derive(Clone, Debug, PartialEq, Eq, Hash)]
4pub struct FieldPath {
5    segments: Vec<String>,
6}
7
8impl FieldPath {
9    pub fn new<S, I>(segments: I) -> FirestoreResult<Self>
10    where
11        S: Into<String>,
12        I: IntoIterator<Item = S>,
13    {
14        let segments: Vec<String> = segments.into_iter().map(Into::into).collect();
15        if segments.is_empty() {
16            return Err(invalid_argument(
17                "FieldPath must contain at least one segment",
18            ));
19        }
20        Ok(Self { segments })
21    }
22
23    pub fn from_dot_separated(path: &str) -> FirestoreResult<Self> {
24        if path.trim().is_empty() {
25            return Err(invalid_argument("FieldPath string cannot be empty"));
26        }
27        FieldPath::new(path.split('.'))
28    }
29
30    pub fn last_segment(&self) -> &str {
31        self.segments
32            .last()
33            .expect("FieldPath always has at least one segment")
34            .as_str()
35    }
36
37    pub fn segments(&self) -> &[String] {
38        &self.segments
39    }
40
41    pub fn canonical_string(&self) -> String {
42        self.segments.join(".")
43    }
44
45    pub fn to_vec(&self) -> Vec<String> {
46        self.segments.clone()
47    }
48
49    pub fn document_id() -> Self {
50        Self {
51            segments: vec!["__name__".to_string()],
52        }
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59
60    #[test]
61    fn from_dot_path() {
62        let field = FieldPath::from_dot_separated("foo.bar").unwrap();
63        assert_eq!(field.segments(), &["foo", "bar"]);
64    }
65
66    #[test]
67    fn rejects_empty() {
68        let err = FieldPath::from_dot_separated("").unwrap_err();
69        assert_eq!(err.code_str(), "firestore/invalid-argument");
70    }
71}