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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//! A trait for String-like types to check if a string is a reserved keyword,
//! and convert it to a safe non-keyword if so.
//!
//! Only strict and reserved keywords are checked against; weak keywords are not included.
//!
//! You can add this dependency with:
//!
//! ```toml
//! [dependencies]
//! check_keyword = "0.2"
//! ```
//!
//! # Example
//!
//! ```
//! use check_keyword::CheckKeyword;
//! let keyword = "match";
//!
//! assert!(keyword.is_keyword());
//! assert_eq!(keyword.into_safe(), "r#match");
//! ```
//! 
//! The [CheckKeyword::into_safe] method automatically checks [CheckKeyword::is_keyword] for you.
//! You don't need to call [CheckKeyword::is_keyword]
//! if you don't care whether it was originally a keyword or not.
//! 
//! # Implementations
//! 
//! There is a special implementation of `CheckKeyword<String>` for [&str], and a
//! blanket implementation of `CheckKeyword<T>` where `T: AsRef<str> + From<String>`.
//! 
//! The blanket implementation covers [String], and is only tested for that, but should
//! cover any other String-like type as well. I can try to broaden the definition to fit
//! other types if needed (open an issue).
//! 
//! # Rust Editions
//!
//! By default, the keywords added in Rust Edition 2018 are included in the list of checked keywords.
//! This can be disabled with `default-features = false` in your Cargo.toml.
//!
//! ```toml
//! [dependencies]
//! check_keyword = { version = "0.2", default-features = false }
//! ```
//!
//! Future Rust editions may add new keywords, and this crate will be updated to reflect that.
//! (Or you can create an issue on github if I don't.)
mod impls;

#[macro_use] mod arr_macro;

/// The main trait.
/// 
/// The generic argument `T` is the output type of `into_safe`, and in the blanket implementation
/// is equal to `Self`. I would have used an associated type,
/// but I ran into the good-old "upstream crates may add new impl of trait" error when implementing [str].
pub trait CheckKeyword<T> {
    /// Checks if `self` is a keyword.
    fn is_keyword(&self) -> bool;

    /// If its a keyword, add "r#" to the beginning.
    /// 
    /// This function consumes self, so that if it is not a keyword,
    /// it can return quickly without cloning. If you want to keep ownership
    /// of the original data, clone it first.
    fn into_safe(self) -> T;
}

arr!(static KEYWORDS: [&'static str; _] = [

    // STRICT, 2015

    "as",
    "break",
    "const",
    "continue",
    "crate",
    "else",
    "enum",
    "extern",
    "false",
    "fn",
    "for",
    "if",
    "impl",
    "in",
    "let",
    "loop",
    "match",
    "mod",
    "move",
    "mut",
    "pub",
    "ref",
    "return",
    "self",
    "Self",
    "static",
    "struct",
    "super",
    "trait",
    "true",
    "type",
    "unsafe",
    "use",
    "where",
    "while",

    // STRICT, 2018

    #[cfg(feature = "2018")] "async",
    #[cfg(feature = "2018")] "await",
    #[cfg(feature = "2018")] "dyn",

    // RESERVED, 2015

    "abstract",
    "become",
    "box",
    "do",
    "final",
    "macro",
    "override",
    "priv",
    "typeof",
    "unsized",
    "virtual",
    "yield",

    // RESERVED, 2018

    #[cfg(feature = "2018")] "try"

]);

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn array_length() {
        println!("Number of keywords: {}", KEYWORDS.len());
    }
}