blob: e16b6b4ef49677766166b069e982802ff3941fb0 [file] [log] [blame]
David Tolnay55535012018-01-05 16:39:23 -08001// 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
David Tolnay63e3dee2017-06-03 20:13:17 -07009use std::cmp::Ordering;
10use std::fmt::{self, Display};
11use std::hash::{Hash, Hasher};
12
David Tolnay98942562017-12-26 21:24:35 -050013use proc_macro2::{Span, Term};
David Tolnay63e3dee2017-06-03 20:13:17 -070014use unicode_xid::UnicodeXID;
15
David Tolnay63e3dee2017-06-03 20:13:17 -070016#[cfg_attr(feature = "extra-traits", derive(Debug))]
17#[cfg_attr(feature = "clone-impls", derive(Clone))]
18pub struct Lifetime {
David Tolnay73c98de2017-12-31 15:56:56 -050019 term: Term,
David Tolnay63e3dee2017-06-03 20:13:17 -070020 pub span: Span,
21}
22
23impl Lifetime {
David Tolnay73c98de2017-12-31 15:56:56 -050024 pub fn new(term: Term, span: Span) -> Self {
25 let s = term.as_str();
David Tolnay63e3dee2017-06-03 20:13:17 -070026
27 if !s.starts_with('\'') {
David Tolnay51382052017-12-27 13:46:21 -050028 panic!(
29 "lifetime name must start with apostrophe as in \"'a\", \
30 got {:?}",
31 s
32 );
David Tolnay63e3dee2017-06-03 20:13:17 -070033 }
34
35 if s == "'" {
36 panic!("lifetime name must not be empty");
37 }
38
39 if s == "'_" {
40 panic!("\"'_\" is not a valid lifetime name");
41 }
42
43 fn xid_ok(s: &str) -> bool {
44 let mut chars = s.chars();
45 let first = chars.next().unwrap();
46 if !(UnicodeXID::is_xid_start(first) || first == '_') {
47 return false;
48 }
49 for ch in chars {
50 if !UnicodeXID::is_xid_continue(ch) {
51 return false;
52 }
53 }
54 true
55 }
56
57 if !xid_ok(&s[1..]) {
David Tolnaybb4ca9f2017-12-26 12:28:58 -050058 panic!("{:?} is not a valid lifetime name", s);
David Tolnay63e3dee2017-06-03 20:13:17 -070059 }
60
61 Lifetime {
David Tolnay73c98de2017-12-31 15:56:56 -050062 term: term,
David Tolnay63e3dee2017-06-03 20:13:17 -070063 span: span,
64 }
65 }
66}
67
68impl Display for Lifetime {
69 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
David Tolnay73c98de2017-12-31 15:56:56 -050070 self.term.as_str().fmt(formatter)
David Tolnay63e3dee2017-06-03 20:13:17 -070071 }
72}
73
74impl PartialEq for Lifetime {
75 fn eq(&self, other: &Lifetime) -> bool {
David Tolnay73c98de2017-12-31 15:56:56 -050076 self.term.as_str() == other.term.as_str()
David Tolnay63e3dee2017-06-03 20:13:17 -070077 }
78}
79
80impl Eq for Lifetime {}
81
82impl PartialOrd for Lifetime {
83 fn partial_cmp(&self, other: &Lifetime) -> Option<Ordering> {
84 Some(self.cmp(other))
85 }
86}
87
88impl Ord for Lifetime {
89 fn cmp(&self, other: &Lifetime) -> Ordering {
David Tolnay73c98de2017-12-31 15:56:56 -050090 self.term.as_str().cmp(other.term.as_str())
David Tolnay63e3dee2017-06-03 20:13:17 -070091 }
92}
93
94impl Hash for Lifetime {
95 fn hash<H: Hasher>(&self, h: &mut H) {
David Tolnay73c98de2017-12-31 15:56:56 -050096 self.term.as_str().hash(h)
David Tolnay63e3dee2017-06-03 20:13:17 -070097 }
98}
99
100#[cfg(feature = "parsing")]
101pub mod parsing {
102 use super::*;
David Tolnayc5ab8c62017-12-26 16:43:39 -0500103 use synom::Synom;
David Tolnaydfc886b2018-01-06 08:03:09 -0800104 use buffer::Cursor;
David Tolnay203557a2017-12-27 23:59:33 -0500105 use parse_error;
106 use synom::PResult;
David Tolnay63e3dee2017-06-03 20:13:17 -0700107
108 impl Synom for Lifetime {
109 fn parse(input: Cursor) -> PResult<Self> {
David Tolnay65729482017-12-31 16:14:50 -0500110 let (span, term, rest) = match input.term() {
David Tolnay73c98de2017-12-31 15:56:56 -0500111 Some(term) => term,
David Tolnay63e3dee2017-06-03 20:13:17 -0700112 _ => return parse_error(),
113 };
David Tolnay73c98de2017-12-31 15:56:56 -0500114 if !term.as_str().starts_with('\'') {
David Tolnay63e3dee2017-06-03 20:13:17 -0700115 return parse_error();
116 }
117
David Tolnay51382052017-12-27 13:46:21 -0500118 Ok((
David Tolnay51382052017-12-27 13:46:21 -0500119 Lifetime {
David Tolnay73c98de2017-12-31 15:56:56 -0500120 term: term,
David Tolnay51382052017-12-27 13:46:21 -0500121 span: span,
122 },
David Tolnayf4aa6b42017-12-31 16:40:33 -0500123 rest,
David Tolnay51382052017-12-27 13:46:21 -0500124 ))
David Tolnay63e3dee2017-06-03 20:13:17 -0700125 }
Sergio Benitez5680d6a2017-12-29 11:20:29 -0800126
127 fn description() -> Option<&'static str> {
128 Some("lifetime")
129 }
David Tolnay63e3dee2017-06-03 20:13:17 -0700130 }
131}
132
133#[cfg(feature = "printing")]
134mod printing {
135 use super::*;
David Tolnay51382052017-12-27 13:46:21 -0500136 use quote::{ToTokens, Tokens};
137 use proc_macro2::{TokenNode, TokenTree};
David Tolnay63e3dee2017-06-03 20:13:17 -0700138
139 impl ToTokens for Lifetime {
140 fn to_tokens(&self, tokens: &mut Tokens) {
141 tokens.append(TokenTree {
David Tolnay98942562017-12-26 21:24:35 -0500142 span: self.span,
David Tolnay73c98de2017-12-31 15:56:56 -0500143 kind: TokenNode::Term(self.term),
David Tolnay63e3dee2017-06-03 20:13:17 -0700144 })
145 }
146 }
147}