python_ast/isidentifier/
mod.rs

1//! This module uses Python to determine if a given string is a valid Python identifier or not.
2//! See [here](https://docs.python.org/3/reference/lexical_analysis.html)
3use pyo3::prelude::*;
4use std::ffi::CString;
5
6/// Determines if a string is a valid Python idetifier, and returns a Python object wrapping a bool.
7fn isidentifier_to_py(input: impl AsRef<str>, py: Python<'_>) -> PyResult<PyObject> {
8    let pymodule_code = include_str!("__init__.py");
9
10    // We want to call tokenize.tokenize from Python.
11    let code_cstr = CString::new(pymodule_code)?;
12    let pymodule = PyModule::from_code(py, &code_cstr, c"__init__.py", c"isidentifier")?;
13    let t = pymodule.getattr("isidentifier")?;
14    assert!(t.is_callable());
15    let args = (input.as_ref(),);
16
17    let isidentifier = t.call1(args)?;
18
19    Ok(isidentifier.into())
20}
21
22/// Takes a string of bytes and returns the Python-tokenized version of it.
23/// use python_ast::parse;
24///
25/// ```Rust
26/// isidentifier("alpha").expect('Should return Ok(true)')
27/// isidentifier("0alpha").expect('Should return Ok(false)')
28/// ```
29fn isidentifier(input: impl AsRef<str>) -> PyResult<bool> {
30    let isidentifier = Python::with_gil(|py| {
31        let isidentifier = isidentifier_to_py(input, py)?;
32        isidentifier.extract(py)
33    })?;
34
35    Ok(isidentifier)
36}
37
38/// Trait that determines if a string contains a valid identifer based on Python rules (which are broadly simiilar to Rust).
39pub trait IsIdentifier: AsRef<str> {
40    fn isidentifier(&self) -> PyResult<bool> {
41        let s = self.as_ref();
42        isidentifier(s)
43    }
44}
45
46/// Blanket implementation for this trait.
47impl<T: AsRef<str>> IsIdentifier for T {}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52
53    #[test]
54    fn good_symbol_works() {
55        assert_eq!(isidentifier("alpha").unwrap(), true)
56    }
57
58    #[test]
59    fn good_symbol_works_as_method() {
60        assert_eq!(isidentifier("alpha").unwrap(), true)
61    }
62
63    #[test]
64    fn bad_symbol_works() {
65        assert_eq!(isidentifier("0alpha").unwrap(), false)
66    }
67
68    #[test]
69    fn bad_symbol_works_as_method() {
70        assert_eq!("0alpha".isidentifier().unwrap(), false)
71    }
72}