#![warn(missing_docs)]
#[cfg(doctest)]
mod test_readme {
macro_rules! external_doc_test {
($x:expr) => {
#[doc = $x]
extern "C" {}
};
}
external_doc_test!(include_str!("../README.md"));
}
use quote::__private::TokenStream;
use quote::{ToTokens, TokenStreamExt};
use indexmap::IndexMap;
use std::collections::HashSet;
use std::error::Error as StdError;
use std::{cmp, fmt, hash};
const STD: [&str; 5] = ["std", "alloc", "core", "proc_macro", "test"];
const CRATE: [&str; 3] = ["self", "super", "crate"];
pub struct UseItems {
items: Vec<syn::ItemUse>,
}
impl UseItems {
#[inline]
pub fn from_items(items: Vec<syn::ItemUse>) -> Self {
Self { items }
}
#[inline]
pub fn into_inner(self) -> Vec<syn::ItemUse> {
self.items
}
}
impl syn::parse::Parse for UseItems {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let mut items = Vec::with_capacity(5);
while !input.is_empty() {
items.push(input.parse()?);
}
Ok(Self { items })
}
}
impl ToTokens for UseItems {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.append_all(&self.items);
}
}
impl IntoIterator for UseItems {
type Item = syn::ItemUse;
type IntoIter = std::vec::IntoIter<Self::Item>;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.items.into_iter()
}
}
#[derive(Clone, Debug, cmp::Eq, hash::Hash, cmp::PartialEq)]
enum UseKey {
Name(syn::Ident),
Rename(syn::Ident, syn::Ident),
Glob,
}
#[derive(Clone, Debug, cmp::Eq, hash::Hash, cmp::PartialEq)]
struct UseData {
vis: syn::Visibility,
attrs: Vec<syn::Attribute>,
has_leading_colons: bool,
}
impl UseData {
#[inline]
fn new(vis: syn::Visibility, attrs: Vec<syn::Attribute>, has_leading_colons: bool) -> Self {
Self {
vis,
attrs,
has_leading_colons,
}
}
}
#[derive(Clone, Default, Debug)]
struct UseValue {
nodes: HashSet<UseData>,
paths: UseBuilder,
}
#[derive(fmt::Debug)]
pub enum Error {
TopLevelGlob,
TopLevelGroup,
UseWithDiffAttr,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use Error::*;
match self {
TopLevelGlob => f.write_str("Top level glob is not allowed"),
TopLevelGroup => f.write_str("Top level group is not allowed"),
UseWithDiffAttr => f.write_str(
"Multiple copies of the same import with differing attributes are not allowed",
),
}
}
}
impl StdError for Error {}
#[derive(Clone, Default)]
struct ItemUseBuilder {
paths: Vec<syn::Ident>,
}
impl ItemUseBuilder {
#[inline]
fn add_path(&mut self, path: syn::Ident) {
self.paths.push(path);
}
fn into_item_use(mut self, names: Vec<UseKey>, data: UseData) -> syn::ItemUse {
let key_to_tree = |key| match key {
UseKey::Name(name) => syn::UseTree::Name(syn::UseName { ident: name }),
UseKey::Rename(name, rename) => syn::UseTree::Rename(syn::UseRename {
ident: name,
as_token: Default::default(),
rename,
}),
_ => unreachable!("Impossible glob"),
};
let mut tree = if names.contains(&UseKey::Glob) {
syn::UseTree::Glob(syn::UseGlob {
star_token: Default::default(),
})
} else if names.len() == 1 {
key_to_tree(names.into_iter().next().unwrap())
} else {
let items = names.into_iter().map(key_to_tree).collect();
syn::UseTree::Group(syn::UseGroup {
brace_token: Default::default(),
items,
})
};
while !self.paths.is_empty() {
let path = self.paths.remove(self.paths.len() - 1);
tree = syn::UseTree::Path(syn::UsePath {
ident: path,
colon2_token: Default::default(),
tree: Box::new(tree),
});
}
let leading_colon = if data.has_leading_colons {
Some(syn::token::Colon2::default())
} else {
None
};
syn::ItemUse {
attrs: data.attrs,
vis: data.vis,
use_token: Default::default(),
leading_colon,
tree,
semi_token: Default::default(),
}
}
}
pub type StdExtCrateUse = (Vec<syn::ItemUse>, Vec<syn::ItemUse>, Vec<syn::ItemUse>);
#[derive(Clone, Default, Debug)]
pub struct UseBuilder {
map: IndexMap<UseKey, UseValue>,
entries: usize,
}
impl UseBuilder {
pub fn from_uses(items: Vec<UseItems>) -> Self {
let mut root_map = Self {
map: IndexMap::new(),
entries: 0,
};
for inner_items in items {
for item in inner_items.items {
let data = UseData::new(item.vis, item.attrs, item.leading_colon.is_some());
root_map.parse_tree(item.tree, data);
}
}
root_map
}
fn add_node(&mut self, entry: UseKey, data: UseData) {
match self.map.entry(entry) {
indexmap::map::Entry::Occupied(mut e) => {
e.get_mut().nodes.insert(data);
}
indexmap::map::Entry::Vacant(e) => {
self.entries += 1;
let mut u = UseValue::default();
u.nodes.insert(data);
e.insert(u);
}
}
}
fn add_path(&mut self, entry: UseKey) -> &mut UseBuilder {
match self.map.entry(entry) {
indexmap::map::Entry::Occupied(e) => &mut e.into_mut().paths,
indexmap::map::Entry::Vacant(e) => {
let u = UseValue::default();
&mut e.insert(u).paths
}
}
}
fn parse_tree(&mut self, tree: syn::UseTree, data: UseData) {
use syn::UseTree::*;
match tree {
Path(syn::UsePath { ident, tree, .. }) => {
let map = self.add_path(UseKey::Name(ident));
map.parse_tree(syn::UseTree::clone(&*tree), data);
}
Name(syn::UseName { ident }) => {
self.add_node(UseKey::Name(ident), data);
}
Rename(syn::UseRename { ident, rename, .. }) => {
self.add_node(UseKey::Rename(ident, rename), data);
}
Glob(syn::UseGlob { .. }) => {
self.add_node(UseKey::Glob, data);
}
Group(syn::UseGroup { items, .. }) => {
for item in items {
self.parse_tree(item, data.clone());
}
}
}
}
fn next_map(
use_map: UseBuilder,
builder: ItemUseBuilder,
items: &mut Vec<syn::ItemUse>,
) -> Result<(), Error> {
let mut map: IndexMap<UseData, Vec<UseKey>> = IndexMap::new();
let len = use_map.map.len();
for (key, value) in use_map.map {
if let UseKey::Name(path) = key.clone() {
let mut builder = builder.clone();
builder.add_path(path);
if let err @ Err(_) = Self::next_map(value.paths, builder, items) {
return err;
}
}
if !value.nodes.is_empty() {
if value.nodes.len() > 1 {
return Err(Error::UseWithDiffAttr);
}
match map.entry(value.nodes.into_iter().next().unwrap()) {
indexmap::map::Entry::Occupied(mut e) => {
e.get_mut().push(key);
}
indexmap::map::Entry::Vacant(e) => {
let mut set = Vec::with_capacity(len);
set.push(key);
e.insert(set);
}
}
}
}
for (data, names) in map {
let item = builder.clone().into_item_use(names, data);
items.push(item);
}
Ok(())
}
pub fn into_items(self) -> Result<Vec<syn::ItemUse>, Error> {
let mut items = Vec::with_capacity(self.entries);
let builder = ItemUseBuilder::default();
Self::next_map(self, builder, &mut items)?;
Ok(items)
}
pub fn into_items_sections(self) -> Result<StdExtCrateUse, Error> {
let items = self.into_items()?;
let mut std_uses = Vec::with_capacity(items.len());
let mut extern_uses = Vec::with_capacity(items.len());
let mut crate_uses = Vec::with_capacity(items.len());
for item in items {
use syn::UseTree::*;
match &item.tree {
Path(syn::UsePath { ident, .. })
| Name(syn::UseName { ident })
| Rename(syn::UseRename { ident, .. }) => {
let name = &*ident.to_string();
if STD.contains(&name) {
std_uses.push(item);
} else if CRATE.contains(&name) {
crate_uses.push(item);
} else {
extern_uses.push(item);
};
}
Glob(_) => return Err(Error::TopLevelGlob),
Group(_) => {}
}
}
Ok((std_uses, extern_uses, crate_uses))
}
}
#[cfg(test)]
mod tests {
use assert_unordered::assert_eq_unordered;
use quote::quote;
use crate::{UseBuilder, UseItems};
fn make_builder() -> UseBuilder {
let use1 = quote! {
use crate::Test;
use std::error::Error as StdError;
use std::fmt::Debug;
};
let use2 = quote! {
use syn::ItemUse;
use std::fmt::Display;
use crate::*;
};
let items1: UseItems = syn::parse2(use1).unwrap();
let items2: UseItems = syn::parse2(use2).unwrap();
UseBuilder::from_uses(vec![items1, items2])
}
#[test]
fn items() {
let builder = make_builder();
let uses = builder.into_items().unwrap();
let expected = quote! {
use crate::*;
use std::error::Error as StdError;
use std::fmt::{Debug, Display};
use syn::ItemUse;
};
let expected = syn::parse2::<UseItems>(expected).unwrap().into_inner();
assert_eq_unordered!(expected, uses);
}
#[test]
fn items_separated() {
let builder = make_builder();
let (std_use, ext_use, crate_use) = builder.into_items_sections().unwrap();
let std_expected = quote! {
use std::error::Error as StdError;
use std::fmt::{Debug, Display};
};
let std_expected = syn::parse2::<UseItems>(std_expected).unwrap().into_inner();
let ext_expected = quote! {
use syn::ItemUse;
};
let ext_expected = syn::parse2::<UseItems>(ext_expected).unwrap().into_inner();
let crate_expected = quote! {
use crate::*;
};
let crate_expected = syn::parse2::<UseItems>(crate_expected)
.unwrap()
.into_inner();
assert_eq_unordered!(std_expected, std_use);
assert_eq_unordered!(ext_expected, ext_use);
assert_eq_unordered!(crate_expected, crate_use);
}
}