python_ast/ast/tree/
name.rs

1use proc_macro2::TokenStream;
2use pyo3::{FromPyObject, PyErr};
3use quote::{format_ident, quote};
4
5use crate::{CodeGen, CodeGenContext, IsIdentifier, PythonOptions, SymbolTableScopes};
6
7use serde::{Deserialize, Serialize};
8
9/// Identifiers represent valid Python identifiers.
10#[derive(Clone, Debug, Default, Eq, FromPyObject, Hash, PartialEq, Serialize, Deserialize)]
11#[repr(transparent)]
12pub struct Identifier(String);
13
14impl TryFrom<&str> for Identifier {
15    type Error = PyErr;
16
17    fn try_from(value: &str) -> Result<Self, Self::Error> {
18        if value.isidentifier()? {
19            Ok(Identifier(value.to_string()))
20        } else {
21            Err(PyErr::new::<pyo3::exceptions::PyNameError, _>(format!(
22                "Invalid Identifier: {}",
23                String::from(value)
24            )))
25        }
26    }
27}
28
29impl AsRef<str> for Identifier {
30    fn as_ref(&self) -> &str {
31        self.0.as_str()
32    }
33}
34
35impl Into<String> for Identifier {
36    fn into(self) -> String {
37        self.0
38    }
39}
40
41/// Names are Python identifiers, separated by '.'
42#[derive(Clone, Debug, Default, Eq, FromPyObject, Hash, PartialEq, Serialize, Deserialize)]
43pub struct Name {
44    pub id: String,
45}
46
47impl TryFrom<&str> for Name {
48    type Error = PyErr;
49
50    fn try_from(s: &str) -> Result<Self, Self::Error> {
51        let parts = s.split('.');
52        println!("parts: {:?}", parts);
53
54        let mut v = Vec::new();
55        for part in parts {
56            let ident = Identifier::try_from(part)?;
57            v.push(String::from(ident.as_ref()));
58        }
59
60        Ok(Name { id: v.join(".") })
61    }
62}
63
64impl AsRef<str> for Name {
65    fn as_ref(&self) -> &str {
66        self.id.as_str()
67    }
68}
69
70impl Into<String> for Name {
71    fn into(self) -> String {
72        self.id
73    }
74}
75
76impl CodeGen for Name {
77    type Context = CodeGenContext;
78    type Options = PythonOptions;
79    type SymbolTable = SymbolTableScopes;
80
81    fn to_rust(
82        self,
83        _ctx: Self::Context,
84        _options: Self::Options,
85        _symbols: Self::SymbolTable,
86    ) -> Result<TokenStream, Box<dyn std::error::Error>> {
87        let name = format_ident!("{}", self.id);
88        Ok(quote!(#name))
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95
96    #[test]
97    fn good_name_works() {
98        let name = Name::try_from("this.symbol");
99        assert!(name.is_ok());
100    }
101
102    #[test]
103    fn bad_name_works() {
104        let name = Name::try_from("this.0symbol");
105        assert!(name.is_err());
106    }
107}