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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
//! Compile-Time Growable Array: `Vec` & `String` for `const`!
//!
//! [`Construe`] & [`StrConstrue`] allow you to write `const` functions that behave as though though they can collect items into a `Vec` or `&str`s into a `String`, returning an array or `&str`, whose length does *not* need to be known before the function is called.
//!
//! The one caveat is that this is only possible for deterministic functions which, given the same inputs, will always return the same *amount* of items.
//! This is because they will be invoked twice: once to determine the size of the buffer needed and once to collect its contents.
//!
//! Some other restrictions (currently) apply:
//! - can't overwrite previous items or assign directly to an index
//! - items can only be added to the end (only `.push()`)
//! - can't remove items from the buffer (e.g. no `.pop()`)
//! - it's not possible to inspect the buffer during construction
//! - no fallback mode for use outside of `const` contexts
//!
//! # Examples
//!
//! Simple compile-time [`&str`] concatenation using [`StrConstrue`]:
//! ```
//! use construe::{StrConstrue, construe};
//!
//! /// Concatenate all `&str`s into a single `&str`
//! const fn concat<const N: usize>(mut slice: &[&str]) -> StrConstrue<N> {
//!     let mut strc = StrConstrue::new();
//!     // no `for` in const
//!     while let [s, rest @ ..] = slice {
//!         slice = rest;
//!         // by-value since there's no `&mut` in const
//!         strc = strc.push_str(s);
//!     }
//!     strc
//! }
//!
//! construe!(const HELLO_WORLD: &str = concat(&["Hello", " ", "World", "!"]));
//!
//! assert_eq!(HELLO_WORLD, "Hello World!");
//! ```
//!
//! And a slightly more complicated example, using [`Construe`]:
//! ```
//! use construe::{Construe, construe};
//!
//! /// Multiply each item in `slice` `x` times
//! const fn multiply<const N: usize, T: Copy>(mut slice: &[T], x: usize)
//!     -> Construe<T, N>
//! {
//!     let mut c = Construe::new();
//!     while let [item, rest @ ..] = slice {
//!         slice = rest;
//!         let mut i = 0;
//!         while i < x {
//!             // see Construe::push docs on why we need `.0` at the end
//!             c = c.push(*item).0;
//!             i += 1;
//!         }
//!     }
//!     c
//! }
//!
//! // as slice:
//! construe!(const NUMBERS: &[u8] = multiply(&[1, 2, 3], 2));
//! assert_eq!(NUMBERS, [1, 1, 2, 2, 3, 3]);
//!
//! // as array:
//! construe!(const NUMBERS_ARRAY: [u8; _] = multiply(&[1, 2, 3], 2));
//! assert_eq!(NUMBERS, &NUMBERS_ARRAY);
//!
//! // or with `&str`s:
//! construe!(const WORDS: &[&str] = multiply(&["hey", "you"], 2));
//! assert_eq!(WORDS, &["hey", "hey", "you", "you"]);
//! ```
//!
//! # Possible Improvements
//!
//! Some of the restrictions mentioned above may or may not be remedied in future versions (roughly in the order given).
//!
//! However, if a specialized version of these types is required, it would be easiest for you to just write it yourself.
//! For instance, if your function will need to inspect, say, the last 4 items of the buffer during construction, it would be fairly easy to write a [`Construe`] that holds a buffer of size 4 for the first run too.  
//! Implementing `pop()` where you don't need the result would be trivial, and if you do, it would be analogous to the previous case, as long as you can guarantee that a buffer of size `N` can keep up (e.g. "`pop()` won't be called more than once between `push()`es").
//! If you can't make this guarantee it might still be possible to implement: you could run your function thrice, once to determine the size of the look-back buffer, then twice like a normal [`Construe`].
//!
//! Generally, I think the method used by this crate can be extended to make any computation work, given that you could implement an allocator: it would abort the computation prematurely if it runs out of space and ask for twice as much.
//! Then you need to invoke the computation at most log<sub>2</sub>(available_memory) times.  
//! Probably better to just wait until `const` support is better though.

#![no_std]

use core::mem::{
	ManuallyDrop,
	MaybeUninit
};

/// Invoke a function returning [`Construe`] or [`StrConstrue`]
///
/// This will invoke the function (or any expression) twice: once to determine the size of the buffer needed, and once to collect its contents.
///
/// In case this is not already obvious to you: the expression must be a [constant expression](https://doc.rust-lang.org/reference/const_eval.html).
/// Even if you're not using the form of the macro that defines a constant for you, the macro still needs to store the results of the expression, which means it must have a size known at compile time, which, because we use the expression itself to determine that size, means it must be evaluable at compile time.
///
/// Can be used in 2 different ways: either to define a `const` item or as an (anonymous) expression.
/// Both require you to specify the type of the array/slice/`&str`, but in the former it is simply part of the constant item definition, so you don't need to repeat yourself.
///
/// In both cases, the type you specify may be:
/// - `&str` for expressions that return [`StrConstrue`]
/// - `[T; _]` if you want an array, where `T` is your type and `_` will be replaced by the macro
/// - `&[T]` if you want a slice of `T`
///
/// It's a very simple macro, so you can easily implement your own version if you need it to be slightly different.
/// But you most likely will want to use *a* macro since otherwise you need to write the entire expression (i.e. the function you call) twice, which would be pretty bad if there's any arguments to it.
#[macro_export]
macro_rules! construe {
	($v:vis const $name:ident: & $($l:lifetime)? str = $f:expr) => {
		$v const $name: &$($l)? str = $crate::construe!(&str => $f);
	};
	($v:vis const $name:ident: & $($l:lifetime)? [$t:ty] = $f:expr) => {
		$v const $name: & $($l)? [$t] = &$crate::construe!([$t; _] => $f);
	};
	($v:vis const $name:ident: [$t:ty; _] = $f:expr) => {
		$v const $name: [$t; {$f.needs_len()}] = $f.finish();
	};
	(&str => $f:expr) => {{
		const ARRAY: [u8; {$f.needs_len()}] = $f.store_bytes();
		$crate::StrConstrue::borrow_str(&ARRAY)
	}};
	(&[$t:ty] => $f:expr) => {{
		const ARRAY: [$t; {$f.needs_len()}] = $f.finish();
		&ARRAY
	}};
	([$t:ty; _] => $f:expr) => {{
		const ARRAY: [$t; {$f.needs_len()}] = $f.finish();
		ARRAY
	}};
}

/// Alternative to `Vec` for `const`
///
/// Used to write `const` functions as though they have a dynamically sized buffer (i.e. a `Vec`).
/// It requires calling the function twice: on the first run, which uses `N = 0`, the [`Construe`] just pretends to store the items it is given, and it only tracks how many there are.
/// Then wherever this function is invoked can use [`Construe::needs_len`] to determine the size of the buffer (i.e. the correct `N`) that is needed and invoke the same function again, but with a "real" buffer instead.
pub struct Construe<T, const N: usize> {
	offset: usize,
	buffer: [MaybeUninit<T>; N]
}

/// Alternative to `String` for `const`
///
/// This is just a [`Construe<u8, N>`] with a few additional methods, see the [`Construe`] documentation for a little more info.
/// ```
/// use construe::{StrConstrue, construe};
///
/// const fn hello_world<const N: usize>() -> StrConstrue<N> {
///     let strc = StrConstrue::new();
///     strc.push_str("Hello")
///         .push_str(" ")
///         .push_str("World")
///         .push_str("!")
/// }
///
/// construe!(const HELLO_WORLD: &str = hello_world());
///
/// // or manually:
/// const HELLO_WORLD_2: &str = {
///     const LEN: usize = hello_world().needs_len();
///     const ARRAY: [u8; LEN] = hello_world().store_bytes();
///     StrConstrue::borrow_str(&ARRAY)
/// };
///
/// assert_eq!(HELLO_WORLD, "Hello World!");
/// assert_eq!(HELLO_WORLD, HELLO_WORLD_2);
/// ```
pub struct StrConstrue<const N: usize>(Construe<u8, N>);


/// Transpose between [`MaybeUninit<[T; N]>`] and [`[MaybeUninit<T>; N]`]
///
/// There are several ways to do this in the standard library but all of them are either unstable, not `const` or not stable in `const` contexts.
/// Simply using [`core::mem::transmute`] doesn't work either.
union ArrayTranspose<T, const N: usize> {
	complete: ManuallyDrop<MaybeUninit<[T; N]>>,
	elements: ManuallyDrop<[MaybeUninit<T>; N]>
}
impl<T, const N: usize> ArrayTranspose<T, N> {
	const fn elements(array: MaybeUninit<[T; N]>) -> [MaybeUninit<T>; N] {
		let transpose = Self {
			complete: ManuallyDrop::new(array)
		};
		unsafe {ManuallyDrop::into_inner(transpose.elements)}
	}
	const fn complete(array: [MaybeUninit<T>; N]) -> MaybeUninit<[T; N]> {
		let transpose = Self {
			elements: ManuallyDrop::new(array)
		};
		unsafe {ManuallyDrop::into_inner(transpose.complete)}
	}
}

impl<T> Construe<T, 0> {
	/// Create a [`Construe`] instance *without the buffer*
	///
	/// This instance will behave (almost) exactly the same as the instance that has the buffer.
	/// Crucially, it is *used* exactly the same, allowing you to write `const` functions that collect items into it as though it were a variably-sized buffer.
	/// Then, you simply *call those functions twice*: the first run (`N = 0`, using this constructor) determines the amount of space you need for the second run (where you use [`Construe::new`] with `N = `[`Construe::needs_len`] from before), which actually stores the collected items.
	/// Obviously, this requires that the functions are deterministic (though it will just panic if not).
	pub const fn start() -> Self {
		Self { offset: 0, buffer: [] }
	}
	/// Amount of items supposed to be stored in the buffer
	///
	/// This is the same as [`Construe::len`], but it exists as a separate method that's only implemented for `N = 0` so that the `const N: usize` generic parameter on functions returning this can be inferred as `0` for the first run.
	pub const fn needs_len(&self) -> usize {
		self.offset
	}
}

impl<T, const N: usize> Construe<T, N> {
	/// Create a [`Construe`] instance (with or without the buffer)
	///
	/// Like [`Construe::start`] but for any `N`, allowing you to write functions that create their own [`Construe`].
	/// They'll need a `const N` generic, which the call site will use to indicate whether this is the first or second run.
	pub const fn new() -> Self {
		Self {
			offset: 0,
			buffer: ArrayTranspose::elements(MaybeUninit::uninit())
		}
	}
	/// Amount of items stored (or supposed to be stored) in the buffer
	pub const fn len(&self) -> usize {
		self.offset
	}
	/// Whether there are no items (supposed to be) stored in the buffer
	pub const fn is_empty(&self) -> bool {
		self.len() == 0
	}

	/// Add item to the buffer
	///
	/// Note that this takes `self` by-value: since `&mut` references aren't yet stable in `const` contexts, we have to keep reassigning the output of the function to the variable.
	///
	/// On the first run, when the buffer isn't available yet, this returns the item inside the [`Option`].
	/// This is necessary because there's no way to restrict `T` to *not* have a destructor (which can't be used by `const` functions).
	/// By giving back the `T` in case we can't store it, we avoid taking on the responsibility to drop it.
	///
	/// ```
	/// use construe::Construe;
	/// // `mut` because we pass it by-value
	/// let mut construe = Construe::start();
	/// // `.0` to ignore (i.e. drop) the `Option<&str>`
	/// construe = construe.push("something").0;
	/// ```
	#[must_use]
	pub const fn push(mut self, item: T) -> (Self, Option<T>) {
		let item = match N {
			0 => Some(item),
			_ => {
				self.buffer[self.offset] = MaybeUninit::new(item);
				None
			}
		};
		self.offset += 1;
		(self, item)
	}

	/// Perform a bitwise copy of the slice contents into the buffer
	///
	/// This function is **untested**.
	/// If at all possible, loop over the slice and use [`Construe::push`] instead.
	///
	/// For [`Copy`] types, use [`copy_from`](Self::copy_from) instead.
	//7
	/// # Safety
	///
	/// Essentially, this function takes ownership of the contents of the slice.
	/// For several reasons, it is not currently possible to accept (and use) a `[T; M]` array by value.
	/// Because of this, the caller must pass in a slice of **an array it owns**, and then **drop/forget the array immediately after** this function returns.
	///
	/// This function may not be called outside of `const` contexts or with a `T` that needs to be dropped.
	pub const unsafe fn steal_from_slice(mut self, slice: &[T]) -> Self {
		assert!(
			!core::mem::needs_drop::<T>(),
			"may not steal from slices of types that need to be dropped"
		);
		if N == 0 {
			self.offset += slice.len();
		}
		else {
			assert!(self.offset + slice.len() < N, "buffer overflow");
			let mut i = 0;
			let len = slice.len();
			let item_ptr = slice.as_ptr();
			while i < len {
				let item = unsafe { item_ptr.add(i).read() };
				self.buffer[self.offset + i] = MaybeUninit::new(item);
				i += 1;
			}
			self.offset += i;
		}
		self
	}

	/// Return the filled buffer, consuming the struct
	///
	/// Panics if the buffer is not filled or if this is a zero-length buffer that was supposed to store items.
	pub const fn finish(self) -> [T; N] {
		assert!(
			N == self.offset || N == 0,
			"tried to extract zero-length buffer that pretended to store items"
		);
		assert!(
			N == self.offset || N != 0,
			"tried to extract partially filled buffer"
		);
		unsafe {ArrayTranspose::complete(self.buffer).assume_init()}
	}
}

impl<T: Copy, const N: usize> Construe<T, N> {
	/// [`Copy`] all items from `slice` into the buffer
	///
	/// If the buffer has length zero, the offset is incremented all the same, the contents of the slice are just not stored.
	pub const fn copy_from(mut self, mut slice: &[T]) -> Self {
		if N == 0 {
			self.offset += slice.len();
		}
		else {
			while let [byte, rest @ ..] = slice {
				slice = rest;
				self.buffer[self.offset] = MaybeUninit::new(*byte);
				self.offset += 1;
			}
		}
		self
	}
}


impl<const N: usize> StrConstrue<N> {
	/// Create a [`StrConstrue`] instance (with or without the buffer)
	pub const fn new() -> Self {Self(Construe::new())}

	/// Amount of bytes stored (or supposed to be stored) in the buffer
	pub const fn len(&self) -> usize {self.0.len()}
	/// Whether there are no bytes (supposed to be) stored in the buffer
	pub const fn is_empty(&self) -> bool {self.0.is_empty()}

	/// Push a [`&str`] into the buffer
	///
	/// Note that this takes `self` by-value: since `&mut` references aren't yet stable in `const` contexts, we have to keep reassigning the output of the function to the variable.
	/// ```
	/// // `mut` because we reassign to it
	/// let mut sc = construe::StrConstrue::<0>::new();
	/// sc = sc.push_str("Test");
	/// // method chaining can save space
	/// sc = sc.push_str("123").push_str(" ").push_str(":^)");
	/// ```
	#[must_use]
	pub const fn push_str(self, s: &str) -> Self {
		Self(self.0.copy_from(s.as_bytes()))
	}

	/// Return the assembled byte buffer, consuming the struct
	///
	/// Panics if the buffer is not filled or if this is a zero-length buffer that was supposed to store bytes.
	///
	/// Use [`StrConstrue::borrow_str`] to obtain a [`&str`] from the buffer.
	pub const fn store_bytes(self) -> [u8; N] {
		self.0.finish()
	}
}

impl StrConstrue<0> {
	/// Amount of bytes needed to store the string
	///
	/// This is the same as [`StrConstrue::len`], but it exists as a separate method that's only implemented for `N = 0` so that the `const N: usize` generic parameter on functions returning this can be inferred as `0` for the first run.
	pub const fn needs_len(&self) -> usize {self.0.needs_len()}
	/// Borrow a [`&str`] from a byte slice
	///
	/// See the example at the top for how to use this, the buffer returned by [`StrConstrue::store_bytes`] needs to be stored in a constant to be able to borrow a `&'static str` from it.
	///
	/// This just calls [`core::str::from_utf8`] and panics if the byte slice contains invalid UTF-8.
	pub const fn borrow_str(byte_slice: &[u8]) -> &str {
		match core::str::from_utf8(byte_slice) {
			Ok(slice_as_str) => slice_as_str,
			Err(_e) => panic!("assembled byte slice contains invalid UTF-8")
		}
	}
}

#[test]
fn try_construe() {
	const fn multiply<const N: usize, T: Copy>(mut slice: &[T], times: usize)
		-> Construe<T, N>
	{
		let mut c = Construe::new();
		while let [item, rest @ ..] = slice {
			slice = rest;
			let mut i = 0;
			while i < times {
				c = c.push(*item).0;
				i += 1;
			}
		}
		c
	}
	const fn concat<const N: usize>(mut slice: &[&str], times: usize)
		-> StrConstrue<N>
	{
		let mut sc = StrConstrue::new();
		while let [s, rest @ ..] = slice {
			slice = rest;
			let mut i = 0;
			while i < times {
				sc = sc.push_str(s);
				i += 1;
			}
		}
		sc
	}

	const WORDS: &[&str] = &{
		const LEN: usize = multiply(&["hey", "you"], 2).needs_len();
		const ARRAY: [&str; LEN] = multiply(&["hey", "you"], 2).finish();
		ARRAY
	};

	assert_eq!(WORDS, &["hey", "hey", "you", "you"]);

	const WORDS2: &[&str] = construe!(&[&str] => multiply(&["hey", "you"], 2));
	assert_eq!(WORDS, WORDS2);

	construe!(const WORDS3: &[&str] = multiply(&["hey", "you"], 2));
	assert_eq!(WORDS, WORDS3);

	construe!(const WORDS4: [&str; _] = multiply(&["hey", "you"], 2));
	assert_eq!(WORDS, &WORDS4);

	construe!(const ONE_WORD: &str = concat(&["hey", "you"], 2));
	assert_eq!(ONE_WORD, "heyheyyouyou");

	// since traits require `&'static` for some reason...
	trait Trait {
		const STR: &'static str;
		const ARR: &'static [u8];
	}
	impl Trait for () {
		construe!(const STR: &'static str = concat(&["a", "b"], 10));
		construe!(const ARR: &'static [u8] = multiply(&[1, 2, 3], 9));
	}
}