holy 0.2.1

Holy is a proc-macro library that provides helper macros.
Documentation
use quote::quote;
use syn::{Attribute, Expr, Lit, Meta, Token, Visibility};
use syn::parse::Parser;
use syn::punctuated::Punctuated;

pub fn determine_visibility(
	vis: &Visibility,
	attrs: &[Attribute],
) -> Result<proc_macro2::TokenStream, syn::Error> {
	if let Some(override_vis) = attrs
		.iter()
		.find(|attr| attr.path().is_ident("holy"))
		.and_then(parse_visibility_override)
	{
		Ok(override_vis)
	} else {
		match vis {
			Visibility::Public(_) => Ok(quote! { pub }),
			Visibility::Restricted(restricted) => Ok(quote! { #restricted }),
			Visibility::Inherited => Ok(quote! {}),
		}
	}
}

fn parse_visibility_override(attr: &Attribute) -> Option<proc_macro2::TokenStream> {
	let Meta::List(meta_list) = &attr.meta else {
		return None;
	};

	let nested = Punctuated::<Meta, Token![,]>::parse_terminated
		.parse2(meta_list.tokens.clone())
		.ok()?;

	for meta in &nested {
		if let Meta::Path(path) = meta {
			if path.is_ident("public") {
				return Some(quote! { pub });
			}
			if path.is_ident("private") {
				return Some(quote! {});
			}
		}
	}
	None
}

pub fn should_skip(attrs: &[Attribute]) -> bool {
	has_holy_argument(attrs, "skip")
}

pub fn has_holy_argument(attrs: &[Attribute], arg_name: &str) -> bool {
	attrs.iter().any(|attr| {
		if !attr.path().is_ident("holy") {
			return false;
		}
		let Meta::List(meta_list) = &attr.meta else {
			return false;
		};
		let Ok(nested) = Punctuated::<Meta, Token![,]>::parse_terminated
			.parse2(meta_list.tokens.clone())
		else {
			return false;
		};
		nested
			.iter()
			.any(|meta| matches!(meta, Meta::Path(path) if path.is_ident(arg_name)))
	})
}

pub fn get_holy_string_value(attrs: &[Attribute], key: &str) -> Option<(String, proc_macro2::Span)> {
	attrs.iter().find_map(|attr| {
		if !attr.path().is_ident("holy") {
			return None;
		}
		let Meta::List(meta_list) = &attr.meta else {
			return None;
		};
		let nested = Punctuated::<Meta, Token![,]>::parse_terminated
			.parse2(meta_list.tokens.clone())
			.ok()?;
		for meta in &nested {
			if let Meta::NameValue(nv) = meta {
				if nv.path.is_ident(key) {
					if let Expr::Lit(expr_lit) = &nv.value {
						if let Lit::Str(lit_str) = &expr_lit.lit {
							return Some((lit_str.value(), lit_str.span()));
						}
					}
				}
			}
		}
		None
	})
}