Skip to main content

nix_compat/nix_daemon/
types.rs

1use crate::derived_path::{DerivedPath, LegacyDerivedPath};
2use crate::nixbase32;
3use crate::wire::de::Error;
4use crate::{
5    narinfo::Signature,
6    nixhash::CAHash,
7    store_path::StorePath,
8    wire::{
9        de::{NixDeserialize, NixRead},
10        ser::{NixSerialize, NixWrite},
11    },
12};
13use nix_compat_derive::{NixDeserialize, NixSerialize};
14use std::future::Future;
15
16/// Marker type that consumes/sends and ignores a u64.
17#[derive(Clone, Debug, NixDeserialize, NixSerialize)]
18#[nix(from = "u64", into = "u64")]
19pub struct IgnoredZero;
20impl From<u64> for IgnoredZero {
21    fn from(_: u64) -> Self {
22        IgnoredZero
23    }
24}
25
26impl From<IgnoredZero> for u64 {
27    fn from(_: IgnoredZero) -> Self {
28        0
29    }
30}
31
32#[derive(Debug, NixSerialize)]
33pub struct TraceLine {
34    have_pos: IgnoredZero,
35    hint: String,
36}
37
38/// Represents an error returned by the nix-daemon to its client.
39///
40/// Adheres to the format described in serialization.md
41#[derive(NixSerialize)]
42pub struct NixError {
43    #[nix(version = "26..")]
44    type_: &'static str,
45
46    #[nix(version = "26..")]
47    level: u64,
48
49    #[nix(version = "26..")]
50    name: &'static str,
51
52    msg: String,
53    #[nix(version = "26..")]
54    have_pos: IgnoredZero,
55
56    #[nix(version = "26..")]
57    traces: Vec<TraceLine>,
58
59    #[nix(version = "..=25")]
60    exit_status: u64,
61}
62
63impl NixError {
64    pub fn new(msg: String) -> Self {
65        Self {
66            type_: "Error",
67            level: 0, // error
68            name: "Error",
69            msg,
70            have_pos: IgnoredZero {},
71            traces: vec![],
72            exit_status: 1,
73        }
74    }
75}
76
77nix_compat_derive::nix_serialize_remote!(#[nix(display)] Signature<String>);
78
79impl NixDeserialize for Signature<String> {
80    async fn try_deserialize<R>(reader: &mut R) -> Result<Option<Self>, R::Error>
81    where
82        R: ?Sized + NixRead + Send,
83    {
84        let value: Option<String> = reader.try_read_value().await?;
85        match value {
86            Some(value) => Ok(Some(
87                Signature::<String>::parse(&value).map_err(R::Error::invalid_data)?,
88            )),
89            None => Ok(None),
90        }
91    }
92}
93
94impl NixSerialize for CAHash {
95    async fn serialize<W>(&self, writer: &mut W) -> Result<(), W::Error>
96    where
97        W: NixWrite,
98    {
99        writer.write_value(&self.to_nix_nixbase32_string()).await
100    }
101}
102
103impl NixSerialize for Option<CAHash> {
104    async fn serialize<W>(&self, writer: &mut W) -> Result<(), W::Error>
105    where
106        W: NixWrite,
107    {
108        match self {
109            Some(value) => writer.write_value(value).await,
110            None => writer.write_value("").await,
111        }
112    }
113}
114
115impl NixDeserialize for CAHash {
116    async fn try_deserialize<R>(reader: &mut R) -> Result<Option<Self>, R::Error>
117    where
118        R: ?Sized + NixRead + Send,
119    {
120        let value: Option<String> = reader.try_read_value().await?;
121        match value {
122            Some(value) => Ok(Some(CAHash::from_nix_hex_str(&value).ok_or_else(|| {
123                R::Error::invalid_data(format!("Invalid cahash {value}"))
124            })?)),
125            None => Ok(None),
126        }
127    }
128}
129
130impl NixDeserialize for Option<CAHash> {
131    async fn try_deserialize<R>(reader: &mut R) -> Result<Option<Self>, R::Error>
132    where
133        R: ?Sized + NixRead + Send,
134    {
135        let value: Option<String> = reader.try_read_value().await?;
136        match value {
137            Some(value) => {
138                if value.is_empty() {
139                    Ok(None)
140                } else {
141                    Ok(Some(Some(CAHash::from_nix_hex_str(&value).ok_or_else(
142                        || R::Error::invalid_data(format!("Invalid cahash {value}")),
143                    )?)))
144                }
145            }
146            None => Ok(None),
147        }
148    }
149}
150
151impl NixSerialize for Option<UnkeyedValidPathInfo> {
152    async fn serialize<W>(&self, writer: &mut W) -> Result<(), W::Error>
153    where
154        W: NixWrite,
155    {
156        match self {
157            Some(value) => {
158                writer.write_value(&true).await?;
159                writer.write_value(value).await
160            }
161            None => writer.write_value(&false).await,
162        }
163    }
164}
165
166// Custom implementation since FromStr does not use from_absolute_path
167impl NixDeserialize for StorePath<String> {
168    async fn try_deserialize<R>(reader: &mut R) -> Result<Option<Self>, R::Error>
169    where
170        R: ?Sized + NixRead + Send,
171    {
172        use crate::wire::de::Error;
173        if let Some(buf) = reader.try_read_bytes().await? {
174            let result = StorePath::<String>::from_absolute_path(&buf);
175            result.map(Some).map_err(R::Error::invalid_data)
176        } else {
177            Ok(None)
178        }
179    }
180}
181
182impl NixDeserialize for Option<StorePath<String>> {
183    async fn try_deserialize<R>(reader: &mut R) -> Result<Option<Self>, R::Error>
184    where
185        R: ?Sized + NixRead + Send,
186    {
187        use crate::wire::de::Error;
188        if let Some(buf) = reader.try_read_bytes().await? {
189            if buf.is_empty() {
190                Ok(Some(None))
191            } else {
192                let result = StorePath::<String>::from_absolute_path(&buf);
193                result
194                    .map(|r| Some(Some(r)))
195                    .map_err(R::Error::invalid_data)
196            }
197        } else {
198            Ok(Some(None))
199        }
200    }
201}
202
203// Custom implementation since Display does not use absolute paths.
204impl<S> NixSerialize for StorePath<S>
205where
206    S: AsRef<str>,
207{
208    fn serialize<W>(&self, writer: &mut W) -> impl Future<Output = Result<(), W::Error>> + Send
209    where
210        W: NixWrite,
211    {
212        let fmt = self.as_absolute_path_fmt();
213        async move { writer.write_display(fmt).await }
214    }
215}
216
217nix_compat_derive::nix_serialize_remote!(
218    #[nix(into = "LegacyDerivedPath", from = "LegacyDerivedPath")]
219    DerivedPath
220);
221nix_compat_derive::nix_serialize_remote!(
222    #[nix(from_str, display)]
223    LegacyDerivedPath
224);
225
226// Writes StorePath or an empty string.
227impl NixSerialize for Option<StorePath<String>> {
228    async fn serialize<W>(&self, writer: &mut W) -> Result<(), W::Error>
229    where
230        W: NixWrite,
231    {
232        match self {
233            Some(value) => writer.write_value(value).await,
234            None => writer.write_value("").await,
235        }
236    }
237}
238
239#[derive(NixSerialize, NixDeserialize, Debug, Clone, PartialEq)]
240pub struct UnkeyedValidPathInfo {
241    pub deriver: Option<StorePath<String>>,
242    pub nar_hash: NarHash,
243    pub references: Vec<StorePath<String>>,
244    pub registration_time: u64,
245    pub nar_size: u64,
246    pub ultimate: bool,
247    pub signatures: Vec<Signature<String>>,
248    pub ca: Option<CAHash>,
249}
250
251/// Request tuple for [super::worker_protocol::Operation::QueryValidPaths]
252#[derive(NixDeserialize)]
253pub struct QueryValidPaths {
254    // Paths to query
255    pub paths: Vec<StorePath<String>>,
256
257    // Whether to try and substitute the paths.
258    #[nix(version = "27..")]
259    pub substitute: bool,
260}
261
262/// newtype wrapper for the byte array that correctly implements NixSerialize, NixDeserialize.
263#[derive(Debug, Clone, Copy, PartialEq, Eq)]
264pub struct NarHash([u8; 32]);
265
266impl NarHash {
267    pub fn from_digest(digest: [u8; 32]) -> Self {
268        NarHash(digest)
269    }
270}
271
272impl std::ops::Deref for NarHash {
273    type Target = [u8; 32];
274
275    fn deref(&self) -> &Self::Target {
276        &self.0
277    }
278}
279
280impl NixDeserialize for NarHash {
281    async fn try_deserialize<R>(reader: &mut R) -> Result<Option<Self>, R::Error>
282    where
283        R: ?Sized + NixRead + Send,
284    {
285        if let Some(bytes) = reader.try_read_bytes().await? {
286            let result = data_encoding::HEXLOWER
287                .decode(bytes.as_ref())
288                .map_err(R::Error::invalid_data)?;
289            Ok(Some(NarHash(result.try_into().map_err(|_| {
290                R::Error::invalid_data("incorrect length")
291            })?)))
292        } else {
293            Ok(None)
294        }
295    }
296}
297
298impl NixSerialize for NarHash {
299    async fn serialize<W>(&self, writer: &mut W) -> Result<(), W::Error>
300    where
301        W: NixWrite,
302    {
303        nixbase32::encode(&self.0).serialize(writer).await
304    }
305}
306
307/// Info type used by [super::worker_protocol::Operation::AddToStoreNar] and [super::worker_protocol::Operation::AddMultipleToStore]
308///
309/// See: [ValidPathInfo reference](https://snix.dev/docs/reference/nix-daemon-protocol/types/#validpathinfo)
310#[derive(NixDeserialize, Debug)]
311pub struct ValidPathInfo {
312    // - path :: [StorePath][se-StorePath]
313    pub path: StorePath<String>,
314    pub info: UnkeyedValidPathInfo,
315}