chj_rustbin/util/
div.rs

1use std::{
2    borrow::Borrow,
3    collections::{
4        btree_map,
5        hash_map::{Entry, OccupiedEntry},
6        BTreeMap, HashMap,
7    },
8    convert::TryInto,
9    fmt::Display,
10    hash::Hash,
11    ops::Add,
12    time::Duration,
13};
14
15// A HashMap::get_mut variant that allows to work around the issue
16// that rustc (pre polonius) does not let go of the reference in the
17// None case; so we use an Err instead and pick up the reference from
18// there.
19pub fn hashmap_get_mut<'m, K: Eq + Hash, P: Eq + Hash + ?Sized, V>(
20    m: &'m mut HashMap<K, V>,
21    k: &P,
22) -> Result<&'m mut V, &'m mut HashMap<K, V>>
23where
24    K: Borrow<P>,
25{
26    let pm: *mut _ = m;
27    // Safe because in the true branch we track using the lifetimes
28    // (just like in the original get_mut), and in the false branch we
29    // just pass the input value.
30    if let Some(v) = unsafe { &mut *pm }.get_mut(k) {
31        Ok(v)
32    } else {
33        Err(unsafe { &mut *pm })
34    }
35}
36
37// Same (see hashmap_get_mut) for BTreeMap.
38pub fn btreemap_get_mut<'m, K: Ord, P: Ord + ?Sized, V>(
39    m: &'m mut BTreeMap<K, V>,
40    k: &P,
41) -> Result<&'m mut V, &'m mut BTreeMap<K, V>>
42where
43    K: Borrow<P>,
44{
45    let pm: *mut _ = m;
46    // Safe because in the true branch we track using the lifetimes
47    // (just like in the original get_mut), and in the false branch we
48    // just pass the input value.
49    if let Some(v) = unsafe { &mut *pm }.get_mut(k) {
50        Ok(v)
51    } else {
52        Err(unsafe { &mut *pm })
53    }
54}
55
56// Modified copy of #[unstable(feature = "map_try_insert", issue =
57// "82766")] from
58// https://doc.rust-lang.org/src/std/collections/hash/map.rs.html#1132-1137,
59// avoiding OccupiedError because that's also unstable. FUTURE:
60// replace with try_insert.
61pub fn hashmap_try_insert<'m, K: Eq + Hash, V>(
62    m: &'m mut HashMap<K, V>,
63    key: K,
64    value: V,
65) -> Result<&'m mut V, OccupiedEntry<'m, K, V>> {
66    match m.entry(key) {
67        Entry::Occupied(entry) => Err(entry),
68        Entry::Vacant(entry) => Ok(entry.insert(value)),
69    }
70}
71
72// Modified copy of #[unstable(feature = "map_try_insert", issue =
73// "82766")] from
74// https://doc.rust-lang.org/src/alloc/collections/btree/map.rs.html#1016-1018;
75// see comments on hashmap_try_insert.
76pub fn btreemap_try_insert<'m, K: Ord, V>(
77    m: &'m mut BTreeMap<K, V>,
78    key: K,
79    value: V,
80) -> Result<&'m mut V, btree_map::OccupiedEntry<'m, K, V>> {
81    match m.entry(key) {
82        btree_map::Entry::Occupied(entry) => Err(entry),
83        btree_map::Entry::Vacant(entry) => Ok(entry.insert(value)),
84    }
85}
86
87pub fn hashmap_get_mut_vivify<'m, K: Eq + Hash + Clone, V>(
88    m: &'m mut HashMap<K, V>,
89    k: &K,
90    create: impl FnOnce() -> V,
91) -> &'m mut V {
92    match hashmap_get_mut(m, k) {
93        Ok(val) => val,
94        Err(m) => {
95            m.insert(k.clone(), create());
96            m.get_mut(k).expect("just inserted")
97        }
98    }
99}
100
101/// Insert a key-value mapping, or if already existing, add the value
102/// to the existing value via Add trait.
103pub fn hashmap_add<K: Hash + Eq, V: Add<Output = V> + Copy>(
104    m: &mut HashMap<K, V>,
105    k: K,
106    v: V,
107) {
108    match hashmap_get_mut(m, &k) {
109        Ok(oldv) => {
110            // *oldv += v;  What's needed for this?
111            *oldv = oldv.add(v);
112        }
113        Err(m) => {
114            m.insert(k, v);
115        }
116    }
117}
118
119// Sigh, for exponential backoff, everybody doing this for themselves?
120pub fn duration_mul_div(
121    orig: Duration,
122    multiplier: u64,
123    divider: u64,
124) -> Option<Duration> {
125    let nanos: u64 = orig
126        .as_nanos()
127        .checked_mul(multiplier as u128)?
128        .checked_div(divider as u128)?
129        .try_into()
130        .ok()?;
131    Some(Duration::from_nanos(nanos))
132}
133
134pub fn first<T>(items: &[T]) -> &T {
135    &items[0]
136}
137
138pub fn rest<T>(items: &[T]) -> &[T] {
139    &items[1..]
140}
141
142pub fn debug_stringlikes<S: Display>(v: &[S]) -> Vec<String> {
143    v.iter().map(|s| s.to_string()).collect()
144}
145
146/// A loop that caches errors and retries with exponential
147/// backoff. (Backoff parameters and error messaging hard coded for
148/// now, as is anyhow::Result.)
149#[macro_export]
150macro_rules! loop_try {
151    ( $($body_parts:tt)* ) => {{
152        let default_error_sleep_duration = Duration::from_millis(500);
153        let mut error_sleep_duration = default_error_sleep_duration;
154        loop {
155            match (|| -> Result<()> { $($body_parts)* })() {
156                Ok(()) => {
157                    error_sleep_duration = default_error_sleep_duration;
158                }
159                Err(e) => {
160                    eprintln!("loop_try: got error {e:#}, sleeping for \
161                               {error_sleep_duration:?}");
162                    thread::sleep(error_sleep_duration);
163                    error_sleep_duration =
164                        $crate::util::duration_mul_div(error_sleep_duration,
165                                                       1200,
166                                                       1000)
167                        .unwrap_or(default_error_sleep_duration);
168                }
169            }
170        }
171    }}
172}
173
174// For cases where the context is not Option, hence `?` does not
175// work. -- vs. try_option ?!
176#[macro_export]
177macro_rules! tryoption {
178    ($e:expr) => {{
179        let res = $e;
180        if let Some(val) = res {
181            val
182        } else {
183            return Ok(None);
184        }
185    }};
186}
187
188#[macro_export]
189macro_rules! try_do {
190    ( $($b:tt)* ) => ( (|| { $($b)* })() )
191}
192
193#[macro_export]
194macro_rules! try_result {
195    ( $($b:tt)* ) => ( (|| -> Result<_> { $($b)* })() )
196}
197
198#[macro_export]
199macro_rules! try_option {
200    ( $($b:tt)* ) => ( (|| -> Option<_> { $($b)* })() )
201}
202
203/// Alternative for `?` in genawaiter closures.
204#[macro_export]
205macro_rules! gen_try_result {
206    ( $e:expr, $co:expr ) => {
207        match $e {
208            Ok(v) => v,
209            Err(e) => {
210                $co.yield_(Err(e)).await;
211                return;
212            }
213        }
214    };
215}