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
146pub 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
169pub(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}