cypher_dto/
stamps.rs

1use chrono::{DateTime, FixedOffset, Utc};
2
3use crate::{format_query_fields, StampMode};
4
5/// The standard timestamps an object uses, and their field names.
6#[derive(Debug, Clone, Copy, PartialEq)]
7pub enum Stamps {
8    None,
9    Created(&'static str),
10    Updated(&'static str),
11    Both(&'static str, &'static str),
12}
13impl Stamps {
14    /// Parse a set of field names.
15    ///
16    /// `created_at` and `updated_at` have priority.
17    /// `created` and `updated` are also supported.
18    pub fn from_fields(fields: &[&'static str]) -> (Stamps, Vec<&'static str>) {
19        let mut has_created_at = false;
20        let mut has_updated_at = false;
21        let mut has_created = false;
22        let mut has_updated = false;
23        let mut non_stamp_fields = Vec::new();
24        for field in fields {
25            match field.as_ref() {
26                "created_at" => has_created_at = true,
27                "updated_at" => has_updated_at = true,
28                "created" => has_created = true,
29                "updated" => has_updated = true,
30                _ => non_stamp_fields.push(*field),
31            }
32        }
33        let created_field = if has_created_at {
34            if has_created {
35                non_stamp_fields.push("created")
36            }
37            Some("created_at")
38        } else if has_created {
39            Some("created")
40        } else {
41            None
42        };
43        let updated_field = if has_updated_at {
44            if has_updated {
45                non_stamp_fields.push("updated")
46            }
47            Some("updated_at")
48        } else if has_updated {
49            Some("updated")
50        } else {
51            None
52        };
53        let stamps = match (created_field, updated_field) {
54            (Some(created), Some(updated)) => Stamps::Both(created, updated),
55            (Some(created), None) => Stamps::Created(created),
56            (None, Some(updated)) => Stamps::Updated(updated),
57            (None, None) => Stamps::None,
58        };
59        (stamps, non_stamp_fields)
60    }
61
62    /// Returns these timestamp fields as a query string, depending on the [StampMode].
63    pub fn as_query_fields(&self, prefix: Option<&str>, mode: StampMode) -> String {
64        if self == &Stamps::None {
65            return "".to_owned();
66        }
67        match mode {
68            // Use placeholders
69            StampMode::Read => {
70                let (created, updated) = self.field_names();
71                format_query_fields([created, updated], prefix)
72            }
73            // Hardcode datetime()
74            StampMode::Create => match self {
75                Stamps::None => "".to_owned(),
76                Stamps::Created(name) => format!("{}: datetime()", name),
77                Stamps::Updated(name) => format!("{}: datetime()", name),
78                Stamps::Both(created, updated) => {
79                    format!("{}: datetime(), {}: datetime()", created, updated)
80                }
81            },
82            // created: placeholder, updated: datetime()
83            StampMode::Update => match self {
84                Stamps::None => "".to_owned(),
85                Stamps::Created(name) => format_query_fields([name], prefix),
86                Stamps::Updated(name) => format!("{}: datetime()", name),
87                Stamps::Both(created, updated) => {
88                    format!(
89                        "{}, {}: datetime()",
90                        format_query_fields([created], prefix),
91                        updated
92                    )
93                }
94            },
95        }
96    }
97
98    /// The created and updated field names. This may return empty strings.
99    ///
100    /// Convenience method rather than having to match [Stamps] every time.
101    fn field_names(&self) -> (&'static str, &'static str) {
102        match self {
103            Stamps::None => ("", ""),
104            Stamps::Created(name) => (*name, ""),
105            Stamps::Updated(name) => ("", *name),
106            Stamps::Both(created, updated) => (*created, *updated),
107        }
108    }
109}
110
111/// An abstraction over the different types of returned values from Neo4j.
112pub enum Neo4jMap<'a> {
113    Row(&'a neo4rs::Row),
114    Node(&'a neo4rs::Node),
115    Relation(&'a neo4rs::Relation),
116    UnboundedRelation(&'a neo4rs::UnboundedRelation),
117}
118impl<'a> Neo4jMap<'a> {
119    pub fn get_timestamp(&self, name: &str) -> Result<DateTime<Utc>, crate::Error> {
120        match self {
121            Neo4jMap::Row(value) => value
122                .get::<DateTime<FixedOffset>>(name)
123                .map(|dt| dt.into())
124                .map_err(|_e| crate::Error::MissingField(name.to_owned())),
125            Neo4jMap::Node(value) => value
126                .get::<DateTime<FixedOffset>>(name)
127                .map(|dt| dt.into())
128                .map_err(|_e| crate::Error::MissingField(name.to_owned())),
129            Neo4jMap::Relation(value) => value
130                .get::<DateTime<FixedOffset>>(name)
131                .map(|dt| dt.into())
132                .map_err(|_e| crate::Error::MissingField(name.to_owned())),
133            Neo4jMap::UnboundedRelation(value) => value
134                .get::<DateTime<FixedOffset>>(name)
135                .map(|dt| dt.into())
136                .map_err(|_e| crate::Error::MissingField(name.to_owned())),
137        }
138    }
139}