#![allow(unused, dead_code)]
use std::any::{Any, TypeId};
use proc_macro2::Span;
use proc_macro2::TokenStream;
use quote::ToTokens;
use quote::quote;
use syn::LitChar;
use syn::{
Expr, Ident, LitStr, Result, Token, Type, TypeGroup, TypeParam,
parse::{Parse, ParseStream},
parse_macro_input,
};
pub enum PortVariant {
InBound,
InOutBound,
OutBound,
}
pub struct Params {
pub port_name: TokenStream,
pub port_type: Option<Type>,
pub port_value: Option<Expr>,
pub collection: Option<Ident>,
}
impl Parse for Params {
fn parse(input: ParseStream) -> Result<Self> {
if input.is_empty() {
panic!("macro needs at least one tuple of two comma separated elements");
}
let old = input;
let port_name = if let Ok(name) = input.parse::<LitStr>() {
quote! {#name}
} else {
let value = old.parse::<Ident>()?;
quote! {#value}
};
input.parse::<Token![,]>()?;
let mut port_type = None;
let mut port_value = None;
let mut collection = None;
let old = input;
if let Ok(ty) = input.parse::<Type>() {
port_type = Some(ty);
if !input.is_empty() {
input.parse::<Token![,]>()?; port_value = Some(old.parse::<Expr>()?);
}
if !input.is_empty() {
input.parse::<Token![,]>()?; collection = Some(old.parse::<Ident>()?);
}
} else {
port_value = Some(old.parse::<Expr>()?)
}
Ok(Params {
port_name,
port_type,
port_value,
collection,
})
}
}
fn is_port_collection_pointer(name: &str) -> bool {
name == "{=}" || (name.starts_with('{') && name.ends_with('}') && is_valid_port_name(&name[1..name.len() - 1]))
}
fn is_valid_port_name(name: &str) -> bool {
if name.is_empty() {
return false;
}
let mut iter = name.chars();
if let Some(c) = iter.next() {
if !c.is_alphabetic() {
return false;
}
} else {
return false;
}
for c in iter {
if !c.is_alphanumeric() && c != '_' && c != ' ' {
return false;
}
}
true
}
pub fn quote_from_input(
name: TokenStream,
tp: Type,
value: Expr,
collection: Option<Ident>,
variant: PortVariant,
parseable: bool,
) -> TokenStream {
let res = match value.clone() {
Expr::Lit(expr_lit) => {
let lit = expr_lit.lit;
match lit {
syn::Lit::Str(lit_str) => {
let lit_value = lit_str.value();
if is_port_collection_pointer(&lit_str.value()) {
if let Some(collection) = collection {
let other_name = if &lit_value == "{=}" {
name.clone()
} else {
let other = &lit_value[1..lit_value.len() - 1];
quote! {#other}
};
quote_from_collection(variant, name, tp, collection.to_token_stream(), other_name, parseable)
} else {
panic!("missing collection")
}
} else {
quote_from_str(variant, name, tp, value.to_token_stream(), parseable)
}
}
syn::Lit::Char(lit_char) => quote_from_char(variant, name, tp, lit_char, parseable),
_ => quote_from_type_value(variant, name, tp, value.to_token_stream(), parseable),
}
}
Expr::Reference(expr_reference) => {
let reference = expr_reference.expr;
match *reference {
Expr::Call(expr_call) => match *expr_call.func {
Expr::Path(expr_path) => {
todo!("something like &String::from(...)");
}
_ => quote_from_type_value(variant, name, tp, value.to_token_stream(), parseable),
},
Expr::Path(expr_path) => {
let derefed = quote! {*{#value}};
quote_from_str(variant, name, tp, derefed, parseable)
}
Expr::Lit(expr_lit) => {
let lit = expr_lit.lit;
match lit {
syn::Lit::Str(lit_str) => {
if is_port_collection_pointer(&lit_str.value()) {
if let Some(collection) = collection {
let lit_value = lit_str.value();
let other_name = if &lit_value == "{=}" {
name.clone()
} else {
let other = &lit_value[1..lit_value.len() - 1];
quote! {#other}
};
quote_from_collection(
variant,
name,
tp,
collection.to_token_stream(),
other_name,
parseable,
)
} else {
panic!("missing collection")
}
} else {
quote_from_str(variant, name, tp, value.to_token_stream(), parseable)
}
}
syn::Lit::Char(lit_char) => quote_from_char(variant, name, tp, lit_char, parseable),
_ => {
let derefed = quote! {*{#value}};
quote_from_type_value(variant, name, tp, derefed, parseable)
}
}
}
_ => quote_from_type_value(variant, name, tp, value.to_token_stream(), parseable),
}
}
_ => quote_from_type_value(variant, name, tp, value.to_token_stream(), parseable),
};
res.into()
}
pub fn quote_from_type(variant: PortVariant, name: TokenStream, tp: Type, parseable: bool) -> TokenStream {
if parseable {
match variant {
PortVariant::InBound => quote! {
({#name}.into(), dataport::PortVariant::InBound(dataport::InBound::new_parseable::<#tp>()))
},
PortVariant::InOutBound => quote! {
({#name}.into(), dataport::PortVariant::InOutBound(dataport::InOutBound::new_parseable::<#tp>()))
},
PortVariant::OutBound => quote! {
({#name}.into(), dataport::PortVariant::OutBound(dataport::OutBound::new_parseable::<#tp>()))
},
}
} else {
match variant {
PortVariant::InBound => quote! {
({#name}.into(), dataport::PortVariant::InBound(dataport::InBound::new::<#tp>()))
},
PortVariant::InOutBound => quote! {
({#name}.into(), dataport::PortVariant::InOutBound(dataport::InOutBound::new::<#tp>()))
},
PortVariant::OutBound => quote! {
({#name}.into(), dataport::PortVariant::OutBound(dataport::OutBound::new::<#tp>()))
},
}
}
}
pub fn quote_from_value(variant: PortVariant, name: TokenStream, value: TokenStream, parseable: bool) -> TokenStream {
if parseable {
match variant {
PortVariant::InBound => quote! {
({#name}.into(), dataport::PortVariant::InBound(dataport::InBound::with_value_parseable({#value})))
},
PortVariant::InOutBound => quote! {
({#name}.into(), dataport::PortVariant::InOutBound(dataport::InOutBound::with_value_parseable({#value})))
},
PortVariant::OutBound => quote! {
({#name}.into(), dataport::PortVariant::OutBound(dataport::OutBound::with_value_parseable({#value})))
},
}
} else {
match variant {
PortVariant::InBound => quote! {
({#name}.into(), dataport::PortVariant::InBound(dataport::InBound::with_value({#value})))
},
PortVariant::InOutBound => quote! {
({#name}.into(), dataport::PortVariant::InOutBound(dataport::InOutBound::with_value({#value})))
},
PortVariant::OutBound => quote! {
({#name}.into(), dataport::PortVariant::OutBound(dataport::OutBound::with_value({#value})))
},
}
}
}
fn quote_from_type_value(
variant: PortVariant,
name: TokenStream,
tp: Type,
value: TokenStream,
parseable: bool,
) -> TokenStream {
if parseable {
match variant {
PortVariant::InBound => quote! {
({#name}.into(), dataport::PortVariant::InBound(dataport::InBound::with_value_parseable::<#tp>({#value})))
},
PortVariant::InOutBound => quote! {
({#name}.into(), dataport::PortVariant::InOutBound(dataport::InOutBound::with_value_parseable::<#tp>({#value})))
},
PortVariant::OutBound => quote! {
({#name}.into(), dataport::PortVariant::OutBound(dataport::OutBound::with_value_parseable::<#tp>({#value})))
},
}
} else {
match variant {
PortVariant::InBound => quote! {
({#name}.into(), dataport::PortVariant::InBound(dataport::InBound::with_value::<#tp>({#value})))
},
PortVariant::InOutBound => quote! {
({#name}.into(), dataport::PortVariant::InOutBound(dataport::InOutBound::with_value::<#tp>({#value})))
},
PortVariant::OutBound => quote! {
({#name}.into(), dataport::PortVariant::OutBound(dataport::OutBound::with_value::<#tp>({#value})))
},
}
}
}
fn quote_from_collection(
variant: PortVariant,
name: TokenStream,
tp: Type,
collection: TokenStream,
other_name: TokenStream,
parseable: bool,
) -> TokenStream {
if parseable {
match variant {
PortVariant::InBound => quote! {
(#name.into(), dataport::PortVariant::InBound(dataport::InBound::from_collection_parseable::<#tp>({#collection}, #other_name)?))
},
PortVariant::InOutBound => quote! {
(#name.into(), dataport::PortVariant::InOutBound(dataport::InOutBound::from_collection_parseable::<#tp>({#collection}, #other_name)?))
},
PortVariant::OutBound => quote! {
(#name.into(), dataport::PortVariant::OutBound(dataport::OutBound::from_collection_parseable::<#tp>({#collection}, #other_name)?))
},
}
} else {
match variant {
PortVariant::InBound => quote! {
(#name.into(), dataport::PortVariant::InBound(dataport::InBound::from_collection::<#tp>({#collection}, #other_name)?))
},
PortVariant::InOutBound => quote! {
(#name.into(), dataport::PortVariant::InOutBound(dataport::InOutBound::from_collection::<#tp>({#collection}, #other_name)?))
},
PortVariant::OutBound => quote! {
(#name.into(), dataport::PortVariant::OutBound(dataport::OutBound::from_collection::<#tp>({#collection}, #other_name)?))
},
}
}
}
fn quote_from_str(variant: PortVariant, name: TokenStream, tp: Type, value: TokenStream, parseable: bool) -> TokenStream {
if parseable {
match variant {
PortVariant::InBound => quote! {
{
let my_value: #tp = core::str::FromStr::from_str({#value})?;
(#name.into(), dataport::PortVariant::InBound(dataport::InBound::with_value_parseable::<#tp>(my_value)))
}
},
PortVariant::InOutBound => quote! {
{
let my_value: #tp = core::str::FromStr::from_str({#value})?;
(#name.into(), dataport::PortVariant::InOutBound(dataport::InOutBound::with_value_parseable::<#tp>(my_value)))
}
},
PortVariant::OutBound => quote! {
{
let my_value: #tp = core::str::FromStr::from_str({#value})?;
(#name.into(), dataport::PortVariant::OutBound(dataport::OutBound::with_value_parseable::<#tp>(my_value)))
}
},
}
} else {
match variant {
PortVariant::InBound => quote! {
{
let my_value: #tp = core::str::FromStr::from_str({#value})?;
(#name.into(), dataport::PortVariant::InBound(dataport::InBound::with_value::<#tp>(my_value)))
}
},
PortVariant::InOutBound => quote! {
{
let my_value: #tp = core::str::FromStr::from_str({#value})?;
(#name.into(), dataport::PortVariant::InOutBound(dataport::InOutBound::with_value::<#tp>(my_value)))
}
},
PortVariant::OutBound => quote! {
{
let my_value: #tp = core::str::FromStr::from_str({#value})?;
(#name.into(), dataport::PortVariant::OutBound(dataport::OutBound::with_value::<#tp>(my_value)))
}
},
}
}
}
fn quote_from_char(variant: PortVariant, name: TokenStream, tp: Type, character: LitChar, parseable: bool) -> TokenStream {
if parseable {
match variant {
PortVariant::InBound => quote! {
{
let my_value: #tp = core::str::FromStr::from_str(#character.encode_utf8(&mut[0; 4]))?;
(#name.into(), dataport::PortVariant::InBound(dataport::InBound::with_value_parseable::<#tp>(my_value)))
}
},
PortVariant::InOutBound => quote! {
{
let my_value: #tp = core::str::FromStr::from_str(#character.encode_utf8(&mut[0; 4]))?;
(#name.into(), dataport::PortVariant::InOutBound(dataport::InOutBound::with_value_parseable::<#tp>(my_value)))
}
},
PortVariant::OutBound => quote! {
{
let my_value: #tp = core::str::FromStr::from_str(#character.encode_utf8(&mut[0; 4]))?;
(#name.into(), dataport::PortVariant::OutBound(dataport::OutBound::with_value_parseable::<#tp>(my_value)))
}
},
}
} else {
match variant {
PortVariant::InBound => quote! {
{
let my_value: #tp = core::str::FromStr::from_str(#character.encode_utf8(&mut[0; 4]))?;
(#name.into(), dataport::PortVariant::InBound(dataport::InBound::with_value::<#tp>(my_value)))
}
},
PortVariant::InOutBound => quote! {
{
let my_value: #tp = core::str::FromStr::from_str(#character.encode_utf8(&mut[0; 4]))?;
(#name.into(), dataport::PortVariant::InOutBound(dataport::InOutBound::with_value::<#tp>(my_value)))
}
},
PortVariant::OutBound => quote! {
{
let my_value: #tp = core::str::FromStr::from_str(#character.encode_utf8(&mut[0; 4]))?;
(#name.into(), dataport::PortVariant::OutBound(dataport::OutBound::with_value::<#tp>(my_value)))
}
},
}
}
}