#![feature(generic_const_exprs)]
#![allow(incomplete_features)]
#![allow(dead_code)]
fn main() {}
trait T {
const S: &'static str;
}
pub struct A<const N: usize>;
const fn number_of_digits<const N: usize>() -> usize {
if N > 0 {
N.ilog10() as usize + 1usize
} else {
1usize
}
}
const USIZE_MAX_LEN: usize = number_of_digits::<{ usize::MAX }>();
macro_rules! digit {
(
$n: expr,
$p: expr$(,)?
) => {
(($n / $p / 10usize > 0) as u8) * (b'0' + ($n / $p / 10usize % 10) as u8)
+ ((!($n / $p / 10usize > 0)) as u8) * b' '
};
}
const EXTRA_LEN: usize = "VARCHAR()".len();
const fn usize_to_u8<const N: usize>() -> &'static [u8; USIZE_MAX_LEN + EXTRA_LEN] {
&[
b'V',
b'A',
b'R',
b'C',
b'H',
b'A',
b'R',
b'(',
digit!(N, 1000000000000000000usize),
digit!(N, 100000000000000000usize),
digit!(N, 10000000000000000usize),
digit!(N, 1000000000000000usize),
digit!(N, 100000000000000usize),
digit!(N, 10000000000000usize),
digit!(N, 1000000000000usize),
digit!(N, 100000000000usize),
digit!(N, 10000000000usize),
digit!(N, 1000000000usize),
digit!(N, 100000000usize),
digit!(N, 10000000usize),
digit!(N, 1000000usize),
digit!(N, 100000usize),
digit!(N, 10000usize),
digit!(N, 1000usize),
digit!(N, 100usize),
digit!(N, 10usize),
digit!(N, 1usize),
b'0' + (N % 10) as u8,
b')',
]
}
const fn usize_to_varchar_str<const N: usize>() -> &'static str {
unsafe { core::str::from_utf8_unchecked(usize_to_u8::<N>().trim_ascii()) }
}
impl<const LEN: usize> T for A<LEN> {
const S: &'static str = usize_to_varchar_str::<LEN>();
}
fn test_case<const N: usize>()
where
[u8; USIZE_MAX_LEN - number_of_digits::<N>()]: Sized,
{
let n_string: String = N.to_string();
assert!(n_string.len() <= USIZE_MAX_LEN);
let spaces =
core::str::from_utf8(&[b' '; USIZE_MAX_LEN - number_of_digits::<N>()]).expect("impossible");
let result = format!("VARCHAR({}{})", spaces, n_string,);
assert_eq!(<A<N> as T>::S, result);
}
#[test]
fn test() {
test_case::<0>();
test_case::<{ usize::MAX }>();
test_case::<10>();
}
Playground
Come up with a very ugly solution. Very. But I don't see how to avoid writing down the whole array without naming variables or creating temporary values, without this.
EDIT:
My use case is much more specific than writing a const_format. In fact, I just want to implement a function of signature const fn<const N: usize>() -> &'static [u8]
where the return is something plus the string of the number N
. Unfortunately, due to my limited scope of knowledge, I find myself crippled by the fact that I can only use const function without defining any const item via N
, without defining runtime variables since it is a const fn to be defined. Please tell me if there is anything I can use to just not do this.
This solution is so frustrating that just removing the leading spaces of the digits (Now I have VARCHAR( 0)
would require thinking about how to do the arithmetic correctly to compute the bytes, which I shall defer into future work.