Skip to main content

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_content_type(self, content_type: Option<String>) -> Self {
37        Self {
38            content_type,
39            ..self
40        }
41    }
42
43    /// Transform metadata for the `collect` operation.
44    ///
45    /// After collecting a stream into a value, `FilePath` data sources are no longer meaningful
46    /// and should be converted to `None`. If all metadata fields become empty after this
47    /// transformation, returns `None` to avoid carrying around empty metadata.
48    pub fn for_collect(self) -> Option<Self> {
49        let Self {
50            data_source,
51            path_columns,
52            content_type,
53            custom,
54        } = self;
55
56        // Transform FilePath to None after collect
57        let data_source = match data_source {
58            DataSource::FilePath(_) => DataSource::None,
59            other => other,
60        };
61
62        // Return None if completely empty
63        if matches!(data_source, DataSource::None)
64            && path_columns.is_empty()
65            && content_type.is_none()
66            && custom.is_empty()
67        {
68            None
69        } else {
70            Some(Self {
71                data_source,
72                path_columns,
73                content_type,
74                custom,
75            })
76        }
77    }
78}
79
80/// Describes where the particular [`PipelineMetadata`] originates.
81///
82/// This can either be a particular family of commands (useful so downstream commands can adjust
83/// the presentation e.g. `Ls`) or the opened file to protect against overwrite-attempts properly.
84#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
85pub enum DataSource {
86    #[deprecated(
87        since = "0.111.0",
88        note = "Use `PipelineMetadata { path_columns: vec![\"name\".to_string()], .. }` instead."
89    )]
90    Ls,
91    HtmlThemes,
92    FilePath(PathBuf),
93    #[default]
94    None,
95}