Skip to content

Commit

Permalink
Merge pull request #57 from jewlexx/sized-string
Browse files Browse the repository at this point in the history
add sized string struct
  • Loading branch information
jewlexx authored Dec 11, 2024
2 parents 829a5e9 + ab36c94 commit c5865a8
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 1 deletion.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,12 @@ windows = { version = "0.58", features = [
nix = { version = "0.29", features = ["user"] }

[features]
all = ["macros", "network", "root", "std", "traits"]
all = ["macros", "network", "root", "std", "traits", "sized_string"]
default = ["all"]
macros = ["quork-proc"]
network = []
root = ["std"]
sized_string = []
std = []
traits = []

Expand Down
13 changes: 13 additions & 0 deletions quork-proc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mod const_str;
mod enum_list;
mod from_tuple;
mod new;
mod sized_string;
mod strip_enum;
mod time_fn;
mod trim_lines;
Expand Down Expand Up @@ -129,3 +130,15 @@ pub fn lstrip_lines(input: TokenStream) -> proc_macro::TokenStream {

trim_lines::trim_lines(&literal, &trim_lines::Alignment::Left).into()
}

/// Creates a [`SizedString`] from a string literal
///
/// # Examples
///
/// ```rust
/// let s = sized_string!("Hello, World!");
/// ```
#[proc_macro]
pub fn sized_string(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
sized_string::sized_string(&syn::parse_macro_input!(input as LitStr)).into()
}
27 changes: 27 additions & 0 deletions quork-proc/src/sized_string.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use proc_macro2::Span;
use proc_macro_crate::FoundCrate;
use syn::Ident;

pub fn sized_string(input: &syn::LitStr) -> proc_macro2::TokenStream {
let length = input.value().chars().count();
let chars = input
.value()
.chars()
.map(|c| quote::quote! { #c as u8 })
.collect::<Vec<_>>();

let quork_crate =
match proc_macro_crate::crate_name("quork").expect("quork is present in `Cargo.toml`") {
FoundCrate::Itself => quote::quote! { crate },
FoundCrate::Name(name) => {
let ident = Ident::new(&name, Span::call_site());
quote::quote! { #ident }
}
};

let output = quote::quote! {
#quork_crate::sized_string::SizedString::<#length>::new([#(#chars),*])
};

output
}
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ pub mod traits;
#[cfg(feature = "network")]
pub mod network;

#[cfg(feature = "sized_string")]
pub mod sized_string;

cfg_if::cfg_if! {
if #[cfg(all(feature = "root", feature = "std"))] {
pub mod root;
Expand Down
79 changes: 79 additions & 0 deletions src/sized_string.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//! A sized, stack allocated string type.
//!
//! This is useful for when you need a string to be stack allocated, but you also need it to be sized (i.e not a reference to a [`str`]).
//!
//! Especially when using shared memory this can be useful as the actual string will be stored in shared memory, rather than just the pointer to the string.
use std::ops::Deref;

#[derive(Copy, Clone, PartialEq, Eq, Hash)]
/// A sized, stack allocated string type.
///
/// This is useful for when you need a string to be stack allocated, but you also need it to be sized (i.e not a reference to a [`str`]).
///
/// Especially when using shared memory this can be useful as the actual string will be stored in shared memory, rather than just the pointer to the string.
pub struct SizedString<const N: usize>([u8; N]);

impl<const N: usize> SizedString<N> {
#[must_use]
/// Construct a new [`SizedString`] from a byte array
pub const fn new(bytes: [u8; N]) -> Self {
Self(bytes)
}

#[must_use]
/// Get the string as a [`str`]
pub const fn as_str(&self) -> &str {
unsafe { std::str::from_utf8_unchecked(&self.0) }
}
}

impl<const N: usize> Deref for SizedString<N> {
type Target = str;

fn deref(&self) -> &Self::Target {
unsafe { std::str::from_utf8_unchecked(&self.0) }
}
}

impl<const N: usize> AsRef<str> for SizedString<N> {
fn as_ref(&self) -> &str {
self
}
}

impl<const N: usize, T> AsRef<T> for SizedString<N>
where
str: std::convert::AsRef<T>,
{
fn as_ref(&self) -> &T {
let s: &str = self.as_ref();
s.as_ref()
}
}

mod string_trait_impls {
use std::fmt::Debug;

use super::SizedString;

impl<const N: usize> Debug for SizedString<N> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("SizedString").field(&self.as_str()).finish()
}
}
}

#[cfg(test)]
mod tests {

#[test]
fn test_sized_string() {
let s = quork_proc::sized_string!("hello world");

let s_str: &str = s.as_ref();

assert_eq!(s.len(), 11);
assert_eq!(s_str, "hello world");
}
}

0 comments on commit c5865a8

Please sign in to comment.