nu_command/debug/
timeit.rs1use nu_engine::{ClosureEvalOnce, command_prelude::*};
2use nu_protocol::engine::Closure;
3use web_time::Instant;
4
5#[derive(Clone)]
6pub struct TimeIt;
7
8impl Command for TimeIt {
9 fn name(&self) -> &str {
10 "timeit"
11 }
12
13 fn description(&self) -> &str {
14 "Time how long it takes a closure to run."
15 }
16
17 fn extra_description(&self) -> &str {
18 "Any pipeline input given to this command is passed to the closure. Note that streaming inputs may affect timing results, and it is recommended to add a `collect` command before this if the input is a stream.
19
20This command will bubble up any errors encountered when running the closure. The return pipeline of the closure is collected into a value and then discarded."
21 }
22
23 fn signature(&self) -> nu_protocol::Signature {
24 Signature::build("timeit")
25 .required("command", SyntaxShape::Closure(None), "The closure to run.")
26 .input_output_types(vec![
27 (Type::Any, Type::Duration),
28 (Type::Nothing, Type::Duration),
29 ])
30 .allow_variants_without_examples(true)
31 .category(Category::Debug)
32 }
33
34 fn search_terms(&self) -> Vec<&str> {
35 vec!["timing", "timer", "benchmark", "measure"]
36 }
37
38 fn requires_ast_for_arguments(&self) -> bool {
39 true
40 }
41
42 fn run(
43 &self,
44 engine_state: &EngineState,
45 stack: &mut Stack,
46 call: &Call,
47 input: PipelineData,
48 ) -> Result<PipelineData, ShellError> {
49 let stack = &mut stack.push_redirection(None, None);
51
52 let closure: Closure = call.req(engine_state, stack, 0)?;
53 let closure = ClosureEvalOnce::new_preserve_out_dest(engine_state, stack, closure);
54
55 let start_time = Instant::now();
57 closure.run_with_input(input)?.into_value(call.head)?;
58 let time = start_time.elapsed();
59
60 let output = Value::duration(time.as_nanos() as i64, call.head);
61 Ok(output.into_pipeline_data())
62 }
63
64 fn examples(&self) -> Vec<Example> {
65 vec![
66 Example {
67 description: "Time a closure containing one command",
68 example: "timeit { sleep 500ms }",
69 result: None,
70 },
71 Example {
72 description: "Time a closure with an input value",
73 example: "'A really long string' | timeit { split chars }",
74 result: None,
75 },
76 Example {
77 description: "Time a closure with an input stream",
78 example: "open some_file.txt | collect | timeit { split chars }",
79 result: None,
80 },
81 Example {
82 description: "Time a closure containing a pipeline",
83 example: "timeit { open some_file.txt | split chars }",
84 result: None,
85 },
86 ]
87 }
88}
89
90#[test]
91fn test_time_block() {
94 use nu_test_support::{nu, nu_repl_code, playground::Playground};
95 Playground::setup("test_time_block", |dirs, _| {
96 let inp = [
97 r#"[2 3 4] | timeit {to nuon | save foo.txt }"#,
98 "open foo.txt",
99 ];
100 let actual_repl = nu!(cwd: dirs.test(), nu_repl_code(&inp));
101 assert_eq!(actual_repl.err, "");
102 assert_eq!(actual_repl.out, "[2, 3, 4]");
103 });
104}
105
106#[test]
107fn test_time_block_2() {
108 use nu_test_support::{nu, nu_repl_code, playground::Playground};
109 Playground::setup("test_time_block", |dirs, _| {
110 let inp = [
111 r#"[2 3 4] | timeit {{result: $in} | to nuon | save foo.txt }"#,
112 "open foo.txt",
113 ];
114 let actual_repl = nu!(cwd: dirs.test(), nu_repl_code(&inp));
115 assert_eq!(actual_repl.err, "");
116 assert_eq!(actual_repl.out, "{result: [2, 3, 4]}");
117 });
118}