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