1- use std :: borrow :: Cow ;
1+ use core :: fmt ;
22
33use pyo3:: exceptions:: PyKeyError ;
44use pyo3:: prelude:: * ;
@@ -96,15 +96,6 @@ pub enum ReprOutput<'py> {
9696 Fallback ( String ) ,
9797}
9898
99- impl ReprOutput < ' _ > {
100- pub fn to_cow ( & self ) -> Cow < ' _ , str > {
101- match self {
102- ReprOutput :: Python ( s) => s. to_string_lossy ( ) ,
103- ReprOutput :: Fallback ( s) => s. into ( ) ,
104- }
105- }
106- }
107-
10899impl std:: fmt:: Display for ReprOutput < ' _ > {
109100 fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
110101 match self {
@@ -124,6 +115,15 @@ pub fn safe_repr<'py>(v: &Bound<'py, PyAny>) -> ReprOutput<'py> {
124115 }
125116}
126117
118+ pub fn truncate_safe_repr ( v : & Bound < ' _ , PyAny > , max_len : Option < usize > ) -> String {
119+ let max_len = max_len. unwrap_or ( 50 ) ; // default to 100 bytes
120+ let input_str = safe_repr ( v) ;
121+ let mut limited_str = String :: with_capacity ( max_len) ;
122+ write_truncated_to_limited_bytes ( & mut limited_str, & input_str. to_string ( ) , max_len)
123+ . expect ( "Writing to a `String` failed" ) ;
124+ limited_str
125+ }
126+
127127pub fn extract_i64 ( v : & Bound < ' _ , PyAny > ) -> Option < i64 > {
128128 #[ cfg( PyPy ) ]
129129 if !v. is_instance_of :: < pyo3:: types:: PyInt > ( ) {
@@ -146,3 +146,47 @@ pub(crate) fn new_py_string<'py>(py: Python<'py>, s: &str, cache_str: StringCach
146146 pystring_fast_new ( py, s, ascii_only)
147147 }
148148}
149+
150+ // TODO: is_utf8_char_boundary, floor_char_boundary and ceil_char_boundary
151+ // with builtin methods once https://github.com/rust-lang/rust/issues/93743 is resolved
152+ // These are just copy pasted from the current implementation
153+ const fn is_utf8_char_boundary ( value : u8 ) -> bool {
154+ // This is bit magic equivalent to: b < 128 || b >= 192
155+ ( value as i8 ) >= -0x40
156+ }
157+
158+ pub fn floor_char_boundary ( value : & str , index : usize ) -> usize {
159+ if index >= value. len ( ) {
160+ value. len ( )
161+ } else {
162+ let lower_bound = index. saturating_sub ( 3 ) ;
163+ let new_index = value. as_bytes ( ) [ lower_bound..=index]
164+ . iter ( )
165+ . rposition ( |b| is_utf8_char_boundary ( * b) ) ;
166+
167+ // SAFETY: we know that the character boundary will be within four bytes
168+ unsafe { lower_bound + new_index. unwrap_unchecked ( ) }
169+ }
170+ }
171+
172+ pub fn ceil_char_boundary ( value : & str , index : usize ) -> usize {
173+ let upper_bound = Ord :: min ( index + 4 , value. len ( ) ) ;
174+ value. as_bytes ( ) [ index..upper_bound]
175+ . iter ( )
176+ . position ( |b| is_utf8_char_boundary ( * b) )
177+ . map_or ( upper_bound, |pos| pos + index)
178+ }
179+
180+ pub fn write_truncated_to_limited_bytes < F : fmt:: Write > ( f : & mut F , val : & str , max_len : usize ) -> std:: fmt:: Result {
181+ if val. len ( ) > max_len {
182+ let mid_point = max_len. div_ceil ( 2 ) ;
183+ write ! (
184+ f,
185+ "{}...{}" ,
186+ & val[ 0 ..floor_char_boundary( val, mid_point) ] ,
187+ & val[ ceil_char_boundary( val, val. len( ) - ( mid_point - 1 ) ) ..]
188+ )
189+ } else {
190+ write ! ( f, "{val}" )
191+ }
192+ }
0 commit comments