oracle_nosql_rust_sdk/
get_indexes_request.rs

1//
2// Copyright (c) 2024, 2025 Oracle and/or its affiliates. All rights reserved.
3//
4// Licensed under the Universal Permissive License v 1.0 as shown at
5//  https://oss.oracle.com/licenses/upl/
6//
7use crate::error::NoSQLError;
8use crate::error::NoSQLErrorCode::BadProtocolMessage;
9use crate::handle::Handle;
10use crate::handle::SendOptions;
11use crate::nson::*;
12use crate::reader::Reader;
13use crate::types::{FieldType, OpCode};
14use crate::writer::Writer;
15use std::result::Result;
16use std::time::Duration;
17
18/// Struct used for querying indexes for a NoSQL table.
19#[derive(Default, Debug)]
20pub struct GetIndexesRequest {
21    pub(crate) table_name: String,
22    pub(crate) index_name: String, // TODO: Option<String>
23    pub(crate) compartment_id: String,
24    pub(crate) namespace: String,
25    pub(crate) timeout: Option<Duration>,
26}
27
28/// Information about a single index including its name and field names.
29#[derive(Default, Debug)]
30pub struct IndexInfo {
31    pub index_name: String,
32    pub field_names: Vec<String>,
33    pub field_types: Vec<String>,
34}
35
36/// Struct representing the result of a [`GetIndexesRequest`].
37#[derive(Default, Debug)]
38pub struct GetIndexesResult {
39    pub indexes: Vec<IndexInfo>,
40}
41
42impl GetIndexesRequest {
43    pub fn new(table_name: &str) -> GetIndexesRequest {
44        GetIndexesRequest {
45            table_name: table_name.to_string(),
46            ..Default::default()
47        }
48    }
49
50    // Name of the index to get. If this is empty, all indexes for
51    // the table are returned.
52    pub fn index_name(mut self, index_name: &str) -> GetIndexesRequest {
53        self.index_name = index_name.to_string();
54        self
55    }
56
57    /// Specify the timeout value for the request.
58    ///
59    /// This is optional.
60    /// If set, it must be greater than or equal to 1 millisecond, otherwise an
61    /// IllegalArgument error will be returned.
62    /// If not set, the default timeout value configured for the [`Handle`](crate::HandleBuilder::timeout()) is used.
63    pub fn timeout(mut self, t: &Duration) -> Self {
64        self.timeout = Some(t.clone());
65        self
66    }
67
68    /// Cloud Service only: set the name or id of a compartment to be used for this operation.
69    ///
70    /// If the associated handle authenticated as an Instance Principal, this value must be an OCID.
71    /// In all other cases, the value may be specified as either a name (or path for nested compartments) or as an OCID.
72    ///
73    /// If no compartment is given, the default compartment id for the handle is used. If that value was
74    /// not specified, the root compartment of the tenancy will be used.
75    pub fn compartment_id(mut self, compartment_id: &str) -> Self {
76        self.compartment_id = compartment_id.to_string();
77        self
78    }
79
80    pub fn namespace(mut self, namespace: &str) -> GetIndexesRequest {
81        self.namespace = namespace.to_string();
82        self
83    }
84
85    pub async fn execute(&self, h: &Handle) -> Result<GetIndexesResult, NoSQLError> {
86        // TODO: validate
87        let mut w: Writer = Writer::new();
88        w.write_i16(h.inner.serial_version);
89        let timeout = h.get_timeout(&self.timeout);
90        self.nson_serialize(&mut w, &timeout);
91        let mut opts = SendOptions {
92            timeout: timeout,
93            retryable: true,
94            compartment_id: self.compartment_id.clone(),
95            ..Default::default()
96        };
97        let mut r = h.send_and_receive(w, &mut opts).await?;
98        let resp = GetIndexesRequest::nson_deserialize(&mut r)?;
99        Ok(resp)
100    }
101
102    pub(crate) fn nson_serialize(&self, w: &mut Writer, timeout: &Duration) {
103        let mut ns = NsonSerializer::start_request(w);
104        ns.start_header();
105        ns.write_header(OpCode::GetIndexes, timeout, &self.table_name);
106        ns.end_header();
107
108        // payload
109        ns.start_payload();
110        ns.write_string_field(INDEX, &self.index_name);
111        // TODO: these are currently only in http headers. Add to NSON?
112        //ns.write_string_field(COMPARTMENT_OCID, &self.compartment_id);
113        //ns.write_string_field(NAMESPACE, &self.namespace);
114        ns.end_payload();
115
116        ns.end_request();
117    }
118
119    pub(crate) fn nson_deserialize(r: &mut Reader) -> Result<GetIndexesResult, NoSQLError> {
120        let mut walker = MapWalker::new(r)?;
121        let mut res: GetIndexesResult = Default::default();
122        while walker.has_next() {
123            walker.next()?;
124            let name = walker.current_name();
125            match name.as_str() {
126                ERROR_CODE => {
127                    walker.handle_error_code()?;
128                }
129                INDEXES => {
130                    // array of index info
131                    MapWalker::expect_type(walker.r, FieldType::Array)?;
132                    let _ = walker.r.read_i32()?; // skip array size in bytes
133                    let num_elements = walker.r.read_i32()?;
134                    res.indexes = Vec::with_capacity(num_elements as usize);
135                    for _n in 1..=num_elements {
136                        res.indexes
137                            .push(GetIndexesRequest::read_index_info(walker.r)?);
138                    }
139                    //println!(" indexes={:?}", res.indexes);
140                }
141                _ => {
142                    //println!("   get_indexes_result: skipping field '{}'", name);
143                    walker.skip_nson_field()?;
144                }
145            }
146        }
147        Ok(res)
148    }
149
150    fn read_index_info(r: &mut Reader) -> Result<IndexInfo, NoSQLError> {
151        let mut walker = MapWalker::new(r)?;
152        let mut res: IndexInfo = Default::default();
153        while walker.has_next() {
154            walker.next()?;
155            let name = walker.current_name();
156            match name.as_str() {
157                NAME => {
158                    res.index_name = walker.read_nson_string()?;
159                }
160                FIELDS => {
161                    // array of maps with PATH, TYPE elements each
162                    MapWalker::expect_type(walker.r, FieldType::Array)?;
163                    let _ = walker.r.read_i32()?; // skip array size in bytes
164                    let num_elements = walker.r.read_i32()?;
165                    res.field_names = Vec::with_capacity(num_elements as usize);
166                    res.field_types = Vec::with_capacity(num_elements as usize);
167                    for _n in 1..=num_elements {
168                        GetIndexesRequest::read_index_fields(walker.r, &mut res)?;
169                    }
170                }
171                _ => {
172                    //println!("   read_index_info: skipping field '{}'", name);
173                    walker.skip_nson_field()?;
174                }
175            }
176        }
177        Ok(res)
178    }
179
180    fn read_index_fields(r: &mut Reader, res: &mut IndexInfo) -> Result<(), NoSQLError> {
181        let mut walker = MapWalker::new(r)?;
182        while walker.has_next() {
183            walker.next()?;
184            // ensure we get both fields
185            let mut num_fields = 0;
186            let name = walker.current_name();
187            match name.as_str() {
188                PATH => {
189                    res.field_names.push(walker.read_nson_string()?);
190                    num_fields += 1;
191                }
192                TYPE => {
193                    res.field_types.push(walker.read_nson_string()?);
194                    num_fields += 1;
195                }
196                _ => {
197                    //println!("   read_index_fields: skipping field '{}'", name);
198                    walker.skip_nson_field()?;
199                }
200            }
201            if num_fields != 2 {
202                return Err(NoSQLError::new(
203                    BadProtocolMessage,
204                    "response missing PATH or TYPE element(s)",
205                ));
206            }
207        }
208        Ok(())
209    }
210}
211
212impl NsonRequest for GetIndexesRequest {
213    fn serialize(&self, w: &mut Writer, timeout: &Duration) {
214        self.nson_serialize(w, timeout);
215    }
216}