11use std:: borrow:: Cow ;
22use std:: fmt:: Debug ;
3+ use std:: io:: { self , Write } ;
34
45use pyo3:: exceptions:: PyTypeError ;
56use pyo3:: prelude:: * ;
@@ -9,7 +10,7 @@ use pyo3::{intern, PyTraverseError, PyVisit};
910
1011use enum_dispatch:: enum_dispatch;
1112use serde:: Serialize ;
12- use serde_json:: ser:: PrettyFormatter ;
13+ use serde_json:: ser:: { Formatter , PrettyFormatter } ;
1314
1415use crate :: build_tools:: py_schema_err;
1516use crate :: build_tools:: py_schema_error_type;
@@ -349,6 +350,71 @@ impl Serialize for PydanticSerializer<'_> {
349350 }
350351}
351352
353+ struct EscapeNonAsciiFormatter ;
354+
355+ impl Formatter for EscapeNonAsciiFormatter {
356+ fn write_string_fragment < W : ?Sized + Write > ( & mut self , writer : & mut W , fragment : & str ) -> io:: Result < ( ) > {
357+ for ch in fragment. chars ( ) {
358+ if ch. is_ascii ( ) {
359+ writer. write_all ( ch. encode_utf8 ( & mut [ 0 ; 4 ] ) . as_bytes ( ) ) ?;
360+ } else {
361+ for escape in ch. encode_utf16 ( & mut [ 0 ; 2 ] ) {
362+ write ! ( writer, "\\ u{escape:04x}" ) ?;
363+ }
364+ }
365+ }
366+ Ok ( ( ) )
367+ }
368+ }
369+
370+ struct EscapeNonAsciiPrettyFormatter < ' a > {
371+ pretty : PrettyFormatter < ' a > ,
372+ escape_non_ascii : EscapeNonAsciiFormatter ,
373+ }
374+
375+ impl < ' a > EscapeNonAsciiPrettyFormatter < ' a > {
376+ pub fn with_indent ( indent : & ' a [ u8 ] ) -> Self {
377+ Self {
378+ pretty : PrettyFormatter :: with_indent ( indent) ,
379+ escape_non_ascii : EscapeNonAsciiFormatter ,
380+ }
381+ }
382+ }
383+
384+ macro_rules! defer {
385+ ( $formatter: ident, $fun: ident) => {
386+ fn $fun<W >( & mut self , writer: & mut W ) -> io:: Result <( ) >
387+ where
388+ W : ?Sized + io:: Write ,
389+ {
390+ self . $formatter. $fun( writer)
391+ }
392+ } ;
393+ ( $formatter: ident, $fun: ident, $val: ty) => {
394+ fn $fun<W >( & mut self , writer: & mut W , val: $val) -> io:: Result <( ) >
395+ where
396+ W : ?Sized + io:: Write ,
397+ {
398+ self . $formatter. $fun( writer, val)
399+ }
400+ } ;
401+ }
402+
403+ #[ allow( clippy:: needless_lifetimes) ]
404+ impl < ' a > Formatter for EscapeNonAsciiPrettyFormatter < ' a > {
405+ defer ! ( escape_non_ascii, write_string_fragment, & str ) ;
406+ defer ! ( pretty, begin_array) ;
407+ defer ! ( pretty, end_array) ;
408+ defer ! ( pretty, begin_array_value, bool ) ;
409+ defer ! ( pretty, end_array_value) ;
410+ defer ! ( pretty, begin_object) ;
411+ defer ! ( pretty, end_object) ;
412+ defer ! ( pretty, begin_object_key, bool ) ;
413+ defer ! ( pretty, end_object_key) ;
414+ defer ! ( pretty, begin_object_value) ;
415+ defer ! ( pretty, end_object_value) ;
416+ }
417+
352418#[ allow( clippy:: too_many_arguments) ]
353419pub ( crate ) fn to_json_bytes (
354420 value : & Bound < ' _ , PyAny > ,
@@ -357,25 +423,40 @@ pub(crate) fn to_json_bytes(
357423 exclude : Option < & Bound < ' _ , PyAny > > ,
358424 extra : & Extra ,
359425 indent : Option < usize > ,
426+ ensure_ascii : bool ,
360427 expected_json_size : usize ,
361428) -> PyResult < Vec < u8 > > {
362429 let serializer = PydanticSerializer :: new ( value, serializer, include, exclude, extra) ;
363430
364431 let writer: Vec < u8 > = Vec :: with_capacity ( expected_json_size) ;
365- let bytes = match indent {
366- Some ( indent) => {
432+
433+ let bytes = match ( indent, ensure_ascii) {
434+ ( Some ( indent) , true ) => {
435+ let indent = vec ! [ b' ' ; indent] ;
436+ let formatter = EscapeNonAsciiPrettyFormatter :: with_indent ( & indent) ;
437+ let mut ser = PythonSerializer :: with_formatter ( writer, formatter) ;
438+ serializer. serialize ( & mut ser) . map_err ( se_err_py_err) ?;
439+ ser. into_inner ( )
440+ }
441+ ( Some ( indent) , false ) => {
367442 let indent = vec ! [ b' ' ; indent] ;
368443 let formatter = PrettyFormatter :: with_indent ( & indent) ;
369444 let mut ser = PythonSerializer :: with_formatter ( writer, formatter) ;
370445 serializer. serialize ( & mut ser) . map_err ( se_err_py_err) ?;
371446 ser. into_inner ( )
372447 }
373- None => {
448+ ( None , true ) => {
449+ let mut ser = PythonSerializer :: with_formatter ( writer, EscapeNonAsciiFormatter ) ;
450+ serializer. serialize ( & mut ser) . map_err ( se_err_py_err) ?;
451+ ser. into_inner ( )
452+ }
453+ ( None , false ) => {
374454 let mut ser = PythonSerializer :: new ( writer) ;
375455 serializer. serialize ( & mut ser) . map_err ( se_err_py_err) ?;
376456 ser. into_inner ( )
377457 }
378458 } ;
459+
379460 Ok ( bytes)
380461}
381462
0 commit comments