1
- use std :: borrow :: Cow ;
1
+ use core :: fmt ;
2
2
3
3
use pyo3:: exceptions:: PyKeyError ;
4
4
use pyo3:: prelude:: * ;
@@ -96,15 +96,6 @@ pub enum ReprOutput<'py> {
96
96
Fallback ( String ) ,
97
97
}
98
98
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
-
108
99
impl std:: fmt:: Display for ReprOutput < ' _ > {
109
100
fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
110
101
match self {
@@ -124,6 +115,15 @@ pub fn safe_repr<'py>(v: &Bound<'py, PyAny>) -> ReprOutput<'py> {
124
115
}
125
116
}
126
117
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
+
127
127
pub fn extract_i64 ( v : & Bound < ' _ , PyAny > ) -> Option < i64 > {
128
128
#[ cfg( PyPy ) ]
129
129
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
146
146
pystring_fast_new ( py, s, ascii_only)
147
147
}
148
148
}
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