use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{Ident, LitInt, Type, parse_quote};
pub fn struct_name(n: u32, is_signed: bool) -> Ident {
let prefix = if is_signed { "I" } else { "U" };
Ident::new(&format!("{}{}", prefix, n), Span::call_site())
}
pub fn feature_name(n: u32, is_signed: bool) -> String {
let prefix = if is_signed { "i" } else { "u" };
format!("{}{}", prefix, n)
}
pub fn inner_type(n: u32, is_signed: bool) -> Type {
if is_signed {
inner_int_type(n)
} else {
inner_uint_type(n)
}
}
pub fn size(n: u32) -> u32 {
if n <= 8 {
8
} else if n <= 16 {
16
} else if n <= 32 {
32
} else if n <= 64 {
64
} else if n <= 128 {
128
} else {
unimplemented!()
}
}
pub fn inner_uint_type(n: u32) -> Type {
if n <= 8 {
parse_quote! { u8 }
} else if n <= 16 {
parse_quote! { u16 }
} else if n <= 32 {
parse_quote! { u32 }
} else if n <= 64 {
parse_quote! { u64 }
} else if n <= 128 {
parse_quote! { u128 }
} else {
unimplemented!()
}
}
pub fn inner_int_type(n: u32) -> Type {
if n <= 8 {
parse_quote! { i8 }
} else if n <= 16 {
parse_quote! { i16 }
} else if n <= 32 {
parse_quote! { i32 }
} else if n <= 64 {
parse_quote! { i64 }
} else if n <= 128 {
parse_quote! { i128 }
} else {
unimplemented!()
}
}
pub fn shift(n: u32) -> Option<LitInt> {
let s = size(n) - n;
if s == 0 { None } else { Some(parse_quote!(#s)) }
}
pub fn masking(shift: &Option<LitInt>) -> TokenStream {
if let Some(shift) = shift {
quote! {
>> #shift << #shift
}
} else {
quote! {}
}
}
pub fn impl_new_func(ty: &Type, shift: &Option<LitInt>) -> TokenStream {
if let Some(shift) = shift {
quote! {
#[doc = " 整数値から生成"]
#[doc = ""]
#[doc = " # Panics"]
#[doc = ""]
#[doc = " デバッグ時に範囲外を超えるとパニックする。"]
#[doc = " デバッグ時でなければ、上位桁が無視される。"]
pub fn new(value: #ty) -> Self {
debug_assert!(
(<Self as num_traits::Bounded>::min_value().0 >> #shift) <= value,
"underflow value: {}",
value
);
debug_assert!(
value <= (<Self as num_traits::Bounded>::max_value().0 >> #shift),
"overflow value: {}",
value
);
Self::from_bits(value << #shift)
}
}
} else {
quote! {
#[doc = " 整数値から生成"]
pub fn new(value: #ty) -> Self {
Self::from_bits(value)
}
}
}
}
fn impl_from_bits(ty: &Type, shift: &Option<LitInt>) -> TokenStream {
if let Some(shift) = shift {
quote! {
#[doc = " ビットから直接生成"]
#[doc = ""]
#[doc = " 直接の利用を非推奨。"]
#[doc = ""]
#[doc = " # Panics"]
#[doc = ""]
#[doc = " デバッグ時に下位桁が 0 でなければパニックする。"]
#[doc = " デバッグ時でなければ、下位桁はそのまま入る。"]
pub fn from_bits(value: #ty) -> Self {
debug_assert_eq!(value % (1 << #shift), 0);
Self(value)
}
}
} else {
quote! {
#[doc = " ビットから直接生成"]
#[doc = ""]
#[doc = " 直接の利用を非推奨。"]
pub fn from_bits(value: #ty) -> Self {
Self(value)
}
}
}
}
pub fn impl_struct(struct_name: &Ident, ty: &Type, shift: &Option<LitInt>) -> TokenStream {
let new_func_impl = impl_new_func(ty, shift);
let from_bits_impl = impl_from_bits(ty, shift);
let doc = {
let sn = format!("{}", struct_name);
if sn.starts_with("U") {
format!(" 符号なし {} ビット整数", sn.strip_prefix("U").unwrap())
} else if sn.starts_with("I") {
format!(" 符号付き {} ビット整数", sn.strip_prefix("I").unwrap())
} else {
unreachable!()
}
};
quote! {
#[doc = #doc]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct #struct_name(#ty);
impl #struct_name {
#new_func_impl
#from_bits_impl
}
}
}
pub fn impl_debug(struct_name: &Ident, shift: &Option<LitInt>) -> TokenStream {
let fs = format!("{}({{}})", struct_name);
if let Some(shift) = shift {
quote! {
impl std::fmt::Debug for #struct_name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, #fs, self.0 >> #shift)
}
}
}
} else {
quote! {
impl std::fmt::Debug for #struct_name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, #fs, self.0)
}
}
}
}
}
pub fn impl_radix(struct_name: &Ident, uty: &Type, shift: &Option<LitInt>) -> TokenStream {
let mut impls = Vec::with_capacity(4);
for (radix, fs) in [
("Binary", "b"),
("Octal", "o"),
("LowerHex", "x"),
("UpperHex", "X"),
] {
let radix = Ident::new(radix, Span::call_site());
let fs = format!("{}({{:{}}})", struct_name, fs);
impls.push(if let Some(shift) = shift {
quote! {
impl std::fmt::#radix for #struct_name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, #fs, self.0 as #uty >> #shift)
}
}
}
} else {
quote! {
impl std::fmt::#radix for #struct_name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, #fs, self.0 as #uty)
}
}
}
});
}
quote! {
#(#impls)*
}
}