python_ast/ast/tree/
f_string.rs1use proc_macro2::TokenStream;
2use pyo3::{Bound, FromPyObject, PyAny, PyResult, prelude::PyAnyMethods};
3use quote::quote;
4use serde::{Deserialize, Serialize};
5
6use crate::{
7 CodeGen, CodeGenContext, ExprType, Node, PythonOptions, SymbolTableScopes,
8 extract_list,
9};
10
11#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
13pub struct JoinedStr {
14 pub values: Vec<ExprType>,
16 pub lineno: Option<usize>,
18 pub col_offset: Option<usize>,
19 pub end_lineno: Option<usize>,
20 pub end_col_offset: Option<usize>,
21}
22
23#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
25pub struct FormattedValue {
26 pub value: Box<ExprType>,
28 pub conversion: Option<i32>,
30 pub format_spec: Option<Box<ExprType>>,
32 pub lineno: Option<usize>,
34 pub col_offset: Option<usize>,
35 pub end_lineno: Option<usize>,
36 pub end_col_offset: Option<usize>,
37}
38
39impl<'a> FromPyObject<'a> for JoinedStr {
40 fn extract_bound(ob: &Bound<'a, PyAny>) -> PyResult<Self> {
41 let values: Vec<ExprType> = extract_list(ob, "values", "joined string values")?;
43
44 Ok(JoinedStr {
45 values,
46 lineno: ob.lineno(),
47 col_offset: ob.col_offset(),
48 end_lineno: ob.end_lineno(),
49 end_col_offset: ob.end_col_offset(),
50 })
51 }
52}
53
54impl<'a> FromPyObject<'a> for FormattedValue {
55 fn extract_bound(ob: &Bound<'a, PyAny>) -> PyResult<Self> {
56 let value: ExprType = ob.getattr("value")?.extract()?;
58
59 let conversion: Option<i32> = if let Ok(conv_attr) = ob.getattr("conversion") {
61 let conv_val: i32 = conv_attr.extract()?;
62 if conv_val == -1 {
63 None } else {
65 Some(conv_val)
66 }
67 } else {
68 None
69 };
70
71 let format_spec: Option<Box<ExprType>> = if let Ok(spec_attr) = ob.getattr("format_spec") {
73 if spec_attr.is_none() {
74 None
75 } else {
76 Some(Box::new(spec_attr.extract()?))
77 }
78 } else {
79 None
80 };
81
82 Ok(FormattedValue {
83 value: Box::new(value),
84 conversion,
85 format_spec,
86 lineno: ob.lineno(),
87 col_offset: ob.col_offset(),
88 end_lineno: ob.end_lineno(),
89 end_col_offset: ob.end_col_offset(),
90 })
91 }
92}
93
94impl Node for JoinedStr {
95 fn lineno(&self) -> Option<usize> { self.lineno }
96 fn col_offset(&self) -> Option<usize> { self.col_offset }
97 fn end_lineno(&self) -> Option<usize> { self.end_lineno }
98 fn end_col_offset(&self) -> Option<usize> { self.end_col_offset }
99}
100
101impl Node for FormattedValue {
102 fn lineno(&self) -> Option<usize> { self.lineno }
103 fn col_offset(&self) -> Option<usize> { self.col_offset }
104 fn end_lineno(&self) -> Option<usize> { self.end_lineno }
105 fn end_col_offset(&self) -> Option<usize> { self.end_col_offset }
106}
107
108impl CodeGen for JoinedStr {
109 type Context = CodeGenContext;
110 type Options = PythonOptions;
111 type SymbolTable = SymbolTableScopes;
112
113 fn find_symbols(self, symbols: Self::SymbolTable) -> Self::SymbolTable {
114 self.values.into_iter().fold(symbols, |acc, val| val.find_symbols(acc))
115 }
116
117 fn to_rust(
118 self,
119 ctx: Self::Context,
120 options: Self::Options,
121 symbols: Self::SymbolTable,
122 ) -> Result<TokenStream, Box<dyn std::error::Error>> {
123 let part_tokens: Result<Vec<TokenStream>, Box<dyn std::error::Error>> = self.values.into_iter()
125 .map(|val| val.to_rust(ctx.clone(), options.clone(), symbols.clone()))
126 .collect();
127 let part_tokens = part_tokens?;
128
129 if part_tokens.is_empty() {
132 Ok(quote! { String::new() })
133 } else {
134 Ok(quote! {
135 format!("{}", #(#part_tokens)+*)
136 })
137 }
138 }
139}
140
141impl CodeGen for FormattedValue {
142 type Context = CodeGenContext;
143 type Options = PythonOptions;
144 type SymbolTable = SymbolTableScopes;
145
146 fn find_symbols(self, symbols: Self::SymbolTable) -> Self::SymbolTable {
147 let symbols = (*self.value).find_symbols(symbols);
148 if let Some(format_spec) = self.format_spec {
149 (*format_spec).find_symbols(symbols)
150 } else {
151 symbols
152 }
153 }
154
155 fn to_rust(
156 self,
157 ctx: Self::Context,
158 options: Self::Options,
159 symbols: Self::SymbolTable,
160 ) -> Result<TokenStream, Box<dyn std::error::Error>> {
161 let value_tokens = (*self.value).to_rust(ctx.clone(), options.clone(), symbols.clone())?;
162
163 if let Some(format_spec) = self.format_spec {
164 let _spec_tokens = (*format_spec).to_rust(ctx, options, symbols)?;
165 Ok(quote! {
168 format!("{}", #value_tokens)
169 })
170 } else {
171 Ok(quote! {
173 format!("{}", #value_tokens)
174 })
175 }
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 }