yottadb/context_api/
mod.rs

1/****************************************************************
2*                                                               *
3* Copyright (c) 2019-2023 YottaDB LLC and/or its subsidiaries.  *
4* All rights reserved.                                          *
5*                                                               *
6*       This source code contains the intellectual property     *
7*       of its copyright holder(s), and is made available       *
8*       under a license.  If you do not know the terms of       *
9*       the license, please stop and do not read further.       *
10*                                                               *
11****************************************************************/
12
13/// This module is a very thin wrapper around the `simple_api`.
14///
15/// Provides a Rust-interface for YottaDB which hides some of the complexity related to
16/// managing error-return buffers and tptokens.
17///
18/// When adding new functions to `Context` or `KeyContext`, please remember to use the helper
19/// functions `tptoken`, `take_buffer` and `recover_buffer`; they not only simplify code, but make it
20/// easier to change `ContextInternal` if necessary.
21///
22/// The iterators could use some cleanup; in particular they have few tests.
23///
24/// Note that `Context` is separate from `ContextInternal` so that the fields can be grouped together
25/// into one `Rc<RefCell>`. The `Rc<RefCell>` is necessary because the *same* context needs to be
26/// reused among all keys, so that the tptoken is the same everywhere. Changing it to remove the
27/// `RefCell` would prevent using `Rc` (since it needs to borrow `buffer` mutably); removing the `Rc`
28/// would prevent it from being used in multiple keys.
29#[allow(unused)]
30const INTERNAL_DOCS: () = ();
31
32mod call_in;
33
34use std::cell::{Cell, RefCell};
35use std::error::Error;
36use std::rc::Rc;
37use std::str::FromStr;
38use std::ops::{AddAssign, Deref, DerefMut};
39use std::time::Duration;
40use std::fmt;
41
42use crate::craw::YDB_ERR_NODEEND;
43use crate::simple_api::{
44    self, tp_st, YDBResult, YDBError, DataReturn, DeleteType, Key, TransactionStatus, TpToken,
45};
46
47/// Private macros to help make iterators
48macro_rules! implement_iterator {
49    ($name:ident, $advance:ident, $return_type:ty, $next:expr) => {
50        #[allow(missing_docs)]
51        pub struct $name<'a> {
52            key: &'a mut KeyContext,
53        }
54
55        impl<'a> Iterator for $name<'a> {
56            type Item = YDBResult<$return_type>;
57
58            fn next(&mut self) -> Option<Self::Item> {
59                match self.key.$advance() {
60                    #[allow(clippy::redundant_closure_call)]
61                    Ok(_) => $next(self),
62                    Err(YDBError { status: YDB_ERR_NODEEND, .. }) => None,
63                    Err(x) => Some(Err(x)),
64                }
65            }
66        }
67    };
68}
69
70macro_rules! gen_iter_proto {
71    ($(#[$meta:meta])*
72     $name:ident, $return_type:tt) => {
73        $(#[$meta])*
74            pub fn $name(&mut self) -> $return_type {
75                $return_type {
76                    key: self,
77                }
78            }
79    }
80}
81
82/// Create a [`KeyContext`] with the given subscripts, provided a context.
83///
84/// # Examples
85///
86/// ```
87/// use std::error::Error;
88/// use yottadb::Context;
89///
90/// fn main() -> Result<(), Box<dyn Error>> {
91///     let mut ctx = Context::new();
92///     let mut key = yottadb::make_ckey!(ctx, "^hello", "world");
93///     key.data()?;
94///
95///     Ok(())
96/// }
97/// ```
98#[macro_export]
99macro_rules! make_ckey {
100    ( $ctx:expr, $var:expr $(,)?) => (
101        $ctx.new_key($crate::Key::variable($var))
102    );
103    ( $ctx:expr, $gbl:expr $(, $x:expr)+ ) => (
104        $ctx.new_key(
105            $crate::make_key!( $gbl, $($x),+ )
106        )
107    );
108}
109
110/// NOTE: all fields in this struct must use interior mutability or they can't be mutated.
111#[derive(Debug)]
112struct ContextInternal {
113    tptoken: Cell<TpToken>,
114    buffer: RefCell<Vec<u8>>,
115    #[cfg(test)]
116    db_lock: RefCell<Option<crate::test_lock::LockGuard<'static>>>,
117}
118
119impl PartialEq for ContextInternal {
120    fn eq(&self, other: &Self) -> bool {
121        self.tptoken == other.tptoken && self.buffer == other.buffer
122    }
123}
124
125impl Eq for ContextInternal {}
126
127#[cfg(test)]
128impl Context {
129    fn write_lock(&self) {
130        drop(self.context.db_lock.borrow_mut().take());
131        *self.context.db_lock.borrow_mut() = Some(crate::test_lock::LockGuard::write());
132    }
133}
134
135/// A struct that keeps track of the current transaction and error buffer.
136///
137/// Since all functions in the YottaDB threaded API take a `tptoken` and `error_buffer`,
138/// it can be inconvenient to keep track of them manually, especially since
139///
140/// > Passing in a different or incorrect tptoken can result in hard-to-debug application behavior, including deadlocks. [1]
141///
142/// This struct keeps track of them for you
143/// so you don't have to clutter your application logic with resource management.
144///
145/// # See also
146/// - [Transaction processing](https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#transaction-processing)
147/// - [Threads](https://docs.yottadb.com/MultiLangProgGuide/programmingnotes.html#threads)
148/// - [Threads and transaction processing](https://docs.yottadb.com/MultiLangProgGuide/programmingnotes.html#threads-and-transaction-processing)
149///
150/// `Context` is _not_ thread-safe, async-safe, or re-entrant.
151///
152/// Example:
153///
154/// ```compile_fail,E0277
155/// use yottadb::{Context, TransactionStatus, make_ckey};
156///
157/// let ctx = Context::new();
158/// let mut key1 = make_ckey!(ctx, "key1");
159/// let mut key2 = make_ckey!(ctx, "key2");
160/// tokio::spawn(async {
161///     // error[E0277]: `dyn std::error::Error` cannot be sent between threads safely
162///     ctx.tp(|_| Ok(TransactionStatus::Ok), "BATCH", &[])
163/// });
164/// ```
165///
166/// [1]: https://docs.yottadb.com/MultiLangProgGuide/programmingnotes.html#threads-and-transaction-processing
167#[derive(Clone, Eq, PartialEq)]
168pub struct Context {
169    context: Rc<ContextInternal>,
170}
171
172impl fmt::Debug for Context {
173    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174        f.debug_struct("Context")
175            .field("tptoken", &self.tptoken())
176            .field("buffer", &String::from_utf8_lossy(&self.context.buffer.borrow()))
177            .finish()
178    }
179}
180
181impl Default for Context {
182    fn default() -> Self {
183        Self::new()
184    }
185}
186
187/// A key which keeps track of the current transaction and error buffer.
188///
189/// Keys are used to get, set, and delete values in the database.
190///
191/// # See also
192/// - [`Key`]
193/// - [Keys, values, nodes, variables, and subscripts](https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#keys-values-nodes-variables-and-subscripts)
194/// - [Local and Global variables](https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#local-and-global-variables)
195/// - [Intrinsic special variables](https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#intrinsic-special-variables)
196#[derive(Debug, Clone, Eq, PartialEq)]
197pub struct KeyContext {
198    /// `KeyContext` implements `Deref<Target = Key>`
199    pub key: Key,
200    context: Context,
201}
202
203impl Context {
204    /// Create a new `Context`
205    pub fn new() -> Context {
206        Context {
207            context: Rc::new(ContextInternal {
208                tptoken: Cell::new(TpToken::default()),
209                buffer: RefCell::new(Vec::new()),
210                #[cfg(test)]
211                db_lock: RefCell::new(Some(crate::test_lock::LockGuard::read())),
212            }),
213        }
214    }
215
216    /// Create a `KeyContext` from this `Context`.
217    ///
218    /// # See also
219    /// - [`KeyContext::new()`]
220    /// - [`KeyContext::with_key`](KeyContext::with_key())
221    /// - [`impl From<(&Context, Key)> for KeyContext`](KeyContext#implementations)
222    pub fn new_key<K: Into<Key>>(&self, key: K) -> KeyContext {
223        KeyContext::with_key(self, key)
224    }
225
226    /// Return the token for the transaction associated with this `Context`.
227    ///
228    /// This allows calling yottadb functions in the `craw` API that have not yet been wrapped
229    /// and require a tptoken from inside a transaction.
230    ///
231    /// # Example
232    /// `tptoken()` can be used to call M FFI from within a transaction:
233    /// ```
234    /// use std::env;
235    /// use std::ffi::CStr;
236    /// use yottadb::{ci_t, Context, TransactionStatus, YDB_NOTTP};
237    ///
238    /// env::set_var("ydb_routines", "examples/m-ffi");
239    /// env::set_var("ydb_ci", "examples/m-ffi/calltab.ci");
240    /// let ctx = Context::new();
241    /// ctx.tp(|ctx| {
242    ///     let tptoken = ctx.tptoken();
243    ///     assert_ne!(tptoken, YDB_NOTTP);
244    ///     let mut routine = CStr::from_bytes_with_nul(b"noop\0").unwrap();
245    ///     unsafe { ci_t!(tptoken, Vec::new(), routine)?; }
246    ///     Ok(TransactionStatus::Ok)
247    /// }, "BATCH", &[]).unwrap();
248    /// ```
249    ///
250    /// # See also
251    /// - [`Context::tp`](Context::tp())
252    #[inline]
253    pub fn tptoken(&self) -> TpToken {
254        self.context.tptoken.get()
255    }
256
257    /// Start a new transaction, where `f` is the transaction to execute.
258    ///
259    /// `tp` stands for 'transaction processing'.
260    ///
261    /// The parameter `trans_id` is the name logged for the transaction.
262    ///     If `trans_id` has the special value `"BATCH"`, durability is not enforced by YottaDB.
263    ///     See the [C documentation] for details.
264    ///
265    /// The argument passed to `f` is a [transaction processing token][threads and transactions].
266    ///
267    /// # Rollbacks and Restarts
268    /// Application code can return a [`TransactionStatus`] in order to rollback or restart.
269    /// `tp_st` behaves as follows:
270    /// - If `f` panics, the transaction is rolled back and the panic resumes afterwards.
271    /// - If `f` returns `Ok(TransactionStatus)`,
272    ///      the transaction will have the behavior documented under `TransactionStatus` (commit, restart, and rollback, respectively).
273    /// - If `f` returns an `Err(YDBError)`, the status from that error will be returned to the YottaDB engine.
274    ///      As a result, if the status for the `YDBError` is `YDB_TP_RESTART`, the transaction will be restarted.
275    ///      Otherwise, the transaction will be rolled back and the error returned from `tp_st`.
276    /// - If `f` returns any other `Err` variant, the transaction will be rolled back and the error returned from `tp_st`.
277    ///
278    /// `f` must be `FnMut`, not `FnOnce`, since the YottaDB engine may
279    /// call `f` many times if necessary to ensure ACID properties.
280    /// This may affect your application logic; if you need to know how many
281    /// times the callback has been executed, get the [intrinsic variable][intrinsics]
282    /// [`$trestart`](https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#trestart).
283    ///
284    /// # Errors
285    /// - YDB_ERR_TPTIMEOUT - The transaction took more than [`$zmaxtptime`] seconds to execute,
286    ///     where `$zmaxtptime` is an [intrinsic special variable][intrinsics].
287    /// - YDB_TP_ROLLBACK — application logic indicates that the transaction should not be committed.
288    /// - A `YDBError` returned by a YottaDB function called by `f`.
289    /// - Another arbitrary error returned by `f`.
290    ///
291    /// # Examples
292    /// Rollback a transaction if an operation fails:
293    /// ```
294    /// use yottadb::{Context, KeyContext, TpToken, TransactionStatus};
295    ///
296    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
297    /// let ctx = Context::new();
298    /// let var = KeyContext::variable(&ctx, "tpRollbackTest");
299    /// var.set("initial value")?;
300    /// println!("starting tp");
301    /// let maybe_err = ctx.tp(|ctx| {
302    ///     println!("in tp");
303    ///     fallible_operation()?;
304    ///     println!("succeeded");
305    ///     var.set("new value")?;
306    ///     Ok(TransactionStatus::Ok)
307    /// }, "BATCH", &[]);
308    /// let expected_val: &[_] = if maybe_err.is_ok() {
309    ///     b"new value"
310    /// } else {
311    ///     b"initial value"
312    /// };
313    /// assert_eq!(var.get()?, expected_val);
314    /// # Ok(())
315    /// # }
316    ///
317    /// fn fallible_operation() -> Result<(), &'static str> {
318    ///     if rand::random() {
319    ///         Ok(())
320    ///     } else {
321    ///         Err("the operation failed")
322    ///     }
323    /// }
324    /// ```
325    ///
326    /// Retry a transaction until it succeeds:
327    /// ```
328    /// use yottadb::{Context, TpToken, TransactionStatus};
329    ///
330    /// let ctx = Context::new();
331    /// ctx.tp(|tptoken| {
332    ///     if fallible_operation().is_ok() {
333    ///         Ok(TransactionStatus::Ok)
334    ///     } else {
335    ///         Ok(TransactionStatus::Restart)
336    ///     }
337    /// }, "BATCH", &[]).unwrap();
338    ///
339    /// fn fallible_operation() -> Result<(), ()> {
340    ///     if rand::random() {
341    ///         Ok(())
342    ///     } else {
343    ///         Err(())
344    ///     }
345    /// }
346    /// ```
347    ///
348    /// # See Also
349    /// - [More details about the underlying FFI call][C documentation]
350    /// - [Transaction Processing in YottaDB](https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#transaction-processing)
351    /// - [Threads and Transaction Processing][threads and transactions]
352    ///
353    /// [`$zmaxtptime`]: https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#zmaxtptime
354    /// [intrinsics]: crate#intrinsic-variables
355    /// [threads and transactions]: https://docs.yottadb.com/MultiLangProgGuide/programmingnotes.html#threads-and-transaction-processing
356    /// [C documentation]: https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#ydb-tp-s-ydb-tp-st
357    pub fn tp<'a, F>(
358        &'a self, mut f: F, trans_id: &str, locals_to_reset: &[&str],
359    ) -> Result<(), Box<dyn Error + Send + Sync>>
360    where
361        // NOTE: `FnMut(&'a Self)` is a distinct type from `FnMut(&Self)`: the first takes a specific
362        // lifetime, the second is sugar for `for<'s> FnMut(&'s Self)`. See
363        // <https://doc.rust-lang.org/nomicon/hrtb.html> for details. The reason to take `&'a Self` is
364        // it imposes fewer requirements on the caller.
365        F: FnMut(&'a Self) -> Result<TransactionStatus, Box<dyn Error + Send + Sync>>,
366    {
367        let initial_token = self.tptoken();
368        let result = tp_st(
369            initial_token,
370            self.take_buffer(),
371            |tptoken: TpToken| {
372                self.context.tptoken.set(tptoken);
373                f(self)
374            },
375            trans_id,
376            locals_to_reset,
377        );
378        self.context.tptoken.set(initial_token);
379        result.map(|x| {
380            *self.context.buffer.borrow_mut() = x;
381        })
382    }
383
384    /// Delete all local variables _except_ for those passed in `saved_variable`.
385    ///
386    /// Passing an empty `saved_variables` slice deletes all local variables.
387    /// Attempting to save a global or intrinsic variable is an error.
388    ///
389    /// # Errors
390    /// - YDB_ERR_NAMECOUNT2HI if `saved_variables.len() > YDB_MAX_NAMES`
391    /// - YDB_ERR_INVVARNAME if attempting to save a global or intrinsic variable
392    /// - Another system [error return code](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
393    ///
394    /// # Examples
395    ///
396    /// ```
397    /// # fn main() -> yottadb::YDBResult<()> {
398    /// use yottadb::{Context, KeyContext, TpToken, YDB_ERR_LVUNDEF};
399    ///
400    /// // Create three variables and set all
401    /// let ctx = Context::new();
402    /// let a = KeyContext::variable(&ctx, "deleteExclTestA");
403    /// a.set("test data")?;
404    /// let b = KeyContext::variable(&ctx, "deleteExclTestB");
405    /// b.set("test data 2")?;
406    /// let c = KeyContext::variable(&ctx, "deleteExclTestC");
407    /// c.set("test data 3")?;
408    ///
409    /// // Delete all variables except `a`
410    /// ctx.delete_excl(&[&a.variable])?;
411    /// assert_eq!(a.get()?, b"test data");
412    /// assert_eq!(b.get().unwrap_err().status, YDB_ERR_LVUNDEF);
413    /// assert_eq!(c.get().unwrap_err().status, YDB_ERR_LVUNDEF);
414    ///
415    /// // Delete `a` too
416    /// ctx.delete_excl(&[])?;
417    /// assert_eq!(a.get().unwrap_err().status, YDB_ERR_LVUNDEF);
418    ///
419    /// # Ok(())
420    /// # }
421    /// ```
422    ///
423    /// # See also
424    /// - The [Simple API documentation](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#ydb-delete-excl-s-ydb-delete-excl-st)
425    /// - [Local and global variables](https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#local-and-global-variables)
426    /// - [Instrinsic special variables](https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#intrinsic-special-variables)
427    pub fn delete_excl(&self, saved_variables: &[&str]) -> YDBResult<()> {
428        use simple_api::delete_excl_st;
429
430        let tptoken = self.tptoken();
431        let buffer = self.take_buffer();
432        self.recover_buffer(delete_excl_st(tptoken, buffer, saved_variables))
433    }
434
435    /// Runs the YottaDB deferred signal handler (if necessary).
436    ///
437    /// This function must be called if an application has a tight loop inside a transaction which never calls a YDB function.
438    ///
439    /// # See also
440    /// - [Signal Handling](super#signal-handling)
441    /// - [`Context::tp`](Context::tp())
442    /// - The [C documentation](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#ydb-eintr-handler-ydb-eintr-handler-t)
443    pub fn eintr_handler(&self) -> YDBResult<()> {
444        use simple_api::eintr_handler_t;
445
446        let tptoken = self.tptoken();
447        let buffer = self.take_buffer();
448        self.recover_buffer(eintr_handler_t(tptoken, buffer))
449    }
450
451    /// Given a binary sequence, serialize it to 'Zwrite format', which is ASCII printable.
452    ///
453    /// # Errors
454    /// - If YDB is in UTF8 mode, will return [`BADCHAR`] on invalid UTF8.
455    /// - Another [error code](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
456    ///
457    /// # Examples
458    ///
459    /// When `ydb_chset=UTF-8` is set, this will preserve UTF-8 characters:
460    ///
461    /// ```
462    /// # fn main() -> Result<(), yottadb::YDBError> {
463    /// use yottadb::Context;
464    ///
465    /// let ctx = Context::new();
466    /// let str2zwr = ctx.str2zwr("💖".as_bytes())?;
467    /// if std::env::var("ydb_chset").as_deref() == Ok("UTF-8") {
468    ///     assert_eq!(str2zwr, "\"💖\"".as_bytes());
469    /// } else {
470    ///     // Note: The "$C" below cannot be expanded to "$CH" or "$CHAR" as that is the output returned by "str2zwr()" in M mode.
471    ///     assert_eq!(str2zwr, b"\"\xf0\"_$C(159,146,150)");
472    /// }
473    /// # Ok(())
474    /// # }
475    /// ```
476    ///
477    /// When the input is invalid UTF-8, it will use the more verbose Zwrite format:
478    /// ```
479    /// # fn main() -> Result<(), yottadb::YDBError> {
480    /// use yottadb::Context;
481    ///
482    /// let ctx = Context::new();
483    /// let input = b"\xff";
484    /// assert!(std::str::from_utf8(input).is_err());
485    /// if std::env::var("ydb_chset").as_deref() == Ok("UTF-8") {
486    ///     assert_eq!(ctx.str2zwr(input)?, b"$ZCH(255)");
487    /// } else {
488    ///     assert_eq!(ctx.str2zwr(input)?, b"$C(255)");
489    /// }
490    /// # Ok(()) }
491    /// ```
492    ///
493    /// # See also
494    /// - [Zwrite format](https://docs.yottadb.com/MultiLangProgGuide/programmingnotes.html#zwrite-formatted)
495    /// - [`zwr2str`](Context::zwr2str()), which deserializes a buffer in Zwrite format back to the original binary.
496    ///
497    /// [`BADCHAR`]: https://docs.yottadb.com/MessageRecovery/errors.html#badchar
498    pub fn str2zwr(&self, original: &[u8]) -> YDBResult<Vec<u8>> {
499        use simple_api::str2zwr_st;
500
501        let tptoken = self.tptoken();
502        str2zwr_st(tptoken, self.take_buffer(), original)
503    }
504
505    /// Given a buffer in 'Zwrite format', deserialize it to the original binary buffer.
506    ///
507    /// `zwr2str_st` writes directly to `out_buf` to avoid returning multiple output buffers.
508    ///
509    /// # Errors
510    /// This function returns an empty array if `serialized` is not in Zwrite format.
511    /// It can also return another [error code](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code).
512    ///
513    /// # Examples
514    ///
515    /// ```
516    /// # use yottadb::YDBError;
517    /// # fn main() -> Result<(), YDBError> {
518    /// use yottadb::Context;
519    ///
520    /// let ctx = Context::new();
521    /// // Use "$ZCH" (instead of "$C") below as that will work in both M and UTF-8 modes (of "ydb_chset" env var)
522    /// // Note: Cannot use "$ZCHAR" below as "$ZCH" is the only input format recognized by "zwr2str()".
523    /// let out_buf = ctx.zwr2str(b"\"\xf0\"_$ZCH(159,146,150)")?;
524    /// assert_eq!(out_buf.as_slice(), "💖".as_bytes());
525    /// # Ok(())
526    /// # }
527    /// ```
528    ///
529    /// # See also
530    /// - [Zwrite format](https://docs.yottadb.com/MultiLangProgGuide/programmingnotes.html#zwrite-formatted)
531    /// - [str2zwr](Context::str2zwr()), the inverse of `zwr2str`.
532    pub fn zwr2str(&self, serialized: &[u8]) -> Result<Vec<u8>, YDBError> {
533        use simple_api::zwr2str_st;
534
535        let tptoken = self.tptoken();
536        zwr2str_st(tptoken, self.take_buffer(), serialized)
537    }
538
539    fn take_buffer(&self) -> Vec<u8> {
540        std::mem::take(&mut self.context.buffer.borrow_mut())
541    }
542
543    fn recover_buffer(&self, result: YDBResult<Vec<u8>>) -> YDBResult<()> {
544        result.map(|x| {
545            *self.context.buffer.borrow_mut() = x;
546        })
547    }
548
549    /// Acquires locks specified in `locks` and releases all others.
550    ///
551    /// This operation is atomic. If any lock cannot be acquired, all locks are released.
552    /// The `timeout` specifies the maximum time to wait before returning an error.
553    /// If no locks are specified, all locks are released.
554    ///
555    /// Note that YottaDB locks are per-process, not per-thread.
556    ///
557    /// # Limitations
558    ///
559    /// For implementation reasons, there is a hard limit to the number of `Key`s that can be passed in `locks`:
560    // floor( (36 - 4)/3 ) = 10
561    /// - 64-bit: 10 `Key`s
562    // floor( (36 - 7)/3 ) = 9
563    /// - 32-bit: 9  `Key`s
564    ///
565    /// If more than this number of keys are passed, `lock_st` will return `YDB_ERR_MAXARGCNT`.
566    ///
567    /// For implementation reasons, `lock_st` only works on 64-bit platforms, or on 32-bit ARM.
568    ///
569    /// `lock_st` will not be compiled on 16, 8, or 128 bit platforms
570    /// (i.e. will fail with 'cannot find function `lock_st` in module `yottadb::simple_api`').
571    ///
572    /// On non-ARM 32-bit platforms, the compiler will allow `lock_st` to be called,
573    /// but it will have unspecified behavior and has not been tested.
574    /// Use [`KeyContext::lock_incr`] and [`KeyContext::lock_decr`] instead.
575    ///
576    /// # Errors
577    ///
578    /// Possible errors for this function include:
579    /// - `YDB_LOCK_TIMEOUT` if all locks could not be acquired within the timeout period.
580    ///   In this case, no locks are acquired.
581    /// - `YDB_ERR_TIME2LONG` if `timeout` is greater than `YDB_MAX_TIME_NSEC`
582    /// - `YDB_ERR_MAXARGCNT` if too many locks have been passed (see [Limitations](#limitations))
583    /// - [error return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
584    ///
585    /// # Examples
586    /// ```
587    /// use std::slice;
588    /// use std::time::Duration;
589    /// use yottadb::{Context, KeyContext, Key, TpToken};
590    ///
591    /// // You can use either a `Key` or a `KeyContext` to acquire a lock.
592    /// // This uses a `KeyContext` to show that you need to use `.key` to get the inner `Key`.
593    /// let ctx = Context::new();
594    /// let a = KeyContext::variable(&ctx, "lockA");
595    ///
596    /// // Acquire a new lock
597    /// // using `from_ref` here allows us to use `a` later without moving it
598    /// ctx.lock(Duration::from_secs(1), slice::from_ref(&a.key)).unwrap();
599    ///
600    /// // Acquire multiple locks
601    /// let locks = vec![a.key, Key::variable("lockB")];
602    /// ctx.lock(Duration::from_secs(1), &locks).unwrap();
603    ///
604    /// // Release all locks
605    /// ctx.lock(Duration::from_secs(1), &[]).unwrap();
606    /// ```
607    ///
608    /// # See also
609    ///
610    /// - The C [Simple API documentation](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#ydb-lock-s-ydb-lock-st)
611    /// - [Locks](https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#locks)
612    ///
613    /// [`KeyContext::lock_incr`]: KeyContext::lock_incr()
614    /// [`KeyContext::lock_decr`]: KeyContext::lock_decr()
615    pub fn lock(&self, timeout: Duration, locks: &[Key]) -> YDBResult<()> {
616        use simple_api::lock_st;
617
618        let tptoken = self.tptoken();
619        let buffer = self.take_buffer();
620        self.recover_buffer(lock_st(tptoken, buffer, timeout, locks))
621    }
622}
623
624/// Utility functions
625impl Context {
626    /// Return the message corresponding to a YottaDB error code
627    ///
628    /// # Errors
629    /// - `YDB_ERR_UNKNOWNSYSERR` if `status` is an unrecognized status code
630    ///
631    /// # See also
632    /// - [`impl Display for YDBError`][`impl Display`], which should meet most use cases for `message_t`.
633    /// - [Function return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#function-return-codes)
634    /// - [ZMessage codes](https://docs.yottadb.com/MessageRecovery/errormsgref.html#zmessage-codes)
635    /// - The [C documentation](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#ydb-message-ydb-message-t)
636    ///
637    /// [`impl Display`]: YDBError#impl-Display-for-YDBError
638    ///
639    /// # Example
640    /// Look up the error message for an undefined local variable:
641    /// ```
642    /// use yottadb::{Context, KeyContext, TpToken, YDB_ERR_LVUNDEF};
643    ///
644    /// let ctx = Context::new();
645    /// let key = KeyContext::variable(&ctx, "oopsNotDefined");
646    ///
647    /// let err = key.get().unwrap_err();
648    /// assert_eq!(err.status, YDB_ERR_LVUNDEF);
649    ///
650    /// let buf = ctx.message(err.status).unwrap();
651    /// let msg = String::from_utf8(buf).unwrap();
652    /// assert!(msg.contains("Undefined local variable"));
653    /// ```
654    pub fn message(&self, status: i32) -> YDBResult<Vec<u8>> {
655        let tptoken = self.tptoken();
656        simple_api::message_t(tptoken, Vec::new(), status)
657    }
658
659    /// Return a string in the format `rustwr <rust wrapper version> <$ZYRELEASE>`
660    ///
661    /// [`$ZYRELEASE`] is the [intrinsic variable] containing the version of the underlying C database
662    /// and `<rust wrapper version>` is the version of `yottadb` published to crates.io.
663    ///
664    /// # Errors
665    /// No errors should occur in normal operation.
666    /// However, in case of system failure, an [error code] may be returned.
667    ///
668    /// [error code]: https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code
669    /// [intrinsic variable]: https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#intrinsic-special-variables
670    /// [`$ZYRELEASE`]: https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#zyrelease
671    ///
672    /// # Example
673    /// ```
674    /// # fn main() -> yottadb::YDBResult<()> {
675    /// use yottadb::Context;
676    /// let ctx = Context::new();
677    /// let release = ctx.release()?;
678    /// # Ok(())
679    /// # }
680    /// ```
681    pub fn release(&self) -> YDBResult<String> {
682        let tptoken = self.tptoken();
683        simple_api::release_t(tptoken, Vec::new())
684    }
685}
686
687impl std::borrow::Borrow<Key> for KeyContext {
688    fn borrow(&self) -> &Key {
689        &self.key
690    }
691}
692
693impl std::borrow::BorrowMut<Key> for KeyContext {
694    fn borrow_mut(&mut self) -> &mut Key {
695        &mut self.key
696    }
697}
698
699impl Deref for KeyContext {
700    type Target = Key;
701
702    fn deref(&self) -> &Self::Target {
703        &self.key
704    }
705}
706
707impl DerefMut for KeyContext {
708    fn deref_mut(&mut self) -> &mut Self::Target {
709        &mut self.key
710    }
711}
712
713impl AddAssign<i32> for KeyContext {
714    fn add_assign(&mut self, rhs: i32) {
715        self.increment(Some(rhs.to_string().as_bytes())).expect("failed to increment node");
716    }
717}
718
719impl From<(&Context, Key)> for KeyContext {
720    fn from((ctx, key): (&Context, Key)) -> Self {
721        KeyContext::with_key(ctx, key)
722    }
723}
724
725/// The error type returned by [`KeyContext::get_and_parse()`]
726#[derive(Debug)]
727pub enum ParseError<T> {
728    /// There was an error retrieving the value from the database.
729    YDB(YDBError),
730    /// Retrieving the value succeeded, but it was not a valid `String`.
731    ///
732    /// The bytes of the value are still available using `.into_bytes()`.
733    Utf8(std::string::FromUtf8Error),
734    /// A valid `String` was retrieved but did not parse successfully.
735    /// The `String` is still available.
736    ///
737    /// The `T` is the type of `FromStr::Err` for the value being parsed.
738    Parse(T, String),
739}
740
741impl<T: fmt::Display> fmt::Display for ParseError<T> {
742    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
743        match self {
744            ParseError::YDB(err) => write!(f, "{}", err),
745            ParseError::Utf8(utf8) => write!(f, "{}", utf8),
746            ParseError::Parse(err, _) => write!(f, "{}", err),
747        }
748    }
749}
750
751impl<T: Error + 'static> Error for ParseError<T> {
752    fn source(&self) -> Option<&(dyn Error + 'static)> {
753        let err = match self {
754            ParseError::YDB(err) => err as &dyn Error,
755            ParseError::Utf8(not_utf8) => not_utf8,
756            ParseError::Parse(not_valid, _) => not_valid,
757        };
758        Some(err)
759    }
760}
761
762impl KeyContext {
763    /// Create a new `KeyContext`, creating the `Key` at the same time.
764    ///
765    /// # See also
766    /// - [`KeyContext::with_key`](KeyContext::with_key())
767    /// - [`Context::new_key()`]
768    /// - [`impl From<(&Context, Key)> for KeyContext`](KeyContext#implementations)
769    pub fn new<V, S>(ctx: &Context, variable: V, subscripts: &[S]) -> KeyContext
770    where
771        V: Into<String>,
772        S: Into<Vec<u8>> + Clone,
773    {
774        Self::with_key(ctx, Key::new(variable, subscripts))
775    }
776    /// Shortcut for creating a `KeyContext` with no subscripts.
777    // this should be kept in sync with `Key::variable`
778    pub fn variable<V: Into<String>>(ctx: &Context, var: V) -> Self {
779        Self::with_key(ctx, var)
780    }
781    /// Create a new `KeyContext` using an existing key.
782    ///
783    /// # See also
784    /// - [`KeyContext::new`](KeyContext::new())
785    /// - [`Context::new_key()`]
786    /// - [`impl From<(&Context, Key)> for KeyContext`](KeyContext#implementations)
787    pub fn with_key<K: Into<Key>>(ctx: &Context, key: K) -> Self {
788        Self { context: ctx.clone(), key: key.into() }
789    }
790
791    fn take_buffer(&self) -> Vec<u8> {
792        self.context.take_buffer()
793    }
794
795    fn recover_buffer(&self, result: YDBResult<Vec<u8>>) -> YDBResult<()> {
796        self.context.recover_buffer(result)
797    }
798
799    /// Gets the value of this key from the database and returns the value.
800    ///
801    /// # Errors
802    ///
803    /// Possible errors for this function include:
804    /// - YDB_ERR_GVUNDEF, YDB_ERR_INVSVN, YDB_ERR_LVUNDEF as appropriate if no such variable or node exists
805    /// - [error return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
806    ///
807    /// # Examples
808    ///
809    /// ```
810    /// # #[macro_use] extern crate yottadb;
811    /// use yottadb::Context;
812    /// use std::error::Error;
813    ///
814    /// fn main() -> Result<(), Box<dyn Error>> {
815    ///     let ctx = Context::new();
816    ///     let mut key = make_ckey!(ctx, "^hello");
817    ///
818    ///     key.set("Hello world!")?;
819    ///     let output_buffer = key.get()?;
820    ///
821    ///     assert_eq!(output_buffer, b"Hello world!");
822    ///
823    ///     Ok(())
824    /// }
825    /// ```
826    pub fn get(&self) -> YDBResult<Vec<u8>> {
827        let tptoken = self.context.tptoken();
828        self.key.get_st(tptoken, Vec::new())
829    }
830
831    /// Retrieve a value from the database and parse it into a Rust data structure.
832    ///
833    /// This is a shorthand for `String::from_utf8(key.get()).parse()`
834    /// that collects the errors into a single enum.
835    ///
836    /// # Examples
837    /// Set and retrieve an integer, with error handling.
838    /// ```
839    /// use yottadb::{Context, ParseError};
840    /// let ctx = Context::new();
841    /// let mut key = ctx.new_key("weekday");
842    /// key.set(5.to_string())?;
843    /// let day: u8 = match key.get_and_parse() {
844    ///     Ok(day) => day,
845    ///     Err(ParseError::YDB(err)) => return Err(err),
846    ///     Err(ParseError::Utf8(err)) => {
847    ///         eprintln!("warning: had an invalid string");
848    ///         String::from_utf8_lossy(&err.as_bytes()).parse().unwrap()
849    ///     }
850    ///     Err(ParseError::Parse(err, original)) => {
851    ///         panic!("{} is not a valid string: {}", original, err);
852    ///     }
853    /// };
854    /// Ok(())
855    /// ```
856    ///
857    /// Set and retrieve an integer, without error handling.
858    /// ```
859    /// # use yottadb::YDBResult;
860    /// # fn main() -> YDBResult<()> {
861    /// use yottadb::Context;
862    /// let ctx = Context::new();
863    /// let mut key = ctx.new_key("weekday");
864    /// key.set(5.to_string())?;
865    /// let day: u8 = key.get_and_parse().unwrap();
866    /// Ok(())
867    /// # }
868    /// ```
869    pub fn get_and_parse<T: FromStr>(&self) -> Result<T, ParseError<T::Err>> {
870        self.get()
871            .map_err(ParseError::YDB)
872            .and_then(|bytes| String::from_utf8(bytes).map_err(ParseError::Utf8))
873            .and_then(|s| s.parse().map_err(|err| ParseError::Parse(err, s)))
874    }
875
876    /// Sets the value of a key in the database.
877    ///
878    /// # Errors
879    ///
880    /// Possible errors for this function include:
881    /// - YDB_ERR_INVSVN if no such intrinsic special variable exists
882    /// - [error return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
883    ///
884    /// # Examples
885    ///
886    /// ```
887    /// # #[macro_use] extern crate yottadb;
888    /// use yottadb::Context;
889    /// use std::error::Error;
890    ///
891    /// fn main() -> Result<(), Box<dyn Error>> {
892    ///     let ctx = Context::new();
893    ///     let mut key = make_ckey!(ctx, "^hello");
894    ///
895    ///     key.set("Hello world!")?;
896    ///
897    ///     Ok(())
898    /// }
899    /// ```
900    pub fn set<U: AsRef<[u8]>>(&self, new_val: U) -> YDBResult<()> {
901        let tptoken = self.context.tptoken();
902        let out_buffer = self.take_buffer();
903        let result = self.key.set_st(tptoken, out_buffer, new_val);
904        self.recover_buffer(result)
905    }
906
907    /// Returns the following information in DataReturn about a local or global variable node:
908    ///
909    /// * NoData: There is neither a value nor a subtree; i.e it is undefined.
910    /// * ValueData: There is a value, but no subtree.
911    /// * TreeData: There is no value, but there is a subtree.
912    /// * ValueTreeData: There are both a value and a subtree.
913    ///
914    /// # Errors
915    ///
916    /// Possible errors for this function include:
917    /// - [error return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
918    ///
919    /// # Examples
920    ///
921    /// ```
922    /// # #[macro_use] extern crate yottadb;
923    /// use yottadb::{Context, DataReturn};
924    /// use std::error::Error;
925    ///
926    /// fn main() -> Result<(), Box<dyn Error>> {
927    ///     let ctx = Context::new();
928    ///     let mut key = make_ckey!(ctx, "^helloDoesNotExist");
929    ///
930    ///     assert_eq!(key.data()?, DataReturn::NoData);
931    ///
932    ///     Ok(())
933    /// }
934    /// ```
935    pub fn data(&self) -> YDBResult<DataReturn> {
936        let tptoken = self.context.tptoken();
937        let out_buffer = self.take_buffer();
938        self.key.data_st(tptoken, out_buffer).map(|(y, x)| {
939            *self.context.context.buffer.borrow_mut() = x;
940            y
941        })
942    }
943
944    /// Delete nodes in the local or global variable tree or subtree specified. A value of DelNode or DelTree for DeleteType
945    /// specifies whether to delete just the node at the root, leaving the (sub)tree intact, or to delete the node as well as the (sub)tree.
946    ///
947    /// # Errors
948    ///
949    /// Possible errors for this function include:
950    /// - [error return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
951    ///
952    /// # Examples
953    ///
954    /// ```
955    /// # #[macro_use] extern crate yottadb;
956    /// use yottadb::{Context, DataReturn, DeleteType};
957    /// use std::error::Error;
958    ///
959    /// fn main() -> Result<(), Box<dyn Error>> {
960    ///     let ctx = Context::new();
961    ///     let mut key = make_ckey!(ctx, "^helloDeleteMe");
962    ///
963    ///     key.set("Hello world!")?;
964    ///     key.delete(DeleteType::DelTree)?;
965    ///
966    ///     assert_eq!(key.data()?, DataReturn::NoData);
967    ///
968    ///     Ok(())
969    /// }
970    /// ```
971    pub fn delete(&self, delete_type: DeleteType) -> YDBResult<()> {
972        let tptoken = self.context.tptoken();
973        let out_buffer = self.take_buffer();
974        let result = self.key.delete_st(tptoken, out_buffer, delete_type);
975        self.recover_buffer(result)
976    }
977
978    /// Converts the value to a [number](https://docs.yottadb.com/MultiLangProgGuide/programmingnotes.html#canonical-numbers)
979    /// and increments it based on the value specifed by Option.
980    ///
981    /// `increment` defaults to 1 if the value is None.
982    ///
983    /// # Errors
984    ///
985    /// Possible errors for this function include:
986    /// - YDB_ERR_NUMOFLOW
987    /// - [error return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
988    ///
989    /// # See also
990    ///
991    /// - [How YDB stores numbers internally](https://docs.yottadb.com/MultiLangProgGuide/programmingnotes.html#numeric-considerations)
992    ///
993    /// # Examples
994    ///
995    /// ```
996    /// # #[macro_use] extern crate yottadb;
997    /// use yottadb::Context;
998    /// use std::error::Error;
999    ///
1000    /// fn main() -> Result<(), Box<dyn Error>> {
1001    ///     let ctx = Context::new();
1002    ///     let mut key = make_ckey!(ctx, "helloIncrementMe");
1003    ///
1004    ///     key.set("0")?;
1005    ///     key.increment(None)?;
1006    ///     let output_buffer = key.get()?;
1007    ///     assert_eq!(output_buffer, b"1");
1008    ///
1009    ///     assert_eq!(key.increment(Some(b"100"))?, b"101");
1010    ///     let output_buffer = key.get()?;
1011    ///     assert_eq!(output_buffer, b"101");
1012    ///
1013    ///     Ok(())
1014    /// }
1015    /// ```
1016    ///
1017    /// As a shorthand, you can use `+=` to increment a key.
1018    ///
1019    /// ```
1020    /// # use yottadb::{Context, KeyContext, DeleteType};
1021    /// # use std::error::Error;
1022    /// # fn main() -> Result<(), Box<dyn Error>> {
1023    /// let ctx = Context::new();
1024    /// let mut key = KeyContext::variable(&ctx, "helloAddAssign");
1025    /// key += 100;
1026    /// let output_buffer = key.get()?;
1027    /// assert_eq!(output_buffer, b"100");
1028    /// # Ok(()) }
1029    /// ```
1030    pub fn increment(&self, increment: Option<&[u8]>) -> YDBResult<Vec<u8>> {
1031        let tptoken = self.context.tptoken();
1032        self.key.incr_st(tptoken, Vec::new(), increment)
1033    }
1034
1035    /// Increment the count of a lock held by the process, or acquire a new lock.
1036    ///
1037    /// If the lock is not currently held by this process, it is acquired.
1038    /// Otherwise, the lock count is incremented.
1039    ///
1040    /// `timeout` specifies a time that the function waits to acquire the requested locks.
1041    /// If `timeout` is 0, the function makes exactly one attempt to acquire the lock.
1042    ///
1043    /// # Errors
1044    /// - `YDB_ERR_INVVARNAME` if `self.variable` is not a valid variable name.
1045    /// - `YDB_LOCK_TIMEOUT` if the lock could not be acquired within the specific time.
1046    /// - `YDB_ERR_TIME2LONG` if `timeout.as_nanos()` exceeds `YDB_MAX_TIME_NSEC`
1047    ///                    or if `timeout.as_nanos()` does not fit into a `c_ulonglong`.
1048    /// - Another [error code](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
1049    ///
1050    /// # Examples
1051    ///
1052    /// ```
1053    /// # fn main() -> Result<(), yottadb::YDBError> {
1054    /// use yottadb::{Context, KeyContext};
1055    /// use std::time::Duration;
1056    ///
1057    /// let ctx = Context::new();
1058    /// let key = KeyContext::variable(&ctx, "lockIncrTest");
1059    /// key.lock_incr(Duration::from_secs(1))?;
1060    /// # Ok(())
1061    /// # }
1062    /// ```
1063    ///
1064    /// # See also
1065    /// - The C [Simple API documentation](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#ydb-lock-decr-s-ydb-lock-decr-st)
1066    /// - [Locks](https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#locks)
1067    /// - [Variables](https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#variables-vs-subscripts-vs-values)
1068    pub fn lock_incr(&self, timeout: std::time::Duration) -> YDBResult<()> {
1069        let tptoken = self.context.tptoken();
1070        let buffer = self.take_buffer();
1071        self.recover_buffer(self.key.lock_incr_st(tptoken, buffer, timeout))
1072    }
1073
1074    /// Decrement the count of a lock held by the process.
1075    ///
1076    /// When a lock goes from 1 to 0, it is released.
1077    /// Attempting to decrement a lock not owned by the current process has no effect.
1078    ///
1079    /// # Errors
1080    /// - `YDB_ERR_INVVARNAME` if `self.variable` is not a valid variable name.
1081    ///
1082    /// # Examples
1083    ///
1084    /// ```
1085    /// # fn main() -> Result<(), yottadb::YDBError> {
1086    /// use yottadb::{Context, KeyContext};
1087    /// use std::time::Duration;
1088    ///
1089    /// let ctx = Context::new();
1090    /// let key = KeyContext::variable(&ctx, "lockDecrTest");
1091    /// key.lock_incr(Duration::from_secs(1))?;
1092    /// key.lock_decr()?;
1093    /// # Ok(())
1094    /// # }
1095    /// ```
1096    ///
1097    /// # See also
1098    /// - The C [Simple API documentation](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#ydb-lock-decr-s-ydb-lock-decr-st)
1099    /// - [Locks](https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#locks)
1100    /// - [Variables](https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#variables-vs-subscripts-vs-values)
1101    pub fn lock_decr(&self) -> YDBResult<()> {
1102        let tptoken = self.context.tptoken();
1103        let buffer = self.take_buffer();
1104        self.recover_buffer(self.key.lock_decr_st(tptoken, buffer))
1105    }
1106
1107    /// Implements breadth-first traversal of a tree by searching for the next subscript, and passes itself in as the output parameter.
1108    ///
1109    /// # Errors
1110    ///
1111    /// Possible errors for this function include:
1112    /// - YDB_ERR_NODEEND
1113    /// - [error return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
1114    ///
1115    /// # Examples
1116    ///
1117    /// ```
1118    /// # #[macro_use] extern crate yottadb;
1119    /// use yottadb::Context;
1120    /// use std::error::Error;
1121    ///
1122    /// fn main() -> Result<(), Box<dyn Error>> {
1123    ///     let ctx = Context::new();
1124    ///     let mut key = make_ckey!(ctx, "^hello", "a");
1125    ///
1126    ///     key.set("Hello world!")?;
1127    ///     key[0] = Vec::from("b");
1128    ///     key.set("Hello world!")?;
1129    ///     key[0] = Vec::from("a");
1130    ///     // Starting at a, the next sub should be b
1131    ///     key.next_sub_self()?;
1132    ///
1133    ///     assert_eq!(key[0], b"b");
1134    ///
1135    ///     Ok(())
1136    /// }
1137    /// ```
1138    pub fn next_sub_self(&mut self) -> YDBResult<()> {
1139        let tptoken = self.context.tptoken();
1140        let out_buffer = self.take_buffer();
1141        let result = self.key.sub_next_self_st(tptoken, out_buffer);
1142        self.recover_buffer(result)
1143    }
1144
1145    /// Implements reverse breadth-first traversal of a tree by searching for the previous subscript, and passes itself in as the output parameter.
1146    ///
1147    /// # Errors
1148    ///
1149    /// Possible errors for this function include:
1150    /// - YDB_ERR_NODEEND
1151    /// - [error return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
1152    ///
1153    /// # Examples
1154    ///
1155    /// ```
1156    /// # #[macro_use] extern crate yottadb;
1157    /// use yottadb::Context;
1158    /// use std::error::Error;
1159    ///
1160    /// fn main() -> Result<(), Box<dyn Error>> {
1161    ///     let ctx = Context::new();
1162    ///     let mut key = make_ckey!(ctx, "^hello", "0");
1163    ///
1164    ///     key.set("Hello world!")?;
1165    ///     key[0] = Vec::from("1");
1166    ///     key.set("Hello world!")?;
1167    ///     key[0] = Vec::from("1");
1168    ///     key.prev_sub_self()?;
1169    ///
1170    ///     assert_eq!(key[0], b"0");
1171    ///
1172    ///     Ok(())
1173    /// }
1174    /// ```
1175    pub fn prev_sub_self(&mut self) -> YDBResult<()> {
1176        let tptoken = self.context.tptoken();
1177        let out_buffer = self.take_buffer();
1178        let result = self.key.sub_prev_self_st(tptoken, out_buffer);
1179        self.recover_buffer(result)
1180    }
1181
1182    /// Implements breadth-first traversal of a tree by searching for the next subscript.
1183    ///
1184    /// # Errors
1185    ///
1186    /// Possible errors for this function include:
1187    /// - YDB_ERR_NODEEND
1188    /// - [error return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
1189    ///
1190    /// # Examples
1191    ///
1192    /// ```
1193    /// # #[macro_use] extern crate yottadb;
1194    /// use yottadb::Context;
1195    /// use std::error::Error;
1196    ///
1197    /// fn main() -> Result<(), Box<dyn Error>> {
1198    ///     let ctx = Context::new();
1199    ///     let mut key = make_ckey!(ctx, "^hello", "0");
1200    ///
1201    ///     key.set("Hello world!")?;
1202    ///     key[0] = Vec::from("1");
1203    ///     key.set("Hello world!")?;
1204    ///     key[0] = Vec::from("0");
1205    ///     let subscript = key.next_sub()?;
1206    ///
1207    ///     assert_eq!(subscript, b"1");
1208    ///
1209    ///     Ok(())
1210    /// }
1211    /// ```
1212    pub fn next_sub(&self) -> YDBResult<Vec<u8>> {
1213        let tptoken = self.context.tptoken();
1214        let out_buffer = self.take_buffer();
1215        self.key.sub_next_st(tptoken, out_buffer)
1216    }
1217
1218    /// Implements reverse breadth-first traversal of a tree by searching for the previous subscript.
1219    ///
1220    /// # Errors
1221    ///
1222    /// Possible errors for this function include:
1223    /// - YDB_ERR_NODEEND
1224    /// - [error return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
1225    ///
1226    /// # Examples
1227    ///
1228    /// ```
1229    /// # #[macro_use] extern crate yottadb;
1230    /// use yottadb::Context;
1231    /// use std::error::Error;
1232    ///
1233    /// fn main() -> Result<(), Box<dyn Error>> {
1234    ///     let ctx = Context::new();
1235    ///     let mut key = make_ckey!(ctx, "^hello", "0");
1236    ///
1237    ///     key.set(b"Hello world!")?;
1238    ///     key[0] = Vec::from("1");
1239    ///     key.set("Hello world!")?;
1240    ///     key[0] = Vec::from("1");
1241    ///     let subscript = key.prev_sub()?;
1242    ///
1243    ///     assert_eq!(subscript, b"0");
1244    ///
1245    ///     Ok(())
1246    /// }
1247    /// ```
1248    pub fn prev_sub(&self) -> YDBResult<Vec<u8>> {
1249        let tptoken = self.context.tptoken();
1250        let out_buffer = self.take_buffer();
1251        self.key.sub_prev_st(tptoken, out_buffer)
1252    }
1253
1254    /// Facilitates depth-first traversal of a local or global variable tree, and passes itself in as the output parameter.
1255    ///
1256    /// For more information on variable trees, see the [overview of YottaDB][how-it-works]
1257    /// as well as the section on [variables and nodes][vars-nodes].
1258    ///
1259    /// # Errors
1260    ///
1261    /// Possible errors for this function include:
1262    /// - YDB_ERR_NODEEND
1263    /// - [error return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
1264    ///
1265    /// # Examples
1266    ///
1267    /// ```
1268    /// # #[macro_use] extern crate yottadb;
1269    /// use yottadb::Context;
1270    /// use std::error::Error;
1271    ///
1272    /// fn main() -> Result<(), Box<dyn Error>> {
1273    ///     let ctx = Context::new();
1274    ///     let mut key = make_ckey!(ctx, "^hello", "0", "0");
1275    ///
1276    ///     key.set("Hello world!")?;
1277    ///     // Forget the second subscript
1278    ///     key.truncate(1);
1279    ///     key.next_node_self()?;
1280    ///
1281    ///     assert_eq!(key[1], b"0");
1282    ///
1283    ///     Ok(())
1284    /// }
1285    /// ```
1286    ///
1287    /// [how-it-works]: https://yottadb.com/product/how-it-works/
1288    /// [vars-nodes]: https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#keys-values-nodes-variables-and-subscripts
1289    pub fn next_node_self(&mut self) -> YDBResult<()> {
1290        let tptoken = self.context.tptoken();
1291        let out_buffer = self.take_buffer();
1292        let result = self.key.node_next_self_st(tptoken, out_buffer);
1293        self.recover_buffer(result)
1294    }
1295
1296    /// Facilitates reverse depth-first traversal of a local or global variable tree and reports the predecessor node, passing itself in as the output parameter.
1297    ///
1298    /// # Errors
1299    ///
1300    /// Possible errors for this function include:
1301    /// - YDB_ERR_NODEEND
1302    /// - [error return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
1303    ///
1304    /// # Examples
1305    ///
1306    /// ```
1307    /// # #[macro_use] extern crate yottadb;
1308    /// use yottadb::Context;
1309    /// use std::error::Error;
1310    ///
1311    /// fn main() -> Result<(), Box<dyn Error>> {
1312    ///     let ctx = Context::new();
1313    ///     let mut key = make_ckey!(ctx, "^hello", "0", "0");
1314    ///
1315    ///     key.set("Hello world!")?;
1316    ///     // Forget the second subscript
1317    ///     key.truncate(1);
1318    ///     // Go to the next node, then walk backward
1319    ///     key[0] = Vec::from("1");
1320    ///     key.prev_node_self()?;
1321    ///
1322    ///     assert_eq!(key[1], b"0");
1323    ///
1324    ///     Ok(())
1325    /// }
1326    /// ```
1327    pub fn prev_node_self(&mut self) -> YDBResult<()> {
1328        let tptoken = self.context.tptoken();
1329        let out_buffer = self.take_buffer();
1330        let result = self.key.node_prev_self_st(tptoken, out_buffer);
1331        self.recover_buffer(result)
1332    }
1333
1334    /// Facilitate depth-first traversal of a local or global variable tree.
1335    ///
1336    /// # Errors
1337    ///
1338    /// Possible errors for this function include:
1339    /// - YDB_ERR_NODEEND
1340    /// - [error return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
1341    ///
1342    /// # Examples
1343    ///
1344    /// ```
1345    /// # #[macro_use] extern crate yottadb;
1346    /// use yottadb::Context;
1347    /// use std::error::Error;
1348    ///
1349    /// fn main() -> Result<(), Box<dyn Error>> {
1350    ///     let ctx = Context::new();
1351    ///     let mut key = make_ckey!(ctx, "^hello", "0", "0");
1352    ///
1353    ///     key.set("Hello world!")?;
1354    ///     // Forget the second subscript
1355    ///     key.truncate(1);
1356    ///     let k2 = key.next_node()?;
1357    ///
1358    ///     assert_eq!(k2[1], b"0");
1359    ///
1360    ///     Ok(())
1361    /// }
1362    /// ```
1363    pub fn next_node(&mut self) -> YDBResult<KeyContext> {
1364        let mut ret = self.clone();
1365        ret.next_node_self()?;
1366        Ok(ret)
1367    }
1368
1369    /// Facilitates reverse depth-first traversal of a local or global variable tree, and returns
1370    /// the previous node.
1371    ///
1372    /// # Errors
1373    ///
1374    /// Possible errors for this function include:
1375    /// - YDB_ERR_NODEEND
1376    /// - [error return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
1377    ///
1378    /// # Examples
1379    ///
1380    /// ```
1381    /// # #[macro_use] extern crate yottadb;
1382    /// use yottadb::Context;
1383    /// use std::error::Error;
1384    ///
1385    /// fn main() -> Result<(), Box<dyn Error>> {
1386    ///     let ctx = Context::new();
1387    ///     let mut key = make_ckey!(ctx, "^helloPrevNode", "0", "0");
1388    ///
1389    ///     key.set("Hello world!")?;
1390    ///     // Forget the second subscript
1391    ///     key.truncate(1);
1392    ///     // Go to the next node, then walk backward
1393    ///     key[0] = "1".into();
1394    ///     let k2 = key.prev_node()?;
1395    ///
1396    ///     assert_eq!(&k2.variable, "^helloPrevNode");
1397    ///     assert_eq!(k2[0], b"0");
1398    ///     assert_eq!(k2[1], b"0");
1399    ///
1400    ///     Ok(())
1401    /// }
1402    /// ```
1403    pub fn prev_node(&mut self) -> YDBResult<KeyContext> {
1404        let mut ret = self.clone();
1405        ret.prev_node_self()?;
1406        Ok(ret)
1407    }
1408
1409    gen_iter_proto!(
1410        /// Iterates over all the values at this level of the database tree and returns the value for
1411        /// each node.
1412        iter_values,
1413        ForwardValueIterator
1414    );
1415
1416    gen_iter_proto!(
1417        /// Iterates over all the subscripts at this level of the database tree and returns the
1418        /// subscript for each node.
1419        iter_subs,
1420        ForwardSubIterator
1421    );
1422
1423    gen_iter_proto!(
1424        /// Iterates over all the subscripts at this level of the database tree and returns the subscript and value for each node.
1425        iter_subs_values,
1426        ForwardSubValueIterator
1427    );
1428
1429    gen_iter_proto!(
1430        /// Iterates over all subscripts at this level of the database tree and returns a copy of the key at each subscript.
1431        iter_key_subs,
1432        ForwardKeySubIterator
1433    );
1434
1435    gen_iter_proto!(
1436        /// Iterates over all nodes for the global pointed to by the key and returns the value at each node.
1437        iter_nodes,
1438        ForwardNodeIterator
1439    );
1440
1441    gen_iter_proto!(
1442        /// Iterates over all nodes for the global pointed to by the key and returns a copy of the key at each node.
1443        iter_key_nodes,
1444        ForwardKeyNodeIterator
1445    );
1446
1447    gen_iter_proto!(
1448        /// Iterates in reverse order over all the values at this level of the database tree and returns the value for
1449        /// each node.
1450        iter_values_reverse,
1451        ReverseValueIterator
1452    );
1453
1454    gen_iter_proto!(
1455        /// Iterates in reverse order over all the subscripts at this level of the database tree and returns the
1456        /// subscript for each node.
1457        iter_subs_reverse,
1458        ReverseSubIterator
1459    );
1460
1461    gen_iter_proto!(
1462        /// Iterates in reverse order over all the subscripts at this level of the database tree and returns the subscript and value for each node.
1463        iter_subs_values_reverse,
1464        ReverseSubValueIterator
1465    );
1466
1467    gen_iter_proto!(
1468        /// Iterates in reverse order over all subscripts at this level of the database tree and returns a copy of the key at each subscript.
1469        iter_key_subs_reverse,
1470        ReverseKeySubIterator
1471    );
1472
1473    gen_iter_proto!(
1474        /// Iterates in reverse order over all nodes for the global pointed to by the key and returns the value at each node.
1475        iter_nodes_reverse,
1476        ReverseNodeIterator
1477    );
1478
1479    gen_iter_proto!(
1480        /// Iterates in reverse oder over all nodes for the global pointed to by the key and returns a copy of the key at each node.
1481        iter_key_nodes_reverse,
1482        ReverseKeyNodeIterator
1483    );
1484}
1485
1486implement_iterator!(
1487    ForwardValueIterator,
1488    next_sub_self,
1489    Vec<u8>,
1490    |me: &mut ForwardValueIterator| { Some(me.key.get()) }
1491);
1492
1493implement_iterator!(ForwardSubIterator, next_sub_self, Vec<u8>, |me: &mut ForwardSubIterator| {
1494    Some(Ok(me.key.last().unwrap().clone()))
1495});
1496
1497implement_iterator!(
1498    ForwardSubValueIterator,
1499    next_sub_self,
1500    (Vec<u8>, Vec<u8>),
1501    |me: &mut ForwardSubValueIterator| {
1502        let val = me.key.get();
1503        if val.is_err() {
1504            let err = val.err().unwrap();
1505            return Some(Err(err));
1506        }
1507        Some(Ok((me.key.last().unwrap().clone(), val.unwrap())))
1508    }
1509);
1510
1511implement_iterator!(
1512    ForwardKeySubIterator,
1513    next_sub_self,
1514    KeyContext,
1515    |me: &mut ForwardKeySubIterator| { Some(Ok(me.key.clone())) }
1516);
1517
1518implement_iterator!(
1519    ForwardNodeIterator,
1520    next_node_self,
1521    Vec<u8>,
1522    |me: &mut ForwardNodeIterator| {
1523        let data = me.key.data().unwrap();
1524        if data != DataReturn::ValueData && data != DataReturn::ValueTreeData {
1525            return me.next();
1526        }
1527        Some(me.key.get())
1528    }
1529);
1530
1531implement_iterator!(
1532    ForwardKeyNodeIterator,
1533    next_node_self,
1534    KeyContext,
1535    |me: &mut ForwardKeyNodeIterator| { Some(Ok(me.key.clone())) }
1536);
1537
1538implement_iterator!(
1539    ReverseValueIterator,
1540    prev_sub_self,
1541    Vec<u8>,
1542    |me: &mut ReverseValueIterator| { Some(me.key.get()) }
1543);
1544
1545implement_iterator!(ReverseSubIterator, prev_sub_self, Vec<u8>, |me: &mut ReverseSubIterator| {
1546    Some(Ok(me.key.last().unwrap().clone()))
1547});
1548
1549implement_iterator!(
1550    ReverseSubValueIterator,
1551    prev_sub_self,
1552    (Vec<u8>, Vec<u8>),
1553    |me: &mut ReverseSubValueIterator| {
1554        let val = me.key.get();
1555        if val.is_err() {
1556            let err = val.err().unwrap();
1557            return Some(Err(err));
1558        }
1559        Some(Ok((me.key.last().unwrap().clone(), val.unwrap())))
1560    }
1561);
1562
1563implement_iterator!(
1564    ReverseKeySubIterator,
1565    prev_sub_self,
1566    KeyContext,
1567    |me: &mut ReverseKeySubIterator| { Some(Ok(me.key.clone())) }
1568);
1569
1570implement_iterator!(
1571    ReverseNodeIterator,
1572    prev_node_self,
1573    Vec<u8>,
1574    |me: &mut ReverseNodeIterator| { Some(me.key.get()) }
1575);
1576
1577implement_iterator!(
1578    ReverseKeyNodeIterator,
1579    prev_node_self,
1580    KeyContext,
1581    |me: &mut ReverseKeyNodeIterator| { Some(Ok(me.key.clone())) }
1582);
1583
1584#[cfg(test)]
1585mod tests;