Skip to main content

nix_compat/wire/
serde.rs

1mod derivation {
2    use crate::{
3        derivation::{OutputHash, OutputName, Outputs, UnverifiedOutputsBuilder},
4        store_path::StorePath,
5        wire::{de::NixDeserialize, ser::NixSerialize},
6    };
7    use data_encoding::HEXLOWER;
8    use nix_compat_derive::nix_serde_remote;
9
10    nix_serde_remote!(
11        #[nix(from_str, display)]
12        OutputName
13    );
14
15    impl NixSerialize for Option<&'_ OutputHash> {
16        async fn serialize<W>(&self, writer: &mut W) -> Result<(), W::Error>
17        where
18            W: crate::wire::ser::NixWrite,
19        {
20            if let Some(output_hash) = self.as_ref() {
21                writer
22                    .write_slice(output_hash.as_mode_and_algo_str().as_bytes())
23                    .await?;
24                writer
25                    .write_display(
26                        &data_encoding::HEXLOWER.encode_display(output_hash.hash.digest_as_bytes()),
27                    )
28                    .await?;
29            } else {
30                writer.write_slice(&[]).await?;
31                writer.write_slice(&[]).await?;
32            }
33            Ok(())
34        }
35    }
36
37    impl NixDeserialize for Option<OutputHash> {
38        async fn try_deserialize<R>(reader: &mut R) -> Result<Option<Self>, R::Error>
39        where
40            R: ?Sized + crate::wire::de::NixRead + Send,
41        {
42            use crate::wire::de::Error;
43            if let Some(mode_and_algo) = reader.try_read_value::<String>().await? {
44                let hash = reader.read_value::<String>().await?;
45                if mode_and_algo.is_empty() && hash.is_empty() {
46                    return Ok(Some(None));
47                }
48                let digest = HEXLOWER
49                    .decode(hash.as_bytes())
50                    .map_err(R::Error::invalid_data)?;
51                Ok(Some(Some(
52                    OutputHash::from_mode_algo_and_digest(&mode_and_algo, digest)
53                        .map_err(R::Error::invalid_data)?,
54                )))
55            } else {
56                Ok(None)
57            }
58        }
59    }
60
61    impl NixSerialize for Outputs {
62        async fn serialize<W>(&self, writer: &mut W) -> Result<(), W::Error>
63        where
64            W: crate::wire::ser::NixWrite,
65        {
66            for (output_name, path) in self {
67                writer.write_value(output_name).await?;
68                writer.write_value(path).await?;
69                writer.write_value(&self.as_fixed_output_hash()).await?;
70            }
71            Ok(())
72        }
73    }
74
75    impl NixDeserialize for Outputs {
76        async fn try_deserialize<R>(reader: &mut R) -> Result<Option<Self>, R::Error>
77        where
78            R: ?Sized + crate::wire::de::NixRead + Send,
79        {
80            use crate::wire::de::Error;
81            if let Some(len) = reader.try_read_value::<usize>().await? {
82                let mut builder = UnverifiedOutputsBuilder::new();
83                for _ in 0..len {
84                    let output_name = reader.read_value::<OutputName>().await?;
85                    let store_path = reader.read_value::<StorePath>().await?;
86                    let output_hash = reader.read_value::<Option<OutputHash>>().await?;
87                    builder
88                        .try_insert(output_name, store_path, output_hash)
89                        .map_err(R::Error::custom)?;
90                }
91                Ok(Some(builder.try_build().map_err(R::Error::custom)?))
92            } else {
93                Ok(None)
94            }
95        }
96    }
97}
98
99mod derived_path {
100    use crate::derived_path::{DerivedPath, LegacyDerivedPath};
101    use nix_compat_derive::nix_serde_remote;
102
103    nix_serde_remote!(
104        #[nix(into = "LegacyDerivedPath", from = "LegacyDerivedPath")]
105        DerivedPath
106    );
107    nix_serde_remote!(
108        #[nix(from_str, display)]
109        LegacyDerivedPath
110    );
111}
112
113mod int {
114    use nix_compat_derive::{nix_deserialize_remote, nix_serde_remote};
115
116    nix_deserialize_remote!(
117        #[nix(try_from = "u64")]
118        u8
119    );
120    nix_serde_remote!(
121        #[nix(try_from = "u64", into = "u64")]
122        u16
123    );
124    nix_serde_remote!(
125        #[nix(try_from = "u64", into = "u64")]
126        u32
127    );
128    nix_serde_remote!(
129        #[nix(try_from = "u64", try_into = "u64")]
130        i64
131    );
132}
133
134mod log {
135    nix_compat_derive::nix_serde_remote!(
136        #[nix(try_from = "u64", into = "u64")]
137        crate::log::VerbosityLevel
138    );
139}
140
141mod narinfo {
142    use crate::narinfo::Signature;
143    use crate::wire::de::{NixDeserialize, NixRead};
144
145    nix_compat_derive::nix_serialize_remote!(#[nix(display)] Signature<String>);
146
147    impl NixDeserialize for Signature<String> {
148        async fn try_deserialize<R>(reader: &mut R) -> Result<Option<Self>, R::Error>
149        where
150            R: ?Sized + NixRead + Send,
151        {
152            use crate::wire::de::Error;
153            let value: Option<String> = reader.try_read_value().await?;
154            match value {
155                Some(value) => Ok(Some(
156                    Signature::<String>::parse(&value).map_err(R::Error::invalid_data)?,
157                )),
158                None => Ok(None),
159            }
160        }
161    }
162}
163
164mod nixhash {
165    use nix_compat_derive::nix_serde_remote;
166
167    use crate::nixhash::{CAHash, HashAlgo};
168    use crate::wire::de::{NixDeserialize, NixRead};
169    use crate::wire::ser::{NixSerialize, NixWrite};
170
171    nix_serde_remote!(
172        #[nix(display, from_str)]
173        HashAlgo
174    );
175
176    impl NixSerialize for CAHash {
177        async fn serialize<W>(&self, writer: &mut W) -> Result<(), W::Error>
178        where
179            W: NixWrite,
180        {
181            writer.write_value(&self.to_string()).await
182        }
183    }
184
185    impl NixDeserialize for CAHash {
186        async fn try_deserialize<R>(reader: &mut R) -> Result<Option<Self>, R::Error>
187        where
188            R: ?Sized + NixRead + Send,
189        {
190            use crate::wire::de::Error;
191            let value: Option<String> = reader.try_read_value().await?;
192            match value {
193                Some(value) => Ok(Some(CAHash::from_nix_hex_str(&value).ok_or_else(|| {
194                    R::Error::invalid_data(format!("Invalid cahash {value}"))
195                })?)),
196                None => Ok(None),
197            }
198        }
199    }
200
201    impl NixSerialize for Option<CAHash> {
202        async fn serialize<W>(&self, writer: &mut W) -> Result<(), W::Error>
203        where
204            W: NixWrite,
205        {
206            match self {
207                Some(value) => writer.write_value(value).await,
208                None => writer.write_value("").await,
209            }
210        }
211    }
212
213    impl NixDeserialize for Option<CAHash> {
214        async fn try_deserialize<R>(reader: &mut R) -> Result<Option<Self>, R::Error>
215        where
216            R: ?Sized + NixRead + Send,
217        {
218            use crate::wire::de::Error;
219            let value: Option<String> = reader.try_read_value().await?;
220            match value {
221                Some(value) => {
222                    if value.is_empty() {
223                        Ok(None)
224                    } else {
225                        Ok(Some(Some(CAHash::from_nix_hex_str(&value).ok_or_else(
226                            || R::Error::invalid_data(format!("Invalid cahash {value}")),
227                        )?)))
228                    }
229                }
230                None => Ok(None),
231            }
232        }
233    }
234}
235
236mod realisation {
237    use nix_compat_derive::nix_serde_remote;
238
239    use crate::realisation::{DrvOutput, Realisation};
240    use crate::wire::de::NixDeserialize;
241    use crate::wire::ser::NixSerialize;
242
243    nix_serde_remote!(
244        #[nix(from_str, display)]
245        DrvOutput
246    );
247
248    impl NixSerialize for Realisation {
249        async fn serialize<W>(&self, writer: &mut W) -> Result<(), W::Error>
250        where
251            W: crate::wire::ser::NixWrite,
252        {
253            use crate::wire::ser::Error;
254            let s = serde_json::to_string(&self).map_err(W::Error::custom)?;
255            writer.write_slice(s.as_bytes()).await
256        }
257    }
258
259    impl NixDeserialize for Realisation {
260        async fn try_deserialize<R>(reader: &mut R) -> Result<Option<Self>, R::Error>
261        where
262            R: ?Sized + crate::wire::de::NixRead + Send,
263        {
264            use crate::wire::de::Error;
265            if let Some(buf) = reader.try_read_bytes().await? {
266                Ok(Some(
267                    serde_json::from_slice(&buf).map_err(R::Error::custom)?,
268                ))
269            } else {
270                Ok(None)
271            }
272        }
273    }
274
275    #[cfg(test)]
276    mod tests {
277        use rstest::rstest;
278
279        use crate::{btree_map, hash_set, realisation::Realisation};
280
281        #[tokio::test]
282        #[rstest]
283        #[case(
284            Realisation {
285                id: "sha256:ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad!out".parse().unwrap(),
286                out_path: "7h7qgvs4kgzsn8a6rb273saxyqh4jxlz-konsole-18.12.3".parse().unwrap(),
287                signatures: hash_set!["cache.nixos.org-1:0CpHca+06TwFp9VkMyz5OaphT3E8mnS+1SWymYlvFaghKSYPCMQ66TS1XPAr1+y9rfQZPLaHrBjjnIRktE/nAA=="],
288                dependent_realisations: btree_map![
289                    "sha256:ba7816bf8f01cfea414140de5dae2223b00361a496177a9cf410ff61f20015ad!dev" => "7h7qgvs4kgzsn8a6rb273saxyqh4jxlz-konsole-18.12.3-dev",
290                    "sha256:ba7816bf8f01cfea414140de5dae2223b00361a696177a9cf410ff61f20015ad!bin" => "7h7qgvs4kgzsn8a6rb273saxyqh4jxlz-konsole-18.12.3-bin",
291
292                ],
293            },
294            "{\"id\":\"sha256:ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad!out\",\"outPath\":\"7h7qgvs4kgzsn8a6rb273saxyqh4jxlz-konsole-18.12.3\",\"signatures\":[\"cache.nixos.org-1:0CpHca+06TwFp9VkMyz5OaphT3E8mnS+1SWymYlvFaghKSYPCMQ66TS1XPAr1+y9rfQZPLaHrBjjnIRktE/nAA==\"],\"dependentRealisations\":{\"sha256:ba7816bf8f01cfea414140de5dae2223b00361a496177a9cf410ff61f20015ad!dev\":\"7h7qgvs4kgzsn8a6rb273saxyqh4jxlz-konsole-18.12.3-dev\",\"sha256:ba7816bf8f01cfea414140de5dae2223b00361a696177a9cf410ff61f20015ad!bin\":\"7h7qgvs4kgzsn8a6rb273saxyqh4jxlz-konsole-18.12.3-bin\"}}",
295        )]
296        async fn nix_write_realisation(#[case] value: Realisation, #[case] expected: &str) {
297            use crate::wire::ser::NixWrite as _;
298
299            let mut mock = crate::test::wire::ser::Builder::new()
300                .write_slice(expected.as_bytes())
301                .build();
302            mock.write_value(&value).await.unwrap();
303        }
304
305        #[tokio::test]
306        #[rstest]
307        #[case(
308            Realisation {
309                id: "sha256:ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad!out".parse().unwrap(),
310                out_path: "7h7qgvs4kgzsn8a6rb273saxyqh4jxlz-konsole-18.12.3".parse().unwrap(),
311                signatures: hash_set!["cache.nixos.org-1:0CpHca+06TwFp9VkMyz5OaphT3E8mnS+1SWymYlvFaghKSYPCMQ66TS1XPAr1+y9rfQZPLaHrBjjnIRktE/nAA=="],
312                dependent_realisations: btree_map![
313                    "sha256:ba7816bf8f01cfea414140de5dae2223b00361a496177a9cf410ff61f20015ad!dev" => "7h7qgvs4kgzsn8a6rb273saxyqh4jxlz-konsole-18.12.3-dev",
314                    "sha256:ba7816bf8f01cfea414140de5dae2223b00361a696177a9cf410ff61f20015ad!bin" => "7h7qgvs4kgzsn8a6rb273saxyqh4jxlz-konsole-18.12.3-bin",
315
316                ],
317            },
318            "{\"id\":\"sha256:ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad!out\",\"outPath\":\"7h7qgvs4kgzsn8a6rb273saxyqh4jxlz-konsole-18.12.3\",\"signatures\":[\"cache.nixos.org-1:0CpHca+06TwFp9VkMyz5OaphT3E8mnS+1SWymYlvFaghKSYPCMQ66TS1XPAr1+y9rfQZPLaHrBjjnIRktE/nAA==\"],\"dependentRealisations\":{\"sha256:ba7816bf8f01cfea414140de5dae2223b00361a496177a9cf410ff61f20015ad!dev\":\"7h7qgvs4kgzsn8a6rb273saxyqh4jxlz-konsole-18.12.3-dev\",\"sha256:ba7816bf8f01cfea414140de5dae2223b00361a696177a9cf410ff61f20015ad!bin\":\"7h7qgvs4kgzsn8a6rb273saxyqh4jxlz-konsole-18.12.3-bin\"}}",
319        )]
320        async fn nix_read_realisation(#[case] expected: Realisation, #[case] value: &str) {
321            use crate::wire::de::NixRead as _;
322
323            let mut mock = crate::test::wire::de::Builder::new()
324                .read_slice(value.as_bytes())
325                .build();
326            let actual: Realisation = mock.read_value().await.unwrap();
327            pretty_assertions::assert_eq!(actual, expected);
328        }
329    }
330}
331
332mod store_path {
333    use crate::store_path::StorePath;
334    use crate::wire::de::{NixDeserialize, NixRead};
335    use crate::wire::ser::{NixSerialize, NixWrite};
336
337    // Custom implementation since FromStr does not use from_absolute_path
338    impl NixDeserialize for StorePath {
339        async fn try_deserialize<R>(reader: &mut R) -> Result<Option<Self>, R::Error>
340        where
341            R: ?Sized + NixRead + Send,
342        {
343            use crate::wire::de::Error;
344            if let Some(buf) = reader.try_read_bytes().await? {
345                let result = StorePath::from_absolute_path(&buf);
346                result.map(Some).map_err(R::Error::invalid_data)
347            } else {
348                Ok(None)
349            }
350        }
351    }
352
353    // Custom implementation since Display does not use absolute paths.
354    impl NixSerialize for StorePath {
355        fn serialize<W>(&self, writer: &mut W) -> impl Future<Output = Result<(), W::Error>> + Send
356        where
357            W: NixWrite,
358        {
359            let sp = self.as_absolute_path_fmt();
360            async move { writer.write_display(&sp).await }
361        }
362    }
363
364    impl NixDeserialize for Option<StorePath> {
365        async fn try_deserialize<R>(reader: &mut R) -> Result<Option<Self>, R::Error>
366        where
367            R: ?Sized + NixRead + Send,
368        {
369            use crate::wire::de::Error;
370            if let Some(buf) = reader.try_read_bytes().await? {
371                if buf.is_empty() {
372                    Ok(Some(None))
373                } else {
374                    let result = StorePath::from_absolute_path(&buf);
375                    result
376                        .map(|r| Some(Some(r)))
377                        .map_err(R::Error::invalid_data)
378                }
379            } else {
380                Ok(Some(None))
381            }
382        }
383    }
384
385    // Writes StorePath or an empty string.
386    impl NixSerialize for Option<StorePath> {
387        async fn serialize<W>(&self, writer: &mut W) -> Result<(), W::Error>
388        where
389            W: NixWrite,
390        {
391            match self {
392                Some(value) => writer.write_value(value).await,
393                None => writer.write_value("").await,
394            }
395        }
396    }
397}