bindgen_jni/
identifiers.rs

1use std::iter::*;
2
3
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
6pub enum JniIdentifier<'a> {
7    Namespace(&'a str),
8    ContainingClass(&'a str),
9    LeafClass(&'a str),
10}
11
12
13
14pub struct JniPathIter<'a> {
15    rest: &'a str,
16}
17
18impl<'a> JniPathIter<'a> {
19    pub fn new(path: &'a str) -> Self { JniPathIter { rest: path } }
20}
21
22impl<'a> Iterator for JniPathIter<'a> {
23    type Item = JniIdentifier<'a>;
24    fn next(&mut self) -> Option<Self::Item> {
25        if let Some(slash) = self.rest.find('/') {
26            let (namespace, rest) = self.rest.split_at(slash);
27            self.rest = &rest[1..];
28            return Some(JniIdentifier::Namespace(namespace));
29        }
30
31        if let Some(dollar) = self.rest.find('$') {
32            let (class, rest) = self.rest.split_at(dollar);
33            self.rest = &rest[1..];
34            return Some(JniIdentifier::ContainingClass(class));
35        }
36
37        if !self.rest.is_empty() {
38            let class = self.rest;
39            self.rest = "";
40            return Some(JniIdentifier::LeafClass(class));
41        }
42
43        None
44    }
45}
46
47#[test] fn jni_path_iter() {
48    assert_eq!(JniPathIter::new("").collect::<Vec<JniIdentifier>>(), &[]);
49
50    assert_eq!(JniPathIter::new("Bar").collect::<Vec<JniIdentifier>>(), &[
51        JniIdentifier::LeafClass("Bar"),
52    ]);
53
54    assert_eq!(JniPathIter::new("java/foo/Bar").collect::<Vec<JniIdentifier>>(), &[
55        JniIdentifier::Namespace("java"),
56        JniIdentifier::Namespace("foo"),
57        JniIdentifier::LeafClass("Bar"),
58    ]);
59
60    assert_eq!(JniPathIter::new("java/foo/Bar$Inner").collect::<Vec<JniIdentifier>>(), &[
61        JniIdentifier::Namespace("java"),
62        JniIdentifier::Namespace("foo"),
63        JniIdentifier::ContainingClass("Bar"),
64        JniIdentifier::LeafClass("Inner"),
65    ]);
66
67    assert_eq!(JniPathIter::new("java/foo/Bar$Inner$MoreInner").collect::<Vec<JniIdentifier>>(), &[
68        JniIdentifier::Namespace("java"),
69        JniIdentifier::Namespace("foo"),
70        JniIdentifier::ContainingClass("Bar"),
71        JniIdentifier::ContainingClass("Inner"),
72        JniIdentifier::LeafClass("MoreInner"),
73    ]);
74}
75
76
77
78#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
79pub enum JniField<'a> {
80    Single(JniBasicType<'a>),
81    Array { levels: usize, inner: JniBasicType<'a> },
82}
83
84#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
85pub enum JniBasicType<'a> {
86    Byte,
87    Char,
88    Double,
89    Float,
90    Int,
91    Long,
92    Class(&'a str),
93    Short,
94    Boolean
95}
96
97impl<'a> JniField<'a> {
98    /// Consume a JniField from a string.  Will set `remaining` to parse the *remainder* of the string.
99    pub fn read_next(remaining: &mut &'a str) -> Result<JniField<'a>, &'static str> {
100        let mut array = 0;
101        let mut chars = remaining.chars();
102
103        let leaf = loop {
104            match chars.next() {
105                None => return Err("Unexpected end of string while parsing for next JNI Field"),
106                Some('B') => { *remaining = chars.as_str(); break JniBasicType::Byte     }
107                Some('C') => { *remaining = chars.as_str(); break JniBasicType::Char     }
108                Some('D') => { *remaining = chars.as_str(); break JniBasicType::Double   }
109                Some('F') => { *remaining = chars.as_str(); break JniBasicType::Float    }
110                Some('I') => { *remaining = chars.as_str(); break JniBasicType::Int      }
111                Some('J') => { *remaining = chars.as_str(); break JniBasicType::Long     }
112                Some('L') => {
113                    let chars_str = chars.as_str();
114                    if let Some(semi) = chars_str.find(';') {
115                        *remaining = &chars_str[(semi+1)..];
116                        break JniBasicType::Class(&chars_str[..semi])
117                    } else {
118                        return Err("Unexpected end of string while parsing for terminating ';' of next JNI Field")
119                    }
120                }
121                Some('S') => { *remaining = chars.as_str(); break JniBasicType::Short    }
122                Some('Z') => { *remaining = chars.as_str(); break JniBasicType::Boolean  }
123                Some('[') => { array += 1; }
124                Some(_ch)  => return Err("Unexpected character in JNI type string"),
125            }
126        };
127
128        match array {
129            0   => Ok(JniField::Single(leaf)),
130            n   => Ok(JniField::Array { levels: n, inner: leaf }),
131        }
132    }
133
134    pub fn from_str(mut field: &'a str) -> Result<JniField<'a>, &'static str> {
135        let next = Self::read_next(&mut field)?;
136        if field.is_empty() {
137            Ok(next)
138        } else {
139            Err("Expected one type field, got multiple")
140        }
141    }
142}
143
144#[test] fn jni_field_from_str() {
145    // Single values
146    assert_eq!(JniField::from_str("F"),                 Ok(JniField::Single(JniBasicType::Float)));
147    assert_eq!(JniField::from_str("Ljava/foo/Bar;"),    Ok(JniField::Single(JniBasicType::Class("java/foo/Bar"))));
148
149    // Arrays
150    assert_eq!(JniField::from_str("[[F"),               Ok(JniField::Array { levels: 2, inner: JniBasicType::Float }));
151    assert_eq!(JniField::from_str("[[[Ljava/foo/Bar;"), Ok(JniField::Array { levels: 3, inner: JniBasicType::Class("java/foo/Bar") }));
152
153    // Erroneous input
154    assert!(JniField::from_str("").is_err());                               // No type
155    assert!(JniField::from_str("[[").is_err());                             // No type for array
156    assert!(JniField::from_str("Ljava/foo/Bar").is_err());                  // Missing semicolon
157    assert!(JniField::from_str("Ljava/foo/Bar;F").is_err());                // More after semicolon
158    assert!(JniField::from_str("Ljava/foo/Bar;Ljava/foo/Bar;").is_err());   // More after semicolon
159
160    // Multiple inputs
161    let mut class_float = "Ljava/foo/Bar;F";
162    assert_eq!(JniField::read_next(&mut class_float),    Ok(JniField::Single(JniBasicType::Class("java/foo/Bar"))));
163    assert_eq!(JniField::read_next(&mut class_float),    Ok(JniField::Single(JniBasicType::Float)));
164    assert_eq!(class_float, "");
165    assert!(   JniField::read_next(&mut class_float).is_err());
166
167    let mut class_class = "Ljava/foo/Bar;Ljava/foo/Bar;";
168    assert_eq!(JniField::read_next(&mut class_class),    Ok(JniField::Single(JniBasicType::Class("java/foo/Bar"))));
169    assert_eq!(JniField::read_next(&mut class_class),    Ok(JniField::Single(JniBasicType::Class("java/foo/Bar"))));
170    assert_eq!(class_class, "");
171    assert!(   JniField::read_next(&mut class_class).is_err());
172}
173
174
175
176
177
178/// Categorizes a rust [identifier](https://doc.rust-lang.org/reference/identifiers.html) for use in rust codegen.
179#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
180pub enum RustIdentifier<'a> {
181    /// Meets the criteria for a Rust [NON_KEYWORD_IDENTIFIER](https://doc.rust-lang.org/reference/identifiers.html)
182    Identifier(&'a str),
183
184    /// Not a rust-safe [identifier](https://doc.rust-lang.org/reference/identifiers.html).  Unicode, strange ASCII
185    /// values, relatively normal ASCII values... you name it.
186    NonIdentifier(&'a str),
187
188    /// A [keyword](https://doc.rust-lang.org/reference/keywords.html) that has had `r#` prepended to it, because it can
189    /// be used as a [RAW_IDENTIFIER](https://doc.rust-lang.org/reference/identifiers.html)
190    KeywordRawSafe(&'a str),
191
192    /// A [keyword](https://doc.rust-lang.org/reference/keywords.html) that has had `_` postpended to it, because it can
193    /// *not* be used as a [RAW_IDENTIFIER](https://doc.rust-lang.org/reference/identifiers.html).
194    KeywordUnderscorePostfix(&'a str),
195}
196
197impl<'a> RustIdentifier<'a> {
198    /// Takes an arbitrary string and tries to treat it as a Rust identifier, doing minor escaping for keywords.
199    pub fn from_str(s: &'a str) -> RustIdentifier<'a> {
200        match s {
201            // [Strict keywords](https://doc.rust-lang.org/reference/keywords.html#strict-keywords) that *are not* valid
202            // [RAW_IDENTIFIER](https://doc.rust-lang.org/reference/identifiers.html)s
203            "crate"     => RustIdentifier::KeywordUnderscorePostfix("crate_"),
204            "extern"    => RustIdentifier::KeywordUnderscorePostfix("extern_"),
205            "self"      => RustIdentifier::KeywordUnderscorePostfix("self_"),
206            "super"     => RustIdentifier::KeywordUnderscorePostfix("super_"),
207            "Self"      => RustIdentifier::KeywordUnderscorePostfix("Self_"),
208
209            // [Strict keywords](https://doc.rust-lang.org/reference/keywords.html#strict-keywords) that *are* valid
210            // [RAW_IDENTIFIER](https://doc.rust-lang.org/reference/identifiers.html)s
211            "as"        => RustIdentifier::KeywordRawSafe("r#as"),
212            "break"     => RustIdentifier::KeywordRawSafe("r#break"),
213            "const"     => RustIdentifier::KeywordRawSafe("r#const"),
214            "continue"  => RustIdentifier::KeywordRawSafe("r#continue"),
215            "else"      => RustIdentifier::KeywordRawSafe("r#else"),
216            "enum"      => RustIdentifier::KeywordRawSafe("r#enum"),
217            "false"     => RustIdentifier::KeywordRawSafe("r#false"),
218            "fn"        => RustIdentifier::KeywordRawSafe("r#fn"),
219            "for"       => RustIdentifier::KeywordRawSafe("r#for"),
220            "if"        => RustIdentifier::KeywordRawSafe("r#if"),
221            "impl"      => RustIdentifier::KeywordRawSafe("r#impl"),
222            "in"        => RustIdentifier::KeywordRawSafe("r#in"),
223            "let"       => RustIdentifier::KeywordRawSafe("r#let"),
224            "loop"      => RustIdentifier::KeywordRawSafe("r#loop"),
225            "match"     => RustIdentifier::KeywordRawSafe("r#match"),
226            "mod"       => RustIdentifier::KeywordRawSafe("r#mod"),
227            "move"      => RustIdentifier::KeywordRawSafe("r#move"),
228            "mut"       => RustIdentifier::KeywordRawSafe("r#mut"),
229            "pub"       => RustIdentifier::KeywordRawSafe("r#pub"),
230            "ref"       => RustIdentifier::KeywordRawSafe("r#ref"),
231            "return"    => RustIdentifier::KeywordRawSafe("r#return"),
232            "static"    => RustIdentifier::KeywordRawSafe("r#static"),
233            "struct"    => RustIdentifier::KeywordRawSafe("r#struct"),
234            "trait"     => RustIdentifier::KeywordRawSafe("r#trait"),
235            "true"      => RustIdentifier::KeywordRawSafe("r#true"),
236            "type"      => RustIdentifier::KeywordRawSafe("r#type"),
237            "unsafe"    => RustIdentifier::KeywordRawSafe("r#unsafe"),
238            "use"       => RustIdentifier::KeywordRawSafe("r#use"),
239            "where"     => RustIdentifier::KeywordRawSafe("r#where"),
240            "while"     => RustIdentifier::KeywordRawSafe("r#while"),
241            "dyn"       => RustIdentifier::KeywordRawSafe("r#dyn"),
242
243            // [Reserved keywords](https://doc.rust-lang.org/reference/keywords.html#reserved-keywords) that *are* valid
244            // [RAW_IDENTIFIER](https://doc.rust-lang.org/reference/identifiers.html)s
245            "abstract"  => RustIdentifier::KeywordRawSafe("r#abstract"),
246            "become"    => RustIdentifier::KeywordRawSafe("r#become"),
247            "box"       => RustIdentifier::KeywordRawSafe("r#box"),
248            "do"        => RustIdentifier::KeywordRawSafe("r#do"),
249            "final"     => RustIdentifier::KeywordRawSafe("r#final"),
250            "macro"     => RustIdentifier::KeywordRawSafe("r#macro"),
251            "override"  => RustIdentifier::KeywordRawSafe("r#override"),
252            "priv"      => RustIdentifier::KeywordRawSafe("r#priv"),
253            "typeof"    => RustIdentifier::KeywordRawSafe("r#typeof"),
254            "unsized"   => RustIdentifier::KeywordRawSafe("r#unsized"),
255            "virtual"   => RustIdentifier::KeywordRawSafe("r#virtual"),
256            "yield"     => RustIdentifier::KeywordRawSafe("r#yield"),
257
258            // Not a keyword, but not a valid [IDENTIFIER](https://doc.rust-lang.org/reference/identifiers.html) either.
259            ""                          => RustIdentifier::NonIdentifier(s),
260            "_"                         => RustIdentifier::NonIdentifier(s),
261            s if is_rust_identifier(s)  => RustIdentifier::Identifier(s),
262            s                           => RustIdentifier::NonIdentifier(s)
263        }
264    }
265}
266
267#[test] fn rust_identifier_from_str() {
268    assert_eq!(RustIdentifier::from_str("foo")  , RustIdentifier::Identifier              ("foo")    );
269    assert_eq!(RustIdentifier::from_str("crate"), RustIdentifier::KeywordUnderscorePostfix("crate_") );
270    assert_eq!(RustIdentifier::from_str("match"), RustIdentifier::KeywordRawSafe          ("r#match"));
271    assert_eq!(RustIdentifier::from_str("föo"),   RustIdentifier::NonIdentifier           ("föo")    );
272    assert_eq!(RustIdentifier::from_str(""),      RustIdentifier::NonIdentifier           ("")       );
273    assert_eq!(RustIdentifier::from_str("_"),     RustIdentifier::NonIdentifier           ("_")      );
274    assert_eq!(RustIdentifier::from_str("_f"),    RustIdentifier::Identifier              ("_f")     );
275    assert_eq!(RustIdentifier::from_str("_1"),    RustIdentifier::Identifier              ("_1")     );
276    assert_eq!(RustIdentifier::from_str("1_"),    RustIdentifier::NonIdentifier           ("1_")     );
277}
278
279fn is_rust_identifier(s: &str) -> bool {
280    // https://doc.rust-lang.org/reference/identifiers.html
281    let mut chars = s.chars();
282
283    // First char
284    let first_char = if let Some(ch) = chars.next() { ch } else { return false };
285    match first_char {
286        'a'..='z' => {},
287        'A'..='Z' => {},
288        '_' => { if s == "_" { return false } },
289        _ => { return false }
290    }
291
292    // Subsequent chars
293    while let Some(ch) = chars.next() {
294        match ch {
295            'a'..='z' => {},
296            'A'..='Z' => {},
297            '0'..='9' => {},
298            '_' => {},
299            _ => { return false }
300        }
301    }
302
303    true
304}