xs/nu/commands/
cat_command.rs1use nu_engine::CallExt;
2use nu_protocol::engine::{Call, Command, EngineState, Stack};
3use nu_protocol::shell_error::generic::GenericError;
4use nu_protocol::{Category, PipelineData, ShellError, Signature, SyntaxShape, Type};
5
6use crate::store::{ReadOptions, Store};
7
8#[derive(Clone)]
9pub struct CatCommand {
10 store: Store,
11}
12
13impl CatCommand {
14 pub fn new(store: Store) -> Self {
15 Self { store }
16 }
17}
18
19impl Command for CatCommand {
20 fn name(&self) -> &str {
21 ".cat"
22 }
23
24 fn signature(&self) -> Signature {
25 Signature::build(".cat")
26 .input_output_types(vec![(Type::Nothing, Type::Any)])
27 .named(
28 "limit",
29 SyntaxShape::Int,
30 "limit the number of frames to retrieve",
31 None,
32 )
33 .named(
34 "after",
35 SyntaxShape::String,
36 "start after a specific frame ID (exclusive)",
37 Some('a'),
38 )
39 .named(
40 "from",
41 SyntaxShape::String,
42 "start from a specific frame ID (inclusive)",
43 None,
44 )
45 .named(
46 "last",
47 SyntaxShape::Int,
48 "return the N most recent frames",
49 None,
50 )
51 .named("topic", SyntaxShape::String, "filter by topic", Some('T'))
52 .switch(
53 "with-timestamp",
54 "include timestamp extracted from frame ID",
55 None,
56 )
57 .category(Category::Experimental)
58 }
59
60 fn description(&self) -> &str {
61 "Reads the event stream and returns frames"
62 }
63
64 fn run(
65 &self,
66 engine_state: &EngineState,
67 stack: &mut Stack,
68 call: &Call,
69 _input: PipelineData,
70 ) -> Result<PipelineData, ShellError> {
71 let limit: Option<usize> = call.get_flag(engine_state, stack, "limit")?;
72 let last: Option<usize> = call.get_flag(engine_state, stack, "last")?;
73 let after: Option<String> = call.get_flag(engine_state, stack, "after")?;
74 let from: Option<String> = call.get_flag(engine_state, stack, "from")?;
75 let topic: Option<String> = call.get_flag(engine_state, stack, "topic")?;
76 let with_timestamp = call.has_flag(engine_state, stack, "with-timestamp")?;
77
78 let parse_id = |s: &str, name: &str| -> Result<scru128::Scru128Id, ShellError> {
80 s.parse().map_err(|e| {
81 ShellError::Generic(GenericError::new(
82 format!("Invalid {name}"),
83 format!("Failed to parse Scru128Id: {e}"),
84 call.head,
85 ))
86 })
87 };
88
89 let after: Option<scru128::Scru128Id> =
90 after.as_deref().map(|s| parse_id(s, "after")).transpose()?;
91 let from: Option<scru128::Scru128Id> =
92 from.as_deref().map(|s| parse_id(s, "from")).transpose()?;
93
94 let options = ReadOptions::builder()
95 .maybe_after(after)
96 .maybe_from(from)
97 .maybe_limit(limit)
98 .maybe_last(last)
99 .maybe_topic(topic)
100 .build();
101
102 let frames: Vec<_> = self.store.read_sync(options).collect();
103
104 use nu_protocol::Value;
105
106 let output = Value::list(
107 frames
108 .into_iter()
109 .map(|frame| crate::nu::util::frame_to_value(&frame, call.head, with_timestamp))
110 .collect(),
111 call.head,
112 );
113
114 Ok(PipelineData::Value(output, None))
115 }
116}