1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use std::borrow::Cow;
use std::fmt::{self, Display};

#[derive(Debug, Clone, Eq, Hash)]
pub struct Ident(String);

impl Ident {
    pub fn new<T: Into<Ident>>(t: T) -> Self {
        t.into()
    }
}

impl<'a> From<&'a str> for Ident {
    fn from(s: &str) -> Self {
        Ident(s.to_owned())
    }
}

impl<'a> From<Cow<'a, str>> for Ident {
    fn from(s: Cow<'a, str>) -> Self {
        Ident(s.into_owned())
    }
}

impl From<String> for Ident {
    fn from(s: String) -> Self {
        Ident(s)
    }
}

impl From<usize> for Ident {
    fn from(u: usize) -> Self {
        Ident(u.to_string())
    }
}

impl AsRef<str> for Ident {
    fn as_ref(&self) -> &str {
        &self.0
    }
}

impl Display for Ident {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        self.0.fmt(formatter)
    }
}

impl<T: ?Sized> PartialEq<T> for Ident
    where T: AsRef<str>
{
    fn eq(&self, other: &T) -> bool {
        self.0 == other.as_ref()
    }
}

#[cfg(feature = "parsing")]
pub mod parsing {
    use super::*;
    use nom::IResult;
    use space::skip_whitespace;
    use unicode_xid::UnicodeXID;

    pub fn ident(input: &str) -> IResult<&str, Ident> {
        let (rest, id) = match word(input) {
            IResult::Done(rest, id) => (rest, id),
            IResult::Error => return IResult::Error,
        };

        match id.as_ref() {
            // From https://doc.rust-lang.org/grammar.html#keywords
            "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" => IResult::Error,
            _ => IResult::Done(rest, id),
        }
    }

    pub fn word(mut input: &str) -> IResult<&str, Ident> {
        input = skip_whitespace(input);

        let mut chars = input.char_indices();
        match chars.next() {
            Some((_, ch)) if UnicodeXID::is_xid_start(ch) || ch == '_' => {}
            _ => return IResult::Error,
        }

        while let Some((i, ch)) = chars.next() {
            if !UnicodeXID::is_xid_continue(ch) {
                return IResult::Done(&input[i..], input[..i].into());
            }
        }

        IResult::Done("", input.into())
    }

    #[cfg(feature = "full")]
    pub fn wordlike(mut input: &str) -> IResult<&str, Ident> {
        input = skip_whitespace(input);

        for (i, ch) in input.char_indices() {
            if !UnicodeXID::is_xid_start(ch) && !UnicodeXID::is_xid_continue(ch) {
                return if i == 0 {
                    IResult::Error
                } else {
                    IResult::Done(&input[i..], input[..i].into())
                };
            }
        }

        IResult::Done("", input.into())
    }
}

#[cfg(feature = "printing")]
mod printing {
    use super::*;
    use quote::{Tokens, ToTokens};

    impl ToTokens for Ident {
        fn to_tokens(&self, tokens: &mut Tokens) {
            tokens.append(self.as_ref())
        }
    }
}