mirror of
https://github.com/nushell/nushell.git
synced 2025-08-17 18:21:06 +02:00
Add well-optimized string types (#16446)
This commit is contained in:
32
Cargo.lock
generated
32
Cargo.lock
generated
@ -815,6 +815,15 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "buf-trait"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "21eaafc770e8c073d6c3facafe7617e774305d4954aa6351b9c452eb37ee17b4"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy 0.7.35",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.16.0"
|
version = "3.16.0"
|
||||||
@ -878,6 +887,15 @@ version = "1.3.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2e93abca9e28e0a1b9877922aacb20576e05d4679ffa78c3d6dc22a26a216659"
|
checksum = "2e93abca9e28e0a1b9877922aacb20576e05d4679ffa78c3d6dc22a26a216659"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteyarn"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b93e51d26468a15ea59f8525e0c13dc405db43e644a0b1e6d44346c72cf4cf7b"
|
||||||
|
dependencies = [
|
||||||
|
"buf-trait",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "calamine"
|
name = "calamine"
|
||||||
version = "0.28.0"
|
version = "0.28.0"
|
||||||
@ -3048,6 +3066,18 @@ version = "1.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lean_string"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75e49998bd00bfb567a44a6d3c3d1a970ce35665b5ebf68d64e5e96410be58ae"
|
||||||
|
dependencies = [
|
||||||
|
"castaway",
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.174"
|
version = "0.2.174"
|
||||||
@ -4234,9 +4264,11 @@ dependencies = [
|
|||||||
name = "nu-utils"
|
name = "nu-utils"
|
||||||
version = "0.106.2"
|
version = "0.106.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"byteyarn",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
"crossterm_winapi",
|
"crossterm_winapi",
|
||||||
"fancy-regex",
|
"fancy-regex",
|
||||||
|
"lean_string",
|
||||||
"log",
|
"log",
|
||||||
"lscolors",
|
"lscolors",
|
||||||
"nix 0.29.0",
|
"nix 0.29.0",
|
||||||
|
@ -72,6 +72,7 @@ brotli = "7.0"
|
|||||||
byteorder = "1.5"
|
byteorder = "1.5"
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
bytesize = "1.3.3"
|
bytesize = "1.3.3"
|
||||||
|
byteyarn = "0.5"
|
||||||
calamine = "0.28"
|
calamine = "0.28"
|
||||||
chardetng = "0.1.17"
|
chardetng = "0.1.17"
|
||||||
chrono = { default-features = false, version = "0.4.34" }
|
chrono = { default-features = false, version = "0.4.34" }
|
||||||
@ -100,6 +101,7 @@ indicatif = "0.17"
|
|||||||
interprocess = "2.2.0"
|
interprocess = "2.2.0"
|
||||||
is_executable = "1.0"
|
is_executable = "1.0"
|
||||||
itertools = "0.14"
|
itertools = "0.14"
|
||||||
|
lean_string = { version = "0.5", features = ["serde"] }
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
libproc = "0.14"
|
libproc = "0.14"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
@ -17,8 +17,10 @@ bench = false
|
|||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
byteyarn.workspace = true
|
||||||
crossterm = { workspace = true, optional = true }
|
crossterm = { workspace = true, optional = true }
|
||||||
fancy-regex = { workspace = true }
|
fancy-regex = { workspace = true }
|
||||||
|
lean_string.workspace = true
|
||||||
lscolors = { workspace = true, default-features = false, features = ["nu-ansi-term"] }
|
lscolors = { workspace = true, default-features = false, features = ["nu-ansi-term"] }
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
num-format = { workspace = true }
|
num-format = { workspace = true }
|
||||||
|
@ -8,6 +8,7 @@ pub mod float;
|
|||||||
pub mod locale;
|
pub mod locale;
|
||||||
mod quoting;
|
mod quoting;
|
||||||
mod shared_cow;
|
mod shared_cow;
|
||||||
|
pub mod strings;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
pub use locale::get_system_locale;
|
pub use locale::get_system_locale;
|
||||||
|
40
crates/nu-utils/src/strings/mod.rs
Normal file
40
crates/nu-utils/src/strings/mod.rs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
//! String utility types with specific semantics.
|
||||||
|
//!
|
||||||
|
//! This module provides additional string types optimized for certain use-cases, offering
|
||||||
|
//! alternatives to standard [`String`] or [`&str`](str) when specific performance characteristics
|
||||||
|
//! are desired.
|
||||||
|
//!
|
||||||
|
//! The pipeline-based, functional programming model, we use, leads to frequent string operations
|
||||||
|
//! that involve immutability and often copying.
|
||||||
|
//! These specialized string types can provide performance and memory benefits for these cases.
|
||||||
|
//!
|
||||||
|
//! ## Choosing a String Type: `SharedString` vs. `UniqueString`
|
||||||
|
//!
|
||||||
|
//! ### Use [`SharedString`] when:
|
||||||
|
//!
|
||||||
|
//! `SharedString` is an owned, immutable string type optimized for **frequent cloning and sharing**.
|
||||||
|
//! Cloning it is very inexpensive (a pointer copy and atomic reference count increment), avoiding
|
||||||
|
//! deep copies of the string data.
|
||||||
|
//! It also benefits from Small String Optimization (SSO) and static string re-use.
|
||||||
|
//!
|
||||||
|
//! **Ideal for:** Strings that need to be duplicated or passed by value frequently across
|
||||||
|
//! pipeline stages or within complex data structures, where many references to the same string
|
||||||
|
//! data are expected.
|
||||||
|
//!
|
||||||
|
//! ### Use [`UniqueString`] when:
|
||||||
|
//!
|
||||||
|
//! `UniqueString` is an owned, immutable string type optimized for
|
||||||
|
//! **strings that are primarily unique** or rarely cloned.
|
||||||
|
//! Cloning a `UniqueString` always involves copying the underlying string data.
|
||||||
|
//! Its advantage lies in avoiding the overhead of atomic reference counting.
|
||||||
|
//! It also benefits from Small String Optimization (SSO) and static string re-use.
|
||||||
|
//!
|
||||||
|
//! **Ideal for:** Strings that are created and consumed locally, or represent unique identifiers
|
||||||
|
//! that are not expected to be duplicated across many ownership boundaries.
|
||||||
|
//! When the cost of copying upon infrequent clones is acceptable.
|
||||||
|
|
||||||
|
mod shared;
|
||||||
|
mod unique;
|
||||||
|
|
||||||
|
pub use shared::SharedString;
|
||||||
|
pub use unique::UniqueString;
|
245
crates/nu-utils/src/strings/shared.rs
Normal file
245
crates/nu-utils/src/strings/shared.rs
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
use std::{
|
||||||
|
borrow::Borrow,
|
||||||
|
fmt::{Arguments, Debug, Display},
|
||||||
|
hash::Hash,
|
||||||
|
ops::Deref,
|
||||||
|
};
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// An owned, immutable string with compact storage and efficient cloning.
|
||||||
|
///
|
||||||
|
/// `SharedString` is designed for immutable strings that are frequently cloned and hold ownership.
|
||||||
|
/// It offers similar characteristics to [`Arc<str>`](std::sync::Arc) but with several key
|
||||||
|
/// optimizations for improved efficiency and memory usage:
|
||||||
|
///
|
||||||
|
/// - **Efficient Cloning:**
|
||||||
|
/// Cloning a `SharedString` is very inexpensive.
|
||||||
|
/// It typically involves just a pointer copy and an atomic reference count increment, without
|
||||||
|
/// copying the actual string data.
|
||||||
|
///
|
||||||
|
/// - **Small String Optimization (SSO):**
|
||||||
|
/// For shorter strings, the data is stored directly within the `SharedString` struct, keeping
|
||||||
|
/// the data on the stack and avoiding indirection.
|
||||||
|
///
|
||||||
|
/// - **Static String Re-use:**
|
||||||
|
/// Strings with a `'static` lifetime are directly referenced, avoiding unnecessary copies or
|
||||||
|
/// allocations.
|
||||||
|
///
|
||||||
|
/// - **Niche Optimization:**
|
||||||
|
/// `SharedString` allows niche-optimization, meaning that [`Option<SharedString>`] has the same
|
||||||
|
/// memory footprint as `SharedString`.
|
||||||
|
///
|
||||||
|
/// - **Compact Size:**
|
||||||
|
/// On 64-bit systems, `SharedString` is 16 bytes.
|
||||||
|
/// This is achieved by disregarding the capacity of a `String` since we only hold the string as
|
||||||
|
/// immutable.
|
||||||
|
///
|
||||||
|
/// Internally, `SharedString` is powered by [`lean_string::LeanString`], which provides the
|
||||||
|
/// underlying implementation for these optimizations.
|
||||||
|
pub struct SharedString(lean_string::LeanString);
|
||||||
|
|
||||||
|
const _: () = const {
|
||||||
|
assert!(size_of::<SharedString>() == size_of::<[usize; 2]>());
|
||||||
|
assert!(size_of::<SharedString>() == size_of::<Option<SharedString>>());
|
||||||
|
};
|
||||||
|
|
||||||
|
impl SharedString {
|
||||||
|
/// Returns a string slice containing the entire `SharedString`.
|
||||||
|
#[inline]
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
self.0.as_str()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a byte slice of this `SharedString`'s contents.
|
||||||
|
#[inline]
|
||||||
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
|
self.0.as_bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the length of this `SharedString`, in bytes.
|
||||||
|
#[inline]
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the `SharedString` has a length of 0, `false` otherwise.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.0.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a `SharedString` by taking ownership of an allocation.
|
||||||
|
#[inline]
|
||||||
|
pub fn from_string(string: String) -> Self {
|
||||||
|
Self(lean_string::LeanString::from(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a `SharedString` pointing to the given slice, without copying.
|
||||||
|
///
|
||||||
|
/// By using this function instead of [`from_string`](Self::from_string), we can avoid any
|
||||||
|
/// copying and always refer to the provided static string slice.
|
||||||
|
#[inline]
|
||||||
|
pub fn from_static_str(string: &'static str) -> Self {
|
||||||
|
Self(lean_string::LeanString::from_static_str(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builds a new `SharedString` from the given formatting arguments.
|
||||||
|
///
|
||||||
|
/// You can get an [`Arguments`] instance by calling [`format_args!`].
|
||||||
|
/// This function is used when using [`sformat!`](crate::sformat) instead of [`format!`] to
|
||||||
|
/// create a `SharedString`.
|
||||||
|
#[inline]
|
||||||
|
pub fn from_fmt(arguments: Arguments) -> Self {
|
||||||
|
match arguments.as_str() {
|
||||||
|
Some(static_str) => Self::from_static_str(static_str),
|
||||||
|
None => Self::from_string(std::fmt::format(arguments)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<str> for SharedString {
|
||||||
|
#[inline]
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
self.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Borrow<str> for SharedString {
|
||||||
|
#[inline]
|
||||||
|
fn borrow(&self) -> &str {
|
||||||
|
self.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for SharedString {
|
||||||
|
#[inline]
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self(self.0.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for SharedString {
|
||||||
|
#[inline]
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
Debug::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SharedString {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for SharedString {
|
||||||
|
type Target = str;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for SharedString {
|
||||||
|
#[inline]
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
Display::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for SharedString {}
|
||||||
|
|
||||||
|
impl From<String> for SharedString {
|
||||||
|
#[inline]
|
||||||
|
fn from(string: String) -> Self {
|
||||||
|
Self::from_string(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&'static str> for SharedString {
|
||||||
|
#[inline]
|
||||||
|
fn from(string: &'static str) -> Self {
|
||||||
|
Self::from_static_str(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for SharedString {
|
||||||
|
#[inline]
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.0.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for SharedString {
|
||||||
|
#[inline]
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
self.0.cmp(&other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for SharedString {
|
||||||
|
#[inline]
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.0 == other.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for SharedString {
|
||||||
|
#[inline]
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for SharedString {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
self.0.serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for SharedString {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
Ok(Self(lean_string::LeanString::deserialize(deserializer)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A macro for creating [`SharedString`] instances from format arguments.
|
||||||
|
///
|
||||||
|
/// This macro works similarly to [`format!`] but returns a [`SharedString`] instead of a [`String`].
|
||||||
|
/// It attempts to optimize for `'static` string literals.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! sformat {
|
||||||
|
($fmt:expr) => {
|
||||||
|
$crate::strings::SharedString::from_fmt(::std::format_args!($fmt))
|
||||||
|
};
|
||||||
|
|
||||||
|
($fmt:expr, $($args:tt)*) => {
|
||||||
|
$crate::strings::SharedString::from_fmt(::std::format_args!($fmt, $($args)*))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn macro_works() {
|
||||||
|
assert!(sformat!("").is_empty());
|
||||||
|
assert_eq!(
|
||||||
|
sformat!("Hello World"),
|
||||||
|
SharedString::from_static_str("Hello World")
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
sformat!("Hello {}", "World"),
|
||||||
|
SharedString::from_static_str("Hello World")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
243
crates/nu-utils/src/strings/unique.rs
Normal file
243
crates/nu-utils/src/strings/unique.rs
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
use std::{
|
||||||
|
borrow::Borrow,
|
||||||
|
fmt::{Arguments, Debug, Display},
|
||||||
|
hash::Hash,
|
||||||
|
ops::Deref,
|
||||||
|
};
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// An owned, immutable string with compact storage.
|
||||||
|
///
|
||||||
|
/// `UniqueString` is designed for immutable strings that are not frequently cloned and hold ownership.
|
||||||
|
/// It offers similar characteristics to `Box<str>` but with several key
|
||||||
|
/// optimizations for improved efficiency and memory usage:
|
||||||
|
///
|
||||||
|
/// - **Efficient for Unique Strings:**
|
||||||
|
/// When strings are not frequently cloned, `UniqueString` can be more performant than
|
||||||
|
/// reference-counted alternatives like [`SharedString`](super::SharedString) as it avoids the
|
||||||
|
/// overhead of atomic reference counting.
|
||||||
|
///
|
||||||
|
/// - **Small String Optimization (SSO):**
|
||||||
|
/// For shorter strings, the data is stored directly within the `UniqueString` struct, keeping
|
||||||
|
/// the data on the stack and avoiding indirection.
|
||||||
|
///
|
||||||
|
/// - **Static String Re-use:**
|
||||||
|
/// Strings with a `'static` lifetime are directly referenced, avoiding unnecessary copies or
|
||||||
|
/// allocations.
|
||||||
|
///
|
||||||
|
/// - **Niche Optimization:**
|
||||||
|
/// `UniqueString` allows niche-optimization, meaning that [`Option<UniqueString>`] has the same
|
||||||
|
/// memory footprint as `UniqueString`.
|
||||||
|
///
|
||||||
|
/// - **Compact Size:**
|
||||||
|
/// On 64-bit systems, `UniqueString` is 16 bytes.
|
||||||
|
/// This is achieved by disregarding the capacity of a `String` since we only hold the string as
|
||||||
|
/// immutable.
|
||||||
|
///
|
||||||
|
/// Internally, `UniqueString` is powered by [`byteyarn::Yarn`], which provides the
|
||||||
|
/// underlying implementation for these optimizations.
|
||||||
|
pub struct UniqueString(byteyarn::Yarn);
|
||||||
|
|
||||||
|
const _: () = const {
|
||||||
|
assert!(size_of::<UniqueString>() == size_of::<[usize; 2]>());
|
||||||
|
assert!(size_of::<UniqueString>() == size_of::<Option<UniqueString>>());
|
||||||
|
};
|
||||||
|
|
||||||
|
impl UniqueString {
|
||||||
|
/// Returns a string slice containing the entire `UniqueString`.
|
||||||
|
#[inline]
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
self.0.as_str()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a byte slice of this `UniqueString`'s contents.
|
||||||
|
#[inline]
|
||||||
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
|
self.0.as_bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the length of this `UniqueString`, in bytes.
|
||||||
|
#[inline]
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.0.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the `UniqueString` has a length of 0, `false` otherwise.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.0.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a `UniqueString` by taking ownership of an allocation.
|
||||||
|
#[inline]
|
||||||
|
pub fn from_string(string: String) -> Self {
|
||||||
|
Self(byteyarn::Yarn::from_string(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a `UniqueString` pointing to the given slice, without copying.
|
||||||
|
///
|
||||||
|
/// By using this function instead of [`from_string`](Self::from_string), we can avoid any
|
||||||
|
/// copying and always refer to the provided static string slice.
|
||||||
|
#[inline]
|
||||||
|
pub fn from_static_str(string: &'static str) -> Self {
|
||||||
|
Self(byteyarn::Yarn::from_static(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builds a new `UniqueString` from the given formatting arguments.
|
||||||
|
///
|
||||||
|
/// You can get an [`Arguments`] instance by calling [`format_args!`].
|
||||||
|
/// This function is used when using [`uformat!`](crate::uformat) instead of [`format!`] to
|
||||||
|
/// create a `UniqueString`.
|
||||||
|
#[inline]
|
||||||
|
pub fn from_fmt(arguments: Arguments) -> Self {
|
||||||
|
Self(byteyarn::Yarn::from_fmt(arguments))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<str> for UniqueString {
|
||||||
|
#[inline]
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
self.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Borrow<str> for UniqueString {
|
||||||
|
#[inline]
|
||||||
|
fn borrow(&self) -> &str {
|
||||||
|
self.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for UniqueString {
|
||||||
|
#[inline]
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self(self.0.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for UniqueString {
|
||||||
|
#[inline]
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
Debug::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for UniqueString {
|
||||||
|
#[inline]
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for UniqueString {
|
||||||
|
type Target = str;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for UniqueString {
|
||||||
|
#[inline]
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
Display::fmt(&self.0, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for UniqueString {}
|
||||||
|
|
||||||
|
impl From<String> for UniqueString {
|
||||||
|
#[inline]
|
||||||
|
fn from(string: String) -> Self {
|
||||||
|
Self::from_string(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&'static str> for UniqueString {
|
||||||
|
#[inline]
|
||||||
|
fn from(string: &'static str) -> Self {
|
||||||
|
Self::from_static_str(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for UniqueString {
|
||||||
|
#[inline]
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.0.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for UniqueString {
|
||||||
|
#[inline]
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
self.0.cmp(&other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for UniqueString {
|
||||||
|
#[inline]
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.0 == other.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for UniqueString {
|
||||||
|
#[inline]
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for UniqueString {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
self.0.serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for UniqueString {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let s = String::deserialize(deserializer)?;
|
||||||
|
Ok(Self::from_string(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A macro for creating [`UniqueString`] instances from format arguments.
|
||||||
|
///
|
||||||
|
/// This macro works similarly to [`format!`] but returns a [`UniqueString`] instead of a [`String`].
|
||||||
|
/// It attempts to optimize for `'static` string literals.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! uformat {
|
||||||
|
($fmt:expr) => {
|
||||||
|
$crate::strings::UniqueString::from_fmt(::std::format_args!($fmt))
|
||||||
|
};
|
||||||
|
|
||||||
|
($fmt:expr, $($args:tt)*) => {
|
||||||
|
$crate::strings::UniqueString::from_fmt(::std::format_args!($fmt, $($args)*))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn macro_works() {
|
||||||
|
assert!(uformat!("").is_empty());
|
||||||
|
assert_eq!(
|
||||||
|
uformat!("Hello World"),
|
||||||
|
UniqueString::from_static_str("Hello World")
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
uformat!("Hello {}", "World"),
|
||||||
|
UniqueString::from_static_str("Hello World")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user