Implement Serialize/Deserialize for OsStr/OsString
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).
This commit is contained in:
parent
71ccc5753b
commit
ce687431f3
17
appveyor.yml
Normal file
17
appveyor.yml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
environment:
|
||||||
|
matrix:
|
||||||
|
- APPVEYOR_RUST_CHANNEL: stable
|
||||||
|
- APPVEYOR_RUST_CHANNEL: nightly
|
||||||
|
|
||||||
|
install:
|
||||||
|
# Install rust, x86_64-pc-windows-msvc host
|
||||||
|
- appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
|
||||||
|
- rustup-init.exe -y --default-host x86_64-pc-windows-msvc --default-toolchain %APPVEYOR_RUST_CHANNEL%
|
||||||
|
- set PATH=C:\msys64\usr\bin;%PATH%;C:\Users\appveyor\.cargo\bin
|
||||||
|
- rustc -vV
|
||||||
|
- cargo -vV
|
||||||
|
|
||||||
|
build: false
|
||||||
|
|
||||||
|
test_script:
|
||||||
|
- sh -c 'PATH=`rustc --print sysroot`/bin:$PATH ./travis.sh'
|
@ -26,7 +26,7 @@ use std::net;
|
|||||||
use std::path;
|
use std::path;
|
||||||
use core::str;
|
use core::str;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use std::ffi::CString;
|
use std::ffi::{CString, OsString};
|
||||||
#[cfg(all(feature = "std", feature="unstable"))]
|
#[cfg(all(feature = "std", feature="unstable"))]
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
|
|
||||||
@ -910,6 +910,7 @@ impl Visitor for PathBufVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
impl Deserialize for path::PathBuf {
|
impl Deserialize for path::PathBuf {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<path::PathBuf, D::Error>
|
fn deserialize<D>(deserializer: D) -> Result<path::PathBuf, D::Error>
|
||||||
@ -921,6 +922,110 @@ impl Deserialize for path::PathBuf {
|
|||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#[cfg(all(feature = "std", any(unix, windows)))]
|
||||||
|
enum OsStringKind {
|
||||||
|
Unix,
|
||||||
|
Windows,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "std", any(unix, windows)))]
|
||||||
|
static OSSTR_VARIANTS: &'static [&'static str] = &["Unix", "Windows"];
|
||||||
|
|
||||||
|
#[cfg(all(feature = "std", any(unix, windows)))]
|
||||||
|
impl Deserialize for OsStringKind {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<OsStringKind, D::Error>
|
||||||
|
where D: Deserializer
|
||||||
|
{
|
||||||
|
struct KindVisitor;
|
||||||
|
|
||||||
|
impl Visitor for KindVisitor {
|
||||||
|
type Value = OsStringKind;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str("`Unix` or `Windows`")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_u32<E>(self, value: u32) -> Result<OsStringKind, E>
|
||||||
|
where E: Error,
|
||||||
|
{
|
||||||
|
match value {
|
||||||
|
0 => Ok(OsStringKind::Unix),
|
||||||
|
1 => Ok(OsStringKind::Windows),
|
||||||
|
_ => Err(Error::invalid_value(Unexpected::Unsigned(value as u64), &self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E>(self, value: &str) -> Result<OsStringKind, E>
|
||||||
|
where E: Error,
|
||||||
|
{
|
||||||
|
match value {
|
||||||
|
"Unix" => Ok(OsStringKind::Unix),
|
||||||
|
"Windows" => Ok(OsStringKind::Windows),
|
||||||
|
_ => Err(Error::unknown_variant(value, OSSTR_VARIANTS)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_bytes<E>(self, value: &[u8]) -> Result<OsStringKind, E>
|
||||||
|
where E: Error,
|
||||||
|
{
|
||||||
|
match value {
|
||||||
|
b"Unix" => Ok(OsStringKind::Unix),
|
||||||
|
b"Windows" => Ok(OsStringKind::Windows),
|
||||||
|
_ => {
|
||||||
|
match str::from_utf8(value) {
|
||||||
|
Ok(value) => Err(Error::unknown_variant(value, OSSTR_VARIANTS)),
|
||||||
|
Err(_) => {
|
||||||
|
Err(Error::invalid_value(Unexpected::Bytes(value), &self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.deserialize(KindVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "std", any(unix, windows)))]
|
||||||
|
struct OsStringVisitor;
|
||||||
|
|
||||||
|
#[cfg(all(feature = "std", any(unix, windows)))]
|
||||||
|
impl Visitor for OsStringVisitor {
|
||||||
|
type Value = OsString;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str("os string")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn visit_enum<V>(self, visitor: V) -> Result<OsString, V::Error>
|
||||||
|
where V: EnumVisitor,
|
||||||
|
{
|
||||||
|
use std::os::unix::ffi::OsStringExt;
|
||||||
|
|
||||||
|
match try!(visitor.visit_variant()) {
|
||||||
|
(OsStringKind::Unix, variant) => {
|
||||||
|
variant.visit_newtype().map(OsString::from_vec)
|
||||||
|
}
|
||||||
|
(OsStringKind::Windows, _) => {
|
||||||
|
Err(Error::custom("cannot deserialize windows os string on unix"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "std", any(unix, windows)))]
|
||||||
|
impl Deserialize for OsString {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<OsString, D::Error>
|
||||||
|
where D: Deserializer
|
||||||
|
{
|
||||||
|
deserializer.deserialize_enum("OsString", OSSTR_VARIANTS, OsStringVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||||
impl<T: Deserialize> Deserialize for Box<T> {
|
impl<T: Deserialize> Deserialize for Box<T> {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Box<T>, D::Error>
|
fn deserialize<D>(deserializer: D) -> Result<Box<T>, D::Error>
|
||||||
|
@ -20,7 +20,7 @@ use core::ops;
|
|||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use std::path;
|
use std::path;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use std::ffi::{CString, CStr};
|
use std::ffi::{CString, CStr, OsString, OsStr};
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
#[cfg(all(feature = "alloc", not(feature = "std")))]
|
#[cfg(all(feature = "alloc", not(feature = "std")))]
|
||||||
@ -714,6 +714,41 @@ impl Serialize for path::PathBuf {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "std", any(unix, windows)))]
|
||||||
|
impl Serialize for OsStr {
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where S: Serializer
|
||||||
|
{
|
||||||
|
use std::os::unix::ffi::OsStrExt;
|
||||||
|
serializer.serialize_newtype_variant("OsString",
|
||||||
|
0,
|
||||||
|
"Unix",
|
||||||
|
self.as_bytes())
|
||||||
|
}
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where S: Serializer
|
||||||
|
{
|
||||||
|
use std::os::windows::ffi::OsStrExt;
|
||||||
|
let val = self.encode_wide().collect::<Vec<_>>();
|
||||||
|
serializer.serialize_newtype_variant("OsString",
|
||||||
|
1,
|
||||||
|
"Windows",
|
||||||
|
&val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "std", any(unix, windows)))]
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl Serialize for OsString {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where S: Serializer
|
||||||
|
{
|
||||||
|
self.as_os_str().serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "unstable")]
|
#[cfg(feature = "unstable")]
|
||||||
impl<T> Serialize for NonZero<T>
|
impl<T> Serialize for NonZero<T>
|
||||||
where T: Serialize + Zeroable
|
where T: Serialize + Zeroable
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
#![feature(lang_items, start, libc)]
|
#![feature(lang_items, start, libc, compiler_builtins_lib)]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
extern crate libc;
|
extern crate libc;
|
||||||
|
extern crate compiler_builtins;
|
||||||
|
|
||||||
#[start]
|
#[start]
|
||||||
fn start(_argc: isize, _argv: *const *const u8) -> isize {
|
fn start(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
|
@ -8,7 +8,7 @@ use std::net;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::ffi::CString;
|
use std::ffi::{CString, OsString};
|
||||||
|
|
||||||
#[cfg(feature = "unstable")]
|
#[cfg(feature = "unstable")]
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
@ -913,6 +913,56 @@ declare_tests! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
#[test]
|
||||||
|
fn test_osstring() {
|
||||||
|
use std::os::unix::ffi::OsStringExt;
|
||||||
|
|
||||||
|
let value = OsString::from_vec(vec![1, 2, 3]);
|
||||||
|
let tokens = [
|
||||||
|
Token::EnumStart("OsString"),
|
||||||
|
Token::Str("Unix"),
|
||||||
|
Token::SeqStart(Some(2)),
|
||||||
|
Token::SeqSep,
|
||||||
|
Token::U8(1),
|
||||||
|
|
||||||
|
Token::SeqSep,
|
||||||
|
Token::U8(2),
|
||||||
|
|
||||||
|
Token::SeqSep,
|
||||||
|
Token::U8(3),
|
||||||
|
Token::SeqEnd,
|
||||||
|
];
|
||||||
|
|
||||||
|
assert_de_tokens(&value, &tokens);
|
||||||
|
assert_de_tokens_ignore(&tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
#[test]
|
||||||
|
fn test_osstring() {
|
||||||
|
use std::os::windows::ffi::OsStringExt;
|
||||||
|
|
||||||
|
let value = OsString::from_wide(&[1, 2, 3]);
|
||||||
|
let tokens = [
|
||||||
|
Token::EnumStart("OsString"),
|
||||||
|
Token::Str("Windows"),
|
||||||
|
Token::SeqStart(Some(2)),
|
||||||
|
Token::SeqSep,
|
||||||
|
Token::U16(1),
|
||||||
|
|
||||||
|
Token::SeqSep,
|
||||||
|
Token::U16(2),
|
||||||
|
|
||||||
|
Token::SeqSep,
|
||||||
|
Token::U16(3),
|
||||||
|
Token::SeqEnd,
|
||||||
|
];
|
||||||
|
|
||||||
|
assert_de_tokens(&value, &tokens);
|
||||||
|
assert_de_tokens_ignore(&tokens);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "unstable")]
|
#[cfg(feature = "unstable")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_cstr() {
|
fn test_cstr() {
|
||||||
|
@ -422,6 +422,7 @@ fn test_net_ipaddr() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg(unix)]
|
||||||
fn test_cannot_serialize_paths() {
|
fn test_cannot_serialize_paths() {
|
||||||
let path = unsafe {
|
let path = unsafe {
|
||||||
str::from_utf8_unchecked(b"Hello \xF0\x90\x80World")
|
str::from_utf8_unchecked(b"Hello \xF0\x90\x80World")
|
||||||
|
@ -10,6 +10,11 @@ channel() {
|
|||||||
pwd
|
pwd
|
||||||
(set -x; cargo "$@")
|
(set -x; cargo "$@")
|
||||||
fi
|
fi
|
||||||
|
elif [ -n "${APPVEYOR}" ]; then
|
||||||
|
if [ "${APPVEYOR_RUST_CHANNEL}" = "${CHANNEL}" ]; then
|
||||||
|
pwd
|
||||||
|
(set -x; cargo "$@")
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
pwd
|
pwd
|
||||||
(set -x; cargo "+${CHANNEL}" "$@")
|
(set -x; cargo "+${CHANNEL}" "$@")
|
||||||
|
Loading…
Reference in New Issue
Block a user