about summary refs log blame commit diff
path: root/tvix/nix-compat/src/wire/de/mod.rs
blob: f85ccd8fea0ef7ff1c20d9bbae2080f91a82eeb3 (plain) (tree)
































































































































































































































                                                                                                 
use std::error::Error as StdError;
use std::future::Future;
use std::ops::RangeInclusive;
use std::{fmt, io};

use ::bytes::Bytes;

use super::ProtocolVersion;

mod bytes;
mod collections;
mod int;
#[cfg(any(test, feature = "test"))]
pub mod mock;
mod reader;

pub use reader::{NixReader, NixReaderBuilder};

/// Like serde the `Error` trait allows `NixRead` implementations to add
/// custom error handling for `NixDeserialize`.
pub trait Error: Sized + StdError {
    /// A totally custom non-specific error.
    fn custom<T: fmt::Display>(msg: T) -> Self;

    /// Some kind of std::io::Error occured.
    fn io_error(err: std::io::Error) -> Self {
        Self::custom(format_args!("There was an I/O error {}", err))
    }

    /// The data read from `NixRead` is invalid.
    /// This could be that some bytes were supposed to be valid UFT-8 but weren't.
    fn invalid_data<T: fmt::Display>(msg: T) -> Self {
        Self::custom(msg)
    }

    /// Required data is missing. This is mostly like an EOF
    fn missing_data<T: fmt::Display>(msg: T) -> Self {
        Self::custom(msg)
    }
}

impl Error for io::Error {
    fn custom<T: fmt::Display>(msg: T) -> Self {
        io::Error::new(io::ErrorKind::Other, msg.to_string())
    }

    fn io_error(err: std::io::Error) -> Self {
        err
    }

    fn invalid_data<T: fmt::Display>(msg: T) -> Self {
        io::Error::new(io::ErrorKind::InvalidData, msg.to_string())
    }

    fn missing_data<T: fmt::Display>(msg: T) -> Self {
        io::Error::new(io::ErrorKind::UnexpectedEof, msg.to_string())
    }
}

/// A reader of data from the Nix daemon protocol.
/// Basically there are two basic types in the Nix daemon protocol
/// u64 and a bytes buffer. Everything else is more or less built on
/// top of these two types.
pub trait NixRead: Send {
    type Error: Error + Send;

    /// Some types are serialized differently depending on the version
    /// of the protocol and so this can be used for implementing that.
    fn version(&self) -> ProtocolVersion;

    /// Read a single u64 from the protocol.
    /// This returns an Option to support graceful shutdown.
    fn try_read_number(
        &mut self,
    ) -> impl Future<Output = Result<Option<u64>, Self::Error>> + Send + '_;

    /// Read bytes from the protocol.
    /// A size limit on the returned bytes has to be specified.
    /// This returns an Option to support graceful shutdown.
    fn try_read_bytes_limited(
        &mut self,
        limit: RangeInclusive<usize>,
    ) -> impl Future<Output = Result<Option<Bytes>, Self::Error>> + Send + '_;

    /// Read bytes from the protocol without a limit.
    /// The default implementation just calls `try_read_bytes_limited` with a
    /// limit of `0..=usize::MAX` but other implementations are free to have a
    /// reader wide limit.
    /// This returns an Option to support graceful shutdown.
    fn try_read_bytes(
        &mut self,
    ) -> impl Future<Output = Result<Option<Bytes>, Self::Error>> + Send + '_ {
        self.try_read_bytes_limited(0..=usize::MAX)
    }

    /// Read a single u64 from the protocol.
    /// This will return an error if the number could not be read.
    fn read_number(&mut self) -> impl Future<Output = Result<u64, Self::Error>> + Send + '_ {
        async move {
            match self.try_read_number().await? {
                Some(v) => Ok(v),
                None => Err(Self::Error::missing_data("unexpected end-of-file")),
            }
        }
    }

    /// Read bytes from the protocol.
    /// A size limit on the returned bytes has to be specified.
    /// This will return an error if the number could not be read.
    fn read_bytes_limited(
        &mut self,
        limit: RangeInclusive<usize>,
    ) -> impl Future<Output = Result<Bytes, Self::Error>> + Send + '_ {
        async move {
            match self.try_read_bytes_limited(limit).await? {
                Some(v) => Ok(v),
                None => Err(Self::Error::missing_data("unexpected end-of-file")),
            }
        }
    }

    /// Read bytes from the protocol.
    /// The default implementation just calls `read_bytes_limited` with a
    /// limit of `0..=usize::MAX` but other implementations are free to have a
    /// reader wide limit.
    /// This will return an error if the bytes could not be read.
    fn read_bytes(&mut self) -> impl Future<Output = Result<Bytes, Self::Error>> + Send + '_ {
        self.read_bytes_limited(0..=usize::MAX)
    }

    /// Read a value from the protocol.
    /// Uses `NixDeserialize::deserialize` to read a value.
    fn read_value<V: NixDeserialize>(
        &mut self,
    ) -> impl Future<Output = Result<V, Self::Error>> + Send + '_ {
        V::deserialize(self)
    }

    /// Read a value from the protocol.
    /// Uses `NixDeserialize::try_deserialize` to read a value.
    /// This returns an Option to support graceful shutdown.
    fn try_read_value<V: NixDeserialize>(
        &mut self,
    ) -> impl Future<Output = Result<Option<V>, Self::Error>> + Send + '_ {
        V::try_deserialize(self)
    }
}

impl<T: ?Sized + NixRead> NixRead for &mut T {
    type Error = T::Error;

    fn version(&self) -> ProtocolVersion {
        (**self).version()
    }

    fn try_read_number(
        &mut self,
    ) -> impl Future<Output = Result<Option<u64>, Self::Error>> + Send + '_ {
        (**self).try_read_number()
    }

    fn try_read_bytes_limited(
        &mut self,
        limit: RangeInclusive<usize>,
    ) -> impl Future<Output = Result<Option<Bytes>, Self::Error>> + Send + '_ {
        (**self).try_read_bytes_limited(limit)
    }

    fn try_read_bytes(
        &mut self,
    ) -> impl Future<Output = Result<Option<Bytes>, Self::Error>> + Send + '_ {
        (**self).try_read_bytes()
    }

    fn read_number(&mut self) -> impl Future<Output = Result<u64, Self::Error>> + Send + '_ {
        (**self).read_number()
    }

    fn read_bytes_limited(
        &mut self,
        limit: RangeInclusive<usize>,
    ) -> impl Future<Output = Result<Bytes, Self::Error>> + Send + '_ {
        (**self).read_bytes_limited(limit)
    }

    fn read_bytes(&mut self) -> impl Future<Output = Result<Bytes, Self::Error>> + Send + '_ {
        (**self).read_bytes()
    }

    fn try_read_value<V: NixDeserialize>(
        &mut self,
    ) -> impl Future<Output = Result<Option<V>, Self::Error>> + Send + '_ {
        (**self).try_read_value()
    }

    fn read_value<V: NixDeserialize>(
        &mut self,
    ) -> impl Future<Output = Result<V, Self::Error>> + Send + '_ {
        (**self).read_value()
    }
}

/// A data structure that can be deserialized from the Nix daemon
/// worker protocol.
pub trait NixDeserialize: Sized {
    /// Read a value from the reader.
    /// This returns an Option to support gracefull shutdown.
    fn try_deserialize<R>(
        reader: &mut R,
    ) -> impl Future<Output = Result<Option<Self>, R::Error>> + Send + '_
    where
        R: ?Sized + NixRead + Send;

    fn deserialize<R>(reader: &mut R) -> impl Future<Output = Result<Self, R::Error>> + Send + '_
    where
        R: ?Sized + NixRead + Send,
    {
        async move {
            match Self::try_deserialize(reader).await? {
                Some(v) => Ok(v),
                None => Err(R::Error::missing_data("unexpected end-of-file")),
            }
        }
    }
}