const_format/wrapper_types/
sliced.rs

1use crate::{
2    fmt::{Error, Formatter},
3    wrapper_types::AsciiStr,
4};
5
6use core::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
7
8/// Wrapper for writing a range of a string slice.
9///
10/// This is a workaround for not being able to do `&string[start..end]` at compile-time.
11///
12/// # Example
13///
14/// ```rust
15///
16/// use const_format::Sliced;
17/// use const_format::{concatc, formatc};
18///
19/// const NUMS: &str = "0123456789";
20/// const SRC: &str = "foo bar baz";
21///
22/// assert_eq!(concatc!(Sliced(NUMS, 1..=4)), "1234");
23/// assert_eq!(concatc!(Sliced(SRC, 0..5), "ros."), "foo bros.");
24///
25/// assert_eq!(formatc!("{}", Sliced(NUMS, 4..)), "456789");
26/// assert_eq!(formatc!("{}t", Sliced(SRC, 4..7)), "bart");
27///
28/// ```
29#[cfg_attr(feature = "__docsrs", doc(cfg(feature = "fmt")))]
30pub struct Sliced<T, R>(pub T, pub R);
31
32impl_fmt! {
33    impl['a,] Sliced<&'a str, Range<usize>>;
34    impl['a,] Sliced<&'a str, RangeFrom<usize>>;
35    impl['a,] Sliced<&'a str, RangeFull>;
36    impl['a,] Sliced<&'a str, RangeInclusive<usize>>;
37    impl['a,] Sliced<&'a str, RangeTo<usize>>;
38    impl['a,] Sliced<&'a str, RangeToInclusive<usize>>;
39
40    ///
41    #[inline]
42    pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
43        f.write_str_range_debug(self.0, self.range())
44    }
45
46    ///
47    #[inline]
48    pub const fn const_display_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
49        f.write_str_range(self.0, self.range())
50    }
51}
52
53impl_fmt! {
54    impl['a,] Sliced<AsciiStr<'a>, Range<usize>>;
55    impl['a,] Sliced<AsciiStr<'a>, RangeFrom<usize>>;
56    impl['a,] Sliced<AsciiStr<'a>, RangeFull>;
57    impl['a,] Sliced<AsciiStr<'a>, RangeInclusive<usize>>;
58    impl['a,] Sliced<AsciiStr<'a>, RangeTo<usize>>;
59    impl['a,] Sliced<AsciiStr<'a>, RangeToInclusive<usize>>;
60
61    ///
62    #[inline]
63    pub const fn const_debug_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
64        f.write_ascii_range_debug(self.0, self.range())
65    }
66
67    ///
68    #[inline]
69    pub const fn const_display_fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
70        f.write_ascii_range(self.0, self.range())
71    }
72}
73
74impl<T> Sliced<T, Range<usize>> {
75    #[inline(always)]
76    const fn range(&self) -> Range<usize> {
77        self.1.start..self.1.end
78    }
79}
80
81impl<T> Sliced<T, RangeFrom<usize>> {
82    #[inline(always)]
83    const fn range(&self) -> Range<usize> {
84        self.1.start..usize::MAX
85    }
86}
87
88impl<T> Sliced<T, RangeFull> {
89    #[inline(always)]
90    const fn range(&self) -> Range<usize> {
91        0..usize::MAX
92    }
93}
94
95const UM: usize = usize::MAX >> 1;
96
97impl<T> Sliced<T, RangeInclusive<usize>> {
98    // If people are somehow indexing over a thing that's larger than isize::MAX,
99    // then we have problems.
100    #[inline(always)]
101    const fn range(&self) -> Range<usize> {
102        *self.1.start()..(*self.1.end() & UM) + 1
103    }
104}
105
106impl<T> Sliced<T, RangeTo<usize>> {
107    #[inline(always)]
108    const fn range(&self) -> Range<usize> {
109        0..self.1.end
110    }
111}
112
113impl<T> Sliced<T, RangeToInclusive<usize>> {
114    // If people are somehow indexing over a thing that's larger than isize::MAX,
115    // then we have problems.
116    #[inline(always)]
117    const fn range(&self) -> Range<usize> {
118        0..(self.1.end & UM) + 1
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    use crate::fmt::{FormattingFlags, StrWriter};
127
128    macro_rules! test_case {
129        (
130            $writer:ident,
131            $str_val:expr,
132            $range:expr,
133            $expected:expr
134        ) => {{
135            const fn inner(fmt: &mut Formatter<'_>) -> Result<(), Error> {
136                let string = Sliced($str_val, $range);
137
138                try_!(string.const_display_fmt(fmt));
139                try_!(fmt.write_str(";"));
140                try_!(string.const_debug_fmt(fmt));
141
142                Ok(())
143            }
144            $writer.clear();
145            inner(&mut $writer.make_formatter(FormattingFlags::NEW)).unwrap();
146
147            assert_eq!($writer.as_str(), $expected, "range = {:?}", $range);
148        }};
149    }
150
151    const S: &str = "\x00\n\t3456789\x06\x07";
152
153    macro_rules! generate_test {
154        ($str_val:expr) => {
155            let writer: &mut StrWriter = &mut StrWriter::new([0u8; 512]);
156
157            test_case!(writer, $str_val, 2..9, "\t345678;\"\\t345678\"");
158
159            test_case!(
160                writer,
161                $str_val,
162                2..,
163                "\t3456789\x06\x07;\"\\t3456789\\x06\\x07\""
164            );
165
166            test_case!(
167                writer,
168                $str_val,
169                ..,
170                "\x00\n\t3456789\x06\x07;\"\\x00\\n\\t3456789\\x06\\x07\""
171            );
172
173            test_case!(
174                writer,
175                $str_val,
176                ..9,
177                "\x00\n\t345678;\"\\x00\\n\\t345678\""
178            );
179
180            test_case!(writer, $str_val, 2..=9, "\t3456789;\"\\t3456789\"");
181            test_case!(
182                writer,
183                $str_val,
184                2..=!0,
185                "\t3456789\x06\x07;\"\\t3456789\\x06\\x07\""
186            );
187
188            test_case!(
189                writer,
190                $str_val,
191                ..=9,
192                "\x00\n\t3456789;\"\\x00\\n\\t3456789\""
193            );
194            test_case!(
195                writer,
196                $str_val,
197                ..=!0,
198                "\x00\n\t3456789\x06\x07;\"\\x00\\n\\t3456789\\x06\\x07\""
199            );
200        };
201    }
202
203    #[test]
204    fn str_tests() {
205        generate_test!(S);
206    }
207    #[test]
208    fn asciistr_tests() {
209        const ASCII: AsciiStr<'_> = ascii_str!(S);
210        generate_test!(ASCII);
211    }
212}