standalone_syn/
lifetime.rs

1// Copyright 2018 Syn Developers
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use std::cmp::Ordering;
10use std::fmt::{self, Display};
11use std::hash::{Hash, Hasher};
12
13use proc_macro2::{Span, Term};
14use unicode_xid::UnicodeXID;
15
16/// A Rust lifetime: `'a`.
17///
18/// Lifetime names must conform to the following rules:
19///
20/// - Must start with an apostrophe.
21/// - Must not consist of just an apostrophe: `'`.
22/// - Character after the apostrophe must be `_` or a Unicode code point with
23///   the XID_Start property.
24/// - All following characters must be Unicode code points with the XID_Continue
25///   property.
26///
27/// *This type is available if Syn is built with the `"derive"` or `"full"`
28/// feature.*
29#[cfg_attr(feature = "extra-traits", derive(Debug))]
30#[derive(Copy, Clone)]
31pub struct Lifetime {
32    term: Term,
33    pub span: Span,
34}
35
36impl Lifetime {
37    pub fn new(term: Term, span: Span) -> Self {
38        let s = term.as_str();
39
40        if !s.starts_with('\'') {
41            panic!(
42                "lifetime name must start with apostrophe as in \"'a\", \
43                 got {:?}",
44                s
45            );
46        }
47
48        if s == "'" {
49            panic!("lifetime name must not be empty");
50        }
51
52        fn xid_ok(s: &str) -> bool {
53            let mut chars = s.chars();
54            let first = chars.next().unwrap();
55            if !(UnicodeXID::is_xid_start(first) || first == '_') {
56                return false;
57            }
58            for ch in chars {
59                if !UnicodeXID::is_xid_continue(ch) {
60                    return false;
61                }
62            }
63            true
64        }
65
66        if !xid_ok(&s[1..]) {
67            panic!("{:?} is not a valid lifetime name", s);
68        }
69
70        Lifetime {
71            term: term,
72            span: span,
73        }
74    }
75}
76
77impl Display for Lifetime {
78    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
79        self.term.as_str().fmt(formatter)
80    }
81}
82
83impl PartialEq for Lifetime {
84    fn eq(&self, other: &Lifetime) -> bool {
85        self.term.as_str() == other.term.as_str()
86    }
87}
88
89impl Eq for Lifetime {}
90
91impl PartialOrd for Lifetime {
92    fn partial_cmp(&self, other: &Lifetime) -> Option<Ordering> {
93        Some(self.cmp(other))
94    }
95}
96
97impl Ord for Lifetime {
98    fn cmp(&self, other: &Lifetime) -> Ordering {
99        self.term.as_str().cmp(other.term.as_str())
100    }
101}
102
103impl Hash for Lifetime {
104    fn hash<H: Hasher>(&self, h: &mut H) {
105        self.term.as_str().hash(h)
106    }
107}
108
109#[cfg(feature = "parsing")]
110pub mod parsing {
111    use super::*;
112    use synom::Synom;
113    use buffer::Cursor;
114    use parse_error;
115    use synom::PResult;
116
117    impl Synom for Lifetime {
118        fn parse(input: Cursor) -> PResult<Self> {
119            let (span, term, rest) = match input.term() {
120                Some(term) => term,
121                _ => return parse_error(),
122            };
123            if !term.as_str().starts_with('\'') {
124                return parse_error();
125            }
126
127            Ok((
128                Lifetime {
129                    term: term,
130                    span: span,
131                },
132                rest,
133            ))
134        }
135
136        fn description() -> Option<&'static str> {
137            Some("lifetime")
138        }
139    }
140}
141
142#[cfg(feature = "printing")]
143mod printing {
144    use super::*;
145    use quote::{ToTokens, Tokens};
146    use proc_macro2::{TokenNode, TokenTree};
147
148    impl ToTokens for Lifetime {
149        fn to_tokens(&self, tokens: &mut Tokens) {
150            tokens.append(TokenTree {
151                span: self.span,
152                kind: TokenNode::Term(self.term),
153            })
154        }
155    }
156}