Skip to main content

oxia/
types.rs

1//! Public domain types returned by client operations.
2
3use crate::errors::OxiaError;
4use crate::key;
5use crate::proto;
6use bytes::Bytes;
7use std::cmp::Ordering;
8use std::fmt;
9
10/// Metadata about the state of a record.
11#[derive(Clone, Debug, PartialEq, Eq)]
12#[non_exhaustive]
13pub struct Version {
14    /// The unique identifier of this version of the record. It changes on every
15    /// modification and is the value to pass to
16    /// [`expected_version_id`](crate::PutBuilder::expected_version_id) for
17    /// compare-and-swap operations.
18    pub version_id: i64,
19    /// The number of modifications made to the record since it was created.
20    pub modifications_count: i64,
21    /// The creation timestamp of the record, in milliseconds since the epoch.
22    pub created_timestamp: u64,
23    /// The timestamp of the latest modification, in milliseconds since the epoch.
24    pub modified_timestamp: u64,
25    /// The identifier of the owning session, when the record is ephemeral.
26    pub session_id: Option<i64>,
27    /// The identity of the client that last modified an ephemeral record.
28    pub client_identity: Option<String>,
29}
30
31impl Version {
32    /// Whether the record is ephemeral (bound to a client session).
33    pub fn is_ephemeral(&self) -> bool {
34        self.session_id.is_some()
35    }
36}
37
38impl From<proto::Version> for Version {
39    fn from(v: proto::Version) -> Self {
40        Version {
41            version_id: v.version_id,
42            modifications_count: v.modifications_count,
43            created_timestamp: v.created_timestamp,
44            modified_timestamp: v.modified_timestamp,
45            session_id: v.session_id,
46            client_identity: v.client_identity,
47        }
48    }
49}
50
51/// The key comparison applied by a [`get`](crate::OxiaClient::get) operation.
52#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
53#[non_exhaustive]
54pub enum ComparisonType {
55    /// The stored key must be equal to the requested key.
56    #[default]
57    Equal,
58    /// Return the record with the highest key `<=` the requested key.
59    Floor,
60    /// Return the record with the lowest key `>=` the requested key.
61    Ceiling,
62    /// Return the record with the highest key `<` the requested key.
63    Lower,
64    /// Return the record with the lowest key `>` the requested key.
65    Higher,
66}
67
68impl ComparisonType {
69    pub(crate) fn to_proto(self) -> proto::KeyComparisonType {
70        match self {
71            ComparisonType::Equal => proto::KeyComparisonType::Equal,
72            ComparisonType::Floor => proto::KeyComparisonType::Floor,
73            ComparisonType::Ceiling => proto::KeyComparisonType::Ceiling,
74            ComparisonType::Lower => proto::KeyComparisonType::Lower,
75            ComparisonType::Higher => proto::KeyComparisonType::Higher,
76        }
77    }
78}
79
80/// The result of a successful [`put`](crate::OxiaClient::put).
81#[derive(Clone, Debug, PartialEq, Eq)]
82#[non_exhaustive]
83pub struct PutResult {
84    /// The key of the stored record. Differs from the requested key when the
85    /// server generated it (sequential keys).
86    pub key: String,
87    /// The version of the newly written record.
88    pub version: Version,
89}
90
91/// A record returned by [`get`](crate::OxiaClient::get) or
92/// [`range_scan`](crate::OxiaClient::range_scan).
93#[derive(Clone, Debug, PartialEq, Eq)]
94#[non_exhaustive]
95pub struct GetResult {
96    /// The key of the record. Differs from the requested key for non-`Equal`
97    /// comparisons (e.g. floor/ceiling queries).
98    pub key: String,
99    /// The value, unless the record has none or the query excluded it with
100    /// [`include_value(false)`](crate::GetBuilder::include_value).
101    pub value: Option<Bytes>,
102    /// The version of the record.
103    pub version: Version,
104    /// The matching secondary-index key, when the query used
105    /// [`use_index`](crate::GetBuilder::use_index).
106    pub secondary_index_key: Option<String>,
107}
108
109impl GetResult {
110    /// Converts a wire response. The server omits the key for exact-match gets
111    /// (it equals the request key, passed as `fallback_key`); scan records must
112    /// always carry one.
113    pub(crate) fn from_proto(
114        response: proto::GetResponse,
115        fallback_key: Option<String>,
116    ) -> Result<Self, OxiaError> {
117        let version = response
118            .version
119            .ok_or_else(|| OxiaError::Decode("missing version in get response".to_string()))?;
120        let key = response
121            .key
122            .or(fallback_key)
123            .ok_or_else(|| OxiaError::Decode("missing key in record".to_string()))?;
124        Ok(GetResult {
125            key,
126            value: response.value,
127            version: version.into(),
128            secondary_index_key: response.secondary_index_key,
129        })
130    }
131
132    pub(crate) fn compare_keys(&self, other: &Self) -> Ordering {
133        key::compare(&self.key, &other.key)
134    }
135}
136
137/// A change notification streamed from
138/// [`OxiaClient::notifications`](crate::OxiaClient::notifications).
139#[derive(Clone, Debug, PartialEq, Eq)]
140#[non_exhaustive]
141pub enum Notification {
142    /// A new record was created.
143    KeyCreated {
144        /// The key of the created record.
145        key: String,
146        /// The version id of the created record.
147        version_id: Option<i64>,
148    },
149    /// An existing record was modified.
150    KeyModified {
151        /// The key of the modified record.
152        key: String,
153        /// The new version id of the record.
154        version_id: Option<i64>,
155    },
156    /// A record was deleted.
157    KeyDeleted {
158        /// The key of the deleted record.
159        key: String,
160    },
161    /// A range of records was deleted.
162    KeyRangeDeleted {
163        /// The start of the deleted range (inclusive).
164        key: String,
165        /// The end of the deleted range (exclusive).
166        key_range_last: Option<String>,
167    },
168}
169
170impl Notification {
171    /// Converts a wire notification entry; `None` for types this client version
172    /// does not recognize (they are skipped).
173    pub(crate) fn from_proto(entry: proto::NotificationEntry) -> Option<Self> {
174        let key = entry.key.unwrap_or_default();
175        let notification = entry.value?;
176        match proto::NotificationType::try_from(notification.r#type).ok()? {
177            proto::NotificationType::KeyCreated => Some(Notification::KeyCreated {
178                key,
179                version_id: notification.version_id,
180            }),
181            proto::NotificationType::KeyModified => Some(Notification::KeyModified {
182                key,
183                version_id: notification.version_id,
184            }),
185            proto::NotificationType::KeyDeleted => Some(Notification::KeyDeleted { key }),
186            proto::NotificationType::KeyRangeDeleted => Some(Notification::KeyRangeDeleted {
187                key,
188                key_range_last: notification.key_range_last,
189            }),
190        }
191    }
192
193    /// The key the notification refers to (the range start for
194    /// [`Notification::KeyRangeDeleted`]).
195    pub fn key(&self) -> &str {
196        match self {
197            Notification::KeyCreated { key, .. }
198            | Notification::KeyModified { key, .. }
199            | Notification::KeyDeleted { key }
200            | Notification::KeyRangeDeleted { key, .. } => key,
201        }
202    }
203}
204
205impl fmt::Display for Notification {
206    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207        match self {
208            Notification::KeyCreated { key, version_id } => {
209                write!(f, "KeyCreated(key={key}, version_id={version_id:?})")
210            }
211            Notification::KeyModified { key, version_id } => {
212                write!(f, "KeyModified(key={key}, version_id={version_id:?})")
213            }
214            Notification::KeyDeleted { key } => write!(f, "KeyDeleted(key={key})"),
215            Notification::KeyRangeDeleted {
216                key,
217                key_range_last,
218            } => {
219                write!(f, "KeyRangeDeleted(key={key}, last={key_range_last:?})")
220            }
221        }
222    }
223}
224
225/// The version id that a conditional operation compares against to assert that
226/// the record does not exist.
227pub(crate) const VERSION_ID_NOT_EXISTS: i64 = -1;