From 9fbafb577cbe1108df1ffa627597428c2983b0d2 Mon Sep 17 00:00:00 2001 From: Package Date: Fri, 6 Nov 2020 14:29:19 +0800 Subject: [PATCH] basic --- .gitignore | 2 + Cargo.toml | 23 +++ build.rs | 12 ++ code_gen.rs | 439 ++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 46 +++++ src/tuple_iter.rs | 23 +++ src/tuple_map.rs | 4 + 7 files changed, 549 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 build.rs create mode 100644 code_gen.rs create mode 100644 src/lib.rs create mode 100644 src/tuple_iter.rs create mode 100644 src/tuple_map.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..0a54e3b --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "tuples" +version = "0.1.0" +authors = ["BatchOperator", "2A5F "] +edition = "2018" +license = "MIT" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[features] +default = ["std", "tuple_combin", "tuple_iter", "tuple_map", "re-exports"] +re-exports = [] +std = [] +tuple_iter = [] +tuple_map = [] +tuple_combin = [] + +[build-dependencies] +syn = "1.0" +quote = "1.0" +proc-macro2 = "1.0" \ No newline at end of file diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..5597e15 --- /dev/null +++ b/build.rs @@ -0,0 +1,12 @@ +use std::env; + +mod code_gen; +use code_gen::*; + +fn main() { + let out_dir = env::var_os("OUT_DIR").unwrap(); + + code_gen(out_dir); + + println!("cargo:rerun-if-changed=build.rs"); +} diff --git a/code_gen.rs b/code_gen.rs new file mode 100644 index 0000000..4767133 --- /dev/null +++ b/code_gen.rs @@ -0,0 +1,439 @@ +use proc_macro2::{Ident, Span, TokenStream}; +use quote::{format_ident, quote}; +use std::{ffi::OsString, fs, path::Path}; +use syn::LitInt; + +pub fn code_gen(out_dir: OsString) { + let t = format_ident!("T"); + let u = format_ident!("U"); + + let ctx = init(33, &t, &u); + + gen_tuple_impl(&ctx, &out_dir); + gen_tuple_n_impl(&ctx, &out_dir); + #[cfg(feature = "tuple_iter")] + gen_tuple_iter(&ctx, &out_dir); + #[cfg(feature = "tuple_map")] + gen_tuple_map(&ctx, &out_dir); +} + +struct Ctx<'a> { + pub t: &'a Ident, + pub u: &'a Ident, + pub size_lits: Vec, + pub ts: Vec<&'a Ident>, + pub us: Vec<&'a Ident>, + pub nts: Vec, +} + +fn init<'a>(max: usize, t: &'a Ident, u: &'a Ident) -> Ctx<'a> { + let size_lits = (0..max + 1) + .into_iter() + .map(|i| LitInt::new(i.to_string().as_str(), Span::call_site())) + .collect::>(); + let ts = (0..max + 1).into_iter().map(|_| t).collect::>(); + let us = (0..max + 1).into_iter().map(|_| u).collect::>(); + let nts = (0..max + 1) + .into_iter() + .map(|i| format_ident!("T{}", i)) + .collect::>(); + let ctx = Ctx { + t, + u, + size_lits, + ts, + us, + nts, + }; + ctx +} + +fn gen_tuple_impl(ctx: &Ctx, out_dir: &OsString) { + let items = (2..33usize) + .into_iter() + .map(|i| gen_tuple_impl_size(ctx, i)); + let tks = quote! { #(#items)* }; + let code = tks.to_string(); + let dest_path = Path::new(out_dir).join("tuple_impl.rs"); + fs::write(&dest_path, code).unwrap(); +} + +fn gen_tuple_impl_size(ctx: &Ctx, size: usize) -> TokenStream { + let size_lit = &ctx.size_lits[size]; + + let ts = &ctx.ts[0..size]; + + let nts = &ctx.nts[0..size]; + let ants = (0..size) + .into_iter() + .map(|i| &nts[i]) + .map(|i| quote! { #i: 'a }) + .collect::>(); + let ref_nts = (0..size) + .into_iter() + .map(|i| &nts[i]) + .map(|id| quote! { &'a #id }) + .collect::>(); + let mut_nts = (0..size) + .into_iter() + .map(|i| &nts[i]) + .map(|id| quote! { &'a mut #id }) + .collect::>(); + + let ref_impl = ctx.size_lits[0..size].iter().map(|l| { + quote! { + &self.#l + } + }); + let mut_impl = ctx.size_lits[0..size].iter().map(|l| { + quote! { + &mut self.#l + } + }); + + let ref_doc = format!("AsRef for Tuple{}", size); + let mut_doc = format!("AsMut for Tuple{}", size); + + let tks = quote! { + impl TupleSame for (#(#ts),*) { } + + impl<#(#nts),*> Tuple for (#(#nts),*) { + fn size(&self) -> usize { + #size_lit + } + } + + impl<'a, #(#ants),*> TupleAsRef<'a> for (#(#nts),*) { + type OutTuple = (#(#ref_nts),*); + + #[doc = #ref_doc] + fn as_ref(&'a self) -> Self::OutTuple { + (#(#ref_impl),*) + } + } + + impl<'a, #(#ants),*> TupleAsMut<'a> for (#(#nts),*) { + type OutTuple = (#(#mut_nts),*); + + #[doc = #mut_doc] + fn as_mut(&'a mut self) -> Self::OutTuple { + (#(#mut_impl),*) + } + } + }; + tks +} + +fn gen_tuple_n_impl(ctx: &Ctx, out_dir: &OsString) { + let item_names = (0..34usize).into_iter().map(|i| format_ident!("Item{}", i)).collect::>(); + let type_items = item_names.iter().map(|i| quote! { type #i; }).collect::>(); + let let_items = item_names.iter().zip(ctx.nts.iter()).map(|(i, t)| quote! { type #i = #t; }).collect::>(); + let items = (2..33usize) + .into_iter() + .map(|i| gen_tuple_n_impl_size(ctx, i, &type_items, &let_items)); + let tks = quote! { #(#items)* }; + let code = tks.to_string(); + let dest_path = Path::new(out_dir).join("tuple_n.rs"); + fs::write(&dest_path, code).unwrap(); +} + +fn gen_tuple_n_impl_size(ctx: &Ctx, size: usize, type_items: &[TokenStream], let_items: &[TokenStream]) -> TokenStream { + let tuple_name = format_ident!("Tuple{}", size); + + let nts = &ctx.nts[0..size]; + + let type_items = &type_items[0..size]; + let let_items = &let_items[0..size]; + + let tks = quote! { + pub trait #tuple_name: Tuple { + #(#type_items)* + } + impl<#(#nts),*> #tuple_name for (#(#nts),*) { + #(#let_items)* + } + }; + tks +} + +#[cfg(feature = "tuple_iter")] +fn gen_tuple_iter(ctx: &Ctx, out_dir: &OsString) { + let items = (2..33usize) + .into_iter() + .map(|i| gen_tuple_iter_size(ctx, i)); + let tks = quote! { #(#items)* }; + let code = tks.to_string(); + let dest_path = Path::new(out_dir).join("tuple_iter.rs"); + fs::write(&dest_path, code).unwrap(); +} + +#[cfg(feature = "tuple_iter")] +fn gen_tuple_iter_size(ctx: &Ctx, size: usize) -> TokenStream { + let size_lit = &ctx.size_lits[size]; + let last_lit = &ctx.size_lits[size - 1]; + + let iter_struct_name = format_ident!("Tuple{}Iter", size); + let into_iter_struct_name = format_ident!("Tuple{}IntoIter", size); + + let ts = &ctx.ts[0..size]; + let ut = quote! { MaybeUninit }; + let uts = (0..size).into_iter().map(|_| &ut); + let utts = ctx.size_lits[0..size] + .iter() + .map(|i| quote! { MaybeUninit::new(t.#i) }); + let iter_match = ctx.size_lits[0..size] + .iter() + .map(|i| quote! { #i => &self.1 .#i, }) + .collect::>(); + let into_match = ctx.size_lits[0..size] + .iter() + .zip(ctx.size_lits[1..size + 1].iter()) + .map(|(i, n)| quote! { #i => std::mem::replace(&mut self.#n, MaybeUninit::uninit()), }) + .collect::>(); + let from = quote! { iter.next().unwrap() }; + let froms = (0..size).into_iter().map(|_| &from); + + let derive_iter = if size > 12 { + quote! {} + } else { + quote! {#[derive(Debug, Clone, Copy)]} + }; + let derive_into = if size > 12 { + quote! {} + } else { + quote! {#[derive(Debug)]} + }; + + let tks = quote! { + #derive_iter + #[doc(hidden)] + pub struct #iter_struct_name<'a, T>(usize, &'a (#(#ts),*)); + impl<'a, T> #iter_struct_name<'a, T> { + #[inline] + pub fn new(t: &'a (#(#ts),*)) -> Self { + Self(0, t) + } + } + #derive_into + #[doc(hidden)] + pub struct #into_iter_struct_name(usize, #(#uts),*); + impl #into_iter_struct_name { + #[inline] + pub fn new(t: (#(#ts),*)) -> Self { + Self(0, #(#utts),*) + } + } + + impl<'a, T> Iterator for #iter_struct_name<'a, T> { + type Item = &'a T; + + #[inline] + fn next(&mut self) -> Option { + let res = match self.0 { + #(#iter_match)* + _ => return None + }; + self.0 += 1; + Some(res) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let exact = self.len(); + (exact, Some(exact)) + } + + #[inline] + fn count(self) -> usize { + self.len() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + if n < self.0 { return None } + let res = match self.0 { + #(#iter_match)* + _ => return None + }; + self.0 = min(n + 1, #size_lit); + Some(res) + } + + #[inline] + fn last(self) -> Option { + if self.len() == 0 { return None } + Some(&self.1 .#last_lit) + } + } + impl<'a, T> ExactSizeIterator for #iter_struct_name<'a, T> { + #[inline] + fn len(&self) -> usize { #size_lit - self.0 } + } + impl<'a, T> FusedIterator for #iter_struct_name<'a, T> { } + impl<'a, T> AsRef<(#(#ts),*)> for #iter_struct_name<'a, T> { + #[inline] + fn as_ref(&self) -> &(#(#ts),*) { + self.1 + } + } + impl<'a, T: 'a> TupleIter<'a> for (#(#ts),*) { + type Iter = #iter_struct_name<'a, T>; + + #[inline] + fn iter(&'a self) -> Self::Iter { + #iter_struct_name::new(self) + } + } + + impl Iterator for #into_iter_struct_name { + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + let res = match self.0 { + #(#into_match)* + _ => return None + }; + self.0 += 1; + Some(unsafe { res.assume_init() }) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let exact = self.len(); + (exact, Some(exact)) + } + + #[inline] + fn count(self) -> usize { + self.len() + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + if n < self.0 { return None } + let res = match self.0 { + #(#into_match)* + _ => return None + }; + self.0 = min(n + 1, #size_lit); + Some(unsafe { res.assume_init() }) + } + + #[inline] + fn last(self) -> Option { + if self.len() == 0 { return None } + Some(unsafe { self.#size_lit.assume_init() }) + } + } + impl ExactSizeIterator for #into_iter_struct_name { + #[inline] + fn len(&self) -> usize { #size_lit - self.0 } + } + impl FusedIterator for #into_iter_struct_name { } + impl TupleIntoIter for (#(#ts),*) { + type Iter = #into_iter_struct_name; + + #[inline] + fn into_iter(self) -> Self::Iter { + #into_iter_struct_name::new(self) + } + } + + impl TupleFromIter for (#(#ts),*) { + fn from_iter>(iter: I) -> Self { + let mut iter = iter.into_iter(); + (#(#froms),*) + } + } + }; + tks +} + +#[cfg(feature = "tuple_map")] +fn gen_tuple_map(ctx: &Ctx, out_dir: &OsString) { + let items = (2..33usize).into_iter().map(|i| gen_tuple_map_size(ctx, i)); + let tks = quote! { #(#items)* }; + let code = tks.to_string(); + let dest_path = Path::new(out_dir).join("tuple_map.rs"); + fs::write(&dest_path, code).unwrap(); +} + +#[cfg(feature = "tuple_map")] +fn gen_tuple_map_size(ctx: &Ctx, size: usize) -> TokenStream { + let items = if size > 16 { + vec![] + } else { + (0..size) + .into_iter() + .map(|n| gen_tuple_map_n_size(ctx, size, n)) + .collect() + }; + + let map_name = format_ident!("Tuple{}Map", size); + + let ts = &ctx.ts[0..size]; + let us = &ctx.us[0..size]; + + let map_impl = ctx.size_lits[0..size].iter().map(|l| { + quote! { + f(self.#l) + } + }); + + let map_doc = format!("Mapping for Tuple{}", size); + + let tks = quote! { + #(#items)* + + #[doc = #map_doc] + pub trait #map_name { + #[doc = #map_doc] + fn map(self, f: impl FnMut(T) -> U) -> (#(#us),*); + } + impl #map_name for (#(#ts),*) { + fn map(self, mut f: impl FnMut(T) -> U) -> (#(#us),*) { + (#(#map_impl),*) + } + } + }; + tks +} + +#[cfg(feature = "tuple_map")] +fn gen_tuple_map_n_size(ctx: &Ctx, size: usize, n: usize) -> TokenStream { + let t = &ctx.nts[n]; + let map_n_name = format_ident!("Tuple{}Map{}", size, n); + let map_n = format_ident!("map{}", n); + + let rts = &ctx.nts[0..size]; + let ts = ctx.nts[0..size] + .iter() + .enumerate() + .map(|(i, l)| if i == n { ctx.u } else { l }) + .collect::>(); + + let impls = ctx.size_lits[0..size].iter().enumerate().map(|(i, l)| { + if i == n { + quote! { f(self.#l) } + } else { + quote! { self.#l } + } + }); + + let doc = format!("Mapping `.{}` for Tuple{}", n, size); + + let tks = quote! { + #[doc=#doc] + pub trait #map_n_name<#(#rts),*> { + #[doc=#doc] + fn #map_n(self, f: impl FnOnce(#t) -> U) -> (#(#ts),*); + } + impl<#(#rts),*> #map_n_name<#(#rts),*> for (#(#rts),*) { + fn #map_n(self, f: impl FnOnce(#t) -> U) -> (#(#ts),*) { + (#(#impls),*) + } + } + }; + tks +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..cf72153 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,46 @@ + +/// AsRef for Tuple +pub trait TupleAsRef<'a> { + type OutTuple: 'a; + + /// AsRef for Tuple + fn as_ref(&'a self) -> Self::OutTuple; +} + +/// AsMut for Tuple +pub trait TupleAsMut<'a> { + type OutTuple: 'a; + + /// AsMut for Tuple + fn as_mut(&'a mut self) -> Self::OutTuple; +} + +/// Tuple meta +pub trait Tuple { + /// Tuple size + fn size(&self) -> usize; +} + +/// Mark traits for all tuples with all item is same type +pub trait TupleSame: Tuple { } + +include!(concat!(env!("OUT_DIR"), "/tuple_impl.rs")); + +/// TupleN +pub mod tuple_n { + use crate::*; + + include!(concat!(env!("OUT_DIR"), "/tuple_n.rs")); +} +#[cfg(feature = "re-exports")] +pub use tuple_n::*; + +#[cfg(feature = "tuple_iter")] +pub mod tuple_iter; +#[cfg(all(feature = "tuple_iter", feature = "re-exports"))] +pub use tuple_iter::*; + +#[cfg(feature = "tuple_map")] +pub mod tuple_map; +#[cfg(all(feature = "tuple_map", feature = "re-exports"))] +pub use tuple_map::*; \ No newline at end of file diff --git a/src/tuple_iter.rs b/src/tuple_iter.rs new file mode 100644 index 0000000..3e969f8 --- /dev/null +++ b/src/tuple_iter.rs @@ -0,0 +1,23 @@ +//! Implemented iterators for tuples + +use core::mem::MaybeUninit; +use core::iter::FusedIterator; +use core::cmp::min; + +include!(concat!(env!("OUT_DIR"), "/tuple_iter.rs")); + +pub trait TupleIter<'a> { + type Iter: Iterator + 'a; + + fn iter(&'a self) -> Self::Iter; +} + +pub trait TupleIntoIter { + type Iter: Iterator; + + fn into_iter(self) -> Self::Iter; +} + +pub trait TupleFromIter { + fn from_iter>(iter: I) -> Self; +} diff --git a/src/tuple_map.rs b/src/tuple_map.rs new file mode 100644 index 0000000..81254e0 --- /dev/null +++ b/src/tuple_map.rs @@ -0,0 +1,4 @@ +//! Mapping for tuples + +include!(concat!(env!("OUT_DIR"), "/tuple_map.rs")); +