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 content_type: Option<String>,
23    #[serde(default)]
24    pub custom: Record,
25}
26
27impl PipelineMetadata {
28    pub fn with_data_source(self, data_source: DataSource) -> Self {
29        Self {
30            data_source,
31            ..self
32        }
33    }
34
35    pub fn with_content_type(self, content_type: Option<String>) -> Self {
36        Self {
37            content_type,
38            ..self
39        }
40    }
41
42    /// Transform metadata for the `collect` operation.
43    ///
44    /// After collecting a stream into a value, `FilePath` data sources are no longer meaningful
45    /// and should be converted to `None`. If all metadata fields become empty after this
46    /// transformation, returns `None` to avoid carrying around empty metadata.
47    pub fn for_collect(self) -> Option<Self> {
48        let Self {
49            data_source,
50            content_type,
51            custom,
52        } = self;
53
54        // Transform FilePath to None after collect
55        let data_source = match data_source {
56            DataSource::FilePath(_) => DataSource::None,
57            other => other,
58        };
59
60        // Return None if completely empty
61        if matches!(data_source, DataSource::None) && content_type.is_none() && custom.is_empty() {
62            None
63        } else {
64            Some(Self {
65                data_source,
66                content_type,
67                custom,
68            })
69        }
70    }
71}
72
73/// Describes where the particular [`PipelineMetadata`] originates.
74///
75/// This can either be a particular family of commands (useful so downstream commands can adjust
76/// the presentation e.g. `Ls`) or the opened file to protect against overwrite-attempts properly.
77#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
78pub enum DataSource {
79    Ls,
80    HtmlThemes,
81    FilePath(PathBuf),
82    #[default]
83    None,
84}