1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
use std::error::Error as StdError;
use std::future::Future;
use std::{fmt, io};

use super::ProtocolVersion;

mod bytes;
mod collections;
#[cfg(feature = "nix-compat-derive")]
mod display;
mod int;
#[cfg(any(test, feature = "test"))]
pub mod mock;
mod writer;

pub use writer::{NixWriter, NixWriterBuilder};

pub trait Error: Sized + StdError {
    fn custom<T: fmt::Display>(msg: T) -> Self;

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

    fn unsupported_data<T: fmt::Display>(msg: T) -> Self {
        Self::custom(msg)
    }

    fn invalid_enum<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 unsupported_data<T: fmt::Display>(msg: T) -> Self {
        io::Error::new(io::ErrorKind::InvalidData, msg.to_string())
    }
}

pub trait NixWrite: Send {
    type Error: Error;

    /// 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;

    /// Write a single u64 to the protocol.
    fn write_number(&mut self, value: u64) -> impl Future<Output = Result<(), Self::Error>> + Send;

    /// Write a slice of bytes to the protocol.
    fn write_slice(&mut self, buf: &[u8]) -> impl Future<Output = Result<(), Self::Error>> + Send;

    /// Write a value that implements `std::fmt::Display` to the protocol.
    /// The protocol uses many small string formats and instead of allocating
    /// a `String` each time we want to write one an implementation of `NixWrite`
    /// can instead use `Display` to dump these formats to a reusable buffer.
    fn write_display<D>(&mut self, msg: D) -> impl Future<Output = Result<(), Self::Error>> + Send
    where
        D: fmt::Display + Send,
        Self: Sized,
    {
        async move {
            let s = msg.to_string();
            self.write_slice(s.as_bytes()).await
        }
    }

    /// Write a value to the protocol.
    /// Uses `NixSerialize::serialize` to write the value.
    fn write_value<V>(&mut self, value: &V) -> impl Future<Output = Result<(), Self::Error>> + Send
    where
        V: NixSerialize + Send + ?Sized,
        Self: Sized,
    {
        value.serialize(self)
    }
}

impl<T: NixWrite> NixWrite for &mut T {
    type Error = T::Error;

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

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

    fn write_slice(&mut self, buf: &[u8]) -> impl Future<Output = Result<(), Self::Error>> + Send {
        (**self).write_slice(buf)
    }

    fn write_display<D>(&mut self, msg: D) -> impl Future<Output = Result<(), Self::Error>> + Send
    where
        D: fmt::Display + Send,
        Self: Sized,
    {
        (**self).write_display(msg)
    }

    fn write_value<V>(&mut self, value: &V) -> impl Future<Output = Result<(), Self::Error>> + Send
    where
        V: NixSerialize + Send + ?Sized,
        Self: Sized,
    {
        (**self).write_value(value)
    }
}

pub trait NixSerialize {
    /// Write a value to the writer.
    fn serialize<W>(&self, writer: &mut W) -> impl Future<Output = Result<(), W::Error>> + Send
    where
        W: NixWrite;
}

// Noop
impl NixSerialize for () {
    async fn serialize<W>(&self, _writer: &mut W) -> Result<(), W::Error>
    where
        W: NixWrite,
    {
        Ok(())
    }
}