csv/
string_record.rs

1use std::{
2    fmt, io,
3    iter::FromIterator,
4    ops::{self, Range},
5    result, str,
6};
7
8use serde::de::Deserialize;
9
10use crate::{
11    byte_record::{ByteRecord, ByteRecordIter, Position},
12    deserializer::deserialize_string_record,
13    error::{Error, ErrorKind, FromUtf8Error, Result},
14    reader::Reader,
15};
16
17/// A single CSV record stored as valid UTF-8 bytes.
18///
19/// A string record permits reading or writing CSV rows that are valid UTF-8.
20/// If string records are used to read CSV data that is not valid UTF-8, then
21/// the CSV reader will return an invalid UTF-8 error. If you do need to read
22/// possibly invalid UTF-8 data, then you should prefer using a
23/// [`ByteRecord`](struct.ByteRecord.html),
24/// since it makes no assumptions about UTF-8.
25///
26/// If you are using the Serde (de)serialization APIs, then you probably never
27/// need to interact with a `ByteRecord` or a `StringRecord`. However, there
28/// are some circumstances in which you might need to use a raw record type
29/// while still using Serde. For example, if you need to deserialize possibly
30/// invalid UTF-8 fields, then you'll need to first read your record into a
31/// `ByteRecord`, and then use `ByteRecord::deserialize` to run Serde. Another
32/// reason for using the raw record deserialization APIs is if you're using
33/// Serde to read into borrowed data such as a `&'a str` or a `&'a [u8]`.
34///
35/// Two `StringRecord`s are compared on the basis of their field data. Any
36/// position information associated with the records is ignored.
37#[derive(Clone, Eq)]
38pub struct StringRecord(ByteRecord);
39
40impl PartialEq for StringRecord {
41    fn eq(&self, other: &StringRecord) -> bool {
42        self.0.iter_eq(&other.0)
43    }
44}
45
46impl<T: AsRef<[u8]>> PartialEq<Vec<T>> for StringRecord {
47    fn eq(&self, other: &Vec<T>) -> bool {
48        self.0.iter_eq(other)
49    }
50}
51
52impl<'a, T: AsRef<[u8]>> PartialEq<Vec<T>> for &'a StringRecord {
53    fn eq(&self, other: &Vec<T>) -> bool {
54        self.0.iter_eq(other)
55    }
56}
57
58impl<T: AsRef<[u8]>> PartialEq<[T]> for StringRecord {
59    fn eq(&self, other: &[T]) -> bool {
60        self.0.iter_eq(other)
61    }
62}
63
64impl<'a, T: AsRef<[u8]>> PartialEq<[T]> for &'a StringRecord {
65    fn eq(&self, other: &[T]) -> bool {
66        self.0.iter_eq(other)
67    }
68}
69
70impl fmt::Debug for StringRecord {
71    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
72        let fields: Vec<&str> = self.iter().collect();
73        write!(f, "StringRecord({:?})", fields)
74    }
75}
76
77impl Default for StringRecord {
78    #[inline]
79    fn default() -> StringRecord {
80        StringRecord::new()
81    }
82}
83
84impl StringRecord {
85    /// Create a new empty `StringRecord`.
86    ///
87    /// Note that you may find the `StringRecord::from` constructor more
88    /// convenient, which is provided by an impl on the `From` trait.
89    ///
90    /// # Example: create an empty record
91    ///
92    /// ```
93    /// use csv::StringRecord;
94    ///
95    /// let record = StringRecord::new();
96    /// assert_eq!(record.len(), 0);
97    /// ```
98    ///
99    /// # Example: initialize a record from a `Vec`
100    ///
101    /// ```
102    /// use csv::StringRecord;
103    ///
104    /// let record = StringRecord::from(vec!["a", "b", "c"]);
105    /// assert_eq!(record.len(), 3);
106    /// ```
107    #[inline]
108    pub fn new() -> StringRecord {
109        StringRecord(ByteRecord::new())
110    }
111
112    /// Create a new empty `StringRecord` with the given capacity.
113    ///
114    /// `buffer` refers to the capacity of the buffer used to store the
115    /// actual row contents. `fields` refers to the number of fields one
116    /// might expect to store.
117    #[inline]
118    pub fn with_capacity(buffer: usize, fields: usize) -> StringRecord {
119        StringRecord(ByteRecord::with_capacity(buffer, fields))
120    }
121
122    /// Create a new `StringRecord` from a `ByteRecord`.
123    ///
124    /// Note that this does UTF-8 validation. If the given `ByteRecord` does
125    /// not contain valid UTF-8, then this returns an error. The error includes
126    /// the UTF-8 error and the original `ByteRecord`.
127    ///
128    /// # Example: valid UTF-8
129    ///
130    /// ```
131    /// use std::error::Error;
132    /// use csv::{ByteRecord, StringRecord};
133    ///
134    /// # fn main() { example().unwrap(); }
135    /// fn example() -> Result<(), Box<dyn Error>> {
136    ///     let byte_record = ByteRecord::from(vec!["a", "b", "c"]);
137    ///     let str_record = StringRecord::from_byte_record(byte_record)?;
138    ///     assert_eq!(str_record.len(), 3);
139    ///     Ok(())
140    /// }
141    /// ```
142    ///
143    /// # Example: invalid UTF-8
144    ///
145    /// ```
146    /// use csv::{ByteRecord, StringRecord};
147    ///
148    /// let byte_record = ByteRecord::from(vec![
149    ///     &b"quux"[..], &b"foo\xFFbar"[..], &b"c"[..],
150    /// ]);
151    /// let err = StringRecord::from_byte_record(byte_record).unwrap_err();
152    /// assert_eq!(err.utf8_error().field(), 1);
153    /// assert_eq!(err.utf8_error().valid_up_to(), 3);
154    /// ```
155    #[inline]
156    pub fn from_byte_record(
157        record: ByteRecord,
158    ) -> result::Result<StringRecord, FromUtf8Error> {
159        match record.validate() {
160            Ok(()) => Ok(StringRecord(record)),
161            Err(err) => Err(FromUtf8Error::new(record, err)),
162        }
163    }
164
165    /// Lossily create a new `StringRecord` from a `ByteRecord`.
166    ///
167    /// This is like `StringRecord::from_byte_record`, except all invalid UTF-8
168    /// sequences are replaced with the `U+FFFD REPLACEMENT CHARACTER`, which
169    /// looks like this: �.
170    ///
171    /// # Example: valid UTF-8
172    ///
173    /// ```
174    /// use csv::{ByteRecord, StringRecord};
175    ///
176    /// let byte_record = ByteRecord::from(vec!["a", "b", "c"]);
177    /// let str_record = StringRecord::from_byte_record_lossy(byte_record);
178    /// assert_eq!(str_record.len(), 3);
179    /// ```
180    ///
181    /// # Example: invalid UTF-8
182    ///
183    /// ```
184    /// use csv::{ByteRecord, StringRecord};
185    ///
186    /// let byte_record = ByteRecord::from(vec![
187    ///     &b"quux"[..], &b"foo\xFFbar"[..], &b"c"[..],
188    /// ]);
189    /// let str_record = StringRecord::from_byte_record_lossy(byte_record);
190    /// assert_eq!(&str_record[0], "quux");
191    /// assert_eq!(&str_record[1], "foo�bar");
192    /// assert_eq!(&str_record[2], "c");
193    /// ```
194    #[inline]
195    pub fn from_byte_record_lossy(record: ByteRecord) -> StringRecord {
196        // If the record is valid UTF-8, then take the easy path.
197        if let Ok(()) = record.validate() {
198            return StringRecord(record);
199        }
200        // TODO: We can be faster here. Not sure if it's worth it.
201        let mut str_record =
202            StringRecord::with_capacity(record.as_slice().len(), record.len());
203        for field in &record {
204            str_record.push_field(&String::from_utf8_lossy(field));
205        }
206        str_record
207    }
208
209    /// Deserialize this record.
210    ///
211    /// The `D` type parameter refers to the type that this record should be
212    /// deserialized into. The `'de` lifetime refers to the lifetime of the
213    /// `StringRecord`. The `'de` lifetime permits deserializing into structs
214    /// that borrow field data from this record.
215    ///
216    /// An optional `headers` parameter permits deserializing into a struct
217    /// based on its field names (corresponding to header values) rather than
218    /// the order in which the fields are defined.
219    ///
220    /// # Example: without headers
221    ///
222    /// This shows how to deserialize a single row into a struct based on the
223    /// order in which fields occur. This example also shows how to borrow
224    /// fields from the `StringRecord`, which results in zero allocation
225    /// deserialization.
226    ///
227    /// ```
228    /// use std::error::Error;
229    ///
230    /// use csv::StringRecord;
231    ///
232    /// #[derive(serde::Deserialize)]
233    /// struct Row<'a> {
234    ///     city: &'a str,
235    ///     country: &'a str,
236    ///     population: u64,
237    /// }
238    ///
239    /// # fn main() { example().unwrap() }
240    /// fn example() -> Result<(), Box<dyn Error>> {
241    ///     let record = StringRecord::from(vec![
242    ///         "Boston", "United States", "4628910",
243    ///     ]);
244    ///
245    ///     let row: Row = record.deserialize(None)?;
246    ///     assert_eq!(row.city, "Boston");
247    ///     assert_eq!(row.country, "United States");
248    ///     assert_eq!(row.population, 4628910);
249    ///     Ok(())
250    /// }
251    /// ```
252    ///
253    /// # Example: with headers
254    ///
255    /// This example is like the previous one, but shows how to deserialize
256    /// into a struct based on the struct's field names. For this to work,
257    /// you must provide a header row.
258    ///
259    /// This example also shows that you can deserialize into owned data
260    /// types (e.g., `String`) instead of borrowed data types (e.g., `&str`).
261    ///
262    /// ```
263    /// use std::error::Error;
264    ///
265    /// use csv::StringRecord;
266    ///
267    /// #[derive(serde::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    /// ```
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]
314    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]
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]
352    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]
367    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]
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]
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    /// ```
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]
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]
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]
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]
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]
556    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]
577    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]
609    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]
617    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)]
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 }))
650            }
651            (Ok(eof), Ok(())) => Ok(eof),
652        }
653    }
654}
655
656impl ops::Index<usize> for StringRecord {
657    type Output = str;
658    #[inline]
659    fn index(&self, i: usize) -> &str {
660        self.get(i).unwrap()
661    }
662}
663
664impl<T: AsRef<str>> From<Vec<T>> for StringRecord {
665    #[inline]
666    fn from(xs: Vec<T>) -> StringRecord {
667        StringRecord::from_iter(xs.into_iter())
668    }
669}
670
671impl<'a, T: AsRef<str>> From<&'a [T]> for StringRecord {
672    #[inline]
673    fn from(xs: &'a [T]) -> StringRecord {
674        StringRecord::from_iter(xs)
675    }
676}
677
678impl<T: AsRef<str>> FromIterator<T> for StringRecord {
679    #[inline]
680    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
687impl<T: AsRef<str>> Extend<T> for StringRecord {
688    #[inline]
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
696impl<'a> IntoIterator for &'a StringRecord {
697    type IntoIter = StringRecordIter<'a>;
698    type Item = &'a str;
699
700    #[inline]
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)]
711pub struct StringRecordIter<'r>(ByteRecordIter<'r>);
712
713impl<'r> Iterator for StringRecordIter<'r> {
714    type Item = &'r str;
715
716    #[inline]
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]
726    fn size_hint(&self) -> (usize, Option<usize>) {
727        self.0.size_hint()
728    }
729
730    #[inline]
731    fn count(self) -> usize {
732        self.0.len()
733    }
734}
735
736impl<'r> DoubleEndedIterator for StringRecordIter<'r> {
737    #[inline]
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)]
748mod tests {
749    use crate::string_record::StringRecord;
750
751    #[test]
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]
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]
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]
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]
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]
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]
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]
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]
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}