Skip to main content

rumtk_core/
core.rs

1/*
2 * rumtk attempts to implement HL7 and medical protocols for interoperability in medicine.
3 * This toolkit aims to be reliable, simple, performant, and standards compliant.
4 * Copyright (C) 2025  Luis M. Santos, M.D.
5 * Copyright (C) 2025  MedicalMasses L.L.C.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21use crate::strings::rumtk_format;
22use crate::strings::RUMString;
23use crate::types::RUMBuffer;
24
25pub type RUMError = RUMString;
26
27///
28/// Type used for propagating error messages.
29///
30pub type RUMResult<T> = Result<T, RUMError>;
31
32pub type RUMVec<T> = Vec<T>;
33
34pub fn is_unique<T: std::cmp::Eq + std::hash::Hash>(data: &Vec<T>) -> bool {
35    let mut keys = ahash::AHashSet::with_capacity(data.len());
36    for itm in data {
37        if !keys.insert(itm) {
38            return false;
39        }
40    }
41    true
42}
43
44///
45/// Take a requested index and the maximum size of the item container.
46/// Check if the index is valid and return an error if it is.
47/// The purpose of this function is to enable handling of out of bounds without triggering a panic.
48/// Also, add negative indices like Python does when doing a reverse search!
49///
50/// * If the index is 0, return Error
51/// * If the index is below 0, return the max - index iff max - index > 0
52/// * If the index is bigger than the defined max, return Error.
53/// * Otherwise, return the given index.
54///
55/// # Examples
56///
57/// ## Min
58/// ```
59/// use ::rumtk_core::core::clamp_index;
60/// use ::rumtk_core::strings::rumtk_format;
61/// let max: isize = 5;
62/// let i: isize = 1;
63/// let result = clamp_index(&i, &max).unwrap();
64/// assert_eq!(&1, &result, "{}", rumtk_format!("Expected to receive 0 but got {}", &result))
65/// ```
66///
67/// ## Max
68/// ```
69/// use ::rumtk_core::core::clamp_index;
70/// use ::rumtk_core::strings::rumtk_format;
71/// let max: isize = 5;
72/// let i: isize = 5;
73/// let result = clamp_index(&i, &max).unwrap();
74/// assert_eq!(&5, &result, "{}", rumtk_format!("Expected to receive 0 but got {}", &result))
75/// ```
76///
77/// ## Valid
78/// ```
79/// use ::rumtk_core::core::clamp_index;
80/// use ::rumtk_core::strings::rumtk_format;
81/// let max: isize = 5;
82/// let i: isize = 5;
83/// let result = clamp_index(&i, &max).unwrap();
84/// assert_eq!(&5, &result, "{}", rumtk_format!("Expected to receive 0 but got {}", &result))
85/// ```
86///
87/// ## Valid Negative Index (reverse lookup)
88/// ```
89/// use ::rumtk_core::core::clamp_index;
90/// use ::rumtk_core::strings::rumtk_format;
91/// let max: isize = 5;
92/// let i: isize = -1;
93/// let result = clamp_index(&i, &max).unwrap();
94/// assert_eq!(&5, &result, "{}", rumtk_format!("Expected to receive 0 but got {}", &result))
95/// ```
96#[inline(always)]
97pub fn clamp_index(given_indx: &isize, max_size: &isize) -> RUMResult<usize> {
98    let neg_max_indx = *max_size * -1;
99    if *given_indx == 0 {
100        return Err(rumtk_format!(
101            "Index {} is invalid! Use 1-indexed values if using positive indices.",
102            given_indx
103        ));
104    }
105
106    if *given_indx >= neg_max_indx && *given_indx < 0 {
107        return Ok((max_size + given_indx + 1) as usize);
108    }
109
110    if *given_indx > 0 && given_indx <= max_size {
111        return Ok(*given_indx as usize);
112    }
113
114    Err(rumtk_format!(
115        "Index {} is outside {} < x < {} boundary!",
116        given_indx,
117        neg_max_indx,
118        max_size
119    ))
120}
121
122///
123/// Generates a new random buffer using the `getrandom` crate and wrapped inside a [RUMBuffer](RUMBuffer).
124/// The buffer will be exactly 1024 bytes.
125///
126/// ## Example
127///
128/// ```
129/// use rumtk_core::core::new_random_buffer;
130///
131/// const BUFFER_SIZE: usize = 1024;
132///
133/// let buffer = new_random_buffer();
134///
135/// assert_eq!(buffer.is_empty(), false, "Function returned an empty random buffer which was unexpected!");
136/// assert_eq!(buffer.len(), BUFFER_SIZE, "The new random buffer does not have the expected size!");
137/// ```
138///
139pub fn new_random_buffer() -> RUMBuffer {
140    let mut buffer = [0u8; 1024];
141    match getrandom::fill(&mut buffer) {
142        Ok(_) => {}
143        Err(_) => {}
144    };
145    RUMBuffer::copy_from_slice(buffer.as_slice())
146}