use core::marker::PhantomData;
use crate::{
Nat,
maxint::{self, Umax},
utils::subslice,
};
macro_rules! fmt {
[] => {
crate::const_fmt::ConstFmtWrap("").enter()
};
[ $f:expr $(, $($then:tt)* )? ] => {
crate::const_fmt::ConstFmtVariants::Pair(
crate::const_fmt::ConstFmtWrap($f).enter(),
crate::const_fmt::fmt![$($($then)*)?],
)
};
}
pub(crate) use fmt;
pub(crate) enum Never {}
pub(crate) unsafe trait DestructFmt<'a>: 'a {
type Inner1: DestructFmt<'a>;
type Inner2: DestructFmt<'a>;
}
unsafe impl<'a> DestructFmt<'a> for Never {
type Inner1 = Self;
type Inner2 = Self;
}
unsafe impl<'a, I1, I2> DestructFmt<'a> for ConstFmtVariants<'a, I1, I2>
where
I1: DestructFmt<'a>,
I2: DestructFmt<'a>,
{
type Inner1 = I1;
type Inner2 = I2;
}
#[allow(type_alias_bounds)]
type ReifyVariants<'a, Fmt: DestructFmt<'a>> = ConstFmtVariants<'a, Fmt::Inner1, Fmt::Inner2>;
const fn reify_variants<'a, Fmt>(fmt: &Fmt) -> &ReifyVariants<'a, Fmt>
where
Fmt: DestructFmt<'a>,
{
unsafe { &*core::ptr::from_ref(fmt).cast() }
}
pub enum ConstFmtVariants<'a, Inner1 = Never, Inner2 = Never> {
Str(&'a str),
Umax(Umax),
Pair(Inner1, Inner2),
}
impl<'a, I1, I2> ConstFmtVariants<'a, I1, I2>
where
I1: DestructFmt<'a>,
I2: DestructFmt<'a>,
{
pub(crate) const fn fmt_write_impl<'b>(&self, out: &'b mut [u8]) -> Option<&'b mut [u8]> {
match *self {
ConstFmtVariants::Str(s) => {
const fn doit_str<'a>(s: &str, mut out: &'a mut [u8]) -> Option<&'a mut [u8]> {
let s = s.as_bytes();
if out.len() < s.len() {
out.copy_from_slice(subslice![&s, _, out.len()]);
return None;
}
let s_out;
(s_out, out) = out.split_at_mut(s.len());
s_out.copy_from_slice(s);
Some(out)
}
doit_str(s, out)
}
ConstFmtVariants::Umax(n) => {
const fn doit_umax(mut n: Umax, mut out: &mut [u8]) -> Option<&mut [u8]> {
if out.is_empty() {
return None;
}
let mut ran_out = false;
while out.len() < maxint::umax_strlen(n) {
ran_out = true;
n /= 10;
}
out = maxint::umax_write(n, out);
if ran_out {
debug_assert!(out.is_empty());
None
} else {
Some(out)
}
}
doit_umax(n, out)
}
ConstFmtVariants::Pair(ref inner1, ref inner2) => {
if let Some(new_out) = reify_variants::<I1>(inner1).fmt_write_impl(out) {
reify_variants::<I2>(inner2).fmt_write_impl(new_out)
} else {
None
}
}
}
}
#[track_caller]
pub(crate) const fn panic_with_bufsize<const BUFSIZE: usize>(&self) -> ! {
#[track_caller]
const fn doit(buf: &mut [u8], rest: Option<usize>) -> ! {
let result = if let Some(rest) = rest {
buf.split_at(buf.len() - rest).0
} else {
const ELLISPIS: &[u8] = b"...";
buf.split_at_mut(buf.len() - ELLISPIS.len())
.1
.copy_from_slice(ELLISPIS);
buf
};
panic!(
"{}",
match core::str::from_utf8(result) {
Ok(out) => out,
Err(_) => unreachable!(),
}
)
}
let mut buf = [0; BUFSIZE];
let buf = buf.as_mut_slice();
let rest = match self.fmt_write_impl(buf) {
Some(rest) => Some(rest.len()),
None => None,
};
doit(buf, rest)
}
#[track_caller]
pub(crate) const fn panic(&self) -> ! {
self.panic_with_bufsize::<{ 10 << 10 }>()
}
}
#[repr(transparent)]
pub(crate) struct ConstFmtWrap<T>(pub T);
impl<T> ConstFmtWrap<T> {
pub(crate) const fn into_inner(self) -> T {
unsafe { crate::utils::union_transmute!(ConstFmtWrap<T>, T, self) }
}
}
macro_rules! enter_impl {
(
[$($generics:tt)*],
$Self:ty,
$VariantsT:ty,
|$self:pat_param| $to_variant:expr
) => {
#[allow(dead_code)]
impl<$($generics)*> ConstFmtWrap<$Self> {
pub(crate) const fn enter(self) -> $VariantsT {
let $self = self.into_inner();
$to_variant
}
}
};
}
enter_impl![
[],
usize, ConstFmtVariants<'static>,
|n| ConstFmtVariants::Umax(n as _)
];
enter_impl![
[],
u128, ConstFmtVariants<'static>,
|n| ConstFmtVariants::Umax(n as _)
];
enter_impl![
[N: Nat],
PhantomData<N>,
ConstFmtVariants<'static>,
|_| ConstFmtVariants::Str(crate::to_str::<N>())
];
enter_impl![
['a],
&'a str,
ConstFmtVariants<'a>,
|s| ConstFmtVariants::Str(s)
];