1use proc_macro2::TokenStream;
4use quote::ToTokens;
5#[allow(unused_imports)]
6use syn::{
7 self,
8 token::{Colon2, Crate, In, Paren, Pub, Super},
9 Path, Visibility,
10};
11
12use core_extensions::SelfOps;
13
14use std::cmp::{Ordering, PartialOrd};
15
16#[derive(Copy, Clone, Debug)]
18pub(crate) struct RelativeVis<'a> {
19 visibility_kind: VisibilityKind<'a>,
20 nesting: u8,
21}
22
23#[derive(Copy, Clone, Debug, Eq, PartialEq)]
25pub enum VisibilityKind<'a> {
26 Private,
27 Super {
29 nth_supermod: usize,
30 },
31 Absolute(&'a Path),
32 Crate,
33 Public,
34}
35
36impl<'a> VisibilityKind<'a> {
37 pub fn new(vis: &'a Visibility) -> Self {
38 match vis {
39 Visibility::Public { .. } => VisibilityKind::Public,
40 Visibility::Crate { .. } => VisibilityKind::Crate,
41 Visibility::Inherited { .. } => VisibilityKind::Private,
42 Visibility::Restricted(restricted) => {
43 let path = &restricted.path;
44 let is_global = restricted.path.leading_colon.is_some();
45 let path_seg_0 = path.segments.first();
46 let is_crate = path_seg_0.map_or(false, |x| x.ident == "crate");
47 if is_global || is_crate {
48 if is_crate && path.segments.len() == 1 {
49 VisibilityKind::Crate
50 } else {
51 VisibilityKind::Absolute(path)
52 }
53 } else if path_seg_0.map_or(false, |x| x.ident == "self") {
54 assert!(
55 path.segments.len() == 1,
56 "paths in pub(...) that start with 'self' \
57 must not be followed by anything:\n{:?}.\n",
58 path
59 );
60
61 VisibilityKind::Private
62 } else if path_seg_0.map_or(false, |x| x.ident == "super") {
63 assert!(
64 path.segments.iter().all(|segment| segment.ident == "super"),
65 "paths in pub(...) that start with 'super' \
66 must only be followed by 'super':\n{:?}.\n",
67 path
68 );
69
70 VisibilityKind::Super {
71 nth_supermod: path.segments.len(),
72 }
73 } else {
74 VisibilityKind::Absolute(path)
75 }
76 }
77 }
78 }
79
80 pub(crate) fn submodule_level(self, nesting: u8) -> RelativeVis<'a> {
86 RelativeVis {
87 visibility_kind: self,
88 nesting,
89 }
90 }
91}
92
93impl<'a> ToTokens for VisibilityKind<'a> {
94 fn to_tokens(&self, tokens: &mut TokenStream) {
95 self.submodule_level(0).to_tokens(tokens)
96 }
97}
98impl<'a> ToTokens for RelativeVis<'a> {
99 fn to_tokens(&self, tokens: &mut TokenStream) {
100 if self.visibility_kind == VisibilityKind::Private && self.nesting == 0 {
101 return;
102 }
103
104 match self.visibility_kind {
105 VisibilityKind::Private | VisibilityKind::Super { .. } => {
106 let supermod = match self.visibility_kind {
107 VisibilityKind::Private => 0,
108 VisibilityKind::Super { nth_supermod } => nth_supermod,
109 _ => unreachable!(),
110 };
111
112 let nesting = supermod + self.nesting as usize;
113
114 Pub::default().to_tokens(tokens);
115 Paren::default().surround(tokens, |tokens| {
116 In::default().to_tokens(tokens);
117
118 let mut iter = (0..nesting).peekable();
119 while iter.next().is_some() {
120 Super::default().to_tokens(tokens);
121 if iter.peek().is_some() {
122 Colon2::default().to_tokens(tokens);
123 }
124 }
125 });
126 }
127 VisibilityKind::Absolute(path) => {
128 Pub::default().to_tokens(tokens);
129 Paren::default().surround(tokens, |tokens| {
130 In::default().to_tokens(tokens);
131 path.to_tokens(tokens);
132 });
133 }
134 VisibilityKind::Crate => {
135 Pub::default().to_tokens(tokens);
136 Paren::default().surround(tokens, |tokens| {
137 Crate::default().to_tokens(tokens);
138 });
139 }
140 VisibilityKind::Public => {
141 Pub::default().to_tokens(tokens);
142 }
143 }
144 }
145}
146
147#[derive(Eq, PartialEq, PartialOrd, Ord)]
150enum VKDiscr {
151 Private,
152 Super,
153 Absolute,
154 Crate,
155 Public,
156}
157
158impl<'a> VisibilityKind<'a> {
159 fn to_discriminant(self) -> VKDiscr {
160 match self {
161 VisibilityKind::Private { .. } => VKDiscr::Private,
162 VisibilityKind::Super { .. } => VKDiscr::Super,
163 VisibilityKind::Absolute { .. } => VKDiscr::Absolute,
164 VisibilityKind::Crate { .. } => VKDiscr::Crate,
165 VisibilityKind::Public { .. } => VKDiscr::Public,
166 }
167 }
168}
169
170impl<'a> PartialOrd for VisibilityKind<'a> {
171 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
172 use self::VisibilityKind as VK;
173
174 match self.to_discriminant().cmp(&other.to_discriminant()) {
175 expr @ Ordering::Less | expr @ Ordering::Greater => return Some(expr),
176 _ => {}
177 }
178
179 match (self, other) {
180 (&VK::Super { nth_supermod: nth0 }, &VK::Super { nth_supermod: nth1 }) => {
181 nth0.partial_cmp(&nth1)
182 }
183 (&VK::Absolute(path0), &VK::Absolute(path1)) => {
184 if path0
185 .segments
186 .iter()
187 .zip(&path1.segments)
188 .all(|(l, r)| l.ident == r.ident)
189 {
190 path0
191 .segments
192 .len()
193 .cmp(&path1.segments.len())
194 .reverse()
195 .piped(Some)
196 } else {
197 None
198 }
199 }
200 _ => Some(Ordering::Equal),
201 }
202 }
203}
204
205#[cfg(all(test, feature = "passed_tests"))]
207mod tests {
208 use super::*;
209
210 #[test]
211 fn test_ordering() {
212 macro_rules! new_visibility {
213 (
214 $ident:ident=$string:expr
215 ) => {
216 let $ident: Visibility = syn::parse_str($string).expect($string);
217 let $ident = VisibilityKind::new(&$ident).kind;
218 };
219 }
220
221 new_visibility! {vis_self="pub(self)"}
222 new_visibility! {vis_self_b=""}
223
224 new_visibility! {vis_super="pub(super)"}
225 new_visibility! {vis_super_1="pub(in super::super)"}
226 new_visibility! {vis_super_2="pub(in super::super::super)"}
227
228 new_visibility! {vis_mod1_mod2="pub(in ::mod1::mod2)"}
229 new_visibility! {vis_mod1_mod2_mod4="pub(in ::mod1::mod2::mod4)"}
230 new_visibility! {vis_mod1_mod3="pub(in ::mod1::mod3)"}
231 new_visibility! {vis_mod1="pub(in ::mod1)"}
232
233 new_visibility! {vis_crate="crate"}
234 new_visibility! {vis_crate_1="pub(crate)"}
235
236 new_visibility! {vis_pub="pub"}
237
238 assert_eq!(vis_self, vis_self_b);
239 assert_eq!(vis_crate, vis_crate_1);
240
241 assert_eq!(vis_self.partial_cmp(&vis_super), Some(Ordering::Less));
242 assert_eq!(vis_self.partial_cmp(&vis_super_1), Some(Ordering::Less));
243 assert_eq!(vis_self.partial_cmp(&vis_super_2), Some(Ordering::Less));
244 assert_eq!(vis_self.partial_cmp(&vis_mod1), Some(Ordering::Less));
245 assert_eq!(vis_self.partial_cmp(&vis_mod1_mod2), Some(Ordering::Less));
246 assert_eq!(vis_self.partial_cmp(&vis_crate), Some(Ordering::Less));
247 assert_eq!(vis_self.partial_cmp(&vis_pub), Some(Ordering::Less));
248
249 assert_eq!(vis_super.partial_cmp(&vis_super_1), Some(Ordering::Less));
250 assert_eq!(vis_super.partial_cmp(&vis_super_2), Some(Ordering::Less));
251 assert_eq!(vis_super_1.partial_cmp(&vis_super_2), Some(Ordering::Less));
252 assert_eq!(vis_super_2.partial_cmp(&vis_mod1), Some(Ordering::Less));
253 assert_eq!(
254 vis_super_2.partial_cmp(&vis_mod1_mod2),
255 Some(Ordering::Less)
256 );
257 assert_eq!(vis_super_2.partial_cmp(&vis_crate), Some(Ordering::Less));
258 assert_eq!(vis_super_2.partial_cmp(&vis_pub), Some(Ordering::Less));
259
260 assert_eq!(
261 vis_mod1_mod2_mod4.partial_cmp(&vis_mod1_mod2),
262 Some(Ordering::Less)
263 );
264 assert_eq!(
265 vis_mod1_mod2.partial_cmp(&vis_mod1_mod2_mod4),
266 Some(Ordering::Greater)
267 );
268
269 assert_eq!(vis_mod1_mod2.partial_cmp(&vis_mod1), Some(Ordering::Less));
270 assert_eq!(vis_mod1_mod3.partial_cmp(&vis_mod1), Some(Ordering::Less));
271 assert_eq!(vis_mod1_mod3.partial_cmp(&vis_mod1_mod2), None);
272
273 assert_eq!(vis_mod1.partial_cmp(&vis_crate), Some(Ordering::Less));
274 assert_eq!(vis_mod1.partial_cmp(&vis_pub), Some(Ordering::Less));
275
276 assert_eq!(vis_crate.partial_cmp(&vis_pub), Some(Ordering::Less));
277 }
278}