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