layered_crate/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use proc_macro::TokenStream;
4use syn::parse_macro_input;
5
6mod graph;
7mod import;
8mod layers;
9
10/// See [`crate documentation`](crate)
11#[proc_macro_attribute]
12pub fn layers(_attr: TokenStream, input: TokenStream) -> TokenStream {
13    let input = parse_macro_input!(input as syn::ItemMod);
14    match layers::expand(input) {
15        Ok(expanded) => expanded,
16        Err(err) => err.to_compile_error().into(),
17    }
18}
19
20/// The import attribute will transform a `use` item to use
21/// `self` and `crate` to refer to the current module and its dependencies
22/// as defined by the [`#[layers]`](crate) attribute.
23///
24/// # Example
25/// Assuming we organize our imports in the following order:
26/// - std
27/// - external dependencies
28/// - dependencies within our crate
29///
30/// ```rust,ignore
31/// use std::fs::File;
32/// // .. other std dependencies
33///
34/// use serde_json::Value;
35/// // .. other external dependencies
36///
37/// #[layered_crate::import]
38/// use my_layer::{
39/// //  ^ `my_layer` refers to the current layer as defined in lib.rs
40///     super::my_dep::MyType,
41///  // ^ `super` in this context is used to refer to this layer's dependencies
42///  //   the above is equivalent to `use crate::my_dep::MyType`,
43///  //   but only works if `my_dep` is declared as a dependency
44///     super::{
45///         my_other_dep::MyOtherType,
46///         my_more_dep::MyMoreType,
47///     },
48///  // ^ other forms work as well
49///     MyDepInThisLayer, // ... import rest from the current layer
50///
51///     self::MyTypeInThisLayer,
52///  // ^ you can also use `self` to refer to the current module
53///  //   if you prefer
54///
55/// };
56///
57/// // any item outside of the macro isn't transformed, and you can
58/// // use this to bypass the layer restrictions
59/// use crate::unchecked;
60///
61/// // if you actually need to import from `super`, also put it
62/// // outside
63/// use super::*;
64/// ```
65///
66/// # Other checks
67/// - It will make sure all of your `super` and `self` (imports from current layer) are grouped
68///   together for readability
69///   - If you actually want to group the imports in another way, consider
70///     using multiple `import` attributes
71/// - It will make sure imports from the current layer either all use `self`
72///   or not use `self` for consistency, the `self` import by itself is always allowed
73#[proc_macro_attribute]
74pub fn import(_attr: TokenStream, input: TokenStream) -> TokenStream {
75    let input = parse_macro_input!(input as syn::ItemUse);
76    match import::expand(input) {
77        Ok(expanded) => expanded,
78        Err(err) => err.to_compile_error().into(),
79    }
80}