ced05a2bb6
This adds a nix-compat-derive derive crate that implements a deriver for NixDeserialize implementations. This is to reduce the amount of code needed to implement deserialization for all the types used by the Nix daemon protocol. Change-Id: I484724b550e8a1d5e9adad9555d9dc1374ae95c2 Reviewed-on: https://cl.tvl.fyi/c/depot/+/12022 Autosubmit: Brian Olsen <me@griff.name> Tested-by: BuildkiteCI Reviewed-by: flokli <flokli@flokli.de>
417 lines
11 KiB
Rust
417 lines
11 KiB
Rust
use std::str::FromStr;
|
|
|
|
use nix_compat::nix_daemon::de::mock::{Builder, Error};
|
|
use nix_compat::nix_daemon::de::NixRead;
|
|
use nix_compat_derive::NixDeserialize;
|
|
|
|
#[derive(Debug, PartialEq, Eq, NixDeserialize)]
|
|
pub struct UnitTest;
|
|
|
|
#[derive(Debug, PartialEq, Eq, NixDeserialize)]
|
|
pub struct EmptyTupleTest();
|
|
|
|
#[derive(Debug, PartialEq, Eq, NixDeserialize)]
|
|
pub struct StructTest {
|
|
first: u64,
|
|
second: String,
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, NixDeserialize)]
|
|
pub struct TupleTest(u64, String);
|
|
|
|
#[derive(Debug, PartialEq, Eq, NixDeserialize)]
|
|
pub struct StructVersionTest {
|
|
test: u64,
|
|
#[nix(version = "20..")]
|
|
hello: String,
|
|
}
|
|
|
|
fn default_test() -> StructVersionTest {
|
|
StructVersionTest {
|
|
test: 89,
|
|
hello: String::from("klomp"),
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, NixDeserialize)]
|
|
pub struct TupleVersionTest(u64, #[nix(version = "25..")] String);
|
|
|
|
#[derive(Debug, PartialEq, Eq, NixDeserialize)]
|
|
pub struct TupleVersionDefaultTest(
|
|
u64,
|
|
#[nix(version = "..25", default = "default_test")] StructVersionTest,
|
|
);
|
|
|
|
#[tokio::test]
|
|
async fn read_unit() {
|
|
let mut mock = Builder::new().build();
|
|
let v: UnitTest = mock.read_value().await.unwrap();
|
|
assert_eq!(UnitTest, v);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_empty_tuple() {
|
|
let mut mock = Builder::new().build();
|
|
let v: EmptyTupleTest = mock.read_value().await.unwrap();
|
|
assert_eq!(EmptyTupleTest(), v);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_struct() {
|
|
let mut mock = Builder::new().read_number(89).read_slice(b"klomp").build();
|
|
let v: StructTest = mock.read_value().await.unwrap();
|
|
assert_eq!(
|
|
StructTest {
|
|
first: 89,
|
|
second: String::from("klomp"),
|
|
},
|
|
v
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_tuple() {
|
|
let mut mock = Builder::new().read_number(89).read_slice(b"klomp").build();
|
|
let v: TupleTest = mock.read_value().await.unwrap();
|
|
assert_eq!(TupleTest(89, String::from("klomp")), v);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_struct_version() {
|
|
let mut mock = Builder::new()
|
|
.version((1, 20))
|
|
.read_number(89)
|
|
.read_slice(b"klomp")
|
|
.build();
|
|
let v: StructVersionTest = mock.read_value().await.unwrap();
|
|
assert_eq!(default_test(), v);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_struct_without_version() {
|
|
let mut mock = Builder::new().version((1, 19)).read_number(89).build();
|
|
let v: StructVersionTest = mock.read_value().await.unwrap();
|
|
assert_eq!(
|
|
StructVersionTest {
|
|
test: 89,
|
|
hello: String::new(),
|
|
},
|
|
v
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_tuple_version() {
|
|
let mut mock = Builder::new()
|
|
.version((1, 26))
|
|
.read_number(89)
|
|
.read_slice(b"klomp")
|
|
.build();
|
|
let v: TupleVersionTest = mock.read_value().await.unwrap();
|
|
assert_eq!(TupleVersionTest(89, "klomp".into()), v);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_tuple_without_version() {
|
|
let mut mock = Builder::new().version((1, 19)).read_number(89).build();
|
|
let v: TupleVersionTest = mock.read_value().await.unwrap();
|
|
assert_eq!(TupleVersionTest(89, String::new()), v);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_complex_1() {
|
|
let mut mock = Builder::new()
|
|
.version((1, 19))
|
|
.read_number(999)
|
|
.read_number(666)
|
|
.build();
|
|
let v: TupleVersionDefaultTest = mock.read_value().await.unwrap();
|
|
assert_eq!(
|
|
TupleVersionDefaultTest(
|
|
999,
|
|
StructVersionTest {
|
|
test: 666,
|
|
hello: String::new()
|
|
}
|
|
),
|
|
v
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_complex_2() {
|
|
let mut mock = Builder::new()
|
|
.version((1, 20))
|
|
.read_number(999)
|
|
.read_number(666)
|
|
.read_slice(b"The quick brown \xF0\x9F\xA6\x8A jumps over 13 lazy \xF0\x9F\x90\xB6.")
|
|
.build();
|
|
let v: TupleVersionDefaultTest = mock.read_value().await.unwrap();
|
|
assert_eq!(
|
|
TupleVersionDefaultTest(
|
|
999,
|
|
StructVersionTest {
|
|
test: 666,
|
|
hello: String::from("The quick brown 🦊 jumps over 13 lazy 🐶.")
|
|
}
|
|
),
|
|
v
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_complex_3() {
|
|
let mut mock = Builder::new().version((1, 25)).read_number(999).build();
|
|
let v: TupleVersionDefaultTest = mock.read_value().await.unwrap();
|
|
assert_eq!(
|
|
TupleVersionDefaultTest(
|
|
999,
|
|
StructVersionTest {
|
|
test: 89,
|
|
hello: String::from("klomp")
|
|
}
|
|
),
|
|
v
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_complex_4() {
|
|
let mut mock = Builder::new().version((1, 26)).read_number(999).build();
|
|
let v: TupleVersionDefaultTest = mock.read_value().await.unwrap();
|
|
assert_eq!(
|
|
TupleVersionDefaultTest(
|
|
999,
|
|
StructVersionTest {
|
|
test: 89,
|
|
hello: String::from("klomp")
|
|
}
|
|
),
|
|
v
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_field_invalid_data() {
|
|
let mut mock = Builder::new()
|
|
.read_number(666)
|
|
.read_slice(b"The quick brown \xED\xA0\x80 jumped.")
|
|
.build();
|
|
let err = mock.read_value::<StructTest>().await.unwrap_err();
|
|
assert_eq!(
|
|
Error::InvalidData("invalid utf-8 sequence of 1 bytes from index 16".into()),
|
|
err
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_field_missing_data() {
|
|
let mut mock = Builder::new().read_number(666).build();
|
|
let err = mock.read_value::<StructTest>().await.unwrap_err();
|
|
assert_eq!(Error::MissingData("unexpected end-of-file".into()), err);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_field_no_data() {
|
|
let mut mock = Builder::new().build();
|
|
let err = mock.read_value::<StructTest>().await.unwrap_err();
|
|
assert_eq!(Error::MissingData("unexpected end-of-file".into()), err);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_field_reader_error_first() {
|
|
let mut mock = Builder::new()
|
|
.read_number_error(Error::InvalidData("Bad reader".into()))
|
|
.build();
|
|
let err = mock.read_value::<StructTest>().await.unwrap_err();
|
|
assert_eq!(Error::InvalidData("Bad reader".into()), err);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_field_reader_error_later() {
|
|
let mut mock = Builder::new()
|
|
.read_number(999)
|
|
.read_bytes_error(Error::InvalidData("Bad reader".into()))
|
|
.build();
|
|
let err = mock.read_value::<StructTest>().await.unwrap_err();
|
|
assert_eq!(Error::InvalidData("Bad reader".into()), err);
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, NixDeserialize)]
|
|
#[nix(from_str)]
|
|
struct TestFromStr;
|
|
|
|
impl FromStr for TestFromStr {
|
|
type Err = String;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
if s == "test" {
|
|
Ok(TestFromStr)
|
|
} else {
|
|
Err(s.into())
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_from_str() {
|
|
let mut mock = Builder::new().read_slice(b"test").build();
|
|
let value = mock.read_value::<TestFromStr>().await.unwrap();
|
|
assert_eq!(TestFromStr, value);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_from_str_invalid_data() {
|
|
let mut mock = Builder::new().read_slice(b"wrong string").build();
|
|
let err = mock.read_value::<TestFromStr>().await.unwrap_err();
|
|
assert_eq!(Error::InvalidData("wrong string".into()), err);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_from_str_invalid_string() {
|
|
let mut mock = Builder::new()
|
|
.read_slice(b"The quick brown \xED\xA0\x80 jumped.")
|
|
.build();
|
|
let err = mock.read_value::<TestFromStr>().await.unwrap_err();
|
|
assert_eq!(
|
|
Error::InvalidData("invalid utf-8 sequence of 1 bytes from index 16".into()),
|
|
err
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_from_str_reader_error() {
|
|
let mut mock = Builder::new()
|
|
.read_bytes_error(Error::InvalidData("Bad reader".into()))
|
|
.build();
|
|
let err = mock.read_value::<TestFromStr>().await.unwrap_err();
|
|
assert_eq!(Error::InvalidData("Bad reader".into()), err);
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, NixDeserialize)]
|
|
#[nix(try_from = "u64")]
|
|
struct TestTryFromU64;
|
|
|
|
impl TryFrom<u64> for TestTryFromU64 {
|
|
type Error = u64;
|
|
|
|
fn try_from(value: u64) -> Result<TestTryFromU64, Self::Error> {
|
|
if value == 42 {
|
|
Ok(TestTryFromU64)
|
|
} else {
|
|
Err(value)
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_try_from_u64() {
|
|
let mut mock = Builder::new().read_number(42).build();
|
|
let value = mock.read_value::<TestTryFromU64>().await.unwrap();
|
|
assert_eq!(TestTryFromU64, value);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_try_from_u64_invalid_data() {
|
|
let mut mock = Builder::new().read_number(666).build();
|
|
let err = mock.read_value::<TestTryFromU64>().await.unwrap_err();
|
|
assert_eq!(Error::InvalidData("666".into()), err);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_try_from_u64_reader_error() {
|
|
let mut mock = Builder::new()
|
|
.read_number_error(Error::InvalidData("Bad reader".into()))
|
|
.build();
|
|
let err = mock.read_value::<TestTryFromU64>().await.unwrap_err();
|
|
assert_eq!(Error::InvalidData("Bad reader".into()), err);
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, NixDeserialize)]
|
|
#[nix(from = "u64")]
|
|
struct TestFromU64;
|
|
|
|
impl From<u64> for TestFromU64 {
|
|
fn from(_value: u64) -> TestFromU64 {
|
|
TestFromU64
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_from_u64() {
|
|
let mut mock = Builder::new().read_number(42).build();
|
|
let value = mock.read_value::<TestFromU64>().await.unwrap();
|
|
assert_eq!(TestFromU64, value);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_from_u64_reader_error() {
|
|
let mut mock = Builder::new()
|
|
.read_number_error(Error::InvalidData("Bad reader".into()))
|
|
.build();
|
|
let err = mock.read_value::<TestFromU64>().await.unwrap_err();
|
|
assert_eq!(Error::InvalidData("Bad reader".into()), err);
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, NixDeserialize)]
|
|
enum TestEnum {
|
|
#[nix(version = "..=19")]
|
|
Pre20(TestTryFromU64),
|
|
#[nix(version = "20..")]
|
|
Post20(StructVersionTest),
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_enum_19() {
|
|
let mut mock = Builder::new().version((1, 19)).read_number(42).build();
|
|
let value = mock.read_value::<TestEnum>().await.unwrap();
|
|
assert_eq!(TestEnum::Pre20(TestTryFromU64), value);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_enum_20() {
|
|
let mut mock = Builder::new()
|
|
.version((1, 20))
|
|
.read_number(42)
|
|
.read_slice(b"klomp")
|
|
.build();
|
|
let value = mock.read_value::<TestEnum>().await.unwrap();
|
|
assert_eq!(
|
|
TestEnum::Post20(StructVersionTest {
|
|
test: 42,
|
|
hello: "klomp".into(),
|
|
}),
|
|
value
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_enum_reader_error() {
|
|
let mut mock = Builder::new()
|
|
.version((1, 19))
|
|
.read_number_error(Error::InvalidData("Bad reader".into()))
|
|
.build();
|
|
let err = mock.read_value::<TestEnum>().await.unwrap_err();
|
|
assert_eq!(Error::InvalidData("Bad reader".into()), err);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_enum_invalid_data_19() {
|
|
let mut mock = Builder::new().version((1, 19)).read_number(666).build();
|
|
let err = mock.read_value::<TestEnum>().await.unwrap_err();
|
|
assert_eq!(Error::InvalidData("666".into()), err);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn read_enum_invalid_data_20() {
|
|
let mut mock = Builder::new()
|
|
.version((1, 20))
|
|
.read_number(666)
|
|
.read_slice(b"The quick brown \xED\xA0\x80 jumped.")
|
|
.build();
|
|
let err = mock.read_value::<TestEnum>().await.unwrap_err();
|
|
assert_eq!(
|
|
Error::InvalidData("invalid utf-8 sequence of 1 bytes from index 16".into()),
|
|
err
|
|
);
|
|
}
|