use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens};
use syn::LitInt;
pub struct SizeOption {
token_stream: TokenStream,
}
impl SizeOption {
pub unsafe fn from_raw(token_stream: TokenStream) -> Self {
Self { token_stream }
}
pub fn from_type(type_name: impl ToTokens, enumerable_trait_path: impl ToTokens) -> Self {
unsafe {
Self::from_raw(quote!(
<#type_name as #enumerable_trait_path>::ENUMERABLE_SIZE_OPTION
))
}
}
pub fn from_usize(size: usize) -> Self {
let size_lit = LitInt::new(&format!("{}usize", size), Span::call_site());
unsafe { Self::from_raw(quote!(Some(#size_lit))) }
}
pub fn from_product(sizes: impl Iterator<Item = SizeOption>) -> Self {
let mut sizes = sizes.peekable();
let size_first = match sizes.next() {
Some(size) => size,
None => {
return Self::from_usize(1);
}
};
match sizes.peek() {
Some(_) => {
unsafe {
Self::from_raw(quote!(
{
let size: Option<usize> = #size_first;
#(
let size: Option<usize> = match (size, #sizes) {
(Some(0), _) | (_, Some(0)) => Some(0),
(Some(size), Some(size_field)) => size.checked_mul(size_field),
_ => None,
};
)*
size
}
))
}
}
None => {
size_first
}
}
}
pub fn from_sum(sizes: impl Iterator<Item = SizeOption>) -> Self {
let mut sizes = sizes.peekable();
let size_first = match sizes.next() {
Some(size) => size,
None => {
return Self::from_usize(0);
}
};
match sizes.peek() {
Some(_) => {
unsafe {
Self::from_raw(quote!(
{
let size: Option<usize> = #size_first;
#(
let size: Option<usize> = match (size, #sizes) {
(Some(size), Some(size_field)) => size.checked_add(size_field),
_ => None,
};
)*
size
}
))
}
}
None => {
size_first
}
}
}
}
impl ToTokens for SizeOption {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.token_stream.to_tokens(tokens);
}
}