diff --git a/Cargo.lock b/Cargo.lock index 44d5bab141..bf8a67979c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -388,6 +388,7 @@ dependencies = [ "indexmap 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "nom 5.0.0-beta1 (registry+https://github.com/rust-lang/crates.io-index)", + "ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "prettytable-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustyline 4.0.0 (git+https://github.com/wycats/rustyline.git)", "subprocess 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", @@ -420,6 +421,14 @@ name = "numtoa" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "ordered-float" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "parse-zoneinfo" version = "0.2.0" @@ -811,6 +820,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" "checksum num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba" "checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" +"checksum ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "18869315e81473c951eb56ad5558bbc56978562d3ecfb87abb7a1e944cea4518" "checksum parse-zoneinfo 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "089a398ccdcdd77b8c38909d5a1e4b67da1bc4c9dbfe6d5b536c828eddb779e5" "checksum prettytable-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0fd04b170004fa2daccf418a7f8253aaf033c27760b5f225889024cf66d7ac2e" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" diff --git a/Cargo.toml b/Cargo.toml index de72a44a7a..375c8e7172 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,3 +26,4 @@ futures-core = "0.2.1" indexmap = "1.0.2" chrono-humanize = "0.0.11" byte-unit = "2.1.0" +ordered-float = "1.0.2" diff --git a/src/commands.rs b/src/commands.rs index 6e8a96d54c..d50f3e1dec 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -6,6 +6,7 @@ crate mod ps; crate mod reject; crate mod select; crate mod skip; +crate mod sort_by; crate mod take; crate mod to_array; crate mod where_; diff --git a/src/commands/sort_by.rs b/src/commands/sort_by.rs new file mode 100644 index 0000000000..d0681c2232 --- /dev/null +++ b/src/commands/sort_by.rs @@ -0,0 +1,29 @@ +use crate::errors::ShellError; +use crate::prelude::*; +use derive_new::new; + +#[derive(new)] +pub struct SortBy; + +impl crate::Command for SortBy { + fn run(&self, args: CommandArgs<'caller>) -> Result, ShellError> { + let fields: Result, _> = args.args.iter().map(|a| a.as_string()).collect(); + let fields = fields?; + + let mut output = args.input.into_iter().collect::>(); + + output.sort_by_key(|item| { + fields + .iter() + .map(|f| item.get_data_by_key(f).borrow().copy()) + .collect::>() + }); + + let output = output + .iter() + .map(|o| ReturnValue::Value(o.copy())) + .collect(); + + Ok(output) + } +} diff --git a/src/errors.rs b/src/errors.rs index e341d4f74d..9d9cadc729 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -4,7 +4,7 @@ use crate::prelude::*; use crate::Value; use derive_new::new; -#[derive(Debug, new)] +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, new)] pub struct ShellError { title: String, error: Value, diff --git a/src/main.rs b/src/main.rs index 68c244bf8f..198f29573b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -74,6 +74,7 @@ fn main() -> Result<(), Box> { ("reject", Box::new(reject::Reject)), ("to-array", Box::new(to_array::ToArray)), ("where", Box::new(where_::Where)), + ("sort-by", Box::new(sort_by::SortBy)), ]); } diff --git a/src/object/base.rs b/src/object/base.rs index eac093cd05..f93ece7442 100644 --- a/src/object/base.rs +++ b/src/object/base.rs @@ -5,12 +5,12 @@ use chrono::{DateTime, Utc}; use chrono_humanize::Humanize; use std::time::SystemTime; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)] pub enum Primitive { Nothing, Int(i64), #[allow(unused)] - Float(f64), + Float(ordered_float::OrderedFloat), Bytes(u128), String(String), Boolean(bool), @@ -49,7 +49,7 @@ impl Primitive { } } -#[derive(Debug)] +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] pub enum Value { Primitive(Primitive), Object(crate::object::Dictionary), @@ -69,6 +69,15 @@ impl Value { } } + crate fn get_data_by_key(&'a self, name: &str) -> crate::MaybeOwned<'a, Value> { + match self { + Value::Primitive(_) => crate::MaybeOwned::Owned(Value::nothing()), + Value::Object(o) => o.get_data_by_key(name), + Value::List(_) => crate::MaybeOwned::Owned(Value::nothing()), + Value::Error(_) => crate::MaybeOwned::Owned(Value::nothing()), + } + } + crate fn get_data(&'a self, desc: &DataDescriptor) -> crate::MaybeOwned<'a, Value> { match self { Value::Primitive(_) => crate::MaybeOwned::Owned(Value::nothing()), diff --git a/src/object/dict.rs b/src/object/dict.rs index c79c08ecbc..3d561a0a19 100644 --- a/src/object/dict.rs +++ b/src/object/dict.rs @@ -5,12 +5,43 @@ use crate::object::desc::DataDescriptor; use crate::object::{Primitive, Value}; use crate::MaybeOwned; use indexmap::IndexMap; +use std::cmp::{Ordering, PartialOrd}; -#[derive(Debug, Default)] +#[derive(Debug, Default, Eq, PartialEq)] pub struct Dictionary { entries: IndexMap, } +impl PartialOrd for Dictionary { + // TODO: FIXME + fn partial_cmp(&self, _other: &Dictionary) -> Option { + Some(Ordering::Less) + } +} + +impl Ord for Dictionary { + // TODO: FIXME + fn cmp(&self, _other: &Dictionary) -> Ordering { + Ordering::Less + } +} + +impl PartialOrd for Dictionary { + fn partial_cmp(&self, _other: &Value) -> Option { + Some(Ordering::Less) + } +} + +impl PartialEq for Dictionary { + // TODO: FIXME + fn eq(&self, other: &Value) -> bool { + match other { + Value::Object(d) => self == d, + _ => false, + } + } +} + impl Dictionary { crate fn add(&mut self, name: impl Into, value: Value) { self.entries.insert(name.into(), value); @@ -41,4 +72,11 @@ impl Dictionary { None => MaybeOwned::Owned(Value::Primitive(Primitive::Nothing)), } } + + crate fn get_data_by_key(&self, name: &str) -> MaybeOwned<'_, Value> { + match self.entries.get(name) { + Some(v) => MaybeOwned::Borrowed(v), + None => MaybeOwned::Owned(Value::Primitive(Primitive::Nothing)), + } + } }