use std::cell::{Cell, RefCell};
use std::rc::Rc;
pub trait SentenceBonds {
fn unicode_sentences<'a>(&self, text: &'a str) -> Vec<&'a str>;
fn as_any(&self) -> &dyn std::any::Any;
}
#[cfg(feature = "desm")]
impl SentenceBonds for syndesmos::Syndesmos {
fn unicode_sentences<'a>(&self, text: &'a str) -> Vec<&'a str> {
syndesmos::Syndesmos::unicode_sentences(self, text)
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
thread_local! {
static NOW: Cell<Option<(i64, u32)>> = const { Cell::new(None) };
static SENTENCE_BONDS: RefCell<Option<Rc<dyn SentenceBonds>>> = const { RefCell::new(None) };
static AMBIENTS: RefCell<Vec<Ambient>> = const { RefCell::new(Vec::new()) };
static FINISHED: RefCell<Ambient> = RefCell::new(Ambient::default());
}
pub fn set_invocation_instant(secs: i64, nanos: u32) {
NOW.with(|c| c.set(Some((secs, nanos))));
}
pub fn invocation_instant() -> Option<(i64, u32)> {
NOW.with(|c| c.get())
}
pub fn now_secs() -> i64 {
match invocation_instant() {
Some((secs, _)) => secs,
None => std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs() as i64)
.unwrap_or(0),
}
}
type UnitScale = dyn Fn(&str) -> Option<(f64, String)>;
thread_local! {
static UNIT_SCALE: RefCell<Option<*const UnitScale>> = const { RefCell::new(None) };
}
pub(crate) fn with_unit_scale<R>(
scale: &(dyn Fn(&str) -> Option<(f64, String)> + '_),
f: impl FnOnce() -> R,
) -> R {
struct Restore(Option<*const UnitScale>);
impl Drop for Restore {
fn drop(&mut self) {
UNIT_SCALE.with(|s| *s.borrow_mut() = self.0);
}
}
let erased: *const UnitScale = unsafe {
std::mem::transmute::<*const (dyn Fn(&str) -> Option<(f64, String)> + '_), *const UnitScale>(
scale,
)
};
let previous = UNIT_SCALE.with(|s| s.replace(Some(erased)));
let _restore = Restore(previous);
f()
}
pub fn unit_scale(expr: &str) -> Option<Option<(f64, String)>> {
let ptr = UNIT_SCALE.with(|s| *s.borrow());
ptr.map(|p| unsafe { (*p)(expr) })
}
pub fn set_sentence_bonds(bonds: Option<Rc<dyn SentenceBonds>>) {
SENTENCE_BONDS.with(|b| *b.borrow_mut() = bonds);
}
pub fn sentence_bonds() -> Option<Rc<dyn SentenceBonds>> {
SENTENCE_BONDS.with(|b| b.borrow().clone())
}
#[derive(Default)]
pub(crate) struct Ambient {
pub(crate) collation: Option<String>,
pub(crate) bonds: Option<Rc<dyn SentenceBonds>>,
pub(crate) refusal: Option<String>,
}
pub(crate) fn enter(a: Ambient) -> AmbientGuard {
AMBIENTS.with(|s| s.borrow_mut().push(a));
AmbientGuard
}
pub(crate) struct AmbientGuard;
impl Drop for AmbientGuard {
fn drop(&mut self) {
if let Some(done) = AMBIENTS.with(|s| s.borrow_mut().pop()) {
FINISHED.with(|f| *f.borrow_mut() = done);
}
}
}
fn with_ambient<R>(f: impl FnOnce(&mut Ambient) -> R) -> R {
AMBIENTS.with(|s| {
let mut s = s.borrow_mut();
match s.last_mut() {
Some(top) => f(top),
None => FINISHED.with(|fin| f(&mut fin.borrow_mut())),
}
})
}
pub(crate) fn ambient<R>(f: impl FnOnce(&Ambient) -> R) -> R {
with_ambient(|a| f(a))
}
pub fn record_refusal(msg: String) {
with_ambient(|a| {
if a.refusal.is_none() {
a.refusal = Some(msg);
}
});
}
pub fn fetched<T: Default, E: std::fmt::Display>(
result: Result<T, E>,
what: impl FnOnce() -> String,
) -> T {
match result {
Ok(v) => v,
Err(e) => {
record_refusal(format!("{}: {e}", what()));
T::default()
}
}
}
pub fn fetched_opt<T, E: std::fmt::Display>(
result: Result<T, E>,
what: impl FnOnce() -> String,
) -> Option<T> {
match result {
Ok(v) => Some(v),
Err(e) => {
record_refusal(format!("{}: {e}", what()));
None
}
}
}
pub fn take_refusal() -> Option<String> {
FINISHED.with(|f| f.borrow_mut().refusal.take())
}