use std::cell::RefCell;
use std::cmp::min;
use std::fmt::{Formatter, Result};
use std::mem::{forget, take};
use tracing::warn;
thread_local!(static ID: RefCell<(u8, Vec::<String>)> = const { RefCell::new((0, Vec::new())) });
pub struct Scope;
impl From<Scope> for Vec<String> {
#[inline]
fn from(scope: Scope) -> Self {
let names = exit();
forget(scope);
names
}
}
impl Drop for Scope {
fn drop(&mut self) {
let _ = exit();
}
}
pub fn id() -> u8 {
ID.with(|id| id.borrow().0)
}
pub fn next() {
ID.with(|id| (id.borrow_mut()).0 += 1);
}
pub fn enter(names: Vec<String>) -> Scope {
if names.len() > u8::BITS as usize {
warn!(
"{} providers exceeds the limit of {}; some provider diagnostics may not output",
names.len(),
u8::BITS
);
}
ID.with(|id| *id.borrow_mut() = (1, names));
Scope
}
fn exit() -> Vec<String> {
ID.with(|id| take(&mut *id.borrow_mut()).1)
}
pub fn overridden(providers: u8, key: &str, old: &str, new: &str) {
ID.with(|ctx| {
let (id, names) = &*ctx.borrow();
crate::overridden(*id, names, providers, key, old, new)
});
}
pub fn expand(providers: u8, names: &[String], f: &mut Formatter<'_>) -> Result {
let mut len = providers.count_ones() as usize;
if len == 0 {
Ok(())
} else {
let mut skip = 0;
if let Some(keep) = f.width() {
if keep == 0 {
return Ok(());
}
let constrained = min(len, keep);
skip = len - constrained;
len = constrained;
}
let mut count = 0;
let mut i = 0;
while count < len && count < names.len() {
while providers & (1u8 << i) == 0 {
i += 1;
}
if skip > 0 {
skip -= 1;
i += 1;
continue;
}
if count > 0 {
f.write_str(" → ")?;
}
f.write_str(&names[i as usize])?;
count += 1;
i += 1;
}
Ok(())
}
}