#![doc = include_str!("../README.md")]
use std::mem;
use proc_macro::TokenStream;
use quote::quote;
use syn::token::Pub;
use syn::*;
const CRATE_NAME: &str = env!("CARGO_PKG_NAME");
macro_rules! bail {
($span: expr, $($arg:tt)*) => {
return Err(syn::Error::new_spanned($span, format!($($arg)*)))
}
}
fn is_exclude(attrs: &mut Vec<Attribute>) -> Result<bool> {
let mut is_exclude = false;
for attr in mem::take(attrs) {
if attr.path().is_ident(CRATE_NAME) {
let arg = attr.parse_args::<Ident>()?;
if arg != "exclude" {
bail!(&arg, "unknown {CRATE_NAME} attribute `{arg}`");
}
if is_exclude {
bail!(attr, "duplicate {CRATE_NAME} attribute `exclude`");
}
is_exclude = true;
} else {
attrs.push(attr);
}
}
Ok(is_exclude)
}
fn make_pub(vis: &mut Visibility) {
*vis = Visibility::Public(Pub::default());
}
fn explore_item(item: &mut Item, recursive: bool) -> Result<()> {
match item {
Item::Const(ItemConst { vis, attrs, .. })
| Item::Enum(ItemEnum { vis, attrs, .. })
| Item::Fn(ItemFn { vis, attrs, .. })
| Item::Static(ItemStatic { vis, attrs, .. })
| Item::Trait(ItemTrait { vis, attrs, .. })
| Item::TraitAlias(ItemTraitAlias { vis, attrs, .. })
| Item::Type(ItemType { vis, attrs, .. }) => {
if !is_exclude(attrs)? {
make_pub(vis);
}
}
Item::ExternCrate(_) | Item::Macro(_) | Item::Use(_) => (),
Item::ForeignMod(ItemForeignMod { attrs, items, .. }) => {
if !is_exclude(attrs)? {
for item in items {
match item {
ForeignItem::Fn(ForeignItemFn { vis, attrs, .. })
| ForeignItem::Static(ForeignItemStatic { vis, attrs, .. })
| ForeignItem::Type(ForeignItemType { vis, attrs, .. }) => {
if !is_exclude(attrs)? {
make_pub(vis);
}
}
ForeignItem::Macro(_) => (),
_ => (),
}
}
}
}
Item::Impl(ItemImpl {
attrs,
trait_,
items,
..
}) => {
if trait_.is_none() && !is_exclude(attrs)? {
for item in items {
match item {
ImplItem::Const(ImplItemConst { vis, attrs, .. })
| ImplItem::Fn(ImplItemFn { vis, attrs, .. })
| ImplItem::Type(ImplItemType { vis, attrs, .. }) => {
if !is_exclude(attrs)? {
make_pub(vis);
}
}
ImplItem::Macro(_) => (),
_ => (),
}
}
}
}
Item::Mod(ItemMod {
vis,
attrs,
content: Some((_, content)),
..
}) => {
if !is_exclude(attrs)? {
make_pub(vis);
if recursive {
for item in content {
explore_item(item, recursive)?;
}
}
}
}
Item::Struct(ItemStruct {
vis, attrs, fields, ..
}) => {
if !is_exclude(attrs)? {
make_pub(vis);
match fields {
Fields::Named(FieldsNamed { named: fields, .. })
| Fields::Unnamed(FieldsUnnamed {
unnamed: fields, ..
}) => {
for Field { vis, attrs, .. } in fields {
if !is_exclude(attrs)? {
make_pub(vis);
}
}
}
Fields::Unit => (),
}
}
}
Item::Union(ItemUnion {
vis,
attrs,
fields: FieldsNamed { named: fields, .. },
..
}) => {
if !is_exclude(attrs)? {
make_pub(vis);
for Field { vis, attrs, .. } in fields {
if !is_exclude(attrs)? {
make_pub(vis);
}
}
}
}
_ => (),
}
Ok(())
}
fn make_fully_pub(attr: Option<Ident>, item: &mut Item) -> Result<()> {
let recursive = match attr {
Some(ident) if ident == "recursive" => true,
Some(ident) => bail!(ident, "invalid argument to `{CRATE_NAME}` attribute macro"),
None => false,
};
explore_item(item, recursive)
}
#[proc_macro_attribute]
pub fn fully_pub(attr: TokenStream, item: TokenStream) -> TokenStream {
let attr = parse_macro_input!(attr as Option<Ident>);
let mut item = parse_macro_input!(item as Item);
match make_fully_pub(attr, &mut item) {
Ok(_) => quote! { #item }.into(),
Err(e) => e.to_compile_error().into(),
}
}