Skip to main content

syn_sem/etc/
util.rs

1use crate::{Map, Result};
2use std::{
3    borrow::Cow,
4    cell::RefCell,
5    ffi::OsStr,
6    fmt::{self, Write},
7    hash::Hash,
8    hash::Hasher,
9    str,
10    sync::{Mutex, MutexGuard},
11};
12
13pub(crate) fn os_str_to_str(os: &OsStr) -> Result<&str> {
14    if let Some(s) = os.to_str() {
15        Ok(s)
16    } else {
17        Err(format!("`{os:?}` is not a UTF-8 literal").into())
18    }
19}
20
21pub(crate) fn push_colon_path<S>(dst: &mut String, add: S)
22where
23    S: fmt::Display,
24{
25    if dst.is_empty() || dst.ends_with("::") {
26        write!(dst, "{}", add)
27    } else {
28        write!(dst, "::{}", add)
29    }
30    .unwrap();
31}
32
33pub(crate) struct FiniteLoop;
34
35impl FiniteLoop {
36    thread_local! {
37        static COUNTERS: RefCell<Map<&'static str, Map<u64, u32>>> = RefCell::new(
38            Map::default()
39        );
40        static LIMITS: RefCell<Map<&'static str, u32>> = RefCell::new(
41            Map::default()
42        );
43    }
44
45    pub(crate) fn set_limit(id: &'static str, limit: u32) {
46        Self::LIMITS.with(|limits| {
47            let mut limits = limits.borrow_mut();
48            limits.entry(id).or_insert(limit);
49        });
50    }
51
52    pub(crate) fn reset(id: &str) {
53        Self::COUNTERS.with(|counters| {
54            let mut counters = counters.borrow_mut();
55            if let Some(counter) = counters.get_mut(id) {
56                counter.clear();
57            }
58        });
59    }
60
61    pub(crate) fn assert<I, II, F>(id: &'static str, keys: I, on_error: F)
62    where
63        I: Iterator<Item = II>,
64        II: Hash,
65        F: FnOnce(),
66    {
67        let limit = Self::LIMITS.with(|limits| {
68            let limits = limits.borrow();
69            limits.get(id).cloned().unwrap_or(10)
70        });
71
72        let mut hasher = fxhash::FxHasher::default();
73        for key in keys {
74            key.hash(&mut hasher);
75        }
76        let hash = hasher.finish();
77
78        Self::COUNTERS.with(|counters| {
79            let mut counters = counters.borrow_mut();
80            let counter = counters.entry(id).or_default();
81            counter
82                .entry(hash)
83                .and_modify(|cnt| {
84                    *cnt -= 1;
85                    if *cnt == 0 {
86                        on_error();
87                    }
88                })
89                .or_insert(limit);
90        })
91    }
92}
93
94pub trait IntoPathSegments: Clone {
95    type Item: AsRef<str>;
96    type Iter: Iterator<Item = Self::Item> + Clone;
97
98    fn segments(self) -> Self::Iter;
99}
100
101impl<'a> IntoPathSegments for &'a str {
102    type Item = &'a str;
103    type Iter = Filter<str::Split<'a, &'a str>>;
104
105    fn segments(self) -> Self::Iter {
106        Filter {
107            segments: self.split("::"),
108        }
109    }
110}
111
112#[derive(Clone)]
113pub struct Filter<I> {
114    segments: I,
115}
116
117impl<'a, I: Iterator<Item = &'a str>> Iterator for Filter<I> {
118    type Item = &'a str;
119
120    fn next(&mut self) -> Option<Self::Item> {
121        self.segments
122            .by_ref()
123            .find(|&segment| !segment.is_empty())
124            .map(|v| v as _)
125    }
126}
127
128#[derive(Debug, Clone)]
129pub struct PathSegments<I>(pub I);
130
131impl<I, II> IntoPathSegments for PathSegments<I>
132where
133    I: Iterator<Item = II> + Clone,
134    II: AsRef<str>,
135{
136    type Item = II;
137    type Iter = I;
138
139    fn segments(self) -> Self::Iter {
140        self.0
141    }
142}
143
144static CRATE_NAME: Mutex<Cow<'static, str>> = Mutex::new(Cow::Borrowed("crate"));
145
146/// Sets crate name without validation.
147///
148/// * Must not be empty
149/// * Must start with a letter
150/// * Must consist of lowercase letters(a-z), numbers(0-9), or underscores(_)
151/// * And mores
152pub fn set_crate_name<T: Into<Cow<'static, str>>>(name: T) {
153    *CRATE_NAME.lock().unwrap() = name.into();
154}
155
156pub fn get_crate_name() -> MutexGuard<'static, Cow<'static, str>> {
157    CRATE_NAME.lock().unwrap()
158}
159
160pub fn cargo_crate_name() -> Cow<'static, str> {
161    let pkg_name = env!("CARGO_PKG_NAME");
162    if !pkg_name.contains('-') {
163        Cow::Borrowed(pkg_name)
164    } else {
165        Cow::Owned(pkg_name.replace('-', "_"))
166    }
167}
168
169/// Creates string which looks like "a::b::C" from the given path ignoring
170/// leading colon.
171pub(crate) fn get_name_path_from_syn_path(path: &syn::Path) -> String {
172    let mut buf = String::new();
173    for segment in &path.segments {
174        push_colon_path(&mut buf, &segment.ident);
175    }
176    buf
177}