Skip to main content

libmqm_constants/
lookup.rs

1use libmqm_sys::MQLONG;
2
3use crate::mapping;
4
5pub trait MqConstant {
6    type Value;
7    fn mq_value(&self) -> Self::Value;
8}
9
10pub type ConstantItem<'a> = (MQLONG, &'a str);
11
12/// Provides an MQ lookup functions to a type
13pub trait ConstLookup {
14    /// All the constant names for the provided value.
15    /// The first value returned by the iterator is the primary constant for the value.
16    fn by_value(&self, value: MQLONG) -> impl Iterator<Item = &str>;
17    /// The constant value for the provided name.
18    fn by_name(&self, name: &str) -> Option<MQLONG>;
19    /// The complete list of value and name constants
20    fn all(&self) -> impl Iterator<Item = ConstantItem<'_>>;
21}
22
23/// MQ constant repository with primary and secondary constants
24pub struct ConstSource<P, S>(pub(crate) P, pub(crate) S);
25pub type PhfSource<'a> = ConstSource<&'a ::phf::Map<MQLONG, &'a str>, &'a [ConstantItem<'a>]>;
26pub type LinearSource<'a> = ConstSource<&'a [ConstantItem<'a>], &'a [ConstantItem<'a>]>;
27pub type BinarySearchSource<'a> = ConstSource<BinarySearch<'a>, &'a [ConstantItem<'a>]>;
28
29/// MQ constant repository to be searched by value using binary search
30pub struct BinarySearch<'a>(pub(crate) &'a [ConstantItem<'a>]);
31
32/// Associated constant lookup table with a type
33pub trait HasConstLookup {
34    /// Retrieve the static constant lookup table
35    fn const_lookup<'a>() -> &'a (impl ConstLookup + 'static);
36}
37
38impl<P: ConstLookup, S: ConstLookup> ConstLookup for ConstSource<P, S> {
39    fn by_value(&self, value: MQLONG) -> impl Iterator<Item = &str> {
40        let Self(source, extra) = self;
41        source.by_value(value).chain(extra.by_value(value))
42    }
43
44    fn by_name(&self, name: &str) -> Option<MQLONG> {
45        let Self(source, extra) = self;
46        source.by_name(name).or_else(|| extra.by_name(name))
47    }
48
49    fn all(&self) -> impl Iterator<Item = ConstantItem<'_>> {
50        let Self(source, extra) = self;
51        source.all().chain(extra.all())
52    }
53}
54
55impl ConstLookup for BinarySearch<'_> {
56    fn by_value(&self, value: MQLONG) -> impl Iterator<Item = &str> {
57        let Self(list) = self;
58        list.binary_search_by_key(&value, |(value, ..)| *value)
59            .map(|index| list[index].1)
60            .into_iter()
61    }
62
63    fn by_name(&self, name: &str) -> Option<MQLONG> {
64        let &Self(list) = self;
65        list.by_name(name)
66    }
67
68    fn all(&self) -> impl Iterator<Item = ConstantItem<'_>> {
69        let list = &self.0;
70        list.iter().copied()
71    }
72}
73
74// phf map of ConstItem
75impl ConstLookup for &::phf::Map<MQLONG, &str> {
76    fn by_value(&self, value: MQLONG) -> impl Iterator<Item = &str> {
77        self.get(&value).copied().into_iter()
78    }
79
80    fn by_name(&self, name: &str) -> Option<MQLONG> {
81        let value: Option<&MQLONG> = mapping::MQI_BY_STRING.get(name);
82        value.filter(|v| self.get(v) == Some(&name)).copied()
83    }
84
85    fn all(&self) -> impl Iterator<Item = ConstantItem<'_>> {
86        self.entries().map(|(&v, &n)| (v, n))
87    }
88}
89
90// Ordered array of ConstItems
91impl ConstLookup for &[ConstantItem<'_>] {
92    fn by_value(&self, value: MQLONG) -> impl Iterator<Item = &str> {
93        self.iter()
94            .take_while(move |(v, ..)| *v <= value)
95            .filter_map(move |(v, name)| (*v == value).then_some(*name))
96    }
97
98    fn by_name(&self, name: &str) -> Option<MQLONG> {
99        self.iter().find_map(|(value, n)| (*n == name).then_some(*value))
100    }
101
102    fn all(&self) -> impl Iterator<Item = ConstantItem<'_>> {
103        self.iter().copied()
104    }
105}
106
107pub trait HasMqNames {
108    fn mq_names(&self) -> impl Iterator<Item = &'static str>;
109    fn mq_primary_name(&self) -> Option<&'static str>;
110}
111
112impl<T: MqConstant + HasConstLookup> HasMqNames for T
113where
114    T::Value: TryInto<MQLONG>,
115{
116    fn mq_names(&self) -> impl Iterator<Item = &'static str> {
117        self.mq_value()
118            .try_into()
119            .map(|value| Self::const_lookup().by_value(value))
120            .into_iter()
121            .flatten()
122    }
123    fn mq_primary_name(&self) -> Option<&'static str> {
124        self.mq_names().next()
125    }
126}
127
128#[cfg(test)]
129#[cfg_attr(coverage_nightly, coverage(off))]
130mod tests {
131    use super::*;
132
133    const ZERO: LinearSource = ConstSource(&[], &[]);
134    const ONE: LinearSource = ConstSource(&[(1, "ONE")], &[]);
135    const ONEB: LinearSource = ConstSource(&[(1, "ONE")], &[(1, "ONEB"), (2, "TWO")]);
136
137    #[test]
138    fn const_source() {
139        assert_eq!(ZERO.by_name("TEST"), None);
140        assert_eq!(ONE.by_name("ONE"), Some(1));
141        assert_eq!(ONE.by_name("ZERO"), None);
142        assert_eq!(ONEB.by_name("ONEB"), Some(1));
143        assert_eq!(ONEB.by_name("THREE"), None);
144
145        assert_eq!(ONEB.by_value(1).collect::<Vec<_>>(), &["ONE", "ONEB"]);
146        assert_eq!(ONEB.by_value(0).collect::<Vec<_>>(), Vec::<&str>::new());
147    }
148}