use std::ops::RangeBounds; use crate::{ShellError, Span, Value}; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct Record { /// Don't use this field publicly! /// /// Only public as command `rename` is not reimplemented in a sane way yet /// Using it or making `vals` public will draw shaming by @sholderbach pub cols: Vec, vals: Vec, } impl Record { pub fn new() -> Self { Self::default() } pub fn with_capacity(capacity: usize) -> Self { Self { cols: Vec::with_capacity(capacity), vals: Vec::with_capacity(capacity), } } /// Create a [`Record`] from a `Vec` of columns and a `Vec` of [`Value`]s /// /// Returns an error if `cols` and `vals` have different lengths. /// /// For perf reasons, this will not validate the rest of the record assumptions: /// - unique keys pub fn from_raw_cols_vals( cols: Vec, vals: Vec, input_span: Span, creation_site_span: Span, ) -> Result { if cols.len() == vals.len() { Ok(Self { cols, vals }) } else { Err(ShellError::RecordColsValsMismatch { bad_value: input_span, creation_site: creation_site_span, }) } } pub fn iter(&self) -> Iter { self.into_iter() } pub fn iter_mut(&mut self) -> IterMut { self.into_iter() } pub fn is_empty(&self) -> bool { debug_assert_eq!(self.cols.len(), self.vals.len()); self.cols.is_empty() } pub fn len(&self) -> usize { debug_assert_eq!(self.cols.len(), self.vals.len()); self.cols.len() } /// Naive push to the end of the datastructure. /// /// May duplicate data! /// /// Consider to use [`Record::insert`] instead pub fn push(&mut self, col: impl Into, val: Value) { self.cols.push(col.into()); self.vals.push(val); } /// Insert into the record, replacing preexisting value if found. /// /// Returns `Some(previous_value)` if found. Else `None` pub fn insert(&mut self, col: K, val: Value) -> Option where K: AsRef + Into, { if let Some(idx) = self.index_of(&col) { // Can panic if vals.len() < cols.len() let curr_val = &mut self.vals[idx]; Some(std::mem::replace(curr_val, val)) } else { self.push(col, val); None } } pub fn contains(&self, col: impl AsRef) -> bool { self.columns().any(|k| k == col.as_ref()) } pub fn index_of(&self, col: impl AsRef) -> Option { self.columns().position(|k| k == col.as_ref()) } pub fn get(&self, col: impl AsRef) -> Option<&Value> { self.index_of(col).and_then(|idx| self.vals.get(idx)) } pub fn get_mut(&mut self, col: impl AsRef) -> Option<&mut Value> { self.index_of(col).and_then(|idx| self.vals.get_mut(idx)) } pub fn get_index(&self, idx: usize) -> Option<(&String, &Value)> { Some((self.cols.get(idx)?, self.vals.get(idx)?)) } /// Remove single value by key /// /// Returns `None` if key not found /// /// Note: makes strong assumption that keys are unique pub fn remove(&mut self, col: impl AsRef) -> Option { let idx = self.index_of(col)?; self.cols.remove(idx); Some(self.vals.remove(idx)) } /// Remove elements in-place that do not satisfy `keep` /// /// ```rust /// use nu_protocol::{record, Value}; /// /// let mut rec = record!( /// "a" => Value::test_nothing(), /// "b" => Value::test_int(42), /// "c" => Value::test_nothing(), /// "d" => Value::test_int(42), /// ); /// rec.retain(|_k, val| !val.is_nothing()); /// let mut iter_rec = rec.columns(); /// assert_eq!(iter_rec.next().map(String::as_str), Some("b")); /// assert_eq!(iter_rec.next().map(String::as_str), Some("d")); /// assert_eq!(iter_rec.next(), None); /// ``` pub fn retain(&mut self, mut keep: F) where F: FnMut(&str, &Value) -> bool, { self.retain_mut(|k, v| keep(k, v)); } /// Remove elements in-place that do not satisfy `keep` while allowing mutation of the value. /// /// This can for example be used to recursively prune nested records. /// /// ```rust /// use nu_protocol::{record, Record, Value}; /// /// fn remove_foo_recursively(val: &mut Value) { /// if let Value::Record {val, ..} = val { /// val.retain_mut(keep_non_foo); /// } /// } /// /// fn keep_non_foo(k: &str, v: &mut Value) -> bool { /// if k == "foo" { /// return false; /// } /// remove_foo_recursively(v); /// true /// } /// /// let mut test = Value::test_record(record!( /// "foo" => Value::test_nothing(), /// "bar" => Value::test_record(record!( /// "foo" => Value::test_nothing(), /// "baz" => Value::test_nothing(), /// )) /// )); /// /// remove_foo_recursively(&mut test); /// let expected = Value::test_record(record!( /// "bar" => Value::test_record(record!( /// "baz" => Value::test_nothing(), /// )) /// )); /// assert_eq!(test, expected); /// ``` pub fn retain_mut(&mut self, mut keep: F) where F: FnMut(&str, &mut Value) -> bool, { // `Vec::retain` is able to optimize memcopies internally. // For maximum benefit, `retain` is used on `vals`, // as `Value` is a larger struct than `String`. // // To do a simultaneous retain on the `cols`, three portions of it are tracked: // [..retained, ..dropped, ..unvisited] // number of elements keep so far, start of ..dropped and length of ..retained let mut retained = 0; // current index of element being checked, start of ..unvisited let mut idx = 0; self.vals.retain_mut(|val| { if keep(&self.cols[idx], val) { // skip swaps for first consecutive run of kept elements if idx != retained { self.cols.swap(idx, retained); } retained += 1; idx += 1; true } else { idx += 1; false } }); self.cols.truncate(retained); } /// Truncate record to the first `len` elements. /// /// `len > self.len()` will be ignored /// /// ```rust /// use nu_protocol::{record, Value}; /// /// let mut rec = record!( /// "a" => Value::test_nothing(), /// "b" => Value::test_int(42), /// "c" => Value::test_nothing(), /// "d" => Value::test_int(42), /// ); /// rec.truncate(42); // this is fine /// assert_eq!(rec.columns().map(String::as_str).collect::(), "abcd"); /// rec.truncate(2); // truncate /// assert_eq!(rec.columns().map(String::as_str).collect::(), "ab"); /// rec.truncate(0); // clear the record /// assert_eq!(rec.len(), 0); /// ``` pub fn truncate(&mut self, len: usize) { self.cols.truncate(len); self.vals.truncate(len); } pub fn columns(&self) -> Columns { Columns { iter: self.cols.iter(), } } pub fn values(&self) -> Values { Values { iter: self.vals.iter(), } } pub fn into_values(self) -> IntoValues { IntoValues { iter: self.vals.into_iter(), } } /// Obtain an iterator to remove elements in `range` /// /// Elements not consumed from the iterator will be dropped /// /// ```rust /// use nu_protocol::{record, Value}; /// /// let mut rec = record!( /// "a" => Value::test_nothing(), /// "b" => Value::test_int(42), /// "c" => Value::test_string("foo"), /// ); /// { /// let mut drainer = rec.drain(1..); /// assert_eq!(drainer.next(), Some(("b".into(), Value::test_int(42)))); /// // Dropping the `Drain` /// } /// let mut rec_iter = rec.into_iter(); /// assert_eq!(rec_iter.next(), Some(("a".into(), Value::test_nothing()))); /// assert_eq!(rec_iter.next(), None); /// ``` pub fn drain(&mut self, range: R) -> Drain where R: RangeBounds + Clone, { debug_assert_eq!(self.cols.len(), self.vals.len()); Drain { keys: self.cols.drain(range.clone()), values: self.vals.drain(range), } } } impl FromIterator<(String, Value)> for Record { fn from_iter>(iter: T) -> Self { let (cols, vals) = iter.into_iter().unzip(); // TODO: should this check for duplicate keys/columns? Self { cols, vals } } } impl Extend<(String, Value)> for Record { fn extend>(&mut self, iter: T) { for (k, v) in iter { // TODO: should this .insert with a check? self.push(k, v) } } } pub struct IntoIter { iter: std::iter::Zip, std::vec::IntoIter>, } impl Iterator for IntoIter { type Item = (String, Value); fn next(&mut self) -> Option { self.iter.next() } } impl DoubleEndedIterator for IntoIter { fn next_back(&mut self) -> Option { self.iter.next_back() } } impl ExactSizeIterator for IntoIter { fn len(&self) -> usize { self.iter.len() } } impl IntoIterator for Record { type Item = (String, Value); type IntoIter = IntoIter; fn into_iter(self) -> Self::IntoIter { IntoIter { iter: self.cols.into_iter().zip(self.vals), } } } pub struct Iter<'a> { iter: std::iter::Zip, std::slice::Iter<'a, Value>>, } impl<'a> Iterator for Iter<'a> { type Item = (&'a String, &'a Value); fn next(&mut self) -> Option { self.iter.next() } } impl<'a> DoubleEndedIterator for Iter<'a> { fn next_back(&mut self) -> Option { self.iter.next_back() } } impl<'a> ExactSizeIterator for Iter<'a> { fn len(&self) -> usize { self.iter.len() } } impl<'a> IntoIterator for &'a Record { type Item = (&'a String, &'a Value); type IntoIter = Iter<'a>; fn into_iter(self) -> Self::IntoIter { Iter { iter: self.cols.iter().zip(&self.vals), } } } pub struct IterMut<'a> { iter: std::iter::Zip, std::slice::IterMut<'a, Value>>, } impl<'a> Iterator for IterMut<'a> { type Item = (&'a String, &'a mut Value); fn next(&mut self) -> Option { self.iter.next() } } impl<'a> DoubleEndedIterator for IterMut<'a> { fn next_back(&mut self) -> Option { self.iter.next_back() } } impl<'a> ExactSizeIterator for IterMut<'a> { fn len(&self) -> usize { self.iter.len() } } impl<'a> IntoIterator for &'a mut Record { type Item = (&'a String, &'a mut Value); type IntoIter = IterMut<'a>; fn into_iter(self) -> Self::IntoIter { IterMut { iter: self.cols.iter().zip(&mut self.vals), } } } pub struct Columns<'a> { iter: std::slice::Iter<'a, String>, } impl<'a> Iterator for Columns<'a> { type Item = &'a String; fn next(&mut self) -> Option { self.iter.next() } fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } impl<'a> DoubleEndedIterator for Columns<'a> { fn next_back(&mut self) -> Option { self.iter.next_back() } } impl<'a> ExactSizeIterator for Columns<'a> { fn len(&self) -> usize { self.iter.len() } } pub struct Values<'a> { iter: std::slice::Iter<'a, Value>, } impl<'a> Iterator for Values<'a> { type Item = &'a Value; fn next(&mut self) -> Option { self.iter.next() } fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } impl<'a> DoubleEndedIterator for Values<'a> { fn next_back(&mut self) -> Option { self.iter.next_back() } } impl<'a> ExactSizeIterator for Values<'a> { fn len(&self) -> usize { self.iter.len() } } pub struct IntoValues { iter: std::vec::IntoIter, } impl Iterator for IntoValues { type Item = Value; fn next(&mut self) -> Option { self.iter.next() } fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } impl DoubleEndedIterator for IntoValues { fn next_back(&mut self) -> Option { self.iter.next_back() } } impl ExactSizeIterator for IntoValues { fn len(&self) -> usize { self.iter.len() } } pub struct Drain<'a> { keys: std::vec::Drain<'a, String>, values: std::vec::Drain<'a, Value>, } impl Iterator for Drain<'_> { type Item = (String, Value); fn next(&mut self) -> Option { Some((self.keys.next()?, self.values.next()?)) } fn size_hint(&self) -> (usize, Option) { self.keys.size_hint() } } impl DoubleEndedIterator for Drain<'_> { fn next_back(&mut self) -> Option { Some((self.keys.next_back()?, self.values.next_back()?)) } } impl ExactSizeIterator for Drain<'_> { fn len(&self) -> usize { self.keys.len() } } #[macro_export] macro_rules! record { // The macro only compiles if the number of columns equals the number of values, // so it's safe to call `unwrap` below. {$($col:expr => $val:expr),+ $(,)?} => { $crate::Record::from_raw_cols_vals( ::std::vec![$($col.into(),)+], ::std::vec![$($val,)+], $crate::Span::unknown(), $crate::Span::unknown(), ).unwrap() }; {} => { $crate::Record::new() }; }