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 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20use crate::strings::RUMString;
21use compact_str::format_compact;
22pub use smallvec::{smallvec, SmallVec};
23
24///
25/// Type used for propagating error messages.
26///
27pub type RUMResult<T> = Result<T, RUMString>;
28
29pub type RUMVec<T> = Vec<T>;
30
31pub fn is_unique<T: std::cmp::Eq + std::hash::Hash>(data: &Vec<T>) -> bool {
32 let mut keys = ahash::AHashSet::with_capacity(data.len());
33 for itm in data {
34 if !keys.insert(itm) {
35 return false;
36 }
37 }
38 true
39}
40
41///
42/// Take a requested index and the maximum size of the item container.
43/// Check if the index is valid and return an error if it is.
44/// The purpose of this function is to enable handling of out of bounds without triggering a panic.
45/// Also, add negative indices like Python does when doing a reverse search!
46///
47/// * If the index is 0, return Error
48/// * If the index is below 0, return the max - index iff max - index > 0
49/// * If the index is bigger than the defined max, return Error.
50/// * Otherwise, return the given index.
51///
52/// # Examples
53///
54/// ## Min
55/// ```
56/// use ::rumtk_core::core::clamp_index;
57/// use ::rumtk_core::strings::format_compact;
58/// let max: isize = 5;
59/// let i: isize = 1;
60/// let result = clamp_index(&i, &max).unwrap();
61/// assert_eq!(&1, &result, "{}", format_compact!("Expected to receive 0 but got {}", &result))
62/// ```
63///
64/// ## Max
65/// ```
66/// use ::rumtk_core::core::clamp_index;
67/// use ::rumtk_core::strings::format_compact;
68/// let max: isize = 5;
69/// let i: isize = 5;
70/// let result = clamp_index(&i, &max).unwrap();
71/// assert_eq!(&5, &result, "{}", format_compact!("Expected to receive 0 but got {}", &result))
72/// ```
73///
74/// ## Valid
75/// ```
76/// use ::rumtk_core::core::clamp_index;
77/// use ::rumtk_core::strings::format_compact;
78/// let max: isize = 5;
79/// let i: isize = 5;
80/// let result = clamp_index(&i, &max).unwrap();
81/// assert_eq!(&5, &result, "{}", format_compact!("Expected to receive 0 but got {}", &result))
82/// ```
83///
84/// ## Valid Negative Index (reverse lookup)
85/// ```
86/// use ::rumtk_core::core::clamp_index;
87/// use ::rumtk_core::strings::format_compact;
88/// let max: isize = 5;
89/// let i: isize = -1;
90/// let result = clamp_index(&i, &max).unwrap();
91/// assert_eq!(&5, &result, "{}", format_compact!("Expected to receive 0 but got {}", &result))
92/// ```
93#[inline(always)]
94pub fn clamp_index(given_indx: &isize, max_size: &isize) -> RUMResult<usize> {
95 let neg_max_indx = *max_size * -1;
96 if *given_indx == 0 {
97 return Err(format_compact!(
98 "Index {} is invalid! Use 1-indexed values if using positive indices.",
99 given_indx
100 ));
101 }
102
103 if *given_indx >= neg_max_indx && *given_indx < 0 {
104 return Ok((max_size + given_indx + 1) as usize);
105 }
106
107 if *given_indx > 0 && given_indx <= max_size {
108 return Ok(*given_indx as usize);
109 }
110
111 Err(format_compact!(
112 "Index {} is outside {} < x < {} boundary!",
113 given_indx,
114 neg_max_indx,
115 max_size
116 ))
117}