use std::borrow::Cow;
use std::cmp::Ordering;
use std::fmt::{self, Display};
use std::hash::{Hash, Hasher};
use proc_macro2::Term;
use unicode_xid::UnicodeXID;
use proc_macro2::Span;
#[derive(Copy, Clone, Debug)]
pub struct Ident {
term: Term,
pub span: Span,
}
impl Ident {
pub fn new(s: &str, span: Span) -> Self {
if s.is_empty() {
panic!("ident is not allowed to be empty; use Option<Ident>");
}
if s.starts_with('\'') {
panic!("ident is not allowed to be a lifetime; use syn::Lifetime");
}
if s == "_" {
panic!("`_` is not a valid ident; use syn::token::Underscore");
}
if s.bytes().all(|digit| digit >= b'0' && digit <= b'9') {
panic!("ident cannot be a number, use syn::Index instead");
}
fn xid_ok(s: &str) -> bool {
let mut chars = s.chars();
let first = chars.next().unwrap();
if !(UnicodeXID::is_xid_start(first) || first == '_') {
return false;
}
for ch in chars {
if !UnicodeXID::is_xid_continue(ch) {
return false;
}
}
true
}
if !xid_ok(s) {
panic!("{:?} is not a valid ident", s);
}
Ident {
term: Term::intern(s),
span: span,
}
}
}
impl<'a> From<&'a str> for Ident {
fn from(s: &str) -> Self {
Ident::new(s, Span::def_site())
}
}
impl From<Token![self]> for Ident {
fn from(tok: Token![self]) -> Self {
Ident::new("self", tok.0)
}
}
impl From<Token![Self]> for Ident {
fn from(tok: Token![Self]) -> Self {
Ident::new("Self", tok.0)
}
}
impl From<Token![super]> for Ident {
fn from(tok: Token![super]) -> Self {
Ident::new("super", tok.0)
}
}
impl From<Token![crate]> for Ident {
fn from(tok: Token![crate]) -> Self {
Ident::new("crate", tok.0)
}
}
impl<'a> From<Cow<'a, str>> for Ident {
fn from(s: Cow<'a, str>) -> Self {
Ident::new(&s, Span::def_site())
}
}
impl From<String> for Ident {
fn from(s: String) -> Self {
Ident::new(&s, Span::def_site())
}
}
impl AsRef<str> for Ident {
fn as_ref(&self) -> &str {
self.term.as_str()
}
}
impl Display for Ident {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
self.term.as_str().fmt(formatter)
}
}
impl<T: ?Sized> PartialEq<T> for Ident
where
T: AsRef<str>,
{
fn eq(&self, other: &T) -> bool {
self.as_ref() == other.as_ref()
}
}
impl Eq for Ident {}
impl PartialOrd for Ident {
fn partial_cmp(&self, other: &Ident) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Ident {
fn cmp(&self, other: &Ident) -> Ordering {
self.as_ref().cmp(other.as_ref())
}
}
impl Hash for Ident {
fn hash<H: Hasher>(&self, h: &mut H) {
self.as_ref().hash(h);
}
}
#[cfg(feature = "parsing")]
pub mod parsing {
use super::*;
use synom::Synom;
use buffer::Cursor;
use parse_error;
use synom::PResult;
impl Synom for Ident {
fn parse(input: Cursor) -> PResult<Self> {
let (span, term, rest) = match input.term() {
Some(term) => term,
_ => return parse_error(),
};
if term.as_str().starts_with('\'') {
return parse_error();
}
match term.as_str() {
"abstract" | "alignof" | "as" | "become" | "box" | "break" | "const"
| "continue" | "crate" | "do" | "else" | "enum" | "extern" | "false" | "final"
| "fn" | "for" | "if" | "impl" | "in" | "let" | "loop" | "macro" | "match"
| "mod" | "move" | "mut" | "offsetof" | "override" | "priv" | "proc" | "pub"
| "pure" | "ref" | "return" | "Self" | "self" | "sizeof" | "static" | "struct"
| "super" | "trait" | "true" | "type" | "typeof" | "unsafe" | "unsized" | "use"
| "virtual" | "where" | "while" | "yield" => return parse_error(),
_ => {}
}
Ok((
Ident {
span: span,
term: term,
},
rest,
))
}
fn description() -> Option<&'static str> {
Some("identifier")
}
}
}
#[cfg(feature = "printing")]
mod printing {
use super::*;
use quote::{ToTokens, Tokens};
use proc_macro2::{TokenNode, TokenTree};
impl ToTokens for Ident {
fn to_tokens(&self, tokens: &mut Tokens) {
tokens.append(TokenTree {
span: self.span,
kind: TokenNode::Term(self.term),
})
}
}
}