@@ -24,6 +24,8 @@ pub mod json;
2424pub mod tab;
2525/// TLS configuration module.
2626pub mod tls;
27+ /// Utility functions module.
28+ pub mod utils;
2729
2830/// Trait for writing data in different formats
2931pub trait FormatWriter {
@@ -32,6 +34,21 @@ pub trait FormatWriter {
3234 fn finalize ( self ) -> Result < ( ) > ;
3335}
3436
37+ /// Trait for streaming data processing (future enhancement)
38+ ///
39+ /// This trait will enable memory-efficient processing of large result sets
40+ /// by processing rows one at a time instead of loading everything into memory.
41+ pub trait StreamingProcessor {
42+ type Item ;
43+ type Error ;
44+
45+ /// Process a single item from the stream
46+ fn process_item ( & mut self , item : Self :: Item ) -> std:: result:: Result < ( ) , Self :: Error > ;
47+
48+ /// Finalize the streaming operation
49+ fn finalize ( self ) -> std:: result:: Result < ( ) , Self :: Error > ;
50+ }
51+
3552// TODO: Implement RowStream with correct QueryResult type signature
3653// pub struct RowStream<'a> {
3754// result: mysql::QueryResult<'a>,
@@ -61,6 +78,7 @@ pub fn rows_to_strings(rows: Vec<Row>) -> anyhow::Result<Vec<Vec<String>>> {
6178 return Ok ( Vec :: new ( ) ) ;
6279 }
6380
81+ // Pre-allocate with known capacity for better performance
6482 let mut result_rows = Vec :: with_capacity ( rows. len ( ) + 1 ) ;
6583
6684 // Extract headers from the first row
@@ -72,13 +90,19 @@ pub fn rows_to_strings(rows: Vec<Row>) -> anyhow::Result<Vec<Vec<String>>> {
7290 result_rows. push ( header_row) ;
7391
7492 // Process each row using safe iteration
75- for row in rows {
93+ for ( row_index , row) in rows. iter ( ) . enumerate ( ) {
7694 let mut data_row = Vec :: with_capacity ( row. len ( ) ) ;
7795 for i in 0 ..row. len ( ) {
7896 match row. as_ref ( i) {
7997 Some ( value) => match mysql_value_to_string ( value) {
8098 Ok ( string_value) => data_row. push ( string_value) ,
81- Err ( e) => return Err ( e. context ( "Type conversion failed during row processing" ) ) ,
99+ Err ( e) => {
100+ return Err ( e. context ( format ! (
101+ "Type conversion failed at row {} column {}" ,
102+ row_index + 1 ,
103+ i + 1
104+ ) ) ) ;
105+ } ,
82106 } ,
83107 None => data_row. push ( String :: new ( ) ) ,
84108 }
@@ -114,11 +138,15 @@ fn mysql_value_to_string(value: &mysql::Value) -> anyhow::Result<String> {
114138 mysql:: Value :: NULL => Ok ( String :: new ( ) ) ,
115139 mysql:: Value :: Bytes ( bytes) => {
116140 // Try to convert bytes to UTF-8 string, fallback to lossy conversion
117- // Use Cow to avoid unnecessary allocation when bytes are valid UTF-8
118- Ok ( match std:: str:: from_utf8 ( bytes) {
119- Ok ( s) => s. to_string ( ) ,
120- Err ( _) => String :: from_utf8_lossy ( bytes) . into_owned ( ) ,
121- } )
141+ // For binary data that's not valid UTF-8, use lossy conversion with clear indication
142+ match std:: str:: from_utf8 ( bytes) {
143+ Ok ( s) => Ok ( s. to_string ( ) ) ,
144+ Err ( _) => {
145+ // For binary data, use lossy conversion
146+ let lossy = String :: from_utf8_lossy ( bytes) ;
147+ Ok ( lossy. into_owned ( ) )
148+ } ,
149+ }
122150 } ,
123151 mysql:: Value :: Int ( i) => Ok ( i. to_string ( ) ) ,
124152 mysql:: Value :: UInt ( u) => Ok ( u. to_string ( ) ) ,
@@ -248,15 +276,12 @@ mod tests {
248276
249277 #[ test]
250278 fn test_get_required_env_present ( ) {
251- unsafe {
252- std:: env:: set_var ( "TEST_ENV_VAR" , "test_value" ) ;
253- }
254- let result = get_required_env ( "TEST_ENV_VAR" ) ;
255- assert ! ( result. is_ok( ) ) ;
256- assert_eq ! ( result. unwrap( ) , "test_value" ) ;
257- unsafe {
258- std:: env:: remove_var ( "TEST_ENV_VAR" ) ;
259- }
279+ // Use temp_env for safer environment variable testing
280+ temp_env:: with_var ( "TEST_ENV_VAR" , Some ( "test_value" ) , || {
281+ let result = get_required_env ( "TEST_ENV_VAR" ) ;
282+ assert ! ( result. is_ok( ) ) ;
283+ assert_eq ! ( result. unwrap( ) , "test_value" ) ;
284+ } ) ;
260285 }
261286
262287 #[ test]
0 commit comments