1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
use std::fmt;

use rmpv::Value;
use serde::Deserialize;

use super::{IndexMetadata, SystemSpacesId};
use crate::{client::ConnectionLike, utils::UniqueIdNameMap, Error};

/// Space metadata from with its indices metadata from [system views](https://www.tarantool.io/en/doc/latest/reference/reference_lua/box_space/system_views/).
#[derive(Clone, Deserialize)]
pub struct SpaceMetadata {
    id: u32,
    owner_id: u32,
    name: String,
    _engine: String, // TODO: enum
    _fields_count: u32,
    _flags: Value,       // TODO: parse flags
    _format: Vec<Value>, // TODO: parse format or remove it entirely
    // TODO: maybe implement hash directly on IndexMetadata and store in set
    // TODO: maybe vec or btreemap would be faster
    #[serde(skip)]
    indices: UniqueIdNameMap<IndexMetadata>,
}

impl fmt::Debug for SpaceMetadata {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("SpaceMetadata")
            .field("id", &self.id)
            .field("owner_id", &self.owner_id)
            .field("name", &self.name)
            .field("engine", &self._engine)
            // TODO: uncomment when fields implemented
            // .field("_flags", &self._flags)
            // .field("_format", &self._format)
            .field("indices", &self.indices)
            .finish()
    }
}

impl SpaceMetadata {
    /// Load metadata of single space by its id.
    pub async fn load_by_id(conn: impl ConnectionLike, id: u32) -> Result<Option<Self>, Error> {
        // 0 - primary id index
        Self::load(conn, 0, id).await
    }

    /// Load metadata of single space by its name.
    pub async fn load_by_name(
        conn: impl ConnectionLike,
        name: &str,
    ) -> Result<Option<Self>, Error> {
        // 2 - index on 'name' field
        Self::load(conn, 2, name).await
    }

    /// Load metadata of single space by key.
    async fn load(
        conn: impl ConnectionLike,
        index_id: u32,
        key: impl Into<Value>,
    ) -> Result<Option<Self>, Error> {
        let Some(mut this): Option<Self> = conn
            .select(
                SystemSpacesId::VSpace as u32,
                index_id,
                None,
                None,
                None,
                vec![key.into()],
            )
            .await?
            .into_iter()
            .next() else {
                return Ok(None)
            };
        this.load_indices(conn).await?;
        Ok(Some(this))
    }

    /// Load indices metadata into current space metadata.
    async fn load_indices(&mut self, conn: impl ConnectionLike) -> Result<(), Error> {
        self.indices = IndexMetadata::load_by_space_id(conn, self.id)
            .await
            .and_then(|x| {
                UniqueIdNameMap::try_from_iter(x).map_err(|err| {
                    Error::MetadataLoad(err.context("Failed to load indices metadata"))
                })
            })?;
        Ok(())
    }

    /// Returns the id of this space.
    pub fn id(&self) -> u32 {
        self.id
    }

    /// Returns user id of ther owner of this space.
    pub fn owner_id(&self) -> u32 {
        self.owner_id
    }

    /// Returns a name of this space.
    pub fn name(&self) -> &str {
        self.name.as_ref()
    }

    /// Returns map of idices in this space.
    pub fn indices(&self) -> &UniqueIdNameMap<IndexMetadata> {
        &self.indices
    }
}