use std::marker::PhantomData;
use super::*;
macro_rules! literal {
($($type:ty,)*) => {
$(
impl Bake for $type {
fn bake(&self, _: &CrateEnv) -> TokenStream {
quote! {
#self
}
}
}
impl BakeSize for $type {
fn borrows_size(&self) -> usize {
0
}
}
)*
}
}
literal!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, f32, f64, char, bool,);
#[test]
fn literal() {
test_bake!(u16, const, 3849u16);
assert_eq!(42u16.borrows_size(), 0);
}
impl Bake for &str {
fn bake(&self, _: &CrateEnv) -> TokenStream {
quote! {
#self
}
}
}
impl BakeSize for &str {
fn borrows_size(&self) -> usize {
self.len()
}
}
impl<T> Bake for &T
where
T: Bake,
{
fn bake(&self, ctx: &CrateEnv) -> TokenStream {
let t = <T as Bake>::bake(*self, ctx);
quote! {
&#t
}
}
}
impl<T> BakeSize for &T
where
T: BakeSize,
{
fn borrows_size(&self) -> usize {
core::mem::size_of_val::<T>(*self) + (*self).borrows_size()
}
}
#[test]
fn r#ref() {
test_bake!(&f32, const, &934.34f32);
assert_eq!(BakeSize::borrows_size(&&934.34f32), 4);
}
impl<T> Bake for &[T]
where
T: Bake,
{
fn bake(&self, ctx: &CrateEnv) -> TokenStream {
if core::mem::size_of::<T>() == core::mem::size_of::<u8>()
&& core::any::type_name::<T>() == core::any::type_name::<u8>()
{
let byte_string = proc_macro2::Literal::byte_string(unsafe {
core::slice::from_raw_parts(self.as_ptr() as *const u8, self.len())
});
return quote!(#byte_string);
}
let data = self.iter().map(|d| d.bake(ctx));
quote! {
&[#(#data),*]
}
}
}
impl<T> BakeSize for &[T]
where
T: BakeSize,
{
fn borrows_size(&self) -> usize {
std::mem::size_of_val(*self) + self.iter().map(BakeSize::borrows_size).sum::<usize>()
}
}
#[test]
fn slice() {
let slice: &[bool] = &[];
assert_eq!(Bake::bake(&slice, &Default::default()).to_string(), "& []");
assert_eq!(BakeSize::borrows_size(&slice), 0);
let slice: &[bool] = &[true];
assert_eq!(
Bake::bake(&slice, &Default::default()).to_string(),
"& [true]",
);
assert_eq!(BakeSize::borrows_size(&slice), 1);
let slice: &[bool] = &[true, false];
assert_eq!(
Bake::bake(&slice, &Default::default()).to_string(),
"& [true , false]",
);
assert_eq!(BakeSize::borrows_size(&slice), 2);
let slice: &[u8] = b"hello";
assert_eq!(
Bake::bake(&slice, &Default::default()).to_string(),
r#"b"hello""#,
);
assert_eq!(BakeSize::borrows_size(&slice), 5);
}
impl<T, const N: usize> Bake for [T; N]
where
T: Bake,
{
fn bake(&self, ctx: &CrateEnv) -> TokenStream {
if core::mem::size_of::<T>() == core::mem::size_of::<u8>()
&& core::any::type_name::<T>() == core::any::type_name::<u8>()
{
let byte_string = proc_macro2::Literal::byte_string(unsafe {
core::slice::from_raw_parts(self.as_ptr() as *const u8, N)
});
return quote!(*#byte_string);
}
let data = self.iter().map(|d| d.bake(ctx));
quote! {
[#(#data),*]
}
}
}
impl<T, const N: usize> BakeSize for [T; N]
where
T: BakeSize,
{
fn borrows_size(&self) -> usize {
self.iter().map(BakeSize::borrows_size).sum()
}
}
#[test]
fn array() {
test_bake!([bool; 0], const, []);
test_bake!([bool; 1], const, [true]);
test_bake!([bool; 2], const, [true, false]);
assert_eq!(BakeSize::borrows_size(&["hello", "world"]), 10);
test_bake!([u8; 5], const, *b"hello");
assert_eq!(BakeSize::borrows_size(b"hello"), 0);
}
impl<T> Bake for Option<T>
where
T: Bake,
{
fn bake(&self, ctx: &CrateEnv) -> TokenStream {
match self {
None => quote! { None },
Some(t) => {
let t = t.bake(ctx);
quote! {
Some(#t)
}
}
}
}
}
impl<T> BakeSize for Option<T>
where
T: BakeSize,
{
fn borrows_size(&self) -> usize {
self.as_ref()
.map(BakeSize::borrows_size)
.unwrap_or_default()
}
}
#[test]
fn option() {
test_bake!(Option<&'static str>, const, Some("hello"));
assert_eq!(BakeSize::borrows_size(&Some("hello")), 5);
test_bake!(Option<&'static str>, const, None);
assert_eq!(BakeSize::borrows_size(&None::<&'static str>), 0);
}
impl<T, E> Bake for Result<T, E>
where
T: Bake,
E: Bake,
{
fn bake(&self, ctx: &CrateEnv) -> TokenStream {
match self {
Ok(ok) => {
let ok = ok.bake(ctx);
quote! { Ok(#ok) }
}
Err(e) => {
let e = e.bake(ctx);
quote! {
Err(#e)
}
}
}
}
}
impl<T, E> BakeSize for Result<T, E>
where
T: BakeSize,
E: BakeSize,
{
fn borrows_size(&self) -> usize {
self.as_ref()
.map_or_else(BakeSize::borrows_size, BakeSize::borrows_size)
}
}
#[test]
fn result() {
test_bake!(Result<&'static str, ()>, const, Ok("hello"));
assert_eq!(BakeSize::borrows_size(&Ok::<_, ()>("hello")), 5);
test_bake!(Result<&'static str, ()>, const, Err(()));
assert_eq!(BakeSize::borrows_size(&Err::<&'static str, _>("hi")), 2);
}
macro_rules! tuple {
($ty:ident, $ident:ident) => {
impl<$ty> Bake for ($ty,) where $ty: Bake {
fn bake(&self, ctx: &CrateEnv) -> TokenStream {
let $ident = self.0.bake(ctx);
quote! {
(#$ident,)
}
}
}
impl<$ty> BakeSize for ($ty,) where $ty: BakeSize {
fn borrows_size(&self) -> usize {
self.0.borrows_size()
}
}
};
($($ty:ident, $ident:ident),*) => {
impl<$($ty),*> Bake for ($($ty,)*) where $($ty: Bake),* {
fn bake(&self, _ctx: &CrateEnv) -> TokenStream {
let ($($ident,)*) = self;
$(
let $ident = $ident.bake(_ctx);
)*
quote! {
($(#$ident),*)
}
}
}
impl<$($ty),*> BakeSize for ($($ty,)*) where $($ty: BakeSize),* {
fn borrows_size(&self) -> usize {
let ($($ident,)*) = self;
#[allow(unused_mut)]
let mut r = 0;
$(
r += BakeSize::borrows_size($ident);
)*
r
}
}
}
}
tuple!();
tuple!(A, a);
tuple!(A, a, B, b);
tuple!(A, a, B, b, C, c);
tuple!(A, a, B, b, C, c, D, d);
tuple!(A, a, B, b, C, c, D, d, E, e);
tuple!(A, a, B, b, C, c, D, d, E, e, F, f);
tuple!(A, a, B, b, C, c, D, d, E, e, F, f, G, g);
tuple!(A, a, B, b, C, c, D, d, E, e, F, f, G, g, H, h);
tuple!(A, a, B, b, C, c, D, d, E, e, F, f, G, g, H, h, I, i);
tuple!(A, a, B, b, C, c, D, d, E, e, F, f, G, g, H, h, I, i, J, j);
#[test]
fn tuple() {
test_bake!((), const, ());
assert_eq!(BakeSize::borrows_size(&()), 0);
test_bake!((u8,), const, (0u8,));
assert_eq!(BakeSize::borrows_size(&("hi",)), 2);
test_bake!((u8, i8), const, (0u8, 0i8));
assert_eq!(BakeSize::borrows_size(&("hi", 8u8)), 2);
}
impl<T: ?Sized> Bake for PhantomData<T> {
fn bake(&self, _ctx: &CrateEnv) -> TokenStream {
quote! {
core::marker::PhantomData
}
}
}
impl<T: ?Sized> BakeSize for PhantomData<T> {
fn borrows_size(&self) -> usize {
0
}
}
#[test]
fn phantom_data() {
test_bake!(PhantomData<usize>, const, core::marker::PhantomData);
}