nu_protocol/pipeline/metadata.rs
1use std::path::PathBuf;
2
3use serde::{Deserialize, Serialize};
4
5use crate::Record;
6
7/// Metadata that is valid for the whole [`PipelineData`](crate::PipelineData)
8///
9/// ## Custom Metadata
10///
11/// The `custom` field allows commands and plugins to attach arbitrary metadata to pipeline data.
12/// To avoid key collisions, it is recommended to use namespaced keys with an underscore separator:
13///
14/// - `"http_response"` - HTTP response metadata (status, headers, etc.)
15/// - `"polars_schema"` - DataFrame schema information
16/// - `"custom_plugin_field"` - Plugin-specific metadata
17///
18/// This convention helps ensure different commands and plugins don't overwrite each other's metadata.
19#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
20pub struct PipelineMetadata {
21 pub data_source: DataSource,
22 pub path_columns: Vec<String>,
23 pub content_type: Option<String>,
24 #[serde(default)]
25 pub custom: Record,
26}
27
28impl PipelineMetadata {
29 pub fn with_data_source(self, data_source: DataSource) -> Self {
30 Self {
31 data_source,
32 ..self
33 }
34 }
35
36 pub fn with_path_columns(self, path_columns: Vec<String>) -> Self {
37 Self {
38 path_columns,
39 ..self
40 }
41 }
42
43 pub fn with_content_type(self, content_type: Option<String>) -> Self {
44 Self {
45 content_type,
46 ..self
47 }
48 }
49
50 /// Transform metadata for the `collect` operation.
51 ///
52 /// After collecting a stream into a value, `FilePath` data sources are no longer meaningful
53 /// and should be converted to `None`. If all metadata fields become empty after this
54 /// transformation, returns `None` to avoid carrying around empty metadata.
55 pub fn for_collect(self) -> Option<Self> {
56 let Self {
57 data_source,
58 path_columns,
59 content_type,
60 custom,
61 } = self;
62
63 // Transform FilePath to None after collect
64 let data_source = match data_source {
65 DataSource::FilePath(_) => DataSource::None,
66 other => other,
67 };
68
69 // Return None if completely empty
70 if matches!(data_source, DataSource::None)
71 && path_columns.is_empty()
72 && content_type.is_none()
73 && custom.is_empty()
74 {
75 None
76 } else {
77 Some(Self {
78 data_source,
79 path_columns,
80 content_type,
81 custom,
82 })
83 }
84 }
85}
86
87/// Describes where the particular [`PipelineMetadata`] originates.
88///
89/// This can either be a particular family of commands (useful so downstream commands can adjust
90/// the presentation e.g. `Ls`) or the opened file to protect against overwrite-attempts properly.
91#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
92pub enum DataSource {
93 #[deprecated(
94 since = "0.111.0",
95 note = "Use `PipelineMetadata { path_columns: vec![\"name\".to_string()], .. }` instead."
96 )]
97 Ls,
98 HtmlThemes,
99 FilePath(PathBuf),
100 #[default]
101 None,
102}