use indexmap::IndexMap; use nu_errors::ShellError; use nu_protocol::{TaggedDictBuilder, UntaggedValue, Value}; use nu_source::{Tag, Tagged, TaggedItem}; use nu_value_ext::{as_string, get_data_by_key}; #[allow(clippy::type_complexity)] pub fn group( column_name: Option>, values: &[Value], grouper: Option Result + Send>>, tag: impl Into, ) -> Result { let tag = tag.into(); let mut groups: IndexMap> = IndexMap::new(); for value in values { let group_key = if let Some(ref column_name) = column_name { get_data_by_key(&value, column_name.borrow_spanned()) } else { Some(value.clone()) }; if let Some(group_key) = group_key { let group_key = if let Some(ref grouper) = grouper { grouper(&group_key) } else { as_string(&group_key) }; let group = groups.entry(group_key?).or_insert(vec![]); group.push((*value).clone()); } else { let column_name = column_name.unwrap_or_else(|| String::from("").tagged(&tag)); let possibilities = value.data_descriptors(); let mut possible_matches: Vec<_> = possibilities .iter() .map(|x| (natural::distance::levenshtein_distance(x, &column_name), x)) .collect(); possible_matches.sort(); if !possible_matches.is_empty() { return Err(ShellError::labeled_error( "Unknown column", format!("did you mean '{}'?", possible_matches[0].1), column_name.tag(), )); } else { return Err(ShellError::labeled_error( "Unknown column", "row does not contain this column", column_name.tag(), )); } } } let mut out = TaggedDictBuilder::new(&tag); for (k, v) in groups.iter() { out.insert_untagged(k, UntaggedValue::table(v)); } Ok(out.into_value()) }