syn-file-expand 0.3.0

Library to load full source code of multi-file crates
Documentation
use proc_macro2;
use proc_macro2::TokenStream;
use quote::ToTokens;
use syn::MacroDelimiter;
use syn::Meta;
use syn::MetaList;
use syn::MetaNameValue;
use syn::Token;
use syn::punctuated::Punctuated;


use super::AttrParseError;


use std::path::PathBuf;


pub(crate) fn read_and_process_attributes(
    input_attrs: &[syn::Attribute],
    path_attrs: &mut Vec<(PathBuf, Option<TokenStream>)>,
    attrs: &mut Vec<syn::Attribute>,
    cfg_attrs: &mut Vec<TokenStream>,
) -> Result<(), AttrParseError> {
    for attr in input_attrs {
        match &attr.meta {
            Meta::List(MetaList { path, delimiter, tokens }) if path.is_ident("cfg") => {
                if !matches!(delimiter, MacroDelimiter::Paren(..)) {
                    return Err(AttrParseError::MalformedCfg);
                }
                cfg_attrs.push(tokens.clone());
            }
            Meta::NameValue(MetaNameValue { path, eq_token: _, value }) if path.is_ident("path") => {
                let path = match value {
                    syn::Expr::Lit(lit) => match &lit.lit {
                        syn::Lit::Str(x) => x.value(),
                        _ => return Err(AttrParseError::SecondTokenIsNotStringLiteral),
                    }
                    _ => return Err(AttrParseError::SecondTokenIsNotStringLiteral),
                };
                path_attrs.push((PathBuf::from(path), None));
            }
            Meta::Path(path) | Meta::List(MetaList { path, .. }) if path.is_ident("path") => {
                return Err(AttrParseError::FirstTokenIsNotEqualSign);
            }
            
            Meta::Path(path) | Meta::NameValue(MetaNameValue { path, .. }) if path.is_ident("cfg_attr") => {
                return Err(AttrParseError::CfgAttrNotRoundGroup);
            }

            Meta::List(MetaList { path, delimiter, .. }) if path.is_ident("cfg_attr") => {
                if !matches!(delimiter, MacroDelimiter::Paren(..)) {
                    return Err(AttrParseError::CfgAttrNotRoundGroup);
                }

                let Ok(nested) = attr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated) else {
                    return Err(AttrParseError::MalformedCfg)
                };

                if nested.len() != 2 {
                    return Err(AttrParseError::CfgAttrNotTwoParams);
                }

                let condition = &nested[0];
                let potential_path = &nested[1];

                if ! potential_path.path().is_ident("path") {
                    attrs.push(attr.clone());
                    continue;
                }

                let path = match potential_path {
                    Meta::NameValue(MetaNameValue { path: _, eq_token: _, value }) => {
                        match value {
                            syn::Expr::Lit(lit) => match &lit.lit {
                                syn::Lit::Str(x) => x.value(),
                                _ => return Err(AttrParseError::SecondTokenIsNotStringLiteral),
                            }
                            _ => return Err(AttrParseError::SecondTokenIsNotStringLiteral),
                        }
                    }
                    _ => return Err(AttrParseError::FirstTokenIsNotEqualSign),
                };

                path_attrs.push((PathBuf::from(path), Some(condition.to_token_stream())));
            }

            _ => attrs.push(attr.clone()),
        }
    }
    Ok(())
}