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/// # Workaround for single `super` import
67///
68/// Currently, rust-analyzer does not process "malformed" imports, including
69/// `super` in the middle of the import:
70/// ```rust,ignore
71/// #[layered_crate::import]
72/// use my_layer::super::my_dep::MyType;
73/// ```
74///
75/// While `layered-crate` transforms this correctly, rust-analyzer will not
76/// provide LSP functionality for this import (e.g. goto definition).
77///
78/// If this is an issue for you, `super_` is allowed as a workaround:
79/// ```rust,ignore
80/// #[layered_crate::import]
81/// use my_layer::super_::my_dep::MyType;
82/// ```
83///
84/// # Other checks
85/// - It will make sure all of your `super` and `self` (imports from current layer) are grouped
86/// together for readability
87/// - If you actually want to group the imports in another way, consider
88/// using multiple `import` attributes
89/// - It will make sure imports from the current layer either all use `self`
90/// or not use `self` for consistency, the `self` import by itself is always allowed
91#[proc_macro_attribute]
92pub fn import(_attr: TokenStream, input: TokenStream) -> TokenStream {
93 let input = parse_macro_input!(input as syn::ItemUse);
94 match import::expand(input) {
95 Ok(expanded) => expanded,
96 Err(err) => err.to_compile_error().into(),
97 }
98}