1use proc_macro2::TokenStream;
2use quote::quote;
3use pyo3::types::{PyAnyMethods, PyTypeMethods};
4use crate::{CodeGen, CodeGenContext, PythonOptions, SymbolTableScopes, ExprType};
5
6pub trait PythonOperator: Clone + std::fmt::Debug {
8 fn to_rust_op(&self) -> Result<TokenStream, Box<dyn std::error::Error>>;
10
11 fn precedence(&self) -> u8 {
13 0 }
15
16 fn is_unknown(&self) -> bool;
18}
19
20pub trait BinaryOperation: Clone + std::fmt::Debug {
22 type OperatorType: PythonOperator;
23
24 fn operator(&self) -> &Self::OperatorType;
26
27 fn left(&self) -> &ExprType;
29
30 fn right(&self) -> &ExprType;
32
33 fn generate_rust_code(
35 &self,
36 ctx: CodeGenContext,
37 options: PythonOptions,
38 symbols: SymbolTableScopes,
39 ) -> Result<TokenStream, Box<dyn std::error::Error>> {
40 let left = self.left()
41 .clone()
42 .to_rust(ctx.clone(), options.clone(), symbols.clone())?;
43 let right = self.right()
44 .clone()
45 .to_rust(ctx, options, symbols)?;
46 let op = self.operator().to_rust_op()?;
47
48 Ok(quote!((#left) #op (#right)))
49 }
50}
51
52pub trait PyAttributeExtractor {
54 fn extract_attr_with_context(&self, attr: &str, context: &str) -> pyo3::PyResult<pyo3::Bound<'_, pyo3::PyAny>>;
56
57 fn extract_type_name(&self, context: &str) -> pyo3::PyResult<String>;
59}
60
61impl<'py> PyAttributeExtractor for pyo3::Bound<'py, pyo3::PyAny> {
62 fn extract_attr_with_context(&self, attr: &str, context: &str) -> pyo3::PyResult<pyo3::Bound<'_, pyo3::PyAny>> {
63 use crate::Node;
64 self.getattr(attr).map_err(|_| {
65 pyo3::exceptions::PyAttributeError::new_err(
66 self.error_message("<unknown>", format!("error getting {}", context))
67 )
68 })
69 }
70
71 fn extract_type_name(&self, context: &str) -> pyo3::PyResult<String> {
72 use crate::Node;
73
74 let type_name = self.get_type().name().map_err(|_| {
75 pyo3::exceptions::PyTypeError::new_err(
76 self.error_message("<unknown>", format!("extracting type name for {}", context))
77 )
78 })?;
79
80 type_name.extract()
81 }
82}
83
84pub trait PositionInfo {
86 fn position_info(&self) -> (Option<usize>, Option<usize>, Option<usize>, Option<usize>);
88
89 fn has_position(&self) -> bool {
91 let (lineno, col_offset, _, _) = self.position_info();
92 lineno.is_some() && col_offset.is_some()
93 }
94}
95
96pub trait DebugInfo {
98 fn debug_description(&self) -> String;
100
101 fn node_type(&self) -> &'static str;
103}
104
105pub trait ChainableOperation {
107 type Operand;
108 type Operator;
109
110 fn operands(&self) -> Vec<&Self::Operand>;
112
113 fn operators(&self) -> Vec<&Self::Operator>;
115
116 fn generate_chained_rust(
118 &self,
119 ctx: CodeGenContext,
120 options: PythonOptions,
121 symbols: SymbolTableScopes,
122 ) -> Result<TokenStream, Box<dyn std::error::Error>>;
123}
124
125pub trait FromPythonString: Sized {
127 fn from_python_string(s: &str) -> Option<Self>;
129
130 fn unknown() -> Self;
132
133 fn parse_or_unknown(s: &str) -> Self {
135 Self::from_python_string(s).unwrap_or_else(Self::unknown)
136 }
137}
138
139pub trait ErrorContext {
141 fn with_context(&self, operation: &str) -> String;
143}
144
145impl<T: std::fmt::Debug> ErrorContext for T {
146 fn with_context(&self, operation: &str) -> String {
147 format!("Error during {}: {:?}", operation, self)
148 }
149}