1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
//! Formatting macros.
/// Concatenates arguments in compile time.
///
/// # Specifying arguments
///
/// Arguments to this macro must be comma-separated. The argument type must be supported; for now,
/// the supported types are:
///
/// - Signed and unsigned integers (`u8`, `i8`, `u16`, `i16`, `u32`, `i32`, `u64`, `i64`, `u128`,
/// `i128`, `usize`, `isize`)
/// - Strings (`&str`)
/// - [`Ascii`](crate::Ascii) strings
/// - Chars (`char`)
/// - References to [`CompileArgs`](crate::CompileArgs).
///
/// Due to how Rust type inference works, you might need to specify the type suffix for integer
/// literals (e.g., `42_usize` instead of `42`).
///
/// Optionally, an argument may specify its [format](crate::Fmt) as `$arg => $fmt`.
/// A format is mandatory if the argument is not a constant; e.g. if it is an argument or a local variable
/// in a `const fn`.
///
/// The value output by the macro is [`CompileArgs`](crate::CompileArgs).
///
/// # Specifying capacity
///
/// You can specify capacity of the returned `CompileArgs` by prefacing arguments with `capacity: $cap,`.
/// Here, `$cap` is a constant expression of type `usize`. The specified capacity must be greater or equal
/// that the capacity inferred from the arguments; the macro will fail with a compilation error otherwise.
///
/// # See also
///
/// - [`compile_panic!`](crate::compile_panic) provides a version of the `panic!` macro with support
/// of dynamic arguments.
/// - [`compile_assert!`](crate::compile_assert) provides a version of the `assert!` macro with support
/// of dynamic arguments.
///
/// # Examples
///
/// ## Basic usage
///
/// ```
/// # use compile_fmt::{compile_args, CompileArgs};
/// const ARGS: CompileArgs<9> = compile_args!(2_u32, " + ", 2_u32, " = ", 2_u32 + 2);
/// assert_eq!(ARGS.as_str(), "2 + 2 = 4");
/// ```
///
/// ## Usage in `const fn` with dynamic args
///
/// ```
/// use compile_fmt::{compile_args, fmt};
/// use std::fmt;
///
/// const fn create_args(x: usize) -> impl fmt::Display {
/// let args = compile_args!(
/// "2 * x + 3 = ", 2 * x + 3 => fmt::<usize>()
/// );
/// // ^ `args` are evaluated in compile time, but are not a constant.
/// // They can still be useful e.g. for creating compile-time panic messages.
/// assert!(x < 1000, "{}", args.as_str());
/// args
/// }
///
/// let args = create_args(100);
/// assert_eq!(args.to_string(), "2 * x + 3 = 203");
/// ```
///
/// ## Usage with explicit capacity
///
/// ```
/// # use compile_fmt::compile_args;
/// let args = compile_args!(capacity: 16, "Value: ", 42_i32);
/// assert_eq!(args.as_str(), "Value: 42");
/// ```
///
/// Insufficient specified capacity will lead to a compilation error:
///
/// ```compile_fail
/// # use compile_fmt::compile_args;
/// let args = compile_args!(capacity: 4, "Value: ", 42_i32);
/// ```
///
/// The error message will include details about the necessary capacity:
///
/// ```text
/// error[E0080]: evaluation of constant value failed
/// -->
/// |
/// | let args = compile_args!(capacity: 4, "Value: ", 42_i32);
/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/// the evaluated program panicked at 'Insufficient capacity (4 bytes)
/// provided for `compile_args` macro; it requires at least 9 bytes'
/// ```
#[macro_export]
macro_rules! compile_args {
(capacity: $cap:expr, $($arg:expr $(=> $fmt:expr)?),+) => {{
const __CAPACITY: usize = $cap;
const _: () = {
let required_capacity = $crate::__compile_args_impl!(@total_capacity $($arg $(=> $fmt)?,)+);
$crate::CompileArgs::<__CAPACITY>::assert_capacity(required_capacity);
};
$crate::CompileArgs::<__CAPACITY>::format(&[
$($crate::ArgumentWrapper::new($arg)$(.with_fmt($fmt))?.into_argument(),)+
]) as $crate::CompileArgs<__CAPACITY>
// ^ The type hint sometimes helps in const contexts
}};
($($arg:expr $(=> $fmt:expr)?),+) => {{
const __CAPACITY: usize = $crate::__compile_args_impl!(@total_capacity $($arg $(=> $fmt)?,)+);
$crate::CompileArgs::<__CAPACITY>::format(&[
$($crate::ArgumentWrapper::new($arg)$(.with_fmt($fmt))?.into_argument(),)+
]) as $crate::CompileArgs<__CAPACITY>
// ^ The type hint sometimes helps in const contexts
}};
}
#[doc(hidden)] // implementation detail of `compile_args`
#[macro_export]
macro_rules! __compile_args_impl {
(@total_capacity $first_arg:expr $(=> $first_fmt:expr)?, $($arg:expr $(=> $fmt:expr)?,)*) => {
$crate::__compile_args_impl!(@arg_capacity $first_arg $(=> $first_fmt)?)
$(+ $crate::__compile_args_impl!(@arg_capacity $arg $(=> $fmt)?))*
};
(@arg_capacity $arg:expr) => {
$crate::ArgumentWrapper::new($arg).into_argument().formatted_len()
};
(@arg_capacity $arg:expr => $fmt:expr) => {
$crate::Fmt::capacity(&$fmt)
};
}
/// Version of the [`panic!`] macro with the ability to format args in compile time.
///
/// Arguments have the same syntax as in the [`compile_args!`] macro.
///
/// # Examples
///
/// ```
/// use compile_fmt::{compile_panic, clip};
///
/// const fn unwrap_result(res: Result<(), &str>) {
/// if let Err(err) = res {
/// compile_panic!("Encountered an error: ", err => clip(64, "…"));
/// }
/// }
/// ```
#[macro_export]
macro_rules! compile_panic {
($($arg:tt)+) => {
::core::panic!("{}", $crate::compile_args!($($arg)+).as_str());
};
}
/// Version of the [`assert!`] macro with the ability to format args in compile time.
///
/// The first argument of the macro must be a boolean value. The remaining arguments have the same syntax
/// as in the [`compile_args!`] macro.
///
/// # Examples
///
/// ```
/// use compile_fmt::{compile_assert, fmt};
///
/// const fn check_args(x: usize, s: &str) {
/// const MAX_STR_LEN: usize = 10;
///
/// compile_assert!(
/// x < 1_000,
/// "`x` should be less than 1000 (got: ",
/// x => fmt::<usize>(), ")"
/// );
/// compile_assert!(
/// s.len() <= MAX_STR_LEN,
/// "String is too long (expected at most ", MAX_STR_LEN,
/// " bytes; got ", s.len() => fmt::<usize>(), " bytes)"
/// );
/// // main logic...
/// }
/// ```
#[macro_export]
macro_rules! compile_assert {
($check:expr, $($arg:tt)+) => {{
if !$check {
::core::panic!("{}", $crate::compile_args!($($arg)+).as_str());
}
}};
}