Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
GnomedDev committed May 7, 2024
1 parent c2adf56 commit 9c5f17e
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1 @@
/target
target
49 changes: 47 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ categories = ["value-formatting", "no-std::no-alloc"]
keywords = ["arraystring", "formatting", "no-alloc", "optimisation"]

[dependencies]
to-arraystring-macros = { path = "to-arraystring-macros" }
arrayvec = { version = "0.7", default-features = false }
itoa = "1.0.10"
ryu = "1.0.17"
Expand Down
14 changes: 14 additions & 0 deletions src/format.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use arrayvec::ArrayString;
use to_arraystring_macros::aformat;

extern crate self as to_arraystring;

fn test_aformat() {
let name = ArrayString::<5>::from("Daisy").unwrap();
let age = 18_u8;

assert_eq!(
&aformat!("Hello {}, you are {} years old", age),
"Hello Daisy, you are 18 years old"
);
}
19 changes: 19 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#![no_std]
#![warn(clippy::pedantic)]
#![feature(type_alias_impl_trait)]

#[cfg(any(doc, test))]
extern crate alloc;
Expand All @@ -18,10 +19,17 @@ use arrayvec::ArrayString;
use macros::{gen_fmt_to_buf, gen_impl};

mod erased;
mod format;
mod macros;

/// A no-alloc version of [`ToString`] implemented for bool/integer/float types formatting into an [`ArrayString`].
pub trait ToArrayString: Copy {
/// The maximum length that Self can be formatted into. This is used for the capacity generic of [`ArrayString`].
///
/// # Note for implementors
/// This must match the capacity generic used in [`ArrayString`], otherwise logic bugs and panics may occur.
const MAX_LENGTH: usize;

/// An associated type to turn [`ArrayString`]'s const generic into a type generic,
/// working around limitations of the current type system.
///
Expand All @@ -32,7 +40,17 @@ pub trait ToArrayString: Copy {
fn to_arraystring(self) -> Self::ArrayString;
}

impl<const MAX_LENGTH: usize> ToArrayString for ArrayString<MAX_LENGTH> {
const MAX_LENGTH: usize = MAX_LENGTH;
type ArrayString = ArrayString<MAX_LENGTH>;

fn to_arraystring(self) -> Self::ArrayString {
self
}
}

impl ToArrayString for char {
const MAX_LENGTH: usize = 4;
type ArrayString = ArrayString<4>;

fn to_arraystring(self) -> Self::ArrayString {
Expand All @@ -44,6 +62,7 @@ impl ToArrayString for char {
}

impl ToArrayString for bool {
const MAX_LENGTH: usize = 5;
type ArrayString = ArrayString<5>;

fn to_arraystring(self) -> Self::ArrayString {
Expand Down
2 changes: 2 additions & 0 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ macro_rules! gen_impl {
macro_rules! $name {
(ToArrayString<$len:literal> for $type:ty) => {
impl ToArrayString for $type {
const MAX_LENGTH: usize = $len;
type ArrayString = ArrayString<$len>;

fn to_arraystring(self) -> Self::ArrayString {
$body(self)
}
Expand Down
47 changes: 47 additions & 0 deletions to-arraystring-macros/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions to-arraystring-macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "to-arraystring-macros"
version = "0.1.0"
edition = "2021"

[lib]
proc-macro = true

[dependencies]
syn = { version = "2.0.61", features = ["parsing", "proc-macro"], default-features = false }
proc-macro2 = "1.0.82"
quote = "1.0.36"
77 changes: 77 additions & 0 deletions to-arraystring-macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use std::error::Error;

use proc_macro2::TokenStream;
use quote::{format_ident, quote, ToTokens as _};
use syn::{punctuated::Punctuated, Ident, Result, Token};

struct Arguments {
format_str: String,
format_str_span: proc_macro2::Span,
arguments: Punctuated<Ident, Token![,]>,
}

impl syn::parse::Parse for Arguments {
fn parse(input: syn::parse::ParseStream) -> Result<Self> {
let format_str = input.parse::<syn::LitStr>()?;
input.parse::<Option<Token![,]>>()?;

Ok(Self {
format_str: format_str.value(),
format_str_span: format_str.span(),
arguments: input.parse_terminated(Ident::parse, Token![,])?,
})
}
}

fn aformat_impl(tokens: proc_macro::TokenStream) -> Result<TokenStream> {
let Arguments {
arguments,
format_str,
format_str_span,
} = syn::parse(tokens)?;

if arguments.is_empty() {
let str_length = format_str.len();
return Ok(quote!(::arrayvec::ArrayString::<#str_length>::from(#format_str).unwrap()));
}

let arg_type_idents = (0..arguments.len()).map(|i| format_ident!("ARG_TYPE_{i}"));
let arg_type_idents_clone = arg_type_idents.clone();
let arguments_iter = arguments.into_iter();
let arguments_iter_clone = arguments_iter.clone();

let split_fmt_str = format_str.split("{}");

let out = quote!(
{
use to_arraystring::{ArrayString, ToArrayString};

#(
type #arg_type_idents = impl ToArrayString;
let _: #arg_type_idents = #arguments_iter;
)*

const OUT_CAP: usize = #(
<#arg_type_idents_clone as ToArrayString>::MAX_LENGTH
)+*;

let mut out_buffer = ArrayString::<OUT_CAP>::new();
#(
out_buffer.push_str(#split_fmt_str);
out_buffer.push_str(&#arguments_iter_clone.to_arraystring());
);*

out_buffer
}
);

Ok(out)
}

#[proc_macro]
pub fn aformat(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
match aformat_impl(tokens) {
Ok(tokens) => tokens.into(),
Err(err) => err.into_compile_error().into(),
}
}

0 comments on commit 9c5f17e

Please sign in to comment.