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(
52 "topic",
53 SyntaxShape::OneOf(vec![
54 SyntaxShape::String,
55 SyntaxShape::List(Box::new(SyntaxShape::String)),
56 ]),
57 "filter by topic pattern(s): string (commas allowed) or list",
58 Some('T'),
59 )
60 .switch(
61 "with-timestamp",
62 "include timestamp extracted from frame ID",
63 None,
64 )
65 .category(Category::Experimental)
66 }
67
68 fn description(&self) -> &str {
69 "Reads the event stream and returns frames"
70 }
71
72 fn run(
73 &self,
74 engine_state: &EngineState,
75 stack: &mut Stack,
76 call: &Call,
77 _input: PipelineData,
78 ) -> Result<PipelineData, ShellError> {
79 let limit: Option<usize> = call.get_flag(engine_state, stack, "limit")?;
80 let last: Option<usize> = call.get_flag(engine_state, stack, "last")?;
81 let after: Option<String> = call.get_flag(engine_state, stack, "after")?;
82 let from: Option<String> = call.get_flag(engine_state, stack, "from")?;
83 let topic: Option<nu_protocol::Value> = call.get_flag(engine_state, stack, "topic")?;
84 let topic: Option<String> = topic
85 .map(crate::nu::util::topic_value_to_string)
86 .transpose()?;
87 let with_timestamp = call.has_flag(engine_state, stack, "with-timestamp")?;
88
89 let parse_id = |s: &str, name: &str| -> Result<scru128::Scru128Id, ShellError> {
91 s.parse().map_err(|e| {
92 ShellError::Generic(GenericError::new(
93 format!("Invalid {name}"),
94 format!("Failed to parse Scru128Id: {e}"),
95 call.head,
96 ))
97 })
98 };
99
100 let after: Option<scru128::Scru128Id> =
101 after.as_deref().map(|s| parse_id(s, "after")).transpose()?;
102 let from: Option<scru128::Scru128Id> =
103 from.as_deref().map(|s| parse_id(s, "from")).transpose()?;
104
105 let options = ReadOptions::builder()
106 .maybe_after(after)
107 .maybe_from(from)
108 .maybe_limit(limit)
109 .maybe_last(last)
110 .maybe_topic(topic)
111 .build();
112
113 let frames: Vec<_> = self.store.read_sync(options).collect();
114
115 use nu_protocol::Value;
116
117 let output = Value::list(
118 frames
119 .into_iter()
120 .map(|frame| crate::nu::util::frame_to_value(&frame, call.head, with_timestamp))
121 .collect(),
122 call.head,
123 );
124
125 Ok(PipelineData::Value(output, None))
126 }
127}