@@ -7,7 +7,8 @@ use pyo3::prelude::*;
77use serde:: ser:: { self , Serialize , SerializeMap , SerializeSeq , Serializer } ;
88use smallvec:: SmallVec ;
99use std:: ffi:: CStr ;
10- use std:: os:: raw:: c_char;
10+ use std:: io:: Write ;
11+ use std:: os:: raw:: { c_char, c_uchar} ;
1112use std:: ptr:: NonNull ;
1213
1314// https://tools.ietf.org/html/rfc7159#section-6
@@ -19,6 +20,7 @@ const RECURSION_LIMIT: u8 = 255;
1920
2021pub const STRICT_INTEGER : u8 = 1 ;
2122pub const SERIALIZE_DATACLASS : u8 = 1 << 4 ;
23+ pub const SERIALIZE_UUID : u8 = 1 << 5 ;
2224
2325macro_rules! obj_name {
2426 ( $obj: ident) => {
@@ -186,6 +188,44 @@ impl<'p> Serialize for SerializePyObject {
186188 let mut dt: SmallVec < [ u8 ; 32 ] > = SmallVec :: with_capacity ( 32 ) ;
187189 write_time ( self . ptr , self . opts , & mut dt) ;
188190 serializer. serialize_str ( str_from_slice ! ( dt. as_ptr( ) , dt. len( ) ) )
191+ } else if is_type ! ( obj_ptr, UUID_PTR ) && ( self . opts & SERIALIZE_UUID == SERIALIZE_UUID ) {
192+ // In Python, `self.int` is the 128-bit integer value of the UUID;
193+ // we can assume this will not fail, as tested in `test_uuid.py`
194+ let py_int = ffi ! ( PyObject_GetAttr ( self . ptr, INT_ATTR_STR ) ) ;
195+ // Copied in from https://github.com/PyO3/pyo3/blob/fb17d5e82f302f09b6611ac608edd1ce37504703/src/types/num.rs#L95
196+ // because we don't have a `pyo3::Python` reference. However, because
197+ // we haven't yet Py_DECREF'd the `py_int` attribute, the reference
198+ // to `self.int` should be valid, and this should be safe to do.
199+
F889
// We know it's a `PyLongObject` because `self.int` is a 128-bit int,
200+ // and know that _PyLong_AsByteArray won't error, as tested in test_uuid.py
201+ let buffer: [ c_uchar ; 16 ] = [ 0 ; 16 ] ;
202+ unsafe {
203+ pyo3:: ffi:: _PyLong_AsByteArray (
204+ py_int as * mut pyo3:: ffi:: PyLongObject ,
205+ buffer. as_ptr ( ) as * const c_uchar ,
206+ 16 ,
207+ 1 , // Return a little-endian array
208+ 0 , // Unsigned - UUIDs can't be negative
209+ )
210+ } ;
211+ ffi ! ( Py_DECREF ( py_int) ) ;
212+ let value = u128:: from_le_bytes ( buffer) ;
213+ let mut hexadecimal: SmallVec < [ u8 ; 32 ] > = SmallVec :: with_capacity ( 32 ) ;
214+ write ! ( hexadecimal, "{:032x}" , value) . unwrap ( ) ;
215+ // Now we manually format it in canonical form: 5 groups separated
216+ // by hyphens, 8-4-4-4-12
217+ // https://en.wikipedia.org/wiki/Universally_unique_identifier#Format
218+ let mut formatted: SmallVec < [ u8 ; 36 ] > = SmallVec :: with_capacity ( 36 ) ;
219+ formatted. extend_from_slice ( & hexadecimal[ ..8 ] ) ;
220+ formatted. push ( '-' as u8 ) ;
221+ formatted. extend_from_slice ( & hexadecimal[ 8 ..12 ] ) ;
222+ formatted. push ( '-' as u8 ) ;
223+ formatted. extend_from_slice ( & hexadecimal[ 12 ..16 ] ) ;
224+ formatted. push ( '-' as u8 ) ;
225+ formatted. extend_from_slice ( & hexadecimal[ 16 ..20 ] ) ;
226+ formatted. push ( '-' as u8 ) ;
227+ formatted. extend_from_slice ( & hexadecimal[ 20 ..] ) ;
228+ serializer. serialize_str ( str_from_slice ! ( formatted. as_ptr( ) , 36 ) )
189229 } else {
190230 if self . opts & SERIALIZE_DATACLASS == SERIALIZE_DATACLASS
191231 && ffi ! ( PyObject_HasAttr ( self . ptr, DATACLASS_FIELDS_STR ) ) == 1
0 commit comments