ce687431f3
This commit implements the two serde traits for the libstd `OsStr` and `OsString` types. This came up as a use case during implementing sccache where we're basically just doing IPC to communicate paths around. Additionally the `Path` and `PathBuf` implementations have been updated to delegate to the os string ones. These types are platform-specific, however, so the serialization/deserialization isn't trivial. Currently this "fakes" a newtype variant for Unix/Windows to prevent cross-platform serialization/deserialization. This means if you're doing IPC within the same OS (e.g. Windows to Windows) then serialization should be infallible. If you're doing IPC across platforms (e.g. Unix to Windows) then using `OsString` is guaranteed to fail as bytes from one OS won't deserialize on the other (even if they're unicode).
463 lines
12 KiB
Rust
463 lines
12 KiB
Rust
#[macro_use]
|
|
extern crate serde_derive;
|
|
|
|
use std::collections::{BTreeMap, HashMap, HashSet};
|
|
use std::net;
|
|
use std::path::{Path, PathBuf};
|
|
use std::str;
|
|
use std::time::Duration;
|
|
use std::ffi::CString;
|
|
|
|
extern crate serde;
|
|
|
|
extern crate serde_test;
|
|
use self::serde_test::{
|
|
Error,
|
|
Token,
|
|
assert_ser_tokens,
|
|
assert_ser_tokens_error,
|
|
};
|
|
|
|
extern crate fnv;
|
|
use self::fnv::FnvHasher;
|
|
|
|
#[macro_use]
|
|
mod macros;
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
#[derive(Serialize)]
|
|
struct UnitStruct;
|
|
|
|
#[derive(Serialize)]
|
|
struct TupleStruct(i32, i32, i32);
|
|
|
|
#[derive(Serialize)]
|
|
struct Struct {
|
|
a: i32,
|
|
b: i32,
|
|
c: i32,
|
|
}
|
|
|
|
#[derive(Serialize, PartialEq, Debug)]
|
|
enum Enum {
|
|
Unit,
|
|
One(i32),
|
|
Seq(i32, i32),
|
|
Map { a: i32, b: i32 },
|
|
#[serde(skip_serializing)]
|
|
SkippedUnit,
|
|
#[serde(skip_serializing)]
|
|
SkippedOne(i32),
|
|
#[serde(skip_serializing)]
|
|
SkippedSeq(i32, i32),
|
|
#[serde(skip_serializing)]
|
|
SkippedMap { _a: i32, _b: i32 },
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
macro_rules! declare_tests {
|
|
($($name:ident { $($value:expr => $tokens:expr,)+ })+) => {
|
|
$(
|
|
#[test]
|
|
fn $name() {
|
|
$(
|
|
assert_ser_tokens(&$value, $tokens);
|
|
)+
|
|
}
|
|
)+
|
|
}
|
|
}
|
|
|
|
declare_tests! {
|
|
test_unit {
|
|
() => &[Token::Unit],
|
|
}
|
|
test_bool {
|
|
true => &[Token::Bool(true)],
|
|
false => &[Token::Bool(false)],
|
|
}
|
|
test_isizes {
|
|
0i8 => &[Token::I8(0)],
|
|
0i16 => &[Token::I16(0)],
|
|
0i32 => &[Token::I32(0)],
|
|
0i64 => &[Token::I64(0)],
|
|
}
|
|
test_usizes {
|
|
0u8 => &[Token::U8(0)],
|
|
0u16 => &[Token::U16(0)],
|
|
0u32 => &[Token::U32(0)],
|
|
0u64 => &[Token::U64(0)],
|
|
}
|
|
test_floats {
|
|
0f32 => &[Token::F32(0.)],
|
|
0f64 => &[Token::F64(0.)],
|
|
}
|
|
test_char {
|
|
'a' => &[Token::Char('a')],
|
|
}
|
|
test_str {
|
|
"abc" => &[Token::Str("abc")],
|
|
"abc".to_owned() => &[Token::Str("abc")],
|
|
}
|
|
test_option {
|
|
None::<i32> => &[Token::Option(false)],
|
|
Some(1) => &[
|
|
Token::Option(true),
|
|
Token::I32(1),
|
|
],
|
|
}
|
|
test_result {
|
|
Ok::<i32, i32>(0) => &[
|
|
Token::EnumNewType("Result", "Ok"),
|
|
Token::I32(0),
|
|
],
|
|
Err::<i32, i32>(1) => &[
|
|
Token::EnumNewType("Result", "Err"),
|
|
Token::I32(1),
|
|
],
|
|
}
|
|
test_slice {
|
|
&[0][..0] => &[
|
|
Token::SeqStart(Some(0)),
|
|
Token::SeqEnd,
|
|
],
|
|
&[1, 2, 3][..] => &[
|
|
Token::SeqStart(Some(3)),
|
|
Token::SeqSep,
|
|
Token::I32(1),
|
|
|
|
Token::SeqSep,
|
|
Token::I32(2),
|
|
|
|
Token::SeqSep,
|
|
Token::I32(3),
|
|
Token::SeqEnd,
|
|
],
|
|
}
|
|
test_array {
|
|
[0; 0] => &[
|
|
Token::SeqArrayStart(0),
|
|
Token::SeqEnd,
|
|
],
|
|
[1, 2, 3] => &[
|
|
Token::SeqArrayStart(3),
|
|
Token::SeqSep,
|
|
Token::I32(1),
|
|
|
|
Token::SeqSep,
|
|
Token::I32(2),
|
|
|
|
Token::SeqSep,
|
|
Token::I32(3),
|
|
Token::SeqEnd,
|
|
],
|
|
}
|
|
test_vec {
|
|
Vec::<isize>::new() => &[
|
|
Token::SeqStart(Some(0)),
|
|
Token::SeqEnd,
|
|
],
|
|
vec![vec![], vec![1], vec![2, 3]] => &[
|
|
Token::SeqStart(Some(3)),
|
|
Token::SeqSep,
|
|
Token::SeqStart(Some(0)),
|
|
Token::SeqEnd,
|
|
|
|
Token::SeqSep,
|
|
Token::SeqStart(Some(1)),
|
|
Token::SeqSep,
|
|
Token::I32(1),
|
|
Token::SeqEnd,
|
|
|
|
Token::SeqSep,
|
|
Token::SeqStart(Some(2)),
|
|
Token::SeqSep,
|
|
Token::I32(2),
|
|
|
|
Token::SeqSep,
|
|
Token::I32(3),
|
|
Token::SeqEnd,
|
|
Token::SeqEnd,
|
|
],
|
|
}
|
|
test_hashset {
|
|
HashSet::<isize>::new() => &[
|
|
Token::SeqStart(Some(0)),
|
|
Token::SeqEnd,
|
|
],
|
|
hashset![1] => &[
|
|
Token::SeqStart(Some(1)),
|
|
Token::SeqSep,
|
|
Token::I32(1),
|
|
Token::SeqEnd,
|
|
],
|
|
hashset![FnvHasher @ 1] => &[
|
|
Token::SeqStart(Some(1)),
|
|
Token::SeqSep,
|
|
Token::I32(1),
|
|
Token::SeqEnd,
|
|
],
|
|
}
|
|
test_tuple {
|
|
(1,) => &[
|
|
Token::TupleStart(1),
|
|
Token::TupleSep,
|
|
Token::I32(1),
|
|
Token::TupleEnd,
|
|
],
|
|
(1, 2, 3) => &[
|
|
Token::TupleStart(3),
|
|
Token::TupleSep,
|
|
Token::I32(1),
|
|
|
|
Token::TupleSep,
|
|
Token::I32(2),
|
|
|
|
Token::TupleSep,
|
|
Token::I32(3),
|
|
Token::TupleEnd,
|
|
],
|
|
}
|
|
test_btreemap {
|
|
btreemap![1 => 2] => &[
|
|
Token::MapStart(Some(1)),
|
|
Token::MapSep,
|
|
Token::I32(1),
|
|
Token::I32(2),
|
|
Token::MapEnd,
|
|
],
|
|
btreemap![1 => 2, 3 => 4] => &[
|
|
Token::MapStart(Some(2)),
|
|
Token::MapSep,
|
|
Token::I32(1),
|
|
Token::I32(2),
|
|
|
|
Token::MapSep,
|
|
Token::I32(3),
|
|
Token::I32(4),
|
|
Token::MapEnd,
|
|
],
|
|
btreemap![1 => btreemap![], 2 => btreemap![3 => 4, 5 => 6]] => &[
|
|
Token::MapStart(Some(2)),
|
|
Token::MapSep,
|
|
Token::I32(1),
|
|
Token::MapStart(Some(0)),
|
|
Token::MapEnd,
|
|
|
|
Token::MapSep,
|
|
Token::I32(2),
|
|
Token::MapStart(Some(2)),
|
|
Token::MapSep,
|
|
Token::I32(3),
|
|
Token::I32(4),
|
|
|
|
Token::MapSep,
|
|
Token::I32(5),
|
|
Token::I32(6),
|
|
Token::MapEnd,
|
|
Token::MapEnd,
|
|
],
|
|
}
|
|
test_hashmap {
|
|
HashMap::<isize, isize>::new() => &[
|
|
Token::MapStart(Some(0)),
|
|
Token::MapEnd,
|
|
],
|
|
hashmap![1 => 2] => &[
|
|
Token::MapStart(Some(1)),
|
|
Token::MapSep,
|
|
Token::I32(1),
|
|
Token::I32(2),
|
|
Token::MapEnd,
|
|
],
|
|
hashmap![FnvHasher @ 1 => 2] => &[
|
|
Token::MapStart(Some(1)),
|
|
Token::MapSep,
|
|
Token::I32(1),
|
|
Token::I32(2),
|
|
Token::MapEnd,
|
|
],
|
|
}
|
|
test_unit_struct {
|
|
UnitStruct => &[Token::UnitStruct("UnitStruct")],
|
|
}
|
|
test_tuple_struct {
|
|
TupleStruct(1, 2, 3) => &[
|
|
Token::TupleStructStart("TupleStruct", 3),
|
|
Token::TupleStructSep,
|
|
Token::I32(1),
|
|
|
|
Token::TupleStructSep,
|
|
Token::I32(2),
|
|
|
|
Token::TupleStructSep,
|
|
Token::I32(3),
|
|
Token::TupleStructEnd,
|
|
],
|
|
}
|
|
test_struct {
|
|
Struct { a: 1, b: 2, c: 3 } => &[
|
|
Token::StructStart("Struct", 3),
|
|
Token::StructSep,
|
|
Token::Str("a"),
|
|
Token::I32(1),
|
|
|
|
Token::StructSep,
|
|
Token::Str("b"),
|
|
Token::I32(2),
|
|
|
|
Token::StructSep,
|
|
Token::Str("c"),
|
|
Token::I32(3),
|
|
Token::StructEnd,
|
|
],
|
|
}
|
|
test_enum {
|
|
Enum::Unit => &[Token::EnumUnit("Enum", "Unit")],
|
|
Enum::One(42) => &[Token::EnumNewType("Enum", "One"), Token::I32(42)],
|
|
Enum::Seq(1, 2) => &[
|
|
Token::EnumSeqStart("Enum", "Seq", 2),
|
|
Token::EnumSeqSep,
|
|
Token::I32(1),
|
|
|
|
Token::EnumSeqSep,
|
|
Token::I32(2),
|
|
Token::EnumSeqEnd,
|
|
],
|
|
Enum::Map { a: 1, b: 2 } => &[
|
|
Token::EnumMapStart("Enum", "Map", 2),
|
|
Token::EnumMapSep,
|
|
Token::Str("a"),
|
|
Token::I32(1),
|
|
|
|
Token::EnumMapSep,
|
|
Token::Str("b"),
|
|
Token::I32(2),
|
|
Token::EnumMapEnd,
|
|
],
|
|
}
|
|
test_box {
|
|
Box::new(0i32) => &[Token::I32(0)],
|
|
}
|
|
test_boxed_slice {
|
|
Box::new([0, 1, 2]) => &[
|
|
Token::SeqArrayStart(3),
|
|
Token::SeqSep,
|
|
Token::I32(0),
|
|
Token::SeqSep,
|
|
Token::I32(1),
|
|
Token::SeqSep,
|
|
Token::I32(2),
|
|
Token::SeqEnd,
|
|
],
|
|
}
|
|
test_duration {
|
|
Duration::new(1, 2) => &[
|
|
Token::StructStart("Duration", 2),
|
|
Token::StructSep,
|
|
Token::Str("secs"),
|
|
Token::U64(1),
|
|
|
|
Token::StructSep,
|
|
Token::Str("nanos"),
|
|
Token::U32(2),
|
|
Token::StructEnd,
|
|
],
|
|
}
|
|
test_range {
|
|
1u32..2u32 => &[
|
|
Token::StructStart("Range", 2),
|
|
Token::StructSep,
|
|
Token::Str("start"),
|
|
Token::U32(1),
|
|
|
|
Token::StructSep,
|
|
Token::Str("end"),
|
|
Token::U32(2),
|
|
Token::StructEnd,
|
|
],
|
|
}
|
|
test_net_ipv4addr {
|
|
"1.2.3.4".parse::<net::Ipv4Addr>().unwrap() => &[Token::Str("1.2.3.4")],
|
|
}
|
|
test_net_ipv6addr {
|
|
"::1".parse::<net::Ipv6Addr>().unwrap() => &[Token::Str("::1")],
|
|
}
|
|
test_net_socketaddr {
|
|
"1.2.3.4:1234".parse::<net::SocketAddr>().unwrap() => &[Token::Str("1.2.3.4:1234")],
|
|
"1.2.3.4:1234".parse::<net::SocketAddrV4>().unwrap() => &[Token::Str("1.2.3.4:1234")],
|
|
"[::1]:1234".parse::<net::SocketAddrV6>().unwrap() => &[Token::Str("[::1]:1234")],
|
|
}
|
|
test_path {
|
|
Path::new("/usr/local/lib") => &[
|
|
Token::Str("/usr/local/lib"),
|
|
],
|
|
}
|
|
test_path_buf {
|
|
PathBuf::from("/usr/local/lib") => &[
|
|
Token::Str("/usr/local/lib"),
|
|
],
|
|
}
|
|
test_cstring {
|
|
CString::new("abc").unwrap() => &[
|
|
Token::Bytes(b"abc"),
|
|
],
|
|
}
|
|
test_cstr {
|
|
(&*CString::new("abc").unwrap()) => &[
|
|
Token::Bytes(b"abc"),
|
|
],
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "unstable")]
|
|
#[test]
|
|
fn test_net_ipaddr() {
|
|
assert_ser_tokens(
|
|
&"1.2.3.4".parse::<net::IpAddr>().unwrap(),
|
|
&[Token::Str("1.2.3.4")],
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(unix)]
|
|
fn test_cannot_serialize_paths() {
|
|
let path = unsafe {
|
|
str::from_utf8_unchecked(b"Hello \xF0\x90\x80World")
|
|
};
|
|
assert_ser_tokens_error(
|
|
&Path::new(path),
|
|
&[],
|
|
Error::Message("path contains invalid UTF-8 characters".to_owned()));
|
|
|
|
let mut path_buf = PathBuf::new();
|
|
path_buf.push(path);
|
|
|
|
assert_ser_tokens_error(
|
|
&path_buf,
|
|
&[],
|
|
Error::Message("path contains invalid UTF-8 characters".to_owned()));
|
|
}
|
|
|
|
#[test]
|
|
fn test_enum_skipped() {
|
|
assert_ser_tokens_error(
|
|
&Enum::SkippedUnit,
|
|
&[],
|
|
Error::Message("the enum variant Enum::SkippedUnit cannot be serialized".to_owned()));
|
|
assert_ser_tokens_error(
|
|
&Enum::SkippedOne(42),
|
|
&[],
|
|
Error::Message("the enum variant Enum::SkippedOne cannot be serialized".to_owned()));
|
|
assert_ser_tokens_error(
|
|
&Enum::SkippedSeq(1, 2),
|
|
&[],
|
|
Error::Message("the enum variant Enum::SkippedSeq cannot be serialized".to_owned()));
|
|
assert_ser_tokens_error(
|
|
&Enum::SkippedMap { _a: 1, _b: 2 },
|
|
&[],
|
|
Error::Message("the enum variant Enum::SkippedMap cannot be serialized".to_owned()));
|
|
}
|