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}