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