From 1fd7b9ac38022e3157a5aa09d14db994c88283ed Mon Sep 17 00:00:00 2001 From: Fernando Herrera Date: Sat, 12 Feb 2022 11:11:54 +0000 Subject: [PATCH] roll commands (#4437) * roll commands * removed repeated funtion --- crates/nu-command/src/default_context.rs | 5 + crates/nu-command/src/filters/mod.rs | 2 + crates/nu-command/src/filters/roll/mod.rs | 101 ++++++++++++++++ crates/nu-command/src/filters/roll/roll_.rs | 35 ++++++ .../nu-command/src/filters/roll/roll_down.rs | 82 +++++++++++++ .../nu-command/src/filters/roll/roll_left.rs | 111 ++++++++++++++++++ .../nu-command/src/filters/roll/roll_right.rs | 111 ++++++++++++++++++ crates/nu-command/src/filters/roll/roll_up.rs | 82 +++++++++++++ 8 files changed, 529 insertions(+) create mode 100644 crates/nu-command/src/filters/roll/mod.rs create mode 100644 crates/nu-command/src/filters/roll/roll_.rs create mode 100644 crates/nu-command/src/filters/roll/roll_down.rs create mode 100644 crates/nu-command/src/filters/roll/roll_left.rs create mode 100644 crates/nu-command/src/filters/roll/roll_right.rs create mode 100644 crates/nu-command/src/filters/roll/roll_up.rs diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index 2433a69b97..3341e674a6 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -94,6 +94,11 @@ pub fn create_default_context(cwd: impl AsRef) -> EngineState { Reject, Rename, Reverse, + Roll, + RollDown, + RollUp, + RollLeft, + RollRight, Rotate, Select, Shuffle, diff --git a/crates/nu-command/src/filters/mod.rs b/crates/nu-command/src/filters/mod.rs index ce97000e40..cc29fa24d8 100644 --- a/crates/nu-command/src/filters/mod.rs +++ b/crates/nu-command/src/filters/mod.rs @@ -31,6 +31,7 @@ mod reduce; mod reject; mod rename; mod reverse; +mod roll; mod rotate; mod select; mod shuffle; @@ -78,6 +79,7 @@ pub use reduce::Reduce; pub use reject::Reject; pub use rename::Rename; pub use reverse::Reverse; +pub use roll::*; pub use rotate::Rotate; pub use select::Select; pub use shuffle::Shuffle; diff --git a/crates/nu-command/src/filters/roll/mod.rs b/crates/nu-command/src/filters/roll/mod.rs new file mode 100644 index 0000000000..c422a6b179 --- /dev/null +++ b/crates/nu-command/src/filters/roll/mod.rs @@ -0,0 +1,101 @@ +mod roll_; +mod roll_down; +mod roll_left; +mod roll_right; +mod roll_up; + +use nu_protocol::{ShellError, Value}; +pub use roll_::Roll; +pub use roll_down::RollDown; +pub use roll_left::RollLeft; +pub use roll_right::RollRight; +pub use roll_up::RollUp; + +enum VerticalDirection { + Up, + Down, +} + +fn vertical_rotate_value( + value: Value, + by: Option, + direction: VerticalDirection, +) -> Result { + match value { + Value::List { mut vals, span } => { + let rotations = by.map(|n| n % vals.len()).unwrap_or(1); + let values = vals.as_mut_slice(); + + match direction { + VerticalDirection::Up => values.rotate_left(rotations), + VerticalDirection::Down => values.rotate_right(rotations), + } + + Ok(Value::List { + vals: values.to_owned(), + span, + }) + } + _ => Err(ShellError::TypeMismatch("list".to_string(), value.span()?)), + } +} + +enum HorizontalDirection { + Left, + Right, +} + +fn horizontal_rotate_value( + value: Value, + by: &Option, + cells_only: bool, + direction: &HorizontalDirection, +) -> Result { + match value { + Value::Record { + mut cols, + mut vals, + span, + } => { + let rotations = by.map(|n| n % vals.len()).unwrap_or(1); + + let columns = if cells_only { + cols + } else { + let columns = cols.as_mut_slice(); + + match direction { + HorizontalDirection::Right => columns.rotate_right(rotations), + HorizontalDirection::Left => columns.rotate_left(rotations), + } + + columns.to_owned() + }; + + let values = vals.as_mut_slice(); + + match direction { + HorizontalDirection::Right => values.rotate_right(rotations), + HorizontalDirection::Left => values.rotate_left(rotations), + } + + Ok(Value::Record { + cols: columns, + vals: values.to_owned(), + span, + }) + } + Value::List { vals, span } => { + let values = vals + .into_iter() + .map(|value| horizontal_rotate_value(value, by, cells_only, direction)) + .collect::, ShellError>>()?; + + Ok(Value::List { vals: values, span }) + } + _ => Err(ShellError::TypeMismatch( + "record".to_string(), + value.span()?, + )), + } +} diff --git a/crates/nu-command/src/filters/roll/roll_.rs b/crates/nu-command/src/filters/roll/roll_.rs new file mode 100644 index 0000000000..188dec4fed --- /dev/null +++ b/crates/nu-command/src/filters/roll/roll_.rs @@ -0,0 +1,35 @@ +use nu_engine::get_full_help; +use nu_protocol::ast::Call; +use nu_protocol::engine::{Command, EngineState, Stack}; +use nu_protocol::{Category, IntoPipelineData, PipelineData, ShellError, Signature, Value}; + +#[derive(Clone)] +pub struct Roll; + +impl Command for Roll { + fn name(&self) -> &str { + "roll" + } + + fn signature(&self) -> Signature { + Signature::build(self.name()).category(Category::Filters) + } + + fn usage(&self) -> &str { + "Rolling commands for tables" + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + _input: PipelineData, + ) -> Result { + Ok(Value::String { + val: get_full_help(&Roll.signature(), &Roll.examples(), engine_state, stack), + span: call.head, + } + .into_pipeline_data()) + } +} diff --git a/crates/nu-command/src/filters/roll/roll_down.rs b/crates/nu-command/src/filters/roll/roll_down.rs new file mode 100644 index 0000000000..da648064c5 --- /dev/null +++ b/crates/nu-command/src/filters/roll/roll_down.rs @@ -0,0 +1,82 @@ +use nu_engine::CallExt; +use nu_protocol::ast::Call; +use nu_protocol::engine::{Command, EngineState, Stack}; +use nu_protocol::{ + Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, + Value, +}; + +use super::{vertical_rotate_value, VerticalDirection}; + +#[derive(Clone)] +pub struct RollDown; + +impl Command for RollDown { + fn name(&self) -> &str { + "roll down" + } + + fn signature(&self) -> Signature { + Signature::build(self.name()) + .named("by", SyntaxShape::Int, "Number of rows to roll", Some('b')) + .category(Category::Filters) + } + + fn usage(&self) -> &str { + "Roll table rows down" + } + + fn examples(&self) -> Vec { + let columns = vec!["a".to_string(), "b".to_string()]; + vec![Example { + description: "Rolls rows down", + example: "[[a b]; [1 2] [3 4] [5 6]] | roll down", + result: Some(Value::List { + vals: vec![ + Value::Record { + cols: columns.clone(), + vals: vec![Value::test_int(5), Value::test_int(6)], + span: Span::test_data(), + }, + Value::Record { + cols: columns.clone(), + vals: vec![Value::test_int(1), Value::test_int(2)], + span: Span::test_data(), + }, + Value::Record { + cols: columns, + vals: vec![Value::test_int(3), Value::test_int(4)], + span: Span::test_data(), + }, + ], + span: Span::test_data(), + }), + }] + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + let by: Option = call.get_flag(engine_state, stack, "by")?; + let value = input.into_value(call.head); + let rotated_value = vertical_rotate_value(value, by, VerticalDirection::Down)?; + + Ok(rotated_value.into_pipeline_data()) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_examples() { + use crate::test_examples; + + test_examples(RollDown {}) + } +} diff --git a/crates/nu-command/src/filters/roll/roll_left.rs b/crates/nu-command/src/filters/roll/roll_left.rs new file mode 100644 index 0000000000..a891363c5a --- /dev/null +++ b/crates/nu-command/src/filters/roll/roll_left.rs @@ -0,0 +1,111 @@ +use nu_engine::CallExt; +use nu_protocol::ast::Call; +use nu_protocol::engine::{Command, EngineState, Stack}; +use nu_protocol::{ + Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, + Value, +}; + +use super::{horizontal_rotate_value, HorizontalDirection}; + +#[derive(Clone)] +pub struct RollLeft; + +impl Command for RollLeft { + fn name(&self) -> &str { + "roll left" + } + + fn signature(&self) -> Signature { + Signature::build(self.name()) + .named( + "by", + SyntaxShape::Int, + "Number of columns to roll", + Some('b'), + ) + .switch( + "cells-only", + "rotates columns leaving headers fixed", + Some('c'), + ) + .category(Category::Filters) + } + + fn usage(&self) -> &str { + "Roll table columns left" + } + + fn examples(&self) -> Vec { + let columns = vec!["a".to_string(), "b".to_string(), "c".to_string()]; + let rotated_columns = vec!["b".to_string(), "c".to_string(), "a".to_string()]; + vec![ + Example { + description: "Rolls columns to the left", + example: "[[a b c]; [1 2 3] [4 5 6]] | roll left", + result: Some(Value::List { + vals: vec![ + Value::Record { + cols: rotated_columns.clone(), + vals: vec![Value::test_int(2), Value::test_int(3), Value::test_int(1)], + span: Span::test_data(), + }, + Value::Record { + cols: rotated_columns, + vals: vec![Value::test_int(5), Value::test_int(6), Value::test_int(4)], + span: Span::test_data(), + }, + ], + span: Span::test_data(), + }), + }, + Example { + description: "Rolls columns to the left with fixed headers", + example: "[[a b c]; [1 2 3] [4 5 6]] | roll left --cells-only", + result: Some(Value::List { + vals: vec![ + Value::Record { + cols: columns.clone(), + vals: vec![Value::test_int(2), Value::test_int(3), Value::test_int(1)], + span: Span::test_data(), + }, + Value::Record { + cols: columns, + vals: vec![Value::test_int(5), Value::test_int(6), Value::test_int(4)], + span: Span::test_data(), + }, + ], + span: Span::test_data(), + }), + }, + ] + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + let by: Option = call.get_flag(engine_state, stack, "by")?; + let cells_only = call.has_flag("cells-only"); + let value = input.into_value(call.head); + let rotated_value = + horizontal_rotate_value(value, &by, cells_only, &HorizontalDirection::Left)?; + + Ok(rotated_value.into_pipeline_data()) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_examples() { + use crate::test_examples; + + test_examples(RollLeft {}) + } +} diff --git a/crates/nu-command/src/filters/roll/roll_right.rs b/crates/nu-command/src/filters/roll/roll_right.rs new file mode 100644 index 0000000000..63eba4b83a --- /dev/null +++ b/crates/nu-command/src/filters/roll/roll_right.rs @@ -0,0 +1,111 @@ +use nu_engine::CallExt; +use nu_protocol::ast::Call; +use nu_protocol::engine::{Command, EngineState, Stack}; +use nu_protocol::{ + Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, + Value, +}; + +use super::{horizontal_rotate_value, HorizontalDirection}; + +#[derive(Clone)] +pub struct RollRight; + +impl Command for RollRight { + fn name(&self) -> &str { + "roll right" + } + + fn signature(&self) -> Signature { + Signature::build(self.name()) + .named( + "by", + SyntaxShape::Int, + "Number of columns to roll", + Some('b'), + ) + .switch( + "cells-only", + "rotates columns leaving headers fixed", + Some('c'), + ) + .category(Category::Filters) + } + + fn usage(&self) -> &str { + "Roll table columns right" + } + + fn examples(&self) -> Vec { + let columns = vec!["a".to_string(), "b".to_string(), "c".to_string()]; + let rotated_columns = vec!["c".to_string(), "a".to_string(), "b".to_string()]; + vec![ + Example { + description: "Rolls columns to the right", + example: "[[a b c]; [1 2 3] [4 5 6]] | roll right", + result: Some(Value::List { + vals: vec![ + Value::Record { + cols: rotated_columns.clone(), + vals: vec![Value::test_int(3), Value::test_int(1), Value::test_int(2)], + span: Span::test_data(), + }, + Value::Record { + cols: rotated_columns, + vals: vec![Value::test_int(6), Value::test_int(4), Value::test_int(5)], + span: Span::test_data(), + }, + ], + span: Span::test_data(), + }), + }, + Example { + description: "Rolls columns to the right with fixed headers", + example: "[[a b c]; [1 2 3] [4 5 6]] | roll right --cells-only", + result: Some(Value::List { + vals: vec![ + Value::Record { + cols: columns.clone(), + vals: vec![Value::test_int(3), Value::test_int(1), Value::test_int(2)], + span: Span::test_data(), + }, + Value::Record { + cols: columns, + vals: vec![Value::test_int(6), Value::test_int(4), Value::test_int(5)], + span: Span::test_data(), + }, + ], + span: Span::test_data(), + }), + }, + ] + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + let by: Option = call.get_flag(engine_state, stack, "by")?; + let cells_only = call.has_flag("cells-only"); + let value = input.into_value(call.head); + let rotated_value = + horizontal_rotate_value(value, &by, cells_only, &HorizontalDirection::Right)?; + + Ok(rotated_value.into_pipeline_data()) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_examples() { + use crate::test_examples; + + test_examples(RollRight {}) + } +} diff --git a/crates/nu-command/src/filters/roll/roll_up.rs b/crates/nu-command/src/filters/roll/roll_up.rs new file mode 100644 index 0000000000..fac9cadcd2 --- /dev/null +++ b/crates/nu-command/src/filters/roll/roll_up.rs @@ -0,0 +1,82 @@ +use nu_engine::CallExt; +use nu_protocol::ast::Call; +use nu_protocol::engine::{Command, EngineState, Stack}; +use nu_protocol::{ + Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, + Value, +}; + +use super::{vertical_rotate_value, VerticalDirection}; + +#[derive(Clone)] +pub struct RollUp; + +impl Command for RollUp { + fn name(&self) -> &str { + "roll up" + } + + fn signature(&self) -> Signature { + Signature::build(self.name()) + .named("by", SyntaxShape::Int, "Number of rows to roll", Some('b')) + .category(Category::Filters) + } + + fn usage(&self) -> &str { + "Roll table rows up" + } + + fn examples(&self) -> Vec { + let columns = vec!["a".to_string(), "b".to_string()]; + vec![Example { + description: "Rolls rows up", + example: "[[a b]; [1 2] [3 4] [5 6]] | roll up", + result: Some(Value::List { + vals: vec![ + Value::Record { + cols: columns.clone(), + vals: vec![Value::test_int(3), Value::test_int(4)], + span: Span::test_data(), + }, + Value::Record { + cols: columns.clone(), + vals: vec![Value::test_int(5), Value::test_int(6)], + span: Span::test_data(), + }, + Value::Record { + cols: columns, + vals: vec![Value::test_int(1), Value::test_int(2)], + span: Span::test_data(), + }, + ], + span: Span::test_data(), + }), + }] + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + let by: Option = call.get_flag(engine_state, stack, "by")?; + let value = input.into_value(call.head); + let rotated_value = vertical_rotate_value(value, by, VerticalDirection::Up)?; + + Ok(rotated_value.into_pipeline_data()) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_examples() { + use crate::test_examples; + + test_examples(RollUp {}) + } +}