2020-09-07 07:54:52 +02:00
|
|
|
use crate::commands::each::group::run_block_on_vec;
|
|
|
|
use crate::prelude::*;
|
2021-01-10 03:50:49 +01:00
|
|
|
use nu_engine::WholeStreamCommand;
|
2020-09-07 07:54:52 +02:00
|
|
|
//use itertools::Itertools;
|
|
|
|
use nu_errors::ShellError;
|
2020-12-18 08:53:49 +01:00
|
|
|
use nu_protocol::{hir::CapturedBlock, Primitive, Signature, SyntaxShape, UntaggedValue};
|
2020-09-07 07:54:52 +02:00
|
|
|
use nu_source::Tagged;
|
|
|
|
use serde::Deserialize;
|
|
|
|
|
|
|
|
pub struct EachWindow;
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
pub struct EachWindowArgs {
|
|
|
|
window_size: Tagged<usize>,
|
2020-12-18 08:53:49 +01:00
|
|
|
block: CapturedBlock,
|
2020-09-07 07:54:52 +02:00
|
|
|
stride: Option<Tagged<usize>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl WholeStreamCommand for EachWindow {
|
|
|
|
fn name(&self) -> &str {
|
|
|
|
"each window"
|
|
|
|
}
|
|
|
|
|
|
|
|
fn signature(&self) -> Signature {
|
|
|
|
Signature::build("each window")
|
|
|
|
.required("window_size", SyntaxShape::Int, "the size of each window")
|
|
|
|
.named(
|
|
|
|
"stride",
|
|
|
|
SyntaxShape::Int,
|
|
|
|
"the number of rows to slide over between windows",
|
|
|
|
Some('s'),
|
|
|
|
)
|
|
|
|
.required(
|
|
|
|
"block",
|
|
|
|
SyntaxShape::Block,
|
|
|
|
"the block to run on each group",
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn usage(&self) -> &str {
|
|
|
|
"Runs a block on sliding windows of `window_size` rows of a table at a time."
|
|
|
|
}
|
|
|
|
|
|
|
|
fn examples(&self) -> Vec<Example> {
|
|
|
|
vec![Example {
|
|
|
|
description: "Echo the sum of each window",
|
|
|
|
example: "echo [1 2 3 4] | each window 2 { echo $it | math sum }",
|
|
|
|
result: None,
|
|
|
|
}]
|
|
|
|
}
|
|
|
|
|
2021-04-12 04:35:01 +02:00
|
|
|
fn run_with_actions(&self, raw_args: CommandArgs) -> Result<ActionStream, ShellError> {
|
2021-02-06 01:34:26 +01:00
|
|
|
let context = Arc::new(EvaluationContext::from_args(&raw_args));
|
2021-04-06 18:19:43 +02:00
|
|
|
let (each_args, mut input): (EachWindowArgs, _) = raw_args.process()?;
|
2020-12-18 08:53:49 +01:00
|
|
|
let block = Arc::new(Box::new(each_args.block));
|
2020-09-07 07:54:52 +02:00
|
|
|
|
|
|
|
let mut window: Vec<_> = input
|
|
|
|
.by_ref()
|
|
|
|
.take(*each_args.window_size - 1)
|
2021-04-06 18:19:43 +02:00
|
|
|
.collect::<Vec<_>>();
|
2020-09-07 07:54:52 +02:00
|
|
|
|
|
|
|
// `window` must start with dummy values, which will be removed on the first iteration
|
|
|
|
let stride = each_args.stride.map(|x| *x).unwrap_or(1);
|
|
|
|
window.insert(0, UntaggedValue::Primitive(Primitive::Nothing).into());
|
|
|
|
|
|
|
|
Ok(input
|
|
|
|
.enumerate()
|
2021-04-06 18:19:43 +02:00
|
|
|
.map(move |(i, input)| {
|
2020-09-07 07:54:52 +02:00
|
|
|
// This would probably be more efficient if `last` was a VecDeque
|
|
|
|
// But we can't have that because it needs to be put into a Table
|
|
|
|
window.remove(0);
|
|
|
|
window.push(input);
|
|
|
|
|
|
|
|
let block = block.clone();
|
|
|
|
let context = context.clone();
|
|
|
|
let local_window = window.clone();
|
|
|
|
|
2021-04-06 18:19:43 +02:00
|
|
|
if i % stride == 0 {
|
|
|
|
Some(run_block_on_vec(local_window, block, context))
|
|
|
|
} else {
|
|
|
|
None
|
2020-09-07 07:54:52 +02:00
|
|
|
}
|
|
|
|
})
|
2021-04-06 18:19:43 +02:00
|
|
|
.filter_map(|x| x)
|
2020-09-07 07:54:52 +02:00
|
|
|
.flatten()
|
2021-04-12 04:35:01 +02:00
|
|
|
.to_action_stream())
|
2020-09-07 07:54:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::EachWindow;
|
2020-10-03 16:06:02 +02:00
|
|
|
use super::ShellError;
|
2020-09-07 07:54:52 +02:00
|
|
|
|
|
|
|
#[test]
|
2020-10-03 16:06:02 +02:00
|
|
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
2020-09-07 07:54:52 +02:00
|
|
|
use crate::examples::test as test_examples;
|
|
|
|
|
2021-02-12 11:13:14 +01:00
|
|
|
test_examples(EachWindow {})
|
2020-09-07 07:54:52 +02:00
|
|
|
}
|
|
|
|
}
|