1
1
use std:: borrow:: Cow ;
2
2
use std:: fmt:: Debug ;
3
+ use std:: io:: { self , Write } ;
3
4
4
5
use pyo3:: exceptions:: PyTypeError ;
5
6
use pyo3:: prelude:: * ;
@@ -9,7 +10,7 @@ use pyo3::{intern, PyTraverseError, PyVisit};
9
10
10
11
use enum_dispatch:: enum_dispatch;
11
12
use serde:: Serialize ;
12
- use serde_json:: ser:: PrettyFormatter ;
13
+ use serde_json:: ser:: { Formatter , PrettyFormatter } ;
13
14
14
15
use crate :: build_tools:: py_schema_err;
15
16
use crate :: build_tools:: py_schema_error_type;
@@ -349,6 +350,71 @@ impl Serialize for PydanticSerializer<'_> {
349
350
}
350
351
}
351
352
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
+
352
418
#[ allow( clippy:: too_many_arguments) ]
353
419
pub ( crate ) fn to_json_bytes (
354
420
value : & Bound < ' _ , PyAny > ,
@@ -357,25 +423,40 @@ pub(crate) fn to_json_bytes(
357
423
exclude : Option < & Bound < ' _ , PyAny > > ,
358
424
extra : & Extra ,
359
425
indent : Option < usize > ,
426
+ ensure_ascii : bool ,
360
427
expected_json_size : usize ,
361
428
) -> PyResult < Vec < u8 > > {
362
429
let serializer = PydanticSerializer :: new ( value, serializer, include, exclude, extra) ;
363
430
364
431
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 ) => {
367
442
let indent = vec ! [ b' ' ; indent] ;
368
443
let formatter = PrettyFormatter :: with_indent ( & indent) ;
369
444
let mut ser = PythonSerializer :: with_formatter ( writer, formatter) ;
370
445
serializer. serialize ( & mut ser) . map_err ( se_err_py_err) ?;
371
446
ser. into_inner ( )
372
447
}
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 ) => {
374
454
let mut ser = PythonSerializer :: new ( writer) ;
375
455
serializer. serialize ( & mut ser) . map_err ( se_err_py_err) ?;
376
456
ser. into_inner ( )
377
457
}
378
458
} ;
459
+
379
460
Ok ( bytes)
380
461
}
381
462
0 commit comments