const_it/lib.rs
1//! # const it!
2//!
3//! This crate provides some utilities for use in const evaluation contexts, in particular
4//! const slice and error handling.
5//!
6//! The [`slice!`] and [`try_slice!`] macros slice (using any usize or range expression):
7//! ```rust
8//! # use const_it::slice;
9//! const STR: &str = slice!("const slice", ..5); // "const"
10//! ```
11//!
12//! The [`slice_split_at!`] and [`slice_try_split_at!`] macros split a slice in two:
13//! ```rust
14//! # use const_it::slice_split_at;
15//! const STR: (&str, &str) = slice_split_at!("const slice", 5); // ("const", " slice")
16//! ```
17//!
18//! The [`slice_cmp!`] and [`slice_eq!`] macros compare slices. [`slice_starts_with!`] and
19//! [`slice_strip_prefix!`] checks for and strips a prefix, respectively, and
20//! [`slice_ends_with!`] and [`slice_strip_suffix!`] do the same for suffixes.
21//!
22//! The [`ok!`], [`expect_ok!`], [`unwrap_ok!`], [`unwrap_ok_or_return!`], [`expect_some!`], [`unwrap_some!`]
23//! and [`unwrap_some_or_return!`] macros work with `Result`s and `Option`s.
24
25#![no_std]
26
27/// Turn a `Result` into an `Option`.
28#[macro_export]
29macro_rules! ok {
30 ($expr:expr) => {
31 match $expr {
32 Ok(ok) => Some(ok),
33 Err(_) => None,
34 }
35 };
36}
37
38/// Slice an item in a const context. The first argument is the item to slice, and
39/// the second is the slice index, which can be a usize or any usize range type.
40/// Panics if the index is out of range or, for strings, if the slice would split a
41/// unicode codepoint.
42///
43/// Alternately use [`try_slice!`] to get an `Option` instead of panicing.
44///
45/// ```rust
46/// # use { const_it::slice, core::ops::Range };
47/// const STR: &str = slice!("const slice", ..5); // "const"
48/// const BYTES: &[u8] = slice!(b"01234", 1..=3); // b"123"
49/// const RANGE: Range<usize> = (BYTES[0] - b'0') as usize..(BYTES[2] - b'0') as usize;
50/// const STR2: &str = slice!(STR, RANGE); // "on"
51/// ```
52#[macro_export]
53macro_rules! slice {
54 ($slicable:expr, $index:expr) => {{
55 let _ = $crate::__internal::SliceTypeCheck($slicable, $index);
56 $crate::__internal::Slice($slicable, $index).index()
57 }};
58}
59
60/// Slice an item in a const context. The first argument is the item to slice, and
61/// the second is the slice index, which can be a usize or any usize range type.
62/// Returns `Some(sliced)`, or `None` if the index is out of range or, for strings,
63/// if the slice would split a unicode codepoint.
64///
65/// Alternately use [`slice!`] if you want to panic on error instead.
66///
67/// ```rust
68/// # use { const_it::{try_slice, unwrap_some}, core::ops::Range };
69/// const STR: Option<&str> = try_slice!("const slice", ..5); // Some("const")
70/// const BYTES: Option<&[u8]> = try_slice!(b"01234", 1..=3); // Some(b"123")
71/// const BYTES2: &[u8] = unwrap_some!(BYTES); // b"123"
72/// const RANGE: Range<usize> = (BYTES2[0] - b'0') as usize..(BYTES2[2] - b'0') as usize;
73/// const STR2: Option<&str> = try_slice!(unwrap_some!(STR), RANGE); // Some("on")
74/// ```
75#[macro_export]
76macro_rules! try_slice {
77 ($slicable:expr, $index:expr) => {{
78 let _ = $crate::__internal::SliceTypeCheck($slicable, $index);
79 $crate::__internal::Slice($slicable, $index).get()
80 }};
81}
82
83/// Split a slice in two at the specified index. Panics on error.
84///
85/// See also [`slice_try_split_at!`].
86#[macro_export]
87macro_rules! slice_split_at {
88 ($slicable:expr, $index:expr) => {{
89 let _: ::core::primitive::usize = $index;
90 $crate::__internal::Slice($slicable, $index).split()
91 }};
92}
93
94/// Split a slice in two at the specified index. Returns `None` on error.
95///
96/// See also [`slice_split_at!`].
97#[macro_export]
98macro_rules! slice_try_split_at {
99 ($slicable:expr, $index:expr) => {{
100 let _: ::core::primitive::usize = $index;
101 $crate::__internal::Slice($slicable, $index).try_split()
102 }};
103}
104
105#[doc(hidden)]
106#[deprecated = "renamed to slice_split_at"]
107#[macro_export]
108macro_rules! split_slice_at {
109 ($slicable:expr, $index:expr) => {{
110 $crate::slice_split_at!($slicable, $index)
111 }};
112}
113
114#[doc(hidden)]
115#[deprecated = "renamed to slice_try_split_at"]
116#[macro_export]
117macro_rules! try_split_slice_at {
118 ($slicable:expr, $index:expr) => {{
119 $crate::slice_try_split_at!($slicable, $index)
120 }};
121}
122
123/// Compare two slices, returning an `Ordering`. This only works for slices of primitive integer types and `str`.
124#[macro_export]
125macro_rules! slice_cmp {
126 ($a:expr, $b:expr) => {
127 $crate::__internal::SliceRef($a).cmp($crate::__internal::SliceRef($b))
128 };
129}
130
131/// Compare two slices, returning an `Option<Ordering>`. Currently all supported types always return `Some`.
132/// This only works for slices of primitive integer types and `str`.
133#[macro_export]
134macro_rules! slice_partial_cmp {
135 ($a:expr, $b:expr) => {
136 $crate::__internal::SliceRef($a).partial_cmp($crate::__internal::SliceRef($b))
137 };
138}
139
140/// Check if two slices are equal. This only works for slices of primitive integer types and `str`.
141#[macro_export]
142macro_rules! slice_eq {
143 ($a:expr, $b:expr) => {
144 ::core::matches!(
145 $crate::slice_partial_cmp!($a, $b),
146 ::core::option::Option::Some(::core::cmp::Ordering::Equal)
147 )
148 };
149}
150
151/// Check if a slice starts with another slice. This only works for slices of primitive integer types and `str`.
152#[macro_export]
153macro_rules! slice_starts_with {
154 ($s:expr, $prefix:expr) => {
155 $crate::slice_strip_prefix!($s, $prefix).is_some()
156 };
157}
158
159/// Check if a slice ends with another slice. This only works for slices of primitive integer types and `str`.
160#[macro_export]
161macro_rules! slice_ends_with {
162 ($s:expr, $prefix:expr) => {
163 $crate::slice_strip_suffix!($s, $prefix).is_some()
164 };
165}
166
167/// Strip a prefix from a slice, returning an Option with the stripped slice on success. This only works for slices of primitive integer types and `str`.
168#[macro_export]
169macro_rules! slice_strip_prefix {
170 ($s:expr, $prefix:expr) => {{
171 let (slice, prefix) = (
172 $crate::__internal::SliceRef($s),
173 $crate::__internal::SliceRef($prefix),
174 );
175 if slice.len() >= prefix.len() {
176 let (pfx, rest) = $crate::slice_split_at!(slice.0, prefix.len());
177 if $crate::slice_eq!(pfx, prefix.0) {
178 Some(rest)
179 } else {
180 None
181 }
182 } else {
183 None
184 }
185 }};
186}
187
188/// Strip a suffix from a slice, returning an Option with the stripped slice on success. This only works for slices of primitive integer types and `str`.
189#[macro_export]
190macro_rules! slice_strip_suffix {
191 ($s:expr, $suffix:expr) => {{
192 let (slice, suffix) = (
193 $crate::__internal::SliceRef($s),
194 $crate::__internal::SliceRef($suffix),
195 );
196 if slice.len() >= suffix.len() {
197 let (rest, suff) = $crate::slice_split_at!(slice.0, slice.len() - suffix.len());
198 if $crate::slice_eq!(suff, suffix.0) {
199 Some(rest)
200 } else {
201 None
202 }
203 } else {
204 None
205 }
206 }};
207}
208
209/// Takes a `Result` and returns the unwrapped `Ok` value, or panics if it's `Err`.
210/// The second argument is the message to use on panic. If the panic message
211/// is omitted, the `Err` value must be of type `&str` and is used as the panic message.
212///
213/// See also [`expect_some!`] and [`unwrap_ok!`].
214#[macro_export]
215macro_rules! expect_ok {
216 ($expr:expr) => {
217 match $expr {
218 ::core::result::Result::Ok(value) => value,
219 ::core::result::Result::Err(err) => panic!("{}", err),
220 }
221 };
222
223 ($expr:expr, $message:expr) => {
224 match $expr {
225 ::core::result::Result::Ok(value) => value,
226 ::core::result::Result::Err(_) => panic!("{}", $message),
227 }
228 };
229}
230
231/// Takes an `Option` and returns the unwrapped `Some` value, or panics if it's `None`.
232/// The second argument is the message to use on panic.
233///
234/// See also [`expect_ok!`] and [`unwrap_some!`].
235#[macro_export]
236macro_rules! expect_some {
237 ($expr:expr, $message:expr) => {
238 match $expr {
239 ::core::option::Option::Some(value) => value,
240 ::core::option::Option::None => panic!("{}", $message),
241 }
242 };
243}
244
245/// Takes a `Result` and returns the unwrapped `Ok` value, or panics if it's `Err`.
246///
247/// See also [`unwrap_some!`] and [`expect_ok!`].
248#[macro_export]
249macro_rules! unwrap_ok {
250 ($expr:expr) => {
251 $crate::expect_ok!($expr, "unwrapped Err value")
252 };
253}
254
255/// Takes an `Option` and returns the unwrapped `Some` value, or panics if it's `None`.
256///
257/// See also [`unwrap_ok!`] and [`expect_some!`].
258#[macro_export]
259macro_rules! unwrap_some {
260 ($expr:expr) => {
261 $crate::expect_some!($expr, "unwrapped None value")
262 };
263}
264
265/// Takes a `Result` and evaluates to the unwrapped `Ok` value, or if it's `Err`, returns the `Err`
266/// to the current function's caller.
267///
268/// See also [`unwrap_some_or_return!`].
269#[macro_export]
270macro_rules! unwrap_ok_or_return {
271 ($expr:expr) => {
272 match $expr {
273 ::core::result::Result::Ok(value) => value,
274 ::core::result::Result::Err(err) => return ::core::result::Result::Err(err),
275 }
276 };
277}
278
279/// Takes an `Option` and evaluates to the unwrapped `Some` value, or if it's `None`, returns the `None`
280/// to the current function's caller.
281///
282/// See also [`unwrap_ok_or_return!`].
283#[macro_export]
284macro_rules! unwrap_some_or_return {
285 ($expr:expr) => {
286 match $expr {
287 ::core::option::Option::Some(value) => value,
288 ::core::option::Option::None => return ::core::option::Option::None,
289 }
290 };
291}
292
293mod slice;
294
295#[doc(hidden)]
296pub mod __internal {
297 pub use super::slice::{Slice, SliceIndex, SliceRef, SliceTypeCheck};
298}
299
300#[cfg(test)]
301mod tests;