1 use std::fmt; 2 use std::io; 3 use std::iter::FromIterator; 4 use std::ops::{self, Range}; 5 use std::result; 6 use std::str; 7 8 use serde::de::Deserialize; 9 10 use crate::byte_record::{ByteRecord, ByteRecordIter, Position}; 11 use crate::deserializer::deserialize_string_record; 12 use crate::error::{Error, ErrorKind, FromUtf8Error, Result}; 13 use crate::reader::Reader; 14 15 /// A single CSV record stored as valid UTF-8 bytes. 16 /// 17 /// A string record permits reading or writing CSV rows that are valid UTF-8. 18 /// If string records are used to read CSV data that is not valid UTF-8, then 19 /// the CSV reader will return an invalid UTF-8 error. If you do need to read 20 /// possibly invalid UTF-8 data, then you should prefer using a 21 /// [`ByteRecord`](struct.ByteRecord.html), 22 /// since it makes no assumptions about UTF-8. 23 /// 24 /// If you are using the Serde (de)serialization APIs, then you probably never 25 /// need to interact with a `ByteRecord` or a `StringRecord`. However, there 26 /// are some circumstances in which you might need to use a raw record type 27 /// while still using Serde. For example, if you need to deserialize possibly 28 /// invalid UTF-8 fields, then you'll need to first read your record into a 29 /// `ByteRecord`, and then use `ByteRecord::deserialize` to run Serde. Another 30 /// reason for using the raw record deserialization APIs is if you're using 31 /// Serde to read into borrowed data such as a `&'a str` or a `&'a [u8]`. 32 /// 33 /// Two `StringRecord`s are compared on the basis of their field data. Any 34 /// position information associated with the records is ignored. 35 #[derive(Clone, Eq)] 36 pub struct StringRecord(ByteRecord); 37 38 impl PartialEq for StringRecord { eq(&self, other: &StringRecord) -> bool39 fn eq(&self, other: &StringRecord) -> bool { 40 self.0.iter_eq(&other.0) 41 } 42 } 43 44 impl<T: AsRef<[u8]>> PartialEq<Vec<T>> for StringRecord { eq(&self, other: &Vec<T>) -> bool45 fn eq(&self, other: &Vec<T>) -> bool { 46 self.0.iter_eq(other) 47 } 48 } 49 50 impl<'a, T: AsRef<[u8]>> PartialEq<Vec<T>> for &'a StringRecord { eq(&self, other: &Vec<T>) -> bool51 fn eq(&self, other: &Vec<T>) -> bool { 52 self.0.iter_eq(other) 53 } 54 } 55 56 impl<T: AsRef<[u8]>> PartialEq<[T]> for StringRecord { eq(&self, other: &[T]) -> bool57 fn eq(&self, other: &[T]) -> bool { 58 self.0.iter_eq(other) 59 } 60 } 61 62 impl<'a, T: AsRef<[u8]>> PartialEq<[T]> for &'a StringRecord { eq(&self, other: &[T]) -> bool63 fn eq(&self, other: &[T]) -> bool { 64 self.0.iter_eq(other) 65 } 66 } 67 68 impl fmt::Debug for StringRecord { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result69 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 70 let fields: Vec<&str> = self.iter().collect(); 71 write!(f, "StringRecord({:?})", fields) 72 } 73 } 74 75 impl Default for StringRecord { 76 #[inline] default() -> StringRecord77 fn default() -> StringRecord { 78 StringRecord::new() 79 } 80 } 81 82 impl StringRecord { 83 /// Create a new empty `StringRecord`. 84 /// 85 /// Note that you may find the `StringRecord::from` constructor more 86 /// convenient, which is provided by an impl on the `From` trait. 87 /// 88 /// # Example: create an empty record 89 /// 90 /// ``` 91 /// use csv::StringRecord; 92 /// 93 /// let record = StringRecord::new(); 94 /// assert_eq!(record.len(), 0); 95 /// ``` 96 /// 97 /// # Example: initialize a record from a `Vec` 98 /// 99 /// ``` 100 /// use csv::StringRecord; 101 /// 102 /// let record = StringRecord::from(vec!["a", "b", "c"]); 103 /// assert_eq!(record.len(), 3); 104 /// ``` 105 #[inline] new() -> StringRecord106 pub fn new() -> StringRecord { 107 StringRecord(ByteRecord::new()) 108 } 109 110 /// Create a new empty `StringRecord` with the given capacity. 111 /// 112 /// `buffer` refers to the capacity of the buffer used to store the 113 /// actual row contents. `fields` refers to the number of fields one 114 /// might expect to store. 115 #[inline] with_capacity(buffer: usize, fields: usize) -> StringRecord116 pub fn with_capacity(buffer: usize, fields: usize) -> StringRecord { 117 StringRecord(ByteRecord::with_capacity(buffer, fields)) 118 } 119 120 /// Create a new `StringRecord` from a `ByteRecord`. 121 /// 122 /// Note that this does UTF-8 validation. If the given `ByteRecord` does 123 /// not contain valid UTF-8, then this returns an error. The error includes 124 /// the UTF-8 error and the original `ByteRecord`. 125 /// 126 /// # Example: valid UTF-8 127 /// 128 /// ``` 129 /// use std::error::Error; 130 /// use csv::{ByteRecord, StringRecord}; 131 /// 132 /// # fn main() { example().unwrap(); } 133 /// fn example() -> Result<(), Box<dyn Error>> { 134 /// let byte_record = ByteRecord::from(vec!["a", "b", "c"]); 135 /// let str_record = StringRecord::from_byte_record(byte_record)?; 136 /// assert_eq!(str_record.len(), 3); 137 /// Ok(()) 138 /// } 139 /// ``` 140 /// 141 /// # Example: invalid UTF-8 142 /// 143 /// ``` 144 /// use csv::{ByteRecord, StringRecord}; 145 /// 146 /// let byte_record = ByteRecord::from(vec![ 147 /// &b"quux"[..], &b"foo\xFFbar"[..], &b"c"[..], 148 /// ]); 149 /// let err = StringRecord::from_byte_record(byte_record).unwrap_err(); 150 /// assert_eq!(err.utf8_error().field(), 1); 151 /// assert_eq!(err.utf8_error().valid_up_to(), 3); 152 /// ``` 153 #[inline] from_byte_record( record: ByteRecord, ) -> result::Result<StringRecord, FromUtf8Error>154 pub fn from_byte_record( 155 record: ByteRecord, 156 ) -> result::Result<StringRecord, FromUtf8Error> { 157 match record.validate() { 158 Ok(()) => Ok(StringRecord(record)), 159 Err(err) => Err(FromUtf8Error::new(record, err)), 160 } 161 } 162 163 /// Lossily create a new `StringRecord` from a `ByteRecord`. 164 /// 165 /// This is like `StringRecord::from_byte_record`, except all invalid UTF-8 166 /// sequences are replaced with the `U+FFFD REPLACEMENT CHARACTER`, which 167 /// looks like this: �. 168 /// 169 /// # Example: valid UTF-8 170 /// 171 /// ``` 172 /// use csv::{ByteRecord, StringRecord}; 173 /// 174 /// let byte_record = ByteRecord::from(vec!["a", "b", "c"]); 175 /// let str_record = StringRecord::from_byte_record_lossy(byte_record); 176 /// assert_eq!(str_record.len(), 3); 177 /// ``` 178 /// 179 /// # Example: invalid UTF-8 180 /// 181 /// ``` 182 /// use csv::{ByteRecord, StringRecord}; 183 /// 184 /// let byte_record = ByteRecord::from(vec![ 185 /// &b"quux"[..], &b"foo\xFFbar"[..], &b"c"[..], 186 /// ]); 187 /// let str_record = StringRecord::from_byte_record_lossy(byte_record); 188 /// assert_eq!(&str_record[0], "quux"); 189 /// assert_eq!(&str_record[1], "foo�bar"); 190 /// assert_eq!(&str_record[2], "c"); 191 /// ``` 192 #[inline] from_byte_record_lossy(record: ByteRecord) -> StringRecord193 pub fn from_byte_record_lossy(record: ByteRecord) -> StringRecord { 194 // If the record is valid UTF-8, then take the easy path. 195 if let Ok(()) = record.validate() { 196 return StringRecord(record); 197 } 198 // TODO: We can be faster here. Not sure if it's worth it. 199 let mut str_record = 200 StringRecord::with_capacity(record.as_slice().len(), record.len()); 201 for field in &record { 202 str_record.push_field(&String::from_utf8_lossy(field)); 203 } 204 str_record 205 } 206 207 /// Deserialize this record. 208 /// 209 /// The `D` type parameter refers to the type that this record should be 210 /// deserialized into. The `'de` lifetime refers to the lifetime of the 211 /// `StringRecord`. The `'de` lifetime permits deserializing into structs 212 /// that borrow field data from this record. 213 /// 214 /// An optional `headers` parameter permits deserializing into a struct 215 /// based on its field names (corresponding to header values) rather than 216 /// the order in which the fields are defined. 217 /// 218 /// # Example: without headers 219 /// 220 /// This shows how to deserialize a single row into a struct based on the 221 /// order in which fields occur. This example also shows how to borrow 222 /// fields from the `StringRecord`, which results in zero allocation 223 /// deserialization. 224 /// 225 /// ``` 226 /// use std::error::Error; 227 /// 228 /// use csv::StringRecord; 229 /// use serde::Deserialize; 230 /// 231 /// #[derive(Deserialize)] 232 /// struct Row<'a> { 233 /// city: &'a str, 234 /// country: &'a str, 235 /// population: u64, 236 /// } 237 /// 238 /// # fn main() { example().unwrap() } 239 /// fn example() -> Result<(), Box<dyn Error>> { 240 /// let record = StringRecord::from(vec![ 241 /// "Boston", "United States", "4628910", 242 /// ]); 243 /// 244 /// let row: Row = record.deserialize(None)?; 245 /// assert_eq!(row.city, "Boston"); 246 /// assert_eq!(row.country, "United States"); 247 /// assert_eq!(row.population, 4628910); 248 /// Ok(()) 249 /// } 250 /// ``` 251 /// 252 /// # Example: with headers 253 /// 254 /// This example is like the previous one, but shows how to deserialize 255 /// into a struct based on the struct's field names. For this to work, 256 /// you must provide a header row. 257 /// 258 /// This example also shows that you can deserialize into owned data 259 /// types (e.g., `String`) instead of borrowed data types (e.g., `&str`). 260 /// 261 /// ``` 262 /// use std::error::Error; 263 /// 264 /// use csv::StringRecord; 265 /// use serde::Deserialize; 266 /// 267 /// #[derive(Deserialize)] 268 /// struct Row { 269 /// city: String, 270 /// country: String, 271 /// population: u64, 272 /// } 273 /// 274 /// # fn main() { example().unwrap() } 275 /// fn example() -> Result<(), Box<dyn Error>> { 276 /// // Notice that the fields are not in the same order 277 /// // as the fields in the struct! 278 /// let header = StringRecord::from(vec![ 279 /// "country", "city", "population", 280 /// ]); 281 /// let record = StringRecord::from(vec![ 282 /// "United States", "Boston", "4628910", 283 /// ]); 284 /// 285 /// let row: Row = record.deserialize(Some(&header))?; 286 /// assert_eq!(row.city, "Boston"); 287 /// assert_eq!(row.country, "United States"); 288 /// assert_eq!(row.population, 4628910); 289 /// Ok(()) 290 /// } 291 /// ``` deserialize<'de, D: Deserialize<'de>>( &'de self, headers: Option<&'de StringRecord>, ) -> Result<D>292 pub fn deserialize<'de, D: Deserialize<'de>>( 293 &'de self, 294 headers: Option<&'de StringRecord>, 295 ) -> Result<D> { 296 deserialize_string_record(self, headers) 297 } 298 299 /// Returns an iterator over all fields in this record. 300 /// 301 /// # Example 302 /// 303 /// This example shows how to iterate over each field in a `StringRecord`. 304 /// 305 /// ``` 306 /// use csv::StringRecord; 307 /// 308 /// let record = StringRecord::from(vec!["a", "b", "c"]); 309 /// for field in record.iter() { 310 /// assert!(field == "a" || field == "b" || field == "c"); 311 /// } 312 /// ``` 313 #[inline] iter(&self) -> StringRecordIter314 pub fn iter(&self) -> StringRecordIter { 315 self.into_iter() 316 } 317 318 /// Return the field at index `i`. 319 /// 320 /// If no field at index `i` exists, then this returns `None`. 321 /// 322 /// # Example 323 /// 324 /// ``` 325 /// use csv::StringRecord; 326 /// 327 /// let record = StringRecord::from(vec!["a", "b", "c"]); 328 /// assert_eq!(record.get(1), Some("b")); 329 /// assert_eq!(record.get(3), None); 330 /// ``` 331 #[inline] get(&self, i: usize) -> Option<&str>332 pub fn get(&self, i: usize) -> Option<&str> { 333 self.0.get(i).map(|bytes| { 334 debug_assert!(str::from_utf8(bytes).is_ok()); 335 // This is safe because we guarantee that all string records 336 // have a valid UTF-8 buffer. It's also safe because we 337 // individually check each field for valid UTF-8. 338 unsafe { str::from_utf8_unchecked(bytes) } 339 }) 340 } 341 342 /// Returns true if and only if this record is empty. 343 /// 344 /// # Example 345 /// 346 /// ``` 347 /// use csv::StringRecord; 348 /// 349 /// assert!(StringRecord::new().is_empty()); 350 /// ``` 351 #[inline] is_empty(&self) -> bool352 pub fn is_empty(&self) -> bool { 353 self.len() == 0 354 } 355 356 /// Returns the number of fields in this record. 357 /// 358 /// # Example 359 /// 360 /// ``` 361 /// use csv::StringRecord; 362 /// 363 /// let record = StringRecord::from(vec!["a", "b", "c"]); 364 /// assert_eq!(record.len(), 3); 365 /// ``` 366 #[inline] len(&self) -> usize367 pub fn len(&self) -> usize { 368 self.0.len() 369 } 370 371 /// Truncate this record to `n` fields. 372 /// 373 /// If `n` is greater than the number of fields in this record, then this 374 /// has no effect. 375 /// 376 /// # Example 377 /// 378 /// ``` 379 /// use csv::StringRecord; 380 /// 381 /// let mut record = StringRecord::from(vec!["a", "b", "c"]); 382 /// assert_eq!(record.len(), 3); 383 /// record.truncate(1); 384 /// assert_eq!(record.len(), 1); 385 /// assert_eq!(record, vec!["a"]); 386 /// ``` 387 #[inline] truncate(&mut self, n: usize)388 pub fn truncate(&mut self, n: usize) { 389 self.0.truncate(n); 390 } 391 392 /// Clear this record so that it has zero fields. 393 /// 394 /// Note that it is not necessary to clear the record to reuse it with 395 /// the CSV reader. 396 /// 397 /// # Example 398 /// 399 /// ``` 400 /// use csv::StringRecord; 401 /// 402 /// let mut record = StringRecord::from(vec!["a", "b", "c"]); 403 /// assert_eq!(record.len(), 3); 404 /// record.clear(); 405 /// assert_eq!(record.len(), 0); 406 /// ``` 407 #[inline] clear(&mut self)408 pub fn clear(&mut self) { 409 self.0.clear(); 410 } 411 412 /// Trim the fields of this record so that leading and trailing whitespace 413 /// is removed. 414 /// 415 /// This method uses the Unicode definition of whitespace. 416 /// 417 /// # Example 418 /// 419 /// ``` 420 /// use csv::StringRecord; 421 /// 422 /// let mut record = StringRecord::from(vec![ 423 /// " ", "\u{3000}\tfoo ", "bar ", "b a z", 424 /// ]); 425 /// record.trim(); 426 /// assert_eq!(record, vec!["", "foo", "bar", "b a z"]); 427 /// ``` trim(&mut self)428 pub fn trim(&mut self) { 429 let length = self.len(); 430 if length == 0 { 431 return; 432 } 433 // TODO: We could likely do this in place, but for now, we allocate. 434 let mut trimmed = 435 StringRecord::with_capacity(self.as_slice().len(), self.len()); 436 trimmed.set_position(self.position().cloned()); 437 for field in &*self { 438 trimmed.push_field(field.trim()); 439 } 440 *self = trimmed; 441 } 442 443 /// Add a new field to this record. 444 /// 445 /// # Example 446 /// 447 /// ``` 448 /// use csv::StringRecord; 449 /// 450 /// let mut record = StringRecord::new(); 451 /// record.push_field("foo"); 452 /// assert_eq!(&record[0], "foo"); 453 /// ``` 454 #[inline] push_field(&mut self, field: &str)455 pub fn push_field(&mut self, field: &str) { 456 self.0.push_field(field.as_bytes()); 457 } 458 459 /// Return the position of this record, if available. 460 /// 461 /// # Example 462 /// 463 /// ``` 464 /// use std::error::Error; 465 /// use csv::{StringRecord, ReaderBuilder}; 466 /// 467 /// # fn main() { example().unwrap(); } 468 /// fn example() -> Result<(), Box<dyn Error>> { 469 /// let mut record = StringRecord::new(); 470 /// let mut rdr = ReaderBuilder::new() 471 /// .has_headers(false) 472 /// .from_reader("a,b,c\nx,y,z".as_bytes()); 473 /// 474 /// assert!(rdr.read_record(&mut record)?); 475 /// { 476 /// let pos = record.position().expect("a record position"); 477 /// assert_eq!(pos.byte(), 0); 478 /// assert_eq!(pos.line(), 1); 479 /// assert_eq!(pos.record(), 0); 480 /// } 481 /// 482 /// assert!(rdr.read_record(&mut record)?); 483 /// { 484 /// let pos = record.position().expect("a record position"); 485 /// assert_eq!(pos.byte(), 6); 486 /// assert_eq!(pos.line(), 2); 487 /// assert_eq!(pos.record(), 1); 488 /// } 489 /// 490 /// // Finish the CSV reader for good measure. 491 /// assert!(!rdr.read_record(&mut record)?); 492 /// Ok(()) 493 /// } 494 /// ``` 495 #[inline] position(&self) -> Option<&Position>496 pub fn position(&self) -> Option<&Position> { 497 self.0.position() 498 } 499 500 /// Set the position of this record. 501 /// 502 /// # Example 503 /// 504 /// ``` 505 /// use csv::{StringRecord, Position}; 506 /// 507 /// let mut record = StringRecord::from(vec!["a", "b", "c"]); 508 /// let mut pos = Position::new(); 509 /// pos.set_byte(100); 510 /// pos.set_line(4); 511 /// pos.set_record(2); 512 /// 513 /// record.set_position(Some(pos.clone())); 514 /// assert_eq!(record.position(), Some(&pos)); 515 /// ``` 516 #[inline] set_position(&mut self, pos: Option<Position>)517 pub fn set_position(&mut self, pos: Option<Position>) { 518 self.0.set_position(pos); 519 } 520 521 /// Return the start and end position of a field in this record. 522 /// 523 /// If no such field exists at the given index, then return `None`. 524 /// 525 /// The range returned can be used with the slice returned by `as_slice`. 526 /// Namely, the range returned is guaranteed to start and end at valid 527 /// UTF-8 sequence boundaries. 528 /// 529 /// # Example 530 /// 531 /// ``` 532 /// use csv::StringRecord; 533 /// 534 /// let record = StringRecord::from(vec!["foo", "quux", "z"]); 535 /// let range = record.range(1).expect("a record range"); 536 /// assert_eq!(&record.as_slice()[range], "quux"); 537 /// ``` 538 #[inline] range(&self, i: usize) -> Option<Range<usize>>539 pub fn range(&self, i: usize) -> Option<Range<usize>> { 540 self.0.range(i) 541 } 542 543 /// Return the entire row as a single string slice. The slice returned 544 /// stores all fields contiguously. The boundaries of each field can be 545 /// determined via the `range` method. 546 /// 547 /// # Example 548 /// 549 /// ``` 550 /// use csv::StringRecord; 551 /// 552 /// let record = StringRecord::from(vec!["foo", "quux", "z"]); 553 /// assert_eq!(record.as_slice(), "fooquuxz"); 554 /// ``` 555 #[inline] as_slice(&self) -> &str556 pub fn as_slice(&self) -> &str { 557 debug_assert!(str::from_utf8(self.0.as_slice()).is_ok()); 558 // This is safe because we guarantee that each field is valid UTF-8. 559 // If each field is valid UTF-8, then the entire buffer (up to the end 560 // of the last field) must also be valid UTF-8. 561 unsafe { str::from_utf8_unchecked(self.0.as_slice()) } 562 } 563 564 /// Return a reference to this record's raw 565 /// [`ByteRecord`](struct.ByteRecord.html). 566 /// 567 /// # Example 568 /// 569 /// ``` 570 /// use csv::StringRecord; 571 /// 572 /// let str_record = StringRecord::from(vec!["a", "b", "c"]); 573 /// let byte_record = str_record.as_byte_record(); 574 /// assert_eq!(&byte_record[2], b"c"); 575 /// ``` 576 #[inline] as_byte_record(&self) -> &ByteRecord577 pub fn as_byte_record(&self) -> &ByteRecord { 578 &self.0 579 } 580 581 /// Convert this `StringRecord` into a 582 /// [`ByteRecord`](struct.ByteRecord.html). 583 /// 584 /// # Example 585 /// 586 /// ``` 587 /// use csv::StringRecord; 588 /// 589 /// let str_record = StringRecord::from(vec!["a", "b", "c"]); 590 /// let byte_record = str_record.into_byte_record(); 591 /// assert_eq!(&byte_record[2], b"c"); 592 /// ``` 593 /// 594 /// Note that this can also be achieved using the `From` impl: 595 /// 596 /// ``` 597 /// use csv::{ByteRecord, StringRecord}; 598 /// 599 /// // Using ByteRecord::from... 600 /// let str_record = StringRecord::from(vec!["a", "b", "c"]); 601 /// assert_eq!(ByteRecord::from(str_record).len(), 3); 602 /// 603 /// // Using StringRecord::into... 604 /// let str_record = StringRecord::from(vec!["a", "b", "c"]); 605 /// let byte_record: ByteRecord = str_record.into(); 606 /// assert_eq!(byte_record.len(), 3); 607 /// ``` 608 #[inline] into_byte_record(self) -> ByteRecord609 pub fn into_byte_record(self) -> ByteRecord { 610 self.0 611 } 612 613 /// Clone this record, but only copy `fields` up to the end of bounds. This 614 /// is useful when one wants to copy a record, but not necessarily any 615 /// excess capacity in that record. 616 #[inline] clone_truncated(&self) -> StringRecord617 pub(crate) fn clone_truncated(&self) -> StringRecord { 618 StringRecord(self.0.clone_truncated()) 619 } 620 621 /// A safe function for reading CSV data into a `StringRecord`. 622 /// 623 /// This relies on the internal representation of `StringRecord`. 624 #[inline(always)] read<R: io::Read>( &mut self, rdr: &mut Reader<R>, ) -> Result<bool>625 pub(crate) fn read<R: io::Read>( 626 &mut self, 627 rdr: &mut Reader<R>, 628 ) -> Result<bool> { 629 // SAFETY: This code is critical to upholding the safety of other code 630 // blocks in this module. Namely, after calling `read_byte_record`, 631 // it is possible for `record` to contain invalid UTF-8. We check for 632 // this in the `validate` method, and if it does have invalid UTF-8, we 633 // clear the record. (It is bad for `record` to contain invalid UTF-8 634 // because other accessor methods, like `get`, assume that every field 635 // is valid UTF-8.) 636 let pos = rdr.position().clone(); 637 let read_res = rdr.read_byte_record(&mut self.0); 638 let utf8_res = match self.0.validate() { 639 Ok(()) => Ok(()), 640 Err(err) => { 641 // If this record isn't valid UTF-8, then completely wipe it. 642 self.0.clear(); 643 Err(err) 644 } 645 }; 646 match (read_res, utf8_res) { 647 (Err(err), _) => Err(err), 648 (Ok(_), Err(err)) => { 649 Err(Error::new(ErrorKind::Utf8 { pos: Some(pos), err: err })) 650 } 651 (Ok(eof), Ok(())) => Ok(eof), 652 } 653 } 654 } 655 656 impl ops::Index<usize> for StringRecord { 657 type Output = str; 658 #[inline] index(&self, i: usize) -> &str659 fn index(&self, i: usize) -> &str { 660 self.get(i).unwrap() 661 } 662 } 663 664 impl<T: AsRef<str>> From<Vec<T>> for StringRecord { 665 #[inline] from(xs: Vec<T>) -> StringRecord666 fn from(xs: Vec<T>) -> StringRecord { 667 StringRecord::from_iter(xs.into_iter()) 668 } 669 } 670 671 impl<'a, T: AsRef<str>> From<&'a [T]> for StringRecord { 672 #[inline] from(xs: &'a [T]) -> StringRecord673 fn from(xs: &'a [T]) -> StringRecord { 674 StringRecord::from_iter(xs) 675 } 676 } 677 678 impl<T: AsRef<str>> FromIterator<T> for StringRecord { 679 #[inline] from_iter<I: IntoIterator<Item = T>>(iter: I) -> StringRecord680 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> StringRecord { 681 let mut record = StringRecord::new(); 682 record.extend(iter); 683 record 684 } 685 } 686 687 impl<T: AsRef<str>> Extend<T> for StringRecord { 688 #[inline] extend<I: IntoIterator<Item = T>>(&mut self, iter: I)689 fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) { 690 for x in iter { 691 self.push_field(x.as_ref()); 692 } 693 } 694 } 695 696 impl<'a> IntoIterator for &'a StringRecord { 697 type IntoIter = StringRecordIter<'a>; 698 type Item = &'a str; 699 700 #[inline] into_iter(self) -> StringRecordIter<'a>701 fn into_iter(self) -> StringRecordIter<'a> { 702 StringRecordIter(self.0.iter()) 703 } 704 } 705 706 /// An iterator over the fields in a string record. 707 /// 708 /// The `'r` lifetime variable refers to the lifetime of the `StringRecord` 709 /// that is being iterated over. 710 #[derive(Clone)] 711 pub struct StringRecordIter<'r>(ByteRecordIter<'r>); 712 713 impl<'r> Iterator for StringRecordIter<'r> { 714 type Item = &'r str; 715 716 #[inline] next(&mut self) -> Option<&'r str>717 fn next(&mut self) -> Option<&'r str> { 718 self.0.next().map(|bytes| { 719 debug_assert!(str::from_utf8(bytes).is_ok()); 720 // See StringRecord::get for safety argument. 721 unsafe { str::from_utf8_unchecked(bytes) } 722 }) 723 } 724 725 #[inline] size_hint(&self) -> (usize, Option<usize>)726 fn size_hint(&self) -> (usize, Option<usize>) { 727 self.0.size_hint() 728 } 729 730 #[inline] count(self) -> usize731 fn count(self) -> usize { 732 self.0.len() 733 } 734 } 735 736 impl<'r> DoubleEndedIterator for StringRecordIter<'r> { 737 #[inline] next_back(&mut self) -> Option<&'r str>738 fn next_back(&mut self) -> Option<&'r str> { 739 self.0.next_back().map(|bytes| { 740 debug_assert!(str::from_utf8(bytes).is_ok()); 741 // See StringRecord::get for safety argument. 742 unsafe { str::from_utf8_unchecked(bytes) } 743 }) 744 } 745 } 746 747 #[cfg(test)] 748 mod tests { 749 use crate::string_record::StringRecord; 750 751 #[test] trim_front()752 fn trim_front() { 753 let mut rec = StringRecord::from(vec![" abc"]); 754 rec.trim(); 755 assert_eq!(rec.get(0), Some("abc")); 756 757 let mut rec = StringRecord::from(vec![" abc", " xyz"]); 758 rec.trim(); 759 assert_eq!(rec.get(0), Some("abc")); 760 assert_eq!(rec.get(1), Some("xyz")); 761 } 762 763 #[test] trim_back()764 fn trim_back() { 765 let mut rec = StringRecord::from(vec!["abc "]); 766 rec.trim(); 767 assert_eq!(rec.get(0), Some("abc")); 768 769 let mut rec = StringRecord::from(vec!["abc ", "xyz "]); 770 rec.trim(); 771 assert_eq!(rec.get(0), Some("abc")); 772 assert_eq!(rec.get(1), Some("xyz")); 773 } 774 775 #[test] trim_both()776 fn trim_both() { 777 let mut rec = StringRecord::from(vec![" abc "]); 778 rec.trim(); 779 assert_eq!(rec.get(0), Some("abc")); 780 781 let mut rec = StringRecord::from(vec![" abc ", " xyz "]); 782 rec.trim(); 783 assert_eq!(rec.get(0), Some("abc")); 784 assert_eq!(rec.get(1), Some("xyz")); 785 } 786 787 #[test] trim_does_not_panic_on_empty_records_1()788 fn trim_does_not_panic_on_empty_records_1() { 789 let mut rec = StringRecord::from(vec![""]); 790 rec.trim(); 791 assert_eq!(rec.get(0), Some("")); 792 } 793 794 #[test] trim_does_not_panic_on_empty_records_2()795 fn trim_does_not_panic_on_empty_records_2() { 796 let mut rec = StringRecord::from(vec!["", ""]); 797 rec.trim(); 798 assert_eq!(rec.get(0), Some("")); 799 assert_eq!(rec.get(1), Some("")); 800 } 801 802 #[test] trim_does_not_panic_on_empty_records_3()803 fn trim_does_not_panic_on_empty_records_3() { 804 let mut rec = StringRecord::new(); 805 rec.trim(); 806 assert_eq!(rec.as_slice().len(), 0); 807 } 808 809 #[test] trim_whitespace_only()810 fn trim_whitespace_only() { 811 let mut rec = StringRecord::from(vec![ 812 "\u{0009}\u{000A}\u{000B}\u{000C}\u{000D}\u{0020}\u{0085}\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{2028}\u{2029}\u{202F}\u{205F}\u{3000}", 813 ]); 814 rec.trim(); 815 assert_eq!(rec.get(0), Some("")); 816 } 817 818 // Check that record equality respects field boundaries. 819 // 820 // Regression test for #138. 821 #[test] eq_field_boundaries()822 fn eq_field_boundaries() { 823 let test1 = StringRecord::from(vec!["12", "34"]); 824 let test2 = StringRecord::from(vec!["123", "4"]); 825 826 assert_ne!(test1, test2); 827 } 828 829 // Check that record equality respects number of fields. 830 // 831 // Regression test for #138. 832 #[test] eq_record_len()833 fn eq_record_len() { 834 let test1 = StringRecord::from(vec!["12", "34", "56"]); 835 let test2 = StringRecord::from(vec!["12", "34"]); 836 assert_ne!(test1, test2); 837 } 838 } 839