rubedo/
std.rs

1//! This module provides extensions to the Rust standard library.
2
3
4
5//		Modules																											
6
7#[cfg(test)]
8#[path = "tests/std.rs"]
9mod tests;
10
11
12
13//		Packages																										
14
15use base64::DecodeError;
16use core::{
17	convert::TryFrom,
18	fmt::{Debug, Display},
19	hash::Hash,
20	str::FromStr,
21};
22use hex::FromHexError;
23use rust_decimal::{
24	Decimal,
25	prelude::ToPrimitive as _,
26};
27use serde::{Deserialize, Serialize};
28use std::{
29	borrow::Cow,
30	env,
31	ffi::OsString,
32	path::{Component as PathComponent, Path, PathBuf},
33};
34use thiserror::Error as ThisError;
35
36#[cfg(feature = "crypto")]
37use crate::crypto::Hashed;
38#[cfg(feature = "crypto")]
39use ::{
40	core::future::Future,
41	digest::Digest as _,
42	std::{
43		fs::File,
44		io::{BufReader, Error as IoError, Read as _},
45	},
46	tokio::{
47		fs::File as AsyncFile,
48		io::{AsyncReadExt as _, BufReader as AsyncBufReader},
49	},
50};
51
52
53
54//		Enums																											
55
56//		ByteSizedError															
57/// The possible errors that can occur when working with [`ByteSized`] types.
58#[derive(Copy, Clone, Debug, Eq, PartialEq, ThisError)]
59#[non_exhaustive]
60pub enum ByteSizedError {
61	/// The supplied data is longer than `ByteSized::SIZE` bytes.
62	#[error("The supplied data is longer than {0} bytes")]
63	DataTooLong(usize),
64	
65	/// The supplied data is shorter than `ByteSized::SIZE` bytes.
66	#[error("The supplied data is shorter than {0} bytes")]
67	DataTooShort(usize),
68	
69	/// The supplied string is not in valid base64 format.
70	#[error("The supplied data is not in valid base64 format")]
71	InvalidBase64String,
72	
73	/// The supplied string is not in valid hexadecimal format.
74	#[error("The supplied data is not in valid hexadecimal format")]
75	InvalidHexString,
76}
77
78
79
80//		Structs																											
81
82//		LimitIterator															
83/// This struct provides an iterator that limits the number of items returned.
84/// 
85/// This will be returned from the [`limit()`](IteratorExt::limit()) method, and
86/// will generally not be used directly.
87/// 
88/// # See also
89/// 
90/// * [`IteratorExt::limit()`]
91/// 
92#[derive(Clone, Debug)]
93pub struct LimitIterator<I> {
94	//		Private properties													
95	/// The iterator to limit.
96	iter:  I,
97	
98	/// The maximum number of items to return.
99	limit: Option<usize>,
100	
101	/// The number of items returned so far.
102	count: usize,
103}
104
105//󰭅		Iterator																
106impl<I: Iterator> Iterator for LimitIterator<I> {
107	type Item = I::Item;
108	
109	//		next																
110	fn next(&mut self) -> Option<Self::Item> {
111		#[expect(clippy::arithmetic_side_effects, reason = "Range is controlled")]
112		if let Some(limit) = self.limit {
113			if self.count >= limit {
114				return None;
115			}
116			//	In this location, the count is guaranteed to not exceed the limit, so
117			//	this will not overflow and a checked_add() is not required.
118			self.count += 1;
119		}
120		self.iter.next()
121	}
122}
123
124
125
126//		Traits																											
127
128//§		AsStr																	
129/// This trait provides an [`as_str()`](AsStr::as_str()) method.
130/// 
131/// This trait requires the presence of an [`as_str()`](AsStr::as_str()) method.
132/// It's not possible to apply this trait purely as a marker to the existing
133/// types such as [`String`] that already have an [`as_str()`](AsStr::as_str())
134/// method and have it recognised that they already have it, due to Rust's
135/// implementation determination allowing multiple methods of the same name,
136/// differentiated by trait. In other words, our trait could define a method
137/// with the same name and signature as another trait, but an implementation of
138/// the function would not be considered to satisfy both. Both traits would have
139/// to have their methods specifically implemented, even if identical, and then
140/// the conflict would be resolved at call-time by specifying which trait's
141/// method is being called.
142/// 
143/// However, it is possible to apply this trait and call the underlying method
144/// on the type, for such cases as this may be required. This trait should
145/// therefore be applied to any types of interest, for which the [`as_str()`](crate::serde::as_str())
146/// serialisation function provided by the [`serde`](crate::serde) module is
147/// intended to be specified. Suitable standard and common types such as
148/// [`String`] and [`str`] have already had this trait implemented, and those
149/// implementations will be brought into scope when this trait is used.
150/// 
151/// In reality, implementations onto standard types should not really be
152/// necessary, as this trait exists primarily for use with the
153/// [`serde::as_str()`](crate::serde::as_str()) method, and Serde already knows
154/// how to handle such types so there is no real advantage to be gained by
155/// implementing this trait for such types. The intent and purpose of this trait
156/// is to provide a way to specify a string representation for types that do not
157/// already have one, such as dual-nature enums, i.e. where they can be
158/// represented as either a string or a number. Still, the trait has been
159/// applied to some common types for consistency and completeness.
160/// 
161/// The only current drawback is that trait functions cannot currently be
162/// declared as `const`, and the scope of the [`as_str()`](AsStr::as_str())
163/// method is usually such that it could be declared as `const` otherwise.
164/// 
165pub trait AsStr {
166	//		as_str																
167	/// Provides a string slice representation of the type.
168	#[must_use]
169	fn as_str(&self) -> &str;
170}
171
172//󰭅		String																	
173impl AsStr for String {
174	//		as_str																
175	fn as_str(&self) -> &str {
176		//	This simply calls the existing method, i.e. String.as_str(), but is
177		//	required to allow the trait to be applied to the type.
178		self.as_str()
179	}
180}
181
182//󰭅		str																		
183impl AsStr for str {
184	//		as_str																
185	fn as_str(&self) -> &str {
186		//	This simply returns the existing value, i.e. self, but is required
187		//	to allow the trait to be applied to the type.
188		self
189	}
190}
191
192//§		ByteSized																
193/// Fixed-size byte container functionality.
194/// 
195/// This trait provides a formalised representation of a fixed-size byte array,
196/// with support for common conversions, including to and from hex and base64
197/// formats.
198/// 
199/// Notably, it is defined in a way that allows implementation onto third-party
200/// types, i.e. those from other libraries, boosting their functionality with
201/// additional methods. Meanwhile, if applying to an owned type, whether an
202/// original or a wrapper, the full range of trait implementations for various
203/// conversions and similar is available via the [`ByteSizedFull`] trait.
204/// 
205/// The container is expected to be stored internally as `[u8; N]`, where `N` is
206/// defined upon the implementation of this trait — but the actual internal type
207/// is arbitrary. Because there may or may not be control over the internal type
208/// (for instance when implementing for a third-party type), the methods that
209/// require the ability to mutate or consume the internal type are split out
210/// into a separate [`ByteSizedMut`] trait.
211///
212/// The conversion to and from a [`String`] defaults to using hex strings rather
213/// than base64-encoded strings, because this is more common for the primary use
214/// case of hashes and keys, due to it being a fixed-length string that is easy
215/// to read, verify, and transmit without any compatibility issues. However,
216/// base64 conversion functions are also provided for convenience in case that
217/// format is preferred.
218/// 
219/// # See also
220/// 
221/// * [`ByteSizedFull`]
222/// * [`ByteSizedMut`]
223/// 
224pub trait ByteSized<const SIZE: usize>:
225	Sized
226	+ Clone
227	+ for<'a> ForceFrom<&'a [u8]>
228//	+ for<'a> ForceFrom<&'a [u8; N]>  //  Cannot specify this as a constraint due to N
229	+ ForceFrom<Vec<u8>>
230	+ for<'a> ForceFrom<&'a Vec<u8>>
231{
232	//		as_bytes															
233	/// Returns a byte slice of the container's contents.
234	/// 
235	/// Provides a read-only view of the byte data within the container, without
236	/// consuming the data. The returned slice is a reference to the actual data
237	/// stored in the container, not a copy. Because of this, it is not possible
238	/// to mutate the contents of the container through the returned slice. It
239	/// does not allocate new memory or change the ownership of the byte data.
240	/// This method is useful when you need to work with the bytes of the
241	/// container in a read-only fashion, or when you want to avoid copying the
242	/// data.
243	/// 
244	///   - This method returns a slice (`&[u8; Self::SIZE]`) referencing the
245	///     bytes of the container contents.
246	///   - The original container value remains intact, and can still be used
247	///     afterward.
248	///   - No reallocation or copying of data occurs since it's just providing
249	///     a view into the original memory.
250	/// 
251	/// Use this method when you need to work with the byte data in a
252	/// non-destructive, read-only manner while keeping the original container
253	/// intact.
254	///
255	/// # See also
256	/// 
257	/// * [`ByteSized::from_bytes()`]
258	/// * [`ByteSized::to_bytes()`]
259	/// * [`ByteSized::to_vec()`]
260	/// * [`ByteSizedMut::as_mut_bytes()`]
261	/// * [`ByteSizedMut::into_bytes()`]
262	/// * [`ByteSizedMut::into_vec()`]
263	/// 
264	#[must_use]
265	fn as_bytes(&self) -> &[u8; SIZE];
266	
267	//		to_bytes															
268	/// Returns a copy of the container data as a fixed-length array of bytes.
269	/// 
270	/// This does not consume the container, but clones it. Following Rust's
271	/// naming conventions and idioms, this method "converts" the data content
272	/// of the container into a byte representation, in a `[u8; SIZE]`. (No
273	/// actual conversion takes place if the data is already stored internally
274	/// as a fixed array of bytes, but this is academic, so "conversion" is
275	/// implied and expected as a theoretical behaviour.) Ownership of the
276	/// cloned and converted byte data is transferred to the caller, and there
277	/// are no side effects on the internal state of the [`ByteSized`] instance.
278	/// 
279	///   - This method returns a `[u8; SIZE]` array of bytes without consuming
280	///     the container contents.
281	///   - The original container value remains intact, and can still be used
282	///     afterward.
283	///   - The container data is copied, and converted/transformed into the
284	///     output value returned.
285	/// 
286	/// Use this method when you need to obtain a copy of the container's byte
287	/// data in the form of a `[u8; SIZE]`, without consuming the container
288	/// itself. This is useful when you need to pass the byte data to a function
289	/// that expects a `[u8; SIZE]`, or when you want to modify the byte data
290	/// without affecting the original container.
291	/// 
292	/// # See also
293	/// 
294	/// * [`ByteSized::as_bytes()`]
295	/// * [`ByteSized::from_bytes()`]
296	/// * [`ByteSized::to_vec()`]
297	/// * [`ByteSizedMut::as_mut_bytes()`]
298	/// * [`ByteSizedMut::into_bytes()`]
299	/// * [`ByteSizedMut::into_vec()`]
300	/// 
301	#[must_use]
302	fn to_bytes(&self) -> [u8; SIZE];
303	
304	//		from_bytes															
305	/// Constructs a [`ByteSized`] type from an array of bytes.
306	/// 
307	/// This method consumes the input array.
308	/// 
309	/// # Parameters
310	/// 
311	/// * `bytes` - The array of bytes to convert into the [`ByteSized`] type.
312	/// 
313	/// # See also
314	/// 
315	/// * [`ByteSized::as_bytes()`]
316	/// * [`ByteSized::to_bytes()`]
317	/// * [`ByteSizedMut::as_mut_bytes()`]
318	/// * [`ByteSizedMut::into_bytes()`]
319	/// 
320	#[must_use]
321	fn from_bytes(bytes: [u8; SIZE]) -> Self;
322	
323	//		to_base64															
324	/// Returns the container data converted to a base64-encoded [`String`].
325	/// 
326	/// This does not consume the container, but clones it, as is necessary to
327	/// perform the conversion to base64.
328	/// 
329	/// # See also
330	/// 
331	/// * [`ByteSized::from_base64()`]
332	/// 
333	#[must_use]
334	fn to_base64(&self) -> String;
335	
336	//		from_base64															
337	/// Converts a base64-encoded [`String`] to a [`ByteSized`] type.
338	/// 
339	/// This method does not consume the input string, but clones it, as is
340	/// necessary to perform the conversion from [`base64`].
341	/// 
342	/// # Parameters
343	/// 
344	/// * `encoded` - The base64-encoded [`String`] to convert into a
345	///               [`ByteSized`] type.
346	/// 
347	/// # Errors
348	/// 
349	/// This method will return an error if the input string is not valid
350	/// base64. Such an error will be returned as a [`DecodeError`], which is
351	/// passed through from the [`base64`] crate.
352	/// 
353	/// Note that if the incoming data results in a [`Vec<u8>`](Vec) that is too
354	/// long to fit, it will be truncated without error or warning. If there is
355	/// not enough data, it will be padded with zeroes. If this situation needs
356	/// checking, decode from base64 manually and then use `try_from()` instead.
357	/// 
358	/// # See also
359	/// 
360	/// * [`ByteSized::to_base64()`]
361	/// 
362	fn from_base64(encoded: &str) -> Result<Self, DecodeError>;
363	
364	//		to_hex																
365	/// Returns the container data converted to a hex-encoded [`String`].
366	/// 
367	/// This does not consume the container, but clones it, as is necessary to
368	/// perform the conversion to hexadecimal representation.
369	/// 
370	/// # See also
371	/// 
372	/// * [`ByteSized::from_hex()`]
373	/// 
374	#[must_use]
375	fn to_hex(&self) -> String;
376	
377	//		from_hex															
378	/// Converts a hex-encoded [`String`] to a [`ByteSized`].
379	/// 
380	/// This method does not consume the input string, but clones it, as is
381	/// necessary to perform the conversion from hexadecimal representation.
382	/// 
383	/// # Parameters
384	/// 
385	/// * `encoded` - The hex-encoded [`String`] to convert into a [`ByteSized`]
386	///               type.
387	/// 
388	/// # Errors
389	/// 
390	/// This method will return an error if the input string is not in valid
391	/// hexadecimal format. Such an error will be returned as a
392	/// [`FromHexError`], which is passed through from the [`hex`] crate.
393	///
394	/// Note that if the incoming data results in a [`Vec<u8>`](Vec) that is too
395	/// long to fit, it will be truncated without error or warning. If there is
396	/// not enough data, it will be padded with zeroes. If this situation needs
397	/// checking, use `try_from()` instead.
398	/// 
399	/// # See also
400	/// 
401	/// * [`ByteSized::to_hex()`]
402	/// 
403	fn from_hex(encoded: &str) -> Result<Self, FromHexError>;
404	
405	//		to_vec																
406	/// Returns a copy of the container data converted to a vector of bytes.
407	/// 
408	/// This does not consume the container, but clones it. Following Rust's
409	/// naming conventions and idioms, this method converts the data content of
410	/// the container into a byte representation, in a [`Vec<u8>`](Vec).
411	/// Ownership of the cloned and converted byte data is transferred to the
412	/// caller, and there are no side effects on the internal state of the
413	/// [`ByteSized`] instance.
414	/// 
415	///   - This method returns a [`Vec<u8>`](Vec) vector of bytes without
416	///     consuming the container contents.
417	///   - The original container value remains intact, and can still be used
418	///     afterward.
419	///   - The container data is copied, and converted/transformed into the
420	///     output value returned.
421	/// 
422	/// Use this method when you need to obtain a copy of the container's byte
423	/// data in the form of a [`Vec<u8>`](Vec), without consuming the container
424	/// itself. This is useful when you need to pass the byte data to a function
425	/// that expects a [`Vec<u8>`](Vec).
426	/// 
427	/// # See also
428	/// 
429	/// * [`ByteSized::as_bytes()`]
430	/// * [`ByteSized::to_bytes()`]
431	/// * [`ByteSizedMut::as_mut_bytes()`]
432	/// * [`ByteSizedMut::into_bytes()`]
433	/// * [`ByteSizedMut::into_vec()`]
434	/// 
435	#[must_use]
436	fn to_vec(&self) -> Vec<u8>;
437}
438
439//§		ByteSizedFull															
440/// Full conversion functionality for [`ByteSized`]-based types.
441/// 
442/// This trait provides no methods, but establishes required trait
443/// implementations that should be present for a full implementation of
444/// [`ByteSized`] functionality onto an owned type or wrapper. This includes
445/// support for common conversions, including serialisation and deserialisation
446/// using [Serde](https://crates.io/crates/serde). The traits that cannot be
447/// implemented for third-party types due to the orphan rule are therefore
448/// listed under this trait as constraints.
449/// 
450/// Because there may or may not be control over the internal type (for instance
451/// when implementing onto a third-party type, or for a wrapper), the
452/// implementations that require the ability to mutate or consume the internal
453/// type are specified in the separate [`ByteSizedMut`] trait.
454/// 
455/// # See also
456/// 
457/// * [`ByteSized`]
458/// * [`ByteSizedMut`]
459/// 
460pub trait ByteSizedFull<const SIZE: usize>:
461	ByteSized<SIZE>
462	+ AsRef<[u8; SIZE]>
463	+ Debug
464	+ Default
465	+ Display
466	+ From<[u8; SIZE]>
467	+ for<'a> From<&'a [u8; SIZE]>
468	+ FromStr
469	+ Hash
470	+ PartialEq
471	+ Serialize
472	+ for<'de> Deserialize<'de>
473	+ for<'a> TryFrom<&'a [u8]>
474	+ for<'a> TryFrom<&'a str>
475	+ TryFrom<String>
476	+ for<'a> TryFrom<&'a String>
477	+ TryFrom<Box<str>>
478	+ for<'a> TryFrom<Cow<'a, str>>
479	+ TryFrom<Vec<u8>>
480	+ for<'a> TryFrom<&'a Vec<u8>>
481{}
482
483//§		ByteSizedMut															
484/// Mutating and consuming functionality for [`ByteSized`].
485/// 
486/// This trait provides methods that mutate and/or consume the underlying data
487/// type represented, expected to be a `[u8; N]`, where `N` is defined upon the
488/// implementation of the [`ByteSized`] trait — but the actual internal type is
489/// arbitrary.
490/// 
491/// Because there may or may not be control over the internal type (for instance
492/// when implementing for a third-party type), the methods that require the
493/// ability to mutate or consume the internal type are split out into this
494/// separate [`ByteSizedMut`] trait, with the read-only methods and constructors
495/// being in the main [`ByteSized`] trait, and the traits that cannot be
496/// implemented for third-party types due to the orphan rule being specified
497/// under the [`ByteSizedFull`] trait.
498/// 
499/// # See also
500/// 
501/// * [`ByteSized`]
502/// * [`ByteSizedFull`]
503/// 
504pub trait ByteSizedMut<const SIZE: usize>:
505	ByteSized<SIZE>
506	+ AsMut<[u8; SIZE]>
507{
508	//		as_mut_bytes														
509	/// Returns a mutable reference to the container's contents.
510	/// 
511	/// Provides a mutable view of the byte data within the container, without
512	/// consuming the data. The returned vector is a reference to the actual
513	/// data stored in the container, not a copy. This method is useful when you
514	/// need to work with, and modify, the bytes of the container directly,
515	/// without copying the data.
516	/// 
517	///   - This method returns a mutable array (`&mut [u8; SIZE]`) referencing
518	///     the bytes of the container contents.
519	///   - The original container value remains intact, and can still be used
520	///     afterward.
521	///   - No reallocation or copying of data occurs since it's just providing
522	///     a reference to the original memory.
523	/// 
524	/// Use this method when you need to work directly with the byte data in a
525	/// mutable manner.
526	/// 
527	/// # See also
528	/// 
529	/// * [`ByteSized::as_bytes()`]
530	/// * [`ByteSized::from_bytes()`]
531	/// * [`ByteSized::to_bytes()`]
532	/// * [`ByteSized::to_vec()`]
533	/// * [`ByteSizedMut::into_bytes()`]
534	/// * [`ByteSizedMut::into_vec()`]
535	/// 
536	fn as_mut_bytes(&mut self) -> &mut [u8; SIZE];
537	
538	//		into_bytes															
539	/// Returns the container as a fixed-length array of bytes.
540	/// 
541	/// This consumes the container, without cloning or copying, and returns a
542	/// new fixed-length array containing the bytes of the container. It
543	/// transfers ownership of the byte data from the container to the new
544	/// array. This method is useful when you need to move the byte data out of
545	/// the container, or when you want to modify the byte data in-place without
546	/// affecting the original container.
547	/// 
548	///   - This method consumes the container contents and returns a
549	///     `[u8; SIZE]` containing its bytes.
550	///   - After calling this method, the original container value is no longer
551	///     available for use, because it has been moved.
552	/// 
553	/// Use this method when you want to consume the container and obtain
554	/// ownership of its byte data in the form of a `[u8; SIZE]`. This is useful
555	/// when you need to modify or move the byte data, or when you want to pass
556	/// it to functions that expect a `[u8; SIZE]`.
557	/// 
558	/// # See also
559	/// 
560	/// * [`ByteSized::as_bytes()`]
561	/// * [`ByteSized::from_bytes()`]
562	/// * [`ByteSized::to_bytes()`]
563	/// * [`ByteSized::to_vec()`]
564	/// * [`ByteSizedMut::as_mut_bytes()`]
565	/// * [`ByteSizedMut::into_vec()`]
566	/// 
567	#[must_use]
568	fn into_bytes(self) -> [u8; SIZE];
569	
570	//		into_vec															
571	/// Returns the container as a vector of bytes.
572	/// 
573	/// This consumes the container, and returns a new vector containing the
574	/// bytes of the container. It transfers ownership of the byte data from the
575	/// container to the new vector. This method is useful when you need to move
576	/// the byte data out of the container, for example to pass it to a function
577	/// that expects a [`Vec<u8>`](Vec). Note, however, that because vectors are
578	/// heap-allocated and can grow dynamically, whereas arrays are fixed-size
579	/// and stack-allocated, there isn't a direct, zero-copy way to consume an
580	/// array into a [`Vec`], and so this process does involve copying the data.
581	/// 
582	///   - This method consumes the container contents and returns a
583	///     [`Vec<u8>`](Vec) containing its bytes.
584	///   - After calling this method, the original container value is no longer
585	///     available for use, because it has been moved.
586	///   - Transforms the container into a vector of bytes, but does copy the
587	///     data.
588	/// 
589	/// Use this method when you want to consume the container and obtain
590	/// ownership of its byte data in the form of a [`Vec<u8>`](Vec). This is
591	/// useful when you need to modify or move the byte data.
592	/// 
593	/// # See also
594	/// 
595	/// * [`ByteSized::as_bytes()`]
596	/// * [`ByteSized::to_bytes()`]
597	/// * [`ByteSized::to_vec()`]
598	/// * [`ByteSizedMut::as_mut_bytes()`]
599	/// * [`ByteSizedMut::into_bytes()`]
600	/// 
601	#[must_use]
602	fn into_vec(self) -> Vec<u8>;
603}
604
605//§		FileExt																	
606/// This trait provides additional functionality to [`File`].
607#[cfg(feature = "crypto")]
608pub trait FileExt {
609	/// Hashes the contents of a file.
610	/// 
611	/// This function reads the contents of a file and hashes it using the
612	/// hashing algorithm associated to the hash type specified. The resulting
613	/// hash is returned as the specified formal [`Hashed`] type.
614	/// 
615	/// # Parameters
616	/// 
617	/// * `path` - The path to the file to hash.
618	/// 
619	/// # Errors
620	/// 
621	/// This function will return an error if the file cannot be opened, or if
622	/// there is a problem reading from the file.
623	/// 
624	fn hash<T: Hashed>(path: &Path) -> Result<T, IoError>;
625}
626
627//󰭅		File																	
628#[cfg(feature = "crypto")]
629impl FileExt for File {
630	fn hash<T: Hashed>(path: &Path) -> Result<T, IoError> {
631		let file       = Self::open(path)?;
632		let mut reader = BufReader::new(file);
633		let mut hasher = T::Algorithm::new();
634		let mut buffer = [0; 0x2000];  //  8KB buffer
635		loop {
636			let count = reader.read(&mut buffer)?;
637			if count == 0 {
638				break;
639			}
640			#[expect(clippy::indexing_slicing, reason = "Infallible")]
641			hasher.update(&buffer[..count]);
642		}
643		Ok(T::from_digest(hasher.finalize()))
644	}
645}
646
647//§		AsyncFileExt															
648/// This trait provides additional functionality to [`AsyncFile`].
649#[cfg(feature = "crypto")]
650pub trait AsyncFileExt {
651	/// Hashes the contents of a file asynchronously.
652	/// 
653	/// This function reads the contents of a file and hashes it using the
654	/// hashing algorithm associated to the hash type specified. The resulting
655	/// hash is returned as the specified formal [`Hashed`] type.
656	/// 
657	/// # Parameters
658	/// 
659	/// * `path` - The path to the file to hash.
660	/// 
661	/// # Errors
662	/// 
663	/// This function will return an error if the file cannot be opened, or if
664	/// there is a problem reading from the file.
665	/// 
666	//	Cannot use the async keyword here due to needing to specify Send as a
667	//	constraint.
668	fn hash<T: Hashed>(path: &Path) -> impl Future<Output = Result<T, IoError>> + Send;
669}
670
671//󰭅		AsyncFile																
672#[cfg(feature = "crypto")]
673impl AsyncFileExt for AsyncFile {
674	async fn hash<T: Hashed>(path: &Path) -> Result<T, IoError> {
675		let file       = Self::open(path).await?;
676		let mut reader = AsyncBufReader::new(file);
677		let mut hasher = T::Algorithm::new();
678		let mut buffer = [0; 0x2000];  //  8KB buffer
679		loop {
680			let count = reader.read(&mut buffer).await?;
681			if count == 0 {
682				break;
683			}
684			#[expect(clippy::indexing_slicing, reason = "Infallible")]
685			hasher.update(&buffer[..count]);
686		}
687		Ok(T::from_digest(hasher.finalize()))
688	}
689}
690
691//§		FromIntWithScale														
692/// Converts from an integer to a floating-point number with a specified scale.
693/// 
694/// This trait requires the presence of a [`from_int_with_scale()`](FromIntWithScale::from_int_with_scale())
695/// method, which converts from an integer to a floating-point number with a
696/// specified scale, i.e. a certain number of decimal places. For example, if
697/// the scale is `2`, then the integer `1234` would be converted to the
698/// floating-point number `12.34`. This is most useful when dealing with
699/// currencies.
700/// 
701/// The trait is implemented for the standard floating-point types, i.e. [`f32`]
702/// and [`f64`], and for the [`Decimal`] type from the [`rust_decimal`](https://crates.io/crates/rust_decimal)
703/// crate. For the corresponding integer types expressed as the generic `T`, it
704/// is implemented for the standard integer types [`i8`], [`i16`], [`i32`],
705/// [`i64`], [`i128`], [`u8`], [`u16`], [`u32`], [`u64`], and [`u128`].
706/// 
707/// Note that not all of these integer types can have their full range
708/// represented by all of the floating-point types, and so naive conversion may
709/// result in them being truncated or rounded. To avoid this happening
710/// invisibly, the conversion will return [`None`] if the input number cannot be
711/// accurately represented in the output type. Care should be taken to assess
712/// the likelihood of this occurring, and to ensure that the correct types are
713/// used. This cannot be guaranteed by the compiler, as the outcome depends
714/// partly on the type and partly on the scale factor, and so an assessment has
715/// to be made at runtime.
716/// 
717pub trait FromIntWithScale<T>: Sized {
718	//		from_int_with_scale													
719	/// Converts from an integer to a floating-point number with a specified
720	/// scale.
721	/// 
722	/// This function converts from an integer to a floating-point number with a
723	/// specified scale, i.e. a certain number of decimal places. For example,
724	/// if the scale is `2`, then the integer `1234` would be converted to the
725	/// floating-point number `12.34`. This is most useful when dealing with
726	/// currencies.
727	/// 
728	/// Note that not all integer types can have their full range represented by
729	/// all of the floating-point types, and so naive conversion may result in
730	/// them being truncated or rounded. To avoid this happening invisibly, the
731	/// conversion will return [`None`] if the input number cannot be accurately
732	/// represented in the output type. Care should be taken to assess the
733	/// likelihood of this occurring, and to ensure that the correct types are
734	/// used. This cannot be guaranteed by the compiler, as the outcome depends
735	/// partly on the type and partly on the scale factor, and so an assessment
736	/// has to be made at runtime.
737	/// 
738	/// # Parameters
739	/// 
740	/// * `value` - The integer value to convert.
741	/// * `scale` - The scale factor, i.e. the number of decimal places. Note
742	///             that this is essentially limited to a maximum of 19 DP of
743	///             movement for an [`f32`] or [`f64`] without overflowing, and
744	///             28 DP for a [`Decimal`].
745	/// 
746	/// # See also
747	/// 
748	/// * [`ToIntWithScale::to_int_with_scale()`]
749	/// 
750	fn from_int_with_scale(value: T, scale: u8) -> Option<Self>;
751}
752
753//		impl_from_int_with_scale_for_float										
754/// Implements the [`FromIntWithScale`] trait for floating-point types.
755macro_rules! impl_from_int_with_scale_for_float {
756	($t:ty, f32) => {
757		//󰭅		Integer for f32													
758		impl FromIntWithScale<$t> for f32 {
759			//		from_int_with_scale											
760			#[allow(clippy::allow_attributes, reason = "Multiple possibilities through the macro invocation")]
761			#[allow(clippy::cast_lossless,    reason = "Being potentially lossy does not matter here")]
762			fn from_int_with_scale(value: $t, scale: u8) -> Option<Self> {
763				let factor = 10_u32.checked_pow(u32::from(scale))?;
764				#[allow(clippy::cast_precision_loss, reason = "Losing precision does not matter here")]
765				let scaled = value as f32 / factor as f32;
766				//	We need to manually check if the value exceeds the range of integer
767				//	values supported by an f32, as that will result in a loss of precision.
768				#[allow(trivial_numeric_casts,              reason = "Trivial casts here are due to the macro permutations")]
769				#[allow(clippy::cast_sign_loss,             reason = "Loss of sign does not matter here, as we are checking for overflow")]
770				#[allow(clippy::cast_possible_wrap,         reason = "Possible wrapping does not matter here, as we are checking for underflow")]
771				#[allow(clippy::invalid_upcast_comparisons, reason = "Superfluous upcast comparisons here are due to the macro permutations")]
772				if scaled.is_infinite() || (value as u128) > 0x0100_0000_u128 || (value as i128) < -0x0100_0000_i128 {
773					None
774				} else {
775					Some(scaled)
776				}
777			}
778		}
779	};
780	($t:ty, f64) => {
781		//󰭅		Integer for f64													
782		impl FromIntWithScale<$t> for f64 {
783			//		from_int_with_scale											
784			#[allow(clippy::allow_attributes, reason = "Multiple possibilities through the macro invocation")]
785			#[allow(clippy::cast_lossless,    reason = "Being potentially lossy does not matter here")]
786			fn from_int_with_scale(value: $t, scale: u8) -> Option<Self> {
787				let factor = 10_u64.checked_pow(u32::from(scale))?;
788				#[allow(clippy::cast_precision_loss, reason = "Losing precision does not matter here")]
789				let scaled = value as f64 / factor as f64;
790				//	We need to manually check if the value exceeds the range of integer
791				//	values supported by an f64, as that will result in a loss of precision.
792				#[allow(trivial_numeric_casts,              reason = "Trivial casts here are due to the macro permutations")]
793				#[allow(clippy::cast_sign_loss,             reason = "Loss of sign does not matter here, as we are checking for overflow")]
794				#[allow(clippy::cast_possible_wrap,         reason = "Possible wrapping does not matter here, as we are checking for underflow")]
795				#[allow(clippy::invalid_upcast_comparisons, reason = "Superfluous upcast comparisons here are due to the macro permutations")]
796				if scaled.is_infinite() || (value as u128) > 0x0020_0000_0000_0000_u128 || (value as i128) < -0x0020_0000_0000_0000_i128 {
797					None
798				} else {
799					Some(scaled)
800				}
801			}
802		}
803	};
804}
805
806impl_from_int_with_scale_for_float!(i8,   f32);
807impl_from_int_with_scale_for_float!(i16,  f32);
808impl_from_int_with_scale_for_float!(i32,  f32);
809impl_from_int_with_scale_for_float!(i64,  f32);
810impl_from_int_with_scale_for_float!(i128, f32);
811impl_from_int_with_scale_for_float!(i8,   f64);
812impl_from_int_with_scale_for_float!(i16,  f64);
813impl_from_int_with_scale_for_float!(i32,  f64);
814impl_from_int_with_scale_for_float!(i64,  f64);
815impl_from_int_with_scale_for_float!(i128, f64);
816impl_from_int_with_scale_for_float!(u8,   f32);
817impl_from_int_with_scale_for_float!(u16,  f32);
818impl_from_int_with_scale_for_float!(u32,  f32);
819impl_from_int_with_scale_for_float!(u64,  f32);
820impl_from_int_with_scale_for_float!(u128, f32);
821impl_from_int_with_scale_for_float!(u8,   f64);
822impl_from_int_with_scale_for_float!(u16,  f64);
823impl_from_int_with_scale_for_float!(u32,  f64);
824impl_from_int_with_scale_for_float!(u64,  f64);
825impl_from_int_with_scale_for_float!(u128, f64);
826
827//		impl_from_int_with_scale_for_decimal									
828/// Implements the [`FromIntWithScale`] trait for the [`Decimal`] type.
829macro_rules! impl_from_int_with_scale_for_decimal {
830	(i128) => {
831		//󰭅		i128 for Decimal												
832		impl FromIntWithScale<i128> for Decimal {
833			//		from_int_with_scale											
834			fn from_int_with_scale(value: i128, scale: u8) -> Option<Self> {
835				//	We should be able to rely upon Decimal::try_from_i128_with_scale() to
836				//	perform the necessary checks, but it currently has issues with numbers
837				//	larger than the supported 96-bit range, so we need to check manually.
838				if value > Decimal::MAX.to_i128().unwrap() || value < Decimal::MIN.to_i128().unwrap() {
839					None
840				} else {
841					Decimal::try_from_i128_with_scale(value, u32::from(scale)).ok()
842				}
843			}
844		}
845	};
846	(u128) => {
847		//󰭅		u128 for Decimal												
848		impl FromIntWithScale<u128> for Decimal {
849			//		from_int_with_scale											
850			#[allow(clippy::allow_attributes, reason = "Multiple possibilities through the macro invocation")]
851			#[allow(clippy::cast_lossless,    reason = "Being potentially lossy does not matter here")]
852			fn from_int_with_scale(value: u128, scale: u8) -> Option<Self> {
853				//	We should be able to rely upon Decimal::try_from_i128_with_scale() to
854				//	perform the necessary checks, but it currently has issues with numbers
855				//	larger than the supported 96-bit range, so we need to check manually.
856				//	Regardless of this, we would have to check if the value is larger than
857				//	supported by an i128 in any case.
858				#[allow(clippy::cast_possible_wrap, reason = "Possible wrapping does not matter here, as we are checking for underflow")]
859				if value > Decimal::MAX.to_u128().unwrap() || (value as i128) < Decimal::MIN.to_i128().unwrap() {
860					None
861				} else {
862					Decimal::try_from_i128_with_scale(value as i128, u32::from(scale)).ok()
863				}
864			}
865		}
866	};
867	($t:ty) => {
868		//󰭅		Integer for Decimal												
869		impl FromIntWithScale<$t> for Decimal {
870			//		from_int_with_scale											
871			#[allow(clippy::allow_attributes, reason = "Multiple possibilities through the macro invocation")]
872			#[allow(clippy::cast_lossless,    reason = "Being potentially lossy does not matter here")]
873			fn from_int_with_scale(value: $t, scale: u8) -> Option<Self> {
874				//	Everything less than 128 bits will fit safely into the Decimal's range.
875				Decimal::try_from_i128_with_scale(value as i128, u32::from(scale)).ok()
876			}
877		}
878	};
879}
880
881impl_from_int_with_scale_for_decimal!(i8);
882impl_from_int_with_scale_for_decimal!(i16);
883impl_from_int_with_scale_for_decimal!(i32);
884impl_from_int_with_scale_for_decimal!(i64);
885impl_from_int_with_scale_for_decimal!(i128);
886impl_from_int_with_scale_for_decimal!(u8);
887impl_from_int_with_scale_for_decimal!(u16);
888impl_from_int_with_scale_for_decimal!(u32);
889impl_from_int_with_scale_for_decimal!(u64);
890impl_from_int_with_scale_for_decimal!(u128);
891
892//§		ToIntWithScale															
893/// Converts from a floating-point number to an integer with a specified scale.
894/// 
895/// This trait requires the presence of a [`to_int_with_scale()`](ToIntWithScale::to_int_with_scale())
896/// method, which converts from a floating-point number to an integer with a
897/// specified scale, i.e. a certain number of decimal places. For example, if
898/// the scale is `2`, then the floating-point number `12.34` would be converted
899/// to the integer `1234`. This is most useful when dealing with currencies.
900/// 
901/// The trait is implemented for the standard floating-point types, i.e. [`f32`]
902/// and [`f64`], and for the [`Decimal`] type from the [`rust_decimal`](https://crates.io/crates/rust_decimal)
903/// crate. For the corresponding integer types expressed as the generic `T`, it
904/// is implemented for the standard integer types [`i8`], [`i16`], [`i32`],
905/// [`i64`], [`i128`], [`u8`], [`u16`], [`u32`], [`u64`], and [`u128`].
906/// 
907/// Note that not all of these floating-point types can have their full range
908/// represented by all of the integer types, and so naive conversion may result
909/// in them being truncated or rounded. To avoid this happening invisibly, the
910/// conversion will return [`None`] if the input number cannot be accurately
911/// represented in the output type. Care should be taken to assess the
912/// likelihood of this occurring, and to ensure that the correct types are used.
913/// This cannot be guaranteed by the compiler, as the outcome depends partly on
914/// the type and partly on the scale factor, and so an assessment has to be made
915/// at runtime.
916/// 
917pub trait ToIntWithScale<T>: Sized {
918	//		to_int_with_scale													
919	/// Converts from a floating-point number to an integer with a specified
920	/// scale.
921	/// 
922	/// This function converts from a floating-point number to an integer with a
923	/// specified scale, i.e. a certain number of decimal places. For example,
924	/// if the scale is `2`, then the integer `1234` would be converted to the
925	/// floating-point number `12.34`. This is most useful when dealing with
926	/// currencies.
927	/// 
928	/// Note that not all floating-point types can have their full range
929	/// represented by all of the integer types, and so naive conversion may
930	/// result in them being truncated or rounded. To avoid this happening
931	/// invisibly, the conversion will return [`None`] if the input number
932	/// cannot be accurately represented in the output type. Care should be
933	/// taken to assess the likelihood of this occurring, and to ensure that the
934	/// correct types are used. This cannot be guaranteed by the compiler, as
935	/// the outcome depends partly on the type and partly on the scale factor,
936	/// and so an assessment has to be made at runtime.
937	/// 
938	/// # Parameters
939	///
940	/// * `scale` - The scale factor, i.e. the number of decimal places. Note
941	///             that this is essentially limited to a maximum of 19 DP of
942	///             movement without overflowing.
943	/// 
944	/// # See also
945	/// 
946	/// * [`FromIntWithScale::from_int_with_scale()`]
947	/// 
948	fn to_int_with_scale(&self, scale: u8) -> Option<T>;
949}
950
951//		impl_to_int_with_scale_for_float										
952/// Implements the [`ToIntWithScale`] trait for floating-point types.
953macro_rules! impl_to_int_with_scale_for_float {
954	($t:ty, $f:ty) => {
955		//󰭅		Integer for Float												
956		impl ToIntWithScale<$t> for $f {
957			//		to_int_with_scale											
958			#[allow(clippy::allow_attributes,    reason = "Multiple possibilities through the macro invocation")]
959			#[allow(clippy::cast_lossless,       reason = "Being potentially lossy does not matter here")]
960			#[allow(clippy::cast_precision_loss, reason = "Losing precision does not matter here")]
961			fn to_int_with_scale(&self, scale: u8) -> Option<$t> {
962				let factor = 10_u64.checked_pow(u32::from(scale))?;
963				let scaled = (self * factor as $f).round();
964				if scaled.is_infinite() || scaled > <$t>::MAX as $f || scaled < <$t>::MIN as $f {
965					None
966				} else {
967					#[allow(clippy::cast_possible_truncation, reason = "Possible truncation does not matter here")]
968					#[allow(clippy::cast_sign_loss,           reason = "Loss of sign will not occur here, as we are casting to a float")]
969					Some(scaled as $t)
970				}
971			}
972		}
973	};
974}
975
976impl_to_int_with_scale_for_float!(i8,   f32);
977impl_to_int_with_scale_for_float!(i16,  f32);
978impl_to_int_with_scale_for_float!(i32,  f32);
979impl_to_int_with_scale_for_float!(i64,  f32);
980impl_to_int_with_scale_for_float!(i128, f32);
981impl_to_int_with_scale_for_float!(i8,   f64);
982impl_to_int_with_scale_for_float!(i16,  f64);
983impl_to_int_with_scale_for_float!(i32,  f64);
984impl_to_int_with_scale_for_float!(i64,  f64);
985impl_to_int_with_scale_for_float!(i128, f64);
986impl_to_int_with_scale_for_float!(u8,   f32);
987impl_to_int_with_scale_for_float!(u16,  f32);
988impl_to_int_with_scale_for_float!(u32,  f32);
989impl_to_int_with_scale_for_float!(u64,  f32);
990impl_to_int_with_scale_for_float!(u128, f32);
991impl_to_int_with_scale_for_float!(u8,   f64);
992impl_to_int_with_scale_for_float!(u16,  f64);
993impl_to_int_with_scale_for_float!(u32,  f64);
994impl_to_int_with_scale_for_float!(u64,  f64);
995impl_to_int_with_scale_for_float!(u128, f64);
996
997//		impl_to_int_with_scale_for_decimal										
998/// Implements the [`ToIntWithScale`] trait for the [`Decimal`] type.
999macro_rules! impl_to_int_with_scale_for_decimal {
1000	(i128) => {
1001		//󰭅		i128 for Decimal												
1002		impl ToIntWithScale<i128> for Decimal {
1003			fn to_int_with_scale(&self, scale: u8) -> Option<i128> {
1004				//	The integer range of the Decimal type is less than that of an i128, but
1005				//	we cannot convert first and then scale, because the floating-point
1006				//	component will be truncated and lost. We therefore need to scale first,
1007				//	but this restricts the range of the final outcome to that of the Decimal
1008				//	type, which is 96 bits.
1009				let factor = 10_u64.checked_pow(u32::from(scale))?;
1010				(self.checked_mul(Decimal::from(factor))?.round()).to_i128()
1011			}
1012		}
1013	};
1014	(u128) => {
1015		//󰭅		u128 for Decimal												
1016		impl ToIntWithScale<u128> for Decimal {
1017			fn to_int_with_scale(&self, scale: u8) -> Option<u128> {
1018				//	The integer range of the Decimal type is less than that of an i128, but
1019				//	we cannot convert first and then scale, because the floating-point
1020				//	component will be truncated and lost. We therefore need to scale first,
1021				//	but this restricts the range of the final outcome to that of the Decimal
1022				//	type, which is 96 bits.
1023				let factor = 10_u64.checked_pow(u32::from(scale))?;
1024				(self.checked_mul(Decimal::from(factor))?.round()).to_u128()
1025			}
1026		}
1027	};
1028	($t:ty) => {
1029		//󰭅		Integer for Decimal												
1030		impl ToIntWithScale<$t> for Decimal {
1031			fn to_int_with_scale(&self, scale: u8) -> Option<$t> {
1032				let factor = 10_u64.checked_pow(u32::from(scale))?;
1033				let scaled = self.checked_mul(Decimal::from(factor))?.round();
1034				//	Everything less than 128 bits will fit safely into the Decimal's range.
1035				if scaled > Decimal::from(<$t>::MAX) || scaled < Decimal::from(<$t>::MIN) {
1036					None
1037				} else {
1038					scaled.to_i128().and_then(|value| value.try_into().ok())
1039				}
1040			}
1041		}
1042	};
1043}
1044
1045impl_to_int_with_scale_for_decimal!(i8);
1046impl_to_int_with_scale_for_decimal!(i16);
1047impl_to_int_with_scale_for_decimal!(i32);
1048impl_to_int_with_scale_for_decimal!(i64);
1049impl_to_int_with_scale_for_decimal!(i128);
1050impl_to_int_with_scale_for_decimal!(u8);
1051impl_to_int_with_scale_for_decimal!(u16);
1052impl_to_int_with_scale_for_decimal!(u32);
1053impl_to_int_with_scale_for_decimal!(u64);
1054impl_to_int_with_scale_for_decimal!(u128);
1055
1056//§		ForceFrom																
1057/// Simple and safe forced infallible type conversion.
1058/// 
1059/// Rust's [`From`] trait provides an infallible (and lossless) mechanism to
1060/// convert one type to another, and [`TryFrom`] provides the equivalent
1061/// fallible mechanism. However, it isn't possible to implement both at the same
1062/// time, because implementing [`From`] brings [`Into`] along for free for the
1063/// reverse operation, and Rust implements a blanket `impl<T, U> TryFrom<U> for
1064/// T, where U: Into<T>` meaning that [`TryFrom`] is available (yet infallible)
1065/// for all implementations of [`From`].
1066/// 
1067/// Therefore, this trait exists in order to provide a non-conflicting mechanism
1068/// for implementing [`From`]-style conversions in situations that *can* fail,
1069/// but in which failure is not necessarily important.
1070/// 
1071/// A good example is that of converting a base64-encoded string into a
1072/// fixed-length array of bytes: it will likely be important to deal with
1073/// decoding errors, but the possible truncation may not matter. Therefore,
1074/// [`TryFrom`] could be implemented for both [`String`] and [`Vec<u8>`](Vec),
1075/// but additionally [`ForceFrom`] could be implemented for [`Vec<u8>`](Vec).
1076/// This then ensures that all [`String`] decoding issues will be caught and
1077/// thought about, but byte data can be chosen to be truncated invisibly, or
1078/// handled as an error, depending on context.
1079/// 
1080/// Handling this as a separate, clearly-signposted approach is more idiomatic
1081/// than obscuring it behind [`From`], which should always be lossless as well
1082/// as needing to be infallible. [`ForceFrom`] is essentially a lossy version of
1083/// [`From`] — which means it should not be used for situations of error that do
1084/// not relate to loss-associated situations.
1085/// 
1086pub trait ForceFrom<T> {
1087	/// Performs the conversion to this type from the input type.
1088	fn force_from(value: T) -> Self;
1089}
1090
1091//§		IteratorExt																
1092/// This trait provides additional functionality to [`Iterator`].
1093pub trait IteratorExt: Iterator {
1094	//		limit																
1095	/// Limits the number of items returned by an iterator.
1096	/// 
1097	/// This is the same as [`Iterator::take()`], but accepts an [`Option`], so
1098	/// that the limit does not have to be specified. It allows a match such as
1099	/// `foo.iter().take(match limit { Some(n) => n, None => foo.len() })`
1100	/// to be simplified to `foo.iter().limit(limit)`, and is especially useful
1101	/// when `foo` is of unknown or infinite length.
1102	/// 
1103	/// # Parameters
1104	/// 
1105	/// * `limit` - The maximum number of items to return. If [`None`], no limit
1106	///             will be applied.
1107	/// 
1108	fn limit(self, limit: Option<usize>) -> LimitIterator<Self> where Self: Sized {
1109		LimitIterator { iter: self, limit, count: 0 }
1110	}
1111}
1112
1113//󰭅		Iterator																
1114impl<I: Iterator> IteratorExt for I {}
1115
1116//§		PathExt																	
1117/// This trait provides additional functionality to [`Path`].
1118pub trait PathExt {
1119	//		append																
1120	/// Appends a string to a path.
1121	/// 
1122	/// Adds a string to the end of a path, and returns the result as a new
1123	/// path. This is specifically different to both [`push()`](PathBuf::push())
1124	/// and [`join()`](Path::join()), as it simply appends the string without
1125	/// having any further effect on the path. By contrast, [`push()`](PathBuf::push())
1126	/// and [`join()`](Path::join()) will append a new string as a new path
1127	/// component, which will then be normalized, and will also replace the path
1128	/// entirely if the string is an absolute path.
1129	/// 
1130	/// # Parameters
1131	/// 
1132	/// * `suffix` - The string to append to the path.
1133	/// 
1134	/// # See also
1135	/// 
1136	/// * [`std::path::Path::join()`]
1137	/// * [`std::path::PathBuf::push()`]
1138	/// 
1139	fn append<P: AsRef<Path>>(&self, suffix: P) -> PathBuf;
1140	
1141	//		is_subjective														
1142	/// Checks if the path is specifically relative to the current directory.
1143	/// 
1144	/// Returns `true` if the path starts with a reference to the current
1145	/// directory, i.e. `.` or `..` (as `..` is the parent of the current
1146	/// directory and therefore related to it), making it specifically and
1147	/// explicitly related to the current working directory. This can be
1148	/// described as a subjective relative path, as opposed to an objective
1149	/// relative path which is generically relative because it lacks a root
1150	/// component.
1151	/// 
1152	/// A path that is subjective is also always relative. It is not possible to
1153	/// have a subjective absolute path, as that would be a contradiction in
1154	/// terms. However, objective paths may be either absolute or relative.
1155	/// There is therefore no method `is_objective()`, as it does not currently
1156	/// appear to have a useful purpose.
1157	/// 
1158	/// # See also
1159	/// 
1160	/// * [`std::path::Path::is_absolute()`]
1161	/// * [`std::path::Path::is_relative()`]
1162	/// 
1163	fn is_subjective(&self) -> bool;
1164	
1165	//		normalize															
1166	/// Normalizes the path.
1167	/// 
1168	/// Computes the canonicalized, absolute path of a file or directory, but
1169	/// without expanding symlinks or checking existence. A path that starts
1170	/// with `.` or without an initial separator will be interpreted relative to
1171	/// the current working directory (or the filesystem root if the current
1172	/// working directory is not accessible). Empty paths and paths of `.` alone
1173	/// will result in the current working directory being returned.
1174	/// 
1175	/// This function will normalize the path by removing any `.` and `..`
1176	/// segments and returning the "real" path. It does this without touching
1177	/// the filesystem, and so is an abstract but also simpler version of
1178	/// [`canonicalize()`](Path::canonicalize()), which does a number of
1179	/// filesystem checks. It does check for the current working directory, on
1180	/// which to base relative paths, but does not perform any other checks.
1181	/// 
1182	/// Key differences are that [`canonicalize()`](Path::canonicalize()) will
1183	/// return an error if the path does not exist, and will resolve symlinks.
1184	/// This function will remove `.` segments, and will remove the parent
1185	/// segment along with the current segment for `..` segments.
1186	/// 
1187	/// # See also
1188	/// 
1189	/// * [`restrict()`](PathExt::restrict())
1190	/// * [`std::fs::canonicalize()`]
1191	/// * [`std::path::Path::canonicalize()`]
1192	/// 
1193	fn normalize(&self) -> PathBuf;
1194	
1195	//		restrict															
1196	/// Restricts the path.
1197	/// 
1198	/// Computes the canonicalized, absolute path of a file or directory, but
1199	/// without allowing parent directory traversal to go beyond the base path.
1200	/// If no base path is specified, the current working directory will be
1201	/// used. If the path starts with `.` then this will be interpreted relative
1202	/// to the base path.
1203	/// 
1204	/// This function calls [`normalize()`](PathExt::normalize()), and so the
1205	/// fundamental behaviour of the resolution performed is the same as that
1206	/// function. The difference is that this function will not allow the path
1207	/// to go beyond the base path, and so any `..` segments will simply be
1208	/// removed from the path if they would otherwise go beyond the anchor
1209	/// point.
1210	/// 
1211	/// This does have the effect that if a path does try to traverse too far,
1212	/// it may lose additional components. For example, a path of `../foo` will
1213	/// end up losing the `foo` component, as the logic will be that `foo` is
1214	/// intended to be a sibling to the base path and not a child of it, and is
1215	/// therefore invalid. So if the base directory is `/home/user` then a path
1216	/// of `../foo` will be resolved to `/home/user` and not `/home/user/foo`.
1217	/// The effect of this continues further, in that all children of `foo` will
1218	/// also be deemed invalid. So `../foo/bar` will also be resolved to
1219	/// `/home/user`, and not `/home/user/foo/bar` or `/home/user/bar`. Care
1220	/// should therefore be taken when using this function to ensure that the
1221	/// path returned is valid for the intended use.
1222	/// 
1223	/// In the case of the path being absolute, it will be resolved and then
1224	/// compared against the base path. If the path is a child of the base path
1225	/// then it will be returned - otherwise the base path will be returned, as
1226	/// the path is invalid. For example, if the base directory is `/home/user`
1227	/// then a path of `/home/user/foo` will be returned, but a path of
1228	/// `/home/otheruser` will return `/home/user`.
1229	/// 
1230	/// Note that this function does not touch the filesystem, does not expand
1231	/// symlinks, and does not check that the path exists - including the
1232	/// base path. Hence when this documentation talks about base directory,
1233	/// it does so interchangeably with base path, as the valid intent would be
1234	/// for the base path to be a directory, but this is not actually checked.
1235	/// 
1236	/// # Parameters
1237	/// 
1238	/// * `base` - The base path to use. If this is [`None`] then the current
1239	///            working directory will be used.
1240	/// 
1241	/// # See also
1242	/// 
1243	/// * [`normalize()`](PathExt::normalize())
1244	/// 
1245	fn restrict<P: AsRef<Path>>(&self, base: P) -> PathBuf;
1246	
1247	//		strip_parentdirs													
1248	/// Removes references to parent directories, i.e. `..`.
1249	/// 
1250	/// Removes any [`ParentDir`](std::path::Component::ParentDir) components
1251	/// from either the beginning of the path or anywhere in the path.
1252	/// 
1253	/// This function does not touch the filesystem, or check if the path is
1254	/// valid or exists. It will also not attempt to resolve the parent
1255	/// directory references that it removes, so they will be taken out with no
1256	/// effect on the rest of the path.
1257	/// 
1258	/// # Parameters
1259	/// 
1260	/// * `remove_all` - If `true` then all parent directory references will be
1261	///                  removed, otherwise only those at the beginning of the
1262	///                  path will be removed.
1263	/// 
1264	/// # See also
1265	/// 
1266	/// * [`std::path::Component`]
1267	/// * [`std::path::Path::components()`]
1268	/// 
1269	fn strip_parentdirs(&self, remove_all: bool) -> PathBuf;
1270	
1271	//		strip_root															
1272	/// Makes the path relative by removing the root and/or prefix components.
1273	/// 
1274	/// Removes any components from the path that are considered to be the root
1275	/// or prefix of the path. The prefix is this context is not the same as in
1276	/// [`strip_prefix()`](Path::strip_prefix()), which removes a specific
1277	/// string prefix from the path. Rather, the prefix here is a
1278	/// [`PrefixComponent`](std::path::PrefixComponent). A path is considered to
1279	/// be absolute if it has a root on Unix, or if it has both root and prefix
1280	/// on Windows. Therefore, in order to convert the path to be relative, both
1281	/// the root and prefix must be removed.
1282	/// 
1283	/// This function does not touch the filesystem, or check if the path is
1284	/// valid or exists. It will also not attempt to resolve special directory
1285	/// references such as `.` or `..`.
1286	/// 
1287	/// # See also
1288	/// 
1289	/// * [`std::path::Path::components()`]
1290	/// * [`std::path::Path::has_root()`]
1291	/// * [`std::path::Path::is_absolute()`]
1292	/// * [`std::path::Path::strip_prefix()`]
1293	/// * [`std::path::Prefix`]
1294	/// * [`std::path::PrefixComponent`]
1295	/// 
1296	fn strip_root(&self) -> PathBuf;
1297}
1298
1299//󰭅		Path																	
1300impl PathExt for Path {
1301	//		append																
1302	fn append<P: AsRef<Self>>(&self, suffix: P) -> PathBuf {
1303		PathBuf::from([
1304			self.as_os_str().to_os_string(),
1305			OsString::from(suffix.as_ref()),
1306		].into_iter().collect::<OsString>())
1307	}
1308	
1309	//		is_subjective														
1310	fn is_subjective(&self) -> bool {
1311		self.is_relative() && {
1312			let mut components = self.components();
1313			matches!(components.next(), Some(PathComponent::CurDir | PathComponent::ParentDir))
1314		}
1315	}
1316	
1317	//		normalize															
1318	fn normalize(&self) -> PathBuf {
1319		let cwd = env::current_dir().unwrap_or_else(|_| PathBuf::from("/"));
1320		if self.as_os_str().is_empty() {
1321			return cwd;
1322		}
1323		let mut segments: Vec<OsString> = vec![];
1324		let mut had_prefix              = false;
1325		let mut had_root                = false;
1326		for (i, component) in self.components().enumerate() {
1327			match component {
1328				PathComponent::Prefix(prefix) => {
1329					segments.push(prefix.as_os_str().to_os_string());
1330					had_prefix = true;
1331				},
1332				PathComponent::RootDir => {
1333					if had_prefix || i == 0 {
1334						segments.push(component.as_os_str().to_os_string());
1335						had_root = true;
1336					}
1337				},
1338				PathComponent::CurDir    |
1339				PathComponent::ParentDir => {
1340					if i == 0 {
1341						segments.append(
1342							cwd.components()
1343								.map(|c| c.as_os_str().to_os_string())
1344								.collect::<Vec<OsString>>()
1345								.as_mut()
1346						);
1347					}
1348					if component == PathComponent::ParentDir {
1349						//	Only pop if we have segments beyond the root components
1350						if segments.len() > usize::from(had_prefix).saturating_add(usize::from(had_root)) {
1351							drop(segments.pop());
1352						}
1353					}
1354				},
1355				PathComponent::Normal(_) => {
1356					if i == 0 {
1357						segments.push(cwd.as_os_str().to_os_string());
1358					}
1359					segments.push(component.as_os_str().to_os_string());
1360				},
1361			}
1362		}
1363		segments.iter().collect()
1364	}
1365	
1366	//		restrict															
1367	fn restrict<P: AsRef<Self>>(&self, base: P) -> PathBuf {
1368		let basepath = base.as_ref().normalize();
1369		if self.as_os_str().is_empty() {
1370			return basepath;
1371		}
1372		let path = if self.is_absolute() {
1373			self.to_path_buf()
1374		} else {
1375			basepath.join(self)
1376		}.normalize();
1377		match path.strip_prefix(&basepath) {
1378			Ok(_)  => path,
1379			Err(_) => basepath,
1380		}
1381	}
1382	
1383	//		strip_parentdirs													
1384	fn strip_parentdirs(&self, remove_all: bool) -> PathBuf {
1385		if self.as_os_str().is_empty() || (!remove_all && self.is_absolute()) {
1386			return self.to_owned();
1387		}
1388		let mut at_start = true;
1389		let mut segments: Vec<OsString> = vec![];
1390		for component in self.components() {
1391			match component {
1392				PathComponent::Prefix(_) |
1393				PathComponent::RootDir   |
1394				PathComponent::CurDir    |
1395				PathComponent::Normal(_) => {
1396					segments.push(component.as_os_str().to_os_string());
1397					at_start = false;
1398				},
1399				PathComponent::ParentDir => {
1400					if !remove_all && !at_start {
1401						segments.push(component.as_os_str().to_os_string());
1402					}
1403				},
1404			}
1405		}
1406		segments.iter().collect()
1407	}
1408	
1409	//		strip_root															
1410	fn strip_root(&self) -> PathBuf {
1411		if self.as_os_str().is_empty() || self.is_relative() {
1412			return self.to_owned();
1413		}
1414		let mut segments: Vec<OsString> = vec![];
1415		for component in self.components() {
1416			match component {
1417				PathComponent::Prefix(_) |
1418				PathComponent::RootDir   => {},
1419				PathComponent::CurDir    |
1420				PathComponent::ParentDir |
1421				PathComponent::Normal(_) => {
1422					segments.push(component.as_os_str().to_os_string());
1423				},
1424			}
1425		}
1426		if cfg!(windows) {
1427			segments.iter()
1428				.collect::<PathBuf>()
1429				.to_str()
1430				.map_or_else(PathBuf::new, |s| PathBuf::from(s.trim_start_matches('\\')))
1431		} else {
1432			segments.iter().collect()
1433		}
1434	}
1435}
1436
1437