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    /// The compartment may be specified as either a name (or path for nested compartments) or as an id (OCID).
71    /// A name (vs id) can only be used when authenticated using a specific user identity. It is not available if
72    /// the associated handle authenticated as an Instance Principal (which can be done when calling the service from
73    /// a compute instance in the Oracle Cloud Infrastructure: see [`HandleBuilder::cloud_auth_from_instance()`](crate::HandleBuilder::cloud_auth_from_instance()).)
74    ///
75    /// If no compartment is given, the root compartment of the tenancy will be used.
76    pub fn compartment_id(mut self, compartment_id: &str) -> Self {
77        self.compartment_id = compartment_id.to_string();
78        self
79    }
80
81    pub fn namespace(mut self, namespace: &str) -> GetIndexesRequest {
82        self.namespace = namespace.to_string();
83        self
84    }
85
86    pub async fn execute(&self, h: &Handle) -> Result<GetIndexesResult, NoSQLError> {
87        // TODO: validate
88        let mut w: Writer = Writer::new();
89        w.write_i16(h.inner.serial_version);
90        let timeout = h.get_timeout(&self.timeout);
91        self.nson_serialize(&mut w, &timeout);
92        let mut opts = SendOptions {
93            timeout: timeout,
94            retryable: true,
95            compartment_id: self.compartment_id.clone(),
96            ..Default::default()
97        };
98        let mut r = h.send_and_receive(w, &mut opts).await?;
99        let resp = GetIndexesRequest::nson_deserialize(&mut r)?;
100        Ok(resp)
101    }
102
103    pub(crate) fn nson_serialize(&self, w: &mut Writer, timeout: &Duration) {
104        let mut ns = NsonSerializer::start_request(w);
105        ns.start_header();
106        ns.write_header(OpCode::GetIndexes, timeout, &self.table_name);
107        ns.end_header();
108
109        // payload
110        ns.start_payload();
111        ns.write_string_field(INDEX, &self.index_name);
112        // TODO: these are currently only in http headers. Add to NSON?
113        //ns.write_string_field(COMPARTMENT_OCID, &self.compartment_id);
114        //ns.write_string_field(NAMESPACE, &self.namespace);
115        ns.end_payload();
116
117        ns.end_request();
118    }
119
120    pub(crate) fn nson_deserialize(r: &mut Reader) -> Result<GetIndexesResult, NoSQLError> {
121        let mut walker = MapWalker::new(r)?;
122        let mut res: GetIndexesResult = Default::default();
123        while walker.has_next() {
124            walker.next()?;
125            let name = walker.current_name();
126            match name.as_str() {
127                ERROR_CODE => {
128                    walker.handle_error_code()?;
129                }
130                INDEXES => {
131                    // array of index info
132                    MapWalker::expect_type(walker.r, FieldType::Array)?;
133                    let _ = walker.r.read_i32()?; // skip array size in bytes
134                    let num_elements = walker.r.read_i32()?;
135                    res.indexes = Vec::with_capacity(num_elements as usize);
136                    for _n in 1..=num_elements {
137                        res.indexes
138                            .push(GetIndexesRequest::read_index_info(walker.r)?);
139                    }
140                    //println!(" indexes={:?}", res.indexes);
141                }
142                _ => {
143                    //println!("   get_indexes_result: skipping field '{}'", name);
144                    walker.skip_nson_field()?;
145                }
146            }
147        }
148        Ok(res)
149    }
150
151    fn read_index_info(r: &mut Reader) -> Result<IndexInfo, NoSQLError> {
152        let mut walker = MapWalker::new(r)?;
153        let mut res: IndexInfo = Default::default();
154        while walker.has_next() {
155            walker.next()?;
156            let name = walker.current_name();
157            match name.as_str() {
158                NAME => {
159                    res.index_name = walker.read_nson_string()?;
160                }
161                FIELDS => {
162                    // array of maps with PATH, TYPE elements each
163                    MapWalker::expect_type(walker.r, FieldType::Array)?;
164                    let _ = walker.r.read_i32()?; // skip array size in bytes
165                    let num_elements = walker.r.read_i32()?;
166                    res.field_names = Vec::with_capacity(num_elements as usize);
167                    res.field_types = Vec::with_capacity(num_elements as usize);
168                    for _n in 1..=num_elements {
169                        GetIndexesRequest::read_index_fields(walker.r, &mut res)?;
170                    }
171                }
172                _ => {
173                    //println!("   read_index_info: skipping field '{}'", name);
174                    walker.skip_nson_field()?;
175                }
176            }
177        }
178        Ok(res)
179    }
180
181    fn read_index_fields(r: &mut Reader, res: &mut IndexInfo) -> Result<(), NoSQLError> {
182        let mut walker = MapWalker::new(r)?;
183        while walker.has_next() {
184            walker.next()?;
185            // ensure we get both fields
186            let mut num_fields = 0;
187            let name = walker.current_name();
188            match name.as_str() {
189                PATH => {
190                    res.field_names.push(walker.read_nson_string()?);
191                    num_fields += 1;
192                }
193                TYPE => {
194                    res.field_types.push(walker.read_nson_string()?);
195                    num_fields += 1;
196                }
197                _ => {
198                    //println!("   read_index_fields: skipping field '{}'", name);
199                    walker.skip_nson_field()?;
200                }
201            }
202            if num_fields != 2 {
203                return Err(NoSQLError::new(
204                    BadProtocolMessage,
205                    "response missing PATH or TYPE element(s)",
206                ));
207            }
208        }
209        Ok(())
210    }
211}
212
213impl NsonRequest for GetIndexesRequest {
214    fn serialize(&self, w: &mut Writer, timeout: &Duration) {
215        self.nson_serialize(w, timeout);
216    }
217}