drive/query/
single_document_drive_query.rs

1use crate::drive::document::paths::contract_document_type_path_vec;
2use crate::util::common::encode::encode_u64;
3
4use crate::drive::votes;
5use crate::error::query::QuerySyntaxError;
6use crate::error::Error;
7use crate::query::Query;
8use grovedb::{PathQuery, SizedQuery};
9use platform_version::version::PlatformVersion;
10use platform_version::TryFromPlatformVersioned;
11
12/// The expected contested status of a document
13/// Drives stores the document in either the not contested location (most of the time)
14/// Or a temporary contested area while the contest is ongoing
15#[derive(Debug, PartialEq, Clone)]
16#[repr(u8)]
17pub enum SingleDocumentDriveQueryContestedStatus {
18    /// The document was not contested by the system.
19    NotContested = 0,
20    /// We don't know if the document was contested by the system, or we are not sure if the contest
21    /// is already over or not.
22    MaybeContested = 1,
23    /// We know that the document was contested by the system and the contest is not over.
24    Contested = 2,
25}
26
27impl TryFrom<i32> for SingleDocumentDriveQueryContestedStatus {
28    type Error = Error;
29
30    fn try_from(value: i32) -> Result<Self, Self::Error> {
31        match value {
32            0 => Ok(SingleDocumentDriveQueryContestedStatus::NotContested),
33            1 => Ok(SingleDocumentDriveQueryContestedStatus::MaybeContested),
34            2 => Ok(SingleDocumentDriveQueryContestedStatus::Contested),
35            n => Err(Error::Query(QuerySyntaxError::Unsupported(format!(
36                "unsupported contested status {}, only 0, 1 and 2 are supported",
37                n
38            )))),
39        }
40    }
41}
42
43/// Drive query struct
44#[derive(Debug, PartialEq, Clone)]
45pub struct SingleDocumentDriveQuery {
46    ///DataContract
47    pub contract_id: [u8; 32],
48    /// Document type
49    pub document_type_name: String,
50    /// Document type keeps history
51    pub document_type_keeps_history: bool,
52    /// Document
53    pub document_id: [u8; 32],
54    /// Block time
55    pub block_time_ms: Option<u64>,
56    /// True if the document might have gone to a contested resolution
57    pub contested_status: SingleDocumentDriveQueryContestedStatus,
58}
59
60impl SingleDocumentDriveQuery {
61    /// Operations to construct a path query.
62    pub fn construct_path_query(
63        &self,
64        platform_version: &PlatformVersion,
65    ) -> Result<PathQuery, Error> {
66        match self.contested_status {
67            SingleDocumentDriveQueryContestedStatus::NotContested => {
68                Ok(self.construct_non_contested_path_query(true))
69            }
70            SingleDocumentDriveQueryContestedStatus::MaybeContested => {
71                let non_contested = self.construct_non_contested_path_query(true);
72                let contested = self.construct_contested_path_query(true);
73                PathQuery::merge(
74                    vec![&non_contested, &contested],
75                    &platform_version.drive.grove_version,
76                )
77                .map_err(Error::from)
78            }
79            SingleDocumentDriveQueryContestedStatus::Contested => {
80                Ok(self.construct_contested_path_query(true))
81            }
82        }
83    }
84
85    /// Operations to construct the normal path query.
86    fn construct_non_contested_path_query(&self, with_limit_1: bool) -> PathQuery {
87        // First we should get the overall document_type_path
88        let mut path =
89            contract_document_type_path_vec(&self.contract_id, self.document_type_name.as_str());
90
91        path.push(vec![0]);
92
93        let mut query = Query::new();
94        query.insert_key(self.document_id.to_vec());
95
96        if self.document_type_keeps_history {
97            // if the documents keep history then we should insert a subquery
98            if let Some(block_time) = self.block_time_ms {
99                let encoded_block_time = encode_u64(block_time);
100                let mut sub_query = Query::new_with_direction(false);
101                sub_query.insert_range_to_inclusive(..=encoded_block_time);
102                query.set_subquery(sub_query);
103            } else {
104                query.set_subquery_key(vec![0]);
105            }
106        }
107
108        let limit = if with_limit_1 { Some(1) } else { None };
109
110        PathQuery::new(path, SizedQuery::new(query, limit, None))
111    }
112
113    /// Operations to construct the contested path query.
114    fn construct_contested_path_query(&self, with_limit_1: bool) -> PathQuery {
115        // First we should get the overall document_type_path
116        let path = votes::paths::vote_contested_resource_contract_documents_storage_path_vec(
117            &self.contract_id,
118            self.document_type_name.as_str(),
119        );
120
121        let mut query = Query::new();
122        query.insert_key(self.document_id.to_vec());
123
124        let limit = if with_limit_1 { Some(1) } else { None };
125
126        PathQuery::new(path, SizedQuery::new(query, limit, None))
127    }
128}
129
130impl TryFromPlatformVersioned<SingleDocumentDriveQuery> for PathQuery {
131    type Error = Error;
132    fn try_from_platform_versioned(
133        value: SingleDocumentDriveQuery,
134        platform_version: &PlatformVersion,
135    ) -> Result<Self, Self::Error> {
136        value.construct_path_query(platform_version)
137    }
138}