bon_macros/util/visibility.rs
1use crate::util::prelude::*;
2
3pub(crate) trait VisibilityExt {
4 /// Returns [`syn::Visibility`] that is equivalent to the current visibility
5 /// but for an item that is inside of the child module. This basically does
6 /// the following conversions.
7 ///
8 /// - `pub` -> `pub` (unchanged)
9 /// - `pub(crate)` -> `pub(crate)` (unchanged)
10 /// - `pub(self)` or ` ` (default private visibility) -> `pub(super)`
11 /// - `pub(super)` -> `pub(in super::super)`
12 /// - `pub(in relative::path)` -> `pub(in super::relative::path)`
13 /// - `pub(in ::absolute::path)` -> `pub(in ::absolute::path)` (unchanged)
14 /// - `pub(in crate::path)` -> `pub(in crate::path)` (unchanged)
15 /// - `pub(in self::path)` -> `pub(in super::path)`
16 /// - `pub(in super::path)` -> `pub(in super::super::path)`
17 ///
18 /// Note that absolute paths in `pub(in ...)` are not supported with Rust 2018+,
19 /// according to the [Rust reference]:
20 ///
21 /// > Edition Differences: Starting with the 2018 edition, paths for pub(in path)
22 /// > must start with crate, self, or super. The 2015 edition may also use paths
23 /// > starting with :: or modules from the crate root.
24 ///
25 /// # Errors
26 ///
27 /// This function may return an error if it encounters some unexpected syntax.
28 /// For example, some syntax that isn't known to the latest version of Rust
29 /// this code was written for.
30 ///
31 /// [Rust reference]: https://doc.rust-lang.org/reference/visibility-and-privacy.html#pubin-path-pubcrate-pubsuper-and-pubself
32 fn into_equivalent_in_child_module(self) -> Result<syn::Visibility>;
33}
34
35impl VisibilityExt for syn::Visibility {
36 fn into_equivalent_in_child_module(mut self) -> Result<syn::Visibility> {
37 match &mut self {
38 Self::Public(_) => Ok(self),
39 Self::Inherited => Ok(syn::parse_quote!(pub(super))),
40 Self::Restricted(syn::VisRestricted {
41 path,
42 in_token: None,
43 ..
44 }) => {
45 if path.is_ident("crate") {
46 return Ok(self);
47 }
48
49 if path.is_ident("super") {
50 return Ok(syn::parse_quote!(pub(in super::#path)));
51 }
52
53 if path.is_ident("self") {
54 return Ok(syn::parse_quote!(pub(super)));
55 }
56
57 bail!(
58 &self,
59 "Expected either `crate` or `super` or `in some::path` inside of \
60 `pub(...)` but got something else. This may be because a new \
61 syntax for visibility was released in a newer Rust version, \
62 but this crate doesn't support it."
63 );
64 }
65 Self::Restricted(syn::VisRestricted {
66 path,
67 in_token: Some(_),
68 ..
69 }) => {
70 if path.leading_colon.is_some() {
71 return Ok(self);
72 }
73
74 if path.starts_with_segment("crate") {
75 return Ok(self);
76 }
77
78 if let Some(first_segment) = path.segments.first_mut() {
79 if first_segment.ident == "self" {
80 let span = first_segment.ident.span();
81 *first_segment = syn::parse_quote_spanned!(span=>super);
82 return Ok(self);
83 }
84 }
85
86 path.segments.insert(0, syn::parse_quote!(super));
87
88 Ok(self)
89 }
90 }
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 use syn::parse_quote as pq;
99
100 #[test]
101 fn all_tests() {
102 #[track_caller]
103 // One less `&` character to type in assertions
104 #[allow(clippy::needless_pass_by_value)]
105 fn test(vis: syn::Visibility, expected: syn::Visibility) {
106 let actual = vis.into_equivalent_in_child_module().unwrap();
107 assert!(
108 actual == expected,
109 "got:\nactual: {}\nexpected: {}",
110 actual.to_token_stream(),
111 expected.to_token_stream()
112 );
113 }
114
115 test(pq!(pub), pq!(pub));
116 test(pq!(pub(crate)), pq!(pub(crate)));
117 test(pq!(pub(self)), pq!(pub(super)));
118 test(pq!(), pq!(pub(super)));
119 test(pq!(pub(super)), pq!(pub(in super::super)));
120
121 test(
122 pq!(pub(in relative::path)),
123 pq!(pub(in super::relative::path)),
124 );
125 test(pq!(pub(in crate::path)), pq!(pub(in crate::path)));
126 test(pq!(pub(in self::path)), pq!(pub(in super::path)));
127 test(pq!(pub(in super::path)), pq!(pub(in super::super::path)));
128
129 test(pq!(pub(in ::absolute::path)), pq!(pub(in ::absolute::path)));
130 }
131}