Repository: nushell/nushell
File: crates/nu-plugin-protocol/src/lib.rs (Rust)

Code before:
/// A stream control or data message.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum StreamMessage {
    /// Append data to the stream. Sent by the stream producer.
    Data(StreamId, StreamData),
    /// End of stream. Sent by the stream producer.
    End(StreamId),
    /// Notify that the read end of the stream has closed, and further messages should not be
    /// sent. Sent by the stream consumer.
    Drop(StreamId),
    /// Acknowledge that a message has been consumed. This is used to implement flow control by
    /// the stream producer. Sent by the stream consumer.
    Ack(StreamId),
}

/// Response to a [`PluginCall`]. The type parameter determines the output type for pipeline data.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum PluginCallResponse<D> {
    Ok,
    Error(LabeledError),
    Metadata(PluginMetadata),
    Signature(Vec<PluginSignature>),
    Ordering(Option<Ordering>),
    PipelineData(D),
}

impl<D> PluginCallResponse<D> {
    /// Convert the data type from `D` to `T`. The function will not be called if the variant does
    /// not contain data.
    pub fn map_data<T>(
        self,
        f: impl FnOnce(D) -> Result<T, ShellError>,
    ) -> Result<PluginCallResponse<T>, ShellError> {
        Ok(match self {
            PluginCallResponse::Ok => PluginCallResponse::Ok,
            PluginCallResponse::Error(err) => PluginCallResponse::Error(err),
            PluginCallResponse::Metadata(meta) => PluginCallResponse::Metadata(meta),
            PluginCallResponse::Signature(sigs) => PluginCallResponse::Signature(sigs),
            PluginCallResponse::Ordering(ordering) => PluginCallResponse::Ordering(ordering),
            PluginCallResponse::PipelineData(input) => PluginCallResponse::PipelineData(f(input)?),
        })
    }
}

impl PluginCallResponse<PipelineDataHeader> {
    /// Construct a plugin call response with a single value
    pub fn value(value: Value) -> PluginCallResponse<PipelineDataHeader> {
        if value.is_nothing() {
            PluginCallResponse::PipelineData(PipelineDataHeader::Empty)
        } else {
            PluginCallResponse::PipelineData(PipelineDataHeader::value(value))

Review comment (suggestion):
What if `save` returns `PipelineData(PipelineDataHeader::Empty)`, just like how builtin `save` returns `PipelineData::empty` on success.
I agree that `Ok` is easier to understand in semantic, but I feel nervous introducing more variant into an enum

Code after:
    type Error = ShellError;

    fn try_from(value: StreamData) -> Result<Result<Vec<u8>, ShellError>, ShellError> {
        Result::<Vec<u8>, LabeledError>::try_from(value).map(|res| res.map_err(ShellError::from))
    }
}

/// A stream control or data message.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum StreamMessage {
    /// Append data to the stream. Sent by the stream producer.
    Data(StreamId, StreamData),
    /// End of stream. Sent by the stream producer.
    End(StreamId),
    /// Notify that the read end of the stream has closed, and further messages should not be
    /// sent. Sent by the stream consumer.
    Drop(StreamId),
    /// Acknowledge that a message has been consumed. This is used to implement flow control by
    /// the stream producer. Sent by the stream consumer.
    Ack(StreamId),
}

/// Response to a [`PluginCall`]. The type parameter determines the output type for pipeline data.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum PluginCallResponse<D> {
    Ok,
    Error(LabeledError),
    Metadata(PluginMetadata),
    Signature(Vec<PluginSignature>),
    Ordering(Option<Ordering>),
    PipelineData(D),
}

impl<D> PluginCallResponse<D> {
    /// Convert the data type from `D` to `T`. The function will not be called if the variant does
    /// not contain data.
    pub fn map_data<T>(
        self,
        f: impl FnOnce(D) -> Result<T, ShellError>,
    ) -> Result<PluginCallResponse<T>, ShellError> {
        Ok(match self {
            PluginCallResponse::Ok => PluginCallResponse::Ok,
            PluginCallResponse::Error(err) => PluginCallResponse::Error(err),
            PluginCallResponse::Metadata(meta) => PluginCallResponse::Metadata(meta),
            PluginCallResponse::Signature(sigs) => PluginCallResponse::Signature(sigs),
            PluginCallResponse::Ordering(ordering) => PluginCallResponse::Ordering(ordering),
            PluginCallResponse::PipelineData(input) => PluginCallResponse::PipelineData(f(input)?),
        })
    }
}