databake-0.2.0/.cargo_vcs_info.json0000644000000001540000000000100125670ustar { "git": { "sha1": "6bd4893cc44c2ca2718de47a119a31cc40045fe5" }, "path_in_vcs": "utils/databake" }databake-0.2.0/Cargo.toml0000644000000031310000000000100105630ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.71.1" name = "databake" version = "0.2.0" authors = ["The ICU4X Project Developers"] build = false include = [ "data/**/*", "src/**/*", "examples/**/*", "benches/**/*", "tests/**/*", "Cargo.toml", "LICENSE", "README.md", ] autobins = false autoexamples = false autotests = false autobenches = false description = "Trait that lets structs represent themselves as (const) Rust expressions" readme = "README.md" keywords = [ "zerocopy", "serialization", "const", "proc", ] categories = [ "rust-patterns", "memory-management", "development-tools::procedural-macro-helpers", "development-tools::build-utils", ] license = "Unicode-3.0" repository = "https://github.com/unicode-org/icu4x" [package.metadata.docs.rs] all-features = true [package.metadata.workspaces] independent = true [lib] name = "databake" path = "src/lib.rs" [dependencies.databake-derive] version = "0.2.0" optional = true default-features = false [dependencies.proc-macro2] version = "1.0.61" [dependencies.quote] version = "1.0.28" [features] derive = ["dep:databake-derive"] databake-0.2.0/Cargo.toml.orig000064400000000000000000000017031046102023000142470ustar 00000000000000# This file is part of ICU4X. For terms of use, please see the file # called LICENSE at the top level of the ICU4X source tree # (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). [package] name = "databake" description = "Trait that lets structs represent themselves as (const) Rust expressions" version = "0.2.0" categories = ["rust-patterns", "memory-management", "development-tools::procedural-macro-helpers", "development-tools::build-utils"] keywords = ["zerocopy", "serialization", "const", "proc"] authors.workspace = true edition.workspace = true include.workspace = true license.workspace = true repository.workspace = true rust-version.workspace = true [package.metadata.workspaces] independent = true [package.metadata.docs.rs] all-features = true [features] derive = ["dep:databake-derive"] [dependencies] proc-macro2 = { workspace = true } quote = { workspace = true } databake-derive = { workspace = true, optional = true} databake-0.2.0/LICENSE000064400000000000000000000042231046102023000123650ustar 00000000000000UNICODE LICENSE V3 COPYRIGHT AND PERMISSION NOTICE Copyright © 2020-2024 Unicode, Inc. NOTICE TO USER: Carefully read the following legal agreement. BY DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. Permission is hereby granted, free of charge, to any person obtaining a copy of data files and any associated documentation (the "Data Files") or software and any associated documentation (the "Software") to deal in the Data Files or Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, and/or sell copies of the Data Files or Software, and to permit persons to whom the Data Files or Software are furnished to do so, provided that either (a) this copyright and permission notice appear with all copies of the Data Files or Software, or (b) this copyright and permission notice appear in associated Documentation. THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA FILES OR SOFTWARE. Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in these Data Files or Software without prior written authorization of the copyright holder. SPDX-License-Identifier: Unicode-3.0 — Portions of ICU4X may have been adapted from ICU4C and/or ICU4J. ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others. databake-0.2.0/README.md000064400000000000000000000027111046102023000126370ustar 00000000000000# databake [![crates.io](https://img.shields.io/crates/v/databake)](https://crates.io/crates/databake) This crate allows data to write itself into Rust code (bake itself in). Types that implement the `Bake` trait can be written into Rust expressions, which allows using Rust code itself as a zero-overhead "serialization" strategy. ## Example ```rust use databake::*; use alloc::borrow::Cow; let data = [Some((18, Cow::Borrowed("hi")))]; assert_eq!( data.bake(&Default::default()).to_string(), r#"[Some ((18i32 , alloc :: borrow :: Cow :: Borrowed ("hi")))]"#, ); ``` ## Derive `Bake` can be automatically derived if the `derive` Cargo feature is enabled. ```rust use databake::*; #[derive(Bake)] #[databake(path = my_crate)] struct MyStruct { number: u32, string: &'static str, slice: &'static [bool], } #[derive(Bake)] #[databake(path = my_crate)] struct AnotherOne(MyStruct, char); ``` ## Testing The [`test_bake`] macro can be used to assert that a particular expression is a `Bake` fixed point. ```rust test_bake!( AnotherOne, const, crate::AnotherOne( crate::MyStruct { number: 17u32, string: "foo", slice: &[true, false], }, 'b', ), my_crate, ); ``` ## More Information For more information on development, authorship, contributing etc. please visit [`ICU4X home page`](https://github.com/unicode-org/icu4x). databake-0.2.0/src/alloc.rs000064400000000000000000000116121046102023000136070ustar 00000000000000// This file is part of ICU4X. For terms of use, please see the file // called LICENSE at the top level of the ICU4X source tree // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). use super::*; extern crate alloc; impl Bake for alloc::borrow::Cow<'_, T> where T: ?Sized + ToOwned, for<'a> &'a T: Bake, { fn bake(&self, ctx: &CrateEnv) -> TokenStream { ctx.insert("alloc"); let t = <&T as Bake>::bake(&&**self, ctx); quote! { alloc::borrow::Cow::Borrowed(#t) } } } impl BakeSize for alloc::borrow::Cow<'_, T> where T: ?Sized + ToOwned, for<'a> &'a T: BakeSize, { fn borrows_size(&self) -> usize { (&**self).borrows_size() } } #[test] fn cow() { test_bake!( alloc::borrow::Cow<'static, str>, const, alloc::borrow::Cow::Borrowed("hi"), alloc ); assert_eq!( BakeSize::borrows_size(&alloc::borrow::Cow::Borrowed("hi")), 2 ); assert_eq!( Bake::bake( &alloc::borrow::Cow::<'static, str>::Borrowed("hi"), &Default::default(), ) .to_string(), Bake::bake( &alloc::borrow::Cow::<'static, str>::Owned("hi".to_owned()), &Default::default(), ) .to_string(), ); assert_eq!( BakeSize::borrows_size(&alloc::borrow::Cow::<'static, str>::Owned("hi".to_owned())), 2 ); } impl Bake for Vec where T: Bake, { fn bake(&self, ctx: &CrateEnv) -> TokenStream { ctx.insert("alloc"); let data = self.iter().map(|d| d.bake(ctx)); quote! { alloc::vec![#(#data,)*] } } } #[test] fn vec() { test_bake!(Vec, alloc::vec![1u8, 2u8], alloc); } impl Bake for alloc::collections::BTreeSet where T: Bake, { fn bake(&self, ctx: &CrateEnv) -> TokenStream { ctx.insert("alloc"); let data = self.iter().map(|d| d.bake(ctx)); quote! { alloc::collections::BTreeSet::from([#(#data),*]) } } } #[test] fn btree_set() { test_bake!( alloc::collections::BTreeSet, alloc::collections::BTreeSet::from([1u8, 2u8]), alloc ); } impl Bake for alloc::collections::BTreeMap where K: Bake, V: Bake, { fn bake(&self, ctx: &CrateEnv) -> TokenStream { ctx.insert("alloc"); let data = self.iter().map(|(k, v)| { let k = k.bake(ctx); let v = v.bake(ctx); quote!((#k, #v)) }); quote! { alloc::collections::BTreeMap::from([#(#data),*]) } } } #[test] fn btree_map() { test_bake!( alloc::collections::BTreeMap, alloc::collections::BTreeMap::from([(1u8, 2u8), (2u8, 4u8)]), alloc ); } impl Bake for std::collections::HashSet where T: Bake, { fn bake(&self, ctx: &CrateEnv) -> TokenStream { ctx.insert("std"); let mut data = self.iter().map(|d| d.bake(ctx)).collect::>(); data.sort_unstable_by_key(|data| data.to_string()); quote! { std::collections::HashSet::from([#(#data),*]) } } } #[test] fn hash_set() { test_bake!( std::collections::HashSet, std::collections::HashSet::from([1u8, 2u8]), std ); } impl Bake for std::collections::HashMap where K: Bake, V: Bake, { fn bake(&self, ctx: &CrateEnv) -> TokenStream { ctx.insert("std"); let mut data = self .iter() .map(|(k, v)| { let k = k.bake(ctx); let v = v.bake(ctx); quote!((#k, #v)) }) .collect::>(); data.sort_unstable_by_key(|data| data.to_string()); quote! { std::collections::HashMap::from([#(#data),*]) } } } #[test] fn hash_map() { test_bake!( std::collections::HashMap, std::collections::HashMap::from([(1u8, 2u8), (2u8, 4u8)]), std ); } impl Bake for String { fn bake(&self, _: &CrateEnv) -> TokenStream { quote! { #self.to_owned() } } } #[test] fn string() { test_bake!(String, "hello".to_owned()); } macro_rules! smart_pointer { ($type:ty, $constuctor:path) => { impl Bake for $type where T: Bake, { fn bake(&self, ctx: &CrateEnv) -> TokenStream { ctx.insert("alloc"); let data = std::ops::Deref::deref(self).bake(ctx); quote! { $constuctor(#data) } } } }; } smart_pointer!(alloc::boxed::Box, alloc::boxed::Box::new); smart_pointer!(alloc::rc::Rc, alloc::rc::Rc::new); smart_pointer!(alloc::sync::Arc, alloc::arc::Arc::new); #[test] fn rc() { test_bake!(alloc::rc::Rc, alloc::rc::Rc::new('b'), alloc); } databake-0.2.0/src/converter.rs000064400000000000000000000135341046102023000145310ustar 00000000000000// This file is part of ICU4X. For terms of use, please see the file // called LICENSE at the top level of the ICU4X source tree // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). //! Helper structs to generate a different output than given input. //! //! This allows e.g. to store a `String` but output a `&'static str`. use crate::{Bake, BakeSize, CrateEnv}; use core::marker::PhantomData; use core::ops::{Deref, DerefMut}; use proc_macro2::TokenStream; use quote::quote; /// Let [`Bake`] output a `&'static str` for anything that can be `AsRef`. /// /// It can be helpful if the type needs to be constructed (e.g. [`String`], [`Cow`](std::borrow::Cow)) but should result in a `&'static str`. /// /// This requires that the crate using the generated data needs a different struct. /// /// ``` /// use databake::{converter::AsStaticStr, Bake}; /// /// #[derive(Bake)] /// #[databake(path = my_crate)] /// struct Data { /// number: usize, /// string: AsStaticStr, // can be written as StringAsStaticStr /// } /// /// let data = Data { /// number: 6, /// string: 6.to_string().into(), /// }; /// /// assert_eq!( /// data.bake(&Default::default()).to_string(), /// r#"my_crate :: Data { number : 6usize , string : "6" , }"# /// ); /// /// mod my_crate { /// struct Data { /// number: usize, /// string: &'static str, /// } /// } /// ``` #[derive(Default)] #[repr(transparent)] pub struct AsStaticStr(pub T) where T: AsRef; impl AsStaticStr where T: AsRef, { #[inline] pub fn into(self) -> T { self.0 } } impl Bake for AsStaticStr where T: AsRef, { fn bake(&self, _ctx: &CrateEnv) -> TokenStream { let value = &self.0.as_ref(); quote!(#value) } } impl BakeSize for AsStaticStr where T: AsRef, { fn borrows_size(&self) -> usize { self.0.as_ref().len() } } impl Deref for AsStaticStr where T: AsRef, { type Target = T; #[inline] fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for AsStaticStr where T: AsRef, { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } impl From for AsStaticStr where T: AsRef, { #[inline] fn from(value: T) -> Self { Self(value) } } #[test] fn as_static_str() { use std::borrow::Cow; assert_eq!( AsStaticStr(Cow::::Owned("hi".to_string())) .bake(&Default::default()) .to_string(), r#""hi""# ); } /// Alias for [`AsStaticStr`]. pub type StringAsStaticStr = AsStaticStr; /// Let [`Bake`] output a `&'static [T]` for anything that can be iterated over. /// /// It can be helpful if the type needs to be constructed (e.g. [`Vec`]) but should result in a `&'static [T]`. /// /// This requires that the crate using the generated data needs a different struct. /// /// ``` /// use databake::{converter::IteratorAsRefSlice, Bake}; /// /// #[derive(Bake, Default)] /// #[databake(path = my_crate)] /// struct Data { /// numbers: IteratorAsRefSlice, usize>, // can be written as `VecAsRefSlice` /// } /// /// let mut data = Data::default(); /// data.numbers.push(6); /// /// assert_eq!( /// data.bake(&Default::default()).to_string(), /// r#"my_crate :: Data { numbers : & [6usize ,] , }"# /// ); /// /// mod my_crate { /// struct Data { /// numbers: &'static [usize], /// } /// } /// ``` #[derive(Default)] #[repr(transparent)] pub struct IteratorAsRefSlice(pub B, pub PhantomData) where for<'a> &'a B: IntoIterator, T: Bake; impl IteratorAsRefSlice where for<'a> &'a B: IntoIterator, T: Bake, { #[inline] pub fn into(self) -> B { self.0 } } impl Bake for IteratorAsRefSlice where for<'a> &'a B: IntoIterator, T: Bake, { fn bake(&self, ctx: &CrateEnv) -> TokenStream { let mut inner = TokenStream::new(); for e in self.0.into_iter() { let e = e.bake(ctx); inner.extend(quote! {#e,}); } quote! {&[#inner]} } } impl BakeSize for IteratorAsRefSlice where for<'a> &'a B: IntoIterator, T: BakeSize, { fn borrows_size(&self) -> usize { self.0.into_iter().map(|x| x.borrows_size()).sum() } } impl Deref for IteratorAsRefSlice where for<'a> &'a B: IntoIterator, T: Bake, { type Target = B; #[inline] fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for IteratorAsRefSlice where for<'a> &'a B: IntoIterator, T: Bake, { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } impl From for IteratorAsRefSlice where for<'a> &'a B: IntoIterator, T: Bake, { #[inline] fn from(value: B) -> Self { Self(value, PhantomData) } } impl FromIterator for IteratorAsRefSlice where B: FromIterator, for<'a> &'a B: IntoIterator, T: Bake, { fn from_iter>(iter: I) -> Self { Self(iter.into_iter().collect(), PhantomData) } } #[test] fn as_ref_slice() { use std::collections::HashSet; let mut set = IteratorAsRefSlice::, usize>::default(); set.insert(42); assert_eq!( set.bake(&Default::default()).to_string(), r#"& [42usize ,]"# ) } /// Alias for [`IteratorAsRefSlice, T>`]. pub type VecAsRefSlice = IteratorAsRefSlice, T>; #[test] fn vec_as_ref_slice() { assert_eq!( VecAsRefSlice::from(vec![6u8]) .bake(&Default::default()) .to_string(), r#"& [6u8 ,]"# ) } databake-0.2.0/src/lib.rs000064400000000000000000000142101046102023000132600ustar 00000000000000// This file is part of ICU4X. For terms of use, please see the file // called LICENSE at the top level of the ICU4X source tree // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). //! This crate allows data to write itself into Rust code (bake itself in). //! //! Types that implement the `Bake` trait can be written into Rust expressions, //! which allows using Rust code itself as a zero-overhead "serialization" strategy. //! //! # Example //! ``` //! use databake::*; //! # extern crate alloc; //! use alloc::borrow::Cow; //! //! let data = [Some((18, Cow::Borrowed("hi")))]; //! assert_eq!( //! data.bake(&Default::default()).to_string(), //! r#"[Some ((18i32 , alloc :: borrow :: Cow :: Borrowed ("hi")))]"#, //! ); //! ``` //! //! # Derive //! //! `Bake` can be automatically derived if the `derive` Cargo feature is enabled. //! //! ``` //! use databake::*; //! //! #[derive(Bake)] //! #[databake(path = my_crate)] //! struct MyStruct { //! number: u32, //! string: &'static str, //! slice: &'static [bool], //! } //! //! #[derive(Bake)] //! #[databake(path = my_crate)] //! struct AnotherOne(MyStruct, char); //! ``` //! //! # Testing //! The [`test_bake`] macro can be used to assert that a particular expression is a `Bake` fixed point. //! //! ``` //! # use databake::*; //! # #[derive(Bake)] //! # #[databake(path = my_crate)] //! # struct MyStruct { //! # number: u32, //! # string: &'static str, //! # slice: &'static [bool], //! # } //! # #[derive(Bake)] //! # #[databake(path = my_crate)] //! # struct AnotherOne(MyStruct, char); //! # fn main() { //! test_bake!( //! AnotherOne, //! const, //! crate::AnotherOne( //! crate::MyStruct { //! number: 17u32, //! string: "foo", //! slice: &[true, false], //! }, //! 'b', //! ), //! my_crate, //! ); //! # } //! ``` mod alloc; pub mod converter; mod primitives; #[doc(no_inline)] pub use proc_macro2::TokenStream; #[doc(no_inline)] pub use quote::quote; #[cfg(feature = "derive")] pub use databake_derive::Bake; use std::collections::HashSet; use std::sync::Mutex; /// A collection of crates that are required for the evaluation of some expression. #[derive(Default)] pub struct CrateEnv(Mutex>); impl CrateEnv { /// Adds a crate to this collection. This can be called concurrently /// and without `mut`. pub fn insert(&self, krate: &'static str) { self.0.lock().expect("poison").insert(krate); } } impl IntoIterator for CrateEnv { type Item = &'static str; type IntoIter = as IntoIterator>::IntoIter; fn into_iter(self) -> Self::IntoIter { self.0.into_inner().expect("poison").into_iter() } } /// The `Bake` trait allows a piece of data to write itself into a Rust expression. /// /// This can be used to generate files with hardcoded data. pub trait Bake { /// Returns a [`TokenStream`] that would evaluate to `self`. /// /// Crates that are required for the evaluation of the [`TokenStream`] will be /// added to `ctx`. fn bake(&self, ctx: &CrateEnv) -> TokenStream; } /// Allows returning the size of data borrowed by a baked struct. pub trait BakeSize: Sized + Bake { /// Returns the size fn borrows_size(&self) -> usize; } /// This macro tests that an expression evaluates to a value that bakes to the same expression. /// /// Its mandatory arguments are a type and an expression (of that type). /// /// ``` /// # use databake::test_bake; /// test_bake!(usize, 18usize); /// ``` /// /// ## `Const` /// /// We usually want baked output to be const constructible. To test this, add the `const:` prefix to /// the expression: /// /// ``` /// # use databake::test_bake; /// test_bake!(usize, const, 18usize); /// ``` /// /// ## Crates and imports /// /// As most output will need to reference its crate, and its not possible to name a crate from /// within it, a third parameter can be used to specify the crate name. The `crate` identifier /// in the original expression will be replaced by this in the expected output. /// /// ```no_run /// # use databake::*; /// # struct MyStruct(usize); /// # impl Bake for MyStruct { /// # fn bake(&self, _: &CrateEnv) -> TokenStream { unimplemented!() } /// # } /// # // We need an explicit main to put the struct at the crate root /// # fn main() { /// test_bake!( /// MyStruct, /// crate::MyStruct(42usize), // matches `::my_crate::MyStruct(42usize)` /// my_crate, /// ); /// # } /// ``` /// /// A fourth, optional, parameter is a list of crate names that are expected to be added to the /// `CrateEnv`. The `crate`-replacement crate will always be checked. #[macro_export] macro_rules! test_bake { ($type:ty, const, $expr:expr $(, $krate:ident)? $(, [$($env_crate:ident),+])? $(,)?) => { const _: &$type = &$expr; $crate::test_bake!($type, $expr $(, $krate)? $(, [$($env_crate),+])?); }; ($type:ty, $expr:expr $(, $krate:ident)? $(, [$($env_crate:ident),+])? $(,)?) => { let env = Default::default(); let expr: &$type = &$expr; let bake = $crate::Bake::bake(expr, &env).to_string(); // For some reason `TokenStream` behaves differently in this line let expected_bake = $crate::quote!($expr).to_string().replace("::<", ":: <").replace(">::", "> ::"); // Trailing commas are a mess as well let bake = bake.replace(" ,)", ")").replace(" ,]", "]").replace(" , }", " }").replace(" , >", " >"); let expected_bake = expected_bake.replace(" ,)", ")").replace(" ,]", "]").replace(" , }", " }").replace(" , >", " >"); $( let expected_bake = expected_bake.replace("crate", stringify!($krate)); )? assert_eq!(bake, expected_bake); #[allow(unused_variables)] let _env = env.into_iter().collect::>(); $( assert!(_env.contains(stringify!($krate)), "Crate {:?} was not added to the CrateEnv", stringify!($krate)); )? $( $( assert!(_env.contains(stringify!($env_crate)), "Crate {:?} was not added to the CrateEnv", stringify!($env_crate)); )+ )? }; } databake-0.2.0/src/primitives.rs000064400000000000000000000206771046102023000147230ustar 00000000000000// This file is part of ICU4X. For terms of use, please see the file // called LICENSE at the top level of the ICU4X source tree // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ). 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 Bake for &T where T: Bake, { fn bake(&self, ctx: &CrateEnv) -> TokenStream { let t = ::bake(*self, ctx); quote! { &#t } } } impl BakeSize for &T where T: BakeSize, { fn borrows_size(&self) -> usize { core::mem::size_of_val::(*self) + (*self).borrows_size() } } #[test] fn r#ref() { test_bake!(&f32, const, &934.34f32); assert_eq!(BakeSize::borrows_size(&&934.34f32), 4); } impl Bake for &[T] where T: Bake, { fn bake(&self, ctx: &CrateEnv) -> TokenStream { if core::mem::size_of::() == core::mem::size_of::() && core::any::type_name::() == core::any::type_name::() { // Safety: self.as_ptr()'s allocation is at least self.len() bytes long, // initialised, and well-alligned. 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 BakeSize for &[T] where T: BakeSize, { fn borrows_size(&self) -> usize { std::mem::size_of_val(*self) + self.iter().map(BakeSize::borrows_size).sum::() } } #[test] fn slice() { // Cannot use test_bake! as it's not possible to write a closed slice expression (&[1] has type &[usize; 1]) 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 Bake for [T; N] where T: Bake, { fn bake(&self, ctx: &CrateEnv) -> TokenStream { if core::mem::size_of::() == core::mem::size_of::() && core::any::type_name::() == core::any::type_name::() { // Safety: self.as_ptr()'s allocation is at least self.len() bytes long, // initialised, and well-alligned. 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 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 Bake for Option 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 BakeSize for Option 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 Bake for Result 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 BakeSize for Result 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 Bake for PhantomData { fn bake(&self, _ctx: &CrateEnv) -> TokenStream { quote! { core::marker::PhantomData } } } impl BakeSize for PhantomData { fn borrows_size(&self) -> usize { 0 } } #[test] fn phantom_data() { test_bake!(PhantomData, const, core::marker::PhantomData); }