tarantool_rs/client/schema/
space.rs

1use std::{
2    fmt::{self, Debug},
3    sync::Arc,
4};
5
6use anyhow::Context;
7use rmpv::Value;
8use serde::{de::DeserializeOwned, Deserialize};
9
10use super::{Index, IndexMetadata, OwnedIndex, SchemaEntityKey, SystemSpacesId, PRIMARY_INDEX_ID};
11use crate::{
12    client::ExecutorExt, tuple::Tuple, utils::UniqueIdNameMap, DmoResponse, Error, Executor,
13    IteratorType, Result, Transaction,
14};
15
16/// Space metadata with its indices metadata from [system views](https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/system_views/).
17#[derive(Clone, Deserialize)]
18pub struct SpaceMetadata {
19    pub(super) id: u32,
20    owner_id: u32,
21    name: String,
22    _engine: String, // TODO: enum
23    _fields_count: u32,
24    _flags: Value,       // TODO: parse flags
25    _format: Vec<Value>, // TODO: parse format or remove it entirely
26}
27
28impl fmt::Debug for SpaceMetadata {
29    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        f.debug_struct("SpaceMetadata")
31            .field("id", &self.id)
32            .field("owner_id", &self.owner_id)
33            .field("name", &self.name)
34            .field("engine", &self._engine)
35            // TODO: uncomment when fields implemented
36            // .field("_flags", &self._flags)
37            // .field("_format", &self._format)
38            .finish()
39    }
40}
41
42impl SpaceMetadata {
43    /// Load metadata of single space by key.
44    ///
45    /// Can be loaded by index (if passed unsigned integer) or name (if passed `&str`).
46    async fn load(conn: impl ExecutorExt, key: SchemaEntityKey) -> Result<Option<Self>> {
47        Ok(conn
48            .select(
49                SystemSpacesId::VSpace as u32,
50                key.space_index_id(),
51                None,
52                None,
53                None,
54                (key.into_value(),),
55            )
56            .await?
57            .into_iter()
58            .next())
59    }
60
61    /// Returns the id of this space.
62    pub fn id(&self) -> u32 {
63        self.id
64    }
65
66    /// Returns user id of ther owner of this space.
67    pub fn owner_id(&self) -> u32 {
68        self.owner_id
69    }
70
71    /// Returns a name of this space.
72    pub fn name(&self) -> &str {
73        self.name.as_ref()
74    }
75
76    // /// Returns map of idices in this space.
77    // pub fn indices(&self) -> &UniqueIdNameMap<IndexMetadata> {
78    //     &self.indices
79    // }
80}
81
82/// Tarantool space.
83///
84/// This is a wrapper around [`Executor`], which allow to make space-related requests
85/// on specific space. All requests over index uses primary index.
86pub struct Space<E> {
87    executor: E,
88    metadata: Arc<SpaceMetadata>,
89    primary_index_metadata: Arc<IndexMetadata>,
90    indices_metadata: Arc<UniqueIdNameMap<IndexMetadata>>,
91}
92
93impl<E: Clone> Clone for Space<E> {
94    fn clone(&self) -> Self {
95        Self {
96            executor: self.executor.clone(),
97            metadata: self.metadata.clone(),
98            primary_index_metadata: self.primary_index_metadata.clone(),
99            indices_metadata: self.indices_metadata.clone(),
100        }
101    }
102}
103
104impl<E> Space<E> {
105    fn get_index(&self, key: impl Into<SchemaEntityKey>) -> Option<&Arc<IndexMetadata>> {
106        match key.into() {
107            SchemaEntityKey::Id(x) => self.indices_metadata.get_by_id(x),
108            SchemaEntityKey::Name(x) => self.indices_metadata.get_by_name(&x),
109        }
110    }
111
112    pub fn executor(&self) -> &E {
113        &self.executor
114    }
115
116    pub fn metadata(&self) -> &SpaceMetadata {
117        &self.metadata
118    }
119
120    pub fn into_executor(self) -> E {
121        self.executor
122    }
123
124    pub fn primary_index(&self) -> Index<&E> {
125        Index::new(&self.executor, &self.primary_index_metadata, &self.metadata)
126    }
127
128    pub fn index(&self, key: impl Into<SchemaEntityKey>) -> Option<Index<&E>> {
129        self.get_index(key)
130            .map(|index| Index::new(&self.executor, index, &self.metadata))
131    }
132}
133
134impl<E: Clone> Space<E> {
135    pub fn owned_primary_index(&self) -> OwnedIndex<E> {
136        OwnedIndex::new(
137            self.executor.clone(),
138            self.primary_index_metadata.clone(),
139            self.metadata.clone(),
140        )
141    }
142
143    pub fn owned_index(&self, key: impl Into<SchemaEntityKey>) -> Option<OwnedIndex<E>> {
144        self.get_index(key).map(|index| {
145            OwnedIndex::new(self.executor.clone(), index.clone(), self.metadata.clone())
146        })
147    }
148}
149
150impl<E: Executor> Space<E> {
151    /// Load metadata of single space by its key.
152    ///
153    /// Can be called with space's index (if passed unsigned integer) or name (if passed `&str`).
154    pub(crate) async fn load(executor: E, key: SchemaEntityKey) -> Result<Option<Self>> {
155        let Some(space_metadata) = SpaceMetadata::load(&executor, key).await? else {
156            return Ok(None);
157        };
158
159        let indices = IndexMetadata::load_by_space_id(&executor, space_metadata.id)
160            .await
161            .and_then(|x| {
162                UniqueIdNameMap::try_from_iter(x)
163                    .context("Duplicate indices in space")
164                    .map_err(Error::Other)
165            })?;
166        let Some(primary_index) = indices.get_by_id(PRIMARY_INDEX_ID).cloned() else {
167            return Err(Error::SpaceMissingPrimaryIndex);
168        };
169
170        Ok(Some(Self {
171            executor,
172            metadata: space_metadata.into(),
173            primary_index_metadata: primary_index,
174            indices_metadata: indices.into(),
175        }))
176    }
177
178    /// Iterator over indices in this space.
179    pub fn indices(&self) -> impl Iterator<Item = Index<&E>> {
180        self.indices_metadata
181            .iter()
182            .map(|index| Index::new(&self.executor, index, &self.metadata))
183    }
184
185    /// Call `select` with primary index on current space.
186    ///
187    /// For details see [`ExecutorExt::select`].
188    pub async fn select<T, A>(
189        &self,
190        limit: Option<u32>,
191        offset: Option<u32>,
192        iterator: Option<IteratorType>,
193        keys: A,
194    ) -> Result<Vec<T>>
195    where
196        T: DeserializeOwned,
197        A: Tuple + Send,
198    {
199        self.executor
200            .select(
201                self.metadata.id,
202                PRIMARY_INDEX_ID,
203                limit,
204                offset,
205                iterator,
206                keys,
207            )
208            .await
209    }
210
211    /// Call `insert` on current space.
212    ///
213    /// For details see [`ExecutorExt::insert`].
214    pub async fn insert<T>(&self, tuple: T) -> Result<DmoResponse>
215    where
216        T: Tuple + Send,
217    {
218        self.executor.insert(self.metadata.id, tuple).await
219    }
220
221    /// Call `update` with primary index on current space.
222    ///
223    /// For details see [`ExecutorExt::update`].
224    pub async fn update<K, O>(&self, keys: K, ops: O) -> Result<DmoResponse>
225    where
226        K: Tuple + Send,
227        O: Tuple + Send,
228    {
229        self.executor
230            .update(self.metadata.id, PRIMARY_INDEX_ID, keys, ops)
231            .await
232    }
233
234    /// Call `upsert` on current space.
235    ///
236    /// For details see [`ExecutorExt::upsert`].
237    pub async fn upsert<T, O>(&self, tuple: T, ops: O) -> Result<DmoResponse>
238    where
239        T: Tuple + Send,
240        O: Tuple + Send,
241    {
242        self.executor.upsert(self.metadata.id, tuple, ops).await
243    }
244
245    /// Call `replace` on current space.
246    ///
247    /// For details see [`ExecutorExt::replace`].
248    pub async fn replace<T>(&self, tuple: T) -> Result<DmoResponse>
249    where
250        T: Tuple + Send,
251    {
252        self.executor.replace(self.metadata.id, tuple).await
253    }
254
255    /// Call `delete` with primary index on current space.
256    ///
257    /// For details see [`ExecutorExt::delete`].
258    pub async fn delete<T>(&self, keys: T) -> Result<DmoResponse>
259    where
260        T: Tuple + Send,
261    {
262        self.executor
263            .delete(self.metadata.id, PRIMARY_INDEX_ID, keys)
264            .await
265    }
266}
267
268impl Space<Transaction> {
269    /// Commit inner tranasction.
270    pub async fn commit(self) -> Result<()> {
271        self.executor.commit().await
272    }
273
274    /// Rollback inner tranasction.
275    pub async fn rollback(self) -> Result<()> {
276        self.executor.rollback().await
277    }
278}
279
280impl<E: Debug> fmt::Debug for Space<E> {
281    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
282        f.debug_struct("SpaceMetadata")
283            .field("executor", &self.executor)
284            .field("metadata", &self.metadata)
285            .finish()
286    }
287}