From 6187029077dee3ee96a007f7125310e9c18b53f0 Mon Sep 17 00:00:00 2001 From: Brian Olsen Date: Tue, 5 Aug 2025 21:18:01 +0200 Subject: [PATCH] feat(nix-compat): Add serde feature flag This makes serde features optional behind a flag that is not enabled by default. So Deserialize and Serialize implementations and anything that deals with JSON. Change-Id: I04830aa3883da13ea99a4a51b28981e8a5ecd426 Reviewed-on: https://cl.snix.dev/c/snix/+/30660 Autosubmit: Brian Olsen Reviewed-by: Florian Klink Tested-by: besadii --- contrib/crunch-v2/Cargo.lock | 2 - contrib/crunch-v2/Cargo.nix | 10 +-- contrib/fetchroots/Cargo.lock | 2 - contrib/fetchroots/Cargo.nix | 10 +-- contrib/narinfo2parquet/Cargo.lock | 2 - contrib/narinfo2parquet/Cargo.nix | 10 +-- contrib/weave/Cargo.lock | 2 - contrib/weave/Cargo.nix | 10 +-- snix/Cargo.nix | 9 ++- snix/nix-compat/Cargo.toml | 10 ++- snix/nix-compat/src/derivation/mod.rs | 12 +-- snix/nix-compat/src/derivation/output.rs | 17 +++- snix/nix-compat/src/derivation/tests/mod.rs | 17 ++++ snix/nix-compat/src/log/mod.rs | 88 ++++++++++----------- snix/nix-compat/src/nar/mod.rs | 1 + snix/nix-compat/src/narinfo/signature.rs | 5 ++ snix/nix-compat/src/nixhash/algos.rs | 4 + snix/nix-compat/src/nixhash/ca_hash.rs | 28 +++++-- snix/nix-compat/src/nixhash/mod.rs | 2 + snix/nix-compat/src/path_info.rs | 42 +++++++--- snix/nix-compat/src/store_path/mod.rs | 12 ++- snix/store/Cargo.toml | 2 +- 22 files changed, 176 insertions(+), 121 deletions(-) diff --git a/contrib/crunch-v2/Cargo.lock b/contrib/crunch-v2/Cargo.lock index 4f35d3d3c..d7a934672 100644 --- a/contrib/crunch-v2/Cargo.lock +++ b/contrib/crunch-v2/Cargo.lock @@ -1427,8 +1427,6 @@ dependencies = [ "num-traits", "num_enum", "pin-project-lite", - "serde", - "serde_json", "sha2 0.10.8", "thiserror 2.0.11", "tokio", diff --git a/contrib/crunch-v2/Cargo.nix b/contrib/crunch-v2/Cargo.nix index 615f9f7cf..66fc29e8a 100644 --- a/contrib/crunch-v2/Cargo.nix +++ b/contrib/crunch-v2/Cargo.nix @@ -4140,15 +4140,6 @@ rec { packageId = "pin-project-lite"; optional = true; } - { - name = "serde"; - packageId = "serde"; - features = [ "derive" ]; - } - { - name = "serde_json"; - packageId = "serde_json"; - } { name = "sha2"; packageId = "sha2 0.10.8"; @@ -4187,6 +4178,7 @@ rec { "futures" = [ "dep:futures" ]; "nix-compat-derive" = [ "dep:nix-compat-derive" ]; "pin-project-lite" = [ "dep:pin-project-lite" ]; + "serde" = [ "dep:serde" "dep:serde_json" ]; "tokio" = [ "dep:tokio" ]; "url" = [ "dep:url" ]; "wire" = [ "tokio" "pin-project-lite" "bytes" ]; diff --git a/contrib/fetchroots/Cargo.lock b/contrib/fetchroots/Cargo.lock index d84ee4280..1186032d6 100644 --- a/contrib/fetchroots/Cargo.lock +++ b/contrib/fetchroots/Cargo.lock @@ -1708,8 +1708,6 @@ dependencies = [ "num-traits", "num_enum", "pin-project-lite", - "serde", - "serde_json", "sha2", "thiserror 2.0.11", "tokio", diff --git a/contrib/fetchroots/Cargo.nix b/contrib/fetchroots/Cargo.nix index e6dc82765..6e245d6e7 100644 --- a/contrib/fetchroots/Cargo.nix +++ b/contrib/fetchroots/Cargo.nix @@ -5573,15 +5573,6 @@ rec { packageId = "pin-project-lite"; optional = true; } - { - name = "serde"; - packageId = "serde"; - features = [ "derive" ]; - } - { - name = "serde_json"; - packageId = "serde_json"; - } { name = "sha2"; packageId = "sha2"; @@ -5620,6 +5611,7 @@ rec { "futures" = [ "dep:futures" ]; "nix-compat-derive" = [ "dep:nix-compat-derive" ]; "pin-project-lite" = [ "dep:pin-project-lite" ]; + "serde" = [ "dep:serde" "dep:serde_json" ]; "tokio" = [ "dep:tokio" ]; "url" = [ "dep:url" ]; "wire" = [ "tokio" "pin-project-lite" "bytes" ]; diff --git a/contrib/narinfo2parquet/Cargo.lock b/contrib/narinfo2parquet/Cargo.lock index dc8a55d9c..f9715421e 100644 --- a/contrib/narinfo2parquet/Cargo.lock +++ b/contrib/narinfo2parquet/Cargo.lock @@ -971,8 +971,6 @@ dependencies = [ "num-traits", "num_enum", "pin-project-lite", - "serde", - "serde_json", "sha2", "thiserror 2.0.11", "tokio", diff --git a/contrib/narinfo2parquet/Cargo.nix b/contrib/narinfo2parquet/Cargo.nix index 71b504ee6..b7f67b647 100644 --- a/contrib/narinfo2parquet/Cargo.nix +++ b/contrib/narinfo2parquet/Cargo.nix @@ -2867,15 +2867,6 @@ rec { packageId = "pin-project-lite"; optional = true; } - { - name = "serde"; - packageId = "serde"; - features = [ "derive" ]; - } - { - name = "serde_json"; - packageId = "serde_json"; - } { name = "sha2"; packageId = "sha2"; @@ -2914,6 +2905,7 @@ rec { "futures" = [ "dep:futures" ]; "nix-compat-derive" = [ "dep:nix-compat-derive" ]; "pin-project-lite" = [ "dep:pin-project-lite" ]; + "serde" = [ "dep:serde" "dep:serde_json" ]; "tokio" = [ "dep:tokio" ]; "url" = [ "dep:url" ]; "wire" = [ "tokio" "pin-project-lite" "bytes" ]; diff --git a/contrib/weave/Cargo.lock b/contrib/weave/Cargo.lock index 01c0cee3e..c7d4f8d64 100644 --- a/contrib/weave/Cargo.lock +++ b/contrib/weave/Cargo.lock @@ -1010,8 +1010,6 @@ dependencies = [ "num-traits", "num_enum", "pin-project-lite", - "serde", - "serde_json", "sha2", "thiserror 2.0.11", "tokio", diff --git a/contrib/weave/Cargo.nix b/contrib/weave/Cargo.nix index b877c1b86..77f93fb3c 100644 --- a/contrib/weave/Cargo.nix +++ b/contrib/weave/Cargo.nix @@ -2949,15 +2949,6 @@ rec { packageId = "pin-project-lite"; optional = true; } - { - name = "serde"; - packageId = "serde"; - features = [ "derive" ]; - } - { - name = "serde_json"; - packageId = "serde_json"; - } { name = "sha2"; packageId = "sha2"; @@ -2996,6 +2987,7 @@ rec { "futures" = [ "dep:futures" ]; "nix-compat-derive" = [ "dep:nix-compat-derive" ]; "pin-project-lite" = [ "dep:pin-project-lite" ]; + "serde" = [ "dep:serde" "dep:serde_json" ]; "tokio" = [ "dep:tokio" ]; "url" = [ "dep:url" ]; "wire" = [ "tokio" "pin-project-lite" "bytes" ]; diff --git a/snix/Cargo.nix b/snix/Cargo.nix index de3c14421..cae6bbefc 100644 --- a/snix/Cargo.nix +++ b/snix/Cargo.nix @@ -8218,7 +8218,7 @@ rec { { name = "drvfmt"; path = "src/bin/drvfmt.rs"; - requiredFeatures = [ ]; + requiredFeatures = [ "serde" ]; } ]; src = lib.cleanSourceWith { filter = sourceFilter; src = ./nix-compat; }; @@ -8292,11 +8292,13 @@ rec { { name = "serde"; packageId = "serde"; + optional = true; features = [ "derive" ]; } { name = "serde_json"; packageId = "serde_json"; + optional = true; } { name = "sha2"; @@ -8381,11 +8383,12 @@ rec { "futures" = [ "dep:futures" ]; "nix-compat-derive" = [ "dep:nix-compat-derive" ]; "pin-project-lite" = [ "dep:pin-project-lite" ]; + "serde" = [ "dep:serde" "dep:serde_json" ]; "tokio" = [ "dep:tokio" ]; "url" = [ "dep:url" ]; "wire" = [ "tokio" "pin-project-lite" "bytes" ]; }; - resolvedDefaultFeatures = [ "async" "bytes" "daemon" "default" "flakeref" "futures" "nix-compat-derive" "pin-project-lite" "test" "tokio" "url" "wire" ]; + resolvedDefaultFeatures = [ "async" "bytes" "daemon" "default" "flakeref" "futures" "nix-compat-derive" "pin-project-lite" "serde" "test" "tokio" "url" "wire" ]; }; "nix-compat-derive" = rec { crateName = "nix-compat-derive"; @@ -14636,7 +14639,7 @@ rec { { name = "nix-compat"; packageId = "nix-compat"; - features = [ "async" ]; + features = [ "async" "serde" ]; } { name = "parking_lot"; diff --git a/snix/nix-compat/Cargo.toml b/snix/nix-compat/Cargo.toml index dbbb8de15..56c001a36 100644 --- a/snix/nix-compat/Cargo.toml +++ b/snix/nix-compat/Cargo.toml @@ -3,6 +3,10 @@ name = "nix-compat" version = "0.1.0" edition = "2024" +[[bin]] +name = "drvfmt" +required-features = ["serde"] + [features] # async NAR writer. Also needs the `wire` feature. async = ["tokio"] @@ -11,6 +15,8 @@ wire = ["tokio", "pin-project-lite", "bytes"] flakeref = ["url"] # nix-daemon protocol handling daemon = ["tokio", "nix-compat-derive", "futures"] +# serde support on types +serde = ["dep:serde", "dep:serde_json"] test = [] # Enable all features by default. @@ -28,8 +34,8 @@ glob.workspace = true mimalloc.workspace = true nom.workspace = true num-traits.workspace = true -serde = { workspace = true, features = ["derive"] } -serde_json.workspace = true +serde = { workspace = true, features = ["derive"], optional = true } +serde_json = { workspace = true, optional = true } sha2.workspace = true thiserror.workspace = true tracing.workspace = true diff --git a/snix/nix-compat/src/derivation/mod.rs b/snix/nix-compat/src/derivation/mod.rs index 8d2311e4a..5944d7b28 100644 --- a/snix/nix-compat/src/derivation/mod.rs +++ b/snix/nix-compat/src/derivation/mod.rs @@ -2,6 +2,7 @@ use crate::store_path::{ self, StorePath, StorePathRef, build_ca_path, build_output_path, build_text_path, }; use bstr::BString; +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; use std::collections::{BTreeMap, BTreeSet}; @@ -26,22 +27,23 @@ pub use validate::validate_output_name; use self::write::AtermWriteable; -#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Debug, Default, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Derivation { - #[serde(rename = "args")] + #[cfg_attr(feature = "serde", serde(rename = "args"))] pub arguments: Vec, pub builder: String, - #[serde(rename = "env")] + #[cfg_attr(feature = "serde", serde(rename = "env"))] pub environment: BTreeMap, /// Map from drv path to output names used from this derivation. - #[serde(rename = "inputDrvs")] + #[cfg_attr(feature = "serde", serde(rename = "inputDrvs"))] pub input_derivations: BTreeMap, BTreeSet>, /// Plain store paths of additional inputs. - #[serde(rename = "inputSrcs")] + #[cfg_attr(feature = "serde", serde(rename = "inputSrcs"))] pub input_sources: BTreeSet>, /// Maps output names to Output. diff --git a/snix/nix-compat/src/derivation/output.rs b/snix/nix-compat/src/derivation/output.rs index 0b81ef3c3..eecf154b3 100644 --- a/snix/nix-compat/src/derivation/output.rs +++ b/snix/nix-compat/src/derivation/output.rs @@ -1,20 +1,25 @@ use crate::nixhash::CAHash; use crate::{derivation::OutputError, store_path::StorePath}; +#[cfg(feature = "serde")] use serde::de::Unexpected; +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +#[cfg(feature = "serde")] use serde_json::Map; use std::borrow::Cow; /// References the derivation output. -#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize)] +#[derive(Clone, Debug, Default, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize))] pub struct Output { /// Store path of build result. pub path: Option>, - #[serde(flatten)] + #[cfg_attr(feature = "serde", serde(flatten))] pub ca_hash: Option, // we can only represent a subset here. } +#[cfg(feature = "serde")] impl<'de> Deserialize<'de> for Output { fn deserialize(deserializer: D) -> Result where @@ -73,6 +78,7 @@ impl Output { /// This ensures that a potentially valid input addressed /// output is deserialized as a non-fixed output. +#[cfg(feature = "serde")] #[test] fn deserialize_valid_input_addressed_output() { let json_bytes = r#" @@ -86,6 +92,7 @@ fn deserialize_valid_input_addressed_output() { /// This ensures that a potentially valid fixed output /// output deserializes fine as a fixed output. +#[cfg(feature = "serde")] #[test] fn deserialize_valid_fixed_output() { let json_bytes = r#" @@ -101,6 +108,7 @@ fn deserialize_valid_fixed_output() { /// This ensures that parsing an input with the invalid hash encoding /// will result in a parsing failure. +#[cfg(feature = "serde")] #[test] fn deserialize_with_error_invalid_hash_encoding_fixed_output() { let json_bytes = r#" @@ -116,6 +124,7 @@ fn deserialize_with_error_invalid_hash_encoding_fixed_output() { /// This ensures that parsing an input with the wrong hash algo /// will result in a parsing failure. +#[cfg(feature = "serde")] #[test] fn deserialize_with_error_invalid_hash_algo_fixed_output() { let json_bytes = r#" @@ -131,6 +140,7 @@ fn deserialize_with_error_invalid_hash_algo_fixed_output() { /// This ensures that parsing an input with the missing hash algo but present hash will result in a /// parsing failure. +#[cfg(feature = "serde")] #[test] fn deserialize_with_error_missing_hash_algo_fixed_output() { let json_bytes = r#" @@ -145,6 +155,7 @@ fn deserialize_with_error_missing_hash_algo_fixed_output() { /// This ensures that parsing an input with the missing hash but present hash algo will result in a /// parsing failure. +#[cfg(feature = "serde")] #[test] fn deserialize_with_error_missing_hash_fixed_output() { let json_bytes = r#" @@ -157,6 +168,7 @@ fn deserialize_with_error_missing_hash_fixed_output() { assert!(output.is_err()); } +#[cfg(feature = "serde")] #[test] fn serialize_deserialize() { let json_bytes = r#" @@ -171,6 +183,7 @@ fn serialize_deserialize() { assert_eq!(output, output2); } +#[cfg(feature = "serde")] #[test] fn serialize_deserialize_fixed() { let json_bytes = r#" diff --git a/snix/nix-compat/src/derivation/tests/mod.rs b/snix/nix-compat/src/derivation/tests/mod.rs index fa933bfd4..9b3fe7ee8 100644 --- a/snix/nix-compat/src/derivation/tests/mod.rs +++ b/snix/nix-compat/src/derivation/tests/mod.rs @@ -1,19 +1,28 @@ use super::parse_error::ErrorKind; use crate::derivation::Derivation; +#[cfg(feature = "serde")] use crate::derivation::output::Output; use crate::derivation::parse_error::NomError; use crate::derivation::parser::Error; +#[cfg(feature = "serde")] use crate::store_path::StorePath; +#[cfg(feature = "serde")] use bstr::{BStr, BString}; +#[cfg(feature = "serde")] use hex_literal::hex; +#[cfg(feature = "serde")] use rstest::rstest; +#[cfg(feature = "serde")] use std::collections::BTreeSet; use std::fs; +#[cfg(feature = "serde")] use std::path::{Path, PathBuf}; +#[cfg(feature = "serde")] use std::str::FromStr; const RESOURCES_PATHS: &str = "src/derivation/tests/derivation_tests"; +#[cfg(feature = "serde")] #[rstest] fn check_serialization( #[files("src/derivation/tests/derivation_tests/ok/*.drv")] @@ -33,6 +42,7 @@ fn check_serialization( assert_eq!(expected, BStr::new(&serialized_derivation)); } +#[cfg(feature = "serde")] #[rstest] fn validate( #[files("src/derivation/tests/derivation_tests/ok/*.drv")] @@ -49,6 +59,7 @@ fn validate( .expect("derivation failed to validate") } +#[cfg(feature = "serde")] #[rstest] fn check_to_aterm_bytes( #[files("src/derivation/tests/derivation_tests/ok/*.drv")] @@ -68,6 +79,7 @@ fn check_to_aterm_bytes( /// Reads in derivations in ATerm representation, parses with that parser, /// then compares the structs with the ones obtained by parsing the JSON /// representations. +#[cfg(feature = "serde")] #[rstest] fn from_aterm_bytes( #[files("src/derivation/tests/derivation_tests/ok/*.drv")] path_to_drv_file: PathBuf, @@ -139,6 +151,7 @@ fn from_aterm_bytes_trailer() { Derivation::from_aterm_bytes(&buf).expect_err("must fail"); } +#[cfg(feature = "serde")] #[rstest] #[case::fixed_sha256("bar", "0hm2f1psjpcwg8fijsmr4wwxrx59s092-bar.drv")] #[case::simple_sha256("foo", "4wvvbi4jwn0prsdxb7vs673qa5h9gr7x-foo.drv")] @@ -164,6 +177,7 @@ fn derivation_path(#[case] name: &str, #[case] expected_path: &str) { /// This trims all output paths from a Derivation struct, /// by setting outputs[$outputName].path and environment[$outputName] to the empty string. +#[cfg(feature = "serde")] fn derivation_without_output_paths(derivation: &Derivation) -> Derivation { let mut trimmed_env = derivation.environment.clone(); let mut trimmed_outputs = derivation.outputs.clone(); @@ -188,6 +202,7 @@ fn derivation_without_output_paths(derivation: &Derivation) -> Derivation { } } +#[cfg(feature = "serde")] #[rstest] #[case::fixed_sha256("0hm2f1psjpcwg8fijsmr4wwxrx59s092-bar.drv", hex!("724f3e3634fce4cbbbd3483287b8798588e80280660b9a63fd13a1bc90485b33"))] #[case::fixed_sha1("ss2p4wmxijn652haqyd7dckxwl4c7hxx-bar.drv", hex!("c79aebd0ce3269393d4a1fde2cbd1d975d879b40f0bf40a48f550edc107fd5df"))] @@ -204,6 +219,7 @@ fn hash_derivation_modulo_fixed(#[case] drv_path: &str, #[case] expected_digest: /// This reads a Derivation (in A-Term), trims out all fields containing /// calculated output paths, then triggers the output path calculation and /// compares the struct to match what was originally read in. +#[cfg(feature = "serde")] #[rstest] #[case::fixed_sha256("bar", "0hm2f1psjpcwg8fijsmr4wwxrx59s092-bar.drv")] #[case::simple_sha256("foo", "4wvvbi4jwn0prsdxb7vs673qa5h9gr7x-foo.drv")] @@ -298,6 +314,7 @@ fn output_paths(#[case] name: &str, #[case] drv_path_str: &str) { /// it, then continues with the foo derivation. /// /// The code ensures the resulting Derivations match our fixtures. +#[cfg(feature = "serde")] #[test] fn output_path_construction() { // create the bar derivation diff --git a/snix/nix-compat/src/log/mod.rs b/snix/nix-compat/src/log/mod.rs index 45e780a4d..228a0f6e4 100644 --- a/snix/nix-compat/src/log/mod.rs +++ b/snix/nix-compat/src/log/mod.rs @@ -1,7 +1,9 @@ //! Contains types Nix uses for its logging, visible in the "internal-json" log //! messages as well as in nix-daemon communication. +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +#[cfg(feature = "serde")] use tracing::warn; /// Every "internal-json" log line emitted by Nix has this prefix. @@ -9,17 +11,13 @@ pub const AT_NIX_PREFIX: &str = "@nix "; /// The different verbosity levels Nix distinguishes. #[derive( - Clone, - Debug, - Eq, - PartialEq, - Serialize, - Deserialize, - num_enum::TryFromPrimitive, - num_enum::IntoPrimitive, - Default, + Clone, Debug, Eq, PartialEq, num_enum::TryFromPrimitive, num_enum::IntoPrimitive, Default, +)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(try_from = "u64", into = "u64") )] -#[serde(try_from = "u64", into = "u64")] #[cfg_attr( feature = "daemon", derive(nix_compat_derive::NixDeserialize, nix_compat_derive::NixSerialize), @@ -59,13 +57,14 @@ impl std::fmt::Display for VerbosityLevel { /// The different types of log messages Nix' `internal-json` format can /// represent. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(tag = "action" /*, deny_unknown_fields */)] +#[derive(Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", + derive(Serialize, Deserialize), + serde(tag = "action", rename_all = "camelCase" /*, deny_unknown_fields */))] // TODO: deny_unknown_fields doesn't seem to work in the testcases below pub enum LogMessage<'a> { - #[serde(rename = "start")] Start { - #[serde(skip_serializing_if = "Option::is_none")] + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] fields: Option>>, id: u64, level: VerbosityLevel, @@ -74,10 +73,10 @@ pub enum LogMessage<'a> { r#type: ActivityType, }, - #[serde(rename = "stop")] - Stop { id: u64 }, + Stop { + id: u64, + }, - #[serde(rename = "result")] Result { fields: Vec>, id: u64, @@ -86,7 +85,6 @@ pub enum LogMessage<'a> { // FUTUREWORK: there sometimes seems to be column/file/line fields set to null, and a raw_msg field, // see msg_with_raw_msg testcase. These should be represented. - #[serde(rename = "msg")] Msg { level: VerbosityLevel, msg: std::borrow::Cow<'a, str>, @@ -94,10 +92,12 @@ pub enum LogMessage<'a> { // Log lines like these are sent by nixpkgs stdenv, present in `nix log` outputs of individual builds. // They are also interpreted by Nix to re-emit [Self::Result]-style messages. - #[serde(rename = "setPhase")] - SetPhase { phase: &'a str }, + SetPhase { + phase: &'a str, + }, } +#[cfg(feature = "serde")] fn serialize_bytes_as_string(b: &[u8], serializer: S) -> Result where S: serde::Serializer, @@ -113,24 +113,22 @@ where /// Fields in a log message can be either ints or strings. /// Sometimes, Nix also uses invalid UTF-8 in here, so we use BStr. -#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] -#[serde(untagged)] +#[derive(Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(untagged))] pub enum Field<'a> { Int(u64), - String(#[serde(serialize_with = "serialize_bytes_as_string")] std::borrow::Cow<'a, [u8]>), + String( + #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_bytes_as_string"))] + std::borrow::Cow<'a, [u8]>, + ), } -#[derive( - Clone, - Debug, - Eq, - PartialEq, - Serialize, - Deserialize, - num_enum::TryFromPrimitive, - num_enum::IntoPrimitive, +#[derive(Clone, Debug, Eq, PartialEq, num_enum::TryFromPrimitive, num_enum::IntoPrimitive)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(try_from = "u8", into = "u8") )] -#[serde(try_from = "u8", into = "u8")] #[repr(u8)] pub enum ActivityType { Unknown = 0, @@ -174,17 +172,12 @@ impl std::fmt::Display for ActivityType { } } -#[derive( - Clone, - Debug, - Eq, - PartialEq, - Serialize, - Deserialize, - num_enum::TryFromPrimitive, - num_enum::IntoPrimitive, +#[derive(Clone, Debug, Eq, PartialEq, num_enum::TryFromPrimitive, num_enum::IntoPrimitive)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(try_from = "u8", into = "u8") )] -#[serde(try_from = "u8", into = "u8")] #[repr(u8)] pub enum ResultType { FileLinked = 100, @@ -200,6 +193,7 @@ pub enum ResultType { impl<'a> LogMessage<'a> { /// Parses a given log message string into a [LogMessage]. + #[cfg(feature = "serde")] pub fn from_json_str(s: &'a str) -> Result { let s = s.strip_prefix(AT_NIX_PREFIX).ok_or(Error::MissingPrefix)?; @@ -207,6 +201,7 @@ impl<'a> LogMessage<'a> { } } +#[cfg(feature = "serde")] impl std::fmt::Display for LogMessage<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( @@ -217,6 +212,7 @@ impl std::fmt::Display for LogMessage<'_> { } } +#[cfg(feature = "serde")] #[derive(Debug, thiserror::Error)] pub enum Error { #[error("Missing @nix prefix")] @@ -231,7 +227,10 @@ pub enum Error { // while it *is* compared. #[allow(unused_variables)] mod test { - use super::{ActivityType, Field, LogMessage, ResultType, VerbosityLevel}; + use super::VerbosityLevel; + #[cfg(feature = "serde")] + use super::{ActivityType, Field, LogMessage, ResultType}; + #[cfg(feature = "serde")] use rstest::rstest; #[test] @@ -247,6 +246,7 @@ mod test { VerbosityLevel::try_from(42).expect_err("must fail parsing"); } + #[cfg(feature = "serde")] #[rstest] #[case::start( r#"@nix {"action":"start","id":1264799149195466,"level":5,"parent":0,"text":"copying '/nix/store/rfqxfljma55x8ybmyg07crnarvqx62sr-nixpkgs-src/pkgs/development/compilers/llvm/18/llvm/lit-shell-script-runner-set-dyld-library-path.patch' to the store","type":0}"#, diff --git a/snix/nix-compat/src/nar/mod.rs b/snix/nix-compat/src/nar/mod.rs index 344c446f7..4a79cbdb7 100644 --- a/snix/nix-compat/src/nar/mod.rs +++ b/snix/nix-compat/src/nar/mod.rs @@ -1,6 +1,7 @@ pub(crate) mod wire; mod copy; +#[cfg(feature = "serde")] pub mod listing; pub mod reader; pub mod writer; diff --git a/snix/nix-compat/src/narinfo/signature.rs b/snix/nix-compat/src/narinfo/signature.rs index 862f0fc19..a42073410 100644 --- a/snix/nix-compat/src/narinfo/signature.rs +++ b/snix/nix-compat/src/narinfo/signature.rs @@ -4,6 +4,7 @@ use std::{ }; use data_encoding::BASE64; +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; const SIGNATURE_LENGTH: usize = std::mem::size_of::(); @@ -100,6 +101,7 @@ where } } +#[cfg(feature = "serde")] impl<'a, 'de, S> Deserialize<'de> for Signature where S: Deref + From<&'a str>, @@ -116,6 +118,7 @@ where } } +#[cfg(feature = "serde")] impl Serialize for Signature where S: Deref, @@ -165,6 +168,7 @@ pub enum Error { mod test { use data_encoding::BASE64; use ed25519_dalek::VerifyingKey; + #[cfg(feature = "serde")] use hex_literal::hex; use std::sync::LazyLock; @@ -221,6 +225,7 @@ mod test { Signature::<&str>::parse(input).expect_err("must fail"); } + #[cfg(feature = "serde")] #[test] fn serialize_deserialize() { let signature_actual = Signature { diff --git a/snix/nix-compat/src/nixhash/algos.rs b/snix/nix-compat/src/nixhash/algos.rs index 91410d3dc..435002f01 100644 --- a/snix/nix-compat/src/nixhash/algos.rs +++ b/snix/nix-compat/src/nixhash/algos.rs @@ -1,5 +1,6 @@ use std::fmt::Display; +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use crate::nixhash::Error; @@ -36,6 +37,7 @@ impl Display for HashAlgo { } } +#[cfg(feature = "serde")] impl Serialize for HashAlgo { fn serialize(&self, serializer: S) -> Result where @@ -45,6 +47,7 @@ impl Serialize for HashAlgo { } } +#[cfg(feature = "serde")] impl<'de> Deserialize<'de> for HashAlgo { fn deserialize(deserializer: D) -> Result where @@ -58,6 +61,7 @@ impl<'de> Deserialize<'de> for HashAlgo { /// TODO(Raito): this could be automated via macros, I suppose. /// But this may be more expensive than just doing it by hand /// and ensuring that is kept in sync. +#[cfg(feature = "serde")] pub const SUPPORTED_ALGOS: [&str; 4] = ["md5", "sha1", "sha256", "sha512"]; impl TryFrom<&str> for HashAlgo { diff --git a/snix/nix-compat/src/nixhash/ca_hash.rs b/snix/nix-compat/src/nixhash/ca_hash.rs index 0563c5fd9..088f7598a 100644 --- a/snix/nix-compat/src/nixhash/ca_hash.rs +++ b/snix/nix-compat/src/nixhash/ca_hash.rs @@ -1,14 +1,11 @@ use crate::nixbase32; -use crate::nixhash::{HashAlgo, NixHash}; -use serde::de::Unexpected; -use serde::ser::SerializeMap; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; +use crate::nixhash::NixHash; +#[cfg(feature = "serde")] +use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Unexpected, ser::SerializeMap}; +#[cfg(feature = "serde")] use serde_json::{Map, Value}; use std::borrow::Cow; -use super::algos::SUPPORTED_ALGOS; -use super::decode_digest; - /// A Nix CAHash describes a content-addressed hash of a path. /// /// The way Nix prints it as a string is a bit confusing, but there's essentially @@ -119,10 +116,15 @@ impl CAHash { /// /// This is to match how `nix show-derivation` command shows them in JSON /// representation. + #[cfg(feature = "serde")] pub(crate) fn from_map<'de, D>(map: &Map) -> Result, D::Error> where D: Deserializer<'de>, { + use super::algos::SUPPORTED_ALGOS; + use super::decode_digest; + use crate::nixhash::HashAlgo; + // If we don't have hash neither hashAlgo, let's just return None. if !map.contains_key("hash") && !map.contains_key("hashAlgo") { return Ok(None); @@ -166,6 +168,7 @@ impl CAHash { } } +#[cfg(feature = "serde")] impl Serialize for CAHash { /// map a CAHash into the serde data model. fn serialize(&self, serializer: S) -> Result @@ -194,6 +197,7 @@ impl Serialize for CAHash { } } +#[cfg(feature = "serde")] impl<'de> Deserialize<'de> for CAHash { fn deserialize(deserializer: D) -> Result where @@ -210,13 +214,16 @@ impl<'de> Deserialize<'de> for CAHash { #[cfg(test)] mod tests { + #[cfg(feature = "serde")] use hex_literal::hex; + #[cfg(feature = "serde")] use crate::{ derivation::CAHash, nixhash::{HashAlgo, NixHash}, }; + #[cfg(feature = "serde")] #[test] fn serialize_flat() { let json_bytes = r#"{ @@ -234,6 +241,7 @@ mod tests { assert_eq!(serialized, json_bytes); } + #[cfg(feature = "serde")] #[test] fn serialize_nar() { let json_bytes = r#"{ @@ -251,6 +259,7 @@ mod tests { assert_eq!(serialized, json_bytes); } + #[cfg(feature = "serde")] #[test] fn deserialize_flat() { let json_bytes = r#" @@ -272,6 +281,7 @@ mod tests { ); } + #[cfg(feature = "serde")] #[test] fn deserialize_hex() { let json_bytes = r#" @@ -293,6 +303,7 @@ mod tests { ); } + #[cfg(feature = "serde")] #[test] fn deserialize_nixbase32() { let json_bytes = r#" @@ -314,6 +325,7 @@ mod tests { ); } + #[cfg(feature = "serde")] #[test] fn deserialize_base64() { let json_bytes = r#" @@ -335,6 +347,7 @@ mod tests { ); } + #[cfg(feature = "serde")] #[test] fn serialize_deserialize_nar() { let json_bytes = r#" @@ -350,6 +363,7 @@ mod tests { assert_eq!(hash, hash2); } + #[cfg(feature = "serde")] #[test] fn serialize_deserialize_flat() { let json_bytes = r#" diff --git a/snix/nix-compat/src/nixhash/mod.rs b/snix/nix-compat/src/nixhash/mod.rs index 944d33c0a..4d1f5872e 100644 --- a/snix/nix-compat/src/nixhash/mod.rs +++ b/snix/nix-compat/src/nixhash/mod.rs @@ -7,6 +7,7 @@ use thiserror; mod algos; mod ca_hash; +#[cfg(feature = "serde")] pub mod serde; pub use algos::HashAlgo; @@ -537,6 +538,7 @@ mod tests { NixHash::from_str(weird_base64, Some(HashAlgo::Sha256)).expect_err("must fail"); } + #[cfg(feature = "serde")] #[test] fn serialize_deserialize() { let nixhash_actual = NixHash::Sha256(hex!( diff --git a/snix/nix-compat/src/path_info.rs b/snix/nix-compat/src/path_info.rs index 4a6934484..36daef33c 100644 --- a/snix/nix-compat/src/path_info.rs +++ b/snix/nix-compat/src/path_info.rs @@ -1,4 +1,7 @@ -use crate::{narinfo::SignatureRef, nixhash, store_path::StorePathRef}; +#[cfg(feature = "serde")] +use crate::nixhash; +use crate::{narinfo::SignatureRef, store_path::StorePathRef}; +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use std::collections::BTreeSet; @@ -7,26 +10,34 @@ use std::collections::BTreeSet; /// This is not to be confused with the format Nix uses in its `nix path-info` command. /// It includes some more fields, like `registrationTime`, `signatures` and `ultimate`, /// does not include the `closureSize` and encodes `narHash` as SRI. -#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +#[cfg_attr( + feature = "serde", + derive(Serialize, Deserialize), + serde(rename_all = "camelCase") +)] pub struct ExportedPathInfo<'a> { - #[serde(rename = "closureSize")] pub closure_size: u64, - #[serde( - rename = "narHash", - serialize_with = "nixhash::serde::to_nix_nixbase32", - deserialize_with = "nixhash::serde::from_nix_nixbase32_or_sri" + #[cfg_attr( + feature = "serde", + serde( + rename = "narHash", + serialize_with = "nixhash::serde::to_nix_nixbase32", + deserialize_with = "nixhash::serde::from_nix_nixbase32_or_sri" + ) )] pub nar_sha256: [u8; 32], - #[serde(rename = "narSize")] pub nar_size: u64, - #[serde(borrow)] + #[cfg_attr(feature = "serde", serde(borrow))] pub path: StorePathRef<'a>, - #[serde(borrow)] - #[serde(skip_serializing_if = "Option::is_none")] + #[cfg_attr( + feature = "serde", + serde(borrow, skip_serializing_if = "Option::is_none") + )] pub deriver: Option>, /// The list of other Store Paths this Store Path refers to. @@ -34,7 +45,10 @@ pub struct ExportedPathInfo<'a> { pub references: BTreeSet>, // more recent versions of Nix also have a `valid: true` field here, Nix 2.3 doesn't, // and nothing seems to use it. - #[serde(default, skip_serializing_if = "Vec::is_empty")] + #[cfg_attr( + feature = "serde", + serde(default, skip_serializing_if = "Vec::is_empty") + )] pub signatures: Vec>, } @@ -53,11 +67,14 @@ impl PartialOrd for ExportedPathInfo<'_> { #[cfg(test)] mod tests { + #[cfg(feature = "serde")] use hex_literal::hex; + #[cfg(feature = "serde")] use super::*; /// Ensure we can create the same JSON as the exportReferencesGraph feature + #[cfg(feature = "serde")] #[test] fn serialize_deserialize() { // JSON extracted from a build of @@ -95,6 +112,7 @@ mod tests { } /// Ensure we can parse output from `nix path-info --json`` + #[cfg(feature = "serde")] #[test] fn serialize_deserialize_from_path_info() { // JSON extracted from diff --git a/snix/nix-compat/src/store_path/mod.rs b/snix/nix-compat/src/store_path/mod.rs index 4a0210885..fc7c24a9f 100644 --- a/snix/nix-compat/src/store_path/mod.rs +++ b/snix/nix-compat/src/store_path/mod.rs @@ -1,5 +1,6 @@ use crate::nixbase32; use data_encoding::{BASE64, DecodeError}; +#[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use std::{ fmt, @@ -236,6 +237,7 @@ impl FromStr for StorePath { } } +#[cfg(feature = "serde")] impl<'a, 'de: 'a, S> Deserialize<'de> for StorePath where S: AsRef + From<&'a str>, @@ -258,6 +260,7 @@ where } } +#[cfg(feature = "serde")] impl Serialize for StorePath where S: AsRef, @@ -345,11 +348,13 @@ mod tests { use hex_literal::hex; use pretty_assertions::assert_eq; use rstest::rstest; + #[cfg(feature = "serde")] use serde::Deserialize; - #[derive(Deserialize)] /// An example struct, holding a StorePathRef. /// Used to test deserializing StorePathRef. + #[cfg(feature = "serde")] + #[derive(Deserialize)] struct Container<'a> { #[serde(borrow)] store_path: StorePathRef<'a>, @@ -492,6 +497,7 @@ mod tests { ); } + #[cfg(feature = "serde")] #[test] fn serialize_ref() { let nixpath_actual = StorePathRef::from_bytes( @@ -507,6 +513,7 @@ mod tests { ); } + #[cfg(feature = "serde")] #[test] fn serialize_owned() { let nixpath_actual = StorePathRef::from_bytes( @@ -522,6 +529,7 @@ mod tests { ); } + #[cfg(feature = "serde")] #[test] fn deserialize_ref() { let store_path_str_json = @@ -536,6 +544,7 @@ mod tests { ); } + #[cfg(feature = "serde")] #[test] fn deserialize_ref_container() { let str_json = "{\"store_path\":\"/nix/store/00bgd045z0d4icpbc2yyz4gx48ak44la-net-tools-1.60_p20170221182432\"}"; @@ -548,6 +557,7 @@ mod tests { ); } + #[cfg(feature = "serde")] #[test] fn deserialize_owned() { let store_path_str_json = diff --git a/snix/store/Cargo.toml b/snix/store/Cargo.toml index 1e5e46f4f..fbb36d8e3 100644 --- a/snix/store/Cargo.toml +++ b/snix/store/Cargo.toml @@ -22,7 +22,7 @@ data-encoding.workspace = true ed25519.workspace = true ed25519-dalek.workspace = true futures.workspace = true -nix-compat = { path = "../nix-compat", features = ["async"] } +nix-compat = { path = "../nix-compat", features = ["async", "serde"] } pin-project-lite.workspace = true prost.workspace = true serde = { workspace = true, features = ["derive"] }