Deprecated: The each() function is deprecated. This message will be suppressed on further calls in /home/zhenxiangba/zhenxiangba.com/public_html/phproxy-improved-master/index.php on line 456
multiversion-macros 0.1.0 - Docs.rs
[go: Go Back, main page]

multiversion-macros 0.1.0

Implementation crate for multiversion
Documentation
use crate::dispatcher::Dispatcher;
use crate::meta::{parse_attributes, parse_crate_path};
use crate::target::Target;
use crate::util;
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use std::convert::{TryFrom, TryInto};
use syn::{parse_quote, spanned::Spanned, Error, ItemFn, Lit, Meta, NestedMeta, Path};

enum Specialization {
    Clone {
        target: Target,
    },
    Override {
        target: Target,
        func: Path,
        is_unsafe: bool,
    },
}

struct Function {
    specializations: Vec<Specialization>,
    func: ItemFn,
    associated: bool,
    crate_path: Path,
}

impl TryFrom<Function> for Dispatcher {
    type Error = Error;

    fn try_from(item: Function) -> Result<Self, Self::Error> {
        let (_, args) = util::normalize_signature(&item.func.sig);
        let fn_params = util::fn_params(&item.func.sig);
        Ok(Self {
            specializations: item
                .specializations
                .iter()
                .map(|specialization| match specialization {
                    Specialization::Clone { target, .. } => crate::dispatcher::Specialization {
                        target: target.clone(),
                        block: item.func.block.as_ref().clone(),
                        normalize: false,
                    },
                    Specialization::Override {
                        target,
                        func,
                        is_unsafe,
                    } => {
                        let call = quote! { #func::<#(#fn_params),*>(#(#args),*) };
                        let call = if item.associated {
                            quote! { Self::#call }
                        } else {
                            call
                        };
                        crate::dispatcher::Specialization {
                            target: target.clone(),
                            block: if *is_unsafe {
                                parse_quote! {
                                    { unsafe { #call } }
                                }
                            } else {
                                parse_quote! {
                                    { #call }
                                }
                            },
                            normalize: true,
                        }
                    }
                })
                .collect(),
            attrs: item.func.attrs,
            vis: item.func.vis,
            sig: item.func.sig,
            default: *item.func.block,
            associated: item.associated,
            crate_path: item.crate_path,
        })
    }
}

impl TryFrom<ItemFn> for Function {
    type Error = Error;

    fn try_from(mut func: ItemFn) -> Result<Self, Self::Error> {
        let associated = crate::util::is_associated_fn(&mut func);
        let mut multiversioned = Function {
            specializations: Vec::new(),
            associated,
            crate_path: parse_quote!(multiversion),
            func: ItemFn {
                attrs: Vec::new(),
                ..func
            },
        };

        multiversioned.func.attrs = parse_attributes(func.attrs.drain(..), |path, nested| {
            Ok(match path.to_string().as_str() {
                "crate_path" => {
                    multiversioned.crate_path = parse_crate_path(nested)?;
                    true
                }
                "clone" => {
                    meta_parser! {
                        nested => [
                            "target" => target,
                        ]
                    }
                    multiversioned.specializations.push(Specialization::Clone {
                        target: target
                            .ok_or_else(|| Error::new(nested.span(), "expected key 'target'"))?
                            .try_into()?,
                    });
                    true
                }
                "specialize" => {
                    meta_parser! {
                        nested => [
                            "target" => target,
                            "fn" => func,
                            "unsafe" => is_unsafe,
                        ]
                    }
                    multiversioned
                        .specializations
                        .push(Specialization::Override {
                            target: target
                                .ok_or_else(|| Error::new(nested.span(), "expected key 'target'"))?
                                .try_into()?,
                            func: match func
                                .ok_or_else(|| Error::new(nested.span(), "expected key 'fn'"))?
                            {
                                Lit::Str(s) => s.parse(),
                                lit => Err(Error::new(lit.span(), "expected literal string")),
                            }?,
                            is_unsafe: is_unsafe.map_or(Ok(false), |lit| match lit {
                                Lit::Bool(b) => Ok(b.value),
                                lit => Err(Error::new(lit.span(), "expected literal bool")),
                            })?,
                        });
                    true
                }
                _ => false,
            })
        })?;

        Ok(multiversioned)
    }
}

pub(crate) fn make_multiversioned_fn(func: ItemFn) -> Result<TokenStream, syn::Error> {
    let function: Function = func.try_into()?;
    let dispatcher: Dispatcher = function.try_into()?;
    Ok(dispatcher.to_token_stream())
}