1- use crate :: cross:: { CLRepr , CLReprObject } ;
2- use crate :: template:: mj_value:: to_minijinja_value ;
1+ use crate :: cross:: * ;
2+ use crate :: template:: mj_value:: * ;
33use crate :: utils:: bind_method;
44use log:: trace;
55use minijinja as mj;
@@ -8,6 +8,9 @@ use neon::prelude::*;
88use std:: cell:: RefCell ;
99use std:: error:: Error ;
1010
11+ #[ cfg( feature = "python" ) ]
12+ use pyo3:: { exceptions:: PyNotImplementedError , prelude:: * , types:: PyTuple , AsPyPointer } ;
13+
1114trait NeonMiniJinjaContext {
1215 fn throw_from_mj_error < T > ( & mut self , err : mj:: Error ) -> NeonResult < T > ;
1316}
@@ -100,6 +103,80 @@ impl JinjaEngine {
100103 ) ;
101104 engine. set_auto_escape_callback ( |_name : & str | mj:: AutoEscape :: Json ) ;
102105
106+ #[ cfg( feature = "python" ) ]
107+ {
108+ let filters = options
109+ . get_value ( cx, "filters" ) ?
110+ . downcast_or_throw :: < JsObject , _ > ( cx) ?;
111+
112+ let filter_names = filters. get_own_property_names ( cx) ?;
113+ for i in 0 ..filter_names. len ( cx) {
114+ let filter_name: Handle < JsString > = filter_names. get ( cx, i) ?;
115+ let filter_fun = CLRepr :: from_js_ref ( filters. get_value ( cx, filter_name) ?, cx) ?;
116+
117+ let py_fun = match filter_fun {
118+ CLRepr :: PythonRef ( py_ref) => match py_ref {
119+ PythonRef :: PyFunction ( py_fun_ref)
120+ | PythonRef :: PyExternalFunction ( py_fun_ref) => py_fun_ref,
121+ other => {
122+ return cx. throw_error ( format ! (
123+ "minijinja::filter must be a function, actual: CLRepr::PythonRef({:?})" ,
124+ other
125+ ) )
126+ }
127+ } ,
128+ other => {
129+ return cx. throw_error ( format ! (
130+ "minijinja::filter must be a function, actual: {:?}" ,
131+ other. kind( )
132+ ) )
133+ }
134+ } ;
135+
136+ engine. add_filter (
137+ filter_name. value ( cx) ,
138+ move |_state : & mj:: State ,
139+ args : & [ mj:: value:: Value ] |
140+ -> Result < mj:: value:: Value , mj:: Error > {
141+ let mut arguments = Vec :: with_capacity ( args. len ( ) ) ;
142+
143+ for arg in args {
144+ arguments. push ( from_minijinja_value ( arg) ?) ;
145+ }
146+
147+ let python_call_res = Python :: with_gil ( |py| {
148+ let mut args_tuple = Vec :: with_capacity ( args. len ( ) ) ;
149+
150+ for arg in arguments {
151+ args_tuple. push ( arg. into_py ( py) ?) ;
152+ }
153+
154+ let tuple = PyTuple :: new ( py, args_tuple) ;
155+
156+ let call_res = py_fun. call1 ( py, tuple) ?;
157+
158+ let is_coroutine =
159+ unsafe { pyo3:: ffi:: PyCoro_CheckExact ( call_res. as_ptr ( ) ) == 1 } ;
160+ if is_coroutine {
161+ Err ( PyErr :: new :: < PyNotImplementedError , _ > (
162+ "Calling async is not supported" ,
163+ ) )
164+ } else {
165+ CLRepr :: from_python_ref ( call_res. as_ref ( py) )
166+ }
167+ } ) ;
168+ match python_call_res {
169+ Ok ( r) => Ok ( to_minijinja_value ( r) ) ,
170+ Err ( err) => Err ( mj:: Error :: new (
171+ minijinja:: ErrorKind :: InvalidOperation ,
172+ format ! ( "Error while calling filter: {}" , err) ,
173+ ) ) ,
174+ }
175+ } ,
176+ )
177+ }
178+ }
179+
103180 Ok ( Self { inner : engine } )
104181 }
105182}
0 commit comments