abi_stable_derive/
my_visibility.rs

1//! Types for conveniently representing visibility.
2
3use 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/// A visibility in a nested module.
17#[derive(Copy, Clone, Debug)]
18pub(crate) struct RelativeVis<'a> {
19    visibility_kind: VisibilityKind<'a>,
20    nesting: u8,
21}
22
23/// A visibility.
24#[derive(Copy, Clone, Debug, Eq, PartialEq)]
25pub enum VisibilityKind<'a> {
26    Private,
27    /// 'super' is 1,'super::super is 2,etc.
28    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    /// Returns a type which outputs the visibility for items in \[sub-\]modules.
81    ///
82    /// nesting==0 means the module deriving this trait
83    ///
84    /// nesting==1 means the module below that.
85    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////////////////////////////////////////////////////////////////////////////////
148
149#[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(test)]
206#[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}