#![no_std]
#[doc(hidden)]
pub const fn concated_size<const N: usize>(array: [&'static str; N], sep: &'static str) -> usize {
let mut n = if N == 0 { 0 } else { N - 1 } * sep.len();
let mut i = N;
loop {
if i <= 0 {
break;
}
i -= 1;
n += array[i].len();
}
n
}
#[doc(hidden)]
pub const fn copy_bytes(src: &[u8], dest: &mut [u8], offset: usize) -> usize {
let mut i = 0;
let mut op = offset;
if !src.is_empty() {
loop {
assert!(dest[op] == b'\0');
dest[op] = src[i];
op += 1;
i += 1;
if i >= src.len() {
break;
}
}
}
op
}
#[doc(hidden)]
pub const fn join_strings(inputs: &[&str], sep: Option<&str>, mut output: &mut [u8]) -> usize {
assert!(output.len() > 0);
let mut n = 0;
let mut op = 0;
loop {
op = copy_bytes(&inputs[n].as_bytes(), &mut output, op);
if n + 1 < inputs.len()
&& let Some(sep) = sep
{
let s = sep.as_bytes();
op = copy_bytes(s, &mut output, op);
}
n += 1;
if n >= inputs.len() {
break;
}
}
op
}
#[macro_export]
macro_rules! joined_array {
($array:expr, $sep:expr) => {
const {
const SIZE: usize = $crate::concated_size($array, $sep);
$crate::joined_array!($array, $sep, SIZE)
}
};
($array:expr, $sep:expr, $size:expr) => {
const {
const ARRAY_LEN: usize = $size;
let sep = $sep;
let sep = if sep.len() > 0 { Some(sep) } else { None };
let array = &$array;
let mut buffer = [0u8; ARRAY_LEN];
let next_position = $crate::join_strings(array, sep, &mut buffer);
assert!(next_position == ARRAY_LEN);
buffer
}
};
}
#[macro_export]
macro_rules! declare_joined_str {
($name:ident, $array:expr, $sep:expr) => {
const $name: &'static str = const {
const SIZE: usize = $crate::concated_size($array, $sep);
static STORAGE: [u8; SIZE] = $crate::joined_array!($array, $sep, SIZE);
if let Ok(v) = core::str::from_utf8(&STORAGE) {
v
} else {
panic!("joined array isn't a valid utf8 string");
}
};
};
}
#[cfg(test)]
mod tests {
use super::*;
const A: &'static str = "A";
const B: &'static str = "B";
const C: &'static str = "C";
const ARRAY_OF_STRINGS: [&'static str; 3] = [A, B, C];
#[test]
fn nested() {
declare_joined_str!(FOO, ARRAY_OF_STRINGS, ":");
const MORE_PARTS: [&'static str; 3] = ["<", FOO, ">"];
declare_joined_str!(MORE, MORE_PARTS, "");
assert_eq!(FOO, "A:B:C");
assert_eq!(MORE, "<A:B:C>");
}
#[test]
fn joined_array() {
let s = joined_array!(ARRAY_OF_STRINGS, "-");
assert_eq!(&s, b"A-B-C");
}
}